Example #1
0
 def __init__(self):
     self.root = None
     self.web = None
     self.mode = MULTI_SERVER_MODE
     self.node = None
     self.script = "appmain.py"
     self.process = ProcessMonitor()
     self.service = service.MultiService()
     self.start_time = 0
 def setUp(self):
     """
     Create an L{ProcessMonitor} wrapped around a fake reactor.
     """
     self.reactor = DummyProcessReactor()
     self.pm = ProcessMonitor(reactor=self.reactor)
     self.pm.minRestartDelay = 2
     self.pm.maxRestartDelay = 10
     self.pm.threshold = 10
Example #3
0
def run_zeo(db):
    """Spawns a zeo daemon and restart it if it crashes"""
    runzeo = 'bin/runzeo'
    # XXX: compat mode for buildout-less runs
    if not os.path.exists('bin/runzeo'):
        runzeo = 'runzeo'

    pm = ProcessMonitor()
    pm.addProcess('zeo', ['/bin/sh', '-c', '%s -f %s/data.fs -a %s/socket >%s/zeo.log 2>&1' % (runzeo, db, db, db)], env=os.environ)
    pm.startService()
Example #4
0
def makeService():

    s = ProcessMonitor()
    s.threshold = 1
    s.killTime = 5
    s.minRestartDelay = 1
    s.maxRestartDelay = 5


    return s
Example #5
0
class DeprecationTests(unittest.SynchronousTestCase):

    """
    Tests that check functionality that should be deprecated is deprecated.
    """

    def setUp(self):
        """
        Create reactor and process monitor.
        """
        self.reactor = DummyProcessReactor()
        self.pm = ProcessMonitor(reactor=self.reactor)

    def test_toTuple(self):
        """
        _Process.toTuple is deprecated.

        When getting the deprecated processes property, the actual
        data (kept in the class _Process) is converted to a tuple --
        which produces a DeprecationWarning per process so converted.
        """
        self.pm.addProcess("foo", ["foo"])
        myprocesses = self.pm.processes
        self.assertEquals(len(myprocesses), 1)
        warnings = self.flushWarnings()
        foundToTuple = False
        for warning in warnings:
            self.assertIs(warning["category"], DeprecationWarning)
            if "toTuple" in warning["message"]:
                foundToTuple = True
        self.assertTrue(foundToTuple, f"no tuple deprecation found:{repr(warnings)}")

    def test_processes(self):
        """
        Accessing L{ProcessMonitor.processes} results in deprecation warning

        Even when there are no processes, and thus no process is converted
        to a tuple, accessing the L{ProcessMonitor.processes} property
        should generate its own DeprecationWarning.
        """
        myProcesses = self.pm.processes
        self.assertEquals(myProcesses, {})
        warnings = self.flushWarnings()
        first = warnings.pop(0)
        self.assertIs(first["category"], DeprecationWarning)
        self.assertEquals(warnings, [])

    def test_getstate(self):
        """
        Pickling an L{ProcessMonitor} results in deprecation warnings
        """
        pickle.dumps(self.pm)
        warnings = self.flushWarnings()
        for warning in warnings:
            self.assertIs(warning["category"], DeprecationWarning)
Example #6
0
    def __init__(self, log=None, reactor=None):
        if reactor:
            BaseMonitor.__init__(self, reactor=reactor)
        else:
            BaseMonitor.__init__(self)

        EventEmitter.__init__(self)

        self.log = log or self.log
        self.settings = dict()
        self.states = dict()
Example #7
0
 def test_addProcessEnv(self):
     """
     L{ProcessMonitor.addProcess} takes an C{env} parameter that is passed
     to C{spawnProcess}.
     """
     spawnedProcesses = []
     def fakeSpawnProcess(*args, **kwargs):
         spawnedProcesses.append((args, kwargs))
     self.patch(reactor, "spawnProcess", fakeSpawnProcess)
     pm = ProcessMonitor()
     pm.active = True
     fakeEnv = {"KEY": "value"}
     pm.addProcess("foo", ["foo"], uid=1, gid=2, env=fakeEnv)
     self.assertEquals(
         spawnedProcesses,
         [((pm.protocols["foo"], "foo", ["foo"]),
           {"uid": 1, "gid": 2, "env": fakeEnv})])
Example #8
0
def makeService(config):
    s = ProcessMonitor()

    s.threshold = config["threshold"]
    s.killTime = config["killtime"]
    s.minRestartDelay = config["minrestartdelay"]
    s.maxRestartDelay = config["maxrestartdelay"]

    s.addProcess(" ".join(config["args"]), config["args"])
    return s
Example #9
0
 def test_addProcess(self):
     """
     L{ProcessMonitor.addProcess} starts the named program and tracks the
     resulting process, a protocol for collecting its stdout, and the time
     it was started.
     """
     spawnedProcesses = []
     def fakeSpawnProcess(*args, **kwargs):
         spawnedProcesses.append((args, kwargs))
     self.patch(reactor, "spawnProcess", fakeSpawnProcess)
     pm = ProcessMonitor()
     pm.active = True
     pm.addProcess("foo", ["arg1", "arg2"], uid=1, gid=2)
     self.assertEquals(pm.processes, {"foo": (["arg1", "arg2"], 1, 2, {})})
     self.assertEquals(pm.protocols.keys(), ["foo"])
     lp = pm.protocols["foo"]
     self.assertEquals(
         spawnedProcesses,
         [((lp, "arg1", ["arg1", "arg2"]),
           {"uid": 1, "gid": 2, "env": {}})])
Example #10
0
    def test_addProcessEnv(self):
        """
        L{ProcessMonitor.addProcess} takes an C{env} parameter that is passed
        to C{spawnProcess}.
        """
        spawnedProcesses = []

        def fakeSpawnProcess(*args, **kwargs):
            spawnedProcesses.append((args, kwargs))

        self.patch(reactor, "spawnProcess", fakeSpawnProcess)
        pm = ProcessMonitor()
        pm.active = True
        fakeEnv = {"KEY": "value"}
        pm.addProcess("foo", ["foo"], uid=1, gid=2, env=fakeEnv)
        self.assertEquals(spawnedProcesses,
                          [((pm.protocols["foo"], "foo", ["foo"]), {
                              "uid": 1,
                              "gid": 2,
                              "env": fakeEnv
                          })])
