• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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