blob: 5848908daa8f74a9b281d4c94d3fb94904b5b3a9 [file] [log] [blame]
# 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')