pandora_client/pandora_client/localnode.py

108 lines
3.6 KiB
Python

import socket
import netifaces
from zeroconf import (
ServiceBrowser, ServiceInfo, ServiceStateChange, Zeroconf
)
import logging
logger = logging.getLogger(__name__)
service_type = '_pandoraclient._tcp.local.'
def get_broadcast_interfaces():
return list(set(
addr['addr']
for iface in netifaces.interfaces()
for addr in netifaces.ifaddresses(iface).get(socket.AF_INET, [])
if addr.get('netmask') != '255.255.255.255' and addr.get('broadcast')
))
class Server(object):
local_info = None
local_ips = None
def __init__(self, port):
self.port = port
self.name = socket.gethostname().partition('.')[0] + '-%s' % port
self.local_ips = get_broadcast_interfaces()
self.zeroconf = {ip: Zeroconf(interfaces=[ip]) for ip in self.local_ips}
self.register_service()
def register_service(self):
if self.local_info:
for local_ip, local_info in self.local_info:
self.zeroconf[local_ip].unregister_service(local_info)
self.local_info = None
local_name = socket.gethostname().partition('.')[0] + '.local.'
port = self.port
desc = {}
self.local_info = []
for i, local_ip in enumerate(get_broadcast_interfaces()):
if i:
name = '%s-%s.%s' % (self.name, i+1, service_type)
else:
name = '%s.%s' % (self.name, service_type)
local_info = ServiceInfo(service_type, name,
socket.inet_aton(local_ip), port, 0, 0, desc, local_name)
self.zeroconf[local_ip].register_service(local_info)
self.local_info.append((local_ip, local_info))
def __del__(self):
self.close()
def close(self):
if self.local_info:
for local_ip, local_info in self.local_info:
try:
self.zeroconf[local_ip].unregister_service(local_info)
except:
logger.debug('exception closing zeroconf', exc_info=True)
self.local_info = None
if self.zeroconf:
for local_ip in self.zeroconf:
try:
self.zeroconf[local_ip].close()
except:
logger.debug('exception closing zeroconf', exc_info=True)
self.zeroconf = None
class LocalNodes(dict):
def __init__(self):
self.local_ips = get_broadcast_interfaces()
self.zeroconf = {ip: Zeroconf(interfaces=[ip]) for ip in self.local_ips}
self.browse()
def browse(self):
self.browser = {
ip: ServiceBrowser(self.zeroconf[ip], service_type, handlers=[self.on_service_state_change])
for ip in self.zeroconf
}
def __del__(self):
self.close()
def close(self):
if self.zeroconf:
for local_ip in self.zeroconf:
try:
self.zeroconf[local_ip].close()
except:
logger.debug('exception closing zeroconf', exc_info=True)
self.zeroconf = None
for id in list(self):
self.pop(id, None)
def on_service_state_change(self, zeroconf, service_type, name, state_change):
id = name.split('.')[0].split('-')[0]
if state_change is ServiceStateChange.Added:
info = zeroconf.get_service_info(service_type, name)
if info:
self[id] = 'http://%s:%s' % (socket.inet_ntoa(info.address), info.port)
elif state_change is ServiceStateChange.Removed:
logger.debug('remove: %s', id)
self.pop(id, None)