def add_process_group(self, config): name = config.name if name not in self.process_groups: config.after_setuid() self.process_groups[name] = config.make_group() events.notify(events.ProcessGroupAddedEvent(name)) return True return False
def tick(self, now=None): """ Send one or more 'tick' events when the timeslice related to the period for the event type rolls over """ if now is None: # now won't be None in unit tests now = time.time() for event in events.TICK_EVENTS: period = event.period last_tick = self.ticks.get(period) if last_tick is None: # we just started up last_tick = self.ticks[period] = timeslice(period, now) this_tick = timeslice(period, now) if this_tick != last_tick: self.ticks[period] = this_tick events.notify(event(this_tick, self))
def handle_result(self, result): process = self.process procname = process.config.name logger = process.config.options.logger try: self.process.group.config.result_handler(process.event, result) logger.debug('%s: event was processed' % procname) self._change_listener_state(EventListenerStates.ACKNOWLEDGED) except RejectEvent: logger.warn('%s: event was rejected' % procname) self._change_listener_state(EventListenerStates.ACKNOWLEDGED) notify(EventRejectedEvent(process, process.event)) except: logger.warn('%s: event caused an error' % procname) self._change_listener_state(EventListenerStates.UNKNOWN) notify(EventRejectedEvent(process, process.event))
def change_state(self, new_state, expected=True): old_state = self.state if new_state is old_state: # exists for unit tests return False event_class = self.event_map.get(new_state) if event_class is not None: event = event_class(self, old_state, expected) events.notify(event) if new_state == ProcessStates.BACKOFF: now = time.time() self.backoff = self.backoff + 1 self.delay = now + self.backoff self.state = new_state
def SendReplaceSupplier(numORidurl, doAck=False): """ Send "FireContact" packet to Central server, this will replace given supplier with a random guy. """ if isinstance(numORidurl, str): idurl = numORidurl else: idurl = contacts.getSupplierID(numORidurl) if not idurl: dhnio.Dprint(2, "central_service.SendReplaceSupplier ERROR supplier not found") return None dhnio.Dprint(4, "central_service.SendReplaceSupplier [%s]" % nameurl.GetName(idurl)) data = 'S\n'+idurl+'\n'+str(contacts.numberForSupplier(idurl)) ret = send2central(commands.FireContact(), data, doAck) events.notify('central_service', 'sent request to dismiss supplier %s' % nameurl.GetName(idurl)) return ret
def sendRemoteCommEvent(self, type, data): """ Send an event that will be received by event listener subprocesses subscribing to the RemoteCommunicationEvent. @param string type String for the "type" key in the event header @param string data Data for the event body @return boolean Always return True unless error """ if isinstance(type, unicode): type = type.encode('utf-8') if isinstance(data, unicode): data = data.encode('utf-8') notify( RemoteCommunicationEvent(type, data) ) return True
def SendReplaceCustomer(numORidurl, doAck=False): """ Send "FireContact" packet to Central server to remove a customer. This man will need to find a new supplier. Central server will do that automatically. """ if isinstance(numORidurl, str): idurl = numORidurl else: idurl = contacts.getCustomerID(numORidurl) if not idurl: dhnio.Dprint(2, "central_service.SendReplaceCustomer ERROR customer not found") return None dhnio.Dprint(4, "central_service.SendReplaceCustomer [%s]" % nameurl.GetName(idurl)) data = 'C\n'+idurl+'\n'+str(contacts.numberForCustomer(idurl)) ret = send2central(commands.FireContact(), data, doAck) events.notify('central_service', 'sent request to dismiss customer %s' % nameurl.GetName(idurl)) return ret
def toggle_capturemode(self): self.capturemode = not self.capturemode if self.capturelog is not None: if self.capturemode: self.childlog = self.capturelog else: for handler in self.capturelog.handlers: handler.flush() data = self.capturelog.getvalue() channel = self.channel procname = self.process.config.name event = self.event_type(self.process, self.process.pid, data) notify(event) msg = "%(procname)r %(channel)s emitted a comm event" self.process.config.options.logger.debug(msg, procname=procname, channel=channel) for handler in self.capturelog.handlers: handler.remove() handler.reopen() self.childlog = self.mainlog
def _log(self, data): if data: config = self.process.config if config.options.strip_ansi: data = stripEscapes(data) if self.childlog: self.childlog.info(data) if self.log_to_mainlog: msg = '%(name)r %(channel)s output:\n%(data)s' config.options.logger.log( self.mainlog_level, msg, name=config.name, channel=self.channel, data=data) if self.channel == 'stdout': if self.stdout_events_enabled: notify( ProcessLogStdoutEvent(self.process, self.process.pid, data) ) else: # channel == stderr if self.stderr_events_enabled: notify( ProcessLogStderrEvent(self.process, self.process.pid, data) )
def _log(self, data): if data: config = self.process.config if config.options.strip_ansi: data = stripEscapes(data) if self.childlog: self.childlog.info(data) if self.log_to_mainlog: msg = '%(name)r %(channel)s output:\n%(data)s' config.options.logger.log(self.mainlog_level, msg, name=config.name, channel=self.channel, data=data) if self.channel == 'stdout': if self.stdout_events_enabled: notify( ProcessLogStdoutEvent(self.process, self.process.pid, data)) else: # channel == stderr if self.stderr_events_enabled: notify( ProcessLogStderrEvent(self.process, self.process.pid, data))
def ListContacts(request): """ Called when "ListContacts" packet is received, it keeps a list of suppliers OR customers and some extra info. I think this is a most important method here, call different code from here. """ global _CentralStatusDict global legal_codes global OnListSuppliersFunc global OnListCustomersFunc data = request.Payload dhnio.Dprint(6, 'central_service.ListContacts\n%s' % data) words = data.split('\n', 1) if len(words) < 2: dhnio.Dprint(1, 'central_service.ListContacts ERROR wrong data packet [%s]' % str(request.Payload)) return code = words[0] if not code in legal_codes: dhnio.Dprint(1, 'central_service.ListContacts ERROR wrong data in the packet [%s] ' % str(request.Payload)) return current_contacts = [] clist, tail = dhnio._unpack_list(words[1]) fire_flag = code.startswith('f') ban_flag = code.startswith('b') contact_type = code.lower()[-1] error_flag = code[-1].islower() spaceDict = None if tail is not None: # extract info about who is alive at the moment onlineArray = '' spaceDict = dhnio._unpack_dict_from_list(tail) if spaceDict.has_key('online'): onlineArray = spaceDict.pop('online') for i in range(len(onlineArray)): if i < len(clist): if clist[i]: _CentralStatusDict[clist[i]] = onlineArray[i] # extract info about contacts local ip # if they are in same LAN we need to connect to local IP, not external local_ips = {} i = 0 while True: idurl_and_local_ip = spaceDict.get('localip%03d' % i, None) if idurl_and_local_ip is None: break try: contact_idurl, contact_local_ip = idurl_and_local_ip.split('|') except: break local_ips[contact_idurl] = contact_local_ip spaceDict.pop('localip%03d' % i) i += 1 dhnio.Dprint(6, 'central_service.ListContacts got local IP for %s: %s' % (nameurl.GetName(contact_idurl), contact_local_ip)) identitycache.SetLocalIPs(local_ips) #---suppliers if contact_type == 's': current_contacts = contacts.getSupplierIDs() contacts.setSupplierIDs(clist) eccmap.init() contacts.saveSupplierIDs() for supid in contacts.getSupplierIDs(): if supid.strip() == '': error_flag = True break dhnio.Dprint(4, "central_service.ListContacts (SUPPLIERS) code:%s error:%s length:%d" % (str(code), str(error_flag), len(clist))) for oldidurl in current_contacts: if oldidurl: if oldidurl not in clist: events.info('central_service', 'supplier %s were disconnected' % nameurl.GetName(oldidurl),) misc.writeSupplierData(oldidurl, 'disconnected', time.strftime('%d%m%y %H:%M:%S')) dhnio.Dprint(6, 'central_service.ListContacts supplier %s were disconnected' % nameurl.GetName(oldidurl)) for newidurl in clist: if newidurl: if newidurl not in current_contacts: transport_control.ClearAliveTime(newidurl) misc.writeSupplierData(newidurl, 'connected', time.strftime('%d%m%y %H:%M:%S')) events.info('central_service', 'new supplier %s connected' % nameurl.GetName(newidurl), '',) dhnio.Dprint(6, 'central_service.ListContacts new supplier %s connected' % nameurl.GetName(newidurl)) backup_control.SetSupplierList(clist) if not fire_flag and current_contacts != clist: dhnio.Dprint(6, 'central_service.ListContacts going to call suppliers') identitypropagate.suppliers(wide=True) if OnListSuppliersFunc is not None: OnListSuppliersFunc() #---customers elif contact_type == 'c': current_contacts = contacts.getCustomerIDs() contacts.setCustomerIDs(clist) contacts.saveCustomerIDs() if spaceDict is not None: dhnio._write_dict(settings.CustomersSpaceFile(), spaceDict) reactor.callLater(3, local_tester.TestUpdateCustomers) # if not fire_flag and current_contacts != clist: identitypropagate.customers(wide=True) dhnio.Dprint(4, "central_service.ListContacts (CUSTOMERS) code:%s error:%s length:%d" % (str(code), str(error_flag), len(clist))) for oldidurl in current_contacts: if oldidurl not in clist: events.info('central_service', 'customer %s were disconnected' % nameurl.GetName(oldidurl),) dhnio.Dprint(6, 'central_service.ListContacts customer %s were disconnected' % nameurl.GetName(oldidurl)) for newidurl in clist: if newidurl not in current_contacts: transport_control.ClearAliveTime(newidurl) events.info('central_service', 'new customer %s connected' % nameurl.GetName(newidurl)) dhnio.Dprint(6, 'central_service.ListContacts new customer %s connected' % nameurl.GetName(newidurl)) if OnListCustomersFunc is not None: OnListCustomersFunc() #---fire_flag if fire_flag: if contact_type == 's': index = -1 for index in range(len(clist)): if clist[index] != current_contacts[index]: break if index >= 0: # we want to send our Identity to new supplier # and than ask a list of files he have # so it should start rebuilding backups immediately # right after we got ListFiles from him identitypropagate.single(clist[index], wide=True) #---ban_flag if ban_flag: events.notify('central_service', 'you have negative balance, all your suppliers was removed', '',) dhnio.Dprint(2, 'central_service.ListContacts !!! you have negative balance, all your suppliers was removed !!!') #---error_flag if error_flag: #reactor.callLater(settings.DefaultNeedSuppliersPacketTimeOut(), SendRequestSuppliers) events.info('central_service', 'could not find available suppliers', 'Central server can not find available suppliers for you.\nCheck your central settings.\n',) dhnio.Dprint(2, 'central_service.ListContacts !!! could not find available suppliers !!!') #---send ack transport_control.SendAck(request) #---automats if contact_type == 's': central_connector.A('list-suppliers', clist) fire_hire.A('list-suppliers', (current_contacts, clist)) data_sender.A('restart') elif contact_type == 'c': central_connector.A('list-customers', clist) #---transport_udp if transport_control._TransportUDPEnable: import lib.transport_udp as transport_udp new_contacts = contacts.getContactsAndCorrespondents() transport_udp.ListContactsCallback(current_contacts, new_contacts)
def remove_process_group(self, name): if self.process_groups[name].get_unstopped_processes(): return False del self.process_groups[name] events.notify(events.ProcessGroupRemovedEvent(name)) return True
def handle_listener_state_change(self): data = self.state_buffer if not data: return process = self.process procname = process.config.name state = process.listener_state if state == EventListenerStates.UNKNOWN: # this is a fatal state self.state_buffer = '' return if state == EventListenerStates.ACKNOWLEDGED: if len(data) < self.READY_FOR_EVENTS_LEN: # not enough info to make a decision return elif data.startswith(self.READY_FOR_EVENTS_TOKEN): self._change_listener_state(EventListenerStates.READY) tokenlen = self.READY_FOR_EVENTS_LEN self.state_buffer = self.state_buffer[tokenlen:] process.event = None else: self._change_listener_state(EventListenerStates.UNKNOWN) self.state_buffer = '' process.event = None if self.state_buffer: # keep going til its too short self.handle_listener_state_change() else: return elif state == EventListenerStates.READY: # the process sent some spurious data, be strict about it self._change_listener_state(EventListenerStates.UNKNOWN) self.state_buffer = '' process.event = None return elif state == EventListenerStates.BUSY: if self.resultlen is None: # we haven't begun gathering result data yet pos = data.find('\n') if pos == -1: # we can't make a determination yet, we dont have a full # results line return result_line = self.state_buffer[:pos] self.state_buffer = self.state_buffer[pos + 1:] # rid LF resultlen = result_line[self.RESULT_TOKEN_START_LEN:] try: self.resultlen = int(resultlen) except ValueError: process.config.options.logger.warn( '%s: bad result line: %r' % (procname, result_line)) self._change_listener_state(EventListenerStates.UNKNOWN) self.state_buffer = '' notify(EventRejectedEvent(process, process.event)) process.event = None return else: needed = self.resultlen - len(self.result) if needed: self.result += self.state_buffer[:needed] self.state_buffer = self.state_buffer[needed:] needed = self.resultlen - len(self.result) if not needed: self.handle_result(self.result) self.process.event = None self.result = '' self.resultlen = None if self.state_buffer: # keep going til its too short self.handle_listener_state_change() else: return
def runforever(self): events.notify(events.SupervisorRunningEvent()) timeout = 1 # this cannot be fewer than the smallest TickEvent (5) socket_map = self.options.get_socket_map() self.options.logger.info('prepare run forever') while 1: combined_map = {} combined_map.update(socket_map) combined_map.update(self.get_process_map()) pgroups = self.process_groups.values() pgroups.sort() if self.options.mood < SupervisorStates.RUNNING: if not self.stopping: # first time, set the stopping flag, do a # notification and set stop_groups self.stopping = True self.stop_groups = pgroups[:] events.notify(events.SupervisorStoppingEvent()) self.ordered_stop_groups_phase_1() if not self.shutdown_report(): # if there are no unstopped processes (we're done # killing everything), it's OK to shutdown or reload raise asyncore.ExitNow for fd, dispatcher in combined_map.items(): if dispatcher.readable(): self.options.poller.register_readable(fd) if dispatcher.writable(): self.options.poller.register_writable(fd) r, w = self.options.poller.poll(timeout) for fd in r: if combined_map.has_key(fd): try: dispatcher = combined_map[fd] self.options.logger.blather( 'read event caused by %(dispatcher)r', dispatcher=dispatcher) dispatcher.handle_read_event() if (not dispatcher.readable() and not dispatcher.writable()): self.options.poller.unregister(fd) except asyncore.ExitNow: raise except: combined_map[fd].handle_error() for fd in w: if combined_map.has_key(fd): try: dispatcher = combined_map[fd] self.options.logger.blather( 'write event caused by %(dispatcher)r', dispatcher=dispatcher) dispatcher.handle_write_event() if (not dispatcher.readable() and not dispatcher.writable()): self.options.poller.unregister(fd) except asyncore.ExitNow: raise except: combined_map[fd].handle_error() for group in pgroups: group.transition() self.reap() self.handle_signal() self.tick() if self.options.mood < SupervisorStates.RUNNING: self.ordered_stop_groups_phase_2() if self.options.test: break
def doReportFailed(self, arg): dhnio.Dprint(6, "restore.doReportFailed ERROR - the block does not look good") self.Done = True self.MyDeferred.errback(Exception(self.BackupID+' failed')) events.notify('restore', '%s failed to restore block number %d' % (self.BackupID, self.BlockNumber))
def finish(self, pid, sts): """ The process was reaped and we need to report and manage its state """ self.drain() es, msg = decode_wait_status(sts) now = time.time() self.laststop = now processname = self.config.name if now > self.laststart: too_quickly = now - self.laststart < self.config.startsecs else: too_quickly = False self.config.options.logger.warn( "process %r (%s) laststart time is in the future, don't " "know how long process was running so assuming it did " "not exit too quickly" % (self.config.name, self.pid)) exit_expected = es in self.config.exitcodes if self.killing: # likely the result of a stop request # implies STOPPING -> STOPPED self.killing = False self.delay = 0 self.exitstatus = es msg = "stopped: %s (%s)" % (processname, msg) self._assertInState(ProcessStates.STOPPING) self.change_state(ProcessStates.STOPPED) elif too_quickly: # the program did not stay up long enough to make it to RUNNING # implies STARTING -> BACKOFF self.exitstatus = None self.spawnerr = 'Exited too quickly (process log may have details)' msg = "exited: %s (%s)" % (processname, msg + "; not expected") self._assertInState(ProcessStates.STARTING) self.change_state(ProcessStates.BACKOFF) else: # this finish was not the result of a stop request, the # program was in the RUNNING state but exited # implies RUNNING -> EXITED normally but see next comment self.delay = 0 self.backoff = 0 self.exitstatus = es # if the process was STARTING but a system time change causes # self.laststart to be in the future, the normal STARTING->RUNNING # transition can be subverted so we perform the transition here. if self.state == ProcessStates.STARTING: self.change_state(ProcessStates.RUNNING) self._assertInState(ProcessStates.RUNNING) if exit_expected: # expected exit code msg = "exited: %s (%s)" % (processname, msg + "; expected") self.change_state(ProcessStates.EXITED, expected=True) else: # unexpected exit code self.spawnerr = 'Bad exit code %s' % es msg = "exited: %s (%s)" % (processname, msg + "; not expected") self.change_state(ProcessStates.EXITED, expected=False) self.config.options.logger.info(msg) self.pid = 0 self.config.options.close_parent_pipes(self.pipes) self.pipes = {} self.dispatchers = {} # if we died before we processed the current event (only happens # if we're an event listener), notify the event system that this # event was rejected so it can be processed again. if self.event is not None: # Note: this should only be true if we were in the BUSY # state when finish() was called. events.notify(events.EventRejectedEvent(self, self.event)) self.event = None
def __init__(self, name): self.name = name def msg(self): return f"{self.name} is thirsty." @events.handle(UserHungryEvent) def handle_hungry(ev): m = ev.msg() print(f"handling event with msg: {m}") @events.handle(UserThirstyEvent) def handle_thirsty(ev): m = ev.msg() print(f"handling thirst with water :d ev with msg: {m}") @events.handleany def events_hunger_thirst_logger(ev): m = ev.msg() print(f"logging from any{m}") ev1 = UserHungryEvent('ahmed') ev2 = UserThirstyEvent('dmdm') events.notify(ev1) events.notify(ev2)
def handle_listener_state_change(self): data = self.state_buffer if not data: return process = self.process procname = process.config.name state = process.listener_state if state == EventListenerStates.UNKNOWN: # this is a fatal state self.state_buffer = '' return if state == EventListenerStates.ACKNOWLEDGED: if len(data) < self.READY_FOR_EVENTS_LEN: # not enough info to make a decision return elif data.startswith(self.READY_FOR_EVENTS_TOKEN): self._change_listener_state(EventListenerStates.READY) tokenlen = self.READY_FOR_EVENTS_LEN self.state_buffer = self.state_buffer[tokenlen:] process.event = None else: self._change_listener_state(EventListenerStates.UNKNOWN) self.state_buffer = '' process.event = None if self.state_buffer: # keep going til its too short self.handle_listener_state_change() else: return elif state == EventListenerStates.READY: # the process sent some spurious data, be strict about it self._change_listener_state(EventListenerStates.UNKNOWN) self.state_buffer = '' process.event = None return elif state == EventListenerStates.BUSY: if self.resultlen is None: # we haven't begun gathering result data yet pos = data.find('\n') if pos == -1: # we can't make a determination yet, we dont have a full # results line return result_line = self.state_buffer[:pos] self.state_buffer = self.state_buffer[pos+1:] # rid LF resultlen = result_line[self.RESULT_TOKEN_START_LEN:] try: self.resultlen = int(resultlen) except ValueError: process.config.options.logger.warn( '%s: bad result line: %r' % (procname, result_line) ) self._change_listener_state(EventListenerStates.UNKNOWN) self.state_buffer = '' notify(EventRejectedEvent(process, process.event)) process.event = None return else: needed = self.resultlen - len(self.result) if needed: self.result += self.state_buffer[:needed] self.state_buffer = self.state_buffer[needed:] needed = self.resultlen - len(self.result) if not needed: self.handle_result(self.result) self.process.event = None self.result = '' self.resultlen = None if self.state_buffer: # keep going til its too short self.handle_listener_state_change() else: return