blob: aaf17fff48a19a176a588d6725fae92a47353b98 [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)
149 raise error.AutoservInstallError(
Dale Curtis74a314b2011-06-23 14:55:46 -0700150 'Unable to find a place to install Autotest; tried %s' %
showardad812bf2009-10-20 23:49:56 +0000151 ', '.join(client_autodir_paths))
jadmanskif22fea82008-11-26 20:57:07 +0000152
153
Eric Lid656d562011-04-20 11:48:29 -0700154 def get_fetch_location(self):
155 c = global_config.global_config
156 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list,
157 default=[])
158 repos.reverse()
159 return repos
160
161
Dan Shib669cbd2013-09-13 11:17:17 -0700162 def install(self, host=None, autodir=None, use_packaging=True):
163 self._install(host=host, autodir=autodir, use_packaging=use_packaging)
jadmanski54f90af2008-10-10 16:20:55 +0000164
165
mblighb8aa75b2009-09-18 16:50:37 +0000166 def install_full_client(self, host=None, autodir=None):
167 self._install(host=host, autodir=autodir, use_autoserv=False,
168 use_packaging=False)
169
170
mblighbccad482009-08-24 22:08:31 +0000171 def install_no_autoserv(self, host=None, autodir=None):
mblighb8aa75b2009-09-18 16:50:37 +0000172 self._install(host=host, autodir=autodir, use_autoserv=False)
mblighbccad482009-08-24 22:08:31 +0000173
174
mblighb8aa75b2009-09-18 16:50:37 +0000175 def _install_using_packaging(self, host, autodir):
Eric Lid656d562011-04-20 11:48:29 -0700176 repos = self.get_fetch_location()
mblighb8aa75b2009-09-18 16:50:37 +0000177 if not repos:
178 raise error.PackageInstallError("No repos to install an "
179 "autotest client from")
180 pkgmgr = packages.PackageManager(autodir, hostname=host.hostname,
181 repo_urls=repos,
182 do_locking=False,
183 run_function=host.run,
184 run_function_dargs=dict(timeout=600))
185 # The packages dir is used to store all the packages that
186 # are fetched on that client. (for the tests,deps etc.
187 # too apart from the client)
188 pkg_dir = os.path.join(autodir, 'packages')
189 # clean up the autodir except for the packages directory
190 host.run('cd %s && ls | grep -v "^packages$"'
Laurence Goodby2de320d2015-07-08 14:45:18 -0700191 ' | xargs rm -rf && rm -rf .[!.]*' % autodir)
mblighb8aa75b2009-09-18 16:50:37 +0000192 pkgmgr.install_pkg('autotest', 'client', pkg_dir, autodir,
193 preserve_install_dir=True)
194 self.installed = True
195
196
197 def _install_using_send_file(self, host, autodir):
Chris Sosa3ee5d5c2012-02-23 11:18:41 -0800198 dirs_to_exclude = set(["tests", "site_tests", "deps", "profilers",
199 "packages"])
mblighb8aa75b2009-09-18 16:50:37 +0000200 light_files = [os.path.join(self.source_material, f)
201 for f in os.listdir(self.source_material)
202 if f not in dirs_to_exclude]
203 host.send_file(light_files, autodir, delete_dest=True)
204
205 # create empty dirs for all the stuff we excluded
206 commands = []
207 for path in dirs_to_exclude:
208 abs_path = os.path.join(autodir, path)
209 abs_path = utils.sh_escape(abs_path)
210 commands.append("mkdir -p '%s'" % abs_path)
211 commands.append("touch '%s'/__init__.py" % abs_path)
212 host.run(';'.join(commands))
213
214
215 def _install(self, host=None, autodir=None, use_autoserv=True,
216 use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000217 """
218 Install autotest. If get() was not called previously, an
219 attempt will be made to install from the autotest svn
220 repository.
mbligh9a3f5e52008-05-28 21:21:43 +0000221
mblighbccad482009-08-24 22:08:31 +0000222 @param host A Host instance on which autotest will be installed
223 @param autodir Location on the remote host to install to
mblighb8aa75b2009-09-18 16:50:37 +0000224 @param use_autoserv Enable install modes that depend on the client
mblighbccad482009-08-24 22:08:31 +0000225 running with the autoserv harness
mblighb8aa75b2009-09-18 16:50:37 +0000226 @param use_packaging Enable install modes that use the packaging system
mbligh9a3f5e52008-05-28 21:21:43 +0000227
mblighbccad482009-08-24 22:08:31 +0000228 @exception AutoservError if a tarball was not specified and
229 the target host does not have svn installed in its path
230 """
jadmanski0afbb632008-06-06 21:10:57 +0000231 if not host:
232 host = self.host
233 if not self.got:
234 self.get()
235 host.wait_up(timeout=30)
236 host.setup()
showardb18134f2009-03-20 20:52:18 +0000237 logging.info("Installing autotest on %s", host.hostname)
mbligh40f122a2007-11-03 23:08:46 +0000238
jadmanski54f90af2008-10-10 16:20:55 +0000239 # set up the autotest directory on the remote machine
240 if not autodir:
showardad812bf2009-10-20 23:49:56 +0000241 autodir = self.get_install_dir(host)
242 logging.info('Using installation dir %s', autodir)
mbligh0562e652008-08-20 20:11:45 +0000243 host.set_autodir(autodir)
jadmanski3c236942009-03-04 17:51:26 +0000244 host.run('mkdir -p %s' % utils.sh_escape(autodir))
mbligh40f122a2007-11-03 23:08:46 +0000245
jadmanski1c3c07b2009-03-03 23:29:36 +0000246 # make sure there are no files in $AUTODIR/results
247 results_path = os.path.join(autodir, 'results')
jadmanski3c236942009-03-04 17:51:26 +0000248 host.run('rm -rf %s/*' % utils.sh_escape(results_path),
jadmanski1c3c07b2009-03-03 23:29:36 +0000249 ignore_status=True)
250
mblighc5ddfd12008-08-04 17:15:00 +0000251 # Fetch the autotest client from the nearest repository
mblighb8aa75b2009-09-18 16:50:37 +0000252 if use_packaging:
253 try:
254 self._install_using_packaging(host, autodir)
Dan Shib669cbd2013-09-13 11:17:17 -0700255 logging.info("Installation of autotest completed using the "
256 "packaging system.")
mblighb8aa75b2009-09-18 16:50:37 +0000257 return
Eric Li6f27d4f2010-09-29 10:55:17 -0700258 except (error.PackageInstallError, error.AutoservRunError,
259 global_config.ConfigError), e:
mblighb8aa75b2009-09-18 16:50:37 +0000260 logging.info("Could not install autotest using the packaging "
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700261 "system: %s. Trying other methods", e)
Dan Shi1889ca12015-04-15 09:36:06 -0700262 else:
263 # Delete the package checksum file to force dut updating local
264 # packages.
265 command = ('rm -f "%s"' %
266 (os.path.join(autodir, base_packages.CHECKSUM_FILE)))
267 host.run(command)
mblighc5ddfd12008-08-04 17:15:00 +0000268
jadmanski0afbb632008-06-06 21:10:57 +0000269 # try to install from file or directory
270 if self.source_material:
jadmanski69bdaac2010-07-28 16:27:20 +0000271 c = global_config.global_config
272 supports_autoserv_packaging = c.get_config_value(
273 "PACKAGES", "serve_packages_from_autoserv", type=bool)
274 # Copy autotest recursively
275 if supports_autoserv_packaging and use_autoserv:
276 self._install_using_send_file(host, autodir)
jadmanski0afbb632008-06-06 21:10:57 +0000277 else:
jadmanski69bdaac2010-07-28 16:27:20 +0000278 host.send_file(self.source_material, autodir, delete_dest=True)
Dan Shib669cbd2013-09-13 11:17:17 -0700279 logging.info("Installation of autotest completed from %s",
280 self.source_material)
jadmanski0afbb632008-06-06 21:10:57 +0000281 self.installed = True
282 return
mbligh91334902007-09-28 01:47:59 +0000283
jadmanski0afbb632008-06-06 21:10:57 +0000284 # if that fails try to install using svn
285 if utils.run('which svn').exit_status:
Dale Curtis74a314b2011-06-23 14:55:46 -0700286 raise error.AutoservError('svn not found on target machine: %s' %
287 host.name)
jadmanski0afbb632008-06-06 21:10:57 +0000288 try:
mbligh78bf5352008-07-11 20:27:36 +0000289 host.run('svn checkout %s %s' % (AUTOTEST_SVN, autodir))
jadmanski0afbb632008-06-06 21:10:57 +0000290 except error.AutoservRunError, e:
mbligh78bf5352008-07-11 20:27:36 +0000291 host.run('svn checkout %s %s' % (AUTOTEST_HTTP, autodir))
Dan Shib669cbd2013-09-13 11:17:17 -0700292 logging.info("Installation of autotest completed using SVN.")
jadmanski0afbb632008-06-06 21:10:57 +0000293 self.installed = True
mbligh91334902007-09-28 01:47:59 +0000294
295
jadmanski7c7aff32009-03-25 22:43:07 +0000296 def uninstall(self, host=None):
297 """
298 Uninstall (i.e. delete) autotest. Removes the autotest client install
299 from the specified host.
300
301 @params host a Host instance from which the client will be removed
302 """
303 if not self.installed:
304 return
305 if not host:
306 host = self.host
307 autodir = host.get_autodir()
308 if not autodir:
309 return
310
311 # perform the actual uninstall
312 host.run("rm -rf %s" % utils.sh_escape(autodir), ignore_status=True)
313 host.set_autodir(None)
314 self.installed = False
315
316
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700317 def get(self, location=None):
jadmanski0afbb632008-06-06 21:10:57 +0000318 if not location:
319 location = os.path.join(self.serverdir, '../client')
320 location = os.path.abspath(location)
321 # If there's stuff run on our client directory already, it
322 # can cause problems. Try giving it a quick clean first.
323 cwd = os.getcwd()
324 os.chdir(location)
showard4b976072009-10-20 23:50:08 +0000325 try:
326 utils.system('tools/make_clean', ignore_status=True)
327 finally:
328 os.chdir(cwd)
jadmanski0afbb632008-06-06 21:10:57 +0000329 super(BaseAutotest, self).get(location)
330 self.got = True
mblighdcd57a82007-07-11 23:06:47 +0000331
332
mblighe7d9c602009-07-02 19:02:33 +0000333 def run(self, control_file, results_dir='.', host=None, timeout=None,
334 tag=None, parallel_flag=False, background=False,
Dan Shib669cbd2013-09-13 11:17:17 -0700335 client_disconnect_timeout=None, use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000336 """
337 Run an autotest job on the remote machine.
mbligh9a3f5e52008-05-28 21:21:43 +0000338
mblighe7d9c602009-07-02 19:02:33 +0000339 @param control_file: An open file-like-obj of the control file.
340 @param results_dir: A str path where the results should be stored
341 on the local filesystem.
342 @param host: A Host instance on which the control file should
343 be run.
344 @param timeout: Maximum number of seconds to wait for the run or None.
345 @param tag: Tag name for the client side instance of autotest.
346 @param parallel_flag: Flag set when multiple jobs are run at the
347 same time.
348 @param background: Indicates that the client should be launched as
349 a background job; the code calling run will be responsible
350 for monitoring the client and collecting the results.
351 @param client_disconnect_timeout: Seconds to wait for the remote host
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700352 to come back after a reboot. Defaults to the host setting for
353 DEFAULT_REBOOT_TIMEOUT.
mblighe7d9c602009-07-02 19:02:33 +0000354
355 @raises AutotestRunError: If there is a problem executing
356 the control file.
jadmanski0afbb632008-06-06 21:10:57 +0000357 """
Dan Shib669cbd2013-09-13 11:17:17 -0700358 host = self._get_host_and_setup(host, use_packaging=use_packaging)
jadmanski0afbb632008-06-06 21:10:57 +0000359 results_dir = os.path.abspath(results_dir)
mblighc1cbc992008-05-27 20:01:45 +0000360
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700361 if client_disconnect_timeout is None:
362 client_disconnect_timeout = host.DEFAULT_REBOOT_TIMEOUT
363
jadmanski0afbb632008-06-06 21:10:57 +0000364 if tag:
365 results_dir = os.path.join(results_dir, tag)
mblighc1cbc992008-05-27 20:01:45 +0000366
mblighb3c0c912008-11-27 00:32:45 +0000367 atrun = _Run(host, results_dir, tag, parallel_flag, background)
jadmanski6dadd832009-02-05 23:39:27 +0000368 self._do_run(control_file, results_dir, host, atrun, timeout,
Dan Shib669cbd2013-09-13 11:17:17 -0700369 client_disconnect_timeout, use_packaging=use_packaging)
mblighd8b39252008-03-20 21:15:03 +0000370
371
Dan Shib669cbd2013-09-13 11:17:17 -0700372 def _get_host_and_setup(self, host, use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000373 if not host:
374 host = self.host
375 if not self.installed:
Dan Shib669cbd2013-09-13 11:17:17 -0700376 self.install(host, use_packaging=use_packaging)
mbligh91334902007-09-28 01:47:59 +0000377
jadmanski0afbb632008-06-06 21:10:57 +0000378 host.wait_up(timeout=30)
379 return host
mblighd8b39252008-03-20 21:15:03 +0000380
381
jadmanski6dadd832009-02-05 23:39:27 +0000382 def _do_run(self, control_file, results_dir, host, atrun, timeout,
Dan Shib669cbd2013-09-13 11:17:17 -0700383 client_disconnect_timeout, use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000384 try:
385 atrun.verify_machine()
386 except:
showardb18134f2009-03-20 20:52:18 +0000387 logging.error("Verify failed on %s. Reinstalling autotest",
388 host.hostname)
jadmanski0afbb632008-06-06 21:10:57 +0000389 self.install(host)
Fang Dengb9cd83c2015-01-27 10:16:08 -0800390 atrun.verify_machine()
jadmanski0afbb632008-06-06 21:10:57 +0000391 debug = os.path.join(results_dir, 'debug')
392 try:
393 os.makedirs(debug)
mbligh09108442008-10-15 16:27:38 +0000394 except Exception:
jadmanski0afbb632008-06-06 21:10:57 +0000395 pass
mbligh9a3f5e52008-05-28 21:21:43 +0000396
mbligh09108442008-10-15 16:27:38 +0000397 delete_file_list = [atrun.remote_control_file,
398 atrun.remote_control_file + '.state',
399 atrun.manual_control_file,
400 atrun.manual_control_file + '.state']
401 cmd = ';'.join('rm -f ' + control for control in delete_file_list)
402 host.run(cmd, ignore_status=True)
mbligh9a3f5e52008-05-28 21:21:43 +0000403
Dale Curtis386eea72011-09-21 18:43:04 -0700404 tmppath = utils.get(control_file, local_copy=True)
mblighc5ddfd12008-08-04 17:15:00 +0000405
jadmanskicb0e1612009-02-27 18:03:10 +0000406 # build up the initialization prologue for the control file
407 prologue_lines = []
jadmanski23afbec2008-09-17 18:12:07 +0000408
mbligh2f076832010-03-30 17:08:20 +0000409 # Add the additional user arguments
jadmanski808f4b12010-04-09 22:30:31 +0000410 prologue_lines.append("args = %r\n" % self.job.args)
mbligh2f076832010-03-30 17:08:20 +0000411
mbligh09108442008-10-15 16:27:38 +0000412 # If the packaging system is being used, add the repository list.
mblighddc9a402010-01-15 20:33:34 +0000413 repos = None
mblighc5ddfd12008-08-04 17:15:00 +0000414 try:
Dan Shib669cbd2013-09-13 11:17:17 -0700415 if use_packaging:
416 repos = self.get_fetch_location()
417 prologue_lines.append('job.add_repository(%s)\n' % repos)
418 else:
419 logging.debug('use_packaging is set to False, do not add any '
420 'repository.')
mblighc5ddfd12008-08-04 17:15:00 +0000421 except global_config.ConfigError, e:
mblighddc9a402010-01-15 20:33:34 +0000422 # If repos is defined packaging is enabled so log the error
423 if repos:
424 logging.error(e)
mblighc5ddfd12008-08-04 17:15:00 +0000425
jadmanskie2eef7b2009-03-03 23:55:13 +0000426 # on full-size installs, turn on any profilers the server is using
jadmanski27b52912009-08-14 17:09:15 +0000427 if not atrun.background:
jadmanskie2eef7b2009-03-03 23:55:13 +0000428 running_profilers = host.job.profilers.add_log.iteritems()
429 for profiler, (args, dargs) in running_profilers:
430 call_args = [repr(profiler)]
431 call_args += [repr(arg) for arg in args]
432 call_args += ["%s=%r" % item for item in dargs.iteritems()]
433 prologue_lines.append("job.profilers.add(%s)\n"
434 % ", ".join(call_args))
435 cfile = "".join(prologue_lines)
436
mbligh09108442008-10-15 16:27:38 +0000437 cfile += open(tmppath).read()
438 open(tmppath, "w").write(cfile)
mblighc5ddfd12008-08-04 17:15:00 +0000439
jadmanskic09fc152008-10-15 17:56:59 +0000440 # Create and copy state file to remote_control_file + '.state'
mblighfc3da5b2010-01-06 18:37:22 +0000441 state_file = host.job.preprocess_client_state()
mblighfbf73ae2009-12-19 05:22:42 +0000442 host.send_file(state_file, atrun.remote_control_file + '.init.state')
jadmanskic09fc152008-10-15 17:56:59 +0000443 os.remove(state_file)
444
mblighc5ddfd12008-08-04 17:15:00 +0000445 # Copy control_file to remote_control_file on the host
jadmanski0afbb632008-06-06 21:10:57 +0000446 host.send_file(tmppath, atrun.remote_control_file)
447 if os.path.abspath(tmppath) != os.path.abspath(control_file):
448 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000449
jadmanski6bb32d72009-03-19 20:25:24 +0000450 atrun.execute_control(
jadmanski6dadd832009-02-05 23:39:27 +0000451 timeout=timeout,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800452 client_disconnect_timeout=client_disconnect_timeout)
jadmanski23afbec2008-09-17 18:12:07 +0000453
454
jadmanski0afbb632008-06-06 21:10:57 +0000455 def run_timed_test(self, test_name, results_dir='.', host=None,
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700456 timeout=None, parallel_flag=False, background=False,
457 client_disconnect_timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000458 """
459 Assemble a tiny little control file to just run one test,
460 and run it as an autotest client-side test
461 """
462 if not host:
463 host = self.host
464 if not self.installed:
465 self.install(host)
466 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
467 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
468 control = "job.run_test(%s)\n" % cmd
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700469 self.run(control, results_dir, host, timeout=timeout,
Andrew Bresticker2da1b762013-01-15 16:11:18 -0800470 parallel_flag=parallel_flag, background=background,
471 client_disconnect_timeout=client_disconnect_timeout)
mbligh0e4613b2007-10-29 16:55:07 +0000472
473
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700474 def run_test(self, test_name, results_dir='.', host=None,
Andrew Bresticker2da1b762013-01-15 16:11:18 -0800475 parallel_flag=False, background=False,
476 client_disconnect_timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000477 self.run_timed_test(test_name, results_dir, host, timeout=None,
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700478 parallel_flag=parallel_flag, background=background,
Andrew Bresticker2da1b762013-01-15 16:11:18 -0800479 client_disconnect_timeout=client_disconnect_timeout,
jadmanskic98c4702009-01-05 15:50:06 +0000480 *args, **dargs)
mblighd54832b2007-07-25 16:46:56 +0000481
482
jadmanski69bdaac2010-07-28 16:27:20 +0000483class _BaseRun(object):
jadmanski0afbb632008-06-06 21:10:57 +0000484 """
485 Represents a run of autotest control file. This class maintains
486 all the state necessary as an autotest control file is executed.
mblighdcd57a82007-07-11 23:06:47 +0000487
jadmanski0afbb632008-06-06 21:10:57 +0000488 It is not intended to be used directly, rather control files
489 should be run using the run method in Autotest.
490 """
mblighb3c0c912008-11-27 00:32:45 +0000491 def __init__(self, host, results_dir, tag, parallel_flag, background):
jadmanski0afbb632008-06-06 21:10:57 +0000492 self.host = host
493 self.results_dir = results_dir
494 self.env = host.env
495 self.tag = tag
496 self.parallel_flag = parallel_flag
mblighb3c0c912008-11-27 00:32:45 +0000497 self.background = background
showardad812bf2009-10-20 23:49:56 +0000498 self.autodir = Autotest.get_installed_autodir(self.host)
mbligh78bf5352008-07-11 20:27:36 +0000499 control = os.path.join(self.autodir, 'control')
jadmanski0afbb632008-06-06 21:10:57 +0000500 if tag:
mbligh78bf5352008-07-11 20:27:36 +0000501 control += '.' + tag
502 self.manual_control_file = control
503 self.remote_control_file = control + '.autoserv'
lmr6d08b3c2009-11-18 19:26:38 +0000504 self.config_file = os.path.join(self.autodir, 'global_config.ini')
mblighdc735a22007-08-02 16:54:37 +0000505
506
jadmanski0afbb632008-06-06 21:10:57 +0000507 def verify_machine(self):
508 binary = os.path.join(self.autodir, 'bin/autotest')
509 try:
510 self.host.run('ls %s > /dev/null 2>&1' % binary)
511 except:
lmrd6d27ed2009-12-08 19:58:33 +0000512 raise error.AutoservInstallError(
513 "Autotest does not appear to be installed")
mblighdc735a22007-08-02 16:54:37 +0000514
jadmanski0afbb632008-06-06 21:10:57 +0000515 if not self.parallel_flag:
516 tmpdir = os.path.join(self.autodir, 'tmp')
517 download = os.path.join(self.autodir, 'tests/download')
518 self.host.run('umount %s' % tmpdir, ignore_status=True)
519 self.host.run('umount %s' % download, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000520
jadmanski6dadd832009-02-05 23:39:27 +0000521
522 def get_base_cmd_args(self, section):
showard234b2962009-07-28 20:02:30 +0000523 args = ['--verbose']
jadmanski0afbb632008-06-06 21:10:57 +0000524 if section > 0:
jadmanski6dadd832009-02-05 23:39:27 +0000525 args.append('-c')
jadmanski0afbb632008-06-06 21:10:57 +0000526 if self.tag:
jadmanski6dadd832009-02-05 23:39:27 +0000527 args.append('-t %s' % self.tag)
jadmanski0afbb632008-06-06 21:10:57 +0000528 if self.host.job.use_external_logging():
jadmanski6dadd832009-02-05 23:39:27 +0000529 args.append('-l')
mblighce955fc2009-08-24 21:59:02 +0000530 if self.host.hostname:
531 args.append('--hostname=%s' % self.host.hostname)
mbligh0d0f67d2009-11-06 03:15:03 +0000532 args.append('--user=%s' % self.host.job.user)
mblighce955fc2009-08-24 21:59:02 +0000533
jadmanski6dadd832009-02-05 23:39:27 +0000534 args.append(self.remote_control_file)
535 return args
536
537
538 def get_background_cmd(self, section):
539 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotest_client')]
540 cmd += self.get_base_cmd_args(section)
jadmanski69bdaac2010-07-28 16:27:20 +0000541 cmd += ['>/dev/null', '2>/dev/null', '&']
jadmanski6dadd832009-02-05 23:39:27 +0000542 return ' '.join(cmd)
543
544
545 def get_daemon_cmd(self, section, monitor_dir):
546 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotestd'),
547 monitor_dir, '-H autoserv']
548 cmd += self.get_base_cmd_args(section)
jadmanski69bdaac2010-07-28 16:27:20 +0000549 cmd += ['>/dev/null', '2>/dev/null', '&']
jadmanski6dadd832009-02-05 23:39:27 +0000550 return ' '.join(cmd)
551
552
553 def get_monitor_cmd(self, monitor_dir, stdout_read, stderr_read):
554 cmd = [os.path.join(self.autodir, 'bin', 'autotestd_monitor'),
555 monitor_dir, str(stdout_read), str(stderr_read)]
jadmanski0afbb632008-06-06 21:10:57 +0000556 return ' '.join(cmd)
mblighadf2aab2007-11-29 18:16:43 +0000557
mblighd8b39252008-03-20 21:15:03 +0000558
jadmanski4d03cf62010-03-04 18:32:28 +0000559 def get_client_log(self):
560 """Find what the "next" client.* prefix should be
561
562 @returns A string of the form client.INTEGER that should be prefixed
563 to all client debug log files.
564 """
565 max_digit = -1
566 debug_dir = os.path.join(self.results_dir, 'debug')
567 client_logs = glob.glob(os.path.join(debug_dir, 'client.*.*'))
568 for log in client_logs:
569 _, number, _ = log.split('.', 2)
570 if number.isdigit():
571 max_digit = max(max_digit, int(number))
572 return 'client.%d' % (max_digit + 1)
573
574
575 def copy_client_config_file(self, client_log_prefix=None):
576 """
577 Create and copy the client config file based on the server config.
578
579 @param client_log_prefix: Optional prefix to prepend to log files.
580 """
581 client_config_file = self._create_client_config_file(client_log_prefix)
582 self.host.send_file(client_config_file, self.config_file)
583 os.remove(client_config_file)
584
585
586 def _create_client_config_file(self, client_log_prefix=None):
587 """
588 Create a temporary file with the [CLIENT] section configuration values
589 taken from the server global_config.ini.
590
591 @param client_log_prefix: Optional prefix to prepend to log files.
592
593 @return: Path of the temporary file generated.
594 """
595 config = global_config.global_config.get_section_values('CLIENT')
596 if client_log_prefix:
597 config.set('CLIENT', 'default_logging_name', client_log_prefix)
598 return self._create_aux_file(config.write)
599
600
601 def _create_aux_file(self, func, *args):
602 """
603 Creates a temporary file and writes content to it according to a
604 content creation function. The file object is appended to *args, which
605 is then passed to the content creation function
606
607 @param func: Function that will be used to write content to the
608 temporary file.
609 @param *args: List of parameters that func takes.
610 @return: Path to the temporary file that was created.
611 """
612 fd, path = tempfile.mkstemp(dir=self.host.job.tmpdir)
613 aux_file = os.fdopen(fd, "w")
614 try:
615 list_args = list(args)
616 list_args.append(aux_file)
617 func(*list_args)
618 finally:
619 aux_file.close()
620 return path
mblighd8b39252008-03-20 21:15:03 +0000621
622
jadmanskib264ed02009-01-12 23:54:27 +0000623 @staticmethod
624 def is_client_job_finished(last_line):
625 return bool(re.match(r'^END .*\t----\t----\t.*$', last_line))
626
627
628 @staticmethod
629 def is_client_job_rebooting(last_line):
630 return bool(re.match(r'^\t*GOOD\t----\treboot\.start.*$', last_line))
631
632
beeps607428b2013-03-25 16:43:20 -0700633 def _diagnose_dut(self, old_boot_id=None):
634 """
635 Run diagnostic checks on a DUT.
636
637 1. ping: A dead host will not respond to pings.
638 2. ssh (happens with 3.): DUT hangs usually fail in authentication
639 but respond to pings.
640 3. Check if a reboot occured: A healthy but unexpected reboot leaves the
641 host running with a new boot id.
642
643 This method will always raise an exception from the AutotestFailure
644 family and should only get called when the reason for a test failing
645 is ambiguous.
646
647 @raises AutotestDeviceNotPingable: If the DUT doesn't respond to ping.
648 @raises AutotestDeviceNotSSHable: If we cannot SSH into the DUT.
649 @raises AutotestDeviceRebooted: If the boot id changed.
650 @raises AutotestAbort: If none of the above exceptions were raised.
651 Since we have no recourse we must abort at this stage.
652 """
653 msg = 'Autotest client terminated unexpectedly: '
654 if utils.ping(self.host.hostname, tries=1, deadline=1) != 0:
655 msg += 'DUT is no longer pingable, it may have rebooted or hung.\n'
656 raise AutotestDeviceNotPingable(msg)
657
658 if old_boot_id:
659 try:
660 new_boot_id = self.host.get_boot_id(timeout=60)
661 except Exception as e:
662 msg += ('DUT is pingable but not SSHable, it most likely'
beeps14768812013-09-25 12:58:45 -0700663 ' sporadically rebooted during testing. %s\n' % str(e))
beeps607428b2013-03-25 16:43:20 -0700664 raise AutotestDeviceNotSSHable(msg)
665 else:
666 if new_boot_id != old_boot_id:
667 msg += 'DUT rebooted during the test run.\n'
668 raise AutotestDeviceRebooted(msg)
669
670 msg += ('DUT is pingable, SSHable and did NOT restart '
671 'un-expectedly. We probably lost connectivity during the '
672 'test.')
673 else:
674 msg += ('DUT is pingable, could not determine if an un-expected '
675 'reboot occured during the test.')
676
677 raise AutotestAbort(msg)
678
679
beeps07f53b92013-01-08 12:55:10 -0800680 def log_unexpected_abort(self, stderr_redirector, old_boot_id=None):
681 """
beeps607428b2013-03-25 16:43:20 -0700682 Logs that something unexpected happened, then tries to diagnose the
683 failure. The purpose of this function is only to close out the status
684 log with the appropriate error message, not to critically terminate
685 the program.
beeps07f53b92013-01-08 12:55:10 -0800686
687 @param stderr_redirector: log stream.
688 @param old_boot_id: boot id used to infer if a reboot occured.
689 """
jadmanskia61edad2009-05-21 22:17:49 +0000690 stderr_redirector.flush_all_buffers()
beeps607428b2013-03-25 16:43:20 -0700691 try:
692 self._diagnose_dut(old_boot_id)
693 except AutotestFailure as e:
694 self.host.job.record('END ABORT', None, None, str(e))
jadmanskib264ed02009-01-12 23:54:27 +0000695
696
jadmanski6dadd832009-02-05 23:39:27 +0000697 def _execute_in_background(self, section, timeout):
698 full_cmd = self.get_background_cmd(section)
699 devnull = open(os.devnull, "w")
mblighd8b39252008-03-20 21:15:03 +0000700
jadmanski4d03cf62010-03-04 18:32:28 +0000701 self.copy_client_config_file(self.get_client_log())
702
mbligh0d0f67d2009-11-06 03:15:03 +0000703 self.host.job.push_execution_context(self.results_dir)
jadmanski0afbb632008-06-06 21:10:57 +0000704 try:
jadmanski0afbb632008-06-06 21:10:57 +0000705 result = self.host.run(full_cmd, ignore_status=True,
706 timeout=timeout,
jadmanski6dadd832009-02-05 23:39:27 +0000707 stdout_tee=devnull,
708 stderr_tee=devnull)
jadmanski0afbb632008-06-06 21:10:57 +0000709 finally:
mbligh0d0f67d2009-11-06 03:15:03 +0000710 self.host.job.pop_execution_context()
jadmanski6dadd832009-02-05 23:39:27 +0000711
712 return result
713
714
715 @staticmethod
716 def _strip_stderr_prologue(stderr):
717 """Strips the 'standard' prologue that get pre-pended to every
718 remote command and returns the text that was actually written to
719 stderr by the remote command."""
720 stderr_lines = stderr.split("\n")[1:]
721 if not stderr_lines:
722 return ""
723 elif stderr_lines[0].startswith("NOTE: autotestd_monitor"):
724 del stderr_lines[0]
725 return "\n".join(stderr_lines)
726
727
728 def _execute_daemon(self, section, timeout, stderr_redirector,
729 client_disconnect_timeout):
730 monitor_dir = self.host.get_tmp_dir()
731 daemon_cmd = self.get_daemon_cmd(section, monitor_dir)
jadmanski4d03cf62010-03-04 18:32:28 +0000732
733 # grab the location for the server-side client log file
734 client_log_prefix = self.get_client_log()
735 client_log_path = os.path.join(self.results_dir, 'debug',
736 client_log_prefix + '.log')
737 client_log = open(client_log_path, 'w', 0)
738 self.copy_client_config_file(client_log_prefix)
jadmanski6dadd832009-02-05 23:39:27 +0000739
740 stdout_read = stderr_read = 0
mbligh0d0f67d2009-11-06 03:15:03 +0000741 self.host.job.push_execution_context(self.results_dir)
jadmanski6dadd832009-02-05 23:39:27 +0000742 try:
743 self.host.run(daemon_cmd, ignore_status=True, timeout=timeout)
jadmanski91d56a92009-04-01 15:20:40 +0000744 disconnect_warnings = []
jadmanski6dadd832009-02-05 23:39:27 +0000745 while True:
746 monitor_cmd = self.get_monitor_cmd(monitor_dir, stdout_read,
747 stderr_read)
748 try:
749 result = self.host.run(monitor_cmd, ignore_status=True,
750 timeout=timeout,
751 stdout_tee=client_log,
752 stderr_tee=stderr_redirector)
753 except error.AutoservRunError, e:
754 result = e.result_obj
755 result.exit_status = None
jadmanski91d56a92009-04-01 15:20:40 +0000756 disconnect_warnings.append(e.description)
757
jadmanski6dadd832009-02-05 23:39:27 +0000758 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000759 "Autotest client was disconnected: %s" % e.description,
760 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000761 except error.AutoservSSHTimeout:
762 result = utils.CmdResult(monitor_cmd, "", "", None, 0)
763 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000764 "Attempt to connect to Autotest client timed out",
765 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000766
767 stdout_read += len(result.stdout)
768 stderr_read += len(self._strip_stderr_prologue(result.stderr))
769
770 if result.exit_status is not None:
Simran Basibca10a62013-01-24 15:52:35 -0800771 # TODO (crosbug.com/38224)- sbasi: Remove extra logging.
772 logging.debug('Result exit status is %d.',
773 result.exit_status)
jadmanski6dadd832009-02-05 23:39:27 +0000774 return result
775 elif not self.host.wait_up(client_disconnect_timeout):
776 raise error.AutoservSSHTimeout(
777 "client was disconnected, reconnect timed out")
778 finally:
jadmanski4d03cf62010-03-04 18:32:28 +0000779 client_log.close()
mbligh0d0f67d2009-11-06 03:15:03 +0000780 self.host.job.pop_execution_context()
jadmanski6dadd832009-02-05 23:39:27 +0000781
782
783 def execute_section(self, section, timeout, stderr_redirector,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800784 client_disconnect_timeout):
showardb18134f2009-03-20 20:52:18 +0000785 logging.info("Executing %s/bin/autotest %s/control phase %d",
786 self.autodir, self.autodir, section)
jadmanski6dadd832009-02-05 23:39:27 +0000787
788 if self.background:
789 result = self._execute_in_background(section, timeout)
790 else:
791 result = self._execute_daemon(section, timeout, stderr_redirector,
792 client_disconnect_timeout)
793
794 last_line = stderr_redirector.last_line
mbligh2bf2db62007-11-27 00:53:18 +0000795
jadmanskib264ed02009-01-12 23:54:27 +0000796 # check if we failed hard enough to warrant an exception
jadmanski0afbb632008-06-06 21:10:57 +0000797 if result.exit_status == 1:
jadmanskib264ed02009-01-12 23:54:27 +0000798 err = error.AutotestRunError("client job was aborted")
799 elif not self.background and not result.stderr:
800 err = error.AutotestRunError(
jadmanskie4130532009-03-17 18:01:28 +0000801 "execute_section %s failed to return anything\n"
802 "stdout:%s\n" % (section, result.stdout))
jadmanskib264ed02009-01-12 23:54:27 +0000803 else:
804 err = None
mbligh0e4613b2007-10-29 16:55:07 +0000805
jadmanskib264ed02009-01-12 23:54:27 +0000806 # log something if the client failed AND never finished logging
Dale Curtis9285ddf2011-01-05 11:47:24 -0800807 if err and not self.is_client_job_finished(last_line):
jadmanskia61edad2009-05-21 22:17:49 +0000808 self.log_unexpected_abort(stderr_redirector)
jadmanskib264ed02009-01-12 23:54:27 +0000809
810 if err:
811 raise err
812 else:
813 return stderr_redirector.last_line
jadmanski4600e342008-10-29 22:54:00 +0000814
815
jadmanskic0354912010-01-12 15:57:29 +0000816 def _wait_for_reboot(self, old_boot_id):
showardb18134f2009-03-20 20:52:18 +0000817 logging.info("Client is rebooting")
818 logging.info("Waiting for client to halt")
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700819 if not self.host.wait_down(self.host.WAIT_DOWN_REBOOT_TIMEOUT,
820 old_boot_id=old_boot_id):
jadmanski4600e342008-10-29 22:54:00 +0000821 err = "%s failed to shutdown after %d"
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700822 err %= (self.host.hostname, self.host.WAIT_DOWN_REBOOT_TIMEOUT)
jadmanski4600e342008-10-29 22:54:00 +0000823 raise error.AutotestRunError(err)
showardb18134f2009-03-20 20:52:18 +0000824 logging.info("Client down, waiting for restart")
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700825 if not self.host.wait_up(self.host.DEFAULT_REBOOT_TIMEOUT):
jadmanski4600e342008-10-29 22:54:00 +0000826 # since reboot failed
827 # hardreset the machine once if possible
828 # before failing this control file
829 warning = "%s did not come back up, hard resetting"
830 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000831 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000832 try:
833 self.host.hardreset(wait=False)
mblighd99d3b272008-12-22 14:41:27 +0000834 except (AttributeError, error.AutoservUnsupportedError):
jadmanski4600e342008-10-29 22:54:00 +0000835 warning = "Hard reset unsupported on %s"
836 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000837 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000838 raise error.AutotestRunError("%s failed to boot after %ds" %
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700839 (self.host.hostname,
840 self.host.DEFAULT_REBOOT_TIMEOUT))
jadmanski4600e342008-10-29 22:54:00 +0000841 self.host.reboot_followup()
mblighdc735a22007-08-02 16:54:37 +0000842
843
Dale Curtis9285ddf2011-01-05 11:47:24 -0800844 def execute_control(self, timeout=None, client_disconnect_timeout=None):
jadmanski6bb32d72009-03-19 20:25:24 +0000845 if not self.background:
846 collector = log_collector(self.host, self.tag, self.results_dir)
847 hostname = self.host.hostname
848 remote_results = collector.client_results_dir
849 local_results = collector.server_results_dir
850 self.host.job.add_client_log(hostname, remote_results,
851 local_results)
jadmanski52053632010-06-11 21:08:10 +0000852 job_record_context = self.host.job.get_record_context()
jadmanski6bb32d72009-03-19 20:25:24 +0000853
jadmanski0afbb632008-06-06 21:10:57 +0000854 section = 0
jadmanski4600e342008-10-29 22:54:00 +0000855 start_time = time.time()
856
jadmanski043e1132008-11-19 17:10:32 +0000857 logger = client_logger(self.host, self.tag, self.results_dir)
jadmanski4600e342008-10-29 22:54:00 +0000858 try:
859 while not timeout or time.time() < start_time + timeout:
860 if timeout:
861 section_timeout = start_time + timeout - time.time()
862 else:
863 section_timeout = None
jadmanskic0354912010-01-12 15:57:29 +0000864 boot_id = self.host.get_boot_id()
jadmanski4600e342008-10-29 22:54:00 +0000865 last = self.execute_section(section, section_timeout,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800866 logger, client_disconnect_timeout)
mblighb3c0c912008-11-27 00:32:45 +0000867 if self.background:
868 return
jadmanski4600e342008-10-29 22:54:00 +0000869 section += 1
jadmanskib264ed02009-01-12 23:54:27 +0000870 if self.is_client_job_finished(last):
showardb18134f2009-03-20 20:52:18 +0000871 logging.info("Client complete")
jadmanski4600e342008-10-29 22:54:00 +0000872 return
jadmanskib264ed02009-01-12 23:54:27 +0000873 elif self.is_client_job_rebooting(last):
jadmanski79ab9282008-11-11 17:53:12 +0000874 try:
jadmanskic0354912010-01-12 15:57:29 +0000875 self._wait_for_reboot(boot_id)
jadmanski79ab9282008-11-11 17:53:12 +0000876 except error.AutotestRunError, e:
jadmanski043e1132008-11-19 17:10:32 +0000877 self.host.job.record("ABORT", None, "reboot", str(e))
878 self.host.job.record("END ABORT", None, None, str(e))
jadmanski79ab9282008-11-11 17:53:12 +0000879 raise
jadmanski4600e342008-10-29 22:54:00 +0000880 continue
881
beeps607428b2013-03-25 16:43:20 -0700882 # If a test fails without probable cause we try to bucket it's
883 # failure into one of 2 categories. If we can determine the
884 # current state of the device and it is suspicious, we close the
885 # status lines indicating a failure. If we either cannot
886 # determine the state of the device, or it appears totally
887 # healthy, we give up and abort.
888 try:
889 self._diagnose_dut(boot_id)
890 except AutotestDeviceError as e:
891 # The status lines of the test are pretty much tailed to
892 # our log, with indentation, from the client job on the DUT.
893 # So if the DUT goes down unexpectedly we'll end up with a
894 # malformed status log unless we manually unwind the status
895 # stack. Ideally we would want to write a nice wrapper like
896 # server_job methods run_reboot, run_group but they expect
897 # reboots and we don't.
898 self.host.job.record('FAIL', None, None, str(e))
899 self.host.job.record('END FAIL', None, None)
900 self.host.job.record('END GOOD', None, None)
Dan Shib03ea9d2013-08-15 17:13:27 -0700901 self.host.job.failed_with_device_error = True
beeps607428b2013-03-25 16:43:20 -0700902 return
903 except AutotestAbort as e:
904 self.host.job.record('ABORT', None, None, str(e))
905 self.host.job.record('END ABORT', None, None)
jadmanski4600e342008-10-29 22:54:00 +0000906
beeps607428b2013-03-25 16:43:20 -0700907 # give the client machine a chance to recover from a crash
908 self.host.wait_up(
909 self.host.HOURS_TO_WAIT_FOR_RECOVERY * 3600)
910 msg = ("Aborting - unexpected final status message from "
911 "client on %s: %s\n") % (self.host.hostname, last)
912 raise error.AutotestRunError(msg)
jadmanski4600e342008-10-29 22:54:00 +0000913 finally:
jadmanski043e1132008-11-19 17:10:32 +0000914 logger.close()
jadmanski6bb32d72009-03-19 20:25:24 +0000915 if not self.background:
916 collector.collect_client_job_results()
jadmanski4d03cf62010-03-04 18:32:28 +0000917 collector.remove_redundant_client_logs()
mblighfc3da5b2010-01-06 18:37:22 +0000918 state_file = os.path.basename(self.remote_control_file
919 + '.state')
920 state_path = os.path.join(self.results_dir, state_file)
921 self.host.job.postprocess_client_state(state_path)
jadmanski6bb32d72009-03-19 20:25:24 +0000922 self.host.job.remove_client_log(hostname, remote_results,
923 local_results)
jadmanski52053632010-06-11 21:08:10 +0000924 job_record_context.restore()
mblighdcd57a82007-07-11 23:06:47 +0000925
jadmanski0afbb632008-06-06 21:10:57 +0000926 # should only get here if we timed out
927 assert timeout
928 raise error.AutotestTimeoutError()
mbligh0e4613b2007-10-29 16:55:07 +0000929
mblighdcd57a82007-07-11 23:06:47 +0000930
jadmanski043e1132008-11-19 17:10:32 +0000931class log_collector(object):
932 def __init__(self, host, client_tag, results_dir):
933 self.host = host
934 if not client_tag:
935 client_tag = "default"
936 self.client_results_dir = os.path.join(host.get_autodir(), "results",
937 client_tag)
938 self.server_results_dir = results_dir
939
940
941 def collect_client_job_results(self):
942 """ A method that collects all the current results of a running
943 client job into the results dir. By default does nothing as no
944 client job is running, but when running a client job you can override
945 this with something that will actually do something. """
946
947 # make an effort to wait for the machine to come up
948 try:
949 self.host.wait_up(timeout=30)
950 except error.AutoservError:
951 # don't worry about any errors, we'll try and
952 # get the results anyway
953 pass
954
jadmanski043e1132008-11-19 17:10:32 +0000955 # Copy all dirs in default to results_dir
Gabe Black1e1c41b2015-02-04 23:55:15 -0800956 timer = autotest_stats.Timer('collect_client_job_results')
Dan Shi209eefd2013-05-30 13:17:48 -0700957 timer.start()
jadmanski043e1132008-11-19 17:10:32 +0000958 try:
jadmanski043e1132008-11-19 17:10:32 +0000959 self.host.get_file(self.client_results_dir + '/',
mbligh45561782009-05-11 21:14:34 +0000960 self.server_results_dir, preserve_symlinks=True)
Dan Shib03ea9d2013-08-15 17:13:27 -0700961
Dan Shi209eefd2013-05-30 13:17:48 -0700962 # Only report time used for successful get_file calls.
963 timer.stop();
jadmanski043e1132008-11-19 17:10:32 +0000964 except Exception:
965 # well, don't stop running just because we couldn't get logs
showardb18134f2009-03-20 20:52:18 +0000966 e_msg = "Unexpected error copying test result logs, continuing ..."
967 logging.error(e_msg)
jadmanski043e1132008-11-19 17:10:32 +0000968 traceback.print_exc(file=sys.stdout)
969
970
jadmanski4d03cf62010-03-04 18:32:28 +0000971 def remove_redundant_client_logs(self):
972 """Remove client.*.log files in favour of client.*.DEBUG files."""
973 debug_dir = os.path.join(self.server_results_dir, 'debug')
974 debug_files = [f for f in os.listdir(debug_dir)
975 if re.search(r'^client\.\d+\.DEBUG$', f)]
976 for debug_file in debug_files:
977 log_file = debug_file.replace('DEBUG', 'log')
978 log_file = os.path.join(debug_dir, log_file)
979 if os.path.exists(log_file):
980 os.remove(log_file)
981
982
jadmanski043e1132008-11-19 17:10:32 +0000983# a file-like object for catching stderr from an autotest client and
984# extracting status logs from it
Chris Sosa3ee5d5c2012-02-23 11:18:41 -0800985class BaseClientLogger(object):
jadmanski043e1132008-11-19 17:10:32 +0000986 """Partial file object to write to both stdout and
987 the status log file. We only implement those methods
988 utils.run() actually calls.
jadmanski043e1132008-11-19 17:10:32 +0000989 """
990 status_parser = re.compile(r"^AUTOTEST_STATUS:([^:]*):(.*)$")
991 test_complete_parser = re.compile(r"^AUTOTEST_TEST_COMPLETE:(.*)$")
jadmanskib1a51132009-08-07 16:45:50 +0000992 fetch_package_parser = re.compile(
993 r"^AUTOTEST_FETCH_PACKAGE:([^:]*):([^:]*):(.*)$")
jadmanski043e1132008-11-19 17:10:32 +0000994 extract_indent = re.compile(r"^(\t*).*$")
jadmanskiefe4ebf2009-05-21 22:12:30 +0000995 extract_timestamp = re.compile(r".*\ttimestamp=(\d+)\t.*$")
jadmanski043e1132008-11-19 17:10:32 +0000996
997 def __init__(self, host, tag, server_results_dir):
998 self.host = host
999 self.job = host.job
1000 self.log_collector = log_collector(host, tag, server_results_dir)
1001 self.leftover = ""
1002 self.last_line = ""
1003 self.logs = {}
jadmanskiefe4ebf2009-05-21 22:12:30 +00001004
1005
jadmanski043e1132008-11-19 17:10:32 +00001006 def _process_log_dict(self, log_dict):
1007 log_list = log_dict.pop("logs", [])
1008 for key in sorted(log_dict.iterkeys()):
1009 log_list += self._process_log_dict(log_dict.pop(key))
1010 return log_list
1011
1012
1013 def _process_logs(self):
1014 """Go through the accumulated logs in self.log and print them
1015 out to stdout and the status log. Note that this processes
1016 logs in an ordering where:
1017
1018 1) logs to different tags are never interleaved
1019 2) logs to x.y come before logs to x.y.z for all z
1020 3) logs to x.y come before x.z whenever y < z
1021
1022 Note that this will in general not be the same as the
1023 chronological ordering of the logs. However, if a chronological
1024 ordering is desired that one can be reconstructed from the
1025 status log by looking at timestamp lines."""
1026 log_list = self._process_log_dict(self.logs)
jadmanski2a89dac2010-06-11 14:32:58 +00001027 for entry in log_list:
1028 self.job.record_entry(entry, log_in_subdir=False)
jadmanski043e1132008-11-19 17:10:32 +00001029 if log_list:
jadmanski2a89dac2010-06-11 14:32:58 +00001030 self.last_line = log_list[-1].render()
jadmanski043e1132008-11-19 17:10:32 +00001031
1032
1033 def _process_quoted_line(self, tag, line):
1034 """Process a line quoted with an AUTOTEST_STATUS flag. If the
1035 tag is blank then we want to push out all the data we've been
1036 building up in self.logs, and then the newest line. If the
1037 tag is not blank, then push the line into the logs for handling
1038 later."""
jadmanski2a89dac2010-06-11 14:32:58 +00001039 entry = base_job.status_log_entry.parse(line)
1040 if entry is None:
1041 return # the line contains no status lines
jadmanski043e1132008-11-19 17:10:32 +00001042 if tag == "":
1043 self._process_logs()
jadmanski2a89dac2010-06-11 14:32:58 +00001044 self.job.record_entry(entry, log_in_subdir=False)
jadmanski043e1132008-11-19 17:10:32 +00001045 self.last_line = line
1046 else:
1047 tag_parts = [int(x) for x in tag.split(".")]
1048 log_dict = self.logs
1049 for part in tag_parts:
1050 log_dict = log_dict.setdefault(part, {})
1051 log_list = log_dict.setdefault("logs", [])
jadmanski2a89dac2010-06-11 14:32:58 +00001052 log_list.append(entry)
jadmanski043e1132008-11-19 17:10:32 +00001053
1054
jadmanskif37df842009-02-11 00:03:26 +00001055 def _process_info_line(self, line):
1056 """Check if line is an INFO line, and if it is, interpret any control
1057 messages (e.g. enabling/disabling warnings) that it may contain."""
1058 match = re.search(r"^\t*INFO\t----\t----(.*)\t[^\t]*$", line)
1059 if not match:
1060 return # not an INFO line
1061 for field in match.group(1).split('\t'):
1062 if field.startswith("warnings.enable="):
jadmanski16a7ff72009-04-01 18:19:53 +00001063 func = self.job.warning_manager.enable_warnings
jadmanskif37df842009-02-11 00:03:26 +00001064 elif field.startswith("warnings.disable="):
jadmanski16a7ff72009-04-01 18:19:53 +00001065 func = self.job.warning_manager.disable_warnings
jadmanskif37df842009-02-11 00:03:26 +00001066 else:
1067 continue
1068 warning_type = field.split("=", 1)[1]
jadmanski16a7ff72009-04-01 18:19:53 +00001069 func(warning_type)
jadmanskif37df842009-02-11 00:03:26 +00001070
1071
jadmanski043e1132008-11-19 17:10:32 +00001072 def _process_line(self, line):
1073 """Write out a line of data to the appropriate stream. Status
1074 lines sent by autotest will be prepended with
1075 "AUTOTEST_STATUS", and all other lines are ssh error
1076 messages."""
1077 status_match = self.status_parser.search(line)
1078 test_complete_match = self.test_complete_parser.search(line)
jadmanskib1a51132009-08-07 16:45:50 +00001079 fetch_package_match = self.fetch_package_parser.search(line)
jadmanski043e1132008-11-19 17:10:32 +00001080 if status_match:
1081 tag, line = status_match.groups()
jadmanskif37df842009-02-11 00:03:26 +00001082 self._process_info_line(line)
jadmanski043e1132008-11-19 17:10:32 +00001083 self._process_quoted_line(tag, line)
1084 elif test_complete_match:
jadmanskifcc0d5d2009-02-12 21:52:54 +00001085 self._process_logs()
jadmanski043e1132008-11-19 17:10:32 +00001086 fifo_path, = test_complete_match.groups()
mbligh060c4712009-12-29 02:43:35 +00001087 try:
1088 self.log_collector.collect_client_job_results()
1089 self.host.run("echo A > %s" % fifo_path)
1090 except Exception:
1091 msg = "Post-test log collection failed, continuing anyway"
1092 logging.exception(msg)
jadmanskib1a51132009-08-07 16:45:50 +00001093 elif fetch_package_match:
1094 pkg_name, dest_path, fifo_path = fetch_package_match.groups()
jadmanskiede7e242009-08-10 15:43:33 +00001095 serve_packages = global_config.global_config.get_config_value(
1096 "PACKAGES", "serve_packages_from_autoserv", type=bool)
1097 if serve_packages and pkg_name.endswith(".tar.bz2"):
1098 try:
1099 self._send_tarball(pkg_name, dest_path)
1100 except Exception:
1101 msg = "Package tarball creation failed, continuing anyway"
1102 logging.exception(msg)
mbligh060c4712009-12-29 02:43:35 +00001103 try:
1104 self.host.run("echo B > %s" % fifo_path)
1105 except Exception:
1106 msg = "Package tarball installation failed, continuing anyway"
1107 logging.exception(msg)
jadmanski043e1132008-11-19 17:10:32 +00001108 else:
showardb18134f2009-03-20 20:52:18 +00001109 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +00001110
1111
jadmanskiede7e242009-08-10 15:43:33 +00001112 def _send_tarball(self, pkg_name, remote_dest):
1113 name, pkg_type = self.job.pkgmgr.parse_tarball_name(pkg_name)
1114 src_dirs = []
1115 if pkg_type == 'test':
mbligh1f572e52010-04-01 17:15:53 +00001116 for test_dir in ['site_tests', 'tests']:
1117 src_dir = os.path.join(self.job.clientdir, test_dir, name)
1118 if os.path.exists(src_dir):
1119 src_dirs += [src_dir]
1120 if autoserv_prebuild:
1121 prebuild.setup(self.job.clientdir, src_dir)
1122 break
jadmanskiede7e242009-08-10 15:43:33 +00001123 elif pkg_type == 'profiler':
1124 src_dirs += [os.path.join(self.job.clientdir, 'profilers', name)]
mbligh1f572e52010-04-01 17:15:53 +00001125 if autoserv_prebuild:
1126 prebuild.setup(self.job.clientdir, src_dir)
jadmanskiede7e242009-08-10 15:43:33 +00001127 elif pkg_type == 'dep':
1128 src_dirs += [os.path.join(self.job.clientdir, 'deps', name)]
1129 elif pkg_type == 'client':
1130 return # you must already have a client to hit this anyway
1131 else:
1132 return # no other types are supported
1133
1134 # iterate over src_dirs until we find one that exists, then tar it
1135 for src_dir in src_dirs:
1136 if os.path.exists(src_dir):
1137 try:
1138 logging.info('Bundling %s into %s', src_dir, pkg_name)
1139 temp_dir = autotemp.tempdir(unique_id='autoserv-packager',
1140 dir=self.job.tmpdir)
1141 tarball_path = self.job.pkgmgr.tar_package(
mblighbccad482009-08-24 22:08:31 +00001142 pkg_name, src_dir, temp_dir.name, " .")
jadmanskiede7e242009-08-10 15:43:33 +00001143 self.host.send_file(tarball_path, remote_dest)
1144 finally:
1145 temp_dir.clean()
1146 return
1147
1148
jadmanski91d56a92009-04-01 15:20:40 +00001149 def log_warning(self, msg, warning_type):
jadmanski6dadd832009-02-05 23:39:27 +00001150 """Injects a WARN message into the current status logging stream."""
jadmanski91d56a92009-04-01 15:20:40 +00001151 timestamp = int(time.time())
1152 if self.job.warning_manager.is_valid(timestamp, warning_type):
Eric Lid656d562011-04-20 11:48:29 -07001153 self.job.record('WARN', None, None, msg)
jadmanski6dadd832009-02-05 23:39:27 +00001154
jadmanski043e1132008-11-19 17:10:32 +00001155
1156 def write(self, data):
jadmanski2a89dac2010-06-11 14:32:58 +00001157 # now start processing the existing buffer and the new data
jadmanski043e1132008-11-19 17:10:32 +00001158 data = self.leftover + data
mbligh060c4712009-12-29 02:43:35 +00001159 lines = data.split('\n')
1160 processed_lines = 0
1161 try:
1162 # process all the buffered data except the last line
1163 # ignore the last line since we may not have all of it yet
1164 for line in lines[:-1]:
mbligh060c4712009-12-29 02:43:35 +00001165 self._process_line(line)
1166 processed_lines += 1
1167 finally:
1168 # save any unprocessed lines for future processing
1169 self.leftover = '\n'.join(lines[processed_lines:])
jadmanski043e1132008-11-19 17:10:32 +00001170
1171
1172 def flush(self):
1173 sys.stdout.flush()
1174
1175
jadmanskia61edad2009-05-21 22:17:49 +00001176 def flush_all_buffers(self):
jadmanski043e1132008-11-19 17:10:32 +00001177 if self.leftover:
1178 self._process_line(self.leftover)
jadmanskia61edad2009-05-21 22:17:49 +00001179 self.leftover = ""
jadmanski043e1132008-11-19 17:10:32 +00001180 self._process_logs()
1181 self.flush()
1182
1183
jadmanskia61edad2009-05-21 22:17:49 +00001184 def close(self):
1185 self.flush_all_buffers()
1186
1187
mbligha7007722009-01-13 00:37:11 +00001188SiteAutotest = client_utils.import_site_class(
1189 __file__, "autotest_lib.server.site_autotest", "SiteAutotest",
1190 BaseAutotest)
mblighd8b39252008-03-20 21:15:03 +00001191
showardad812bf2009-10-20 23:49:56 +00001192
jadmanski69bdaac2010-07-28 16:27:20 +00001193_SiteRun = client_utils.import_site_class(
1194 __file__, "autotest_lib.server.site_autotest", "_SiteRun", _BaseRun)
1195
1196
Chris Sosa3ee5d5c2012-02-23 11:18:41 -08001197SiteClientLogger = client_utils.import_site_class(
1198 __file__, "autotest_lib.server.site_autotest", "SiteClientLogger",
1199 BaseClientLogger)
1200
1201
mblighd8b39252008-03-20 21:15:03 +00001202class Autotest(SiteAutotest):
jadmanski0afbb632008-06-06 21:10:57 +00001203 pass
jadmanskic6136e92010-07-19 16:41:49 +00001204
1205
Chris Sosa3ee5d5c2012-02-23 11:18:41 -08001206class client_logger(SiteClientLogger):
1207 pass
1208
1209
jadmanski69bdaac2010-07-28 16:27:20 +00001210class _Run(_SiteRun):
1211 pass
1212
1213
jadmanskic6136e92010-07-19 16:41:49 +00001214class AutotestHostMixin(object):
1215 """A generic mixin to add a run_test method to classes, which will allow
1216 you to run an autotest client test on a machine directly."""
1217
1218 # for testing purposes
1219 _Autotest = Autotest
1220
1221 def run_test(self, test_name, **dargs):
1222 """Run an autotest client test on the host.
1223
1224 @param test_name: The name of the client test.
1225 @param dargs: Keyword arguments to pass to the test.
1226
1227 @returns: True if the test passes, False otherwise."""
1228 at = self._Autotest()
1229 control_file = ('result = job.run_test(%s)\n'
1230 'job.set_state("test_result", result)\n')
1231 test_args = [repr(test_name)]
1232 test_args += ['%s=%r' % (k, v) for k, v in dargs.iteritems()]
1233 control_file %= ', '.join(test_args)
1234 at.run(control_file, host=self)
1235 return at.job.get_state('test_result', default=False)