[Supervisor-checkins] r880 - in supervisor/trunk: . src/supervisor src/supervisor/skel src/supervisor/tests

Mike Naberezny mike at maintainable.com
Thu Jul 23 17:42:31 EDT 2009


Author: Mike Naberezny <mike at maintainable.com>
Date: Thu Jul 23 17:42:30 2009
New Revision: 880

Log:
Revert r879.

Modified:
   supervisor/trunk/CHANGES.txt
   supervisor/trunk/src/supervisor/rpcinterface.py
   supervisor/trunk/src/supervisor/skel/sample.conf
   supervisor/trunk/src/supervisor/supervisorctl.py
   supervisor/trunk/src/supervisor/tests/base.py
   supervisor/trunk/src/supervisor/tests/test_rpcinterfaces.py
   supervisor/trunk/src/supervisor/tests/test_supervisorctl.py

Modified: supervisor/trunk/CHANGES.txt
==============================================================================
--- supervisor/trunk/CHANGES.txt	(original)
+++ supervisor/trunk/CHANGES.txt	Thu Jul 23 17:42:30 2009
@@ -8,24 +8,6 @@
 
   - Added release dates to CHANGES.txt.
 
-  - The configuration rereading functionality introduced in 3.0a7 has been
-    moved into its own RPC interface and supervisorctl plugin.  These will
-    continue to be bundled with the main Supervisor package.  
-    
-    Users who upgraded to 3.0a7 and wish to continue using the reread
-    functionality must add these lines to their existing supervisord.conf:
-
-    [rpcinterface:supervisor_reread]
-    supervisor.rpcinterface_factory = supervisor.rpcinterface:make_reread_rpcinterface
-    [ctlplugin:supervisor_reread]
-    supervisor.ctl_factory = supervisor.supervisorctl:make_reread_controllerplugin
-
-    The lines above are now included in the sample configuration file 
-    output by the ``echo_supervisord_conf`` console script.  
-
-    Please note that due to this change, supervisorctl from 3.0a7 cannot
-    control the reread functions of 3.0a8 (and later).
-
 3.0a7 (2009-05-24)
  
   - We now bundle our own patched version of Medusa contributed by Jason

Modified: supervisor/trunk/src/supervisor/rpcinterface.py
==============================================================================
--- supervisor/trunk/src/supervisor/rpcinterface.py	(original)
+++ supervisor/trunk/src/supervisor/rpcinterface.py	Thu Jul 23 17:42:30 2009
@@ -165,6 +165,56 @@
         self.supervisord.options.mood = SupervisorStates.RESTARTING
         return True
 
+    def reloadConfig(self):
+        """
+        Reload configuration
+
+        @return boolean result  always return True unless error
+        """
+        self._update('reloadConfig')
+        try:
+            self.supervisord.options.process_config_file(do_usage=False)
+        except ValueError, msg:
+            raise RPCError(Faults.CANT_REREAD, msg)
+            
+        added, changed, removed = self.supervisord.diff_to_active()
+
+        added = [group.name for group in added]
+        changed = [group.name for group in changed]
+        removed = [group.name for group in removed]
+        return [[added, changed, removed]] # cannot return len > 1, apparently
+
+    def addProcessGroup(self, name):
+        """ Update the config for a running process from config file.
+
+        @param string name         name of process group to add
+        @return boolean result     true if successful
+        """
+        self._update('addProcessGroup')
+
+        for config in self.supervisord.options.process_group_configs:
+            if config.name == name:
+                result = self.supervisord.add_process_group(config)
+                if not result:
+                    raise RPCError(Faults.ALREADY_ADDED, name)
+                return True
+        raise RPCError(Faults.BAD_NAME, name)
+
+    def removeProcessGroup(self, name):
+        """ Remove a stopped process from the active configuration.
+
+        @param string name         name of process group to remove
+        @return boolean result     Indicates wether the removal was successful
+        """
+        self._update('removeProcessGroup')
+        if name not in self.supervisord.process_groups:
+            raise RPCError(Faults.BAD_NAME, name)
+
+        result = self.supervisord.remove_process_group(name)
+        if not result:
+            raise RPCError(Faults.STILL_RUNNING)
+        return True
+
     def _getAllProcesses(self, lexical=False):
         # if lexical is true, return processes sorted in lexical order,
         # otherwise, sort in priority order
@@ -405,6 +455,29 @@
         killall.rpcinterface = self
         return killall # deferred
 
+    def getAllConfigInfo(self):
+        """ Get info about all availible process configurations. Each record
+        represents a single process (i.e. groups get flattened).
+
+        @return array result  An array of process config info records
+        """
+        self._update('getAllConfigInfo')
+
+        configinfo = []
+        for gconfig in self.supervisord.options.process_group_configs:
+            inuse = gconfig.name in self.supervisord.process_groups
+            for pconfig in gconfig.process_configs:
+                configinfo.append(
+                    { 'name': pconfig.name,
+                      'group': gconfig.name,
+                      'inuse': inuse,
+                      'autostart': pconfig.autostart,
+                      'group_prio': gconfig.priority,
+                      'process_prio': pconfig.priority })
+
+        configinfo.sort()
+        return configinfo
+
     def _interpretProcessInfo(self, info):
         state = info['state']
 
@@ -790,94 +863,7 @@
 def isNotRunning(process):
     return not isRunning(process)
 
-
-class RereadNamespaceRPCInterface:
-    def __init__(self, supervisord):
-        self.supervisord = supervisord
-
-    def _update(self, text):
-        self.update_text = text # for unit tests, mainly
-        if self.supervisord.options.mood < SupervisorStates.RUNNING:
-            raise RPCError(Faults.SHUTDOWN_STATE)
-
-    def getAllConfigInfo(self):
-        """ Get info about all availible process configurations. Each record
-        represents a single process (i.e. groups get flattened).
-
-        @return array result  An array of process config info records
-        """
-        self._update('getAllConfigInfo')
-
-        configinfo = []
-        for gconfig in self.supervisord.options.process_group_configs:
-            inuse = gconfig.name in self.supervisord.process_groups
-            for pconfig in gconfig.process_configs:
-                configinfo.append(
-                    { 'name': pconfig.name,
-                      'group': gconfig.name,
-                      'inuse': inuse,
-                      'autostart': pconfig.autostart,
-                      'group_prio': gconfig.priority,
-                      'process_prio': pconfig.priority })
-
-        configinfo.sort()
-        return configinfo
-
-    def reloadConfig(self):
-        """
-        Reload configuration
-
-        @return boolean result  always return True unless error
-        """
-        self._update('reloadConfig')
-        try:
-            self.supervisord.options.process_config_file(do_usage=False)
-        except ValueError, msg:
-            raise RPCError(Faults.CANT_REREAD, msg)
-            
-        added, changed, removed = self.supervisord.diff_to_active()
-
-        added = [group.name for group in added]
-        changed = [group.name for group in changed]
-        removed = [group.name for group in removed]
-        return [[added, changed, removed]] # cannot return len > 1, apparently
-
-    def addProcessGroup(self, name):
-        """ Update the config for a running process from config file.
-
-        @param string name         name of process group to add
-        @return boolean result     true if successful
-        """
-        self._update('addProcessGroup')
-
-        for config in self.supervisord.options.process_group_configs:
-            if config.name == name:
-                result = self.supervisord.add_process_group(config)
-                if not result:
-                    raise RPCError(Faults.ALREADY_ADDED, name)
-                return True
-        raise RPCError(Faults.BAD_NAME, name)
-
-    def removeProcessGroup(self, name):
-        """ Remove a stopped process from the active configuration.
-
-        @param string name         name of process group to remove
-        @return boolean result     Indicates wether the removal was successful
-        """
-        self._update('removeProcessGroup')
-        if name not in self.supervisord.process_groups:
-            raise RPCError(Faults.BAD_NAME, name)
-
-        result = self.supervisord.remove_process_group(name)
-        if not result:
-            raise RPCError(Faults.STILL_RUNNING)
-        return True
-
-
 # this is not used in code but referenced via an entry point in the conf file
 def make_main_rpcinterface(supervisord):
     return SupervisorNamespaceRPCInterface(supervisord)
 
