blob: 45fa374547d75c2ffcc774fd71419d60e06bb53e [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
beeps07f53b92013-01-08 12:55:10 -08004import re, os, sys, traceback, time, glob, tempfile
5import logging
Prathmesh Prabhu7cc11532016-11-23 17:51:08 -08006
Prathmesh Prabhu7cc11532016-11-23 17:51:08 -08007import common
mbligh1f572e52010-04-01 17:15:53 +00008from autotest_lib.server import installable_object, prebuild, utils
beeps07f53b92013-01-08 12:55:10 -08009from autotest_lib.client.common_lib import base_job, error, autotemp
Allen Libf0c4412017-02-03 16:49:53 -080010from autotest_lib.client.common_lib import packages
Dan Shi1889ca12015-04-15 09:36:06 -070011from autotest_lib.client.common_lib import global_config
mbligha7007722009-01-13 00:37:11 +000012from autotest_lib.client.common_lib import utils as client_utils
mbligh3c7a1502008-07-24 18:08:47 +000013
Dan Shi5e2efb72017-02-07 11:40:23 -080014try:
15 from chromite.lib import metrics
16except ImportError:
17 metrics = client_utils.metrics_mock
18
19
Dale Curtiscb7bfaf2011-06-07 16:21:57 -070020AUTOTEST_SVN = 'svn://test.kernel.org/autotest/trunk/client'
mblighdcd57a82007-07-11 23:06:47 +000021AUTOTEST_HTTP = 'http://test.kernel.org/svn/autotest/trunk/client'
22
mblighdcd57a82007-07-11 23:06:47 +000023
mbligh1f572e52010-04-01 17:15:53 +000024get_value = global_config.global_config.get_config_value
25autoserv_prebuild = get_value('AUTOSERV', 'enable_server_prebuild',
26 type=bool, default=False)
27
jadmanski2a89dac2010-06-11 14:32:58 +000028
showardad812bf2009-10-20 23:49:56 +000029class AutodirNotFoundError(Exception):
30 """No Autotest installation could be found."""
31
32
beeps607428b2013-03-25 16:43:20 -070033class AutotestFailure(Exception):
34 """Gereric exception class for failures during a test run."""
35
36
37class AutotestAbort(AutotestFailure):
38 """
39 AutotestAborts are thrown when the DUT seems fine,
40 and the test doesn't give us an explicit reason for
41 failure; In this case we have no choice but to abort.
42 """
43
44
45class AutotestDeviceError(AutotestFailure):
46 """
47 Exceptions that inherit from AutotestDeviceError
48 are thrown when we can determine the current
49 state of the DUT and conclude that it probably
50 lead to the test failing; these exceptions lead
51 to failures instead of aborts.
52 """
53
54
55class AutotestDeviceNotPingable(AutotestDeviceError):
56 """Error for when a DUT becomes unpingable."""
57
58
59class AutotestDeviceNotSSHable(AutotestDeviceError):
60 """Error for when a DUT is pingable but not SSHable."""
61
62
63class AutotestDeviceRebooted(AutotestDeviceError):
64 """Error for when a DUT rebooted unexpectedly."""
65
66
mblighd8b39252008-03-20 21:15:03 +000067class BaseAutotest(installable_object.InstallableObject):
jadmanski0afbb632008-06-06 21:10:57 +000068 """
69 This class represents the Autotest program.
mblighdcd57a82007-07-11 23:06:47 +000070
jadmanski0afbb632008-06-06 21:10:57 +000071 Autotest is used to run tests automatically and collect the results.
72 It also supports profilers.
mblighdcd57a82007-07-11 23:06:47 +000073
jadmanski0afbb632008-06-06 21:10:57 +000074 Implementation details:
75 This is a leaf class in an abstract class hierarchy, it must
76 implement the unimplemented methods in parent classes.
77 """
mbligh119c12a2007-11-12 22:13:44 +000078
Dale Curtiscb7bfaf2011-06-07 16:21:57 -070079 def __init__(self, host=None):
jadmanski0afbb632008-06-06 21:10:57 +000080 self.host = host
81 self.got = False
82 self.installed = False
83 self.serverdir = utils.get_server_dir()
84 super(BaseAutotest, self).__init__()
mblighc8949b82007-07-23 16:33:58 +000085
mblighdc735a22007-08-02 16:54:37 +000086
jadmanskif22fea82008-11-26 20:57:07 +000087 install_in_tmpdir = False
88 @classmethod
89 def set_install_in_tmpdir(cls, flag):
90 """ Sets a flag that controls whether or not Autotest should by
91 default be installed in a "standard" directory (e.g.
92 /home/autotest, /usr/local/autotest) or a temporary directory. """
93 cls.install_in_tmpdir = flag
94
95
showardad812bf2009-10-20 23:49:56 +000096 @classmethod
97 def get_client_autodir_paths(cls, host):
98 return global_config.global_config.get_config_value(
99 'AUTOSERV', 'client_autodir_paths', type=list)
100
101
102 @classmethod
103 def get_installed_autodir(cls, host):
104 """
105 Find where the Autotest client is installed on the host.
106 @returns an absolute path to an installed Autotest client root.
107 @raises AutodirNotFoundError if no Autotest installation can be found.
108 """
109 autodir = host.get_autodir()
110 if autodir:
111 logging.debug('Using existing host autodir: %s', autodir)
112 return autodir
113
114 for path in Autotest.get_client_autodir_paths(host):
115 try:
116 autotest_binary = os.path.join(path, 'bin', 'autotest')
117 host.run('test -x %s' % utils.sh_escape(autotest_binary))
Eric Li861b2d52011-02-04 14:50:35 -0800118 host.run('test -w %s' % utils.sh_escape(path))
showardad812bf2009-10-20 23:49:56 +0000119 logging.debug('Found existing autodir at %s', path)
120 return path
121 except error.AutoservRunError:
122 logging.debug('%s does not exist on %s', autotest_binary,
123 host.hostname)
124 raise AutodirNotFoundError
125
126
127 @classmethod
128 def get_install_dir(cls, host):
129 """
130 Determines the location where autotest should be installed on
jadmanskif22fea82008-11-26 20:57:07 +0000131 host. If self.install_in_tmpdir is set, it will return a unique
showardad812bf2009-10-20 23:49:56 +0000132 temporary directory that autotest can be installed in. Otherwise, looks
133 for an existing installation to use; if none is found, looks for a
134 usable directory in the global config client_autodir_paths.
135 """
jadmanskif22fea82008-11-26 20:57:07 +0000136 try:
lmr9dcf0832009-12-08 21:28:55 +0000137 install_dir = cls.get_installed_autodir(host)
showardad812bf2009-10-20 23:49:56 +0000138 except AutodirNotFoundError:
lmr9dcf0832009-12-08 21:28:55 +0000139 install_dir = cls._find_installable_dir(host)
140
141 if cls.install_in_tmpdir:
142 return host.get_tmp_dir(parent=install_dir)
143 return install_dir
showardad812bf2009-10-20 23:49:56 +0000144
145
146 @classmethod
147 def _find_installable_dir(cls, host):
148 client_autodir_paths = cls.get_client_autodir_paths(host)
149 for path in client_autodir_paths:
150 try:
J. Richard Barnettef44ff962011-01-05 10:14:28 -0800151 host.run('mkdir -p %s' % utils.sh_escape(path))
Eric Li861b2d52011-02-04 14:50:35 -0800152 host.run('test -w %s' % utils.sh_escape(path))
showardad812bf2009-10-20 23:49:56 +0000153 return path
154 except error.AutoservRunError:
155 logging.debug('Failed to create %s', path)
Prathmesh Prabhu7cc11532016-11-23 17:51:08 -0800156 metrics.Counter(
157 'chromeos/autotest/errors/no_autotest_install_path').increment(
158 fields={'dut_host_name': host.hostname})
showardad812bf2009-10-20 23:49:56 +0000159 raise error.AutoservInstallError(
Dale Curtis74a314b2011-06-23 14:55:46 -0700160 'Unable to find a place to install Autotest; tried %s' %
showardad812bf2009-10-20 23:49:56 +0000161 ', '.join(client_autodir_paths))
jadmanskif22fea82008-11-26 20:57:07 +0000162
163
Eric Lid656d562011-04-20 11:48:29 -0700164 def get_fetch_location(self):
165 c = global_config.global_config
166 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list,
167 default=[])
168 repos.reverse()
169 return repos
170
171
Dan Shib669cbd2013-09-13 11:17:17 -0700172 def install(self, host=None, autodir=None, use_packaging=True):
173 self._install(host=host, autodir=autodir, use_packaging=use_packaging)
jadmanski54f90af2008-10-10 16:20:55 +0000174
175
mblighb8aa75b2009-09-18 16:50:37 +0000176 def install_full_client(self, host=None, autodir=None):
177 self._install(host=host, autodir=autodir, use_autoserv=False,
178 use_packaging=False)
179
180
mblighbccad482009-08-24 22:08:31 +0000181 def install_no_autoserv(self, host=None, autodir=None):
mblighb8aa75b2009-09-18 16:50:37 +0000182 self._install(host=host, autodir=autodir, use_autoserv=False)
mblighbccad482009-08-24 22:08:31 +0000183
184
mblighb8aa75b2009-09-18 16:50:37 +0000185 def _install_using_packaging(self, host, autodir):
Eric Lid656d562011-04-20 11:48:29 -0700186 repos = self.get_fetch_location()
mblighb8aa75b2009-09-18 16:50:37 +0000187 if not repos:
188 raise error.PackageInstallError("No repos to install an "
189 "autotest client from")
190 pkgmgr = packages.PackageManager(autodir, hostname=host.hostname,
191 repo_urls=repos,
192 do_locking=False,
193 run_function=host.run,
194 run_function_dargs=dict(timeout=600))
195 # The packages dir is used to store all the packages that
196 # are fetched on that client. (for the tests,deps etc.
197 # too apart from the client)
198 pkg_dir = os.path.join(autodir, 'packages')
199 # clean up the autodir except for the packages directory
200 host.run('cd %s && ls | grep -v "^packages$"'
Laurence Goodby2de320d2015-07-08 14:45:18 -0700201 ' | xargs rm -rf && rm -rf .[!.]*' % autodir)
mblighb8aa75b2009-09-18 16:50:37 +0000202 pkgmgr.install_pkg('autotest', 'client', pkg_dir, autodir,
203 preserve_install_dir=True)
204 self.installed = True
205
206
207 def _install_using_send_file(self, host, autodir):
Chris Sosa3ee5d5c2012-02-23 11:18:41 -0800208 dirs_to_exclude = set(["tests", "site_tests", "deps", "profilers",
209 "packages"])
mblighb8aa75b2009-09-18 16:50:37 +0000210 light_files = [os.path.join(self.source_material, f)
211 for f in os.listdir(self.source_material)
212 if f not in dirs_to_exclude]
213 host.send_file(light_files, autodir, delete_dest=True)
214
215 # create empty dirs for all the stuff we excluded
216 commands = []
217 for path in dirs_to_exclude:
218 abs_path = os.path.join(autodir, path)
219 abs_path = utils.sh_escape(abs_path)
220 commands.append("mkdir -p '%s'" % abs_path)
221 commands.append("touch '%s'/__init__.py" % abs_path)
222 host.run(';'.join(commands))
223
224
225 def _install(self, host=None, autodir=None, use_autoserv=True,
226 use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000227 """
228 Install autotest. If get() was not called previously, an
229 attempt will be made to install from the autotest svn
230 repository.
mbligh9a3f5e52008-05-28 21:21:43 +0000231
mblighbccad482009-08-24 22:08:31 +0000232 @param host A Host instance on which autotest will be installed
233 @param autodir Location on the remote host to install to
mblighb8aa75b2009-09-18 16:50:37 +0000234 @param use_autoserv Enable install modes that depend on the client
mblighbccad482009-08-24 22:08:31 +0000235 running with the autoserv harness
mblighb8aa75b2009-09-18 16:50:37 +0000236 @param use_packaging Enable install modes that use the packaging system
mbligh9a3f5e52008-05-28 21:21:43 +0000237
mblighbccad482009-08-24 22:08:31 +0000238 @exception AutoservError if a tarball was not specified and
239 the target host does not have svn installed in its path
240 """
jadmanski0afbb632008-06-06 21:10:57 +0000241 if not host:
242 host = self.host
243 if not self.got:
244 self.get()
245 host.wait_up(timeout=30)
246 host.setup()
showardb18134f2009-03-20 20:52:18 +0000247 logging.info("Installing autotest on %s", host.hostname)
mbligh40f122a2007-11-03 23:08:46 +0000248
jadmanski54f90af2008-10-10 16:20:55 +0000249 # set up the autotest directory on the remote machine
250 if not autodir:
showardad812bf2009-10-20 23:49:56 +0000251 autodir = self.get_install_dir(host)
252 logging.info('Using installation dir %s', autodir)
mbligh0562e652008-08-20 20:11:45 +0000253 host.set_autodir(autodir)
jadmanski3c236942009-03-04 17:51:26 +0000254 host.run('mkdir -p %s' % utils.sh_escape(autodir))
mbligh40f122a2007-11-03 23:08:46 +0000255
jadmanski1c3c07b2009-03-03 23:29:36 +0000256 # make sure there are no files in $AUTODIR/results
257 results_path = os.path.join(autodir, 'results')
jadmanski3c236942009-03-04 17:51:26 +0000258 host.run('rm -rf %s/*' % utils.sh_escape(results_path),
jadmanski1c3c07b2009-03-03 23:29:36 +0000259 ignore_status=True)
260
mblighc5ddfd12008-08-04 17:15:00 +0000261 # Fetch the autotest client from the nearest repository
mblighb8aa75b2009-09-18 16:50:37 +0000262 if use_packaging:
263 try:
264 self._install_using_packaging(host, autodir)
Dan Shib669cbd2013-09-13 11:17:17 -0700265 logging.info("Installation of autotest completed using the "
266 "packaging system.")
mblighb8aa75b2009-09-18 16:50:37 +0000267 return
Eric Li6f27d4f2010-09-29 10:55:17 -0700268 except (error.PackageInstallError, error.AutoservRunError,
269 global_config.ConfigError), e:
mblighb8aa75b2009-09-18 16:50:37 +0000270 logging.info("Could not install autotest using the packaging "
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700271 "system: %s. Trying other methods", e)
Dan Shi1889ca12015-04-15 09:36:06 -0700272 else:
273 # Delete the package checksum file to force dut updating local
274 # packages.
275 command = ('rm -f "%s"' %
Allen Libf0c4412017-02-03 16:49:53 -0800276 (os.path.join(autodir, packages.CHECKSUM_FILE)))
Dan Shi1889ca12015-04-15 09:36:06 -0700277 host.run(command)
mblighc5ddfd12008-08-04 17:15:00 +0000278
jadmanski0afbb632008-06-06 21:10:57 +0000279 # try to install from file or directory
280 if self.source_material:
jadmanski69bdaac2010-07-28 16:27:20 +0000281 c = global_config.global_config
282 supports_autoserv_packaging = c.get_config_value(
283 "PACKAGES", "serve_packages_from_autoserv", type=bool)
284 # Copy autotest recursively
285 if supports_autoserv_packaging and use_autoserv:
286 self._install_using_send_file(host, autodir)
jadmanski0afbb632008-06-06 21:10:57 +0000287 else:
jadmanski69bdaac2010-07-28 16:27:20 +0000288 host.send_file(self.source_material, autodir, delete_dest=True)
Dan Shib669cbd2013-09-13 11:17:17 -0700289 logging.info("Installation of autotest completed from %s",
290 self.source_material)
jadmanski0afbb632008-06-06 21:10:57 +0000291 self.installed = True
292 return
mbligh91334902007-09-28 01:47:59 +0000293
jadmanski0afbb632008-06-06 21:10:57 +0000294 # if that fails try to install using svn
295 if utils.run('which svn').exit_status:
Dale Curtis74a314b2011-06-23 14:55:46 -0700296 raise error.AutoservError('svn not found on target machine: %s' %
297 host.name)
jadmanski0afbb632008-06-06 21:10:57 +0000298 try:
mbligh78bf5352008-07-11 20:27:36 +0000299 host.run('svn checkout %s %s' % (AUTOTEST_SVN, autodir))
jadmanski0afbb632008-06-06 21:10:57 +0000300 except error.AutoservRunError, e:
mbligh78bf5352008-07-11 20:27:36 +0000301 host.run('svn checkout %s %s' % (AUTOTEST_HTTP, autodir))
Dan Shib669cbd2013-09-13 11:17:17 -0700302 logging.info("Installation of autotest completed using SVN.")
jadmanski0afbb632008-06-06 21:10:57 +0000303 self.installed = True
mbligh91334902007-09-28 01:47:59 +0000304
305
jadmanski7c7aff32009-03-25 22:43:07 +0000306 def uninstall(self, host=None):
307 """
308 Uninstall (i.e. delete) autotest. Removes the autotest client install
309 from the specified host.
310
311 @params host a Host instance from which the client will be removed
312 """
313 if not self.installed:
314 return
315 if not host:
316 host = self.host
317 autodir = host.get_autodir()
318 if not autodir:
319 return
320
321 # perform the actual uninstall
322 host.run("rm -rf %s" % utils.sh_escape(autodir), ignore_status=True)
323 host.set_autodir(None)
324 self.installed = False
325
326
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700327 def get(self, location=None):
jadmanski0afbb632008-06-06 21:10:57 +0000328 if not location:
329 location = os.path.join(self.serverdir, '../client')
330 location = os.path.abspath(location)
331 # If there's stuff run on our client directory already, it
332 # can cause problems. Try giving it a quick clean first.
333 cwd = os.getcwd()
334 os.chdir(location)
showard4b976072009-10-20 23:50:08 +0000335 try:
336 utils.system('tools/make_clean', ignore_status=True)
337 finally:
338 os.chdir(cwd)
jadmanski0afbb632008-06-06 21:10:57 +0000339 super(BaseAutotest, self).get(location)
340 self.got = True
mblighdcd57a82007-07-11 23:06:47 +0000341
342
mblighe7d9c602009-07-02 19:02:33 +0000343 def run(self, control_file, results_dir='.', host=None, timeout=None,
344 tag=None, parallel_flag=False, background=False,
Dan Shib669cbd2013-09-13 11:17:17 -0700345 client_disconnect_timeout=None, use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000346 """
347 Run an autotest job on the remote machine.
mbligh9a3f5e52008-05-28 21:21:43 +0000348
mblighe7d9c602009-07-02 19:02:33 +0000349 @param control_file: An open file-like-obj of the control file.
350 @param results_dir: A str path where the results should be stored
351 on the local filesystem.
352 @param host: A Host instance on which the control file should
353 be run.
354 @param timeout: Maximum number of seconds to wait for the run or None.
355 @param tag: Tag name for the client side instance of autotest.
356 @param parallel_flag: Flag set when multiple jobs are run at the
357 same time.
358 @param background: Indicates that the client should be launched as
359 a background job; the code calling run will be responsible
360 for monitoring the client and collecting the results.
361 @param client_disconnect_timeout: Seconds to wait for the remote host
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700362 to come back after a reboot. Defaults to the host setting for
363 DEFAULT_REBOOT_TIMEOUT.
mblighe7d9c602009-07-02 19:02:33 +0000364
365 @raises AutotestRunError: If there is a problem executing
366 the control file.
jadmanski0afbb632008-06-06 21:10:57 +0000367 """
Dan Shib669cbd2013-09-13 11:17:17 -0700368 host = self._get_host_and_setup(host, use_packaging=use_packaging)
xixuan02b6fee2017-02-01 18:35:20 -0800369 logging.debug('Autotest job starts on remote host: %s',
370 host.hostname)
jadmanski0afbb632008-06-06 21:10:57 +0000371 results_dir = os.path.abspath(results_dir)
mblighc1cbc992008-05-27 20:01:45 +0000372
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700373 if client_disconnect_timeout is None:
374 client_disconnect_timeout = host.DEFAULT_REBOOT_TIMEOUT
375
jadmanski0afbb632008-06-06 21:10:57 +0000376 if tag:
377 results_dir = os.path.join(results_dir, tag)
mblighc1cbc992008-05-27 20:01:45 +0000378
mblighb3c0c912008-11-27 00:32:45 +0000379 atrun = _Run(host, results_dir, tag, parallel_flag, background)
jadmanski6dadd832009-02-05 23:39:27 +0000380 self._do_run(control_file, results_dir, host, atrun, timeout,
Dan Shib669cbd2013-09-13 11:17:17 -0700381 client_disconnect_timeout, use_packaging=use_packaging)
mblighd8b39252008-03-20 21:15:03 +0000382
383
Dan Shib669cbd2013-09-13 11:17:17 -0700384 def _get_host_and_setup(self, host, use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000385 if not host:
386 host = self.host
387 if not self.installed:
Dan Shib669cbd2013-09-13 11:17:17 -0700388 self.install(host, use_packaging=use_packaging)
mbligh91334902007-09-28 01:47:59 +0000389
jadmanski0afbb632008-06-06 21:10:57 +0000390 host.wait_up(timeout=30)
391 return host
mblighd8b39252008-03-20 21:15:03 +0000392
393
jadmanski6dadd832009-02-05 23:39:27 +0000394 def _do_run(self, control_file, results_dir, host, atrun, timeout,
Dan Shib669cbd2013-09-13 11:17:17 -0700395 client_disconnect_timeout, use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000396 try:
397 atrun.verify_machine()
398 except:
showardb18134f2009-03-20 20:52:18 +0000399 logging.error("Verify failed on %s. Reinstalling autotest",
400 host.hostname)
jadmanski0afbb632008-06-06 21:10:57 +0000401 self.install(host)
Fang Dengb9cd83c2015-01-27 10:16:08 -0800402 atrun.verify_machine()
jadmanski0afbb632008-06-06 21:10:57 +0000403 debug = os.path.join(results_dir, 'debug')
404 try:
405 os.makedirs(debug)
mbligh09108442008-10-15 16:27:38 +0000406 except Exception:
jadmanski0afbb632008-06-06 21:10:57 +0000407 pass
mbligh9a3f5e52008-05-28 21:21:43 +0000408
mbligh09108442008-10-15 16:27:38 +0000409 delete_file_list = [atrun.remote_control_file,
410 atrun.remote_control_file + '.state',
411 atrun.manual_control_file,
412 atrun.manual_control_file + '.state']
413 cmd = ';'.join('rm -f ' + control for control in delete_file_list)
414 host.run(cmd, ignore_status=True)
mbligh9a3f5e52008-05-28 21:21:43 +0000415
Dale Curtis386eea72011-09-21 18:43:04 -0700416 tmppath = utils.get(control_file, local_copy=True)
mblighc5ddfd12008-08-04 17:15:00 +0000417
jadmanskicb0e1612009-02-27 18:03:10 +0000418 # build up the initialization prologue for the control file
419 prologue_lines = []
jadmanski23afbec2008-09-17 18:12:07 +0000420
mbligh2f076832010-03-30 17:08:20 +0000421 # Add the additional user arguments
jadmanski808f4b12010-04-09 22:30:31 +0000422 prologue_lines.append("args = %r\n" % self.job.args)
mbligh2f076832010-03-30 17:08:20 +0000423
mbligh09108442008-10-15 16:27:38 +0000424 # If the packaging system is being used, add the repository list.
mblighddc9a402010-01-15 20:33:34 +0000425 repos = None
mblighc5ddfd12008-08-04 17:15:00 +0000426 try:
Dan Shib669cbd2013-09-13 11:17:17 -0700427 if use_packaging:
428 repos = self.get_fetch_location()
429 prologue_lines.append('job.add_repository(%s)\n' % repos)
430 else:
431 logging.debug('use_packaging is set to False, do not add any '
432 'repository.')
mblighc5ddfd12008-08-04 17:15:00 +0000433 except global_config.ConfigError, e:
mblighddc9a402010-01-15 20:33:34 +0000434 # If repos is defined packaging is enabled so log the error
435 if repos:
436 logging.error(e)
mblighc5ddfd12008-08-04 17:15:00 +0000437
jadmanskie2eef7b2009-03-03 23:55:13 +0000438 # on full-size installs, turn on any profilers the server is using
jadmanski27b52912009-08-14 17:09:15 +0000439 if not atrun.background:
jadmanskie2eef7b2009-03-03 23:55:13 +0000440 running_profilers = host.job.profilers.add_log.iteritems()
441 for profiler, (args, dargs) in running_profilers:
442 call_args = [repr(profiler)]
443 call_args += [repr(arg) for arg in args]
444 call_args += ["%s=%r" % item for item in dargs.iteritems()]
445 prologue_lines.append("job.profilers.add(%s)\n"
446 % ", ".join(call_args))
447 cfile = "".join(prologue_lines)
448
mbligh09108442008-10-15 16:27:38 +0000449 cfile += open(tmppath).read()
450 open(tmppath, "w").write(cfile)
mblighc5ddfd12008-08-04 17:15:00 +0000451
jadmanskic09fc152008-10-15 17:56:59 +0000452 # Create and copy state file to remote_control_file + '.state'
mblighfc3da5b2010-01-06 18:37:22 +0000453 state_file = host.job.preprocess_client_state()
mblighfbf73ae2009-12-19 05:22:42 +0000454 host.send_file(state_file, atrun.remote_control_file + '.init.state')
jadmanskic09fc152008-10-15 17:56:59 +0000455 os.remove(state_file)
456
mblighc5ddfd12008-08-04 17:15:00 +0000457 # Copy control_file to remote_control_file on the host
jadmanski0afbb632008-06-06 21:10:57 +0000458 host.send_file(tmppath, atrun.remote_control_file)
459 if os.path.abspath(tmppath) != os.path.abspath(control_file):
460 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000461
jadmanski6bb32d72009-03-19 20:25:24 +0000462 atrun.execute_control(
jadmanski6dadd832009-02-05 23:39:27 +0000463 timeout=timeout,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800464 client_disconnect_timeout=client_disconnect_timeout)
jadmanski23afbec2008-09-17 18:12:07 +0000465
466
harpreetf531d072016-04-19 18:37:26 -0700467 @classmethod
468 def _check_client_test_result(cls, host, test_name):
469 """
470 Check result of client test.
471 Autotest will store results in the file name status.
472 We check that second to last line in that file begins with 'END GOOD'
473
474 @raises TestFail: If client test does not pass.
475 """
476 client_result_dir = '%s/results/default' % host.autodir
477 command = 'tail -2 %s/status | head -1' % client_result_dir
478 status = host.run(command).stdout.strip()
479 logging.info(status)
480 if status[:8] != 'END GOOD':
481 raise error.TestFail('%s client test did not pass.' % test_name)
482
483
jadmanski0afbb632008-06-06 21:10:57 +0000484 def run_timed_test(self, test_name, results_dir='.', host=None,
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700485 timeout=None, parallel_flag=False, background=False,
486 client_disconnect_timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000487 """
488 Assemble a tiny little control file to just run one test,
489 and run it as an autotest client-side test
490 """
491 if not host:
492 host = self.host
493 if not self.installed:
494 self.install(host)
495 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
496 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
497 control = "job.run_test(%s)\n" % cmd
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700498 self.run(control, results_dir, host, timeout=timeout,
Andrew Bresticker2da1b762013-01-15 16:11:18 -0800499 parallel_flag=parallel_flag, background=background,
500 client_disconnect_timeout=client_disconnect_timeout)
mbligh0e4613b2007-10-29 16:55:07 +0000501
harpreetf0452062016-04-21 11:22:46 -0700502 if dargs.get('check_client_result', False):
503 self._check_client_test_result(host, test_name)
harpreetf531d072016-04-19 18:37:26 -0700504
mbligh0e4613b2007-10-29 16:55:07 +0000505
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700506 def run_test(self, test_name, results_dir='.', host=None,
Andrew Bresticker2da1b762013-01-15 16:11:18 -0800507 parallel_flag=False, background=False,
508 client_disconnect_timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000509 self.run_timed_test(test_name, results_dir, host, timeout=None,
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700510 parallel_flag=parallel_flag, background=background,
Andrew Bresticker2da1b762013-01-15 16:11:18 -0800511 client_disconnect_timeout=client_disconnect_timeout,
jadmanskic98c4702009-01-05 15:50:06 +0000512 *args, **dargs)
mblighd54832b2007-07-25 16:46:56 +0000513
514
jadmanski69bdaac2010-07-28 16:27:20 +0000515class _BaseRun(object):
jadmanski0afbb632008-06-06 21:10:57 +0000516 """
517 Represents a run of autotest control file. This class maintains
518 all the state necessary as an autotest control file is executed.
mblighdcd57a82007-07-11 23:06:47 +0000519
jadmanski0afbb632008-06-06 21:10:57 +0000520 It is not intended to be used directly, rather control files
521 should be run using the run method in Autotest.
522 """
mblighb3c0c912008-11-27 00:32:45 +0000523 def __init__(self, host, results_dir, tag, parallel_flag, background):
jadmanski0afbb632008-06-06 21:10:57 +0000524 self.host = host
525 self.results_dir = results_dir
526 self.env = host.env
527 self.tag = tag
528 self.parallel_flag = parallel_flag
mblighb3c0c912008-11-27 00:32:45 +0000529 self.background = background
showardad812bf2009-10-20 23:49:56 +0000530 self.autodir = Autotest.get_installed_autodir(self.host)
mbligh78bf5352008-07-11 20:27:36 +0000531 control = os.path.join(self.autodir, 'control')
jadmanski0afbb632008-06-06 21:10:57 +0000532 if tag:
mbligh78bf5352008-07-11 20:27:36 +0000533 control += '.' + tag
534 self.manual_control_file = control
535 self.remote_control_file = control + '.autoserv'
lmr6d08b3c2009-11-18 19:26:38 +0000536 self.config_file = os.path.join(self.autodir, 'global_config.ini')
mblighdc735a22007-08-02 16:54:37 +0000537
538
jadmanski0afbb632008-06-06 21:10:57 +0000539 def verify_machine(self):
540 binary = os.path.join(self.autodir, 'bin/autotest')
541 try:
542 self.host.run('ls %s > /dev/null 2>&1' % binary)
543 except:
lmrd6d27ed2009-12-08 19:58:33 +0000544 raise error.AutoservInstallError(
545 "Autotest does not appear to be installed")
mblighdc735a22007-08-02 16:54:37 +0000546
jadmanski0afbb632008-06-06 21:10:57 +0000547 if not self.parallel_flag:
548 tmpdir = os.path.join(self.autodir, 'tmp')
549 download = os.path.join(self.autodir, 'tests/download')
550 self.host.run('umount %s' % tmpdir, ignore_status=True)
551 self.host.run('umount %s' % download, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000552
jadmanski6dadd832009-02-05 23:39:27 +0000553
554 def get_base_cmd_args(self, section):
showard234b2962009-07-28 20:02:30 +0000555 args = ['--verbose']
jadmanski0afbb632008-06-06 21:10:57 +0000556 if section > 0:
jadmanski6dadd832009-02-05 23:39:27 +0000557 args.append('-c')
jadmanski0afbb632008-06-06 21:10:57 +0000558 if self.tag:
jadmanski6dadd832009-02-05 23:39:27 +0000559 args.append('-t %s' % self.tag)
jadmanski0afbb632008-06-06 21:10:57 +0000560 if self.host.job.use_external_logging():
jadmanski6dadd832009-02-05 23:39:27 +0000561 args.append('-l')
mblighce955fc2009-08-24 21:59:02 +0000562 if self.host.hostname:
563 args.append('--hostname=%s' % self.host.hostname)
mbligh0d0f67d2009-11-06 03:15:03 +0000564 args.append('--user=%s' % self.host.job.user)
mblighce955fc2009-08-24 21:59:02 +0000565
jadmanski6dadd832009-02-05 23:39:27 +0000566 args.append(self.remote_control_file)
567 return args
568
569
570 def get_background_cmd(self, section):
571 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotest_client')]
572 cmd += self.get_base_cmd_args(section)
jadmanski69bdaac2010-07-28 16:27:20 +0000573 cmd += ['>/dev/null', '2>/dev/null', '&']
jadmanski6dadd832009-02-05 23:39:27 +0000574 return ' '.join(cmd)
575
576
577 def get_daemon_cmd(self, section, monitor_dir):
578 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotestd'),
579 monitor_dir, '-H autoserv']
580 cmd += self.get_base_cmd_args(section)
jadmanski69bdaac2010-07-28 16:27:20 +0000581 cmd += ['>/dev/null', '2>/dev/null', '&']
jadmanski6dadd832009-02-05 23:39:27 +0000582 return ' '.join(cmd)
583
584
585 def get_monitor_cmd(self, monitor_dir, stdout_read, stderr_read):
586 cmd = [os.path.join(self.autodir, 'bin', 'autotestd_monitor'),
587 monitor_dir, str(stdout_read), str(stderr_read)]
jadmanski0afbb632008-06-06 21:10:57 +0000588 return ' '.join(cmd)
mblighadf2aab2007-11-29 18:16:43 +0000589
mblighd8b39252008-03-20 21:15:03 +0000590
jadmanski4d03cf62010-03-04 18:32:28 +0000591 def get_client_log(self):
592 """Find what the "next" client.* prefix should be
593
594 @returns A string of the form client.INTEGER that should be prefixed
595 to all client debug log files.
596 """
597 max_digit = -1
598 debug_dir = os.path.join(self.results_dir, 'debug')
599 client_logs = glob.glob(os.path.join(debug_dir, 'client.*.*'))
600 for log in client_logs:
601 _, number, _ = log.split('.', 2)
602 if number.isdigit():
603 max_digit = max(max_digit, int(number))
604 return 'client.%d' % (max_digit + 1)
605
606
607 def copy_client_config_file(self, client_log_prefix=None):
608 """
609 Create and copy the client config file based on the server config.
610
611 @param client_log_prefix: Optional prefix to prepend to log files.
612 """
613 client_config_file = self._create_client_config_file(client_log_prefix)
614 self.host.send_file(client_config_file, self.config_file)
615 os.remove(client_config_file)
616
617
618 def _create_client_config_file(self, client_log_prefix=None):
619 """
620 Create a temporary file with the [CLIENT] section configuration values
621 taken from the server global_config.ini.
622
623 @param client_log_prefix: Optional prefix to prepend to log files.
624
625 @return: Path of the temporary file generated.
626 """
627 config = global_config.global_config.get_section_values('CLIENT')
628 if client_log_prefix:
629 config.set('CLIENT', 'default_logging_name', client_log_prefix)
630 return self._create_aux_file(config.write)
631
632
633 def _create_aux_file(self, func, *args):
634 """
635 Creates a temporary file and writes content to it according to a
636 content creation function. The file object is appended to *args, which
637 is then passed to the content creation function
638
639 @param func: Function that will be used to write content to the
640 temporary file.
641 @param *args: List of parameters that func takes.
642 @return: Path to the temporary file that was created.
643 """
644 fd, path = tempfile.mkstemp(dir=self.host.job.tmpdir)
645 aux_file = os.fdopen(fd, "w")
646 try:
647 list_args = list(args)
648 list_args.append(aux_file)
649 func(*list_args)
650 finally:
651 aux_file.close()
652 return path
mblighd8b39252008-03-20 21:15:03 +0000653
654
jadmanskib264ed02009-01-12 23:54:27 +0000655 @staticmethod
656 def is_client_job_finished(last_line):
657 return bool(re.match(r'^END .*\t----\t----\t.*$', last_line))
658
659
660 @staticmethod
661 def is_client_job_rebooting(last_line):
662 return bool(re.match(r'^\t*GOOD\t----\treboot\.start.*$', last_line))
663
664
beeps607428b2013-03-25 16:43:20 -0700665 def _diagnose_dut(self, old_boot_id=None):
666 """
667 Run diagnostic checks on a DUT.
668
669 1. ping: A dead host will not respond to pings.
670 2. ssh (happens with 3.): DUT hangs usually fail in authentication
671 but respond to pings.
672 3. Check if a reboot occured: A healthy but unexpected reboot leaves the
673 host running with a new boot id.
674
675 This method will always raise an exception from the AutotestFailure
676 family and should only get called when the reason for a test failing
677 is ambiguous.
678
679 @raises AutotestDeviceNotPingable: If the DUT doesn't respond to ping.
680 @raises AutotestDeviceNotSSHable: If we cannot SSH into the DUT.
681 @raises AutotestDeviceRebooted: If the boot id changed.
682 @raises AutotestAbort: If none of the above exceptions were raised.
683 Since we have no recourse we must abort at this stage.
684 """
685 msg = 'Autotest client terminated unexpectedly: '
686 if utils.ping(self.host.hostname, tries=1, deadline=1) != 0:
687 msg += 'DUT is no longer pingable, it may have rebooted or hung.\n'
688 raise AutotestDeviceNotPingable(msg)
689
690 if old_boot_id:
691 try:
692 new_boot_id = self.host.get_boot_id(timeout=60)
693 except Exception as e:
694 msg += ('DUT is pingable but not SSHable, it most likely'
beeps14768812013-09-25 12:58:45 -0700695 ' sporadically rebooted during testing. %s\n' % str(e))
beeps607428b2013-03-25 16:43:20 -0700696 raise AutotestDeviceNotSSHable(msg)
697 else:
698 if new_boot_id != old_boot_id:
699 msg += 'DUT rebooted during the test run.\n'
700 raise AutotestDeviceRebooted(msg)
701
702 msg += ('DUT is pingable, SSHable and did NOT restart '
703 'un-expectedly. We probably lost connectivity during the '
704 'test.')
705 else:
706 msg += ('DUT is pingable, could not determine if an un-expected '
707 'reboot occured during the test.')
708
709 raise AutotestAbort(msg)
710
711
beeps07f53b92013-01-08 12:55:10 -0800712 def log_unexpected_abort(self, stderr_redirector, old_boot_id=None):
713 """
beeps607428b2013-03-25 16:43:20 -0700714 Logs that something unexpected happened, then tries to diagnose the
715 failure. The purpose of this function is only to close out the status
716 log with the appropriate error message, not to critically terminate
717 the program.
beeps07f53b92013-01-08 12:55:10 -0800718
719 @param stderr_redirector: log stream.
720 @param old_boot_id: boot id used to infer if a reboot occured.
721 """
jadmanskia61edad2009-05-21 22:17:49 +0000722 stderr_redirector.flush_all_buffers()
beeps607428b2013-03-25 16:43:20 -0700723 try:
724 self._diagnose_dut(old_boot_id)
725 except AutotestFailure as e:
726 self.host.job.record('END ABORT', None, None, str(e))
jadmanskib264ed02009-01-12 23:54:27 +0000727
728
jadmanski6dadd832009-02-05 23:39:27 +0000729 def _execute_in_background(self, section, timeout):
730 full_cmd = self.get_background_cmd(section)
731 devnull = open(os.devnull, "w")
mblighd8b39252008-03-20 21:15:03 +0000732
jadmanski4d03cf62010-03-04 18:32:28 +0000733 self.copy_client_config_file(self.get_client_log())
734
mbligh0d0f67d2009-11-06 03:15:03 +0000735 self.host.job.push_execution_context(self.results_dir)
jadmanski0afbb632008-06-06 21:10:57 +0000736 try:
jadmanski0afbb632008-06-06 21:10:57 +0000737 result = self.host.run(full_cmd, ignore_status=True,
738 timeout=timeout,
jadmanski6dadd832009-02-05 23:39:27 +0000739 stdout_tee=devnull,
740 stderr_tee=devnull)
jadmanski0afbb632008-06-06 21:10:57 +0000741 finally:
mbligh0d0f67d2009-11-06 03:15:03 +0000742 self.host.job.pop_execution_context()
jadmanski6dadd832009-02-05 23:39:27 +0000743
744 return result
745
746
747 @staticmethod
748 def _strip_stderr_prologue(stderr):
749 """Strips the 'standard' prologue that get pre-pended to every
750 remote command and returns the text that was actually written to
751 stderr by the remote command."""
752 stderr_lines = stderr.split("\n")[1:]
753 if not stderr_lines:
754 return ""
755 elif stderr_lines[0].startswith("NOTE: autotestd_monitor"):
756 del stderr_lines[0]
757 return "\n".join(stderr_lines)
758
759
760 def _execute_daemon(self, section, timeout, stderr_redirector,
761 client_disconnect_timeout):
762 monitor_dir = self.host.get_tmp_dir()
763 daemon_cmd = self.get_daemon_cmd(section, monitor_dir)
jadmanski4d03cf62010-03-04 18:32:28 +0000764
765 # grab the location for the server-side client log file
766 client_log_prefix = self.get_client_log()
767 client_log_path = os.path.join(self.results_dir, 'debug',
768 client_log_prefix + '.log')
769 client_log = open(client_log_path, 'w', 0)
770 self.copy_client_config_file(client_log_prefix)
jadmanski6dadd832009-02-05 23:39:27 +0000771
772 stdout_read = stderr_read = 0
mbligh0d0f67d2009-11-06 03:15:03 +0000773 self.host.job.push_execution_context(self.results_dir)
jadmanski6dadd832009-02-05 23:39:27 +0000774 try:
775 self.host.run(daemon_cmd, ignore_status=True, timeout=timeout)
jadmanski91d56a92009-04-01 15:20:40 +0000776 disconnect_warnings = []
jadmanski6dadd832009-02-05 23:39:27 +0000777 while True:
778 monitor_cmd = self.get_monitor_cmd(monitor_dir, stdout_read,
779 stderr_read)
780 try:
781 result = self.host.run(monitor_cmd, ignore_status=True,
782 timeout=timeout,
783 stdout_tee=client_log,
784 stderr_tee=stderr_redirector)
785 except error.AutoservRunError, e:
786 result = e.result_obj
787 result.exit_status = None
jadmanski91d56a92009-04-01 15:20:40 +0000788 disconnect_warnings.append(e.description)
789
jadmanski6dadd832009-02-05 23:39:27 +0000790 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000791 "Autotest client was disconnected: %s" % e.description,
792 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000793 except error.AutoservSSHTimeout:
794 result = utils.CmdResult(monitor_cmd, "", "", None, 0)
795 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000796 "Attempt to connect to Autotest client timed out",
797 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000798
799 stdout_read += len(result.stdout)
800 stderr_read += len(self._strip_stderr_prologue(result.stderr))
801
802 if result.exit_status is not None:
Simran Basibca10a62013-01-24 15:52:35 -0800803 # TODO (crosbug.com/38224)- sbasi: Remove extra logging.
804 logging.debug('Result exit status is %d.',
805 result.exit_status)
jadmanski6dadd832009-02-05 23:39:27 +0000806 return result
807 elif not self.host.wait_up(client_disconnect_timeout):
808 raise error.AutoservSSHTimeout(
809 "client was disconnected, reconnect timed out")
810 finally:
jadmanski4d03cf62010-03-04 18:32:28 +0000811 client_log.close()
mbligh0d0f67d2009-11-06 03:15:03 +0000812 self.host.job.pop_execution_context()
jadmanski6dadd832009-02-05 23:39:27 +0000813
814
815 def execute_section(self, section, timeout, stderr_redirector,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800816 client_disconnect_timeout):
showardb18134f2009-03-20 20:52:18 +0000817 logging.info("Executing %s/bin/autotest %s/control phase %d",
818 self.autodir, self.autodir, section)
jadmanski6dadd832009-02-05 23:39:27 +0000819
820 if self.background:
821 result = self._execute_in_background(section, timeout)
822 else:
823 result = self._execute_daemon(section, timeout, stderr_redirector,
824 client_disconnect_timeout)
825
826 last_line = stderr_redirector.last_line
mbligh2bf2db62007-11-27 00:53:18 +0000827
jadmanskib264ed02009-01-12 23:54:27 +0000828 # check if we failed hard enough to warrant an exception
jadmanski0afbb632008-06-06 21:10:57 +0000829 if result.exit_status == 1:
jadmanskib264ed02009-01-12 23:54:27 +0000830 err = error.AutotestRunError("client job was aborted")
831 elif not self.background and not result.stderr:
832 err = error.AutotestRunError(
jadmanskie4130532009-03-17 18:01:28 +0000833 "execute_section %s failed to return anything\n"
834 "stdout:%s\n" % (section, result.stdout))
jadmanskib264ed02009-01-12 23:54:27 +0000835 else:
836 err = None
mbligh0e4613b2007-10-29 16:55:07 +0000837
jadmanskib264ed02009-01-12 23:54:27 +0000838 # log something if the client failed AND never finished logging
Dale Curtis9285ddf2011-01-05 11:47:24 -0800839 if err and not self.is_client_job_finished(last_line):
jadmanskia61edad2009-05-21 22:17:49 +0000840 self.log_unexpected_abort(stderr_redirector)
jadmanskib264ed02009-01-12 23:54:27 +0000841
842 if err:
843 raise err
844 else:
845 return stderr_redirector.last_line
jadmanski4600e342008-10-29 22:54:00 +0000846
847
jadmanskic0354912010-01-12 15:57:29 +0000848 def _wait_for_reboot(self, old_boot_id):
showardb18134f2009-03-20 20:52:18 +0000849 logging.info("Client is rebooting")
850 logging.info("Waiting for client to halt")
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700851 if not self.host.wait_down(self.host.WAIT_DOWN_REBOOT_TIMEOUT,
852 old_boot_id=old_boot_id):
jadmanski4600e342008-10-29 22:54:00 +0000853 err = "%s failed to shutdown after %d"
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700854 err %= (self.host.hostname, self.host.WAIT_DOWN_REBOOT_TIMEOUT)
jadmanski4600e342008-10-29 22:54:00 +0000855 raise error.AutotestRunError(err)
showardb18134f2009-03-20 20:52:18 +0000856 logging.info("Client down, waiting for restart")
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700857 if not self.host.wait_up(self.host.DEFAULT_REBOOT_TIMEOUT):
jadmanski4600e342008-10-29 22:54:00 +0000858 # since reboot failed
859 # hardreset the machine once if possible
860 # before failing this control file
861 warning = "%s did not come back up, hard resetting"
862 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000863 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000864 try:
865 self.host.hardreset(wait=False)
mblighd99d3b272008-12-22 14:41:27 +0000866 except (AttributeError, error.AutoservUnsupportedError):
jadmanski4600e342008-10-29 22:54:00 +0000867 warning = "Hard reset unsupported on %s"
868 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000869 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000870 raise error.AutotestRunError("%s failed to boot after %ds" %
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700871 (self.host.hostname,
872 self.host.DEFAULT_REBOOT_TIMEOUT))
jadmanski4600e342008-10-29 22:54:00 +0000873 self.host.reboot_followup()
mblighdc735a22007-08-02 16:54:37 +0000874
875
Dale Curtis9285ddf2011-01-05 11:47:24 -0800876 def execute_control(self, timeout=None, client_disconnect_timeout=None):
jadmanski6bb32d72009-03-19 20:25:24 +0000877 if not self.background:
878 collector = log_collector(self.host, self.tag, self.results_dir)
879 hostname = self.host.hostname
880 remote_results = collector.client_results_dir
881 local_results = collector.server_results_dir
882 self.host.job.add_client_log(hostname, remote_results,
883 local_results)
jadmanski52053632010-06-11 21:08:10 +0000884 job_record_context = self.host.job.get_record_context()
jadmanski6bb32d72009-03-19 20:25:24 +0000885
jadmanski0afbb632008-06-06 21:10:57 +0000886 section = 0
jadmanski4600e342008-10-29 22:54:00 +0000887 start_time = time.time()
888
jadmanski043e1132008-11-19 17:10:32 +0000889 logger = client_logger(self.host, self.tag, self.results_dir)
jadmanski4600e342008-10-29 22:54:00 +0000890 try:
891 while not timeout or time.time() < start_time + timeout:
892 if timeout:
893 section_timeout = start_time + timeout - time.time()
894 else:
895 section_timeout = None
jadmanskic0354912010-01-12 15:57:29 +0000896 boot_id = self.host.get_boot_id()
jadmanski4600e342008-10-29 22:54:00 +0000897 last = self.execute_section(section, section_timeout,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800898 logger, client_disconnect_timeout)
mblighb3c0c912008-11-27 00:32:45 +0000899 if self.background:
900 return
jadmanski4600e342008-10-29 22:54:00 +0000901 section += 1
jadmanskib264ed02009-01-12 23:54:27 +0000902 if self.is_client_job_finished(last):
showardb18134f2009-03-20 20:52:18 +0000903 logging.info("Client complete")
jadmanski4600e342008-10-29 22:54:00 +0000904 return
jadmanskib264ed02009-01-12 23:54:27 +0000905 elif self.is_client_job_rebooting(last):
jadmanski79ab9282008-11-11 17:53:12 +0000906 try:
jadmanskic0354912010-01-12 15:57:29 +0000907 self._wait_for_reboot(boot_id)
jadmanski79ab9282008-11-11 17:53:12 +0000908 except error.AutotestRunError, e:
jadmanski043e1132008-11-19 17:10:32 +0000909 self.host.job.record("ABORT", None, "reboot", str(e))
910 self.host.job.record("END ABORT", None, None, str(e))
jadmanski79ab9282008-11-11 17:53:12 +0000911 raise
jadmanski4600e342008-10-29 22:54:00 +0000912 continue
913
beeps607428b2013-03-25 16:43:20 -0700914 # If a test fails without probable cause we try to bucket it's
915 # failure into one of 2 categories. If we can determine the
916 # current state of the device and it is suspicious, we close the
917 # status lines indicating a failure. If we either cannot
918 # determine the state of the device, or it appears totally
919 # healthy, we give up and abort.
920 try:
921 self._diagnose_dut(boot_id)
922 except AutotestDeviceError as e:
923 # The status lines of the test are pretty much tailed to
924 # our log, with indentation, from the client job on the DUT.
925 # So if the DUT goes down unexpectedly we'll end up with a
926 # malformed status log unless we manually unwind the status
927 # stack. Ideally we would want to write a nice wrapper like
928 # server_job methods run_reboot, run_group but they expect
929 # reboots and we don't.
930 self.host.job.record('FAIL', None, None, str(e))
931 self.host.job.record('END FAIL', None, None)
932 self.host.job.record('END GOOD', None, None)
Dan Shib03ea9d2013-08-15 17:13:27 -0700933 self.host.job.failed_with_device_error = True
beeps607428b2013-03-25 16:43:20 -0700934 return
935 except AutotestAbort as e:
936 self.host.job.record('ABORT', None, None, str(e))
937 self.host.job.record('END ABORT', None, None)
jadmanski4600e342008-10-29 22:54:00 +0000938
beeps607428b2013-03-25 16:43:20 -0700939 # give the client machine a chance to recover from a crash
940 self.host.wait_up(
941 self.host.HOURS_TO_WAIT_FOR_RECOVERY * 3600)
942 msg = ("Aborting - unexpected final status message from "
943 "client on %s: %s\n") % (self.host.hostname, last)
944 raise error.AutotestRunError(msg)
jadmanski4600e342008-10-29 22:54:00 +0000945 finally:
xixuan02b6fee2017-02-01 18:35:20 -0800946 logging.debug('Autotest job finishes running. Below is the '
947 'post-processing operations.')
jadmanski043e1132008-11-19 17:10:32 +0000948 logger.close()
jadmanski6bb32d72009-03-19 20:25:24 +0000949 if not self.background:
950 collector.collect_client_job_results()
jadmanski4d03cf62010-03-04 18:32:28 +0000951 collector.remove_redundant_client_logs()
mblighfc3da5b2010-01-06 18:37:22 +0000952 state_file = os.path.basename(self.remote_control_file
953 + '.state')
954 state_path = os.path.join(self.results_dir, state_file)
955 self.host.job.postprocess_client_state(state_path)
jadmanski6bb32d72009-03-19 20:25:24 +0000956 self.host.job.remove_client_log(hostname, remote_results,
957 local_results)
jadmanski52053632010-06-11 21:08:10 +0000958 job_record_context.restore()
mblighdcd57a82007-07-11 23:06:47 +0000959
xixuan02b6fee2017-02-01 18:35:20 -0800960 logging.debug('Autotest job finishes.')
961
jadmanski0afbb632008-06-06 21:10:57 +0000962 # should only get here if we timed out
963 assert timeout
964 raise error.AutotestTimeoutError()
mbligh0e4613b2007-10-29 16:55:07 +0000965
mblighdcd57a82007-07-11 23:06:47 +0000966
jadmanski043e1132008-11-19 17:10:32 +0000967class log_collector(object):
968 def __init__(self, host, client_tag, results_dir):
969 self.host = host
970 if not client_tag:
971 client_tag = "default"
972 self.client_results_dir = os.path.join(host.get_autodir(), "results",
973 client_tag)
974 self.server_results_dir = results_dir
975
976
977 def collect_client_job_results(self):
978 """ A method that collects all the current results of a running
979 client job into the results dir. By default does nothing as no
980 client job is running, but when running a client job you can override
981 this with something that will actually do something. """
982
983 # make an effort to wait for the machine to come up
984 try:
985 self.host.wait_up(timeout=30)
986 except error.AutoservError:
987 # don't worry about any errors, we'll try and
988 # get the results anyway
989 pass
990
jadmanski043e1132008-11-19 17:10:32 +0000991 # Copy all dirs in default to results_dir
992 try:
Prathmesh Prabhu7cc11532016-11-23 17:51:08 -0800993 with metrics.SecondsTimer(
994 'chromeos/autotest/job/log_collection_duration',
995 fields={'dut_host_name': self.host.hostname}):
996 self.host.get_file(
997 self.client_results_dir + '/',
998 self.server_results_dir,
999 preserve_symlinks=True)
jadmanski043e1132008-11-19 17:10:32 +00001000 except Exception:
1001 # well, don't stop running just because we couldn't get logs
showardb18134f2009-03-20 20:52:18 +00001002 e_msg = "Unexpected error copying test result logs, continuing ..."
1003 logging.error(e_msg)
jadmanski043e1132008-11-19 17:10:32 +00001004 traceback.print_exc(file=sys.stdout)
1005
1006
jadmanski4d03cf62010-03-04 18:32:28 +00001007 def remove_redundant_client_logs(self):
1008 """Remove client.*.log files in favour of client.*.DEBUG files."""
1009 debug_dir = os.path.join(self.server_results_dir, 'debug')
1010 debug_files = [f for f in os.listdir(debug_dir)
1011 if re.search(r'^client\.\d+\.DEBUG$', f)]
1012 for debug_file in debug_files:
1013 log_file = debug_file.replace('DEBUG', 'log')
1014 log_file = os.path.join(debug_dir, log_file)
1015 if os.path.exists(log_file):
1016 os.remove(log_file)
1017
1018
jadmanski043e1132008-11-19 17:10:32 +00001019# a file-like object for catching stderr from an autotest client and
1020# extracting status logs from it
Chris Sosa3ee5d5c2012-02-23 11:18:41 -08001021class BaseClientLogger(object):
jadmanski043e1132008-11-19 17:10:32 +00001022 """Partial file object to write to both stdout and
1023 the status log file. We only implement those methods
1024 utils.run() actually calls.
jadmanski043e1132008-11-19 17:10:32 +00001025 """
1026 status_parser = re.compile(r"^AUTOTEST_STATUS:([^:]*):(.*)$")
1027 test_complete_parser = re.compile(r"^AUTOTEST_TEST_COMPLETE:(.*)$")
jadmanskib1a51132009-08-07 16:45:50 +00001028 fetch_package_parser = re.compile(
1029 r"^AUTOTEST_FETCH_PACKAGE:([^:]*):([^:]*):(.*)$")
jadmanski043e1132008-11-19 17:10:32 +00001030 extract_indent = re.compile(r"^(\t*).*$")
jadmanskiefe4ebf2009-05-21 22:12:30 +00001031 extract_timestamp = re.compile(r".*\ttimestamp=(\d+)\t.*$")
jadmanski043e1132008-11-19 17:10:32 +00001032
1033 def __init__(self, host, tag, server_results_dir):
1034 self.host = host
1035 self.job = host.job
1036 self.log_collector = log_collector(host, tag, server_results_dir)
1037 self.leftover = ""
1038 self.last_line = ""
1039 self.logs = {}
jadmanskiefe4ebf2009-05-21 22:12:30 +00001040
1041
jadmanski043e1132008-11-19 17:10:32 +00001042 def _process_log_dict(self, log_dict):
1043 log_list = log_dict.pop("logs", [])
1044 for key in sorted(log_dict.iterkeys()):
1045 log_list += self._process_log_dict(log_dict.pop(key))
1046 return log_list
1047
1048
1049 def _process_logs(self):
1050 """Go through the accumulated logs in self.log and print them
1051 out to stdout and the status log. Note that this processes
1052 logs in an ordering where:
1053
1054 1) logs to different tags are never interleaved
1055 2) logs to x.y come before logs to x.y.z for all z
1056 3) logs to x.y come before x.z whenever y < z
1057
1058 Note that this will in general not be the same as the
1059 chronological ordering of the logs. However, if a chronological
1060 ordering is desired that one can be reconstructed from the
1061 status log by looking at timestamp lines."""
1062 log_list = self._process_log_dict(self.logs)
jadmanski2a89dac2010-06-11 14:32:58 +00001063 for entry in log_list:
1064 self.job.record_entry(entry, log_in_subdir=False)
jadmanski043e1132008-11-19 17:10:32 +00001065 if log_list:
jadmanski2a89dac2010-06-11 14:32:58 +00001066 self.last_line = log_list[-1].render()
jadmanski043e1132008-11-19 17:10:32 +00001067
1068
1069 def _process_quoted_line(self, tag, line):
1070 """Process a line quoted with an AUTOTEST_STATUS flag. If the
1071 tag is blank then we want to push out all the data we've been
1072 building up in self.logs, and then the newest line. If the
1073 tag is not blank, then push the line into the logs for handling
1074 later."""
jadmanski2a89dac2010-06-11 14:32:58 +00001075 entry = base_job.status_log_entry.parse(line)
1076 if entry is None:
1077 return # the line contains no status lines
jadmanski043e1132008-11-19 17:10:32 +00001078 if tag == "":
1079 self._process_logs()
jadmanski2a89dac2010-06-11 14:32:58 +00001080 self.job.record_entry(entry, log_in_subdir=False)
jadmanski043e1132008-11-19 17:10:32 +00001081 self.last_line = line
1082 else:
1083 tag_parts = [int(x) for x in tag.split(".")]
1084 log_dict = self.logs
1085 for part in tag_parts:
1086 log_dict = log_dict.setdefault(part, {})
1087 log_list = log_dict.setdefault("logs", [])
jadmanski2a89dac2010-06-11 14:32:58 +00001088 log_list.append(entry)
jadmanski043e1132008-11-19 17:10:32 +00001089
1090
jadmanskif37df842009-02-11 00:03:26 +00001091 def _process_info_line(self, line):
1092 """Check if line is an INFO line, and if it is, interpret any control
1093 messages (e.g. enabling/disabling warnings) that it may contain."""
1094 match = re.search(r"^\t*INFO\t----\t----(.*)\t[^\t]*$", line)
1095 if not match:
1096 return # not an INFO line
1097 for field in match.group(1).split('\t'):
1098 if field.startswith("warnings.enable="):
jadmanski16a7ff72009-04-01 18:19:53 +00001099 func = self.job.warning_manager.enable_warnings
jadmanskif37df842009-02-11 00:03:26 +00001100 elif field.startswith("warnings.disable="):
jadmanski16a7ff72009-04-01 18:19:53 +00001101 func = self.job.warning_manager.disable_warnings
jadmanskif37df842009-02-11 00:03:26 +00001102 else:
1103 continue
1104 warning_type = field.split("=", 1)[1]
jadmanski16a7ff72009-04-01 18:19:53 +00001105 func(warning_type)
jadmanskif37df842009-02-11 00:03:26 +00001106
1107
jadmanski043e1132008-11-19 17:10:32 +00001108 def _process_line(self, line):
1109 """Write out a line of data to the appropriate stream. Status
1110 lines sent by autotest will be prepended with
1111 "AUTOTEST_STATUS", and all other lines are ssh error
1112 messages."""
1113 status_match = self.status_parser.search(line)
1114 test_complete_match = self.test_complete_parser.search(line)
jadmanskib1a51132009-08-07 16:45:50 +00001115 fetch_package_match = self.fetch_package_parser.search(line)
jadmanski043e1132008-11-19 17:10:32 +00001116 if status_match:
1117 tag, line = status_match.groups()
jadmanskif37df842009-02-11 00:03:26 +00001118 self._process_info_line(line)
jadmanski043e1132008-11-19 17:10:32 +00001119 self._process_quoted_line(tag, line)
1120 elif test_complete_match:
jadmanskifcc0d5d2009-02-12 21:52:54 +00001121 self._process_logs()
jadmanski043e1132008-11-19 17:10:32 +00001122 fifo_path, = test_complete_match.groups()
mbligh060c4712009-12-29 02:43:35 +00001123 try:
1124 self.log_collector.collect_client_job_results()
1125 self.host.run("echo A > %s" % fifo_path)
1126 except Exception:
1127 msg = "Post-test log collection failed, continuing anyway"
1128 logging.exception(msg)
jadmanskib1a51132009-08-07 16:45:50 +00001129 elif fetch_package_match:
1130 pkg_name, dest_path, fifo_path = fetch_package_match.groups()
jadmanskiede7e242009-08-10 15:43:33 +00001131 serve_packages = global_config.global_config.get_config_value(
1132 "PACKAGES", "serve_packages_from_autoserv", type=bool)
1133 if serve_packages and pkg_name.endswith(".tar.bz2"):
1134 try:
1135 self._send_tarball(pkg_name, dest_path)
1136 except Exception:
1137 msg = "Package tarball creation failed, continuing anyway"
1138 logging.exception(msg)
mbligh060c4712009-12-29 02:43:35 +00001139 try:
1140 self.host.run("echo B > %s" % fifo_path)
1141 except Exception:
1142 msg = "Package tarball installation failed, continuing anyway"
1143 logging.exception(msg)
jadmanski043e1132008-11-19 17:10:32 +00001144 else:
showardb18134f2009-03-20 20:52:18 +00001145 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +00001146
1147
jadmanskiede7e242009-08-10 15:43:33 +00001148 def _send_tarball(self, pkg_name, remote_dest):
1149 name, pkg_type = self.job.pkgmgr.parse_tarball_name(pkg_name)
1150 src_dirs = []
1151 if pkg_type == 'test':
mbligh1f572e52010-04-01 17:15:53 +00001152 for test_dir in ['site_tests', 'tests']:
1153 src_dir = os.path.join(self.job.clientdir, test_dir, name)
1154 if os.path.exists(src_dir):
1155 src_dirs += [src_dir]
1156 if autoserv_prebuild:
1157 prebuild.setup(self.job.clientdir, src_dir)
1158 break
jadmanskiede7e242009-08-10 15:43:33 +00001159 elif pkg_type == 'profiler':
1160 src_dirs += [os.path.join(self.job.clientdir, 'profilers', name)]
mbligh1f572e52010-04-01 17:15:53 +00001161 if autoserv_prebuild:
1162 prebuild.setup(self.job.clientdir, src_dir)
jadmanskiede7e242009-08-10 15:43:33 +00001163 elif pkg_type == 'dep':
1164 src_dirs += [os.path.join(self.job.clientdir, 'deps', name)]
1165 elif pkg_type == 'client':
1166 return # you must already have a client to hit this anyway
1167 else:
1168 return # no other types are supported
1169
1170 # iterate over src_dirs until we find one that exists, then tar it
1171 for src_dir in src_dirs:
1172 if os.path.exists(src_dir):
1173 try:
1174 logging.info('Bundling %s into %s', src_dir, pkg_name)
1175 temp_dir = autotemp.tempdir(unique_id='autoserv-packager',
1176 dir=self.job.tmpdir)
1177 tarball_path = self.job.pkgmgr.tar_package(
mblighbccad482009-08-24 22:08:31 +00001178 pkg_name, src_dir, temp_dir.name, " .")
jadmanskiede7e242009-08-10 15:43:33 +00001179 self.host.send_file(tarball_path, remote_dest)
1180 finally:
1181 temp_dir.clean()
1182 return
1183
1184
jadmanski91d56a92009-04-01 15:20:40 +00001185 def log_warning(self, msg, warning_type):
jadmanski6dadd832009-02-05 23:39:27 +00001186 """Injects a WARN message into the current status logging stream."""
jadmanski91d56a92009-04-01 15:20:40 +00001187 timestamp = int(time.time())
1188 if self.job.warning_manager.is_valid(timestamp, warning_type):
Eric Lid656d562011-04-20 11:48:29 -07001189 self.job.record('WARN', None, None, msg)
jadmanski6dadd832009-02-05 23:39:27 +00001190
jadmanski043e1132008-11-19 17:10:32 +00001191
1192 def write(self, data):
jadmanski2a89dac2010-06-11 14:32:58 +00001193 # now start processing the existing buffer and the new data
jadmanski043e1132008-11-19 17:10:32 +00001194 data = self.leftover + data
mbligh060c4712009-12-29 02:43:35 +00001195 lines = data.split('\n')
1196 processed_lines = 0
1197 try:
1198 # process all the buffered data except the last line
1199 # ignore the last line since we may not have all of it yet
1200 for line in lines[:-1]:
mbligh060c4712009-12-29 02:43:35 +00001201 self._process_line(line)
1202 processed_lines += 1
1203 finally:
1204 # save any unprocessed lines for future processing
1205 self.leftover = '\n'.join(lines[processed_lines:])
jadmanski043e1132008-11-19 17:10:32 +00001206
1207
1208 def flush(self):
1209 sys.stdout.flush()
1210
1211
jadmanskia61edad2009-05-21 22:17:49 +00001212 def flush_all_buffers(self):
jadmanski043e1132008-11-19 17:10:32 +00001213 if self.leftover:
1214 self._process_line(self.leftover)
jadmanskia61edad2009-05-21 22:17:49 +00001215 self.leftover = ""
jadmanski043e1132008-11-19 17:10:32 +00001216 self._process_logs()
1217 self.flush()
1218
1219
jadmanskia61edad2009-05-21 22:17:49 +00001220 def close(self):
1221 self.flush_all_buffers()
1222
1223
mbligha7007722009-01-13 00:37:11 +00001224SiteAutotest = client_utils.import_site_class(
1225 __file__, "autotest_lib.server.site_autotest", "SiteAutotest",
1226 BaseAutotest)
mblighd8b39252008-03-20 21:15:03 +00001227
showardad812bf2009-10-20 23:49:56 +00001228
jadmanski69bdaac2010-07-28 16:27:20 +00001229_SiteRun = client_utils.import_site_class(
1230 __file__, "autotest_lib.server.site_autotest", "_SiteRun", _BaseRun)
1231
1232
Chris Sosa3ee5d5c2012-02-23 11:18:41 -08001233SiteClientLogger = client_utils.import_site_class(
1234 __file__, "autotest_lib.server.site_autotest", "SiteClientLogger",
1235 BaseClientLogger)
1236
1237
mblighd8b39252008-03-20 21:15:03 +00001238class Autotest(SiteAutotest):
jadmanski0afbb632008-06-06 21:10:57 +00001239 pass
jadmanskic6136e92010-07-19 16:41:49 +00001240
1241
Chris Sosa3ee5d5c2012-02-23 11:18:41 -08001242class client_logger(SiteClientLogger):
1243 pass
1244
1245
jadmanski69bdaac2010-07-28 16:27:20 +00001246class _Run(_SiteRun):
1247 pass