[Supervisor-checkins] r791 - supervisor/branches/gsoc/src/supervisor

Siddhant Goel siddhantgoel at gmail.com
Mon Aug 4 10:30:54 EDT 2008


Author: Siddhant Goel <siddhantgoel at gmail.com>
Date: Mon Aug  4 10:30:54 2008
New Revision: 791

Log:
added foreground mode, and tab completions for supervisorctl

Modified:
   supervisor/branches/gsoc/src/supervisor/supervisorctl.py

Modified: supervisor/branches/gsoc/src/supervisor/supervisorctl.py
==============================================================================
--- supervisor/branches/gsoc/src/supervisor/supervisorctl.py	(original)
+++ supervisor/branches/gsoc/src/supervisor/supervisorctl.py	Mon Aug  4 10:30:54 2008
@@ -42,11 +42,68 @@
 import asyncore
 import errno
 import urlparse
+import threading
 
 from supervisor.options import ClientOptions
 from supervisor.options import split_namespec
 from supervisor import xmlrpc
 
+class fgthread(threading.Thread):
+    
+    # A subclass of threading.Thread, with a kill() method.
+    # To be used for foreground output/error streaming.
+    # http://mail.python.org/pipermail/python-list/2004-May/260937.html
+  
+    def __init__(self, program, ctl):
+        threading.Thread.__init__(self)
+        import http_client
+        self.killed = False
+        self.program=program
+        self.ctl=ctl
+        self.listener=http_client.Listener()
+        self.output_handler=http_client.HTTPHandler(self.listener,
+                                                    self.ctl.options.username,
+                                                    self.ctl.options.password)
+        self.error_handler=http_client.HTTPHandler(self.listener,
+                                                   self.ctl.options.username,
+                                                   self.ctl.options.password)
+
+    def start(self):
+        # Start the thread
+        self.__run_backup = self.run
+        self.run = self.__run
+        threading.Thread.start(self)
+
+    def run(self):
+        self.output_handler.get(self.ctl.options.serverurl,
+                                '/logtail/%s/stdout'%self.program)
+        self.error_handler.get(self.ctl.options.serverurl,
+                               '/logtail/%s/stderr'%self.program)
+        asyncore.loop()
+
+    def __run(self):
+        # Hacked run function, which installs the trace
+        sys.settrace(self.globaltrace)
+        self.__run_backup()
+        self.run = self.__run_backup
+
+    def globaltrace(self, frame, why, arg):
+        if why == 'call':
+            return self.localtrace
+        else:
+            return None
+
+    def localtrace(self, frame, why, arg):
+        if self.killed:
+            if why == 'line':
+                raise SystemExit()
+        return self.localtrace
+
+    def kill(self):
+        self.output_handler.close()
+        self.error_handler.close()
+        self.killed = True
+
 class Controller(cmd.Cmd):
 
     def __init__(self, options, completekey='tab', stdin=None,
@@ -54,6 +111,10 @@
         self.options = options
         self.prompt = self.options.prompt + '> '
         self.options.plugins = []
+        self.vocab = ['add','exit','maintail','pid','reload',
+                      'restart','start','stop','version','clear',
+                      'fg','open','quit','remove','shutdown','status',
+                      'tail']
         cmd.Cmd.__init__(self, completekey, stdin, stdout)
         for name, factory, kwargs in self.options.plugin_factories:
             plugin = factory(self, **kwargs)
@@ -160,6 +221,77 @@
             raise
         return True
 
+    def completionmatches(self,text,line,flag=0):
+        supervisor=self.get_supervisor()
+        li=supervisor.getAllProcessInfo()
+        groups=[]
+        programs=[]
+        groupwiseprograms={}
+        for i in li:
+            programs.append(i['name'])
+            if i['group'] not in groups:
+                groups.append(i['group'])
+                groupwiseprograms[i['group']]=[]
+            groupwiseprograms[i['group']].append(i['name'])
+        total=[]
+        for i in groups:
+            if i in programs:
+                total.append(i+' ')
+            else:
+                for n in groupwiseprograms[i]:
+                    total.append(i+':'+n+' ')
+        if flag:
+            return [i+' ' for i in groups if i.startswith(text)]
+        current=line.split()[-1]
+        if len(line.split()) == 1:
+            return total
+        else:
+            current=line.split()[-1]
+            if line.endswith(' ') and len(line.split()) > 1:
+                results=[i for i in total if i.startswith(text)]
+                return results
+            if ':' in current:
+                g=current.split(':')[0]
+                results = [i+' ' for i in groupwiseprograms[g]
+                           if i.startswith(text)]
+                return results
+            results = [i for i in total if i.startswith(text)]
+            return results
+
+    def complete(self,text,state):
+        try:
+            import readline
+        except ImportError:
+            return None
+        line=readline.get_line_buffer()
+        if line == '':
+            results = [i+' ' for i in self.vocab if i.startswith(text)]+[None]
+            return results[state]
+        else:
+            exp=line.split()[0]
+            if exp in ['start','stop','restart','clear','status','tail','fg']:
+                if not line.endswith(' ') and len(line.split()) == 1:
+                    return [text+' ',None][state]
+                if exp == 'fg':
+                    if line.endswith(' ') and len(line.split()) > 1:
+                        return None
+                results=self.completionmatches(text,line)+[None]
+                return results[state]
+            elif exp in ['maintail','pid','reload','shutdown','exit','open',
+                         'quit','version','EOF']:
+                return None
+            elif exp == 'help':
+                if line.endswith(' ') and len(line.split()) > 1:
+                    return None
+                results=[i+' ' for i in self.vocab if i.startswith(text)]+[None]
+                return results[state]
+            elif exp in ['add','remove']:
+                results=self.completionmatches(text,line,flag=1)+[None]
+                return results[state]
+            else:
+                results=[i+' ' for i in self.vocab if i.startswith(text)]+[None]
+                return results[state]
+
     def do_help(self, arg):
         for plugin in self.options.plugins:
             plugin.do_help(arg)
@@ -780,6 +912,53 @@
             "version\t\t\tShow the version of the remote supervisord "
             "process")
 
