Chris Sosa | b76e0ee | 2013-05-22 16:55:41 -0700 | [diff] [blame^] | 1 | # Copyright (c) 2013 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 | |
| 5 | import logging |
| 6 | import time |
| 7 | |
| 8 | from autotest_lib.client.common_lib import error |
| 9 | from autotest_lib.client.common_lib.cros import autoupdater, dev_server |
| 10 | from autotest_lib.server import autotest, test |
| 11 | from autotest_lib.server.cros.dynamic_suite import tools |
| 12 | |
| 13 | |
| 14 | class platform_RebootAfterUpdate(test.test): |
| 15 | """Test that updates the machine, reboots, and logs in / logs out. |
| 16 | |
| 17 | This test keeps a reboot timeout in order to keep the system boot times |
| 18 | regressing in performance. For example, if Chrome takes a long time to |
| 19 | shutdown / hangs, other tests have much longer timeouts (to prevent them |
| 20 | from being flaky) while this test targets these hangs. Note, this test only |
| 21 | has smaller timeouts for boot, not for login/logout. Also, these timeouts |
| 22 | are still fairly conservative and are only meant to catch large regressions |
| 23 | or hangs, not small regressions. |
| 24 | |
| 25 | """ |
| 26 | version = 1 |
| 27 | |
| 28 | _REBOOT_ERROR_MESSAGE = ( |
| 29 | 'System failed to restart within the timeout after ' |
| 30 | '%(reason)s. This failure indicates that the system after ' |
| 31 | 'receiving a reboot request and restarting did not ' |
| 32 | 'reconnect via ssh within the timeout. Actual time %(actual)d ' |
| 33 | 'seconds vs expected time: %(expected)d seconds') |
| 34 | |
| 35 | # Timeouts specific to this test. These should be as low as possible. |
| 36 | |
| 37 | # Total amount of time to wait for a reboot to return. |
| 38 | _REBOOT_TIMEOUT = 60 |
| 39 | |
| 40 | |
| 41 | @classmethod |
| 42 | def reboot_with_timeout(cls, host, reason): |
| 43 | """Reboots the device and checks to see if it completed within desired. |
| 44 | |
| 45 | @param host: Autotest host object to reboot. |
| 46 | @param reason: string representing why we are rebooting e.g. autoupdate. |
| 47 | |
| 48 | Raises: |
| 49 | error.TestFail: If it takes too long to reboot. |
| 50 | """ |
| 51 | start_time = time.time() |
| 52 | host.reboot() |
| 53 | reboot_duration = time.time() - start_time |
| 54 | if reboot_duration > cls._REBOOT_TIMEOUT: |
| 55 | raise error.TestFail( |
| 56 | cls._REBOOT_ERROR_MESSAGE % dict( |
| 57 | reason=reason, actual=reboot_duration, |
| 58 | expected=cls._REBOOT_TIMEOUT)) |
| 59 | |
| 60 | |
| 61 | def run_once(self, host, job_repo_url=None): |
| 62 | """Runs the test. |
| 63 | |
| 64 | @param host: a host object representing the DUT |
| 65 | @param job_repo_url: URL to get the image. |
| 66 | |
| 67 | @raise error.TestError if anything went wrong with setting up the test; |
| 68 | error.TestFail if any part of the test has failed. |
| 69 | |
| 70 | """ |
| 71 | # Get the job_repo_url -- if not present, attempt to use the one |
| 72 | # specified in the host attributes for the host. |
| 73 | if not job_repo_url: |
| 74 | try: |
| 75 | job_repo_url = host.lookup_job_repo_url() |
| 76 | except KeyError: |
| 77 | logging.fatal('Could not lookup job_repo_url from afe.') |
| 78 | |
| 79 | if not job_repo_url: |
| 80 | raise error.TestError( |
| 81 | 'Test could not be run. Missing the url with which to ' |
| 82 | 're-image the device!') |
| 83 | |
| 84 | # Get the devserver url and build (image) from the repo url e.g. |
| 85 | # 'http://mydevserver:8080', 'x86-alex-release/R27-123.0.0' |
| 86 | ds, build = tools.get_devserver_build_from_package_url(job_repo_url) |
| 87 | devserver = dev_server.ImageServer(ds) |
| 88 | |
| 89 | # We only need to update stateful to do this test. |
| 90 | updater = autoupdater.ChromiumOSUpdater( |
| 91 | devserver.get_update_url(build), host=host) |
| 92 | updater.update_stateful(clobber=True) |
| 93 | |
| 94 | logging.info('Rebooting after performing update.') |
| 95 | self.reboot_with_timeout(host, 'update') |
| 96 | |
| 97 | # TODO(sosa): Ideally we would be able to just use |
| 98 | # autotest.run_static_method to login/logout, however, this |
| 99 | # functionality is currently nested deep into the test logic. Once |
| 100 | # telemetry has replaced pyauto login and has been librarized, we |
| 101 | # should switch to using that code and not have to rely on running a |
| 102 | # client test to do what we want. |
| 103 | logging.info('Running sanity desktop login to see that we can ' |
| 104 | 'login and logout after performing an update.') |
| 105 | client_at = autotest.Autotest(host) |
| 106 | self.job.set_state('client_success', False) |
| 107 | client_at.run_test('login_LoginSuccess') |
| 108 | if not self.job.get_state('client_success'): |
| 109 | raise error.TestFail( |
| 110 | 'Failed to login successfully after an update.') |
| 111 | |
| 112 | logging.info('Rebooting the DUT after first login/logout.') |
| 113 | self.reboot_with_timeout(host, 'first login') |