Example #1
0
 def test_get_loop(self):
     """ Test the get_loop method. """
     from supvisors.mainloop import SupvisorsMainLoop
     main_loop = SupvisorsMainLoop(self.supvisors)
     self.assertFalse(main_loop.get_loop())
     main_loop.loop = True
     self.assertTrue(main_loop.get_loop())
Example #2
0
 def test_stopping(self):
     """ Test the get_loop method. """
     from supvisors.mainloop import SupvisorsMainLoop
     main_loop = SupvisorsMainLoop(self.supvisors)
     self.assertFalse(main_loop.stopping())
     main_loop.stop_event.set()
     self.assertTrue(main_loop.stopping())
Example #3
0
 def on_running(self, event):
     """ Called when Supervisor is RUNNING.
     This method start the Supvisors main loop. """
     self.logger.info('local supervisord is RUNNING')
     # replace the default handler for web ui
     self.info_source.replace_default_handler()
     # create zmq sockets
     self.supvisors.zmq = SupvisorsZmq(self.supvisors)
     # keep a reference to the internal events publisher
     self.publisher = self.supvisors.zmq.internal_publisher
     # start the main loop
     self.main_loop = SupvisorsMainLoop(self.supvisors)
     self.main_loop.start()
Example #4
0
 def test_check_events(self, mocked_send, mocked_stderr):
     """ Test the processing of the events received. """
     from supvisors.mainloop import SupvisorsMainLoop
     main_loop = SupvisorsMainLoop(self.supvisors)
     # test with empty socks
     mocked_subscriber = Mock(socket='zmq socket')
     socks = {}
     main_loop.check_events(mocked_subscriber, socks)
     self.assertEqual(0, mocked_subscriber.call_count)
     self.assertEqual(0, mocked_send.call_count)
     # test with appropriate socks but with exception
     mocked_subscriber = Mock(socket='zmq socket',
                              **{'receive.side_effect': Exception})
     socks = {'zmq socket': 1}
     main_loop.check_events(mocked_subscriber, socks)
     self.assertEqual(1, mocked_subscriber.receive.call_count)
     self.assertEqual(0, mocked_send.call_count)
     mocked_subscriber.receive.reset_mock()
     # test with appropriate socks and without exception
     mocked_subscriber = Mock(socket='zmq socket',
                              **{'receive.return_value': 'a zmq message'})
     main_loop.check_events(mocked_subscriber, socks)
     self.assertEqual(1, mocked_subscriber.receive.call_count)
     self.assertEqual([call('event', '"a zmq message"')],
                      mocked_send.call_args_list)
Example #5
0
 def test_send_request(self):
     """ Test the execution of a deferred Supervisor request. """
     from supvisors.mainloop import SupvisorsMainLoop
     from supvisors.utils import DeferredRequestHeaders
     main_loop = SupvisorsMainLoop(self.supvisors)
     # patch main loop subscriber
     with patch.multiple(main_loop,
                         check_address=DEFAULT,
                         start_process=DEFAULT,
                         stop_process=DEFAULT,
                         restart=DEFAULT,
                         shutdown=DEFAULT) as mocked_loop:
         # test check address
         self.check_call(main_loop, mocked_loop, 'check_address',
                         DeferredRequestHeaders.CHECK_ADDRESS,
                         ('10.0.0.2', ))
         # test isolate addresses
         self.check_call(main_loop, mocked_loop, '',
                         DeferredRequestHeaders.ISOLATE_ADDRESSES,
                         ('10.0.0.2', '10.0.0.3'))
         # test start process
         self.check_call(main_loop, mocked_loop, 'start_process',
                         DeferredRequestHeaders.START_PROCESS,
                         ('10.0.0.2', 'dummy_process', 'extra args'))
         # test stop process
         self.check_call(main_loop, mocked_loop, 'stop_process',
                         DeferredRequestHeaders.STOP_PROCESS,
                         ('10.0.0.2', 'dummy_process'))
         # test restart
         self.check_call(main_loop, mocked_loop, 'restart',
                         DeferredRequestHeaders.RESTART, ('10.0.0.2', ))
         # test shutdown
         self.check_call(main_loop, mocked_loop, 'shutdown',
                         DeferredRequestHeaders.SHUTDOWN, ('10.0.0.2', ))
