[Supervisor-checkins] r828 - in supervisor/trunk: . src/supervisor src/supervisor/tests
Chris McDonough
chrism at agendaless.com
Sun Nov 23 14:36:01 EST 2008
Author: Chris McDonough <chrism at agendaless.com>
Date: Sun Nov 23 14:36:00 2008
New Revision: 828
Log:
Merge chrism-30-merge branch to trunk.
Modified:
supervisor/trunk/CHANGES.txt
supervisor/trunk/src/supervisor/options.py
supervisor/trunk/src/supervisor/rpcinterface.py
supervisor/trunk/src/supervisor/supervisorctl.py
supervisor/trunk/src/supervisor/supervisord.py
supervisor/trunk/src/supervisor/tests/base.py
supervisor/trunk/src/supervisor/tests/test_options.py
supervisor/trunk/src/supervisor/tests/test_rpcinterfaces.py
supervisor/trunk/src/supervisor/tests/test_supervisorctl.py
supervisor/trunk/src/supervisor/tests/test_supervisord.py
supervisor/trunk/src/supervisor/xmlrpc.py
Modified: supervisor/trunk/CHANGES.txt
==============================================================================
--- supervisor/trunk/CHANGES.txt (original)
+++ supervisor/trunk/CHANGES.txt Sun Nov 23 14:36:00 2008
@@ -1,5 +1,69 @@
Next Release
+ - Add ``reread``, ``update``, and ``avail`` commands based on Anders
+ Quist's ``online_config_reload.diff`` patch. This patch extends
+ the "add" and "drop" commands with automagical behavior::
+
+ In supervisorctl:
+
+ supervisor> status
+ bar RUNNING pid 14864, uptime 18:03:42
+ baz RUNNING pid 23260, uptime 0:10:16
+ foo RUNNING pid 14866, uptime 18:03:42
+ gazonk RUNNING pid 23261, uptime 0:10:16
+ supervisor> avail
+ bar in use auto 999:999
+ baz in use auto 999:999
+ foo in use auto 999:999
+ gazonk in use auto 999:999
+ quux avail auto 999:999
+
+ Now we add this to our conf:
+
+ [group:zegroup]
+ programs=baz,gazonk
+
+ Then we reread conf:
+
+ supervisor> reread
+ baz: disappeared
+ gazonk: disappeared
+ quux: available
+ zegroup: available
+ supervisor> avail
+ bar in use auto 999:999
+ foo in use auto 999:999
+ quux avail auto 999:999
+ zegroup:baz avail auto 999:999
+ zegroup:gazonk avail auto 999:999
+ supervisor> status
+ bar RUNNING pid 14864, uptime 18:04:18
+ baz RUNNING pid 23260, uptime 0:10:52
+ foo RUNNING pid 14866, uptime 18:04:18
+ gazonk RUNNING pid 23261, uptime 0:10:52
+
+ The magic make-it-so command:
+
+ supervisor> update
+ baz: stopped
+ baz: removed process group
+ gazonk: stopped
+ gazonk: removed process group
+ zegroup: added process group
+ quux: added process group
+ supervisor> status
+ bar RUNNING pid 14864, uptime 18:04:43
+ foo RUNNING pid 14866, uptime 18:04:43
+ quux RUNNING pid 23561, uptime 0:00:02
+ zegroup:baz RUNNING pid 23559, uptime 0:00:02
+ zegroup:gazonk RUNNING pid 23560, uptime 0:00:02
+ supervisor> avail
+ bar in use auto 999:999
+ foo in use auto 999:999
+ quux in use auto 999:999
+ zegroup:baz in use auto 999:999
+ zegroup:gazonk in use auto 999:999
+
- Fix bug with symptom "KeyError: 'process_name'" when using a logfile name
including documented 'process_name' Python string expansions.
Modified: supervisor/trunk/src/supervisor/options.py
==============================================================================
--- supervisor/trunk/src/supervisor/options.py (original)
+++ supervisor/trunk/src/supervisor/options.py Sun Nov 23 14:36:00 2008
@@ -75,6 +75,9 @@
pass
class Options:
+ stderr = sys.stderr
+ stdout = sys.stdout
+ exit = sys.exit
uid = gid = None
@@ -124,13 +127,13 @@
if help.find("%s") > 0:
help = help.replace("%s", self.progname)
print help,
- sys.exit(0)
+ self.exit(0)
def usage(self, msg):
"""Print a brief error message to stderr and exit(2)."""
- sys.stderr.write("Error: %s\n" % str(msg))
- sys.stderr.write("For help, use %s -h\n" % self.progname)
- sys.exit(2)
+ self.stderr.write("Error: %s\n" % str(msg))
+ self.stderr.write("For help, use %s -h\n" % self.progname)
+ self.exit(2)
def add(self,
name=None, # attribute name on self
@@ -292,7 +295,7 @@
self.process_config_file()
- def process_config_file(self):
+ def process_config_file(self, do_usage=True):
# Process config file
if not hasattr(self.configfile, 'read'):
self.here = os.path.abspath(os.path.dirname(self.configfile))
@@ -300,7 +303,12 @@
try:
self.read_config(self.configfile)
except ValueError, msg:
- self.usage(str(msg))
+ if do_usage:
+ # if this is not called from an RPC method, run usage and exit.
+ self.usage(str(msg))
+ else:
+ # if this is called from an RPC method, raise an error
+ raise ValueError(msg)
# Copy config options to attributes of self. This only fills
# in options that aren't already set from the command line.
@@ -471,27 +479,11 @@
self.identifier = section.identifier
- def diff_process_groups(self, new):
- cur = self.process_group_configs
-
- curdict = dict(zip([cfg.name for cfg in cur], cur))
- newdict = dict(zip([cfg.name for cfg in new], new))
-
- added = [cand for cand in new if cand.name not in curdict]
- removed = [cand for cand in cur if cand.name not in newdict]
-
- changed = [cand for cand in new
- if cand != curdict.get(cand.name, cand)]
-
- return added, changed, removed
-
- def process_config_file(self):
- Options.process_config_file(self)
+ def process_config_file(self, do_usage=True):
+ Options.process_config_file(self, do_usage=do_usage)
new = self.configroot.supervisord.process_group_configs
- changes = self.diff_process_groups(new)
self.process_group_configs = new
- return changes
def read_config(self, fp):
section = self.configroot.supervisord
@@ -501,7 +493,10 @@
except (IOError, OSError):
raise ValueError("could not find config file %s" % fp)
parser = UnhosedConfigParser()
- parser.readfp(fp)
+ try:
+ parser.readfp(fp)
+ except ConfigParser.ParsingError, why:
+ raise ValueError(str(why))
if parser.has_section('include'):
if not parser.has_option('include', 'files'):
@@ -518,7 +513,10 @@
for filename in glob.glob(pattern):
self.parse_warnings.append(
'Included extra file "%s" during parsing' % filename)
- parser.read(filename)
+ try:
+ parser.read(filename)
+ except ConfigParser.ParsingError, why:
+ raise ValueError(str(why))
sections = parser.sections()
if not 'supervisord' in sections:
@@ -919,11 +917,11 @@
self.logger.info("set current directory: %r"
% self.directory)
os.close(0)
- sys.stdin = sys.__stdin__ = open("/dev/null")
+ self.stdin = sys.stdin = sys.__stdin__ = open("/dev/null")
os.close(1)
- sys.stdout = sys.__stdout__ = open("/dev/null", "w")
+ self.stdout = sys.stdout = sys.__stdout__ = open("/dev/null", "w")
os.close(2)
- sys.stderr = sys.__stderr__ = open("/dev/null", "w")
+ self.stderr = sys.stderr = sys.__stderr__ = open("/dev/null", "w")
os.setsid()
os.umask(self.umask)
# XXX Stevens, in his Advanced Unix book, section 13.3 (page
Modified: supervisor/trunk/src/supervisor/rpcinterface.py
==============================================================================
--- supervisor/trunk/src/supervisor/rpcinterface.py (original)
+++ supervisor/trunk/src/supervisor/rpcinterface.py Sun Nov 23 14:36:00 2008
@@ -162,6 +162,25 @@
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.
@@ -433,6 +452,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']
Modified: supervisor/trunk/src/supervisor/supervisorctl.py
==============================================================================
--- supervisor/trunk/src/supervisor/supervisorctl.py (original)
+++ supervisor/trunk/src/supervisor/supervisorctl.py Sun Nov 23 14:36:00 2008
@@ -778,6 +778,70 @@
def help_reload(self):
self.ctl.output("reload \t\tRestart the remote supervisord.")
+ def _formatChanges(self, (added, changed, dropped)):
+ changedict = {}
+ for n, t in [(added, 'available'),
+ (changed, 'changed'),
+ (dropped, 'disappeared')]:
+ changedict.update(dict(zip(n, [t] * len(n))))
+
+ if changedict:
+ for name in sorted(changedict):
+ self.ctl.output("%s: %s" % (name, changedict[name]))
+ else:
+ self.ctl.output("No config updates to processes")
+
+ def _formatConfigInfo(self, configinfo):
+ if configinfo['group'] == configinfo['name']:
+ name = configinfo['group']
+ else:
+ name = "%s:%s" % (configinfo['group'], configinfo['name'])
+ formatted = { 'name': name }
+ if configinfo['inuse']:
+ formatted['inuse'] = 'in use'
+ else:
+ formatted['inuse'] = 'avail'
+ if configinfo['autostart']:
+ formatted['autostart'] = 'auto'
+ else:
+ formatted['autostart'] = 'manual'
+ formatted['priority'] = "%s:%s" % (configinfo['group_prio'],
+ configinfo['process_prio'])
+
+ template = '%(name)-32s %(inuse)-9s %(autostart)-9s %(priority)s'
+ return template % formatted
+
+ def do_avail(self, arg):
+ supervisor = self.ctl.get_supervisor()
+ try:
+ configinfo = supervisor.getAllConfigInfo()
+ except xmlrpclib.Fault, e:
+ if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
+ self.ctl.output('ERROR: supervisor shutting down')
+ else:
+ for pinfo in configinfo:
+ self.ctl.output(self._formatConfigInfo(pinfo))
+
+ def help_avail(self):
+ self.ctl.output("avail\t\t\tDisplay all configured processes")
+
+ def do_reread(self, arg):
+ supervisor = self.ctl.get_supervisor()
+ try:
+ result = supervisor.reloadConfig()
+ except xmlrpclib.Fault, e:
+ if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
+ self.ctl.output('ERROR: supervisor shutting down')
+ elif e.faultCode == xmlrpc.Faults.CANT_REREAD:
+ self.ctl.output('ERROR: %s' % e.faultString)
+ else:
+ raise
+ else:
+ self._formatChanges(result[0])
+
+ def help_reread(self):
+ self.ctl.output("reread \t\t\tReload the daemon's configuration files")
+
def do_add(self, arg):
names = arg.strip().split()
@@ -819,12 +883,55 @@
else:
raise
else:
- self.ctl.output("%s: removed" % name)
+ self.ctl.output("%s: removed process group" % name)
def help_remove(self):
self.ctl.output("remove <name> [...]\tRemoves process/group from "
"active config")
+ def do_update(self, arg):
+ def log(name, message):
+ self.ctl.output("%s: %s" % (name, message))
+
+ supervisor = self.ctl.get_supervisor()
+ try:
+ result = supervisor.reloadConfig()
+ except xmlrpclib.Fault, e:
+ if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
+ self.ctl.output('ERROR: already shutting down')
+ return
+ else:
+ raise e
+
+ added, changed, removed = result[0]
+
+ for gname in removed:
+ results = supervisor.stopProcessGroup(gname)
+ log(gname, "stopped")
+
+ fails = [res for res in results
+ if res['status'] == xmlrpc.Faults.FAILED]
+ if fails:
+ log(gname, "has problems; not removing")
+ continue
+ supervisor.removeProcessGroup(gname)
+ log(gname, "removed process group")
+
+ for gname in changed:
+ results = supervisor.stopProcessGroup(gname)
+ log(gname, "stopped")
+
+ supervisor.removeProcessGroup(gname)
+ supervisor.addProcessGroup(gname)
+ log(gname, "updated process group")
+
+ for gname in added:
+ 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 _clearresult(self, result):
name = result['name']
code = result['status']
Modified: supervisor/trunk/src/supervisor/supervisord.py
==============================================================================
--- supervisor/trunk/src/supervisor/supervisord.py (original)
+++ supervisor/trunk/src/supervisor/supervisord.py Sun Nov 23 14:36:00 2008
@@ -108,6 +108,22 @@
finally:
self.options.cleanup()
+ def diff_to_active(self, new=None):
+ if not new:
+ new = self.options.process_group_configs
+ cur = [group.config for group in self.process_groups.values()]
+
+ curdict = dict(zip([cfg.name for cfg in cur], cur))
+ newdict = dict(zip([cfg.name for cfg in new], new))
+
+ added = [cand for cand in new if cand.name not in curdict]
+ removed = [cand for cand in cur if cand.name not in newdict]
+
+ changed = [cand for cand in new
+ if cand != curdict.get(cand.name, cand)]
+
+ return added, changed, removed
+
def add_process_group(self, config):
name = config.name
if name not in self.process_groups:
Modified: supervisor/trunk/src/supervisor/tests/base.py
==============================================================================
--- supervisor/trunk/src/supervisor/tests/base.py (original)
+++ supervisor/trunk/src/supervisor/tests/base.py Sun Nov 23 14:36:00 2008
@@ -79,6 +79,9 @@
self.realizeargs = args
self.realizekw = kw
+ def process_config_file(self, do_usage=True):
+ pass
+
def cleanup_fds(self):
self.fds_cleaned_up = True
@@ -767,7 +770,7 @@
raise Fault(xmlrpc.Faults.SHUTDOWN_STATE, '')
def reloadConfig(self):
- return [[['added'], ['changed'], ['dropped']]]
+ return [[['added'], ['changed'], ['removed']]]
def addProcessGroup(self, name):
from xmlrpclib import Fault
Modified: supervisor/trunk/src/supervisor/tests/test_options.py
==============================================================================
--- supervisor/trunk/src/supervisor/tests/test_options.py (original)
+++ supervisor/trunk/src/supervisor/tests/test_options.py Sun Nov 23 14:36:00 2008
@@ -20,7 +20,7 @@
class OptionTests(unittest.TestCase):
- def _makeOptions(self):
+ def _makeOptions(self, read_error=False):
from cStringIO import StringIO
from supervisor.options import Options
from supervisor.datatypes import integer
@@ -28,12 +28,15 @@
class MyOptions(Options):
master = {
'other': 41 }
- def __init__(self):
+ def __init__(self, read_error=read_error):
+ self.read_error = read_error
Options.__init__(self)
class Foo(object): pass
self.configroot = Foo()
def read_config(self, fp):
+ if self.read_error:
+ raise ValueError(self.read_error)
# Pretend we read it from file:
self.configroot.__dict__.update(self.default_map)
self.configroot.__dict__.update(self.master)
@@ -73,6 +76,24 @@
options.process_config_file()
self.assertEquals(options.other, 42)
+ def test_config_reload_do_usage_false(self):
+ options = self._makeOptions(read_error='error')
+ self.assertRaises(ValueError, options.process_config_file,
+ False)
+
+ def test_config_reload_do_usage_true(self):
+ options = self._makeOptions(read_error='error')
+ from StringIO import StringIO
+ L = []
+ def exit(num):
+ L.append(num)
+ options.stderr = options.stdout = StringIO()
+ options.exit = exit
+ options.configroot.anoption = 1
+ options.configroot.other = 1
+ options.process_config_file(True)
+ self.assertEqual(L, [2])
+
def test__set(self):
from supervisor.options import Options
options = Options()
@@ -405,94 +426,6 @@
self.assertTrue(section.process_group_configs is
instance.process_group_configs)
- def test_diff_add_remove(self):
- options = self._makeOne()
-
- pconfig = DummyPConfig(options, 'process1', 'process1')
- group1 = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig])
-
- pconfig = DummyPConfig(options, 'process2', 'process2')
- group2 = DummyPGroupConfig(options, 'group2', pconfigs=[pconfig])
-
- new = [group1, group2]
-
- added, changed, removed = options.diff_process_groups([])
- self.assertEqual(added, options.process_group_configs)
- self.assertEqual(removed, [])
-
- options.process_group_configs = list(new)
- added, changed, removed = options.diff_process_groups(new)
- self.assertEqual(added, [])
- self.assertEqual(changed, [])
- self.assertEqual(removed, [])
-
- pconfig = DummyPConfig(options, 'process3', 'process3')
- new_group1 = DummyPGroupConfig(options, pconfigs=[pconfig])
-
- pconfig = DummyPConfig(options, 'process4', 'process4')
- new_group2 = DummyPGroupConfig(options, pconfigs=[pconfig])
-
- new = [group2, new_group1, new_group2]
-
- added, changed, removed = options.diff_process_groups(new)
- self.assertEqual(added, [new_group1, new_group2])
- self.assertEqual(changed, [])
- self.assertEqual(removed, [group1])
-
- def test_diff_changed(self):
- from supervisor.options import ProcessConfig, ProcessGroupConfig
-
- options = self._makeOne()
-
- def make_pconfig(name, command, **params):
- result = {
- 'name': name, 'command': command,
- 'directory': None, 'umask': None, 'priority': 999, 'autostart': True,
- 'autorestart': True, 'startsecs': 10, 'startretries': 999,
- 'uid': None, 'stdout_logfile': None, 'stdout_capture_maxbytes': 0,
- 'stdout_logfile_backups': 0, 'stdout_logfile_maxbytes': 0,
- 'stderr_logfile': None, 'stderr_capture_maxbytes': 0,
- 'stderr_logfile_backups': 0, 'stderr_logfile_maxbytes': 0,
- 'redirect_stderr': False,
- 'stopsignal': None, 'stopwaitsecs': 10,
- 'exitcodes': (0,2), 'environment': None, 'serverurl': None }
- result.update(params)
- return ProcessConfig(options, **result)
-
- def make_gconfig(name, pconfigs):
- return ProcessGroupConfig(options, name, 25, pconfigs)
-
- pconfig = make_pconfig('process1', 'process1', uid='new')
- group1 = make_gconfig('group1', [pconfig])
-
- pconfig = make_pconfig('process2', 'process2')
- group2 = make_gconfig('group2', [pconfig])
- new = [group1, group2]
-
- pconfig = make_pconfig('process1', 'process1', uid='old')
- group3 = make_gconfig('group1', [pconfig])
-
- pconfig = make_pconfig('process2', 'process2')
- group4 = make_gconfig('group2', [pconfig])
- options.process_group_configs = [group4, group3]
-
- added, changed, removed = options.diff_process_groups(new)
-
- self.assertEqual([added, removed], [[], []])
- self.assertEqual(changed, [group1])
-
- options = self._makeOne()
- pconfig1 = make_pconfig('process1', 'process1')
- pconfig2 = make_pconfig('process2', 'process2')
- group1 = make_gconfig('group1', [pconfig1, pconfig2])
- new = [group1]
-
- options.process_group_configs = [make_gconfig('group1', [pconfig1])]
-
- added, changed, removed = options.diff_process_groups(new)
- self.assertEqual([added, removed], [[], []])
- self.assertEqual(changed, [group1])
-
def test_readFile_failed(self):
from supervisor.options import readFile
try:
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 Sun Nov 23 14:36:00 2008
@@ -218,6 +218,30 @@
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
@@ -821,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)
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 Sun Nov 23 14:36:00 2008
@@ -472,6 +472,65 @@
self.assertEqual(result, None)
self.assertEqual(options._server.supervisor._shutdown, True)
+ def test__formatChanges(self):
+ plugin = self._makeOne()
+ # Don't explode, plz
+ plugin._formatChanges([['added'], ['changed'], ['removed']])
+ plugin._formatChanges([[], [], []])
+
+ def test_reread(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']])
+
+ def test_reread_Fault(self):
+ plugin = self._makeOne()
+ from supervisor import xmlrpc
+ 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(),
+ 'ERROR: cant\n')
+
+ def test__formatConfigInfo(self):
+ info = { 'group': 'group1',
+ 'name': 'process1',
+ 'inuse': True,
+ 'autostart': True,
+ 'process_prio': 999,
+ 'group_prio': 999 }
+ 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)
+
+ def test_avail(self):
+ calls = []
+ plugin = self._makeOne()
+
+ class FakeSupervisor(object):
+ def getAllConfigInfo(self):
+ return [{ 'group': 'group1', 'name': 'process1',
+ 'inuse': False, 'autostart': False,
+ '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)
+
def test_add(self):
plugin = self._makeOne()
result = plugin.do_add('foo')
@@ -521,6 +580,125 @@
self.assertEqual(plugin.ctl.stdout.getvalue(),
'ERROR: process/group still running: STILL_RUNNING\n')
+ def test_update_not_on_shutdown(self):
+ plugin = self._makeOne()
+ 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 = plugin.ctl.options._server.supervisor
+
+ calls = []
+ def reloadConfig():
+ return [[['new_proc'], [], []]]
+ supervisor.reloadConfig = reloadConfig
+
+ plugin.do_update('')
+ self.assertEqual(supervisor.processes, ['new_proc'])
+
+ def test_update_changed_procs(self):
+ from supervisor import xmlrpc
+ import xmlrpclib
+
+ plugin = self._makeOne()
+ supervisor = plugin.ctl.options._server.supervisor
+
+ calls = []
+ def reloadConfig():
+ return [[[], ['changed_group'], []]]
+ supervisor.reloadConfig = reloadConfig
+ supervisor.startProcess = lambda x: calls.append(('start', x))
+
+ supervisor.addProcessGroup('changed_group') # fake existence
+ results = [{'name': 'changed_process',
+ 'group': 'changed_group',
+ 'status': xmlrpc.Faults.SUCCESS,
+ 'description': 'blah'}]
+ def stopProcessGroup(name):
+ calls.append(('stop', name))
+ return results
+ supervisor.stopProcessGroup = stopProcessGroup
+
+ plugin.do_update('')
+ self.assertEqual(calls, [('stop', 'changed_group')])
+
+ supervisor.addProcessGroup('changed_group') # fake existence
+ calls[:] = []
+ results[:] = [{'name': 'changed_process1',
+ 'group': 'changed_group',
+ 'status': xmlrpc.Faults.NOT_RUNNING,
+ 'description': 'blah'},
+ {'name': 'changed_process2',
+ 'group': 'changed_group',
+ 'status': xmlrpc.Faults.FAILED,
+ 'description': 'blah'}]
+
+ plugin.do_update('')
+ self.assertEqual(calls, [('stop', 'changed_group')])
+
+ supervisor.addProcessGroup('changed_group') # fake existence
+ calls[:] = []
+ results[:] = [{'name': 'changed_process1',
+ 'group': 'changed_group',
+ 'status': xmlrpc.Faults.FAILED,
+ 'description': 'blah'},
+ {'name': 'changed_process2',
+ 'group': 'changed_group',
+ 'status': xmlrpc.Faults.SUCCESS,
+ 'description': 'blah'}]
+
+ plugin.do_update('')
+ self.assertEqual(calls, [('stop', 'changed_group')])
+
+ def test_update_removed_procs(self):
+ from supervisor import xmlrpc
+
+ plugin = self._makeOne()
+ supervisor = plugin.ctl.options._server.supervisor
+
+ def reloadConfig():
+ return [[[], [], ['removed_group']]]
+ supervisor.reloadConfig = reloadConfig
+
+ results = [{'name': 'removed_process',
+ 'group': 'removed_group',
+ 'status': xmlrpc.Faults.SUCCESS,
+ 'description': 'blah'}]
+ supervisor.processes = ['removed_group']
+
+ def stopProcessGroup(name):
+ return results
+ supervisor.stopProcessGroup = stopProcessGroup
+
+ plugin.do_update('')
+ self.assertEqual(supervisor.processes, [])
+
+ results[:] = [{'name': 'removed_process',
+ 'group': 'removed_group',
+ 'status': xmlrpc.Faults.NOT_RUNNING,
+ 'description': 'blah'}]
+ supervisor.processes = ['removed_group']
+
+ plugin.do_update('')
+ self.assertEqual(supervisor.processes, [])
+
+ results[:] = [{'name': 'removed_process',
+ 'group': 'removed_group',
+ 'status': xmlrpc.Faults.FAILED,
+ 'description': 'blah'}]
+ supervisor.processes = ['removed_group']
+
+ plugin.do_update('')
+ self.assertEqual(supervisor.processes, ['removed_group'])
+
def test_pid(self):
plugin = self._makeOne()
result = plugin.do_pid('')
Modified: supervisor/trunk/src/supervisor/tests/test_supervisord.py
==============================================================================
--- supervisor/trunk/src/supervisor/tests/test_supervisord.py (original)
+++ supervisor/trunk/src/supervisor/tests/test_supervisord.py Sun Nov 23 14:36:00 2008
@@ -173,6 +173,106 @@
self.assertEqual(options.logger.data[0],
'received SIGUSR1 indicating nothing')
+ def test_diff_add_remove(self):
+ options = DummyOptions()
+ supervisord = self._makeOne(options)
+
+ pconfig = DummyPConfig(options, 'process1', 'process1')
+ group1 = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig])
+
+ pconfig = DummyPConfig(options, 'process2', 'process2')
+ group2 = DummyPGroupConfig(options, 'group2', pconfigs=[pconfig])
+
+ new = [group1, group2]
+
+ added, changed, removed = supervisord.diff_to_active()
+ self.assertEqual(added, [])
+ self.assertEqual(changed, [])
+ self.assertEqual(removed, [])
+
+ added, changed, removed = supervisord.diff_to_active(new)
+ self.assertEqual(added, new)
+ self.assertEqual(changed, [])
+ self.assertEqual(removed, [])
+
+ supervisord.options.process_group_configs = new
+ added, changed, removed = supervisord.diff_to_active()
+ self.assertEqual(added, new)
+
+ supervisord.add_process_group(group1)
+ supervisord.add_process_group(group2)
+
+ pconfig = DummyPConfig(options, 'process3', 'process3')
+ new_group1 = DummyPGroupConfig(options, pconfigs=[pconfig])
+
+ pconfig = DummyPConfig(options, 'process4', 'process4')
+ new_group2 = DummyPGroupConfig(options, pconfigs=[pconfig])
+
+ new = [group2, new_group1, new_group2]
+
+ added, changed, removed = supervisord.diff_to_active(new)
+ self.assertEqual(added, [new_group1, new_group2])
+ self.assertEqual(changed, [])
+ self.assertEqual(removed, [group1])
+
+ def test_diff_changed(self):
+ from supervisor.options import ProcessConfig, ProcessGroupConfig
+
+ options = DummyOptions()
+ supervisord = self._makeOne(options)
+
+ def make_pconfig(name, command, **params):
+ result = {
+ 'name': name, 'command': command,
+ 'directory': None, 'umask': None, 'priority': 999, 'autostart': True,
+ 'autorestart': True, 'startsecs': 10, 'startretries': 999,
+ 'uid': None, 'stdout_logfile': None, 'stdout_capture_maxbytes': 0,
+ 'stdout_logfile_backups': 0, 'stdout_logfile_maxbytes': 0,
+ 'stderr_logfile': None, 'stderr_capture_maxbytes': 0,
+ 'stderr_logfile_backups': 0, 'stderr_logfile_maxbytes': 0,
+ 'redirect_stderr': False,
+ 'stopsignal': None, 'stopwaitsecs': 10,
+ 'exitcodes': (0,2), 'environment': None, 'serverurl': None }
+ result.update(params)
+ return ProcessConfig(options, **result)
+
+ def make_gconfig(name, pconfigs):
+ return ProcessGroupConfig(options, name, 25, pconfigs)
+
+ pconfig = make_pconfig('process1', 'process1', uid='new')
+ group1 = make_gconfig('group1', [pconfig])
+
+ pconfig = make_pconfig('process2', 'process2')
+ group2 = make_gconfig('group2', [pconfig])
+ new = [group1, group2]
+
+ pconfig = make_pconfig('process1', 'process1', uid='old')
+ group3 = make_gconfig('group1', [pconfig])
+
+ pconfig = make_pconfig('process2', 'process2')
+ group4 = make_gconfig('group2', [pconfig])
+ supervisord.add_process_group(group3)
+ supervisord.add_process_group(group4)
+
+ added, changed, removed = supervisord.diff_to_active(new)
+
+ self.assertEqual([added, removed], [[], []])
+ self.assertEqual(changed, [group1])
+
+ options = DummyOptions()
+ supervisord = self._makeOne(options)
+
+ pconfig1 = make_pconfig('process1', 'process1')
+ pconfig2 = make_pconfig('process2', 'process2')
+ group1 = make_gconfig('group1', [pconfig1, pconfig2])
+ new = [group1]
+
+ supervisord.add_process_group(make_gconfig('group1', [pconfig1]))
+
+ added, changed, removed = supervisord.diff_to_active(new)
+ self.assertEqual([added, removed], [[], []])
+ self.assertEqual(changed, [group1])
+
def test_add_process_group(self):
options = DummyOptions()
pconfig = DummyPConfig(options, 'foo', 'foo', '/bin/foo')
Modified: supervisor/trunk/src/supervisor/xmlrpc.py
==============================================================================
--- supervisor/trunk/src/supervisor/xmlrpc.py (original)
+++ supervisor/trunk/src/supervisor/xmlrpc.py Sun Nov 23 14:36:00 2008
@@ -45,6 +45,7 @@
SUCCESS = 80
ALREADY_ADDED = 90
STILL_RUNNING = 91
+ CANT_REREAD = 92
def getFaultDescription(code):
for faultname in Faults.__dict__:
More information about the Supervisor-checkins
mailing list