blob: eb250952ba7bdd58d565ef230e54e5fd6342e684 [file] [log] [blame]
mblighdcd57a82007-07-11 23:06:47 +00001# Copyright 2007 Google Inc. Released under the GPL v2
2
jadmanski4d03cf62010-03-04 18:32:28 +00003import re, os, sys, traceback, subprocess, time, pickle, glob, tempfile
4import logging, getpass
mbligh1f572e52010-04-01 17:15:53 +00005from autotest_lib.server import installable_object, prebuild, utils
jadmanski2a89dac2010-06-11 14:32:58 +00006from autotest_lib.client.common_lib import base_job, log, 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
mbligh3c7a1502008-07-24 18:08:47 +00009
mblighdcd57a82007-07-11 23:06:47 +000010AUTOTEST_SVN = 'svn://test.kernel.org/autotest/trunk/client'
11AUTOTEST_HTTP = 'http://test.kernel.org/svn/autotest/trunk/client'
12
13# Timeouts for powering down and up respectively
14HALT_TIME = 300
mbligh07c1eac2007-11-05 18:39:29 +000015BOOT_TIME = 1800
jadmanskiec859142008-05-29 21:33:39 +000016CRASH_RECOVERY_TIME = 9000
mbligh0e4613b2007-10-29 16:55:07 +000017
mblighdcd57a82007-07-11 23:06:47 +000018
mbligh1f572e52010-04-01 17:15:53 +000019get_value = global_config.global_config.get_config_value
20autoserv_prebuild = get_value('AUTOSERV', 'enable_server_prebuild',
21 type=bool, default=False)
22
jadmanski2a89dac2010-06-11 14:32:58 +000023
showardad812bf2009-10-20 23:49:56 +000024class AutodirNotFoundError(Exception):
25 """No Autotest installation could be found."""
26
27
mblighd8b39252008-03-20 21:15:03 +000028class BaseAutotest(installable_object.InstallableObject):
jadmanski0afbb632008-06-06 21:10:57 +000029 """
30 This class represents the Autotest program.
mblighdcd57a82007-07-11 23:06:47 +000031
jadmanski0afbb632008-06-06 21:10:57 +000032 Autotest is used to run tests automatically and collect the results.
33 It also supports profilers.
mblighdcd57a82007-07-11 23:06:47 +000034
jadmanski0afbb632008-06-06 21:10:57 +000035 Implementation details:
36 This is a leaf class in an abstract class hierarchy, it must
37 implement the unimplemented methods in parent classes.
38 """
mbligh119c12a2007-11-12 22:13:44 +000039
jadmanski0afbb632008-06-06 21:10:57 +000040 def __init__(self, host = None):
41 self.host = host
42 self.got = False
43 self.installed = False
44 self.serverdir = utils.get_server_dir()
45 super(BaseAutotest, self).__init__()
mblighc8949b82007-07-23 16:33:58 +000046
mblighdc735a22007-08-02 16:54:37 +000047
jadmanskif22fea82008-11-26 20:57:07 +000048 install_in_tmpdir = False
49 @classmethod
50 def set_install_in_tmpdir(cls, flag):
51 """ Sets a flag that controls whether or not Autotest should by
52 default be installed in a "standard" directory (e.g.
53 /home/autotest, /usr/local/autotest) or a temporary directory. """
54 cls.install_in_tmpdir = flag
55
56
showardad812bf2009-10-20 23:49:56 +000057 @classmethod
58 def get_client_autodir_paths(cls, host):
59 return global_config.global_config.get_config_value(
60 'AUTOSERV', 'client_autodir_paths', type=list)
61
62
63 @classmethod
64 def get_installed_autodir(cls, host):
65 """
66 Find where the Autotest client is installed on the host.
67 @returns an absolute path to an installed Autotest client root.
68 @raises AutodirNotFoundError if no Autotest installation can be found.
69 """
70 autodir = host.get_autodir()
71 if autodir:
72 logging.debug('Using existing host autodir: %s', autodir)
73 return autodir
74
75 for path in Autotest.get_client_autodir_paths(host):
76 try:
77 autotest_binary = os.path.join(path, 'bin', 'autotest')
78 host.run('test -x %s' % utils.sh_escape(autotest_binary))
79 logging.debug('Found existing autodir at %s', path)
80 return path
81 except error.AutoservRunError:
82 logging.debug('%s does not exist on %s', autotest_binary,
83 host.hostname)
84 raise AutodirNotFoundError
85
86
87 @classmethod
88 def get_install_dir(cls, host):
89 """
90 Determines the location where autotest should be installed on
jadmanskif22fea82008-11-26 20:57:07 +000091 host. If self.install_in_tmpdir is set, it will return a unique
showardad812bf2009-10-20 23:49:56 +000092 temporary directory that autotest can be installed in. Otherwise, looks
93 for an existing installation to use; if none is found, looks for a
94 usable directory in the global config client_autodir_paths.
95 """
jadmanskif22fea82008-11-26 20:57:07 +000096 try:
lmr9dcf0832009-12-08 21:28:55 +000097 install_dir = cls.get_installed_autodir(host)
showardad812bf2009-10-20 23:49:56 +000098 except AutodirNotFoundError:
lmr9dcf0832009-12-08 21:28:55 +000099 install_dir = cls._find_installable_dir(host)
100
101 if cls.install_in_tmpdir:
102 return host.get_tmp_dir(parent=install_dir)
103 return install_dir
showardad812bf2009-10-20 23:49:56 +0000104
105
106 @classmethod
107 def _find_installable_dir(cls, host):
108 client_autodir_paths = cls.get_client_autodir_paths(host)
109 for path in client_autodir_paths:
110 try:
J. Richard Barnettef44ff962011-01-05 10:14:28 -0800111 host.run('mkdir -p %s' % utils.sh_escape(path))
showardad812bf2009-10-20 23:49:56 +0000112 return path
113 except error.AutoservRunError:
114 logging.debug('Failed to create %s', path)
115 raise error.AutoservInstallError(
116 'Unable to find a place to install Autotest; tried %s',
117 ', '.join(client_autodir_paths))
jadmanskif22fea82008-11-26 20:57:07 +0000118
119
mblighb3c0c912008-11-27 00:32:45 +0000120 def install(self, host=None, autodir=None):
121 self._install(host=host, autodir=autodir)
jadmanski54f90af2008-10-10 16:20:55 +0000122
123
mblighb8aa75b2009-09-18 16:50:37 +0000124 def install_full_client(self, host=None, autodir=None):
125 self._install(host=host, autodir=autodir, use_autoserv=False,
126 use_packaging=False)
127
128
mblighbccad482009-08-24 22:08:31 +0000129 def install_no_autoserv(self, host=None, autodir=None):
mblighb8aa75b2009-09-18 16:50:37 +0000130 self._install(host=host, autodir=autodir, use_autoserv=False)
mblighbccad482009-08-24 22:08:31 +0000131
132
mblighb8aa75b2009-09-18 16:50:37 +0000133 def _install_using_packaging(self, host, autodir):
134 c = global_config.global_config
jadmanski2315a7e2009-09-18 18:39:37 +0000135 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list,
136 default=[])
137 repos.reverse()
mblighb8aa75b2009-09-18 16:50:37 +0000138 if not repos:
139 raise error.PackageInstallError("No repos to install an "
140 "autotest client from")
141 pkgmgr = packages.PackageManager(autodir, hostname=host.hostname,
142 repo_urls=repos,
143 do_locking=False,
144 run_function=host.run,
145 run_function_dargs=dict(timeout=600))
146 # The packages dir is used to store all the packages that
147 # are fetched on that client. (for the tests,deps etc.
148 # too apart from the client)
149 pkg_dir = os.path.join(autodir, 'packages')
150 # clean up the autodir except for the packages directory
151 host.run('cd %s && ls | grep -v "^packages$"'
152 ' | xargs rm -rf && rm -rf .[^.]*' % autodir)
153 pkgmgr.install_pkg('autotest', 'client', pkg_dir, autodir,
154 preserve_install_dir=True)
155 self.installed = True
156
157
158 def _install_using_send_file(self, host, autodir):
159 dirs_to_exclude = set(["tests", "site_tests", "deps", "profilers"])
160 light_files = [os.path.join(self.source_material, f)
161 for f in os.listdir(self.source_material)
162 if f not in dirs_to_exclude]
163 host.send_file(light_files, autodir, delete_dest=True)
164
165 # create empty dirs for all the stuff we excluded
166 commands = []
167 for path in dirs_to_exclude:
168 abs_path = os.path.join(autodir, path)
169 abs_path = utils.sh_escape(abs_path)
170 commands.append("mkdir -p '%s'" % abs_path)
171 commands.append("touch '%s'/__init__.py" % abs_path)
172 host.run(';'.join(commands))
173
174
175 def _install(self, host=None, autodir=None, use_autoserv=True,
176 use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000177 """
178 Install autotest. If get() was not called previously, an
179 attempt will be made to install from the autotest svn
180 repository.
mbligh9a3f5e52008-05-28 21:21:43 +0000181
mblighbccad482009-08-24 22:08:31 +0000182 @param host A Host instance on which autotest will be installed
183 @param autodir Location on the remote host to install to
mblighb8aa75b2009-09-18 16:50:37 +0000184 @param use_autoserv Enable install modes that depend on the client
mblighbccad482009-08-24 22:08:31 +0000185 running with the autoserv harness
mblighb8aa75b2009-09-18 16:50:37 +0000186 @param use_packaging Enable install modes that use the packaging system
mbligh9a3f5e52008-05-28 21:21:43 +0000187
mblighbccad482009-08-24 22:08:31 +0000188 @exception AutoservError if a tarball was not specified and
189 the target host does not have svn installed in its path
190 """
jadmanski0afbb632008-06-06 21:10:57 +0000191 if not host:
192 host = self.host
193 if not self.got:
194 self.get()
195 host.wait_up(timeout=30)
196 host.setup()
showardb18134f2009-03-20 20:52:18 +0000197 logging.info("Installing autotest on %s", host.hostname)
mbligh40f122a2007-11-03 23:08:46 +0000198
jadmanski54f90af2008-10-10 16:20:55 +0000199 # set up the autotest directory on the remote machine
200 if not autodir:
showardad812bf2009-10-20 23:49:56 +0000201 autodir = self.get_install_dir(host)
202 logging.info('Using installation dir %s', autodir)
mbligh0562e652008-08-20 20:11:45 +0000203 host.set_autodir(autodir)
jadmanski3c236942009-03-04 17:51:26 +0000204 host.run('mkdir -p %s' % utils.sh_escape(autodir))
mbligh40f122a2007-11-03 23:08:46 +0000205
jadmanski1c3c07b2009-03-03 23:29:36 +0000206 # make sure there are no files in $AUTODIR/results
207 results_path = os.path.join(autodir, 'results')
jadmanski3c236942009-03-04 17:51:26 +0000208 host.run('rm -rf %s/*' % utils.sh_escape(results_path),
jadmanski1c3c07b2009-03-03 23:29:36 +0000209 ignore_status=True)
210
mblighc5ddfd12008-08-04 17:15:00 +0000211 # Fetch the autotest client from the nearest repository
mblighb8aa75b2009-09-18 16:50:37 +0000212 if use_packaging:
213 try:
214 self._install_using_packaging(host, autodir)
215 return
Eric Li6f27d4f2010-09-29 10:55:17 -0700216 except (error.PackageInstallError, error.AutoservRunError,
217 global_config.ConfigError), e:
mblighb8aa75b2009-09-18 16:50:37 +0000218 logging.info("Could not install autotest using the packaging "
Eric Li6f27d4f2010-09-29 10:55:17 -0700219 "system: %s. Trying other methods", e)
mblighc5ddfd12008-08-04 17:15:00 +0000220
jadmanski0afbb632008-06-06 21:10:57 +0000221 # try to install from file or directory
222 if self.source_material:
jadmanski69bdaac2010-07-28 16:27:20 +0000223 c = global_config.global_config
224 supports_autoserv_packaging = c.get_config_value(
225 "PACKAGES", "serve_packages_from_autoserv", type=bool)
226 # Copy autotest recursively
227 if supports_autoserv_packaging and use_autoserv:
228 self._install_using_send_file(host, autodir)
jadmanski0afbb632008-06-06 21:10:57 +0000229 else:
jadmanski69bdaac2010-07-28 16:27:20 +0000230 host.send_file(self.source_material, autodir, delete_dest=True)
showardb18134f2009-03-20 20:52:18 +0000231 logging.info("Installation of autotest completed")
jadmanski0afbb632008-06-06 21:10:57 +0000232 self.installed = True
233 return
mbligh91334902007-09-28 01:47:59 +0000234
jadmanski0afbb632008-06-06 21:10:57 +0000235 # if that fails try to install using svn
236 if utils.run('which svn').exit_status:
mbligh78bf5352008-07-11 20:27:36 +0000237 raise error.AutoservError('svn not found on target machine: %s'
238 % host.name)
jadmanski0afbb632008-06-06 21:10:57 +0000239 try:
mbligh78bf5352008-07-11 20:27:36 +0000240 host.run('svn checkout %s %s' % (AUTOTEST_SVN, autodir))
jadmanski0afbb632008-06-06 21:10:57 +0000241 except error.AutoservRunError, e:
mbligh78bf5352008-07-11 20:27:36 +0000242 host.run('svn checkout %s %s' % (AUTOTEST_HTTP, autodir))
showardb18134f2009-03-20 20:52:18 +0000243 logging.info("Installation of autotest completed")
jadmanski0afbb632008-06-06 21:10:57 +0000244 self.installed = True
mbligh91334902007-09-28 01:47:59 +0000245
246
jadmanski7c7aff32009-03-25 22:43:07 +0000247 def uninstall(self, host=None):
248 """
249 Uninstall (i.e. delete) autotest. Removes the autotest client install
250 from the specified host.
251
252 @params host a Host instance from which the client will be removed
253 """
254 if not self.installed:
255 return
256 if not host:
257 host = self.host
258 autodir = host.get_autodir()
259 if not autodir:
260 return
261
262 # perform the actual uninstall
263 host.run("rm -rf %s" % utils.sh_escape(autodir), ignore_status=True)
264 host.set_autodir(None)
265 self.installed = False
266
267
jadmanski0afbb632008-06-06 21:10:57 +0000268 def get(self, location = None):
269 if not location:
270 location = os.path.join(self.serverdir, '../client')
271 location = os.path.abspath(location)
272 # If there's stuff run on our client directory already, it
273 # can cause problems. Try giving it a quick clean first.
274 cwd = os.getcwd()
275 os.chdir(location)
showard4b976072009-10-20 23:50:08 +0000276 try:
277 utils.system('tools/make_clean', ignore_status=True)
278 finally:
279 os.chdir(cwd)
jadmanski0afbb632008-06-06 21:10:57 +0000280 super(BaseAutotest, self).get(location)
281 self.got = True
mblighdcd57a82007-07-11 23:06:47 +0000282
283
mblighe7d9c602009-07-02 19:02:33 +0000284 def run(self, control_file, results_dir='.', host=None, timeout=None,
285 tag=None, parallel_flag=False, background=False,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800286 client_disconnect_timeout=1800):
jadmanski0afbb632008-06-06 21:10:57 +0000287 """
288 Run an autotest job on the remote machine.
mbligh9a3f5e52008-05-28 21:21:43 +0000289
mblighe7d9c602009-07-02 19:02:33 +0000290 @param control_file: An open file-like-obj of the control file.
291 @param results_dir: A str path where the results should be stored
292 on the local filesystem.
293 @param host: A Host instance on which the control file should
294 be run.
295 @param timeout: Maximum number of seconds to wait for the run or None.
296 @param tag: Tag name for the client side instance of autotest.
297 @param parallel_flag: Flag set when multiple jobs are run at the
298 same time.
299 @param background: Indicates that the client should be launched as
300 a background job; the code calling run will be responsible
301 for monitoring the client and collecting the results.
302 @param client_disconnect_timeout: Seconds to wait for the remote host
303 to come back after a reboot. [default: 30 minutes]
mblighe7d9c602009-07-02 19:02:33 +0000304
305 @raises AutotestRunError: If there is a problem executing
306 the control file.
jadmanski0afbb632008-06-06 21:10:57 +0000307 """
308 host = self._get_host_and_setup(host)
309 results_dir = os.path.abspath(results_dir)
mblighc1cbc992008-05-27 20:01:45 +0000310
jadmanski0afbb632008-06-06 21:10:57 +0000311 if tag:
312 results_dir = os.path.join(results_dir, tag)
mblighc1cbc992008-05-27 20:01:45 +0000313
mblighb3c0c912008-11-27 00:32:45 +0000314 atrun = _Run(host, results_dir, tag, parallel_flag, background)
jadmanski6dadd832009-02-05 23:39:27 +0000315 self._do_run(control_file, results_dir, host, atrun, timeout,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800316 client_disconnect_timeout)
mblighd8b39252008-03-20 21:15:03 +0000317
318
jadmanski0afbb632008-06-06 21:10:57 +0000319 def _get_host_and_setup(self, host):
320 if not host:
321 host = self.host
322 if not self.installed:
323 self.install(host)
mbligh91334902007-09-28 01:47:59 +0000324
jadmanski0afbb632008-06-06 21:10:57 +0000325 host.wait_up(timeout=30)
326 return host
mblighd8b39252008-03-20 21:15:03 +0000327
328
jadmanski6dadd832009-02-05 23:39:27 +0000329 def _do_run(self, control_file, results_dir, host, atrun, timeout,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800330 client_disconnect_timeout):
jadmanski0afbb632008-06-06 21:10:57 +0000331 try:
332 atrun.verify_machine()
333 except:
showardb18134f2009-03-20 20:52:18 +0000334 logging.error("Verify failed on %s. Reinstalling autotest",
335 host.hostname)
jadmanski0afbb632008-06-06 21:10:57 +0000336 self.install(host)
337 atrun.verify_machine()
338 debug = os.path.join(results_dir, 'debug')
339 try:
340 os.makedirs(debug)
mbligh09108442008-10-15 16:27:38 +0000341 except Exception:
jadmanski0afbb632008-06-06 21:10:57 +0000342 pass
mbligh9a3f5e52008-05-28 21:21:43 +0000343
mbligh09108442008-10-15 16:27:38 +0000344 delete_file_list = [atrun.remote_control_file,
345 atrun.remote_control_file + '.state',
346 atrun.manual_control_file,
347 atrun.manual_control_file + '.state']
348 cmd = ';'.join('rm -f ' + control for control in delete_file_list)
349 host.run(cmd, ignore_status=True)
mbligh9a3f5e52008-05-28 21:21:43 +0000350
jadmanski0afbb632008-06-06 21:10:57 +0000351 tmppath = utils.get(control_file)
mblighc5ddfd12008-08-04 17:15:00 +0000352
jadmanskicb0e1612009-02-27 18:03:10 +0000353 # build up the initialization prologue for the control file
354 prologue_lines = []
jadmanski23afbec2008-09-17 18:12:07 +0000355
mbligh2f076832010-03-30 17:08:20 +0000356 # Add the additional user arguments
jadmanski808f4b12010-04-09 22:30:31 +0000357 prologue_lines.append("args = %r\n" % self.job.args)
mbligh2f076832010-03-30 17:08:20 +0000358
mbligh09108442008-10-15 16:27:38 +0000359 # If the packaging system is being used, add the repository list.
mblighddc9a402010-01-15 20:33:34 +0000360 repos = None
mblighc5ddfd12008-08-04 17:15:00 +0000361 try:
mblighc5ddfd12008-08-04 17:15:00 +0000362 c = global_config.global_config
363 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list)
jadmanskiede7e242009-08-10 15:43:33 +0000364 repos.reverse() # high priority packages should be added last
mbligh76d19f72008-10-15 16:24:43 +0000365 pkgmgr = packages.PackageManager('autotest', hostname=host.hostname,
366 repo_urls=repos)
jadmanskib1a51132009-08-07 16:45:50 +0000367 prologue_lines.append('job.add_repository(%s)\n' % repos)
mblighc5ddfd12008-08-04 17:15:00 +0000368 except global_config.ConfigError, e:
mblighddc9a402010-01-15 20:33:34 +0000369 # If repos is defined packaging is enabled so log the error
370 if repos:
371 logging.error(e)
mblighc5ddfd12008-08-04 17:15:00 +0000372
jadmanskie2eef7b2009-03-03 23:55:13 +0000373 # on full-size installs, turn on any profilers the server is using
jadmanski27b52912009-08-14 17:09:15 +0000374 if not atrun.background:
jadmanskie2eef7b2009-03-03 23:55:13 +0000375 running_profilers = host.job.profilers.add_log.iteritems()
376 for profiler, (args, dargs) in running_profilers:
377 call_args = [repr(profiler)]
378 call_args += [repr(arg) for arg in args]
379 call_args += ["%s=%r" % item for item in dargs.iteritems()]
380 prologue_lines.append("job.profilers.add(%s)\n"
381 % ", ".join(call_args))
382 cfile = "".join(prologue_lines)
383
mbligh09108442008-10-15 16:27:38 +0000384 cfile += open(tmppath).read()
385 open(tmppath, "w").write(cfile)
mblighc5ddfd12008-08-04 17:15:00 +0000386
jadmanskic09fc152008-10-15 17:56:59 +0000387 # Create and copy state file to remote_control_file + '.state'
mblighfc3da5b2010-01-06 18:37:22 +0000388 state_file = host.job.preprocess_client_state()
mblighfbf73ae2009-12-19 05:22:42 +0000389 host.send_file(state_file, atrun.remote_control_file + '.init.state')
jadmanskic09fc152008-10-15 17:56:59 +0000390 os.remove(state_file)
391
mblighc5ddfd12008-08-04 17:15:00 +0000392 # Copy control_file to remote_control_file on the host
jadmanski0afbb632008-06-06 21:10:57 +0000393 host.send_file(tmppath, atrun.remote_control_file)
394 if os.path.abspath(tmppath) != os.path.abspath(control_file):
395 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000396
jadmanski6bb32d72009-03-19 20:25:24 +0000397 atrun.execute_control(
jadmanski6dadd832009-02-05 23:39:27 +0000398 timeout=timeout,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800399 client_disconnect_timeout=client_disconnect_timeout)
jadmanski23afbec2008-09-17 18:12:07 +0000400
401
jadmanski0afbb632008-06-06 21:10:57 +0000402 def run_timed_test(self, test_name, results_dir='.', host=None,
jadmanskic98c4702009-01-05 15:50:06 +0000403 timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000404 """
405 Assemble a tiny little control file to just run one test,
406 and run it as an autotest client-side test
407 """
408 if not host:
409 host = self.host
410 if not self.installed:
411 self.install(host)
412 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
413 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
414 control = "job.run_test(%s)\n" % cmd
jadmanskic98c4702009-01-05 15:50:06 +0000415 self.run(control, results_dir, host, timeout=timeout)
mbligh0e4613b2007-10-29 16:55:07 +0000416
417
jadmanskic98c4702009-01-05 15:50:06 +0000418 def run_test(self, test_name, results_dir='.', host=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000419 self.run_timed_test(test_name, results_dir, host, timeout=None,
jadmanskic98c4702009-01-05 15:50:06 +0000420 *args, **dargs)
mblighd54832b2007-07-25 16:46:56 +0000421
422
jadmanski69bdaac2010-07-28 16:27:20 +0000423class _BaseRun(object):
jadmanski0afbb632008-06-06 21:10:57 +0000424 """
425 Represents a run of autotest control file. This class maintains
426 all the state necessary as an autotest control file is executed.
mblighdcd57a82007-07-11 23:06:47 +0000427
jadmanski0afbb632008-06-06 21:10:57 +0000428 It is not intended to be used directly, rather control files
429 should be run using the run method in Autotest.
430 """
mblighb3c0c912008-11-27 00:32:45 +0000431 def __init__(self, host, results_dir, tag, parallel_flag, background):
jadmanski0afbb632008-06-06 21:10:57 +0000432 self.host = host
433 self.results_dir = results_dir
434 self.env = host.env
435 self.tag = tag
436 self.parallel_flag = parallel_flag
mblighb3c0c912008-11-27 00:32:45 +0000437 self.background = background
showardad812bf2009-10-20 23:49:56 +0000438 self.autodir = Autotest.get_installed_autodir(self.host)
mbligh78bf5352008-07-11 20:27:36 +0000439 control = os.path.join(self.autodir, 'control')
jadmanski0afbb632008-06-06 21:10:57 +0000440 if tag:
mbligh78bf5352008-07-11 20:27:36 +0000441 control += '.' + tag
442 self.manual_control_file = control
443 self.remote_control_file = control + '.autoserv'
lmr6d08b3c2009-11-18 19:26:38 +0000444 self.config_file = os.path.join(self.autodir, 'global_config.ini')
mblighdc735a22007-08-02 16:54:37 +0000445
446
jadmanski0afbb632008-06-06 21:10:57 +0000447 def verify_machine(self):
448 binary = os.path.join(self.autodir, 'bin/autotest')
449 try:
450 self.host.run('ls %s > /dev/null 2>&1' % binary)
451 except:
lmrd6d27ed2009-12-08 19:58:33 +0000452 raise error.AutoservInstallError(
453 "Autotest does not appear to be installed")
mblighdc735a22007-08-02 16:54:37 +0000454
jadmanski0afbb632008-06-06 21:10:57 +0000455 if not self.parallel_flag:
456 tmpdir = os.path.join(self.autodir, 'tmp')
457 download = os.path.join(self.autodir, 'tests/download')
458 self.host.run('umount %s' % tmpdir, ignore_status=True)
459 self.host.run('umount %s' % download, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000460
jadmanski6dadd832009-02-05 23:39:27 +0000461
462 def get_base_cmd_args(self, section):
showard234b2962009-07-28 20:02:30 +0000463 args = ['--verbose']
jadmanski0afbb632008-06-06 21:10:57 +0000464 if section > 0:
jadmanski6dadd832009-02-05 23:39:27 +0000465 args.append('-c')
jadmanski0afbb632008-06-06 21:10:57 +0000466 if self.tag:
jadmanski6dadd832009-02-05 23:39:27 +0000467 args.append('-t %s' % self.tag)
jadmanski0afbb632008-06-06 21:10:57 +0000468 if self.host.job.use_external_logging():
jadmanski6dadd832009-02-05 23:39:27 +0000469 args.append('-l')
mblighce955fc2009-08-24 21:59:02 +0000470 if self.host.hostname:
471 args.append('--hostname=%s' % self.host.hostname)
mbligh0d0f67d2009-11-06 03:15:03 +0000472 args.append('--user=%s' % self.host.job.user)
mblighce955fc2009-08-24 21:59:02 +0000473
jadmanski6dadd832009-02-05 23:39:27 +0000474 args.append(self.remote_control_file)
475 return args
476
477
478 def get_background_cmd(self, section):
479 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotest_client')]
480 cmd += self.get_base_cmd_args(section)
jadmanski69bdaac2010-07-28 16:27:20 +0000481 cmd += ['>/dev/null', '2>/dev/null', '&']
jadmanski6dadd832009-02-05 23:39:27 +0000482 return ' '.join(cmd)
483
484
485 def get_daemon_cmd(self, section, monitor_dir):
486 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotestd'),
487 monitor_dir, '-H autoserv']
488 cmd += self.get_base_cmd_args(section)
jadmanski69bdaac2010-07-28 16:27:20 +0000489 cmd += ['>/dev/null', '2>/dev/null', '&']
jadmanski6dadd832009-02-05 23:39:27 +0000490 return ' '.join(cmd)
491
492
493 def get_monitor_cmd(self, monitor_dir, stdout_read, stderr_read):
494 cmd = [os.path.join(self.autodir, 'bin', 'autotestd_monitor'),
495 monitor_dir, str(stdout_read), str(stderr_read)]
jadmanski0afbb632008-06-06 21:10:57 +0000496 return ' '.join(cmd)
mblighadf2aab2007-11-29 18:16:43 +0000497
mblighd8b39252008-03-20 21:15:03 +0000498
jadmanski4d03cf62010-03-04 18:32:28 +0000499 def get_client_log(self):
500 """Find what the "next" client.* prefix should be
501
502 @returns A string of the form client.INTEGER that should be prefixed
503 to all client debug log files.
504 """
505 max_digit = -1
506 debug_dir = os.path.join(self.results_dir, 'debug')
507 client_logs = glob.glob(os.path.join(debug_dir, 'client.*.*'))
508 for log in client_logs:
509 _, number, _ = log.split('.', 2)
510 if number.isdigit():
511 max_digit = max(max_digit, int(number))
512 return 'client.%d' % (max_digit + 1)
513
514
515 def copy_client_config_file(self, client_log_prefix=None):
516 """
517 Create and copy the client config file based on the server config.
518
519 @param client_log_prefix: Optional prefix to prepend to log files.
520 """
521 client_config_file = self._create_client_config_file(client_log_prefix)
522 self.host.send_file(client_config_file, self.config_file)
523 os.remove(client_config_file)
524
525
526 def _create_client_config_file(self, client_log_prefix=None):
527 """
528 Create a temporary file with the [CLIENT] section configuration values
529 taken from the server global_config.ini.
530
531 @param client_log_prefix: Optional prefix to prepend to log files.
532
533 @return: Path of the temporary file generated.
534 """
535 config = global_config.global_config.get_section_values('CLIENT')
536 if client_log_prefix:
537 config.set('CLIENT', 'default_logging_name', client_log_prefix)
538 return self._create_aux_file(config.write)
539
540
541 def _create_aux_file(self, func, *args):
542 """
543 Creates a temporary file and writes content to it according to a
544 content creation function. The file object is appended to *args, which
545 is then passed to the content creation function
546
547 @param func: Function that will be used to write content to the
548 temporary file.
549 @param *args: List of parameters that func takes.
550 @return: Path to the temporary file that was created.
551 """
552 fd, path = tempfile.mkstemp(dir=self.host.job.tmpdir)
553 aux_file = os.fdopen(fd, "w")
554 try:
555 list_args = list(args)
556 list_args.append(aux_file)
557 func(*list_args)
558 finally:
559 aux_file.close()
560 return path
mblighd8b39252008-03-20 21:15:03 +0000561
562
jadmanskib264ed02009-01-12 23:54:27 +0000563 @staticmethod
564 def is_client_job_finished(last_line):
565 return bool(re.match(r'^END .*\t----\t----\t.*$', last_line))
566
567
568 @staticmethod
569 def is_client_job_rebooting(last_line):
570 return bool(re.match(r'^\t*GOOD\t----\treboot\.start.*$', last_line))
571
572
jadmanskia61edad2009-05-21 22:17:49 +0000573 def log_unexpected_abort(self, stderr_redirector):
574 stderr_redirector.flush_all_buffers()
jadmanskib264ed02009-01-12 23:54:27 +0000575 msg = "Autotest client terminated unexpectedly"
576 self.host.job.record("END ABORT", None, None, msg)
577
578
jadmanski6dadd832009-02-05 23:39:27 +0000579 def _execute_in_background(self, section, timeout):
580 full_cmd = self.get_background_cmd(section)
581 devnull = open(os.devnull, "w")
mblighd8b39252008-03-20 21:15:03 +0000582
jadmanski4d03cf62010-03-04 18:32:28 +0000583 self.copy_client_config_file(self.get_client_log())
584
mbligh0d0f67d2009-11-06 03:15:03 +0000585 self.host.job.push_execution_context(self.results_dir)
jadmanski0afbb632008-06-06 21:10:57 +0000586 try:
jadmanski0afbb632008-06-06 21:10:57 +0000587 result = self.host.run(full_cmd, ignore_status=True,
588 timeout=timeout,
jadmanski6dadd832009-02-05 23:39:27 +0000589 stdout_tee=devnull,
590 stderr_tee=devnull)
jadmanski0afbb632008-06-06 21:10:57 +0000591 finally:
mbligh0d0f67d2009-11-06 03:15:03 +0000592 self.host.job.pop_execution_context()
jadmanski6dadd832009-02-05 23:39:27 +0000593
594 return result
595
596
597 @staticmethod
598 def _strip_stderr_prologue(stderr):
599 """Strips the 'standard' prologue that get pre-pended to every
600 remote command and returns the text that was actually written to
601 stderr by the remote command."""
602 stderr_lines = stderr.split("\n")[1:]
603 if not stderr_lines:
604 return ""
605 elif stderr_lines[0].startswith("NOTE: autotestd_monitor"):
606 del stderr_lines[0]
607 return "\n".join(stderr_lines)
608
609
610 def _execute_daemon(self, section, timeout, stderr_redirector,
611 client_disconnect_timeout):
612 monitor_dir = self.host.get_tmp_dir()
613 daemon_cmd = self.get_daemon_cmd(section, monitor_dir)
jadmanski4d03cf62010-03-04 18:32:28 +0000614
615 # grab the location for the server-side client log file
616 client_log_prefix = self.get_client_log()
617 client_log_path = os.path.join(self.results_dir, 'debug',
618 client_log_prefix + '.log')
619 client_log = open(client_log_path, 'w', 0)
620 self.copy_client_config_file(client_log_prefix)
jadmanski6dadd832009-02-05 23:39:27 +0000621
622 stdout_read = stderr_read = 0
mbligh0d0f67d2009-11-06 03:15:03 +0000623 self.host.job.push_execution_context(self.results_dir)
jadmanski6dadd832009-02-05 23:39:27 +0000624 try:
625 self.host.run(daemon_cmd, ignore_status=True, timeout=timeout)
jadmanski91d56a92009-04-01 15:20:40 +0000626 disconnect_warnings = []
jadmanski6dadd832009-02-05 23:39:27 +0000627 while True:
628 monitor_cmd = self.get_monitor_cmd(monitor_dir, stdout_read,
629 stderr_read)
630 try:
631 result = self.host.run(monitor_cmd, ignore_status=True,
632 timeout=timeout,
633 stdout_tee=client_log,
634 stderr_tee=stderr_redirector)
635 except error.AutoservRunError, e:
636 result = e.result_obj
637 result.exit_status = None
jadmanski91d56a92009-04-01 15:20:40 +0000638 disconnect_warnings.append(e.description)
639
jadmanski6dadd832009-02-05 23:39:27 +0000640 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000641 "Autotest client was disconnected: %s" % e.description,
642 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000643 except error.AutoservSSHTimeout:
644 result = utils.CmdResult(monitor_cmd, "", "", None, 0)
645 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000646 "Attempt to connect to Autotest client timed out",
647 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000648
649 stdout_read += len(result.stdout)
650 stderr_read += len(self._strip_stderr_prologue(result.stderr))
651
652 if result.exit_status is not None:
653 return result
654 elif not self.host.wait_up(client_disconnect_timeout):
655 raise error.AutoservSSHTimeout(
656 "client was disconnected, reconnect timed out")
657 finally:
jadmanski4d03cf62010-03-04 18:32:28 +0000658 client_log.close()
mbligh0d0f67d2009-11-06 03:15:03 +0000659 self.host.job.pop_execution_context()
jadmanski6dadd832009-02-05 23:39:27 +0000660
661
662 def execute_section(self, section, timeout, stderr_redirector,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800663 client_disconnect_timeout):
showardb18134f2009-03-20 20:52:18 +0000664 logging.info("Executing %s/bin/autotest %s/control phase %d",
665 self.autodir, self.autodir, section)
jadmanski6dadd832009-02-05 23:39:27 +0000666
667 if self.background:
668 result = self._execute_in_background(section, timeout)
669 else:
670 result = self._execute_daemon(section, timeout, stderr_redirector,
671 client_disconnect_timeout)
672
673 last_line = stderr_redirector.last_line
mbligh2bf2db62007-11-27 00:53:18 +0000674
jadmanskib264ed02009-01-12 23:54:27 +0000675 # check if we failed hard enough to warrant an exception
jadmanski0afbb632008-06-06 21:10:57 +0000676 if result.exit_status == 1:
jadmanskib264ed02009-01-12 23:54:27 +0000677 err = error.AutotestRunError("client job was aborted")
678 elif not self.background and not result.stderr:
679 err = error.AutotestRunError(
jadmanskie4130532009-03-17 18:01:28 +0000680 "execute_section %s failed to return anything\n"
681 "stdout:%s\n" % (section, result.stdout))
jadmanskib264ed02009-01-12 23:54:27 +0000682 else:
683 err = None
mbligh0e4613b2007-10-29 16:55:07 +0000684
jadmanskib264ed02009-01-12 23:54:27 +0000685 # log something if the client failed AND never finished logging
Dale Curtis9285ddf2011-01-05 11:47:24 -0800686 if err and not self.is_client_job_finished(last_line):
jadmanskia61edad2009-05-21 22:17:49 +0000687 self.log_unexpected_abort(stderr_redirector)
jadmanskib264ed02009-01-12 23:54:27 +0000688
689 if err:
690 raise err
691 else:
692 return stderr_redirector.last_line
jadmanski4600e342008-10-29 22:54:00 +0000693
694
jadmanskic0354912010-01-12 15:57:29 +0000695 def _wait_for_reboot(self, old_boot_id):
showardb18134f2009-03-20 20:52:18 +0000696 logging.info("Client is rebooting")
697 logging.info("Waiting for client to halt")
jadmanskic0354912010-01-12 15:57:29 +0000698 if not self.host.wait_down(HALT_TIME, old_boot_id=old_boot_id):
jadmanski4600e342008-10-29 22:54:00 +0000699 err = "%s failed to shutdown after %d"
700 err %= (self.host.hostname, HALT_TIME)
701 raise error.AutotestRunError(err)
showardb18134f2009-03-20 20:52:18 +0000702 logging.info("Client down, waiting for restart")
jadmanski4600e342008-10-29 22:54:00 +0000703 if not self.host.wait_up(BOOT_TIME):
704 # since reboot failed
705 # hardreset the machine once if possible
706 # before failing this control file
707 warning = "%s did not come back up, hard resetting"
708 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000709 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000710 try:
711 self.host.hardreset(wait=False)
mblighd99d3b272008-12-22 14:41:27 +0000712 except (AttributeError, error.AutoservUnsupportedError):
jadmanski4600e342008-10-29 22:54:00 +0000713 warning = "Hard reset unsupported on %s"
714 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000715 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000716 raise error.AutotestRunError("%s failed to boot after %ds" %
717 (self.host.hostname, BOOT_TIME))
718 self.host.reboot_followup()
mblighdc735a22007-08-02 16:54:37 +0000719
720
Dale Curtis9285ddf2011-01-05 11:47:24 -0800721 def execute_control(self, timeout=None, client_disconnect_timeout=None):
jadmanski6bb32d72009-03-19 20:25:24 +0000722 if not self.background:
723 collector = log_collector(self.host, self.tag, self.results_dir)
724 hostname = self.host.hostname
725 remote_results = collector.client_results_dir
726 local_results = collector.server_results_dir
727 self.host.job.add_client_log(hostname, remote_results,
728 local_results)
jadmanski52053632010-06-11 21:08:10 +0000729 job_record_context = self.host.job.get_record_context()
jadmanski6bb32d72009-03-19 20:25:24 +0000730
jadmanski0afbb632008-06-06 21:10:57 +0000731 section = 0
jadmanski4600e342008-10-29 22:54:00 +0000732 start_time = time.time()
733
jadmanski043e1132008-11-19 17:10:32 +0000734 logger = client_logger(self.host, self.tag, self.results_dir)
jadmanski4600e342008-10-29 22:54:00 +0000735 try:
736 while not timeout or time.time() < start_time + timeout:
737 if timeout:
738 section_timeout = start_time + timeout - time.time()
739 else:
740 section_timeout = None
jadmanskic0354912010-01-12 15:57:29 +0000741 boot_id = self.host.get_boot_id()
jadmanski4600e342008-10-29 22:54:00 +0000742 last = self.execute_section(section, section_timeout,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800743 logger, client_disconnect_timeout)
mblighb3c0c912008-11-27 00:32:45 +0000744 if self.background:
745 return
jadmanski4600e342008-10-29 22:54:00 +0000746 section += 1
jadmanskib264ed02009-01-12 23:54:27 +0000747 if self.is_client_job_finished(last):
showardb18134f2009-03-20 20:52:18 +0000748 logging.info("Client complete")
jadmanski4600e342008-10-29 22:54:00 +0000749 return
jadmanskib264ed02009-01-12 23:54:27 +0000750 elif self.is_client_job_rebooting(last):
jadmanski79ab9282008-11-11 17:53:12 +0000751 try:
jadmanskic0354912010-01-12 15:57:29 +0000752 self._wait_for_reboot(boot_id)
jadmanski79ab9282008-11-11 17:53:12 +0000753 except error.AutotestRunError, e:
jadmanski043e1132008-11-19 17:10:32 +0000754 self.host.job.record("ABORT", None, "reboot", str(e))
755 self.host.job.record("END ABORT", None, None, str(e))
jadmanski79ab9282008-11-11 17:53:12 +0000756 raise
jadmanski4600e342008-10-29 22:54:00 +0000757 continue
758
759 # if we reach here, something unexpected happened
jadmanskia61edad2009-05-21 22:17:49 +0000760 self.log_unexpected_abort(logger)
jadmanski4600e342008-10-29 22:54:00 +0000761
762 # give the client machine a chance to recover from a crash
763 self.host.wait_up(CRASH_RECOVERY_TIME)
764 msg = ("Aborting - unexpected final status message from "
jadmanskib1de5492010-06-11 14:29:01 +0000765 "client on %s: %s\n") % (self.host.hostname, last)
jadmanski4600e342008-10-29 22:54:00 +0000766 raise error.AutotestRunError(msg)
767 finally:
jadmanski043e1132008-11-19 17:10:32 +0000768 logger.close()
jadmanski6bb32d72009-03-19 20:25:24 +0000769 if not self.background:
770 collector.collect_client_job_results()
jadmanski4d03cf62010-03-04 18:32:28 +0000771 collector.remove_redundant_client_logs()
mblighfc3da5b2010-01-06 18:37:22 +0000772 state_file = os.path.basename(self.remote_control_file
773 + '.state')
774 state_path = os.path.join(self.results_dir, state_file)
775 self.host.job.postprocess_client_state(state_path)
jadmanski6bb32d72009-03-19 20:25:24 +0000776 self.host.job.remove_client_log(hostname, remote_results,
777 local_results)
jadmanski52053632010-06-11 21:08:10 +0000778 job_record_context.restore()
mblighdcd57a82007-07-11 23:06:47 +0000779
jadmanski0afbb632008-06-06 21:10:57 +0000780 # should only get here if we timed out
781 assert timeout
782 raise error.AutotestTimeoutError()
mbligh0e4613b2007-10-29 16:55:07 +0000783
mblighdcd57a82007-07-11 23:06:47 +0000784
jadmanski043e1132008-11-19 17:10:32 +0000785class log_collector(object):
786 def __init__(self, host, client_tag, results_dir):
787 self.host = host
788 if not client_tag:
789 client_tag = "default"
790 self.client_results_dir = os.path.join(host.get_autodir(), "results",
791 client_tag)
792 self.server_results_dir = results_dir
793
794
795 def collect_client_job_results(self):
796 """ A method that collects all the current results of a running
797 client job into the results dir. By default does nothing as no
798 client job is running, but when running a client job you can override
799 this with something that will actually do something. """
800
801 # make an effort to wait for the machine to come up
802 try:
803 self.host.wait_up(timeout=30)
804 except error.AutoservError:
805 # don't worry about any errors, we'll try and
806 # get the results anyway
807 pass
808
jadmanski043e1132008-11-19 17:10:32 +0000809 # Copy all dirs in default to results_dir
810 try:
jadmanski043e1132008-11-19 17:10:32 +0000811 self.host.get_file(self.client_results_dir + '/',
mbligh45561782009-05-11 21:14:34 +0000812 self.server_results_dir, preserve_symlinks=True)
jadmanski043e1132008-11-19 17:10:32 +0000813 except Exception:
814 # well, don't stop running just because we couldn't get logs
showardb18134f2009-03-20 20:52:18 +0000815 e_msg = "Unexpected error copying test result logs, continuing ..."
816 logging.error(e_msg)
jadmanski043e1132008-11-19 17:10:32 +0000817 traceback.print_exc(file=sys.stdout)
818
819
jadmanski4d03cf62010-03-04 18:32:28 +0000820 def remove_redundant_client_logs(self):
821 """Remove client.*.log files in favour of client.*.DEBUG files."""
822 debug_dir = os.path.join(self.server_results_dir, 'debug')
823 debug_files = [f for f in os.listdir(debug_dir)
824 if re.search(r'^client\.\d+\.DEBUG$', f)]
825 for debug_file in debug_files:
826 log_file = debug_file.replace('DEBUG', 'log')
827 log_file = os.path.join(debug_dir, log_file)
828 if os.path.exists(log_file):
829 os.remove(log_file)
830
831
jadmanski043e1132008-11-19 17:10:32 +0000832# a file-like object for catching stderr from an autotest client and
833# extracting status logs from it
834class client_logger(object):
835 """Partial file object to write to both stdout and
836 the status log file. We only implement those methods
837 utils.run() actually calls.
jadmanski043e1132008-11-19 17:10:32 +0000838 """
839 status_parser = re.compile(r"^AUTOTEST_STATUS:([^:]*):(.*)$")
840 test_complete_parser = re.compile(r"^AUTOTEST_TEST_COMPLETE:(.*)$")
jadmanskib1a51132009-08-07 16:45:50 +0000841 fetch_package_parser = re.compile(
842 r"^AUTOTEST_FETCH_PACKAGE:([^:]*):([^:]*):(.*)$")
jadmanski043e1132008-11-19 17:10:32 +0000843 extract_indent = re.compile(r"^(\t*).*$")
jadmanskiefe4ebf2009-05-21 22:12:30 +0000844 extract_timestamp = re.compile(r".*\ttimestamp=(\d+)\t.*$")
jadmanski043e1132008-11-19 17:10:32 +0000845
846 def __init__(self, host, tag, server_results_dir):
847 self.host = host
848 self.job = host.job
849 self.log_collector = log_collector(host, tag, server_results_dir)
850 self.leftover = ""
851 self.last_line = ""
852 self.logs = {}
jadmanskiefe4ebf2009-05-21 22:12:30 +0000853
854
jadmanski043e1132008-11-19 17:10:32 +0000855 def _process_log_dict(self, log_dict):
856 log_list = log_dict.pop("logs", [])
857 for key in sorted(log_dict.iterkeys()):
858 log_list += self._process_log_dict(log_dict.pop(key))
859 return log_list
860
861
862 def _process_logs(self):
863 """Go through the accumulated logs in self.log and print them
864 out to stdout and the status log. Note that this processes
865 logs in an ordering where:
866
867 1) logs to different tags are never interleaved
868 2) logs to x.y come before logs to x.y.z for all z
869 3) logs to x.y come before x.z whenever y < z
870
871 Note that this will in general not be the same as the
872 chronological ordering of the logs. However, if a chronological
873 ordering is desired that one can be reconstructed from the
874 status log by looking at timestamp lines."""
875 log_list = self._process_log_dict(self.logs)
jadmanski2a89dac2010-06-11 14:32:58 +0000876 for entry in log_list:
877 self.job.record_entry(entry, log_in_subdir=False)
jadmanski043e1132008-11-19 17:10:32 +0000878 if log_list:
jadmanski2a89dac2010-06-11 14:32:58 +0000879 self.last_line = log_list[-1].render()
jadmanski043e1132008-11-19 17:10:32 +0000880
881
882 def _process_quoted_line(self, tag, line):
883 """Process a line quoted with an AUTOTEST_STATUS flag. If the
884 tag is blank then we want to push out all the data we've been
885 building up in self.logs, and then the newest line. If the
886 tag is not blank, then push the line into the logs for handling
887 later."""
jadmanski2a89dac2010-06-11 14:32:58 +0000888 entry = base_job.status_log_entry.parse(line)
889 if entry is None:
890 return # the line contains no status lines
jadmanski043e1132008-11-19 17:10:32 +0000891 if tag == "":
892 self._process_logs()
jadmanski2a89dac2010-06-11 14:32:58 +0000893 self.job.record_entry(entry, log_in_subdir=False)
jadmanski043e1132008-11-19 17:10:32 +0000894 self.last_line = line
895 else:
896 tag_parts = [int(x) for x in tag.split(".")]
897 log_dict = self.logs
898 for part in tag_parts:
899 log_dict = log_dict.setdefault(part, {})
900 log_list = log_dict.setdefault("logs", [])
jadmanski2a89dac2010-06-11 14:32:58 +0000901 log_list.append(entry)
jadmanski043e1132008-11-19 17:10:32 +0000902
903
jadmanskif37df842009-02-11 00:03:26 +0000904 def _process_info_line(self, line):
905 """Check if line is an INFO line, and if it is, interpret any control
906 messages (e.g. enabling/disabling warnings) that it may contain."""
907 match = re.search(r"^\t*INFO\t----\t----(.*)\t[^\t]*$", line)
908 if not match:
909 return # not an INFO line
910 for field in match.group(1).split('\t'):
911 if field.startswith("warnings.enable="):
jadmanski16a7ff72009-04-01 18:19:53 +0000912 func = self.job.warning_manager.enable_warnings
jadmanskif37df842009-02-11 00:03:26 +0000913 elif field.startswith("warnings.disable="):
jadmanski16a7ff72009-04-01 18:19:53 +0000914 func = self.job.warning_manager.disable_warnings
jadmanskif37df842009-02-11 00:03:26 +0000915 else:
916 continue
917 warning_type = field.split("=", 1)[1]
jadmanski16a7ff72009-04-01 18:19:53 +0000918 func(warning_type)
jadmanskif37df842009-02-11 00:03:26 +0000919
920
jadmanski043e1132008-11-19 17:10:32 +0000921 def _process_line(self, line):
922 """Write out a line of data to the appropriate stream. Status
923 lines sent by autotest will be prepended with
924 "AUTOTEST_STATUS", and all other lines are ssh error
925 messages."""
926 status_match = self.status_parser.search(line)
927 test_complete_match = self.test_complete_parser.search(line)
jadmanskib1a51132009-08-07 16:45:50 +0000928 fetch_package_match = self.fetch_package_parser.search(line)
jadmanski043e1132008-11-19 17:10:32 +0000929 if status_match:
930 tag, line = status_match.groups()
jadmanskif37df842009-02-11 00:03:26 +0000931 self._process_info_line(line)
jadmanski043e1132008-11-19 17:10:32 +0000932 self._process_quoted_line(tag, line)
933 elif test_complete_match:
jadmanskifcc0d5d2009-02-12 21:52:54 +0000934 self._process_logs()
jadmanski043e1132008-11-19 17:10:32 +0000935 fifo_path, = test_complete_match.groups()
mbligh060c4712009-12-29 02:43:35 +0000936 try:
937 self.log_collector.collect_client_job_results()
938 self.host.run("echo A > %s" % fifo_path)
939 except Exception:
940 msg = "Post-test log collection failed, continuing anyway"
941 logging.exception(msg)
jadmanskib1a51132009-08-07 16:45:50 +0000942 elif fetch_package_match:
943 pkg_name, dest_path, fifo_path = fetch_package_match.groups()
jadmanskiede7e242009-08-10 15:43:33 +0000944 serve_packages = global_config.global_config.get_config_value(
945 "PACKAGES", "serve_packages_from_autoserv", type=bool)
946 if serve_packages and pkg_name.endswith(".tar.bz2"):
947 try:
948 self._send_tarball(pkg_name, dest_path)
949 except Exception:
950 msg = "Package tarball creation failed, continuing anyway"
951 logging.exception(msg)
mbligh060c4712009-12-29 02:43:35 +0000952 try:
953 self.host.run("echo B > %s" % fifo_path)
954 except Exception:
955 msg = "Package tarball installation failed, continuing anyway"
956 logging.exception(msg)
jadmanski043e1132008-11-19 17:10:32 +0000957 else:
showardb18134f2009-03-20 20:52:18 +0000958 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +0000959
960
jadmanskiede7e242009-08-10 15:43:33 +0000961 def _send_tarball(self, pkg_name, remote_dest):
962 name, pkg_type = self.job.pkgmgr.parse_tarball_name(pkg_name)
963 src_dirs = []
964 if pkg_type == 'test':
mbligh1f572e52010-04-01 17:15:53 +0000965 for test_dir in ['site_tests', 'tests']:
966 src_dir = os.path.join(self.job.clientdir, test_dir, name)
967 if os.path.exists(src_dir):
968 src_dirs += [src_dir]
969 if autoserv_prebuild:
970 prebuild.setup(self.job.clientdir, src_dir)
971 break
jadmanskiede7e242009-08-10 15:43:33 +0000972 elif pkg_type == 'profiler':
973 src_dirs += [os.path.join(self.job.clientdir, 'profilers', name)]
mbligh1f572e52010-04-01 17:15:53 +0000974 if autoserv_prebuild:
975 prebuild.setup(self.job.clientdir, src_dir)
jadmanskiede7e242009-08-10 15:43:33 +0000976 elif pkg_type == 'dep':
977 src_dirs += [os.path.join(self.job.clientdir, 'deps', name)]
978 elif pkg_type == 'client':
979 return # you must already have a client to hit this anyway
980 else:
981 return # no other types are supported
982
983 # iterate over src_dirs until we find one that exists, then tar it
984 for src_dir in src_dirs:
985 if os.path.exists(src_dir):
986 try:
987 logging.info('Bundling %s into %s', src_dir, pkg_name)
988 temp_dir = autotemp.tempdir(unique_id='autoserv-packager',
989 dir=self.job.tmpdir)
990 tarball_path = self.job.pkgmgr.tar_package(
mblighbccad482009-08-24 22:08:31 +0000991 pkg_name, src_dir, temp_dir.name, " .")
jadmanskiede7e242009-08-10 15:43:33 +0000992 self.host.send_file(tarball_path, remote_dest)
993 finally:
994 temp_dir.clean()
995 return
996
997
jadmanski91d56a92009-04-01 15:20:40 +0000998 def log_warning(self, msg, warning_type):
jadmanski6dadd832009-02-05 23:39:27 +0000999 """Injects a WARN message into the current status logging stream."""
jadmanski91d56a92009-04-01 15:20:40 +00001000 timestamp = int(time.time())
1001 if self.job.warning_manager.is_valid(timestamp, warning_type):
jadmanski2a89dac2010-06-11 14:32:58 +00001002 self.job.record('WARN', None, None, {}, msg)
jadmanski6dadd832009-02-05 23:39:27 +00001003
jadmanski043e1132008-11-19 17:10:32 +00001004
1005 def write(self, data):
jadmanski2a89dac2010-06-11 14:32:58 +00001006 # now start processing the existing buffer and the new data
jadmanski043e1132008-11-19 17:10:32 +00001007 data = self.leftover + data
mbligh060c4712009-12-29 02:43:35 +00001008 lines = data.split('\n')
1009 processed_lines = 0
1010 try:
1011 # process all the buffered data except the last line
1012 # ignore the last line since we may not have all of it yet
1013 for line in lines[:-1]:
mbligh060c4712009-12-29 02:43:35 +00001014 self._process_line(line)
1015 processed_lines += 1
1016 finally:
1017 # save any unprocessed lines for future processing
1018 self.leftover = '\n'.join(lines[processed_lines:])
jadmanski043e1132008-11-19 17:10:32 +00001019
1020
1021 def flush(self):
1022 sys.stdout.flush()
1023
1024
jadmanskia61edad2009-05-21 22:17:49 +00001025 def flush_all_buffers(self):
jadmanski043e1132008-11-19 17:10:32 +00001026 if self.leftover:
1027 self._process_line(self.leftover)
jadmanskia61edad2009-05-21 22:17:49 +00001028 self.leftover = ""
jadmanski043e1132008-11-19 17:10:32 +00001029 self._process_logs()
1030 self.flush()
1031
1032
jadmanskia61edad2009-05-21 22:17:49 +00001033 def close(self):
1034 self.flush_all_buffers()
1035
1036
mbligha7007722009-01-13 00:37:11 +00001037SiteAutotest = client_utils.import_site_class(
1038 __file__, "autotest_lib.server.site_autotest", "SiteAutotest",
1039 BaseAutotest)
mblighd8b39252008-03-20 21:15:03 +00001040
showardad812bf2009-10-20 23:49:56 +00001041
jadmanski69bdaac2010-07-28 16:27:20 +00001042_SiteRun = client_utils.import_site_class(
1043 __file__, "autotest_lib.server.site_autotest", "_SiteRun", _BaseRun)
1044
1045
mblighd8b39252008-03-20 21:15:03 +00001046class Autotest(SiteAutotest):
jadmanski0afbb632008-06-06 21:10:57 +00001047 pass
jadmanskic6136e92010-07-19 16:41:49 +00001048
1049
jadmanski69bdaac2010-07-28 16:27:20 +00001050class _Run(_SiteRun):
1051 pass
1052
1053
jadmanskic6136e92010-07-19 16:41:49 +00001054class AutotestHostMixin(object):
1055 """A generic mixin to add a run_test method to classes, which will allow
1056 you to run an autotest client test on a machine directly."""
1057
1058 # for testing purposes
1059 _Autotest = Autotest
1060
1061 def run_test(self, test_name, **dargs):
1062 """Run an autotest client test on the host.
1063
1064 @param test_name: The name of the client test.
1065 @param dargs: Keyword arguments to pass to the test.
1066
1067 @returns: True if the test passes, False otherwise."""
1068 at = self._Autotest()
1069 control_file = ('result = job.run_test(%s)\n'
1070 'job.set_state("test_result", result)\n')
1071 test_args = [repr(test_name)]
1072 test_args += ['%s=%r' % (k, v) for k, v in dargs.iteritems()]
1073 control_file %= ', '.join(test_args)
1074 at.run(control_file, host=self)
1075 return at.job.get_state('test_result', default=False)