Example #11
0
def makeService(config):
    s = ProcessMonitor()

    s.threshold = config["threshold"]
    s.killTime = config["killtime"]
    s.minRestartDelay = config["minrestartdelay"]
    s.maxRestartDelay = config["maxrestartdelay"]

    s.addProcess(" ".join(config["args"]), config["args"])
    return s
Example #12
0
    def test_addProcess(self):
        """
        L{ProcessMonitor.addProcess} starts the named program and tracks the
        resulting process, a protocol for collecting its stdout, and the time
        it was started.
        """
        spawnedProcesses = []

        def fakeSpawnProcess(*args, **kwargs):
            spawnedProcesses.append((args, kwargs))

        self.patch(reactor, "spawnProcess", fakeSpawnProcess)
        pm = ProcessMonitor()
        pm.active = True
        pm.addProcess("foo", ["arg1", "arg2"], uid=1, gid=2)
        self.assertEquals(pm.processes, {"foo": (["arg1", "arg2"], 1, 2, {})})
        self.assertEquals(pm.protocols.keys(), ["foo"])
        lp = pm.protocols["foo"]
        self.assertEquals(spawnedProcesses, [((lp, "arg1", ["arg1", "arg2"]), {
            "uid": 1,
            "gid": 2,
            "env": {}
        })])
Example #13
0
def run_zeo(db):
    """Spawns a zeo daemon and restart it if it crashes"""
    runzeo = 'bin/runzeo'
    # XXX: compat mode for buildout-less runs
    if not os.path.exists('bin/runzeo'):
        runzeo = 'runzeo'

    pm = ProcessMonitor()
    pm.addProcess('zeo', [
        '/bin/sh', '-c',
        '%s -f %s/data.fs -a %s/socket >%s/zeo.log 2>&1' % (runzeo, db, db, db)
    ],
                  env=os.environ)
    pm.startService()
Example #14
0
from twisted.runner.procmon import ProcessMonitor

import time

def makeService():

    s = ProcessMonitor()
    s.threshold = 1
    s.killTime = 5
    s.minRestartDelay = 1
    s.maxRestartDelay = 5


    return s

ss = ProcessMonitor()
#ss.startService()
ss.startService()
ss.addProcess("calc.exe *32", ['c:\windows\system32\calc.exe', ])


while True:

    #ss.startProcess('calc.exe')
    time.sleep(5)
    break
    #break
ss.stopService()