Example #6
0
 def test_run(self, check_evt, check_rqt, register, unregister, poll):
     """ Test the running of the main loop thread. """
     from supvisors.mainloop import SupvisorsMainLoop
     main_loop = SupvisorsMainLoop(self.supvisors)
     # patch one loops
     with patch.object(main_loop,
                       'stopping',
                       side_effect=[False, False, True]):
         main_loop.run()
     # test that register was called twice
     self.assertEqual(2, register.call_count)
     # test that poll was called once
     self.assertEqual([call(500)], poll.call_args_list)
     # test that check_events was called once
     self.assertEqual(1, check_evt.call_count)
     # test that check_requests was called once
     self.assertEqual(1, check_rqt.call_count)
     # test that unregister was called twice
     self.assertEqual(2, unregister.call_count)
Example #7
0
 def test_run(self, register, unregister, poll):
     """ Test the running of the main loop thread. """
     from supvisors.mainloop import SupvisorsMainLoop
     main_loop = SupvisorsMainLoop(self.supvisors)
     # configure patches
     main_loop.subscriber.receive.side_effect = [Exception, 'subscription']
     main_loop.puller.receive.side_effect = [Exception, ('pull', 'data')]
     # patch 4 loops
     with patch.object(main_loop,
                       'get_loop',
                       side_effect=[True] * 4 + [False]):
         # patch zmq calls: 2 loops for subscriber, 2 loops for puller
         effects = [{
             main_loop.subscriber.socket: 1
         }] * 2 + [{
             main_loop.puller.socket: 1
         }] * 2
         poll.side_effect = effects
         with patch.multiple(main_loop,
                             send_remote_comm_event=DEFAULT,
                             send_request=DEFAULT) as mocked_loop:
             main_loop.run()
             # test that poll was called 4 times
             self.assertEqual([call(500)] * 4, poll.call_args_list)
             # test that register was called twice
             self.assertEqual([
                 call(main_loop.subscriber.socket, 1),
                 call(main_loop.puller.socket, 1)
             ], register.call_args_list)
             # test that unregister was called twice
             self.assertEqual([
                 call(main_loop.puller.socket),
                 call(main_loop.subscriber.socket)
             ], unregister.call_args_list)
             # test that send_remote_comm_event was called once
             self.assertEqual(
                 [call(u'event', '"subscription"')],
                 mocked_loop['send_remote_comm_event'].call_args_list)
             # test that send_request was called once
             self.assertEqual([call('pull', 'data')],
                              mocked_loop['send_request'].call_args_list)
Example #8
0
 def test_check_requests(self, mocked_send, mocked_stderr):
     """ Test the processing of the requests received. """
     from supvisors.mainloop import SupvisorsMainLoop
     main_loop = SupvisorsMainLoop(self.supvisors)
     # mock parameters
     mocked_sockets = Mock(
         puller=Mock(socket='zmq socket',
                     **{'receive.side_effect': Exception}),
         internal_subscriber=Mock(**{'disconnect.return_value': None}))
     mocked_receive = mocked_sockets.puller.receive
     mocked_disconnect = mocked_sockets.internal_subscriber.disconnect
     # test with empty socks
     socks = {}
     main_loop.check_requests(mocked_sockets, socks)
     self.assertEqual(0, mocked_receive.call_count)
     self.assertEqual(0, mocked_send.call_count)
     self.assertEqual(0, mocked_disconnect.call_count)
     # test with appropriate socks but with exception
     socks = {'zmq socket': 1}
     main_loop.check_requests(mocked_sockets, socks)
     self.assertEqual(1, mocked_receive.call_count)
     self.assertEqual(0, mocked_send.call_count)
     self.assertEqual(0, mocked_disconnect.call_count)
     mocked_receive.reset_mock()
     # test with appropriate socks and without exception
     mocked_receive.side_effect = None
     mocked_receive.return_value = ('a zmq header', 'a zmq message')
     main_loop.check_requests(mocked_sockets, socks)
     self.assertEqual([call('a zmq header', 'a zmq message')],
                      mocked_send.call_args_list)
     self.assertEqual(0, mocked_disconnect.call_count)
     mocked_receive.reset_mock()
     mocked_send.reset_mock()
     # test disconnection request
     mocked_receive.return_value = (1, 'an address')
     main_loop.check_requests(mocked_sockets, socks)
     self.assertEqual(1, mocked_receive.call_count)
     self.assertEqual([call('an address')],
                      mocked_disconnect.call_args_list)
     self.assertEqual(0, mocked_send.call_count)