-# this is not used in code but referenced via an entry point in the conf file
-def make_reread_rpcinterface(supervisord):
-    return RereadNamespaceRPCInterface(supervisord)

Modified: supervisor/trunk/src/supervisor/skel/sample.conf
==============================================================================
--- supervisor/trunk/src/supervisor/skel/sample.conf	(original)
+++ supervisor/trunk/src/supervisor/skel/sample.conf	Thu Jul 23 17:42:30 2009
@@ -30,20 +30,12 @@
 ;environment=KEY=value       ; (key value pairs to add to environment)
 ;strip_ansi=false            ; (strip ansi escape codes in logs; def. false)
 
-; the section [rpcinterface:supervisor] must remain in the config file
-; for RPC (supervisorctl/web interface) to work, additional interfaces may be
+; the below section must remain in the config file for RPC
+; (supervisorctl/web interface) to work, additional interfaces may be
 ; added by defining them in separate rpcinterface: sections
 [rpcinterface:supervisor]
 supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
 
-; the sections [rpcinterface:supervisor_reread] and [ctlplugin:supervisor_reread]
-; enable supervisor's ability to reread its configuration after it has started.
-; remove these two sections if you want to disable this functionality. 
-[rpcinterface:supervisor_reread]
-supervisor.rpcinterface_factory = supervisor.rpcinterface:make_reread_rpcinterface
-[ctlplugin:supervisor_reread]
-supervisor.ctl_factory = supervisor.supervisorctl:make_reread_controllerplugin
-
 [supervisorctl]
 serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL  for a unix socket
 ;serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket

Modified: supervisor/trunk/src/supervisor/supervisorctl.py
==============================================================================
--- supervisor/trunk/src/supervisor/supervisorctl.py	(original)
+++ supervisor/trunk/src/supervisor/supervisorctl.py	Thu Jul 23 17:42:30 2009
@@ -790,142 +790,6 @@
     def help_reload(self):
         self.ctl.output("reload \t\tRestart the remote supervisord.")
 