ss.startService()
while True:
class ProcmonTests(unittest.TestCase):
    """
    Tests for L{ProcessMonitor}.
    """

    def setUp(self):
        """
        Create an L{ProcessMonitor} wrapped around a fake reactor.
        """
        self.reactor = DummyProcessReactor()
        self.pm = ProcessMonitor(reactor=self.reactor)
        self.pm.minRestartDelay = 2
        self.pm.maxRestartDelay = 10
        self.pm.threshold = 10


    def test_getStateIncludesProcesses(self):
        """
        The list of monitored processes must be included in the pickle state.
        """
        self.pm.addProcess("foo", ["arg1", "arg2"],
                           uid=1, gid=2, env={})
        self.assertEqual(self.pm.__getstate__()['processes'],
                          {'foo': (['arg1', 'arg2'], 1, 2, {})})


    def test_getStateExcludesReactor(self):
        """
        The private L{ProcessMonitor._reactor} instance variable should not be
        included in the pickle state.
        """
        self.assertNotIn('_reactor', self.pm.__getstate__())


    def test_addProcess(self):
        """
        L{ProcessMonitor.addProcess} only starts the named program if
        L{ProcessMonitor.startService} has been called.
        """
        self.pm.addProcess("foo", ["arg1", "arg2"],
                           uid=1, gid=2, env={})
        self.assertEqual(self.pm.protocols, {})
        self.assertEqual(self.pm.processes,
                          {"foo": (["arg1", "arg2"], 1, 2, {})})
        self.pm.startService()
        self.reactor.advance(0)
        self.assertEqual(self.pm.protocols.keys(), ["foo"])


    def test_addProcessDuplicateKeyError(self):
        """
        L{ProcessMonitor.addProcess} raises a C{KeyError} if a process with the
        given name already exists.
        """
        self.pm.addProcess("foo", ["arg1", "arg2"],
                           uid=1, gid=2, env={})
        self.assertRaises(KeyError, self.pm.addProcess,
                          "foo", ["arg1", "arg2"], uid=1, gid=2, env={})


    def test_addProcessEnv(self):
        """
        L{ProcessMonitor.addProcess} takes an C{env} parameter that is passed to
        L{IReactorProcess.spawnProcess}.
        """
        fakeEnv = {"KEY": "value"}
        self.pm.startService()
        self.pm.addProcess("foo", ["foo"], uid=1, gid=2, env=fakeEnv)
        self.reactor.advance(0)
        self.assertEqual(
            self.reactor.spawnedProcesses[0]._environment, fakeEnv)


    def test_removeProcess(self):
        """
        L{ProcessMonitor.removeProcess} removes the process from the public
        processes list.
        """
        self.pm.startService()
        self.pm.addProcess("foo", ["foo"])
        self.assertEqual(len(self.pm.processes), 1)
        self.pm.removeProcess("foo")
        self.assertEqual(len(self.pm.processes), 0)


    def test_removeProcessUnknownKeyError(self):
        """
        L{ProcessMonitor.removeProcess} raises a C{KeyError} if the given
        process name isn't recognised.
        """
        self.pm.startService()
        self.assertRaises(KeyError, self.pm.removeProcess, "foo")


    def test_startProcess(self):
        """
        When a process has been started, an instance of L{LoggingProtocol} will
        be added to the L{ProcessMonitor.protocols} dict and the start time of
        the process will be recorded in the L{ProcessMonitor.timeStarted}
        dictionary.
        """
        self.pm.addProcess("foo", ["foo"])
        self.pm.startProcess("foo")
        self.assertIsInstance(self.pm.protocols["foo"], LoggingProtocol)
        self.assertIn("foo", self.pm.timeStarted.keys())


    def test_startProcessAlreadyStarted(self):
        """
        L{ProcessMonitor.startProcess} silently returns if the named process is
        already started.
        """
        self.pm.addProcess("foo", ["foo"])
        self.pm.startProcess("foo")
        self.assertIsNone(self.pm.startProcess("foo"))


    def test_startProcessUnknownKeyError(self):
        """
        L{ProcessMonitor.startProcess} raises a C{KeyError} if the given
        process name isn't recognised.
        """
        self.assertRaises(KeyError, self.pm.startProcess, "foo")


    def test_stopProcessNaturalTermination(self):
        """
        L{ProcessMonitor.stopProcess} immediately sends a TERM signal to the
        named process.
        """
        self.pm.startService()
        self.pm.addProcess("foo", ["foo"])
        self.assertIn("foo", self.pm.protocols)

        # Configure fake process to die 1 second after receiving term signal
        timeToDie = self.pm.protocols["foo"].transport._terminationDelay = 1

        # Advance the reactor to just before the short lived process threshold
        # and leave enough time for the process to die
        self.reactor.advance(self.pm.threshold)
        # Then signal the process to stop
        self.pm.stopProcess("foo")

        # Advance the reactor just enough to give the process time to die and
        # verify that the process restarts
        self.reactor.advance(timeToDie)

        # We expect it to be restarted immediately
        self.assertEqual(self.reactor.seconds(),
                         self.pm.timeStarted["foo"])


    def test_stopProcessForcedKill(self):
        """
        L{ProcessMonitor.stopProcess} kills a process which fails to terminate
        naturally within L{ProcessMonitor.killTime} seconds.
        """
        self.pm.startService()
        self.pm.addProcess("foo", ["foo"])
        self.assertIn("foo", self.pm.protocols)
        self.reactor.advance(self.pm.threshold)
        proc = self.pm.protocols["foo"].transport
        # Arrange for the fake process to live longer than the killTime
        proc._terminationDelay = self.pm.killTime + 1
        self.pm.stopProcess("foo")
        # If process doesn't die before the killTime, procmon should
        # terminate it
        self.reactor.advance(self.pm.killTime - 1)
        self.assertEqual(0.0, self.pm.timeStarted["foo"])

        self.reactor.advance(1)
        # We expect it to be immediately restarted
        self.assertEqual(self.reactor.seconds(), self.pm.timeStarted["foo"])


    def test_stopProcessUnknownKeyError(self):
        """
        L{ProcessMonitor.stopProcess} raises a C{KeyError} if the given process
        name isn't recognised.
        """
        self.assertRaises(KeyError, self.pm.stopProcess, "foo")


    def test_stopProcessAlreadyStopped(self):
        """
        L{ProcessMonitor.stopProcess} silently returns if the named process
        is already stopped. eg Process has crashed and a restart has been
        rescheduled, but in the meantime, the service is stopped.
        """
        self.pm.addProcess("foo", ["foo"])
        self.assertIsNone(self.pm.stopProcess("foo"))


    def test_connectionLostLongLivedProcess(self):
        """
        L{ProcessMonitor.connectionLost} should immediately restart a process
        if it has been running longer than L{ProcessMonitor.threshold} seconds.
        """
        self.pm.addProcess("foo", ["foo"])
        # Schedule the process to start
        self.pm.startService()
        # advance the reactor to start the process
        self.reactor.advance(0)
        self.assertIn("foo", self.pm.protocols)
        # Long time passes
        self.reactor.advance(self.pm.threshold)
        # Process dies after threshold
        self.pm.protocols["foo"].processEnded(Failure(ProcessDone(0)))
        self.assertNotIn("foo", self.pm.protocols)
        # Process should be restarted immediately
        self.reactor.advance(0)
        self.assertIn("foo", self.pm.protocols)


    def test_connectionLostMurderCancel(self):
        """
        L{ProcessMonitor.connectionLost} cancels a scheduled process killer and
        deletes the DelayedCall from the L{ProcessMonitor.murder} list.
        """
        self.pm.addProcess("foo", ["foo"])
        # Schedule the process to start
        self.pm.startService()
        # Advance 1s to start the process then ask ProcMon to stop it
        self.reactor.advance(1)
        self.pm.stopProcess("foo")
        # A process killer has been scheduled, delayedCall is active
        self.assertIn("foo", self.pm.murder)
        delayedCall = self.pm.murder["foo"]
        self.assertTrue(delayedCall.active())
        # Advance to the point at which the dummy process exits
        self.reactor.advance(
            self.pm.protocols["foo"].transport._terminationDelay)
        # Now the delayedCall has been cancelled and deleted
        self.assertFalse(delayedCall.active())
        self.assertNotIn("foo", self.pm.murder)


    def test_connectionLostProtocolDeletion(self):
        """
        L{ProcessMonitor.connectionLost} removes the corresponding
        ProcessProtocol instance from the L{ProcessMonitor.protocols} list.
        """
        self.pm.startService()
        self.pm.addProcess("foo", ["foo"])
        self.assertIn("foo", self.pm.protocols)
        self.pm.protocols["foo"].transport.signalProcess("KILL")
        self.reactor.advance(
            self.pm.protocols["foo"].transport._terminationDelay)
        self.assertNotIn("foo", self.pm.protocols)


    def test_connectionLostMinMaxRestartDelay(self):
        """
        L{ProcessMonitor.connectionLost} will wait at least minRestartDelay s
        and at most maxRestartDelay s
        """
        self.pm.minRestartDelay = 2
        self.pm.maxRestartDelay = 3

        self.pm.startService()
        self.pm.addProcess("foo", ["foo"])

        self.assertEqual(self.pm.delay["foo"], self.pm.minRestartDelay)
        self.reactor.advance(self.pm.threshold - 1)
        self.pm.protocols["foo"].processEnded(Failure(ProcessDone(0)))
        self.assertEqual(self.pm.delay["foo"], self.pm.maxRestartDelay)


    def test_connectionLostBackoffDelayDoubles(self):
        """
        L{ProcessMonitor.connectionLost} doubles the restart delay each time
        the process dies too quickly.
        """
        self.pm.startService()
        self.pm.addProcess("foo", ["foo"])
        self.reactor.advance(self.pm.threshold - 1) #9s
        self.assertIn("foo", self.pm.protocols)
        self.assertEqual(self.pm.delay["foo"], self.pm.minRestartDelay)
        # process dies within the threshold and should not restart immediately
        self.pm.protocols["foo"].processEnded(Failure(ProcessDone(0)))
        self.assertEqual(self.pm.delay["foo"], self.pm.minRestartDelay * 2)


    def test_startService(self):
        """
        L{ProcessMonitor.startService} starts all monitored processes.
        """
        self.pm.addProcess("foo", ["foo"])
        # Schedule the process to start
        self.pm.startService()
        # advance the reactor to start the process
        self.reactor.advance(0)
        self.assertIn("foo", self.pm.protocols)


    def test_stopService(self):
        """
        L{ProcessMonitor.stopService} should stop all monitored processes.
        """
        self.pm.addProcess("foo", ["foo"])
        self.pm.addProcess("bar", ["bar"])
        # Schedule the process to start
        self.pm.startService()
        # advance the reactor to start the processes
        self.reactor.advance(self.pm.threshold)
        self.assertIn("foo", self.pm.protocols)
        self.assertIn("bar", self.pm.protocols)

        self.reactor.advance(1)

        self.pm.stopService()
        # Advance to beyond the killTime - all monitored processes
        # should have exited
        self.reactor.advance(self.pm.killTime + 1)
        # The processes shouldn't be restarted
        self.assertEqual({}, self.pm.protocols)


    def test_stopServiceCancelRestarts(self):
        """
        L{ProcessMonitor.stopService} should cancel any scheduled process
        restarts.
        """
        self.pm.addProcess("foo", ["foo"])
        # Schedule the process to start
        self.pm.startService()
        # advance the reactor to start the processes
        self.reactor.advance(self.pm.threshold)
        self.assertIn("foo", self.pm.protocols)

        self.reactor.advance(1)
        # Kill the process early
        self.pm.protocols["foo"].processEnded(Failure(ProcessDone(0)))
        self.assertTrue(self.pm.restart['foo'].active())
        self.pm.stopService()
        # Scheduled restart should have been cancelled
        self.assertFalse(self.pm.restart['foo'].active())


    def test_stopServiceCleanupScheduledRestarts(self):
        """
        L{ProcessMonitor.stopService} should cancel all scheduled process
        restarts.
        """
        self.pm.threshold = 5
        self.pm.minRestartDelay = 5
        # Start service and add a process (started immediately)
        self.pm.startService()
        self.pm.addProcess("foo", ["foo"])
        # Stop the process after 1s
        self.reactor.advance(1)
        self.pm.stopProcess("foo")
        # Wait 1s for it to exit it will be scheduled to restart 5s later
        self.reactor.advance(1)
        # Meanwhile stop the service
        self.pm.stopService()
        # Advance to beyond the process restart time
        self.reactor.advance(6)
        # The process shouldn't have restarted because stopService has cancelled
        # all pending process restarts.
        self.assertEqual(self.pm.protocols, {})