Example #9
0
 def on_running(self, event):
     """ Called when Supervisor is RUNNING.
     This method start the Supvisors main loop. """
     self.logger.info('local supervisord is RUNNING')
     # replace the default handler for web ui
     self.info_source.replace_default_handler()
     # create zmq sockets
     self.supvisors.zmq = SupervisorZmq(self.supvisors)
     # keep a reference to the internal events publisher
     self.publisher = self.supvisors.zmq.internal_publisher
     # start the main loop
     # env is needed to create XML-RPC proxy
     self.main_loop = SupvisorsMainLoop(self.supvisors)
     self.main_loop.start()
Example #10
0
 def test_stop(self):
     """ Test the stopping of the main loop thread. """
     from supvisors.mainloop import SupvisorsMainLoop
     main_loop = SupvisorsMainLoop(self.supvisors)
     # try to stop main loop before it is started
     with self.assertRaises(RuntimeError):
         main_loop.stop()
     # stop main loop after it is started
     main_loop.loop = True
     with patch.object(main_loop, 'join') as mocked_join:
         main_loop.stop()
         self.assertFalse(main_loop.loop)
         self.assertEqual(1, mocked_join.call_count)
Example #11
0
 def test_creation(self):
     """ Test the values set at construction. """
     from supvisors.mainloop import SupvisorsMainLoop
     main_loop = SupvisorsMainLoop(self.supvisors)
     self.assertIsInstance(main_loop, Thread)
     self.assertIs(self.supvisors, main_loop.supvisors)
     self.assertFalse(main_loop.stop_event.is_set())
     self.assertDictEqual(
         {
             'SUPERVISOR_SERVER_URL': 'http://127.0.0.1:65000',
             'SUPERVISOR_USERNAME': '',
             'SUPERVISOR_PASSWORD': ''
         }, main_loop.env)
     self.assertEqual(1, self.mocked_rpc.call_count)
     self.assertEqual(call('localhost', main_loop.env),
                      self.mocked_rpc.call_args)
Example #12
0
 def test_stop(self):
     """ Test the stopping of the main loop thread. """
     from supvisors.mainloop import SupvisorsMainLoop
     main_loop = SupvisorsMainLoop(self.supvisors)
     with patch.object(main_loop, 'join') as mocked_join:
         # try to stop main loop before it is started
         main_loop.stop()
         self.assertFalse(main_loop.stop_event.is_set())
         self.assertEqual(0, mocked_join.call_count)
         # stop main loop when alive
         with patch.object(main_loop, 'is_alive', return_value=True):
             main_loop.stop()
             self.assertTrue(main_loop.stop_event.is_set())
             self.assertEqual(1, mocked_join.call_count)
