blob: 87107f469fb1f9cd037be804e3d97ad387bcc7b7 [file] [log] [blame]
David Haddock95e7fbe2017-12-01 17:49:53 -08001# 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.
4import json
5import logging
6import os
7import time
8
9from autotest_lib.client.common_lib import error
10from autotest_lib.client.common_lib import lsbrelease_utils
11from autotest_lib.client.common_lib.cros import dev_server
12from autotest_lib.server import autotest
13from autotest_lib.server.cros.dynamic_suite import tools
14from autotest_lib.server.cros.update_engine import omaha_devserver
15from autotest_lib.server.cros.update_engine import update_engine_test
16
17
18class 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)