1# Lint as: python2, python3 2# Copyright (c) 2014 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6import collections 7import logging 8 9from autotest_lib.client.common_lib import error 10from autotest_lib.client.common_lib import global_config 11from autotest_lib.client.common_lib import utils 12from autotest_lib.client.common_lib.cros import dev_server 13from autotest_lib.client.common_lib.cros.network import ping_runner 14from autotest_lib.server import hosts 15from autotest_lib.server import site_linux_router 16from autotest_lib.server import test 17from autotest_lib.server.cros import dnsname_mangler 18from autotest_lib.server.cros import provisioner 19from autotest_lib.server.cros.network import wifi_test_context_manager 20 21 22# Stable versions come from the builders. 23# The builder version is used to build the URL of the corresponding image 24# in Google Storage. 25# The image version is a line from /etc/lsb-release in the corresponding image. 26StableVersion = collections.namedtuple('StableVersion', 27 ['builder_version', 'release_version']) 28 29class network_WiFi_UpdateRouter(test.test): 30 """Updates a router to the most recent stable version. 31 32 This is not a test per se, since it does not test client behavior. However 33 it is advantageous to write this as a test so that we can schedule it to 34 run periodically via the same infrastructure we use to run tests. 35 36 Note that this test is very much patterned on provision_QuickProvision. 37 38 """ 39 version = 1 40 41 STABLE_VERSIONS = { 42 ## crbug.com/1098024: these are left here as documentation of what the 43 # last stable version is, but the current updater code does not support 44 # them. 45 'whirlwind': 46 StableVersion('whirlwind-test-ap-tryjob/R85-13310.60.0-b4641849', 47 '13310.60.2020_08_25_0212'), 48 'gale': 49 StableVersion('gale-test-ap-tryjob/R92-13982.81.0-b4959409', 50 '13982.81.2021_08_11_1044'), 51 } 52 53 # List of files to remove. 54 FILES_TO_REMOVE = ['/var/spool/crash/*', '/tmp/*', 55 '/var/lib/metrics/uma-events'] 56 57 58 def get_release_version(self, host): 59 result = host.run('cat /etc/lsb-release') 60 for line in result.stdout.splitlines(): 61 if line.startswith('CHROMEOS_RELEASE_VERSION='): 62 return line.split('=', 1)[1] 63 64 65 def get_update_url(self, ds_url, image): 66 CONFIG = global_config.global_config 67 IMAGE_URL_PATTERN = CONFIG.get_config_value( 68 'CROS', 'image_url_pattern', type=str) 69 return IMAGE_URL_PATTERN % (ds_url, image) 70 71 72 def warmup(self, raw_cmdline_args): 73 """Possibly parse the router hostname from the commandline. 74 75 @param raw_cmdline_args raw input from autotest. 76 77 """ 78 cmdline_args = utils.args_to_dict(raw_cmdline_args) 79 logging.info('Running wifi test with commandline arguments: %r', 80 cmdline_args) 81 self._router_hostname_from_cmdline = cmdline_args.get( 82 wifi_test_context_manager.WiFiTestContextManager. \ 83 CMDLINE_ROUTER_ADDR) 84 85 86 def freeup_disk_space(self, device_host): 87 """Remove files to free up disk space. 88 89 @param device_host: router / pcap host object 90 91 """ 92 for path in self.FILES_TO_REMOVE: 93 device_host.run('rm -rf %s' % path, ignore_status=True) 94 95 def stop_recover_duts(self, device_host): 96 """Stop running recover_duts on the host. 97 98 b/177380545: recover_duts is currently providing negative value on 99 routers. TBD: decided whether we should re-enable this when router 100 images are updated to fix hang issues? 101 102 @param device_host: router / pcap host object 103 """ 104 device_host.run('rm -f %s' % provisioner.LAB_MACHINE_FILE, 105 ignore_status=True) 106 device_host.run('stop recover_duts', ignore_status=True) 107 108 def run_once(self, host, is_pcap=False): 109 """Update router / packet capture associated with host. 110 111 @param host DUT connected to AP/Pcap that needs update 112 113 """ 114 if is_pcap: 115 device_hostname = dnsname_mangler.get_pcap_addr( 116 client_hostname=host.hostname) 117 else: 118 device_hostname = site_linux_router.build_router_hostname( 119 client_hostname=host.hostname, 120 router_hostname=self._router_hostname_from_cmdline) 121 122 ping_helper = ping_runner.PingRunner() 123 if not ping_helper.simple_ping(device_hostname): 124 # Pcap devices aren't always present. Just claim Not Applicable if 125 # we couldn't find it. 126 e = error.TestNAError if is_pcap else error.TestError 127 raise e('%s not found / is down.' % device_hostname) 128 129 # Use CrosHost for all router/pcap hosts and avoid host detection. 130 # Host detection would use JetstreamHost for Whirlwind routers. 131 # JetstreamHost assumes ap-daemons are running. 132 # Testbed routers run the testbed-ap profile with no ap-daemons. 133 # TODO(ecgh): crbug.com/757075 Fix testbed-ap JetstreamHost detection. 134 device_host = hosts.create_host(device_hostname, 135 host_class=hosts.CrosHost, 136 allow_failure=True) 137 138 # Stop recover_duts now, for cases where we don't go through a full 139 # update below. 140 self.stop_recover_duts(device_host) 141 142 # Remove un-wanted files to freeup diskspace before starting update. 143 self.freeup_disk_space(device_host) 144 self.update_device(device_host) 145 146 # Stop recover_duts again, in case provisioning re-enabled it. 147 self.stop_recover_duts(device_host) 148 149 def update_device(self, device_host): 150 """Update router and pcap associated with host. 151 152 @param device_host: router / pcap host object 153 @param device_board: router / pcap board name 154 155 """ 156 device_board = device_host.get_board().split(':', 1)[1] 157 desired = self.STABLE_VERSIONS.get(device_board, None) 158 if desired is None: 159 raise error.TestFail('No stable version found for %s with board=%s.' 160 % (device_host.hostname, device_board)) 161 162 logging.info('Checking whether %s is at the latest stable version: %s', 163 device_host.hostname, desired.release_version) 164 current_release_version = self.get_release_version(device_host) 165 if desired.release_version == current_release_version: 166 raise error.TestNAError('%s is already at latest version %s.' % 167 (device_host.hostname, 168 desired.release_version)) 169 170 logging.info('Updating %s to image %s from %s', 171 device_host.hostname, desired.release_version, 172 current_release_version) 173 try: 174 ds = dev_server.ImageServer.resolve(desired.builder_version, 175 device_host.hostname) 176 except dev_server.DevServerException as e: 177 logging.error(e) 178 raise error.TestFail(str(e)) 179 180 url = self.get_update_url(ds.url(), desired.builder_version) 181 provisioner.ChromiumOSProvisioner(url, 182 host=device_host).run_provision() 183