Example #13
0
 def test_comm_event(self):
     """ Test the protocol to send a comm event to the local Supervisor. """
     from supvisors.mainloop import SupvisorsMainLoop
     main_loop = SupvisorsMainLoop(self.supvisors)
     # test rpc error
     with patch.object(main_loop.proxy.supervisor,
                       'sendRemoteCommEvent',
                       side_effect=Exception):
         main_loop.send_remote_comm_event('event type', 'event data')
     # test with a mocked rpc interface
     with patch.object(main_loop.proxy.supervisor,
                       'sendRemoteCommEvent') as mocked_supervisor:
         main_loop.send_remote_comm_event('event type', 'event data')
         self.assertEqual(1, mocked_supervisor.call_count)
         self.assertEqual(call('event type', 'event data'),
                          mocked_supervisor.call_args)
Example #14
0
 def test_shutdown(self):
     """ Test the protocol to shutdown a remote Supervisor. """
     from supvisors.mainloop import SupvisorsMainLoop
     main_loop = SupvisorsMainLoop(self.supvisors)
     # test rpc error
     self.mocked_rpc.side_effect = Exception
     main_loop.shutdown('10.0.0.1')
     self.assertEqual(2, self.mocked_rpc.call_count)
     self.assertEqual(call('10.0.0.1', main_loop.env),
                      self.mocked_rpc.call_args)
     # test with a mocked rpc interface
     rpc_intf = DummyRpcInterface()
     self.mocked_rpc.side_effect = None
     self.mocked_rpc.return_value = rpc_intf
     with patch.object(rpc_intf.supervisor, 'shutdown') as mocked_shutdown:
         main_loop.shutdown('10.0.0.1')
         self.assertEqual(3, self.mocked_rpc.call_count)
         self.assertEqual(call('10.0.0.1', main_loop.env),
                          self.mocked_rpc.call_args)
         self.assertEqual(1, mocked_shutdown.call_count)
         self.assertEqual(call(), mocked_shutdown.call_args)
Example #15
0
 def test_stop_process(self):
     """ Test the protocol to stop a process handled by a remote Supervisor. """
     from supvisors.mainloop import SupvisorsMainLoop
     main_loop = SupvisorsMainLoop(self.supvisors)
     # test rpc error
     self.mocked_rpc.side_effect = Exception
     main_loop.stop_process('10.0.0.1', 'dummy_process')
     self.assertEqual(2, self.mocked_rpc.call_count)
     self.assertEqual(call('10.0.0.1', main_loop.env),
                      self.mocked_rpc.call_args)
     # test with a mocked rpc interface
     rpc_intf = DummyRpcInterface()
     self.mocked_rpc.side_effect = None
     self.mocked_rpc.return_value = rpc_intf
     with patch.object(rpc_intf.supervisor,
                       'stopProcess') as mocked_supervisor:
         main_loop.stop_process('10.0.0.1', 'dummy_process')
         self.assertEqual(3, self.mocked_rpc.call_count)
         self.assertEqual(call('10.0.0.1', main_loop.env),
                          self.mocked_rpc.call_args)
         self.assertEqual(1, mocked_supervisor.call_count)
         self.assertEqual(call('dummy_process', False),
                          mocked_supervisor.call_args)