-    def _clearresult(self, result):
-        name = result['name']
-        code = result['status']
-        template = '%s: ERROR (%s)'
-        if code == xmlrpc.Faults.BAD_NAME:
-            return template % (name, 'no such process')
-        elif code == xmlrpc.Faults.FAILED:
-            return template % (name, 'failed')
-        elif code == xmlrpc.Faults.SUCCESS:
-            return '%s: cleared' % name
-        raise ValueError('Unknown result code %s for %s' % (code, name))
-
-    def do_clear(self, arg):
-        if not self.ctl.upcheck():
-            return
-
-        names = arg.strip().split()
-
-        if not names:
-            self.ctl.output('Error: clear requires a process name')
-            self.help_clear()
-            return
-
-        supervisor = self.ctl.get_supervisor()
-
-        if 'all' in names:
-            results = supervisor.clearAllProcessLogs()
-            for result in results:
-                result = self._clearresult(result)
-                self.ctl.output(result)
-
-        else:
-
-            for name in names:
-                try:
-                    result = supervisor.clearProcessLogs(name)
-                except xmlrpclib.Fault, e:
-                    error = self._clearresult({'status':e.faultCode,
-                                               'name':name,
-                                               'description':e.faultString})
-                    self.ctl.output(error)
-                else:
-                    self.ctl.output('%s: cleared' % name)
-
-    def help_clear(self):
-        self.ctl.output("clear <name>\t\tClear a process' log files.")
-        self.ctl.output(
-            "clear <name> <name>\tClear multiple process' log files")
-        self.ctl.output("clear all\t\tClear all process' log files")
-
-    def do_open(self, arg):
-        url = arg.strip()
-        parts = urlparse.urlparse(url)
-        if parts[0] not in ('unix', 'http'):
-            self.ctl.output('ERROR: url must be http:// or unix://')
-            return
-        self.ctl.options.serverurl = url
-        self.do_status('')
-
-    def help_open(self):
-        self.ctl.output("open <url>\tConnect to a remote supervisord process.")
-        self.ctl.output("\t\t(for UNIX domain socket, use unix:///socket/path)")
-
-    def do_version(self, arg):
-        if not self.ctl.upcheck():
-            return
-        supervisor = self.ctl.get_supervisor()
-        self.ctl.output(supervisor.getSupervisorVersion())
-
-    def help_version(self):
-        self.ctl.output(
-            "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.ctl.output('Error: no process name supplied')
-            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 == xmlrpc.Faults.BAD_NAME:
-                self.ctl.output('Error: bad process name supplied')
-                return
-            # for any other fault
-            self.ctl.output(str(msg))
-            return
-        if not info['state'] == states.ProcessStates.RUNNING:
-            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:
-                    if msg.faultCode == xmlrpc.Faults.NOT_RUNNING:
-                        self.ctl.output('Process got killed')
-                        self.ctl.output('Exiting foreground')
-                        a.kill()
-                        return
-                info = supervisor.getProcessInfo(program)
-                if not info['state'] == states.ProcessStates.RUNNING:
-                    self.ctl.output('Process got killed')
-                    self.ctl.output('Exiting foreground')
-                    a.kill()
-                    return
-                continue
-        except (KeyboardInterrupt, EOFError):
-            a.kill()
-            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')
-
-class RereadControllerPlugin(ControllerPluginBase):
-    name = 'reread'
-    listener = None # for unit tests
-
     def _formatChanges(self, (added, changed, dropped)):
         changedict = {}
         for n, t in [(added, 'available'),
@@ -960,9 +824,9 @@
         return template % formatted
 
     def do_avail(self, arg):
-        supervisor_reread = self.ctl.get_server_proxy('supervisor_reread')
+        supervisor = self.ctl.get_supervisor()
         try:
-            configinfo = supervisor_reread.getAllConfigInfo()
+            configinfo = supervisor.getAllConfigInfo()
         except xmlrpclib.Fault, e:
             if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
                 self.ctl.output('ERROR: supervisor shutting down')
@@ -974,9 +838,9 @@
         self.ctl.output("avail\t\t\tDisplay all configured processes")
 
     def do_reread(self, arg):
-        supervisor_reread = self.ctl.get_server_proxy('supervisor_reread')
+        supervisor = self.ctl.get_supervisor()
         try:
-            result = supervisor_reread.reloadConfig()
+            result = supervisor.reloadConfig()
         except xmlrpclib.Fault, e:
             if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
                 self.ctl.output('ERROR: supervisor shutting down')
@@ -993,15 +857,10 @@
     def do_add(self, arg):
         names = arg.strip().split()
 
-        if not names:
-            self.ctl.output("ERROR: add requires a process group name")
-            self.help_add()
-            return
-
-        supervisor_reread = self.ctl.get_server_proxy('supervisor_reread')
+        supervisor = self.ctl.get_supervisor()
         for name in names:
             try:
-                supervisor_reread.addProcessGroup(name)
+                supervisor.addProcessGroup(name)
             except xmlrpclib.Fault, e:
                 if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
                     self.ctl.output('ERROR: shutting down')
@@ -1022,19 +881,12 @@
     def do_remove(self, arg):
         names = arg.strip().split()
 
-        if not names:
-            self.ctl.output("ERROR: remove requires a process group name")
-            self.help_add()
-            return
-
-        supervisor_reread = self.ctl.get_server_proxy('supervisor_reread')
+        supervisor = self.ctl.get_supervisor()
         for name in names:
             try:
-                result = supervisor_reread.removeProcessGroup(name)
+                result = supervisor.removeProcessGroup(name)
             except xmlrpclib.Fault, e:
-                if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
-                    self.ctl.output('ERROR: shutting down')
-                elif e.faultCode == xmlrpc.Faults.STILL_RUNNING:
+                if e.faultCode == xmlrpc.Faults.STILL_RUNNING:
                     self.ctl.output('ERROR: process/group still running: %s'
                                     % name)
                 elif e.faultCode == xmlrpc.Faults.BAD_NAME:
@@ -1054,13 +906,11 @@
             self.ctl.output("%s: %s" % (name, message))
 
         supervisor = self.ctl.get_supervisor()
-        supervisor_reread = self.ctl.get_server_proxy('supervisor_reread')
-
         try:
-            result = supervisor_reread.reloadConfig()
+            result = supervisor.reloadConfig()
         except xmlrpclib.Fault, e:
             if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
-                self.ctl.output('ERROR: shutting down')
+                self.ctl.output('ERROR: already shutting down')
                 return
             else:
                 raise e
@@ -1083,20 +933,148 @@
             results = supervisor.stopProcessGroup(gname)
             log(gname, "stopped")
 
-            supervisor_reread.removeProcessGroup(gname)
-            supervisor_reread.addProcessGroup(gname)
+            supervisor.removeProcessGroup(gname)
+            supervisor.addProcessGroup(gname)
             log(gname, "updated process group")
 
         for gname in added:
-            supervisor_reread.addProcessGroup(gname)
+            supervisor.addProcessGroup(gname)
             log(gname, "added process group")
 
     def help_update(self):
         self.ctl.output("update\t\tReload config and add/remove as necessary")
 
-def make_reread_controllerplugin(controller, **config):
-    return RereadControllerPlugin(controller)
+    def _clearresult(self, result):
+        name = result['name']
+        code = result['status']
+        template = '%s: ERROR (%s)'
+        if code == xmlrpc.Faults.BAD_NAME:
+            return template % (name, 'no such process')
+        elif code == xmlrpc.Faults.FAILED:
+            return template % (name, 'failed')
+        elif code == xmlrpc.Faults.SUCCESS:
+            return '%s: cleared' % name
+        raise ValueError('Unknown result code %s for %s' % (code, name))
+
+    def do_clear(self, arg):
+        if not self.ctl.upcheck():
+            return
+
+        names = arg.strip().split()
+
+        if not names:
+            self.ctl.output('Error: clear requires a process name')
+            self.help_clear()
+            return
+
+        supervisor = self.ctl.get_supervisor()
+
+        if 'all' in names:
+            results = supervisor.clearAllProcessLogs()
+            for result in results:
+                result = self._clearresult(result)
+                self.ctl.output(result)
+
+        else:
+
+            for name in names:
+                try:
+                    result = supervisor.clearProcessLogs(name)
+                except xmlrpclib.Fault, e:
+                    error = self._clearresult({'status':e.faultCode,
+                                               'name':name,
+                                               'description':e.faultString})
+                    self.ctl.output(error)
+                else:
+                    self.ctl.output('%s: cleared' % name)
+
+    def help_clear(self):
+        self.ctl.output("clear <name>\t\tClear a process' log files.")
+        self.ctl.output(
+            "clear <name> <name>\tClear multiple process' log files")
+        self.ctl.output("clear all\t\tClear all process' log files")
+
+    def do_open(self, arg):
+        url = arg.strip()
+        parts = urlparse.urlparse(url)
+        if parts[0] not in ('unix', 'http'):
+            self.ctl.output('ERROR: url must be http:// or unix://')
+            return
+        self.ctl.options.serverurl = url
+        self.do_status('')
+
+    def help_open(self):
+        self.ctl.output("open <url>\tConnect to a remote supervisord process.")
+        self.ctl.output("\t\t(for UNIX domain socket, use unix:///socket/path)")
+
+    def do_version(self, arg):
+        if not self.ctl.upcheck():
+            return
+        supervisor = self.ctl.get_supervisor()
+        self.ctl.output(supervisor.getSupervisorVersion())
+
+    def help_version(self):
+        self.ctl.output(
+            "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.ctl.output('Error: no process name supplied')
+            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 == xmlrpc.Faults.BAD_NAME:
+                self.ctl.output('Error: bad process name supplied')
+                return
+            # for any other fault
+            self.ctl.output(str(msg))
+            return
+        if not info['state'] == states.ProcessStates.RUNNING:
+            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:
+                    if msg.faultCode == xmlrpc.Faults.NOT_RUNNING:
+                        self.ctl.output('Process got killed')
+                        self.ctl.output('Exiting foreground')
+                        a.kill()
+                        return
+                info = supervisor.getProcessInfo(program)
+                if not info['state'] == states.ProcessStates.RUNNING:
+                    self.ctl.output('Process got killed')
+                    self.ctl.output('Exiting foreground')
+                    a.kill()
+                    return
+                continue
+        except (KeyboardInterrupt, EOFError):
+            a.kill()
+            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:

Modified: supervisor/trunk/src/supervisor/tests/base.py
==============================================================================
--- supervisor/trunk/src/supervisor/tests/base.py	(original)
+++ supervisor/trunk/src/supervisor/tests/base.py	Thu Jul 23 17:42:30 2009
@@ -588,20 +588,18 @@
 class DummyRPCServer:
     def __init__(self):
         self.supervisor = DummySupervisorRPCNamespace()
-        self.supervisor_reread = DummyRereadRPCNamespace()
         self.system = DummySystemRPCNamespace()
 
 class DummySystemRPCNamespace:
     pass
 
 class DummySupervisorRPCNamespace:
-    processes = []
-
     _restartable = True
     _restarted = False
     _shutdown = False
     _readlog_error = False
 
+
     from supervisor.process import ProcessStates
     all_process_info = [
         {
@@ -840,43 +838,6 @@
             raise Fault(self._readlog_error, '')
         return 'mainlogdata'
 
-class DummyRereadRPCNamespace:
-    def __init__(self):
-        self.added_process_groups   = []
-        self.removed_process_groups = [] 
-        self.processes = [] # xxx hack
-
-    def addProcessGroup(self, name):
-        from xmlrpclib import Fault
-        from supervisor import xmlrpc
-        if name == 'SHUTDOWN_STATE':
-            raise Fault(xmlrpc.Faults.SHUTDOWN_STATE, '') 
-        elif name == 'ALREADY_ADDED':
-            raise Fault(xmlrpc.Faults.ALREADY_ADDED, '')
-        elif name == 'BAD_NAME':
-            raise Fault(xmlrpc.Faults.BAD_NAME, '')
-        else:
-            self.processes.append(name) # xxx hack
-            self.added_process_groups.append(name)
-            return True
-
-    def removeProcessGroup(self, name):
-        from xmlrpclib import Fault
-        from supervisor import xmlrpc
-        if name == 'SHUTDOWN_STATE':
-            raise Fault(xmlrpc.Faults.SHUTDOWN_STATE, '')  
-        elif name == 'STILL_RUNNING':
-            raise Fault(xmlrpc.Faults.STILL_RUNNING, '')
-        elif name == 'BAD_NAME':
-            raise Fault(xmlrpc.Faults.BAD_NAME, '')
-        else:
-            self.removed_process_groups.append(name)
-            return True
-
-    def reloadConfig(self):
-        return [[['added'], ['changed'], ['removed']]]
-
-
 class DummyPGroupConfig:
     def __init__(self, options, name='whatever', priority=999, pconfigs=None):
         self.options = options

Modified: supervisor/trunk/src/supervisor/tests/test_rpcinterfaces.py
==============================================================================
--- supervisor/trunk/src/supervisor/tests/test_rpcinterfaces.py	(original)
+++ supervisor/trunk/src/supervisor/tests/test_rpcinterfaces.py	Thu Jul 23 17:42:30 2009
@@ -30,24 +30,8 @@
         else:
             raise AssertionError("Didnt raise")
 
-class TopLevelFunctionTests(TestBase):
-    def test_factory_for_main_rpc_interface(self):
-        from supervisor import rpcinterface
-        supervisor = DummySupervisor()
-        interface = rpcinterface.make_main_rpcinterface(supervisor)                                   
-
-        expected = rpcinterface.SupervisorNamespaceRPCInterface
-        self.assertTrue(isinstance(interface, expected))
-        
-    def test_factory_for_reread_rpc_interface(self):
-        from supervisor import rpcinterface
-        supervisor = DummySupervisor()
-        interface = rpcinterface.make_reread_rpcinterface(supervisor)
-        
-        expected = rpcinterface.RereadNamespaceRPCInterface
-        self.assertTrue(isinstance(interface, expected))
-
 class MainXMLRPCInterfaceTests(TestBase):
+
     def _getTargetClass(self):
         from supervisor import xmlrpc
         return xmlrpc.RootRPCInterface
@@ -233,6 +217,99 @@
         value = interface.restart()
         self.assertEqual(value, True)
         self.assertEqual(supervisord.options.mood, 0)
+
+    def test_reloadConfig(self):
+        options = DummyOptions()
+        supervisord = DummySupervisor(options)
+        interface = self._makeOne(supervisord)
+
+        changes = [ [DummyPGroupConfig(options, 'added')],
+                    [DummyPGroupConfig(options, 'changed')],
+                    [DummyPGroupConfig(options, 'dropped')] ]
+
+        supervisord.diff_to_active = lambda : changes
+
+        value = interface.reloadConfig()
+        self.assertEqual(value, [[['added'], ['changed'], ['dropped']]])
+
+    def test_reloadConfig_process_config_file_raises_ValueError(self):
+        from supervisor import xmlrpc
+        options = DummyOptions()
+        def raise_exc(*arg, **kw):
+            raise ValueError('foo')
+        options.process_config_file = raise_exc
+        supervisord = DummySupervisor(options)
+        interface = self._makeOne(supervisord)
+        self._assertRPCError(xmlrpc.Faults.CANT_REREAD, interface.reloadConfig)
+
+    def test_addProcessGroup(self):
+        from supervisor.supervisord import Supervisor
+        from supervisor import xmlrpc
+        options = DummyOptions()
+        supervisord = Supervisor(options)
+        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
+        gconfig = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig])
+        supervisord.options.process_group_configs = [gconfig]
+
+        interface = self._makeOne(supervisord)
+
+        result = interface.addProcessGroup('group1')
+        self.assertTrue(result)
+        self.assertEqual(supervisord.process_groups.keys(), ['group1'])
+
+        self._assertRPCError(xmlrpc.Faults.ALREADY_ADDED,
+                             interface.addProcessGroup, 'group1')
+        self.assertEqual(supervisord.process_groups.keys(), ['group1'])
+
+        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
+                             interface.addProcessGroup, 'asdf')
+        self.assertEqual(supervisord.process_groups.keys(), ['group1'])
+
+    def test_removeProcessGroup(self):
+        from supervisor.supervisord import Supervisor
+        options = DummyOptions()
+        supervisord = Supervisor(options)
+        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
+        gconfig = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig])
+        supervisord.options.process_group_configs = [gconfig]
+
+        interface = self._makeOne(supervisord)
+
+        interface.addProcessGroup('group1')
+        result = interface.removeProcessGroup('group1')
+        self.assertTrue(result)
+        self.assertEqual(supervisord.process_groups.keys(), [])
+
+    def test_removeProcessGroup_bad_name(self):
+        from supervisor.supervisord import Supervisor
+        from supervisor import xmlrpc
+        options = DummyOptions()
+        supervisord = Supervisor(options)
+        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
+        gconfig = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig])
+        supervisord.options.process_group_configs = [gconfig]
+
+        interface = self._makeOne(supervisord)
+
+        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
+                             interface.removeProcessGroup, 'asdf')
+
+    def test_removeProcessGroup_still_running(self):
+        from supervisor.supervisord import Supervisor
+        from supervisor import xmlrpc
+        options = DummyOptions()
+        supervisord = Supervisor(options)
+        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
+        gconfig = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig])
+        supervisord.options.process_group_configs = [gconfig]
+        process = DummyProcessGroup(gconfig)
+        process.unstopped_processes = [123]
+        supervisord.process_groups = {'group1':process}
+        interface = self._makeOne(supervisord)
+        self._assertRPCError(xmlrpc.Faults.STILL_RUNNING,
+                             interface.removeProcessGroup, 'group1')
+
+
     def test_startProcess_already_started(self):
         from supervisor import xmlrpc
         options = DummyOptions()
