blob: c35f4a1ed1e3b695ef4218170d0466df5d7d5d81 [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
mbligh09108442008-10-15 16:27:38 +0000360 # If the packaging system is being used, add the repository list.
mblighddc9a402010-01-15 20:33:34 +0000361 repos = None
mblighc5ddfd12008-08-04 17:15:00 +0000362 try:
mblighc5ddfd12008-08-04 17:15:00 +0000363 c = global_config.global_config
364 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list)
jadmanskiede7e242009-08-10 15:43:33 +0000365 repos.reverse() # high priority packages should be added last
mbligh76d19f72008-10-15 16:24:43 +0000366 pkgmgr = packages.PackageManager('autotest', hostname=host.hostname,
367 repo_urls=repos)
jadmanskib1a51132009-08-07 16:45:50 +0000368 prologue_lines.append('job.add_repository(%s)\n' % repos)
mblighc5ddfd12008-08-04 17:15:00 +0000369 except global_config.ConfigError, e:
mblighddc9a402010-01-15 20:33:34 +0000370 # If repos is defined packaging is enabled so log the error
371 if repos:
372 logging.error(e)
mblighc5ddfd12008-08-04 17:15:00 +0000373
jadmanskie2eef7b2009-03-03 23:55:13 +0000374 # on full-size installs, turn on any profilers the server is using
jadmanski27b52912009-08-14 17:09:15 +0000375 if not atrun.background:
jadmanskie2eef7b2009-03-03 23:55:13 +0000376 running_profilers = host.job.profilers.add_log.iteritems()
377 for profiler, (args, dargs) in running_profilers:
378 call_args = [repr(profiler)]
379 call_args += [repr(arg) for arg in args]
380 call_args += ["%s=%r" % item for item in dargs.iteritems()]
381 prologue_lines.append("job.profilers.add(%s)\n"
382 % ", ".join(call_args))
383 cfile = "".join(prologue_lines)
384
mbligh09108442008-10-15 16:27:38 +0000385 cfile += open(tmppath).read()
386 open(tmppath, "w").write(cfile)
mblighc5ddfd12008-08-04 17:15:00 +0000387
jadmanskic09fc152008-10-15 17:56:59 +0000388 # Create and copy state file to remote_control_file + '.state'
mblighfc3da5b2010-01-06 18:37:22 +0000389 state_file = host.job.preprocess_client_state()
mblighfbf73ae2009-12-19 05:22:42 +0000390 host.send_file(state_file, atrun.remote_control_file + '.init.state')
jadmanskic09fc152008-10-15 17:56:59 +0000391 os.remove(state_file)
392
mblighc5ddfd12008-08-04 17:15:00 +0000393 # Copy control_file to remote_control_file on the host
jadmanski0afbb632008-06-06 21:10:57 +0000394 host.send_file(tmppath, atrun.remote_control_file)
395 if os.path.abspath(tmppath) != os.path.abspath(control_file):
396 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000397
jadmanski6bb32d72009-03-19 20:25:24 +0000398 atrun.execute_control(
jadmanski6dadd832009-02-05 23:39:27 +0000399 timeout=timeout,
400 client_disconnect_timeout=client_disconnect_timeout)
jadmanski23afbec2008-09-17 18:12:07 +0000401
402
jadmanski0afbb632008-06-06 21:10:57 +0000403 def run_timed_test(self, test_name, results_dir='.', host=None,
jadmanskic98c4702009-01-05 15:50:06 +0000404 timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000405 """
406 Assemble a tiny little control file to just run one test,
407 and run it as an autotest client-side test
408 """
409 if not host:
410 host = self.host
411 if not self.installed:
412 self.install(host)
413 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
414 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
415 control = "job.run_test(%s)\n" % cmd
jadmanskic98c4702009-01-05 15:50:06 +0000416 self.run(control, results_dir, host, timeout=timeout)
mbligh0e4613b2007-10-29 16:55:07 +0000417
418
jadmanskic98c4702009-01-05 15:50:06 +0000419 def run_test(self, test_name, results_dir='.', host=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000420 self.run_timed_test(test_name, results_dir, host, timeout=None,
jadmanskic98c4702009-01-05 15:50:06 +0000421 *args, **dargs)
mblighd54832b2007-07-25 16:46:56 +0000422
423
mblighdcd57a82007-07-11 23:06:47 +0000424class _Run(object):
jadmanski0afbb632008-06-06 21:10:57 +0000425 """
426 Represents a run of autotest control file. This class maintains
427 all the state necessary as an autotest control file is executed.
mblighdcd57a82007-07-11 23:06:47 +0000428
jadmanski0afbb632008-06-06 21:10:57 +0000429 It is not intended to be used directly, rather control files
430 should be run using the run method in Autotest.
431 """
mblighb3c0c912008-11-27 00:32:45 +0000432 def __init__(self, host, results_dir, tag, parallel_flag, background):
jadmanski0afbb632008-06-06 21:10:57 +0000433 self.host = host
434 self.results_dir = results_dir
435 self.env = host.env
436 self.tag = tag
437 self.parallel_flag = parallel_flag
mblighb3c0c912008-11-27 00:32:45 +0000438 self.background = background
showardad812bf2009-10-20 23:49:56 +0000439 self.autodir = Autotest.get_installed_autodir(self.host)
mbligh78bf5352008-07-11 20:27:36 +0000440 control = os.path.join(self.autodir, 'control')
jadmanski0afbb632008-06-06 21:10:57 +0000441 if tag:
mbligh78bf5352008-07-11 20:27:36 +0000442 control += '.' + tag
443 self.manual_control_file = control
444 self.remote_control_file = control + '.autoserv'
lmr6d08b3c2009-11-18 19:26:38 +0000445 self.config_file = os.path.join(self.autodir, 'global_config.ini')
mblighdc735a22007-08-02 16:54:37 +0000446
447
jadmanski0afbb632008-06-06 21:10:57 +0000448 def verify_machine(self):
449 binary = os.path.join(self.autodir, 'bin/autotest')
450 try:
451 self.host.run('ls %s > /dev/null 2>&1' % binary)
452 except:
lmrd6d27ed2009-12-08 19:58:33 +0000453 raise error.AutoservInstallError(
454 "Autotest does not appear to be installed")
mblighdc735a22007-08-02 16:54:37 +0000455
jadmanski0afbb632008-06-06 21:10:57 +0000456 if not self.parallel_flag:
457 tmpdir = os.path.join(self.autodir, 'tmp')
458 download = os.path.join(self.autodir, 'tests/download')
459 self.host.run('umount %s' % tmpdir, ignore_status=True)
460 self.host.run('umount %s' % download, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000461
jadmanski6dadd832009-02-05 23:39:27 +0000462
463 def get_base_cmd_args(self, section):
showard234b2962009-07-28 20:02:30 +0000464 args = ['--verbose']
jadmanski0afbb632008-06-06 21:10:57 +0000465 if section > 0:
jadmanski6dadd832009-02-05 23:39:27 +0000466 args.append('-c')
jadmanski0afbb632008-06-06 21:10:57 +0000467 if self.tag:
jadmanski6dadd832009-02-05 23:39:27 +0000468 args.append('-t %s' % self.tag)
jadmanski0afbb632008-06-06 21:10:57 +0000469 if self.host.job.use_external_logging():
jadmanski6dadd832009-02-05 23:39:27 +0000470 args.append('-l')
mblighce955fc2009-08-24 21:59:02 +0000471 if self.host.hostname:
472 args.append('--hostname=%s' % self.host.hostname)
mbligh0d0f67d2009-11-06 03:15:03 +0000473 args.append('--user=%s' % self.host.job.user)
mblighce955fc2009-08-24 21:59:02 +0000474
jadmanski6dadd832009-02-05 23:39:27 +0000475 args.append(self.remote_control_file)
476 return args
477
478
479 def get_background_cmd(self, section):
480 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotest_client')]
481 cmd += self.get_base_cmd_args(section)
482 cmd.append('>/dev/null 2>/dev/null &')
483 return ' '.join(cmd)
484
485
486 def get_daemon_cmd(self, section, monitor_dir):
487 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotestd'),
488 monitor_dir, '-H autoserv']
489 cmd += self.get_base_cmd_args(section)
490 cmd.append('>/dev/null 2>/dev/null </dev/null &')
491 return ' '.join(cmd)
492
493
494 def get_monitor_cmd(self, monitor_dir, stdout_read, stderr_read):
495 cmd = [os.path.join(self.autodir, 'bin', 'autotestd_monitor'),
496 monitor_dir, str(stdout_read), str(stderr_read)]
jadmanski0afbb632008-06-06 21:10:57 +0000497 return ' '.join(cmd)
mblighadf2aab2007-11-29 18:16:43 +0000498
mblighd8b39252008-03-20 21:15:03 +0000499
jadmanski4d03cf62010-03-04 18:32:28 +0000500 def get_client_log(self):
501 """Find what the "next" client.* prefix should be
502
503 @returns A string of the form client.INTEGER that should be prefixed
504 to all client debug log files.
505 """
506 max_digit = -1
507 debug_dir = os.path.join(self.results_dir, 'debug')
508 client_logs = glob.glob(os.path.join(debug_dir, 'client.*.*'))
509 for log in client_logs:
510 _, number, _ = log.split('.', 2)
511 if number.isdigit():
512 max_digit = max(max_digit, int(number))
513 return 'client.%d' % (max_digit + 1)
514
515
516 def copy_client_config_file(self, client_log_prefix=None):
517 """
518 Create and copy the client config file based on the server config.
519
520 @param client_log_prefix: Optional prefix to prepend to log files.
521 """
522 client_config_file = self._create_client_config_file(client_log_prefix)
523 self.host.send_file(client_config_file, self.config_file)
524 os.remove(client_config_file)
525
526
527 def _create_client_config_file(self, client_log_prefix=None):
528 """
529 Create a temporary file with the [CLIENT] section configuration values
530 taken from the server global_config.ini.
531
532 @param client_log_prefix: Optional prefix to prepend to log files.
533
534 @return: Path of the temporary file generated.
535 """
536 config = global_config.global_config.get_section_values('CLIENT')
537 if client_log_prefix:
538 config.set('CLIENT', 'default_logging_name', client_log_prefix)
539 return self._create_aux_file(config.write)
540
541
542 def _create_aux_file(self, func, *args):
543 """
544 Creates a temporary file and writes content to it according to a
545 content creation function. The file object is appended to *args, which
546 is then passed to the content creation function
547
548 @param func: Function that will be used to write content to the
549 temporary file.
550 @param *args: List of parameters that func takes.
551 @return: Path to the temporary file that was created.
552 """
553 fd, path = tempfile.mkstemp(dir=self.host.job.tmpdir)
554 aux_file = os.fdopen(fd, "w")
555 try:
556 list_args = list(args)
557 list_args.append(aux_file)
558 func(*list_args)
559 finally:
560 aux_file.close()
561 return path
mblighd8b39252008-03-20 21:15:03 +0000562
563
jadmanskib264ed02009-01-12 23:54:27 +0000564 @staticmethod
565 def is_client_job_finished(last_line):
566 return bool(re.match(r'^END .*\t----\t----\t.*$', last_line))
567
568
569 @staticmethod
570 def is_client_job_rebooting(last_line):
571 return bool(re.match(r'^\t*GOOD\t----\treboot\.start.*$', last_line))
572
573
jadmanskia61edad2009-05-21 22:17:49 +0000574 def log_unexpected_abort(self, stderr_redirector):
575 stderr_redirector.flush_all_buffers()
jadmanskib264ed02009-01-12 23:54:27 +0000576 msg = "Autotest client terminated unexpectedly"
577 self.host.job.record("END ABORT", None, None, msg)
578
579
jadmanski6dadd832009-02-05 23:39:27 +0000580 def _execute_in_background(self, section, timeout):
581 full_cmd = self.get_background_cmd(section)
582 devnull = open(os.devnull, "w")
mblighd8b39252008-03-20 21:15:03 +0000583
jadmanski4d03cf62010-03-04 18:32:28 +0000584 self.copy_client_config_file(self.get_client_log())
585
mbligh0d0f67d2009-11-06 03:15:03 +0000586 self.host.job.push_execution_context(self.results_dir)
jadmanski0afbb632008-06-06 21:10:57 +0000587 try:
jadmanski0afbb632008-06-06 21:10:57 +0000588 result = self.host.run(full_cmd, ignore_status=True,
589 timeout=timeout,
jadmanski6dadd832009-02-05 23:39:27 +0000590 stdout_tee=devnull,
591 stderr_tee=devnull)
jadmanski0afbb632008-06-06 21:10:57 +0000592 finally:
mbligh0d0f67d2009-11-06 03:15:03 +0000593 self.host.job.pop_execution_context()
jadmanski6dadd832009-02-05 23:39:27 +0000594
595 return result
596
597
598 @staticmethod
599 def _strip_stderr_prologue(stderr):
600 """Strips the 'standard' prologue that get pre-pended to every
601 remote command and returns the text that was actually written to
602 stderr by the remote command."""
603 stderr_lines = stderr.split("\n")[1:]
604 if not stderr_lines:
605 return ""
606 elif stderr_lines[0].startswith("NOTE: autotestd_monitor"):
607 del stderr_lines[0]
608 return "\n".join(stderr_lines)
609
610
611 def _execute_daemon(self, section, timeout, stderr_redirector,
612 client_disconnect_timeout):
613 monitor_dir = self.host.get_tmp_dir()
614 daemon_cmd = self.get_daemon_cmd(section, monitor_dir)
jadmanski4d03cf62010-03-04 18:32:28 +0000615
616 # grab the location for the server-side client log file
617 client_log_prefix = self.get_client_log()
618 client_log_path = os.path.join(self.results_dir, 'debug',
619 client_log_prefix + '.log')
620 client_log = open(client_log_path, 'w', 0)
621 self.copy_client_config_file(client_log_prefix)
jadmanski6dadd832009-02-05 23:39:27 +0000622
623 stdout_read = stderr_read = 0
mbligh0d0f67d2009-11-06 03:15:03 +0000624 self.host.job.push_execution_context(self.results_dir)
jadmanski6dadd832009-02-05 23:39:27 +0000625 try:
626 self.host.run(daemon_cmd, ignore_status=True, timeout=timeout)
jadmanski91d56a92009-04-01 15:20:40 +0000627 disconnect_warnings = []
jadmanski6dadd832009-02-05 23:39:27 +0000628 while True:
629 monitor_cmd = self.get_monitor_cmd(monitor_dir, stdout_read,
630 stderr_read)
631 try:
632 result = self.host.run(monitor_cmd, ignore_status=True,
633 timeout=timeout,
634 stdout_tee=client_log,
635 stderr_tee=stderr_redirector)
636 except error.AutoservRunError, e:
637 result = e.result_obj
638 result.exit_status = None
jadmanski91d56a92009-04-01 15:20:40 +0000639 disconnect_warnings.append(e.description)
640
jadmanski6dadd832009-02-05 23:39:27 +0000641 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000642 "Autotest client was disconnected: %s" % e.description,
643 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000644 except error.AutoservSSHTimeout:
645 result = utils.CmdResult(monitor_cmd, "", "", None, 0)
646 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000647 "Attempt to connect to Autotest client timed out",
648 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000649
650 stdout_read += len(result.stdout)
651 stderr_read += len(self._strip_stderr_prologue(result.stderr))
652
653 if result.exit_status is not None:
654 return result
655 elif not self.host.wait_up(client_disconnect_timeout):
656 raise error.AutoservSSHTimeout(
657 "client was disconnected, reconnect timed out")
658 finally:
jadmanski4d03cf62010-03-04 18:32:28 +0000659 client_log.close()
mbligh0d0f67d2009-11-06 03:15:03 +0000660 self.host.job.pop_execution_context()
jadmanski6dadd832009-02-05 23:39:27 +0000661
662
663 def execute_section(self, section, timeout, stderr_redirector,
664 client_disconnect_timeout):
showardb18134f2009-03-20 20:52:18 +0000665 logging.info("Executing %s/bin/autotest %s/control phase %d",
666 self.autodir, self.autodir, section)
jadmanski6dadd832009-02-05 23:39:27 +0000667
668 if self.background:
669 result = self._execute_in_background(section, timeout)
670 else:
671 result = self._execute_daemon(section, timeout, stderr_redirector,
672 client_disconnect_timeout)
673
674 last_line = stderr_redirector.last_line
mbligh2bf2db62007-11-27 00:53:18 +0000675
jadmanskib264ed02009-01-12 23:54:27 +0000676 # check if we failed hard enough to warrant an exception
jadmanski0afbb632008-06-06 21:10:57 +0000677 if result.exit_status == 1:
jadmanskib264ed02009-01-12 23:54:27 +0000678 err = error.AutotestRunError("client job was aborted")
679 elif not self.background and not result.stderr:
680 err = error.AutotestRunError(
jadmanskie4130532009-03-17 18:01:28 +0000681 "execute_section %s failed to return anything\n"
682 "stdout:%s\n" % (section, result.stdout))
jadmanskib264ed02009-01-12 23:54:27 +0000683 else:
684 err = None
mbligh0e4613b2007-10-29 16:55:07 +0000685
jadmanskib264ed02009-01-12 23:54:27 +0000686 # log something if the client failed AND never finished logging
687 if err and not self.is_client_job_finished(last_line):
jadmanskia61edad2009-05-21 22:17:49 +0000688 self.log_unexpected_abort(stderr_redirector)
jadmanskib264ed02009-01-12 23:54:27 +0000689
690 if err:
691 raise err
692 else:
693 return stderr_redirector.last_line
jadmanski4600e342008-10-29 22:54:00 +0000694
695
jadmanskic0354912010-01-12 15:57:29 +0000696 def _wait_for_reboot(self, old_boot_id):
showardb18134f2009-03-20 20:52:18 +0000697 logging.info("Client is rebooting")
698 logging.info("Waiting for client to halt")
jadmanskic0354912010-01-12 15:57:29 +0000699 if not self.host.wait_down(HALT_TIME, old_boot_id=old_boot_id):
jadmanski4600e342008-10-29 22:54:00 +0000700 err = "%s failed to shutdown after %d"
701 err %= (self.host.hostname, HALT_TIME)
702 raise error.AutotestRunError(err)
showardb18134f2009-03-20 20:52:18 +0000703 logging.info("Client down, waiting for restart")
jadmanski4600e342008-10-29 22:54:00 +0000704 if not self.host.wait_up(BOOT_TIME):
705 # since reboot failed
706 # hardreset the machine once if possible
707 # before failing this control file
708 warning = "%s did not come back up, hard resetting"
709 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000710 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000711 try:
712 self.host.hardreset(wait=False)
mblighd99d3b272008-12-22 14:41:27 +0000713 except (AttributeError, error.AutoservUnsupportedError):
jadmanski4600e342008-10-29 22:54:00 +0000714 warning = "Hard reset unsupported on %s"
715 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000716 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000717 raise error.AutotestRunError("%s failed to boot after %ds" %
718 (self.host.hostname, BOOT_TIME))
719 self.host.reboot_followup()
mblighdc735a22007-08-02 16:54:37 +0000720
721
jadmanski6dadd832009-02-05 23:39:27 +0000722 def execute_control(self, timeout=None, client_disconnect_timeout=None):
jadmanski6bb32d72009-03-19 20:25:24 +0000723 if not self.background:
724 collector = log_collector(self.host, self.tag, self.results_dir)
725 hostname = self.host.hostname
726 remote_results = collector.client_results_dir
727 local_results = collector.server_results_dir
728 self.host.job.add_client_log(hostname, remote_results,
729 local_results)
730
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,
jadmanski6dadd832009-02-05 23:39:27 +0000743 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 "
765 "client: %s\n") % last
766 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)
mblighdcd57a82007-07-11 23:06:47 +0000778
jadmanski0afbb632008-06-06 21:10:57 +0000779 # should only get here if we timed out
780 assert timeout
781 raise error.AutotestTimeoutError()
mbligh0e4613b2007-10-29 16:55:07 +0000782
mblighdcd57a82007-07-11 23:06:47 +0000783
jadmanski043e1132008-11-19 17:10:32 +0000784class log_collector(object):
785 def __init__(self, host, client_tag, results_dir):
786 self.host = host
787 if not client_tag:
788 client_tag = "default"
789 self.client_results_dir = os.path.join(host.get_autodir(), "results",
790 client_tag)
791 self.server_results_dir = results_dir
792
793
794 def collect_client_job_results(self):
795 """ A method that collects all the current results of a running
796 client job into the results dir. By default does nothing as no
797 client job is running, but when running a client job you can override
798 this with something that will actually do something. """
799
800 # make an effort to wait for the machine to come up
801 try:
802 self.host.wait_up(timeout=30)
803 except error.AutoservError:
804 # don't worry about any errors, we'll try and
805 # get the results anyway
806 pass
807
jadmanski043e1132008-11-19 17:10:32 +0000808 # Copy all dirs in default to results_dir
809 try:
jadmanski043e1132008-11-19 17:10:32 +0000810 self.host.get_file(self.client_results_dir + '/',
mbligh45561782009-05-11 21:14:34 +0000811 self.server_results_dir, preserve_symlinks=True)
jadmanski043e1132008-11-19 17:10:32 +0000812 except Exception:
813 # well, don't stop running just because we couldn't get logs
showardb18134f2009-03-20 20:52:18 +0000814 e_msg = "Unexpected error copying test result logs, continuing ..."
815 logging.error(e_msg)
jadmanski043e1132008-11-19 17:10:32 +0000816 traceback.print_exc(file=sys.stdout)
817
818
jadmanski4d03cf62010-03-04 18:32:28 +0000819 def remove_redundant_client_logs(self):
820 """Remove client.*.log files in favour of client.*.DEBUG files."""
821 debug_dir = os.path.join(self.server_results_dir, 'debug')
822 debug_files = [f for f in os.listdir(debug_dir)
823 if re.search(r'^client\.\d+\.DEBUG$', f)]
824 for debug_file in debug_files:
825 log_file = debug_file.replace('DEBUG', 'log')
826 log_file = os.path.join(debug_dir, log_file)
827 if os.path.exists(log_file):
828 os.remove(log_file)
829
830
jadmanski043e1132008-11-19 17:10:32 +0000831# a file-like object for catching stderr from an autotest client and
832# extracting status logs from it
833class client_logger(object):
834 """Partial file object to write to both stdout and
835 the status log file. We only implement those methods
836 utils.run() actually calls.
837
838 Note that this class is fairly closely coupled with server_job, as it
839 uses special job._ methods to actually carry out the loggging.
840 """
841 status_parser = re.compile(r"^AUTOTEST_STATUS:([^:]*):(.*)$")
842 test_complete_parser = re.compile(r"^AUTOTEST_TEST_COMPLETE:(.*)$")
jadmanskib1a51132009-08-07 16:45:50 +0000843 fetch_package_parser = re.compile(
844 r"^AUTOTEST_FETCH_PACKAGE:([^:]*):([^:]*):(.*)$")
jadmanski043e1132008-11-19 17:10:32 +0000845 extract_indent = re.compile(r"^(\t*).*$")
jadmanskiefe4ebf2009-05-21 22:12:30 +0000846 extract_timestamp = re.compile(r".*\ttimestamp=(\d+)\t.*$")
jadmanski043e1132008-11-19 17:10:32 +0000847
848 def __init__(self, host, tag, server_results_dir):
849 self.host = host
850 self.job = host.job
851 self.log_collector = log_collector(host, tag, server_results_dir)
852 self.leftover = ""
853 self.last_line = ""
jadmanskiefe4ebf2009-05-21 22:12:30 +0000854 self.newest_timestamp = float("-inf")
jadmanski043e1132008-11-19 17:10:32 +0000855 self.logs = {}
jadmanski6dadd832009-02-05 23:39:27 +0000856 self.server_warnings = []
jadmanski043e1132008-11-19 17:10:32 +0000857
858
jadmanskiefe4ebf2009-05-21 22:12:30 +0000859 def _update_timestamp(self, line):
860 match = self.extract_timestamp.search(line)
861 if match:
862 self.newest_timestamp = max(self.newest_timestamp,
863 int(match.group(1)))
864
865
jadmanski043e1132008-11-19 17:10:32 +0000866 def _process_log_dict(self, log_dict):
867 log_list = log_dict.pop("logs", [])
868 for key in sorted(log_dict.iterkeys()):
869 log_list += self._process_log_dict(log_dict.pop(key))
870 return log_list
871
872
873 def _process_logs(self):
874 """Go through the accumulated logs in self.log and print them
875 out to stdout and the status log. Note that this processes
876 logs in an ordering where:
877
878 1) logs to different tags are never interleaved
879 2) logs to x.y come before logs to x.y.z for all z
880 3) logs to x.y come before x.z whenever y < z
881
882 Note that this will in general not be the same as the
883 chronological ordering of the logs. However, if a chronological
884 ordering is desired that one can be reconstructed from the
885 status log by looking at timestamp lines."""
886 log_list = self._process_log_dict(self.logs)
887 for line in log_list:
888 self.job._record_prerendered(line + '\n')
889 if log_list:
890 self.last_line = log_list[-1]
891
892
893 def _process_quoted_line(self, tag, line):
894 """Process a line quoted with an AUTOTEST_STATUS flag. If the
895 tag is blank then we want to push out all the data we've been
896 building up in self.logs, and then the newest line. If the
897 tag is not blank, then push the line into the logs for handling
898 later."""
showardb18134f2009-03-20 20:52:18 +0000899 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +0000900 if tag == "":
901 self._process_logs()
902 self.job._record_prerendered(line + '\n')
903 self.last_line = line
904 else:
905 tag_parts = [int(x) for x in tag.split(".")]
906 log_dict = self.logs
907 for part in tag_parts:
908 log_dict = log_dict.setdefault(part, {})
909 log_list = log_dict.setdefault("logs", [])
910 log_list.append(line)
911
912
jadmanskif37df842009-02-11 00:03:26 +0000913 def _process_info_line(self, line):
914 """Check if line is an INFO line, and if it is, interpret any control
915 messages (e.g. enabling/disabling warnings) that it may contain."""
916 match = re.search(r"^\t*INFO\t----\t----(.*)\t[^\t]*$", line)
917 if not match:
918 return # not an INFO line
919 for field in match.group(1).split('\t'):
920 if field.startswith("warnings.enable="):
jadmanski16a7ff72009-04-01 18:19:53 +0000921 func = self.job.warning_manager.enable_warnings
jadmanskif37df842009-02-11 00:03:26 +0000922 elif field.startswith("warnings.disable="):
jadmanski16a7ff72009-04-01 18:19:53 +0000923 func = self.job.warning_manager.disable_warnings
jadmanskif37df842009-02-11 00:03:26 +0000924 else:
925 continue
926 warning_type = field.split("=", 1)[1]
jadmanski16a7ff72009-04-01 18:19:53 +0000927 func(warning_type)
jadmanskif37df842009-02-11 00:03:26 +0000928
929
jadmanski043e1132008-11-19 17:10:32 +0000930 def _process_line(self, line):
931 """Write out a line of data to the appropriate stream. Status
932 lines sent by autotest will be prepended with
933 "AUTOTEST_STATUS", and all other lines are ssh error
934 messages."""
935 status_match = self.status_parser.search(line)
936 test_complete_match = self.test_complete_parser.search(line)
jadmanskib1a51132009-08-07 16:45:50 +0000937 fetch_package_match = self.fetch_package_parser.search(line)
jadmanski043e1132008-11-19 17:10:32 +0000938 if status_match:
939 tag, line = status_match.groups()
jadmanskif37df842009-02-11 00:03:26 +0000940 self._process_info_line(line)
jadmanski043e1132008-11-19 17:10:32 +0000941 self._process_quoted_line(tag, line)
942 elif test_complete_match:
jadmanskifcc0d5d2009-02-12 21:52:54 +0000943 self._process_logs()
jadmanski043e1132008-11-19 17:10:32 +0000944 fifo_path, = test_complete_match.groups()
mbligh060c4712009-12-29 02:43:35 +0000945 try:
946 self.log_collector.collect_client_job_results()
947 self.host.run("echo A > %s" % fifo_path)
948 except Exception:
949 msg = "Post-test log collection failed, continuing anyway"
950 logging.exception(msg)
jadmanskib1a51132009-08-07 16:45:50 +0000951 elif fetch_package_match:
952 pkg_name, dest_path, fifo_path = fetch_package_match.groups()
jadmanskiede7e242009-08-10 15:43:33 +0000953 serve_packages = global_config.global_config.get_config_value(
954 "PACKAGES", "serve_packages_from_autoserv", type=bool)
955 if serve_packages and pkg_name.endswith(".tar.bz2"):
956 try:
957 self._send_tarball(pkg_name, dest_path)
958 except Exception:
959 msg = "Package tarball creation failed, continuing anyway"
960 logging.exception(msg)
mbligh060c4712009-12-29 02:43:35 +0000961 try:
962 self.host.run("echo B > %s" % fifo_path)
963 except Exception:
964 msg = "Package tarball installation failed, continuing anyway"
965 logging.exception(msg)
jadmanski043e1132008-11-19 17:10:32 +0000966 else:
showardb18134f2009-03-20 20:52:18 +0000967 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +0000968
969
jadmanskiede7e242009-08-10 15:43:33 +0000970 def _send_tarball(self, pkg_name, remote_dest):
971 name, pkg_type = self.job.pkgmgr.parse_tarball_name(pkg_name)
972 src_dirs = []
973 if pkg_type == 'test':
974 src_dirs += [os.path.join(self.job.clientdir, 'site_tests', name),
975 os.path.join(self.job.clientdir, 'tests', name)]
976 elif pkg_type == 'profiler':
977 src_dirs += [os.path.join(self.job.clientdir, 'profilers', name)]
978 elif pkg_type == 'dep':
979 src_dirs += [os.path.join(self.job.clientdir, 'deps', name)]
980 elif pkg_type == 'client':
981 return # you must already have a client to hit this anyway
982 else:
983 return # no other types are supported
984
985 # iterate over src_dirs until we find one that exists, then tar it
986 for src_dir in src_dirs:
987 if os.path.exists(src_dir):
988 try:
989 logging.info('Bundling %s into %s', src_dir, pkg_name)
990 temp_dir = autotemp.tempdir(unique_id='autoserv-packager',
991 dir=self.job.tmpdir)
992 tarball_path = self.job.pkgmgr.tar_package(
mblighbccad482009-08-24 22:08:31 +0000993 pkg_name, src_dir, temp_dir.name, " .")
jadmanskiede7e242009-08-10 15:43:33 +0000994 self.host.send_file(tarball_path, remote_dest)
995 finally:
996 temp_dir.clean()
997 return
998
999
jadmanski043e1132008-11-19 17:10:32 +00001000 def _format_warnings(self, last_line, warnings):
1001 # use the indentation of whatever the last log line was
1002 indent = self.extract_indent.match(last_line).group(1)
1003 # if the last line starts a new group, add an extra indent
1004 if last_line.lstrip('\t').startswith("START\t"):
1005 indent += '\t'
1006 return [self.job._render_record("WARN", None, None, msg,
1007 timestamp, indent).rstrip('\n')
1008 for timestamp, msg in warnings]
1009
1010
1011 def _process_warnings(self, last_line, log_dict, warnings):
1012 if log_dict.keys() in ([], ["logs"]):
1013 # there are no sub-jobs, just append the warnings here
1014 warnings = self._format_warnings(last_line, warnings)
1015 log_list = log_dict.setdefault("logs", [])
1016 log_list += warnings
1017 for warning in warnings:
1018 sys.stdout.write(warning + '\n')
1019 else:
1020 # there are sub-jobs, so put the warnings in there
1021 log_list = log_dict.get("logs", [])
1022 if log_list:
1023 last_line = log_list[-1]
1024 for key in sorted(log_dict.iterkeys()):
1025 if key != "logs":
1026 self._process_warnings(last_line,
1027 log_dict[key],
1028 warnings)
1029
jadmanskif37df842009-02-11 00:03:26 +00001030
jadmanski91d56a92009-04-01 15:20:40 +00001031 def log_warning(self, msg, warning_type):
jadmanski6dadd832009-02-05 23:39:27 +00001032 """Injects a WARN message into the current status logging stream."""
jadmanski91d56a92009-04-01 15:20:40 +00001033 timestamp = int(time.time())
1034 if self.job.warning_manager.is_valid(timestamp, warning_type):
1035 self.server_warnings.append((timestamp, msg))
jadmanski6dadd832009-02-05 23:39:27 +00001036
jadmanski043e1132008-11-19 17:10:32 +00001037
1038 def write(self, data):
1039 # first check for any new console warnings
mbligh060c4712009-12-29 02:43:35 +00001040 self.server_warnings = self.job._read_warnings() + self.server_warnings
1041 warnings = self.server_warnings
jadmanski6dadd832009-02-05 23:39:27 +00001042 warnings.sort() # sort into timestamp order
mbligh060c4712009-12-29 02:43:35 +00001043 # now start processing the existng buffer and the new data
jadmanski043e1132008-11-19 17:10:32 +00001044 data = self.leftover + data
mbligh060c4712009-12-29 02:43:35 +00001045 lines = data.split('\n')
1046 processed_lines = 0
1047 try:
1048 # process all the buffered data except the last line
1049 # ignore the last line since we may not have all of it yet
1050 for line in lines[:-1]:
1051 self._update_timestamp(line)
1052 # output any warnings between now and the next status line
1053 old_warnings = [(timestamp, msg) for timestamp, msg in warnings
1054 if timestamp < self.newest_timestamp]
jadmanskicb777f12010-03-23 21:17:52 +00001055 self._process_warnings(self.last_line, self.logs, old_warnings)
mbligh060c4712009-12-29 02:43:35 +00001056 del warnings[:len(old_warnings)]
1057 # now process the line itself
1058 self._process_line(line)
1059 processed_lines += 1
1060 finally:
1061 # save any unprocessed lines for future processing
1062 self.leftover = '\n'.join(lines[processed_lines:])
jadmanski043e1132008-11-19 17:10:32 +00001063
1064
1065 def flush(self):
1066 sys.stdout.flush()
1067
1068
jadmanskia61edad2009-05-21 22:17:49 +00001069 def flush_all_buffers(self):
jadmanski043e1132008-11-19 17:10:32 +00001070 if self.leftover:
1071 self._process_line(self.leftover)
jadmanskia61edad2009-05-21 22:17:49 +00001072 self.leftover = ""
jadmanskiefe4ebf2009-05-21 22:12:30 +00001073 self._process_warnings(self.last_line, self.logs, self.server_warnings)
jadmanski043e1132008-11-19 17:10:32 +00001074 self._process_logs()
1075 self.flush()
1076
1077
jadmanskia61edad2009-05-21 22:17:49 +00001078 def close(self):
1079 self.flush_all_buffers()
1080
1081
mbligha7007722009-01-13 00:37:11 +00001082SiteAutotest = client_utils.import_site_class(
1083 __file__, "autotest_lib.server.site_autotest", "SiteAutotest",
1084 BaseAutotest)
mblighd8b39252008-03-20 21:15:03 +00001085
showardad812bf2009-10-20 23:49:56 +00001086
mblighd8b39252008-03-20 21:15:03 +00001087class Autotest(SiteAutotest):
jadmanski0afbb632008-06-06 21:10:57 +00001088 pass