Example #16
0
 def test_check_address(self):
     """ Test the protocol to get the processes handled by a remote Supervisor. """
     from supvisors.mainloop import SupvisorsMainLoop
     from supvisors.ttypes import AddressStates
     main_loop = SupvisorsMainLoop(self.supvisors)
     # patch the main loop send_remote_comm_event
     # test the check_address behaviour through the calls to internal events
     with patch.object(main_loop, 'send_remote_comm_event') as mocked_evt:
         # test rpc error: no event is sent to local Supervisor
         self.mocked_rpc.side_effect = Exception
         main_loop.check_address('10.0.0.1')
         self.assertEqual(2, self.mocked_rpc.call_count)
         self.assertEqual(call('10.0.0.1', main_loop.env),
                          self.mocked_rpc.call_args)
         self.assertEqual(0, mocked_evt.call_count)
         # test with a mocked rpc interface
         rpc_intf = DummyRpcInterface()
         self.mocked_rpc.side_effect = None
         self.mocked_rpc.return_value = rpc_intf
         self.mocked_rpc.reset_mock()
         # test with address in isolation
         with patch.object(rpc_intf.supervisor,
                           'getAllProcessInfo') as mocked_supervisor:
             for state in [AddressStates.ISOLATING, AddressStates.ISOLATED]:
                 with patch.object(rpc_intf.supvisors,
                                   'get_address_info',
                                   return_value={'statecode': state}):
                     main_loop.check_address('10.0.0.1')
                     self.assertEqual(1, self.mocked_rpc.call_count)
                     self.assertEqual(call('10.0.0.1', main_loop.env),
                                      self.mocked_rpc.call_args)
                     self.assertEqual(1, mocked_evt.call_count)
                     self.assertEqual(
                         call('auth',
                              'address_name:10.0.0.1 authorized:False'),
                         mocked_evt.call_args)
                     self.assertEqual(0, mocked_supervisor.call_count)
                     # reset counters
                     mocked_evt.reset_mock()
                     self.mocked_rpc.reset_mock()
         # test with address not in isolation
         with patch.object(rpc_intf.supervisor,
                           'getAllProcessInfo',
                           return_value=['dummy_list'
                                         ]) as mocked_supervisor:
             for state in [
                     AddressStates.UNKNOWN, AddressStates.CHECKING,
                     AddressStates.RUNNING, AddressStates.SILENT
             ]:
                 with patch.object(rpc_intf.supvisors,
                                   'get_address_info',
                                   return_value={'statecode': state}):
                     main_loop.check_address('10.0.0.1')
                     self.assertEqual(1, self.mocked_rpc.call_count)
                     self.assertEqual(call('10.0.0.1', main_loop.env),
                                      self.mocked_rpc.call_args)
                     self.assertEqual(2, mocked_evt.call_count)
                     self.assertEqual([
                         call('info', '["10.0.0.1", ["dummy_list"]]'),
                         call('auth',
                              'address_name:10.0.0.1 authorized:True')
                     ], mocked_evt.call_args_list)
                     self.assertEqual(1, mocked_supervisor.call_count)
                     # reset counters
                     mocked_evt.reset_mock()
                     mocked_supervisor.reset_mock()
                     self.mocked_rpc.reset_mock()
Example #17
0
 def test_TODO(self):
     """ Test the values set at construction. """
     from supvisors.mainloop import SupvisorsMainLoop
     main_loop = SupvisorsMainLoop(self.supvisors)
     self.assertIsNotNone(main_loop)
