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 5import logging 6import os 7import tempfile 8import urllib2 9 10from autotest_lib.client.common_lib import error, global_config 11from autotest_lib.client.common_lib.cros import dev_server 12from autotest_lib.server import installable_object, autoserv_parser 13from autotest_lib.server import utils as server_utils 14from autotest_lib.server.cros.dynamic_suite import tools 15from 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 22class 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 188class 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 243class _SiteRun(object): 244 pass 245