class mDNS(object): def __init__(self, port, mdns_desc, mdns_addr): try: ni.ifaddresses('wlan0') self.ip = ni.ifaddresses('eth0')[ni.AF_INET][0]['addr'] except ValueError: self.ip = socket.gethostbyname(socket.gethostname()) self.port = port desc = { 'version': '0.1', 'base_url': "http://{}:{}/".format(self.ip, str(self.port)), 'path': '/' } self.info = ServiceInfo("_http._tcp.local.", "{}._http._tcp.local.".format(mdns_desc), socket.inet_aton(self.ip), self.port, 0, 0, desc, "{}.".format(mdns_addr)) self.zeroconf = Zeroconf() def register(self): self.zeroconf.register_service(self.info) def unregister(self): self.zeroconf.unregister_service(self.info)
class AdvertiseServer: def __init__(self): self._zeroconf = Zeroconf() self._addresses = [socket.inet_aton(_my_ip())] self._id2info = {} def register_device(self, id, device): info = ServiceInfo(type_="_repl._tcp.local.", name=device.name + "." + "_repl._tcp.local.", port=Config.get_attr('server_port', '34567'), properties={ "uid": device.uid, "name": device.name }, addresses=self._addresses) self._id2info[id] = info self._zeroconf.register_service(info, allow_name_change=True) logger.debug(f"advertise {id}") def unregister_device(self, id): try: logger.debug(f"no longer advertise {id}") info = self._id2info[id] if info: self._zeroconf.unregister_service(info) del self._id2info[id] except AttributeError: pass
class ZeroconfService(): def register(self, port: int): self._zeroconf = Zeroconf() hostname = socket.gethostname() host = socket.gethostbyname(hostname) desc = { 'host': host, 'port': str(port), 'hostname': hostname, } self._info = ServiceInfo( "_mpv-http-router._tcp.local.", "MPV HTTP Router._mpv-http-router._tcp.local.", socket.inet_aton(host), port, 0, 0, desc) self._zeroconf.register_service(self._info) self._registered = True logger.info( "MPV HTTP Router has been registered in the network on %s:%s", hostname, port) def unregister(self): if self._zeroconf is None: return self._zeroconf.unregister_service(self._info) self._zeroconf.close() self._registered = False logger.info("MPV HTTP Router has been unregistered")
def advertise(self): postfix = self.config['global']['service_prefix'] self.port = int(self.config['global']['port']) #print(self.config['device']['hostname']+postfix) info = ServiceInfo(postfix, self.config['device']['hostname']+"."+postfix, socket.inet_aton(self.ip), self.port, 0, 0, {'info': self.config['device']['description']}, "hazc.local.") self.bindConnection() zeroconf = Zeroconf() zeroconf.register_service(info) try: while True: # try: print("Ready") self.conn, self.addr = self.webcontrol.accept() self.listen() self.conn.close() except KeyboardInterrupt: pass finally: print() print("Unregistering...") zeroconf.unregister_service(info) zeroconf.close() try: print("Shutting down socket") self.webcontrol.shutdown(socket.SHUT_RDWR) except Exception as e: print(e)
class Server: def __init__(self, name, address, port, description): self.zeroconf = Zeroconf() self.name = name self.description = description self.address = address self.port = port self.full_name = get_full_name(name) self.info = ServiceInfo(type_=TYPE, name=self.full_name, address=socket.inet_aton(self.address), port=self.port, weight=0, priority=0, properties=self.description) def register(self): logger.info("Broadcast server {} at {}:{}".format( self.full_name, self.address, self.port)) self.zeroconf.register_service(self.info) def unregister(self): self.zeroconf.unregister_service(self.info) def close(self): self.zeroconf.close()
def test_aaaa_query(): """Test that queries for AAAA records work.""" zc = Zeroconf(interfaces=['127.0.0.1']) type_ = "_knownservice._tcp.local." name = "knownname" registration_name = "%s.%s" % (name, type_) desc = {'path': '/~paulsm/'} server_name = "ash-2.local." ipv6_address = socket.inet_pton(socket.AF_INET6, "2001:db8::1") info = ServiceInfo(type_, registration_name, 80, 0, 0, desc, server_name, addresses=[ipv6_address]) zc.register_service(info) _clear_cache(zc) generated = r.DNSOutgoing(const._FLAGS_QR_QUERY) question = r.DNSQuestion(server_name, const._TYPE_AAAA, const._CLASS_IN) generated.add_question(question) packets = generated.packets() _, multicast_out = zc.query_handler.response(r.DNSIncoming(packets[0]), "1.2.3.4", const._MDNS_PORT) assert multicast_out.answers[0][0].address == ipv6_address # unregister zc.unregister_service(info) zc.close()
def test_integration(): service_added = Event() service_removed = Event() type_ = "_http._tcp.local." registration_name = "xxxyyy.%s" % type_ def on_service_state_change(zeroconf, service_type, state_change, name): if name == registration_name: if state_change is ServiceStateChange.Added: service_added.set() elif state_change is ServiceStateChange.Removed: service_removed.set() zeroconf_browser = Zeroconf() browser = ServiceBrowser(zeroconf_browser, type_, [on_service_state_change]) zeroconf_registrar = Zeroconf() desc = {'path': '/~paulsm/'} info = ServiceInfo( type_, registration_name, socket.inet_aton("10.0.1.2"), 80, 0, 0, desc, "ash-2.local.") zeroconf_registrar.register_service(info) try: service_added.wait(1) assert service_added.is_set() zeroconf_registrar.unregister_service(info) service_removed.wait(1) assert service_removed.is_set() finally: zeroconf_registrar.close() browser.cancel() zeroconf_browser.close()
class RegisterCar(): def __init__(self): self.zeroconf = None self.info = None def register_car(self, name): my_ip_address = [ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")][:1] # Link: https://stackoverflow.com/questions/166506/finding-local-ip-addresses-using-pythons-stdlib if len(my_ip_address) > 0: logging.info("Service IP: " + my_ip_address[0]) my_ip_address = socket.inet_aton(my_ip_address[0]) desc = {'name': name} self.info = ServiceInfo("_http._tcp.local.", name + " DrivingMatter._http._tcp.local.", my_ip_address, 80, 0, 0, desc) self.zeroconf = Zeroconf() logging.info("Registration of a service, press Ctrl-C to exit...") self.zeroconf.register_service(self.info) return True else: logging.error("No network interface available, please connect to any network") #raise Exception("No network interface") return False def unregister_car(self): if self.zeroconf: self.zeroconf.unregister_service(self.info) self.zeroconf.close() logging.info("Service unregistered successfully") else: logging.error("No Zeroconf established yet")
def test_01(self): """Node can discover network registration service via mDNS""" test = Test("Node can discover network registration service via mDNS") self.registry.reset() default_gw_interface = netifaces.gateways()['default'][ netifaces.AF_INET][1] default_ip = netifaces.ifaddresses(default_gw_interface)[ netifaces.AF_INET][0]['addr'] # TODO: Set api_ver to just the version under test. Later test support for parsing CSV string txt = {'api_ver': self.test_version, 'api_proto': 'http', 'pri': '0'} info = ServiceInfo("_nmos-registration._tcp.local.", "NMOS Test Suite._nmos-registration._tcp.local.", socket.inet_aton(default_ip), 5000, 0, 0, txt, "nmos-test.local.") zeroconf = Zeroconf() zeroconf.register_service(info) while ( time.time() - self.registry.last_time ) < 5: # Ensure we allow 5 seconds to get at least one heartbeat time.sleep(1) zeroconf.unregister_service(info) zeroconf.close() if len(self.registry.get_data()) > 0: return test.PASS() return test.FAIL( "Node did not attempt to register with the advertised registry.")
def test_integration(): service_added = Event() service_removed = Event() type_ = "_http._tcp.local." registration_name = "xxxyyy.%s" % type_ def on_service_state_change(zeroconf, service_type, state_change, name): if name == registration_name: if state_change is ServiceStateChange.Added: service_added.set() elif state_change is ServiceStateChange.Removed: service_removed.set() zeroconf_browser = Zeroconf() browser = ServiceBrowser(zeroconf_browser, type_, [on_service_state_change]) zeroconf_registrar = Zeroconf() desc = {'path': '/~paulsm/'} info = ServiceInfo(type_, registration_name, socket.inet_aton("10.0.1.2"), 80, 0, 0, desc, "ash-2.local.") zeroconf_registrar.register_service(info) try: service_added.wait(1) assert service_added.is_set() zeroconf_registrar.unregister_service(info) service_removed.wait(1) assert service_removed.is_set() finally: zeroconf_registrar.close() browser.cancel() zeroconf_browser.close()
def run(): try: zeroconf = Zeroconf(ip_version=IPVersion.All, interfaces=InterfaceChoice.All) s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("1.1.1.1", 80)) ip = s.getsockname()[0] info = ServiceInfo( "_http._tcp.local.", "Member Matters Server._http._tcp.local.", addresses=[socket.inet_aton(ip)], properties={"ip": ip}, port=80, server="membermatters.local.", ) zeroconf.register_service(info) except: pass try: while True: time.sleep(0.1) except KeyboardInterrupt: pass finally: print("Unregistering...") zeroconf.unregister_service(info) zeroconf.close()
class GlancesAutoDiscoverClient(object): """Implementation of the zeroconf protocol (client side for the Glances server).""" def __init__(self, hostname, args=None): if zeroconf_tag: zeroconf_bind_address = args.bind_address try: self.zeroconf = Zeroconf() except socket.error as e: logger.error("Cannot start zeroconf: {}".format(e)) # XXX *BSDs: Segmentation fault (core dumped) # -- https://bitbucket.org/al45tair/netifaces/issues/15 if not BSD: try: # -B @ overwrite the dynamic IPv4 choice if zeroconf_bind_address == '0.0.0.0': zeroconf_bind_address = self.find_active_ip_address() except KeyError: # Issue #528 (no network interface available) pass self.info = ServiceInfo( zeroconf_type, '{}:{}.{}'.format(hostname, args.port, zeroconf_type), address=socket.inet_aton(zeroconf_bind_address), port=args.port, weight=0, priority=0, properties={}, server=hostname) try: self.zeroconf.register_service(self.info) except socket.error as e: logger.error( "Error while announcing Glances server: {}".format(e)) else: print( "Announce the Glances server on the LAN (using {} IP address)" .format(zeroconf_bind_address)) else: logger.error( "Cannot announce Glances server on the network: zeroconf library not found." ) @staticmethod def find_active_ip_address(): """Try to find the active IP addresses.""" import netifaces # Interface of the default gateway gateway_itf = netifaces.gateways()['default'][netifaces.AF_INET][1] # IP address for the interface return netifaces.ifaddresses(gateway_itf)[netifaces.AF_INET][0]['addr'] def close(self): if zeroconf_tag: self.zeroconf.unregister_service(self.info) self.zeroconf.close()
def test_ptr_optimization(): # instantiate a zeroconf instance zc = Zeroconf(interfaces=['127.0.0.1']) # service definition type_ = "_test-srvc-type._tcp.local." name = "xxxyyy" registration_name = "%s.%s" % (name, type_) desc = {'path': '/~paulsm/'} info = ServiceInfo( type_, registration_name, 80, 0, 0, desc, "ash-2.local.", addresses=[socket.inet_aton("10.0.1.2")] ) # register zc.register_service(info) # Verify we won't respond for 1s with the same multicast query = r.DNSOutgoing(const._FLAGS_QR_QUERY) query.add_question(r.DNSQuestion(info.type, const._TYPE_PTR, const._CLASS_IN)) unicast_out, multicast_out = zc.query_handler.response( [r.DNSIncoming(packet) for packet in query.packets()], None, const._MDNS_PORT ) assert unicast_out is None assert multicast_out is None # Clear the cache to allow responding again _clear_cache(zc) # Verify we will now respond query = r.DNSOutgoing(const._FLAGS_QR_QUERY) query.add_question(r.DNSQuestion(info.type, const._TYPE_PTR, const._CLASS_IN)) unicast_out, multicast_out = zc.query_handler.response( [r.DNSIncoming(packet) for packet in query.packets()], None, const._MDNS_PORT ) assert multicast_out.id == query.id assert unicast_out is None assert multicast_out is not None has_srv = has_txt = has_a = False nbr_additionals = 0 nbr_answers = len(multicast_out.answers) nbr_authorities = len(multicast_out.authorities) for answer in multicast_out.additionals: nbr_additionals += 1 if answer.type == const._TYPE_SRV: has_srv = True elif answer.type == const._TYPE_TXT: has_txt = True elif answer.type == const._TYPE_A: has_a = True assert nbr_answers == 1 and nbr_additionals == 3 and nbr_authorities == 0 assert has_srv and has_txt and has_a # unregister zc.unregister_service(info) zc.close()
class AirplayServer(HTTPServer): """ Start an Airplay remote server on this device with a specific port. Available Events: - on_remote_command(AirplayCommand) """ def __init__(self, dacp_id, active_remote, port=52485): super(AirplayServer, self).__init__(("", port), RequestHandler) self.dacp_id = dacp_id self.active_remote = active_remote self.on_remote_command = EventHook() hostname = socket.gethostname() local_ip = socket.gethostbyname(hostname) # setup the service information self.info = ServiceInfo(type_=AIRPLAY_ZEROCONF_SERVICE, name="iTunes_Ctrl_{0}.{1}".format( dacp_id, AIRPLAY_ZEROCONF_SERVICE), address=socket.inet_aton(local_ip), port=port, properties={}, server="{0}.local.".format(hostname)) self._zeroconf = Zeroconf() def start(self): """ Start the main http server and register the dacp service. :return: server thread """ thread = Thread(target=self.serve_forever, daemon=True) thread.start() logger.debug("Started HTTP server on port: %s", self.info.port) # register a zeroconf dacp service self._zeroconf.register_service(self.info) logger.debug("Published service with info: %s", self.info) return thread def stop(self): """ Close the current server. :return: """ # unregister zero conf service self._zeroconf.unregister_service(self.info) self._zeroconf.close() logger.debug("Removed service with info: %s", self.info) # shutdown the http server self.shutdown() logger.debug("Stopped HTTP server on port: %s", self.info.port)
class GlancesAutoDiscoverClient(object): """Implementation of the zeroconf protocol (client side for the Glances server).""" def __init__(self, hostname, args=None): if zeroconf_tag: zeroconf_bind_address = args.bind_address try: self.zeroconf = Zeroconf() except socket.error as e: logger.error("Cannot start zeroconf: {0}".format(e)) if netifaces_tag: # -B @ overwrite the dynamic IPv4 choice if zeroconf_bind_address == '0.0.0.0': zeroconf_bind_address = self.find_active_ip_address() else: logger.error( "Couldn't find the active IP address: netifaces library not found." ) logger.info( "Announce the Glances server on the LAN (using {0} IP address)" .format(zeroconf_bind_address)) print( "Announce the Glances server on the LAN (using {0} IP address)" .format(zeroconf_bind_address)) self.info = ServiceInfo( zeroconf_type, '{0}:{1}.{2}'.format(hostname, args.port, zeroconf_type), address=socket.inet_aton(zeroconf_bind_address), port=args.port, weight=0, priority=0, properties={}, server=hostname) self.zeroconf.register_service(self.info) else: logger.error( "Cannot announce Glances server on the network: zeroconf library not found." ) def find_active_ip_address(self): """Try to find the active IP addresses.""" try: # Interface of the default gateway gateway_itf = netifaces.gateways()['default'][netifaces.AF_INET][1] # IP address for the interface return netifaces.ifaddresses(gateway_itf)[ netifaces.AF_INET][0]['addr'] except Exception: return None def close(self): if zeroconf_tag: self.zeroconf.unregister_service(self.info) self.zeroconf.close()
def mDNS(): #zerconf info #logging.basicConfig(level=logging.DEBUG) #logging.getLogger('zeroconf').setLevel(logging.DEBUG) info = [] zeroconf = Zeroconf(InterfaceChoice.All) output = subprocess.check_output('for /f "usebackq tokens=2 delims=:" %f in (`ipconfig ^| findstr /c:"IPv4 Address"`) do @echo off && echo%f',shell=True , stderr=subprocess.STDOUT,stdin=subprocess.PIPE).decode("utf-8") #F**K PYTHON output = output[:-1] i = 0 infs = sorted(output.split('\n')) for ip in output.split('\n'): i += 1 print(ip) info.append(ServiceInfo( "_coap._udp.local.", "hwmon" + str(i) + "._coap._udp.local.", addresses=[socket.inet_aton(ip)], port=5683, server="hwmon" + str(i) + ".local.", #host_ttl=0, #other_ttl=0, )) zeroconf.register_service(info[-1]) print("service started") while (stop_threads == False): output = subprocess.check_output('for /f "usebackq tokens=2 delims=:" %f in (`ipconfig ^| findstr /c:"IPv4 Address"`) do @echo off && echo%f',shell=True , stderr=subprocess.STDOUT,stdin=subprocess.PIPE).decode("utf-8") #F**K PYTHON TWICE output = output[:-1] i = 0 if(sorted(output.split('\n')) != infs): for inf in info: try: zeroconf.unregister_service(inf) except : pass zeroconf.unregister_all_services() info.clear() print("new interface") for ip in output.split('\n'): i += 1 print(ip) info.append(ServiceInfo( "_coap._udp.local.", "hwmon" + str(i) + "._coap._udp.local.", addresses=[socket.inet_aton(ip)], port=5683, server="hwmon" + str(i) + ".local.", #host_ttl=0, #other_ttl=0, )) zeroconf.register_service(info[-1]) infs = sorted(output.split('\n')) time.sleep(1) zeroconf.close()
def test_ptr_optimization(): # instantiate a zeroconf instance zc = Zeroconf(interfaces=['127.0.0.1']) # service definition type_ = "_test-srvc-type._tcp.local." name = "xxxyyy" registration_name = "%s.%s" % (name, type_) desc = {'path': '/~paulsm/'} info = ServiceInfo(type_, registration_name, socket.inet_aton("10.0.1.2"), 80, 0, 0, desc, "ash-2.local.") # we are going to monkey patch the zeroconf send to check packet sizes old_send = zc.send nbr_answers = nbr_additionals = nbr_authorities = 0 has_srv = has_txt = has_a = False def send(out, addr=r._MDNS_ADDR, port=r._MDNS_PORT): """Sends an outgoing packet.""" nonlocal nbr_answers, nbr_additionals, nbr_authorities nonlocal has_srv, has_txt, has_a nbr_answers += len(out.answers) nbr_authorities += len(out.authorities) for answer in out.additionals: nbr_additionals += 1 if answer.type == r._TYPE_SRV: has_srv = True elif answer.type == r._TYPE_TXT: has_txt = True elif answer.type == r._TYPE_A: has_a = True old_send(out, addr=addr, port=port) # monkey patch the zeroconf send setattr(zc, "send", send) # register zc.register_service(info) nbr_answers = nbr_additionals = nbr_authorities = 0 # query query = r.DNSOutgoing(r._FLAGS_QR_QUERY | r._FLAGS_AA) query.add_question(r.DNSQuestion(info.type, r._TYPE_PTR, r._CLASS_IN)) zc.handle_query(r.DNSIncoming(query.packet()), r._MDNS_ADDR, r._MDNS_PORT) assert nbr_answers == 1 and nbr_additionals == 3 and nbr_authorities == 0 assert has_srv and has_txt and has_a # unregister zc.unregister_service(info)
class YokeDrv: def __init__(self, daemon): self.daemon = daemon self.controller = None self.zeroconf = Zeroconf() self.trecv = time.time() self.irecv = 0 log.debug("Registering zeroconf service...") # open udp socket on random available port self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 512) # small buffer for low latency self.sock.bind((YokeDrv.get_ip_address(), 0)) self.sock.settimeout(0) adr, port = self.sock.getsockname() # self.daemon.add_mainloop(self.mainloop) poller = self.daemon.get_poller() poller.register(self.sock.fileno(), poller.POLLIN, self.on_data_ready) # create zeroconf service stype = "_yoke._udp.local." netname = socket.gethostname() + "-sc-controller" fullname = netname + "." + stype self.info = ServiceInfo(stype, fullname, socket.inet_aton(adr), port, 0, 0, {}, fullname) self.zeroconf.register_service(self.info, ttl=10) log.info('To connect, select "%s" on your device.' % (netname, )) @staticmethod def get_ip_address(): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("8.8.8.8", 80)) ip = s.getsockname()[0] s.close() return ip def on_exit(self, *a): self.daemon.get_poller().unregister(self.sock.fileno()) if self.sock is not None: self.sock.close() if self.info is not None: self.zeroconf.unregister_service(self.info) log.debug("Unregistered zeroconf service.") def on_data_ready(self, *a): try: m, address = self.sock.recvfrom(128) if self.controller is None: self.controller = YokeController(self, address) self.daemon.add_controller(self.controller) self.controller.input(m) except Exception, e: log.exception(e)
class GlancesAutoDiscoverClient(object): """Implementation of the zeroconf protocol (client side for the Glances server).""" def __init__(self, hostname, args=None): if zeroconf_tag: zeroconf_bind_address = args.bind_address try: self.zeroconf = Zeroconf() except socket.error as e: logger.error("Cannot start zeroconf: {}".format(e)) # XXX *BSDs: Segmentation fault (core dumped) # -- https://bitbucket.org/al45tair/netifaces/issues/15 if not BSD: try: # -B @ overwrite the dynamic IPv4 choice if zeroconf_bind_address == '0.0.0.0': zeroconf_bind_address = self.find_active_ip_address() except KeyError: # Issue #528 (no network interface available) pass # Check IP v4/v6 address_family = socket.getaddrinfo(zeroconf_bind_address, args.port)[0][0] # Start the zeroconf service self.info = ServiceInfo( zeroconf_type, '{}:{}.{}'.format(hostname, args.port, zeroconf_type), address=socket.inet_pton(address_family, zeroconf_bind_address), port=args.port, weight=0, priority=0, properties={}, server=hostname) try: self.zeroconf.register_service(self.info) except socket.error as e: logger.error("Error while announcing Glances server: {}".format(e)) else: print("Announce the Glances server on the LAN (using {} IP address)".format(zeroconf_bind_address)) else: logger.error("Cannot announce Glances server on the network: zeroconf library not found.") @staticmethod def find_active_ip_address(): """Try to find the active IP addresses.""" import netifaces # Interface of the default gateway gateway_itf = netifaces.gateways()['default'][netifaces.AF_INET][1] # IP address for the interface return netifaces.ifaddresses(gateway_itf)[netifaces.AF_INET][0]['addr'] def close(self): if zeroconf_tag: self.zeroconf.unregister_service(self.info) self.zeroconf.close()
class ZeroconfService(AbstractZeroconfService): """ :class:`ZeroconfService` uses `python zeroconf`_ .. _python zeroconf: https://pypi.org/project/zeroconf/ Install:: .. code-block:: bash pip install zeroconf """ def __init__(self, name, port): super(ZeroconfService, self).__init__(name, port) self._zeroconf = None self._infos = [] @classmethod def has_support(cls): return support def start(self): self._zeroconf = Zeroconf() for index, ip in enumerate(self.ips): info = self._gerenate_service_info(index, ip) self._infos.append(info) self._zeroconf.register_service(info) self._log('Zeroconf {} - Registered service: name={}, regtype={}, domain={}', self.__class__.__name__, self.name, self.type, 'local.') self._log(' Network: {}', ip) def _gerenate_service_info(self, index, ip): name = '{}-{}.{}.local.'.format(self.name.lower(), index, self.type, '.local.') return ServiceInfo( self.type + '.local.', name, socket.inet_aton(ip), self.port, 0, 0, {} ) def close(self): for info in self._infos: self._zeroconf.unregister_service(info)
def bootstrap(self): try: zeroconf = Zeroconf(interfaces=[self.bootstrap_ip]) self._log.debug('Zeroconf instantiated.') except: self._log.error( 'Error instantiating zeroconf instance on interface: {0} with IP: {1}' .format(self.iface, self.bootstrap_ip)) raise try: zeroconf.register_service(self.service_discovery_def) discovery_browser = ServiceBrowser( zeroconf, self.service_discovery_type, handlers=[self._election_handler]) leader_browser = ServiceBrowser(zeroconf, self.service_leader_type, handlers=[self._election_handler]) except: self._log.error( 'Error encountered with registered zeroconf services or browser.' ) raise while True: sleep(.1) if self._discovery_peer_count <= 0: log.debug('All peers unregistered from discovery service.') break if self.election_id == self._leader_id: zeroconf.unregister_service(self.service_leader_def) self._log.debug('Leader service unregistered.') sleep(1) try: zeroconf.close() except: self._log.error('Error encountered closing zerconf instance.') raise self._log.debug('Zerconf instance close.') if self.election_id == self._leader_id: bootstrap_command = [self.action] + self.get_peer_addresses( self._peers) self._log.info('Leader performing bootstrap action: {}'.format( ' '.join(bootstrap_command))) Popen(bootstrap_command)
def main(): logging.basicConfig(level=logging.DEBUG) parser = argparse.ArgumentParser() parser.add_argument('--debug', action='store_true') parser.add_argument("--name", dest="name", default=None, help="mdns service name") version_group = parser.add_mutually_exclusive_group() version_group.add_argument('--v6', action='store_true') version_group.add_argument('--v6-only', action='store_true') args = parser.parse_args() if args.debug: logging.getLogger('zeroconf').setLevel(logging.DEBUG) if args.v6: ip_version = IPVersion.All elif args.v6_only: ip_version = IPVersion.V6Only else: ip_version = IPVersion.V4Only properties = {'who': 'codelab'} # 详细信息 name = args.name if args.name else uuid.uuid4().hex[:8] service_type = "_http._tcp.local." service_name = f"{name}._http._tcp.local." server = f"{name}.local." port = 12358 info = ServiceInfo( service_type, service_name, addresses=[socket.inet_aton(get_local_ip())], port=port, properties=properties, server=server, ) print(info) zeroconf = Zeroconf(ip_version=ip_version) print("Registration of a service, press Ctrl-C to exit...") zeroconf.register_service(info) try: while True: sleep(0.1) except KeyboardInterrupt: pass finally: print("Unregistering...") zeroconf.unregister_service(info) zeroconf.close()
def __start_advertising(self): self.service = ServiceInfo("{}._tcp.local.".format(self.type), "{}.{}._tcp.local.".format(self.name, self.type), socket.inet_aton(self.address), self.port, 0, 0, self.properties, "{}.local.".format(self.server)) zeroconf = Zeroconf() zeroconf.register_service(self.service) while self.alive: time.sleep(.1) zeroconf.unregister_service(self.service) zeroconf.close()
def mDNS(): #zerconf info infomqtt = ServiceInfo( "_mqtt._tcp.local.", "hwmon._mqtt._tcp.local.", addresses=[ socket.inet_aton(socket.gethostbyname(socket.gethostname())) ], port=1883, server="hwmon.local.", ) infocoap = ServiceInfo( "_coap._udp.local.", "hwmon._coap._udp.local.", addresses=[ socket.inet_aton(socket.gethostbyname(socket.gethostname())) ], port=5683, server="hwmon.local.", ) zeroconf = Zeroconf(ip_version=IPVersion.All) while (stop_threads == False): try: zeroconf.unregister_service(infomqtt) zeroconf.unregister_service(infocoap) zeroconf.close() zeroconf.close() zeroconf = Zeroconf(ip_version=IPVersion.All) infomqtt.addresses = [ socket.inet_aton(socket.gethostbyname(socket.gethostname())) ] infocoap.addresses = [ socket.inet_aton(socket.gethostbyname(socket.gethostname())) ] zeroconf.register_service(infomqtt) zeroconf.register_service(infocoap) except: pass while (infocoap.addresses == [ socket.inet_aton(socket.gethostbyname(socket.gethostname())) ] and stop_threads == False): try: client.publish("display/hwmon", json.dumps(data)) time.sleep(0.2) except: pass zeroconf.unregister_service(infomqtt) zeroconf.unregister_service(infocoap) zeroconf.close()
class MDNSServer: """Announce accessory on the network via mDNS / DNS-SD To debug service: avahi-browse -r -k _hap._tcp """ # TODO: restart / reload zeroconf on config change def __init__(self, config: Config) -> None: self.zeroconf = Zeroconf() self.hap_service: ServiceInfo = None self.config = config def update_service(self): self.hap_service = ServiceInfo( type_=self.config.service_type, name=f'{self.config.model_name}.{self.config.service_type}', address=inet_aton(self.config.server_ip), port=self.config.server_port, properties={ 'c#': str(self.config.configuration_number), 'ff': '0', # feature flag: enable HAP pairing # TODO: disable pairing once paired 'id': self.config.device_id, 'md': self.config.model_name, 'pv': '1.0', # protocol version 's#': '1', # current state number, this must have a value of '1' 'sf': str(StatusFlag.not_paired.value), # pylint: disable=no-member 'ci': str(AccessoryCategory.bridge.value ), # accessory category identifier }, ) def start(self): """Start announcing accessory on the network""" self.update_service() self.zeroconf.register_service(self.hap_service) def restart(self): self.stop() self.update_service() self.start() def stop(self): self.zeroconf.unregister_service(self.hap_service) def close(self): self.zeroconf.close()
class RegisterCar(): def __init__(self): logging.info("RegisterCar.__init__()") self.zeroconf = None self.info = None def register_car(self, name, port=8000): logging.info("RegisterCar.register_car()") my_ip_address = None while not my_ip_address: try: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("8.8.8.8", 80)) my_ip_address = s.getsockname()[0] s.close() except Exception: sleep(1) my_ip_address = None if len(my_ip_address) > 0: logging.info("Service IP: " + my_ip_address) my_ip_address = socket.inet_aton(my_ip_address) desc = {'name': name} self.info = ServiceInfo("_http._tcp.local.", name + " DrivingMatter._http._tcp.local.", my_ip_address, port, 0, 0, desc) self.zeroconf = Zeroconf() logging.info("Registration of a service, press Ctrl-C to exit...") self.zeroconf.register_service(self.info) return True else: logging.error( "No network interface available, please connect to any network" ) #raise Exception("No network interface") return False def unregister_car(self): if self.zeroconf: self.zeroconf.unregister_service(self.info) self.zeroconf.close() logging.info("Service unregistered successfully") else: logging.error("No Zeroconf established yet")
def test_unicast_response(): """Ensure we send a unicast response when the source port is not the MDNS port.""" # instantiate a zeroconf instance zc = Zeroconf(interfaces=['127.0.0.1']) # service definition type_ = "_test-srvc-type._tcp.local." name = "xxxyyy" registration_name = "%s.%s" % (name, type_) desc = {'path': '/~paulsm/'} info = ServiceInfo(type_, registration_name, 80, 0, 0, desc, "ash-2.local.", addresses=[socket.inet_aton("10.0.1.2")]) # register zc.register_service(info) _clear_cache(zc) # query query = r.DNSOutgoing(const._FLAGS_QR_QUERY | const._FLAGS_AA) query.add_question( r.DNSQuestion(info.type, const._TYPE_PTR, const._CLASS_IN)) unicast_out, multicast_out = zc.query_handler.response( r.DNSIncoming(query.packets()[0]), "1.2.3.4", 1234) for out in (unicast_out, multicast_out): assert out.id == query.id has_srv = has_txt = has_a = False nbr_additionals = 0 nbr_answers = len(out.answers) nbr_authorities = len(out.authorities) for answer in out.additionals: nbr_additionals += 1 if answer.type == const._TYPE_SRV: has_srv = True elif answer.type == const._TYPE_TXT: has_txt = True elif answer.type == const._TYPE_A: has_a = True assert nbr_answers == 1 and nbr_additionals == 3 and nbr_authorities == 0 assert has_srv and has_txt and has_a # unregister zc.unregister_service(info) zc.close()
class HSMZeroConfSetup(object): def __init__(self, ip_addr, serial, firmware_version, sd_version): self.zeroconf = None self.registered = False caps = "" try: with open("/etc/diamond-hsm/caps", "rt") as fp: for line in fp: caps = "%s %s" % (caps, line.strip("\r\n")) except: pass desc = { 'serial': serial, 'host': 'dks-hsm', 'type': 'Diamond HSM Prototype', 'firmware': firmware_version, 'sd': sd_version, 'IP': ip_addr, 'caps': caps } self.service_info = ServiceInfo("_dks-hsm-cty._tcp.local.", "%s._dks-hsm-cty._tcp.local." % serial, socket.inet_aton(ip_addr), 8081, 0, 0, desc, "dkey.local.") atexit.register(self.unregister_service) def register_service(self): if (not self.registered): print "zeroconf registered" self.zeroconf = Zeroconf() self.zeroconf.register_service(self.service_info) self.registered = True def unregister_service(self): if (self.registered): print "zeroconf unregistered" self.zeroconf.unregister_service(self.service_info) self.zeroconf.close() self.zeroconf = None self.registered = False
def createService(): zeroconf = Zeroconf() # Look up info's __init__ in python-zeroconf's documentation # info = ServiceInfo("_http._tcp.local.", "Takiyaki._http._tcp.local.", socket.inet_aton(socket.gethostbyname(socket.gethostname())), 8080,0,0,socket.gethostname() + ".local.") # Server is supported but not compulsory, set server as inputted name # print "Registered Service [" + info.name + "]" zeroconf.register_service(info) try: while True: sleep(0.1) except KeyboardInterrupt: zeroconf.unregister_service(info) print("Unregistered") zeroconf.close() print("Service registered") zeroconf.close()
class Advertisement(object): def __init__(self, ip=None): """ :ip: if string `ip` given, register on given IP (if None: default route's IP). """ self.zeroconf = Zeroconf() self.info = build_service_info(ip=ip or default_route_interface()) def register(self): """Registers the service on the network. """ self.zeroconf.register_service(self.info) log.debug("Registered {} on {}:{}".format( self.info.name, self.ip, self.info.port )) def unregister(self): """Unregisters the service. """ self.zeroconf.unregister_service(self.info) log.debug("Unregistered touchoscbridge.") def update(self, ip=None): """Re-register the the service on the network. :ip: if string `ip` is given, use given IP when registering. """ self.unregister() self.info = build_service_info(ip=ip or default_route_interface()) self.register() def close(self): """Free resources. Advertisement.unregister() should be called before closing. """ self.zeroconf.close() def get_ip(self): """:return: the service's IP as a string. """ return socket.inet_ntoa(self.info.address) ip = property(get_ip)
class GlancesAutoDiscoverClient(object): """Implementation of the zeroconf protocol (client side for the Glances server).""" def __init__(self, hostname, args=None): if zeroconf_tag: zeroconf_bind_address = args.bind_address try: self.zeroconf = Zeroconf() except socket.error as e: logger.error("Cannot start zeroconf: {0}".format(e)) try: # -B @ overwrite the dynamic IPv4 choice if zeroconf_bind_address == '0.0.0.0': zeroconf_bind_address = self.find_active_ip_address() except KeyError: # Issue #528 (no network interface available) pass print("Announce the Glances server on the LAN (using {0} IP address)".format(zeroconf_bind_address)) self.info = ServiceInfo( zeroconf_type, '{0}:{1}.{2}'.format(hostname, args.port, zeroconf_type), address=socket.inet_aton(zeroconf_bind_address), port=args.port, weight=0, priority=0, properties={}, server=hostname) self.zeroconf.register_service(self.info) else: logger.error("Cannot announce Glances server on the network: zeroconf library not found.") @staticmethod def find_active_ip_address(): """Try to find the active IP addresses.""" if not 'freebsd' in sys.platform: import netifaces # Interface of the default gateway gateway_itf = netifaces.gateways()['default'][netifaces.AF_INET][1] # IP address for the interface return netifaces.ifaddresses(gateway_itf)[netifaces.AF_INET][0]['addr'] else: raise KeyError, 'On FreeBSD, this would segfault' def close(self): if zeroconf_tag: self.zeroconf.unregister_service(self.info) self.zeroconf.close()
def start(self): """ Start the service """ http_info = ServiceInfo('_http._tcp.local.', 'OpenLP._http._tcp.local.', address=self.address, port=self.http_port, properties={}) ws_info = ServiceInfo('_ws._tcp.local.', 'OpenLP._ws._tcp.local.', address=self.address, port=self.ws_port, properties={}) zc = Zeroconf() zc.register_service(http_info) zc.register_service(ws_info) self._can_run = True while self.can_run(): sleep(0.1) zc.unregister_service(http_info) zc.unregister_service(ws_info) zc.close() self.quit.emit()
class Discovery: def __init__(self, zeroconf_name="", address="127.0.0.1", port=None, model_name="", uuid="", device_name=""): service_type = "._googlecast._tcp.local." service_name = zeroconf_name + service_type address_bytes = socket.inet_aton(address) self._zeroconf = None self._service = ServiceInfo(service_type, service_name, address_bytes, port, properties={ "md": model_name, "id": uuid, "fn": device_name, }) def run(self): if self._zeroconf is not None: return logger.info("starting discovery") try: self._zeroconf = Zeroconf() self._zeroconf.register_service(self._service) except KeyboardInterrupt: pass def stop(self): if self._zeroconf is None: return logger.info("stopping discovery") self._zeroconf.unregister_service(self._service) self._zeroconf.close() self._zeroconf = None
class ZeroconfService(AbstractZeroconfService): """ :class:`ZeroconfService` uses `python zeroconf`_ .. _python zeroconf: https://pypi.org/project/zeroconf/ Install:: .. code-block:: bash pip install zeroconf """ def __init__(self, name, port): super(ZeroconfService, self).__init__(name, port) self._zeroconf = None self._infos = [] @classmethod def has_support(cls): return support def start(self): self._zeroconf = Zeroconf() for index, ip in enumerate(self.ips): info = self._gerenate_service_info(index, ip) self._infos.append(info) self._zeroconf.register_service(info) self._log( 'Zeroconf {} - Registered service: name={}, regtype={}, domain={}', self.__class__.__name__, self.name, self.type, 'local.') self._log(' Network: {}', ip) def _gerenate_service_info(self, index, ip): name = '{}-{}.{}.local.'.format(self.name.lower(), index, self.type, '.local.') return ServiceInfo(self.type + '.local.', name, socket.inet_aton(ip), self.port, 0, 0, {}) def close(self): for info in self._infos: self._zeroconf.unregister_service(info)
class GlancesAutoDiscoverClient(object): """Implementation of the zeroconf protocol (client side for the Glances server).""" def __init__(self, hostname, args=None): if zeroconf_tag: zeroconf_bind_address = args.bind_address try: self.zeroconf = Zeroconf() except socket.error as e: logger.error("Cannot start zeroconf: {0}".format(e)) if netifaces_tag: # -B @ overwrite the dynamic IPv4 choice if zeroconf_bind_address == '0.0.0.0': zeroconf_bind_address = self.find_active_ip_address() else: logger.error("Couldn't find the active IP address: netifaces library not found.") logger.info("Announce the Glances server on the LAN (using {0} IP address)".format(zeroconf_bind_address)) print("Announce the Glances server on the LAN (using {0} IP address)".format(zeroconf_bind_address)) self.info = ServiceInfo( zeroconf_type, '{0}:{1}.{2}'.format(hostname, args.port, zeroconf_type), address=socket.inet_aton(zeroconf_bind_address), port=args.port, weight=0, priority=0, properties={}, server=hostname) self.zeroconf.register_service(self.info) else: logger.error("Cannot announce Glances server on the network: zeroconf library not found.") def find_active_ip_address(self): """Try to find the active IP addresses.""" try: # Interface of the default gateway gateway_itf = netifaces.gateways()['default'][netifaces.AF_INET][1] # IP address for the interface return netifaces.ifaddresses(gateway_itf)[netifaces.AF_INET][0]['addr'] except Exception: return None def close(self): if zeroconf_tag: self.zeroconf.unregister_service(self.info) self.zeroconf.close()
class Advertisement(object): def __init__(self, ip=None): """ :ip: if string `ip` given, register on given IP (if None: default route's IP). """ self.zeroconf = Zeroconf() self.info = build_service_info(ip=ip or main_ip()) def register(self): """Registers the service on the network. """ self.zeroconf.register_service(self.info) log.debug("Registered {} on {}:{}".format(self.info.name, self.ip, self.info.port)) def unregister(self): """Unregisters the service. """ self.zeroconf.unregister_service(self.info) log.debug("Unregistered touchoscbridge.") def update(self, ip=None): """Re-register the the service on the network. :ip: if string `ip` is given, use given IP when registering. """ self.unregister() self.info = build_service_info(ip=ip or main_ip()) self.register() def close(self): """Free resources. Advertisement.unregister() should be called before closing. """ self.zeroconf.close() def get_ip(self): """:return: the service's IP as a string. """ return socket.inet_ntoa(self.info.address) ip = property(get_ip)
class BonjourService: ip_version = IPVersion.V4Only def __init__(self, host, type, domain, port, ips): self.info = ServiceInfo(type + domain, host + '.' + type + domain, addresses=[socket.inet_aton(ip) for ip in ips], port=port, properties={'path': '/~paulsm/'}, server=host + '.' + domain) self.zeroconf = Zeroconf(ip_version=self.ip_version) def registration(self): self.zeroconf.register_service(self.info) def close(self): self.zeroconf.unregister_service(self.info) self.zeroconf.close()
def __start_advertising(self): self.service = ServiceInfo("{}._tcp.local.".format(self.type), "{}.{}._tcp.local.".format( self.name, self.type), port=self.port, weight=0, priority=0, properties=self.properties, server="{}.local.".format(self.server), addresses=[socket.inet_aton(self.address)]) zeroconf = Zeroconf() zeroconf.register_service(self.service) while self.alive: time.sleep(.5) zeroconf.unregister_service(self.service) zeroconf.close()
class ServiceDiscoveryServer: def __init__(self, port): inet = netifaces.AF_INET hostname = socket.gethostname() def_gw = netifaces.gateways()['default'][inet][1] addr = netifaces.ifaddresses(def_gw)[inet][0]['addr'] addr = socket.inet_aton(addr) self.zeroconf = Zeroconf() self.info = ServiceInfo( SRV_TYPE, srv_fqname(), addr, port, 0, 0, {}, '{}.local.'.format(hostname) ) def start(self): self.zeroconf.register_service(self.info) def stop(self): self.zeroconf.unregister_service(self.info) self.zeroconf.close()
def test_integration(): service_added = Event() service_removed = Event() type_ = "_http._tcp.local." registration_name = "xxxyyy.%s" % type_ class MyListener(object): def remove_service(self, zeroconf, type_, name): if name == registration_name: service_removed.set() def add_service(self, zeroconf, type_, name): if name == registration_name: service_added.set() zeroconf_browser = Zeroconf() listener = MyListener() browser = ServiceBrowser(zeroconf_browser, type_, listener) zeroconf_registrar = Zeroconf() desc = {'path': '/~paulsm/'} info = ServiceInfo( type_, registration_name, socket.inet_aton("10.0.1.2"), 80, 0, 0, desc, "ash-2.local.") zeroconf_registrar.register_service(info) try: service_added.wait(1) assert service_added.is_set() zeroconf_registrar.unregister_service(info) service_removed.wait(1) assert service_removed.is_set() finally: zeroconf_registrar.close() browser.cancel() zeroconf_browser.close()
def bootstrap(self): try: zeroconf = Zeroconf(interfaces=[ self.bootstrap_ip ]) self._log.debug('Zeroconf instantiated.') except: self._log.error('Error instantiating zeroconf instance on interface: {0} with IP: {1}'.format(self.iface, self.bootstrap_ip)) raise try: zeroconf.register_service(self.service_discovery_def) discovery_browser = ServiceBrowser(zeroconf, self.service_discovery_type, handlers=[ self._election_handler ]) leader_browser = ServiceBrowser(zeroconf, self.service_leader_type, handlers=[ self._election_handler ]) except: self._log.error('Error encountered with registered zeroconf services or browser.') raise while True: sleep(.1) if self._discovery_peer_count <= 0: log.debug('All peers unregistered from discovery service.') break if self.election_id == self._leader_id: zeroconf.unregister_service(self.service_leader_def) self._log.debug('Leader service unregistered.') sleep(1) try: zeroconf.close() except: self._log.error('Error encountered closing zerconf instance.') raise self._log.debug('Zerconf instance close.') if self.election_id == self._leader_id: bootstrap_command = [ self.action ] + self.get_peer_addresses(self._peers) self._log.info('Leader performing bootstrap action: {}'.format(' '.join(bootstrap_command))) Popen(bootstrap_command)
class ZstStage(ZstNode): def __init__(self, stageName="stage", port=6000): ZstNode.__init__(self, stageName) self.zeroconf = Zeroconf() address = "tcp://*:" + str(port) self.reply.socket.bind(address) desc = {'name': self.name} addr = socket.gethostbyname(socket.gethostname()) servicename = "ShowtimeStage" self.stageServiceInfo = ServiceInfo("_zeromq._tcp.local.", servicename + "._zeromq._tcp.local.", socket.inet_aton(addr), port, 0, 0, desc) self.zeroconf.register_service(self.stageServiceInfo) print("Stage active on address " + str(self.reply.socket.getsockopt(zmq.LAST_ENDPOINT))) def close(self): self.zeroconf.unregister_service(self.stageServiceInfo) self.zeroconf.close() ZstNode.close(self)
class Zeroconf(object): """ A simple class to publish a network service using zeroconf. """ def __init__(self, name, port, **kwargs): self.zconf = None stype = kwargs.get('stype', "_http._tcp.local.") full_name = name if not name.endswith('.'): full_name += '.' + stype props = {'txtvers': '1', 'iTSh Version': '131073', #'196609' 'Machine Name': name, 'Password': '******'} self.svc_info = ServiceInfo(stype, full_name, find_local_ipaddress(), port, 0, 0, props) def publish(self): """ Publish the service to the network. """ if self.zconf is None: self.zconf = ZeroC() self.zconf.register_service(self.svc_info) def unpublish(self): """ Tell the network we're closed :-) """ if self.zconf is not None: self.zconf.unregister_service(self.svc_info) self.zconf.close() self.zconf = None
def test_ttl(self): # instantiate a zeroconf instance zc = Zeroconf(interfaces=['127.0.0.1']) # service definition type_ = "_test-srvc-type._tcp.local." name = "xxxyyy" registration_name = "%s.%s" % (name, type_) desc = {'path': '/~paulsm/'} info = ServiceInfo( type_, registration_name, socket.inet_aton("10.0.1.2"), 80, 0, 0, desc, "ash-2.local." ) # we are going to monkey patch the zeroconf send to check packet sizes old_send = zc.send nbr_answers = nbr_additionals = nbr_authorities = 0 def get_ttl(record_type): if expected_ttl is not None: return expected_ttl elif record_type in [r._TYPE_A, r._TYPE_SRV]: return r._DNS_HOST_TTL else: return r._DNS_OTHER_TTL def send(out, addr=r._MDNS_ADDR, port=r._MDNS_PORT): """Sends an outgoing packet.""" nonlocal nbr_answers, nbr_additionals, nbr_authorities for answer, time_ in out.answers: nbr_answers += 1 assert answer.ttl == get_ttl(answer.type) for answer in out.additionals: nbr_additionals += 1 assert answer.ttl == get_ttl(answer.type) for answer in out.authorities: nbr_authorities += 1 assert answer.ttl == get_ttl(answer.type) old_send(out, addr=addr, port=port) # monkey patch the zeroconf send setattr(zc, "send", send) # register service with default TTL expected_ttl = None zc.register_service(info) assert nbr_answers == 12 and nbr_additionals == 0 and nbr_authorities == 3 nbr_answers = nbr_additionals = nbr_authorities = 0 # query query = r.DNSOutgoing(r._FLAGS_QR_QUERY | r._FLAGS_AA) query.add_question(r.DNSQuestion(info.type, r._TYPE_PTR, r._CLASS_IN)) query.add_question(r.DNSQuestion(info.name, r._TYPE_SRV, r._CLASS_IN)) query.add_question(r.DNSQuestion(info.name, r._TYPE_TXT, r._CLASS_IN)) query.add_question(r.DNSQuestion(info.server, r._TYPE_A, r._CLASS_IN)) zc.handle_query(r.DNSIncoming(query.packet()), r._MDNS_ADDR, r._MDNS_PORT) assert nbr_answers == 4 and nbr_additionals == 1 and nbr_authorities == 0 nbr_answers = nbr_additionals = nbr_authorities = 0 # unregister expected_ttl = 0 zc.unregister_service(info) assert nbr_answers == 12 and nbr_additionals == 0 and nbr_authorities == 0 nbr_answers = nbr_additionals = nbr_authorities = 0 # register service with custom TTL expected_ttl = r._DNS_HOST_TTL * 2 assert expected_ttl != r._DNS_HOST_TTL zc.register_service(info, ttl=expected_ttl) assert nbr_answers == 12 and nbr_additionals == 0 and nbr_authorities == 3 nbr_answers = nbr_additionals = nbr_authorities = 0 # query query = r.DNSOutgoing(r._FLAGS_QR_QUERY | r._FLAGS_AA) query.add_question(r.DNSQuestion(info.type, r._TYPE_PTR, r._CLASS_IN)) query.add_question(r.DNSQuestion(info.name, r._TYPE_SRV, r._CLASS_IN)) query.add_question(r.DNSQuestion(info.name, r._TYPE_TXT, r._CLASS_IN)) query.add_question(r.DNSQuestion(info.server, r._TYPE_A, r._CLASS_IN)) zc.handle_query(r.DNSIncoming(query.packet()), r._MDNS_ADDR, r._MDNS_PORT) assert nbr_answers == 4 and nbr_additionals == 1 and nbr_authorities == 0 nbr_answers = nbr_additionals = nbr_authorities = 0 # unregister expected_ttl = 0 zc.unregister_service(info) assert nbr_answers == 12 and nbr_additionals == 0 and nbr_authorities == 0 nbr_answers = nbr_additionals = nbr_authorities = 0
logging.basicConfig(level=logging.DEBUG) if len(sys.argv) > 1: assert sys.argv[1:] == ['--debug'] logging.getLogger('zeroconf').setLevel(logging.DEBUG) # Test a few module features, including service registration, service # query (for Zoe), and service unregistration. print("Multicast DNS Service Discovery for Python, version %s" % (__version__,)) r = Zeroconf() print("1. Testing registration of a service...") desc = {'version': '0.10', 'a': 'test value', 'b': 'another value'} info = ServiceInfo("_http._tcp.local.", "My Service Name._http._tcp.local.", socket.inet_aton("127.0.0.1"), 1234, 0, 0, desc) print(" Registering service...") r.register_service(info) print(" Registration done.") print("2. Testing query of service information...") print(" Getting ZOE service: %s" % ( r.get_service_info("_http._tcp.local.", "ZOE._http._tcp.local."))) print(" Query done.") print("3. Testing query of own service...") queried_info = r.get_service_info("_http._tcp.local.", "My Service Name._http._tcp.local.") assert queried_info print(" Getting self: %s" % (queried_info,)) print(" Query done.") print("4. Testing unregister of service information...") r.unregister_service(info) print(" Unregister done.") r.close()
class LightService(service.Service): step_sizes = [0.01, 0.05, 0.1, 0.5, 1, 5, 10, 30, 60] def __init__(self, counter=None, loop=None, device = AdaDevice(serial=DummySerialDevice()), step_time_index=2, current_value="default", avail_progs=None, avail_filters = {}, default_filters=[], default_prog=None, lambent_port=8680, discovery_name="", remote_device_managed=False, xbar_component=None, xbar_runner_kwargs=None, **kwargs): self.current_value = current_value self.step_time_index = step_time_index self.available_progs = avail_progs self.remote_device_managed = remote_device_managed if not device: self.device = device else: self.device = device if default_prog: if default_prog in self.available_progs.keys(): prog = self.available_progs.get(default_prog) # self.change_program(prog, default_prog) prog_kwargs = prog['kwargs'] prog_kwargs['device'] = self.device prog_kwargs.update(GLOBAL_KWARGS) counter = prog['class'](**prog_kwargs) self.current_value = default_prog else: sys.stderr.write("SELECTED (%s) NOT IN (%s)" % (default_prog, ",".join(i for i in self.available_progs))) if not counter: self.counter = WaitingCounter(5) else: self.counter = counter self.service_avail_filters = avail_filters self.service_enabled_filters = collections.OrderedDict() for filter in default_filters: try: self.service_enabled_filters[uuid.uuid4().hex[0:4].upper()] = [filter,self.service_avail_filters[filter]()] except: sys.stderr.write("LAMBENT FILTER DIDN'T EXIST '" + filter + "', SKIPPING\n") # these next 2 lines for testing # self.service_enabled_filters[uuid.uuid4().hex[0:4].upper()] = ['GRB',self.service_avail_filters['GRB']()] # self.service_enabled_filters[uuid.uuid4().hex[0:4].upper()] = ['GRB',self.service_avail_filters['GRB']()] self.counter.filters = self.get_filters() if not loop: self.loop = task.LoopingCall(self.counter.step) self.loop.start(self.step_time) else: self.loop = loop self.update_filters() self.disc_name = discovery_name self.announce(discovery_name, port=lambent_port) if xbar_runner_kwargs: self.xbar_session = None self.xbar_component = xbar_component self.xbar_runner_kwargs = xbar_runner_kwargs self.xbar_init() @inlineCallbacks def _init_xbar(self): if self.xbar_session is None: self.xbar_session = yield self.start_crossbar() self.xbar_session.name = self.disc_name.lower() @inlineCallbacks def start_crossbar(self): running_deferred = Deferred() extra = dict(running=running_deferred) self.xbar_runner_kwargs['extra'] = extra xbar_runner = ApplicationRunner(**self.xbar_runner_kwargs) xbar_runner.run(self.xbar_component, start_reactor=False, auto_reconnect=True) session = yield running_deferred returnValue(session) # @inlineCallbacks def xbar_change_program(self, program): # this should be improved and normalized later to not repat code but for now it works if program not in self.available_progs.keys(): return json.dumps( { "status": "ERROR_NON_EXISTENT_PROGRAM_PARAMETER", "value": program } ) else: prog = self.available_progs.get(program, None) self.change_program(prog, program) return json.dumps( { "status": "SUCCESS_CHANGED_PROGRAM", "value": program } ) @inlineCallbacks def xbar_init(self): yield self._init_xbar() yield self.xbar_hello() yield self.xbar_register_methods() @inlineCallbacks def xbar_register_methods(self): xbar_method = "us.thingcosm.aethers.lambent.%(name)s.program" % {"name":self.disc_name.lower()} # print("qqq") # print(xbar_method) self.xbar_method = xbar_method yield self.xbar_session.register(self.xbar_change_program, xbar_method) # Continuous Lookup And Data Publishing yield self.xbar_session.subscribe(self.xbar_info_ui, "us.thingcosm.aethers.continuous") yield self.xbar_session.publish("us.thingcosm.aethers.continuous", options=PublishOptions(exclude_me=False)) # yield self.xbar_session.publish("us.thingcosm.aethers.continuous", 0) @inlineCallbacks def xbar_hello(self): yield self.xbar_session.publish("us.thingcosm.aethers.lambent.updates.program", server_name=self.disc_name, server_program=self.current_value) @inlineCallbacks def xbar_update_speed(self): yield self.xbar_session.publish("us.thingcosm.aethers.lambent.updates.speed", server_name=self.disc_name, server_value=self.step_time) @inlineCallbacks def xbar_info_ui_items(self): progs_grp = {} for k,v in self.available_progs.iteritems(): key = deepcopy(k) grp = v.get("grouping", "NONE") if grp in progs_grp: progs_grp[grp].append(key) else: progs_grp[grp] = [key] loader = txtemplate.Jinja2TemplateLoader(TEMPLATE_DIR) template = loader.load("xbar_ui.jinja2") context={"programs":progs_grp, "method":self.xbar_method} rendered = yield template.render(**context) print(rendered) returnValue(rendered) # return {"a":"B"} @inlineCallbacks def xbar_info_ui(self, *args, **kwargs): """ When the frontend looks for us, populate it""" body = yield self.xbar_info_ui_items() yield self.xbar_session.publish("us.thingcosm.aethers.continuous.ui", cls="lambent", name=self.disc_name.lower(), body=body) # yield self.xbar_session.publish("us.thingcosm.aethers.continuous.ui", cls="lambent", name=self.disc_name.upper(), body=body) @property def step_time(self): return self.step_sizes[self.step_time_index] def getCntr(self): return self.counter def setCntr(self, cntr): self.counter = cntr def setLoop(self, loop): self.loop = loop def setLoopInterval(self, value): self.step_time = value self.loop.stop() self.loop.start(self.step_time) # blendy bits def bigblender(self, upper, lower, count=64): blend = [int(lower + x*(upper-lower)/count) for x in range(count)] return blend def do_all_filters(self, value): for f in self.get_filters(): value = f.do_filter(value) return value def change_program(self, prog, val): self.current_value = val # # stop the existing one if hasattr(self, "loop"): loop_old = self.loop try: loop_old.stop() except AssertionError: pass print "too quick slick" # # setup self.program_class = prog['class'] self.program_args = prog.get('kwargs', {}) self.program_args['device'] = self.device self.program_args.update(GLOBAL_KWARGS) initiated_prog = self.program_class(**self.program_args) # doing it here, prevents that flicker initiated_prog.filters = self.get_filters() ## # # Transitions if self.counter and hasattr(self.counter, "leds") and hasattr(initiated_prog, "leds"): # if self.transition: if self.counter.transitions_list: leds_nowflat = self.counter.transitions_list.pop(0) else: leds_now = [i.read_rgb() for i in self.counter.leds] leds_filtered = [self.do_all_filters(i) for i in leds_now] leds_nowflat = list(itertools.chain(*leds_filtered)) leds_l8r = [k.read_rgb() for k in initiated_prog.leds] leds_l8r_filt = [self.do_all_filters(i) for i in leds_l8r] leds_l8rflat = list(itertools.chain(*leds_l8r_filt)) place_to_hold_stuff = [list() for i in xrange(128)] for j,l in zip(leds_nowflat, leds_l8rflat): blendvals = self.bigblender(l,j,count=128) for v,p in zip(blendvals, place_to_hold_stuff): p.append(v) # for l in place_to_hold_stuff: # self.device.write(l) else: place_to_hold_stuff = [] if place_to_hold_stuff and remote_device_managed != False: """ This is a stupid quick implementation of taking far fewer steps, its not particularly perfect, but its Good Enough """ len_pl = len(place_to_hold_stuff) if remote_device_managed == 0: # no fade place_to_hold_stuff = [] elif remote_device_managed == 1: # half as many place_to_hold_stuff = place_to_hold_stuff[::2] elif remote_device_managed == 2: # 1/4 as many last = place_to_hold_stuff[-1] place_to_hold_stuff = place_to_hold_stuff[::4] if place_to_hold_stuff[-1] != last: place_to_hold_stuff.append(last) elif remote_device_managed == 3: last = place_to_hold_stuff[-1] place_to_hold_stuff = place_to_hold_stuff[::10] if place_to_hold_stuff[-1] != last: place_to_hold_stuff.append(last) elif remote_device_managed == 4: pass elif remote_device_managed == 5 and len_pl > 3: # single intermediate start = place_to_hold_stuff[0] end = place_to_hold_stuff[-1] middle = len_pl/2 place_to_hold_stuff=[start, place_to_hold_stuff[middle], end] elif remote_device_managed == 6 and len_pl > 5: start = place_to_hold_stuff[0] end = place_to_hold_stuff[-1] mid_1 = len_pl / 4 * 1 # 25%ish mid_2 = len_pl / 4 * 2 # 50%ish mid_3 = len_pl / 4 * 3 # 75%ish place_to_hold_stuff = [start, place_to_hold_stuff[mid_1], place_to_hold_stuff[mid_2], place_to_hold_stuff[mid_3], end] elif remote_device_managed == 7 and len_pl > 7: start = place_to_hold_stuff[0] end = place_to_hold_stuff[-1] mid_1 = len_pl / 6 * 1 # 16%ish mid_2 = len_pl / 6 * 2 # 32%ish mid_3 = len_pl / 6 * 3 # 48%ish mid_4 = len_pl / 6 * 4 # 64%ish mid_5 = len_pl / 6 * 5 # 80%ish place_to_hold_stuff = [start, place_to_hold_stuff[mid_1], place_to_hold_stuff[mid_2], place_to_hold_stuff[mid_3], place_to_hold_stuff[mid_4], place_to_hold_stuff[mid_5], end] else: # fallback place_to_hold_stuff = [] pass initiated_prog.transitions_list = place_to_hold_stuff loop_new = task.LoopingCall(initiated_prog.step) loop_new.start(self.step_time) self.setLoop(loop_new) self.setCntr(initiated_prog) self.update_filters() self.xbar_hello() def get_filters(self): filters = [f[1] for f in self.service_enabled_filters.values()] return filters def update_filters(self): self.counter.filters = self.get_filters() def loop_set(self): self.loop.stop() self.loop.start(self.step_time) self.xbar_update_speed() def loop_slower(self): max_index = len(self.step_sizes) - 1 if self.step_time_index >= max_index: return self.step_time, False else: self.step_time_index += 1 self.loop_set() return self.step_time, True # return 0,True def loop_faster(self): if self.step_time_index <= 0: return self.step_time, False else: self.step_time_index -= 1 self.loop_set() return self.step_time, True def set_time_index(self, value): self.step_time_index = value self.loop_set() def getLightFactory(self): f = protocol.ServerFactory() f.protocol = TelnetLightProtocol f.counter = self.counter f.loop = self.loop f.device = self.device f.setLoop = self.setLoop f.setCntr = self.setCntr f.change_program = self.change_program return f def getLightResource(self): r = LightHTMLTree(self) r.putChild("", r) st = LightStatus(self) r.putChild("status", st) si = LightInfo(self) r.putChild("info", si) pl = LightProgramList(self) r.putChild("progs", pl) plg = LightProgramListGrouped(self) r.putChild("progs_grp", plg) se = LightProgramSetter(self) r.putChild("set", se) ass = static.File('assets') r.putChild("assets", ass) filt_enabled = LightProgramEnabledFilterList(self) r.putChild("filt_en", filt_enabled) filt_list = LightProgramFilterList(self) r.putChild("filt_ls", filt_list) filt_delete = LightProgramRMFilter(self) r.putChild("filt_rm", filt_delete) filt_add = LightProgramAddFilter(self) r.putChild("filt_add", filt_add) sp_faster = LightSpeedFaster(self) r.putChild("sp_up", sp_faster) sp_slower = LightSpeedSlower(self) r.putChild("sp_dn", sp_slower) sp_list = LightSpeedOptions(self) r.putChild("sp_ls", sp_list) sp_set = LightSpeedSet(self) r.putChild("sp_set", sp_set) return r def announce(self, discovery_name, port=8680): self.zeroconf = Zeroconf() self.zconfigs = [] for i in netifaces.interfaces(): if i.startswith("lo"): # remove loopback from announce continue if i.startswith("veth"): # remove docker interface from announce continue addrs = netifaces.ifaddresses(i) if addrs.keys() == [17]: continue print addrs for a in addrs[netifaces.AF_INET]: print a info_desc = {'path': '/progs_grp/', 'name': discovery_name, "port":port} config = ServiceInfo("_aether._tcp.local.", "%s_%s_%s_lambent._aether._tcp.local." % (socket.gethostname(),i, port), socket.inet_aton(a['addr']), port, 0, 0, info_desc) try: self.zeroconf.register_service(config) self.zconfigs.append(config) except: print("Service %s failed to register" % config) def stopService(self): for c in self.zconfigs: self.zeroconf.unregister_service(c) self.zeroconf.close()
def put( filename, output=None, interface=None, address=None, port=None, reporthook=None, timeout=None, ): """Send a file using the zget protocol. Parameters ---------- filename : string The filename to be transferred output : string The alias to share on the network. Optional. If empty, the input filename will be used. interface : string The network interface to use. Optional. address : string The network address to use. Optional. port : int The network port to use. Optional. reporthook : callable A hook that will be called during transfer. Handy for watching the transfer. See :code:`urllib.urlretrieve` for callback parameters. Optional. timeout : int Seconds to wait until process is aborted. A running transfer is not aborted even when timeout was hit. Optional. Raises ------- TimeoutException When a timeout occurred. """ if port is None: port = utils.config().getint('DEFAULT', 'port') if interface is None: interface = utils.config().get('DEFAULT', 'interface') if not 0 <= port <= 65535: raise ValueError(_("Port %d exceeds allowed range") % port) basename = os.path.basename(filename) filehashes = [] filehashes.append(hashlib.sha1(basename.encode('utf-8')).hexdigest()) if output is not None: filehashes.append(hashlib.sha1(output.encode('utf-8')).hexdigest()) if interface is None: interface = utils.default_interface() if address is None: address = utils.ip_addr(interface) server = StateHTTPServer((address, port), FileHandler) server.timeout = timeout server.filename = filename server.allowed_basenames.append(basename) if output is not None: server.allowed_basenames.append(output) server.reporthook = reporthook port = server.server_port utils.logger.debug( _("Using interface %s") % interface ) utils.logger.debug( _("Listening on %(a)s:%(p)d \n" "you may change address using --address and " "port using --port") % {'a': address, 'p': port} ) zeroconf = Zeroconf() infos = [] for filehash in filehashes: utils.logger.debug( _("Broadcasting as %s._zget._http._tcp.local.") % filehash ) infos.append(ServiceInfo( "_zget._http._tcp.local.", "%s._zget._http._tcp.local." % filehash, socket.inet_aton(address), port, 0, 0, {'path': None} )) try: for info in infos: zeroconf.register_service(info) server.handle_request() except KeyboardInterrupt: pass server.socket.close() for info in infos: zeroconf.unregister_service(info) zeroconf.close() if timeout is not None and not server.downloaded: raise utils.TimeoutException() else: utils.logger.info(_("Done."))
import logging import socket import sys from time import sleep from zeroconf import ServiceInfo, Zeroconf if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG) if len(sys.argv) > 1: assert sys.argv[1:] == ['--debug'] logging.getLogger('zeroconf').setLevel(logging.DEBUG) desc = {'version': '0.1'} info = ServiceInfo("_http._tcp.local.", "pispotify._http._tcp.local.", socket.inet_aton("192.168.0.17"), 80, 0, 0, desc) #use the right ip zeroconf = Zeroconf() print("Registration of a service, press Ctrl-C to exit...") zeroconf.register_service(info) try: while True: sleep(0.1) except KeyboardInterrupt: pass finally: print("Unregistering...") zeroconf.unregister_service(info) zeroconf.close()
class UbiquitousService(service.Service): def __init__(self, devices, discovery_name, port, update_freq=5.0): self.devices = devices self.update_freq = update_freq # start the update loop self.loop = task.LoopingCall(self.devices_update) self.loop.start(self.update_freq) self.announce(discovery_name, port) def devices_update(self): [d.update_port_status() for d in self.devices] def device_as_prg_grp(self, d): return d._as_prg_grp() def devices_as_prg_grp(self): return {d.name: self.device_as_prg_grp(d) for d in self.devices} def all_programs(self): # l = [] # [l.extend(d._as_prg_list()) for d in self.devices] # print [d._as_prg_list() for d in self.devices] l = {} for d in self.devices: ps = d._as_prg_list() for p in ps: l[p] = d print l return l def announce(self, discovery_name, port): self.zeroconf = Zeroconf() self.zconfigs = [] for i in netifaces.interfaces(): if i.startswith("lo"): # remove loopback from announce continue if i.startswith("veth"): # remove docker interface from announce continue addrs = netifaces.ifaddresses(i) if addrs.keys() == [17]: continue print addrs for a in addrs[netifaces.AF_INET]: print a info_desc = {'path': '/progs_grp/', 'name': discovery_name} config = ServiceInfo("_aether._tcp.local.", "%s_%s_%s_ubiquitous._aether._tcp.local." % (socket.gethostname(),i, port), socket.inet_aton(a['addr']), port, 0, 0, info_desc)# , "aether-autodisc-0.local.") self.zeroconf.register_service(config) self.zconfigs.append(config) def stopService(self): for c in self.zconfigs: self.zeroconf.unregister_service(c) self.zeroconf.close() def getResources(self): r = resource.Resource() r.putChild("", r) se = ProgramSetter(self) r.putChild("set", se) pl = ProgramList(self) r.putChild("progs", pl) plg = ProgramListGrouped(self) r.putChild("progs_grp", plg) st = ServiceStatus(self) r.putChild("status", st) return r
def test_integration_with_listener_class(self): service_added = Event() service_removed = Event() type_ = "_http._tcp.local." name = "xxxyyy" registration_name = "%s.%s" % (name, type_) class MyListener(object): def add_service(self, zeroconf, type, name): zeroconf.get_service_info(type, name) service_added.set() def remove_service(self, zeroconf, type, name): service_removed.set() zeroconf_browser = Zeroconf() zeroconf_browser.add_service_listener(type_, MyListener()) properties = dict( prop_none=None, prop_string=b'a_prop', prop_float=1.0, prop_blank=b'a blanked string', prop_true=1, prop_false=0, ) zeroconf_registrar = Zeroconf() desc = {'path': '/~paulsm/'} desc.update(properties) info = ServiceInfo( type_, registration_name, socket.inet_aton("10.0.1.2"), 80, 0, 0, desc, "ash-2.local.") zeroconf_registrar.register_service(info) try: service_added.wait(1) assert service_added.is_set() # short pause to allow multicast timers to expire time.sleep(2) # clear the answer cache to force query for record in zeroconf_browser.cache.entries(): zeroconf_browser.cache.remove(record) # get service info without answer cache info = zeroconf_browser.get_service_info(type_, registration_name) assert info.properties[b'prop_none'] is False assert info.properties[b'prop_string'] == properties['prop_string'] assert info.properties[b'prop_float'] is False assert info.properties[b'prop_blank'] == properties['prop_blank'] assert info.properties[b'prop_true'] is True assert info.properties[b'prop_false'] is False zeroconf_registrar.unregister_service(info) service_removed.wait(1) assert service_removed.is_set() finally: zeroconf_registrar.close() zeroconf_browser.close()
def test_integration_with_listener_class(self): service_added = Event() service_removed = Event() service_updated = Event() subtype_name = "My special Subtype" type_ = "_http._tcp.local." subtype = subtype_name + "._sub." + type_ name = "xxxyyyæøå" registration_name = "%s.%s" % (name, subtype) class MyListener(r.ServiceListener): def add_service(self, zeroconf, type, name): zeroconf.get_service_info(type, name) service_added.set() def remove_service(self, zeroconf, type, name): service_removed.set() class MySubListener(r.ServiceListener): def add_service(self, zeroconf, type, name): pass def remove_service(self, zeroconf, type, name): pass def update_service(self, zeroconf, type, name): service_updated.set() listener = MyListener() zeroconf_browser = Zeroconf(interfaces=['127.0.0.1']) zeroconf_browser.add_service_listener(subtype, listener) properties = dict( prop_none=None, prop_string=b'a_prop', prop_float=1.0, prop_blank=b'a blanked string', prop_true=1, prop_false=0, ) zeroconf_registrar = Zeroconf(interfaces=['127.0.0.1']) desc = {'path': '/~paulsm/'} # type: r.ServicePropertiesType desc.update(properties) info_service = ServiceInfo( subtype, registration_name, socket.inet_aton("10.0.1.2"), 80, 0, 0, desc, "ash-2.local." ) zeroconf_registrar.register_service(info_service) try: service_added.wait(1) assert service_added.is_set() # short pause to allow multicast timers to expire time.sleep(3) # clear the answer cache to force query for record in zeroconf_browser.cache.entries(): zeroconf_browser.cache.remove(record) # get service info without answer cache info = zeroconf_browser.get_service_info(type_, registration_name) assert info is not None assert info.properties[b'prop_none'] is False assert info.properties[b'prop_string'] == properties['prop_string'] assert info.properties[b'prop_float'] is False assert info.properties[b'prop_blank'] == properties['prop_blank'] assert info.properties[b'prop_true'] is True assert info.properties[b'prop_false'] is False info = zeroconf_browser.get_service_info(subtype, registration_name) assert info is not None assert info.properties[b'prop_none'] is False # Begin material test addition sublistener = MySubListener() zeroconf_browser.add_service_listener(registration_name, sublistener) properties['prop_blank'] = b'an updated string' desc.update(properties) info_service = ServiceInfo( subtype, registration_name, socket.inet_aton("10.0.1.2"), 80, 0, 0, desc, "ash-2.local." ) zeroconf_registrar.update_service(info_service) service_updated.wait(1) assert service_updated.is_set() info = zeroconf_browser.get_service_info(type_, registration_name) assert info is not None assert info.properties[b'prop_blank'] == properties['prop_blank'] # End material test addition zeroconf_registrar.unregister_service(info_service) service_removed.wait(1) assert service_removed.is_set() finally: zeroconf_registrar.close() zeroconf_browser.remove_service_listener(listener) zeroconf_browser.close()
class VoicePlayZeroConf(object): """ Very basic mDNS/Zeroconf check/registration """ def __init__(self): self.zeroconf = None self.known_servers = [] self.info = None self.threads = None self.exit = False @staticmethod def get_ip_address(): """ Get my IP """ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("8.8.8.8", 80)) return s.getsockname()[0] def service_info(self, hostname): """ Prepare service information for (my) hostname """ port = int(Config.cfg_data().get('webapp_port')) info = ServiceInfo("_http._tcp.local.", "{0!s}._http._tcp.local.".format(hostname), socket.inet_aton(self.get_ip_address()), port, 0, 0, {'path': '/'}, "{0!s}.local.".format(hostname)) return info def on_service_state_change(self, zeroconf, service_type, name, state_change): """ Service state change callback """ if state_change is ServiceStateChange.Added: info = zeroconf.get_service_info(service_type, name) if info and not info.server in self.known_servers: self.known_servers.append(info.server) def get_others(self): """ Wait for other services to make themselves visible """ zeroconf = Zeroconf() _ = ServiceBrowser(zeroconf, "_http._tcp.local.", handlers=[self.on_service_state_change]) for _ in range(1, 10 + 1): time.sleep(1) zeroconf.close() return self.known_servers def run(self): """ Actual Zeroconf runner """ # has to be here, otherwise quick start/stop will not provide self.zeroconf descriptor self.zeroconf = Zeroconf() servers = self.get_others() hostname = __title__.lower() if '{0!s}.local'.format(hostname) in servers: hostname = '{0!s}-{1!s}'.format(__title__.lower(), str(uuid.uuid4())) self.info = self.service_info(hostname) self.zeroconf.register_service(self.info) while not self.exit: try: time.sleep(0.1) except Exception as _: break def unregister(self): """ Unregister (remove) service from local network """ if self.info: self.zeroconf.unregister_service(self.info) self.zeroconf.close() def start(self): """ Start Zeroconf inside thread """ self.threads = ThreadGroup() self.threads.targets = [self.run] self.threads.start_all() def stop(self): """ Stop and unregister Zeroconf service """ self.exit = True self.unregister() if self.threads: self.threads.stop_all()
def test_ttl(self): # instantiate a zeroconf instance zc = Zeroconf(interfaces=['127.0.0.1']) # service definition type_ = "_test-srvc-type._tcp.local." name = "xxxyyy" registration_name = "%s.%s" % (name, type_) desc = {'path': '/~paulsm/'} info = ServiceInfo( type_, registration_name, socket.inet_aton("10.0.1.2"), 80, 0, 0, desc, "ash-2.local.") # we are going to monkey patch the zeroconf send to check packet sizes old_send = zc.send # needs to be a list so that we can modify it in our phony send nbr_answers = [0, None] nbr_additionals = [0, None] nbr_authorities = [0, None] def send(out, addr=r._MDNS_ADDR, port=r._MDNS_PORT): """Sends an outgoing packet.""" for answer, time_ in out.answers: nbr_answers[0] += 1 assert answer.ttl == expected_ttl for answer in out.additionals: nbr_additionals[0] += 1 assert answer.ttl == expected_ttl for answer in out.authorities: nbr_authorities[0] += 1 assert answer.ttl == expected_ttl old_send(out, addr=addr, port=port) # monkey patch the zeroconf send zc.send = send # register service with default TTL expected_ttl = r._DNS_TTL zc.register_service(info) assert nbr_answers[0] == 12 and nbr_additionals[0] == 0 and nbr_authorities[0] == 3 nbr_answers[0] = nbr_additionals[0] = nbr_authorities[0] = 0 # query query = r.DNSOutgoing(r._FLAGS_QR_QUERY | r._FLAGS_AA) query.add_question(r.DNSQuestion(info.type, r._TYPE_PTR, r._CLASS_IN)) query.add_question(r.DNSQuestion(info.name, r._TYPE_SRV, r._CLASS_IN)) query.add_question(r.DNSQuestion(info.name, r._TYPE_TXT, r._CLASS_IN)) query.add_question(r.DNSQuestion(info.server, r._TYPE_A, r._CLASS_IN)) zc.handle_query(query, r._MDNS_ADDR, r._MDNS_PORT) assert nbr_answers[0] == 4 and nbr_additionals[0] == 1 and nbr_authorities[0] == 0 nbr_answers[0] = nbr_additionals[0] = nbr_authorities[0] = 0 # unregister expected_ttl = 0 zc.unregister_service(info) assert nbr_answers[0] == 12 and nbr_additionals[0] == 0 and nbr_authorities[0] == 0 nbr_answers[0] = nbr_additionals[0] = nbr_authorities[0] = 0 # register service with custom TTL expected_ttl = r._DNS_TTL * 2 assert expected_ttl != r._DNS_TTL zc.register_service(info, ttl=expected_ttl) assert nbr_answers[0] == 12 and nbr_additionals[0] == 0 and nbr_authorities[0] == 3 nbr_answers[0] = nbr_additionals[0] = nbr_authorities[0] = 0 # query query = r.DNSOutgoing(r._FLAGS_QR_QUERY | r._FLAGS_AA) query.add_question(r.DNSQuestion(info.type, r._TYPE_PTR, r._CLASS_IN)) query.add_question(r.DNSQuestion(info.name, r._TYPE_SRV, r._CLASS_IN)) query.add_question(r.DNSQuestion(info.name, r._TYPE_TXT, r._CLASS_IN)) query.add_question(r.DNSQuestion(info.server, r._TYPE_A, r._CLASS_IN)) zc.handle_query(query, r._MDNS_ADDR, r._MDNS_PORT) assert nbr_answers[0] == 4 and nbr_additionals[0] == 1 and nbr_authorities[0] == 0 nbr_answers[0] = nbr_additionals[0] = nbr_authorities[0] = 0 # unregister expected_ttl = 0 zc.unregister_service(info) assert nbr_answers[0] == 12 and nbr_additionals[0] == 0 and nbr_authorities[0] == 0 nbr_answers[0] = nbr_additionals[0] = nbr_authorities[0] = 0
class CentralServer: _server_name = "CentralServer" _vhost = "/bottle" _exchange = "pebble" _routing_key = "central" def _get_service_name(self): return CentralServer._server_name + "._http._tcp.local." def _get_service_ip(self): wlanIfaceAddrs = netifaces.ifaddresses('wlan0') ethIfaceAddrs = netifaces.ifaddresses('eth0') if netifaces.AF_INET in ethIfaceAddrs and "addr" in ethIfaceAddrs[netifaces.AF_INET][0]: return ethIfaceAddrs[netifaces.AF_INET][0]["addr"], "eth0" elif netifaces.AF_INET in wlanIfaceAddrs and "addr" in wlanIfaceAddrs[netifaces.AF_INET][0]: return wlanIfaceAddrs[netifaces.AF_INET][0]["addr"], "wlan0" else: return None, None def __init__(self, listen_pin): GPIO.setwarnings(False) GPIO.setmode(GPIO.BOARD) GPIO.setup(listen_pin, GPIO.IN) self._pin = listen_pin self._log_fmt = logging.Formatter(fmt="%(asctime)s [%(levelname)-8s] %(message)s", datefmt="%b %d %H:%M:%S") self._log = logging.getLogger() self._log.setLevel(logging.INFO) streamHandler = logging.StreamHandler() streamHandler.setLevel(logging.DEBUG) streamHandler.setFormatter(self._log_fmt) self._log.addHandler(streamHandler) self._display = DisplayRunner.DisplayRunner() self._display.set_mode(0) self._display.start() self._new_connections = [] self._accept_responses = threading.Event() self._responses = [] self._conn = pika.BlockingConnection(pika.ConnectionParameters(host="localhost", virtual_host=self._vhost)) self._chan = self._conn.channel() queueResult = self._chan.queue_declare(auto_delete=True) if queueResult is None: self._log.error("Could not create connect queue") raise RuntimeError("Error configuring RabbitMQ") else: self._log.info("Created queue \'%s\'" % (queueResult.method.queue,)) self._log.info("Using exchange \'%s/%s\'" % (self._vhost, self._exchange,)) self._chan.exchange_declare(exchange=self._exchange, type="topic", auto_delete=True) self._chan.queue_bind(exchange=self._exchange, queue=queueResult.method.queue, routing_key=self._routing_key) self._queue_name = queueResult.method.queue server_ip, ifaceName = self._get_service_ip() if server_ip is None: self._log.error("Could not determine server IP") raise RuntimeError("Error finding server IP") else: self._log.info("Broadcasting service %s with IP %s (%s)" % (self._get_service_name(), server_ip, ifaceName)) # Configure zeroconf to broadcast this service self._zeroconf = Zeroconf() self._zeroconf_info = ServiceInfo("_http._tcp.local.", self._get_service_name(), socket.inet_aton(server_ip), 5672, 0, 0, {"exchange_name": self._exchange, "routing_key": self._routing_key, "virtual_host": self._vhost}, None) try: self._zeroconf.register_service(self._zeroconf_info) except Zeroconf.NonUniqueNameException: self._log.warn("Service with name \'%s\' already broadcasting on this network!" % (self._get_service_name(),)) def start(self): state = "IDLE_INIT" startTime = None msg = None try: while True: if state == "IDLE_INIT": self._display.set_message("NEED A TABLE?") self._display.set_mode(2) state = "IDLE" elif state == "IDLE": # If new nodes join the network, display them on the screen if len(self._new_connections) > 0: # Display / log newly connected nodes, if present pass # Otherwise if the user presses the button, ping the network and wait elif GPIO.input(self._pin): self._accept_responses.set() self.ping() self._display.set_mode(0) time.sleep(0.5) self._display.set_message("SEARCHING...") self._display.set_mode(2) startTime = time.time() state = "WAIT_FOR_RESPONSE" elif state == "WAIT_FOR_RESPONSE": timeElapsed = math.floor(time.time() - startTime) msg = self._chan.basic_get(queue=self._queue_name, no_ack=True) # If nodes respond, pick a node from the list of responses and display it self._log.info(str(msg)) if msg[0] is not None: self._log.info("RESPONSES RECEIVED %s" % str(msg[2])) self._display.set_mode(0) time.sleep(0.5) self._display.set_message("TABLE %s IS FREE" % str(msg[2])) self._display.set_mode(2) startTime = time.time() state = "DISPLAY_RESULT" # Or if 30 seconds pass then all tables are probably full elif timeElapsed > 45: self._log.info("REQUEST TIMED OUT") self._display.set_mode(0) time.sleep(0.5) self._display.set_message("SORRY, COULDN'T FIND ANYTING!") self._display.set_mode(2) startTime = time.time() self._accept_responses.clear() state = "DISPLAY_TIMEOUT" # ... Or if the user presses the button, cancel the request elif GPIO.input(self._pin): self._log.info("REQUEST CANCELLED") self._display.set_mode(0) time.sleep(0.5) self._display.set_message("REQUEST CANCELLED") self._display.set_mode(2) startTime = time.time() self._accept_responses.clear() state = "REQ_CANCEL" elif state == "DISPLAY_RESULT": if time.time() > (startTime + 30): self._display.set_mode(0) time.sleep(0.5) self._display.set_message("NEED A TABLE?") self._display.set_mode(2) state = "IDLE" elif state == "DISPLAY_TIMEOUT": if time.time() > (startTime + 10): self._display.set_mode(0) time.sleep(0.5) self._display.set_message("NEED A TABLE?") self._display.set_mode(2) state = "IDLE" elif state == "REQ_CANCEL": # Display cancelled message if time.time() > (startTime + 5): self._display.set_mode(0) time.sleep(0.5) self._display.set_message("NEED A TABLE?") self._display.set_mode(2) state = "IDLE" else: state = "IDLE_INIT" time.sleep(0.5) except KeyboardInterrupt: pass finally: self._display.set_mode(0) self._chan.stop_consuming() self._log.info("Shutting down server...") self._log.info("Closing connection with RabbitMQ") if self._conn is not None: self._conn.close() self._log.info("Unregistering server") self._zeroconf.unregister_service(self._zeroconf_info) self._zeroconf.close() self._log.info("Shutdown complete!") def ping(self): self._responses = [] self._log.info("Pinging network...") tempConn = pika.BlockingConnection(pika.ConnectionParameters(host='localhost', virtual_host=self._vhost)) tempChan = tempConn.channel() tempChan.basic_publish(exchange=self._exchange, routing_key="node", body="") tempConn.close() def _consume(self, ch, method, properties, body): print "MSG: %s" % body