Example #16
0
class Master:
    def __init__(self):
        self.root = None
        self.web = None
        self.mode = MULTI_SERVER_MODE
        self.node = None
        self.script = "appmain.py"
        self.process = ProcessMonitor()
        self.service = service.MultiService()
        self.start_time = 0

    def set_mode(self, mode):
        self.mode = mode

    def set_node(self, node):
        self.node = node

    def set_script(self, script):
        self.script = script

    def create_master(self):
        """
        创建Master服务
        :return:
        """
        config = Config().config
        GlobalObject().json_config = config
        mastercnf = config.get('master')
        rootport = mastercnf.get('rootport')
        webport = mastercnf.get('webport')
        masterlog = mastercnf.get('log')
        self.root = PBRoot()
        rootservice = services.Service("rootservice")
        self.root.addServiceChannel(rootservice)
        self.web = vhost.NameVirtualHost()
        self.web.addHost('0.0.0.0', './')
        GlobalObject().root = self.root
        GlobalObject().webroot = self.web
        import webapp
        import rootapp
        internet.TCPServer(webport,
                           DelaySite(self.web)).setServiceParent(self.service)
        internet.TCPServer(rootport, BilateralFactory(
            self.root)).setServiceParent(self.service)
        self.process.setServiceParent(self.service)

    def create_node(self, name):
        """
        创建节点服务
        :param name:
        :return:
        """
        args = ["python", self.script, name]
        self.process.addProcess(name, args, env=os.environ)

    def start(self, app):
        """
        启动APP
        :param app:
        :return:
        """
        self.start_time = reactor.seconds()
        if self.mode == MULTI_SERVER_MODE:
            self.create_master()
            servers = Config().servers
            for name in servers.keys():
                self.create_node(name)
        elif self.mode == SINGLE_SERVER_MODE:
            self.create_node(self.node)
        else:
            self.create_master()
        reactor.addSystemEventTrigger('after', 'startup', self.startAfter)
        reactor.addSystemEventTrigger('before', 'shutdown', self.stopBefore)
        if "-y" in sys.argv and "-n" not in sys.argv:
            app.setComponent(
                log.ILogObserver,
                log.FileLogObserver(DailyLogFile("logs/master.log", "")).emit)
        self.service.setServiceParent(app)
        GlobalObject().server = self

    def startAfter(self):
        """
        启动之后
        :return:
        """
        log.msg("*** The master in the %s launch ***" % time.strftime(
            '%Y-%m-%d %H:%M:%S', time.localtime(self.start_time)))
        # 在根目录写入启动进程信息
        process = [(os.getpid(), 'master')]
        for name, proc in self.process.protocols.items():
            process.append((proc.transport.pid, name))
        with open("status.json", "w+") as f:
            f.write(Jsonify(process))
            f.close()

    def stopBefore(self):
        """
        关闭之前
        :return:
        """
        log.msg("*** Wait for the child process to exit ***")
        wait = dict([(proc.transport.pid, name)
                     for name, proc in self.process.protocols.items()])
        while True:
            try:
                pid = os.wait()[0]
                log.msg("[%s] child node has quit, pid: %s" %
                        (wait.get(pid), pid))
            except:
                break
        signal.alarm(1)
