[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