• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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