Example #17
0
 def stopService(self):
     self.connectionLost = self.stoppedConnectionLost
     # Can't use super here as ProcessMonitor is an old-style object.
     return ProcessMonitor.stopService(self)
 def makeService(self, options):
     if not os.path.isfile(options['config']):
         raise ConfigNotFoundException()
     
     config = yaml.load(open(options['config']))
     
     use_ssl = config['usessl']
     
     from apiserver import settings
     settings.BASE_URL = '%s://%s:%s' % (use_ssl and 'https' or 'http', config['external_ip'], config['port'])
     
     settings.METADATA_FILE = config['metadata_database']
     settings.DB_FILE = config['database']
     if 'datapath' in config:
         if os.path.isdir(config['datapath']):
             settings.DATA_PATH = config['datapath']
         else:
             log.err("Datapath '%s' does not exist" % config['datapath'])
     
     from apiserver.interfaces import IBackend, ISection, IService
     
     import apiserver.backends
     backend_types = dict((p.name, p) for p in getPlugins(IBackend, apiserver.backends))
     
     import apiserver.sections
     section_types = dict((p.name, p) for p in getPlugins(ISection, apiserver.sections))
     
     import apiserver.services
     settings.SERVICES = list(getPlugins(IService, apiserver.services))
     log.err('Found services: %s' % ', '.join(s.name for s in settings.SERVICES))
     
     known_nodes = {}
     for name, node in config['nodes'].iteritems():
         if node['backend'] not in backend_types:
             log.err("Unknown backend %s for node %s" % (node['backend'], name))
             continue
         node['type'] = backend_types[node['backend']]
         node['id'] = name
         known_nodes[name] = node
     
     for name, cfg in config['sections'].items():
         if 'nodes' not in cfg:
             log.err("No nodes found for section %s" % name)
             continue
         
         if 'type' not in cfg:
             log.err("Type missing for section %s" % name)
             continue
         
         if cfg['type'] not in section_types:
             log.err("Unknown section type %s for node %s" % (cfg['type'], name))
             continue
         
         section_type = section_types[cfg['type']]
         
         section = settings.SECTIONS[name] = section_type(name, cfg)
         
         for node_name, section_cfg in cfg['nodes'].items():
             if node_name not in known_nodes:
                 log.err("Unknown node %s for setion %s" % (node_name, name))
                 continue
             
             node_cfg = known_nodes[node_name]
             node_type = node_cfg['type']
             node = node_type(section, node_cfg, section_cfg)
             section.nodes.append(node)
     
     if 'plugins' in config:
         settings.PLUGINS.update(config['plugins'])
     
     from apiserver.resource import TidalStreamHTTPAuthSessionWrapper
     from apiserver.server import RootResource
     from apiserver.tokenauth import InternalTokenAuthFactory, TokenPasswordChecker
     
     settings.PASSWORD_CHECKER = password_checker = FilePasswordDB(config['userfile'])
     settings.TOKEN_PASSWORD_CHECKER = token_password_checker = TokenPasswordChecker()
     checkers = [password_checker, token_password_checker]
     
     wrapper = TidalStreamHTTPAuthSessionWrapper(
         Portal(TidalStreamRealm(RootResource()),
                checkers), [guard.BasicCredentialFactory('TidalStream'), InternalTokenAuthFactory()])
     
     site = server.Site(wrapper)
     
     ms = MultiService()
     
     if config.get('phantomjs', None):
         phantom_exe = config['phantomjs']
         if os.path.isfile(phantom_exe):
             pm = ProcessMonitor()
             pm.addProcess('phantomjs', [phantom_exe, '--webdriver=127.0.0.1:%s' % settings.PHANTOMJS_PORT])
             
             ms.addService(pm)
     
     if use_ssl:
         ms.addService(internet.SSLServer(config['port'], site,
                                          ServerContextFactory(config['certfile'])))
     else:
         ms.addService(internet.TCPServer(config['port'], site))
     
     return ms
Example #19
0
 def setUp(self):
     """
     Create reactor and process monitor.
     """
     self.reactor = DummyProcessReactor()
     self.pm = ProcessMonitor(reactor=self.reactor)
