blob: b0f88163cb2794b66c501612419a8964b0f8b12c [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
mbligh1f572e52010-04-01 17:15:53 +00006from autotest_lib.server import installable_object, prebuild, utils
beeps07f53b92013-01-08 12:55:10 -08007from autotest_lib.client.common_lib import base_job, error, autotemp
Dan Shi1889ca12015-04-15 09:36:06 -07008from autotest_lib.client.common_lib import base_packages, packages
9from autotest_lib.client.common_lib import global_config
mbligha7007722009-01-13 00:37:11 +000010from autotest_lib.client.common_lib import utils as client_utils
Gabe Black1e1c41b2015-02-04 23:55:15 -080011from autotest_lib.client.common_lib.cros.graphite import autotest_stats
mbligh3c7a1502008-07-24 18:08:47 +000012
Dale Curtiscb7bfaf2011-06-07 16:21:57 -070013AUTOTEST_SVN = 'svn://test.kernel.org/autotest/trunk/client'
mblighdcd57a82007-07-11 23:06:47 +000014AUTOTEST_HTTP = 'http://test.kernel.org/svn/autotest/trunk/client'
15
mblighdcd57a82007-07-11 23:06:47 +000016
mbligh1f572e52010-04-01 17:15:53 +000017get_value = global_config.global_config.get_config_value
18autoserv_prebuild = get_value('AUTOSERV', 'enable_server_prebuild',
19 type=bool, default=False)
20
jadmanski2a89dac2010-06-11 14:32:58 +000021
showardad812bf2009-10-20 23:49:56 +000022class AutodirNotFoundError(Exception):
23 """No Autotest installation could be found."""
24
25
beeps607428b2013-03-25 16:43:20 -070026class AutotestFailure(Exception):
27 """Gereric exception class for failures during a test run."""
28
29
30class AutotestAbort(AutotestFailure):
31 """
32 AutotestAborts are thrown when the DUT seems fine,
33 and the test doesn't give us an explicit reason for
34 failure; In this case we have no choice but to abort.
35 """
36
37
38class AutotestDeviceError(AutotestFailure):
39 """
40 Exceptions that inherit from AutotestDeviceError
41 are thrown when we can determine the current
42 state of the DUT and conclude that it probably
43 lead to the test failing; these exceptions lead
44 to failures instead of aborts.
45 """
46
47
48class AutotestDeviceNotPingable(AutotestDeviceError):
49 """Error for when a DUT becomes unpingable."""
50
51
52class AutotestDeviceNotSSHable(AutotestDeviceError):
53 """Error for when a DUT is pingable but not SSHable."""
54
55
56class AutotestDeviceRebooted(AutotestDeviceError):
57 """Error for when a DUT rebooted unexpectedly."""
58
59
mblighd8b39252008-03-20 21:15:03 +000060class BaseAutotest(installable_object.InstallableObject):
jadmanski0afbb632008-06-06 21:10:57 +000061 """
62 This class represents the Autotest program.
mblighdcd57a82007-07-11 23:06:47 +000063
jadmanski0afbb632008-06-06 21:10:57 +000064 Autotest is used to run tests automatically and collect the results.
65 It also supports profilers.
mblighdcd57a82007-07-11 23:06:47 +000066
jadmanski0afbb632008-06-06 21:10:57 +000067 Implementation details:
68 This is a leaf class in an abstract class hierarchy, it must
69 implement the unimplemented methods in parent classes.
70 """
mbligh119c12a2007-11-12 22:13:44 +000071
Dale Curtiscb7bfaf2011-06-07 16:21:57 -070072 def __init__(self, host=None):
jadmanski0afbb632008-06-06 21:10:57 +000073 self.host = host
74 self.got = False
75 self.installed = False
76 self.serverdir = utils.get_server_dir()
77 super(BaseAutotest, self).__init__()
mblighc8949b82007-07-23 16:33:58 +000078
mblighdc735a22007-08-02 16:54:37 +000079
jadmanskif22fea82008-11-26 20:57:07 +000080 install_in_tmpdir = False
81 @classmethod
82 def set_install_in_tmpdir(cls, flag):
83 """ Sets a flag that controls whether or not Autotest should by
84 default be installed in a "standard" directory (e.g.
85 /home/autotest, /usr/local/autotest) or a temporary directory. """
86 cls.install_in_tmpdir = flag
87
88
showardad812bf2009-10-20 23:49:56 +000089 @classmethod
90 def get_client_autodir_paths(cls, host):
91 return global_config.global_config.get_config_value(
92 'AUTOSERV', 'client_autodir_paths', type=list)
93
94
95 @classmethod
96 def get_installed_autodir(cls, host):
97 """
98 Find where the Autotest client is installed on the host.
99 @returns an absolute path to an installed Autotest client root.
100 @raises AutodirNotFoundError if no Autotest installation can be found.
101 """
102 autodir = host.get_autodir()
103 if autodir:
104 logging.debug('Using existing host autodir: %s', autodir)
105 return autodir
106
107 for path in Autotest.get_client_autodir_paths(host):
108 try:
109 autotest_binary = os.path.join(path, 'bin', 'autotest')
110 host.run('test -x %s' % utils.sh_escape(autotest_binary))
Eric Li861b2d52011-02-04 14:50:35 -0800111 host.run('test -w %s' % utils.sh_escape(path))
showardad812bf2009-10-20 23:49:56 +0000112 logging.debug('Found existing autodir at %s', path)
113 return path
114 except error.AutoservRunError:
115 logging.debug('%s does not exist on %s', autotest_binary,
116 host.hostname)
117 raise AutodirNotFoundError
118
119
120 @classmethod
121 def get_install_dir(cls, host):
122 """
123 Determines the location where autotest should be installed on
jadmanskif22fea82008-11-26 20:57:07 +0000124 host. If self.install_in_tmpdir is set, it will return a unique
showardad812bf2009-10-20 23:49:56 +0000125 temporary directory that autotest can be installed in. Otherwise, looks
126 for an existing installation to use; if none is found, looks for a
127 usable directory in the global config client_autodir_paths.
128 """
jadmanskif22fea82008-11-26 20:57:07 +0000129 try:
lmr9dcf0832009-12-08 21:28:55 +0000130 install_dir = cls.get_installed_autodir(host)
showardad812bf2009-10-20 23:49:56 +0000131 except AutodirNotFoundError:
lmr9dcf0832009-12-08 21:28:55 +0000132 install_dir = cls._find_installable_dir(host)
133
134 if cls.install_in_tmpdir:
135 return host.get_tmp_dir(parent=install_dir)
136 return install_dir
showardad812bf2009-10-20 23:49:56 +0000137
138
139 @classmethod
140 def _find_installable_dir(cls, host):
141 client_autodir_paths = cls.get_client_autodir_paths(host)
142 for path in client_autodir_paths:
143 try:
J. Richard Barnettef44ff962011-01-05 10:14:28 -0800144 host.run('mkdir -p %s' % utils.sh_escape(path))
Eric Li861b2d52011-02-04 14:50:35 -0800145 host.run('test -w %s' % utils.sh_escape(path))
showardad812bf2009-10-20 23:49:56 +0000146 return path
147 except error.AutoservRunError:
148 logging.debug('Failed to create %s', path)
Dan Shia8ff78d2015-09-18 13:59:51 -0700149 metadata = {'_type': 'AutoservInstallError',
150 'hostname': host.hostname}
151 autotest_stats.Counter('AutoservInstallError',
152 metadata=metadata).increment()
showardad812bf2009-10-20 23:49:56 +0000153 raise error.AutoservInstallError(
Dale Curtis74a314b2011-06-23 14:55:46 -0700154 'Unable to find a place to install Autotest; tried %s' %
showardad812bf2009-10-20 23:49:56 +0000155 ', '.join(client_autodir_paths))
jadmanskif22fea82008-11-26 20:57:07 +0000156
157
Eric Lid656d562011-04-20 11:48:29 -0700158 def get_fetch_location(self):
159 c = global_config.global_config
160 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list,
161 default=[])
162 repos.reverse()
163 return repos
164
165
Dan Shib669cbd2013-09-13 11:17:17 -0700166 def install(self, host=None, autodir=None, use_packaging=True):
167 self._install(host=host, autodir=autodir, use_packaging=use_packaging)
jadmanski54f90af2008-10-10 16:20:55 +0000168
169
mblighb8aa75b2009-09-18 16:50:37 +0000170 def install_full_client(self, host=None, autodir=None):
171 self._install(host=host, autodir=autodir, use_autoserv=False,
172 use_packaging=False)
173
174
mblighbccad482009-08-24 22:08:31 +0000175 def install_no_autoserv(self, host=None, autodir=None):
mblighb8aa75b2009-09-18 16:50:37 +0000176 self._install(host=host, autodir=autodir, use_autoserv=False)
mblighbccad482009-08-24 22:08:31 +0000177
178
mblighb8aa75b2009-09-18 16:50:37 +0000179 def _install_using_packaging(self, host, autodir):
Eric Lid656d562011-04-20 11:48:29 -0700180 repos = self.get_fetch_location()
mblighb8aa75b2009-09-18 16:50:37 +0000181 if not repos:
182 raise error.PackageInstallError("No repos to install an "
183 "autotest client from")
184 pkgmgr = packages.PackageManager(autodir, hostname=host.hostname,
185 repo_urls=repos,
186 do_locking=False,
187 run_function=host.run,
188 run_function_dargs=dict(timeout=600))
189 # The packages dir is used to store all the packages that
190 # are fetched on that client. (for the tests,deps etc.
191 # too apart from the client)
192 pkg_dir = os.path.join(autodir, 'packages')
193 # clean up the autodir except for the packages directory
194 host.run('cd %s && ls | grep -v "^packages$"'
Laurence Goodby2de320d2015-07-08 14:45:18 -0700195 ' | xargs rm -rf && rm -rf .[!.]*' % autodir)
mblighb8aa75b2009-09-18 16:50:37 +0000196 pkgmgr.install_pkg('autotest', 'client', pkg_dir, autodir,
197 preserve_install_dir=True)
198 self.installed = True
199
200
201 def _install_using_send_file(self, host, autodir):
Chris Sosa3ee5d5c2012-02-23 11:18:41 -0800202 dirs_to_exclude = set(["tests", "site_tests", "deps", "profilers",
203 "packages"])
mblighb8aa75b2009-09-18 16:50:37 +0000204 light_files = [os.path.join(self.source_material, f)
205 for f in os.listdir(self.source_material)
206 if f not in dirs_to_exclude]
207 host.send_file(light_files, autodir, delete_dest=True)
208
209 # create empty dirs for all the stuff we excluded
210 commands = []
211 for path in dirs_to_exclude:
212 abs_path = os.path.join(autodir, path)
213 abs_path = utils.sh_escape(abs_path)
214 commands.append("mkdir -p '%s'" % abs_path)
215 commands.append("touch '%s'/__init__.py" % abs_path)
216 host.run(';'.join(commands))
217
218
219 def _install(self, host=None, autodir=None, use_autoserv=True,
220 use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000221 """
222 Install autotest. If get() was not called previously, an
223 attempt will be made to install from the autotest svn
224 repository.
mbligh9a3f5e52008-05-28 21:21:43 +0000225
mblighbccad482009-08-24 22:08:31 +0000226 @param host A Host instance on which autotest will be installed
227 @param autodir Location on the remote host to install to
mblighb8aa75b2009-09-18 16:50:37 +0000228 @param use_autoserv Enable install modes that depend on the client
mblighbccad482009-08-24 22:08:31 +0000229 running with the autoserv harness
mblighb8aa75b2009-09-18 16:50:37 +0000230 @param use_packaging Enable install modes that use the packaging system
mbligh9a3f5e52008-05-28 21:21:43 +0000231
mblighbccad482009-08-24 22:08:31 +0000232 @exception AutoservError if a tarball was not specified and
233 the target host does not have svn installed in its path
234 """
jadmanski0afbb632008-06-06 21:10:57 +0000235 if not host:
236 host = self.host
237 if not self.got:
238 self.get()
239 host.wait_up(timeout=30)
240 host.setup()
showardb18134f2009-03-20 20:52:18 +0000241 logging.info("Installing autotest on %s", host.hostname)
mbligh40f122a2007-11-03 23:08:46 +0000242
jadmanski54f90af2008-10-10 16:20:55 +0000243 # set up the autotest directory on the remote machine
244 if not autodir:
showardad812bf2009-10-20 23:49:56 +0000245 autodir = self.get_install_dir(host)
246 logging.info('Using installation dir %s', autodir)
mbligh0562e652008-08-20 20:11:45 +0000247 host.set_autodir(autodir)
jadmanski3c236942009-03-04 17:51:26 +0000248 host.run('mkdir -p %s' % utils.sh_escape(autodir))
mbligh40f122a2007-11-03 23:08:46 +0000249
jadmanski1c3c07b2009-03-03 23:29:36 +0000250 # make sure there are no files in $AUTODIR/results
251 results_path = os.path.join(autodir, 'results')
jadmanski3c236942009-03-04 17:51:26 +0000252 host.run('rm -rf %s/*' % utils.sh_escape(results_path),
jadmanski1c3c07b2009-03-03 23:29:36 +0000253 ignore_status=True)
254
mblighc5ddfd12008-08-04 17:15:00 +0000255 # Fetch the autotest client from the nearest repository
mblighb8aa75b2009-09-18 16:50:37 +0000256 if use_packaging:
257 try:
258 self._install_using_packaging(host, autodir)
Dan Shib669cbd2013-09-13 11:17:17 -0700259 logging.info("Installation of autotest completed using the "
260 "packaging system.")
mblighb8aa75b2009-09-18 16:50:37 +0000261 return
Eric Li6f27d4f2010-09-29 10:55:17 -0700262 except (error.PackageInstallError, error.AutoservRunError,
263 global_config.ConfigError), e:
mblighb8aa75b2009-09-18 16:50:37 +0000264 logging.info("Could not install autotest using the packaging "
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700265 "system: %s. Trying other methods", e)
Dan Shi1889ca12015-04-15 09:36:06 -0700266 else:
267 # Delete the package checksum file to force dut updating local
268 # packages.
269 command = ('rm -f "%s"' %
270 (os.path.join(autodir, base_packages.CHECKSUM_FILE)))
271 host.run(command)
mblighc5ddfd12008-08-04 17:15:00 +0000272
jadmanski0afbb632008-06-06 21:10:57 +0000273 # try to install from file or directory
274 if self.source_material:
jadmanski69bdaac2010-07-28 16:27:20 +0000275 c = global_config.global_config
276 supports_autoserv_packaging = c.get_config_value(
277 "PACKAGES", "serve_packages_from_autoserv", type=bool)
278 # Copy autotest recursively
279 if supports_autoserv_packaging and use_autoserv:
280 self._install_using_send_file(host, autodir)
jadmanski0afbb632008-06-06 21:10:57 +0000281 else:
jadmanski69bdaac2010-07-28 16:27:20 +0000282 host.send_file(self.source_material, autodir, delete_dest=True)
Dan Shib669cbd2013-09-13 11:17:17 -0700283 logging.info("Installation of autotest completed from %s",
284 self.source_material)
jadmanski0afbb632008-06-06 21:10:57 +0000285 self.installed = True
286 return
mbligh91334902007-09-28 01:47:59 +0000287
jadmanski0afbb632008-06-06 21:10:57 +0000288 # if that fails try to install using svn
289 if utils.run('which svn').exit_status:
Dale Curtis74a314b2011-06-23 14:55:46 -0700290 raise error.AutoservError('svn not found on target machine: %s' %
291 host.name)
jadmanski0afbb632008-06-06 21:10:57 +0000292 try:
mbligh78bf5352008-07-11 20:27:36 +0000293 host.run('svn checkout %s %s' % (AUTOTEST_SVN, autodir))
jadmanski0afbb632008-06-06 21:10:57 +0000294 except error.AutoservRunError, e:
mbligh78bf5352008-07-11 20:27:36 +0000295 host.run('svn checkout %s %s' % (AUTOTEST_HTTP, autodir))
Dan Shib669cbd2013-09-13 11:17:17 -0700296 logging.info("Installation of autotest completed using SVN.")
jadmanski0afbb632008-06-06 21:10:57 +0000297 self.installed = True
mbligh91334902007-09-28 01:47:59 +0000298
299
jadmanski7c7aff32009-03-25 22:43:07 +0000300 def uninstall(self, host=None):
301 """
302 Uninstall (i.e. delete) autotest. Removes the autotest client install
303 from the specified host.
304
305 @params host a Host instance from which the client will be removed
306 """
307 if not self.installed:
308 return
309 if not host:
310 host = self.host
311 autodir = host.get_autodir()
312 if not autodir:
313 return
314
315 # perform the actual uninstall
316 host.run("rm -rf %s" % utils.sh_escape(autodir), ignore_status=True)
317 host.set_autodir(None)
318 self.installed = False
319
320
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700321 def get(self, location=None):
jadmanski0afbb632008-06-06 21:10:57 +0000322 if not location:
323 location = os.path.join(self.serverdir, '../client')
324 location = os.path.abspath(location)
325 # If there's stuff run on our client directory already, it
326 # can cause problems. Try giving it a quick clean first.
327 cwd = os.getcwd()
328 os.chdir(location)
showard4b976072009-10-20 23:50:08 +0000329 try:
330 utils.system('tools/make_clean', ignore_status=True)
331 finally:
332 os.chdir(cwd)
jadmanski0afbb632008-06-06 21:10:57 +0000333 super(BaseAutotest, self).get(location)
334 self.got = True
mblighdcd57a82007-07-11 23:06:47 +0000335
336
mblighe7d9c602009-07-02 19:02:33 +0000337 def run(self, control_file, results_dir='.', host=None, timeout=None,
338 tag=None, parallel_flag=False, background=False,
Dan Shib669cbd2013-09-13 11:17:17 -0700339 client_disconnect_timeout=None, use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000340 """
341 Run an autotest job on the remote machine.
mbligh9a3f5e52008-05-28 21:21:43 +0000342
mblighe7d9c602009-07-02 19:02:33 +0000343 @param control_file: An open file-like-obj of the control file.
344 @param results_dir: A str path where the results should be stored
345 on the local filesystem.
346 @param host: A Host instance on which the control file should
347 be run.
348 @param timeout: Maximum number of seconds to wait for the run or None.
349 @param tag: Tag name for the client side instance of autotest.
350 @param parallel_flag: Flag set when multiple jobs are run at the
351 same time.
352 @param background: Indicates that the client should be launched as
353 a background job; the code calling run will be responsible
354 for monitoring the client and collecting the results.
355 @param client_disconnect_timeout: Seconds to wait for the remote host
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700356 to come back after a reboot. Defaults to the host setting for
357 DEFAULT_REBOOT_TIMEOUT.
mblighe7d9c602009-07-02 19:02:33 +0000358
359 @raises AutotestRunError: If there is a problem executing
360 the control file.
jadmanski0afbb632008-06-06 21:10:57 +0000361 """
Dan Shib669cbd2013-09-13 11:17:17 -0700362 host = self._get_host_and_setup(host, use_packaging=use_packaging)
jadmanski0afbb632008-06-06 21:10:57 +0000363 results_dir = os.path.abspath(results_dir)
mblighc1cbc992008-05-27 20:01:45 +0000364
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700365 if client_disconnect_timeout is None:
366 client_disconnect_timeout = host.DEFAULT_REBOOT_TIMEOUT
367
jadmanski0afbb632008-06-06 21:10:57 +0000368 if tag:
369 results_dir = os.path.join(results_dir, tag)
mblighc1cbc992008-05-27 20:01:45 +0000370
mblighb3c0c912008-11-27 00:32:45 +0000371 atrun = _Run(host, results_dir, tag, parallel_flag, background)
jadmanski6dadd832009-02-05 23:39:27 +0000372 self._do_run(control_file, results_dir, host, atrun, timeout,
Dan Shib669cbd2013-09-13 11:17:17 -0700373 client_disconnect_timeout, use_packaging=use_packaging)
mblighd8b39252008-03-20 21:15:03 +0000374
375
Dan Shib669cbd2013-09-13 11:17:17 -0700376 def _get_host_and_setup(self, host, use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000377 if not host:
378 host = self.host
379 if not self.installed:
Dan Shib669cbd2013-09-13 11:17:17 -0700380 self.install(host, use_packaging=use_packaging)
mbligh91334902007-09-28 01:47:59 +0000381
jadmanski0afbb632008-06-06 21:10:57 +0000382 host.wait_up(timeout=30)
383 return host
mblighd8b39252008-03-20 21:15:03 +0000384
385
jadmanski6dadd832009-02-05 23:39:27 +0000386 def _do_run(self, control_file, results_dir, host, atrun, timeout,
Dan Shib669cbd2013-09-13 11:17:17 -0700387 client_disconnect_timeout, use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000388 try:
389 atrun.verify_machine()
390 except:
showardb18134f2009-03-20 20:52:18 +0000391 logging.error("Verify failed on %s. Reinstalling autotest",
392 host.hostname)
jadmanski0afbb632008-06-06 21:10:57 +0000393 self.install(host)
Fang Dengb9cd83c2015-01-27 10:16:08 -0800394 atrun.verify_machine()
jadmanski0afbb632008-06-06 21:10:57 +0000395 debug = os.path.join(results_dir, 'debug')
396 try:
397 os.makedirs(debug)
mbligh09108442008-10-15 16:27:38 +0000398 except Exception:
jadmanski0afbb632008-06-06 21:10:57 +0000399 pass
mbligh9a3f5e52008-05-28 21:21:43 +0000400
mbligh09108442008-10-15 16:27:38 +0000401 delete_file_list = [atrun.remote_control_file,
402 atrun.remote_control_file + '.state',
403 atrun.manual_control_file,
404 atrun.manual_control_file + '.state']
405 cmd = ';'.join('rm -f ' + control for control in delete_file_list)
406 host.run(cmd, ignore_status=True)
mbligh9a3f5e52008-05-28 21:21:43 +0000407
Dale Curtis386eea72011-09-21 18:43:04 -0700408 tmppath = utils.get(control_file, local_copy=True)
mblighc5ddfd12008-08-04 17:15:00 +0000409
jadmanskicb0e1612009-02-27 18:03:10 +0000410 # build up the initialization prologue for the control file
411 prologue_lines = []
jadmanski23afbec2008-09-17 18:12:07 +0000412
mbligh2f076832010-03-30 17:08:20 +0000413 # Add the additional user arguments
jadmanski808f4b12010-04-09 22:30:31 +0000414 prologue_lines.append("args = %r\n" % self.job.args)
mbligh2f076832010-03-30 17:08:20 +0000415
mbligh09108442008-10-15 16:27:38 +0000416 # If the packaging system is being used, add the repository list.
mblighddc9a402010-01-15 20:33:34 +0000417 repos = None
mblighc5ddfd12008-08-04 17:15:00 +0000418 try:
Dan Shib669cbd2013-09-13 11:17:17 -0700419 if use_packaging:
420 repos = self.get_fetch_location()
421 prologue_lines.append('job.add_repository(%s)\n' % repos)
422 else:
423 logging.debug('use_packaging is set to False, do not add any '
424 'repository.')
mblighc5ddfd12008-08-04 17:15:00 +0000425 except global_config.ConfigError, e:
mblighddc9a402010-01-15 20:33:34 +0000426 # If repos is defined packaging is enabled so log the error
427 if repos:
428 logging.error(e)
mblighc5ddfd12008-08-04 17:15:00 +0000429
jadmanskie2eef7b2009-03-03 23:55:13 +0000430 # on full-size installs, turn on any profilers the server is using
jadmanski27b52912009-08-14 17:09:15 +0000431 if not atrun.background:
jadmanskie2eef7b2009-03-03 23:55:13 +0000432 running_profilers = host.job.profilers.add_log.iteritems()
433 for profiler, (args, dargs) in running_profilers:
434 call_args = [repr(profiler)]
435 call_args += [repr(arg) for arg in args]
436 call_args += ["%s=%r" % item for item in dargs.iteritems()]
437 prologue_lines.append("job.profilers.add(%s)\n"
438 % ", ".join(call_args))
439 cfile = "".join(prologue_lines)
440
mbligh09108442008-10-15 16:27:38 +0000441 cfile += open(tmppath).read()
442 open(tmppath, "w").write(cfile)
mblighc5ddfd12008-08-04 17:15:00 +0000443
jadmanskic09fc152008-10-15 17:56:59 +0000444 # Create and copy state file to remote_control_file + '.state'
mblighfc3da5b2010-01-06 18:37:22 +0000445 state_file = host.job.preprocess_client_state()
mblighfbf73ae2009-12-19 05:22:42 +0000446 host.send_file(state_file, atrun.remote_control_file + '.init.state')
jadmanskic09fc152008-10-15 17:56:59 +0000447 os.remove(state_file)
448
mblighc5ddfd12008-08-04 17:15:00 +0000449 # Copy control_file to remote_control_file on the host
jadmanski0afbb632008-06-06 21:10:57 +0000450 host.send_file(tmppath, atrun.remote_control_file)
451 if os.path.abspath(tmppath) != os.path.abspath(control_file):
452 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000453
jadmanski6bb32d72009-03-19 20:25:24 +0000454 atrun.execute_control(
jadmanski6dadd832009-02-05 23:39:27 +0000455 timeout=timeout,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800456 client_disconnect_timeout=client_disconnect_timeout)
jadmanski23afbec2008-09-17 18:12:07 +0000457
458
harpreetf531d072016-04-19 18:37:26 -0700459 @classmethod
460 def _check_client_test_result(cls, host, test_name):
461 """
462 Check result of client test.
463 Autotest will store results in the file name status.
464 We check that second to last line in that file begins with 'END GOOD'
465
466 @raises TestFail: If client test does not pass.
467 """
468 client_result_dir = '%s/results/default' % host.autodir
469 command = 'tail -2 %s/status | head -1' % client_result_dir
470 status = host.run(command).stdout.strip()
471 logging.info(status)
472 if status[:8] != 'END GOOD':
473 raise error.TestFail('%s client test did not pass.' % test_name)
474
475
jadmanski0afbb632008-06-06 21:10:57 +0000476 def run_timed_test(self, test_name, results_dir='.', host=None,
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700477 timeout=None, parallel_flag=False, background=False,
478 client_disconnect_timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000479 """
480 Assemble a tiny little control file to just run one test,
481 and run it as an autotest client-side test
482 """
483 if not host:
484 host = self.host
485 if not self.installed:
486 self.install(host)
487 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
488 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
489 control = "job.run_test(%s)\n" % cmd
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700490 self.run(control, results_dir, host, timeout=timeout,
Andrew Bresticker2da1b762013-01-15 16:11:18 -0800491 parallel_flag=parallel_flag, background=background,
492 client_disconnect_timeout=client_disconnect_timeout)
mbligh0e4613b2007-10-29 16:55:07 +0000493
harpreetf531d072016-04-19 18:37:26 -0700494 dargs_dict = dict(dargs)
495 if 'check_client_result' in dargs_dict:
496 if dargs_dict['check_client_result']:
497 self._check_client_test_result(host, test_name)
498
mbligh0e4613b2007-10-29 16:55:07 +0000499
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700500 def run_test(self, test_name, results_dir='.', host=None,
Andrew Bresticker2da1b762013-01-15 16:11:18 -0800501 parallel_flag=False, background=False,
502 client_disconnect_timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000503 self.run_timed_test(test_name, results_dir, host, timeout=None,
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700504 parallel_flag=parallel_flag, background=background,
Andrew Bresticker2da1b762013-01-15 16:11:18 -0800505 client_disconnect_timeout=client_disconnect_timeout,
jadmanskic98c4702009-01-05 15:50:06 +0000506 *args, **dargs)
mblighd54832b2007-07-25 16:46:56 +0000507
508
jadmanski69bdaac2010-07-28 16:27:20 +0000509class _BaseRun(object):
jadmanski0afbb632008-06-06 21:10:57 +0000510 """
511 Represents a run of autotest control file. This class maintains
512 all the state necessary as an autotest control file is executed.
mblighdcd57a82007-07-11 23:06:47 +0000513
jadmanski0afbb632008-06-06 21:10:57 +0000514 It is not intended to be used directly, rather control files
515 should be run using the run method in Autotest.
516 """
mblighb3c0c912008-11-27 00:32:45 +0000517 def __init__(self, host, results_dir, tag, parallel_flag, background):
jadmanski0afbb632008-06-06 21:10:57 +0000518 self.host = host
519 self.results_dir = results_dir
520 self.env = host.env
521 self.tag = tag
522 self.parallel_flag = parallel_flag
mblighb3c0c912008-11-27 00:32:45 +0000523 self.background = background
showardad812bf2009-10-20 23:49:56 +0000524 self.autodir = Autotest.get_installed_autodir(self.host)
mbligh78bf5352008-07-11 20:27:36 +0000525 control = os.path.join(self.autodir, 'control')
jadmanski0afbb632008-06-06 21:10:57 +0000526 if tag:
mbligh78bf5352008-07-11 20:27:36 +0000527 control += '.' + tag
528 self.manual_control_file = control
529 self.remote_control_file = control + '.autoserv'
lmr6d08b3c2009-11-18 19:26:38 +0000530 self.config_file = os.path.join(self.autodir, 'global_config.ini')
mblighdc735a22007-08-02 16:54:37 +0000531
532
jadmanski0afbb632008-06-06 21:10:57 +0000533 def verify_machine(self):
534 binary = os.path.join(self.autodir, 'bin/autotest')
535 try:
536 self.host.run('ls %s > /dev/null 2>&1' % binary)
537 except:
lmrd6d27ed2009-12-08 19:58:33 +0000538 raise error.AutoservInstallError(
539 "Autotest does not appear to be installed")
mblighdc735a22007-08-02 16:54:37 +0000540
jadmanski0afbb632008-06-06 21:10:57 +0000541 if not self.parallel_flag:
542 tmpdir = os.path.join(self.autodir, 'tmp')
543 download = os.path.join(self.autodir, 'tests/download')
544 self.host.run('umount %s' % tmpdir, ignore_status=True)
545 self.host.run('umount %s' % download, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000546
jadmanski6dadd832009-02-05 23:39:27 +0000547
548 def get_base_cmd_args(self, section):
showard234b2962009-07-28 20:02:30 +0000549 args = ['--verbose']
jadmanski0afbb632008-06-06 21:10:57 +0000550 if section > 0:
jadmanski6dadd832009-02-05 23:39:27 +0000551 args.append('-c')
jadmanski0afbb632008-06-06 21:10:57 +0000552 if self.tag:
jadmanski6dadd832009-02-05 23:39:27 +0000553 args.append('-t %s' % self.tag)
jadmanski0afbb632008-06-06 21:10:57 +0000554 if self.host.job.use_external_logging():
jadmanski6dadd832009-02-05 23:39:27 +0000555 args.append('-l')
mblighce955fc2009-08-24 21:59:02 +0000556 if self.host.hostname:
557 args.append('--hostname=%s' % self.host.hostname)
mbligh0d0f67d2009-11-06 03:15:03 +0000558 args.append('--user=%s' % self.host.job.user)
mblighce955fc2009-08-24 21:59:02 +0000559
jadmanski6dadd832009-02-05 23:39:27 +0000560 args.append(self.remote_control_file)
561 return args
562
563
564 def get_background_cmd(self, section):
565 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotest_client')]
566 cmd += self.get_base_cmd_args(section)
jadmanski69bdaac2010-07-28 16:27:20 +0000567 cmd += ['>/dev/null', '2>/dev/null', '&']
jadmanski6dadd832009-02-05 23:39:27 +0000568 return ' '.join(cmd)
569
570
571 def get_daemon_cmd(self, section, monitor_dir):
572 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotestd'),
573 monitor_dir, '-H autoserv']
574 cmd += self.get_base_cmd_args(section)
jadmanski69bdaac2010-07-28 16:27:20 +0000575 cmd += ['>/dev/null', '2>/dev/null', '&']
jadmanski6dadd832009-02-05 23:39:27 +0000576 return ' '.join(cmd)
577
578
579 def get_monitor_cmd(self, monitor_dir, stdout_read, stderr_read):
580 cmd = [os.path.join(self.autodir, 'bin', 'autotestd_monitor'),
581 monitor_dir, str(stdout_read), str(stderr_read)]
jadmanski0afbb632008-06-06 21:10:57 +0000582 return ' '.join(cmd)
mblighadf2aab2007-11-29 18:16:43 +0000583
mblighd8b39252008-03-20 21:15:03 +0000584
jadmanski4d03cf62010-03-04 18:32:28 +0000585 def get_client_log(self):
586 """Find what the "next" client.* prefix should be
587
588 @returns A string of the form client.INTEGER that should be prefixed
589 to all client debug log files.
590 """
591 max_digit = -1
592 debug_dir = os.path.join(self.results_dir, 'debug')
593 client_logs = glob.glob(os.path.join(debug_dir, 'client.*.*'))
594 for log in client_logs:
595 _, number, _ = log.split('.', 2)
596 if number.isdigit():
597 max_digit = max(max_digit, int(number))
598 return 'client.%d' % (max_digit + 1)
599
600
601 def copy_client_config_file(self, client_log_prefix=None):
602 """
603 Create and copy the client config file based on the server config.
604
605 @param client_log_prefix: Optional prefix to prepend to log files.
606 """
607 client_config_file = self._create_client_config_file(client_log_prefix)
608 self.host.send_file(client_config_file, self.config_file)
609 os.remove(client_config_file)
610
611
612 def _create_client_config_file(self, client_log_prefix=None):
613 """
614 Create a temporary file with the [CLIENT] section configuration values
615 taken from the server global_config.ini.
616
617 @param client_log_prefix: Optional prefix to prepend to log files.
618
619 @return: Path of the temporary file generated.
620 """
621 config = global_config.global_config.get_section_values('CLIENT')
622 if client_log_prefix:
623 config.set('CLIENT', 'default_logging_name', client_log_prefix)
624 return self._create_aux_file(config.write)
625
626
627 def _create_aux_file(self, func, *args):
628 """
629 Creates a temporary file and writes content to it according to a
630 content creation function. The file object is appended to *args, which
631 is then passed to the content creation function
632
633 @param func: Function that will be used to write content to the
634 temporary file.
635 @param *args: List of parameters that func takes.
636 @return: Path to the temporary file that was created.
637 """
638 fd, path = tempfile.mkstemp(dir=self.host.job.tmpdir)
639 aux_file = os.fdopen(fd, "w")
640 try:
641 list_args = list(args)
642 list_args.append(aux_file)
643 func(*list_args)
644 finally:
645 aux_file.close()
646 return path
mblighd8b39252008-03-20 21:15:03 +0000647
648
jadmanskib264ed02009-01-12 23:54:27 +0000649 @staticmethod
650 def is_client_job_finished(last_line):
651 return bool(re.match(r'^END .*\t----\t----\t.*$', last_line))
652
653
654 @staticmethod
655 def is_client_job_rebooting(last_line):
656 return bool(re.match(r'^\t*GOOD\t----\treboot\.start.*$', last_line))
657
658
beeps607428b2013-03-25 16:43:20 -0700659 def _diagnose_dut(self, old_boot_id=None):
660 """
661 Run diagnostic checks on a DUT.
662
663 1. ping: A dead host will not respond to pings.
664 2. ssh (happens with 3.): DUT hangs usually fail in authentication
665 but respond to pings.
666 3. Check if a reboot occured: A healthy but unexpected reboot leaves the
667 host running with a new boot id.
668
669 This method will always raise an exception from the AutotestFailure
670 family and should only get called when the reason for a test failing
671 is ambiguous.
672
673 @raises AutotestDeviceNotPingable: If the DUT doesn't respond to ping.
674 @raises AutotestDeviceNotSSHable: If we cannot SSH into the DUT.
675 @raises AutotestDeviceRebooted: If the boot id changed.
676 @raises AutotestAbort: If none of the above exceptions were raised.
677 Since we have no recourse we must abort at this stage.
678 """
679 msg = 'Autotest client terminated unexpectedly: '
680 if utils.ping(self.host.hostname, tries=1, deadline=1) != 0:
681 msg += 'DUT is no longer pingable, it may have rebooted or hung.\n'
682 raise AutotestDeviceNotPingable(msg)
683
684 if old_boot_id:
685 try:
686 new_boot_id = self.host.get_boot_id(timeout=60)
687 except Exception as e:
688 msg += ('DUT is pingable but not SSHable, it most likely'
beeps14768812013-09-25 12:58:45 -0700689 ' sporadically rebooted during testing. %s\n' % str(e))
beeps607428b2013-03-25 16:43:20 -0700690 raise AutotestDeviceNotSSHable(msg)
691 else:
692 if new_boot_id != old_boot_id:
693 msg += 'DUT rebooted during the test run.\n'
694 raise AutotestDeviceRebooted(msg)
695
696 msg += ('DUT is pingable, SSHable and did NOT restart '
697 'un-expectedly. We probably lost connectivity during the '
698 'test.')
699 else:
700 msg += ('DUT is pingable, could not determine if an un-expected '
701 'reboot occured during the test.')
702
703 raise AutotestAbort(msg)
704
705
beeps07f53b92013-01-08 12:55:10 -0800706 def log_unexpected_abort(self, stderr_redirector, old_boot_id=None):
707 """
beeps607428b2013-03-25 16:43:20 -0700708 Logs that something unexpected happened, then tries to diagnose the
709 failure. The purpose of this function is only to close out the status
710 log with the appropriate error message, not to critically terminate
711 the program.
beeps07f53b92013-01-08 12:55:10 -0800712
713 @param stderr_redirector: log stream.
714 @param old_boot_id: boot id used to infer if a reboot occured.
715 """
jadmanskia61edad2009-05-21 22:17:49 +0000716 stderr_redirector.flush_all_buffers()
beeps607428b2013-03-25 16:43:20 -0700717 try:
718 self._diagnose_dut(old_boot_id)
719 except AutotestFailure as e:
720 self.host.job.record('END ABORT', None, None, str(e))
jadmanskib264ed02009-01-12 23:54:27 +0000721
722
jadmanski6dadd832009-02-05 23:39:27 +0000723 def _execute_in_background(self, section, timeout):
724 full_cmd = self.get_background_cmd(section)
725 devnull = open(os.devnull, "w")
mblighd8b39252008-03-20 21:15:03 +0000726
jadmanski4d03cf62010-03-04 18:32:28 +0000727 self.copy_client_config_file(self.get_client_log())
728
mbligh0d0f67d2009-11-06 03:15:03 +0000729 self.host.job.push_execution_context(self.results_dir)
jadmanski0afbb632008-06-06 21:10:57 +0000730 try:
jadmanski0afbb632008-06-06 21:10:57 +0000731 result = self.host.run(full_cmd, ignore_status=True,
732 timeout=timeout,
jadmanski6dadd832009-02-05 23:39:27 +0000733 stdout_tee=devnull,
734 stderr_tee=devnull)
jadmanski0afbb632008-06-06 21:10:57 +0000735 finally:
mbligh0d0f67d2009-11-06 03:15:03 +0000736 self.host.job.pop_execution_context()
jadmanski6dadd832009-02-05 23:39:27 +0000737
738 return result
739
740
741 @staticmethod
742 def _strip_stderr_prologue(stderr):
743 """Strips the 'standard' prologue that get pre-pended to every
744 remote command and returns the text that was actually written to
745 stderr by the remote command."""
746 stderr_lines = stderr.split("\n")[1:]
747 if not stderr_lines:
748 return ""
749 elif stderr_lines[0].startswith("NOTE: autotestd_monitor"):
750 del stderr_lines[0]
751 return "\n".join(stderr_lines)
752
753
754 def _execute_daemon(self, section, timeout, stderr_redirector,
755 client_disconnect_timeout):
756 monitor_dir = self.host.get_tmp_dir()
757 daemon_cmd = self.get_daemon_cmd(section, monitor_dir)
jadmanski4d03cf62010-03-04 18:32:28 +0000758
759 # grab the location for the server-side client log file
760 client_log_prefix = self.get_client_log()
761 client_log_path = os.path.join(self.results_dir, 'debug',
762 client_log_prefix + '.log')
763 client_log = open(client_log_path, 'w', 0)
764 self.copy_client_config_file(client_log_prefix)
jadmanski6dadd832009-02-05 23:39:27 +0000765
766 stdout_read = stderr_read = 0
mbligh0d0f67d2009-11-06 03:15:03 +0000767 self.host.job.push_execution_context(self.results_dir)
jadmanski6dadd832009-02-05 23:39:27 +0000768 try:
769 self.host.run(daemon_cmd, ignore_status=True, timeout=timeout)
jadmanski91d56a92009-04-01 15:20:40 +0000770 disconnect_warnings = []
jadmanski6dadd832009-02-05 23:39:27 +0000771 while True:
772 monitor_cmd = self.get_monitor_cmd(monitor_dir, stdout_read,
773 stderr_read)
774 try:
775 result = self.host.run(monitor_cmd, ignore_status=True,
776 timeout=timeout,
777 stdout_tee=client_log,
778 stderr_tee=stderr_redirector)
779 except error.AutoservRunError, e:
780 result = e.result_obj
781 result.exit_status = None
jadmanski91d56a92009-04-01 15:20:40 +0000782 disconnect_warnings.append(e.description)
783
jadmanski6dadd832009-02-05 23:39:27 +0000784 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000785 "Autotest client was disconnected: %s" % e.description,
786 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000787 except error.AutoservSSHTimeout:
788 result = utils.CmdResult(monitor_cmd, "", "", None, 0)
789 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000790 "Attempt to connect to Autotest client timed out",
791 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000792
793 stdout_read += len(result.stdout)
794 stderr_read += len(self._strip_stderr_prologue(result.stderr))
795
796 if result.exit_status is not None:
Simran Basibca10a62013-01-24 15:52:35 -0800797 # TODO (crosbug.com/38224)- sbasi: Remove extra logging.
798 logging.debug('Result exit status is %d.',
799 result.exit_status)
jadmanski6dadd832009-02-05 23:39:27 +0000800 return result
801 elif not self.host.wait_up(client_disconnect_timeout):
802 raise error.AutoservSSHTimeout(
803 "client was disconnected, reconnect timed out")
804 finally:
jadmanski4d03cf62010-03-04 18:32:28 +0000805 client_log.close()
mbligh0d0f67d2009-11-06 03:15:03 +0000806 self.host.job.pop_execution_context()
jadmanski6dadd832009-02-05 23:39:27 +0000807
808
809 def execute_section(self, section, timeout, stderr_redirector,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800810 client_disconnect_timeout):
showardb18134f2009-03-20 20:52:18 +0000811 logging.info("Executing %s/bin/autotest %s/control phase %d",
812 self.autodir, self.autodir, section)
jadmanski6dadd832009-02-05 23:39:27 +0000813
814 if self.background:
815 result = self._execute_in_background(section, timeout)
816 else:
817 result = self._execute_daemon(section, timeout, stderr_redirector,
818 client_disconnect_timeout)
819
820 last_line = stderr_redirector.last_line
mbligh2bf2db62007-11-27 00:53:18 +0000821
jadmanskib264ed02009-01-12 23:54:27 +0000822 # check if we failed hard enough to warrant an exception
jadmanski0afbb632008-06-06 21:10:57 +0000823 if result.exit_status == 1:
jadmanskib264ed02009-01-12 23:54:27 +0000824 err = error.AutotestRunError("client job was aborted")
825 elif not self.background and not result.stderr:
826 err = error.AutotestRunError(
jadmanskie4130532009-03-17 18:01:28 +0000827 "execute_section %s failed to return anything\n"
828 "stdout:%s\n" % (section, result.stdout))
jadmanskib264ed02009-01-12 23:54:27 +0000829 else:
830 err = None
mbligh0e4613b2007-10-29 16:55:07 +0000831
jadmanskib264ed02009-01-12 23:54:27 +0000832 # log something if the client failed AND never finished logging
Dale Curtis9285ddf2011-01-05 11:47:24 -0800833 if err and not self.is_client_job_finished(last_line):
jadmanskia61edad2009-05-21 22:17:49 +0000834 self.log_unexpected_abort(stderr_redirector)
jadmanskib264ed02009-01-12 23:54:27 +0000835
836 if err:
837 raise err
838 else:
839 return stderr_redirector.last_line
jadmanski4600e342008-10-29 22:54:00 +0000840
841
jadmanskic0354912010-01-12 15:57:29 +0000842 def _wait_for_reboot(self, old_boot_id):
showardb18134f2009-03-20 20:52:18 +0000843 logging.info("Client is rebooting")
844 logging.info("Waiting for client to halt")
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700845 if not self.host.wait_down(self.host.WAIT_DOWN_REBOOT_TIMEOUT,
846 old_boot_id=old_boot_id):
jadmanski4600e342008-10-29 22:54:00 +0000847 err = "%s failed to shutdown after %d"
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700848 err %= (self.host.hostname, self.host.WAIT_DOWN_REBOOT_TIMEOUT)
jadmanski4600e342008-10-29 22:54:00 +0000849 raise error.AutotestRunError(err)
showardb18134f2009-03-20 20:52:18 +0000850 logging.info("Client down, waiting for restart")
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700851 if not self.host.wait_up(self.host.DEFAULT_REBOOT_TIMEOUT):
jadmanski4600e342008-10-29 22:54:00 +0000852 # since reboot failed
853 # hardreset the machine once if possible
854 # before failing this control file
855 warning = "%s did not come back up, hard resetting"
856 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000857 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000858 try:
859 self.host.hardreset(wait=False)
mblighd99d3b272008-12-22 14:41:27 +0000860 except (AttributeError, error.AutoservUnsupportedError):
jadmanski4600e342008-10-29 22:54:00 +0000861 warning = "Hard reset unsupported on %s"
862 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000863 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000864 raise error.AutotestRunError("%s failed to boot after %ds" %
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700865 (self.host.hostname,
866 self.host.DEFAULT_REBOOT_TIMEOUT))
jadmanski4600e342008-10-29 22:54:00 +0000867 self.host.reboot_followup()
mblighdc735a22007-08-02 16:54:37 +0000868
869
Dale Curtis9285ddf2011-01-05 11:47:24 -0800870 def execute_control(self, timeout=None, client_disconnect_timeout=None):
jadmanski6bb32d72009-03-19 20:25:24 +0000871 if not self.background:
872 collector = log_collector(self.host, self.tag, self.results_dir)
873 hostname = self.host.hostname
874 remote_results = collector.client_results_dir
875 local_results = collector.server_results_dir
876 self.host.job.add_client_log(hostname, remote_results,
877 local_results)
jadmanski52053632010-06-11 21:08:10 +0000878 job_record_context = self.host.job.get_record_context()
jadmanski6bb32d72009-03-19 20:25:24 +0000879
jadmanski0afbb632008-06-06 21:10:57 +0000880 section = 0
jadmanski4600e342008-10-29 22:54:00 +0000881 start_time = time.time()
882
jadmanski043e1132008-11-19 17:10:32 +0000883 logger = client_logger(self.host, self.tag, self.results_dir)
jadmanski4600e342008-10-29 22:54:00 +0000884 try:
885 while not timeout or time.time() < start_time + timeout:
886 if timeout:
887 section_timeout = start_time + timeout - time.time()
888 else:
889 section_timeout = None
jadmanskic0354912010-01-12 15:57:29 +0000890 boot_id = self.host.get_boot_id()
jadmanski4600e342008-10-29 22:54:00 +0000891 last = self.execute_section(section, section_timeout,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800892 logger, client_disconnect_timeout)
mblighb3c0c912008-11-27 00:32:45 +0000893 if self.background:
894 return
jadmanski4600e342008-10-29 22:54:00 +0000895 section += 1
jadmanskib264ed02009-01-12 23:54:27 +0000896 if self.is_client_job_finished(last):
showardb18134f2009-03-20 20:52:18 +0000897 logging.info("Client complete")
jadmanski4600e342008-10-29 22:54:00 +0000898 return
jadmanskib264ed02009-01-12 23:54:27 +0000899 elif self.is_client_job_rebooting(last):
jadmanski79ab9282008-11-11 17:53:12 +0000900 try:
jadmanskic0354912010-01-12 15:57:29 +0000901 self._wait_for_reboot(boot_id)
jadmanski79ab9282008-11-11 17:53:12 +0000902 except error.AutotestRunError, e:
jadmanski043e1132008-11-19 17:10:32 +0000903 self.host.job.record("ABORT", None, "reboot", str(e))
904 self.host.job.record("END ABORT", None, None, str(e))
jadmanski79ab9282008-11-11 17:53:12 +0000905 raise
jadmanski4600e342008-10-29 22:54:00 +0000906 continue
907
beeps607428b2013-03-25 16:43:20 -0700908 # If a test fails without probable cause we try to bucket it's
909 # failure into one of 2 categories. If we can determine the
910 # current state of the device and it is suspicious, we close the
911 # status lines indicating a failure. If we either cannot
912 # determine the state of the device, or it appears totally
913 # healthy, we give up and abort.
914 try:
915 self._diagnose_dut(boot_id)
916 except AutotestDeviceError as e:
917 # The status lines of the test are pretty much tailed to
918 # our log, with indentation, from the client job on the DUT.
919 # So if the DUT goes down unexpectedly we'll end up with a
920 # malformed status log unless we manually unwind the status
921 # stack. Ideally we would want to write a nice wrapper like
922 # server_job methods run_reboot, run_group but they expect
923 # reboots and we don't.
924 self.host.job.record('FAIL', None, None, str(e))
925 self.host.job.record('END FAIL', None, None)
926 self.host.job.record('END GOOD', None, None)
Dan Shib03ea9d2013-08-15 17:13:27 -0700927 self.host.job.failed_with_device_error = True
beeps607428b2013-03-25 16:43:20 -0700928 return
929 except AutotestAbort as e:
930 self.host.job.record('ABORT', None, None, str(e))
931 self.host.job.record('END ABORT', None, None)
jadmanski4600e342008-10-29 22:54:00 +0000932
beeps607428b2013-03-25 16:43:20 -0700933 # give the client machine a chance to recover from a crash
934 self.host.wait_up(
935 self.host.HOURS_TO_WAIT_FOR_RECOVERY * 3600)
936 msg = ("Aborting - unexpected final status message from "
937 "client on %s: %s\n") % (self.host.hostname, last)
938 raise error.AutotestRunError(msg)
jadmanski4600e342008-10-29 22:54:00 +0000939 finally:
jadmanski043e1132008-11-19 17:10:32 +0000940 logger.close()
jadmanski6bb32d72009-03-19 20:25:24 +0000941 if not self.background:
942 collector.collect_client_job_results()
jadmanski4d03cf62010-03-04 18:32:28 +0000943 collector.remove_redundant_client_logs()
mblighfc3da5b2010-01-06 18:37:22 +0000944 state_file = os.path.basename(self.remote_control_file
945 + '.state')
946 state_path = os.path.join(self.results_dir, state_file)
947 self.host.job.postprocess_client_state(state_path)
jadmanski6bb32d72009-03-19 20:25:24 +0000948 self.host.job.remove_client_log(hostname, remote_results,
949 local_results)
jadmanski52053632010-06-11 21:08:10 +0000950 job_record_context.restore()
mblighdcd57a82007-07-11 23:06:47 +0000951
jadmanski0afbb632008-06-06 21:10:57 +0000952 # should only get here if we timed out
953 assert timeout
954 raise error.AutotestTimeoutError()
mbligh0e4613b2007-10-29 16:55:07 +0000955
mblighdcd57a82007-07-11 23:06:47 +0000956
jadmanski043e1132008-11-19 17:10:32 +0000957class log_collector(object):
958 def __init__(self, host, client_tag, results_dir):
959 self.host = host
960 if not client_tag:
961 client_tag = "default"
962 self.client_results_dir = os.path.join(host.get_autodir(), "results",
963 client_tag)
964 self.server_results_dir = results_dir
965
966
967 def collect_client_job_results(self):
968 """ A method that collects all the current results of a running
969 client job into the results dir. By default does nothing as no
970 client job is running, but when running a client job you can override
971 this with something that will actually do something. """
972
973 # make an effort to wait for the machine to come up
974 try:
975 self.host.wait_up(timeout=30)
976 except error.AutoservError:
977 # don't worry about any errors, we'll try and
978 # get the results anyway
979 pass
980
jadmanski043e1132008-11-19 17:10:32 +0000981 # Copy all dirs in default to results_dir
Gabe Black1e1c41b2015-02-04 23:55:15 -0800982 timer = autotest_stats.Timer('collect_client_job_results')
Dan Shi209eefd2013-05-30 13:17:48 -0700983 timer.start()
jadmanski043e1132008-11-19 17:10:32 +0000984 try:
jadmanski043e1132008-11-19 17:10:32 +0000985 self.host.get_file(self.client_results_dir + '/',
mbligh45561782009-05-11 21:14:34 +0000986 self.server_results_dir, preserve_symlinks=True)
Dan Shib03ea9d2013-08-15 17:13:27 -0700987
Dan Shi209eefd2013-05-30 13:17:48 -0700988 # Only report time used for successful get_file calls.
989 timer.stop();
jadmanski043e1132008-11-19 17:10:32 +0000990 except Exception:
991 # well, don't stop running just because we couldn't get logs
showardb18134f2009-03-20 20:52:18 +0000992 e_msg = "Unexpected error copying test result logs, continuing ..."
993 logging.error(e_msg)
jadmanski043e1132008-11-19 17:10:32 +0000994 traceback.print_exc(file=sys.stdout)
995
996
jadmanski4d03cf62010-03-04 18:32:28 +0000997 def remove_redundant_client_logs(self):
998 """Remove client.*.log files in favour of client.*.DEBUG files."""
999 debug_dir = os.path.join(self.server_results_dir, 'debug')
1000 debug_files = [f for f in os.listdir(debug_dir)
1001 if re.search(r'^client\.\d+\.DEBUG$', f)]
1002 for debug_file in debug_files:
1003 log_file = debug_file.replace('DEBUG', 'log')
1004 log_file = os.path.join(debug_dir, log_file)
1005 if os.path.exists(log_file):
1006 os.remove(log_file)
1007
1008
jadmanski043e1132008-11-19 17:10:32 +00001009# a file-like object for catching stderr from an autotest client and
1010# extracting status logs from it
Chris Sosa3ee5d5c2012-02-23 11:18:41 -08001011class BaseClientLogger(object):
jadmanski043e1132008-11-19 17:10:32 +00001012 """Partial file object to write to both stdout and
1013 the status log file. We only implement those methods
1014 utils.run() actually calls.
jadmanski043e1132008-11-19 17:10:32 +00001015 """
1016 status_parser = re.compile(r"^AUTOTEST_STATUS:([^:]*):(.*)$")
1017 test_complete_parser = re.compile(r"^AUTOTEST_TEST_COMPLETE:(.*)$")
jadmanskib1a51132009-08-07 16:45:50 +00001018 fetch_package_parser = re.compile(
1019 r"^AUTOTEST_FETCH_PACKAGE:([^:]*):([^:]*):(.*)$")
jadmanski043e1132008-11-19 17:10:32 +00001020 extract_indent = re.compile(r"^(\t*).*$")
jadmanskiefe4ebf2009-05-21 22:12:30 +00001021 extract_timestamp = re.compile(r".*\ttimestamp=(\d+)\t.*$")
jadmanski043e1132008-11-19 17:10:32 +00001022
1023 def __init__(self, host, tag, server_results_dir):
1024 self.host = host
1025 self.job = host.job
1026 self.log_collector = log_collector(host, tag, server_results_dir)
1027 self.leftover = ""
1028 self.last_line = ""
1029 self.logs = {}
jadmanskiefe4ebf2009-05-21 22:12:30 +00001030
1031
jadmanski043e1132008-11-19 17:10:32 +00001032 def _process_log_dict(self, log_dict):
1033 log_list = log_dict.pop("logs", [])
1034 for key in sorted(log_dict.iterkeys()):
1035 log_list += self._process_log_dict(log_dict.pop(key))
1036 return log_list
1037
1038
1039 def _process_logs(self):
1040 """Go through the accumulated logs in self.log and print them
1041 out to stdout and the status log. Note that this processes
1042 logs in an ordering where:
1043
1044 1) logs to different tags are never interleaved
1045 2) logs to x.y come before logs to x.y.z for all z
1046 3) logs to x.y come before x.z whenever y < z
1047
1048 Note that this will in general not be the same as the
1049 chronological ordering of the logs. However, if a chronological
1050 ordering is desired that one can be reconstructed from the
1051 status log by looking at timestamp lines."""
1052 log_list = self._process_log_dict(self.logs)
jadmanski2a89dac2010-06-11 14:32:58 +00001053 for entry in log_list:
1054 self.job.record_entry(entry, log_in_subdir=False)
jadmanski043e1132008-11-19 17:10:32 +00001055 if log_list:
jadmanski2a89dac2010-06-11 14:32:58 +00001056 self.last_line = log_list[-1].render()
jadmanski043e1132008-11-19 17:10:32 +00001057
1058
1059 def _process_quoted_line(self, tag, line):
1060 """Process a line quoted with an AUTOTEST_STATUS flag. If the
1061 tag is blank then we want to push out all the data we've been
1062 building up in self.logs, and then the newest line. If the
1063 tag is not blank, then push the line into the logs for handling
1064 later."""
jadmanski2a89dac2010-06-11 14:32:58 +00001065 entry = base_job.status_log_entry.parse(line)
1066 if entry is None:
1067 return # the line contains no status lines
jadmanski043e1132008-11-19 17:10:32 +00001068 if tag == "":
1069 self._process_logs()
jadmanski2a89dac2010-06-11 14:32:58 +00001070 self.job.record_entry(entry, log_in_subdir=False)
jadmanski043e1132008-11-19 17:10:32 +00001071 self.last_line = line
1072 else:
1073 tag_parts = [int(x) for x in tag.split(".")]
1074 log_dict = self.logs
1075 for part in tag_parts:
1076 log_dict = log_dict.setdefault(part, {})
1077 log_list = log_dict.setdefault("logs", [])
jadmanski2a89dac2010-06-11 14:32:58 +00001078 log_list.append(entry)
jadmanski043e1132008-11-19 17:10:32 +00001079
1080
jadmanskif37df842009-02-11 00:03:26 +00001081 def _process_info_line(self, line):
1082 """Check if line is an INFO line, and if it is, interpret any control
1083 messages (e.g. enabling/disabling warnings) that it may contain."""
1084 match = re.search(r"^\t*INFO\t----\t----(.*)\t[^\t]*$", line)
1085 if not match:
1086 return # not an INFO line
1087 for field in match.group(1).split('\t'):
1088 if field.startswith("warnings.enable="):
jadmanski16a7ff72009-04-01 18:19:53 +00001089 func = self.job.warning_manager.enable_warnings
jadmanskif37df842009-02-11 00:03:26 +00001090 elif field.startswith("warnings.disable="):
jadmanski16a7ff72009-04-01 18:19:53 +00001091 func = self.job.warning_manager.disable_warnings
jadmanskif37df842009-02-11 00:03:26 +00001092 else:
1093 continue
1094 warning_type = field.split("=", 1)[1]
jadmanski16a7ff72009-04-01 18:19:53 +00001095 func(warning_type)
jadmanskif37df842009-02-11 00:03:26 +00001096
1097
jadmanski043e1132008-11-19 17:10:32 +00001098 def _process_line(self, line):
1099 """Write out a line of data to the appropriate stream. Status
1100 lines sent by autotest will be prepended with
1101 "AUTOTEST_STATUS", and all other lines are ssh error
1102 messages."""
1103 status_match = self.status_parser.search(line)
1104 test_complete_match = self.test_complete_parser.search(line)
jadmanskib1a51132009-08-07 16:45:50 +00001105 fetch_package_match = self.fetch_package_parser.search(line)
jadmanski043e1132008-11-19 17:10:32 +00001106 if status_match:
1107 tag, line = status_match.groups()
jadmanskif37df842009-02-11 00:03:26 +00001108 self._process_info_line(line)
jadmanski043e1132008-11-19 17:10:32 +00001109 self._process_quoted_line(tag, line)
1110 elif test_complete_match:
jadmanskifcc0d5d2009-02-12 21:52:54 +00001111 self._process_logs()
jadmanski043e1132008-11-19 17:10:32 +00001112 fifo_path, = test_complete_match.groups()
mbligh060c4712009-12-29 02:43:35 +00001113 try:
1114 self.log_collector.collect_client_job_results()
1115 self.host.run("echo A > %s" % fifo_path)
1116 except Exception:
1117 msg = "Post-test log collection failed, continuing anyway"
1118 logging.exception(msg)
jadmanskib1a51132009-08-07 16:45:50 +00001119 elif fetch_package_match:
1120 pkg_name, dest_path, fifo_path = fetch_package_match.groups()
jadmanskiede7e242009-08-10 15:43:33 +00001121 serve_packages = global_config.global_config.get_config_value(
1122 "PACKAGES", "serve_packages_from_autoserv", type=bool)
1123 if serve_packages and pkg_name.endswith(".tar.bz2"):
1124 try:
1125 self._send_tarball(pkg_name, dest_path)
1126 except Exception:
1127 msg = "Package tarball creation failed, continuing anyway"
1128 logging.exception(msg)
mbligh060c4712009-12-29 02:43:35 +00001129 try:
1130 self.host.run("echo B > %s" % fifo_path)
1131 except Exception:
1132 msg = "Package tarball installation failed, continuing anyway"
1133 logging.exception(msg)
jadmanski043e1132008-11-19 17:10:32 +00001134 else:
showardb18134f2009-03-20 20:52:18 +00001135 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +00001136
1137
jadmanskiede7e242009-08-10 15:43:33 +00001138 def _send_tarball(self, pkg_name, remote_dest):
1139 name, pkg_type = self.job.pkgmgr.parse_tarball_name(pkg_name)
1140 src_dirs = []
1141 if pkg_type == 'test':
mbligh1f572e52010-04-01 17:15:53 +00001142 for test_dir in ['site_tests', 'tests']:
1143 src_dir = os.path.join(self.job.clientdir, test_dir, name)
1144 if os.path.exists(src_dir):
1145 src_dirs += [src_dir]
1146 if autoserv_prebuild:
1147 prebuild.setup(self.job.clientdir, src_dir)
1148 break
jadmanskiede7e242009-08-10 15:43:33 +00001149 elif pkg_type == 'profiler':
1150 src_dirs += [os.path.join(self.job.clientdir, 'profilers', name)]
mbligh1f572e52010-04-01 17:15:53 +00001151 if autoserv_prebuild:
1152 prebuild.setup(self.job.clientdir, src_dir)
jadmanskiede7e242009-08-10 15:43:33 +00001153 elif pkg_type == 'dep':
1154 src_dirs += [os.path.join(self.job.clientdir, 'deps', name)]
1155 elif pkg_type == 'client':
1156 return # you must already have a client to hit this anyway
1157 else:
1158 return # no other types are supported
1159
1160 # iterate over src_dirs until we find one that exists, then tar it
1161 for src_dir in src_dirs:
1162 if os.path.exists(src_dir):
1163 try:
1164 logging.info('Bundling %s into %s', src_dir, pkg_name)
1165 temp_dir = autotemp.tempdir(unique_id='autoserv-packager',
1166 dir=self.job.tmpdir)
1167 tarball_path = self.job.pkgmgr.tar_package(
mblighbccad482009-08-24 22:08:31 +00001168 pkg_name, src_dir, temp_dir.name, " .")
jadmanskiede7e242009-08-10 15:43:33 +00001169 self.host.send_file(tarball_path, remote_dest)
1170 finally:
1171 temp_dir.clean()
1172 return
1173
1174
jadmanski91d56a92009-04-01 15:20:40 +00001175 def log_warning(self, msg, warning_type):
jadmanski6dadd832009-02-05 23:39:27 +00001176 """Injects a WARN message into the current status logging stream."""
jadmanski91d56a92009-04-01 15:20:40 +00001177 timestamp = int(time.time())
1178 if self.job.warning_manager.is_valid(timestamp, warning_type):
Eric Lid656d562011-04-20 11:48:29 -07001179 self.job.record('WARN', None, None, msg)
jadmanski6dadd832009-02-05 23:39:27 +00001180
jadmanski043e1132008-11-19 17:10:32 +00001181
1182 def write(self, data):
jadmanski2a89dac2010-06-11 14:32:58 +00001183 # now start processing the existing buffer and the new data
jadmanski043e1132008-11-19 17:10:32 +00001184 data = self.leftover + data
mbligh060c4712009-12-29 02:43:35 +00001185 lines = data.split('\n')
1186 processed_lines = 0
1187 try:
1188 # process all the buffered data except the last line
1189 # ignore the last line since we may not have all of it yet
1190 for line in lines[:-1]:
mbligh060c4712009-12-29 02:43:35 +00001191 self._process_line(line)
1192 processed_lines += 1
1193 finally:
1194 # save any unprocessed lines for future processing
1195 self.leftover = '\n'.join(lines[processed_lines:])
jadmanski043e1132008-11-19 17:10:32 +00001196
1197
1198 def flush(self):
1199 sys.stdout.flush()
1200
1201
jadmanskia61edad2009-05-21 22:17:49 +00001202 def flush_all_buffers(self):
jadmanski043e1132008-11-19 17:10:32 +00001203 if self.leftover:
1204 self._process_line(self.leftover)
jadmanskia61edad2009-05-21 22:17:49 +00001205 self.leftover = ""
jadmanski043e1132008-11-19 17:10:32 +00001206 self._process_logs()
1207 self.flush()
1208
1209
jadmanskia61edad2009-05-21 22:17:49 +00001210 def close(self):
1211 self.flush_all_buffers()
1212
1213
mbligha7007722009-01-13 00:37:11 +00001214SiteAutotest = client_utils.import_site_class(
1215 __file__, "autotest_lib.server.site_autotest", "SiteAutotest",
1216 BaseAutotest)
mblighd8b39252008-03-20 21:15:03 +00001217
showardad812bf2009-10-20 23:49:56 +00001218
jadmanski69bdaac2010-07-28 16:27:20 +00001219_SiteRun = client_utils.import_site_class(
1220 __file__, "autotest_lib.server.site_autotest", "_SiteRun", _BaseRun)
1221
1222
Chris Sosa3ee5d5c2012-02-23 11:18:41 -08001223SiteClientLogger = client_utils.import_site_class(
1224 __file__, "autotest_lib.server.site_autotest", "SiteClientLogger",
1225 BaseClientLogger)
1226
1227
mblighd8b39252008-03-20 21:15:03 +00001228class Autotest(SiteAutotest):
jadmanski0afbb632008-06-06 21:10:57 +00001229 pass
jadmanskic6136e92010-07-19 16:41:49 +00001230
1231
Chris Sosa3ee5d5c2012-02-23 11:18:41 -08001232class client_logger(SiteClientLogger):
1233 pass
1234
1235
jadmanski69bdaac2010-07-28 16:27:20 +00001236class _Run(_SiteRun):
1237 pass