blob: f19e350b48603ad91809245c7cf861e955fc04f0 [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
jadmanski043e1132008-11-19 17:10:32 +00005from autotest_lib.server import installable_object, utils
jadmanskiede7e242009-08-10 15:43:33 +00006from autotest_lib.client.common_lib import 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
showardad812bf2009-10-20 23:49:56 +000019class AutodirNotFoundError(Exception):
20 """No Autotest installation could be found."""
21
22
mblighd8b39252008-03-20 21:15:03 +000023class BaseAutotest(installable_object.InstallableObject):
jadmanski0afbb632008-06-06 21:10:57 +000024 """
25 This class represents the Autotest program.
mblighdcd57a82007-07-11 23:06:47 +000026
jadmanski0afbb632008-06-06 21:10:57 +000027 Autotest is used to run tests automatically and collect the results.
28 It also supports profilers.
mblighdcd57a82007-07-11 23:06:47 +000029
jadmanski0afbb632008-06-06 21:10:57 +000030 Implementation details:
31 This is a leaf class in an abstract class hierarchy, it must
32 implement the unimplemented methods in parent classes.
33 """
mbligh119c12a2007-11-12 22:13:44 +000034
jadmanski0afbb632008-06-06 21:10:57 +000035 def __init__(self, host = None):
36 self.host = host
37 self.got = False
38 self.installed = False
39 self.serverdir = utils.get_server_dir()
40 super(BaseAutotest, self).__init__()
mblighc8949b82007-07-23 16:33:58 +000041
mblighdc735a22007-08-02 16:54:37 +000042
jadmanskif22fea82008-11-26 20:57:07 +000043 install_in_tmpdir = False
44 @classmethod
45 def set_install_in_tmpdir(cls, flag):
46 """ Sets a flag that controls whether or not Autotest should by
47 default be installed in a "standard" directory (e.g.
48 /home/autotest, /usr/local/autotest) or a temporary directory. """
49 cls.install_in_tmpdir = flag
50
51
showardad812bf2009-10-20 23:49:56 +000052 @classmethod
53 def get_client_autodir_paths(cls, host):
54 return global_config.global_config.get_config_value(
55 'AUTOSERV', 'client_autodir_paths', type=list)
56
57
58 @classmethod
59 def get_installed_autodir(cls, host):
60 """
61 Find where the Autotest client is installed on the host.
62 @returns an absolute path to an installed Autotest client root.
63 @raises AutodirNotFoundError if no Autotest installation can be found.
64 """
65 autodir = host.get_autodir()
66 if autodir:
67 logging.debug('Using existing host autodir: %s', autodir)
68 return autodir
69
70 for path in Autotest.get_client_autodir_paths(host):
71 try:
72 autotest_binary = os.path.join(path, 'bin', 'autotest')
73 host.run('test -x %s' % utils.sh_escape(autotest_binary))
74 logging.debug('Found existing autodir at %s', path)
75 return path
76 except error.AutoservRunError:
77 logging.debug('%s does not exist on %s', autotest_binary,
78 host.hostname)
79 raise AutodirNotFoundError
80
81
82 @classmethod
83 def get_install_dir(cls, host):
84 """
85 Determines the location where autotest should be installed on
jadmanskif22fea82008-11-26 20:57:07 +000086 host. If self.install_in_tmpdir is set, it will return a unique
showardad812bf2009-10-20 23:49:56 +000087 temporary directory that autotest can be installed in. Otherwise, looks
88 for an existing installation to use; if none is found, looks for a
89 usable directory in the global config client_autodir_paths.
90 """
jadmanskif22fea82008-11-26 20:57:07 +000091 try:
lmr9dcf0832009-12-08 21:28:55 +000092 install_dir = cls.get_installed_autodir(host)
showardad812bf2009-10-20 23:49:56 +000093 except AutodirNotFoundError:
lmr9dcf0832009-12-08 21:28:55 +000094 install_dir = cls._find_installable_dir(host)
95
96 if cls.install_in_tmpdir:
97 return host.get_tmp_dir(parent=install_dir)
98 return install_dir
showardad812bf2009-10-20 23:49:56 +000099
100
101 @classmethod
102 def _find_installable_dir(cls, host):
103 client_autodir_paths = cls.get_client_autodir_paths(host)
104 for path in client_autodir_paths:
105 try:
106 host.run('mkdir -p %s' % utils.sh_escape(path))
107 return path
108 except error.AutoservRunError:
109 logging.debug('Failed to create %s', path)
110 raise error.AutoservInstallError(
111 'Unable to find a place to install Autotest; tried %s',
112 ', '.join(client_autodir_paths))
jadmanskif22fea82008-11-26 20:57:07 +0000113
114
mbligh1b3b3762008-09-25 02:46:34 +0000115 @log.record
mblighb3c0c912008-11-27 00:32:45 +0000116 def install(self, host=None, autodir=None):
117 self._install(host=host, autodir=autodir)
jadmanski54f90af2008-10-10 16:20:55 +0000118
119
mblighb8aa75b2009-09-18 16:50:37 +0000120 @log.record
121 def install_full_client(self, host=None, autodir=None):
122 self._install(host=host, autodir=autodir, use_autoserv=False,
123 use_packaging=False)
124
125
mblighbccad482009-08-24 22:08:31 +0000126 def install_no_autoserv(self, host=None, autodir=None):
mblighb8aa75b2009-09-18 16:50:37 +0000127 self._install(host=host, autodir=autodir, use_autoserv=False)
mblighbccad482009-08-24 22:08:31 +0000128
129
mblighb8aa75b2009-09-18 16:50:37 +0000130 def _install_using_packaging(self, host, autodir):
131 c = global_config.global_config
jadmanski2315a7e2009-09-18 18:39:37 +0000132 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list,
133 default=[])
134 repos.reverse()
mblighb8aa75b2009-09-18 16:50:37 +0000135 if not repos:
136 raise error.PackageInstallError("No repos to install an "
137 "autotest client from")
138 pkgmgr = packages.PackageManager(autodir, hostname=host.hostname,
139 repo_urls=repos,
140 do_locking=False,
141 run_function=host.run,
142 run_function_dargs=dict(timeout=600))
143 # The packages dir is used to store all the packages that
144 # are fetched on that client. (for the tests,deps etc.
145 # too apart from the client)
146 pkg_dir = os.path.join(autodir, 'packages')
147 # clean up the autodir except for the packages directory
148 host.run('cd %s && ls | grep -v "^packages$"'
149 ' | xargs rm -rf && rm -rf .[^.]*' % autodir)
150 pkgmgr.install_pkg('autotest', 'client', pkg_dir, autodir,
151 preserve_install_dir=True)
152 self.installed = True
153
154
155 def _install_using_send_file(self, host, autodir):
156 dirs_to_exclude = set(["tests", "site_tests", "deps", "profilers"])
157 light_files = [os.path.join(self.source_material, f)
158 for f in os.listdir(self.source_material)
159 if f not in dirs_to_exclude]
160 host.send_file(light_files, autodir, delete_dest=True)
161
162 # create empty dirs for all the stuff we excluded
163 commands = []
164 for path in dirs_to_exclude:
165 abs_path = os.path.join(autodir, path)
166 abs_path = utils.sh_escape(abs_path)
167 commands.append("mkdir -p '%s'" % abs_path)
168 commands.append("touch '%s'/__init__.py" % abs_path)
169 host.run(';'.join(commands))
170
171
172 def _install(self, host=None, autodir=None, use_autoserv=True,
173 use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000174 """
175 Install autotest. If get() was not called previously, an
176 attempt will be made to install from the autotest svn
177 repository.
mbligh9a3f5e52008-05-28 21:21:43 +0000178
mblighbccad482009-08-24 22:08:31 +0000179 @param host A Host instance on which autotest will be installed
180 @param autodir Location on the remote host to install to
mblighb8aa75b2009-09-18 16:50:37 +0000181 @param use_autoserv Enable install modes that depend on the client
mblighbccad482009-08-24 22:08:31 +0000182 running with the autoserv harness
mblighb8aa75b2009-09-18 16:50:37 +0000183 @param use_packaging Enable install modes that use the packaging system
mbligh9a3f5e52008-05-28 21:21:43 +0000184
mblighbccad482009-08-24 22:08:31 +0000185 @exception AutoservError if a tarball was not specified and
186 the target host does not have svn installed in its path
187 """
jadmanski0afbb632008-06-06 21:10:57 +0000188 if not host:
189 host = self.host
190 if not self.got:
191 self.get()
192 host.wait_up(timeout=30)
193 host.setup()
showardb18134f2009-03-20 20:52:18 +0000194 logging.info("Installing autotest on %s", host.hostname)
mbligh40f122a2007-11-03 23:08:46 +0000195
jadmanski54f90af2008-10-10 16:20:55 +0000196 # set up the autotest directory on the remote machine
197 if not autodir:
showardad812bf2009-10-20 23:49:56 +0000198 autodir = self.get_install_dir(host)
199 logging.info('Using installation dir %s', autodir)
mbligh0562e652008-08-20 20:11:45 +0000200 host.set_autodir(autodir)
jadmanski3c236942009-03-04 17:51:26 +0000201 host.run('mkdir -p %s' % utils.sh_escape(autodir))
mbligh40f122a2007-11-03 23:08:46 +0000202
jadmanski1c3c07b2009-03-03 23:29:36 +0000203 # make sure there are no files in $AUTODIR/results
204 results_path = os.path.join(autodir, 'results')
jadmanski3c236942009-03-04 17:51:26 +0000205 host.run('rm -rf %s/*' % utils.sh_escape(results_path),
jadmanski1c3c07b2009-03-03 23:29:36 +0000206 ignore_status=True)
207
mblighc5ddfd12008-08-04 17:15:00 +0000208 # Fetch the autotest client from the nearest repository
mblighb8aa75b2009-09-18 16:50:37 +0000209 if use_packaging:
210 try:
211 self._install_using_packaging(host, autodir)
212 return
213 except global_config.ConfigError, e:
214 logging.info("Could not install autotest using the packaging "
215 "system: %s", e)
216 except (error.PackageInstallError, error.AutoservRunError), e:
217 logging.error("Could not install autotest from repos")
mblighc5ddfd12008-08-04 17:15:00 +0000218
jadmanski0afbb632008-06-06 21:10:57 +0000219 # try to install from file or directory
220 if self.source_material:
221 if os.path.isdir(self.source_material):
jadmanski27b52912009-08-14 17:09:15 +0000222 c = global_config.global_config
223 supports_autoserv_packaging = c.get_config_value(
224 "PACKAGES", "serve_packages_from_autoserv", type=bool)
jadmanski0afbb632008-06-06 21:10:57 +0000225 # Copy autotest recursively
mblighb8aa75b2009-09-18 16:50:37 +0000226 if supports_autoserv_packaging and use_autoserv:
227 self._install_using_send_file(host, autodir)
jadmanski54f90af2008-10-10 16:20:55 +0000228 else:
mbligh89e258d2008-10-24 13:58:08 +0000229 host.send_file(self.source_material, autodir,
230 delete_dest=True)
jadmanski0afbb632008-06-06 21:10:57 +0000231 else:
232 # Copy autotest via tarball
233 e_msg = 'Installation method not yet implemented!'
234 raise NotImplementedError(e_msg)
showardb18134f2009-03-20 20:52:18 +0000235 logging.info("Installation of autotest completed")
jadmanski0afbb632008-06-06 21:10:57 +0000236 self.installed = True
237 return
mbligh91334902007-09-28 01:47:59 +0000238
jadmanski0afbb632008-06-06 21:10:57 +0000239 # if that fails try to install using svn
240 if utils.run('which svn').exit_status:
mbligh78bf5352008-07-11 20:27:36 +0000241 raise error.AutoservError('svn not found on target machine: %s'
242 % host.name)
jadmanski0afbb632008-06-06 21:10:57 +0000243 try:
mbligh78bf5352008-07-11 20:27:36 +0000244 host.run('svn checkout %s %s' % (AUTOTEST_SVN, autodir))
jadmanski0afbb632008-06-06 21:10:57 +0000245 except error.AutoservRunError, e:
mbligh78bf5352008-07-11 20:27:36 +0000246 host.run('svn checkout %s %s' % (AUTOTEST_HTTP, autodir))
showardb18134f2009-03-20 20:52:18 +0000247 logging.info("Installation of autotest completed")
jadmanski0afbb632008-06-06 21:10:57 +0000248 self.installed = True
mbligh91334902007-09-28 01:47:59 +0000249
250
jadmanski7c7aff32009-03-25 22:43:07 +0000251 def uninstall(self, host=None):
252 """
253 Uninstall (i.e. delete) autotest. Removes the autotest client install
254 from the specified host.
255
256 @params host a Host instance from which the client will be removed
257 """
258 if not self.installed:
259 return
260 if not host:
261 host = self.host
262 autodir = host.get_autodir()
263 if not autodir:
264 return
265
266 # perform the actual uninstall
267 host.run("rm -rf %s" % utils.sh_escape(autodir), ignore_status=True)
268 host.set_autodir(None)
269 self.installed = False
270
271
jadmanski0afbb632008-06-06 21:10:57 +0000272 def get(self, location = None):
273 if not location:
274 location = os.path.join(self.serverdir, '../client')
275 location = os.path.abspath(location)
276 # If there's stuff run on our client directory already, it
277 # can cause problems. Try giving it a quick clean first.
278 cwd = os.getcwd()
279 os.chdir(location)
showard4b976072009-10-20 23:50:08 +0000280 try:
281 utils.system('tools/make_clean', ignore_status=True)
282 finally:
283 os.chdir(cwd)
jadmanski0afbb632008-06-06 21:10:57 +0000284 super(BaseAutotest, self).get(location)
285 self.got = True
mblighdcd57a82007-07-11 23:06:47 +0000286
287
mblighe7d9c602009-07-02 19:02:33 +0000288 def run(self, control_file, results_dir='.', host=None, timeout=None,
289 tag=None, parallel_flag=False, background=False,
mbligh9de6ed72010-01-11 19:01:10 +0000290 client_disconnect_timeout=1800):
jadmanski0afbb632008-06-06 21:10:57 +0000291 """
292 Run an autotest job on the remote machine.
mbligh9a3f5e52008-05-28 21:21:43 +0000293
mblighe7d9c602009-07-02 19:02:33 +0000294 @param control_file: An open file-like-obj of the control file.
295 @param results_dir: A str path where the results should be stored
296 on the local filesystem.
297 @param host: A Host instance on which the control file should
298 be run.
299 @param timeout: Maximum number of seconds to wait for the run or None.
300 @param tag: Tag name for the client side instance of autotest.
301 @param parallel_flag: Flag set when multiple jobs are run at the
302 same time.
303 @param background: Indicates that the client should be launched as
304 a background job; the code calling run will be responsible
305 for monitoring the client and collecting the results.
306 @param client_disconnect_timeout: Seconds to wait for the remote host
307 to come back after a reboot. [default: 30 minutes]
mblighe7d9c602009-07-02 19:02:33 +0000308
309 @raises AutotestRunError: If there is a problem executing
310 the control file.
jadmanski0afbb632008-06-06 21:10:57 +0000311 """
312 host = self._get_host_and_setup(host)
313 results_dir = os.path.abspath(results_dir)
mblighc1cbc992008-05-27 20:01:45 +0000314
jadmanski0afbb632008-06-06 21:10:57 +0000315 if tag:
316 results_dir = os.path.join(results_dir, tag)
mblighc1cbc992008-05-27 20:01:45 +0000317
mblighb3c0c912008-11-27 00:32:45 +0000318 atrun = _Run(host, results_dir, tag, parallel_flag, background)
jadmanski6dadd832009-02-05 23:39:27 +0000319 self._do_run(control_file, results_dir, host, atrun, timeout,
mbligh9de6ed72010-01-11 19:01:10 +0000320 client_disconnect_timeout)
mblighd8b39252008-03-20 21:15:03 +0000321
322
jadmanski0afbb632008-06-06 21:10:57 +0000323 def _get_host_and_setup(self, host):
324 if not host:
325 host = self.host
326 if not self.installed:
327 self.install(host)
mbligh91334902007-09-28 01:47:59 +0000328
jadmanski0afbb632008-06-06 21:10:57 +0000329 host.wait_up(timeout=30)
330 return host
mblighd8b39252008-03-20 21:15:03 +0000331
332
jadmanski6dadd832009-02-05 23:39:27 +0000333 def _do_run(self, control_file, results_dir, host, atrun, timeout,
mbligh9de6ed72010-01-11 19:01:10 +0000334 client_disconnect_timeout):
jadmanski0afbb632008-06-06 21:10:57 +0000335 try:
336 atrun.verify_machine()
337 except:
showardb18134f2009-03-20 20:52:18 +0000338 logging.error("Verify failed on %s. Reinstalling autotest",
339 host.hostname)
jadmanski0afbb632008-06-06 21:10:57 +0000340 self.install(host)
341 atrun.verify_machine()
342 debug = os.path.join(results_dir, 'debug')
343 try:
344 os.makedirs(debug)
mbligh09108442008-10-15 16:27:38 +0000345 except Exception:
jadmanski0afbb632008-06-06 21:10:57 +0000346 pass
mbligh9a3f5e52008-05-28 21:21:43 +0000347
mbligh09108442008-10-15 16:27:38 +0000348 delete_file_list = [atrun.remote_control_file,
349 atrun.remote_control_file + '.state',
350 atrun.manual_control_file,
351 atrun.manual_control_file + '.state']
352 cmd = ';'.join('rm -f ' + control for control in delete_file_list)
353 host.run(cmd, ignore_status=True)
mbligh9a3f5e52008-05-28 21:21:43 +0000354
jadmanski0afbb632008-06-06 21:10:57 +0000355 tmppath = utils.get(control_file)
mblighc5ddfd12008-08-04 17:15:00 +0000356
jadmanskicb0e1612009-02-27 18:03:10 +0000357 # build up the initialization prologue for the control file
358 prologue_lines = []
jadmanski23afbec2008-09-17 18:12:07 +0000359
mbligh2f076832010-03-30 17:08:20 +0000360 # Add the additional user arguments
361 prologue_lines.append("args = %r\n" % self.job._args)
362
mbligh09108442008-10-15 16:27:38 +0000363 # If the packaging system is being used, add the repository list.
mblighddc9a402010-01-15 20:33:34 +0000364 repos = None
mblighc5ddfd12008-08-04 17:15:00 +0000365 try:
mblighc5ddfd12008-08-04 17:15:00 +0000366 c = global_config.global_config
367 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list)
jadmanskiede7e242009-08-10 15:43:33 +0000368 repos.reverse() # high priority packages should be added last
mbligh76d19f72008-10-15 16:24:43 +0000369 pkgmgr = packages.PackageManager('autotest', hostname=host.hostname,
370 repo_urls=repos)
jadmanskib1a51132009-08-07 16:45:50 +0000371 prologue_lines.append('job.add_repository(%s)\n' % repos)
mblighc5ddfd12008-08-04 17:15:00 +0000372 except global_config.ConfigError, e:
mblighddc9a402010-01-15 20:33:34 +0000373 # If repos is defined packaging is enabled so log the error
374 if repos:
375 logging.error(e)
mblighc5ddfd12008-08-04 17:15:00 +0000376
jadmanskie2eef7b2009-03-03 23:55:13 +0000377 # on full-size installs, turn on any profilers the server is using
jadmanski27b52912009-08-14 17:09:15 +0000378 if not atrun.background:
jadmanskie2eef7b2009-03-03 23:55:13 +0000379 running_profilers = host.job.profilers.add_log.iteritems()
380 for profiler, (args, dargs) in running_profilers:
381 call_args = [repr(profiler)]
382 call_args += [repr(arg) for arg in args]
383 call_args += ["%s=%r" % item for item in dargs.iteritems()]
384 prologue_lines.append("job.profilers.add(%s)\n"
385 % ", ".join(call_args))
386 cfile = "".join(prologue_lines)
387
mbligh09108442008-10-15 16:27:38 +0000388 cfile += open(tmppath).read()
389 open(tmppath, "w").write(cfile)
mblighc5ddfd12008-08-04 17:15:00 +0000390
jadmanskic09fc152008-10-15 17:56:59 +0000391 # Create and copy state file to remote_control_file + '.state'
mblighfc3da5b2010-01-06 18:37:22 +0000392 state_file = host.job.preprocess_client_state()
mblighfbf73ae2009-12-19 05:22:42 +0000393 host.send_file(state_file, atrun.remote_control_file + '.init.state')
jadmanskic09fc152008-10-15 17:56:59 +0000394 os.remove(state_file)
395
mblighc5ddfd12008-08-04 17:15:00 +0000396 # Copy control_file to remote_control_file on the host
jadmanski0afbb632008-06-06 21:10:57 +0000397 host.send_file(tmppath, atrun.remote_control_file)
398 if os.path.abspath(tmppath) != os.path.abspath(control_file):
399 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000400
jadmanski6bb32d72009-03-19 20:25:24 +0000401 atrun.execute_control(
jadmanski6dadd832009-02-05 23:39:27 +0000402 timeout=timeout,
403 client_disconnect_timeout=client_disconnect_timeout)
jadmanski23afbec2008-09-17 18:12:07 +0000404
405
jadmanski0afbb632008-06-06 21:10:57 +0000406 def run_timed_test(self, test_name, results_dir='.', host=None,
jadmanskic98c4702009-01-05 15:50:06 +0000407 timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000408 """
409 Assemble a tiny little control file to just run one test,
410 and run it as an autotest client-side test
411 """
412 if not host:
413 host = self.host
414 if not self.installed:
415 self.install(host)
416 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
417 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
418 control = "job.run_test(%s)\n" % cmd
jadmanskic98c4702009-01-05 15:50:06 +0000419 self.run(control, results_dir, host, timeout=timeout)
mbligh0e4613b2007-10-29 16:55:07 +0000420
421
jadmanskic98c4702009-01-05 15:50:06 +0000422 def run_test(self, test_name, results_dir='.', host=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000423 self.run_timed_test(test_name, results_dir, host, timeout=None,
jadmanskic98c4702009-01-05 15:50:06 +0000424 *args, **dargs)
mblighd54832b2007-07-25 16:46:56 +0000425
426
mblighdcd57a82007-07-11 23:06:47 +0000427class _Run(object):
jadmanski0afbb632008-06-06 21:10:57 +0000428 """
429 Represents a run of autotest control file. This class maintains
430 all the state necessary as an autotest control file is executed.
mblighdcd57a82007-07-11 23:06:47 +0000431
jadmanski0afbb632008-06-06 21:10:57 +0000432 It is not intended to be used directly, rather control files
433 should be run using the run method in Autotest.
434 """
mblighb3c0c912008-11-27 00:32:45 +0000435 def __init__(self, host, results_dir, tag, parallel_flag, background):
jadmanski0afbb632008-06-06 21:10:57 +0000436 self.host = host
437 self.results_dir = results_dir
438 self.env = host.env
439 self.tag = tag
440 self.parallel_flag = parallel_flag
mblighb3c0c912008-11-27 00:32:45 +0000441 self.background = background
showardad812bf2009-10-20 23:49:56 +0000442 self.autodir = Autotest.get_installed_autodir(self.host)
mbligh78bf5352008-07-11 20:27:36 +0000443 control = os.path.join(self.autodir, 'control')
jadmanski0afbb632008-06-06 21:10:57 +0000444 if tag:
mbligh78bf5352008-07-11 20:27:36 +0000445 control += '.' + tag
446 self.manual_control_file = control
447 self.remote_control_file = control + '.autoserv'
lmr6d08b3c2009-11-18 19:26:38 +0000448 self.config_file = os.path.join(self.autodir, 'global_config.ini')
mblighdc735a22007-08-02 16:54:37 +0000449
450
jadmanski0afbb632008-06-06 21:10:57 +0000451 def verify_machine(self):
452 binary = os.path.join(self.autodir, 'bin/autotest')
453 try:
454 self.host.run('ls %s > /dev/null 2>&1' % binary)
455 except:
lmrd6d27ed2009-12-08 19:58:33 +0000456 raise error.AutoservInstallError(
457 "Autotest does not appear to be installed")
mblighdc735a22007-08-02 16:54:37 +0000458
jadmanski0afbb632008-06-06 21:10:57 +0000459 if not self.parallel_flag:
460 tmpdir = os.path.join(self.autodir, 'tmp')
461 download = os.path.join(self.autodir, 'tests/download')
462 self.host.run('umount %s' % tmpdir, ignore_status=True)
463 self.host.run('umount %s' % download, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000464
jadmanski6dadd832009-02-05 23:39:27 +0000465
466 def get_base_cmd_args(self, section):
showard234b2962009-07-28 20:02:30 +0000467 args = ['--verbose']
jadmanski0afbb632008-06-06 21:10:57 +0000468 if section > 0:
jadmanski6dadd832009-02-05 23:39:27 +0000469 args.append('-c')
jadmanski0afbb632008-06-06 21:10:57 +0000470 if self.tag:
jadmanski6dadd832009-02-05 23:39:27 +0000471 args.append('-t %s' % self.tag)
jadmanski0afbb632008-06-06 21:10:57 +0000472 if self.host.job.use_external_logging():
jadmanski6dadd832009-02-05 23:39:27 +0000473 args.append('-l')
mblighce955fc2009-08-24 21:59:02 +0000474 if self.host.hostname:
475 args.append('--hostname=%s' % self.host.hostname)
mbligh0d0f67d2009-11-06 03:15:03 +0000476 args.append('--user=%s' % self.host.job.user)
mblighce955fc2009-08-24 21:59:02 +0000477
jadmanski6dadd832009-02-05 23:39:27 +0000478 args.append(self.remote_control_file)
479 return args
480
481
482 def get_background_cmd(self, section):
483 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotest_client')]
484 cmd += self.get_base_cmd_args(section)
485 cmd.append('>/dev/null 2>/dev/null &')
486 return ' '.join(cmd)
487
488
489 def get_daemon_cmd(self, section, monitor_dir):
490 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotestd'),
491 monitor_dir, '-H autoserv']
492 cmd += self.get_base_cmd_args(section)
493 cmd.append('>/dev/null 2>/dev/null </dev/null &')
494 return ' '.join(cmd)
495
496
497 def get_monitor_cmd(self, monitor_dir, stdout_read, stderr_read):
498 cmd = [os.path.join(self.autodir, 'bin', 'autotestd_monitor'),
499 monitor_dir, str(stdout_read), str(stderr_read)]
jadmanski0afbb632008-06-06 21:10:57 +0000500 return ' '.join(cmd)
mblighadf2aab2007-11-29 18:16:43 +0000501
mblighd8b39252008-03-20 21:15:03 +0000502
jadmanski4d03cf62010-03-04 18:32:28 +0000503 def get_client_log(self):
504 """Find what the "next" client.* prefix should be
505
506 @returns A string of the form client.INTEGER that should be prefixed
507 to all client debug log files.
508 """
509 max_digit = -1
510 debug_dir = os.path.join(self.results_dir, 'debug')
511 client_logs = glob.glob(os.path.join(debug_dir, 'client.*.*'))
512 for log in client_logs:
513 _, number, _ = log.split('.', 2)
514 if number.isdigit():
515 max_digit = max(max_digit, int(number))
516 return 'client.%d' % (max_digit + 1)
517
518
519 def copy_client_config_file(self, client_log_prefix=None):
520 """
521 Create and copy the client config file based on the server config.
522
523 @param client_log_prefix: Optional prefix to prepend to log files.
524 """
525 client_config_file = self._create_client_config_file(client_log_prefix)
526 self.host.send_file(client_config_file, self.config_file)
527 os.remove(client_config_file)
528
529
530 def _create_client_config_file(self, client_log_prefix=None):
531 """
532 Create a temporary file with the [CLIENT] section configuration values
533 taken from the server global_config.ini.
534
535 @param client_log_prefix: Optional prefix to prepend to log files.
536
537 @return: Path of the temporary file generated.
538 """
539 config = global_config.global_config.get_section_values('CLIENT')
540 if client_log_prefix:
541 config.set('CLIENT', 'default_logging_name', client_log_prefix)
542 return self._create_aux_file(config.write)
543
544
545 def _create_aux_file(self, func, *args):
546 """
547 Creates a temporary file and writes content to it according to a
548 content creation function. The file object is appended to *args, which
549 is then passed to the content creation function
550
551 @param func: Function that will be used to write content to the
552 temporary file.
553 @param *args: List of parameters that func takes.
554 @return: Path to the temporary file that was created.
555 """
556 fd, path = tempfile.mkstemp(dir=self.host.job.tmpdir)
557 aux_file = os.fdopen(fd, "w")
558 try:
559 list_args = list(args)
560 list_args.append(aux_file)
561 func(*list_args)
562 finally:
563 aux_file.close()
564 return path
mblighd8b39252008-03-20 21:15:03 +0000565
566
jadmanskib264ed02009-01-12 23:54:27 +0000567 @staticmethod
568 def is_client_job_finished(last_line):
569 return bool(re.match(r'^END .*\t----\t----\t.*$', last_line))
570
571
572 @staticmethod
573 def is_client_job_rebooting(last_line):
574 return bool(re.match(r'^\t*GOOD\t----\treboot\.start.*$', last_line))
575
576
jadmanskia61edad2009-05-21 22:17:49 +0000577 def log_unexpected_abort(self, stderr_redirector):
578 stderr_redirector.flush_all_buffers()
jadmanskib264ed02009-01-12 23:54:27 +0000579 msg = "Autotest client terminated unexpectedly"
580 self.host.job.record("END ABORT", None, None, msg)
581
582
jadmanski6dadd832009-02-05 23:39:27 +0000583 def _execute_in_background(self, section, timeout):
584 full_cmd = self.get_background_cmd(section)
585 devnull = open(os.devnull, "w")
mblighd8b39252008-03-20 21:15:03 +0000586
jadmanski4d03cf62010-03-04 18:32:28 +0000587 self.copy_client_config_file(self.get_client_log())
588
mbligh0d0f67d2009-11-06 03:15:03 +0000589 self.host.job.push_execution_context(self.results_dir)
jadmanski0afbb632008-06-06 21:10:57 +0000590 try:
jadmanski0afbb632008-06-06 21:10:57 +0000591 result = self.host.run(full_cmd, ignore_status=True,
592 timeout=timeout,
jadmanski6dadd832009-02-05 23:39:27 +0000593 stdout_tee=devnull,
594 stderr_tee=devnull)
jadmanski0afbb632008-06-06 21:10:57 +0000595 finally:
mbligh0d0f67d2009-11-06 03:15:03 +0000596 self.host.job.pop_execution_context()
jadmanski6dadd832009-02-05 23:39:27 +0000597
598 return result
599
600
601 @staticmethod
602 def _strip_stderr_prologue(stderr):
603 """Strips the 'standard' prologue that get pre-pended to every
604 remote command and returns the text that was actually written to
605 stderr by the remote command."""
606 stderr_lines = stderr.split("\n")[1:]
607 if not stderr_lines:
608 return ""
609 elif stderr_lines[0].startswith("NOTE: autotestd_monitor"):
610 del stderr_lines[0]
611 return "\n".join(stderr_lines)
612
613
614 def _execute_daemon(self, section, timeout, stderr_redirector,
615 client_disconnect_timeout):
616 monitor_dir = self.host.get_tmp_dir()
617 daemon_cmd = self.get_daemon_cmd(section, monitor_dir)
jadmanski4d03cf62010-03-04 18:32:28 +0000618
619 # grab the location for the server-side client log file
620 client_log_prefix = self.get_client_log()
621 client_log_path = os.path.join(self.results_dir, 'debug',
622 client_log_prefix + '.log')
623 client_log = open(client_log_path, 'w', 0)
624 self.copy_client_config_file(client_log_prefix)
jadmanski6dadd832009-02-05 23:39:27 +0000625
626 stdout_read = stderr_read = 0
mbligh0d0f67d2009-11-06 03:15:03 +0000627 self.host.job.push_execution_context(self.results_dir)
jadmanski6dadd832009-02-05 23:39:27 +0000628 try:
629 self.host.run(daemon_cmd, ignore_status=True, timeout=timeout)
jadmanski91d56a92009-04-01 15:20:40 +0000630 disconnect_warnings = []
jadmanski6dadd832009-02-05 23:39:27 +0000631 while True:
632 monitor_cmd = self.get_monitor_cmd(monitor_dir, stdout_read,
633 stderr_read)
634 try:
635 result = self.host.run(monitor_cmd, ignore_status=True,
636 timeout=timeout,
637 stdout_tee=client_log,
638 stderr_tee=stderr_redirector)
639 except error.AutoservRunError, e:
640 result = e.result_obj
641 result.exit_status = None
jadmanski91d56a92009-04-01 15:20:40 +0000642 disconnect_warnings.append(e.description)
643
jadmanski6dadd832009-02-05 23:39:27 +0000644 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000645 "Autotest client was disconnected: %s" % e.description,
646 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000647 except error.AutoservSSHTimeout:
648 result = utils.CmdResult(monitor_cmd, "", "", None, 0)
649 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000650 "Attempt to connect to Autotest client timed out",
651 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000652
653 stdout_read += len(result.stdout)
654 stderr_read += len(self._strip_stderr_prologue(result.stderr))
655
656 if result.exit_status is not None:
657 return result
658 elif not self.host.wait_up(client_disconnect_timeout):
659 raise error.AutoservSSHTimeout(
660 "client was disconnected, reconnect timed out")
661 finally:
jadmanski4d03cf62010-03-04 18:32:28 +0000662 client_log.close()
mbligh0d0f67d2009-11-06 03:15:03 +0000663 self.host.job.pop_execution_context()
jadmanski6dadd832009-02-05 23:39:27 +0000664
665
666 def execute_section(self, section, timeout, stderr_redirector,
667 client_disconnect_timeout):
showardb18134f2009-03-20 20:52:18 +0000668 logging.info("Executing %s/bin/autotest %s/control phase %d",
669 self.autodir, self.autodir, section)
jadmanski6dadd832009-02-05 23:39:27 +0000670
671 if self.background:
672 result = self._execute_in_background(section, timeout)
673 else:
674 result = self._execute_daemon(section, timeout, stderr_redirector,
675 client_disconnect_timeout)
676
677 last_line = stderr_redirector.last_line
mbligh2bf2db62007-11-27 00:53:18 +0000678
jadmanskib264ed02009-01-12 23:54:27 +0000679 # check if we failed hard enough to warrant an exception
jadmanski0afbb632008-06-06 21:10:57 +0000680 if result.exit_status == 1:
jadmanskib264ed02009-01-12 23:54:27 +0000681 err = error.AutotestRunError("client job was aborted")
682 elif not self.background and not result.stderr:
683 err = error.AutotestRunError(
jadmanskie4130532009-03-17 18:01:28 +0000684 "execute_section %s failed to return anything\n"
685 "stdout:%s\n" % (section, result.stdout))
jadmanskib264ed02009-01-12 23:54:27 +0000686 else:
687 err = None
mbligh0e4613b2007-10-29 16:55:07 +0000688
jadmanskib264ed02009-01-12 23:54:27 +0000689 # log something if the client failed AND never finished logging
690 if err and not self.is_client_job_finished(last_line):
jadmanskia61edad2009-05-21 22:17:49 +0000691 self.log_unexpected_abort(stderr_redirector)
jadmanskib264ed02009-01-12 23:54:27 +0000692
693 if err:
694 raise err
695 else:
696 return stderr_redirector.last_line
jadmanski4600e342008-10-29 22:54:00 +0000697
698
jadmanskic0354912010-01-12 15:57:29 +0000699 def _wait_for_reboot(self, old_boot_id):
showardb18134f2009-03-20 20:52:18 +0000700 logging.info("Client is rebooting")
701 logging.info("Waiting for client to halt")
jadmanskic0354912010-01-12 15:57:29 +0000702 if not self.host.wait_down(HALT_TIME, old_boot_id=old_boot_id):
jadmanski4600e342008-10-29 22:54:00 +0000703 err = "%s failed to shutdown after %d"
704 err %= (self.host.hostname, HALT_TIME)
705 raise error.AutotestRunError(err)
showardb18134f2009-03-20 20:52:18 +0000706 logging.info("Client down, waiting for restart")
jadmanski4600e342008-10-29 22:54:00 +0000707 if not self.host.wait_up(BOOT_TIME):
708 # since reboot failed
709 # hardreset the machine once if possible
710 # before failing this control file
711 warning = "%s did not come back up, hard resetting"
712 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000713 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000714 try:
715 self.host.hardreset(wait=False)
mblighd99d3b272008-12-22 14:41:27 +0000716 except (AttributeError, error.AutoservUnsupportedError):
jadmanski4600e342008-10-29 22:54:00 +0000717 warning = "Hard reset unsupported on %s"
718 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000719 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000720 raise error.AutotestRunError("%s failed to boot after %ds" %
721 (self.host.hostname, BOOT_TIME))
722 self.host.reboot_followup()
mblighdc735a22007-08-02 16:54:37 +0000723
724
jadmanski6dadd832009-02-05 23:39:27 +0000725 def execute_control(self, timeout=None, client_disconnect_timeout=None):
jadmanski6bb32d72009-03-19 20:25:24 +0000726 if not self.background:
727 collector = log_collector(self.host, self.tag, self.results_dir)
728 hostname = self.host.hostname
729 remote_results = collector.client_results_dir
730 local_results = collector.server_results_dir
731 self.host.job.add_client_log(hostname, remote_results,
732 local_results)
733
jadmanski0afbb632008-06-06 21:10:57 +0000734 section = 0
jadmanski4600e342008-10-29 22:54:00 +0000735 start_time = time.time()
736
jadmanski043e1132008-11-19 17:10:32 +0000737 logger = client_logger(self.host, self.tag, self.results_dir)
jadmanski4600e342008-10-29 22:54:00 +0000738 try:
739 while not timeout or time.time() < start_time + timeout:
740 if timeout:
741 section_timeout = start_time + timeout - time.time()
742 else:
743 section_timeout = None
jadmanskic0354912010-01-12 15:57:29 +0000744 boot_id = self.host.get_boot_id()
jadmanski4600e342008-10-29 22:54:00 +0000745 last = self.execute_section(section, section_timeout,
jadmanski6dadd832009-02-05 23:39:27 +0000746 logger, client_disconnect_timeout)
mblighb3c0c912008-11-27 00:32:45 +0000747 if self.background:
748 return
jadmanski4600e342008-10-29 22:54:00 +0000749 section += 1
jadmanskib264ed02009-01-12 23:54:27 +0000750 if self.is_client_job_finished(last):
showardb18134f2009-03-20 20:52:18 +0000751 logging.info("Client complete")
jadmanski4600e342008-10-29 22:54:00 +0000752 return
jadmanskib264ed02009-01-12 23:54:27 +0000753 elif self.is_client_job_rebooting(last):
jadmanski79ab9282008-11-11 17:53:12 +0000754 try:
jadmanskic0354912010-01-12 15:57:29 +0000755 self._wait_for_reboot(boot_id)
jadmanski79ab9282008-11-11 17:53:12 +0000756 except error.AutotestRunError, e:
jadmanski043e1132008-11-19 17:10:32 +0000757 self.host.job.record("ABORT", None, "reboot", str(e))
758 self.host.job.record("END ABORT", None, None, str(e))
jadmanski79ab9282008-11-11 17:53:12 +0000759 raise
jadmanski4600e342008-10-29 22:54:00 +0000760 continue
761
762 # if we reach here, something unexpected happened
jadmanskia61edad2009-05-21 22:17:49 +0000763 self.log_unexpected_abort(logger)
jadmanski4600e342008-10-29 22:54:00 +0000764
765 # give the client machine a chance to recover from a crash
766 self.host.wait_up(CRASH_RECOVERY_TIME)
767 msg = ("Aborting - unexpected final status message from "
768 "client: %s\n") % last
769 raise error.AutotestRunError(msg)
770 finally:
jadmanski043e1132008-11-19 17:10:32 +0000771 logger.close()
jadmanski6bb32d72009-03-19 20:25:24 +0000772 if not self.background:
773 collector.collect_client_job_results()
jadmanski4d03cf62010-03-04 18:32:28 +0000774 collector.remove_redundant_client_logs()
mblighfc3da5b2010-01-06 18:37:22 +0000775 state_file = os.path.basename(self.remote_control_file
776 + '.state')
777 state_path = os.path.join(self.results_dir, state_file)
778 self.host.job.postprocess_client_state(state_path)
jadmanski6bb32d72009-03-19 20:25:24 +0000779 self.host.job.remove_client_log(hostname, remote_results,
780 local_results)
mblighdcd57a82007-07-11 23:06:47 +0000781
jadmanski0afbb632008-06-06 21:10:57 +0000782 # should only get here if we timed out
783 assert timeout
784 raise error.AutotestTimeoutError()
mbligh0e4613b2007-10-29 16:55:07 +0000785
mblighdcd57a82007-07-11 23:06:47 +0000786
jadmanski043e1132008-11-19 17:10:32 +0000787class log_collector(object):
788 def __init__(self, host, client_tag, results_dir):
789 self.host = host
790 if not client_tag:
791 client_tag = "default"
792 self.client_results_dir = os.path.join(host.get_autodir(), "results",
793 client_tag)
794 self.server_results_dir = results_dir
795
796
797 def collect_client_job_results(self):
798 """ A method that collects all the current results of a running
799 client job into the results dir. By default does nothing as no
800 client job is running, but when running a client job you can override
801 this with something that will actually do something. """
802
803 # make an effort to wait for the machine to come up
804 try:
805 self.host.wait_up(timeout=30)
806 except error.AutoservError:
807 # don't worry about any errors, we'll try and
808 # get the results anyway
809 pass
810
jadmanski043e1132008-11-19 17:10:32 +0000811 # Copy all dirs in default to results_dir
812 try:
jadmanski043e1132008-11-19 17:10:32 +0000813 self.host.get_file(self.client_results_dir + '/',
mbligh45561782009-05-11 21:14:34 +0000814 self.server_results_dir, preserve_symlinks=True)
jadmanski043e1132008-11-19 17:10:32 +0000815 except Exception:
816 # well, don't stop running just because we couldn't get logs
showardb18134f2009-03-20 20:52:18 +0000817 e_msg = "Unexpected error copying test result logs, continuing ..."
818 logging.error(e_msg)
jadmanski043e1132008-11-19 17:10:32 +0000819 traceback.print_exc(file=sys.stdout)
820
821
jadmanski4d03cf62010-03-04 18:32:28 +0000822 def remove_redundant_client_logs(self):
823 """Remove client.*.log files in favour of client.*.DEBUG files."""
824 debug_dir = os.path.join(self.server_results_dir, 'debug')
825 debug_files = [f for f in os.listdir(debug_dir)
826 if re.search(r'^client\.\d+\.DEBUG$', f)]
827 for debug_file in debug_files:
828 log_file = debug_file.replace('DEBUG', 'log')
829 log_file = os.path.join(debug_dir, log_file)
830 if os.path.exists(log_file):
831 os.remove(log_file)
832
833
jadmanski043e1132008-11-19 17:10:32 +0000834# a file-like object for catching stderr from an autotest client and
835# extracting status logs from it
836class client_logger(object):
837 """Partial file object to write to both stdout and
838 the status log file. We only implement those methods
839 utils.run() actually calls.
840
841 Note that this class is fairly closely coupled with server_job, as it
842 uses special job._ methods to actually carry out the loggging.
843 """
844 status_parser = re.compile(r"^AUTOTEST_STATUS:([^:]*):(.*)$")
845 test_complete_parser = re.compile(r"^AUTOTEST_TEST_COMPLETE:(.*)$")
jadmanskib1a51132009-08-07 16:45:50 +0000846 fetch_package_parser = re.compile(
847 r"^AUTOTEST_FETCH_PACKAGE:([^:]*):([^:]*):(.*)$")
jadmanski043e1132008-11-19 17:10:32 +0000848 extract_indent = re.compile(r"^(\t*).*$")
jadmanskiefe4ebf2009-05-21 22:12:30 +0000849 extract_timestamp = re.compile(r".*\ttimestamp=(\d+)\t.*$")
jadmanski043e1132008-11-19 17:10:32 +0000850
851 def __init__(self, host, tag, server_results_dir):
852 self.host = host
853 self.job = host.job
854 self.log_collector = log_collector(host, tag, server_results_dir)
855 self.leftover = ""
856 self.last_line = ""
jadmanskiefe4ebf2009-05-21 22:12:30 +0000857 self.newest_timestamp = float("-inf")
jadmanski043e1132008-11-19 17:10:32 +0000858 self.logs = {}
jadmanski6dadd832009-02-05 23:39:27 +0000859 self.server_warnings = []
jadmanski043e1132008-11-19 17:10:32 +0000860
861
jadmanskiefe4ebf2009-05-21 22:12:30 +0000862 def _update_timestamp(self, line):
863 match = self.extract_timestamp.search(line)
864 if match:
865 self.newest_timestamp = max(self.newest_timestamp,
866 int(match.group(1)))
867
868
jadmanski043e1132008-11-19 17:10:32 +0000869 def _process_log_dict(self, log_dict):
870 log_list = log_dict.pop("logs", [])
871 for key in sorted(log_dict.iterkeys()):
872 log_list += self._process_log_dict(log_dict.pop(key))
873 return log_list
874
875
876 def _process_logs(self):
877 """Go through the accumulated logs in self.log and print them
878 out to stdout and the status log. Note that this processes
879 logs in an ordering where:
880
881 1) logs to different tags are never interleaved
882 2) logs to x.y come before logs to x.y.z for all z
883 3) logs to x.y come before x.z whenever y < z
884
885 Note that this will in general not be the same as the
886 chronological ordering of the logs. However, if a chronological
887 ordering is desired that one can be reconstructed from the
888 status log by looking at timestamp lines."""
889 log_list = self._process_log_dict(self.logs)
890 for line in log_list:
891 self.job._record_prerendered(line + '\n')
892 if log_list:
893 self.last_line = log_list[-1]
894
895
896 def _process_quoted_line(self, tag, line):
897 """Process a line quoted with an AUTOTEST_STATUS flag. If the
898 tag is blank then we want to push out all the data we've been
899 building up in self.logs, and then the newest line. If the
900 tag is not blank, then push the line into the logs for handling
901 later."""
showardb18134f2009-03-20 20:52:18 +0000902 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +0000903 if tag == "":
904 self._process_logs()
905 self.job._record_prerendered(line + '\n')
906 self.last_line = line
907 else:
908 tag_parts = [int(x) for x in tag.split(".")]
909 log_dict = self.logs
910 for part in tag_parts:
911 log_dict = log_dict.setdefault(part, {})
912 log_list = log_dict.setdefault("logs", [])
913 log_list.append(line)
914
915
jadmanskif37df842009-02-11 00:03:26 +0000916 def _process_info_line(self, line):
917 """Check if line is an INFO line, and if it is, interpret any control
918 messages (e.g. enabling/disabling warnings) that it may contain."""
919 match = re.search(r"^\t*INFO\t----\t----(.*)\t[^\t]*$", line)
920 if not match:
921 return # not an INFO line
922 for field in match.group(1).split('\t'):
923 if field.startswith("warnings.enable="):
jadmanski16a7ff72009-04-01 18:19:53 +0000924 func = self.job.warning_manager.enable_warnings
jadmanskif37df842009-02-11 00:03:26 +0000925 elif field.startswith("warnings.disable="):
jadmanski16a7ff72009-04-01 18:19:53 +0000926 func = self.job.warning_manager.disable_warnings
jadmanskif37df842009-02-11 00:03:26 +0000927 else:
928 continue
929 warning_type = field.split("=", 1)[1]
jadmanski16a7ff72009-04-01 18:19:53 +0000930 func(warning_type)
jadmanskif37df842009-02-11 00:03:26 +0000931
932
jadmanski043e1132008-11-19 17:10:32 +0000933 def _process_line(self, line):
934 """Write out a line of data to the appropriate stream. Status
935 lines sent by autotest will be prepended with
936 "AUTOTEST_STATUS", and all other lines are ssh error
937 messages."""
938 status_match = self.status_parser.search(line)
939 test_complete_match = self.test_complete_parser.search(line)
jadmanskib1a51132009-08-07 16:45:50 +0000940 fetch_package_match = self.fetch_package_parser.search(line)
jadmanski043e1132008-11-19 17:10:32 +0000941 if status_match:
942 tag, line = status_match.groups()
jadmanskif37df842009-02-11 00:03:26 +0000943 self._process_info_line(line)
jadmanski043e1132008-11-19 17:10:32 +0000944 self._process_quoted_line(tag, line)
945 elif test_complete_match:
jadmanskifcc0d5d2009-02-12 21:52:54 +0000946 self._process_logs()
jadmanski043e1132008-11-19 17:10:32 +0000947 fifo_path, = test_complete_match.groups()
mbligh060c4712009-12-29 02:43:35 +0000948 try:
949 self.log_collector.collect_client_job_results()
950 self.host.run("echo A > %s" % fifo_path)
951 except Exception:
952 msg = "Post-test log collection failed, continuing anyway"
953 logging.exception(msg)
jadmanskib1a51132009-08-07 16:45:50 +0000954 elif fetch_package_match:
955 pkg_name, dest_path, fifo_path = fetch_package_match.groups()
jadmanskiede7e242009-08-10 15:43:33 +0000956 serve_packages = global_config.global_config.get_config_value(
957 "PACKAGES", "serve_packages_from_autoserv", type=bool)
958 if serve_packages and pkg_name.endswith(".tar.bz2"):
959 try:
960 self._send_tarball(pkg_name, dest_path)
961 except Exception:
962 msg = "Package tarball creation failed, continuing anyway"
963 logging.exception(msg)
mbligh060c4712009-12-29 02:43:35 +0000964 try:
965 self.host.run("echo B > %s" % fifo_path)
966 except Exception:
967 msg = "Package tarball installation failed, continuing anyway"
968 logging.exception(msg)
jadmanski043e1132008-11-19 17:10:32 +0000969 else:
showardb18134f2009-03-20 20:52:18 +0000970 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +0000971
972
jadmanskiede7e242009-08-10 15:43:33 +0000973 def _send_tarball(self, pkg_name, remote_dest):
974 name, pkg_type = self.job.pkgmgr.parse_tarball_name(pkg_name)
975 src_dirs = []
976 if pkg_type == 'test':
977 src_dirs += [os.path.join(self.job.clientdir, 'site_tests', name),
978 os.path.join(self.job.clientdir, 'tests', name)]
979 elif pkg_type == 'profiler':
980 src_dirs += [os.path.join(self.job.clientdir, 'profilers', name)]
981 elif pkg_type == 'dep':
982 src_dirs += [os.path.join(self.job.clientdir, 'deps', name)]
983 elif pkg_type == 'client':
984 return # you must already have a client to hit this anyway
985 else:
986 return # no other types are supported
987
988 # iterate over src_dirs until we find one that exists, then tar it
989 for src_dir in src_dirs:
990 if os.path.exists(src_dir):
991 try:
992 logging.info('Bundling %s into %s', src_dir, pkg_name)
993 temp_dir = autotemp.tempdir(unique_id='autoserv-packager',
994 dir=self.job.tmpdir)
995 tarball_path = self.job.pkgmgr.tar_package(
mblighbccad482009-08-24 22:08:31 +0000996 pkg_name, src_dir, temp_dir.name, " .")
jadmanskiede7e242009-08-10 15:43:33 +0000997 self.host.send_file(tarball_path, remote_dest)
998 finally:
999 temp_dir.clean()
1000 return
1001
1002
jadmanski043e1132008-11-19 17:10:32 +00001003 def _format_warnings(self, last_line, warnings):
1004 # use the indentation of whatever the last log line was
1005 indent = self.extract_indent.match(last_line).group(1)
1006 # if the last line starts a new group, add an extra indent
1007 if last_line.lstrip('\t').startswith("START\t"):
1008 indent += '\t'
1009 return [self.job._render_record("WARN", None, None, msg,
1010 timestamp, indent).rstrip('\n')
1011 for timestamp, msg in warnings]
1012
1013
1014 def _process_warnings(self, last_line, log_dict, warnings):
1015 if log_dict.keys() in ([], ["logs"]):
1016 # there are no sub-jobs, just append the warnings here
1017 warnings = self._format_warnings(last_line, warnings)
1018 log_list = log_dict.setdefault("logs", [])
1019 log_list += warnings
1020 for warning in warnings:
1021 sys.stdout.write(warning + '\n')
1022 else:
1023 # there are sub-jobs, so put the warnings in there
1024 log_list = log_dict.get("logs", [])
1025 if log_list:
1026 last_line = log_list[-1]
1027 for key in sorted(log_dict.iterkeys()):
1028 if key != "logs":
1029 self._process_warnings(last_line,
1030 log_dict[key],
1031 warnings)
1032
jadmanskif37df842009-02-11 00:03:26 +00001033
jadmanski91d56a92009-04-01 15:20:40 +00001034 def log_warning(self, msg, warning_type):
jadmanski6dadd832009-02-05 23:39:27 +00001035 """Injects a WARN message into the current status logging stream."""
jadmanski91d56a92009-04-01 15:20:40 +00001036 timestamp = int(time.time())
1037 if self.job.warning_manager.is_valid(timestamp, warning_type):
1038 self.server_warnings.append((timestamp, msg))
jadmanski6dadd832009-02-05 23:39:27 +00001039
jadmanski043e1132008-11-19 17:10:32 +00001040
1041 def write(self, data):
1042 # first check for any new console warnings
mbligh060c4712009-12-29 02:43:35 +00001043 self.server_warnings = self.job._read_warnings() + self.server_warnings
1044 warnings = self.server_warnings
jadmanski6dadd832009-02-05 23:39:27 +00001045 warnings.sort() # sort into timestamp order
mbligh060c4712009-12-29 02:43:35 +00001046 # now start processing the existng buffer and the new data
jadmanski043e1132008-11-19 17:10:32 +00001047 data = self.leftover + data
mbligh060c4712009-12-29 02:43:35 +00001048 lines = data.split('\n')
1049 processed_lines = 0
1050 try:
1051 # process all the buffered data except the last line
1052 # ignore the last line since we may not have all of it yet
1053 for line in lines[:-1]:
1054 self._update_timestamp(line)
1055 # output any warnings between now and the next status line
1056 old_warnings = [(timestamp, msg) for timestamp, msg in warnings
1057 if timestamp < self.newest_timestamp]
jadmanskicb777f12010-03-23 21:17:52 +00001058 self._process_warnings(self.last_line, self.logs, old_warnings)
mbligh060c4712009-12-29 02:43:35 +00001059 del warnings[:len(old_warnings)]
1060 # now process the line itself
1061 self._process_line(line)
1062 processed_lines += 1
1063 finally:
1064 # save any unprocessed lines for future processing
1065 self.leftover = '\n'.join(lines[processed_lines:])
jadmanski043e1132008-11-19 17:10:32 +00001066
1067
1068 def flush(self):
1069 sys.stdout.flush()
1070
1071
jadmanskia61edad2009-05-21 22:17:49 +00001072 def flush_all_buffers(self):
jadmanski043e1132008-11-19 17:10:32 +00001073 if self.leftover:
1074 self._process_line(self.leftover)
jadmanskia61edad2009-05-21 22:17:49 +00001075 self.leftover = ""
jadmanskiefe4ebf2009-05-21 22:12:30 +00001076 self._process_warnings(self.last_line, self.logs, self.server_warnings)
jadmanski043e1132008-11-19 17:10:32 +00001077 self._process_logs()
1078 self.flush()
1079
1080
jadmanskia61edad2009-05-21 22:17:49 +00001081 def close(self):
1082 self.flush_all_buffers()
1083
1084
mbligha7007722009-01-13 00:37:11 +00001085SiteAutotest = client_utils.import_site_class(
1086 __file__, "autotest_lib.server.site_autotest", "SiteAutotest",
1087 BaseAutotest)
mblighd8b39252008-03-20 21:15:03 +00001088
showardad812bf2009-10-20 23:49:56 +00001089
mblighd8b39252008-03-20 21:15:03 +00001090class Autotest(SiteAutotest):
jadmanski0afbb632008-06-06 21:10:57 +00001091 pass