1 # Copyright 2017 The Chromium OS Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 5 import logging 6 import os 7 import urlparse 8 9 from autotest_lib.server import autotest 10 11 class ChromiumOSTestPlatform(object): 12 """Represents a CrOS device during autoupdate. 13 14 This class is used with autoupdate_EndToEndTest. It has functions for all 15 the device specific things that we need during an update: reboot, 16 check active slot, login, get logs, start an update etc. 17 """ 18 19 _UPDATE_ENGINE_PERF_PATH = '/mnt/stateful_partition/unencrypted/preserve' 20 _UPDATE_ENGINE_PERF_SCRIPT = 'update_engine_performance_monitor.py' 21 _UPDATE_ENGINE_PERF_RESULTS_FILE = 'perf_data_results.json' 22 23 def __init__(self, host, autotest_devserver, results_dir): 24 """Initialize the class. 25 26 @param: host: The DUT host. 27 @param: autotest_devserver: The devserver to call cros_au on. 28 @param: results_dir: Where to save the autoupdate logs files. 29 """ 30 self._host = host 31 self._autotest_devserver = autotest_devserver 32 self._results_dir = results_dir 33 34 35 def _install_version(self, payload_uri, clobber_stateful=False): 36 """Install the specified payload. 37 38 @param payload_uri: GS URI of the payload to install. 39 @param clobber_stateful: force a reinstall of the stateful image. 40 """ 41 build_name, payload_file = self._get_update_parameters_from_uri( 42 payload_uri) 43 logging.info('Installing %s on the DUT', payload_uri) 44 45 try: 46 ds = self._autotest_devserver 47 _, pid = ds.auto_update(host_name=self._host.hostname, 48 build_name=build_name, 49 force_update=True, 50 full_update=True, 51 log_dir=self._results_dir, 52 payload_filename=payload_file, 53 clobber_stateful=clobber_stateful) 54 except: 55 logging.fatal('ERROR: Failed to install image on the DUT.') 56 raise 57 return pid 58 59 60 def _run_login_test(self, tag): 61 """Runs login_LoginSuccess test on the DUT.""" 62 client_at = autotest.Autotest(self._host) 63 client_at.run_test('login_LoginSuccess', tag=tag) 64 65 66 @staticmethod 67 def _get_update_parameters_from_uri(payload_uri): 68 """Extract the two vars needed for cros_au from the Google Storage URI. 69 70 dev_server.auto_update needs two values from this test: 71 (1) A build_name string e.g samus-release/R60-9583.0.0 72 (2) A filename of the exact payload file to use for the update. This 73 payload needs to have already been staged on the devserver. 74 75 This function extracts those two values from a Google Storage URI. 76 77 @param payload_uri: Google Storage URI to extract values from 78 """ 79 archive_url, _, payload_file = payload_uri.rpartition('/') 80 build_name = urlparse.urlsplit(archive_url).path.strip('/') 81 82 # This test supports payload uris from two Google Storage buckets. 83 # They store their payloads slightly differently. One stores them in 84 # a separate payloads directory. E.g 85 # gs://chromeos-image-archive/samus-release/R60-9583.0.0/blah.bin 86 # gs://chromeos-releases/dev-channel/samus/9334.0.0/payloads/blah.bin 87 if build_name.endswith('payloads'): 88 build_name = build_name.rpartition('/')[0] 89 payload_file = 'payloads/' + payload_file 90 91 logging.debug('Extracted build_name: %s, payload_file: %s from %s.', 92 build_name, payload_file, payload_uri) 93 return build_name, payload_file 94 95 96 def reboot_device(self): 97 """Reboot the device.""" 98 self._host.reboot() 99 100 101 def install_source_image(self, source_payload_uri): 102 """Install source payload on device.""" 103 if source_payload_uri: 104 self._install_version(source_payload_uri, clobber_stateful=True) 105 106 107 def check_login_after_source_update(self): 108 """Make sure we can login before the target update.""" 109 self._run_login_test('source_update') 110 111 112 def get_active_slot(self): 113 """Returns the current active slot.""" 114 return self._host.run('rootdev -s').stdout.strip() 115 116 117 def copy_perf_script_to_device(self, bindir): 118 """Copy performance monitoring script to DUT. 119 120 The updater will kick off the script during the update. 121 """ 122 logging.info('Copying %s to device.', self._UPDATE_ENGINE_PERF_SCRIPT) 123 path = os.path.join(bindir, self._UPDATE_ENGINE_PERF_SCRIPT) 124 self._host.send_file(path, self._UPDATE_ENGINE_PERF_PATH) 125 126 127 def get_perf_stats_for_update(self, resultdir): 128 """ Get the performance metrics created during update.""" 129 try: 130 path = os.path.join('/var/log', 131 self._UPDATE_ENGINE_PERF_RESULTS_FILE) 132 self._host.get_file(path, resultdir) 133 self._host.run('rm %s' % path) 134 script = os.path.join(self._UPDATE_ENGINE_PERF_PATH, 135 self._UPDATE_ENGINE_PERF_SCRIPT) 136 self._host.run('rm %s' % script) 137 return os.path.join(resultdir, 138 self._UPDATE_ENGINE_PERF_RESULTS_FILE) 139 except: 140 logging.warning('Failed to copy performance metrics from DUT.') 141 return None 142 143 144 def install_target_image(self, target_payload_uri): 145 """Install target payload on the device.""" 146 logging.info('Updating device to target image.') 147 return self._install_version(target_payload_uri) 148 149 150 def get_update_log(self, num_lines): 151 """Get the latest lines from the update engine log.""" 152 return self._host.run_output( 153 'tail -n %d /var/log/update_engine.log' % num_lines, 154 stdout_tee=None) 155 156 157 def check_login_after_target_update(self): 158 """Check we can login after updating.""" 159 self._run_login_test('target_update') 160 161 162 def oobe_triggers_update(self): 163 """Check if this device has an OOBE that completes itself.""" 164 return self._host.oobe_triggers_update() 165 166 167 def get_cros_version(self): 168 """Returns the ChromeOS version installed on this device.""" 169 return self._host.get_release_version() 170