Home | History | Annotate | Download | only in server
      1 # Copyright (c) 2012 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 os
      7 import tempfile
      8 import urllib2
      9 
     10 from autotest_lib.client.common_lib import error, global_config
     11 from autotest_lib.client.common_lib.cros import dev_server
     12 from autotest_lib.server import installable_object, autoserv_parser
     13 from autotest_lib.server import utils as server_utils
     14 from autotest_lib.server.cros.dynamic_suite import tools
     15 from autotest_lib.server.cros.dynamic_suite.constants import JOB_REPO_URL
     16 
     17 
     18 _CONFIG = global_config.global_config
     19 _PARSER = autoserv_parser.autoserv_parser
     20 
     21 
     22 class SiteAutotest(installable_object.InstallableObject):
     23     """Site implementation of Autotest."""
     24 
     25     def get(self, location=None):
     26         if not location:
     27             location = os.path.join(self.serverdir, '../client')
     28             location = os.path.abspath(location)
     29         installable_object.InstallableObject.get(self, location)
     30         self.got = True
     31 
     32 
     33     def _get_fetch_location_from_host_attribute(self):
     34         """Get repo to use for packages from host attribute, if possible.
     35 
     36         Hosts are tagged with an attribute containing the URL
     37         from which to source packages when running a test on that host.
     38         If self.host is set, attempt to look this attribute up by calling out
     39         to the AFE.
     40 
     41         @returns value of the 'job_repo_url' host attribute, if present.
     42         """
     43         try:
     44             from autotest_lib.server.cros.dynamic_suite import frontend_wrappers
     45             if self.host:
     46                 afe = frontend_wrappers.RetryingAFE(timeout_min=5, delay_sec=10)
     47                 hosts = afe.get_hosts(hostname=self.host.hostname)
     48                 if hosts and JOB_REPO_URL in hosts[0].attributes:
     49                     logging.info('Get job repo url from host attributes: %s',
     50                                  hosts[0].attributes[JOB_REPO_URL])
     51                     return hosts[0].attributes[JOB_REPO_URL]
     52                 logging.warning("No %s for %s", JOB_REPO_URL, self.host)
     53         except (ImportError, urllib2.URLError):
     54             logging.warning('Not attempting to look for %s', JOB_REPO_URL)
     55             pass
     56         return None
     57 
     58 
     59     def get_fetch_location(self):
     60         """Generate list of locations where autotest can look for packages.
     61 
     62         Old n' busted: Autotest packages are always stored at a URL that can
     63         be derived from the one passed via the voodoo magic --image argument.
     64         New hotness: Hosts are tagged with an attribute containing the URL
     65         from which to source packages when running a test on that host.
     66 
     67         @returns the list of candidate locations to check for packages.
     68         """
     69         repos = super(SiteAutotest, self).get_fetch_location()
     70 
     71         if _PARSER.options.image:
     72             image_opt = _PARSER.options.image
     73             if image_opt.startswith('http://'):
     74                 # A devserver HTTP url was specified, set that as the repo_url.
     75                 repos.append(image_opt.replace(
     76                     'update', 'static').rstrip('/') + '/autotest')
     77             else:
     78                 # An image_name like stumpy-release/R27-3437.0.0 was specified,
     79                 # set this as the repo_url for the host. If an AFE is not being
     80                 # run, this will ensure that the installed build uses the
     81                 # associated artifacts for the test specified when running
     82                 # autoserv with --image. However, any subsequent tests run on
     83                 # the host will no longer have the context of the image option
     84                 # and will revert back to utilizing test code/artifacts that are
     85                 # currently present in the users source checkout.
     86                 # devserver selected must be in the same subnet of self.host, if
     87                 # the host is in restricted subnet. Otherwise, host may not be
     88                 # able to reach the devserver and download packages from the
     89                 # repo_url.
     90                 hostname = self.host.hostname if self.host else None
     91                 devserver_url = dev_server.ImageServer.resolve(
     92                         image_opt, hostname).url()
     93                 repo_url = tools.get_package_url(devserver_url, image_opt)
     94                 repos.append(repo_url)
     95         elif not server_utils.is_inside_chroot():
     96             # Only try to get fetch location from host attribute if the test
     97             # is not running inside chroot.
     98             # No --image option was specified, look for the repo url via
     99             # the host attribute. If we are not running with a full AFE
    100             # autoserv will fall back to serving packages itself from whatever
    101             # source version it is sync'd to rather than using the proper
    102             # artifacts for the build on the host.
    103             found_repo = self._get_fetch_location_from_host_attribute()
    104             if found_repo is not None:
    105                 # Add our new repo to the end, the package manager will
    106                 # later reverse the list of repositories resulting in ours
    107                 # being first
    108                 repos.append(found_repo)
    109 
    110         return repos
    111 
    112 
    113     def install(self, host=None, autodir=None, use_packaging=True):
    114         """Install autotest.  If |host| is not None, stores it in |self.host|.
    115 
    116         @param host A Host instance on which autotest will be installed
    117         @param autodir Location on the remote host to install to
    118         @param use_packaging Enable install modes that use the packaging system.
    119 
    120         """
    121         if host:
    122             self.host = host
    123 
    124         super(SiteAutotest, self).install(host=host, autodir=autodir,
    125                                           use_packaging=use_packaging)
    126 
    127 
    128     def _install(self, host=None, autodir=None, use_autoserv=True,
    129                  use_packaging=True):
    130         """
    131         Install autotest.  If get() was not called previously, an
    132         attempt will be made to install from the autotest svn
    133         repository.
    134 
    135         @param host A Host instance on which autotest will be installed
    136         @param autodir Location on the remote host to install to
    137         @param use_autoserv Enable install modes that depend on the client
    138             running with the autoserv harness
    139         @param use_packaging Enable install modes that use the packaging system
    140 
    141         @exception AutoservError if a tarball was not specified and
    142             the target host does not have svn installed in its path
    143         """
    144         # TODO(milleral): http://crbug.com/258161
    145         super(SiteAutotest, self)._install(host, autodir, use_autoserv,
    146                                            use_packaging)
    147         # Send over the most recent global_config.ini after installation if one
    148         # is available.
    149         # This code is a bit duplicated from
    150         # _BaseRun._create_client_config_file, but oh well.
    151         if self.installed and self.source_material:
    152             logging.info('Installing updated global_config.ini.')
    153             destination = os.path.join(self.host.get_autodir(),
    154                                        'global_config.ini')
    155             with tempfile.NamedTemporaryFile() as client_config:
    156                 config = global_config.global_config
    157                 client_section = config.get_section_values('CLIENT')
    158                 client_section.write(client_config)
    159                 client_config.flush()
    160                 self.host.send_file(client_config.name, destination)
    161 
    162 
    163     def run_static_method(self, module, method, results_dir='.', host=None,
    164                           *args):
    165         """Runs a non-instance method with |args| from |module| on the client.
    166 
    167         This method runs a static/class/module autotest method on the client.
    168         For example:
    169           run_static_method("autotest_lib.client.cros.cros_ui", "reboot")
    170 
    171         Will run autotest_lib.client.cros.cros_ui.reboot() on the client.
    172 
    173         @param module: module name as you would refer to it when importing in a
    174             control file. e.g. autotest_lib.client.common_lib.module_name.
    175         @param method: the method you want to call.
    176         @param results_dir: A str path where the results should be stored
    177             on the local filesystem.
    178         @param host: A Host instance on which the control file should
    179             be run.
    180         @param args: args to pass to the method.
    181         """
    182         control = "\n".join(["import %s" % module,
    183                              "%s.%s(%s)\n" % (module, method,
    184                                               ','.join(map(repr, args)))])
    185         self.run(control, results_dir=results_dir, host=host)
    186 
    187 
    188 class SiteClientLogger(object):
    189     """Overrides default client logger to allow for using a local package cache.
    190     """
    191 
    192     def _process_line(self, line):
    193         """Returns the package checksum file if it exists."""
    194         logging.debug(line)
    195         fetch_package_match = self.fetch_package_parser.search(line)
    196         if fetch_package_match:
    197             pkg_name, dest_path, fifo_path = fetch_package_match.groups()
    198             serve_packages = _CONFIG.get_config_value(
    199                 "PACKAGES", "serve_packages_from_autoserv", type=bool)
    200             if serve_packages and pkg_name == 'packages.checksum':
    201                 try:
    202                     checksum_file = os.path.join(
    203                         self.job.pkgmgr.pkgmgr_dir, 'packages', pkg_name)
    204                     if os.path.exists(checksum_file):
    205                         self.host.send_file(checksum_file, dest_path)
    206                 except error.AutoservRunError:
    207                     msg = "Package checksum file not found, continuing anyway"
    208                     logging.exception(msg)
    209 
    210                 try:
    211                     # When fetching a package, the client expects to be
    212                     # notified when the fetching is complete. Autotest
    213                     # does this pushing a B to a fifo queue to the client.
    214                     self.host.run("echo B > %s" % fifo_path)
    215                 except error.AutoservRunError:
    216                     msg = "Checksum installation failed, continuing anyway"
    217                     logging.exception(msg)
    218                 finally:
    219                     return
    220 
    221         # Fall through to process the line using the default method.
    222         super(SiteClientLogger, self)._process_line(line)
    223 
    224 
    225     def _send_tarball(self, pkg_name, remote_dest):
    226         """Uses tarballs in package manager by default."""
    227         try:
    228             server_package = os.path.join(self.job.pkgmgr.pkgmgr_dir,
    229                                           'packages', pkg_name)
    230             if os.path.exists(server_package):
    231               self.host.send_file(server_package, remote_dest)
    232               return
    233 
    234         except error.AutoservRunError:
    235             msg = ("Package %s could not be sent from the package cache." %
    236                    pkg_name)
    237             logging.exception(msg)
    238 
    239         # Fall through to send tarball the default method.
    240         super(SiteClientLogger, self)._send_tarball(pkg_name, remote_dest)
    241 
    242 
    243 class _SiteRun(object):
    244     pass
    245