@@ -768,6 +845,31 @@
         self.assertEqual(result[1]['status'],  Faults.SUCCESS)
         self.assertEqual(result[1]['description'], 'OK')
 
+    def test_getAllConfigInfo(self):
+        options = DummyOptions()
+        supervisord = DummySupervisor(options, 'foo')
+
+        pconfig1 = DummyPConfig(options, 'process1', __file__)
+        pconfig2 = DummyPConfig(options, 'process2', __file__)
+        gconfig = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig1, pconfig2])
+        supervisord.process_groups = {'group1': DummyProcessGroup(gconfig)}
+        supervisord.options.process_group_configs = [gconfig]
+
+        interface = self._makeOne(supervisord)
+        configs = interface.getAllConfigInfo()
+        self.assertEqual(configs, [{ 'group': 'group1',
+                                     'name': 'process1',
+                                     'inuse': True,
+                                     'autostart': True,
+                                     'process_prio': 999,
+                                     'group_prio': 999 },
+                                   { 'group': 'group1',
+                                     'name': 'process2',
+                                     'inuse': True,
+                                     'autostart': True,
+                                     'process_prio': 999,
+                                     'group_prio': 999 }])
+
     def test__interpretProcessInfo(self):
         supervisord = DummySupervisor()
         interface = self._makeOne(supervisord)
@@ -1487,10 +1589,13 @@
         L = []
         def callback(event):
             L.append(event)
-
-        events.subscribe(events.RemoteCommunicationEvent, callback)         
-        result = interface.sendRemoteCommEvent('foo', 'bar')
-        events.clear()
+        
+        try:
+            events.callbacks[:] = [(events.RemoteCommunicationEvent, callback)]
+            result = interface.sendRemoteCommEvent('foo', 'bar')
+        finally:
+            events.callbacks[:] = []
+            events.clear()
 
         self.assertTrue(result)
         self.assertEqual(len(L), 1)
@@ -1507,142 +1612,20 @@
         L = []
         def callback(event):
             L.append(event)
-
-        events.subscribe(events.RemoteCommunicationEvent, callback)
-        result = interface.sendRemoteCommEvent(u'fi\xed 1', u'fi\xed 2')
-        events.clear()
+        
+        try:
+            events.callbacks[:] = [(events.RemoteCommunicationEvent, callback)]
+            result = interface.sendRemoteCommEvent(u'fi\xed once', u'fi\xed twice')
+        finally:
+            events.callbacks[:] = []
+            events.clear()
 
         self.assertTrue(result)
         self.assertEqual(len(L), 1)
         event = L[0]                                     
