discoverer: Rework how discoveries are done
This implements a backoff timer that waits an additional second every time an
announcement is done. This should allow us to discover all the devices on a
local segment fully.
Change-Id: I0443e44fd80cf494a5bb010b260fce5537f770b4
diff --git a/mdt/command.py b/mdt/command.py
index 7f4b110..73f76f5 100644
--- a/mdt/command.py
+++ b/mdt/command.py
@@ -56,16 +56,12 @@
self.address = self.device
if not self.address:
- self.discoverer.start()
if self.device:
print('Waiting for device {0}...'.format(self.device))
else:
print('Waiting for a device...')
+ self.discoverer.discover()
- while not self.address:
- sleep(0.1)
-
- self.discoverer.stop()
client = None
try:
print('Connecting to {0} at {1}'.format(self.device, self.address))
diff --git a/mdt/devices.py b/mdt/devices.py
index 89f9421..a74fdca 100644
--- a/mdt/devices.py
+++ b/mdt/devices.py
@@ -40,8 +40,7 @@
self.device = Config().preferredDevice()
def run(self, args):
- self.discoverer.start()
- sleep(1)
+ self.discoverer.discover()
discoveries = self.discoverer.discoveries
for host, address in discoveries.items():
if self.device and host == self.device:
@@ -53,16 +52,7 @@
class DevicesWaitCommand:
'''Usage: mdt wait-for-device
-Waits for either the first device found, or your preferred device to be
-discovered on the local network segment.
-
-Variables used:
- preferred-device: contains the device name you want as your default
- Can be set to an IPv4 address to bypass the mDNS lookup.
-
-Note: if preferred-device is cleared, then this will return on the first
-available device found. Also, MDT uses a python implementation of mDNS
-ZeroConf for discovery, so it does not require a running Avahi daemon.
+Waits until a device is found.
'''
def __init__(self):
@@ -76,7 +66,9 @@
def run(self, args):
print('Waiting for device...')
- self.discoverer.start()
+
while not self.found_devices:
- sleep(0.1)
- print('Device found: {0} ({1})'.format(self.hostname, self.address))
+ self.discoverer.discover()
+
+ print('Found {0} devices.'.format(len(self.discoverer.discoveries)))
+ return 0
diff --git a/mdt/discoverer.py b/mdt/discoverer.py
index 3be13e1..3550c6a 100644
--- a/mdt/discoverer.py
+++ b/mdt/discoverer.py
@@ -19,36 +19,58 @@
from zeroconf import ServiceBrowser, Zeroconf
import socket
+import time
class Discoverer:
+ ANNOUNCE_PERIOD_SECS = 1
+ MAXIMUM_WAIT_CYCLES = 10
+ SERVICE_TYPE = "_googlemdt._tcp.local."
+
def __init__(self, listener=None):
- self.zeroconf = Zeroconf()
self.discoveries = {}
self.listener = listener
- self.browser = None
+ self.zeroconf = None
- def start(self):
- self.browser = ServiceBrowser(self.zeroconf, "_googlemdt._tcp.local.", self)
+ def discover(self):
+ self.zeroconf = Zeroconf()
+ self.browser = ServiceBrowser(self.zeroconf, Discoverer.SERVICE_TYPE, self)
+ self._heard_announcement = True
+ cycle_count = 0
+
+ # Keep waiting until we stop hearing announcements for a full second, or until we've waited 10 seconds
+ while self._heard_announcement and cycle_count < Discoverer.MAXIMUM_WAIT_CYCLES:
+ cycle_count += 1
+ self._heard_announcement = False
+ time.sleep(Discoverer.ANNOUNCE_PERIOD_SECS)
+
+ self.browser.cancel()
+ self.browser = None
+ self.zeroconf = None
def add_service(self, zeroconf, type, name):
info = self.zeroconf.get_service_info(type, name)
+
if info:
hostname = info.server.split('.')[0]
address = socket.inet_ntoa(cast(bytes, info.address))
+
+ # Prevent duplicate announcements from extending the discovery delay
+ if hostname not in self.discoveries:
+ self._heard_announcement = True
+
self.discoveries[hostname] = address
+
if self.listener and hasattr(self.listener, "add_device"):
self.listener.add_device(hostname, address)
def remove_service(self, zeroconf, type, name):
info = self.zeroconf.get_service_info(type, name)
+ self._heard_announcement = True
+
if self.listener and hasattr(self.listener, "remove_device"):
self.listener.remove_device(info.server,
self.discoveries[info.server])
+
if info.server in self.discoveries:
del(self.discoveries[info.server])
-
- def stop(self):
- if self.browser:
- self.browser.cancel()
- self.browser = None