blob: ea8713af9a69e95a89f1caa12d4b3890a1c9ad98 [file] [log] [blame]
mblighdcd57a82007-07-11 23:06:47 +00001# Copyright 2007 Google Inc. Released under the GPL v2
2
beeps07f53b92013-01-08 12:55:10 -08003import re, os, sys, traceback, time, glob, tempfile
4import logging
mbligh1f572e52010-04-01 17:15:53 +00005from autotest_lib.server import installable_object, prebuild, utils
beeps07f53b92013-01-08 12:55:10 -08006from autotest_lib.client.common_lib import base_job, error, autotemp
mbligh09108442008-10-15 16:27:38 +00007from autotest_lib.client.common_lib import global_config, packages
mbligha7007722009-01-13 00:37:11 +00008from autotest_lib.client.common_lib import utils as client_utils
Michael Liangda8c60a2014-06-03 13:24:51 -07009from autotest_lib.client.common_lib.cros.graphite import stats
mbligh3c7a1502008-07-24 18:08:47 +000010
Dale Curtiscb7bfaf2011-06-07 16:21:57 -070011AUTOTEST_SVN = 'svn://test.kernel.org/autotest/trunk/client'
mblighdcd57a82007-07-11 23:06:47 +000012AUTOTEST_HTTP = 'http://test.kernel.org/svn/autotest/trunk/client'
13
mblighdcd57a82007-07-11 23:06:47 +000014
mbligh1f572e52010-04-01 17:15:53 +000015get_value = global_config.global_config.get_config_value
16autoserv_prebuild = get_value('AUTOSERV', 'enable_server_prebuild',
17 type=bool, default=False)
18
jadmanski2a89dac2010-06-11 14:32:58 +000019
showardad812bf2009-10-20 23:49:56 +000020class AutodirNotFoundError(Exception):
21 """No Autotest installation could be found."""
22
23
beeps607428b2013-03-25 16:43:20 -070024class AutotestFailure(Exception):
25 """Gereric exception class for failures during a test run."""
26
27
28class AutotestAbort(AutotestFailure):
29 """
30 AutotestAborts are thrown when the DUT seems fine,
31 and the test doesn't give us an explicit reason for
32 failure; In this case we have no choice but to abort.
33 """
34
35
36class AutotestDeviceError(AutotestFailure):
37 """
38 Exceptions that inherit from AutotestDeviceError
39 are thrown when we can determine the current
40 state of the DUT and conclude that it probably
41 lead to the test failing; these exceptions lead
42 to failures instead of aborts.
43 """
44
45
46class AutotestDeviceNotPingable(AutotestDeviceError):
47 """Error for when a DUT becomes unpingable."""
48
49
50class AutotestDeviceNotSSHable(AutotestDeviceError):
51 """Error for when a DUT is pingable but not SSHable."""
52
53
54class AutotestDeviceRebooted(AutotestDeviceError):
55 """Error for when a DUT rebooted unexpectedly."""
56
57
mblighd8b39252008-03-20 21:15:03 +000058class BaseAutotest(installable_object.InstallableObject):
jadmanski0afbb632008-06-06 21:10:57 +000059 """
60 This class represents the Autotest program.
mblighdcd57a82007-07-11 23:06:47 +000061
jadmanski0afbb632008-06-06 21:10:57 +000062 Autotest is used to run tests automatically and collect the results.
63 It also supports profilers.
mblighdcd57a82007-07-11 23:06:47 +000064
jadmanski0afbb632008-06-06 21:10:57 +000065 Implementation details:
66 This is a leaf class in an abstract class hierarchy, it must
67 implement the unimplemented methods in parent classes.
68 """
mbligh119c12a2007-11-12 22:13:44 +000069
Dale Curtiscb7bfaf2011-06-07 16:21:57 -070070 def __init__(self, host=None):
jadmanski0afbb632008-06-06 21:10:57 +000071 self.host = host
72 self.got = False
73 self.installed = False
74 self.serverdir = utils.get_server_dir()
75 super(BaseAutotest, self).__init__()
mblighc8949b82007-07-23 16:33:58 +000076
mblighdc735a22007-08-02 16:54:37 +000077
jadmanskif22fea82008-11-26 20:57:07 +000078 install_in_tmpdir = False
79 @classmethod
80 def set_install_in_tmpdir(cls, flag):
81 """ Sets a flag that controls whether or not Autotest should by
82 default be installed in a "standard" directory (e.g.
83 /home/autotest, /usr/local/autotest) or a temporary directory. """
84 cls.install_in_tmpdir = flag
85
86
showardad812bf2009-10-20 23:49:56 +000087 @classmethod
88 def get_client_autodir_paths(cls, host):
89 return global_config.global_config.get_config_value(
90 'AUTOSERV', 'client_autodir_paths', type=list)
91
92
93 @classmethod
94 def get_installed_autodir(cls, host):
95 """
96 Find where the Autotest client is installed on the host.
97 @returns an absolute path to an installed Autotest client root.
98 @raises AutodirNotFoundError if no Autotest installation can be found.
99 """
100 autodir = host.get_autodir()
101 if autodir:
102 logging.debug('Using existing host autodir: %s', autodir)
103 return autodir
104
105 for path in Autotest.get_client_autodir_paths(host):
106 try:
107 autotest_binary = os.path.join(path, 'bin', 'autotest')
108 host.run('test -x %s' % utils.sh_escape(autotest_binary))
Eric Li861b2d52011-02-04 14:50:35 -0800109 host.run('test -w %s' % utils.sh_escape(path))
showardad812bf2009-10-20 23:49:56 +0000110 logging.debug('Found existing autodir at %s', path)
111 return path
112 except error.AutoservRunError:
113 logging.debug('%s does not exist on %s', autotest_binary,
114 host.hostname)
115 raise AutodirNotFoundError
116
117
118 @classmethod
119 def get_install_dir(cls, host):
120 """
121 Determines the location where autotest should be installed on
jadmanskif22fea82008-11-26 20:57:07 +0000122 host. If self.install_in_tmpdir is set, it will return a unique
showardad812bf2009-10-20 23:49:56 +0000123 temporary directory that autotest can be installed in. Otherwise, looks
124 for an existing installation to use; if none is found, looks for a
125 usable directory in the global config client_autodir_paths.
126 """
jadmanskif22fea82008-11-26 20:57:07 +0000127 try:
lmr9dcf0832009-12-08 21:28:55 +0000128 install_dir = cls.get_installed_autodir(host)
showardad812bf2009-10-20 23:49:56 +0000129 except AutodirNotFoundError:
lmr9dcf0832009-12-08 21:28:55 +0000130 install_dir = cls._find_installable_dir(host)
131
132 if cls.install_in_tmpdir:
133 return host.get_tmp_dir(parent=install_dir)
134 return install_dir
showardad812bf2009-10-20 23:49:56 +0000135
136
137 @classmethod
138 def _find_installable_dir(cls, host):
139 client_autodir_paths = cls.get_client_autodir_paths(host)
140 for path in client_autodir_paths:
141 try:
J. Richard Barnettef44ff962011-01-05 10:14:28 -0800142 host.run('mkdir -p %s' % utils.sh_escape(path))
Eric Li861b2d52011-02-04 14:50:35 -0800143 host.run('test -w %s' % utils.sh_escape(path))
showardad812bf2009-10-20 23:49:56 +0000144 return path
145 except error.AutoservRunError:
146 logging.debug('Failed to create %s', path)
147 raise error.AutoservInstallError(
Dale Curtis74a314b2011-06-23 14:55:46 -0700148 'Unable to find a place to install Autotest; tried %s' %
showardad812bf2009-10-20 23:49:56 +0000149 ', '.join(client_autodir_paths))
jadmanskif22fea82008-11-26 20:57:07 +0000150
151
Eric Lid656d562011-04-20 11:48:29 -0700152 def get_fetch_location(self):
153 c = global_config.global_config
154 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list,
155 default=[])
156 repos.reverse()
157 return repos
158
159
mblighb3c0c912008-11-27 00:32:45 +0000160 def install(self, host=None, autodir=None):
161 self._install(host=host, autodir=autodir)
jadmanski54f90af2008-10-10 16:20:55 +0000162
163
mblighb8aa75b2009-09-18 16:50:37 +0000164 def install_full_client(self, host=None, autodir=None):
165 self._install(host=host, autodir=autodir, use_autoserv=False,
166 use_packaging=False)
167
168
mblighbccad482009-08-24 22:08:31 +0000169 def install_no_autoserv(self, host=None, autodir=None):
mblighb8aa75b2009-09-18 16:50:37 +0000170 self._install(host=host, autodir=autodir, use_autoserv=False)
mblighbccad482009-08-24 22:08:31 +0000171
172
mblighb8aa75b2009-09-18 16:50:37 +0000173 def _install_using_packaging(self, host, autodir):
Eric Lid656d562011-04-20 11:48:29 -0700174 repos = self.get_fetch_location()
mblighb8aa75b2009-09-18 16:50:37 +0000175 if not repos:
176 raise error.PackageInstallError("No repos to install an "
177 "autotest client from")
178 pkgmgr = packages.PackageManager(autodir, hostname=host.hostname,
179 repo_urls=repos,
180 do_locking=False,
181 run_function=host.run,
182 run_function_dargs=dict(timeout=600))
183 # The packages dir is used to store all the packages that
184 # are fetched on that client. (for the tests,deps etc.
185 # too apart from the client)
186 pkg_dir = os.path.join(autodir, 'packages')
187 # clean up the autodir except for the packages directory
188 host.run('cd %s && ls | grep -v "^packages$"'
189 ' | xargs rm -rf && rm -rf .[^.]*' % autodir)
190 pkgmgr.install_pkg('autotest', 'client', pkg_dir, autodir,
191 preserve_install_dir=True)
192 self.installed = True
193
194
195 def _install_using_send_file(self, host, autodir):
Chris Sosa3ee5d5c2012-02-23 11:18:41 -0800196 dirs_to_exclude = set(["tests", "site_tests", "deps", "profilers",
197 "packages"])
mblighb8aa75b2009-09-18 16:50:37 +0000198 light_files = [os.path.join(self.source_material, f)
199 for f in os.listdir(self.source_material)
200 if f not in dirs_to_exclude]
201 host.send_file(light_files, autodir, delete_dest=True)
202
203 # create empty dirs for all the stuff we excluded
204 commands = []
205 for path in dirs_to_exclude:
206 abs_path = os.path.join(autodir, path)
207 abs_path = utils.sh_escape(abs_path)
208 commands.append("mkdir -p '%s'" % abs_path)
209 commands.append("touch '%s'/__init__.py" % abs_path)
210 host.run(';'.join(commands))
211
212
213 def _install(self, host=None, autodir=None, use_autoserv=True,
214 use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000215 """
216 Install autotest. If get() was not called previously, an
217 attempt will be made to install from the autotest svn
218 repository.
mbligh9a3f5e52008-05-28 21:21:43 +0000219
mblighbccad482009-08-24 22:08:31 +0000220 @param host A Host instance on which autotest will be installed
221 @param autodir Location on the remote host to install to
mblighb8aa75b2009-09-18 16:50:37 +0000222 @param use_autoserv Enable install modes that depend on the client
mblighbccad482009-08-24 22:08:31 +0000223 running with the autoserv harness
mblighb8aa75b2009-09-18 16:50:37 +0000224 @param use_packaging Enable install modes that use the packaging system
mbligh9a3f5e52008-05-28 21:21:43 +0000225
mblighbccad482009-08-24 22:08:31 +0000226 @exception AutoservError if a tarball was not specified and
227 the target host does not have svn installed in its path
228 """
jadmanski0afbb632008-06-06 21:10:57 +0000229 if not host:
230 host = self.host
231 if not self.got:
232 self.get()
233 host.wait_up(timeout=30)
234 host.setup()
showardb18134f2009-03-20 20:52:18 +0000235 logging.info("Installing autotest on %s", host.hostname)
mbligh40f122a2007-11-03 23:08:46 +0000236
jadmanski54f90af2008-10-10 16:20:55 +0000237 # set up the autotest directory on the remote machine
238 if not autodir:
showardad812bf2009-10-20 23:49:56 +0000239 autodir = self.get_install_dir(host)
240 logging.info('Using installation dir %s', autodir)
mbligh0562e652008-08-20 20:11:45 +0000241 host.set_autodir(autodir)
jadmanski3c236942009-03-04 17:51:26 +0000242 host.run('mkdir -p %s' % utils.sh_escape(autodir))
mbligh40f122a2007-11-03 23:08:46 +0000243
jadmanski1c3c07b2009-03-03 23:29:36 +0000244 # make sure there are no files in $AUTODIR/results
245 results_path = os.path.join(autodir, 'results')
jadmanski3c236942009-03-04 17:51:26 +0000246 host.run('rm -rf %s/*' % utils.sh_escape(results_path),
jadmanski1c3c07b2009-03-03 23:29:36 +0000247 ignore_status=True)
248
mblighc5ddfd12008-08-04 17:15:00 +0000249 # Fetch the autotest client from the nearest repository
mblighb8aa75b2009-09-18 16:50:37 +0000250 if use_packaging:
251 try:
252 self._install_using_packaging(host, autodir)
253 return
Eric Li6f27d4f2010-09-29 10:55:17 -0700254 except (error.PackageInstallError, error.AutoservRunError,
255 global_config.ConfigError), e:
mblighb8aa75b2009-09-18 16:50:37 +0000256 logging.info("Could not install autotest using the packaging "
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700257 "system: %s. Trying other methods", e)
mblighc5ddfd12008-08-04 17:15:00 +0000258
jadmanski0afbb632008-06-06 21:10:57 +0000259 # try to install from file or directory
260 if self.source_material:
jadmanski69bdaac2010-07-28 16:27:20 +0000261 c = global_config.global_config
262 supports_autoserv_packaging = c.get_config_value(
263 "PACKAGES", "serve_packages_from_autoserv", type=bool)
264 # Copy autotest recursively
265 if supports_autoserv_packaging and use_autoserv:
266 self._install_using_send_file(host, autodir)
jadmanski0afbb632008-06-06 21:10:57 +0000267 else:
jadmanski69bdaac2010-07-28 16:27:20 +0000268 host.send_file(self.source_material, autodir, delete_dest=True)
showardb18134f2009-03-20 20:52:18 +0000269 logging.info("Installation of autotest completed")
jadmanski0afbb632008-06-06 21:10:57 +0000270 self.installed = True
271 return
mbligh91334902007-09-28 01:47:59 +0000272
jadmanski0afbb632008-06-06 21:10:57 +0000273 # if that fails try to install using svn
274 if utils.run('which svn').exit_status:
Dale Curtis74a314b2011-06-23 14:55:46 -0700275 raise error.AutoservError('svn not found on target machine: %s' %
276 host.name)
jadmanski0afbb632008-06-06 21:10:57 +0000277 try:
mbligh78bf5352008-07-11 20:27:36 +0000278 host.run('svn checkout %s %s' % (AUTOTEST_SVN, autodir))
jadmanski0afbb632008-06-06 21:10:57 +0000279 except error.AutoservRunError, e:
mbligh78bf5352008-07-11 20:27:36 +0000280 host.run('svn checkout %s %s' % (AUTOTEST_HTTP, autodir))
showardb18134f2009-03-20 20:52:18 +0000281 logging.info("Installation of autotest completed")
jadmanski0afbb632008-06-06 21:10:57 +0000282 self.installed = True
mbligh91334902007-09-28 01:47:59 +0000283
284
jadmanski7c7aff32009-03-25 22:43:07 +0000285 def uninstall(self, host=None):
286 """
287 Uninstall (i.e. delete) autotest. Removes the autotest client install
288 from the specified host.
289
290 @params host a Host instance from which the client will be removed
291 """
292 if not self.installed:
293 return
294 if not host:
295 host = self.host
296 autodir = host.get_autodir()
297 if not autodir:
298 return
299
300 # perform the actual uninstall
301 host.run("rm -rf %s" % utils.sh_escape(autodir), ignore_status=True)
302 host.set_autodir(None)
303 self.installed = False
304
305
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700306 def get(self, location=None):
jadmanski0afbb632008-06-06 21:10:57 +0000307 if not location:
308 location = os.path.join(self.serverdir, '../client')
309 location = os.path.abspath(location)
310 # If there's stuff run on our client directory already, it
311 # can cause problems. Try giving it a quick clean first.
312 cwd = os.getcwd()
313 os.chdir(location)
showard4b976072009-10-20 23:50:08 +0000314 try:
315 utils.system('tools/make_clean', ignore_status=True)
316 finally:
317 os.chdir(cwd)
jadmanski0afbb632008-06-06 21:10:57 +0000318 super(BaseAutotest, self).get(location)
319 self.got = True
mblighdcd57a82007-07-11 23:06:47 +0000320
321
mblighe7d9c602009-07-02 19:02:33 +0000322 def run(self, control_file, results_dir='.', host=None, timeout=None,
323 tag=None, parallel_flag=False, background=False,
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700324 client_disconnect_timeout=None):
jadmanski0afbb632008-06-06 21:10:57 +0000325 """
326 Run an autotest job on the remote machine.
mbligh9a3f5e52008-05-28 21:21:43 +0000327
mblighe7d9c602009-07-02 19:02:33 +0000328 @param control_file: An open file-like-obj of the control file.
329 @param results_dir: A str path where the results should be stored
330 on the local filesystem.
331 @param host: A Host instance on which the control file should
332 be run.
333 @param timeout: Maximum number of seconds to wait for the run or None.
334 @param tag: Tag name for the client side instance of autotest.
335 @param parallel_flag: Flag set when multiple jobs are run at the
336 same time.
337 @param background: Indicates that the client should be launched as
338 a background job; the code calling run will be responsible
339 for monitoring the client and collecting the results.
340 @param client_disconnect_timeout: Seconds to wait for the remote host
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700341 to come back after a reboot. Defaults to the host setting for
342 DEFAULT_REBOOT_TIMEOUT.
mblighe7d9c602009-07-02 19:02:33 +0000343
344 @raises AutotestRunError: If there is a problem executing
345 the control file.
jadmanski0afbb632008-06-06 21:10:57 +0000346 """
347 host = self._get_host_and_setup(host)
348 results_dir = os.path.abspath(results_dir)
mblighc1cbc992008-05-27 20:01:45 +0000349
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700350 if client_disconnect_timeout is None:
351 client_disconnect_timeout = host.DEFAULT_REBOOT_TIMEOUT
352
jadmanski0afbb632008-06-06 21:10:57 +0000353 if tag:
354 results_dir = os.path.join(results_dir, tag)
mblighc1cbc992008-05-27 20:01:45 +0000355
mblighb3c0c912008-11-27 00:32:45 +0000356 atrun = _Run(host, results_dir, tag, parallel_flag, background)
jadmanski6dadd832009-02-05 23:39:27 +0000357 self._do_run(control_file, results_dir, host, atrun, timeout,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800358 client_disconnect_timeout)
mblighd8b39252008-03-20 21:15:03 +0000359
360
jadmanski0afbb632008-06-06 21:10:57 +0000361 def _get_host_and_setup(self, host):
362 if not host:
363 host = self.host
364 if not self.installed:
365 self.install(host)
mbligh91334902007-09-28 01:47:59 +0000366
jadmanski0afbb632008-06-06 21:10:57 +0000367 host.wait_up(timeout=30)
368 return host
mblighd8b39252008-03-20 21:15:03 +0000369
370
jadmanski6dadd832009-02-05 23:39:27 +0000371 def _do_run(self, control_file, results_dir, host, atrun, timeout,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800372 client_disconnect_timeout):
jadmanski0afbb632008-06-06 21:10:57 +0000373 try:
374 atrun.verify_machine()
375 except:
showardb18134f2009-03-20 20:52:18 +0000376 logging.error("Verify failed on %s. Reinstalling autotest",
377 host.hostname)
jadmanski0afbb632008-06-06 21:10:57 +0000378 self.install(host)
379 atrun.verify_machine()
380 debug = os.path.join(results_dir, 'debug')
381 try:
382 os.makedirs(debug)
mbligh09108442008-10-15 16:27:38 +0000383 except Exception:
jadmanski0afbb632008-06-06 21:10:57 +0000384 pass
mbligh9a3f5e52008-05-28 21:21:43 +0000385
mbligh09108442008-10-15 16:27:38 +0000386 delete_file_list = [atrun.remote_control_file,
387 atrun.remote_control_file + '.state',
388 atrun.manual_control_file,
389 atrun.manual_control_file + '.state']
390 cmd = ';'.join('rm -f ' + control for control in delete_file_list)
391 host.run(cmd, ignore_status=True)
mbligh9a3f5e52008-05-28 21:21:43 +0000392
Dale Curtis386eea72011-09-21 18:43:04 -0700393 tmppath = utils.get(control_file, local_copy=True)
mblighc5ddfd12008-08-04 17:15:00 +0000394
jadmanskicb0e1612009-02-27 18:03:10 +0000395 # build up the initialization prologue for the control file
396 prologue_lines = []
jadmanski23afbec2008-09-17 18:12:07 +0000397
mbligh2f076832010-03-30 17:08:20 +0000398 # Add the additional user arguments
jadmanski808f4b12010-04-09 22:30:31 +0000399 prologue_lines.append("args = %r\n" % self.job.args)
mbligh2f076832010-03-30 17:08:20 +0000400
mbligh09108442008-10-15 16:27:38 +0000401 # If the packaging system is being used, add the repository list.
mblighddc9a402010-01-15 20:33:34 +0000402 repos = None
mblighc5ddfd12008-08-04 17:15:00 +0000403 try:
Eric Lid656d562011-04-20 11:48:29 -0700404 repos = self.get_fetch_location()
mbligh76d19f72008-10-15 16:24:43 +0000405 pkgmgr = packages.PackageManager('autotest', hostname=host.hostname,
406 repo_urls=repos)
jadmanskib1a51132009-08-07 16:45:50 +0000407 prologue_lines.append('job.add_repository(%s)\n' % repos)
mblighc5ddfd12008-08-04 17:15:00 +0000408 except global_config.ConfigError, e:
mblighddc9a402010-01-15 20:33:34 +0000409 # If repos is defined packaging is enabled so log the error
410 if repos:
411 logging.error(e)
mblighc5ddfd12008-08-04 17:15:00 +0000412
jadmanskie2eef7b2009-03-03 23:55:13 +0000413 # on full-size installs, turn on any profilers the server is using
jadmanski27b52912009-08-14 17:09:15 +0000414 if not atrun.background:
jadmanskie2eef7b2009-03-03 23:55:13 +0000415 running_profilers = host.job.profilers.add_log.iteritems()
416 for profiler, (args, dargs) in running_profilers:
417 call_args = [repr(profiler)]
418 call_args += [repr(arg) for arg in args]
419 call_args += ["%s=%r" % item for item in dargs.iteritems()]
420 prologue_lines.append("job.profilers.add(%s)\n"
421 % ", ".join(call_args))
422 cfile = "".join(prologue_lines)
423
mbligh09108442008-10-15 16:27:38 +0000424 cfile += open(tmppath).read()
425 open(tmppath, "w").write(cfile)
mblighc5ddfd12008-08-04 17:15:00 +0000426
jadmanskic09fc152008-10-15 17:56:59 +0000427 # Create and copy state file to remote_control_file + '.state'
mblighfc3da5b2010-01-06 18:37:22 +0000428 state_file = host.job.preprocess_client_state()
mblighfbf73ae2009-12-19 05:22:42 +0000429 host.send_file(state_file, atrun.remote_control_file + '.init.state')
jadmanskic09fc152008-10-15 17:56:59 +0000430 os.remove(state_file)
431
mblighc5ddfd12008-08-04 17:15:00 +0000432 # Copy control_file to remote_control_file on the host
jadmanski0afbb632008-06-06 21:10:57 +0000433 host.send_file(tmppath, atrun.remote_control_file)
434 if os.path.abspath(tmppath) != os.path.abspath(control_file):
435 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000436
jadmanski6bb32d72009-03-19 20:25:24 +0000437 atrun.execute_control(
jadmanski6dadd832009-02-05 23:39:27 +0000438 timeout=timeout,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800439 client_disconnect_timeout=client_disconnect_timeout)
jadmanski23afbec2008-09-17 18:12:07 +0000440
441
jadmanski0afbb632008-06-06 21:10:57 +0000442 def run_timed_test(self, test_name, results_dir='.', host=None,
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700443 timeout=None, parallel_flag=False, background=False,
444 client_disconnect_timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000445 """
446 Assemble a tiny little control file to just run one test,
447 and run it as an autotest client-side test
448 """
449 if not host:
450 host = self.host
451 if not self.installed:
452 self.install(host)
453 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
454 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
455 control = "job.run_test(%s)\n" % cmd
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700456 self.run(control, results_dir, host, timeout=timeout,
Andrew Bresticker2da1b762013-01-15 16:11:18 -0800457 parallel_flag=parallel_flag, background=background,
458 client_disconnect_timeout=client_disconnect_timeout)
mbligh0e4613b2007-10-29 16:55:07 +0000459
460
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700461 def run_test(self, test_name, results_dir='.', host=None,
Andrew Bresticker2da1b762013-01-15 16:11:18 -0800462 parallel_flag=False, background=False,
463 client_disconnect_timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000464 self.run_timed_test(test_name, results_dir, host, timeout=None,
Dennis Jeffreyb0be88b2013-04-18 11:18:38 -0700465 parallel_flag=parallel_flag, background=background,
Andrew Bresticker2da1b762013-01-15 16:11:18 -0800466 client_disconnect_timeout=client_disconnect_timeout,
jadmanskic98c4702009-01-05 15:50:06 +0000467 *args, **dargs)
mblighd54832b2007-07-25 16:46:56 +0000468
469
jadmanski69bdaac2010-07-28 16:27:20 +0000470class _BaseRun(object):
jadmanski0afbb632008-06-06 21:10:57 +0000471 """
472 Represents a run of autotest control file. This class maintains
473 all the state necessary as an autotest control file is executed.
mblighdcd57a82007-07-11 23:06:47 +0000474
jadmanski0afbb632008-06-06 21:10:57 +0000475 It is not intended to be used directly, rather control files
476 should be run using the run method in Autotest.
477 """
mblighb3c0c912008-11-27 00:32:45 +0000478 def __init__(self, host, results_dir, tag, parallel_flag, background):
jadmanski0afbb632008-06-06 21:10:57 +0000479 self.host = host
480 self.results_dir = results_dir
481 self.env = host.env
482 self.tag = tag
483 self.parallel_flag = parallel_flag
mblighb3c0c912008-11-27 00:32:45 +0000484 self.background = background
showardad812bf2009-10-20 23:49:56 +0000485 self.autodir = Autotest.get_installed_autodir(self.host)
mbligh78bf5352008-07-11 20:27:36 +0000486 control = os.path.join(self.autodir, 'control')
jadmanski0afbb632008-06-06 21:10:57 +0000487 if tag:
mbligh78bf5352008-07-11 20:27:36 +0000488 control += '.' + tag
489 self.manual_control_file = control
490 self.remote_control_file = control + '.autoserv'
lmr6d08b3c2009-11-18 19:26:38 +0000491 self.config_file = os.path.join(self.autodir, 'global_config.ini')
mblighdc735a22007-08-02 16:54:37 +0000492
493
jadmanski0afbb632008-06-06 21:10:57 +0000494 def verify_machine(self):
495 binary = os.path.join(self.autodir, 'bin/autotest')
496 try:
497 self.host.run('ls %s > /dev/null 2>&1' % binary)
498 except:
lmrd6d27ed2009-12-08 19:58:33 +0000499 raise error.AutoservInstallError(
500 "Autotest does not appear to be installed")
mblighdc735a22007-08-02 16:54:37 +0000501
jadmanski0afbb632008-06-06 21:10:57 +0000502 if not self.parallel_flag:
503 tmpdir = os.path.join(self.autodir, 'tmp')
504 download = os.path.join(self.autodir, 'tests/download')
505 self.host.run('umount %s' % tmpdir, ignore_status=True)
506 self.host.run('umount %s' % download, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000507
jadmanski6dadd832009-02-05 23:39:27 +0000508
509 def get_base_cmd_args(self, section):
showard234b2962009-07-28 20:02:30 +0000510 args = ['--verbose']
jadmanski0afbb632008-06-06 21:10:57 +0000511 if section > 0:
jadmanski6dadd832009-02-05 23:39:27 +0000512 args.append('-c')
jadmanski0afbb632008-06-06 21:10:57 +0000513 if self.tag:
jadmanski6dadd832009-02-05 23:39:27 +0000514 args.append('-t %s' % self.tag)
jadmanski0afbb632008-06-06 21:10:57 +0000515 if self.host.job.use_external_logging():
jadmanski6dadd832009-02-05 23:39:27 +0000516 args.append('-l')
mblighce955fc2009-08-24 21:59:02 +0000517 if self.host.hostname:
518 args.append('--hostname=%s' % self.host.hostname)
mbligh0d0f67d2009-11-06 03:15:03 +0000519 args.append('--user=%s' % self.host.job.user)
mblighce955fc2009-08-24 21:59:02 +0000520
jadmanski6dadd832009-02-05 23:39:27 +0000521 args.append(self.remote_control_file)
522 return args
523
524
525 def get_background_cmd(self, section):
526 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotest_client')]
527 cmd += self.get_base_cmd_args(section)
jadmanski69bdaac2010-07-28 16:27:20 +0000528 cmd += ['>/dev/null', '2>/dev/null', '&']
jadmanski6dadd832009-02-05 23:39:27 +0000529 return ' '.join(cmd)
530
531
532 def get_daemon_cmd(self, section, monitor_dir):
533 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotestd'),
534 monitor_dir, '-H autoserv']
535 cmd += self.get_base_cmd_args(section)
jadmanski69bdaac2010-07-28 16:27:20 +0000536 cmd += ['>/dev/null', '2>/dev/null', '&']
jadmanski6dadd832009-02-05 23:39:27 +0000537 return ' '.join(cmd)
538
539
540 def get_monitor_cmd(self, monitor_dir, stdout_read, stderr_read):
541 cmd = [os.path.join(self.autodir, 'bin', 'autotestd_monitor'),
542 monitor_dir, str(stdout_read), str(stderr_read)]
jadmanski0afbb632008-06-06 21:10:57 +0000543 return ' '.join(cmd)
mblighadf2aab2007-11-29 18:16:43 +0000544
mblighd8b39252008-03-20 21:15:03 +0000545
jadmanski4d03cf62010-03-04 18:32:28 +0000546 def get_client_log(self):
547 """Find what the "next" client.* prefix should be
548
549 @returns A string of the form client.INTEGER that should be prefixed
550 to all client debug log files.
551 """
552 max_digit = -1
553 debug_dir = os.path.join(self.results_dir, 'debug')
554 client_logs = glob.glob(os.path.join(debug_dir, 'client.*.*'))
555 for log in client_logs:
556 _, number, _ = log.split('.', 2)
557 if number.isdigit():
558 max_digit = max(max_digit, int(number))
559 return 'client.%d' % (max_digit + 1)
560
561
562 def copy_client_config_file(self, client_log_prefix=None):
563 """
564 Create and copy the client config file based on the server config.
565
566 @param client_log_prefix: Optional prefix to prepend to log files.
567 """
568 client_config_file = self._create_client_config_file(client_log_prefix)
569 self.host.send_file(client_config_file, self.config_file)
570 os.remove(client_config_file)
571
572
573 def _create_client_config_file(self, client_log_prefix=None):
574 """
575 Create a temporary file with the [CLIENT] section configuration values
576 taken from the server global_config.ini.
577
578 @param client_log_prefix: Optional prefix to prepend to log files.
579
580 @return: Path of the temporary file generated.
581 """
582 config = global_config.global_config.get_section_values('CLIENT')
583 if client_log_prefix:
584 config.set('CLIENT', 'default_logging_name', client_log_prefix)
585 return self._create_aux_file(config.write)
586
587
588 def _create_aux_file(self, func, *args):
589 """
590 Creates a temporary file and writes content to it according to a
591 content creation function. The file object is appended to *args, which
592 is then passed to the content creation function
593
594 @param func: Function that will be used to write content to the
595 temporary file.
596 @param *args: List of parameters that func takes.
597 @return: Path to the temporary file that was created.
598 """
599 fd, path = tempfile.mkstemp(dir=self.host.job.tmpdir)
600 aux_file = os.fdopen(fd, "w")
601 try:
602 list_args = list(args)
603 list_args.append(aux_file)
604 func(*list_args)
605 finally:
606 aux_file.close()
607 return path
mblighd8b39252008-03-20 21:15:03 +0000608
609
jadmanskib264ed02009-01-12 23:54:27 +0000610 @staticmethod
611 def is_client_job_finished(last_line):
612 return bool(re.match(r'^END .*\t----\t----\t.*$', last_line))
613
614
615 @staticmethod
616 def is_client_job_rebooting(last_line):
617 return bool(re.match(r'^\t*GOOD\t----\treboot\.start.*$', last_line))
618
619
beeps607428b2013-03-25 16:43:20 -0700620 def _diagnose_dut(self, old_boot_id=None):
621 """
622 Run diagnostic checks on a DUT.
623
624 1. ping: A dead host will not respond to pings.
625 2. ssh (happens with 3.): DUT hangs usually fail in authentication
626 but respond to pings.
627 3. Check if a reboot occured: A healthy but unexpected reboot leaves the
628 host running with a new boot id.
629
630 This method will always raise an exception from the AutotestFailure
631 family and should only get called when the reason for a test failing
632 is ambiguous.
633
634 @raises AutotestDeviceNotPingable: If the DUT doesn't respond to ping.
635 @raises AutotestDeviceNotSSHable: If we cannot SSH into the DUT.
636 @raises AutotestDeviceRebooted: If the boot id changed.
637 @raises AutotestAbort: If none of the above exceptions were raised.
638 Since we have no recourse we must abort at this stage.
639 """
640 msg = 'Autotest client terminated unexpectedly: '
641 if utils.ping(self.host.hostname, tries=1, deadline=1) != 0:
642 msg += 'DUT is no longer pingable, it may have rebooted or hung.\n'
643 raise AutotestDeviceNotPingable(msg)
644
645 if old_boot_id:
646 try:
647 new_boot_id = self.host.get_boot_id(timeout=60)
648 except Exception as e:
649 msg += ('DUT is pingable but not SSHable, it most likely'
beeps14768812013-09-25 12:58:45 -0700650 ' sporadically rebooted during testing. %s\n' % str(e))
beeps607428b2013-03-25 16:43:20 -0700651 raise AutotestDeviceNotSSHable(msg)
652 else:
653 if new_boot_id != old_boot_id:
654 msg += 'DUT rebooted during the test run.\n'
655 raise AutotestDeviceRebooted(msg)
656
657 msg += ('DUT is pingable, SSHable and did NOT restart '
658 'un-expectedly. We probably lost connectivity during the '
659 'test.')
660 else:
661 msg += ('DUT is pingable, could not determine if an un-expected '
662 'reboot occured during the test.')
663
664 raise AutotestAbort(msg)
665
666
beeps07f53b92013-01-08 12:55:10 -0800667 def log_unexpected_abort(self, stderr_redirector, old_boot_id=None):
668 """
beeps607428b2013-03-25 16:43:20 -0700669 Logs that something unexpected happened, then tries to diagnose the
670 failure. The purpose of this function is only to close out the status
671 log with the appropriate error message, not to critically terminate
672 the program.
beeps07f53b92013-01-08 12:55:10 -0800673
674 @param stderr_redirector: log stream.
675 @param old_boot_id: boot id used to infer if a reboot occured.
676 """
jadmanskia61edad2009-05-21 22:17:49 +0000677 stderr_redirector.flush_all_buffers()
beeps607428b2013-03-25 16:43:20 -0700678 try:
679 self._diagnose_dut(old_boot_id)
680 except AutotestFailure as e:
681 self.host.job.record('END ABORT', None, None, str(e))
jadmanskib264ed02009-01-12 23:54:27 +0000682
683
jadmanski6dadd832009-02-05 23:39:27 +0000684 def _execute_in_background(self, section, timeout):
685 full_cmd = self.get_background_cmd(section)
686 devnull = open(os.devnull, "w")
mblighd8b39252008-03-20 21:15:03 +0000687
jadmanski4d03cf62010-03-04 18:32:28 +0000688 self.copy_client_config_file(self.get_client_log())
689
mbligh0d0f67d2009-11-06 03:15:03 +0000690 self.host.job.push_execution_context(self.results_dir)
jadmanski0afbb632008-06-06 21:10:57 +0000691 try:
jadmanski0afbb632008-06-06 21:10:57 +0000692 result = self.host.run(full_cmd, ignore_status=True,
693 timeout=timeout,
jadmanski6dadd832009-02-05 23:39:27 +0000694 stdout_tee=devnull,
695 stderr_tee=devnull)
jadmanski0afbb632008-06-06 21:10:57 +0000696 finally:
mbligh0d0f67d2009-11-06 03:15:03 +0000697 self.host.job.pop_execution_context()
jadmanski6dadd832009-02-05 23:39:27 +0000698
699 return result
700
701
702 @staticmethod
703 def _strip_stderr_prologue(stderr):
704 """Strips the 'standard' prologue that get pre-pended to every
705 remote command and returns the text that was actually written to
706 stderr by the remote command."""
707 stderr_lines = stderr.split("\n")[1:]
708 if not stderr_lines:
709 return ""
710 elif stderr_lines[0].startswith("NOTE: autotestd_monitor"):
711 del stderr_lines[0]
712 return "\n".join(stderr_lines)
713
714
715 def _execute_daemon(self, section, timeout, stderr_redirector,
716 client_disconnect_timeout):
717 monitor_dir = self.host.get_tmp_dir()
718 daemon_cmd = self.get_daemon_cmd(section, monitor_dir)
jadmanski4d03cf62010-03-04 18:32:28 +0000719
720 # grab the location for the server-side client log file
721 client_log_prefix = self.get_client_log()
722 client_log_path = os.path.join(self.results_dir, 'debug',
723 client_log_prefix + '.log')
724 client_log = open(client_log_path, 'w', 0)
725 self.copy_client_config_file(client_log_prefix)
jadmanski6dadd832009-02-05 23:39:27 +0000726
727 stdout_read = stderr_read = 0
mbligh0d0f67d2009-11-06 03:15:03 +0000728 self.host.job.push_execution_context(self.results_dir)
jadmanski6dadd832009-02-05 23:39:27 +0000729 try:
730 self.host.run(daemon_cmd, ignore_status=True, timeout=timeout)
jadmanski91d56a92009-04-01 15:20:40 +0000731 disconnect_warnings = []
jadmanski6dadd832009-02-05 23:39:27 +0000732 while True:
733 monitor_cmd = self.get_monitor_cmd(monitor_dir, stdout_read,
734 stderr_read)
735 try:
736 result = self.host.run(monitor_cmd, ignore_status=True,
737 timeout=timeout,
738 stdout_tee=client_log,
739 stderr_tee=stderr_redirector)
740 except error.AutoservRunError, e:
741 result = e.result_obj
742 result.exit_status = None
jadmanski91d56a92009-04-01 15:20:40 +0000743 disconnect_warnings.append(e.description)
744
jadmanski6dadd832009-02-05 23:39:27 +0000745 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000746 "Autotest client was disconnected: %s" % e.description,
747 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000748 except error.AutoservSSHTimeout:
749 result = utils.CmdResult(monitor_cmd, "", "", None, 0)
750 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000751 "Attempt to connect to Autotest client timed out",
752 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000753
754 stdout_read += len(result.stdout)
755 stderr_read += len(self._strip_stderr_prologue(result.stderr))
756
757 if result.exit_status is not None:
Simran Basibca10a62013-01-24 15:52:35 -0800758 # TODO (crosbug.com/38224)- sbasi: Remove extra logging.
759 logging.debug('Result exit status is %d.',
760 result.exit_status)
jadmanski6dadd832009-02-05 23:39:27 +0000761 return result
762 elif not self.host.wait_up(client_disconnect_timeout):
763 raise error.AutoservSSHTimeout(
764 "client was disconnected, reconnect timed out")
765 finally:
jadmanski4d03cf62010-03-04 18:32:28 +0000766 client_log.close()
mbligh0d0f67d2009-11-06 03:15:03 +0000767 self.host.job.pop_execution_context()
jadmanski6dadd832009-02-05 23:39:27 +0000768
769
770 def execute_section(self, section, timeout, stderr_redirector,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800771 client_disconnect_timeout):
showardb18134f2009-03-20 20:52:18 +0000772 logging.info("Executing %s/bin/autotest %s/control phase %d",
773 self.autodir, self.autodir, section)
jadmanski6dadd832009-02-05 23:39:27 +0000774
775 if self.background:
776 result = self._execute_in_background(section, timeout)
777 else:
778 result = self._execute_daemon(section, timeout, stderr_redirector,
779 client_disconnect_timeout)
780
781 last_line = stderr_redirector.last_line
mbligh2bf2db62007-11-27 00:53:18 +0000782
jadmanskib264ed02009-01-12 23:54:27 +0000783 # check if we failed hard enough to warrant an exception
jadmanski0afbb632008-06-06 21:10:57 +0000784 if result.exit_status == 1:
jadmanskib264ed02009-01-12 23:54:27 +0000785 err = error.AutotestRunError("client job was aborted")
786 elif not self.background and not result.stderr:
787 err = error.AutotestRunError(
jadmanskie4130532009-03-17 18:01:28 +0000788 "execute_section %s failed to return anything\n"
789 "stdout:%s\n" % (section, result.stdout))
jadmanskib264ed02009-01-12 23:54:27 +0000790 else:
791 err = None
mbligh0e4613b2007-10-29 16:55:07 +0000792
jadmanskib264ed02009-01-12 23:54:27 +0000793 # log something if the client failed AND never finished logging
Dale Curtis9285ddf2011-01-05 11:47:24 -0800794 if err and not self.is_client_job_finished(last_line):
jadmanskia61edad2009-05-21 22:17:49 +0000795 self.log_unexpected_abort(stderr_redirector)
jadmanskib264ed02009-01-12 23:54:27 +0000796
797 if err:
798 raise err
799 else:
800 return stderr_redirector.last_line
jadmanski4600e342008-10-29 22:54:00 +0000801
802
jadmanskic0354912010-01-12 15:57:29 +0000803 def _wait_for_reboot(self, old_boot_id):
showardb18134f2009-03-20 20:52:18 +0000804 logging.info("Client is rebooting")
805 logging.info("Waiting for client to halt")
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700806 if not self.host.wait_down(self.host.WAIT_DOWN_REBOOT_TIMEOUT,
807 old_boot_id=old_boot_id):
jadmanski4600e342008-10-29 22:54:00 +0000808 err = "%s failed to shutdown after %d"
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700809 err %= (self.host.hostname, self.host.WAIT_DOWN_REBOOT_TIMEOUT)
jadmanski4600e342008-10-29 22:54:00 +0000810 raise error.AutotestRunError(err)
showardb18134f2009-03-20 20:52:18 +0000811 logging.info("Client down, waiting for restart")
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700812 if not self.host.wait_up(self.host.DEFAULT_REBOOT_TIMEOUT):
jadmanski4600e342008-10-29 22:54:00 +0000813 # since reboot failed
814 # hardreset the machine once if possible
815 # before failing this control file
816 warning = "%s did not come back up, hard resetting"
817 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000818 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000819 try:
820 self.host.hardreset(wait=False)
mblighd99d3b272008-12-22 14:41:27 +0000821 except (AttributeError, error.AutoservUnsupportedError):
jadmanski4600e342008-10-29 22:54:00 +0000822 warning = "Hard reset unsupported on %s"
823 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000824 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000825 raise error.AutotestRunError("%s failed to boot after %ds" %
Dale Curtiscb7bfaf2011-06-07 16:21:57 -0700826 (self.host.hostname,
827 self.host.DEFAULT_REBOOT_TIMEOUT))
jadmanski4600e342008-10-29 22:54:00 +0000828 self.host.reboot_followup()
mblighdc735a22007-08-02 16:54:37 +0000829
830
Dale Curtis9285ddf2011-01-05 11:47:24 -0800831 def execute_control(self, timeout=None, client_disconnect_timeout=None):
jadmanski6bb32d72009-03-19 20:25:24 +0000832 if not self.background:
833 collector = log_collector(self.host, self.tag, self.results_dir)
834 hostname = self.host.hostname
835 remote_results = collector.client_results_dir
836 local_results = collector.server_results_dir
837 self.host.job.add_client_log(hostname, remote_results,
838 local_results)
jadmanski52053632010-06-11 21:08:10 +0000839 job_record_context = self.host.job.get_record_context()
jadmanski6bb32d72009-03-19 20:25:24 +0000840
jadmanski0afbb632008-06-06 21:10:57 +0000841 section = 0
jadmanski4600e342008-10-29 22:54:00 +0000842 start_time = time.time()
843
jadmanski043e1132008-11-19 17:10:32 +0000844 logger = client_logger(self.host, self.tag, self.results_dir)
jadmanski4600e342008-10-29 22:54:00 +0000845 try:
846 while not timeout or time.time() < start_time + timeout:
847 if timeout:
848 section_timeout = start_time + timeout - time.time()
849 else:
850 section_timeout = None
jadmanskic0354912010-01-12 15:57:29 +0000851 boot_id = self.host.get_boot_id()
jadmanski4600e342008-10-29 22:54:00 +0000852 last = self.execute_section(section, section_timeout,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800853 logger, client_disconnect_timeout)
mblighb3c0c912008-11-27 00:32:45 +0000854 if self.background:
855 return
jadmanski4600e342008-10-29 22:54:00 +0000856 section += 1
jadmanskib264ed02009-01-12 23:54:27 +0000857 if self.is_client_job_finished(last):
showardb18134f2009-03-20 20:52:18 +0000858 logging.info("Client complete")
jadmanski4600e342008-10-29 22:54:00 +0000859 return
jadmanskib264ed02009-01-12 23:54:27 +0000860 elif self.is_client_job_rebooting(last):
jadmanski79ab9282008-11-11 17:53:12 +0000861 try:
jadmanskic0354912010-01-12 15:57:29 +0000862 self._wait_for_reboot(boot_id)
jadmanski79ab9282008-11-11 17:53:12 +0000863 except error.AutotestRunError, e:
jadmanski043e1132008-11-19 17:10:32 +0000864 self.host.job.record("ABORT", None, "reboot", str(e))
865 self.host.job.record("END ABORT", None, None, str(e))
jadmanski79ab9282008-11-11 17:53:12 +0000866 raise
jadmanski4600e342008-10-29 22:54:00 +0000867 continue
868
beeps607428b2013-03-25 16:43:20 -0700869 # If a test fails without probable cause we try to bucket it's
870 # failure into one of 2 categories. If we can determine the
871 # current state of the device and it is suspicious, we close the
872 # status lines indicating a failure. If we either cannot
873 # determine the state of the device, or it appears totally
874 # healthy, we give up and abort.
875 try:
876 self._diagnose_dut(boot_id)
877 except AutotestDeviceError as e:
878 # The status lines of the test are pretty much tailed to
879 # our log, with indentation, from the client job on the DUT.
880 # So if the DUT goes down unexpectedly we'll end up with a
881 # malformed status log unless we manually unwind the status
882 # stack. Ideally we would want to write a nice wrapper like
883 # server_job methods run_reboot, run_group but they expect
884 # reboots and we don't.
885 self.host.job.record('FAIL', None, None, str(e))
886 self.host.job.record('END FAIL', None, None)
887 self.host.job.record('END GOOD', None, None)
Dan Shib03ea9d2013-08-15 17:13:27 -0700888 self.host.job.failed_with_device_error = True
beeps607428b2013-03-25 16:43:20 -0700889 return
890 except AutotestAbort as e:
891 self.host.job.record('ABORT', None, None, str(e))
892 self.host.job.record('END ABORT', None, None)
jadmanski4600e342008-10-29 22:54:00 +0000893
beeps607428b2013-03-25 16:43:20 -0700894 # give the client machine a chance to recover from a crash
895 self.host.wait_up(
896 self.host.HOURS_TO_WAIT_FOR_RECOVERY * 3600)
897 msg = ("Aborting - unexpected final status message from "
898 "client on %s: %s\n") % (self.host.hostname, last)
899 raise error.AutotestRunError(msg)
jadmanski4600e342008-10-29 22:54:00 +0000900 finally:
jadmanski043e1132008-11-19 17:10:32 +0000901 logger.close()
jadmanski6bb32d72009-03-19 20:25:24 +0000902 if not self.background:
903 collector.collect_client_job_results()
jadmanski4d03cf62010-03-04 18:32:28 +0000904 collector.remove_redundant_client_logs()
mblighfc3da5b2010-01-06 18:37:22 +0000905 state_file = os.path.basename(self.remote_control_file
906 + '.state')
907 state_path = os.path.join(self.results_dir, state_file)
908 self.host.job.postprocess_client_state(state_path)
jadmanski6bb32d72009-03-19 20:25:24 +0000909 self.host.job.remove_client_log(hostname, remote_results,
910 local_results)
jadmanski52053632010-06-11 21:08:10 +0000911 job_record_context.restore()
mblighdcd57a82007-07-11 23:06:47 +0000912
jadmanski0afbb632008-06-06 21:10:57 +0000913 # should only get here if we timed out
914 assert timeout
915 raise error.AutotestTimeoutError()
mbligh0e4613b2007-10-29 16:55:07 +0000916
mblighdcd57a82007-07-11 23:06:47 +0000917
jadmanski043e1132008-11-19 17:10:32 +0000918class log_collector(object):
919 def __init__(self, host, client_tag, results_dir):
920 self.host = host
921 if not client_tag:
922 client_tag = "default"
923 self.client_results_dir = os.path.join(host.get_autodir(), "results",
924 client_tag)
925 self.server_results_dir = results_dir
926
927
928 def collect_client_job_results(self):
929 """ A method that collects all the current results of a running
930 client job into the results dir. By default does nothing as no
931 client job is running, but when running a client job you can override
932 this with something that will actually do something. """
933
934 # make an effort to wait for the machine to come up
935 try:
936 self.host.wait_up(timeout=30)
937 except error.AutoservError:
938 # don't worry about any errors, we'll try and
939 # get the results anyway
940 pass
941
jadmanski043e1132008-11-19 17:10:32 +0000942 # Copy all dirs in default to results_dir
Dan Shi209eefd2013-05-30 13:17:48 -0700943 timer = stats.Timer('collect_client_job_results')
944 timer.start()
jadmanski043e1132008-11-19 17:10:32 +0000945 try:
jadmanski043e1132008-11-19 17:10:32 +0000946 self.host.get_file(self.client_results_dir + '/',
mbligh45561782009-05-11 21:14:34 +0000947 self.server_results_dir, preserve_symlinks=True)
Dan Shib03ea9d2013-08-15 17:13:27 -0700948
Dan Shi209eefd2013-05-30 13:17:48 -0700949 # Only report time used for successful get_file calls.
950 timer.stop();
jadmanski043e1132008-11-19 17:10:32 +0000951 except Exception:
952 # well, don't stop running just because we couldn't get logs
showardb18134f2009-03-20 20:52:18 +0000953 e_msg = "Unexpected error copying test result logs, continuing ..."
954 logging.error(e_msg)
jadmanski043e1132008-11-19 17:10:32 +0000955 traceback.print_exc(file=sys.stdout)
956
957
jadmanski4d03cf62010-03-04 18:32:28 +0000958 def remove_redundant_client_logs(self):
959 """Remove client.*.log files in favour of client.*.DEBUG files."""
960 debug_dir = os.path.join(self.server_results_dir, 'debug')
961 debug_files = [f for f in os.listdir(debug_dir)
962 if re.search(r'^client\.\d+\.DEBUG$', f)]
963 for debug_file in debug_files:
964 log_file = debug_file.replace('DEBUG', 'log')
965 log_file = os.path.join(debug_dir, log_file)
966 if os.path.exists(log_file):
967 os.remove(log_file)
968
969
jadmanski043e1132008-11-19 17:10:32 +0000970# a file-like object for catching stderr from an autotest client and
971# extracting status logs from it
Chris Sosa3ee5d5c2012-02-23 11:18:41 -0800972class BaseClientLogger(object):
jadmanski043e1132008-11-19 17:10:32 +0000973 """Partial file object to write to both stdout and
974 the status log file. We only implement those methods
975 utils.run() actually calls.
jadmanski043e1132008-11-19 17:10:32 +0000976 """
977 status_parser = re.compile(r"^AUTOTEST_STATUS:([^:]*):(.*)$")
978 test_complete_parser = re.compile(r"^AUTOTEST_TEST_COMPLETE:(.*)$")
jadmanskib1a51132009-08-07 16:45:50 +0000979 fetch_package_parser = re.compile(
980 r"^AUTOTEST_FETCH_PACKAGE:([^:]*):([^:]*):(.*)$")
jadmanski043e1132008-11-19 17:10:32 +0000981 extract_indent = re.compile(r"^(\t*).*$")
jadmanskiefe4ebf2009-05-21 22:12:30 +0000982 extract_timestamp = re.compile(r".*\ttimestamp=(\d+)\t.*$")
jadmanski043e1132008-11-19 17:10:32 +0000983
984 def __init__(self, host, tag, server_results_dir):
985 self.host = host
986 self.job = host.job
987 self.log_collector = log_collector(host, tag, server_results_dir)
988 self.leftover = ""
989 self.last_line = ""
990 self.logs = {}
jadmanskiefe4ebf2009-05-21 22:12:30 +0000991
992
jadmanski043e1132008-11-19 17:10:32 +0000993 def _process_log_dict(self, log_dict):
994 log_list = log_dict.pop("logs", [])
995 for key in sorted(log_dict.iterkeys()):
996 log_list += self._process_log_dict(log_dict.pop(key))
997 return log_list
998
999
1000 def _process_logs(self):
1001 """Go through the accumulated logs in self.log and print them
1002 out to stdout and the status log. Note that this processes
1003 logs in an ordering where:
1004
1005 1) logs to different tags are never interleaved
1006 2) logs to x.y come before logs to x.y.z for all z
1007 3) logs to x.y come before x.z whenever y < z
1008
1009 Note that this will in general not be the same as the
1010 chronological ordering of the logs. However, if a chronological
1011 ordering is desired that one can be reconstructed from the
1012 status log by looking at timestamp lines."""
1013 log_list = self._process_log_dict(self.logs)
jadmanski2a89dac2010-06-11 14:32:58 +00001014 for entry in log_list:
1015 self.job.record_entry(entry, log_in_subdir=False)
jadmanski043e1132008-11-19 17:10:32 +00001016 if log_list:
jadmanski2a89dac2010-06-11 14:32:58 +00001017 self.last_line = log_list[-1].render()
jadmanski043e1132008-11-19 17:10:32 +00001018
1019
1020 def _process_quoted_line(self, tag, line):
1021 """Process a line quoted with an AUTOTEST_STATUS flag. If the
1022 tag is blank then we want to push out all the data we've been
1023 building up in self.logs, and then the newest line. If the
1024 tag is not blank, then push the line into the logs for handling
1025 later."""
jadmanski2a89dac2010-06-11 14:32:58 +00001026 entry = base_job.status_log_entry.parse(line)
1027 if entry is None:
1028 return # the line contains no status lines
jadmanski043e1132008-11-19 17:10:32 +00001029 if tag == "":
1030 self._process_logs()
jadmanski2a89dac2010-06-11 14:32:58 +00001031 self.job.record_entry(entry, log_in_subdir=False)
jadmanski043e1132008-11-19 17:10:32 +00001032 self.last_line = line
1033 else:
1034 tag_parts = [int(x) for x in tag.split(".")]
1035 log_dict = self.logs
1036 for part in tag_parts:
1037 log_dict = log_dict.setdefault(part, {})
1038 log_list = log_dict.setdefault("logs", [])
jadmanski2a89dac2010-06-11 14:32:58 +00001039 log_list.append(entry)
jadmanski043e1132008-11-19 17:10:32 +00001040
1041
jadmanskif37df842009-02-11 00:03:26 +00001042 def _process_info_line(self, line):
1043 """Check if line is an INFO line, and if it is, interpret any control
1044 messages (e.g. enabling/disabling warnings) that it may contain."""
1045 match = re.search(r"^\t*INFO\t----\t----(.*)\t[^\t]*$", line)
1046 if not match:
1047 return # not an INFO line
1048 for field in match.group(1).split('\t'):
1049 if field.startswith("warnings.enable="):
jadmanski16a7ff72009-04-01 18:19:53 +00001050 func = self.job.warning_manager.enable_warnings
jadmanskif37df842009-02-11 00:03:26 +00001051 elif field.startswith("warnings.disable="):
jadmanski16a7ff72009-04-01 18:19:53 +00001052 func = self.job.warning_manager.disable_warnings
jadmanskif37df842009-02-11 00:03:26 +00001053 else:
1054 continue
1055 warning_type = field.split("=", 1)[1]
jadmanski16a7ff72009-04-01 18:19:53 +00001056 func(warning_type)
jadmanskif37df842009-02-11 00:03:26 +00001057
1058
jadmanski043e1132008-11-19 17:10:32 +00001059 def _process_line(self, line):
1060 """Write out a line of data to the appropriate stream. Status
1061 lines sent by autotest will be prepended with
1062 "AUTOTEST_STATUS", and all other lines are ssh error
1063 messages."""
1064 status_match = self.status_parser.search(line)
1065 test_complete_match = self.test_complete_parser.search(line)
jadmanskib1a51132009-08-07 16:45:50 +00001066 fetch_package_match = self.fetch_package_parser.search(line)
jadmanski043e1132008-11-19 17:10:32 +00001067 if status_match:
1068 tag, line = status_match.groups()
jadmanskif37df842009-02-11 00:03:26 +00001069 self._process_info_line(line)
jadmanski043e1132008-11-19 17:10:32 +00001070 self._process_quoted_line(tag, line)
1071 elif test_complete_match:
jadmanskifcc0d5d2009-02-12 21:52:54 +00001072 self._process_logs()
jadmanski043e1132008-11-19 17:10:32 +00001073 fifo_path, = test_complete_match.groups()
mbligh060c4712009-12-29 02:43:35 +00001074 try:
1075 self.log_collector.collect_client_job_results()
1076 self.host.run("echo A > %s" % fifo_path)
1077 except Exception:
1078 msg = "Post-test log collection failed, continuing anyway"
1079 logging.exception(msg)
jadmanskib1a51132009-08-07 16:45:50 +00001080 elif fetch_package_match:
1081 pkg_name, dest_path, fifo_path = fetch_package_match.groups()
jadmanskiede7e242009-08-10 15:43:33 +00001082 serve_packages = global_config.global_config.get_config_value(
1083 "PACKAGES", "serve_packages_from_autoserv", type=bool)
1084 if serve_packages and pkg_name.endswith(".tar.bz2"):
1085 try:
1086 self._send_tarball(pkg_name, dest_path)
1087 except Exception:
1088 msg = "Package tarball creation failed, continuing anyway"
1089 logging.exception(msg)
mbligh060c4712009-12-29 02:43:35 +00001090 try:
1091 self.host.run("echo B > %s" % fifo_path)
1092 except Exception:
1093 msg = "Package tarball installation failed, continuing anyway"
1094 logging.exception(msg)
jadmanski043e1132008-11-19 17:10:32 +00001095 else:
showardb18134f2009-03-20 20:52:18 +00001096 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +00001097
1098
jadmanskiede7e242009-08-10 15:43:33 +00001099 def _send_tarball(self, pkg_name, remote_dest):
1100 name, pkg_type = self.job.pkgmgr.parse_tarball_name(pkg_name)
1101 src_dirs = []
1102 if pkg_type == 'test':
mbligh1f572e52010-04-01 17:15:53 +00001103 for test_dir in ['site_tests', 'tests']:
1104 src_dir = os.path.join(self.job.clientdir, test_dir, name)
1105 if os.path.exists(src_dir):
1106 src_dirs += [src_dir]
1107 if autoserv_prebuild:
1108 prebuild.setup(self.job.clientdir, src_dir)
1109 break
jadmanskiede7e242009-08-10 15:43:33 +00001110 elif pkg_type == 'profiler':
1111 src_dirs += [os.path.join(self.job.clientdir, 'profilers', name)]
mbligh1f572e52010-04-01 17:15:53 +00001112 if autoserv_prebuild:
1113 prebuild.setup(self.job.clientdir, src_dir)
jadmanskiede7e242009-08-10 15:43:33 +00001114 elif pkg_type == 'dep':
1115 src_dirs += [os.path.join(self.job.clientdir, 'deps', name)]
1116 elif pkg_type == 'client':
1117 return # you must already have a client to hit this anyway
1118 else:
1119 return # no other types are supported
1120
1121 # iterate over src_dirs until we find one that exists, then tar it
1122 for src_dir in src_dirs:
1123 if os.path.exists(src_dir):
1124 try:
1125 logging.info('Bundling %s into %s', src_dir, pkg_name)
1126 temp_dir = autotemp.tempdir(unique_id='autoserv-packager',
1127 dir=self.job.tmpdir)
1128 tarball_path = self.job.pkgmgr.tar_package(
mblighbccad482009-08-24 22:08:31 +00001129 pkg_name, src_dir, temp_dir.name, " .")
jadmanskiede7e242009-08-10 15:43:33 +00001130 self.host.send_file(tarball_path, remote_dest)
1131 finally:
1132 temp_dir.clean()
1133 return
1134
1135
jadmanski91d56a92009-04-01 15:20:40 +00001136 def log_warning(self, msg, warning_type):
jadmanski6dadd832009-02-05 23:39:27 +00001137 """Injects a WARN message into the current status logging stream."""
jadmanski91d56a92009-04-01 15:20:40 +00001138 timestamp = int(time.time())
1139 if self.job.warning_manager.is_valid(timestamp, warning_type):
Eric Lid656d562011-04-20 11:48:29 -07001140 self.job.record('WARN', None, None, msg)
jadmanski6dadd832009-02-05 23:39:27 +00001141
jadmanski043e1132008-11-19 17:10:32 +00001142
1143 def write(self, data):
jadmanski2a89dac2010-06-11 14:32:58 +00001144 # now start processing the existing buffer and the new data
jadmanski043e1132008-11-19 17:10:32 +00001145 data = self.leftover + data
mbligh060c4712009-12-29 02:43:35 +00001146 lines = data.split('\n')
1147 processed_lines = 0
1148 try:
1149 # process all the buffered data except the last line
1150 # ignore the last line since we may not have all of it yet
1151 for line in lines[:-1]:
mbligh060c4712009-12-29 02:43:35 +00001152 self._process_line(line)
1153 processed_lines += 1
1154 finally:
1155 # save any unprocessed lines for future processing
1156 self.leftover = '\n'.join(lines[processed_lines:])
jadmanski043e1132008-11-19 17:10:32 +00001157
1158
1159 def flush(self):
1160 sys.stdout.flush()
1161
1162
jadmanskia61edad2009-05-21 22:17:49 +00001163 def flush_all_buffers(self):
jadmanski043e1132008-11-19 17:10:32 +00001164 if self.leftover:
1165 self._process_line(self.leftover)
jadmanskia61edad2009-05-21 22:17:49 +00001166 self.leftover = ""
jadmanski043e1132008-11-19 17:10:32 +00001167 self._process_logs()
1168 self.flush()
1169
1170
jadmanskia61edad2009-05-21 22:17:49 +00001171 def close(self):
1172 self.flush_all_buffers()
1173
1174
mbligha7007722009-01-13 00:37:11 +00001175SiteAutotest = client_utils.import_site_class(
1176 __file__, "autotest_lib.server.site_autotest", "SiteAutotest",
1177 BaseAutotest)
mblighd8b39252008-03-20 21:15:03 +00001178
showardad812bf2009-10-20 23:49:56 +00001179
jadmanski69bdaac2010-07-28 16:27:20 +00001180_SiteRun = client_utils.import_site_class(
1181 __file__, "autotest_lib.server.site_autotest", "_SiteRun", _BaseRun)
1182
1183
Chris Sosa3ee5d5c2012-02-23 11:18:41 -08001184SiteClientLogger = client_utils.import_site_class(
1185 __file__, "autotest_lib.server.site_autotest", "SiteClientLogger",
1186 BaseClientLogger)
1187
1188
mblighd8b39252008-03-20 21:15:03 +00001189class Autotest(SiteAutotest):
jadmanski0afbb632008-06-06 21:10:57 +00001190 pass
jadmanskic6136e92010-07-19 16:41:49 +00001191
1192
Chris Sosa3ee5d5c2012-02-23 11:18:41 -08001193class client_logger(SiteClientLogger):
1194 pass
1195
1196
jadmanski69bdaac2010-07-28 16:27:20 +00001197class _Run(_SiteRun):
1198 pass
1199
1200
jadmanskic6136e92010-07-19 16:41:49 +00001201class AutotestHostMixin(object):
1202 """A generic mixin to add a run_test method to classes, which will allow
1203 you to run an autotest client test on a machine directly."""
1204
1205 # for testing purposes
1206 _Autotest = Autotest
1207
1208 def run_test(self, test_name, **dargs):
1209 """Run an autotest client test on the host.
1210
1211 @param test_name: The name of the client test.
1212 @param dargs: Keyword arguments to pass to the test.
1213
1214 @returns: True if the test passes, False otherwise."""
1215 at = self._Autotest()
1216 control_file = ('result = job.run_test(%s)\n'
1217 'job.set_state("test_result", result)\n')
1218 test_args = [repr(test_name)]
1219 test_args += ['%s=%r' % (k, v) for k, v in dargs.iteritems()]
1220 control_file %= ', '.join(test_args)
1221 at.run(control_file, host=self)
1222 return at.job.get_state('test_result', default=False)