Example #18
0
class SupervisorListener(object):
    """ This class subscribes directly to the internal Supervisor events.
    These events are published to all Supvisors instances.

    Attributes are:

        - supvisors: a reference to the Supvisors context,
        - address: the address name where this process is running,
        - main_loop: the Supvisors' event thread,
        - publisher: the ZeroMQ socket used to publish Supervisor events
        to all Supvisors threads.
    """
    def __init__(self, supvisors):
        """ Initialization of the attributes. """
        self.supvisors = supvisors
        # shortcuts for source code readability
        supvisors_short_cuts(self,
                             ['fsm', 'info_source', 'logger', 'statistician'])
        # test if statistics collector can be created for local host
        try:
            from supvisors.statscollector import instant_statistics
            self.collector = instant_statistics
        except ImportError:
            self.logger.warn('psutil not installed')
            self.logger.warn('this Supvisors will not publish statistics')
            self.collector = None
        # other attributes
        self.address = self.supvisors.address_mapper.local_address
        self.publisher = None
        self.main_loop = None
        # subscribe to internal events
        events.subscribe(events.SupervisorRunningEvent, self.on_running)
        events.subscribe(events.SupervisorStoppingEvent, self.on_stopping)
        events.subscribe(events.ProcessStateEvent, self.on_process)
        events.subscribe(events.Tick5Event, self.on_tick)
        events.subscribe(events.RemoteCommunicationEvent, self.on_remote_event)

    def on_running(self, event):
        """ Called when Supervisor is RUNNING.
        This method start the Supvisors main loop. """
        self.logger.info('local supervisord is RUNNING')
        # replace the default handler for web ui
        self.info_source.replace_default_handler()
        # create zmq sockets
        self.supvisors.zmq = SupervisorZmq(self.supvisors)
        # keep a reference to the internal events publisher
        self.publisher = self.supvisors.zmq.internal_publisher
        # start the main loop
        # env is needed to create XML-RPC proxy
        self.main_loop = SupvisorsMainLoop(self.supvisors)
        self.main_loop.start()

    def on_stopping(self, event):
        """ Called when Supervisor is STOPPING.
        This method stops the Supvisors main loop. """
        self.logger.warn('local supervisord is STOPPING')
        # force Supervisor to close HTTP servers
        # this will prevent any pending XML-RPC request to block the main loop
        self.info_source.close_httpservers()
        # stop the main loop
        self.logger.info('request to stop main loop')
        self.main_loop.stop()
        self.logger.info('end of main loop')
        # close zmq sockets
        self.supvisors.zmq.close()
        # unsubscribe from events
        events.clear()
        # finally, close logger
        self.logger.close()

    def on_process(self, event):
        """ Called when a ProcessEvent is sent by the local Supervisor.
        The event is published to all Supvisors instances. """
        event_name = events.getEventNameByType(event.__class__)
        self.logger.debug('got Process event from supervisord: {} {}'.format(
            event_name, event))
        # create payload from event
        payload = {
            'name': event.process.config.name,
            'group': event.process.group.config.name,
            'state': ProcessStates._from_string(event_name.split('_')[-1]),
            'now': int(time.time()),
            'pid': event.process.pid,
            'expected': event.expected
        }
        self.logger.debug('payload={}'.format(payload))
        self.publisher.send_process_event(payload)

    def on_tick(self, event):
        """ Called when a TickEvent is notified.
        The event is published to all Supvisors instances.
        Then statistics are published and periodic task is triggered. """
        self.logger.debug('got Tick event from supervisord: {}'.format(event))
        payload = {'when': event.when}
        self.publisher.send_tick_event(payload)
        # get and publish statistics at tick time (optional)
        if self.collector:
            status = self.supvisors.context.addresses[self.address]
            self.publisher.send_statistics(
                self.collector(status.pid_processes()))
        # periodic task
        addresses = self.fsm.on_timer_event()
        # pushes isolated addresses to main loop
        self.supvisors.zmq.pusher.send_isolate_addresses(addresses)

    def on_remote_event(self, event):
        """ Called when a RemoteCommunicationEvent is notified.
        This is used to sequence the events received from the Supvisors thread
        with the other events handled by the local Supervisor. """
        self.logger.debug(
            'got Remote event from supervisord: {}'.format(event))
        if event.type == RemoteCommEvents.SUPVISORS_AUTH:
            self.authorization(event.data)
        elif event.type == RemoteCommEvents.SUPVISORS_EVENT:
            self.unstack_event(event.data)
        elif event.type == RemoteCommEvents.SUPVISORS_INFO:
            self.unstack_info(event.data)

    def unstack_event(self, message):
        """ Unstack and process one event from the event queue. """
        event_type, event_address, event_data = json.loads(message)
        if event_type == InternalEventHeaders.TICK:
            self.logger.trace('got tick event from {}: {}'.format(
                event_address, event_data))
            self.fsm.on_tick_event(event_address, event_data)
        elif event_type == InternalEventHeaders.PROCESS:
            self.logger.trace('got process event from {}: {}'.format(
                event_address, event_data))
            self.fsm.on_process_event(event_address, event_data)
        elif event_type == InternalEventHeaders.STATISTICS:
            # this Supvisors could handle statistics
            # even if psutil is not installed
            self.logger.trace('got statistics event from {}: {}'.format(
                event_address, event_data))
            self.statistician.push_statistics(event_address, event_data)

    def unstack_info(self, message):
        """ Unstack the process info received. """
        # unstack the queue for process info
        address_name, info = json.loads(message)
        self.logger.trace(
            'got process info event from {}'.format(address_name))
        self.fsm.on_process_info(address_name, info)

    def authorization(self, data):
        """ Extract authorization and address from data and process event. """
        self.logger.trace('got authorization event: {}'.format(data))
        # split the line received
        address_name, authorized = tuple(x.split(':')[1] for x in data.split())
        self.fsm.on_authorization(address_name, boolean(authorized))

    def force_process_fatal(self, namespec):
        """ Publishes a fake process event showing a FATAL state for
        the process. """
        self.force_process_state(namespec, ProcessStates.FATAL)

    def force_process_unknown(self, namespec):
        """ Publishes a fake process event showing an UNKNOWN state for
        the process. """
        self.force_process_state(namespec, ProcessStates.UNKNOWN)

    def force_process_state(self, namespec, state):
        """ Publishes a fake process event showing a state for the process. """
        application_name, process_name = split_namespec(namespec)
        # create payload from event
        payload = {
            'processname': process_name,
            'groupname': application_name,
            'state': state,
            'now': int(time.time()),
            'pid': 0,
            'expected': False
        }
        self.logger.debug('payload={}'.format(payload))
        self.publisher.send_process_event(payload)
