Chris Masone | 8abb6fc | 2012-01-31 09:27:36 -0800 | [diff] [blame] | 1 | # Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
Dale Curtis | 386eea7 | 2011-09-21 18:43:04 -0700 | [diff] [blame] | 2 | # Use of this source code is governed by a BSD-style license that can be |
| 3 | # found in the LICENSE file. |
| 4 | |
Chris Masone | 8abb6fc | 2012-01-31 09:27:36 -0800 | [diff] [blame] | 5 | import logging |
Eric Li | a11a045 | 2010-01-07 22:01:58 -0800 | [diff] [blame] | 6 | import os |
Christopher Wiley | 809301c | 2013-07-02 18:00:31 -0700 | [diff] [blame] | 7 | import tempfile |
Alex Miller | 4f34170 | 2013-03-25 12:39:12 -0700 | [diff] [blame] | 8 | import urllib2 |
Christopher Wiley | 809301c | 2013-07-02 18:00:31 -0700 | [diff] [blame] | 9 | |
Chris Sosa | 3ee5d5c | 2012-02-23 11:18:41 -0800 | [diff] [blame] | 10 | from autotest_lib.client.common_lib import error, global_config |
Scott Zawalski | eadbf70 | 2013-03-14 09:23:06 -0400 | [diff] [blame] | 11 | from autotest_lib.client.common_lib.cros import dev_server |
Chris Masone | 8abb6fc | 2012-01-31 09:27:36 -0800 | [diff] [blame] | 12 | from autotest_lib.server import installable_object, autoserv_parser |
Dan Shi | 82997b9 | 2015-05-06 12:08:02 -0700 | [diff] [blame] | 13 | from autotest_lib.server import utils as server_utils |
Scott Zawalski | eadbf70 | 2013-03-14 09:23:06 -0400 | [diff] [blame] | 14 | from autotest_lib.server.cros.dynamic_suite import tools |
Chris Masone | 44e4d6c | 2012-08-15 14:25:53 -0700 | [diff] [blame] | 15 | from autotest_lib.server.cros.dynamic_suite.constants import JOB_REPO_URL |
Eric Li | 2eb800f | 2011-04-21 16:52:58 -0700 | [diff] [blame] | 16 | |
| 17 | |
Chris Masone | 44e4d6c | 2012-08-15 14:25:53 -0700 | [diff] [blame] | 18 | _CONFIG = global_config.global_config |
| 19 | _PARSER = autoserv_parser.autoserv_parser |
Eric Li | 2eb800f | 2011-04-21 16:52:58 -0700 | [diff] [blame] | 20 | |
Eric Li | a11a045 | 2010-01-07 22:01:58 -0800 | [diff] [blame] | 21 | |
| 22 | class SiteAutotest(installable_object.InstallableObject): |
Dan Shi | b669cbd | 2013-09-13 11:17:17 -0700 | [diff] [blame] | 23 | """Site implementation of Autotest.""" |
Eric Li | a11a045 | 2010-01-07 22:01:58 -0800 | [diff] [blame] | 24 | |
Chris Sosa | 3ee5d5c | 2012-02-23 11:18:41 -0800 | [diff] [blame] | 25 | def get(self, location=None): |
Eric Li | a11a045 | 2010-01-07 22:01:58 -0800 | [diff] [blame] | 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 | |
Eric Li | c7444bd | 2011-02-15 17:12:10 -0800 | [diff] [blame] | 32 | |
Chris Masone | 8abb6fc | 2012-01-31 09:27:36 -0800 | [diff] [blame] | 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: |
Dan Shi | 349a33c | 2016-03-16 23:57:21 -0700 | [diff] [blame] | 44 | from autotest_lib.server.cros.dynamic_suite import frontend_wrappers |
Chris Masone | 8abb6fc | 2012-01-31 09:27:36 -0800 | [diff] [blame] | 45 | if self.host: |
Dan Shi | 349a33c | 2016-03-16 23:57:21 -0700 | [diff] [blame] | 46 | afe = frontend_wrappers.RetryingAFE(timeout_min=5, delay_sec=10) |
Chris Masone | 8abb6fc | 2012-01-31 09:27:36 -0800 | [diff] [blame] | 47 | hosts = afe.get_hosts(hostname=self.host.hostname) |
Chris Masone | 44e4d6c | 2012-08-15 14:25:53 -0700 | [diff] [blame] | 48 | if hosts and JOB_REPO_URL in hosts[0].attributes: |
Dan Shi | 349a33c | 2016-03-16 23:57:21 -0700 | [diff] [blame] | 49 | logging.info('Get job repo url from host attributes: %s', |
| 50 | hosts[0].attributes[JOB_REPO_URL]) |
Chris Masone | 44e4d6c | 2012-08-15 14:25:53 -0700 | [diff] [blame] | 51 | return hosts[0].attributes[JOB_REPO_URL] |
| 52 | logging.warning("No %s for %s", JOB_REPO_URL, self.host) |
Alex Miller | 4f34170 | 2013-03-25 12:39:12 -0700 | [diff] [blame] | 53 | except (ImportError, urllib2.URLError): |
Chris Masone | 44e4d6c | 2012-08-15 14:25:53 -0700 | [diff] [blame] | 54 | logging.warning('Not attempting to look for %s', JOB_REPO_URL) |
Chris Masone | 8abb6fc | 2012-01-31 09:27:36 -0800 | [diff] [blame] | 55 | pass |
| 56 | return None |
| 57 | |
| 58 | |
Eric Li | 2eb800f | 2011-04-21 16:52:58 -0700 | [diff] [blame] | 59 | def get_fetch_location(self): |
Chris Masone | 8abb6fc | 2012-01-31 09:27:36 -0800 | [diff] [blame] | 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 | """ |
Dale Curtis | 386eea7 | 2011-09-21 18:43:04 -0700 | [diff] [blame] | 69 | repos = super(SiteAutotest, self).get_fetch_location() |
Chris Masone | 8abb6fc | 2012-01-31 09:27:36 -0800 | [diff] [blame] | 70 | |
Chris Masone | 44e4d6c | 2012-08-15 14:25:53 -0700 | [diff] [blame] | 71 | if _PARSER.options.image: |
Scott Zawalski | eadbf70 | 2013-03-14 09:23:06 -0400 | [diff] [blame] | 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( |
joychen | 03eaad9 | 2013-06-26 09:55:21 -0700 | [diff] [blame] | 76 | 'update', 'static').rstrip('/') + '/autotest') |
Scott Zawalski | eadbf70 | 2013-03-14 09:23:06 -0400 | [diff] [blame] | 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. |
Dan Shi | 69b9b97 | 2016-04-15 23:19:57 -0700 | [diff] [blame] | 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() |
Scott Zawalski | eadbf70 | 2013-03-14 09:23:06 -0400 | [diff] [blame] | 93 | repo_url = tools.get_package_url(devserver_url, image_opt) |
| 94 | repos.append(repo_url) |
Dan Shi | 82997b9 | 2015-05-06 12:08:02 -0700 | [diff] [blame] | 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. |
Scott Zawalski | eadbf70 | 2013-03-14 09:23:06 -0400 | [diff] [blame] | 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. |
Chris Masone | be78eb0 | 2012-02-23 14:28:19 -0800 | [diff] [blame] | 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) |
Eric Li | 2eb800f | 2011-04-21 16:52:58 -0700 | [diff] [blame] | 109 | |
Dale Curtis | 386eea7 | 2011-09-21 18:43:04 -0700 | [diff] [blame] | 110 | return repos |
Eric Li | 2eb800f | 2011-04-21 16:52:58 -0700 | [diff] [blame] | 111 | |
| 112 | |
Dan Shi | b669cbd | 2013-09-13 11:17:17 -0700 | [diff] [blame] | 113 | def install(self, host=None, autodir=None, use_packaging=True): |
Chris Masone | 8abb6fc | 2012-01-31 09:27:36 -0800 | [diff] [blame] | 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 |
Dan Shi | b669cbd | 2013-09-13 11:17:17 -0700 | [diff] [blame] | 118 | @param use_packaging Enable install modes that use the packaging system. |
| 119 | |
Chris Masone | 8abb6fc | 2012-01-31 09:27:36 -0800 | [diff] [blame] | 120 | """ |
| 121 | if host: |
| 122 | self.host = host |
| 123 | |
Dan Shi | b669cbd | 2013-09-13 11:17:17 -0700 | [diff] [blame] | 124 | super(SiteAutotest, self).install(host=host, autodir=autodir, |
| 125 | use_packaging=use_packaging) |
Chris Masone | 8abb6fc | 2012-01-31 09:27:36 -0800 | [diff] [blame] | 126 | |
| 127 | |
Christopher Wiley | 809301c | 2013-07-02 18:00:31 -0700 | [diff] [blame] | 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 | |
Chris Sosa | f4d43ff | 2012-10-30 11:21:05 -0700 | [diff] [blame] | 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 | |
Chris Sosa | 3ee5d5c | 2012-02-23 11:18:41 -0800 | [diff] [blame] | 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() |
Chris Masone | 44e4d6c | 2012-08-15 14:25:53 -0700 | [diff] [blame] | 198 | serve_packages = _CONFIG.get_config_value( |
Chris Sosa | 3ee5d5c | 2012-02-23 11:18:41 -0800 | [diff] [blame] | 199 | "PACKAGES", "serve_packages_from_autoserv", type=bool) |
| 200 | if serve_packages and pkg_name == 'packages.checksum': |
Chris Sosa | 3ee5d5c | 2012-02-23 11:18:41 -0800 | [diff] [blame] | 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) |
Chris Sosa | 3ee5d5c | 2012-02-23 11:18:41 -0800 | [diff] [blame] | 206 | except error.AutoservRunError: |
| 207 | msg = "Package checksum file not found, continuing anyway" |
| 208 | logging.exception(msg) |
| 209 | |
Chris Sosa | 2cc4da0 | 2012-07-09 16:18:24 -0700 | [diff] [blame] | 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 |
Chris Sosa | 3ee5d5c | 2012-02-23 11:18:41 -0800 | [diff] [blame] | 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 | |
Eric Li | c7444bd | 2011-02-15 17:12:10 -0800 | [diff] [blame] | 243 | class _SiteRun(object): |
| 244 | pass |