• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Lint as: python2, python3
2# Copyright 2017 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import logging
7import random
8import time
9
10from autotest_lib.client.common_lib.cros import dev_server
11from autotest_lib.client.common_lib import error
12from autotest_lib.client.common_lib import utils
13from autotest_lib.client.common_lib.cros import kernel_utils
14from autotest_lib.client.common_lib.cros import tpm_utils
15from autotest_lib.server.cros import provisioner
16from autotest_lib.server.cros.update_engine import update_engine_test
17
18class autoupdate_ForcedOOBEUpdate(update_engine_test.UpdateEngineTest):
19    """Runs a forced autoupdate during OOBE."""
20    version = 1
21
22
23    def cleanup(self):
24        # Get the last two update_engine logs: before and after reboot.
25        self._save_extra_update_engine_logs(number_of_logs=2)
26
27        self._clear_custom_lsb_release()
28
29        # Clean up the nebraska usr dir.
30        self._clear_nebraska_dir()
31
32        self._set_update_over_cellular_setting(False)
33
34        # Cancel any update still in progress.
35        if not self._is_update_engine_idle():
36            logging.debug('Canceling the in-progress update.')
37            self._restart_update_engine()
38        super(autoupdate_ForcedOOBEUpdate, self).cleanup()
39        if self._m2n:
40            self._restore_stateful()
41
42    def _wait_for_reboot_after_update(self, timeout_minutes=15):
43        """
44        Waits for the OOBE update to finish and autoreboot.
45
46        The update goes through the following statuses: DOWNLOADING to
47        FINALIZING to NEED_REBOOT. It then automatically reboots back to the
48        same screen of OOBE. Detecting the reboot is done by:
49
50        1) Checking the number of logs in /var/log/update_engine/ increased.
51        2) Checking that the two recent statuses were FINALIZING and IDLE.
52
53        @param timeout_minutes: How long to wait for the update to finish.
54                                See crbug/1073855 for context on this default.
55
56        """
57        timeout = time.time() + 60 * timeout_minutes
58        last_status = None
59        logs_before = len(self._get_update_engine_logs())
60
61        while True:
62            # Use timeout so if called during reboot we fail early and retry.
63            status = self._get_update_engine_status(timeout=10,
64                                                    ignore_timeout=True)
65
66            # Check that the status is not reporting an error.
67            if status is not None:
68                if self._is_checking_for_update(status):
69                    continue
70                if self._is_update_engine_reporting_error(status):
71                    err_str = self._get_last_error_string()
72                    raise error.TestFail('Update status reported error '
73                                         'during OOBE update: %s' % err_str)
74                # if status is IDLE we need to figure out if an error occurred
75                # or the DUT autorebooted.
76                elif self._is_update_engine_idle(status):
77                    self._host.run(
78                            'ls /mnt/stateful_partition/etc/lsb-release')
79                    if self._is_update_finished_downloading(last_status):
80                        if len(self._get_update_engine_logs()) > logs_before:
81                            return
82                    err_str = self._get_last_error_string()
83                    raise error.TestFail('Update status was IDLE during '
84                                         'update: %s' % err_str)
85                last_status = status
86
87            time.sleep(1)
88            if time.time() > timeout:
89                raise error.TestFail(
90                    'OOBE update did not finish in %d minutes. Last status: %s,'
91                    ' Last Progress: %s' % (timeout_minutes,
92                    status[self._CURRENT_OP], status[self._PROGRESS]))
93
94
95    def _wait_for_oobe_update_to_complete(self):
96        """Wait for the update that started to complete."""
97        self._wait_for_reboot_after_update()
98        def found_post_reboot_event():
99            """
100            Now that the device is rebooted, we have to make sure update_engine
101            is up and running and post reboot update check has been performed.
102
103            """
104            self._get_update_engine_status(timeout=10, ignore_timeout=False)
105            return self._check_update_engine_log_for_entry(
106                  'Omaha request response:')
107        utils.poll_for_condition(found_post_reboot_event, timeout=60,
108                                 desc='post-reboot event to fire after reboot')
109
110
111    def run_once(self,
112                 full_payload=True,
113                 cellular=False,
114                 interrupt=None,
115                 job_repo_url=None,
116                 moblab=False,
117                 m2n=False):
118        """
119        Runs a forced autoupdate during ChromeOS OOBE.
120
121        @param full_payload: True for a full payload. False for delta.
122        @param cellular: True to do the update over a cellualar connection.
123                         Requires that the DUT have a sim card slot.
124        @param interrupt: Type of interrupt to try. See _SUPPORTED_INTERRUPTS.
125        @param job_repo_url: Used for debugging locally. This is used to figure
126                             out the current build and the devserver to use.
127                             The test will read this from a host argument
128                             when run in the lab.
129        @param moblab: True if we are running on moblab.
130        @param m2n: True if we should first provision the latest stable version
131                    for the current board so that we can perform a M->N update.
132
133        """
134        if interrupt and interrupt not in self._SUPPORTED_INTERRUPTS:
135            raise error.TestFail('Unknown interrupt type: %s' % interrupt)
136        tpm_utils.ClearTPMOwnerRequest(self._host)
137
138        self._m2n = m2n
139        if self._m2n:
140            # Provision latest stable build for the current build.
141            build_name = self._get_latest_serving_stable_build()
142
143            # Install the matching build with quick provision.
144            autotest_devserver = dev_server.ImageServer.resolve(
145                    build_name, self._host.hostname)
146            update_url = autotest_devserver.get_update_url(build_name)
147            logging.info('Installing source image with update url: %s',
148                         update_url)
149            provisioner.ChromiumOSProvisioner(
150                    update_url, host=self._host,
151                    is_release_bucket=True).run_provision()
152
153        payload_url = None
154        if cellular:
155            self._set_update_over_cellular_setting(True)
156            payload_url = self.get_payload_url_on_public_bucket(
157                job_repo_url, full_payload=full_payload)
158        else:
159            payload_url = self.get_payload_for_nebraska(
160                    job_repo_url, full_payload=full_payload)
161        before_version = self._host.get_release_version()
162
163        # Clear any previously started updates.
164        self._remove_update_engine_pref(self._UPDATE_CHECK_RESPONSE_HASH)
165        self._restart_update_engine(ignore_status=True)
166
167        progress = None
168        if interrupt is not None:
169            # Choose a random downloaded progress to interrupt the update.
170            # Moblab may have higher download speeds and take longer to return
171            # from the client test, so use a reduced progress range there.
172            progress_limit = 0.3 if moblab else 0.6
173            progress = random.uniform(0.1, progress_limit)
174            logging.info('Progress when we will interrupt: %f', progress)
175
176        active, inactive = kernel_utils.get_kernel_state(self._host)
177        # Call client test to start the forced OOBE update.
178        self._run_client_test_and_check_result(
179                'autoupdate_StartOOBEUpdate',
180                payload_url=payload_url,
181                full_payload=full_payload,
182                cellular=cellular,
183                critical_update=True,
184                interrupt_network=interrupt == self._NETWORK_INTERRUPT,
185                interrupt_progress=progress)
186
187        if interrupt in [self._REBOOT_INTERRUPT, self._SUSPEND_INTERRUPT]:
188            logging.info('Waiting to interrupt update.')
189            self._wait_for_progress(progress)
190            logging.info('The update will be interrupted now...')
191            completed = self._get_update_progress()
192
193            self._take_screenshot(self._BEFORE_INTERRUPT_FILENAME)
194            if interrupt == self._REBOOT_INTERRUPT:
195                self._host.reboot()
196            elif interrupt == self._SUSPEND_INTERRUPT:
197                self._suspend_then_resume()
198            self._take_screenshot(self._AFTER_INTERRUPT_FILENAME)
199
200            if self._is_update_engine_idle():
201                raise error.TestFail('The update was IDLE after interrupt.')
202            if not self._update_continued_where_it_left_off(
203                completed, reboot_interrupt=interrupt is 'reboot'):
204                raise error.TestFail('The update did not continue where it '
205                                     'left off after interruption.')
206
207            # Remove screenshots since interrupt test succeeded.
208            self._remove_screenshots()
209
210        # Set no_update=True in the nebraska startup config to get the
211        # post-reboot update event.
212        self._edit_nebraska_startup_config(no_update=True)
213
214        self._wait_for_oobe_update_to_complete()
215
216        # Verify the update was successful by checking hostlog and kernel.
217        rootfs_hostlog, reboot_hostlog = self._create_hostlog_files()
218        self.verify_update_events(self._CUSTOM_LSB_VERSION, rootfs_hostlog)
219        self.verify_update_events(self._CUSTOM_LSB_VERSION, reboot_hostlog,
220                                  self._CUSTOM_LSB_VERSION)
221        kernel_utils.verify_boot_expectations(inactive, host=self._host)
222        logging.info('Successfully force updated from %s to %s.',
223                     before_version, self._host.get_release_version())
224