Merge "ginstall: Also check gfactive for active partition"
diff --git a/conman/connection_manager.py b/conman/connection_manager.py
index 11da31c..f43ee8d 100755
--- a/conman/connection_manager.py
+++ b/conman/connection_manager.py
@@ -26,6 +26,15 @@
import ratchet
import status
+try:
+ import monotime # pylint: disable=unused-import,g-import-not-at-top
+except ImportError:
+ pass
+try:
+ _gettime = time.monotonic
+except AttributeError:
+ _gettime = time.time
+
HOSTNAME = socket.gethostname()
TMP_HOSTS = '/tmp/hosts'
@@ -446,7 +455,7 @@
to join the WLAN again.
7. Sleep for the rest of the duration of _run_duration_s.
"""
- start_time = time.time()
+ start_time = _gettime()
self.notifier.process_events()
while self.notifier.check_events():
self.notifier.read_events()
@@ -507,7 +516,7 @@
# routes to the ACS for provisioning.
if ((not self.acs() or provisioning_failed) and
not getattr(wifi, 'last_successful_bss_info', None) and
- time.time() > wifi.last_wifi_scan_time + self._wifi_scan_period_s):
+ _gettime() > wifi.last_wifi_scan_time + self._wifi_scan_period_s):
logging.debug('Performing scan on %s.', wifi.name)
self._wifi_scan(wifi)
@@ -518,7 +527,7 @@
# case 5 is unavailable for some reason.
for band in wifi.bands:
wlan_configuration = self._wlan_configuration.get(band, None)
- if wlan_configuration and time.time() >= self._try_wlan_after[band]:
+ if wlan_configuration and _gettime() >= self._try_wlan_after[band]:
logging.info('Trying to join WLAN on %s.', wifi.name)
wlan_configuration.start_client()
if self._connected_to_wlan(wifi):
@@ -529,7 +538,7 @@
else:
logging.error('Failed to connect to WLAN on %s.', wifi.name)
wifi.status.connected_to_wlan = False
- self._try_wlan_after[band] = time.time() + self._wlan_retry_s
+ self._try_wlan_after[band] = _gettime() + self._wlan_retry_s
else:
# If we are aren't on the WLAN, can ping the ACS, and haven't gotten a
# new WLAN configuration yet, there are two possibilities:
@@ -552,7 +561,7 @@
if provisioning_failed:
wifi.last_successful_bss_info = None
- now = time.time()
+ now = _gettime()
if self._wlan_configuration:
logging.info('ACS has not updated WLAN configuration; will retry '
' with old config.')
@@ -571,7 +580,7 @@
# If we didn't manage to join the WLAN, and we don't have an ACS
# connection or the ACS session failed, we should try another open AP.
if not self.acs() or provisioning_failed:
- now = time.time()
+ now = _gettime()
if self._connected_to_open(wifi) and not provisioning_failed:
logging.debug('Waiting for provisioning for %ds.',
now - self.provisioning_since(wifi))
@@ -579,7 +588,7 @@
logging.debug('Not connected to ACS or provisioning failed')
self._try_next_bssid(wifi)
- time.sleep(max(0, self._run_duration_s - (time.time() - start_time)))
+ time.sleep(max(0, self._run_duration_s - (_gettime() - start_time)))
def acs(self):
result = False
@@ -784,7 +793,7 @@
def _wifi_scan(self, wifi):
"""Perform a wifi scan and update wifi.cycler."""
logging.info('Scanning on %s...', wifi.name)
- wifi.last_wifi_scan_time = time.time()
+ wifi.last_wifi_scan_time = _gettime()
subprocess.call(self.IFUP + [wifi.name])
# /bin/wifi takes a --band option but then finds the right interface for it,
# so it's okay to just pick the first band here.
@@ -825,7 +834,7 @@
wifi.attach_wpa_control(self._wpa_control_interface)
wifi.handle_wpa_events()
wifi.status.connected_to_open = True
- now = time.time()
+ now = _gettime()
wifi.complain_about_acs_at = now + 5
logging.info('Attempting to provision via SSID %s', bss_info.ssid)
self._try_to_upload_logs = True
diff --git a/conman/cycler.py b/conman/cycler.py
index 23f2ce4..7795368 100755
--- a/conman/cycler.py
+++ b/conman/cycler.py
@@ -4,6 +4,15 @@
import time
+try:
+ import monotime # pylint: disable=unused-import,g-import-not-at-top
+except ImportError:
+ pass
+try:
+ _gettime = time.monotonic
+except AttributeError:
+ _gettime = time.time
+
class AgingPriorityCycler(object):
"""A modified priority queue.
@@ -40,7 +49,7 @@
try:
self._items[item][0] = priority
except KeyError:
- self._items[item] = [priority, time.time()]
+ self._items[item] = [priority, _gettime()]
def remove(self, item):
if item in self._items:
@@ -65,7 +74,7 @@
if self.empty():
return
- now = time.time()
+ now = _gettime()
def aged_priority(key_value):
_, (priority, birth) = key_value
@@ -86,7 +95,7 @@
Args:
items: An iterable of (item, priority).
"""
- now = time.time()
+ now = _gettime()
new_items = {}
for item, priority in items:
t = now
diff --git a/conman/ratchet.py b/conman/ratchet.py
index 350ed47..07e61a8 100644
--- a/conman/ratchet.py
+++ b/conman/ratchet.py
@@ -6,6 +6,15 @@
import os
import time
+try:
+ import monotime # pylint: disable=unused-import,g-import-not-at-top
+except ImportError:
+ pass
+try:
+ _gettime = time.monotonic
+except AttributeError:
+ _gettime = time.time
+
# This has to be called before another module calls it with a higher log level.
# pylint: disable=g-import-not-at-top
logging.basicConfig(level=logging.DEBUG)
@@ -37,8 +46,8 @@
t0: The timestamp after which to evaluate the condition.
start_at: The timestamp from which to compute the timeout.
"""
- self.t0 = t0 or time.time()
- self.start_at = start_at or time.time()
+ self.t0 = t0 or _gettime()
+ self.start_at = start_at or _gettime()
self.done_after = None
self.done_by = None
self.timed_out = False
@@ -56,21 +65,21 @@
self.mark_done()
return True
- now = time.time()
+ now = _gettime()
if now > self.start_at + self.timeout:
self.timed_out = True
self.logger.info('%s timed out after %.2f seconds',
self.name, now - self.t0)
raise TimeoutException()
- self.not_done_before = time.time()
+ self.not_done_before = _gettime()
return False
def mark_done(self):
# In general, we don't know when a condition finished, but we know it was
# *after* whenever it was most recently not done.
self.done_after = self.not_done_before
- self.done_by = time.time()
+ self.done_by = _gettime()
self.logger.info('%s completed after %.2f seconds',
self.name, self.done_by - self.t0)
diff --git a/waveguide/fake/devlist b/waveguide/fake/devlist
new file mode 100644
index 0000000..e46a05f
--- /dev/null
+++ b/waveguide/fake/devlist
@@ -0,0 +1,30 @@
+phy#1
+ Interface wlan1_portal
+ ifindex 13
+ wdev 0x100000002
+ addr aa:32:ed:07:7f:a8
+ ssid GFiberSetupAutomation
+ type AP
+ channel 153 (5765 MHz), width: 80 MHz, center1: 5775 MHz
+ Interface wlan1
+ ifindex 10
+ wdev 0x100000001
+ addr 88:dc:96:21:13:a1
+ ssid Tangent
+ type AP
+ channel 153 (5765 MHz), width: 80 MHz, center1: 5775 MHz
+phy#0
+ Interface wlan0_portal
+ ifindex 12
+ wdev 0x2
+ addr aa:3b:1c:64:41:93
+ ssid GFiberSetupAutomation
+ type AP
+ channel 1 (2412 MHz), width: 20 MHz, center1: 2412 MHz
+ Interface wlan0
+ ifindex 6
+ wdev 0x1
+ addr f4:f5:e8:81:54:77
+ ssid Tangent
+ type AP
+ channel 1 (2412 MHz), width: 20 MHz, center1: 2412 MHz
diff --git a/waveguide/fake/iw b/waveguide/fake/iw
index ba954f6..b642a29 100755
--- a/waveguide/fake/iw
+++ b/waveguide/fake/iw
@@ -52,6 +52,10 @@
[ -r "stationdump.$dev" ] && cat "stationdump.$dev"
exit 0
;;
+ dev)
+ [ -r "devlist" ] && cat "devlist"
+ exit 0
+ ;;
*)
exit 1
;;
diff --git a/waveguide/waveguide.py b/waveguide/waveguide.py
index 594f83f..8f0c3f2 100755
--- a/waveguide/waveguide.py
+++ b/waveguide/waveguide.py
@@ -16,6 +16,7 @@
# pylint:disable=invalid-name
"""Wifi channel selection and roaming daemon."""
+import collections
import errno
import gc
import json
@@ -204,10 +205,22 @@
class WlanManager(object):
- """A class representing one wifi interface on the local host."""
+ """A class representing one wifi interface on the local host.
+
+ Args:
+ phyname (str): name of the phy, like phy0
+ vdevname (str): name of the vdev, like wlan0 or wlan1_portal
+ high_power (bool): advertise the AP as high power
+ tv_box (bool): advertise the AP as a TV box
+ wifiblaster_controller(:obj:`WifiblasterController`): a shared
+ WifiblasterController to probe associating STAs
+ primary (bool): True if the primary AP on a radio, False otherwise.
+ If False, defers most functionality to the WlanManager for the primary AP
+ and logs associated stations only.
+ """
def __init__(self, phyname, vdevname, high_power, tv_box,
- wifiblaster_controller):
+ wifiblaster_controller, primary=True):
self.phyname = phyname
self.vdevname = vdevname
self.mac = '\0\0\0\0\0\0'
@@ -234,6 +247,7 @@
self.auto_disabled = None
self.autochan_2g = self.autochan_5g = self.autochan_free = 0
self.wifiblaster_controller = wifiblaster_controller
+ self.primary = primary
helpers.Unlink(self.Filename('disabled'))
def Filename(self, suffix):
@@ -253,7 +267,10 @@
# TODO(apenwarr): when we have async subprocs, add those here
def GetReadFds(self):
- return [self.mcast.rsock]
+ if self.primary:
+ return [self.mcast.rsock]
+ else:
+ return []
def NextTimeout(self):
return self.next_scan_time
@@ -365,8 +382,10 @@
def UpdateStationInfo(self):
# These change in the background, not as the result of a scan
- RunProc(callback=self._SurveyResults,
- args=['iw', 'dev', self.vdevname, 'survey', 'dump'])
+ if self.primary:
+ RunProc(callback=self._SurveyResults,
+ args=['iw', 'dev', self.vdevname, 'survey', 'dump'])
+
RunProc(callback=self._AssocResults,
args=['iw', 'dev', self.vdevname, 'station', 'dump'])
@@ -837,15 +856,12 @@
raise Exception('failed (%d) getting wifi dev list: %r' %
(errcode, stderr))
phy = dev = devtype = None
- phy_devs = {}
+ phy_devs = collections.defaultdict(list)
def AddEntry():
if phy and dev:
if devtype == 'AP':
- # We only want one vdev per PHY. Special-purpose vdevs are
- # probably the same name with an extension, so use the shortest one.
- if phy not in phy_devs or len(phy_devs[phy]) > len(dev):
- phy_devs[phy] = dev
+ phy_devs[phy].append(dev)
else:
log.Debug('Skipping dev %r because type %r != AP', dev, devtype)
@@ -867,17 +883,30 @@
if g:
devtype = g.group(1)
AddEntry()
+
existing_devs = dict((m.vdevname, m) for m in managers)
+ new_devs = set()
+
+ for phy, devs in phy_devs.items():
+ new_devs.update(devs)
+
+ # We only want one full-fledged vdev per PHY. Special-purpose vdevs are
+ # probably the same name with an extension, so treat the vdev with the
+ # shortest name as the full-fledged one.
+ devs.sort(key=lambda dev: (len(dev), dev))
+ for i, dev in enumerate(devs):
+ primary = i == 0
+ if dev not in existing_devs:
+ log.Debug('Creating wlan manager for (%r, %r)', phy, dev)
+ managers.append(
+ WlanManager(phy, dev, high_power=high_power, tv_box=tv_box,
+ wifiblaster_controller=wifiblaster_controller,
+ primary=primary))
+
for dev, m in existing_devs.iteritems():
- if dev not in phy_devs.values():
+ if dev not in new_devs:
log.Log('Forgetting interface %r.', dev)
managers.remove(m)
- for phy, dev in phy_devs.iteritems():
- if dev not in existing_devs:
- log.Debug('Creating wlan manager for (%r, %r)', phy, dev)
- managers.append(
- WlanManager(phy, dev, high_power=high_power, tv_box=tv_box,
- wifiblaster_controller=wifiblaster_controller))
RunProc(callback=ParseDevList, args=['iw', 'dev'])
@@ -1171,7 +1200,9 @@
# node joins, so it can learn about the other nodes as quickly as
# possible. But if we do that, we need to rate limit it somehow.
for m in managers:
- m.DoScans()
+ if m.primary:
+ m.DoScans()
+
if ((opt.tx_interval and now - last_sent > opt.tx_interval) or (
opt.autochan_interval and now - last_autochan > opt.autochan_interval)):
if not opt.fake:
@@ -1182,12 +1213,15 @@
if opt.tx_interval and now - last_sent > opt.tx_interval:
last_sent = now
for m in managers:
- m.SendUpdate()
- log.WriteEventFile('sentpacket')
+ if m.primary:
+ m.SendUpdate()
+ log.WriteEventFile('sentpacket')
if opt.autochan_interval and now - last_autochan > opt.autochan_interval:
last_autochan = now
for m in managers:
- m.ChooseChannel()
+ if m.primary:
+ m.ChooseChannel()
+
if opt.print_interval and now - last_print > opt.print_interval:
last_print = now
selfmacs = set()
@@ -1249,10 +1283,10 @@
else:
can2G_count += 1
capability = '2.4'
- log.Log('Connected station %s supports %s GHz', station, capability)
+ m.Log('Connected station %s supports %s GHz', station, capability)
species = clientinfo.taxonomize(station)
if species:
- log.Log('Connected station %s taxonomy: %s', station, species)
+ m.Log('Connected station %s taxonomy: %s', station, species)
if log_sta_band_capabilities:
log.Log('Connected stations: total %d, 5 GHz %d, 2.4 GHz %d',
can5G_count + can2G_count, can5G_count, can2G_count)
diff --git a/waveguide/waveguide_test.py b/waveguide/waveguide_test.py
index bd7a65d..9a67bbe 100644
--- a/waveguide/waveguide_test.py
+++ b/waveguide/waveguide_test.py
@@ -19,6 +19,13 @@
from wvtest import wvtest
+class FakeOptDict(object):
+ """A fake options.OptDict containing default values."""
+
+ def __init__(self):
+ self.status_dir = '/tmp/waveguide'
+
+
@wvtest.wvtest
def IwTimeoutTest():
old_timeout = waveguide.IW_TIMEOUT_SECS
@@ -30,5 +37,28 @@
os.environ['PATH'] = old_path
waveguide.IW_TIMEOUT_SECS = old_timeout
+
+@wvtest.wvtest
+def ParseDevListTest():
+ waveguide.opt = FakeOptDict()
+
+ old_path = os.environ['PATH']
+ os.environ['PATH'] = 'fake:' + os.environ['PATH']
+ managers = []
+ waveguide.CreateManagers(managers, False, False, None)
+
+ got_manager_summary = set((m.phyname, m.vdevname, m.primary)
+ for m in managers)
+ want_manager_summary = set((
+ ('phy1', 'wlan1', True),
+ ('phy1', 'wlan1_portal', False),
+ ('phy0', 'wlan0', True),
+ ('phy0', 'wlan0_portal', False)))
+
+ wvtest.WVPASSEQ(got_manager_summary, want_manager_summary)
+
+ os.environ['PATH'] = old_path
+
+
if __name__ == '__main__':
wvtest.wvtest_main()
diff --git a/wifi/quantenna.py b/wifi/quantenna.py
index 7aad0d0..f3f96ef 100755
--- a/wifi/quantenna.py
+++ b/wifi/quantenna.py
@@ -54,7 +54,7 @@
def _set_link_state(hif, state):
- subprocess.check_output(['ip', 'link', 'set', 'dev', hif, state])
+ subprocess.check_output(['if' + state, hif])
def _ifplugd_action(hif, state):