blob: fa875432b2814919bdd92feceec8e3adbe20b492 [file] [log] [blame]
mblighdcd57a82007-07-11 23:06:47 +00001# Copyright 2007 Google Inc. Released under the GPL v2
Dan Shib669cbd2013-09-13 11:17:17 -07002#pylint: disable-msg=C0111
mblighdcd57a82007-07-11 23:06:47 +00003
Dan Shi9f879fb2017-05-26 15:44:04 -07004import glob
beeps07f53b92013-01-08 12:55:10 -08005import logging
Dan Shi9f879fb2017-05-26 15:44:04 -07006import os
7import re
8import sys
9import tempfile
10import time
11import traceback
Prathmesh Prabhu7cc11532016-11-23 17:51:08 -080012
Prathmesh Prabhu7cc11532016-11-23 17:51:08 -080013import common
Dan Shi010c0bc2017-06-21 17:02:51 -070014from autotest_lib.client.bin.result_tools import runner as result_tools_runner
15from autotest_lib.client.common_lib import autotemp
16from autotest_lib.client.common_lib import base_job
17from autotest_lib.client.common_lib import error
Dan Shi1889ca12015-04-15 09:36:06 -070018from autotest_lib.client.common_lib import global_config
Allen Li944ac462017-02-07 15:57:20 -080019from autotest_lib.client.common_lib import packages
Allen Lid5abdab2017-02-07 16:03:43 -080020from autotest_lib.client.common_lib.cros import dev_server
Allen Li79623692017-08-17 16:27:11 -070021from autotest_lib.client.common_lib import utils as client_utils
Allen Lid5abdab2017-02-07 16:03:43 -080022from autotest_lib.server import autoserv_parser
Dan Shi9f879fb2017-05-26 15:44:04 -070023from autotest_lib.server import installable_object
Dan Shi9f879fb2017-05-26 15:44:04 -070024from autotest_lib.server import utils
Allen Lid5abdab2017-02-07 16:03:43 -080025from autotest_lib.server import utils as server_utils
26from autotest_lib.server.cros.dynamic_suite import tools
27from autotest_lib.server.cros.dynamic_suite.constants import JOB_REPO_URL
28
mbligh3c7a1502008-07-24 18:08:47 +000029
Dan Shi5e2efb72017-02-07 11:40:23 -080030try:
31 from chromite.lib import metrics
32except ImportError:
33 metrics = client_utils.metrics_mock
34
35
Dale Curtiscb7bfaf2011-06-07 16:21:57 -070036AUTOTEST_SVN = 'svn://test.kernel.org/autotest/trunk/client'
mblighdcd57a82007-07-11 23:06:47 +000037AUTOTEST_HTTP = 'http://test.kernel.org/svn/autotest/trunk/client'
38
Allen Lid5abdab2017-02-07 16:03:43 -080039_PARSER = autoserv_parser.autoserv_parser
40
Allen Li944ac462017-02-07 15:57:20 -080041_CONFIG = global_config.global_config
42AUTOSERV_PREBUILD = _CONFIG.get_config_value(
Dan Shic958a3d2017-07-06 14:43:18 -070043 'AUTOSERV', 'enable_server_prebuild', type=bool, default=False)
jadmanski2a89dac2010-06-11 14:32:58 +000044
showardad812bf2009-10-20 23:49:56 +000045class AutodirNotFoundError(Exception):
46 """No Autotest installation could be found."""
47
48
beeps607428b2013-03-25 16:43:20 -070049class AutotestFailure(Exception):
50 """Gereric exception class for failures during a test run."""
51
52
53class AutotestAbort(AutotestFailure):
54 """
55 AutotestAborts are thrown when the DUT seems fine,
56 and the test doesn't give us an explicit reason for
57 failure; In this case we have no choice but to abort.
58 """
59
60
61class AutotestDeviceError(AutotestFailure):
62 """
63 Exceptions that inherit from AutotestDeviceError
64 are thrown when we can determine the current
65 state of the DUT and conclude that it probably
66 lead to the test failing; these exceptions lead
67 to failures instead of aborts.
68 """
69
70
71class AutotestDeviceNotPingable(AutotestDeviceError):
72 """Error for when a DUT becomes unpingable."""
73
74
75class AutotestDeviceNotSSHable(AutotestDeviceError):
76 """Error for when a DUT is pingable but not SSHable."""
77
78
79class AutotestDeviceRebooted(AutotestDeviceError):
80 """Error for when a DUT rebooted unexpectedly."""
81
82
Allen Lid5abdab2017-02-07 16:03:43 -080083class Autotest(installable_object.InstallableObject):
jadmanski0afbb632008-06-06 21:10:57 +000084 """
85 This class represents the Autotest program.
mblighdcd57a82007-07-11 23:06:47 +000086
jadmanski0afbb632008-06-06 21:10:57 +000087 Autotest is used to run tests automatically and collect the results.
88 It also supports profilers.
mblighdcd57a82007-07-11 23:06:47 +000089
jadmanski0afbb632008-06-06 21:10:57 +000090 Implementation details:
91 This is a leaf class in an abstract class hierarchy, it must
92 implement the unimplemented methods in parent classes.
93 """
mbligh119c12a2007-11-12 22:13:44 +000094
Dale Curtiscb7bfaf2011-06-07 16:21:57 -070095 def __init__(self, host=None):
jadmanski0afbb632008-06-06 21:10:57 +000096 self.host = host
97 self.got = False
98 self.installed = False
99 self.serverdir = utils.get_server_dir()
Allen Lid5abdab2017-02-07 16:03:43 -0800100 super(Autotest, self).__init__()
mblighc8949b82007-07-23 16:33:58 +0000101
mblighdc735a22007-08-02 16:54:37 +0000102
jadmanskif22fea82008-11-26 20:57:07 +0000103 install_in_tmpdir = False
104 @classmethod
105 def set_install_in_tmpdir(cls, flag):
106 """ Sets a flag that controls whether or not Autotest should by
107 default be installed in a "standard" directory (e.g.
108 /home/autotest, /usr/local/autotest) or a temporary directory. """
109 cls.install_in_tmpdir = flag
110
111
showardad812bf2009-10-20 23:49:56 +0000112 @classmethod
113 def get_client_autodir_paths(cls, host):
114 return global_config.global_config.get_config_value(
115 'AUTOSERV', 'client_autodir_paths', type=list)
116
117
118 @classmethod
119 def get_installed_autodir(cls, host):
120 """
121 Find where the Autotest client is installed on the host.
122 @returns an absolute path to an installed Autotest client root.
123 @raises AutodirNotFoundError if no Autotest installation can be found.
124 """
125 autodir = host.get_autodir()
126 if autodir:
127 logging.debug('Using existing host autodir: %s', autodir)
128 return autodir
129
130 for path in Autotest.get_client_autodir_paths(host):
131 try:
132 autotest_binary = os.path.join(path, 'bin', 'autotest')
Allen Liad719c12017-06-27 23:48:04 +0000133 host.run('test -x %s' % utils.sh_escape(autotest_binary))
134 host.run('test -w %s' % utils.sh_escape(path))
showardad812bf2009-10-20 23:49:56 +0000135 logging.debug('Found existing autodir at %s', path)
136 return path
Justin Giorgia4a843d2017-03-07 13:09:48 -0800137 except error.GenericHostRunError:
showardad812bf2009-10-20 23:49:56 +0000138 logging.debug('%s does not exist on %s', autotest_binary,
139 host.hostname)
140 raise AutodirNotFoundError
141
142
143 @classmethod
144 def get_install_dir(cls, host):
145 """
146 Determines the location where autotest should be installed on
jadmanskif22fea82008-11-26 20:57:07 +0000147 host. If self.install_in_tmpdir is set, it will return a unique
showardad812bf2009-10-20 23:49:56 +0000148 temporary directory that autotest can be installed in. Otherwise, looks
149 for an existing installation to use; if none is found, looks for a
150 usable directory in the global config client_autodir_paths.
151 """
jadmanskif22fea82008-11-26 20:57:07 +0000152 try:
lmr9dcf0832009-12-08 21:28:55 +0000153 install_dir = cls.get_installed_autodir(host)
showardad812bf2009-10-20 23:49:56 +0000154 except AutodirNotFoundError:
lmr9dcf0832009-12-08 21:28:55 +0000155 install_dir = cls._find_installable_dir(host)
156
157 if cls.install_in_tmpdir:
158 return host.get_tmp_dir(parent=install_dir)
159 return install_dir
showardad812bf2009-10-20 23:49:56 +0000160
161
162 @classmethod
163 def _find_installable_dir(cls, host):
164 client_autodir_paths = cls.get_client_autodir_paths(host)
165 for path in client_autodir_paths:
166 try:
Allen Liad719c12017-06-27 23:48:04 +0000167 host.run('mkdir -p %s' % utils.sh_escape(path))
168 host.run('test -w %s' % utils.sh_escape(path))
showardad812bf2009-10-20 23:49:56 +0000169 return path
170 except error.AutoservRunError:
171 logging.debug('Failed to create %s', path)
Prathmesh Prabhu7cc11532016-11-23 17:51:08 -0800172 metrics.Counter(
173 'chromeos/autotest/errors/no_autotest_install_path').increment(
174 fields={'dut_host_name': host.hostname})
showardad812bf2009-10-20 23:49:56 +0000175 raise error.AutoservInstallError(
Dale Curtis74a314b2011-06-23 14:55:46 -0700176 'Unable to find a place to install Autotest; tried %s' %
showardad812bf2009-10-20 23:49:56 +0000177 ', '.join(client_autodir_paths))
jadmanskif22fea82008-11-26 20:57:07 +0000178
179
Eric Lid656d562011-04-20 11:48:29 -0700180 def get_fetch_location(self):
Allen Lid5abdab2017-02-07 16:03:43 -0800181 """Generate list of locations where autotest can look for packages.
182
183 Old n' busted: Autotest packages are always stored at a URL that can
184 be derived from the one passed via the voodoo magic --image argument.
185 New hotness: Hosts are tagged with an attribute containing the URL
186 from which to source packages when running a test on that host.
187
188 @returns the list of candidate locations to check for packages.
189 """
Eric Lid656d562011-04-20 11:48:29 -0700190 c = global_config.global_config
191 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list,
192 default=[])
193 repos.reverse()
Allen Lid5abdab2017-02-07 16:03:43 -0800194
195 if _PARSER.options.image:
196 image_opt = _PARSER.options.image
197 if image_opt.startswith('http://'):
198 # A devserver HTTP url was specified, set that as the repo_url.
199 repos.append(image_opt.replace(
200 'update', 'static').rstrip('/') + '/autotest')
201 else:
202 # An image_name like stumpy-release/R27-3437.0.0 was specified,
203 # set this as the repo_url for the host. If an AFE is not being
204 # run, this will ensure that the installed build uses the
205 # associated artifacts for the test specified when running
206 # autoserv with --image. However, any subsequent tests run on
207 # the host will no longer have the context of the image option
208 # and will revert back to utilizing test code/artifacts that are
209 # currently present in the users source checkout.
210 # devserver selected must be in the same subnet of self.host, if
211 # the host is in restricted subnet. Otherwise, host may not be
212 # able to reach the devserver and download packages from the
213 # repo_url.
214 hostname = self.host.hostname if self.host else None
215 devserver_url = dev_server.ImageServer.resolve(
216 image_opt, hostname).url()
217 repo_url = tools.get_package_url(devserver_url, image_opt)
218 repos.append(repo_url)
219 elif not server_utils.is_inside_chroot():
220 # Only try to get fetch location from host attribute if the test
221 # is not running inside chroot.
222 # No --image option was specified, look for the repo url via
223 # the host attribute. If we are not running with a full AFE
224 # autoserv will fall back to serving packages itself from whatever
225 # source version it is sync'd to rather than using the proper
226 # artifacts for the build on the host.
227 found_repo = self._get_fetch_location_from_host_attribute()
228 if found_repo is not None:
229 # Add our new repo to the end, the package manager will
230 # later reverse the list of repositories resulting in ours
231 # being first
232 repos.append(found_repo)
233
Eric Lid656d562011-04-20 11:48:29 -0700234 return repos
235
236
Allen Lid5abdab2017-02-07 16:03:43 -0800237 def _get_fetch_location_from_host_attribute(self):
238 """Get repo to use for packages from host attribute, if possible.
239
240 Hosts are tagged with an attribute containing the URL
241 from which to source packages when running a test on that host.
242 If self.host is set, attempt to look this attribute in the host info.
243
244 @returns value of the 'job_repo_url' host attribute, if present.
245 """
246 if not self.host:
247 return None
248
249 try:
250 info = self.host.host_info_store.get()
251 except Exception as e:
252 # TODO(pprabhu): We really want to catch host_info.StoreError here,
253 # but we can't import host_info from this module.
254 # - autotest_lib.hosts.host_info pulls in (naturally)
255 # autotest_lib.hosts.__init__
256 # - This pulls in all the host classes ever defined
257 # - That includes abstract_ssh, which depends on autotest
258 logging.warning('Failed to obtain host info: %r', e)
259 logging.warning('Skipping autotest fetch location based on %s',
260 JOB_REPO_URL)
261 return None
262
263 job_repo_url = info.attributes.get(JOB_REPO_URL, '')
264 if not job_repo_url:
265 logging.warning("No %s for %s", JOB_REPO_URL, self.host)
266 return None
267
268 logging.info('Got job repo url from host attributes: %s',
269 job_repo_url)
270 return job_repo_url
271
272
Dan Shib669cbd2013-09-13 11:17:17 -0700273 def install(self, host=None, autodir=None, use_packaging=True):
Allen Lid5abdab2017-02-07 16:03:43 -0800274 """Install autotest. If |host| is not None, stores it in |self.host|.
275
276 @param host A Host instance on which autotest will be installed
277 @param autodir Location on the remote host to install to
278 @param use_packaging Enable install modes that use the packaging system.
279
280 """
281 if host:
282 self.host = host
Dan Shib669cbd2013-09-13 11:17:17 -0700283 self._install(host=host, autodir=autodir, use_packaging=use_packaging)
jadmanski54f90af2008-10-10 16:20:55 +0000284
285
mblighb8aa75b2009-09-18 16:50:37 +0000286 def install_full_client(self, host=None, autodir=None):
287 self._install(host=host, autodir=autodir, use_autoserv=False,
288 use_packaging=False)
289
290
mblighbccad482009-08-24 22:08:31 +0000291 def install_no_autoserv(self, host=None, autodir=None):
mblighb8aa75b2009-09-18 16:50:37 +0000292 self._install(host=host, autodir=autodir, use_autoserv=False)
mblighbccad482009-08-24 22:08:31 +0000293
294
mblighb8aa75b2009-09-18 16:50:37 +0000295 def _install_using_packaging(self, host, autodir):
Eric Lid656d562011-04-20 11:48:29 -0700296 repos = self.get_fetch_location()
mblighb8aa75b2009-09-18 16:50:37 +0000297 if not repos:
298 raise error.PackageInstallError("No repos to install an "
299 "autotest client from")
300 pkgmgr = packages.PackageManager(autodir, hostname=host.hostname,
301 repo_urls=repos,
302 do_locking=False,
303 run_function=host.run,
304 run_function_dargs=dict(timeout=600))
305 # The packages dir is used to store all the packages that
306 # are fetched on that client. (for the tests,deps etc.
307 # too apart from the client)
308 pkg_dir = os.path.join(autodir, 'packages')
Dan Shi86de0942017-08-24 21:37:24 -0700309 # clean up the autodir except for the packages and result_tools
310 # directory.
311 host.run('cd %s && ls | grep -v "^packages$" | grep -v "^result_tools$"'
Allen Liad719c12017-06-27 23:48:04 +0000312 ' | xargs rm -rf && rm -rf .[!.]*' % autodir)
mblighb8aa75b2009-09-18 16:50:37 +0000313 pkgmgr.install_pkg('autotest', 'client', pkg_dir, autodir,
314 preserve_install_dir=True)
315 self.installed = True
316
317
318 def _install_using_send_file(self, host, autodir):
Chris Sosa3ee5d5c2012-02-23 11:18:41 -0800319 dirs_to_exclude = set(["tests", "site_tests", "deps", "profilers",
320 "packages"])
mblighb8aa75b2009-09-18 16:50:37 +0000321 light_files = [os.path.join(self.source_material, f)
322 for f in os.listdir(self.source_material)
323 if f not in dirs_to_exclude]
324 host.send_file(light_files, autodir, delete_dest=True)
325
326 # create empty dirs for all the stuff we excluded
327 commands = []
328 for path in dirs_to_exclude:
329 abs_path = os.path.join(autodir, path)
330 abs_path = utils.sh_escape(abs_path)
331 commands.append("mkdir -p '%s'" % abs_path)
332 commands.append("touch '%s'/__init__.py" % abs_path)
Allen Liad719c12017-06-27 23:48:04 +0000333 host.run(';'.join(commands))
mblighb8aa75b2009-09-18 16:50:37 +0000334
335
336 def _install(self, host=None, autodir=None, use_autoserv=True,
337 use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000338 """
339 Install autotest. If get() was not called previously, an
340 attempt will be made to install from the autotest svn
341 repository.
mbligh9a3f5e52008-05-28 21:21:43 +0000342
mblighbccad482009-08-24 22:08:31 +0000343 @param host A Host instance on which autotest will be installed
344 @param autodir Location on the remote host to install to
mblighb8aa75b2009-09-18 16:50:37 +0000345 @param use_autoserv Enable install modes that depend on the client
mblighbccad482009-08-24 22:08:31 +0000346 running with the autoserv harness
mblighb8aa75b2009-09-18 16:50:37 +0000347 @param use_packaging Enable install modes that use the packaging system
mbligh9a3f5e52008-05-28 21:21:43 +0000348
mblighbccad482009-08-24 22:08:31 +0000349 @exception AutoservError if a tarball was not specified and
350 the target host does not have svn installed in its path
351 """
jadmanski0afbb632008-06-06 21:10:57 +0000352 if not host:
353 host = self.host
354 if not self.got:
355 self.get()
356 host.wait_up(timeout=30)
357 host.setup()
showardb18134f2009-03-20 20:52:18 +0000358 logging.info("Installing autotest on %s", host.hostname)
mbligh40f122a2007-11-03 23:08:46 +0000359
jadmanski54f90af2008-10-10 16:20:55 +0000360 # set up the autotest directory on the remote machine
361 if not autodir:
showardad812bf2009-10-20 23:49:56 +0000362 autodir = self.get_install_dir(host)
363 logging.info('Using installation dir %s', autodir)
mbligh0562e652008-08-20 20:11:45 +0000364 host.set_autodir(autodir)
Allen Liad719c12017-06-27 23:48:04 +0000365 host.run('mkdir -p %s' % utils.sh_escape(autodir))
mbligh40f122a2007-11-03 23:08:46 +0000366
jadmanski1c3c07b2009-03-03 23:29:36 +0000367 # make sure there are no files in $AUTODIR/results
368 results_path = os.path.join(autodir, 'results')
Allen Liad719c12017-06-27 23:48:04 +0000369 host.run('rm -rf %s/*' % utils.sh_escape(results_path),
370 ignore_status=True)
jadmanski1c3c07b2009-03-03 23:29:36 +0000371
mblighc5ddfd12008-08-04 17:15:00 +0000372 # Fetch the autotest client from the nearest repository
mblighb8aa75b2009-09-18 16:50:37 +0000373 if use_packaging:
374 try:
375 self._install_using_packaging(host, autodir)
Dan Shib669cbd2013-09-13 11:17:17 -0700376 logging.info("Installation of autotest completed using the "
377 "packaging system.")
mblighb8aa75b2009-09-18 16:50:37 +0000378 return
Eric Li6f27d4f2010-09-29 10:55:17 -0700379 except (error.PackageInstallError, error.AutoservRunError,
380 global_config.ConfigError), e:
mblighb8aa75b2009-09-18 16:50:37 +0000381 logging.info("Could not install autotest using the packaging "
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700382 "system: %s. Trying other methods", e)
Dan Shi1889ca12015-04-15 09:36:06 -0700383 else:
384 # Delete the package checksum file to force dut updating local
385 # packages.
386 command = ('rm -f "%s"' %
Allen Libf0c4412017-02-03 16:49:53 -0800387 (os.path.join(autodir, packages.CHECKSUM_FILE)))
Allen Liad719c12017-06-27 23:48:04 +0000388 host.run(command)
mblighc5ddfd12008-08-04 17:15:00 +0000389
jadmanski0afbb632008-06-06 21:10:57 +0000390 # try to install from file or directory
391 if self.source_material:
jadmanski69bdaac2010-07-28 16:27:20 +0000392 c = global_config.global_config
393 supports_autoserv_packaging = c.get_config_value(
394 "PACKAGES", "serve_packages_from_autoserv", type=bool)
395 # Copy autotest recursively
396 if supports_autoserv_packaging and use_autoserv:
397 self._install_using_send_file(host, autodir)
jadmanski0afbb632008-06-06 21:10:57 +0000398 else:
jadmanski69bdaac2010-07-28 16:27:20 +0000399 host.send_file(self.source_material, autodir, delete_dest=True)
Dan Shib669cbd2013-09-13 11:17:17 -0700400 logging.info("Installation of autotest completed from %s",
401 self.source_material)
jadmanski0afbb632008-06-06 21:10:57 +0000402 self.installed = True
Allen Li79623692017-08-17 16:27:11 -0700403 else:
404 # if that fails try to install using svn
405 if utils.run('which svn').exit_status:
406 raise error.AutoservError(
407 'svn not found on target machine: %s' %
408 host.hostname)
409 try:
410 host.run('svn checkout %s %s' % (AUTOTEST_SVN, autodir))
411 except error.AutoservRunError, e:
412 host.run('svn checkout %s %s' % (AUTOTEST_HTTP, autodir))
413 logging.info("Installation of autotest completed using SVN.")
414 self.installed = True
mbligh91334902007-09-28 01:47:59 +0000415
Allen Lid5abdab2017-02-07 16:03:43 -0800416 # TODO(milleral): http://crbug.com/258161
417 # Send over the most recent global_config.ini after installation if one
418 # is available.
419 # This code is a bit duplicated from
420 # _Run._create_client_config_file, but oh well.
421 if self.installed and self.source_material:
Allen Li79623692017-08-17 16:27:11 -0700422 self._send_shadow_config()
423
424 def _send_shadow_config(self):
425 logging.info('Installing updated global_config.ini.')
426 destination = os.path.join(self.host.get_autodir(),
427 'global_config.ini')
428 with tempfile.NamedTemporaryFile() as client_config:
429 config = global_config.global_config
430 client_section = config.get_section_values('CLIENT')
431 client_section.write(client_config)
432 client_config.flush()
433 self.host.send_file(client_config.name, destination)
Allen Lid5abdab2017-02-07 16:03:43 -0800434
mbligh91334902007-09-28 01:47:59 +0000435
jadmanski7c7aff32009-03-25 22:43:07 +0000436 def uninstall(self, host=None):
437 """
438 Uninstall (i.e. delete) autotest. Removes the autotest client install
439 from the specified host.
440
441 @params host a Host instance from which the client will be removed
442 """
443 if not self.installed:
444 return
445 if not host:
446 host = self.host
447 autodir = host.get_autodir()
448 if not autodir:
449 return
450
451 # perform the actual uninstall
Allen Liad719c12017-06-27 23:48:04 +0000452 host.run("rm -rf %s" % utils.sh_escape(autodir), ignore_status=True)
jadmanski7c7aff32009-03-25 22:43:07 +0000453 host.set_autodir(None)
454 self.installed = False
455
456
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700457 def get(self, location=None):
jadmanski0afbb632008-06-06 21:10:57 +0000458 if not location:
459 location = os.path.join(self.serverdir, '../client')
460 location = os.path.abspath(location)
Allen Lid5abdab2017-02-07 16:03:43 -0800461 installable_object.InstallableObject.get(self, location)
jadmanski0afbb632008-06-06 21:10:57 +0000462 self.got = True
mblighdcd57a82007-07-11 23:06:47 +0000463
464
mblighe7d9c602009-07-02 19:02:33 +0000465 def run(self, control_file, results_dir='.', host=None, timeout=None,
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700466 tag=None, parallel_flag=False, background=False,
467 client_disconnect_timeout=None, use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000468 """
469 Run an autotest job on the remote machine.
mbligh9a3f5e52008-05-28 21:21:43 +0000470
mblighe7d9c602009-07-02 19:02:33 +0000471 @param control_file: An open file-like-obj of the control file.
472 @param results_dir: A str path where the results should be stored
473 on the local filesystem.
474 @param host: A Host instance on which the control file should
475 be run.
476 @param timeout: Maximum number of seconds to wait for the run or None.
477 @param tag: Tag name for the client side instance of autotest.
478 @param parallel_flag: Flag set when multiple jobs are run at the
479 same time.
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700480 @param background: Indicates that the client should be launched as
481 a background job; the code calling run will be responsible
482 for monitoring the client and collecting the results.
mblighe7d9c602009-07-02 19:02:33 +0000483 @param client_disconnect_timeout: Seconds to wait for the remote host
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700484 to come back after a reboot. Defaults to the host setting for
485 DEFAULT_REBOOT_TIMEOUT.
mblighe7d9c602009-07-02 19:02:33 +0000486
487 @raises AutotestRunError: If there is a problem executing
488 the control file.
jadmanski0afbb632008-06-06 21:10:57 +0000489 """
Dan Shib669cbd2013-09-13 11:17:17 -0700490 host = self._get_host_and_setup(host, use_packaging=use_packaging)
xixuan02b6fee2017-02-01 18:35:20 -0800491 logging.debug('Autotest job starts on remote host: %s',
492 host.hostname)
jadmanski0afbb632008-06-06 21:10:57 +0000493 results_dir = os.path.abspath(results_dir)
mblighc1cbc992008-05-27 20:01:45 +0000494
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700495 if client_disconnect_timeout is None:
496 client_disconnect_timeout = host.DEFAULT_REBOOT_TIMEOUT
497
jadmanski0afbb632008-06-06 21:10:57 +0000498 if tag:
499 results_dir = os.path.join(results_dir, tag)
mblighc1cbc992008-05-27 20:01:45 +0000500
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700501 atrun = _Run(host, results_dir, tag, parallel_flag, background)
jadmanski6dadd832009-02-05 23:39:27 +0000502 self._do_run(control_file, results_dir, host, atrun, timeout,
Dan Shib669cbd2013-09-13 11:17:17 -0700503 client_disconnect_timeout, use_packaging=use_packaging)
mblighd8b39252008-03-20 21:15:03 +0000504
505
Dan Shib669cbd2013-09-13 11:17:17 -0700506 def _get_host_and_setup(self, host, use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000507 if not host:
508 host = self.host
509 if not self.installed:
Dan Shib669cbd2013-09-13 11:17:17 -0700510 self.install(host, use_packaging=use_packaging)
mbligh91334902007-09-28 01:47:59 +0000511
jadmanski0afbb632008-06-06 21:10:57 +0000512 host.wait_up(timeout=30)
513 return host
mblighd8b39252008-03-20 21:15:03 +0000514
515
jadmanski6dadd832009-02-05 23:39:27 +0000516 def _do_run(self, control_file, results_dir, host, atrun, timeout,
Dan Shib669cbd2013-09-13 11:17:17 -0700517 client_disconnect_timeout, use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000518 try:
519 atrun.verify_machine()
520 except:
showardb18134f2009-03-20 20:52:18 +0000521 logging.error("Verify failed on %s. Reinstalling autotest",
522 host.hostname)
jadmanski0afbb632008-06-06 21:10:57 +0000523 self.install(host)
Fang Dengb9cd83c2015-01-27 10:16:08 -0800524 atrun.verify_machine()
jadmanski0afbb632008-06-06 21:10:57 +0000525 debug = os.path.join(results_dir, 'debug')
526 try:
527 os.makedirs(debug)
mbligh09108442008-10-15 16:27:38 +0000528 except Exception:
jadmanski0afbb632008-06-06 21:10:57 +0000529 pass
mbligh9a3f5e52008-05-28 21:21:43 +0000530
mbligh09108442008-10-15 16:27:38 +0000531 delete_file_list = [atrun.remote_control_file,
532 atrun.remote_control_file + '.state',
533 atrun.manual_control_file,
534 atrun.manual_control_file + '.state']
535 cmd = ';'.join('rm -f ' + control for control in delete_file_list)
Allen Liad719c12017-06-27 23:48:04 +0000536 host.run(cmd, ignore_status=True)
mbligh9a3f5e52008-05-28 21:21:43 +0000537
Dale Curtis386eea72011-09-21 18:43:04 -0700538 tmppath = utils.get(control_file, local_copy=True)
mblighc5ddfd12008-08-04 17:15:00 +0000539
jadmanskicb0e1612009-02-27 18:03:10 +0000540 # build up the initialization prologue for the control file
541 prologue_lines = []
jadmanski23afbec2008-09-17 18:12:07 +0000542
mbligh2f076832010-03-30 17:08:20 +0000543 # Add the additional user arguments
jadmanski808f4b12010-04-09 22:30:31 +0000544 prologue_lines.append("args = %r\n" % self.job.args)
mbligh2f076832010-03-30 17:08:20 +0000545
mbligh09108442008-10-15 16:27:38 +0000546 # If the packaging system is being used, add the repository list.
mblighddc9a402010-01-15 20:33:34 +0000547 repos = None
mblighc5ddfd12008-08-04 17:15:00 +0000548 try:
Dan Shib669cbd2013-09-13 11:17:17 -0700549 if use_packaging:
550 repos = self.get_fetch_location()
551 prologue_lines.append('job.add_repository(%s)\n' % repos)
552 else:
553 logging.debug('use_packaging is set to False, do not add any '
554 'repository.')
mblighc5ddfd12008-08-04 17:15:00 +0000555 except global_config.ConfigError, e:
mblighddc9a402010-01-15 20:33:34 +0000556 # If repos is defined packaging is enabled so log the error
557 if repos:
558 logging.error(e)
mblighc5ddfd12008-08-04 17:15:00 +0000559
jadmanskie2eef7b2009-03-03 23:55:13 +0000560 # on full-size installs, turn on any profilers the server is using
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700561 if not atrun.background:
562 running_profilers = host.job.profilers.add_log.iteritems()
563 for profiler, (args, dargs) in running_profilers:
564 call_args = [repr(profiler)]
565 call_args += [repr(arg) for arg in args]
566 call_args += ["%s=%r" % item for item in dargs.iteritems()]
567 prologue_lines.append("job.profilers.add(%s)\n"
568 % ", ".join(call_args))
jadmanskie2eef7b2009-03-03 23:55:13 +0000569 cfile = "".join(prologue_lines)
570
mbligh09108442008-10-15 16:27:38 +0000571 cfile += open(tmppath).read()
572 open(tmppath, "w").write(cfile)
mblighc5ddfd12008-08-04 17:15:00 +0000573
jadmanskic09fc152008-10-15 17:56:59 +0000574 # Create and copy state file to remote_control_file + '.state'
mblighfc3da5b2010-01-06 18:37:22 +0000575 state_file = host.job.preprocess_client_state()
mblighfbf73ae2009-12-19 05:22:42 +0000576 host.send_file(state_file, atrun.remote_control_file + '.init.state')
jadmanskic09fc152008-10-15 17:56:59 +0000577 os.remove(state_file)
578
mblighc5ddfd12008-08-04 17:15:00 +0000579 # Copy control_file to remote_control_file on the host
jadmanski0afbb632008-06-06 21:10:57 +0000580 host.send_file(tmppath, atrun.remote_control_file)
581 if os.path.abspath(tmppath) != os.path.abspath(control_file):
582 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000583
jadmanski6bb32d72009-03-19 20:25:24 +0000584 atrun.execute_control(
jadmanski6dadd832009-02-05 23:39:27 +0000585 timeout=timeout,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800586 client_disconnect_timeout=client_disconnect_timeout)
jadmanski23afbec2008-09-17 18:12:07 +0000587
588
harpreetf531d072016-04-19 18:37:26 -0700589 @classmethod
590 def _check_client_test_result(cls, host, test_name):
591 """
592 Check result of client test.
593 Autotest will store results in the file name status.
594 We check that second to last line in that file begins with 'END GOOD'
595
596 @raises TestFail: If client test does not pass.
597 """
598 client_result_dir = '%s/results/default' % host.autodir
599 command = 'tail -2 %s/status | head -1' % client_result_dir
Allen Liad719c12017-06-27 23:48:04 +0000600 status = host.run(command).stdout.strip()
harpreetf531d072016-04-19 18:37:26 -0700601 logging.info(status)
602 if status[:8] != 'END GOOD':
603 raise error.TestFail('%s client test did not pass.' % test_name)
604
605
jadmanski0afbb632008-06-06 21:10:57 +0000606 def run_timed_test(self, test_name, results_dir='.', host=None,
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700607 timeout=None, parallel_flag=False, background=False,
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700608 client_disconnect_timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000609 """
610 Assemble a tiny little control file to just run one test,
611 and run it as an autotest client-side test
612 """
613 if not host:
614 host = self.host
615 if not self.installed:
616 self.install(host)
617 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
618 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
619 control = "job.run_test(%s)\n" % cmd
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700620 self.run(control, results_dir, host, timeout=timeout,
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700621 parallel_flag=parallel_flag, background=background,
Andrew Bresticker2da1b762013-01-15 16:11:18 -0800622 client_disconnect_timeout=client_disconnect_timeout)
mbligh0e4613b2007-10-29 16:55:07 +0000623
harpreetf0452062016-04-21 11:22:46 -0700624 if dargs.get('check_client_result', False):
625 self._check_client_test_result(host, test_name)
harpreetf531d072016-04-19 18:37:26 -0700626
mbligh0e4613b2007-10-29 16:55:07 +0000627
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700628 def run_test(self, test_name, results_dir='.', host=None,
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700629 parallel_flag=False, background=False,
Andrew Bresticker2da1b762013-01-15 16:11:18 -0800630 client_disconnect_timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000631 self.run_timed_test(test_name, results_dir, host, timeout=None,
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700632 parallel_flag=parallel_flag, background=background,
Andrew Bresticker2da1b762013-01-15 16:11:18 -0800633 client_disconnect_timeout=client_disconnect_timeout,
jadmanskic98c4702009-01-05 15:50:06 +0000634 *args, **dargs)
mblighd54832b2007-07-25 16:46:56 +0000635
636
Allen Lid5abdab2017-02-07 16:03:43 -0800637 def run_static_method(self, module, method, results_dir='.', host=None,
638 *args):
639 """Runs a non-instance method with |args| from |module| on the client.
640
641 This method runs a static/class/module autotest method on the client.
642 For example:
643 run_static_method("autotest_lib.client.cros.cros_ui", "reboot")
644
645 Will run autotest_lib.client.cros.cros_ui.reboot() on the client.
646
647 @param module: module name as you would refer to it when importing in a
648 control file. e.g. autotest_lib.client.common_lib.module_name.
649 @param method: the method you want to call.
650 @param results_dir: A str path where the results should be stored
651 on the local filesystem.
652 @param host: A Host instance on which the control file should
653 be run.
654 @param args: args to pass to the method.
655 """
656 control = "\n".join(["import %s" % module,
657 "%s.%s(%s)\n" % (module, method,
658 ','.join(map(repr, args)))])
659 self.run(control, results_dir=results_dir, host=host)
660
661
Allen Li24381f32017-02-07 15:43:37 -0800662class _Run(object):
jadmanski0afbb632008-06-06 21:10:57 +0000663 """
664 Represents a run of autotest control file. This class maintains
665 all the state necessary as an autotest control file is executed.
mblighdcd57a82007-07-11 23:06:47 +0000666
jadmanski0afbb632008-06-06 21:10:57 +0000667 It is not intended to be used directly, rather control files
668 should be run using the run method in Autotest.
669 """
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700670 def __init__(self, host, results_dir, tag, parallel_flag, background):
jadmanski0afbb632008-06-06 21:10:57 +0000671 self.host = host
672 self.results_dir = results_dir
673 self.env = host.env
674 self.tag = tag
675 self.parallel_flag = parallel_flag
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700676 self.background = background
showardad812bf2009-10-20 23:49:56 +0000677 self.autodir = Autotest.get_installed_autodir(self.host)
mbligh78bf5352008-07-11 20:27:36 +0000678 control = os.path.join(self.autodir, 'control')
jadmanski0afbb632008-06-06 21:10:57 +0000679 if tag:
mbligh78bf5352008-07-11 20:27:36 +0000680 control += '.' + tag
681 self.manual_control_file = control
682 self.remote_control_file = control + '.autoserv'
lmr6d08b3c2009-11-18 19:26:38 +0000683 self.config_file = os.path.join(self.autodir, 'global_config.ini')
mblighdc735a22007-08-02 16:54:37 +0000684
685
jadmanski0afbb632008-06-06 21:10:57 +0000686 def verify_machine(self):
687 binary = os.path.join(self.autodir, 'bin/autotest')
688 try:
Allen Liad719c12017-06-27 23:48:04 +0000689 self.host.run('ls %s > /dev/null 2>&1' % binary)
jadmanski0afbb632008-06-06 21:10:57 +0000690 except:
lmrd6d27ed2009-12-08 19:58:33 +0000691 raise error.AutoservInstallError(
692 "Autotest does not appear to be installed")
mblighdc735a22007-08-02 16:54:37 +0000693
jadmanski0afbb632008-06-06 21:10:57 +0000694 if not self.parallel_flag:
695 tmpdir = os.path.join(self.autodir, 'tmp')
696 download = os.path.join(self.autodir, 'tests/download')
Allen Liad719c12017-06-27 23:48:04 +0000697 self.host.run('umount %s' % tmpdir, ignore_status=True)
698 self.host.run('umount %s' % download, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000699
jadmanski6dadd832009-02-05 23:39:27 +0000700
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +0000701 def get_base_cmd_args(self, section):
showard234b2962009-07-28 20:02:30 +0000702 args = ['--verbose']
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +0000703 if section > 0:
704 args.append('-c')
jadmanski0afbb632008-06-06 21:10:57 +0000705 if self.tag:
jadmanski6dadd832009-02-05 23:39:27 +0000706 args.append('-t %s' % self.tag)
jadmanski0afbb632008-06-06 21:10:57 +0000707 if self.host.job.use_external_logging():
jadmanski6dadd832009-02-05 23:39:27 +0000708 args.append('-l')
mblighce955fc2009-08-24 21:59:02 +0000709 if self.host.hostname:
710 args.append('--hostname=%s' % self.host.hostname)
mbligh0d0f67d2009-11-06 03:15:03 +0000711 args.append('--user=%s' % self.host.job.user)
mblighce955fc2009-08-24 21:59:02 +0000712
jadmanski6dadd832009-02-05 23:39:27 +0000713 args.append(self.remote_control_file)
714 return args
715
716
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700717 def get_background_cmd(self, section):
718 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotest_client')]
719 cmd += self.get_base_cmd_args(section)
720 cmd += ['>/dev/null', '2>/dev/null', '&']
721 return ' '.join(cmd)
722
723
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +0000724 def get_daemon_cmd(self, section, monitor_dir):
jadmanski6dadd832009-02-05 23:39:27 +0000725 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotestd'),
726 monitor_dir, '-H autoserv']
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +0000727 cmd += self.get_base_cmd_args(section)
jadmanski69bdaac2010-07-28 16:27:20 +0000728 cmd += ['>/dev/null', '2>/dev/null', '&']
jadmanski6dadd832009-02-05 23:39:27 +0000729 return ' '.join(cmd)
730
731
732 def get_monitor_cmd(self, monitor_dir, stdout_read, stderr_read):
733 cmd = [os.path.join(self.autodir, 'bin', 'autotestd_monitor'),
734 monitor_dir, str(stdout_read), str(stderr_read)]
jadmanski0afbb632008-06-06 21:10:57 +0000735 return ' '.join(cmd)
mblighadf2aab2007-11-29 18:16:43 +0000736
mblighd8b39252008-03-20 21:15:03 +0000737
jadmanski4d03cf62010-03-04 18:32:28 +0000738 def get_client_log(self):
739 """Find what the "next" client.* prefix should be
740
741 @returns A string of the form client.INTEGER that should be prefixed
742 to all client debug log files.
743 """
744 max_digit = -1
745 debug_dir = os.path.join(self.results_dir, 'debug')
746 client_logs = glob.glob(os.path.join(debug_dir, 'client.*.*'))
747 for log in client_logs:
748 _, number, _ = log.split('.', 2)
749 if number.isdigit():
750 max_digit = max(max_digit, int(number))
751 return 'client.%d' % (max_digit + 1)
752
753
754 def copy_client_config_file(self, client_log_prefix=None):
755 """
756 Create and copy the client config file based on the server config.
757
758 @param client_log_prefix: Optional prefix to prepend to log files.
759 """
760 client_config_file = self._create_client_config_file(client_log_prefix)
761 self.host.send_file(client_config_file, self.config_file)
762 os.remove(client_config_file)
763
764
765 def _create_client_config_file(self, client_log_prefix=None):
766 """
767 Create a temporary file with the [CLIENT] section configuration values
768 taken from the server global_config.ini.
769
770 @param client_log_prefix: Optional prefix to prepend to log files.
771
772 @return: Path of the temporary file generated.
773 """
774 config = global_config.global_config.get_section_values('CLIENT')
775 if client_log_prefix:
776 config.set('CLIENT', 'default_logging_name', client_log_prefix)
777 return self._create_aux_file(config.write)
778
779
780 def _create_aux_file(self, func, *args):
781 """
782 Creates a temporary file and writes content to it according to a
783 content creation function. The file object is appended to *args, which
784 is then passed to the content creation function
785
786 @param func: Function that will be used to write content to the
787 temporary file.
788 @param *args: List of parameters that func takes.
789 @return: Path to the temporary file that was created.
790 """
791 fd, path = tempfile.mkstemp(dir=self.host.job.tmpdir)
792 aux_file = os.fdopen(fd, "w")
793 try:
794 list_args = list(args)
795 list_args.append(aux_file)
796 func(*list_args)
797 finally:
798 aux_file.close()
799 return path
mblighd8b39252008-03-20 21:15:03 +0000800
801
jadmanskib264ed02009-01-12 23:54:27 +0000802 @staticmethod
803 def is_client_job_finished(last_line):
804 return bool(re.match(r'^END .*\t----\t----\t.*$', last_line))
805
806
807 @staticmethod
808 def is_client_job_rebooting(last_line):
809 return bool(re.match(r'^\t*GOOD\t----\treboot\.start.*$', last_line))
810
811
beeps607428b2013-03-25 16:43:20 -0700812 def _diagnose_dut(self, old_boot_id=None):
813 """
814 Run diagnostic checks on a DUT.
815
816 1. ping: A dead host will not respond to pings.
817 2. ssh (happens with 3.): DUT hangs usually fail in authentication
818 but respond to pings.
819 3. Check if a reboot occured: A healthy but unexpected reboot leaves the
820 host running with a new boot id.
821
822 This method will always raise an exception from the AutotestFailure
823 family and should only get called when the reason for a test failing
824 is ambiguous.
825
826 @raises AutotestDeviceNotPingable: If the DUT doesn't respond to ping.
827 @raises AutotestDeviceNotSSHable: If we cannot SSH into the DUT.
828 @raises AutotestDeviceRebooted: If the boot id changed.
829 @raises AutotestAbort: If none of the above exceptions were raised.
830 Since we have no recourse we must abort at this stage.
831 """
832 msg = 'Autotest client terminated unexpectedly: '
833 if utils.ping(self.host.hostname, tries=1, deadline=1) != 0:
834 msg += 'DUT is no longer pingable, it may have rebooted or hung.\n'
835 raise AutotestDeviceNotPingable(msg)
836
837 if old_boot_id:
838 try:
839 new_boot_id = self.host.get_boot_id(timeout=60)
840 except Exception as e:
841 msg += ('DUT is pingable but not SSHable, it most likely'
beeps14768812013-09-25 12:58:45 -0700842 ' sporadically rebooted during testing. %s\n' % str(e))
beeps607428b2013-03-25 16:43:20 -0700843 raise AutotestDeviceNotSSHable(msg)
844 else:
845 if new_boot_id != old_boot_id:
846 msg += 'DUT rebooted during the test run.\n'
847 raise AutotestDeviceRebooted(msg)
848
849 msg += ('DUT is pingable, SSHable and did NOT restart '
850 'un-expectedly. We probably lost connectivity during the '
851 'test.')
852 else:
853 msg += ('DUT is pingable, could not determine if an un-expected '
854 'reboot occured during the test.')
855
856 raise AutotestAbort(msg)
857
858
beeps07f53b92013-01-08 12:55:10 -0800859 def log_unexpected_abort(self, stderr_redirector, old_boot_id=None):
860 """
beeps607428b2013-03-25 16:43:20 -0700861 Logs that something unexpected happened, then tries to diagnose the
862 failure. The purpose of this function is only to close out the status
863 log with the appropriate error message, not to critically terminate
864 the program.
beeps07f53b92013-01-08 12:55:10 -0800865
866 @param stderr_redirector: log stream.
867 @param old_boot_id: boot id used to infer if a reboot occured.
868 """
jadmanskia61edad2009-05-21 22:17:49 +0000869 stderr_redirector.flush_all_buffers()
beeps607428b2013-03-25 16:43:20 -0700870 try:
871 self._diagnose_dut(old_boot_id)
872 except AutotestFailure as e:
873 self.host.job.record('END ABORT', None, None, str(e))
jadmanskib264ed02009-01-12 23:54:27 +0000874
875
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700876 def _execute_in_background(self, section, timeout):
877 full_cmd = self.get_background_cmd(section)
878 devnull = open(os.devnull, "w")
879
880 self.copy_client_config_file(self.get_client_log())
881
882 self.host.job.push_execution_context(self.results_dir)
883 try:
884 result = self.host.run(full_cmd, ignore_status=True,
885 timeout=timeout,
886 stdout_tee=devnull,
887 stderr_tee=devnull)
888 finally:
889 self.host.job.pop_execution_context()
890
891 return result
892
893
jadmanski6dadd832009-02-05 23:39:27 +0000894 @staticmethod
895 def _strip_stderr_prologue(stderr):
896 """Strips the 'standard' prologue that get pre-pended to every
897 remote command and returns the text that was actually written to
898 stderr by the remote command."""
899 stderr_lines = stderr.split("\n")[1:]
900 if not stderr_lines:
901 return ""
902 elif stderr_lines[0].startswith("NOTE: autotestd_monitor"):
903 del stderr_lines[0]
904 return "\n".join(stderr_lines)
905
906
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +0000907 def _execute_daemon(self, section, timeout, stderr_redirector,
jadmanski6dadd832009-02-05 23:39:27 +0000908 client_disconnect_timeout):
909 monitor_dir = self.host.get_tmp_dir()
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +0000910 daemon_cmd = self.get_daemon_cmd(section, monitor_dir)
jadmanski4d03cf62010-03-04 18:32:28 +0000911
912 # grab the location for the server-side client log file
913 client_log_prefix = self.get_client_log()
914 client_log_path = os.path.join(self.results_dir, 'debug',
915 client_log_prefix + '.log')
916 client_log = open(client_log_path, 'w', 0)
917 self.copy_client_config_file(client_log_prefix)
jadmanski6dadd832009-02-05 23:39:27 +0000918
919 stdout_read = stderr_read = 0
mbligh0d0f67d2009-11-06 03:15:03 +0000920 self.host.job.push_execution_context(self.results_dir)
jadmanski6dadd832009-02-05 23:39:27 +0000921 try:
Allen Liad719c12017-06-27 23:48:04 +0000922 self.host.run(daemon_cmd, ignore_status=True, timeout=timeout)
jadmanski91d56a92009-04-01 15:20:40 +0000923 disconnect_warnings = []
jadmanski6dadd832009-02-05 23:39:27 +0000924 while True:
925 monitor_cmd = self.get_monitor_cmd(monitor_dir, stdout_read,
926 stderr_read)
927 try:
Allen Liad719c12017-06-27 23:48:04 +0000928 result = self.host.run(monitor_cmd, ignore_status=True,
929 timeout=timeout,
930 stdout_tee=client_log,
931 stderr_tee=stderr_redirector)
jadmanski6dadd832009-02-05 23:39:27 +0000932 except error.AutoservRunError, e:
933 result = e.result_obj
934 result.exit_status = None
jadmanski91d56a92009-04-01 15:20:40 +0000935 disconnect_warnings.append(e.description)
936
jadmanski6dadd832009-02-05 23:39:27 +0000937 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000938 "Autotest client was disconnected: %s" % e.description,
939 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000940 except error.AutoservSSHTimeout:
941 result = utils.CmdResult(monitor_cmd, "", "", None, 0)
942 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000943 "Attempt to connect to Autotest client timed out",
944 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000945
946 stdout_read += len(result.stdout)
947 stderr_read += len(self._strip_stderr_prologue(result.stderr))
948
949 if result.exit_status is not None:
Simran Basibca10a62013-01-24 15:52:35 -0800950 # TODO (crosbug.com/38224)- sbasi: Remove extra logging.
951 logging.debug('Result exit status is %d.',
952 result.exit_status)
jadmanski6dadd832009-02-05 23:39:27 +0000953 return result
954 elif not self.host.wait_up(client_disconnect_timeout):
955 raise error.AutoservSSHTimeout(
956 "client was disconnected, reconnect timed out")
957 finally:
jadmanski4d03cf62010-03-04 18:32:28 +0000958 client_log.close()
mbligh0d0f67d2009-11-06 03:15:03 +0000959 self.host.job.pop_execution_context()
jadmanski6dadd832009-02-05 23:39:27 +0000960
961
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +0000962 def execute_section(self, section, timeout, stderr_redirector,
963 client_disconnect_timeout):
964 # TODO(crbug.com/684311) The claim is that section is never more than 0
965 # in pratice. After validating for a week or so, delete all support of
966 # multiple sections.
967 metrics.Counter('chromeos/autotest/autotest/sections').increment(
968 fields={'is_first_section': (section == 0)})
969 logging.info("Executing %s/bin/autotest %s/control phase %d",
970 self.autodir, self.autodir, section)
jadmanski6dadd832009-02-05 23:39:27 +0000971
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700972 if self.background:
973 result = self._execute_in_background(section, timeout)
974 else:
975 result = self._execute_daemon(section, timeout, stderr_redirector,
976 client_disconnect_timeout)
jadmanski6dadd832009-02-05 23:39:27 +0000977
978 last_line = stderr_redirector.last_line
mbligh2bf2db62007-11-27 00:53:18 +0000979
jadmanskib264ed02009-01-12 23:54:27 +0000980 # check if we failed hard enough to warrant an exception
jadmanski0afbb632008-06-06 21:10:57 +0000981 if result.exit_status == 1:
jadmanskib264ed02009-01-12 23:54:27 +0000982 err = error.AutotestRunError("client job was aborted")
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700983 elif not self.background and not result.stderr:
jadmanskib264ed02009-01-12 23:54:27 +0000984 err = error.AutotestRunError(
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +0000985 "execute_section %s failed to return anything\n"
986 "stdout:%s\n" % (section, result.stdout))
jadmanskib264ed02009-01-12 23:54:27 +0000987 else:
988 err = None
mbligh0e4613b2007-10-29 16:55:07 +0000989
jadmanskib264ed02009-01-12 23:54:27 +0000990 # log something if the client failed AND never finished logging
Dale Curtis9285ddf2011-01-05 11:47:24 -0800991 if err and not self.is_client_job_finished(last_line):
jadmanskia61edad2009-05-21 22:17:49 +0000992 self.log_unexpected_abort(stderr_redirector)
jadmanskib264ed02009-01-12 23:54:27 +0000993
994 if err:
995 raise err
996 else:
997 return stderr_redirector.last_line
jadmanski4600e342008-10-29 22:54:00 +0000998
999
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +00001000 def _wait_for_reboot(self, old_boot_id):
1001 logging.info("Client is rebooting")
1002 logging.info("Waiting for client to halt")
1003 if not self.host.wait_down(self.host.WAIT_DOWN_REBOOT_TIMEOUT,
1004 old_boot_id=old_boot_id):
1005 err = "%s failed to shutdown after %d"
1006 err %= (self.host.hostname, self.host.WAIT_DOWN_REBOOT_TIMEOUT)
1007 raise error.AutotestRunError(err)
1008 logging.info("Client down, waiting for restart")
1009 if not self.host.wait_up(self.host.DEFAULT_REBOOT_TIMEOUT):
1010 # since reboot failed
1011 # hardreset the machine once if possible
1012 # before failing this control file
1013 warning = "%s did not come back up, hard resetting"
1014 warning %= self.host.hostname
1015 logging.warning(warning)
1016 try:
1017 self.host.hardreset(wait=False)
1018 except (AttributeError, error.AutoservUnsupportedError):
1019 warning = "Hard reset unsupported on %s"
1020 warning %= self.host.hostname
1021 logging.warning(warning)
1022 raise error.AutotestRunError("%s failed to boot after %ds" %
1023 (self.host.hostname,
1024 self.host.DEFAULT_REBOOT_TIMEOUT))
1025 self.host.reboot_followup()
1026
1027
Dale Curtis9285ddf2011-01-05 11:47:24 -08001028 def execute_control(self, timeout=None, client_disconnect_timeout=None):
Prathmesh Prabhubeaee902017-07-26 11:58:44 -07001029 if not self.background:
1030 collector = log_collector(self.host, self.tag, self.results_dir)
1031 hostname = self.host.hostname
1032 remote_results = collector.client_results_dir
1033 local_results = collector.server_results_dir
1034 self.host.job.add_client_log(hostname, remote_results,
1035 local_results)
1036 job_record_context = self.host.job.get_record_context()
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +00001037
1038 section = 0
1039 start_time = time.time()
1040
jadmanski043e1132008-11-19 17:10:32 +00001041 logger = client_logger(self.host, self.tag, self.results_dir)
jadmanski4600e342008-10-29 22:54:00 +00001042 try:
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +00001043 while not timeout or time.time() < start_time + timeout:
1044 if timeout:
1045 section_timeout = start_time + timeout - time.time()
1046 else:
1047 section_timeout = None
1048 boot_id = self.host.get_boot_id()
1049 last = self.execute_section(section, section_timeout,
1050 logger, client_disconnect_timeout)
Prathmesh Prabhubeaee902017-07-26 11:58:44 -07001051 if self.background:
1052 return
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +00001053 section += 1
1054 if self.is_client_job_finished(last):
1055 logging.info("Client complete")
1056 return
1057 elif self.is_client_job_rebooting(last):
1058 try:
1059 self._wait_for_reboot(boot_id)
1060 except error.AutotestRunError, e:
1061 self.host.job.record("ABORT", None, "reboot", str(e))
1062 self.host.job.record("END ABORT", None, None, str(e))
1063 raise
1064 continue
jadmanski4600e342008-10-29 22:54:00 +00001065
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +00001066 # If a test fails without probable cause we try to bucket it's
1067 # failure into one of 2 categories. If we can determine the
1068 # current state of the device and it is suspicious, we close the
1069 # status lines indicating a failure. If we either cannot
1070 # determine the state of the device, or it appears totally
1071 # healthy, we give up and abort.
1072 try:
1073 self._diagnose_dut(boot_id)
1074 except AutotestDeviceError as e:
1075 # The status lines of the test are pretty much tailed to
1076 # our log, with indentation, from the client job on the DUT.
1077 # So if the DUT goes down unexpectedly we'll end up with a
1078 # malformed status log unless we manually unwind the status
1079 # stack. Ideally we would want to write a nice wrapper like
1080 # server_job methods run_reboot, run_group but they expect
1081 # reboots and we don't.
1082 self.host.job.record('FAIL', None, None, str(e))
1083 self.host.job.record('END FAIL', None, None)
1084 self.host.job.record('END GOOD', None, None)
1085 self.host.job.failed_with_device_error = True
1086 return
1087 except AutotestAbort as e:
1088 self.host.job.record('ABORT', None, None, str(e))
1089 self.host.job.record('END ABORT', None, None)
jadmanski4600e342008-10-29 22:54:00 +00001090
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +00001091 # give the client machine a chance to recover from a crash
1092 self.host.wait_up(
1093 self.host.HOURS_TO_WAIT_FOR_RECOVERY * 3600)
1094 logging.debug('Unexpected final status message from '
1095 'client %s: %s', self.host.hostname, last)
1096 # The line 'last' may have sensitive phrases, like
1097 # 'END GOOD', which breaks the tko parser. So the error
1098 # message will exclude it, since it will be recorded to
1099 # status.log.
1100 msg = ("Aborting - unexpected final status message from "
1101 "client on %s\n") % self.host.hostname
1102 raise error.AutotestRunError(msg)
jadmanski4600e342008-10-29 22:54:00 +00001103 finally:
xixuan02b6fee2017-02-01 18:35:20 -08001104 logging.debug('Autotest job finishes running. Below is the '
1105 'post-processing operations.')
jadmanski043e1132008-11-19 17:10:32 +00001106 logger.close()
Prathmesh Prabhubeaee902017-07-26 11:58:44 -07001107 if not self.background:
1108 collector.collect_client_job_results()
1109 collector.remove_redundant_client_logs()
1110 state_file = os.path.basename(self.remote_control_file
1111 + '.state')
1112 state_path = os.path.join(self.results_dir, state_file)
1113 self.host.job.postprocess_client_state(state_path)
1114 self.host.job.remove_client_log(hostname, remote_results,
1115 local_results)
1116 job_record_context.restore()
mblighdcd57a82007-07-11 23:06:47 +00001117
xixuan02b6fee2017-02-01 18:35:20 -08001118 logging.debug('Autotest job finishes.')
1119
jadmanski0afbb632008-06-06 21:10:57 +00001120 # should only get here if we timed out
1121 assert timeout
1122 raise error.AutotestTimeoutError()
mbligh0e4613b2007-10-29 16:55:07 +00001123
mblighdcd57a82007-07-11 23:06:47 +00001124
jadmanski043e1132008-11-19 17:10:32 +00001125class log_collector(object):
1126 def __init__(self, host, client_tag, results_dir):
1127 self.host = host
1128 if not client_tag:
1129 client_tag = "default"
1130 self.client_results_dir = os.path.join(host.get_autodir(), "results",
1131 client_tag)
1132 self.server_results_dir = results_dir
1133
1134
1135 def collect_client_job_results(self):
1136 """ A method that collects all the current results of a running
1137 client job into the results dir. By default does nothing as no
1138 client job is running, but when running a client job you can override
1139 this with something that will actually do something. """
jadmanski043e1132008-11-19 17:10:32 +00001140 # make an effort to wait for the machine to come up
1141 try:
1142 self.host.wait_up(timeout=30)
1143 except error.AutoservError:
1144 # don't worry about any errors, we'll try and
1145 # get the results anyway
1146 pass
1147
jadmanski043e1132008-11-19 17:10:32 +00001148 # Copy all dirs in default to results_dir
1149 try:
Dan Shi010c0bc2017-06-21 17:02:51 -07001150 # Build test result directory summary
1151 result_tools_runner.run_on_client(
Dan Shic958a3d2017-07-06 14:43:18 -07001152 self.host, self.client_results_dir)
Dan Shi9f879fb2017-05-26 15:44:04 -07001153
1154 with metrics.SecondsTimer(
Prathmesh Prabhu7cc11532016-11-23 17:51:08 -08001155 'chromeos/autotest/job/log_collection_duration',
1156 fields={'dut_host_name': self.host.hostname}):
1157 self.host.get_file(
1158 self.client_results_dir + '/',
1159 self.server_results_dir,
1160 preserve_symlinks=True)
jadmanski043e1132008-11-19 17:10:32 +00001161 except Exception:
1162 # well, don't stop running just because we couldn't get logs
showardb18134f2009-03-20 20:52:18 +00001163 e_msg = "Unexpected error copying test result logs, continuing ..."
1164 logging.error(e_msg)
jadmanski043e1132008-11-19 17:10:32 +00001165 traceback.print_exc(file=sys.stdout)
1166
1167
jadmanski4d03cf62010-03-04 18:32:28 +00001168 def remove_redundant_client_logs(self):
1169 """Remove client.*.log files in favour of client.*.DEBUG files."""
1170 debug_dir = os.path.join(self.server_results_dir, 'debug')
1171 debug_files = [f for f in os.listdir(debug_dir)
1172 if re.search(r'^client\.\d+\.DEBUG$', f)]
1173 for debug_file in debug_files:
1174 log_file = debug_file.replace('DEBUG', 'log')
1175 log_file = os.path.join(debug_dir, log_file)
1176 if os.path.exists(log_file):
1177 os.remove(log_file)
1178
1179
jadmanski043e1132008-11-19 17:10:32 +00001180# a file-like object for catching stderr from an autotest client and
1181# extracting status logs from it
Allen Li944ac462017-02-07 15:57:20 -08001182class client_logger(object):
jadmanski043e1132008-11-19 17:10:32 +00001183 """Partial file object to write to both stdout and
1184 the status log file. We only implement those methods
1185 utils.run() actually calls.
jadmanski043e1132008-11-19 17:10:32 +00001186 """
1187 status_parser = re.compile(r"^AUTOTEST_STATUS:([^:]*):(.*)$")
1188 test_complete_parser = re.compile(r"^AUTOTEST_TEST_COMPLETE:(.*)$")
jadmanskib1a51132009-08-07 16:45:50 +00001189 fetch_package_parser = re.compile(
1190 r"^AUTOTEST_FETCH_PACKAGE:([^:]*):([^:]*):(.*)$")
jadmanski043e1132008-11-19 17:10:32 +00001191 extract_indent = re.compile(r"^(\t*).*$")
jadmanskiefe4ebf2009-05-21 22:12:30 +00001192 extract_timestamp = re.compile(r".*\ttimestamp=(\d+)\t.*$")
jadmanski043e1132008-11-19 17:10:32 +00001193
1194 def __init__(self, host, tag, server_results_dir):
1195 self.host = host
1196 self.job = host.job
1197 self.log_collector = log_collector(host, tag, server_results_dir)
1198 self.leftover = ""
1199 self.last_line = ""
1200 self.logs = {}
jadmanskiefe4ebf2009-05-21 22:12:30 +00001201
1202
jadmanski043e1132008-11-19 17:10:32 +00001203 def _process_log_dict(self, log_dict):
1204 log_list = log_dict.pop("logs", [])
1205 for key in sorted(log_dict.iterkeys()):
1206 log_list += self._process_log_dict(log_dict.pop(key))
1207 return log_list
1208
1209
1210 def _process_logs(self):
1211 """Go through the accumulated logs in self.log and print them
1212 out to stdout and the status log. Note that this processes
1213 logs in an ordering where:
1214
1215 1) logs to different tags are never interleaved
1216 2) logs to x.y come before logs to x.y.z for all z
1217 3) logs to x.y come before x.z whenever y < z
1218
1219 Note that this will in general not be the same as the
1220 chronological ordering of the logs. However, if a chronological
1221 ordering is desired that one can be reconstructed from the
1222 status log by looking at timestamp lines."""
1223 log_list = self._process_log_dict(self.logs)
jadmanski2a89dac2010-06-11 14:32:58 +00001224 for entry in log_list:
1225 self.job.record_entry(entry, log_in_subdir=False)
jadmanski043e1132008-11-19 17:10:32 +00001226 if log_list:
jadmanski2a89dac2010-06-11 14:32:58 +00001227 self.last_line = log_list[-1].render()
jadmanski043e1132008-11-19 17:10:32 +00001228
1229
1230 def _process_quoted_line(self, tag, line):
1231 """Process a line quoted with an AUTOTEST_STATUS flag. If the
1232 tag is blank then we want to push out all the data we've been
1233 building up in self.logs, and then the newest line. If the
1234 tag is not blank, then push the line into the logs for handling
1235 later."""
jadmanski2a89dac2010-06-11 14:32:58 +00001236 entry = base_job.status_log_entry.parse(line)
1237 if entry is None:
1238 return # the line contains no status lines
jadmanski043e1132008-11-19 17:10:32 +00001239 if tag == "":
1240 self._process_logs()
jadmanski2a89dac2010-06-11 14:32:58 +00001241 self.job.record_entry(entry, log_in_subdir=False)
jadmanski043e1132008-11-19 17:10:32 +00001242 self.last_line = line
1243 else:
1244 tag_parts = [int(x) for x in tag.split(".")]
1245 log_dict = self.logs
1246 for part in tag_parts:
1247 log_dict = log_dict.setdefault(part, {})
1248 log_list = log_dict.setdefault("logs", [])
jadmanski2a89dac2010-06-11 14:32:58 +00001249 log_list.append(entry)
jadmanski043e1132008-11-19 17:10:32 +00001250
1251
jadmanskif37df842009-02-11 00:03:26 +00001252 def _process_info_line(self, line):
1253 """Check if line is an INFO line, and if it is, interpret any control
1254 messages (e.g. enabling/disabling warnings) that it may contain."""
1255 match = re.search(r"^\t*INFO\t----\t----(.*)\t[^\t]*$", line)
1256 if not match:
1257 return # not an INFO line
1258 for field in match.group(1).split('\t'):
1259 if field.startswith("warnings.enable="):
jadmanski16a7ff72009-04-01 18:19:53 +00001260 func = self.job.warning_manager.enable_warnings
jadmanskif37df842009-02-11 00:03:26 +00001261 elif field.startswith("warnings.disable="):
jadmanski16a7ff72009-04-01 18:19:53 +00001262 func = self.job.warning_manager.disable_warnings
jadmanskif37df842009-02-11 00:03:26 +00001263 else:
1264 continue
1265 warning_type = field.split("=", 1)[1]
jadmanski16a7ff72009-04-01 18:19:53 +00001266 func(warning_type)
jadmanskif37df842009-02-11 00:03:26 +00001267
1268
jadmanski043e1132008-11-19 17:10:32 +00001269 def _process_line(self, line):
Allen Li944ac462017-02-07 15:57:20 -08001270 """Write out a line of data to the appropriate stream.
1271
1272 Returns the package checksum file if it exists.
1273
1274 Status lines sent by autotest will be prepended with
1275 "AUTOTEST_STATUS", and all other lines are ssh error messages.
1276 """
1277 logging.debug(line)
1278 fetch_package_match = self.fetch_package_parser.search(line)
1279 if fetch_package_match:
1280 pkg_name, dest_path, fifo_path = fetch_package_match.groups()
1281 serve_packages = _CONFIG.get_config_value(
1282 "PACKAGES", "serve_packages_from_autoserv", type=bool)
1283 if serve_packages and pkg_name == 'packages.checksum':
1284 try:
1285 checksum_file = os.path.join(
1286 self.job.pkgmgr.pkgmgr_dir, 'packages', pkg_name)
1287 if os.path.exists(checksum_file):
1288 self.host.send_file(checksum_file, dest_path)
1289 except error.AutoservRunError:
1290 msg = "Package checksum file not found, continuing anyway"
1291 logging.exception(msg)
1292
1293 try:
1294 # When fetching a package, the client expects to be
1295 # notified when the fetching is complete. Autotest
1296 # does this pushing a B to a fifo queue to the client.
1297 self.host.run("echo B > %s" % fifo_path)
1298 except error.AutoservRunError:
1299 msg = "Checksum installation failed, continuing anyway"
1300 logging.exception(msg)
1301 finally:
1302 return
1303
jadmanski043e1132008-11-19 17:10:32 +00001304 status_match = self.status_parser.search(line)
1305 test_complete_match = self.test_complete_parser.search(line)
jadmanskib1a51132009-08-07 16:45:50 +00001306 fetch_package_match = self.fetch_package_parser.search(line)
jadmanski043e1132008-11-19 17:10:32 +00001307 if status_match:
1308 tag, line = status_match.groups()
jadmanskif37df842009-02-11 00:03:26 +00001309 self._process_info_line(line)
jadmanski043e1132008-11-19 17:10:32 +00001310 self._process_quoted_line(tag, line)
1311 elif test_complete_match:
jadmanskifcc0d5d2009-02-12 21:52:54 +00001312 self._process_logs()
jadmanski043e1132008-11-19 17:10:32 +00001313 fifo_path, = test_complete_match.groups()
mbligh060c4712009-12-29 02:43:35 +00001314 try:
1315 self.log_collector.collect_client_job_results()
Allen Liad719c12017-06-27 23:48:04 +00001316 self.host.run("echo A > %s" % fifo_path)
mbligh060c4712009-12-29 02:43:35 +00001317 except Exception:
1318 msg = "Post-test log collection failed, continuing anyway"
1319 logging.exception(msg)
jadmanskib1a51132009-08-07 16:45:50 +00001320 elif fetch_package_match:
1321 pkg_name, dest_path, fifo_path = fetch_package_match.groups()
jadmanskiede7e242009-08-10 15:43:33 +00001322 serve_packages = global_config.global_config.get_config_value(
1323 "PACKAGES", "serve_packages_from_autoserv", type=bool)
1324 if serve_packages and pkg_name.endswith(".tar.bz2"):
1325 try:
1326 self._send_tarball(pkg_name, dest_path)
1327 except Exception:
1328 msg = "Package tarball creation failed, continuing anyway"
1329 logging.exception(msg)
mbligh060c4712009-12-29 02:43:35 +00001330 try:
Allen Liad719c12017-06-27 23:48:04 +00001331 self.host.run("echo B > %s" % fifo_path)
mbligh060c4712009-12-29 02:43:35 +00001332 except Exception:
1333 msg = "Package tarball installation failed, continuing anyway"
1334 logging.exception(msg)
jadmanski043e1132008-11-19 17:10:32 +00001335 else:
showardb18134f2009-03-20 20:52:18 +00001336 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +00001337
1338
jadmanskiede7e242009-08-10 15:43:33 +00001339 def _send_tarball(self, pkg_name, remote_dest):
Allen Li944ac462017-02-07 15:57:20 -08001340 """Uses tarballs in package manager by default."""
1341 try:
1342 server_package = os.path.join(self.job.pkgmgr.pkgmgr_dir,
1343 'packages', pkg_name)
1344 if os.path.exists(server_package):
1345 self.host.send_file(server_package, remote_dest)
1346 return
1347
1348 except error.AutoservRunError:
1349 msg = ("Package %s could not be sent from the package cache." %
1350 pkg_name)
1351 logging.exception(msg)
1352
jadmanskiede7e242009-08-10 15:43:33 +00001353 name, pkg_type = self.job.pkgmgr.parse_tarball_name(pkg_name)
1354 src_dirs = []
1355 if pkg_type == 'test':
mbligh1f572e52010-04-01 17:15:53 +00001356 for test_dir in ['site_tests', 'tests']:
1357 src_dir = os.path.join(self.job.clientdir, test_dir, name)
1358 if os.path.exists(src_dir):
1359 src_dirs += [src_dir]
mbligh1f572e52010-04-01 17:15:53 +00001360 break
jadmanskiede7e242009-08-10 15:43:33 +00001361 elif pkg_type == 'profiler':
1362 src_dirs += [os.path.join(self.job.clientdir, 'profilers', name)]
1363 elif pkg_type == 'dep':
1364 src_dirs += [os.path.join(self.job.clientdir, 'deps', name)]
1365 elif pkg_type == 'client':
1366 return # you must already have a client to hit this anyway
1367 else:
1368 return # no other types are supported
1369
1370 # iterate over src_dirs until we find one that exists, then tar it
1371 for src_dir in src_dirs:
1372 if os.path.exists(src_dir):
1373 try:
1374 logging.info('Bundling %s into %s', src_dir, pkg_name)
1375 temp_dir = autotemp.tempdir(unique_id='autoserv-packager',
1376 dir=self.job.tmpdir)
1377 tarball_path = self.job.pkgmgr.tar_package(
mblighbccad482009-08-24 22:08:31 +00001378 pkg_name, src_dir, temp_dir.name, " .")
jadmanskiede7e242009-08-10 15:43:33 +00001379 self.host.send_file(tarball_path, remote_dest)
1380 finally:
1381 temp_dir.clean()
1382 return
1383
1384
jadmanski91d56a92009-04-01 15:20:40 +00001385 def log_warning(self, msg, warning_type):
jadmanski6dadd832009-02-05 23:39:27 +00001386 """Injects a WARN message into the current status logging stream."""
jadmanski91d56a92009-04-01 15:20:40 +00001387 timestamp = int(time.time())
1388 if self.job.warning_manager.is_valid(timestamp, warning_type):
Eric Lid656d562011-04-20 11:48:29 -07001389 self.job.record('WARN', None, None, msg)
jadmanski6dadd832009-02-05 23:39:27 +00001390
jadmanski043e1132008-11-19 17:10:32 +00001391
1392 def write(self, data):
jadmanski2a89dac2010-06-11 14:32:58 +00001393 # now start processing the existing buffer and the new data
jadmanski043e1132008-11-19 17:10:32 +00001394 data = self.leftover + data
mbligh060c4712009-12-29 02:43:35 +00001395 lines = data.split('\n')
1396 processed_lines = 0
1397 try:
1398 # process all the buffered data except the last line
1399 # ignore the last line since we may not have all of it yet
1400 for line in lines[:-1]:
mbligh060c4712009-12-29 02:43:35 +00001401 self._process_line(line)
1402 processed_lines += 1
1403 finally:
1404 # save any unprocessed lines for future processing
1405 self.leftover = '\n'.join(lines[processed_lines:])
jadmanski043e1132008-11-19 17:10:32 +00001406
1407
1408 def flush(self):
1409 sys.stdout.flush()
1410
1411
jadmanskia61edad2009-05-21 22:17:49 +00001412 def flush_all_buffers(self):
jadmanski043e1132008-11-19 17:10:32 +00001413 if self.leftover:
1414 self._process_line(self.leftover)
jadmanskia61edad2009-05-21 22:17:49 +00001415 self.leftover = ""
jadmanski043e1132008-11-19 17:10:32 +00001416 self._process_logs()
1417 self.flush()
1418
1419
jadmanskia61edad2009-05-21 22:17:49 +00001420 def close(self):
1421 self.flush_all_buffers()