Example #1
0
    def initiate(self, messaging, database, journaler):
        '''
        Asynchronous part of agency initialization. Needs to be called before
        agency is used for anything.
        '''
        self._hostname = unicode(socket.gethostbyaddr(socket.gethostname())[0])
        self._ip = unicode(socket.gethostbyname(socket.gethostname()))
        self._database = IDbConnectionFactory(database)
        self._messaging = IConnectionFactory(messaging)
        self._journaler = IJournaler(journaler)
        self._jourconn = self._journaler.get_connection(self)
        self.redirect_log(self._jourconn)

        self._messaging.add_disconnected_cb(self._on_disconnected)
        self._messaging.add_reconnected_cb(self._check_msg_and_db_state)
        self._database.add_disconnected_cb(self._on_disconnected)
        self._database.add_reconnected_cb(self._check_msg_and_db_state)
        self._check_msg_and_db_state()
        return defer.succeed(self)
Example #2
0
class Agency(log.LogProxy, log.Logger, manhole.Manhole,
             dependency.AgencyDependencyMixin, common.ConnectionManager):

    log_category = 'agency'

    implements(IAgency, IExternalizer, ITimeProvider)

    agency_agent_factory = AgencyAgent

    _error_handler = error_handler

    def __init__(self):
        log.LogProxy.__init__(self, log.FluLogKeeper())
        log.Logger.__init__(self, self)
        dependency.AgencyDependencyMixin.__init__(self, ExecMode.test)
        common.ConnectionManager.__init__(self)

        self._agents = []

        self.registry = weakref.WeakValueDictionary()
        # IJournaler
        self._journaler = None
        # IJournalerConnection
        self._jourconn = None
        # IDbConnectionFactory
        self._database = None
        # IConnectionFactory
        self._messaging = None
        self._hostname = None

        self._agency_id = str(uuid.uuid1())
        self.log_name = self._agency_id

        self.add_reconnected_cb(self._notify_agents_about_reconnection)
        self.add_disconnected_cb(self._notify_agents_about_disconnection)

    ### Public Methods ###

    def initiate(self, messaging, database, journaler):
        '''
        Asynchronous part of agency initialization. Needs to be called before
        agency is used for anything.
        '''
        self._hostname = unicode(socket.gethostbyaddr(socket.gethostname())[0])
        self._ip = unicode(socket.gethostbyname(socket.gethostname()))
        self._database = IDbConnectionFactory(database)
        self._messaging = IConnectionFactory(messaging)
        self._journaler = IJournaler(journaler)
        self._jourconn = self._journaler.get_connection(self)
        self.redirect_log(self._jourconn)

        self._messaging.add_disconnected_cb(self._on_disconnected)
        self._messaging.add_reconnected_cb(self._check_msg_and_db_state)
        self._database.add_disconnected_cb(self._on_disconnected)
        self._database.add_reconnected_cb(self._check_msg_and_db_state)
        self._check_msg_and_db_state()
        return defer.succeed(self)

    @property
    def agency_id(self):
        return self._agency_id

    def iter_agents(self):
        return iter(self._agents)

    def get_agency(self, agency_id):
        if agency_id == self.agency_id:
            return self
        return None

    def get_agent(self, agent_id):
        for agent in self._agents:
            if agent.get_agent_id() == agent_id:
                return agent
        return None

    @manhole.expose()
    def start_agent(self, descriptor, **kwargs):
        factory = IAgentFactory(registry_lookup(descriptor.document_type))
        self.log('I will start: %r agent. Kwargs: %r', factory, kwargs)
        medium = self.agency_agent_factory(self, factory, descriptor)
        self.register_agent(medium)

        d = self.wait_connected()
        d.addCallback(defer.drop_param, medium.initiate, **kwargs)
        return d

    @manhole.expose()
    def get_hostname(self):
        return self._hostname

    @manhole.expose()
    def get_ip(self):
        return self._ip

    @manhole.expose()
    def get_logging_filter(self):
        return log.FluLogKeeper.get_debug()

    @manhole.expose()
    def set_logging_filter(self, filter):
        log.FluLogKeeper.set_debug(filter)

    def shutdown(self):
        '''Called when the agency is ordered to shutdown all the agents..'''
        d = defer.DeferredList([x._terminate() for x in self._agents])
        d.addBoth(defer.drop_param, self._messaging.disconnect)
        return d

    def upgrade(self, upgrade_cmd):
        '''Called as the result of upgrade process triggered by host agent.'''
        return self.shutdown()

    def on_killed(self):
        '''Called when the agency process is terminating. (SIGTERM)'''
        d = defer.DeferredList([x.on_killed() for x in self._agents])
        d.addBoth(defer.drop_param, self._messaging.disconnect)
        return d

    def register_agent(self, medium):
        self._agents.append(medium)

    def unregister_agent(self, medium):
        agent_id = medium.get_descriptor().doc_id
        self.debug('Unregistering agent id: %r', agent_id)
        self._agents.remove(medium)

        # FIXME: This shouldn't be necessary! Here we are manually getting
        # rid of things which should just be garbage collected (self.registry
        # is a WeekRefDict). It doesn't happpen supposingly
        self.remove_agent_recorders(agent_id)

    def remove_agent_recorders(self, agent_id):
        for key in self.registry.keys():
            if key[0] == agent_id:
                try:
                    self.log("Removing recorder id %r, instance: %r",
                             key, self.registry[key])
                    del(self.registry[key])
                except KeyError:
                    self.debug("Removing recorder id %r failed. It seems it "
                               "dissapeared from WeakRefDict between "
                               "obtaining the list of keys and iterating "
                               "over it.", key)

    def is_idle(self):
        return all([x.is_idle() for x in self._agents])

    ### Journaling Methods ###

    def register(self, recorder):
        j_id = recorder.journal_id
        self.log('Registering recorder: %r, id: %r',
                 recorder.__class__.__name__, j_id)
        if j_id in self.registry:
            raise RuntimeError(
                'Journal id %r already in registry, it points to %r object'
                % (j_id, self.registry[j_id], ))
        self.registry[j_id] = recorder

    def journal_new_entry(self, agent_id, instance_id, journal_id,
                          function_id, *args, **kwargs):
        return self._jourconn.new_entry(agent_id, instance_id, journal_id,
                                         function_id, *args, **kwargs)

    def journal_agency_entry(self, agent_id, instance_id, function_id,
                             *args, **kwargs):
        if journal.add_effect(function_id, *args, **kwargs):
            return

        section = fiber.WovenSection()
        section.enter()

        try:

            desc = section.descriptor
            entry = self.journal_new_entry(agent_id, instance_id, 'agency',
                                           function_id, *args, **kwargs)
            entry.set_fiber_context(desc.fiber_id, desc.fiber_depth)
            entry.set_result(None)
            entry.commit()

        finally:

            section.abort()

    def journal_protocol_created(self, agent_id, instance_id, protocol_factory,
                                 medium, *args, **kwargs):
        self.journal_agency_entry(agent_id, instance_id, 'protocol_created',
                                  protocol_factory, medium, *args, **kwargs)

    def journal_protocol_deleted(self, agent_id, instance_id,
                                 protocol_instance, dummy_id):
        self.journal_agency_entry(agent_id, instance_id, 'protocol_deleted',
                                  protocol_instance.journal_id, dummy_id)

    def journal_agent_created(self, agent_id, instance_id, agent_factory,
                              dummy_id):
        self.journal_agency_entry(agent_id, instance_id, 'agent_created',
                                  agent_factory, dummy_id)

    def journal_agent_deleted(self, agent_id, instance_id):
        self.journal_agency_entry(agent_id, instance_id, 'agent_deleted')

    def journal_agent_snapshot(self, agent_id, instance_id, snapshot):
        self.log("Storing agents snapshot. Agent_id: %r, Instance_id: %r.",
                 agent_id, instance_id)
        self._jourconn.snapshot(agent_id, instance_id, snapshot)

    ### IExternalizer Methods ###

    def identify(self, instance):
        if (IRecorder.providedBy(instance) and
            instance.journal_id in self.registry):
            return instance.journal_id

    def lookup(self, _):
        raise RuntimeError("OOPS, this should never be called "
                           "in production code!!")

    ### ITimeProvider Methods ###

    @serialization.freeze_tag('Agency.get_time')
    @replay.named_side_effect('Agency.get_time')
    def get_time(self):
        return time.time()

    ### Introspection and Manhole Methods ###

    @manhole.expose()
    def find_agent(self, desc):
        '''find_agent(agent_id_or_descriptor) -> Gives medium class of the
        agent if the agency hosts it.'''
        agent_id = (desc.doc_id
                    if isinstance(desc, descriptor.Descriptor)
                    else desc)
        self.log("I'm trying to find the agent with id: %s", agent_id)
        result = first(x for x in self._agents
                       if x._descriptor.doc_id == agent_id)
        return defer.succeed(result)

    @manhole.expose()
    def snapshot_agents(self, force=False):
        '''snapshot_agents(force=False): snapshot agents if number of entries
        from last snapshot if greater than 1000. Use force=True to override.'''
        for agent in self._agents:
            agent.check_if_should_snapshot(force)

    @manhole.expose()
    def list_agents(self):
        '''list_agents() -> List agents hosted by the agency.'''
        t = text_helper.Table(fields=("Agent ID", "Agent class", "State"),
                              lengths=(40, 25, 15))

        return t.render((a._descriptor.doc_id, a.log_category,
                         a._get_machine_state().name)
                        for a in self._agents)

    @manhole.expose()
    def get_nth_agent(self, n):
        '''get_nth_agent(n) -> Get the agent by his index in the list.'''
        return self._agents[n]

    @manhole.expose()
    def get_agents(self):
        '''get_agents() -> Get the list of agents hosted by this agency.'''
        return self._agents

    ### private ###

    def _notify_agents_about_disconnection(self):
        for medium in self.iter_agents():
            medium.on_disconnect()

    def _notify_agents_about_reconnection(self):
        for medium in self.iter_agents():
            medium.on_reconnect()

    def _check_msg_and_db_state(self):
        all_connected = self._messaging.is_connected() and \
                        self._database.is_connected()
        if not all_connected:
            self._on_disconnected()
        else:
            self._on_connected()