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 5import logging 6import re 7import sys 8import urllib2 9 10from autotest_lib.client.common_lib import error 11from autotest_lib.client.common_lib import global_config 12from autotest_lib.client.common_lib.cros import dev_server 13from autotest_lib.server import afe_utils 14from autotest_lib.server import test 15from autotest_lib.server.cros import provision 16 17 18_CONFIG = global_config.global_config 19# pylint: disable-msg=E1120 20_IMAGE_URL_PATTERN = _CONFIG.get_config_value( 21 'CROS', 'image_url_pattern', type=str) 22 23 24class provision_AutoUpdate(test.test): 25 """A test that can provision a machine to the correct ChromeOS version.""" 26 version = 1 27 28 def initialize(self, host, value, force=False, is_test_na=False): 29 """Initialize. 30 31 @param host: The host object to update to |value|. 32 @param value: The build type and version to install on the host. 33 @param force: not used by initialize. 34 @param is_test_na: boolean, if True, will simply skip the test 35 and emit TestNAError. The control file 36 determines whether the test should be skipped 37 and passes the decision via this argument. Note 38 we can't raise TestNAError in control file as it won't 39 be caught and handled properly. 40 """ 41 if is_test_na: 42 raise error.TestNAError( 43 'Test not available for test_that. chroot detected, ' 44 'you are probably using test_that.') 45 # We check value in initialize so that it fails faster. 46 if not value: 47 raise error.TestFail('No build version specified.') 48 49 50 def run_once(self, host, value, force=False): 51 """The method called by the control file to start the test. 52 53 @param host: The host object to update to |value|. 54 @param value: The host object to provision with a build corresponding 55 to |value|. 56 @param force: True iff we should re-provision the machine regardless of 57 the current image version. If False and the image 58 version matches our expected image version, no 59 provisioning will be done. 60 """ 61 with_cheets = False 62 logging.debug('Start provisioning %s to %s.', host, value) 63 if value.endswith(provision.CHEETS_SUFFIX): 64 image = re.sub(provision.CHEETS_SUFFIX + '$', '', value) 65 with_cheets = True 66 else: 67 image = value 68 69 # If the host is already on the correct build, we have nothing to do. 70 # Note that this means we're not doing any sort of stateful-only 71 # update, and that we're relying more on cleanup to do cleanup. 72 # We could just not pass |force_update=True| to |machine_install|, 73 # but I'd like the semantics that a provision test 'returns' TestNA 74 # if the machine is already properly provisioned. 75 if not force: 76 info = host.host_info_store.get() 77 if info.build == value: 78 # We can't raise a TestNA, as would make sense, as that makes 79 # job.run_test return False as if the job failed. However, it'd 80 # still be nice to get this into the status.log, so we manually 81 # emit an INFO line instead. 82 self.job.record('INFO', None, None, 83 'Host already running %s' % value) 84 return 85 86 # We're about to reimage a machine, so we need full_payload and 87 # stateful. If something happened where the devserver doesn't have one 88 # of these, then it's also likely that it'll be missing autotest. 89 # Therefore, we require the devserver to also have autotest staged, so 90 # that the test that runs after this provision finishes doesn't error 91 # out because the devserver that its job_repo_url is set to is missing 92 # autotest test code. 93 # TODO(milleral): http://crbug.com/249426 94 # Add an asynchronous staging call so that we can ask the devserver to 95 # fetch autotest in the background here, and then wait on it after 96 # reimaging finishes or at some other point in the provisioning. 97 ds = None 98 try: 99 ds = dev_server.ImageServer.resolve(image, host.hostname) 100 ds.stage_artifacts(image, ['full_payload', 'stateful', 101 'autotest_packages']) 102 try: 103 ds.stage_artifacts(image, ['quick_provision']) 104 except dev_server.DevServerException as e: 105 logging.warning('Unable to stage quick provision payload: %s', 106 e) 107 except dev_server.DevServerException as e: 108 raise error.TestFail, str(e), sys.exc_info()[2] 109 finally: 110 # If a devserver is resolved, Log what has been downloaded so far. 111 if ds: 112 try: 113 ds.list_image_dir(image) 114 except (dev_server.DevServerException, urllib2.URLError) as e2: 115 logging.warning('Failed to list_image_dir for build %s. ' 116 'Error: %s', image, e2) 117 118 url = _IMAGE_URL_PATTERN % (ds.url(), image) 119 120 logging.debug('Installing image') 121 try: 122 afe_utils.machine_install_and_update_labels( 123 host, 124 force_update=True, 125 update_url=url, 126 force_full_update=force, 127 with_cheets=with_cheets) 128 except error.InstallError as e: 129 logging.error(e) 130 raise error.TestFail, str(e), sys.exc_info()[2] 131 logging.debug('Finished provisioning %s to %s', host, value) 132