David Haddock | 95e7fbe | 2017-12-01 17:49:53 -0800 | [diff] [blame^] | 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 | import json |
| 5 | import logging |
| 6 | import os |
| 7 | import time |
| 8 | |
| 9 | from autotest_lib.client.common_lib import error |
| 10 | from autotest_lib.client.common_lib import lsbrelease_utils |
| 11 | from autotest_lib.client.common_lib.cros import dev_server |
| 12 | from autotest_lib.server import autotest |
| 13 | from autotest_lib.server.cros.dynamic_suite import tools |
| 14 | from autotest_lib.server.cros.update_engine import omaha_devserver |
| 15 | from autotest_lib.server.cros.update_engine import update_engine_test |
| 16 | |
| 17 | |
| 18 | class autoupdate_ForcedOOBEUpdate(update_engine_test.UpdateEngineTest): |
| 19 | """Runs a forced autoupdate during OOBE.""" |
| 20 | version = 1 |
| 21 | |
| 22 | # We override the default lsb-release file. |
| 23 | _CUSTOM_LSB_RELEASE = '/mnt/stateful_partition/etc/lsb-release' |
| 24 | |
| 25 | # Version we tell the DUT it is on before update. |
| 26 | _CUSTOM_LSB_VERSION = '0.0.0.0' |
| 27 | |
| 28 | # Expected hostlog events during update: 4 during rootfs |
| 29 | _ROOTFS_HOSTLOG_EVENTS = 4 |
| 30 | |
| 31 | |
| 32 | def setup(self): |
| 33 | self._omaha_devserver = None |
| 34 | |
| 35 | |
| 36 | def cleanup(self): |
| 37 | if self._omaha_devserver is not None: |
| 38 | self._omaha_devserver.stop_devserver() |
| 39 | self._host.run('rm %s' % self._CUSTOM_LSB_RELEASE, ignore_status=True) |
| 40 | |
| 41 | # Get the last two update_engine logs: before and after reboot. |
| 42 | files = self._host.run('ls -t -1 ' |
| 43 | '/var/log/update_engine/').stdout.splitlines() |
| 44 | for i in range(2): |
| 45 | self._host.get_file('/var/log/update_engine/%s' % files[i], |
| 46 | self.resultsdir) |
| 47 | |
| 48 | |
| 49 | def _get_chromeos_version(self): |
| 50 | """Read the ChromeOS version from /etc/lsb-release.""" |
| 51 | lsb = self._host.run('cat /etc/lsb-release').stdout |
| 52 | return lsbrelease_utils.get_chromeos_release_version(lsb) |
| 53 | |
| 54 | |
| 55 | def _get_payload_url_from_job_repo_url(self, job_repo_url): |
| 56 | """Get the payload to update to. |
| 57 | |
| 58 | We will use the job_repo_url to get a payload that matches the build |
| 59 | number that the DUT is currently running. That way we will update |
| 60 | from N->N at OOBE. |
| 61 | |
| 62 | @param job_repo_url: a url you can pass to the test for local debugging. |
| 63 | """ |
| 64 | if job_repo_url is None: |
| 65 | info = self._host.host_info_store.get() |
| 66 | job_repo_url = info.attributes.get( |
| 67 | self._host.job_repo_url_attribute, '') |
| 68 | if not job_repo_url: |
| 69 | raise error.TestFail('There was no job_repo_url so we cannot get ' |
| 70 | 'a payload to use.') |
| 71 | ds_url, build = tools.get_devserver_build_from_package_url(job_repo_url) |
| 72 | self._autotest_devserver = dev_server.ImageServer(ds_url) |
| 73 | self._autotest_devserver.stage_artifacts(build, ['full_payload']) |
| 74 | payload_url = self._autotest_devserver.get_full_payload_url(build) |
| 75 | |
| 76 | # The devserver adds on the update.gz filename again during |
| 77 | # HandleUpdatePing() so we take it off here. |
| 78 | return payload_url.rpartition('/')[0] |
| 79 | |
| 80 | |
| 81 | def _create_hostlog_files(self): |
| 82 | """Create the two hostlog files for the update. |
| 83 | |
| 84 | To ensure the update was succesful we need to compare the update |
| 85 | events against expected update events. There is a hostlog for the |
| 86 | rootfs update and for the post reboot update check. |
| 87 | """ |
| 88 | hostlog = self._omaha_devserver.get_hostlog(self._host.ip) |
| 89 | logging.info('Hostlog: %s', hostlog) |
| 90 | |
| 91 | # File names to save the hostlog events to. |
| 92 | rootfs_hostlog = os.path.join(self.resultsdir, 'hostlog_rootfs') |
| 93 | reboot_hostlog = os.path.join(self.resultsdir, 'hostlog_reboot') |
| 94 | |
| 95 | with open(rootfs_hostlog, 'w') as outfile: |
| 96 | json.dump(hostlog[:self._ROOTFS_HOSTLOG_EVENTS], outfile) |
| 97 | with open(reboot_hostlog, 'w') as outfile: |
| 98 | json.dump(hostlog[self._ROOTFS_HOSTLOG_EVENTS:], outfile) |
| 99 | return rootfs_hostlog, reboot_hostlog |
| 100 | |
| 101 | |
| 102 | def _wait_for_update_to_complete(self): |
| 103 | """Wait for the update that started to complete. |
| 104 | |
| 105 | Repeated check status of update. It should move from DOWNLOADING to |
| 106 | FINALIZING to COMPLETE to IDLE. |
| 107 | """ |
| 108 | while True: |
| 109 | status = self._host.run('update_engine_client --status', |
| 110 | ignore_timeout=True, |
| 111 | timeout=10) |
| 112 | |
| 113 | # During reboot, status will be None |
| 114 | if status is not None: |
| 115 | status = status.stdout.splitlines() |
| 116 | logging.debug(status) |
| 117 | if "UPDATE_STATUS_IDLE" in status[2]: |
| 118 | break |
| 119 | time.sleep(1) |
| 120 | |
| 121 | |
| 122 | def run_once(self, host, job_repo_url=None): |
| 123 | self._host = host |
| 124 | |
| 125 | # Get a payload that matches the current build on the DUT. |
| 126 | update_url = self._get_payload_url_from_job_repo_url(job_repo_url) |
| 127 | logging.info('Payload url to use: %s', update_url) |
| 128 | |
| 129 | # Start a devserver in the lab that will serve a critical update. |
| 130 | self._omaha_devserver = omaha_devserver.OmahaDevserver( |
| 131 | self._autotest_devserver.hostname, update_url) |
| 132 | self._omaha_devserver.start_devserver() |
| 133 | |
| 134 | before = self._get_chromeos_version() |
| 135 | |
| 136 | # Call client test to start the forced OOBE update. |
| 137 | client_at = autotest.Autotest(self._host) |
| 138 | client_at.run_test('autoupdate_StartOOBEUpdate', |
| 139 | image_url=self._omaha_devserver.get_update_url()) |
| 140 | |
| 141 | # Don't continue the test if the client failed for any reason. |
| 142 | client_at._check_client_test_result(self._host, |
| 143 | 'autoupdate_StartOOBEUpdate') |
| 144 | |
| 145 | self._wait_for_update_to_complete() |
| 146 | |
| 147 | # Verify that the update completed successfully by checking hostlog. |
| 148 | rootfs_hostlog, reboot_hostlog = self._create_hostlog_files() |
| 149 | self.verify_update_events(self._CUSTOM_LSB_VERSION, rootfs_hostlog) |
| 150 | self.verify_update_events(self._CUSTOM_LSB_VERSION, reboot_hostlog, |
| 151 | self._CUSTOM_LSB_VERSION) |
| 152 | |
| 153 | after = self._get_chromeos_version() |
| 154 | logging.info('Successfully force updated from %s to %s.', before, after) |