+    def do_fg(self,args=None):
+        if not self.ctl.upcheck():
+            return
+        if not args:
+            self.help_fg()
+            return
+        args=args.split()
+        if len(args)>1:
+            self.ctl.output('ERROR : too many process names supplied')
+            return
+        program=args[0]
+        supervisor=self.ctl.get_supervisor()
+        try:
+            info=supervisor.getProcessInfo(program)
+        except xmlrpclib.Fault, msg:
+            if msg.faultCode == 10:
+                self.ctl.output('Bad process name supplied')
+                return
+            # for any other fault
+            self.ctl.output(str(msg))
+            return
+        if info['pid'] == 0:
+            self.ctl.output('ERROR : Process not running')
+            return
+        # everything good; continue
+        try:
+            a=fgthread(program,self.ctl)
+            # this thread takes care of
+            # the output/error messages
+            a.start()
+            while True:
+                # this takes care of the user input
+                inp = raw_input() + '\n'
+                try:
+                    supervisor.sendProcessStdin(program,inp)
+                except xmlrpclib.Fault, msg:
+                    self.ctl.output('Error -- %s' % msg)
+                    continue
+        except KeyboardInterrupt:
+            a.kill() # kill the thread
+            self.ctl.output('Exiting foreground')
+        return
+
+    def help_fg(self,args=None):
+        self.ctl.output('fg <process>\tConnect to a process in foreground mode')
+        self.ctl.output('Press Ctrl+C to exit foreground')
+
 def main(args=None, options=None):
     if options is None:
         options = ClientOptions()


More information about the Supervisor-checkins mailing list