-        self.assertEqual(event.type, 'fi\xc3\xad 1')
-        self.assertEqual(event.data, 'fi\xc3\xad 2')
-
+        self.assertEqual(event.type, 'fi\xc3\xad once')
+        self.assertEqual(event.data, 'fi\xc3\xad twice')
         
-class RereadNamespaceXMLRPCInterfaceTests(TestBase):  
-    def _getTargetClass(self):
-        from supervisor import rpcinterface
-        return rpcinterface.RereadNamespaceRPCInterface
-
-    def _makeOne(self, *args, **kw):
-        return self._getTargetClass()(*args, **kw)
-
-    def test_getAllConfigInfo(self):
-        options = DummyOptions()
-        supervisord = DummySupervisor(options, 'foo')
-
-        pconfig1 = DummyPConfig(options, 'process1', __file__)
-        pconfig2 = DummyPConfig(options, 'process2', __file__)
-        gconfig = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig1, pconfig2])
-        supervisord.process_groups = {'group1': DummyProcessGroup(gconfig)}
-        supervisord.options.process_group_configs = [gconfig]
-
-        interface = self._makeOne(supervisord)
-        configs = interface.getAllConfigInfo()
-        self.assertEqual(configs, [{ 'group': 'group1',
-                                     'name': 'process1',
-                                     'inuse': True,
-                                     'autostart': True,
-                                     'process_prio': 999,
-                                     'group_prio': 999 },
-                                   { 'group': 'group1',
-                                     'name': 'process2',
-                                     'inuse': True,
-                                     'autostart': True,
-                                     'process_prio': 999,
-                                     'group_prio': 999 }])
-
-    def test_reloadConfig(self):
-        options = DummyOptions()
-        supervisord = DummySupervisor(options)
-        interface = self._makeOne(supervisord)
-
-        changes = [ [DummyPGroupConfig(options, 'added')],
-                    [DummyPGroupConfig(options, 'changed')],
-                    [DummyPGroupConfig(options, 'dropped')] ]
-
-        supervisord.diff_to_active = lambda : changes
-
-        value = interface.reloadConfig()
-        self.assertEqual(value, [[['added'], ['changed'], ['dropped']]])
-
-    def test_reloadConfig_process_config_file_raises_ValueError(self):
-        from supervisor import xmlrpc
-        options = DummyOptions()
-        def raise_exc(*arg, **kw):
-            raise ValueError('foo')
-        options.process_config_file = raise_exc
-        supervisord = DummySupervisor(options)
-        interface = self._makeOne(supervisord)
-        self._assertRPCError(xmlrpc.Faults.CANT_REREAD, interface.reloadConfig)
-
-    def test_addProcessGroup(self):
-        from supervisor.supervisord import Supervisor
-        from supervisor import xmlrpc
-        options = DummyOptions()
-        supervisord = Supervisor(options)
-        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
-        gconfig = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig])
-        supervisord.options.process_group_configs = [gconfig]
-
-        interface = self._makeOne(supervisord)
-
-        result = interface.addProcessGroup('group1')
-        self.assertTrue(result)
-        self.assertEqual(supervisord.process_groups.keys(), ['group1'])
-
-        self._assertRPCError(xmlrpc.Faults.ALREADY_ADDED,
-                             interface.addProcessGroup, 'group1')
-        self.assertEqual(supervisord.process_groups.keys(), ['group1'])
-
-        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
-                             interface.addProcessGroup, 'asdf')
-        self.assertEqual(supervisord.process_groups.keys(), ['group1'])
-
-    def test_removeProcessGroup(self):
-        from supervisor.supervisord import Supervisor
-        options = DummyOptions()
-        supervisord = Supervisor(options)
-        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
-        gconfig = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig])
-        supervisord.options.process_group_configs = [gconfig]
-
-        interface = self._makeOne(supervisord)
-
-        interface.addProcessGroup('group1')
-        result = interface.removeProcessGroup('group1')
-        self.assertTrue(result)
-        self.assertEqual(supervisord.process_groups.keys(), [])
-
-    def test_removeProcessGroup_bad_name(self):
-        from supervisor.supervisord import Supervisor
-        from supervisor import xmlrpc
-        options = DummyOptions()
-        supervisord = Supervisor(options)
-        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
-        gconfig = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig])
-        supervisord.options.process_group_configs = [gconfig]
-
-        interface = self._makeOne(supervisord)
-
-        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
-                             interface.removeProcessGroup, 'asdf')
-
-    def test_removeProcessGroup_still_running(self):
-        from supervisor.supervisord import Supervisor
-        from supervisor import xmlrpc
-        options = DummyOptions()
-        supervisord = Supervisor(options)
-        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
-        gconfig = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig])
-        supervisord.options.process_group_configs = [gconfig]
-        process = DummyProcessGroup(gconfig)
-        process.unstopped_processes = [123]
-        supervisord.process_groups = {'group1':process}
-        interface = self._makeOne(supervisord)
-        self._assertRPCError(xmlrpc.Faults.STILL_RUNNING,
-                             interface.removeProcessGroup, 'group1')
-    
 
 class SystemNamespaceXMLRPCInterfaceTests(TestBase):
     def _getTargetClass(self):

Modified: supervisor/trunk/src/supervisor/tests/test_supervisorctl.py
==============================================================================
--- supervisor/trunk/src/supervisor/tests/test_supervisorctl.py	(original)
+++ supervisor/trunk/src/supervisor/tests/test_supervisorctl.py	Thu Jul 23 17:42:30 2009
@@ -511,310 +511,137 @@
         self.assertEqual(result, None)
         self.assertEqual(options._server.supervisor._shutdown, True)
 
-    def test_pid(self):
-        plugin = self._makeOne()
-        result = plugin.do_pid('')
-        options = plugin.ctl.options
-        self.assertEqual(result, None)
-        lines = plugin.ctl.stdout.getvalue().split('\n')
-        self.assertEqual(len(lines), 2)
-        self.assertEqual(lines[0], str(options._server.supervisor.getPID()))
-
-    def test_maintail_toomanyargs(self):
-        plugin = self._makeOne()
-        result = plugin.do_maintail('foo bar')
-        val = plugin.ctl.stdout.getvalue()
-        self.failUnless(val.startswith('Error: too many'), val)
-
-    def test_maintail_minus_string_fails(self):
-        plugin = self._makeOne()
-        result = plugin.do_maintail('-wrong')
-        val = plugin.ctl.stdout.getvalue()
-        self.failUnless(val.startswith('Error: bad argument -wrong'), val)
-
-    def test_maintail_wrong(self):
-        plugin = self._makeOne()
-        result = plugin.do_maintail('wrong')
-        val = plugin.ctl.stdout.getvalue()
-        self.failUnless(val.startswith('Error: bad argument wrong'), val)
-
-    def test_maintail_dashf(self):
-        plugin = self._makeOne()
-        plugin.listener = DummyListener()
-        result = plugin.do_maintail('-f')
-        errors = plugin.listener.errors
-        self.assertEqual(len(errors), 1)
-        error = errors[0]
-        self.assertEqual(plugin.listener.closed,
-                         'http://localhost:92491/mainlogtail')
-        self.assertEqual(error[0],
-                         'http://localhost:92491/mainlogtail')
-        for msg in ('Cannot connect', 'socket.error', '32', 'Broken pipe'):
-            self.assertTrue(msg in error[1])
-
-    def test_maintail_nobytes(self):
-        plugin = self._makeOne()
-        result = plugin.do_maintail('')
-        self.assertEqual(plugin.ctl.stdout.getvalue(), 'mainlogdata\n')
-
-    def test_maintail_dashbytes(self):
+    def test__formatChanges(self):
         plugin = self._makeOne()
