Merge "Add chameleon diags dependency"
diff --git a/conman/connection_manager.py b/conman/connection_manager.py
index 3808458..374d495 100755
--- a/conman/connection_manager.py
+++ b/conman/connection_manager.py
@@ -79,7 +79,7 @@
'-s': 'ssid',
'--ssid': 'ssid',
'-S': 'interface_suffix',
- '--interface_suffix': 'interface_suffix',
+ '--interface-suffix': 'interface_suffix',
}
attr = None
for line in self.command:
@@ -91,7 +91,7 @@
attr = binwifi_option_attrs.get(line, None)
if line.startswith('WIFI_PSK='):
- self.passphrase = line.split('WIFI_PSK=')[-1]
+ self.passphrase = line[len('WIFI_PSK='):]
if self.ssid is None:
raise ValueError('Command file does not specify SSID')
@@ -601,6 +601,8 @@
if lowest_metric_interface:
ip = lowest_metric_interface[1].get_ip_address()
ip_line = '%s %s\n' % (ip, HOSTNAME) if ip else ''
+ logging.info('Lowest metric default route is on dev %r',
+ lowest_metric_interface[1].name)
new_tmp_hosts = '%s127.0.0.1 localhost' % ip_line
diff --git a/conman/connection_manager_test.py b/conman/connection_manager_test.py
index 271cac7..7cf1785 100755
--- a/conman/connection_manager_test.py
+++ b/conman/connection_manager_test.py
@@ -248,6 +248,21 @@
f.write(value)
+@wvtest.wvtest
+def WLANConfigurationParseTest(): # pylint: disable=invalid-name
+ """Test WLANConfiguration parsing."""
+ cmd = '\n'.join([
+ 'WIFI_PSK=abcdWIFI_PSK=qwer', 'wifi', 'set', '-P', '-b', '5',
+ '--bridge=br0', '-s', 'my ssid=1', '--interface-suffix', '_suffix',
+ ])
+ config = WLANConfiguration('5', interface_test.Wifi('wcli0', 20), cmd, None,
+ None)
+
+ wvtest.WVPASSEQ('my ssid=1', config.ssid)
+ wvtest.WVPASSEQ('abcdWIFI_PSK=qwer', config.passphrase)
+ wvtest.WVPASSEQ('_suffix', config.interface_suffix)
+
+
class Wifi(interface_test.Wifi):
def __init__(self, *args, **kwargs):
diff --git a/conman/interface.py b/conman/interface.py
index cd4d323..2d0fbbe 100755
--- a/conman/interface.py
+++ b/conman/interface.py
@@ -17,7 +17,6 @@
METRIC_5GHZ = 20
METRIC_24GHZ_5GHZ = 21
METRIC_24GHZ = 22
-METRIC_TEMPORARY_CONNECTION_CHECK = 99
RFC2385_MULTICAST_ROUTE = '239.0.0.0/8'
@@ -83,21 +82,7 @@
' (ACS)' if check_acs else '', self.name)
return False
- # Temporarily add a route to make sure the connection check can be run.
- # Give it a high metric so that it won't interfere with normal default
- # routes.
- added_temporary_route = False
- if 'default' not in self.current_routes():
- logging.debug('Adding temporary connection check routes for dev %s',
- self.name)
- self._ip_route('add', self._gateway_ip,
- 'dev', self.name,
- 'metric', str(METRIC_TEMPORARY_CONNECTION_CHECK))
- self._ip_route('add', 'default',
- 'via', self._gateway_ip,
- 'dev', self.name,
- 'metric', str(METRIC_TEMPORARY_CONNECTION_CHECK))
- added_temporary_route = True
+ self.add_routes()
cmd = [self.CONNECTION_CHECK, '-I', self.name]
if check_acs:
@@ -110,17 +95,6 @@
self.name,
'passed' if result else 'failed')
- # Delete the temporary route.
- if added_temporary_route:
- logging.debug('Deleting temporary connection check routes for dev %s',
- self.name)
- self._ip_route('del', 'default',
- 'dev', self.name,
- 'metric', str(METRIC_TEMPORARY_CONNECTION_CHECK))
- self._ip_route('del', self._gateway_ip,
- 'dev', self.name,
- 'metric', str(METRIC_TEMPORARY_CONNECTION_CHECK))
-
return result
def gateway(self):
@@ -147,40 +121,48 @@
logging.info('Cannot add route for %s without a metric.', self.name)
return
- if self._gateway_ip is None:
- logging.info('Cannot add route for %s without a gateway IP.', self.name)
- return
-
# If the current routes are the same, there is nothing to do. If either
# exists but is different, delete it before adding an updated one.
current = self.current_routes()
- default = current.get('default', {})
- if ((default.get('via', None), default.get('metric', None)) !=
- (self._gateway_ip, str(self.metric))):
- logging.debug('Adding default route for dev %s', self.name)
- self.delete_route('default')
- self._ip_route('add', 'default',
- 'via', self._gateway_ip,
- 'dev', self.name,
- 'metric', str(self.metric))
+
+ to_add = []
subnet = current.get('subnet', {})
- if (self._subnet and
- (subnet.get('via', None), subnet.get('metric', None)) !=
- (self._gateway_ip, str(self.metric))):
- logging.debug('Adding subnet route for dev %s', self.name)
- self.delete_route('subnet')
- self._ip_route('add', self._subnet,
- 'dev', self.name,
- 'metric', str(self.metric))
+ if self._subnet:
+ if ((subnet.get('route', None), subnet.get('metric', None)) !=
+ (self._subnet, str(self.metric))):
+ logging.debug('Adding subnet route for dev %s', self.name)
+ to_add.append(('subnet', ('add', self._subnet, 'dev', self.name,
+ 'metric', str(self.metric))))
+ subnet = self._subnet
+ else:
+ subnet = None
+ self.delete_route('default', 'subnet')
+
+ default = current.get('default', {})
+ if self._gateway_ip:
+ if (subnet and
+ (default.get('via', None), default.get('metric', None)) !=
+ (self._gateway_ip, str(self.metric))):
+ logging.debug('Adding default route for dev %s', self.name)
+ to_add.append(('default',
+ ('add', 'default', 'via', self._gateway_ip,
+ 'dev', self.name, 'metric', str(self.metric))))
+ else:
+ self.delete_route('default')
# RFC2365 multicast route.
if current.get('multicast', {}).get('metric', None) != str(self.metric):
logging.debug('Adding multicast route for dev %s', self.name)
- self.delete_route('multicast')
- self._ip_route('add', RFC2385_MULTICAST_ROUTE,
- 'dev', self.name,
- 'metric', str(self.metric))
+ to_add.append(('multicast', ('add', RFC2385_MULTICAST_ROUTE,
+ 'dev', self.name,
+ 'metric', str(self.metric))))
+
+ for route_type, _ in to_add[::-1]:
+ self.delete_route(route_type)
+
+ for _, cmd in to_add:
+ self._ip_route(*cmd)
def delete_route(self, *args):
"""Delete default and/or subnet routes for this interface.
@@ -198,7 +180,8 @@
raise ValueError(
'Must specify at least one of default, subnet, multicast to delete.')
- for route_type in args:
+ # Use a sorted list to ensure that default comes before subnet.
+ for route_type in sorted(list(args)):
while route_type in self.current_routes():
logging.debug('Deleting %s route for dev %s', route_type, self.name)
self._ip_route('del', self.current_routes()[route_type]['route'],
@@ -242,17 +225,17 @@
' '.join(self.IP_ROUTE), ' '.join(args))
return ''
- return self._really_ip_route(*args)
-
- def _really_ip_route(self, *args):
try:
logging.debug('%s calling ip route %s', self.name, ' '.join(args))
- return subprocess.check_output(self.IP_ROUTE + list(args))
+ return self._really_ip_route(*args)
except subprocess.CalledProcessError as e:
logging.error('Failed to call "ip route" with args %r: %s', args,
e.message)
return ''
+ def _really_ip_route(self, *args):
+ return subprocess.check_output(self.IP_ROUTE + list(args))
+
def _ip_addr_show(self):
try:
return subprocess.check_output(self.IP_ADDR_SHOW + [self.name])
diff --git a/conman/interface_test.py b/conman/interface_test.py
index e8ab4ca..79c58ae 100755
--- a/conman/interface_test.py
+++ b/conman/interface_test.py
@@ -5,6 +5,9 @@
import logging
import os
import shutil
+import socket
+import struct
+import subprocess
import tempfile
import time
@@ -38,6 +41,13 @@
self.routing_table = {}
self.ip_testonly = None
+ def _connection_check(self, *args, **kwargs):
+ result = super(FakeInterfaceMixin, self)._connection_check(*args, **kwargs)
+ if (self.current_routes().get('default', {}).get('via', None) !=
+ self._gateway_ip):
+ return False
+ return result
+
def set_connection_check_result(self, result):
if result in ['succeed', 'fail', 'restricted']:
# pylint: disable=invalid-name
@@ -46,6 +56,29 @@
raise ValueError('Invalid fake connection_check script.')
def _really_ip_route(self, *args):
+ def can_add_route():
+ def ip_to_int(ip):
+ return struct.unpack('!I', socket.inet_pton(socket.AF_INET, ip))[0]
+
+ if args[1] != 'default':
+ return True
+
+ via = ip_to_int(args[args.index('via') + 1])
+ for (ifc, route, _), _ in self.routing_table.iteritems():
+ if ifc != self.name:
+ continue
+
+ netmask = 0
+ if '/' in route:
+ route, netmask = route.split('/')
+ netmask = 32 - int(netmask)
+ route = ip_to_int(route)
+
+ if (route >> netmask) == (via >> netmask):
+ return True
+
+ return False
+
if not args:
return '\n'.join(self.routing_table.values() +
['1.2.3.4/24 dev fake0 proto kernel scope link',
@@ -61,6 +94,10 @@
route = args[1]
key = (self.name, route, metric)
if args[0] == 'add' and key not in self.routing_table:
+ if not can_add_route():
+ raise subprocess.CalledProcessError(
+ 'Tried to add default route without subnet route: %r',
+ self.routing_table)
logging.debug('Adding route for %r', key)
self.routing_table[key] = ' '.join(args[1:])
elif args[0] == 'del':
@@ -94,8 +131,8 @@
"""Fake wpactrl.WPACtrl."""
# pylint: disable=unused-argument
- def __init__(self, socket):
- self._socket = socket
+ def __init__(self, wpa_socket):
+ self._socket = wpa_socket
self.events = []
self.attached = False
self.connected = False
@@ -344,8 +381,8 @@
b.add_moca_station(0)
wvtest.WVFAIL(os.path.exists(autoprov_filepath))
- b.set_gateway_ip('192.168.1.1')
b.set_subnet('192.168.1.0/24')
+ b.set_gateway_ip('192.168.1.1')
wvtest.WVFAIL(os.path.exists(autoprov_filepath))
# Everything should fail because the interface is not initialized.
wvtest.WVFAIL(b.acs())
@@ -403,6 +440,41 @@
b.ip_testonly = '192.168.1.100'
wvtest.WVPASSEQ(b.get_ip_address(), '192.168.1.100')
+ # Get a new gateway/subnet (e.g. due to joining a new network).
+ # Not on the subnet; adding IP should fail.
+ b.set_gateway_ip('192.168.2.1')
+ wvtest.WVFAIL('default' in b.current_routes())
+ wvtest.WVPASS('subnet' in b.current_routes())
+ # Without a default route, the connection check should fail.
+ wvtest.WVFAIL(b.acs())
+
+ # Now we get the subnet and should add updated subnet and gateway routes.
+ b.set_subnet('192.168.2.0/24')
+ wvtest.WVPASSEQ(b.current_routes()['default']['via'], '192.168.2.1')
+ wvtest.WVPASSLE(int(b.current_routes()['default']['metric']), 50)
+ wvtest.WVPASSEQ(b.current_routes()['subnet']['route'], '192.168.2.0/24')
+ wvtest.WVPASSLE(int(b.current_routes()['subnet']['metric']), 50)
+ wvtest.WVPASS(b.acs())
+
+ # If we have no subnet, make sure that both subnet and default routes are
+ # removed.
+ b.set_subnet(None)
+ wvtest.WVFAIL('subnet' in b.current_routes())
+ wvtest.WVFAIL('default' in b.current_routes())
+
+ # Now repeat the new-network test, but with a faulty connection. Make sure
+ # the metrics are set appropriately.
+ b.set_connection_check_result('fail')
+ b.set_subnet('192.168.3.0/24')
+ b.set_gateway_ip('192.168.3.1')
+ wvtest.WVPASSGE(int(b.current_routes()['default']['metric']), 50)
+ wvtest.WVPASSGE(int(b.current_routes()['subnet']['metric']), 50)
+
+ # Now test deleting only the gateway IP.
+ b.set_gateway_ip(None)
+ wvtest.WVPASS('subnet' in b.current_routes())
+ wvtest.WVFAIL('default' in b.current_routes())
+
finally:
shutil.rmtree(tmp_dir)
@@ -505,6 +577,7 @@
b = Bridge('br0', '10', acs_autoprovisioning_filepath=autoprov_filepath)
b.add_moca_station(0)
b.set_gateway_ip('192.168.1.1')
+ b.set_subnet('192.168.1.0/24')
b.set_connection_check_result('succeed')
b.initialize()
diff --git a/ledpattern/ledpatterns b/ledpattern/ledpatterns
index 2e0ab63..0b86fb7 100644
--- a/ledpattern/ledpatterns
+++ b/ledpattern/ledpatterns
@@ -1,12 +1,28 @@
-HALTED,P,R
-NO_LASER_CHANNEL,P,P
-SET_LASER_FAILED,P,R,R
-LOSLOF_ALARM,P,R,B
-OTHER_ALARM,P,R,P
-GPON_INITIAL,P,B,R
-GPON_STANDBY,P,B,P
-GPON_SERIAL,P,P,R
-GPON_RANGING,P,P,B
+SET_LASER_FAILED_0,P,R,R,R,R
+SET_LASER_FAILED_1,P,R,R,R,B
+SET_LASER_FAILED_2,P,R,R,B,R
+SET_LASER_FAILED_3,P,R,R,B,B
+SET_LASER_FAILED_4,P,R,B,R,R
+SET_LASER_FAILED_5,P,R,B,R,B
+SET_LASER_FAILED_6,P,R,B,B,R
+SET_LASER_FAILED_7,P,R,B,B,B
+SET_LASER_FAILED_8,P,B,R,R,R
+SET_LASER_FAILED_9,P,B,R,R,B
+SET_LASER_FAILED_10,P,B,R,B,R
+SET_LASER_FAILED_11,P,B,R,B,B
+SET_LASER_FAILED_12,P,B,B,R,R
+SET_LASER_FAILED_13,P,B,B,R,B
+SET_LASER_FAILED_14,P,B,B,B,R
+SET_LASER_FAILED_15,P,B,B,B,B
+GPON_INITIAL,P,P,R,R
+GPON_STANDBY,P,P,R,B
+GPON_SERIAL,P,P,B,R
+GPON_RANGING,P,P,B,B
+HALTED,P,R,R
+NO_LASER_CHANNEL,P,R,B
+LOSLOF_ALARM,P,R,P
+OTHER_ALARM,P,B,R
WAIT_ACS,P,B,B
-ALL_OK,P,B,B,B
-UNKNOWN_ERROR,P,R,R,R
+ALL_OK,P,B
+UNKNOWN_ERROR,P,R
+
diff --git a/ledpattern/ledtapcode.sh b/ledpattern/ledtapcode.sh
index 6841f2f..7793993 100755
--- a/ledpattern/ledtapcode.sh
+++ b/ledpattern/ledtapcode.sh
@@ -44,8 +44,15 @@
if [ -f "$LASER_STATUS_FILE" ]; then
laser_status=$(cat "$LASER_STATUS_FILE")
if [ "$laser_status" -ne 0 ]; then
- echo "Playing SET_LASER_FAILED pattern"
- PlayPatternAndExit SET_LASER_FAILED
+ # Blink out requested laser channel that we failed to tune to
+ laser_channel=$(cat "$LASER_CHANNEL_FILE")
+ if [ "$laser_channel" -eq -1 ]; then
+ echo "$LASER_STATUS_FILE indicates success but there is no requested
+ channel in $LASER_CHANNEL_FILE"
+ PlayPatternAndExit UNKNOWN_ERROR
+ fi
+ echo "Playing SET_LASER_FAILED_${laser_channel} pattern"
+ PlayPatternAndExit "SET_LASER_FAILED_${laser_channel}"
fi
fi
@@ -89,19 +96,35 @@
PlayPatternAndExit GPON_RANGING
fi
-laser_channel=$(cat "$LASER_CHANNEL_FILE")
-if [ ! -f "$ACS_FILE" ] && [ "$laser_channel" -eq "-1" ]; then
- echo "Playing NO_LASER_CHANNEL pattern"
- PlayPatternAndExit NO_LASER_CHANNEL
-elif [ ! -f "$ACS_FILE" ] && [ $laser_channel -ne "-1" ]; then
- echo "Playing WAIT_ACS pattern"
- PlayPatternAndExit WAIT_ACS
-elif [ -f "$ACS_FILE" ] && [ $laser_channel -ne "-1" ]; then
- echo "Playing ALL_OK pattern"
- PlayPatternAndExit ALL_OK
-else
- # If we get all the way here and nothing triggered on the way then this really
- # is an unknown error...
- echo "Nothing triggered? Playing UNKNOWN_ERROR pattern..."
- PlayPatternAndExit UNKNOWN_ERROR
+# GFLT110 does not have tuneable laser
+tuneable_laser="false"
+if startswith "$(cat /etc/platform)" "GFLT3"; then
+ tuneable_laser="true"
fi
+
+if [ "$tuneable_laser" = false ]; then
+ if [ ! -f "$ACS_FILE" ]; then
+ echo "Playing WAIT_ACS pattern"
+ PlayPatternAndExit WAIT_ACS
+ else
+ echo "Playing ALL_OK pattern"
+ PlayPatternAndExit ALL_OK
+ fi
+else
+ laser_channel=$(cat "$LASER_CHANNEL_FILE")
+ if [ ! -f "$ACS_FILE" ] && [ "$laser_channel" -eq "-1" ]; then
+ echo "Playing NO_LASER_CHANNEL pattern"
+ PlayPatternAndExit NO_LASER_CHANNEL
+ elif [ ! -f "$ACS_FILE" ] && [ "$laser_channel" -ne "-1" ]; then
+ echo "Playing WAIT_ACS pattern"
+ PlayPatternAndExit WAIT_ACS
+ elif [ -f "$ACS_FILE" ] && [ "$laser_channel" -eq "-1" ]; then
+ echo "Has ACS but no laser channel"
+ echo "Playing NO_LASER_CHANNEL pattern"
+ PlayPatternAndExit NO_LASER_CHANNEL
+ else
+ echo "Playing ALL_OK pattern"
+ PlayPatternAndExit ALL_OK
+ fi
+fi
+
diff --git a/wifi/quantenna.py b/wifi/quantenna.py
index 1408574..7aad0d0 100755
--- a/wifi/quantenna.py
+++ b/wifi/quantenna.py
@@ -34,20 +34,27 @@
raise utils.BinWifiException('no VLAN ID for interface %s' % hif)
-def _get_interface(mode, suffix):
+def _get_interfaces(mode, suffix):
# Each host interface (hif) maps to exactly one LHOST interface (lif) based on
# the VLAN ID as follows: the lif is wifiX where X is the VLAN ID - 2 (VLAN
# IDs start at 2). The client interface must map to wifi0, so it must have
# VLAN ID 2.
prefix = 'wlan' if mode == 'ap' else 'wcli'
- suffix = '_' + suffix if suffix else ''
+ suffix = r'.*' if suffix == 'ALL' else suffix
for hif in _get_quantenna_interfaces():
- if re.match(prefix + r'\d*' + suffix, hif):
+ if re.match(r'^' + prefix + r'\d*' + suffix + r'$', hif):
vlan = _get_vlan(hif)
lif = 'wifi%d' % (vlan - 2)
mac = _get_external_mac(hif)
- return hif, lif, mac, vlan
- return None, None, None, None
+ yield hif, lif, mac, vlan
+
+
+def _get_interface(mode, suffix):
+ return next(_get_interfaces(mode, suffix), (None, None, None, None))
+
+
+def _set_link_state(hif, state):
+ subprocess.check_output(['ip', 'link', 'set', 'dev', hif, state])
def _ifplugd_action(hif, state):
@@ -145,6 +152,7 @@
_qcsapi('vlan_config', 'pcie0', 'trunk', vlan)
_qcsapi('block_bss', lif, 0)
+ _set_link_state(hif, 'up')
_ifplugd_action(hif, 'up')
except:
stop_ap_wifi(opt)
@@ -188,6 +196,7 @@
_qcsapi('vlan_config', 'pcie0', 'enable')
_qcsapi('vlan_config', 'pcie0', 'trunk', vlan)
+ _set_link_state(hif, 'up')
_ifplugd_action(hif, 'up')
except:
stop_client_wifi(opt)
@@ -198,34 +207,32 @@
def stop_ap_wifi(opt):
"""Disable AP."""
- hif, lif, _, _ = _get_interface('ap', opt.interface_suffix)
- if not hif:
- return False
+ hif = None
+ for hif, lif, _, _ in _get_interfaces('ap', opt.interface_suffix):
+ try:
+ _qcsapi('wifi_remove_bss', lif)
+ except subprocess.CalledProcessError:
+ pass
- try:
- _qcsapi('wifi_remove_bss', lif)
- except subprocess.CalledProcessError:
- pass
+ _set_link_state(hif, 'down')
+ _ifplugd_action(hif, 'down')
- _ifplugd_action(hif, 'down')
-
- return True
+ return hif is not None
def stop_client_wifi(opt):
"""Disable client."""
- hif, lif, _, _ = _get_interface('sta', opt.interface_suffix)
- if not hif:
- return False
+ hif = None
+ for hif, lif, _, _ in _get_interfaces('sta', opt.interface_suffix):
+ try:
+ _qcsapi('remove_ssid', lif, _qcsapi('get_ssid_list', lif, 1))
+ except subprocess.CalledProcessError:
+ pass
- try:
- _qcsapi('remove_ssid', lif, _qcsapi('get_ssid_list', lif, 1))
- except subprocess.CalledProcessError:
- pass
+ _set_link_state(hif, 'down')
+ _ifplugd_action(hif, 'down')
- _ifplugd_action(hif, 'down')
-
- return True
+ return hif is not None
def scan_wifi(_):
diff --git a/wifi/quantenna_test.py b/wifi/quantenna_test.py
index 177f557..b0fb485 100755
--- a/wifi/quantenna_test.py
+++ b/wifi/quantenna_test.py
@@ -35,7 +35,7 @@
quantenna._get_vlan = lambda _: 3
wvtest.WVPASSEQ(quantenna._get_interface('ap', ''),
('wlan0', 'wifi1', '00:00:00:00:00:00', 3))
- wvtest.WVPASSEQ(quantenna._get_interface('ap', 'portal'),
+ wvtest.WVPASSEQ(quantenna._get_interface('ap', '_portal'),
('wlan0_portal', 'wifi1', '00:00:00:00:00:00', 3))
wvtest.WVPASSEQ(quantenna._get_interface('sta', ''),
(None, None, None, None))