244 lines
11 KiB
Python
Executable file
244 lines
11 KiB
Python
Executable file
# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
import logging
|
|
import os
|
|
import tempfile
|
|
import urllib2
|
|
|
|
from autotest_lib.client.common_lib import error, global_config
|
|
from autotest_lib.client.common_lib.cros import dev_server
|
|
from autotest_lib.server import installable_object, autoserv_parser
|
|
from autotest_lib.server import utils as server_utils
|
|
from autotest_lib.server.cros.dynamic_suite import tools
|
|
from autotest_lib.server.cros.dynamic_suite.constants import JOB_REPO_URL
|
|
|
|
|
|
_CONFIG = global_config.global_config
|
|
_PARSER = autoserv_parser.autoserv_parser
|
|
|
|
|
|
class SiteAutotest(installable_object.InstallableObject):
|
|
"""Site implementation of Autotest."""
|
|
|
|
def get(self, location=None):
|
|
if not location:
|
|
location = os.path.join(self.serverdir, '../client')
|
|
location = os.path.abspath(location)
|
|
installable_object.InstallableObject.get(self, location)
|
|
self.got = True
|
|
|
|
|
|
def _get_fetch_location_from_host_attribute(self):
|
|
"""Get repo to use for packages from host attribute, if possible.
|
|
|
|
Hosts are tagged with an attribute containing the URL
|
|
from which to source packages when running a test on that host.
|
|
If self.host is set, attempt to look this attribute up by calling out
|
|
to the AFE.
|
|
|
|
@returns value of the 'job_repo_url' host attribute, if present.
|
|
"""
|
|
try:
|
|
from autotest_lib.server.cros.dynamic_suite import frontend_wrappers
|
|
if self.host:
|
|
afe = frontend_wrappers.RetryingAFE(timeout_min=5, delay_sec=10)
|
|
hosts = afe.get_hosts(hostname=self.host.hostname)
|
|
if hosts and JOB_REPO_URL in hosts[0].attributes:
|
|
logging.info('Get job repo url from host attributes: %s',
|
|
hosts[0].attributes[JOB_REPO_URL])
|
|
return hosts[0].attributes[JOB_REPO_URL]
|
|
logging.warning("No %s for %s", JOB_REPO_URL, self.host)
|
|
except (ImportError, urllib2.URLError):
|
|
logging.warning('Not attempting to look for %s', JOB_REPO_URL)
|
|
pass
|
|
return None
|
|
|
|
|
|
def get_fetch_location(self):
|
|
"""Generate list of locations where autotest can look for packages.
|
|
|
|
Old n' busted: Autotest packages are always stored at a URL that can
|
|
be derived from the one passed via the voodoo magic --image argument.
|
|
New hotness: Hosts are tagged with an attribute containing the URL
|
|
from which to source packages when running a test on that host.
|
|
|
|
@returns the list of candidate locations to check for packages.
|
|
"""
|
|
repos = super(SiteAutotest, self).get_fetch_location()
|
|
|
|
if _PARSER.options.image:
|
|
image_opt = _PARSER.options.image
|
|
if image_opt.startswith('http://'):
|
|
# A devserver HTTP url was specified, set that as the repo_url.
|
|
repos.append(image_opt.replace(
|
|
'update', 'static').rstrip('/') + '/autotest')
|
|
else:
|
|
# An image_name like stumpy-release/R27-3437.0.0 was specified,
|
|
# set this as the repo_url for the host. If an AFE is not being
|
|
# run, this will ensure that the installed build uses the
|
|
# associated artifacts for the test specified when running
|
|
# autoserv with --image. However, any subsequent tests run on
|
|
# the host will no longer have the context of the image option
|
|
# and will revert back to utilizing test code/artifacts that are
|
|
# currently present in the users source checkout.
|
|
# devserver selected must be in the same subnet of self.host, if
|
|
# the host is in restricted subnet. Otherwise, host may not be
|
|
# able to reach the devserver and download packages from the
|
|
# repo_url.
|
|
hostname = self.host.hostname if self.host else None
|
|
devserver_url = dev_server.ImageServer.resolve(
|
|
image_opt, hostname).url()
|
|
repo_url = tools.get_package_url(devserver_url, image_opt)
|
|
repos.append(repo_url)
|
|
elif not server_utils.is_inside_chroot():
|
|
# Only try to get fetch location from host attribute if the test
|
|
# is not running inside chroot.
|
|
# No --image option was specified, look for the repo url via
|
|
# the host attribute. If we are not running with a full AFE
|
|
# autoserv will fall back to serving packages itself from whatever
|
|
# source version it is sync'd to rather than using the proper
|
|
# artifacts for the build on the host.
|
|
found_repo = self._get_fetch_location_from_host_attribute()
|
|
if found_repo is not None:
|
|
# Add our new repo to the end, the package manager will
|
|
# later reverse the list of repositories resulting in ours
|
|
# being first
|
|
repos.append(found_repo)
|
|
|
|
return repos
|
|
|
|
|
|
def install(self, host=None, autodir=None, use_packaging=True):
|
|
"""Install autotest. If |host| is not None, stores it in |self.host|.
|
|
|
|
@param host A Host instance on which autotest will be installed
|
|
@param autodir Location on the remote host to install to
|
|
@param use_packaging Enable install modes that use the packaging system.
|
|
|
|
"""
|
|
if host:
|
|
self.host = host
|
|
|
|
super(SiteAutotest, self).install(host=host, autodir=autodir,
|
|
use_packaging=use_packaging)
|
|
|
|
|
|
def _install(self, host=None, autodir=None, use_autoserv=True,
|
|
use_packaging=True):
|
|
"""
|
|
Install autotest. If get() was not called previously, an
|
|
attempt will be made to install from the autotest svn
|
|
repository.
|
|
|
|
@param host A Host instance on which autotest will be installed
|
|
@param autodir Location on the remote host to install to
|
|
@param use_autoserv Enable install modes that depend on the client
|
|
running with the autoserv harness
|
|
@param use_packaging Enable install modes that use the packaging system
|
|
|
|
@exception AutoservError if a tarball was not specified and
|
|
the target host does not have svn installed in its path
|
|
"""
|
|
# TODO(milleral): http://crbug.com/258161
|
|
super(SiteAutotest, self)._install(host, autodir, use_autoserv,
|
|
use_packaging)
|
|
# Send over the most recent global_config.ini after installation if one
|
|
# is available.
|
|
# This code is a bit duplicated from
|
|
# _BaseRun._create_client_config_file, but oh well.
|
|
if self.installed and self.source_material:
|
|
logging.info('Installing updated global_config.ini.')
|
|
destination = os.path.join(self.host.get_autodir(),
|
|
'global_config.ini')
|
|
with tempfile.NamedTemporaryFile() as client_config:
|
|
config = global_config.global_config
|
|
client_section = config.get_section_values('CLIENT')
|
|
client_section.write(client_config)
|
|
client_config.flush()
|
|
self.host.send_file(client_config.name, destination)
|
|
|
|
|
|
def run_static_method(self, module, method, results_dir='.', host=None,
|
|
*args):
|
|
"""Runs a non-instance method with |args| from |module| on the client.
|
|
|
|
This method runs a static/class/module autotest method on the client.
|
|
For example:
|
|
run_static_method("autotest_lib.client.cros.cros_ui", "reboot")
|
|
|
|
Will run autotest_lib.client.cros.cros_ui.reboot() on the client.
|
|
|
|
@param module: module name as you would refer to it when importing in a
|
|
control file. e.g. autotest_lib.client.common_lib.module_name.
|
|
@param method: the method you want to call.
|
|
@param results_dir: A str path where the results should be stored
|
|
on the local filesystem.
|
|
@param host: A Host instance on which the control file should
|
|
be run.
|
|
@param args: args to pass to the method.
|
|
"""
|
|
control = "\n".join(["import %s" % module,
|
|
"%s.%s(%s)\n" % (module, method,
|
|
','.join(map(repr, args)))])
|
|
self.run(control, results_dir=results_dir, host=host)
|
|
|
|
|
|
class SiteClientLogger(object):
|
|
"""Overrides default client logger to allow for using a local package cache.
|
|
"""
|
|
|
|
def _process_line(self, line):
|
|
"""Returns the package checksum file if it exists."""
|
|
logging.debug(line)
|
|
fetch_package_match = self.fetch_package_parser.search(line)
|
|
if fetch_package_match:
|
|
pkg_name, dest_path, fifo_path = fetch_package_match.groups()
|
|
serve_packages = _CONFIG.get_config_value(
|
|
"PACKAGES", "serve_packages_from_autoserv", type=bool)
|
|
if serve_packages and pkg_name == 'packages.checksum':
|
|
try:
|
|
checksum_file = os.path.join(
|
|
self.job.pkgmgr.pkgmgr_dir, 'packages', pkg_name)
|
|
if os.path.exists(checksum_file):
|
|
self.host.send_file(checksum_file, dest_path)
|
|
except error.AutoservRunError:
|
|
msg = "Package checksum file not found, continuing anyway"
|
|
logging.exception(msg)
|
|
|
|
try:
|
|
# When fetching a package, the client expects to be
|
|
# notified when the fetching is complete. Autotest
|
|
# does this pushing a B to a fifo queue to the client.
|
|
self.host.run("echo B > %s" % fifo_path)
|
|
except error.AutoservRunError:
|
|
msg = "Checksum installation failed, continuing anyway"
|
|
logging.exception(msg)
|
|
finally:
|
|
return
|
|
|
|
# Fall through to process the line using the default method.
|
|
super(SiteClientLogger, self)._process_line(line)
|
|
|
|
|
|
def _send_tarball(self, pkg_name, remote_dest):
|
|
"""Uses tarballs in package manager by default."""
|
|
try:
|
|
server_package = os.path.join(self.job.pkgmgr.pkgmgr_dir,
|
|
'packages', pkg_name)
|
|
if os.path.exists(server_package):
|
|
self.host.send_file(server_package, remote_dest)
|
|
return
|
|
|
|
except error.AutoservRunError:
|
|
msg = ("Package %s could not be sent from the package cache." %
|
|
pkg_name)
|
|
logging.exception(msg)
|
|
|
|
# Fall through to send tarball the default method.
|
|
super(SiteClientLogger, self)._send_tarball(pkg_name, remote_dest)
|
|
|
|
|
|
class _SiteRun(object):
|
|
pass
|