-        result = plugin.do_maintail('-100')
-        self.assertEqual(plugin.ctl.stdout.getvalue(), 'mainlogdata\n')
+        # Don't explode, plz
+        plugin._formatChanges([['added'], ['changed'], ['removed']])
+        plugin._formatChanges([[], [], []])
 
-    def test_maintail_readlog_error_nofile(self):
+    def test_reread(self):
         plugin = self._makeOne()
-        supervisor_rpc = plugin.ctl.get_supervisor()
-        from supervisor import xmlrpc
-        supervisor_rpc._readlog_error = xmlrpc.Faults.NO_FILE
-        result = plugin.do_maintail('-100')
-        self.assertEqual(plugin.ctl.stdout.getvalue(),
-                         'supervisord: ERROR (no log file)\n')
+        calls = []
+        plugin._formatChanges = lambda x: calls.append(x)
+        result = plugin.do_reread(None)
+        self.assertEqual(result, None)
+        self.assertEqual(calls[0], [['added'], ['changed'], ['removed']])
 
-    def test_maintail_readlog_error_failed(self):
+    def test_reread_Fault(self):
         plugin = self._makeOne()
-        supervisor_rpc = plugin.ctl.get_supervisor()
         from supervisor import xmlrpc
-        supervisor_rpc._readlog_error = xmlrpc.Faults.FAILED
-        result = plugin.do_maintail('-100')
+        import xmlrpclib
+        def raise_fault(*arg, **kw):
+            raise xmlrpclib.Fault(xmlrpc.Faults.CANT_REREAD, 'cant')
+        plugin.ctl.options._server.supervisor.reloadConfig = raise_fault
+        plugin.do_reread(None)
         self.assertEqual(plugin.ctl.stdout.getvalue(),
-                         'supervisord: ERROR (unknown error reading log)\n')
-
-    def test_fg_too_few_args(self):
-        plugin = self._makeOne()
-        result = plugin.do_fg('')
-        lines = plugin.ctl.stdout.getvalue().split('\n')
-        self.assertEqual(result, None)
-        self.assertEqual(lines[0], 'Error: no process name supplied')
+                         'ERROR: cant\n')
 
-    def test_fg_too_many_args(self):
+    def test__formatConfigInfo(self):
+        info = { 'group': 'group1',
+                 'name': 'process1',
+                 'inuse': True,
+                 'autostart': True,
+                 'process_prio': 999,
+                 'group_prio': 999 }
         plugin = self._makeOne()
-        result = plugin.do_fg('foo bar')
-        line = plugin.ctl.stdout.getvalue()
-        self.assertEqual(result, None)
-        self.assertEqual(line, 'Error: too many process names supplied\n')
+        result = plugin._formatConfigInfo(info)
+        self.assertTrue('in use' in result)
+        info = { 'group': 'group1',
+                 'name': 'process1',
+                 'inuse': False,
+                 'autostart': False,
+                 'process_prio': 999,
+                 'group_prio': 999 }
+        result = plugin._formatConfigInfo(info)
+        self.assertTrue('avail' in result)
 
-    def test_fg_badprocname(self):
+    def test_avail(self):
+        calls = []
         plugin = self._makeOne()
-        result = plugin.do_fg('BAD_NAME')
-        line = plugin.ctl.stdout.getvalue()
-        self.assertEqual(result, None)
-        self.assertEqual(line, 'Error: bad process name supplied\n')
 
-    def test_fg_procnotrunning(self):
-        plugin = self._makeOne()
-        result = plugin.do_fg('bar')
-        line = plugin.ctl.stdout.getvalue()
-        self.assertEqual(result, None)
-        self.assertEqual(line, 'Error: process not running\n')
-        result = plugin.do_fg('baz_01')
-        lines = plugin.ctl.stdout.getvalue().split('\n')
-        self.assertEqual(result, None)
-        self.assertEqual(lines[-2], 'Error: process not running')
-
-class TestRereadControllerPlugin(unittest.TestCase):
-    def _getTargetClass(self):
-        from supervisor.supervisorctl import RereadControllerPlugin
-        return RereadControllerPlugin
-
-    def _makeOne(self, *arg, **kw):
-        klass = self._getTargetClass()
-        options = DummyClientOptions()
-        ctl = DummyController(options)
-        plugin = klass(ctl, *arg, **kw)
-        return plugin
-
-    def test_avail_handles_shutdown_state_fault(self):
-        plugin = self._makeOne()
-        
-        class DummyRereadInterface:
+        class FakeSupervisor(object):
             def getAllConfigInfo(self):
-                import xmlrpclib
-                from supervisor.xmlrpc import Faults
-                raise xmlrpclib.Fault(Faults.SHUTDOWN_STATE, 'bye')
-
-        server = plugin.ctl.options._server
-        server.supervisor_reread = DummyRereadInterface()
-
-        result = plugin.do_avail('')               
-        self.assertEqual(result, None)
-
-        self.assertEqual(plugin.ctl.stdout.getvalue(),
-                         'ERROR: supervisor shutting down\n')
-
-    def test_avail_displays_formatted_config_info(self):
-        plugin = self._makeOne()
-        
-        class DummyRereadInterface:  
-            def __init__(self):
-                first  = {'group': 'group1', 'name': 'process1',
-                          'inuse': False, 'autostart': False,
-                          'process_prio': 999, 'group_prio': 999}
-                second = {'group': 'group2', 'name': 'process2',
+                return [{ 'group': 'group1', 'name': 'process1',
                           'inuse': False, 'autostart': False,
-                          'process_prio': 999, 'group_prio': 999}
-                self.info = [first, second]
-            
-            def getAllConfigInfo(self):
-                return self.info
-
-        server = plugin.ctl.options._server
-        interface = DummyRereadInterface()
-        server.supervisor_reread = interface 
+                          'process_prio': 999, 'group_prio': 999 }]
 
+        plugin.ctl.get_supervisor = lambda : FakeSupervisor()
+        plugin.ctl.output = calls.append
         result = plugin.do_avail('')
         self.assertEqual(result, None)
 
-        expected = plugin._formatConfigInfo(interface.info[0])  + "\n" + \
-                   plugin._formatConfigInfo(interface.info[1]) + "\n"
-        self.assertEqual(plugin.ctl.stdout.getvalue(), expected)
-
-    def test_add_displays_error_and_help_when_given_no_args(self):
-        plugin = self._makeOne()
-        
-        result = plugin.do_add('  ') # extra whitespace intentional
-        self.assertEqual(result, None)
-
-        output = plugin.ctl.stdout.getvalue()
-        self.assertTrue('ERROR: add requires a process group name' in output)
-        self.assertTrue('add <name>' in output)
-
-    def test_add_will_add_a_single_process_group(self):
-        plugin = self._makeOne()
-        
-        result = plugin.do_add('  foo') # extra whitespace intentional
-        self.assertEqual(result, None)
-
-        expected = 'foo: added process group\n' 
-        self.assertEqual(plugin.ctl.stdout.getvalue(), expected)
-
-        interface = plugin.ctl.options._server.supervisor_reread
-        self.assertEqual(interface.added_process_groups, ['foo'])                        
-        
-    def test_add_will_adds_multiple_process_groups(self):
-        plugin = self._makeOne()
-        
-        result = plugin.do_add(' foo  bar ')  # extra whitespace intentional
-        self.assertEqual(result, None)
-
-        expected = "foo: added process group\n" \
-                   "bar: added process group\n"
-        self.assertEqual(plugin.ctl.stdout.getvalue(), expected)
-
-        interface = plugin.ctl.options._server.supervisor_reread
-        self.assertEqual(interface.added_process_groups, ['foo', 'bar'])                        
-
-    def test_add_handles_shutdown_state_fault(self):
+    def test_add(self):
         plugin = self._makeOne()
