blob: 391eb0b5ab9e73d574374440a6ffa2f35afa47e6 [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")
Matt Mallett6da5d6b2018-09-27 12:57:47 -0700276 # Make sure devserver has the autotest package staged
277 host.verify_job_repo_url()
mblighb8aa75b2009-09-18 16:50:37 +0000278 pkgmgr = packages.PackageManager(autodir, hostname=host.hostname,
279 repo_urls=repos,
280 do_locking=False,
281 run_function=host.run,
282 run_function_dargs=dict(timeout=600))
283 # The packages dir is used to store all the packages that
284 # are fetched on that client. (for the tests,deps etc.
285 # too apart from the client)
286 pkg_dir = os.path.join(autodir, 'packages')
Dan Shi86de0942017-08-24 21:37:24 -0700287 # clean up the autodir except for the packages and result_tools
288 # directory.
289 host.run('cd %s && ls | grep -v "^packages$" | grep -v "^result_tools$"'
Allen Liad719c12017-06-27 23:48:04 +0000290 ' | xargs rm -rf && rm -rf .[!.]*' % autodir)
mblighb8aa75b2009-09-18 16:50:37 +0000291 pkgmgr.install_pkg('autotest', 'client', pkg_dir, autodir,
292 preserve_install_dir=True)
293 self.installed = True
294
295
296 def _install_using_send_file(self, host, autodir):
Chris Sosa3ee5d5c2012-02-23 11:18:41 -0800297 dirs_to_exclude = set(["tests", "site_tests", "deps", "profilers",
298 "packages"])
mblighb8aa75b2009-09-18 16:50:37 +0000299 light_files = [os.path.join(self.source_material, f)
300 for f in os.listdir(self.source_material)
301 if f not in dirs_to_exclude]
302 host.send_file(light_files, autodir, delete_dest=True)
303
304 # create empty dirs for all the stuff we excluded
305 commands = []
306 for path in dirs_to_exclude:
307 abs_path = os.path.join(autodir, path)
308 abs_path = utils.sh_escape(abs_path)
309 commands.append("mkdir -p '%s'" % abs_path)
310 commands.append("touch '%s'/__init__.py" % abs_path)
Allen Liad719c12017-06-27 23:48:04 +0000311 host.run(';'.join(commands))
mblighb8aa75b2009-09-18 16:50:37 +0000312
313
314 def _install(self, host=None, autodir=None, use_autoserv=True,
315 use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000316 """
317 Install autotest. If get() was not called previously, an
318 attempt will be made to install from the autotest svn
319 repository.
mbligh9a3f5e52008-05-28 21:21:43 +0000320
mblighbccad482009-08-24 22:08:31 +0000321 @param host A Host instance on which autotest will be installed
322 @param autodir Location on the remote host to install to
mblighb8aa75b2009-09-18 16:50:37 +0000323 @param use_autoserv Enable install modes that depend on the client
mblighbccad482009-08-24 22:08:31 +0000324 running with the autoserv harness
mblighb8aa75b2009-09-18 16:50:37 +0000325 @param use_packaging Enable install modes that use the packaging system
mbligh9a3f5e52008-05-28 21:21:43 +0000326
mblighbccad482009-08-24 22:08:31 +0000327 @exception AutoservError if a tarball was not specified and
328 the target host does not have svn installed in its path
329 """
jadmanski0afbb632008-06-06 21:10:57 +0000330 if not host:
331 host = self.host
332 if not self.got:
333 self.get()
334 host.wait_up(timeout=30)
335 host.setup()
showardb18134f2009-03-20 20:52:18 +0000336 logging.info("Installing autotest on %s", host.hostname)
mbligh40f122a2007-11-03 23:08:46 +0000337
jadmanski54f90af2008-10-10 16:20:55 +0000338 # set up the autotest directory on the remote machine
339 if not autodir:
showardad812bf2009-10-20 23:49:56 +0000340 autodir = self.get_install_dir(host)
341 logging.info('Using installation dir %s', autodir)
mbligh0562e652008-08-20 20:11:45 +0000342 host.set_autodir(autodir)
Allen Liad719c12017-06-27 23:48:04 +0000343 host.run('mkdir -p %s' % utils.sh_escape(autodir))
mbligh40f122a2007-11-03 23:08:46 +0000344
jadmanski1c3c07b2009-03-03 23:29:36 +0000345 # make sure there are no files in $AUTODIR/results
346 results_path = os.path.join(autodir, 'results')
Allen Liad719c12017-06-27 23:48:04 +0000347 host.run('rm -rf %s/*' % utils.sh_escape(results_path),
348 ignore_status=True)
jadmanski1c3c07b2009-03-03 23:29:36 +0000349
mblighc5ddfd12008-08-04 17:15:00 +0000350 # Fetch the autotest client from the nearest repository
mblighb8aa75b2009-09-18 16:50:37 +0000351 if use_packaging:
352 try:
353 self._install_using_packaging(host, autodir)
Dan Shib669cbd2013-09-13 11:17:17 -0700354 logging.info("Installation of autotest completed using the "
355 "packaging system.")
mblighb8aa75b2009-09-18 16:50:37 +0000356 return
Eric Li6f27d4f2010-09-29 10:55:17 -0700357 except (error.PackageInstallError, error.AutoservRunError,
358 global_config.ConfigError), e:
mblighb8aa75b2009-09-18 16:50:37 +0000359 logging.info("Could not install autotest using the packaging "
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700360 "system: %s. Trying other methods", e)
Dan Shi1889ca12015-04-15 09:36:06 -0700361 else:
362 # Delete the package checksum file to force dut updating local
363 # packages.
364 command = ('rm -f "%s"' %
Allen Libf0c4412017-02-03 16:49:53 -0800365 (os.path.join(autodir, packages.CHECKSUM_FILE)))
Allen Liad719c12017-06-27 23:48:04 +0000366 host.run(command)
mblighc5ddfd12008-08-04 17:15:00 +0000367
jadmanski0afbb632008-06-06 21:10:57 +0000368 # try to install from file or directory
369 if self.source_material:
jadmanski69bdaac2010-07-28 16:27:20 +0000370 c = global_config.global_config
371 supports_autoserv_packaging = c.get_config_value(
372 "PACKAGES", "serve_packages_from_autoserv", type=bool)
373 # Copy autotest recursively
374 if supports_autoserv_packaging and use_autoserv:
375 self._install_using_send_file(host, autodir)
jadmanski0afbb632008-06-06 21:10:57 +0000376 else:
jadmanski69bdaac2010-07-28 16:27:20 +0000377 host.send_file(self.source_material, autodir, delete_dest=True)
Dan Shib669cbd2013-09-13 11:17:17 -0700378 logging.info("Installation of autotest completed from %s",
379 self.source_material)
jadmanski0afbb632008-06-06 21:10:57 +0000380 self.installed = True
Allen Li79623692017-08-17 16:27:11 -0700381 else:
382 # if that fails try to install using svn
383 if utils.run('which svn').exit_status:
384 raise error.AutoservError(
385 'svn not found on target machine: %s' %
386 host.hostname)
387 try:
388 host.run('svn checkout %s %s' % (AUTOTEST_SVN, autodir))
389 except error.AutoservRunError, e:
390 host.run('svn checkout %s %s' % (AUTOTEST_HTTP, autodir))
391 logging.info("Installation of autotest completed using SVN.")
392 self.installed = True
mbligh91334902007-09-28 01:47:59 +0000393
Allen Lid5abdab2017-02-07 16:03:43 -0800394 # TODO(milleral): http://crbug.com/258161
395 # Send over the most recent global_config.ini after installation if one
396 # is available.
397 # This code is a bit duplicated from
398 # _Run._create_client_config_file, but oh well.
399 if self.installed and self.source_material:
Allen Li79623692017-08-17 16:27:11 -0700400 self._send_shadow_config()
401
402 def _send_shadow_config(self):
403 logging.info('Installing updated global_config.ini.')
404 destination = os.path.join(self.host.get_autodir(),
405 'global_config.ini')
406 with tempfile.NamedTemporaryFile() as client_config:
407 config = global_config.global_config
408 client_section = config.get_section_values('CLIENT')
409 client_section.write(client_config)
410 client_config.flush()
411 self.host.send_file(client_config.name, destination)
Allen Lid5abdab2017-02-07 16:03:43 -0800412
mbligh91334902007-09-28 01:47:59 +0000413
jadmanski7c7aff32009-03-25 22:43:07 +0000414 def uninstall(self, host=None):
415 """
416 Uninstall (i.e. delete) autotest. Removes the autotest client install
417 from the specified host.
418
419 @params host a Host instance from which the client will be removed
420 """
421 if not self.installed:
422 return
423 if not host:
424 host = self.host
425 autodir = host.get_autodir()
426 if not autodir:
427 return
428
429 # perform the actual uninstall
Allen Liad719c12017-06-27 23:48:04 +0000430 host.run("rm -rf %s" % utils.sh_escape(autodir), ignore_status=True)
jadmanski7c7aff32009-03-25 22:43:07 +0000431 host.set_autodir(None)
432 self.installed = False
433
434
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700435 def get(self, location=None):
jadmanski0afbb632008-06-06 21:10:57 +0000436 if not location:
437 location = os.path.join(self.serverdir, '../client')
438 location = os.path.abspath(location)
Allen Lid5abdab2017-02-07 16:03:43 -0800439 installable_object.InstallableObject.get(self, location)
jadmanski0afbb632008-06-06 21:10:57 +0000440 self.got = True
mblighdcd57a82007-07-11 23:06:47 +0000441
442
mblighe7d9c602009-07-02 19:02:33 +0000443 def run(self, control_file, results_dir='.', host=None, timeout=None,
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700444 tag=None, parallel_flag=False, background=False,
445 client_disconnect_timeout=None, use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000446 """
447 Run an autotest job on the remote machine.
mbligh9a3f5e52008-05-28 21:21:43 +0000448
mblighe7d9c602009-07-02 19:02:33 +0000449 @param control_file: An open file-like-obj of the control file.
450 @param results_dir: A str path where the results should be stored
451 on the local filesystem.
452 @param host: A Host instance on which the control file should
453 be run.
454 @param timeout: Maximum number of seconds to wait for the run or None.
455 @param tag: Tag name for the client side instance of autotest.
456 @param parallel_flag: Flag set when multiple jobs are run at the
457 same time.
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700458 @param background: Indicates that the client should be launched as
459 a background job; the code calling run will be responsible
460 for monitoring the client and collecting the results.
mblighe7d9c602009-07-02 19:02:33 +0000461 @param client_disconnect_timeout: Seconds to wait for the remote host
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700462 to come back after a reboot. Defaults to the host setting for
463 DEFAULT_REBOOT_TIMEOUT.
mblighe7d9c602009-07-02 19:02:33 +0000464
465 @raises AutotestRunError: If there is a problem executing
466 the control file.
jadmanski0afbb632008-06-06 21:10:57 +0000467 """
Dan Shib669cbd2013-09-13 11:17:17 -0700468 host = self._get_host_and_setup(host, use_packaging=use_packaging)
xixuan02b6fee2017-02-01 18:35:20 -0800469 logging.debug('Autotest job starts on remote host: %s',
470 host.hostname)
jadmanski0afbb632008-06-06 21:10:57 +0000471 results_dir = os.path.abspath(results_dir)
mblighc1cbc992008-05-27 20:01:45 +0000472
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700473 if client_disconnect_timeout is None:
474 client_disconnect_timeout = host.DEFAULT_REBOOT_TIMEOUT
475
jadmanski0afbb632008-06-06 21:10:57 +0000476 if tag:
477 results_dir = os.path.join(results_dir, tag)
mblighc1cbc992008-05-27 20:01:45 +0000478
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700479 atrun = _Run(host, results_dir, tag, parallel_flag, background)
jadmanski6dadd832009-02-05 23:39:27 +0000480 self._do_run(control_file, results_dir, host, atrun, timeout,
Dan Shib669cbd2013-09-13 11:17:17 -0700481 client_disconnect_timeout, use_packaging=use_packaging)
mblighd8b39252008-03-20 21:15:03 +0000482
483
Dan Shib669cbd2013-09-13 11:17:17 -0700484 def _get_host_and_setup(self, host, use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000485 if not host:
486 host = self.host
487 if not self.installed:
Dan Shib669cbd2013-09-13 11:17:17 -0700488 self.install(host, use_packaging=use_packaging)
mbligh91334902007-09-28 01:47:59 +0000489
jadmanski0afbb632008-06-06 21:10:57 +0000490 host.wait_up(timeout=30)
491 return host
mblighd8b39252008-03-20 21:15:03 +0000492
493
jadmanski6dadd832009-02-05 23:39:27 +0000494 def _do_run(self, control_file, results_dir, host, atrun, timeout,
Dan Shib669cbd2013-09-13 11:17:17 -0700495 client_disconnect_timeout, use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000496 try:
497 atrun.verify_machine()
498 except:
showardb18134f2009-03-20 20:52:18 +0000499 logging.error("Verify failed on %s. Reinstalling autotest",
500 host.hostname)
jadmanski0afbb632008-06-06 21:10:57 +0000501 self.install(host)
Fang Dengb9cd83c2015-01-27 10:16:08 -0800502 atrun.verify_machine()
jadmanski0afbb632008-06-06 21:10:57 +0000503 debug = os.path.join(results_dir, 'debug')
504 try:
505 os.makedirs(debug)
mbligh09108442008-10-15 16:27:38 +0000506 except Exception:
jadmanski0afbb632008-06-06 21:10:57 +0000507 pass
mbligh9a3f5e52008-05-28 21:21:43 +0000508
mbligh09108442008-10-15 16:27:38 +0000509 delete_file_list = [atrun.remote_control_file,
510 atrun.remote_control_file + '.state',
511 atrun.manual_control_file,
512 atrun.manual_control_file + '.state']
513 cmd = ';'.join('rm -f ' + control for control in delete_file_list)
Allen Liad719c12017-06-27 23:48:04 +0000514 host.run(cmd, ignore_status=True)
mbligh9a3f5e52008-05-28 21:21:43 +0000515
Dale Curtis386eea72011-09-21 18:43:04 -0700516 tmppath = utils.get(control_file, local_copy=True)
mblighc5ddfd12008-08-04 17:15:00 +0000517
jadmanskicb0e1612009-02-27 18:03:10 +0000518 # build up the initialization prologue for the control file
519 prologue_lines = []
jadmanski23afbec2008-09-17 18:12:07 +0000520
mbligh2f076832010-03-30 17:08:20 +0000521 # Add the additional user arguments
jadmanski808f4b12010-04-09 22:30:31 +0000522 prologue_lines.append("args = %r\n" % self.job.args)
mbligh2f076832010-03-30 17:08:20 +0000523
mbligh09108442008-10-15 16:27:38 +0000524 # If the packaging system is being used, add the repository list.
mblighddc9a402010-01-15 20:33:34 +0000525 repos = None
mblighc5ddfd12008-08-04 17:15:00 +0000526 try:
Dan Shib669cbd2013-09-13 11:17:17 -0700527 if use_packaging:
528 repos = self.get_fetch_location()
529 prologue_lines.append('job.add_repository(%s)\n' % repos)
530 else:
531 logging.debug('use_packaging is set to False, do not add any '
532 'repository.')
mblighc5ddfd12008-08-04 17:15:00 +0000533 except global_config.ConfigError, e:
mblighddc9a402010-01-15 20:33:34 +0000534 # If repos is defined packaging is enabled so log the error
535 if repos:
536 logging.error(e)
mblighc5ddfd12008-08-04 17:15:00 +0000537
jadmanskie2eef7b2009-03-03 23:55:13 +0000538 # on full-size installs, turn on any profilers the server is using
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700539 if not atrun.background:
540 running_profilers = host.job.profilers.add_log.iteritems()
541 for profiler, (args, dargs) in running_profilers:
542 call_args = [repr(profiler)]
543 call_args += [repr(arg) for arg in args]
544 call_args += ["%s=%r" % item for item in dargs.iteritems()]
545 prologue_lines.append("job.profilers.add(%s)\n"
546 % ", ".join(call_args))
jadmanskie2eef7b2009-03-03 23:55:13 +0000547 cfile = "".join(prologue_lines)
548
mbligh09108442008-10-15 16:27:38 +0000549 cfile += open(tmppath).read()
550 open(tmppath, "w").write(cfile)
mblighc5ddfd12008-08-04 17:15:00 +0000551
jadmanskic09fc152008-10-15 17:56:59 +0000552 # Create and copy state file to remote_control_file + '.state'
mblighfc3da5b2010-01-06 18:37:22 +0000553 state_file = host.job.preprocess_client_state()
mblighfbf73ae2009-12-19 05:22:42 +0000554 host.send_file(state_file, atrun.remote_control_file + '.init.state')
jadmanskic09fc152008-10-15 17:56:59 +0000555 os.remove(state_file)
556
mblighc5ddfd12008-08-04 17:15:00 +0000557 # Copy control_file to remote_control_file on the host
jadmanski0afbb632008-06-06 21:10:57 +0000558 host.send_file(tmppath, atrun.remote_control_file)
559 if os.path.abspath(tmppath) != os.path.abspath(control_file):
560 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000561
jadmanski6bb32d72009-03-19 20:25:24 +0000562 atrun.execute_control(
jadmanski6dadd832009-02-05 23:39:27 +0000563 timeout=timeout,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800564 client_disconnect_timeout=client_disconnect_timeout)
jadmanski23afbec2008-09-17 18:12:07 +0000565
566
Kevin Chengc51cc1f2017-11-17 15:49:55 -0800567 @staticmethod
568 def extract_test_failure_msg(failure_status_line):
569 """Extract the test failure message from the status line.
570
571 @param failure_status_line: String of test failure status line, it will
572 look like:
573 FAIL <test name> <test name> timestamp=<ts> localtime=<lt> <reason>
574
575 @returns String of the reason, return empty string if we can't regex out
576 reason.
577 """
578 fail_msg = ''
579 match = _FAIL_STATUS_RE.match(failure_status_line)
580 if match:
581 fail_msg = match.group('fail_msg')
582 return fail_msg
583
584
harpreetf531d072016-04-19 18:37:26 -0700585 @classmethod
586 def _check_client_test_result(cls, host, test_name):
587 """
588 Check result of client test.
589 Autotest will store results in the file name status.
590 We check that second to last line in that file begins with 'END GOOD'
591
592 @raises TestFail: If client test does not pass.
593 """
594 client_result_dir = '%s/results/default' % host.autodir
595 command = 'tail -2 %s/status | head -1' % client_result_dir
Allen Liad719c12017-06-27 23:48:04 +0000596 status = host.run(command).stdout.strip()
harpreetf531d072016-04-19 18:37:26 -0700597 logging.info(status)
598 if status[:8] != 'END GOOD':
Kevin Cheng821e2612017-11-27 10:21:15 -0800599 test_fail_status_line_cmd = (
600 'grep "^\s*FAIL\s*%s" %s/status | tail -n 1' %
601 (test_name, client_result_dir))
Kevin Chengc51cc1f2017-11-17 15:49:55 -0800602 test_fail_msg = cls.extract_test_failure_msg(
603 host.run(test_fail_status_line_cmd).stdout.strip())
604 test_fail_msg_reason = ('' if not test_fail_msg
605 else ' (reason: %s)' % test_fail_msg)
606 test_fail_status = '%s client test did not pass%s.' % (
607 test_name, test_fail_msg_reason)
608 raise error.TestFail(test_fail_status)
harpreetf531d072016-04-19 18:37:26 -0700609
610
jadmanski0afbb632008-06-06 21:10:57 +0000611 def run_timed_test(self, test_name, results_dir='.', host=None,
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700612 timeout=None, parallel_flag=False, background=False,
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700613 client_disconnect_timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000614 """
615 Assemble a tiny little control file to just run one test,
616 and run it as an autotest client-side test
617 """
618 if not host:
619 host = self.host
620 if not self.installed:
621 self.install(host)
Max Timkovich4657b962018-03-26 17:01:53 -0700622
jadmanski0afbb632008-06-06 21:10:57 +0000623 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
624 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
625 control = "job.run_test(%s)\n" % cmd
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700626 self.run(control, results_dir, host, timeout=timeout,
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700627 parallel_flag=parallel_flag, background=background,
Andrew Bresticker2da1b762013-01-15 16:11:18 -0800628 client_disconnect_timeout=client_disconnect_timeout)
mbligh0e4613b2007-10-29 16:55:07 +0000629
harpreetf0452062016-04-21 11:22:46 -0700630 if dargs.get('check_client_result', False):
631 self._check_client_test_result(host, test_name)
harpreetf531d072016-04-19 18:37:26 -0700632
mbligh0e4613b2007-10-29 16:55:07 +0000633
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700634 def run_test(self, test_name, results_dir='.', host=None,
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700635 parallel_flag=False, background=False,
Andrew Bresticker2da1b762013-01-15 16:11:18 -0800636 client_disconnect_timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000637 self.run_timed_test(test_name, results_dir, host, timeout=None,
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700638 parallel_flag=parallel_flag, background=background,
Andrew Bresticker2da1b762013-01-15 16:11:18 -0800639 client_disconnect_timeout=client_disconnect_timeout,
jadmanskic98c4702009-01-05 15:50:06 +0000640 *args, **dargs)
mblighd54832b2007-07-25 16:46:56 +0000641
642
Allen Lid5abdab2017-02-07 16:03:43 -0800643 def run_static_method(self, module, method, results_dir='.', host=None,
644 *args):
645 """Runs a non-instance method with |args| from |module| on the client.
646
647 This method runs a static/class/module autotest method on the client.
648 For example:
649 run_static_method("autotest_lib.client.cros.cros_ui", "reboot")
650
651 Will run autotest_lib.client.cros.cros_ui.reboot() on the client.
652
653 @param module: module name as you would refer to it when importing in a
654 control file. e.g. autotest_lib.client.common_lib.module_name.
655 @param method: the method you want to call.
656 @param results_dir: A str path where the results should be stored
657 on the local filesystem.
658 @param host: A Host instance on which the control file should
659 be run.
660 @param args: args to pass to the method.
661 """
662 control = "\n".join(["import %s" % module,
663 "%s.%s(%s)\n" % (module, method,
664 ','.join(map(repr, args)))])
665 self.run(control, results_dir=results_dir, host=host)
666
667
Allen Li24381f32017-02-07 15:43:37 -0800668class _Run(object):
jadmanski0afbb632008-06-06 21:10:57 +0000669 """
670 Represents a run of autotest control file. This class maintains
671 all the state necessary as an autotest control file is executed.
mblighdcd57a82007-07-11 23:06:47 +0000672
jadmanski0afbb632008-06-06 21:10:57 +0000673 It is not intended to be used directly, rather control files
674 should be run using the run method in Autotest.
675 """
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700676 def __init__(self, host, results_dir, tag, parallel_flag, background):
jadmanski0afbb632008-06-06 21:10:57 +0000677 self.host = host
678 self.results_dir = results_dir
679 self.env = host.env
680 self.tag = tag
681 self.parallel_flag = parallel_flag
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700682 self.background = background
showardad812bf2009-10-20 23:49:56 +0000683 self.autodir = Autotest.get_installed_autodir(self.host)
mbligh78bf5352008-07-11 20:27:36 +0000684 control = os.path.join(self.autodir, 'control')
jadmanski0afbb632008-06-06 21:10:57 +0000685 if tag:
mbligh78bf5352008-07-11 20:27:36 +0000686 control += '.' + tag
687 self.manual_control_file = control
688 self.remote_control_file = control + '.autoserv'
lmr6d08b3c2009-11-18 19:26:38 +0000689 self.config_file = os.path.join(self.autodir, 'global_config.ini')
mblighdc735a22007-08-02 16:54:37 +0000690
691
jadmanski0afbb632008-06-06 21:10:57 +0000692 def verify_machine(self):
693 binary = os.path.join(self.autodir, 'bin/autotest')
694 try:
Allen Liad719c12017-06-27 23:48:04 +0000695 self.host.run('ls %s > /dev/null 2>&1' % binary)
jadmanski0afbb632008-06-06 21:10:57 +0000696 except:
lmrd6d27ed2009-12-08 19:58:33 +0000697 raise error.AutoservInstallError(
698 "Autotest does not appear to be installed")
mblighdc735a22007-08-02 16:54:37 +0000699
jadmanski0afbb632008-06-06 21:10:57 +0000700 if not self.parallel_flag:
701 tmpdir = os.path.join(self.autodir, 'tmp')
702 download = os.path.join(self.autodir, 'tests/download')
Allen Liad719c12017-06-27 23:48:04 +0000703 self.host.run('umount %s' % tmpdir, ignore_status=True)
704 self.host.run('umount %s' % download, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000705
jadmanski6dadd832009-02-05 23:39:27 +0000706
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +0000707 def get_base_cmd_args(self, section):
showard234b2962009-07-28 20:02:30 +0000708 args = ['--verbose']
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +0000709 if section > 0:
710 args.append('-c')
jadmanski0afbb632008-06-06 21:10:57 +0000711 if self.tag:
jadmanski6dadd832009-02-05 23:39:27 +0000712 args.append('-t %s' % self.tag)
jadmanski0afbb632008-06-06 21:10:57 +0000713 if self.host.job.use_external_logging():
jadmanski6dadd832009-02-05 23:39:27 +0000714 args.append('-l')
mblighce955fc2009-08-24 21:59:02 +0000715 if self.host.hostname:
716 args.append('--hostname=%s' % self.host.hostname)
mbligh0d0f67d2009-11-06 03:15:03 +0000717 args.append('--user=%s' % self.host.job.user)
mblighce955fc2009-08-24 21:59:02 +0000718
jadmanski6dadd832009-02-05 23:39:27 +0000719 args.append(self.remote_control_file)
720 return args
721
722
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700723 def get_background_cmd(self, section):
724 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotest_client')]
725 cmd += self.get_base_cmd_args(section)
726 cmd += ['>/dev/null', '2>/dev/null', '&']
727 return ' '.join(cmd)
728
729
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +0000730 def get_daemon_cmd(self, section, monitor_dir):
jadmanski6dadd832009-02-05 23:39:27 +0000731 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotestd'),
732 monitor_dir, '-H autoserv']
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +0000733 cmd += self.get_base_cmd_args(section)
jadmanski69bdaac2010-07-28 16:27:20 +0000734 cmd += ['>/dev/null', '2>/dev/null', '&']
jadmanski6dadd832009-02-05 23:39:27 +0000735 return ' '.join(cmd)
736
737
738 def get_monitor_cmd(self, monitor_dir, stdout_read, stderr_read):
739 cmd = [os.path.join(self.autodir, 'bin', 'autotestd_monitor'),
740 monitor_dir, str(stdout_read), str(stderr_read)]
jadmanski0afbb632008-06-06 21:10:57 +0000741 return ' '.join(cmd)
mblighadf2aab2007-11-29 18:16:43 +0000742
mblighd8b39252008-03-20 21:15:03 +0000743
jadmanski4d03cf62010-03-04 18:32:28 +0000744 def get_client_log(self):
745 """Find what the "next" client.* prefix should be
746
747 @returns A string of the form client.INTEGER that should be prefixed
748 to all client debug log files.
749 """
750 max_digit = -1
751 debug_dir = os.path.join(self.results_dir, 'debug')
752 client_logs = glob.glob(os.path.join(debug_dir, 'client.*.*'))
753 for log in client_logs:
754 _, number, _ = log.split('.', 2)
755 if number.isdigit():
756 max_digit = max(max_digit, int(number))
757 return 'client.%d' % (max_digit + 1)
758
759
760 def copy_client_config_file(self, client_log_prefix=None):
761 """
762 Create and copy the client config file based on the server config.
763
764 @param client_log_prefix: Optional prefix to prepend to log files.
765 """
766 client_config_file = self._create_client_config_file(client_log_prefix)
767 self.host.send_file(client_config_file, self.config_file)
768 os.remove(client_config_file)
769
770
771 def _create_client_config_file(self, client_log_prefix=None):
772 """
773 Create a temporary file with the [CLIENT] section configuration values
774 taken from the server global_config.ini.
775
776 @param client_log_prefix: Optional prefix to prepend to log files.
777
778 @return: Path of the temporary file generated.
779 """
780 config = global_config.global_config.get_section_values('CLIENT')
781 if client_log_prefix:
782 config.set('CLIENT', 'default_logging_name', client_log_prefix)
783 return self._create_aux_file(config.write)
784
785
786 def _create_aux_file(self, func, *args):
787 """
788 Creates a temporary file and writes content to it according to a
789 content creation function. The file object is appended to *args, which
790 is then passed to the content creation function
791
792 @param func: Function that will be used to write content to the
793 temporary file.
794 @param *args: List of parameters that func takes.
795 @return: Path to the temporary file that was created.
796 """
797 fd, path = tempfile.mkstemp(dir=self.host.job.tmpdir)
798 aux_file = os.fdopen(fd, "w")
799 try:
800 list_args = list(args)
801 list_args.append(aux_file)
802 func(*list_args)
803 finally:
804 aux_file.close()
805 return path
mblighd8b39252008-03-20 21:15:03 +0000806
807
jadmanskib264ed02009-01-12 23:54:27 +0000808 @staticmethod
809 def is_client_job_finished(last_line):
Fei Shao24a583c2019-01-30 17:14:25 +0800810 return bool(re.match(r'^\t*END .*\t[\w.-]+\t[\w.-]+\t.*$', last_line))
jadmanskib264ed02009-01-12 23:54:27 +0000811
812
813 @staticmethod
814 def is_client_job_rebooting(last_line):
Fei Shao24a583c2019-01-30 17:14:25 +0800815 return bool(re.match(r'^\t*GOOD\t[\w.-]+\treboot\.start.*$', last_line))
jadmanskib264ed02009-01-12 23:54:27 +0000816
817
Ilja H. Friedelede3e7b2019-12-13 20:28:51 -0800818 # Roughly ordered list from concrete to less specific reboot causes.
819 _failure_reasons = [
820 # Try to find possible reasons leading towards failure.
821 ('ethernet recovery methods have failed. Rebooting.',
822 'dead ethernet dongle crbug/1031035'),
823 # GPU hangs are not always recovered from.
824 ('[drm:amdgpu_job_timedout] \*ERROR\* ring gfx timeout',
825 'drm ring gfx timeout'),
826 ('[drm:do_aquire_global_lock] \*ERROR(.*)hw_done or flip_done timed',
827 'drm hw/flip timeout'),
828 ('[drm:i915_hangcheck_hung] \*ERROR\* Hangcheck(.*)GPU hung',
829 'drm GPU hung'),
830 # TODO(ihf): try to get a better magic signature for kernel crashes.
831 ('BUG: unable to handle kernel paging request', 'kernel paging'),
832 ('Kernel panic - not syncing: Out of memory', 'kernel out of memory'),
833 ('Kernel panic - not syncing', 'kernel panic'),
834 # Fish for user mode killing OOM messages. Shows unstable system.
835 ('out_of_memory', 'process out of memory'),
836 # Reboot was bad enough to have truncated the logs.
837 ('crash_reporter(.*)Stored kcrash', 'kcrash'),
838 ('crash_reporter(.*)Last shutdown was not clean', 'not clean'),
839 ]
840
841 def _diagnose_reboot(self):
842 """
843 Runs diagnostic check on a rebooted DUT.
844
845 TODO(ihf): if this analysis is useful consider moving the code to the
846 DUT into a script and call it from here. This is more
847 powerful and might be cleaner to grow in functionality. But
848 it may also be less robust if stateful is damaged during the
849 reboot.
850
851 @returns msg describing reboot reason.
852 """
853 reasons = []
854 for (message, bucket) in self._failure_reasons:
Ilja H. Friedel8443b252019-12-18 13:13:04 -0800855 # Use -a option for grep to avoid "binary file" warning to stdout.
Ilja H. Friedelede3e7b2019-12-13 20:28:51 -0800856 # The grep -v is added to not match itself in the log (across jobs).
857 # Using grep is slightly problematic as it finds any reason, not
858 # just the most recent reason (since 2 boots ago), so it may guess
859 # wrong. Multiple reboots are unusual in the lab setting though and
860 # it is better to have a reasonable guess than no reason at all.
861 found = self.host.run(
Ilja H. Friedel8443b252019-12-18 13:13:04 -0800862 "grep -aE '" + message + "' /var/log/messages | grep -av grep",
Ilja H. Friedelede3e7b2019-12-13 20:28:51 -0800863 ignore_status=True
864 ).stdout
865 if found and found.strip():
866 reasons.append(bucket)
867 signature = 'reason unknown'
868 if reasons:
869 # Concatenate possible reasons found to obtain a magic signature.
870 signature = ', '.join(reasons)
871 return ('DUT rebooted during the test run. (%s)\n' % signature)
872
873
beeps607428b2013-03-25 16:43:20 -0700874 def _diagnose_dut(self, old_boot_id=None):
875 """
876 Run diagnostic checks on a DUT.
877
878 1. ping: A dead host will not respond to pings.
879 2. ssh (happens with 3.): DUT hangs usually fail in authentication
880 but respond to pings.
881 3. Check if a reboot occured: A healthy but unexpected reboot leaves the
882 host running with a new boot id.
883
884 This method will always raise an exception from the AutotestFailure
885 family and should only get called when the reason for a test failing
886 is ambiguous.
887
888 @raises AutotestDeviceNotPingable: If the DUT doesn't respond to ping.
889 @raises AutotestDeviceNotSSHable: If we cannot SSH into the DUT.
890 @raises AutotestDeviceRebooted: If the boot id changed.
891 @raises AutotestAbort: If none of the above exceptions were raised.
892 Since we have no recourse we must abort at this stage.
893 """
894 msg = 'Autotest client terminated unexpectedly: '
895 if utils.ping(self.host.hostname, tries=1, deadline=1) != 0:
896 msg += 'DUT is no longer pingable, it may have rebooted or hung.\n'
897 raise AutotestDeviceNotPingable(msg)
898
899 if old_boot_id:
900 try:
901 new_boot_id = self.host.get_boot_id(timeout=60)
902 except Exception as e:
903 msg += ('DUT is pingable but not SSHable, it most likely'
beeps14768812013-09-25 12:58:45 -0700904 ' sporadically rebooted during testing. %s\n' % str(e))
beeps607428b2013-03-25 16:43:20 -0700905 raise AutotestDeviceNotSSHable(msg)
906 else:
907 if new_boot_id != old_boot_id:
Ilja H. Friedelede3e7b2019-12-13 20:28:51 -0800908 msg += self._diagnose_reboot()
beeps607428b2013-03-25 16:43:20 -0700909 raise AutotestDeviceRebooted(msg)
910
911 msg += ('DUT is pingable, SSHable and did NOT restart '
912 'un-expectedly. We probably lost connectivity during the '
913 'test.')
914 else:
915 msg += ('DUT is pingable, could not determine if an un-expected '
916 'reboot occured during the test.')
917
918 raise AutotestAbort(msg)
919
920
beeps07f53b92013-01-08 12:55:10 -0800921 def log_unexpected_abort(self, stderr_redirector, old_boot_id=None):
922 """
beeps607428b2013-03-25 16:43:20 -0700923 Logs that something unexpected happened, then tries to diagnose the
924 failure. The purpose of this function is only to close out the status
925 log with the appropriate error message, not to critically terminate
926 the program.
beeps07f53b92013-01-08 12:55:10 -0800927
928 @param stderr_redirector: log stream.
929 @param old_boot_id: boot id used to infer if a reboot occured.
930 """
jadmanskia61edad2009-05-21 22:17:49 +0000931 stderr_redirector.flush_all_buffers()
beeps607428b2013-03-25 16:43:20 -0700932 try:
933 self._diagnose_dut(old_boot_id)
934 except AutotestFailure as e:
935 self.host.job.record('END ABORT', None, None, str(e))
jadmanskib264ed02009-01-12 23:54:27 +0000936
937
Prathmesh Prabhubeaee902017-07-26 11:58:44 -0700938 def _execute_in_background(self, section, timeout):
939 full_cmd = self.get_background_cmd(section)
940 devnull = open(os.devnull, "w")
941
942 self.copy_client_config_file(self.get_client_log())
943
944 self.host.job.push_execution_context(self.results_dir)
945 try:
946 result = self.host.run(full_cmd, ignore_status=True,
947 timeout=timeout,
948 stdout_tee=devnull,
949 stderr_tee=devnull)
950 finally:
951 self.host.job.pop_execution_context()
952
953 return result
954
955
jadmanski6dadd832009-02-05 23:39:27 +0000956 @staticmethod
957 def _strip_stderr_prologue(stderr):
958 """Strips the 'standard' prologue that get pre-pended to every
959 remote command and returns the text that was actually written to
960 stderr by the remote command."""
961 stderr_lines = stderr.split("\n")[1:]
962 if not stderr_lines:
963 return ""
964 elif stderr_lines[0].startswith("NOTE: autotestd_monitor"):
965 del stderr_lines[0]
966 return "\n".join(stderr_lines)
967
968
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +0000969 def _execute_daemon(self, section, timeout, stderr_redirector,
jadmanski6dadd832009-02-05 23:39:27 +0000970 client_disconnect_timeout):
971 monitor_dir = self.host.get_tmp_dir()
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +0000972 daemon_cmd = self.get_daemon_cmd(section, monitor_dir)
jadmanski4d03cf62010-03-04 18:32:28 +0000973
974 # grab the location for the server-side client log file
975 client_log_prefix = self.get_client_log()
976 client_log_path = os.path.join(self.results_dir, 'debug',
977 client_log_prefix + '.log')
978 client_log = open(client_log_path, 'w', 0)
979 self.copy_client_config_file(client_log_prefix)
jadmanski6dadd832009-02-05 23:39:27 +0000980
981 stdout_read = stderr_read = 0
mbligh0d0f67d2009-11-06 03:15:03 +0000982 self.host.job.push_execution_context(self.results_dir)
jadmanski6dadd832009-02-05 23:39:27 +0000983 try:
Allen Liad719c12017-06-27 23:48:04 +0000984 self.host.run(daemon_cmd, ignore_status=True, timeout=timeout)
jadmanski91d56a92009-04-01 15:20:40 +0000985 disconnect_warnings = []
jadmanski6dadd832009-02-05 23:39:27 +0000986 while True:
987 monitor_cmd = self.get_monitor_cmd(monitor_dir, stdout_read,
988 stderr_read)
989 try:
Allen Liad719c12017-06-27 23:48:04 +0000990 result = self.host.run(monitor_cmd, ignore_status=True,
991 timeout=timeout,
992 stdout_tee=client_log,
993 stderr_tee=stderr_redirector)
jadmanski6dadd832009-02-05 23:39:27 +0000994 except error.AutoservRunError, e:
995 result = e.result_obj
996 result.exit_status = None
jadmanski91d56a92009-04-01 15:20:40 +0000997 disconnect_warnings.append(e.description)
998
jadmanski6dadd832009-02-05 23:39:27 +0000999 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +00001000 "Autotest client was disconnected: %s" % e.description,
1001 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +00001002 except error.AutoservSSHTimeout:
1003 result = utils.CmdResult(monitor_cmd, "", "", None, 0)
1004 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +00001005 "Attempt to connect to Autotest client timed out",
1006 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +00001007
1008 stdout_read += len(result.stdout)
1009 stderr_read += len(self._strip_stderr_prologue(result.stderr))
1010
1011 if result.exit_status is not None:
Simran Basibca10a62013-01-24 15:52:35 -08001012 # TODO (crosbug.com/38224)- sbasi: Remove extra logging.
1013 logging.debug('Result exit status is %d.',
1014 result.exit_status)
jadmanski6dadd832009-02-05 23:39:27 +00001015 return result
1016 elif not self.host.wait_up(client_disconnect_timeout):
1017 raise error.AutoservSSHTimeout(
1018 "client was disconnected, reconnect timed out")
1019 finally:
jadmanski4d03cf62010-03-04 18:32:28 +00001020 client_log.close()
mbligh0d0f67d2009-11-06 03:15:03 +00001021 self.host.job.pop_execution_context()
jadmanski6dadd832009-02-05 23:39:27 +00001022
1023
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +00001024 def execute_section(self, section, timeout, stderr_redirector,
1025 client_disconnect_timeout):
1026 # TODO(crbug.com/684311) The claim is that section is never more than 0
1027 # in pratice. After validating for a week or so, delete all support of
1028 # multiple sections.
1029 metrics.Counter('chromeos/autotest/autotest/sections').increment(
1030 fields={'is_first_section': (section == 0)})
1031 logging.info("Executing %s/bin/autotest %s/control phase %d",
1032 self.autodir, self.autodir, section)
jadmanski6dadd832009-02-05 23:39:27 +00001033
Prathmesh Prabhubeaee902017-07-26 11:58:44 -07001034 if self.background:
1035 result = self._execute_in_background(section, timeout)
1036 else:
1037 result = self._execute_daemon(section, timeout, stderr_redirector,
1038 client_disconnect_timeout)
jadmanski6dadd832009-02-05 23:39:27 +00001039
1040 last_line = stderr_redirector.last_line
mbligh2bf2db62007-11-27 00:53:18 +00001041
jadmanskib264ed02009-01-12 23:54:27 +00001042 # check if we failed hard enough to warrant an exception
jadmanski0afbb632008-06-06 21:10:57 +00001043 if result.exit_status == 1:
jadmanskib264ed02009-01-12 23:54:27 +00001044 err = error.AutotestRunError("client job was aborted")
Prathmesh Prabhubeaee902017-07-26 11:58:44 -07001045 elif not self.background and not result.stderr:
jadmanskib264ed02009-01-12 23:54:27 +00001046 err = error.AutotestRunError(
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +00001047 "execute_section %s failed to return anything\n"
1048 "stdout:%s\n" % (section, result.stdout))
jadmanskib264ed02009-01-12 23:54:27 +00001049 else:
1050 err = None
mbligh0e4613b2007-10-29 16:55:07 +00001051
jadmanskib264ed02009-01-12 23:54:27 +00001052 # log something if the client failed AND never finished logging
Dale Curtis9285ddf2011-01-05 11:47:24 -08001053 if err and not self.is_client_job_finished(last_line):
jadmanskia61edad2009-05-21 22:17:49 +00001054 self.log_unexpected_abort(stderr_redirector)
jadmanskib264ed02009-01-12 23:54:27 +00001055
1056 if err:
1057 raise err
1058 else:
1059 return stderr_redirector.last_line
jadmanski4600e342008-10-29 22:54:00 +00001060
1061
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +00001062 def _wait_for_reboot(self, old_boot_id):
1063 logging.info("Client is rebooting")
1064 logging.info("Waiting for client to halt")
1065 if not self.host.wait_down(self.host.WAIT_DOWN_REBOOT_TIMEOUT,
1066 old_boot_id=old_boot_id):
1067 err = "%s failed to shutdown after %d"
1068 err %= (self.host.hostname, self.host.WAIT_DOWN_REBOOT_TIMEOUT)
1069 raise error.AutotestRunError(err)
1070 logging.info("Client down, waiting for restart")
1071 if not self.host.wait_up(self.host.DEFAULT_REBOOT_TIMEOUT):
1072 # since reboot failed
1073 # hardreset the machine once if possible
1074 # before failing this control file
1075 warning = "%s did not come back up, hard resetting"
1076 warning %= self.host.hostname
1077 logging.warning(warning)
1078 try:
1079 self.host.hardreset(wait=False)
1080 except (AttributeError, error.AutoservUnsupportedError):
1081 warning = "Hard reset unsupported on %s"
1082 warning %= self.host.hostname
1083 logging.warning(warning)
1084 raise error.AutotestRunError("%s failed to boot after %ds" %
1085 (self.host.hostname,
1086 self.host.DEFAULT_REBOOT_TIMEOUT))
1087 self.host.reboot_followup()
1088
1089
Dale Curtis9285ddf2011-01-05 11:47:24 -08001090 def execute_control(self, timeout=None, client_disconnect_timeout=None):
Prathmesh Prabhubeaee902017-07-26 11:58:44 -07001091 if not self.background:
1092 collector = log_collector(self.host, self.tag, self.results_dir)
1093 hostname = self.host.hostname
1094 remote_results = collector.client_results_dir
1095 local_results = collector.server_results_dir
1096 self.host.job.add_client_log(hostname, remote_results,
1097 local_results)
1098 job_record_context = self.host.job.get_record_context()
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +00001099
1100 section = 0
1101 start_time = time.time()
1102
jadmanski043e1132008-11-19 17:10:32 +00001103 logger = client_logger(self.host, self.tag, self.results_dir)
jadmanski4600e342008-10-29 22:54:00 +00001104 try:
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +00001105 while not timeout or time.time() < start_time + timeout:
1106 if timeout:
1107 section_timeout = start_time + timeout - time.time()
1108 else:
1109 section_timeout = None
1110 boot_id = self.host.get_boot_id()
1111 last = self.execute_section(section, section_timeout,
1112 logger, client_disconnect_timeout)
Prathmesh Prabhubeaee902017-07-26 11:58:44 -07001113 if self.background:
1114 return
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +00001115 section += 1
1116 if self.is_client_job_finished(last):
1117 logging.info("Client complete")
1118 return
1119 elif self.is_client_job_rebooting(last):
1120 try:
1121 self._wait_for_reboot(boot_id)
1122 except error.AutotestRunError, e:
1123 self.host.job.record("ABORT", None, "reboot", str(e))
1124 self.host.job.record("END ABORT", None, None, str(e))
1125 raise
1126 continue
jadmanski4600e342008-10-29 22:54:00 +00001127
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +00001128 # If a test fails without probable cause we try to bucket it's
1129 # failure into one of 2 categories. If we can determine the
1130 # current state of the device and it is suspicious, we close the
1131 # status lines indicating a failure. If we either cannot
1132 # determine the state of the device, or it appears totally
1133 # healthy, we give up and abort.
1134 try:
1135 self._diagnose_dut(boot_id)
1136 except AutotestDeviceError as e:
1137 # The status lines of the test are pretty much tailed to
1138 # our log, with indentation, from the client job on the DUT.
1139 # So if the DUT goes down unexpectedly we'll end up with a
1140 # malformed status log unless we manually unwind the status
1141 # stack. Ideally we would want to write a nice wrapper like
1142 # server_job methods run_reboot, run_group but they expect
1143 # reboots and we don't.
1144 self.host.job.record('FAIL', None, None, str(e))
1145 self.host.job.record('END FAIL', None, None)
1146 self.host.job.record('END GOOD', None, None)
1147 self.host.job.failed_with_device_error = True
1148 return
1149 except AutotestAbort as e:
1150 self.host.job.record('ABORT', None, None, str(e))
1151 self.host.job.record('END ABORT', None, None)
jadmanski4600e342008-10-29 22:54:00 +00001152
Prathmesh Prabhucb4f5492017-07-26 18:57:21 +00001153 # give the client machine a chance to recover from a crash
1154 self.host.wait_up(
1155 self.host.HOURS_TO_WAIT_FOR_RECOVERY * 3600)
1156 logging.debug('Unexpected final status message from '
1157 'client %s: %s', self.host.hostname, last)
1158 # The line 'last' may have sensitive phrases, like
1159 # 'END GOOD', which breaks the tko parser. So the error
1160 # message will exclude it, since it will be recorded to
1161 # status.log.
1162 msg = ("Aborting - unexpected final status message from "
1163 "client on %s\n") % self.host.hostname
1164 raise error.AutotestRunError(msg)
jadmanski4600e342008-10-29 22:54:00 +00001165 finally:
xixuan02b6fee2017-02-01 18:35:20 -08001166 logging.debug('Autotest job finishes running. Below is the '
1167 'post-processing operations.')
jadmanski043e1132008-11-19 17:10:32 +00001168 logger.close()
Prathmesh Prabhubeaee902017-07-26 11:58:44 -07001169 if not self.background:
1170 collector.collect_client_job_results()
1171 collector.remove_redundant_client_logs()
1172 state_file = os.path.basename(self.remote_control_file
1173 + '.state')
1174 state_path = os.path.join(self.results_dir, state_file)
1175 self.host.job.postprocess_client_state(state_path)
1176 self.host.job.remove_client_log(hostname, remote_results,
1177 local_results)
1178 job_record_context.restore()
mblighdcd57a82007-07-11 23:06:47 +00001179
xixuan02b6fee2017-02-01 18:35:20 -08001180 logging.debug('Autotest job finishes.')
1181
jadmanski0afbb632008-06-06 21:10:57 +00001182 # should only get here if we timed out
1183 assert timeout
1184 raise error.AutotestTimeoutError()
mbligh0e4613b2007-10-29 16:55:07 +00001185
mblighdcd57a82007-07-11 23:06:47 +00001186
jadmanski043e1132008-11-19 17:10:32 +00001187class log_collector(object):
1188 def __init__(self, host, client_tag, results_dir):
1189 self.host = host
1190 if not client_tag:
1191 client_tag = "default"
1192 self.client_results_dir = os.path.join(host.get_autodir(), "results",
1193 client_tag)
1194 self.server_results_dir = results_dir
1195
1196
1197 def collect_client_job_results(self):
1198 """ A method that collects all the current results of a running
1199 client job into the results dir. By default does nothing as no
1200 client job is running, but when running a client job you can override
1201 this with something that will actually do something. """
jadmanski043e1132008-11-19 17:10:32 +00001202 # make an effort to wait for the machine to come up
1203 try:
1204 self.host.wait_up(timeout=30)
1205 except error.AutoservError:
1206 # don't worry about any errors, we'll try and
1207 # get the results anyway
1208 pass
1209
jadmanski043e1132008-11-19 17:10:32 +00001210 # Copy all dirs in default to results_dir
1211 try:
Dan Shi010c0bc2017-06-21 17:02:51 -07001212 # Build test result directory summary
1213 result_tools_runner.run_on_client(
Dan Shic958a3d2017-07-06 14:43:18 -07001214 self.host, self.client_results_dir)
Dan Shi9f879fb2017-05-26 15:44:04 -07001215
1216 with metrics.SecondsTimer(
Prathmesh Prabhu7cc11532016-11-23 17:51:08 -08001217 'chromeos/autotest/job/log_collection_duration',
1218 fields={'dut_host_name': self.host.hostname}):
1219 self.host.get_file(
1220 self.client_results_dir + '/',
1221 self.server_results_dir,
1222 preserve_symlinks=True)
jadmanski043e1132008-11-19 17:10:32 +00001223 except Exception:
1224 # well, don't stop running just because we couldn't get logs
showardb18134f2009-03-20 20:52:18 +00001225 e_msg = "Unexpected error copying test result logs, continuing ..."
1226 logging.error(e_msg)
jadmanski043e1132008-11-19 17:10:32 +00001227 traceback.print_exc(file=sys.stdout)
1228
1229
jadmanski4d03cf62010-03-04 18:32:28 +00001230 def remove_redundant_client_logs(self):
1231 """Remove client.*.log files in favour of client.*.DEBUG files."""
1232 debug_dir = os.path.join(self.server_results_dir, 'debug')
1233 debug_files = [f for f in os.listdir(debug_dir)
1234 if re.search(r'^client\.\d+\.DEBUG$', f)]
1235 for debug_file in debug_files:
1236 log_file = debug_file.replace('DEBUG', 'log')
1237 log_file = os.path.join(debug_dir, log_file)
1238 if os.path.exists(log_file):
1239 os.remove(log_file)
1240
1241
jadmanski043e1132008-11-19 17:10:32 +00001242# a file-like object for catching stderr from an autotest client and
1243# extracting status logs from it
Allen Li944ac462017-02-07 15:57:20 -08001244class client_logger(object):
jadmanski043e1132008-11-19 17:10:32 +00001245 """Partial file object to write to both stdout and
1246 the status log file. We only implement those methods
1247 utils.run() actually calls.
jadmanski043e1132008-11-19 17:10:32 +00001248 """
1249 status_parser = re.compile(r"^AUTOTEST_STATUS:([^:]*):(.*)$")
1250 test_complete_parser = re.compile(r"^AUTOTEST_TEST_COMPLETE:(.*)$")
jadmanskib1a51132009-08-07 16:45:50 +00001251 fetch_package_parser = re.compile(
1252 r"^AUTOTEST_FETCH_PACKAGE:([^:]*):([^:]*):(.*)$")
jadmanski043e1132008-11-19 17:10:32 +00001253 extract_indent = re.compile(r"^(\t*).*$")
jadmanskiefe4ebf2009-05-21 22:12:30 +00001254 extract_timestamp = re.compile(r".*\ttimestamp=(\d+)\t.*$")
jadmanski043e1132008-11-19 17:10:32 +00001255
1256 def __init__(self, host, tag, server_results_dir):
1257 self.host = host
1258 self.job = host.job
1259 self.log_collector = log_collector(host, tag, server_results_dir)
1260 self.leftover = ""
1261 self.last_line = ""
1262 self.logs = {}
jadmanskiefe4ebf2009-05-21 22:12:30 +00001263
1264
jadmanski043e1132008-11-19 17:10:32 +00001265 def _process_log_dict(self, log_dict):
1266 log_list = log_dict.pop("logs", [])
1267 for key in sorted(log_dict.iterkeys()):
1268 log_list += self._process_log_dict(log_dict.pop(key))
1269 return log_list
1270
1271
1272 def _process_logs(self):
1273 """Go through the accumulated logs in self.log and print them
1274 out to stdout and the status log. Note that this processes
1275 logs in an ordering where:
1276
1277 1) logs to different tags are never interleaved
1278 2) logs to x.y come before logs to x.y.z for all z
1279 3) logs to x.y come before x.z whenever y < z
1280
1281 Note that this will in general not be the same as the
1282 chronological ordering of the logs. However, if a chronological
1283 ordering is desired that one can be reconstructed from the
1284 status log by looking at timestamp lines."""
1285 log_list = self._process_log_dict(self.logs)
jadmanski2a89dac2010-06-11 14:32:58 +00001286 for entry in log_list:
1287 self.job.record_entry(entry, log_in_subdir=False)
jadmanski043e1132008-11-19 17:10:32 +00001288 if log_list:
jadmanski2a89dac2010-06-11 14:32:58 +00001289 self.last_line = log_list[-1].render()
jadmanski043e1132008-11-19 17:10:32 +00001290
1291
1292 def _process_quoted_line(self, tag, line):
1293 """Process a line quoted with an AUTOTEST_STATUS flag. If the
1294 tag is blank then we want to push out all the data we've been
1295 building up in self.logs, and then the newest line. If the
1296 tag is not blank, then push the line into the logs for handling
1297 later."""
jadmanski2a89dac2010-06-11 14:32:58 +00001298 entry = base_job.status_log_entry.parse(line)
1299 if entry is None:
1300 return # the line contains no status lines
jadmanski043e1132008-11-19 17:10:32 +00001301 if tag == "":
1302 self._process_logs()
jadmanski2a89dac2010-06-11 14:32:58 +00001303 self.job.record_entry(entry, log_in_subdir=False)
jadmanski043e1132008-11-19 17:10:32 +00001304 self.last_line = line
1305 else:
1306 tag_parts = [int(x) for x in tag.split(".")]
1307 log_dict = self.logs
1308 for part in tag_parts:
1309 log_dict = log_dict.setdefault(part, {})
1310 log_list = log_dict.setdefault("logs", [])
jadmanski2a89dac2010-06-11 14:32:58 +00001311 log_list.append(entry)
jadmanski043e1132008-11-19 17:10:32 +00001312
1313
jadmanskif37df842009-02-11 00:03:26 +00001314 def _process_info_line(self, line):
1315 """Check if line is an INFO line, and if it is, interpret any control
1316 messages (e.g. enabling/disabling warnings) that it may contain."""
1317 match = re.search(r"^\t*INFO\t----\t----(.*)\t[^\t]*$", line)
1318 if not match:
1319 return # not an INFO line
1320 for field in match.group(1).split('\t'):
1321 if field.startswith("warnings.enable="):
jadmanski16a7ff72009-04-01 18:19:53 +00001322 func = self.job.warning_manager.enable_warnings
jadmanskif37df842009-02-11 00:03:26 +00001323 elif field.startswith("warnings.disable="):
jadmanski16a7ff72009-04-01 18:19:53 +00001324 func = self.job.warning_manager.disable_warnings
jadmanskif37df842009-02-11 00:03:26 +00001325 else:
1326 continue
1327 warning_type = field.split("=", 1)[1]
jadmanski16a7ff72009-04-01 18:19:53 +00001328 func(warning_type)
jadmanskif37df842009-02-11 00:03:26 +00001329
1330
jadmanski043e1132008-11-19 17:10:32 +00001331 def _process_line(self, line):
Allen Li944ac462017-02-07 15:57:20 -08001332 """Write out a line of data to the appropriate stream.
1333
1334 Returns the package checksum file if it exists.
1335
1336 Status lines sent by autotest will be prepended with
1337 "AUTOTEST_STATUS", and all other lines are ssh error messages.
1338 """
1339 logging.debug(line)
1340 fetch_package_match = self.fetch_package_parser.search(line)
1341 if fetch_package_match:
1342 pkg_name, dest_path, fifo_path = fetch_package_match.groups()
1343 serve_packages = _CONFIG.get_config_value(
1344 "PACKAGES", "serve_packages_from_autoserv", type=bool)
1345 if serve_packages and pkg_name == 'packages.checksum':
1346 try:
1347 checksum_file = os.path.join(
1348 self.job.pkgmgr.pkgmgr_dir, 'packages', pkg_name)
1349 if os.path.exists(checksum_file):
1350 self.host.send_file(checksum_file, dest_path)
1351 except error.AutoservRunError:
1352 msg = "Package checksum file not found, continuing anyway"
1353 logging.exception(msg)
1354
1355 try:
1356 # When fetching a package, the client expects to be
1357 # notified when the fetching is complete. Autotest
1358 # does this pushing a B to a fifo queue to the client.
1359 self.host.run("echo B > %s" % fifo_path)
1360 except error.AutoservRunError:
1361 msg = "Checksum installation failed, continuing anyway"
1362 logging.exception(msg)
1363 finally:
1364 return
1365
jadmanski043e1132008-11-19 17:10:32 +00001366 status_match = self.status_parser.search(line)
1367 test_complete_match = self.test_complete_parser.search(line)
jadmanskib1a51132009-08-07 16:45:50 +00001368 fetch_package_match = self.fetch_package_parser.search(line)
jadmanski043e1132008-11-19 17:10:32 +00001369 if status_match:
1370 tag, line = status_match.groups()
jadmanskif37df842009-02-11 00:03:26 +00001371 self._process_info_line(line)
jadmanski043e1132008-11-19 17:10:32 +00001372 self._process_quoted_line(tag, line)
1373 elif test_complete_match:
jadmanskifcc0d5d2009-02-12 21:52:54 +00001374 self._process_logs()
jadmanski043e1132008-11-19 17:10:32 +00001375 fifo_path, = test_complete_match.groups()
mbligh060c4712009-12-29 02:43:35 +00001376 try:
1377 self.log_collector.collect_client_job_results()
Allen Liad719c12017-06-27 23:48:04 +00001378 self.host.run("echo A > %s" % fifo_path)
mbligh060c4712009-12-29 02:43:35 +00001379 except Exception:
1380 msg = "Post-test log collection failed, continuing anyway"
1381 logging.exception(msg)
jadmanskib1a51132009-08-07 16:45:50 +00001382 elif fetch_package_match:
1383 pkg_name, dest_path, fifo_path = fetch_package_match.groups()
jadmanskiede7e242009-08-10 15:43:33 +00001384 serve_packages = global_config.global_config.get_config_value(
1385 "PACKAGES", "serve_packages_from_autoserv", type=bool)
1386 if serve_packages and pkg_name.endswith(".tar.bz2"):
1387 try:
1388 self._send_tarball(pkg_name, dest_path)
1389 except Exception:
1390 msg = "Package tarball creation failed, continuing anyway"
1391 logging.exception(msg)
mbligh060c4712009-12-29 02:43:35 +00001392 try:
Allen Liad719c12017-06-27 23:48:04 +00001393 self.host.run("echo B > %s" % fifo_path)
mbligh060c4712009-12-29 02:43:35 +00001394 except Exception:
1395 msg = "Package tarball installation failed, continuing anyway"
1396 logging.exception(msg)
jadmanski043e1132008-11-19 17:10:32 +00001397 else:
showardb18134f2009-03-20 20:52:18 +00001398 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +00001399
1400
jadmanskiede7e242009-08-10 15:43:33 +00001401 def _send_tarball(self, pkg_name, remote_dest):
Allen Li944ac462017-02-07 15:57:20 -08001402 """Uses tarballs in package manager by default."""
1403 try:
1404 server_package = os.path.join(self.job.pkgmgr.pkgmgr_dir,
1405 'packages', pkg_name)
1406 if os.path.exists(server_package):
1407 self.host.send_file(server_package, remote_dest)
1408 return
1409
1410 except error.AutoservRunError:
1411 msg = ("Package %s could not be sent from the package cache." %
1412 pkg_name)
1413 logging.exception(msg)
1414
jadmanskiede7e242009-08-10 15:43:33 +00001415 name, pkg_type = self.job.pkgmgr.parse_tarball_name(pkg_name)
1416 src_dirs = []
1417 if pkg_type == 'test':
mbligh1f572e52010-04-01 17:15:53 +00001418 for test_dir in ['site_tests', 'tests']:
1419 src_dir = os.path.join(self.job.clientdir, test_dir, name)
1420 if os.path.exists(src_dir):
1421 src_dirs += [src_dir]
mbligh1f572e52010-04-01 17:15:53 +00001422 break
jadmanskiede7e242009-08-10 15:43:33 +00001423 elif pkg_type == 'profiler':
1424 src_dirs += [os.path.join(self.job.clientdir, 'profilers', name)]
1425 elif pkg_type == 'dep':
1426 src_dirs += [os.path.join(self.job.clientdir, 'deps', name)]
1427 elif pkg_type == 'client':
1428 return # you must already have a client to hit this anyway
1429 else:
1430 return # no other types are supported
1431
1432 # iterate over src_dirs until we find one that exists, then tar it
1433 for src_dir in src_dirs:
1434 if os.path.exists(src_dir):
1435 try:
1436 logging.info('Bundling %s into %s', src_dir, pkg_name)
1437 temp_dir = autotemp.tempdir(unique_id='autoserv-packager',
1438 dir=self.job.tmpdir)
1439 tarball_path = self.job.pkgmgr.tar_package(
mblighbccad482009-08-24 22:08:31 +00001440 pkg_name, src_dir, temp_dir.name, " .")
jadmanskiede7e242009-08-10 15:43:33 +00001441 self.host.send_file(tarball_path, remote_dest)
1442 finally:
1443 temp_dir.clean()
1444 return
1445
1446
jadmanski91d56a92009-04-01 15:20:40 +00001447 def log_warning(self, msg, warning_type):
jadmanski6dadd832009-02-05 23:39:27 +00001448 """Injects a WARN message into the current status logging stream."""
jadmanski91d56a92009-04-01 15:20:40 +00001449 timestamp = int(time.time())
1450 if self.job.warning_manager.is_valid(timestamp, warning_type):
Eric Lid656d562011-04-20 11:48:29 -07001451 self.job.record('WARN', None, None, msg)
jadmanski6dadd832009-02-05 23:39:27 +00001452
jadmanski043e1132008-11-19 17:10:32 +00001453
1454 def write(self, data):
jadmanski2a89dac2010-06-11 14:32:58 +00001455 # now start processing the existing buffer and the new data
jadmanski043e1132008-11-19 17:10:32 +00001456 data = self.leftover + data
mbligh060c4712009-12-29 02:43:35 +00001457 lines = data.split('\n')
1458 processed_lines = 0
1459 try:
1460 # process all the buffered data except the last line
1461 # ignore the last line since we may not have all of it yet
1462 for line in lines[:-1]:
mbligh060c4712009-12-29 02:43:35 +00001463 self._process_line(line)
1464 processed_lines += 1
1465 finally:
1466 # save any unprocessed lines for future processing
1467 self.leftover = '\n'.join(lines[processed_lines:])
jadmanski043e1132008-11-19 17:10:32 +00001468
1469
1470 def flush(self):
1471 sys.stdout.flush()
1472
1473
jadmanskia61edad2009-05-21 22:17:49 +00001474 def flush_all_buffers(self):
jadmanski043e1132008-11-19 17:10:32 +00001475 if self.leftover:
1476 self._process_line(self.leftover)
jadmanskia61edad2009-05-21 22:17:49 +00001477 self.leftover = ""
jadmanski043e1132008-11-19 17:10:32 +00001478 self._process_logs()
1479 self.flush()
1480
1481
jadmanskia61edad2009-05-21 22:17:49 +00001482 def close(self):
1483 self.flush_all_buffers()