class mDNSBridge(object): def __init__(self, domain=None): self.mdns = MDNSEngine() self.mdns.start() self.services = {} self.domain = domain for type in VALID_TYPES: self.services[type] = [] self.mdns.callback_on_services("_" + type + "._tcp", self._mdns_callback, registerOnly=False, domain=self.domain) def _mdns_callback(self, data): srv_type = data["type"][1:].split(".")[0] if data["action"] == "add": priority = 0 if "pri" in data["txt"]: if data["txt"]["pri"].isdigit() and data["txt"]["pri"] >= 100: priority = int(data["txt"]["pri"]) service_entry = { "name": data["name"], "address": data["address"], "port": data["port"], "txt": data["txt"], "priority": priority } for service in self.services[srv_type]: if service["name"] == data["name"] and service[ "address"] == data["address"]: service.update(service_entry) return if nmoscommonconfig.config.get('prefer_ipv6', False) == False: if ":" not in data["address"]: self.services[srv_type].append(service_entry) else: if not data["address"].startswith( "fe80::") and "." not in data["address"]: self.services[srv_type].append(service_entry) elif data["action"] == "remove": for service in self.services[srv_type]: if service["name"] == data["name"] and service[ "address"] == data["address"]: self.services[srv_type].remove(service) break def get_services(self, type): if type not in VALID_TYPES: return None return self.services[type] def stop(self): self.mdns.stop()
class QueryService: def __init__(self, logger=None): self.running = False self.logger = Logger("regquery") self.logger.writeDebug('Running QueryService') # HTTPS under test only at present # enabled = Use HTTPS only in all URLs and mDNS adverts # disabled = Use HTTP only in all URLs and mDNS adverts # mixed = Use HTTP in all URLs, but additionally advertise an HTTPS endpoint for discovery of this API only self.config = {"priority": 100, "https_mode": "disabled"} self._load_config() self.mdns = MDNSEngine() self.httpServer = HttpServer(QueryServiceAPI, WS_PORT, '0.0.0.0', api_args=[self.logger, self.config]) def start(self): if self.running: gevent.signal(signal.SIGINT, self.sig_handler) gevent.signal(signal.SIGTERM, self.sig_handler) self.running = True self.mdns.start() self.logger.writeDebug('Running web socket server on %i' % WS_PORT) self.httpServer.start() while not self.httpServer.started.is_set(): self.logger.writeDebug('Waiting for httpserver to start...') self.httpServer.started.wait() if self.httpServer.failed is not None: raise self.httpServer.failed self.logger.writeDebug("Running on port: {}".format(self.httpServer.port)) priority = self.config["priority"] if not str(priority).isdigit() or priority < 100: priority = 0 if self.config["https_mode"] != "enabled": self.mdns.register(DNS_SD_NAME + "_http", DNS_SD_TYPE, DNS_SD_HTTP_PORT, {"pri": priority, "api_ver": ",".join(API_VERSIONS), "api_proto": "http"}) if self.config["https_mode"] != "disabled": self.mdns.register(DNS_SD_NAME + "_https", DNS_SD_TYPE, DNS_SD_HTTPS_PORT, {"pri": priority, "api_ver": ",".join(API_VERSIONS), "api_proto": "https"}) def run(self): self.running = True self.start() while self.running: time.sleep(1) self._cleanup() def _cleanup(self): self.mdns.stop() self.mdns.close() self.httpServer.stop() def sig_handler(self): self.stop() def stop(self): self.running = False def _load_config(self): try: config_file = "/etc/ips-regquery/config.json" if os.path.isfile(config_file): f = open(config_file, 'r') extra_config = json.loads(f.read()) self.config.update(extra_config) except Exception as e: self.logger.writeDebug("Exception loading config: {}".format(e))
class mDNSBridge(object): def __init__(self, domain=None): self.mdns = MDNSEngine() self.mdns.start() self.services = {} self.domain = domain for type in VALID_TYPES: self.services[type] = [] self.mdns.callback_on_services("_" + type + "._tcp", self._mdns_callback, registerOnly=False, domain=self.domain) def _mdns_callback(self, data): srv_type = data["type"][1:].split(".")[0] if data["action"] == "add": priority = 0 versions = ["v1.0"] protocol = "http" if "pri" in data["txt"]: if data["txt"]["pri"].isdigit() and data["txt"]["pri"] >= 100: priority = int(data["txt"]["pri"]) if "api_ver" in data["txt"]: versions = data["txt"]["api_ver"].split(",") if "api_proto" in data["txt"]: protocol = data["txt"]["api_proto"] service_entry = { "name": data["name"], "address": data["address"], "port": data["port"], "txt": data["txt"], "priority": priority, "versions": versions, "protocol": protocol } for service in self.services[srv_type]: if service["name"] == data["name"] and service[ "address"] == data["address"]: service.update(service_entry) return if nmoscommonconfig.config.get('prefer_ipv6', False) == False: if ":" not in data["address"]: self.services[srv_type].append(service_entry) else: if not data["address"].startswith( "fe80::") and "." not in data["address"]: self.services[srv_type].append(service_entry) #TODO: Due to issues with python requests library, IPv6 link local addresses are not compatable with requests.request(). #Therefore, IPv6 Global addresses must be used for nodes to register with pipeline manager through ipppython aggregator.py #Below code will allow link-local addresses to be used if requests bug is fixed #else: #service_entry["address"] = str(data["address"])+str("%%")+str(if_indextoname(data["interface"])) #self.services[srv_type].append(service_entry) elif data["action"] == "remove": for service in self.services[srv_type]: if service["name"] == data["name"]: self.services[srv_type].remove(service) break def get_services(self, type): if type not in VALID_TYPES: return None return self.services[type] def stop(self): self.mdns.stop()
class NodeFacadeService: def __init__(self, interactive=False): self.logger = Logger("facade", None) if HOST == "": self.logger.writeFatal( "Unable to start facade due to lack of connectivity") sys.exit(1) self.running = False self.httpServer = None self.interface = None self.interactive = interactive self.registry = None self.registry_cleaner = None self.node_id = None self.mdns = MDNSEngine() self.mdnsname_suffix = '_' + str(HOSTNAME) + "_" + str(getpid()) self.mappings = { "device": "ver_dvc", "flow": "ver_flw", "source": "ver_src", "sender": "ver_snd", "receiver": "ver_rcv", "self": "ver_slf" } self.mdns_updater = MDNSUpdater(self.mdns, "_nmos-node._tcp", "node" + self.mdnsname_suffix, self.mappings, PORT, self.logger, txt_recs={ "api_ver": "v1.0,v1.1,v1.2", "api_proto": "http" }) self.aggregator = Aggregator(self.logger, self.mdns_updater) def sig_handler(self): print 'Pressed ctrl+c' self.stop() def sig_hup_handler(self): if getLocalIP() != "": global HOST HOST = updateHost() self.registry.modify_node(href=self.generate_href(), host=HOST, api={ "versions": NODE_APIVERSIONS, "endpoints": self.generate_endpoints() }, interfaces=self.list_interfaces()) def generate_endpoints(self): endpoints = [] if HTTPS_MODE != "enabled": endpoints.append({ "host": HOST, "port": 80, #Everything should go via apache proxy "protocol": "http" }) if HTTPS_MODE != "disabled": endpoints.append({ "host": HOST, "port": 443, #Everything should go via apache proxy "protocol": "https" }) return endpoints def generate_href(self): if HTTPS_MODE == "enabled": return "https://{}/".format(HOST) else: return "http://{}/".format(HOST) def list_interfaces(self): interfaces = {} # Initially populate interfaces from known-good location net_path = "/sys/class/net/" if os.path.exists(net_path): for interface_name in os.listdir(net_path): if interface_name != "lo": address_path = net_path + interface_name + "/address" if os.path.exists(address_path): address = open(address_path, "r").readline() interfaces[interface_name] = { "name": interface_name, "chassis_id": None, "port_id": address.lower().strip("\n").replace(":", "-") } # Attempt to source proper LLDP data for interfaces if os.path.exists("/usr/sbin/lldpcli"): try: chassis_data = json.loads( check_output( ["/usr/sbin/lldpcli", "show", "chassis", "-f", "json"])) chassis_id = chassis_data["local-chassis"]['chassis'].values( )[0]["id"]["value"] if chassis_data["local-chassis"]['chassis'].values( )[0]["id"]["type"] == "mac": chassis_id = chassis_id.lower().replace(":", "-") interface_data = json.loads( check_output([ "/usr/sbin/lldpcli", "show", "statistics", "-f", "json" ])) if isinstance(interface_data["lldp"]["interface"], dict): for interface_name in interface_data["lldp"][ "interface"].keys(): if interface_name in interfaces: # Only correct the Chassis ID. Port ID MUST be a MAC address interfaces[interface_name][ "chassis_id"] = chassis_id else: for interface_block in interface_data["lldp"]["interface"]: interface_name = interface_block.keys()[0] if interface_name in interfaces: # Only correct the Chassis ID. Port ID MUST be a MAC address interfaces[interface_name][ "chassis_id"] = chassis_id except Exception: pass return interfaces.values() def start(self): if self.running: gevent.signal(signal.SIGINT, self.sig_handler) gevent.signal(signal.SIGTERM, self.sig_handler) gevent.signal(signal.SIGHUP, self.sig_hup_handler) self.mdns.start() self.node_id = get_node_id() node_version = str(ptptime.ptp_detail()[0]) + ":" + str( ptptime.ptp_detail()[1]) node_data = { "id": self.node_id, "label": nmoscommonconfig.config.get('node_label', FQDN), "description": nmoscommonconfig.config.get('node_description', "Node on {}".format(FQDN)), "tags": nmoscommonconfig.config.get('node_tags', {}), "href": self.generate_href(), "host": HOST, "services": [], "hostname": HOSTNAME, "caps": {}, "version": node_version, "api": { "versions": NODE_APIVERSIONS, "endpoints": self.generate_endpoints(), }, "clocks": [ { "name": "clk0", "ref_type": "internal", }, { "name": "clk1", "ref_type": "ptp", "version": "IEEE1588-2008", "traceable": False, "gmid": "00-00-00-00-00-00-00-00", "locked": False, }, # Extra values will be filled in as needed at point of checking ], "interfaces": self.list_interfaces() } self.registry = FacadeRegistry(self.mappings.keys(), self.aggregator, self.mdns_updater, self.node_id, node_data, self.logger) self.registry_cleaner = FacadeRegistryCleaner(self.registry) self.registry_cleaner.start() self.httpServer = HttpServer(FacadeAPI, PORT, '0.0.0.0', api_args=[self.registry]) self.httpServer.start() while not self.httpServer.started.is_set(): self.logger.writeInfo('Waiting for httpserver to start...') self.httpServer.started.wait() if self.httpServer.failed is not None: raise self.httpServer.failed self.logger.writeInfo("Running on port: {}".format( self.httpServer.port)) try: self.logger.writeInfo("Registering as {}...".format(self.node_id)) self.aggregator.register( 'node', self.node_id, **legalise_resource(node_data, "node", NODE_REGVERSION)) except Exception as e: self.logger.writeWarning("Could not register: {}".format( e.__repr__())) self.interface = FacadeInterface(self.registry, self.logger) self.interface.start() def run(self): self.running = True pidfile = "/tmp/ips-nodefacade.pid" file(pidfile, 'w').write(str(getpid())) self.start() daemon.notify("READY=1") while self.running: self.registry.update_ptp() time.sleep(1) os.unlink(pidfile) self._cleanup() def _cleanup(self): try: self.logger.writeDebug("cleanup: unregister facade " + self.node_id) self.aggregator.unregister('node', self.node_id) except Exception as e: self.logger.writeWarning("Could not unregister: {}".format(e)) if self.mdns: try: self.mdns.stop() except Exception as e: self.logger.writeWarning("Could not stop mdns: {}".format(e)) self.registry_cleaner.stop() self.interface.stop() self.httpServer.stop() self.aggregator.stop() self.logger.writeInfo("Stopped main()") def stop(self): self.running = False
class QueryService: def __init__(self, logger=None): self.running = False self.logger = Logger("regquery") self.logger.writeDebug('Running QueryService') self.config = {"priority": 0} self._load_config() self.mdns = MDNSEngine() self.httpServer = HttpServer(QueryServiceAPI, WS_PORT, '0.0.0.0', api_args=[self.logger]) def start(self): if self.running: gevent.signal(signal.SIGINT, self.sig_handler) gevent.signal(signal.SIGTERM, self.sig_handler) self.running = True self.mdns.start() HOST = getLocalIP() self.logger.writeDebug('Running web socket server on %i' % WS_PORT) self.httpServer.start() while not self.httpServer.started.is_set(): self.logger.writeDebug('Waiting for httpserver to start...') self.httpServer.started.wait() if self.httpServer.failed is not None: raise self.httpServer.failed self.logger.writeDebug("Running on port: {}".format( self.httpServer.port)) priority = self.config["priority"] if not str(priority).isdigit() or priority < 100: priority = 0 self.mdns.register('query_' + str(HOST), '_nmos-query._tcp', DNS_SD_PORT, { "pri": priority, "api_ver": "v1.0,v1.1,v1.2", "api_proto": "http" }) def run(self): self.running = True self.start() while self.running: time.sleep(1) self._cleanup() def _cleanup(self): self.mdns.stop() self.mdns.close() self.httpServer.stop() def sig_handler(self): self.stop() def stop(self): self.running = False def _load_config(self): try: config_file = "/etc/nmos-query/config.json" if os.path.isfile(config_file): f = open(config_file, 'r') extra_config = json.loads(f.read()) self.config.update(extra_config) except Exception as e: self.logger.writeDebug("Exception loading config: {}".format(e))
class NodeFacadeService: def __init__(self, interactive=False): self.logger = Logger("facade", None) if HOST == "": self.logger.writeFatal( "Unable to start facade due to lack of connectivity") sys.exit(1) self.running = False self.httpServer = None self.interface = None self.interactive = interactive self.registry = None self.registry_cleaner = None self.node_id = None self.mdns = MDNSEngine() self.mappings = { "device": "ver_dvc", "flow": "ver_flw", "source": "ver_src", "sender": "ver_snd", "receiver": "ver_rcv", "self": "ver_slf" } self.mdns_updater = None self.auth_registry = AuthRegistry(app=None, scope=ALLOWED_SCOPE) self.protocol = PROTOCOL if PROTOCOL == "https": self.dns_sd_port = DNS_SD_HTTPS_PORT else: self.dns_sd_port = DNS_SD_HTTP_PORT if ENABLE_P2P: self.mdns_updater = MDNSUpdater(self.mdns, DNS_SD_TYPE, DNS_SD_NAME, self.mappings, self.dns_sd_port, self.logger, txt_recs=self._mdns_txt( NODE_APIVERSIONS, self.protocol, OAUTH_MODE)) self.aggregator = Aggregator(self.logger, self.mdns_updater, self.auth_registry) def _mdns_txt(self, versions, protocol, oauth_mode): return { "api_ver": ",".join(versions), "api_proto": protocol, "api_auth": str(oauth_mode).lower() } def sig_handler(self): print('Pressed ctrl+c') self.stop() def sig_hup_handler(self): if getLocalIP() != "": global HOST HOST = updateHost() self.registry.modify_node(href=self.generate_href(), host=HOST, api={ "versions": NODE_APIVERSIONS, "endpoints": self.generate_endpoints() }, interfaces=self.list_interfaces()) def generate_endpoints(self): endpoints = [] endpoints.append({ "host": HOST, "port": self.dns_sd_port, # Everything should go via apache proxy "protocol": self.protocol, "authorization": OAUTH_MODE }) return endpoints def generate_href(self): return "{}://{}/".format(PROTOCOL, HOST) def list_interfaces(self): interfaces = {} # Initially populate interfaces from known-good location net_path = "/sys/class/net/" if os.path.exists(net_path): for interface_name in os.listdir(net_path): address_path = net_path + interface_name + "/address" if os.path.exists(address_path) and interface_name != 'lo': with open(address_path, 'r') as address_file: address = address_file.readline().strip('\n') if address: interfaces[interface_name] = { "name": interface_name, "chassis_id": None, "port_id": address.lower().replace(":", "-") } # Attempt to source proper LLDP data for interfaces if os.path.exists("/usr/sbin/lldpcli"): try: chassis_data = json.loads( check_output( ["/usr/sbin/lldpcli", "show", "chassis", "-f", "json"])) chassis_id = chassis_data["local-chassis"]['chassis'].values( )[0]["id"]["value"] if chassis_data["local-chassis"]['chassis'].values( )[0]["id"]["type"] == "mac": chassis_id = chassis_id.lower().replace(":", "-") interface_data = json.loads( check_output([ "/usr/sbin/lldpcli", "show", "statistics", "-f", "json" ])) if isinstance(interface_data["lldp"]["interface"], dict): for interface_name in interface_data["lldp"][ "interface"].keys(): if interface_name in interfaces: # Only correct the Chassis ID. Port ID MUST be a MAC address interfaces[interface_name][ "chassis_id"] = chassis_id else: for interface_block in interface_data["lldp"]["interface"]: interface_name = interface_block.keys()[0] if interface_name in interfaces: # Only correct the Chassis ID. Port ID MUST be a MAC address interfaces[interface_name][ "chassis_id"] = chassis_id except Exception: pass return list(itervalues(interfaces)) def start(self): if self.running: gevent.signal_handler(signal.SIGINT, self.sig_handler) gevent.signal_handler(signal.SIGTERM, self.sig_handler) gevent.signal_handler(signal.SIGHUP, self.sig_hup_handler) self.mdns.start() self.node_id = get_node_id() node_version = str(ptptime.ptp_detail()[0]) + ":" + str( ptptime.ptp_detail()[1]) node_data = { "id": self.node_id, "label": _config.get('node_label', FQDN), "description": _config.get('node_description', "Node on {}".format(FQDN)), "tags": _config.get('node_tags', {}), "href": self.generate_href(), "host": HOST, "services": [], "hostname": HOSTNAME, "caps": {}, "version": node_version, "api": { "versions": NODE_APIVERSIONS, "endpoints": self.generate_endpoints(), }, "clocks": [], "interfaces": self.list_interfaces() } self.registry = FacadeRegistry(self.mappings.keys(), self.aggregator, self.mdns_updater, self.node_id, node_data, self.logger) self.registry_cleaner = FacadeRegistryCleaner(self.registry) self.registry_cleaner.start() self.httpServer = HttpServer( FacadeAPI, PORT, '0.0.0.0', api_args=[self.registry, self.auth_registry]) self.httpServer.start() while not self.httpServer.started.is_set(): self.logger.writeInfo('Waiting for httpserver to start...') self.httpServer.started.wait() if self.httpServer.failed is not None: raise self.httpServer.failed self.logger.writeInfo("Running on port: {}".format( self.httpServer.port)) try: self.logger.writeInfo("Registering as {}...".format(self.node_id)) self.aggregator.register( 'node', self.node_id, **translate_api_version(node_data, "node", NODE_REGVERSION)) except Exception as e: self.logger.writeWarning("Could not register: {}".format( e.__repr__())) self.interface = FacadeInterface(self.registry, self.logger) self.interface.start() def run(self): self.running = True pidfile = "/tmp/ips-nodefacade.pid" with open(pidfile, 'w') as f: f.write(str(getpid())) self.start() daemon.notify(SYSTEMD_READY) while self.running: self.registry.update_ptp() time.sleep(1) os.unlink(pidfile) def _cleanup(self): try: self.logger.writeDebug("cleanup: unregister facade " + self.node_id) self.aggregator.unregister('node', self.node_id) except Exception as e: self.logger.writeWarning("Could not unregister: {}".format(e)) if self.mdns: try: self.mdns.stop() except Exception as e: self.logger.writeWarning("Could not stop mdns: {}".format(e)) self.registry_cleaner.stop() self.interface.stop() self.httpServer.stop() self.aggregator.stop() self.mdns_updater.stop() self.logger.writeInfo("Stopped main()") def stop(self): self._cleanup() self.running = False
class TestMDNSEngine(unittest.TestCase): def setUp(self): self.dut = MDNSEngine() self.dut.registrationController = MagicMock() self.name = "192.168.0.1_nmos-node._tcp.local." self.type = "_nmos-node._tcp" self.port = 8080 def _helper_build_callback_handler(self): callbackHandler = MagicMock() callbackHandler.entryCollision = MagicMock() callbackHandler.entryFailed = MagicMock() callbackHandler.entryEstablished = MagicMock() return callbackHandler def test_add_registration_handle_errors_zeroconf_collision(self): thrower = self.dut.registrationController.addRegistration = MagicMock() thrower.side_effect = zeroconf.NonUniqueNameException callbackHandler = self._helper_build_callback_handler() self.dut._add_registration_handle_errors(MagicMock(), callbackHandler) self.assertTrue(callbackHandler.entryCollision.called) def test_add_registration_handle_errors_internal_collision(self): thrower = self.dut.registrationController.addRegistration = MagicMock() thrower.side_effect = ServiceAlreadyExistsException callbackHandler = self._helper_build_callback_handler() self.dut._add_registration_handle_errors(MagicMock(), callbackHandler) self.assertTrue(callbackHandler.entryCollision.called) def test_add_registration_handle_errors_fail(self): thrower = self.dut.registrationController.addRegistration = MagicMock() thrower.side_effect = zeroconf.Error callbackHandler = self._helper_build_callback_handler() self.dut._add_registration_handle_errors(MagicMock(), callbackHandler) self.assertTrue(callbackHandler.entryFailed.called) def test_add_registration_handle_errors_ok(self): callbackHandler = self._helper_build_callback_handler() self.dut._add_registration_handle_errors(MagicMock(), callbackHandler) self.assertTrue(callbackHandler.entryEstablished.called) def test_catch_interface_exception(self): with patch('nmoscommon.mdns.mdnsEngine.MDNSRegistration'): with patch( 'nmoscommon.mdns.mdnsEngine.MDNSAdvertisementCallbackHandler' ) as callbackHandler: self.dut._autostart_if_required = MagicMock() callback = callbackHandler.return_value = MagicMock() self.dut._add_registration_handle_errors = MagicMock() callback.entryFailed = MagicMock() self.dut.interfaceController = MagicMock() self.dut.interfaceController.getInterfaces = MagicMock() self.dut.interfaceController.getInterfaces.side_effect = InterfaceNotFoundException self.dut.register(None, None, None, None) self.assertTrue(callback.entryFailed.called) """The class should roll with it if not explicitly started before registering""" def test_implicit_start_register(self): self.dut.register(self.name, self.type, self.port, {}) def test_implicit_start_update(self): with patch('nmoscommon.mdns.mdnsEngine.MDNSRegistrationController'): self.dut.update(self.name, self.type, txtRecord={}) def helper_mock_registration_controller(self): registration = MagicMock() registration.update = MagicMock() controller = MagicMock() controller.__getitem__.side_effect = TypeError controller.registrations = {self.type: {self.name: registration}} controller.update = MagicMock() return controller def test_update_normal(self): self.dut.start() self.dut.registrationController = self.helper_mock_registration_controller( ) regMock = self.dut.registrationController.registrations[self.type][ self.name] self.dut.update(self.name, self.type, {}) self.assertTrue(regMock.update.called) def test_update_record_not_found(self): self.dut.start() with self.assertRaises(ServiceNotFoundException): self.dut.update("not", "this") def test_disable_mdns_discover(self): with patch('nmoscommon.mdns.mdnsEngine.MDNSListener') as listener: self.dut.config = {"mdns_discover": False} callbackHandler = self._helper_build_callback_handler() self.dut._add_registration_handle_errors(MagicMock(), callbackHandler) listener.assert_not_called() def test_disable_dns_discover(self): with patch('nmoscommon.mdns.mdnsEngine.DNSServiceController' ) as controller: self.dut.config = {"dns_discover": False} callbackHandler = self._helper_build_callback_handler() self.dut._add_registration_handle_errors(MagicMock(), callbackHandler) controller.assert_not_called()
class RegistryAggregatorService(object): def __init__(self, logger=None, interactive=False): self.config = {"priority": 0} self._load_config() self.running = False self.httpServer = None self.interactive = interactive self.mdns = MDNSEngine() self.logger = Logger("aggregation", logger) def _load_config(self): try: config_file = "/etc/nmos-registration/config.json" if os.path.isfile(config_file): f = open(config_file, 'r') extra_config = json.loads(f.read()) self.config.update(extra_config) except Exception as e: print "Exception loading config: {}".format(e) def start(self): if self.running: gevent.signal(signal.SIGINT, self.sig_handler) gevent.signal(signal.SIGTERM, self.sig_handler) self.mdns.start() self.httpServer = HttpServer(AggregatorAPI, SERVICE_PORT, '0.0.0.0', api_args=[self.logger, self.config]) self.httpServer.start() while not self.httpServer.started.is_set(): print 'Waiting for httpserver to start...' self.httpServer.started.wait() if self.httpServer.failed is not None: raise self.httpServer.failed print "Running on port: {}".format(self.httpServer.port) priority = self.config["priority"] if not str(priority).isdigit() or priority < 100: priority = 0 self.mdns.register('registration_' + str(HOST), '_nmos-registration._tcp', DNS_SD_PORT, {"pri": priority, "api_ver": "v1.0,v1.1,v1.2", "api_proto": "http"}) def run(self): self.running = True self.start() while self.running: time.sleep(1) self._cleanup() def _cleanup(self): self.mdns.stop() self.mdns.close() self.httpServer.stop() print "Stopped main()" def stop(self): self.running = False def sig_handler(self): print 'Pressed ctrl+c' self.stop()