Example #20
0
class ProcmonTests(unittest.TestCase):
    """
    Tests for L{ProcessMonitor}.
    """
    def setUp(self):
        """
        Create an L{ProcessMonitor} wrapped around a fake reactor.
        """
        self.reactor = DummyProcessReactor()
        self.pm = ProcessMonitor(reactor=self.reactor)
        self.pm.minRestartDelay = 2
        self.pm.maxRestartDelay = 10
        self.pm.threshold = 10

    def test_reprLooksGood(self):
        """
        Repr includes all details
        """
        self.pm.addProcess("foo", ["arg1", "arg2"], uid=1, gid=2, env={})
        representation = repr(self.pm)
        self.assertIn("foo", representation)
        self.assertIn("1", representation)
        self.assertIn("2", representation)

    def test_simpleReprLooksGood(self):
        """
        Repr does not include unneeded details.

        Values of attributes that just mean "inherit from launching
        process" do not appear in the repr of a process.
        """
        self.pm.addProcess("foo", ["arg1", "arg2"], env={})
        representation = repr(self.pm)
        self.assertNotIn("(", representation)
        self.assertNotIn(")", representation)

    def test_getStateIncludesProcesses(self):
        """
        The list of monitored processes must be included in the pickle state.
        """
        self.pm.addProcess("foo", ["arg1", "arg2"], uid=1, gid=2, env={})
        self.assertEqual(self.pm.__getstate__()["processes"],
                         {"foo": (["arg1", "arg2"], 1, 2, {})})

    def test_getStateExcludesReactor(self):
        """
        The private L{ProcessMonitor._reactor} instance variable should not be
        included in the pickle state.
        """
        self.assertNotIn("_reactor", self.pm.__getstate__())

    def test_addProcess(self):
        """
        L{ProcessMonitor.addProcess} only starts the named program if
        L{ProcessMonitor.startService} has been called.
        """
        self.pm.addProcess("foo", ["arg1", "arg2"], uid=1, gid=2, env={})
        self.assertEqual(self.pm.protocols, {})
        self.assertEqual(self.pm.processes,
                         {"foo": (["arg1", "arg2"], 1, 2, {})})
        self.pm.startService()
        self.reactor.advance(0)
        self.assertEqual(list(self.pm.protocols.keys()), ["foo"])

    def test_addProcessDuplicateKeyError(self):
        """
        L{ProcessMonitor.addProcess} raises a C{KeyError} if a process with the
        given name already exists.
        """
        self.pm.addProcess("foo", ["arg1", "arg2"], uid=1, gid=2, env={})
        self.assertRaises(KeyError,
                          self.pm.addProcess,
                          "foo", ["arg1", "arg2"],
                          uid=1,
                          gid=2,
                          env={})

    def test_addProcessEnv(self):
        """
        L{ProcessMonitor.addProcess} takes an C{env} parameter that is passed to
        L{IReactorProcess.spawnProcess}.
        """
        fakeEnv = {"KEY": "value"}
        self.pm.startService()
        self.pm.addProcess("foo", ["foo"], uid=1, gid=2, env=fakeEnv)
        self.reactor.advance(0)
        self.assertEqual(self.reactor.spawnedProcesses[0]._environment,
                         fakeEnv)

    def test_addProcessCwd(self):
        """
        L{ProcessMonitor.addProcess} takes an C{cwd} parameter that is passed
        to L{IReactorProcess.spawnProcess}.
        """
        self.pm.startService()
        self.pm.addProcess("foo", ["foo"], cwd="/mnt/lala")
        self.reactor.advance(0)
        self.assertEqual(self.reactor.spawnedProcesses[0]._path, "/mnt/lala")

    def test_removeProcess(self):
        """
        L{ProcessMonitor.removeProcess} removes the process from the public
        processes list.
        """
        self.pm.startService()
        self.pm.addProcess("foo", ["foo"])
        self.assertEqual(len(self.pm.processes), 1)
        self.pm.removeProcess("foo")
        self.assertEqual(len(self.pm.processes), 0)

    def test_removeProcessUnknownKeyError(self):
        """
        L{ProcessMonitor.removeProcess} raises a C{KeyError} if the given
        process name isn't recognised.
        """
        self.pm.startService()
        self.assertRaises(KeyError, self.pm.removeProcess, "foo")

    def test_startProcess(self):
        """
        When a process has been started, an instance of L{LoggingProtocol} will
        be added to the L{ProcessMonitor.protocols} dict and the start time of
        the process will be recorded in the L{ProcessMonitor.timeStarted}
        dictionary.
        """
        self.pm.addProcess("foo", ["foo"])
        self.pm.startProcess("foo")
        self.assertIsInstance(self.pm.protocols["foo"], LoggingProtocol)
        self.assertIn("foo", self.pm.timeStarted.keys())

    def test_startProcessAlreadyStarted(self):
        """
        L{ProcessMonitor.startProcess} silently returns if the named process is
        already started.
        """
        self.pm.addProcess("foo", ["foo"])
        self.pm.startProcess("foo")
        self.assertIsNone(self.pm.startProcess("foo"))

    def test_startProcessUnknownKeyError(self):
        """
        L{ProcessMonitor.startProcess} raises a C{KeyError} if the given
        process name isn't recognised.
        """
        self.assertRaises(KeyError, self.pm.startProcess, "foo")

    def test_stopProcessNaturalTermination(self):
        """
        L{ProcessMonitor.stopProcess} immediately sends a TERM signal to the
        named process.
        """
        self.pm.startService()
        self.pm.addProcess("foo", ["foo"])
        self.assertIn("foo", self.pm.protocols)

        # Configure fake process to die 1 second after receiving term signal
        timeToDie = self.pm.protocols["foo"].transport._terminationDelay = 1

        # Advance the reactor to just before the short lived process threshold
        # and leave enough time for the process to die
        self.reactor.advance(self.pm.threshold)
        # Then signal the process to stop
        self.pm.stopProcess("foo")

        # Advance the reactor just enough to give the process time to die and
        # verify that the process restarts
        self.reactor.advance(timeToDie)

        # We expect it to be restarted immediately
        self.assertEqual(self.reactor.seconds(), self.pm.timeStarted["foo"])

    def test_stopProcessForcedKill(self):
        """
        L{ProcessMonitor.stopProcess} kills a process which fails to terminate
        naturally within L{ProcessMonitor.killTime} seconds.
        """
        self.pm.startService()
        self.pm.addProcess("foo", ["foo"])
        self.assertIn("foo", self.pm.protocols)
        self.reactor.advance(self.pm.threshold)
        proc = self.pm.protocols["foo"].transport
        # Arrange for the fake process to live longer than the killTime
        proc._terminationDelay = self.pm.killTime + 1
        self.pm.stopProcess("foo")
        # If process doesn't die before the killTime, procmon should
        # terminate it
        self.reactor.advance(self.pm.killTime - 1)
        self.assertEqual(0.0, self.pm.timeStarted["foo"])

        self.reactor.advance(1)
        # We expect it to be immediately restarted
        self.assertEqual(self.reactor.seconds(), self.pm.timeStarted["foo"])

    def test_stopProcessUnknownKeyError(self):
        """
        L{ProcessMonitor.stopProcess} raises a C{KeyError} if the given process
        name isn't recognised.
        """
        self.assertRaises(KeyError, self.pm.stopProcess, "foo")

    def test_stopProcessAlreadyStopped(self):
        """
        L{ProcessMonitor.stopProcess} silently returns if the named process
        is already stopped. eg Process has crashed and a restart has been
        rescheduled, but in the meantime, the service is stopped.
        """
        self.pm.addProcess("foo", ["foo"])
        self.assertIsNone(self.pm.stopProcess("foo"))

    def test_outputReceivedCompleteLine(self):
        """
        Getting a complete output line on stdout generates a log message.
        """
        events = []
        self.addCleanup(globalLogPublisher.removeObserver, events.append)
        globalLogPublisher.addObserver(events.append)
        self.pm.addProcess("foo", ["foo"])
        # Schedule the process to start
        self.pm.startService()
        # Advance the reactor to start the process
        self.reactor.advance(0)
        self.assertIn("foo", self.pm.protocols)
        # Long time passes
        self.reactor.advance(self.pm.threshold)
        # Process greets
        self.pm.protocols["foo"].outReceived(b"hello world!\n")
        self.assertEquals(len(events), 1)
        namespace = events[0]["log_namespace"]
        stream = events[0]["stream"]
        tag = events[0]["tag"]
        line = events[0]["line"]
        self.assertEquals(namespace, "twisted.runner.procmon.ProcessMonitor")
        self.assertEquals(stream, "stdout")
        self.assertEquals(tag, "foo")
        self.assertEquals(line, "hello world!")

    def test_ouputReceivedCompleteErrLine(self):
        """
        Getting a complete output line on stderr generates a log message.
        """
        events = []
        self.addCleanup(globalLogPublisher.removeObserver, events.append)
        globalLogPublisher.addObserver(events.append)
        self.pm.addProcess("foo", ["foo"])
        # Schedule the process to start
        self.pm.startService()
        # Advance the reactor to start the process
        self.reactor.advance(0)
        self.assertIn("foo", self.pm.protocols)
        # Long time passes
        self.reactor.advance(self.pm.threshold)
        # Process greets
        self.pm.protocols["foo"].errReceived(b"hello world!\n")
        self.assertEquals(len(events), 1)
        namespace = events[0]["log_namespace"]
        stream = events[0]["stream"]
        tag = events[0]["tag"]
        line = events[0]["line"]
        self.assertEquals(namespace, "twisted.runner.procmon.ProcessMonitor")
        self.assertEquals(stream, "stderr")
        self.assertEquals(tag, "foo")
        self.assertEquals(line, "hello world!")

    def test_outputReceivedCompleteLineInvalidUTF8(self):
        """
        Getting invalid UTF-8 results in the repr of the raw message
        """
        events = []
        self.addCleanup(globalLogPublisher.removeObserver, events.append)
        globalLogPublisher.addObserver(events.append)
        self.pm.addProcess("foo", ["foo"])
        # Schedule the process to start
        self.pm.startService()
        # Advance the reactor to start the process
        self.reactor.advance(0)
        self.assertIn("foo", self.pm.protocols)
        # Long time passes
        self.reactor.advance(self.pm.threshold)
        # Process greets
        self.pm.protocols["foo"].outReceived(b"\xffhello world!\n")
        self.assertEquals(len(events), 1)
        message = events[0]
        namespace = message["log_namespace"]
        stream = message["stream"]
        tag = message["tag"]
        output = message["line"]
        self.assertEquals(namespace, "twisted.runner.procmon.ProcessMonitor")
        self.assertEquals(stream, "stdout")
        self.assertEquals(tag, "foo")
        self.assertEquals(output, repr(b"\xffhello world!"))

    def test_outputReceivedPartialLine(self):
        """
        Getting partial line results in no events until process end
        """
        events = []
        self.addCleanup(globalLogPublisher.removeObserver, events.append)
        globalLogPublisher.addObserver(events.append)
        self.pm.addProcess("foo", ["foo"])
        # Schedule the process to start
        self.pm.startService()
        # Advance the reactor to start the process
        self.reactor.advance(0)
        self.assertIn("foo", self.pm.protocols)
        # Long time passes
        self.reactor.advance(self.pm.threshold)
        # Process greets
        self.pm.protocols["foo"].outReceived(b"hello world!")
        self.assertEquals(len(events), 0)
        self.pm.protocols["foo"].processEnded(Failure(ProcessDone(0)))
        self.assertEquals(len(events), 1)
        namespace = events[0]["log_namespace"]
        stream = events[0]["stream"]
        tag = events[0]["tag"]
        line = events[0]["line"]
        self.assertEquals(namespace, "twisted.runner.procmon.ProcessMonitor")
        self.assertEquals(stream, "stdout")
        self.assertEquals(tag, "foo")
        self.assertEquals(line, "hello world!")

    def test_connectionLostLongLivedProcess(self):
        """
        L{ProcessMonitor.connectionLost} should immediately restart a process
        if it has been running longer than L{ProcessMonitor.threshold} seconds.
        """
        self.pm.addProcess("foo", ["foo"])
        # Schedule the process to start
        self.pm.startService()
        # advance the reactor to start the process
        self.reactor.advance(0)
        self.assertIn("foo", self.pm.protocols)
        # Long time passes
        self.reactor.advance(self.pm.threshold)
        # Process dies after threshold
        self.pm.protocols["foo"].processEnded(Failure(ProcessDone(0)))
        self.assertNotIn("foo", self.pm.protocols)
        # Process should be restarted immediately
        self.reactor.advance(0)
        self.assertIn("foo", self.pm.protocols)

    def test_connectionLostMurderCancel(self):
        """
        L{ProcessMonitor.connectionLost} cancels a scheduled process killer and
        deletes the DelayedCall from the L{ProcessMonitor.murder} list.
        """
        self.pm.addProcess("foo", ["foo"])
        # Schedule the process to start
        self.pm.startService()
        # Advance 1s to start the process then ask ProcMon to stop it
        self.reactor.advance(1)
        self.pm.stopProcess("foo")
        # A process killer has been scheduled, delayedCall is active
        self.assertIn("foo", self.pm.murder)
        delayedCall = self.pm.murder["foo"]
        self.assertTrue(delayedCall.active())
        # Advance to the point at which the dummy process exits
        self.reactor.advance(
            self.pm.protocols["foo"].transport._terminationDelay)
        # Now the delayedCall has been cancelled and deleted
        self.assertFalse(delayedCall.active())
        self.assertNotIn("foo", self.pm.murder)

    def test_connectionLostProtocolDeletion(self):
        """
        L{ProcessMonitor.connectionLost} removes the corresponding
        ProcessProtocol instance from the L{ProcessMonitor.protocols} list.
        """
        self.pm.startService()
        self.pm.addProcess("foo", ["foo"])
        self.assertIn("foo", self.pm.protocols)
        self.pm.protocols["foo"].transport.signalProcess("KILL")
        self.reactor.advance(
            self.pm.protocols["foo"].transport._terminationDelay)
        self.assertNotIn("foo", self.pm.protocols)

    def test_connectionLostMinMaxRestartDelay(self):
        """
        L{ProcessMonitor.connectionLost} will wait at least minRestartDelay s
        and at most maxRestartDelay s
        """
        self.pm.minRestartDelay = 2
        self.pm.maxRestartDelay = 3

        self.pm.startService()
        self.pm.addProcess("foo", ["foo"])

        self.assertEqual(self.pm.delay["foo"], self.pm.minRestartDelay)
        self.reactor.advance(self.pm.threshold - 1)
        self.pm.protocols["foo"].processEnded(Failure(ProcessDone(0)))
        self.assertEqual(self.pm.delay["foo"], self.pm.maxRestartDelay)

    def test_connectionLostBackoffDelayDoubles(self):
        """
        L{ProcessMonitor.connectionLost} doubles the restart delay each time
        the process dies too quickly.
        """
        self.pm.startService()
        self.pm.addProcess("foo", ["foo"])
        self.reactor.advance(self.pm.threshold - 1)  # 9s
        self.assertIn("foo", self.pm.protocols)
        self.assertEqual(self.pm.delay["foo"], self.pm.minRestartDelay)
        # process dies within the threshold and should not restart immediately
        self.pm.protocols["foo"].processEnded(Failure(ProcessDone(0)))
        self.assertEqual(self.pm.delay["foo"], self.pm.minRestartDelay * 2)

    def test_startService(self):
        """
        L{ProcessMonitor.startService} starts all monitored processes.
        """
        self.pm.addProcess("foo", ["foo"])
        # Schedule the process to start
        self.pm.startService()
        # advance the reactor to start the process
        self.reactor.advance(0)
        self.assertIn("foo", self.pm.protocols)

    def test_stopService(self):
        """
        L{ProcessMonitor.stopService} should stop all monitored processes.
        """
        self.pm.addProcess("foo", ["foo"])
        self.pm.addProcess("bar", ["bar"])
        # Schedule the process to start
        self.pm.startService()
        # advance the reactor to start the processes
        self.reactor.advance(self.pm.threshold)
        self.assertIn("foo", self.pm.protocols)
        self.assertIn("bar", self.pm.protocols)

        self.reactor.advance(1)

        self.pm.stopService()
        # Advance to beyond the killTime - all monitored processes
        # should have exited
        self.reactor.advance(self.pm.killTime + 1)
        # The processes shouldn't be restarted
        self.assertEqual({}, self.pm.protocols)

    def test_restartAllRestartsOneProcess(self):
        """
        L{ProcessMonitor.restartAll} succeeds when there is one process.
        """
        self.pm.addProcess("foo", ["foo"])
        self.pm.startService()
        self.reactor.advance(1)
        self.pm.restartAll()
        # Just enough time for the process to die,
        # not enough time to start a new one.
        self.reactor.advance(1)
        processes = list(self.reactor.spawnedProcesses)
        myProcess = processes.pop()
        self.assertEquals(processes, [])
        self.assertIsNone(myProcess.pid)

    def test_stopServiceCancelRestarts(self):
        """
        L{ProcessMonitor.stopService} should cancel any scheduled process
        restarts.
        """
        self.pm.addProcess("foo", ["foo"])
        # Schedule the process to start
        self.pm.startService()
        # advance the reactor to start the processes
        self.reactor.advance(self.pm.threshold)
        self.assertIn("foo", self.pm.protocols)

        self.reactor.advance(1)
        # Kill the process early
        self.pm.protocols["foo"].processEnded(Failure(ProcessDone(0)))
        self.assertTrue(self.pm.restart["foo"].active())
        self.pm.stopService()
        # Scheduled restart should have been cancelled
        self.assertFalse(self.pm.restart["foo"].active())

    def test_stopServiceCleanupScheduledRestarts(self):
        """
        L{ProcessMonitor.stopService} should cancel all scheduled process
        restarts.
        """
        self.pm.threshold = 5
        self.pm.minRestartDelay = 5
        # Start service and add a process (started immediately)
        self.pm.startService()
        self.pm.addProcess("foo", ["foo"])
        # Stop the process after 1s
        self.reactor.advance(1)
        self.pm.stopProcess("foo")
        # Wait 1s for it to exit it will be scheduled to restart 5s later
        self.reactor.advance(1)
        # Meanwhile stop the service
        self.pm.stopService()
        # Advance to beyond the process restart time
        self.reactor.advance(6)
        # The process shouldn't have restarted because stopService has cancelled
        # all pending process restarts.
        self.assertEqual(self.pm.protocols, {})