Example #1
0
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()
Example #2
0
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))
Example #3
0
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()
Example #4
0
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))
Example #6
0
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()