blob: ba31b1c041e274b60c172154480f15f69761b6fa [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 Li79623692017-08-17 16:27:11 -070020from autotest_lib.client.common_lib import utils as client_utils
Dan Shi9f879fb2017-05-26 15:44:04 -070021from autotest_lib.server import installable_object
Dan Shi9f879fb2017-05-26 15:44:04 -070022from autotest_lib.server import utils
Allen Lid5abdab2017-02-07 16:03:43 -080023from autotest_lib.server import utils as server_utils
Allen Lid5abdab2017-02-07 16:03:43 -080024from autotest_lib.server.cros.dynamic_suite.constants import JOB_REPO_URL
25
mbligh3c7a1502008-07-24 18:08:47 +000026
Dan Shi5e2efb72017-02-07 11:40:23 -080027try:
28 from chromite.lib import metrics
29except ImportError:
30 metrics = client_utils.metrics_mock
31
32
Dale Curtiscb7bfaf2011-06-07 16:21:57 -070033AUTOTEST_SVN = 'svn://test.kernel.org/autotest/trunk/client'
mblighdcd57a82007-07-11 23:06:47 +000034AUTOTEST_HTTP = 'http://test.kernel.org/svn/autotest/trunk/client'
35
Allen Li944ac462017-02-07 15:57:20 -080036_CONFIG = global_config.global_config
37AUTOSERV_PREBUILD = _CONFIG.get_config_value(
Dan Shic958a3d2017-07-06 14:43:18 -070038 'AUTOSERV', 'enable_server_prebuild', type=bool, default=False)
jadmanski2a89dac2010-06-11 14:32:58 +000039
Kevin Chengc51cc1f2017-11-17 15:49:55 -080040# Match on a line like this:
41# FAIL test_name test_name timestamp=1 localtime=Nov 15 12:43:10 <fail_msg>
42_FAIL_STATUS_RE = re.compile(
Kevin Cheng5684c512017-12-01 11:12:32 -080043 r'\s*FAIL.*localtime=.*\s*.*\s*[0-9]+:[0-9]+:[0-9]+\s*(?P<fail_msg>.*)')
Kevin Chengc51cc1f2017-11-17 15:49:55 -080044
45
showardad812bf2009-10-20 23:49:56 +000046class AutodirNotFoundError(Exception):
47 """No Autotest installation could be found."""
48
49
beeps607428b2013-03-25 16:43:20 -070050class AutotestFailure(Exception):
51 """Gereric exception class for failures during a test run."""
52
53
54class AutotestAbort(AutotestFailure):
55 """
56 AutotestAborts are thrown when the DUT seems fine,
57 and the test doesn't give us an explicit reason for
58 failure; In this case we have no choice but to abort.
59 """
60
61
62class AutotestDeviceError(AutotestFailure):
63 """
64 Exceptions that inherit from AutotestDeviceError
65 are thrown when we can determine the current
66 state of the DUT and conclude that it probably
67 lead to the test failing; these exceptions lead
68 to failures instead of aborts.
69 """
70
71
72class AutotestDeviceNotPingable(AutotestDeviceError):
73 """Error for when a DUT becomes unpingable."""
74
75
76class AutotestDeviceNotSSHable(AutotestDeviceError):
77 """Error for when a DUT is pingable but not SSHable."""
78
79
80class AutotestDeviceRebooted(AutotestDeviceError):
81 """Error for when a DUT rebooted unexpectedly."""
82
83
Allen Lid5abdab2017-02-07 16:03:43 -080084class Autotest(installable_object.InstallableObject):
jadmanski0afbb632008-06-06 21:10:57 +000085 """
86 This class represents the Autotest program.
mblighdcd57a82007-07-11 23:06:47 +000087
jadmanski0afbb632008-06-06 21:10:57 +000088 Autotest is used to run tests automatically and collect the results.
89 It also supports profilers.
mblighdcd57a82007-07-11 23:06:47 +000090
jadmanski0afbb632008-06-06 21:10:57 +000091 Implementation details:
92 This is a leaf class in an abstract class hierarchy, it must
93 implement the unimplemented methods in parent classes.
94 """
mbligh119c12a2007-11-12 22:13:44 +000095
Dale Curtiscb7bfaf2011-06-07 16:21:57 -070096 def __init__(self, host=None):
jadmanski0afbb632008-06-06 21:10:57 +000097 self.host = host
98 self.got = False
99 self.installed = False
100 self.serverdir = utils.get_server_dir()
Allen Lid5abdab2017-02-07 16:03:43 -0800101 super(Autotest, self).__init__()
mblighc8949b82007-07-23 16:33:58 +0000102
mblighdc735a22007-08-02 16:54:37 +0000103
jadmanskif22fea82008-11-26 20:57:07 +0000104 install_in_tmpdir = False
105 @classmethod
106 def set_install_in_tmpdir(cls, flag):
107 """ Sets a flag that controls whether or not Autotest should by
108 default be installed in a "standard" directory (e.g.
109 /home/autotest, /usr/local/autotest) or a temporary directory. """
110 cls.install_in_tmpdir = flag
111
112
showardad812bf2009-10-20 23:49:56 +0000113 @classmethod
114 def get_client_autodir_paths(cls, host):
115 return global_config.global_config.get_config_value(
116 'AUTOSERV', 'client_autodir_paths', type=list)
117
118
119 @classmethod
120 def get_installed_autodir(cls, host):
121 """
122 Find where the Autotest client is installed on the host.
123 @returns an absolute path to an installed Autotest client root.
124 @raises AutodirNotFoundError if no Autotest installation can be found.
125 """
126 autodir = host.get_autodir()
127 if autodir:
128 logging.debug('Using existing host autodir: %s', autodir)
129 return autodir
130
131 for path in Autotest.get_client_autodir_paths(host):
132 try:
133 autotest_binary = os.path.join(path, 'bin', 'autotest')
Allen Liad719c12017-06-27 23:48:04 +0000134 host.run('test -x %s' % utils.sh_escape(autotest_binary))
135 host.run('test -w %s' % utils.sh_escape(path))
showardad812bf2009-10-20 23:49:56 +0000136 logging.debug('Found existing autodir at %s', path)
137 return path
Justin Giorgia4a843d2017-03-07 13:09:48 -0800138 except error.GenericHostRunError:
showardad812bf2009-10-20 23:49:56 +0000139 logging.debug('%s does not exist on %s', autotest_binary,
140 host.hostname)
141 raise AutodirNotFoundError
142
143
144 @classmethod
145 def get_install_dir(cls, host):
146 """
147 Determines the location where autotest should be installed on
jadmanskif22fea82008-11-26 20:57:07 +0000148 host. If self.install_in_tmpdir is set, it will return a unique
showardad812bf2009-10-20 23:49:56 +0000149 temporary directory that autotest can be installed in. Otherwise, looks
150 for an existing installation to use; if none is found, looks for a
151 usable directory in the global config client_autodir_paths.
152 """
jadmanskif22fea82008-11-26 20:57:07 +0000153 try:
lmr9dcf0832009-12-08 21:28:55 +0000154 install_dir = cls.get_installed_autodir(host)
showardad812bf2009-10-20 23:49:56 +0000155 except AutodirNotFoundError:
lmr9dcf0832009-12-08 21:28:55 +0000156 install_dir = cls._find_installable_dir(host)
157
158 if cls.install_in_tmpdir:
159 return host.get_tmp_dir(parent=install_dir)
160 return install_dir
showardad812bf2009-10-20 23:49:56 +0000161
162
163 @classmethod
164 def _find_installable_dir(cls, host):
165 client_autodir_paths = cls.get_client_autodir_paths(host)
166 for path in client_autodir_paths:
167 try:
Allen Liad719c12017-06-27 23:48:04 +0000168 host.run('mkdir -p %s' % utils.sh_escape(path))
169 host.run('test -w %s' % utils.sh_escape(path))
showardad812bf2009-10-20 23:49:56 +0000170 return path
171 except error.AutoservRunError:
172 logging.debug('Failed to create %s', path)
Prathmesh Prabhu7cc11532016-11-23 17:51:08 -0800173 metrics.Counter(
174 'chromeos/autotest/errors/no_autotest_install_path').increment(
175 fields={'dut_host_name': host.hostname})
showardad812bf2009-10-20 23:49:56 +0000176 raise error.AutoservInstallError(
Dale Curtis74a314b2011-06-23 14:55:46 -0700177 'Unable to find a place to install Autotest; tried %s' %
showardad812bf2009-10-20 23:49:56 +0000178 ', '.join(client_autodir_paths))
jadmanskif22fea82008-11-26 20:57:07 +0000179
180
Eric Lid656d562011-04-20 11:48:29 -0700181 def get_fetch_location(self):
Allen Lid5abdab2017-02-07 16:03:43 -0800182 """Generate list of locations where autotest can look for packages.
183
Richard Barnette71854c72018-03-30 14:22:09 -0700184 Hosts are tagged with an attribute containing the URL from which
185 to source packages when running a test on that host.
Allen Lid5abdab2017-02-07 16:03:43 -0800186
187 @returns the list of candidate locations to check for packages.
188 """
Eric Lid656d562011-04-20 11:48:29 -0700189 c = global_config.global_config
190 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list,
191 default=[])
192 repos.reverse()
Allen Lid5abdab2017-02-07 16:03:43 -0800193
Richard Barnette71854c72018-03-30 14:22:09 -0700194 if not server_utils.is_inside_chroot():
195 # Only try to get fetch location from host attribute if the
196 # test is not running inside chroot.
197 #
198 # Look for the repo url via the host attribute. If we are
199 # not running with a full AFE autoserv will fall back to
200 # serving packages itself from whatever source version it is
201 # sync'd to rather than using the proper artifacts for the
202 # build on the host.
Allen Lid5abdab2017-02-07 16:03:43 -0800203 found_repo = self._get_fetch_location_from_host_attribute()
204 if found_repo is not None:
205 # Add our new repo to the end, the package manager will
206 # later reverse the list of repositories resulting in ours
207 # being first
208 repos.append(found_repo)
209
Eric Lid656d562011-04-20 11:48:29 -0700210 return repos
211
212
Allen Lid5abdab2017-02-07 16:03:43 -0800213 def _get_fetch_location_from_host_attribute(self):
214 """Get repo to use for packages from host attribute, if possible.
215
216 Hosts are tagged with an attribute containing the URL
217 from which to source packages when running a test on that host.
218 If self.host is set, attempt to look this attribute in the host info.
219
220 @returns value of the 'job_repo_url' host attribute, if present.
221 """
222 if not self.host:
223 return None
224
225 try:
226 info = self.host.host_info_store.get()
227 except Exception as e:
228 # TODO(pprabhu): We really want to catch host_info.StoreError here,
229 # but we can't import host_info from this module.
230 # - autotest_lib.hosts.host_info pulls in (naturally)
231 # autotest_lib.hosts.__init__
232 # - This pulls in all the host classes ever defined
233 # - That includes abstract_ssh, which depends on autotest
234 logging.warning('Failed to obtain host info: %r', e)
235 logging.warning('Skipping autotest fetch location based on %s',
236 JOB_REPO_URL)
237 return None
238
239 job_repo_url = info.attributes.get(JOB_REPO_URL, '')
240 if not job_repo_url:
241 logging.warning("No %s for %s", JOB_REPO_URL, self.host)
242 return None
243
244 logging.info('Got job repo url from host attributes: %s',
245 job_repo_url)
246 return job_repo_url
247
248
Dan Shib669cbd2013-09-13 11:17:17 -0700249 def install(self, host=None, autodir=None, use_packaging=True):
Allen Lid5abdab2017-02-07 16:03:43 -0800250 """Install autotest. If |host| is not None, stores it in |self.host|.
251
252 @param host A Host instance on which autotest will be installed
253 @param autodir Location on the remote host to install to
254 @param use_packaging Enable install modes that use the packaging system.
255
256 """
257 if host:
258 self.host = host
Dan Shib669cbd2013-09-13 11:17:17 -0700259 self._install(host=host, autodir=autodir, use_packaging=use_packaging)
jadmanski54f90af2008-10-10 16:20:55 +0000260
261
mblighb8aa75b2009-09-18 16:50:37 +0000262 def install_full_client(self, host=None, autodir=None):
263 self._install(host=host, autodir=autodir, use_autoserv=False,
264 use_packaging=False)
265
266
mblighbccad482009-08-24 22:08:31 +0000267 def install_no_autoserv(self, host=None, autodir=None):
mblighb8aa75b2009-09-18 16:50:37 +0000268 self._install(host=host, autodir=autodir, use_autoserv=False)
mblighbccad482009-08-24 22:08:31 +0000269
270
mblighb8aa75b2009-09-18 16:50:37 +0000271 def _install_using_packaging(self, host, autodir):
Eric Lid656d562011-04-20 11:48:29 -0700272 repos = self.get_fetch_location()
mblighb8aa75b2009-09-18 16:50:37 +0000273 if not repos:
274 raise error.PackageInstallError("No repos to install an "
275 "autotest client from")
276 pkgmgr = packages.PackageManager(autodir, hostname=host.hostname,
277 repo_urls=repos,
278 do_locking=False,
279 run_function=host.run,
280 run_function_dargs=dict(timeout=600))
281 # The packages dir is used to store all the packages that
282 # are fetched on that client. (for the tests,deps etc.
283 # too apart from the client)
284 pkg_dir = os.path.join(autodir, 'packages')
Dan Shi86de0942017-08-24 21:37:24 -0700285 # clean up the autodir except for the packages and result_tools
286 # directory.
287 host.run('cd %s && ls | grep -v "^packages$" | grep -v "^result_tools$"'
Allen Liad719c12017-06-27 23:48:04 +0000288 ' | xargs rm -rf && rm -rf .[!.]*' % autodir)
mblighb8aa75b2009-09-18 16:50:37 +0000289 pkgmgr.install_pkg('autotest', 'client', pkg_dir, autodir,
290 preserve_install_dir=True)
291 self.installed = True
292
293
294 def _install_using_send_file(self, host, autodir):
Chris Sosa3ee5d5c2012-02-23 11:18:41 -0800295 dirs_to_exclude = set(["tests", "site_tests", "deps", "profilers",
296 "packages"])
mblighb8aa75b2009-09-18 16:50:37 +0000297 light_files = [os.path.join(self.source_material, f)
298 for f in os.listdir(self.source_material)
299 if f not in dirs_to_exclude]
300 host.send_file(light_files, autodir, delete_dest=True)
301
302 # create empty dirs for all the stuff we excluded
303 commands = []
304 for path in dirs_to_exclude:
305 abs_path = os.path.join(autodir, path)
306 abs_path = utils.sh_escape(abs_path)
307 commands.append("mkdir -p '%s'" % abs_path)
308 commands.append("touch '%s'/__init__.py" % abs_path)
Allen Liad719c12017-06-27 23:48:04 +0000309 host.run(';'.join(commands))
mblighb8aa75b2009-09-18 16:50:37 +0000310
311
312 def _install(self, host=None, autodir=None, use_autoserv=True,
313 use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000314 """
315 Install autotest. If get() was not called previously, an
316 attempt will be made to install from the autotest svn
317 repository.
mbligh9a3f5e52008-05-28 21:21:43 +0000318
mblighbccad482009-08-24 22:08:31 +0000319 @param host A Host instance on which autotest will be installed
320 @param autodir Location on the remote host to install to
mblighb8aa75b2009-09-18 16:50:37 +0000321 @param use_autoserv Enable install modes that depend on the client
mblighbccad482009-08-24 22:08:31 +0000322 running with the autoserv harness
mblighb8aa75b2009-09-18 16:50:37 +0000323 @param use_packaging Enable install modes that use the packaging system
mbligh9a3f5e52008-05-28 21:21:43 +0000324
mblighbccad482009-08-24 22:08:31 +0000325 @exception AutoservError if a tarball was not specified and
326 the target host does not have svn installed in its path
327 """
jadmanski0afbb632008-06-06 21:10:57 +0000328 if not host:
329 host = self.host
330 if not self.got:
331 self.get()
332 host.wait_up(timeout=30)
333 host.setup()
showardb18134f2009-03-20 20:52:18 +0000334 logging.info("Installing autotest on %s", host.hostname)
mbligh40f122a2007-11-03 23:08:46 +0000335
jadmanski54f90af2008-10-10 16:20:55 +0000336 # set up the autotest directory on the remote machine
337 if not autodir:
showardad812bf2009-10-20 23:49:56 +0000338 autodir = self.get_install_dir(host)
339 logging.info('Using installation dir %s', autodir)
mbligh0562e652008-08-20 20:11:45 +0000340 host.set_autodir(autodir)
Allen Liad719c12017-06-27 23:48:04 +0000341 host.run('mkdir -p %s' % utils.sh_escape(autodir))
mbligh40f122a2007-11-03 23:08:46 +0000342
jadmanski1c3c07b2009-03-03 23:29:36 +0000343 # make sure there are no files in $AUTODIR/results
344 results_path = os.path.join(autodir, 'results')
Allen Liad719c12017-06-27 23:48:04 +0000345 host.run('rm -rf %s/*' % utils.sh_escape(results_path),
346 ignore_status=True)
jadmanski1c3c07b2009-03-03 23:29:36 +0000347
mblighc5ddfd12008-08-04 17:15:00 +0000348 # Fetch the autotest client from the nearest repository
mblighb8aa75b2009-09-18 16:50:37 +0000349 if use_packaging:
350 try:
351 self._install_using_packaging(host, autodir)
Dan Shib669cbd2013-09-13 11:17:17 -0700352 logging.info("Installation of autotest completed using the "
353 "packaging system.")
mblighb8aa75b2009-09-18 16:50:37 +0000354 return
Eric Li6f27d4f2010-09-29 10:55:17 -0700355 except (error.PackageInstallError, error.AutoservRunError,
356 global_config.ConfigError), e:
mblighb8aa75b2009-09-18 16:50:37 +0000357 logging.info("Could not install autotest using the packaging "
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700358 "system: %s. Trying other methods", e)
Dan Shi1889ca12015-04-15 09:36:06 -0700359 else:
360 # Delete the package checksum file to force dut updating local
361 # packages.
362 command = ('rm -f "%s"' %
Allen Libf0c4412017-02-03 16:49:53 -0800363 (os.path.join(autodir, packages.CHECKSUM_FILE)))
Allen Liad719c12017-06-27 23:48:04 +0000364 host.run(command)
mblighc5ddfd12008-08-04 17:15:00 +0000365
jadmanski0afbb632008-06-06 21:10:57 +0000366 # try to install from file or directory
367 if self.source_material:
jadmanski69bdaac2010-07-28 16:27:20 +0000368 c = global_config.global_config
369 supports_autoserv_packaging = c.get_config_value(
370 "PACKAGES", "serve_packages_from_autoserv", type=bool)
371 # Copy autotest recursively
372 if supports_autoserv_packaging and use_autoserv:
373 self._install_using_send_file(host, autodir)
jadmanski0afbb632008-06-06 21:10:57 +0000374 else:
jadmanski69bdaac2010-07-28 16:27:20 +0000375 host.send_file(self.source_material, autodir, delete_dest=True)
Dan Shib669cbd2013-09-13 11:17:17 -0700376 logging.info("Installation of autotest completed from %s",
377 self.source_material)
jadmanski0afbb632008-06-06 21:10:57 +0000378 self.installed = True
Allen Li79623692017-08-17 16:27:11 -0700379 else:
380 # if that fails try to install using svn
381 if utils.run('which svn').exit_status:
382 raise error.AutoservError(
383 'svn not found on target machine: %s' %
384 host.hostname)
385 try:
386 host.run('svn checkout %s %s' % (AUTOTEST_SVN, autodir))
387 except error.AutoservRunError, e:
388 host.run('svn checkout %s %s' % (AUTOTEST_HTTP, autodir))
389 logging.info("Installation of autotest completed using SVN.")
390 self.installed = True
mbligh91334902007-09-28 01:47:59 +0000391
Allen Lid5abdab2017-02-07 16:03:43 -0800392 # TODO(milleral): http://crbug.com/258161
393 # Send over the most recent global_config.ini after installation if one
394 # is available.
395 # This code is a bit duplicated from
396 # _Run._create_client_config_file, but oh well.
397 if self.installed and self.source_material:
Allen Li79623692017-08-17 16:27:11 -0700398 self._send_shadow_config()
399
400 def _send_shadow_config(self):
401 logging.info('Installing updated global_config.ini.')
402 destination = os.path.join(self.host.get_autodir(),
403 'global_config.ini')
404 with tempfile.NamedTemporaryFile() as client_config:
405 config = global_config.global_config
406 client_section = config.get_section_values('CLIENT')
407 client_section.write(client_config)
408 client_config.flush()
409 self.host.send_file(client_config.name, destination)
Allen Lid5abdab2017-02-07 16:03:43 -0800410
mbligh91334902007-09-28 01:47:59 +0000411
jadmanski7c7aff32009-03-25 22:43:07 +0000412 def uninstall(self, host=None):
413 """
414 Uninstall (i.e. delete) autotest. Removes the autotest client install
415 from the specified host.
416
417 @params host a Host instance from which the client will be removed
418 """
419 if not self.installed:
420 return
421 if not host:
422 host = self.host
423 autodir = host.get_autodir()
424 if not autodir:
425 return
426
427 # perform the actual uninstall
Allen Liad719c12017-06-27 23:48:04 +0000428 host.run("rm -rf %s" % utils.sh_escape(autodir), ignore_status=True)
jadmanski7c7aff32009-03-25 22:43:07 +0000429 host.set_autodir(None)
430 self.installed = False
431
432
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700433 def get(self, location=None):
jadmanski0afbb632008-06-06 21:10:57 +0000434 if not location:
435 location = os.path.join(self.serverdir, '../client')
436 location = os.path.abspath(location)
Allen Lid5abdab2017-02-07 16:03:43 -0800437 installable_object.InstallableObject.get(self, location)
jadmanski0afbb632008-06-06 21:10:57 +0000438 self.got = True
mblighdcd57a82007-07-11 23:06:47 +0000439
440
mblighe7d9c602009-07-02 19:02:33 +0000441 def run(self, control_file, results_dir='.', host=None, timeout=None,
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700442 tag=None, parallel_flag=False, background=False,
443 client_disconnect_timeout=None, use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000444 """
445 Run an autotest job on the remote machine.
mbligh9a3f5e52008-05-28 21:21:43 +0000446
mblighe7d9c602009-07-02 19:02:33 +0000447 @param control_file: An open file-like-obj of the control file.
448 @param results_dir: A str path where the results should be stored
449 on the local filesystem.
450 @param host: A Host instance on which the control file should
451 be run.
452 @param timeout: Maximum number of seconds to wait for the run or None.
453 @param tag: Tag name for the client side instance of autotest.
454 @param parallel_flag: Flag set when multiple jobs are run at the
455 same time.
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700456 @param background: Indicates that the client should be launched as
457 a background job; the code calling run will be responsible
458 for monitoring the client and collecting the results.
mblighe7d9c602009-07-02 19:02:33 +0000459 @param client_disconnect_timeout: Seconds to wait for the remote host
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700460 to come back after a reboot. Defaults to the host setting for
461 DEFAULT_REBOOT_TIMEOUT.
mblighe7d9c602009-07-02 19:02:33 +0000462
463 @raises AutotestRunError: If there is a problem executing
464 the control file.
jadmanski0afbb632008-06-06 21:10:57 +0000465 """
Dan Shib669cbd2013-09-13 11:17:17 -0700466 host = self._get_host_and_setup(host, use_packaging=use_packaging)
xixuan02b6fee2017-02-01 18:35:20 -0800467 logging.debug('Autotest job starts on remote host: %s',
468 host.hostname)
jadmanski0afbb632008-06-06 21:10:57 +0000469 results_dir = os.path.abspath(results_dir)
mblighc1cbc992008-05-27 20:01:45 +0000470
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700471 if client_disconnect_timeout is None:
472 client_disconnect_timeout = host.DEFAULT_REBOOT_TIMEOUT
473
jadmanski0afbb632008-06-06 21:10:57 +0000474 if tag:
475 results_dir = os.path.join(results_dir, tag)
mblighc1cbc992008-05-27 20:01:45 +0000476
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700477 atrun = _Run(host, results_dir, tag, parallel_flag, background)
jadmanski6dadd832009-02-05 23:39:27 +0000478 self._do_run(control_file, results_dir, host, atrun, timeout,
Dan Shib669cbd2013-09-13 11:17:17 -0700479 client_disconnect_timeout, use_packaging=use_packaging)
mblighd8b39252008-03-20 21:15:03 +0000480
481
Dan Shib669cbd2013-09-13 11:17:17 -0700482 def _get_host_and_setup(self, host, use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000483 if not host:
484 host = self.host
485 if not self.installed:
Dan Shib669cbd2013-09-13 11:17:17 -0700486 self.install(host, use_packaging=use_packaging)
mbligh91334902007-09-28 01:47:59 +0000487
jadmanski0afbb632008-06-06 21:10:57 +0000488 host.wait_up(timeout=30)
489 return host
mblighd8b39252008-03-20 21:15:03 +0000490
491
jadmanski6dadd832009-02-05 23:39:27 +0000492 def _do_run(self, control_file, results_dir, host, atrun, timeout,
Dan Shib669cbd2013-09-13 11:17:17 -0700493 client_disconnect_timeout, use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000494 try:
495 atrun.verify_machine()
496 except:
showardb18134f2009-03-20 20:52:18 +0000497 logging.error("Verify failed on %s. Reinstalling autotest",
498 host.hostname)
jadmanski0afbb632008-06-06 21:10:57 +0000499 self.install(host)
Fang Dengb9cd83c2015-01-27 10:16:08 -0800500 atrun.verify_machine()
jadmanski0afbb632008-06-06 21:10:57 +0000501 debug = os.path.join(results_dir, 'debug')
502 try:
503 os.makedirs(debug)
mbligh09108442008-10-15 16:27:38 +0000504 except Exception:
jadmanski0afbb632008-06-06 21:10:57 +0000505 pass
mbligh9a3f5e52008-05-28 21:21:43 +0000506
mbligh09108442008-10-15 16:27:38 +0000507 delete_file_list = [atrun.remote_control_file,
508 atrun.remote_control_file + '.state',
509 atrun.manual_control_file,
510 atrun.manual_control_file + '.state']
511 cmd = ';'.join('rm -f ' + control for control in delete_file_list)
Allen Liad719c12017-06-27 23:48:04 +0000512 host.run(cmd, ignore_status=True)
mbligh9a3f5e52008-05-28 21:21:43 +0000513
Dale Curtis386eea72011-09-21 18:43:04 -0700514 tmppath = utils.get(control_file, local_copy=True)
mblighc5ddfd12008-08-04 17:15:00 +0000515
jadmanskicb0e1612009-02-27 18:03:10 +0000516 # build up the initialization prologue for the control file
517 prologue_lines = []
jadmanski23afbec2008-09-17 18:12:07 +0000518
mbligh2f076832010-03-30 17:08:20 +0000519 # Add the additional user arguments
jadmanski808f4b12010-04-09 22:30:31 +0000520 prologue_lines.append("args = %r\n" % self.job.args)
mbligh2f076832010-03-30 17:08:20 +0000521
mbligh09108442008-10-15 16:27:38 +0000522 # If the packaging system is being used, add the repository list.
mblighddc9a402010-01-15 20:33:34 +0000523 repos = None
mblighc5ddfd12008-08-04 17:15:00 +0000524 try:
Dan Shib669cbd2013-09-13 11:17:17 -0700525 if use_packaging:
526 repos = self.get_fetch_location()
527 prologue_lines.append('job.add_repository(%s)\n' % repos)
528 else:
529 logging.debug('use_packaging is set to False, do not add any '
530 'repository.')
mblighc5ddfd12008-08-04 17:15:00 +0000531 except global_config.ConfigError, e:
mblighddc9a402010-01-15 20:33:34 +0000532 # If repos is defined packaging is enabled so log the error
533 if repos:
534 logging.error(e)
mblighc5ddfd12008-08-04 17:15:00 +0000535
jadmanskie2eef7b2009-03-03 23:55:13 +0000536 # on full-size installs, turn on any profilers the server is using
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700537 if not atrun.background:
538 running_profilers = host.job.profilers.add_log.iteritems()
539 for profiler, (args, dargs) in running_profilers:
540 call_args = [repr(profiler)]
541 call_args += [repr(arg) for arg in args]
542 call_args += ["%s=%r" % item for item in dargs.iteritems()]
543 prologue_lines.append("job.profilers.add(%s)\n"
544 % ", ".join(call_args))
jadmanskie2eef7b2009-03-03 23:55:13 +0000545 cfile = "".join(prologue_lines)
546
mbligh09108442008-10-15 16:27:38 +0000547 cfile += open(tmppath).read()
548 open(tmppath, "w").write(cfile)
mblighc5ddfd12008-08-04 17:15:00 +0000549
jadmanskic09fc152008-10-15 17:56:59 +0000550 # Create and copy state file to remote_control_file + '.state'
mblighfc3da5b2010-01-06 18:37:22 +0000551 state_file = host.job.preprocess_client_state()
mblighfbf73ae2009-12-19 05:22:42 +0000552 host.send_file(state_file, atrun.remote_control_file + '.init.state')
jadmanskic09fc152008-10-15 17:56:59 +0000553 os.remove(state_file)
554
mblighc5ddfd12008-08-04 17:15:00 +0000555 # Copy control_file to remote_control_file on the host
jadmanski0afbb632008-06-06 21:10:57 +0000556 host.send_file(tmppath, atrun.remote_control_file)
557 if os.path.abspath(tmppath) != os.path.abspath(control_file):
558 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000559
jadmanski6bb32d72009-03-19 20:25:24 +0000560 atrun.execute_control(
jadmanski6dadd832009-02-05 23:39:27 +0000561 timeout=timeout,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800562 client_disconnect_timeout=client_disconnect_timeout)
jadmanski23afbec2008-09-17 18:12:07 +0000563
564
Kevin Chengc51cc1f2017-11-17 15:49:55 -0800565 @staticmethod
566 def extract_test_failure_msg(failure_status_line):
567 """Extract the test failure message from the status line.
568
569 @param failure_status_line: String of test failure status line, it will
570 look like:
571 FAIL <test name> <test name> timestamp=<ts> localtime=<lt> <reason>
572
573 @returns String of the reason, return empty string if we can't regex out
574 reason.
575 """
576 fail_msg = ''
577 match = _FAIL_STATUS_RE.match(failure_status_line)
578 if match:
579 fail_msg = match.group('fail_msg')
580 return fail_msg
581
582
harpreetf531d072016-04-19 18:37:26 -0700583 @classmethod
584 def _check_client_test_result(cls, host, test_name):
585 """
586 Check result of client test.
587 Autotest will store results in the file name status.
588 We check that second to last line in that file begins with 'END GOOD'
589
590 @raises TestFail: If client test does not pass.
591 """
592 client_result_dir = '%s/results/default' % host.autodir
593 command = 'tail -2 %s/status | head -1' % client_result_dir
Allen Liad719c12017-06-27 23:48:04 +0000594 status = host.run(command).stdout.strip()
harpreetf531d072016-04-19 18:37:26 -0700595 logging.info(status)
596 if status[:8] != 'END GOOD':
Kevin Cheng821e2612017-11-27 10:21:15 -0800597 test_fail_status_line_cmd = (
598 'grep "^\s*FAIL\s*%s" %s/status | tail -n 1' %
599 (test_name, client_result_dir))
Kevin Chengc51cc1f2017-11-17 15:49:55 -0800600 test_fail_msg = cls.extract_test_failure_msg(
601 host.run(test_fail_status_line_cmd).stdout.strip())
602 test_fail_msg_reason = ('' if not test_fail_msg
603 else ' (reason: %s)' % test_fail_msg)
604 test_fail_status = '%s client test did not pass%s.' % (
605 test_name, test_fail_msg_reason)
606 raise error.TestFail(test_fail_status)
harpreetf531d072016-04-19 18:37:26 -0700607
608
jadmanski0afbb632008-06-06 21:10:57 +0000609 def run_timed_test(self, test_name, results_dir='.', host=None,
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700610 timeout=None, parallel_flag=False, background=False,
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700611 client_disconnect_timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000612 """
613 Assemble a tiny little control file to just run one test,
614 and run it as an autotest client-side test
615 """
616 if not host:
617 host = self.host
618 if not self.installed:
619 self.install(host)
Max Timkovich4657b962018-03-26 17:01:53 -0700620
jadmanski0afbb632008-06-06 21:10:57 +0000621 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
622 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
623 control = "job.run_test(%s)\n" % cmd
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700624 self.run(control, results_dir, host, timeout=timeout,
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700625 parallel_flag=parallel_flag, background=background,
Andrew Bresticker2da1b762013-01-15 16:11:18 -0800626 client_disconnect_timeout=client_disconnect_timeout)
mbligh0e4613b2007-10-29 16:55:07 +0000627
harpreetf0452062016-04-21 11:22:46 -0700628 if dargs.get('check_client_result', False):
629 self._check_client_test_result(host, test_name)
harpreetf531d072016-04-19 18:37:26 -0700630
mbligh0e4613b2007-10-29 16:55:07 +0000631
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700632 def run_test(self, test_name, results_dir='.', host=None,
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700633 parallel_flag=False, background=False,
Andrew Bresticker2da1b762013-01-15 16:11:18 -0800634 client_disconnect_timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000635 self.run_timed_test(test_name, results_dir, host, timeout=None,
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700636 parallel_flag=parallel_flag, background=background,
Andrew Bresticker2da1b762013-01-15 16:11:18 -0800637 client_disconnect_timeout=client_disconnect_timeout,
jadmanskic98c4702009-01-05 15:50:06 +0000638 *args, **dargs)
mblighd54832b2007-07-25 16:46:56 +0000639
640
Allen Lid5abdab2017-02-07 16:03:43 -0800641 def run_static_method(self, module, method, results_dir='.', host=None,
642 *args):
643 """Runs a non-instance method with |args| from |module| on the client.
644
645 This method runs a static/class/module autotest method on the client.
646 For example:
647 run_static_method("autotest_lib.client.cros.cros_ui", "reboot")
648
649 Will run autotest_lib.client.cros.cros_ui.reboot() on the client.
650
651 @param module: module name as you would refer to it when importing in a
652 control file. e.g. autotest_lib.client.common_lib.module_name.
653 @param method: the method you want to call.
654 @param results_dir: A str path where the results should be stored
655 on the local filesystem.
656 @param host: A Host instance on which the control file should
657 be run.
658 @param args: args to pass to the method.
659 """
660 control = "\n".join(["import %s" % module,
661 "%s.%s(%s)\n" % (module, method,
662 ','.join(map(repr, args)))])
663 self.run(control, results_dir=results_dir, host=host)
664
665
Allen Li24381f32017-02-07 15:43:37 -0800666class _Run(object):
jadmanski0afbb632008-06-06 21:10:57 +0000667 """
668 Represents a run of autotest control file. This class maintains
669 all the state necessary as an autotest control file is executed.
mblighdcd57a82007-07-11 23:06:47 +0000670
jadmanski0afbb632008-06-06 21:10:57 +0000671 It is not intended to be used directly, rather control files
672 should be run using the run method in Autotest.
673 """
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700674 def __init__(self, host, results_dir, tag, parallel_flag, background):
jadmanski0afbb632008-06-06 21:10:57 +0000675 self.host = host
676 self.results_dir = results_dir
677 self.env = host.env
678 self.tag = tag
679 self.parallel_flag = parallel_flag
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700680 self.background = background
showardad812bf2009-10-20 23:49:56 +0000681 self.autodir = Autotest.get_installed_autodir(self.host)
mbligh78bf5352008-07-11 20:27:36 +0000682 control = os.path.join(self.autodir, 'control')
jadmanski0afbb632008-06-06 21:10:57 +0000683 if tag:
mbligh78bf5352008-07-11 20:27:36 +0000684 control += '.' + tag
685 self.manual_control_file = control
686 self.remote_control_file = control + '.autoserv'
lmr6d08b3c2009-11-18 19:26:38 +0000687 self.config_file = os.path.join(self.autodir, 'global_config.ini')
mblighdc735a22007-08-02 16:54:37 +0000688
689
jadmanski0afbb632008-06-06 21:10:57 +0000690 def verify_machine(self):
691 binary = os.path.join(self.autodir, 'bin/autotest')
692 try:
Allen Liad719c12017-06-27 23:48:04 +0000693 self.host.run('ls %s > /dev/null 2>&1' % binary)
jadmanski0afbb632008-06-06 21:10:57 +0000694 except:
lmrd6d27ed2009-12-08 19:58:33 +0000695 raise error.AutoservInstallError(
696 "Autotest does not appear to be installed")
mblighdc735a22007-08-02 16:54:37 +0000697
jadmanski0afbb632008-06-06 21:10:57 +0000698 if not self.parallel_flag:
699 tmpdir = os.path.join(self.autodir, 'tmp')
700 download = os.path.join(self.autodir, 'tests/download')
Allen Liad719c12017-06-27 23:48:04 +0000701 self.host.run('umount %s' % tmpdir, ignore_status=True)
702 self.host.run('umount %s' % download, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000703
jadmanski6dadd832009-02-05 23:39:27 +0000704
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +0000705 def get_base_cmd_args(self, section):
showard234b2962009-07-28 20:02:30 +0000706 args = ['--verbose']
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +0000707 if section > 0:
708 args.append('-c')
jadmanski0afbb632008-06-06 21:10:57 +0000709 if self.tag:
jadmanski6dadd832009-02-05 23:39:27 +0000710 args.append('-t %s' % self.tag)
jadmanski0afbb632008-06-06 21:10:57 +0000711 if self.host.job.use_external_logging():
jadmanski6dadd832009-02-05 23:39:27 +0000712 args.append('-l')
mblighce955fc2009-08-24 21:59:02 +0000713 if self.host.hostname:
714 args.append('--hostname=%s' % self.host.hostname)
mbligh0d0f67d2009-11-06 03:15:03 +0000715 args.append('--user=%s' % self.host.job.user)
mblighce955fc2009-08-24 21:59:02 +0000716
jadmanski6dadd832009-02-05 23:39:27 +0000717 args.append(self.remote_control_file)
718 return args
719
720
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700721 def get_background_cmd(self, section):
722 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotest_client')]
723 cmd += self.get_base_cmd_args(section)
724 cmd += ['>/dev/null', '2>/dev/null', '&']
725 return ' '.join(cmd)
726
727
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +0000728 def get_daemon_cmd(self, section, monitor_dir):
jadmanski6dadd832009-02-05 23:39:27 +0000729 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotestd'),
730 monitor_dir, '-H autoserv']
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +0000731 cmd += self.get_base_cmd_args(section)
jadmanski69bdaac2010-07-28 16:27:20 +0000732 cmd += ['>/dev/null', '2>/dev/null', '&']
jadmanski6dadd832009-02-05 23:39:27 +0000733 return ' '.join(cmd)
734
735
736 def get_monitor_cmd(self, monitor_dir, stdout_read, stderr_read):
737 cmd = [os.path.join(self.autodir, 'bin', 'autotestd_monitor'),
738 monitor_dir, str(stdout_read), str(stderr_read)]
jadmanski0afbb632008-06-06 21:10:57 +0000739 return ' '.join(cmd)
mblighadf2aab2007-11-29 18:16:43 +0000740
mblighd8b39252008-03-20 21:15:03 +0000741
jadmanski4d03cf62010-03-04 18:32:28 +0000742 def get_client_log(self):
743 """Find what the "next" client.* prefix should be
744
745 @returns A string of the form client.INTEGER that should be prefixed
746 to all client debug log files.
747 """
748 max_digit = -1
749 debug_dir = os.path.join(self.results_dir, 'debug')
750 client_logs = glob.glob(os.path.join(debug_dir, 'client.*.*'))
751 for log in client_logs:
752 _, number, _ = log.split('.', 2)
753 if number.isdigit():
754 max_digit = max(max_digit, int(number))
755 return 'client.%d' % (max_digit + 1)
756
757
758 def copy_client_config_file(self, client_log_prefix=None):
759 """
760 Create and copy the client config file based on the server config.
761
762 @param client_log_prefix: Optional prefix to prepend to log files.
763 """
764 client_config_file = self._create_client_config_file(client_log_prefix)
765 self.host.send_file(client_config_file, self.config_file)
766 os.remove(client_config_file)
767
768
769 def _create_client_config_file(self, client_log_prefix=None):
770 """
771 Create a temporary file with the [CLIENT] section configuration values
772 taken from the server global_config.ini.
773
774 @param client_log_prefix: Optional prefix to prepend to log files.
775
776 @return: Path of the temporary file generated.
777 """
778 config = global_config.global_config.get_section_values('CLIENT')
779 if client_log_prefix:
780 config.set('CLIENT', 'default_logging_name', client_log_prefix)
781 return self._create_aux_file(config.write)
782
783
784 def _create_aux_file(self, func, *args):
785 """
786 Creates a temporary file and writes content to it according to a
787 content creation function. The file object is appended to *args, which
788 is then passed to the content creation function
789
790 @param func: Function that will be used to write content to the
791 temporary file.
792 @param *args: List of parameters that func takes.
793 @return: Path to the temporary file that was created.
794 """
795 fd, path = tempfile.mkstemp(dir=self.host.job.tmpdir)
796 aux_file = os.fdopen(fd, "w")
797 try:
798 list_args = list(args)
799 list_args.append(aux_file)
800 func(*list_args)
801 finally:
802 aux_file.close()
803 return path
mblighd8b39252008-03-20 21:15:03 +0000804
805
jadmanskib264ed02009-01-12 23:54:27 +0000806 @staticmethod
807 def is_client_job_finished(last_line):
808 return bool(re.match(r'^END .*\t----\t----\t.*$', last_line))
809
810
811 @staticmethod
812 def is_client_job_rebooting(last_line):
813 return bool(re.match(r'^\t*GOOD\t----\treboot\.start.*$', last_line))
814
815
beeps607428b2013-03-25 16:43:20 -0700816 def _diagnose_dut(self, old_boot_id=None):
817 """
818 Run diagnostic checks on a DUT.
819
820 1. ping: A dead host will not respond to pings.
821 2. ssh (happens with 3.): DUT hangs usually fail in authentication
822 but respond to pings.
823 3. Check if a reboot occured: A healthy but unexpected reboot leaves the
824 host running with a new boot id.
825
826 This method will always raise an exception from the AutotestFailure
827 family and should only get called when the reason for a test failing
828 is ambiguous.
829
830 @raises AutotestDeviceNotPingable: If the DUT doesn't respond to ping.
831 @raises AutotestDeviceNotSSHable: If we cannot SSH into the DUT.
832 @raises AutotestDeviceRebooted: If the boot id changed.
833 @raises AutotestAbort: If none of the above exceptions were raised.
834 Since we have no recourse we must abort at this stage.
835 """
836 msg = 'Autotest client terminated unexpectedly: '
837 if utils.ping(self.host.hostname, tries=1, deadline=1) != 0:
838 msg += 'DUT is no longer pingable, it may have rebooted or hung.\n'
839 raise AutotestDeviceNotPingable(msg)
840
841 if old_boot_id:
842 try:
843 new_boot_id = self.host.get_boot_id(timeout=60)
844 except Exception as e:
845 msg += ('DUT is pingable but not SSHable, it most likely'
beeps14768812013-09-25 12:58:45 -0700846 ' sporadically rebooted during testing. %s\n' % str(e))
beeps607428b2013-03-25 16:43:20 -0700847 raise AutotestDeviceNotSSHable(msg)
848 else:
849 if new_boot_id != old_boot_id:
850 msg += 'DUT rebooted during the test run.\n'
851 raise AutotestDeviceRebooted(msg)
852
853 msg += ('DUT is pingable, SSHable and did NOT restart '
854 'un-expectedly. We probably lost connectivity during the '
855 'test.')
856 else:
857 msg += ('DUT is pingable, could not determine if an un-expected '
858 'reboot occured during the test.')
859
860 raise AutotestAbort(msg)
861
862
beeps07f53b92013-01-08 12:55:10 -0800863 def log_unexpected_abort(self, stderr_redirector, old_boot_id=None):
864 """
beeps607428b2013-03-25 16:43:20 -0700865 Logs that something unexpected happened, then tries to diagnose the
866 failure. The purpose of this function is only to close out the status
867 log with the appropriate error message, not to critically terminate
868 the program.
beeps07f53b92013-01-08 12:55:10 -0800869
870 @param stderr_redirector: log stream.
871 @param old_boot_id: boot id used to infer if a reboot occured.
872 """
jadmanskia61edad2009-05-21 22:17:49 +0000873 stderr_redirector.flush_all_buffers()
beeps607428b2013-03-25 16:43:20 -0700874 try:
875 self._diagnose_dut(old_boot_id)
876 except AutotestFailure as e:
877 self.host.job.record('END ABORT', None, None, str(e))
jadmanskib264ed02009-01-12 23:54:27 +0000878
879
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700880 def _execute_in_background(self, section, timeout):
881 full_cmd = self.get_background_cmd(section)
882 devnull = open(os.devnull, "w")
883
884 self.copy_client_config_file(self.get_client_log())
885
886 self.host.job.push_execution_context(self.results_dir)
887 try:
888 result = self.host.run(full_cmd, ignore_status=True,
889 timeout=timeout,
890 stdout_tee=devnull,
891 stderr_tee=devnull)
892 finally:
893 self.host.job.pop_execution_context()
894
895 return result
896
897
jadmanski6dadd832009-02-05 23:39:27 +0000898 @staticmethod
899 def _strip_stderr_prologue(stderr):
900 """Strips the 'standard' prologue that get pre-pended to every
901 remote command and returns the text that was actually written to
902 stderr by the remote command."""
903 stderr_lines = stderr.split("\n")[1:]
904 if not stderr_lines:
905 return ""
906 elif stderr_lines[0].startswith("NOTE: autotestd_monitor"):
907 del stderr_lines[0]
908 return "\n".join(stderr_lines)
909
910
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +0000911 def _execute_daemon(self, section, timeout, stderr_redirector,
jadmanski6dadd832009-02-05 23:39:27 +0000912 client_disconnect_timeout):
913 monitor_dir = self.host.get_tmp_dir()
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +0000914 daemon_cmd = self.get_daemon_cmd(section, monitor_dir)
jadmanski4d03cf62010-03-04 18:32:28 +0000915
916 # grab the location for the server-side client log file
917 client_log_prefix = self.get_client_log()
918 client_log_path = os.path.join(self.results_dir, 'debug',
919 client_log_prefix + '.log')
920 client_log = open(client_log_path, 'w', 0)
921 self.copy_client_config_file(client_log_prefix)
jadmanski6dadd832009-02-05 23:39:27 +0000922
923 stdout_read = stderr_read = 0
mbligh0d0f67d2009-11-06 03:15:03 +0000924 self.host.job.push_execution_context(self.results_dir)
jadmanski6dadd832009-02-05 23:39:27 +0000925 try:
Allen Liad719c12017-06-27 23:48:04 +0000926 self.host.run(daemon_cmd, ignore_status=True, timeout=timeout)
jadmanski91d56a92009-04-01 15:20:40 +0000927 disconnect_warnings = []
jadmanski6dadd832009-02-05 23:39:27 +0000928 while True:
929 monitor_cmd = self.get_monitor_cmd(monitor_dir, stdout_read,
930 stderr_read)
931 try:
Allen Liad719c12017-06-27 23:48:04 +0000932 result = self.host.run(monitor_cmd, ignore_status=True,
933 timeout=timeout,
934 stdout_tee=client_log,
935 stderr_tee=stderr_redirector)
jadmanski6dadd832009-02-05 23:39:27 +0000936 except error.AutoservRunError, e:
937 result = e.result_obj
938 result.exit_status = None
jadmanski91d56a92009-04-01 15:20:40 +0000939 disconnect_warnings.append(e.description)
940
jadmanski6dadd832009-02-05 23:39:27 +0000941 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000942 "Autotest client was disconnected: %s" % e.description,
943 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000944 except error.AutoservSSHTimeout:
945 result = utils.CmdResult(monitor_cmd, "", "", None, 0)
946 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000947 "Attempt to connect to Autotest client timed out",
948 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000949
950 stdout_read += len(result.stdout)
951 stderr_read += len(self._strip_stderr_prologue(result.stderr))
952
953 if result.exit_status is not None:
Simran Basibca10a62013-01-24 15:52:35 -0800954 # TODO (crosbug.com/38224)- sbasi: Remove extra logging.
955 logging.debug('Result exit status is %d.',
956 result.exit_status)
jadmanski6dadd832009-02-05 23:39:27 +0000957 return result
958 elif not self.host.wait_up(client_disconnect_timeout):
959 raise error.AutoservSSHTimeout(
960 "client was disconnected, reconnect timed out")
961 finally:
jadmanski4d03cf62010-03-04 18:32:28 +0000962 client_log.close()
mbligh0d0f67d2009-11-06 03:15:03 +0000963 self.host.job.pop_execution_context()
jadmanski6dadd832009-02-05 23:39:27 +0000964
965
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +0000966 def execute_section(self, section, timeout, stderr_redirector,
967 client_disconnect_timeout):
968 # TODO(crbug.com/684311) The claim is that section is never more than 0
969 # in pratice. After validating for a week or so, delete all support of
970 # multiple sections.
971 metrics.Counter('chromeos/autotest/autotest/sections').increment(
972 fields={'is_first_section': (section == 0)})
973 logging.info("Executing %s/bin/autotest %s/control phase %d",
974 self.autodir, self.autodir, section)
jadmanski6dadd832009-02-05 23:39:27 +0000975
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700976 if self.background:
977 result = self._execute_in_background(section, timeout)
978 else:
979 result = self._execute_daemon(section, timeout, stderr_redirector,
980 client_disconnect_timeout)
jadmanski6dadd832009-02-05 23:39:27 +0000981
982 last_line = stderr_redirector.last_line
mbligh2bf2db62007-11-27 00:53:18 +0000983
jadmanskib264ed02009-01-12 23:54:27 +0000984 # check if we failed hard enough to warrant an exception
jadmanski0afbb632008-06-06 21:10:57 +0000985 if result.exit_status == 1:
jadmanskib264ed02009-01-12 23:54:27 +0000986 err = error.AutotestRunError("client job was aborted")
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700987 elif not self.background and not result.stderr:
jadmanskib264ed02009-01-12 23:54:27 +0000988 err = error.AutotestRunError(
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +0000989 "execute_section %s failed to return anything\n"
990 "stdout:%s\n" % (section, result.stdout))
jadmanskib264ed02009-01-12 23:54:27 +0000991 else:
992 err = None
mbligh0e4613b2007-10-29 16:55:07 +0000993
jadmanskib264ed02009-01-12 23:54:27 +0000994 # log something if the client failed AND never finished logging
Dale Curtis9285ddf2011-01-05 11:47:24 -0800995 if err and not self.is_client_job_finished(last_line):
jadmanskia61edad2009-05-21 22:17:49 +0000996 self.log_unexpected_abort(stderr_redirector)
jadmanskib264ed02009-01-12 23:54:27 +0000997
998 if err:
999 raise err
1000 else:
1001 return stderr_redirector.last_line
jadmanski4600e342008-10-29 22:54:00 +00001002
1003
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +00001004 def _wait_for_reboot(self, old_boot_id):
1005 logging.info("Client is rebooting")
1006 logging.info("Waiting for client to halt")
1007 if not self.host.wait_down(self.host.WAIT_DOWN_REBOOT_TIMEOUT,
1008 old_boot_id=old_boot_id):
1009 err = "%s failed to shutdown after %d"
1010 err %= (self.host.hostname, self.host.WAIT_DOWN_REBOOT_TIMEOUT)
1011 raise error.AutotestRunError(err)
1012 logging.info("Client down, waiting for restart")
1013 if not self.host.wait_up(self.host.DEFAULT_REBOOT_TIMEOUT):
1014 # since reboot failed
1015 # hardreset the machine once if possible
1016 # before failing this control file
1017 warning = "%s did not come back up, hard resetting"
1018 warning %= self.host.hostname
1019 logging.warning(warning)
1020 try:
1021 self.host.hardreset(wait=False)
1022 except (AttributeError, error.AutoservUnsupportedError):
1023 warning = "Hard reset unsupported on %s"
1024 warning %= self.host.hostname
1025 logging.warning(warning)
1026 raise error.AutotestRunError("%s failed to boot after %ds" %
1027 (self.host.hostname,
1028 self.host.DEFAULT_REBOOT_TIMEOUT))
1029 self.host.reboot_followup()
1030
1031
Dale Curtis9285ddf2011-01-05 11:47:24 -08001032 def execute_control(self, timeout=None, client_disconnect_timeout=None):
Prathmesh Prabhubeaee902017-07-26 11:58:44 -07001033 if not self.background:
1034 collector = log_collector(self.host, self.tag, self.results_dir)
1035 hostname = self.host.hostname
1036 remote_results = collector.client_results_dir
1037 local_results = collector.server_results_dir
1038 self.host.job.add_client_log(hostname, remote_results,
1039 local_results)
1040 job_record_context = self.host.job.get_record_context()
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +00001041
1042 section = 0
1043 start_time = time.time()
1044
jadmanski043e1132008-11-19 17:10:32 +00001045 logger = client_logger(self.host, self.tag, self.results_dir)
jadmanski4600e342008-10-29 22:54:00 +00001046 try:
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +00001047 while not timeout or time.time() < start_time + timeout:
1048 if timeout:
1049 section_timeout = start_time + timeout - time.time()
1050 else:
1051 section_timeout = None
1052 boot_id = self.host.get_boot_id()
1053 last = self.execute_section(section, section_timeout,
1054 logger, client_disconnect_timeout)
Prathmesh Prabhubeaee902017-07-26 11:58:44 -07001055 if self.background:
1056 return
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +00001057 section += 1
1058 if self.is_client_job_finished(last):
1059 logging.info("Client complete")
1060 return
1061 elif self.is_client_job_rebooting(last):
1062 try:
1063 self._wait_for_reboot(boot_id)
1064 except error.AutotestRunError, e:
1065 self.host.job.record("ABORT", None, "reboot", str(e))
1066 self.host.job.record("END ABORT", None, None, str(e))
1067 raise
1068 continue
jadmanski4600e342008-10-29 22:54:00 +00001069
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +00001070 # If a test fails without probable cause we try to bucket it's
1071 # failure into one of 2 categories. If we can determine the
1072 # current state of the device and it is suspicious, we close the
1073 # status lines indicating a failure. If we either cannot
1074 # determine the state of the device, or it appears totally
1075 # healthy, we give up and abort.
1076 try:
1077 self._diagnose_dut(boot_id)
1078 except AutotestDeviceError as e:
1079 # The status lines of the test are pretty much tailed to
1080 # our log, with indentation, from the client job on the DUT.
1081 # So if the DUT goes down unexpectedly we'll end up with a
1082 # malformed status log unless we manually unwind the status
1083 # stack. Ideally we would want to write a nice wrapper like
1084 # server_job methods run_reboot, run_group but they expect
1085 # reboots and we don't.
1086 self.host.job.record('FAIL', None, None, str(e))
1087 self.host.job.record('END FAIL', None, None)
1088 self.host.job.record('END GOOD', None, None)
1089 self.host.job.failed_with_device_error = True
1090 return
1091 except AutotestAbort as e:
1092 self.host.job.record('ABORT', None, None, str(e))
1093 self.host.job.record('END ABORT', None, None)
jadmanski4600e342008-10-29 22:54:00 +00001094
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +00001095 # give the client machine a chance to recover from a crash
1096 self.host.wait_up(
1097 self.host.HOURS_TO_WAIT_FOR_RECOVERY * 3600)
1098 logging.debug('Unexpected final status message from '
1099 'client %s: %s', self.host.hostname, last)
1100 # The line 'last' may have sensitive phrases, like
1101 # 'END GOOD', which breaks the tko parser. So the error
1102 # message will exclude it, since it will be recorded to
1103 # status.log.
1104 msg = ("Aborting - unexpected final status message from "
1105 "client on %s\n") % self.host.hostname
1106 raise error.AutotestRunError(msg)
jadmanski4600e342008-10-29 22:54:00 +00001107 finally:
xixuan02b6fee2017-02-01 18:35:20 -08001108 logging.debug('Autotest job finishes running. Below is the '
1109 'post-processing operations.')
jadmanski043e1132008-11-19 17:10:32 +00001110 logger.close()
Prathmesh Prabhubeaee902017-07-26 11:58:44 -07001111 if not self.background:
1112 collector.collect_client_job_results()
1113 collector.remove_redundant_client_logs()
1114 state_file = os.path.basename(self.remote_control_file
1115 + '.state')
1116 state_path = os.path.join(self.results_dir, state_file)
1117 self.host.job.postprocess_client_state(state_path)
1118 self.host.job.remove_client_log(hostname, remote_results,
1119 local_results)
1120 job_record_context.restore()
mblighdcd57a82007-07-11 23:06:47 +00001121
xixuan02b6fee2017-02-01 18:35:20 -08001122 logging.debug('Autotest job finishes.')
1123
jadmanski0afbb632008-06-06 21:10:57 +00001124 # should only get here if we timed out
1125 assert timeout
1126 raise error.AutotestTimeoutError()
mbligh0e4613b2007-10-29 16:55:07 +00001127
mblighdcd57a82007-07-11 23:06:47 +00001128
jadmanski043e1132008-11-19 17:10:32 +00001129class log_collector(object):
1130 def __init__(self, host, client_tag, results_dir):
1131 self.host = host
1132 if not client_tag:
1133 client_tag = "default"
1134 self.client_results_dir = os.path.join(host.get_autodir(), "results",
1135 client_tag)
1136 self.server_results_dir = results_dir
1137
1138
1139 def collect_client_job_results(self):
1140 """ A method that collects all the current results of a running
1141 client job into the results dir. By default does nothing as no
1142 client job is running, but when running a client job you can override
1143 this with something that will actually do something. """
jadmanski043e1132008-11-19 17:10:32 +00001144 # make an effort to wait for the machine to come up
1145 try:
1146 self.host.wait_up(timeout=30)
1147 except error.AutoservError:
1148 # don't worry about any errors, we'll try and
1149 # get the results anyway
1150 pass
1151
jadmanski043e1132008-11-19 17:10:32 +00001152 # Copy all dirs in default to results_dir
1153 try:
Dan Shi010c0bc2017-06-21 17:02:51 -07001154 # Build test result directory summary
1155 result_tools_runner.run_on_client(
Dan Shic958a3d2017-07-06 14:43:18 -07001156 self.host, self.client_results_dir)
Dan Shi9f879fb2017-05-26 15:44:04 -07001157
1158 with metrics.SecondsTimer(
Prathmesh Prabhu7cc11532016-11-23 17:51:08 -08001159 'chromeos/autotest/job/log_collection_duration',
1160 fields={'dut_host_name': self.host.hostname}):
1161 self.host.get_file(
1162 self.client_results_dir + '/',
1163 self.server_results_dir,
1164 preserve_symlinks=True)
jadmanski043e1132008-11-19 17:10:32 +00001165 except Exception:
1166 # well, don't stop running just because we couldn't get logs
showardb18134f2009-03-20 20:52:18 +00001167 e_msg = "Unexpected error copying test result logs, continuing ..."
1168 logging.error(e_msg)
jadmanski043e1132008-11-19 17:10:32 +00001169 traceback.print_exc(file=sys.stdout)
1170
1171
jadmanski4d03cf62010-03-04 18:32:28 +00001172 def remove_redundant_client_logs(self):
1173 """Remove client.*.log files in favour of client.*.DEBUG files."""
1174 debug_dir = os.path.join(self.server_results_dir, 'debug')
1175 debug_files = [f for f in os.listdir(debug_dir)
1176 if re.search(r'^client\.\d+\.DEBUG$', f)]
1177 for debug_file in debug_files:
1178 log_file = debug_file.replace('DEBUG', 'log')
1179 log_file = os.path.join(debug_dir, log_file)
1180 if os.path.exists(log_file):
1181 os.remove(log_file)
1182
1183
jadmanski043e1132008-11-19 17:10:32 +00001184# a file-like object for catching stderr from an autotest client and
1185# extracting status logs from it
Allen Li944ac462017-02-07 15:57:20 -08001186class client_logger(object):
jadmanski043e1132008-11-19 17:10:32 +00001187 """Partial file object to write to both stdout and
1188 the status log file. We only implement those methods
1189 utils.run() actually calls.
jadmanski043e1132008-11-19 17:10:32 +00001190 """
1191 status_parser = re.compile(r"^AUTOTEST_STATUS:([^:]*):(.*)$")
1192 test_complete_parser = re.compile(r"^AUTOTEST_TEST_COMPLETE:(.*)$")
jadmanskib1a51132009-08-07 16:45:50 +00001193 fetch_package_parser = re.compile(
1194 r"^AUTOTEST_FETCH_PACKAGE:([^:]*):([^:]*):(.*)$")
jadmanski043e1132008-11-19 17:10:32 +00001195 extract_indent = re.compile(r"^(\t*).*$")
jadmanskiefe4ebf2009-05-21 22:12:30 +00001196 extract_timestamp = re.compile(r".*\ttimestamp=(\d+)\t.*$")
jadmanski043e1132008-11-19 17:10:32 +00001197
1198 def __init__(self, host, tag, server_results_dir):
1199 self.host = host
1200 self.job = host.job
1201 self.log_collector = log_collector(host, tag, server_results_dir)
1202 self.leftover = ""
1203 self.last_line = ""
1204 self.logs = {}
jadmanskiefe4ebf2009-05-21 22:12:30 +00001205
1206
jadmanski043e1132008-11-19 17:10:32 +00001207 def _process_log_dict(self, log_dict):
1208 log_list = log_dict.pop("logs", [])
1209 for key in sorted(log_dict.iterkeys()):
1210 log_list += self._process_log_dict(log_dict.pop(key))
1211 return log_list
1212
1213
1214 def _process_logs(self):
1215 """Go through the accumulated logs in self.log and print them
1216 out to stdout and the status log. Note that this processes
1217 logs in an ordering where:
1218
1219 1) logs to different tags are never interleaved
1220 2) logs to x.y come before logs to x.y.z for all z
1221 3) logs to x.y come before x.z whenever y < z
1222
1223 Note that this will in general not be the same as the
1224 chronological ordering of the logs. However, if a chronological
1225 ordering is desired that one can be reconstructed from the
1226 status log by looking at timestamp lines."""
1227 log_list = self._process_log_dict(self.logs)
jadmanski2a89dac2010-06-11 14:32:58 +00001228 for entry in log_list:
1229 self.job.record_entry(entry, log_in_subdir=False)
jadmanski043e1132008-11-19 17:10:32 +00001230 if log_list:
jadmanski2a89dac2010-06-11 14:32:58 +00001231 self.last_line = log_list[-1].render()
jadmanski043e1132008-11-19 17:10:32 +00001232
1233
1234 def _process_quoted_line(self, tag, line):
1235 """Process a line quoted with an AUTOTEST_STATUS flag. If the
1236 tag is blank then we want to push out all the data we've been
1237 building up in self.logs, and then the newest line. If the
1238 tag is not blank, then push the line into the logs for handling
1239 later."""
jadmanski2a89dac2010-06-11 14:32:58 +00001240 entry = base_job.status_log_entry.parse(line)
1241 if entry is None:
1242 return # the line contains no status lines
jadmanski043e1132008-11-19 17:10:32 +00001243 if tag == "":
1244 self._process_logs()
jadmanski2a89dac2010-06-11 14:32:58 +00001245 self.job.record_entry(entry, log_in_subdir=False)
jadmanski043e1132008-11-19 17:10:32 +00001246 self.last_line = line
1247 else:
1248 tag_parts = [int(x) for x in tag.split(".")]
1249 log_dict = self.logs
1250 for part in tag_parts:
1251 log_dict = log_dict.setdefault(part, {})
1252 log_list = log_dict.setdefault("logs", [])
jadmanski2a89dac2010-06-11 14:32:58 +00001253 log_list.append(entry)
jadmanski043e1132008-11-19 17:10:32 +00001254
1255
jadmanskif37df842009-02-11 00:03:26 +00001256 def _process_info_line(self, line):
1257 """Check if line is an INFO line, and if it is, interpret any control
1258 messages (e.g. enabling/disabling warnings) that it may contain."""
1259 match = re.search(r"^\t*INFO\t----\t----(.*)\t[^\t]*$", line)
1260 if not match:
1261 return # not an INFO line
1262 for field in match.group(1).split('\t'):
1263 if field.startswith("warnings.enable="):
jadmanski16a7ff72009-04-01 18:19:53 +00001264 func = self.job.warning_manager.enable_warnings
jadmanskif37df842009-02-11 00:03:26 +00001265 elif field.startswith("warnings.disable="):
jadmanski16a7ff72009-04-01 18:19:53 +00001266 func = self.job.warning_manager.disable_warnings
jadmanskif37df842009-02-11 00:03:26 +00001267 else:
1268 continue
1269 warning_type = field.split("=", 1)[1]
jadmanski16a7ff72009-04-01 18:19:53 +00001270 func(warning_type)
jadmanskif37df842009-02-11 00:03:26 +00001271
1272
jadmanski043e1132008-11-19 17:10:32 +00001273 def _process_line(self, line):
Allen Li944ac462017-02-07 15:57:20 -08001274 """Write out a line of data to the appropriate stream.
1275
1276 Returns the package checksum file if it exists.
1277
1278 Status lines sent by autotest will be prepended with
1279 "AUTOTEST_STATUS", and all other lines are ssh error messages.
1280 """
1281 logging.debug(line)
1282 fetch_package_match = self.fetch_package_parser.search(line)
1283 if fetch_package_match:
1284 pkg_name, dest_path, fifo_path = fetch_package_match.groups()
1285 serve_packages = _CONFIG.get_config_value(
1286 "PACKAGES", "serve_packages_from_autoserv", type=bool)
1287 if serve_packages and pkg_name == 'packages.checksum':
1288 try:
1289 checksum_file = os.path.join(
1290 self.job.pkgmgr.pkgmgr_dir, 'packages', pkg_name)
1291 if os.path.exists(checksum_file):
1292 self.host.send_file(checksum_file, dest_path)
1293 except error.AutoservRunError:
1294 msg = "Package checksum file not found, continuing anyway"
1295 logging.exception(msg)
1296
1297 try:
1298 # When fetching a package, the client expects to be
1299 # notified when the fetching is complete. Autotest
1300 # does this pushing a B to a fifo queue to the client.
1301 self.host.run("echo B > %s" % fifo_path)
1302 except error.AutoservRunError:
1303 msg = "Checksum installation failed, continuing anyway"
1304 logging.exception(msg)
1305 finally:
1306 return
1307
jadmanski043e1132008-11-19 17:10:32 +00001308 status_match = self.status_parser.search(line)
1309 test_complete_match = self.test_complete_parser.search(line)
jadmanskib1a51132009-08-07 16:45:50 +00001310 fetch_package_match = self.fetch_package_parser.search(line)
jadmanski043e1132008-11-19 17:10:32 +00001311 if status_match:
1312 tag, line = status_match.groups()
jadmanskif37df842009-02-11 00:03:26 +00001313 self._process_info_line(line)
jadmanski043e1132008-11-19 17:10:32 +00001314 self._process_quoted_line(tag, line)
1315 elif test_complete_match:
jadmanskifcc0d5d2009-02-12 21:52:54 +00001316 self._process_logs()
jadmanski043e1132008-11-19 17:10:32 +00001317 fifo_path, = test_complete_match.groups()
mbligh060c4712009-12-29 02:43:35 +00001318 try:
1319 self.log_collector.collect_client_job_results()
Allen Liad719c12017-06-27 23:48:04 +00001320 self.host.run("echo A > %s" % fifo_path)
mbligh060c4712009-12-29 02:43:35 +00001321 except Exception:
1322 msg = "Post-test log collection failed, continuing anyway"
1323 logging.exception(msg)
jadmanskib1a51132009-08-07 16:45:50 +00001324 elif fetch_package_match:
1325 pkg_name, dest_path, fifo_path = fetch_package_match.groups()
jadmanskiede7e242009-08-10 15:43:33 +00001326 serve_packages = global_config.global_config.get_config_value(
1327 "PACKAGES", "serve_packages_from_autoserv", type=bool)
1328 if serve_packages and pkg_name.endswith(".tar.bz2"):
1329 try:
1330 self._send_tarball(pkg_name, dest_path)
1331 except Exception:
1332 msg = "Package tarball creation failed, continuing anyway"
1333 logging.exception(msg)
mbligh060c4712009-12-29 02:43:35 +00001334 try:
Allen Liad719c12017-06-27 23:48:04 +00001335 self.host.run("echo B > %s" % fifo_path)
mbligh060c4712009-12-29 02:43:35 +00001336 except Exception:
1337 msg = "Package tarball installation failed, continuing anyway"
1338 logging.exception(msg)
jadmanski043e1132008-11-19 17:10:32 +00001339 else:
showardb18134f2009-03-20 20:52:18 +00001340 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +00001341
1342
jadmanskiede7e242009-08-10 15:43:33 +00001343 def _send_tarball(self, pkg_name, remote_dest):
Allen Li944ac462017-02-07 15:57:20 -08001344 """Uses tarballs in package manager by default."""
1345 try:
1346 server_package = os.path.join(self.job.pkgmgr.pkgmgr_dir,
1347 'packages', pkg_name)
1348 if os.path.exists(server_package):
1349 self.host.send_file(server_package, remote_dest)
1350 return
1351
1352 except error.AutoservRunError:
1353 msg = ("Package %s could not be sent from the package cache." %
1354 pkg_name)
1355 logging.exception(msg)
1356
jadmanskiede7e242009-08-10 15:43:33 +00001357 name, pkg_type = self.job.pkgmgr.parse_tarball_name(pkg_name)
1358 src_dirs = []
1359 if pkg_type == 'test':
mbligh1f572e52010-04-01 17:15:53 +00001360 for test_dir in ['site_tests', 'tests']:
1361 src_dir = os.path.join(self.job.clientdir, test_dir, name)
1362 if os.path.exists(src_dir):
1363 src_dirs += [src_dir]
mbligh1f572e52010-04-01 17:15:53 +00001364 break
jadmanskiede7e242009-08-10 15:43:33 +00001365 elif pkg_type == 'profiler':
1366 src_dirs += [os.path.join(self.job.clientdir, 'profilers', name)]
1367 elif pkg_type == 'dep':
1368 src_dirs += [os.path.join(self.job.clientdir, 'deps', name)]
1369 elif pkg_type == 'client':
1370 return # you must already have a client to hit this anyway
1371 else:
1372 return # no other types are supported
1373
1374 # iterate over src_dirs until we find one that exists, then tar it
1375 for src_dir in src_dirs:
1376 if os.path.exists(src_dir):
1377 try:
1378 logging.info('Bundling %s into %s', src_dir, pkg_name)
1379 temp_dir = autotemp.tempdir(unique_id='autoserv-packager',
1380 dir=self.job.tmpdir)
1381 tarball_path = self.job.pkgmgr.tar_package(
mblighbccad482009-08-24 22:08:31 +00001382 pkg_name, src_dir, temp_dir.name, " .")
jadmanskiede7e242009-08-10 15:43:33 +00001383 self.host.send_file(tarball_path, remote_dest)
1384 finally:
1385 temp_dir.clean()
1386 return
1387
1388
jadmanski91d56a92009-04-01 15:20:40 +00001389 def log_warning(self, msg, warning_type):
jadmanski6dadd832009-02-05 23:39:27 +00001390 """Injects a WARN message into the current status logging stream."""
jadmanski91d56a92009-04-01 15:20:40 +00001391 timestamp = int(time.time())
1392 if self.job.warning_manager.is_valid(timestamp, warning_type):
Eric Lid656d562011-04-20 11:48:29 -07001393 self.job.record('WARN', None, None, msg)
jadmanski6dadd832009-02-05 23:39:27 +00001394
jadmanski043e1132008-11-19 17:10:32 +00001395
1396 def write(self, data):
jadmanski2a89dac2010-06-11 14:32:58 +00001397 # now start processing the existing buffer and the new data
jadmanski043e1132008-11-19 17:10:32 +00001398 data = self.leftover + data
mbligh060c4712009-12-29 02:43:35 +00001399 lines = data.split('\n')
1400 processed_lines = 0
1401 try:
1402 # process all the buffered data except the last line
1403 # ignore the last line since we may not have all of it yet
1404 for line in lines[:-1]:
mbligh060c4712009-12-29 02:43:35 +00001405 self._process_line(line)
1406 processed_lines += 1
1407 finally:
1408 # save any unprocessed lines for future processing
1409 self.leftover = '\n'.join(lines[processed_lines:])
jadmanski043e1132008-11-19 17:10:32 +00001410
1411
1412 def flush(self):
1413 sys.stdout.flush()
1414
1415
jadmanskia61edad2009-05-21 22:17:49 +00001416 def flush_all_buffers(self):
jadmanski043e1132008-11-19 17:10:32 +00001417 if self.leftover:
1418 self._process_line(self.leftover)
jadmanskia61edad2009-05-21 22:17:49 +00001419 self.leftover = ""
jadmanski043e1132008-11-19 17:10:32 +00001420 self._process_logs()
1421 self.flush()
1422
1423
jadmanskia61edad2009-05-21 22:17:49 +00001424 def close(self):
1425 self.flush_all_buffers()