def reg_update(self, addrs, addr_tout): for addr in addrs: if addr_tout > 0: self.addr_add(str(addr), addr_tout) elif str(addr) in self.maddrs.keys(): self.addr_remove(str(addr)) db.set('mlr_cache', str(self.maddrs))
def _configure(): global SERIAL_DEV dongle_status = send_cmd('show status', debug_level=kiserial.KiDebug.NONE)[0] # Wait for the dongle to reach a steady status logging.info('Waiting until dongle is joined...') db.set('dongle_status', 'disconnected') dongle_status = '' while not ('none' in dongle_status or 'joined' in dongle_status): dongle_status = send_cmd('show status', debug_level=kiserial.KiDebug.NONE)[0] time.sleep(1) # Different actions according to dongle status if dongle_status == 'none': if db.get('discovered') == 0: _dongle_apply_config() _enable_br() send_cmd('ifup') _configure() elif dongle_status == 'none - saved configuration': _enable_br() send_cmd('ifup') _configure() elif dongle_status == 'joined': pass else: # Other 'none' statuses logging.warning('Dongle status was "%s".' % dongle_status) send_cmd('clear') _configure()
def _main(): global SERVER # Load database db.load() # Exterior network configuration global_netconfig() # Find connected dongle enable_ncp() # Start web interface webserver.start() # Start subtasks mdns = MDNS() TASKS.append(SERIAL()) TASKS.append(NETWORK()) TASKS.append(DHCP()) TASKS.append(NAT()) TASKS.append(DNS()) TASKS.append(MDNS()) TASKS.append(DIAGS()) TASKS.append(COAPSERVER()) # Launch mDNS already asyncio.ensure_future(mdns.run()) if db.get('autostart') == 1: db.set('action_kibra', 'start') asyncio.ensure_future(_master()) asyncio.get_event_loop().run_forever()
def _configure(): global SERIAL_DEV # Wait for the NCP to reach a steady status logging.info('Waiting until NCP is steady...') ncp_status = 'disconnected' while not ('none' in ncp_status or 'joined' in ncp_status): ncp_status = send_cmd('show status')[0] time.sleep(1) db.set('ncp_status', ncp_status) # Different actions according to NCP status if ncp_status == 'none': if not kibra.__harness__: _ncp_apply_config() _enable_br() send_cmd('ifup') elif ncp_status == 'none - saved configuration': _enable_br() send_cmd('ifup') elif ncp_status == 'joined': send_cmd('ifdown') _configure() else: # Other 'none' statuses logging.warning('Dongle status was "%s".' % ncp_status) send_cmd('clear') SERIAL_DEV.wait_for('status', 'none') _configure()
def bbr_dataset_update(): ''' Update Thread BBR Service Data Automatically increases the sequence number ''' # Increase sequence number bbr_sequence_number = (db.get('bbr_seq') + 1) % 0xFF # Build s_server_data reregistration_delay = db.get('rereg_delay') mlr_timeout = db.get('mlr_timeout') s_server_data = struct.pack( DEFS.THREAD_SERVICE_DATA_FMT, bbr_sequence_number, reregistration_delay, mlr_timeout, ) # Store used values db.set('bbr_seq', bbr_sequence_number) # Enable BBR send_cmd('config service add %u %s %s' % ( DEFS.THREAD_ENTERPRISE_NUMBER, DEFS.THREAD_SERVICE_DATA_BBR, bytes(s_server_data).hex(), )) logging.info('BBR update: Seq. = %d MLR Timeout = %d, Rereg. Delay = %d' % (bbr_sequence_number, mlr_timeout, reregistration_delay))
async def render_post(self, request): req_dua = None status = DMStatus.ST_UNSPEC # Incoming TLVs parsing logging.info('in %s req: %s' % (URI.N_DR, ThreadTLV.sub_tlvs_str(request.payload))) # BBR Primary/Secondary status if 'primary' not in db.get('bbr_status'): status = DMStatus.ST_NOT_PRI elif len(DUA_HNDLR.entries) >= DUA_LIMIT: status = DMStatus.ST_RES_SHRT else: dua = None eid = None elapsed = 0 # ML-EID TLV value = ThreadTLV.get_value(request.payload, TLV.A_ML_EID) if value: eid = value.hex() # Target EID TLV value = ThreadTLV.get_value(request.payload, TLV.A_TARGET_EID) if value: try: req_dua = bytes(value) dua = ipaddress.IPv6Address(req_dua).compressed except: status = DMStatus.ST_INV_ADDR # Time Since Last Transaction TLV value = ThreadTLV.get_value(request.payload, TLV.A_TIME_SINCE_LAST_TRANSACTION) if value: elapsed = struct.unpack('!I', value)[0] if eid and dua: # Thread Harness may force response status if db.get('dua_next_status'): status = int(db.get('dua_next_status')) db.set('dua_next_status', '') elif DUA_HNDLR.reg_update(eid, dua, elapsed): status = DMStatus.ST_SUCESS else: # Duplication detected (resource shortage not contemplated) status = DMStatus.ST_DUP_ADDR # Fill and return the response payload = ThreadTLV(t=TLV.A_STATUS, l=1, v=[status]).array() if req_dua: payload += ThreadTLV(t=TLV.A_TARGET_EID, l=16, v=req_dua).array() logging.info( 'out %s rsp: %s' % (URI.N_DR, ThreadTLV.sub_tlvs_str(payload))) return aiocoap.Message(code=Code.CHANGED, payload=payload)
async def periodic(self): # Detect if serial was disconnected try: SERIAL_DEV.is_active() except IOError: logging.error('Device %s has been disconnected.', db.get('serial_device')) self.kstop() self.kill() except Exception: logging.error('Device %s is not responding.', db.get('serial_device')) return # Don't continue if device is not joined if db.get('ncp_status') != 'joined' or db.get( 'status_serial') != 'running': return if not db.get('prefix_active'): slaac, dhcp, dp = _get_prefix_flags() # Don't continue if servers are not running if dhcp and db.get('status_dhcp') not in 'running': return if dp and db.get('status_coapserver') not in 'running': return # Enable border agent _bagent_on() # Add route NETWORK.ncp_route_enable(db.get('prefix')) # Announce prefix to the network prefix_handle( 'prefix', 'add', db.get('prefix'), stable=True, on_mesh=True, default=True, slaac=slaac, dhcp=dhcp, dp=dp, ) # Start as Secondary (KiNOS will notify the change to Primary) db.set('bbr_status', 'secondary') logging.info('This BBR is now Secondary.') # Announce service bbr_dataset_update() # Mark prefix as active db.set('prefix_active', 1)
def kstart(self): ll_addr = ipaddress.IPv6Address(db.get('dongle_ll')).compressed self.br_permanent_addr = '%s%%%s' % (ll_addr, db.get('interior_ifname')) DIAGS_DB['nodes'] = [] # Delete old values to prevent MDNS from using them before obtaning # the updated ones db.delete('dongle_xpanid') db.delete('dongle_netname') db.set('bbr_status', 'off')
def coap_con_request(): req_str = db.get('coap_req') if req_str: db.set('coap_req', '') req = json.loads(req_str.replace("'", '"')) dst = req.get('dst')[0] prt = int(req.get('prt')[0]) uri = req.get('uri')[0] pld = bytes.fromhex(req.get('pld')[0]) logging.info('out %s ntf: %s' % (uri, ThreadTLV.sub_tlvs_str(pld))) asyncio.ensure_future(DUA_HNDLR.ntf_client.con_request(dst, prt, uri, pld))
def kstart(self): db.set('bbr_status', 'off') db.delete('ncp_rloc') db.delete('ncp_mleid') # Get interior link-local address iface_addrs = NETWORK.get_addrs(db.get('interior_ifname'), socket.AF_INET6) for addr in iface_addrs: if addr.startswith('fe80'): db.set('interior_ipv6_ll', addr) # Start listening to KiNOS syslog messages self.syslog = Syslog_Parser(addr)
def start(self, props): while self.run: request = self.sock.recvfrom(1024) try: payload = request[0].decode() except: payload = '' if payload == 'BBR': db.set('discovered', 1) dst_addr = request[1][0] dst_port = request[1][1] logging.info('HDP request from %s' % dst_addr) self.sock.sendto( json.dumps(props).encode(), (dst_addr, dst_port))
def kstop(self): logging.info('Stopping CoAP servers') for coap_server in self.running_coap_servers: coap_server.stop() db.set('bbr_status', 'off') # Un-listen for CoAP in required multicast addresses for group, params in self.mcast_groups.items(): logging.info('Leaving %s group: %s' % (params[1], db.get(group))) MCAST_HNDLR.mcrouter.join_leave_group('leave', db.get(group), db.get(params[0])) logging.info('Stopping Multicast handler') MCAST_HNDLR.stop() logging.info('Stopping DUA handler') DUA_HNDLR.stop()
def addr_remove(self, addr): # Remove the address from the volatile list self.maddrs.pop(addr) # Apply changes to cached addresses db.set('mlr_cache', self.maddrs) # Remove the address from the presistent list self.addr_perm_remove(addr) # Remove the existing multicast routes for this address self.mcrouter.rem_group_routes(addr) # Leave the multicast group self.mcrouter.join_leave_group('leave', addr) logging.info('Multicast address %s registration removed.' % addr)
async def periodic(self): # Detect if interior interface goes down try: IPR.link_lookup(ifname=db.get('interior_ifname'), operstate='UP') except: logging.error('Interface %s went down.', db.get('interior_ifname')) self.kstop() self.kill() # Don't continue if NCP RLOC has not been asigned yet if not db.has_keys(['ncp_rloc']): return # Keep track of exterior addresses iface_addrs = [] iface_addrs += get_addrs(db.get('exterior_ifname'), socket.AF_INET) iface_addrs += get_addrs(db.get('exterior_ifname'), socket.AF_INET6) # Find which addresses to remove and which ones to add ext_addrs = db.get('exterior_addrs') old_addrs = ext_addrs new_addrs = [] for addr in iface_addrs: if addr not in old_addrs: new_addrs.append(addr) else: old_addrs.remove(addr) # Remove old addresses for addr in old_addrs: NAT.handle_nat64_masking(addr, enable=False) IPTABLES.handle_bagent_fwd(addr, db.get('ncp_rloc'), enable=False) # Add new addresses for addr in new_addrs: # TODO: except link local NAT.handle_nat64_masking(addr, enable=True) IPTABLES.handle_bagent_fwd(addr, db.get('ncp_rloc'), enable=True) # Notify MDNS service if new_addrs: MDNS.new_external_addresses() db.set('exterior_addrs', iface_addrs)
def enable_ncp(): '''Find the device and initialize the port''' global SERIAL_DEV # Find device and initialize port port = _find_device(db.get('ncp_serial')) if not port: return logging.info('Serial device is %s.', port) db.set('serial_device', port) SERIAL_DEV = kiserial.KiSerial(port, debug=kiserial.KiDebug( kiserial.KiDebug.NONE)) send_cmd('debug level none', debug_level=kiserial.KiDebug.NONE) # Save serial number serial = send_cmd('show snum')[0] db.set('ncp_serial', serial) # Update the NCP firmware if needed if kibra.__kinosver__ not in send_cmd('show swver')[-1]: logging.info('NCP needs a firmware update.') ncp_fw_update() enable_ncp() # No need to continue if NCP fw version is up to date else: logging.info('NCP firmware is up to date.') # Make sure we are running Thread v3 (1.2.0) if not kibra.__harness__ and 'Thread v3' not in send_cmd( 'show thver')[0]: send_cmd('clear') SERIAL_DEV.wait_for('status', 'none') send_cmd('config thver 3') # Enable ECM if not enabled if 'off' in send_cmd('show hwconfig')[3]: logging.info('Enabling CDC Ethernet and reseting device.') send_cmd('config hwmode 4') send_cmd('reset') time.sleep(3) del SERIAL_DEV enable_ncp()
def global_netconfig(): set_ext_iface() logging.info('External interface is %s.', db.get('exterior_ifname')) if not db.has_keys(['prefix']): logging.info('Trying to obtain a prefix via Prefix Delegation...') prefix = _get_prefix(db.get('exterior_ifname')) if not prefix: logging.info('It was not possible to obtain a global prefix.') prefix = _get_ula() logging.info('Generated the ULA prefix %s.' % prefix) # Obtain /64 subprefix prefix = '%s/64' % prefix.split('/')[0] db.set('prefix', prefix) # Find exterior interface addresses # Global IPv4 addresses ipv4_addrs = get_addrs(db.get('exterior_ifname'), AF_INET, scope=0) if ipv4_addrs: logging.info('Using %s as exterior IPv4 address.', ipv4_addrs[0]) db.set('exterior_ipv4', ipv4_addrs[0]) # Link-local IPv4 addresses ipv4_addrs = get_addrs(db.get('exterior_ifname'), AF_INET, scope=253) if ipv4_addrs: logging.info('Using %s as exterior IPv4 link-local address.', ipv4_addrs[0]) db.set('exterior_ipv4_ll', ipv4_addrs[0]) # Global IPv6 addresses ipv6_addrs = get_addrs(db.get('exterior_ifname'), AF_INET6, scope=0) if ipv6_addrs: logging.info('Using %s as exterior IPv6 address.', ipv6_addrs[0]) db.set('exterior_ipv6', ipv6_addrs[0]) # Link-local IPv6 addresses ipv6_addrs = get_addrs(db.get('exterior_ifname'), AF_INET6, scope=253) if ipv6_addrs: logging.info('Using %s as exterior link-local IPv6 address.', ipv6_addrs[0]) db.set('exterior_ipv6_ll', ipv6_addrs[0])
def _process_message(msgid, uptime, payload): logging.debug('msgid = %s, uptime = %s, payload = %s' % (msgid, uptime, payload)) if msgid == SYSLOG_MSG_ID_CACHE_DEL: cached_eids = db.get('ncp_eid_cache') try: cached_eids.remove(payload) db.set('ncp_eid_cache', cached_eids) except: pass # It didn't exist in the list logging.info('Address %s is not cached anymore.' % payload) elif msgid == SYSLOG_MSG_ID_CACHE_ADD: cached_eids = db.get('ncp_eid_cache') cached_eids.append(payload) db.set('ncp_eid_cache', cached_eids) logging.info('Address %s is now cached.' % payload) elif msgid == SYSLOG_MSG_ID_ALOC_DEL: NETWORK.handle_addr(payload, 'del') elif msgid == SYSLOG_MSG_ID_ALOC_ADD: NETWORK.handle_addr(payload, 'add') elif msgid == SYSLOG_MSG_ID_UNICAST_SYS_ADD: NETWORK.handle_addr(payload, 'add') elif msgid == SYSLOG_MSG_ID_AOPD_SAVED: _parse_active_dataset(payload) logging.info('Active dataset changed.') elif msgid == SYSLOG_MSG_ID_JOIN_STATUS_OK: db.set('ncp_status', 'joined') logging.info('Device just joined to the Thread network') elif msgid == SYSLOG_MSG_ID_JOIN_STATUS_ERR: # TODO: notify user logging.info('Device could not join to the Thread network')
async def periodic(self): # Detect if serial was disconnected try: SERIAL_DEV.is_active() except IOError: logging.error('Device %s has been disconnected.', db.get('serial_device')) self.kstop() self.kill() if not db.get('prefix_active'): dp = True if db.get('prefix_dua') else False dhcp = True if db.get('prefix_dhcp') else False slaac = True if not dp and not dhcp else False # Don't continue if servers are not running if dhcp and db.get('status_dhcp') not in 'running': return if dp and db.get('status_coapserver') not in 'running': return # Add route dongle_route_enable(db.get('prefix')) # Announce prefix to the network prefix_handle('prefix', 'add', db.get('prefix'), stable=True, on_mesh=True, default=True, slaac=slaac, dhcp=dhcp, dp=dp) if dp: bbr_dataset_update() # Mark prefix as active db.set('prefix_active', 1)
def set_ext_iface(): '''Select the right external interface''' if not db.get('exterior_ifname'): links = IPR.get_links() for link in links: # Don't choose the loopback interface if link['flags'] & IFF_LOOPBACK: continue # Must be up if not link['flags'] & IFF_UP: continue # Must have multicast enabled if not link['flags'] & IFF_MULTICAST: continue # Don't choose the Kirale's Thread device if link.get_attr('IFLA_ADDRESS').startswith('84:04:d2'): continue # First interface matching all criteria is selected db.set('exterior_ifname', link.get_attr('IFLA_IFNAME')) break # No appropiate interface was found if not db.get('exterior_ifname'): raise Exception('No exterior interface available.') # Set exterior index idx = IPR.link_lookup(ifname=db.get('exterior_ifname'))[0] db.set('exterior_ifnumber', idx) # Set exterior MAC db.set('exterior_mac', IPR.get_links(idx)[0].get_attr('IFLA_ADDRESS'))
def kstop(self): if db.get('prefix_active'): # Remove prefix from the network dp = True if db.get('prefix_dua') else False dhcp = True if db.get('prefix_dhcp') else False slaac = True if not dp and not dhcp else False prefix_handle('prefix', 'remove', db.get('prefix'), stable=True, on_mesh=True, default=True, slaac=slaac, dhcp=dhcp, dp=dp) # Mark prefix as active db.set('prefix_active', 0) _bagent_off() send_cmd('ifdown')
def kstop(self): if db.get('prefix_active'): # Remove prefix from the network slaac, dhcp, dp = _get_prefix_flags() prefix_handle( 'prefix', 'remove', db.get('prefix'), stable=True, on_mesh=True, default=True, slaac=slaac, dhcp=dhcp, dp=dp, ) # Mark prefix as inactive db.set('prefix_active', 0) _bagent_off() send_cmd('ifdown')
def addr_add(self, addr, addr_tout): if addr_tout == 0xffffffff: tout = datetime.datetime.max # Save the address in the presistent list maddrs_perm = db.get('maddrs_perm') or [] if addr not in maddrs_perm: maddrs_perm.append(addr) db.set('maddrs_perm', maddrs_perm) else: if addr_tout < DEFS.MIN_MLR_TIMEOUT: addr_tout = DEFS.MIN_MLR_TIMEOUT tout = datetime.datetime.now().timestamp() + addr_tout # Join the multicast group in the external interface for MLDv2 handling if addr not in self.maddrs.keys(): self.mcrouter.join_leave_group('join', addr) # Save the new address in the volatile list self.maddrs[addr] = tout logging.info('Multicast address %s registration updated (+%d s)' % (addr, addr_tout))
def main(): global SERVER logging.info('Launching KiBRA v%s' % kibra.__version__) # Load database db.load() # Exterior network configuration global_netconfig() # Find connected NCP enable_ncp() # Start web interface webserver.start() # Start subtasks mdns = MDNS() TASKS.append(NETWORK()) TASKS.append(SERIAL()) TASKS.append(SYSLOG()) TASKS.append(DHCP()) TASKS.append(NAT()) TASKS.append(DNS()) TASKS.append(mdns) TASKS.append(DIAGS()) if db.get('bbr_enable'): TASKS.append(COAPSERVER()) # Launch mDNS already asyncio.ensure_future(mdns.run()) if db.get('autostart') == 1: db.set('action_kibra', 'start') asyncio.ensure_future(_master()) asyncio.get_event_loop().run_forever()
def kstop(self): logging.info('Stopping CoAP servers') for coap_server in self.coap_servers: coap_server.stop() all_network_bbrs = db.get('all_network_bbrs') logging.info('Leaving All Network BBRs group: %s' % all_network_bbrs) MCAST_HNDLR.mcrouter.join_leave_group('leave', all_network_bbrs) db.set('bbr_status', 'off') if db.get('prefix_dua'): all_domain_bbrs = db.get('all_domain_bbrs') logging.info('Leaving All Domain BBRs group: %s' % all_domain_bbrs) MCAST_HNDLR.mcrouter.join_leave_group('leave', all_domain_bbrs) logging.info('Leaving Realm-Local All-Routers group: ff03::2') MCAST_HNDLR.mcrouter.join_leave_group('leave', 'ff03::2', db.get('interior_ifnumber')) logging.info('Stopping Multicast handler') MCAST_HNDLR.stop() logging.info('Stopping DUA handler') DUA_HNDLR.stop()
def _parse_net_data(self, tlvs): is_pbbr = False value = ThreadTLV.get_value(tlvs, TLV.D_NETWORK_DATA) if value: for tlv in ThreadTLV.sub_tlvs(value): type_ = tlv.type >> 1 if type_ is TLV.N_SERVICE: # Detect BBR Dataset encoding if (tlv.value[0] >> 7 and tlv.value[1] is 1 and tlv.value[2] is 1): server_tlvs = ThreadTLV.sub_tlvs(tlv.value[3:]) '''BBR is primary if there is only one Server TLV in the BBR Dataset and the RLOC16 is the same as ours''' if len(server_tlvs) == 1: node_rloc = ipaddress.IPv6Address( db.get('dongle_rloc')).packed if node_rloc[14:16] == server_tlvs[0].value[0:2]: is_pbbr = True elif type_ is TLV.N_PREFIX: if db.get('prefix_dhcp') and not db.get('dhcp_aloc'): # Detect DHCPv6 Agent ALOC length = math.ceil(tlv.value[1] / 8) byt_prefix = tlv.value[2:2 + length] + bytes(length) int_prefix = int.from_bytes(byt_prefix, byteorder='big') str_prefix = ipaddress.IPv6Address( int_prefix).compressed str_prefix += '/%s' % tlv.value[1] if str_prefix == db.get('prefix'): # This is the prefix that we announced for subtlv in ThreadTLV.sub_tlvs( tlv.value[(length + 2):]): # TODO: verify that there is a Border Router TLV # matching our RLOC16 and DHCP flag if subtlv.type >> 1 is TLV.N_6LOWPAN_ID: cid = subtlv.value[0] & 0x0f rloc = db.get('dongle_rloc') aloc = list( ipaddress.IPv6Address(rloc).packed) aloc[14] = 0xfc aloc[15] = cid aloc = ipaddress.IPv6Address( bytes(aloc)).compressed db.set('dhcp_aloc', aloc) # Listen to the DHCP ALOC which is going to be # used by BR MTD children netmap(aloc, rloc) if is_pbbr: if 'primary' not in db.get('bbr_status'): logging.info('Setting this BBR as Primary') db.set('bbr_status', 'primary') else: if 'secondary' not in db.get('bbr_status'): logging.info('Setting this BBR as Secondary') db.set('bbr_status', 'secondary')
def kstart(self): db.set('prefix_active', 0) db.set('ncp_heui64', send_cmd('show heui64')[0]) _configure()
def kill(self): logging.info('Killing task [%s]...', self.name) db.set(self.action_key, action.KILL)
def _parse_active_dataset(self, payload): # No response to /c/ag if payload is None or b'': db.set('dongle_secpol', '0') # Response present else: value = ThreadTLV.get_value(payload, TLV.C_CHANNEL) if value: db.set('dongle_channel', int(value[2])) value = ThreadTLV.get_value(payload, TLV.C_PAN_ID) if value: db.set('dongle_panid', '0x' + ''.join('%02x' % byte for byte in value)) value = ThreadTLV.get_value(payload, TLV.C_EXTENDED_PAN_ID) if value: db.set('dongle_xpanid', '0x' + ''.join('%02x' % byte for byte in value)) value = ThreadTLV.get_value(payload, TLV.C_NETWORK_NAME) if value: db.set('dongle_netname', ''.join('%c' % byte for byte in value)) value = ThreadTLV.get_value(payload, TLV.C_NETWORK_MESH_LOCAL_PREFIX) if value: prefix_bytes = bytes(value) + bytes(8) prefix_addr = ipaddress.IPv6Address(prefix_bytes) db.set('dongle_prefix', prefix_addr.compressed + '/64') value = ThreadTLV.get_value(payload, TLV.C_ACTIVE_TIMESTAMP) value = ThreadTLV.get_value(payload, TLV.C_SECURITY_POLICY) if value: db.set('dongle_secpol', value.hex())
async def run(self): logging.info('Loading task [%s]...', self.name) self.is_alive = True # Preconfiguration if self.check_status() is status.STOPPED: db.set(self.status_key, status.STOPPED) db.set(self.action_key, action.START) else: db.set(self.action_key, action.NONE) db.set(self.status_key, self.check_status()) # Loop while self.is_alive: task_action = db.get(self.action_key) if task_action in (action.STOP, action.KILL): db.set(self.status_key, status.STOPPING) task_status = db.get(self.status_key) # Stopped case if task_status is status.STOPPED: # Start task if needed if task_action is action.START: db.set(self.status_key, status.STARTING) # Wait for tasks for task in self.start_tasks: logging.info('Task [%s] is waiting for [%s] to start.', self.name, task) while db.get('status_' + task) is not status.RUNNING: await asyncio.sleep(1) # Wait for keys while not db.has_keys(self.start_keys): await asyncio.sleep(1) try: self.kstart() db.set(self.status_key, status.RUNNING) logging.info('Task [%s] has now started.', self.name) except Exception as exc: db.set(self.status_key, status.ERRORED) logging.error('Task [%s] errored on start: %s', self.name, exc) db.set(self.action_key, action.NONE) elif task_action is action.KILL: self.is_alive = False # Running case if task_status is status.RUNNING: # Check if other dependant tasks have stopped or errored for task in self.start_tasks: if db.get('status_' + task) is not status.RUNNING: logging.info( 'Task [%s] stopped and forced [%s] to stop.', task, self.name) self.kill() break # Periodic tasks if task_action is action.NONE: # Avoid execution on start/stop processes await self.periodic() # Stop task if needed if task_status is status.STOPPING: if task_action in (action.STOP, action.KILL): for task in self.stop_tasks: logging.info('Task [%s] is waiting for [%s] to stop.', self.name, task) while db.get('status_' + task) is not (status.STOPPED or None): await asyncio.sleep(1) while not db.has_keys(self.stop_keys): logging.info('Task [%s] cannot be stopped' % self.name) await asyncio.sleep(1) self.kstop() if task_action is action.KILL: self.is_alive = False db.set(self.action_key, action.NONE) db.set(self.status_key, status.STOPPED) logging.info('Task [%s] has now stopped.', self.name) # All cases await asyncio.sleep(self.period)
async def _master(): # TODO: Have a way to completely stop the daemon while True: # Start over db.set('status_kibra', 'stopped') # Wait until the start command is received while db.get('action_kibra') != 'start': await asyncio.sleep(0.2) # Start all tasks db.set('status_kibra', 'starting') for thread in TASKS: asyncio.ensure_future(thread.run()) # Wait until all tasks have started for thread in TASKS: while db.get('status_' + thread.name) is not status.RUNNING: await asyncio.sleep(1) db.set('action_kibra', 'none') db.set('status_kibra', 'running') db.save() logging.info('All tasks have now started.') # Run forever tasks_alive = True while tasks_alive: tasks_alive = False for thread in TASKS: if db.get('status_' + thread.name) is status.RUNNING: tasks_alive = True break await asyncio.sleep(0.2) # Kill all tasks if stop command is received if db.get('action_kibra') == 'stop': db.set('status_kibra', 'stopping') for thread in TASKS: db.set('action_' + thread.name, 'kill') db.set('action_kibra', 'none') logging.info('Killing all tasks...') db.set('status_kibra', 'stopped') logging.info('All tasks have now stopped.')