Wait for correct IP subnet changes during roaming
This allows roaming tests to pass when run against drivers
that do roaming without involving userspace. In these situations
we're briefly associated with the roaming target before we detect
that our DHCP lease is invalid and re-negotiate the lease. Tolerate
that window by also waiting for the IP address on the WiFi interface
to move to the correct subnet.
Add a library to make common netblock->mask/subnet/broadcast address
calculations. Pull duplicate logic to do this out of interface.py
and site_linux_router.py.
BUG=chromium:352847
TEST=wifi_matfunc continues to pass with this change.
Change-Id: I2e6786514aee533e36eb84e1329f19891d3744ba
Reviewed-on: https://chromium-review.googlesource.com/190173
Tested-by: Christopher Wiley <wiley@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Commit-Queue: Christopher Wiley <wiley@chromium.org>
diff --git a/server/cros/network/wifi_client.py b/server/cros/network/wifi_client.py
index 95b205f..376082f 100644
--- a/server/cros/network/wifi_client.py
+++ b/server/cros/network/wifi_client.py
@@ -148,6 +148,12 @@
@property
+ def wifi_ip_subnet(self):
+ """@return string IPv4 subnet prefix of self.wifi_if."""
+ return self._interface.ipv4_subnet
+
+
+ @property
def wifi_signal_level(self):
"""Returns the signal level of this DUT's WiFi interface.
diff --git a/server/cros/network/wifi_test_context_manager.py b/server/cros/network/wifi_test_context_manager.py
index e3237cd..f7bfc1b 100644
--- a/server/cros/network/wifi_test_context_manager.py
+++ b/server/cros/network/wifi_test_context_manager.py
@@ -285,7 +285,7 @@
if ap_num is None:
ap_num = 0
if ping_config is None:
- ping_ip = self.get_wifi_addr(ap_num=ap_num)
+ ping_ip = self.router.get_wifi_ip(ap_num=ap_num)
ping_config = ping_runner.PingConfig(ping_ip)
self.client.ping(ping_config)
@@ -321,6 +321,9 @@
start_time = time.time()
duration = lambda: time.time() - start_time
success = False
+ if ap_num is None:
+ ap_num = 0
+ desired_subnet = self.router.get_wifi_ip_subnet(ap_num)
while duration() < timeout_seconds:
success, state, _ = self.client.wait_for_service_states(
ssid, self.CONNECTED_STATES, timeout_seconds - duration())
@@ -332,9 +335,18 @@
actual_freq = self.client.get_iw_link_value(
iw_runner.IW_LINK_KEY_FREQUENCY)
if str(freq) != actual_freq:
+ logging.debug('Waiting for desired frequency %s (got %s).',
+ freq, actual_freq)
time.sleep(POLLING_INTERVAL_SECONDS)
continue
+ actual_subnet = self.client.wifi_ip_subnet
+ if actual_subnet != desired_subnet:
+ logging.debug('Waiting for desired subnet %s (got %s).',
+ desired_subnet, actual_subnet)
+ time.sleep(POLLING_INTERVAL_SECONDS)
+ continue
+
self.assert_ping_from_dut(ap_num=ap_num)
return
diff --git a/server/site_linux_router.py b/server/site_linux_router.py
index afc93d8..2f382fd 100644
--- a/server/site_linux_router.py
+++ b/server/site_linux_router.py
@@ -10,6 +10,7 @@
from autotest_lib.client.common_lib import error
from autotest_lib.client.common_lib.cros.network import interface
+from autotest_lib.client.common_lib.cros.network import netblock
from autotest_lib.server import site_linux_system
from autotest_lib.server.cros import wifi_test_utils
from autotest_lib.server.cros.network import hostap_config
@@ -277,34 +278,6 @@
logging.info('AP configured.')
- @staticmethod
- def ip_addr(netblock, idx):
- """Simple IPv4 calculator.
-
- Takes host address in "IP/bits" notation and returns netmask, broadcast
- address as well as integer offsets into the address range.
-
- @param netblock string host address in "IP/bits" notation.
- @param idx string describing what to return.
- @return string containing something you hopefully requested.
-
- """
- addr_str,bits = netblock.split('/')
- addr = map(int, addr_str.split('.'))
- mask_bits = (-1 << (32-int(bits))) & 0xffffffff
- mask = [(mask_bits >> s) & 0xff for s in range(24, -1, -8)]
- if idx == 'local':
- return addr_str
- elif idx == 'netmask':
- return '.'.join(map(str, mask))
- elif idx == 'broadcast':
- offset = [m ^ 0xff for m in mask]
- else:
- offset = [(idx >> s) & 0xff for s in range(24, -1, -8)]
- return '.'.join(map(str, [(a & m) + o
- for a, m, o in zip(addr, mask, offset)]))
-
-
def ibss_configure(self, config):
"""Configure a station based AP in IBSS mode.
@@ -372,33 +345,32 @@
@param interface string (e.g. wlan0)
"""
- logging.info("Starting up local server...")
+ logging.info('Starting up local server...')
if len(self.local_servers) >= 256:
raise error.TestFail('Exhausted available local servers')
- netblock = '%s/24' % self.local_server_address(len(self.local_servers))
+ server_addr = netblock.Netblock.from_addr(
+ self.local_server_address(len(self.local_servers)),
+ prefix_len=24)
params = {}
- params['netblock'] = netblock
- params['subnet'] = self.ip_addr(netblock, 0)
- params['netmask'] = self.ip_addr(netblock, 'netmask')
+ params['netblock'] = server_addr
params['dhcp_range'] = ' '.join(
- (self.ip_addr(netblock, self.dhcp_low),
- self.ip_addr(netblock, self.dhcp_high)))
+ (server_addr.get_addr_in_block(1),
+ server_addr.get_addr_in_block(128)))
params['interface'] = interface
-
- params['ip_params'] = ("%s broadcast %s dev %s" %
- (netblock,
- self.ip_addr(netblock, 'broadcast'),
+ params['ip_params'] = ('%s broadcast %s dev %s' %
+ (server_addr.netblock,
+ server_addr.broadcast,
interface))
self.local_servers.append(params)
- self.router.run("%s addr flush %s" %
+ self.router.run('%s addr flush %s' %
(self.cmd_ip, interface))
- self.router.run("%s addr add %s" %
+ self.router.run('%s addr add %s' %
(self.cmd_ip, params['ip_params']))
- self.router.run("%s link set %s up" %
+ self.router.run('%s link set %s up' %
(self.cmd_ip, interface))
self.start_dhcp_server(interface)
@@ -416,13 +388,14 @@
else:
raise error.TestFail('Could not find local server '
'to match interface: %r' % interface)
-
+ server_addr = params['netblock']
dhcpd_conf_file = self.dhcpd_conf % interface
dhcp_conf = '\n'.join([
'port=0', # disables DNS server
'bind-interfaces',
'log-dhcp',
- 'dhcp-range=%s' % params['dhcp_range'].replace(' ', ','),
+ 'dhcp-range=%s' % ','.join((server_addr.get_addr_in_block(1),
+ server_addr.get_addr_in_block(128))),
'interface=%s' % params['interface'],
'dhcp-leasefile=%s' % self.dhcpd_leases])
self.router.run('cat <<EOF >%s\n%s\nEOF\n' %
@@ -464,11 +437,24 @@
@param ap_num int which local server to get an address from.
"""
- if self.local_servers:
- return self.ip_addr(self.local_servers[ap_num]['netblock'],
- 'local')
- else:
- raise error.TestFail("No IP address assigned")
+ if not self.local_servers:
+ raise error.TestError('No IP address assigned')
+
+ return self.local_servers[ap_num]['netblock'].addr
+
+
+ def get_wifi_ip_subnet(self, ap_num):
+ """Return subnet of WiFi AP instance.
+
+ If no APs are configured a TestError will be raised.
+
+ @param ap_num int which local server to get an address from.
+
+ """
+ if not self.local_servers:
+ raise error.TestError('No APs configured.')
+
+ return self.local_servers[ap_num]['netblock'].subnet
def get_hostapd_interface(self, ap_num):
diff --git a/server/site_tests/network_WiFi_Roam/network_WiFi_Roam.py b/server/site_tests/network_WiFi_Roam/network_WiFi_Roam.py
index 618a7f2..3381877 100644
--- a/server/site_tests/network_WiFi_Roam/network_WiFi_Roam.py
+++ b/server/site_tests/network_WiFi_Roam/network_WiFi_Roam.py
@@ -47,6 +47,5 @@
# Expect that the DUT will re-connect to the new AP.
self.context.wait_for_connection(router_ssid,
- self._router1_conf.frequency,
- ap_num=1);
+ self._router1_conf.frequency)
self.context.router.deconfig()