Example #19
0
class SupervisorListener(object):
    """ This class subscribes directly to the internal Supervisor events.
    These events are published to all Supvisors instances.

    Attributes are:

        - supvisors: a reference to the Supvisors context,
        - address: the address name where this process is running,
        - main_loop: the Supvisors' event thread,
        - publisher: the ZeroMQ socket used to publish Supervisor events
        to all Supvisors threads.
    """

    def __init__(self, supvisors):
        """ Initialization of the attributes. """
        self.supvisors = supvisors
        # shortcuts for source code readability
        supvisors_short_cuts(self, ['fsm', 'info_source',
                                    'logger', 'statistician'])
        # test if statistics collector can be created for local host
        try:
            from supvisors.statscollector import instant_statistics
            self.collector = instant_statistics
        except ImportError:
            self.logger.warn('psutil not installed')
            self.logger.warn('this Supvisors will not publish statistics')
            self.collector = None
        # other attributes
        self.address = self.supvisors.address_mapper.local_address
        self.publisher = None
        self.main_loop = None
        # subscribe to internal events
        events.subscribe(events.SupervisorRunningEvent, self.on_running)
        events.subscribe(events.SupervisorStoppingEvent, self.on_stopping)
        events.subscribe(events.ProcessStateEvent, self.on_process)
        events.subscribe(events.Tick5Event, self.on_tick)
        events.subscribe(events.RemoteCommunicationEvent, self.on_remote_event)

    def on_running(self, event):
        """ Called when Supervisor is RUNNING.
        This method start the Supvisors main loop. """
        self.logger.info('local supervisord is RUNNING')
        # replace the default handler for web ui
        self.info_source.replace_default_handler()
        # create zmq sockets
        self.supvisors.zmq = SupervisorZmq(self.supvisors)
        # keep a reference to the internal events publisher
        self.publisher = self.supvisors.zmq.internal_publisher
        # start the main loop
        # env is needed to create XML-RPC proxy
        self.main_loop = SupvisorsMainLoop(self.supvisors)
        self.main_loop.start()


    def on_stopping(self, event):
        """ Called when Supervisor is STOPPING.
        This method stops the Supvisors main loop. """
        self.logger.warn('local supervisord is STOPPING')
        # force Supervisor to close HTTP servers
        # this will prevent any pending XML-RPC request to block the main loop
        self.info_source.close_httpservers()
        # stop the main loop
        self.logger.info('request to stop main loop')
        self.main_loop.stop()
        self.logger.info('end of main loop')
        # close zmq sockets
        self.supvisors.zmq.close()
        # unsubscribe from events
        events.clear()
        # finally, close logger
        self.logger.close()


    def on_process(self, event):
        """ Called when a ProcessEvent is sent by the local Supervisor.
        The event is published to all Supvisors instances. """
        event_name = events.getEventNameByType(event.__class__)
        self.logger.debug('got Process event from supervisord: {} {}'.format(
            event_name, event))
        # create payload from event
        payload = {'name': event.process.config.name,
            'group': event.process.group.config.name,
            'state': ProcessStates._from_string(event_name.split('_')[-1]),
            'now': int(time.time()),
            'pid': event.process.pid,
            'expected': event.expected}
        self.logger.debug('payload={}'.format(payload))
        self.publisher.send_process_event(payload)

    def on_tick(self, event):
        """ Called when a TickEvent is notified.
        The event is published to all Supvisors instances.
        Then statistics are published and periodic task is triggered. """
        self.logger.debug('got Tick event from supervisord: {}'.format(event))
        payload = {'when': event.when}
        self.publisher.send_tick_event(payload)
        # get and publish statistics at tick time (optional)
        if self.collector:
            status = self.supvisors.context.addresses[self.address]
            self.publisher.send_statistics(
                self.collector(status.pid_processes()))
        # periodic task
        addresses = self.fsm.on_timer_event()
        # pushes isolated addresses to main loop
        self.supvisors.zmq.pusher.send_isolate_addresses(addresses)

    def on_remote_event(self, event):
        """ Called when a RemoteCommunicationEvent is notified.
        This is used to sequence the events received from the Supvisors thread
        with the other events handled by the local Supervisor. """
        self.logger.debug('got Remote event from supervisord: {}'.format(event))
        if event.type == RemoteCommEvents.SUPVISORS_AUTH:
            self.authorization(event.data)
        elif event.type == RemoteCommEvents.SUPVISORS_EVENT:
            self.unstack_event(event.data)
        elif event.type == RemoteCommEvents.SUPVISORS_INFO:
            self.unstack_info(event.data)

    def unstack_event(self, message):
        """ Unstack and process one event from the event queue. """
        event_type, event_address, event_data = json.loads(message)
        if event_type == InternalEventHeaders.TICK:
            self.logger.trace('got tick event from {}: {}'.format(
                event_address, event_data))
            self.fsm.on_tick_event(event_address, event_data)
        elif event_type == InternalEventHeaders.PROCESS:
            self.logger.trace('got process event from {}: {}'.format(
                event_address, event_data))
            self.fsm.on_process_event(event_address, event_data)
        elif event_type == InternalEventHeaders.STATISTICS:
            # this Supvisors could handle statistics
            # even if psutil is not installed
            self.logger.trace('got statistics event from {}: {}'.format(
                event_address, event_data))
            self.statistician.push_statistics(event_address, event_data)

    def unstack_info(self, message):
        """ Unstack the process info received. """
        # unstack the queue for process info
        address_name, info = json.loads(message)
        self.logger.trace('got process info event from {}'.format(
            address_name))
        self.fsm.on_process_info(address_name, info)

    def authorization(self, data):
        """ Extract authorization and address from data and process event. """
        self.logger.trace('got authorization event: {}'.format(data))
        # split the line received
        address_name, authorized = tuple(x.split(':')[1] for x in data.split())
        self.fsm.on_authorization(address_name, boolean(authorized))

    def force_process_fatal(self, namespec):
        """ Publishes a fake process event showing a FATAL state for
        the process. """
        self.force_process_state(namespec, ProcessStates.FATAL)

    def force_process_unknown(self, namespec):
        """ Publishes a fake process event showing an UNKNOWN state for
        the process. """
        self.force_process_state(namespec, ProcessStates.UNKNOWN)

    def force_process_state(self, namespec, state):
        """ Publishes a fake process event showing a state for the process. """
        application_name, process_name = split_namespec(namespec)
        # create payload from event
        payload = {'processname': process_name,
            'groupname': application_name,
            'state': state,
            'now': int(time.time()),
            'pid': 0,
            'expected': False}
        self.logger.debug('payload={}'.format(payload))
        self.publisher.send_process_event(payload)