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