| # Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import logging |
| import os |
| |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.client.common_lib.cros import dev_server |
| from autotest_lib.server.cros.update_engine import chromiumos_test_platform |
| from autotest_lib.server.cros.update_engine import update_engine_test |
| |
| |
| class autoupdate_EndToEndTest(update_engine_test.UpdateEngineTest): |
| """Complete update test between two Chrome OS releases. |
| |
| Performs an end-to-end test of updating a ChromeOS device from one version |
| to another. The test performs the following steps: |
| |
| - Stages the source (full) and target update payloads on a devserver. |
| - Installs source image on the DUT (if provided) and reboots to it. |
| - Verifies that sign in works correctly on the source image. |
| - Installs target image on the DUT and reboots. |
| - Does a final update check. |
| - Verifies that sign in works correctly on the target image. |
| - Returns the hostlogs collected during each update check for |
| verification against expected update events. |
| |
| This class interacts with several others: |
| UpdateEngineTest: base class for comparing expected update events against |
| the events listed in the hostlog. |
| UpdateEngineEvent: class representing a single expected update engine event. |
| ChromiumOSTestPlatform: A class representing the Chrome OS device we are |
| updating. It has functions for things the DUT can |
| do: get logs, reboot, start update etc |
| |
| """ |
| version = 1 |
| |
| _LOGIN_TEST = 'login_LoginSuccess' |
| |
| |
| def _get_hostlog_file(self, filename, identifier): |
| """Return the hostlog file location. |
| |
| This hostlog file contains the update engine events that were fired |
| during the update. |
| |
| @param filename: The partial filename to look for. |
| @param identifier: A string that is appended to the logfile when it is |
| saved so that multiple files with the same name can |
| be differentiated. |
| |
| """ |
| hostlog = '%s_%s_%s' % (filename, self._host.hostname, identifier) |
| file_url = os.path.join(self.job.resultdir, |
| dev_server.AUTO_UPDATE_LOG_DIR, |
| hostlog) |
| if os.path.exists(file_url): |
| logging.info('Hostlog file to be used for checking update ' |
| 'steps: %s', file_url) |
| return file_url |
| raise error.TestFail('Could not find %s' % filename) |
| |
| |
| def _verify_active_slot_changed(self, source_active_slot, |
| target_active_slot, source_release, |
| target_release): |
| """Make sure we're using a different slot after the update.""" |
| if target_active_slot == source_active_slot: |
| err_msg = 'The active image slot did not change after the update.' |
| if source_release is None: |
| err_msg += ( |
| ' The DUT likely rebooted into the old image, which ' |
| 'probably means that the payload we applied was ' |
| 'corrupt.') |
| elif source_release == target_release: |
| err_msg += (' Given that the source and target versions are ' |
| 'identical, we rebooted into the old image due to ' |
| 'a bad payload.') |
| else: |
| err_msg += (' This is strange since the DUT reported the ' |
| 'correct target version. This is probably a system ' |
| 'bug; check the DUT system log.') |
| raise error.TestFail(err_msg) |
| |
| logging.info('Target active slot changed as expected: %s', |
| target_active_slot) |
| |
| |
| def update_device_without_cros_au_rpc(self, cros_device, payload_uri, |
| clobber_stateful=False): |
| """Updates the device. |
| |
| @param cros_device: The device to be updated. |
| @param payload_uri: The payload with which the device should be updated. |
| @param clobber_stateful: Boolean that determines whether the stateful |
| of the device should be force updated. By |
| default, set to False |
| |
| @raise error.TestFail if anything goes wrong with the update. |
| |
| @return Path to directory where generated hostlog files and nebraska |
| logfiles are stored. |
| """ |
| try: |
| logs_dir = cros_device.install_version_without_cros_au_rpc( |
| payload_uri, clobber_stateful=clobber_stateful) |
| except Exception as e: |
| logging.error('ERROR: Failed to update device.') |
| raise error.TestFail(str(e)) |
| return logs_dir |
| |
| |
| def run_update_test(self, cros_device, test_conf): |
| """Runs the update test and checks it succeeded. |
| |
| @param cros_device: The device under test. |
| @param test_conf: A dictionary containing test configuration values. |
| |
| """ |
| # Record the active root partition. |
| source_active_slot = self._host.get_active_boot_slot() |
| logging.info('Source active slot: %s', source_active_slot) |
| |
| source_release = test_conf['source_release'] |
| target_release = test_conf['target_release'] |
| |
| logs_dir = self.update_device_without_cros_au_rpc( |
| cros_device, test_conf['target_payload_uri']) |
| self._copy_generated_nebraska_logs(logs_dir, 'target') |
| |
| # Compare hostlog events from the update to the expected ones. |
| rootfs = self._get_hostlog_file(self._DEVSERVER_HOSTLOG_ROOTFS, |
| 'target') |
| reboot = self._get_hostlog_file(self._DEVSERVER_HOSTLOG_REBOOT, |
| 'target') |
| |
| self.verify_update_events(source_release, rootfs) |
| self.verify_update_events(source_release, reboot, target_release) |
| |
| target_active_slot = self._host.get_active_boot_slot() |
| self._verify_active_slot_changed(source_active_slot, |
| target_active_slot, |
| source_release, target_release) |
| |
| logging.info('Update successful, test completed') |
| |
| |
| def run_once(self, test_conf): |
| """Performs a complete auto update test. |
| |
| @param test_conf: a dictionary containing test configuration values. |
| |
| """ |
| logging.debug('The test configuration supplied: %s', test_conf) |
| self._autotest_devserver = self._get_devserver_for_test(test_conf) |
| self._stage_payloads(test_conf['source_payload_uri'], |
| test_conf['source_archive_uri']) |
| |
| self._stage_payloads(test_conf['target_payload_uri'], |
| test_conf['target_archive_uri']) |
| |
| # Get an object representing the CrOS DUT. |
| cros_device = chromiumos_test_platform.ChromiumOSTestPlatform( |
| self._host, self._autotest_devserver, self.job.resultdir) |
| |
| # Install source image |
| source_payload_uri = test_conf['source_payload_uri'] |
| if source_payload_uri is not None: |
| logs_dir = self.update_device_without_cros_au_rpc( |
| cros_device, source_payload_uri, clobber_stateful=True) |
| self._copy_generated_nebraska_logs(logs_dir, 'source') |
| self._run_client_test_and_check_result(self._LOGIN_TEST, |
| tag='source') |
| |
| # Start the update to the target image. |
| self.run_update_test(cros_device, test_conf) |
| |
| # Check we can login after the update. |
| self._run_client_test_and_check_result(self._LOGIN_TEST, tag='target') |