-        
-        result = plugin.do_add('SHUTDOWN_STATE')
+        result = plugin.do_add('foo')
         self.assertEqual(result, None)
+        supervisor = plugin.ctl.options._server.supervisor
+        self.assertEqual(supervisor.processes, ['foo'])
 
-        expected = "ERROR: shutting down\n"
-        self.assertEqual(plugin.ctl.stdout.getvalue(), expected)
-                                                                                   
-    def test_add_handles_already_added_fault(self):
+    def test_add_already_added(self):
         plugin = self._makeOne()
-        
         result = plugin.do_add('ALREADY_ADDED')
         self.assertEqual(result, None)
+        supervisor = plugin.ctl.options._server.supervisor
+        self.assertEqual(plugin.ctl.stdout.getvalue(),
+                         'ERROR: process group already active\n')
 
-        expected = "ERROR: process group already active\n"
-        self.assertEqual(plugin.ctl.stdout.getvalue(), expected)
-
-    def test_add_handles_bad_name_fault(self):
+    def test_add_bad_name(self):
         plugin = self._makeOne()
-        
         result = plugin.do_add('BAD_NAME')
         self.assertEqual(result, None)
+        supervisor = plugin.ctl.options._server.supervisor
+        self.assertEqual(plugin.ctl.stdout.getvalue(),
+                         'ERROR: no such process/group: BAD_NAME\n')
 
-        expected = "ERROR: no such process/group: BAD_NAME\n"
-        self.assertEqual(plugin.ctl.stdout.getvalue(), expected)
-
-    def test_remove_will_remove_a_single_process_group(self):
-        plugin = self._makeOne()
-
-        result = plugin.do_remove('  foo')  # extra whitespace intentional
-        self.assertEqual(result, None)
-
-        expected = "foo: removed process group\n"
-        self.assertEqual(plugin.ctl.stdout.getvalue(), expected)  
-
-        interface = plugin.ctl.options._server.supervisor_reread
-        self.assertEqual(interface.removed_process_groups, ['foo'])                        
-
-    def test_remove_will_remove_multiple_process_groups(self):
+    def test_remove(self):
         plugin = self._makeOne()
-
-        result = plugin.do_remove(' foo  bar ') # extra whitespace intentional
+        supervisor = plugin.ctl.options._server.supervisor
+        supervisor.processes = ['foo']
+        result = plugin.do_remove('foo')
         self.assertEqual(result, None)
+        self.assertEqual(supervisor.processes, [])
 
-        expected = "foo: removed process group\n" \
-                   "bar: removed process group\n"
-        self.assertEqual(plugin.ctl.stdout.getvalue(), expected)  
-
-        interface = plugin.ctl.options._server.supervisor_reread
-        self.assertEqual(interface.removed_process_groups, ['foo', 'bar'])                        
-
-    def test_remove_handles_shutdown_state_fault(self):
+    def test_remove_bad_name(self):
         plugin = self._makeOne()
-
-        result = plugin.do_remove('SHUTDOWN_STATE')
+        supervisor = plugin.ctl.options._server.supervisor
+        supervisor.processes = ['foo']
+        result = plugin.do_remove('BAD_NAME')
         self.assertEqual(result, None)
-
         self.assertEqual(plugin.ctl.stdout.getvalue(),
-                         'ERROR: shutting down\n')
+                         'ERROR: no such process/group: BAD_NAME\n')
 
-    def test_remove_handles_still_running_fault(self):
+    def test_remove_still_running(self):
         plugin = self._makeOne()
-
+        supervisor = plugin.ctl.options._server.supervisor
+        supervisor.processes = ['foo']
         result = plugin.do_remove('STILL_RUNNING')
         self.assertEqual(result, None)
-
         self.assertEqual(plugin.ctl.stdout.getvalue(),
                          'ERROR: process/group still running: STILL_RUNNING\n')
 
-    def test_remove_handles_bad_name_fault(self):
-        plugin = self._makeOne()
-
-        result = plugin.do_remove('BAD_NAME')
-        self.assertEqual(result, None)
-
-        self.assertEqual(plugin.ctl.stdout.getvalue(),
-                         'ERROR: no such process/group: BAD_NAME\n')
-
-    def test_update_handles_shutdown_state_fault(self):
+    def test_update_not_on_shutdown(self):
         plugin = self._makeOne()
-
-        class DummyRereadInterface: 
-            def reloadConfig(self):
-                from supervisor import xmlrpc
-                import xmlrpclib
-                raise xmlrpclib.Fault(xmlrpc.Faults.SHUTDOWN_STATE, 'bye')
-
-        server = plugin.ctl.options._server
-        interface = DummyRereadInterface()
-        server.supervisor_reread = interface 
-
-        result = plugin.do_update('')
-        self.assertEqual(result, None)
-                                              
-        self.assertEquals(plugin.ctl.stdout.getvalue(),
-                         'ERROR: shutting down\n') 
+        supervisor = plugin.ctl.options._server.supervisor
+        def reloadConfig():
+            from supervisor import xmlrpc
+            import xmlrpclib
+            raise xmlrpclib.Fault(xmlrpc.Faults.SHUTDOWN_STATE, 'blah')
+        supervisor.reloadConfig = reloadConfig
+        supervisor.processes = ['removed']
+        plugin.do_update('')
+        self.assertEqual(supervisor.processes, ['removed'])
 
     def test_update_added_procs(self):
         plugin = self._makeOne()
-        supervisor_reread = plugin.ctl.options._server.supervisor_reread
         supervisor = plugin.ctl.options._server.supervisor
-        supervisor_reread.processes = supervisor.processes
 
         calls = []
         def reloadConfig():
             return [[['new_proc'], [], []]]
-        supervisor_reread.reloadConfig = reloadConfig
+        supervisor.reloadConfig = reloadConfig
 
         plugin.do_update('')
-        self.assertEqual(supervisor_reread.processes, ['new_proc'])
+        self.assertEqual(supervisor.processes, ['new_proc'])
 
     def test_update_changed_procs(self):
         from supervisor import xmlrpc
@@ -822,16 +649,14 @@
 
         plugin = self._makeOne()
         supervisor = plugin.ctl.options._server.supervisor
-        supervisor_reread = plugin.ctl.options._server.supervisor_reread
-        supervisor.processes = supervisor_reread.processes = []
 
         calls = []
         def reloadConfig():
             return [[[], ['changed_group'], []]]
-        supervisor_reread.reloadConfig = reloadConfig
+        supervisor.reloadConfig = reloadConfig
         supervisor.startProcess = lambda x: calls.append(('start', x))
 
