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)
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()