-        supervisor_reread.addProcessGroup('changed_group') # fake existence
+        supervisor.addProcessGroup('changed_group') # fake existence
         results = [{'name':        'changed_process',
                     'group':       'changed_group',
                     'status':      xmlrpc.Faults.SUCCESS,
@@ -844,7 +669,7 @@
         plugin.do_update('')
         self.assertEqual(calls, [('stop', 'changed_group')])
 
-        supervisor_reread.addProcessGroup('changed_group') # fake existence
+        supervisor.addProcessGroup('changed_group') # fake existence
         calls[:] = []
         results[:] = [{'name':        'changed_process1',
                        'group':       'changed_group',
@@ -858,7 +683,7 @@
         plugin.do_update('')
         self.assertEqual(calls, [('stop', 'changed_group')])
 
-        supervisor_reread.addProcessGroup('changed_group') # fake existence
+        supervisor.addProcessGroup('changed_group') # fake existence
         calls[:] = []
         results[:] = [{'name':        'changed_process1',
                        'group':       'changed_group',
@@ -877,17 +702,16 @@
 
         plugin = self._makeOne()
         supervisor = plugin.ctl.options._server.supervisor
-        supervisor_reread = plugin.ctl.options._server.supervisor_reread
 
         def reloadConfig():
             return [[[], [], ['removed_group']]]
-        supervisor_reread.reloadConfig = reloadConfig
+        supervisor.reloadConfig = reloadConfig
 
         results = [{'name':        'removed_process',
                     'group':       'removed_group',
                     'status':      xmlrpc.Faults.SUCCESS,
                     'description': 'blah'}]
-        supervisor.processes = supervisor_reread.processes = ['removed_group']
+        supervisor.processes = ['removed_group']
 
         def stopProcessGroup(name):
             return results
@@ -900,7 +724,7 @@
                        'group':       'removed_group',
                        'status':      xmlrpc.Faults.NOT_RUNNING,
                        'description': 'blah'}]
-        supervisor.processes = supervisor_reread.processes = ['removed_group']
+        supervisor.processes = ['removed_group']
 
         plugin.do_update('')
         self.assertEqual(supervisor.processes, [])
@@ -909,55 +733,111 @@
                        'group':       'removed_group',
                        'status':      xmlrpc.Faults.FAILED,
                        'description': 'blah'}]
-        supervisor.processes = supervisor_reread.processes = ['removed_group']
+        supervisor.processes = ['removed_group']
 
         plugin.do_update('')
         self.assertEqual(supervisor.processes, ['removed_group'])
 
-    def test__formatChanges(self):
+    def test_pid(self):
         plugin = self._makeOne()
-        # Don't explode, plz
-        plugin._formatChanges([['added'], ['changed'], ['removed']])
-        plugin._formatChanges([[], [], []])
+        result = plugin.do_pid('')
+        options = plugin.ctl.options
+        self.assertEqual(result, None)
+        lines = plugin.ctl.stdout.getvalue().split('\n')
+        self.assertEqual(len(lines), 2)
+        self.assertEqual(lines[0], str(options._server.supervisor.getPID()))
 
-    def test_reread(self):
+    def test_maintail_toomanyargs(self):
         plugin = self._makeOne()
-        calls = []
-        plugin._formatChanges = lambda x: calls.append(x)
-        result = plugin.do_reread(None)
-        self.assertEqual(result, None)
-        self.assertEqual(calls[0], [['added'], ['changed'], ['removed']])
+        result = plugin.do_maintail('foo bar')
+        val = plugin.ctl.stdout.getvalue()
+        self.failUnless(val.startswith('Error: too many'), val)
 
-    def test_reread_Fault(self):
+    def test_maintail_minus_string_fails(self):
         plugin = self._makeOne()
+        result = plugin.do_maintail('-wrong')
+        val = plugin.ctl.stdout.getvalue()
+        self.failUnless(val.startswith('Error: bad argument -wrong'), val)
+
+    def test_maintail_wrong(self):
+        plugin = self._makeOne()
+        result = plugin.do_maintail('wrong')
+        val = plugin.ctl.stdout.getvalue()
+        self.failUnless(val.startswith('Error: bad argument wrong'), val)
+
+    def test_maintail_dashf(self):
+        plugin = self._makeOne()
+        plugin.listener = DummyListener()
+        result = plugin.do_maintail('-f')
+        errors = plugin.listener.errors
+        self.assertEqual(len(errors), 1)
+        error = errors[0]
+        self.assertEqual(plugin.listener.closed,
+                         'http://localhost:92491/mainlogtail')
+        self.assertEqual(error[0],
+                         'http://localhost:92491/mainlogtail')
+        for msg in ('Cannot connect', 'socket.error', '32', 'Broken pipe'):
+            self.assertTrue(msg in error[1])
+
+    def test_maintail_nobytes(self):
+        plugin = self._makeOne()
+        result = plugin.do_maintail('')
+        self.assertEqual(plugin.ctl.stdout.getvalue(), 'mainlogdata\n')
+
+    def test_maintail_dashbytes(self):
+        plugin = self._makeOne()
+        result = plugin.do_maintail('-100')
+        self.assertEqual(plugin.ctl.stdout.getvalue(), 'mainlogdata\n')
+
+    def test_maintail_readlog_error_nofile(self):
+        plugin = self._makeOne()
+        supervisor_rpc = plugin.ctl.get_supervisor()
         from supervisor import xmlrpc
-        import xmlrpclib
-        def raise_fault(*arg, **kw):
-            raise xmlrpclib.Fault(xmlrpc.Faults.CANT_REREAD, 'cant')
-        plugin.ctl.options._server.supervisor_reread.reloadConfig = raise_fault
-        plugin.do_reread(None)
+        supervisor_rpc._readlog_error = xmlrpc.Faults.NO_FILE
+        result = plugin.do_maintail('-100')
         self.assertEqual(plugin.ctl.stdout.getvalue(),
-                         'ERROR: cant\n')
+                         'supervisord: ERROR (no log file)\n')
 
-    def test__formatConfigInfo(self):
-        info = { 'group': 'group1',
-                 'name': 'process1',
-                 'inuse': True,
-                 'autostart': True,
-                 'process_prio': 999,
-                 'group_prio': 999 }
+    def test_maintail_readlog_error_failed(self):
         plugin = self._makeOne()
-        result = plugin._formatConfigInfo(info)
-        self.assertTrue('in use' in result)
-        info = { 'group': 'group1',
-                 'name': 'process1',
-                 'inuse': False,
-                 'autostart': False,
-                 'process_prio': 999,
-                 'group_prio': 999 }
-        result = plugin._formatConfigInfo(info)
-        self.assertTrue('avail' in result)
+        supervisor_rpc = plugin.ctl.get_supervisor()
+        from supervisor import xmlrpc
+        supervisor_rpc._readlog_error = xmlrpc.Faults.FAILED
+        result = plugin.do_maintail('-100')
+        self.assertEqual(plugin.ctl.stdout.getvalue(),
+                         'supervisord: ERROR (unknown error reading log)\n')
+
+    def test_fg_too_few_args(self):
+        plugin = self._makeOne()
+        result = plugin.do_fg('')
+        lines = plugin.ctl.stdout.getvalue().split('\n')
+        self.assertEqual(result, None)
+        self.assertEqual(lines[0], 'Error: no process name supplied')
+
+    def test_fg_too_many_args(self):
+        plugin = self._makeOne()
+        result = plugin.do_fg('foo bar')
+        line = plugin.ctl.stdout.getvalue()
+        self.assertEqual(result, None)
+        self.assertEqual(line, 'Error: too many process names supplied\n')
+
+    def test_fg_badprocname(self):
+        plugin = self._makeOne()
+        result = plugin.do_fg('BAD_NAME')
+        line = plugin.ctl.stdout.getvalue()
+        self.assertEqual(result, None)
+        self.assertEqual(line, 'Error: bad process name supplied\n')
 
+    def test_fg_procnotrunning(self):
+        plugin = self._makeOne()
+        result = plugin.do_fg('bar')
+        line = plugin.ctl.stdout.getvalue()
+        self.assertEqual(result, None)
+        self.assertEqual(line, 'Error: process not running\n')
+        result = plugin.do_fg('baz_01')
+        lines = plugin.ctl.stdout.getvalue().split('\n')
+        self.assertEqual(result, None)
+        self.assertEqual(lines[-2], 'Error: process not running')
 
 class DummyListener:
     def __init__(self):


More information about the Supervisor-checkins mailing list