blob: 5619ec1dd7187bb7aadf4b1dde745500faca9d8d [file] [log] [blame]
mblighdcd57a82007-07-11 23:06:47 +00001# Copyright 2007 Google Inc. Released under the GPL v2
2
showardb18134f2009-03-20 20:52:18 +00003import re, os, sys, traceback, subprocess, tempfile, time, pickle, glob, logging
jadmanski043e1132008-11-19 17:10:32 +00004from autotest_lib.server import installable_object, utils
jadmanskiede7e242009-08-10 15:43:33 +00005from autotest_lib.client.common_lib import log, error, autotemp
mbligh09108442008-10-15 16:27:38 +00006from autotest_lib.client.common_lib import global_config, packages
mbligha7007722009-01-13 00:37:11 +00007from autotest_lib.client.common_lib import utils as client_utils
mbligh3c7a1502008-07-24 18:08:47 +00008
mblighdcd57a82007-07-11 23:06:47 +00009AUTOTEST_SVN = 'svn://test.kernel.org/autotest/trunk/client'
10AUTOTEST_HTTP = 'http://test.kernel.org/svn/autotest/trunk/client'
11
mbligh483d1da2009-08-24 22:07:14 +000012# This should probably live in global_config.ini
13CLIENT_AUTODIR_PATHS = ('/usr/local/autotest', '/home/autotest')
14
mblighdcd57a82007-07-11 23:06:47 +000015# Timeouts for powering down and up respectively
16HALT_TIME = 300
mbligh07c1eac2007-11-05 18:39:29 +000017BOOT_TIME = 1800
jadmanskiec859142008-05-29 21:33:39 +000018CRASH_RECOVERY_TIME = 9000
mbligh0e4613b2007-10-29 16:55:07 +000019
mblighdcd57a82007-07-11 23:06:47 +000020
mblighd8b39252008-03-20 21:15:03 +000021class BaseAutotest(installable_object.InstallableObject):
jadmanski0afbb632008-06-06 21:10:57 +000022 """
23 This class represents the Autotest program.
mblighdcd57a82007-07-11 23:06:47 +000024
jadmanski0afbb632008-06-06 21:10:57 +000025 Autotest is used to run tests automatically and collect the results.
26 It also supports profilers.
mblighdcd57a82007-07-11 23:06:47 +000027
jadmanski0afbb632008-06-06 21:10:57 +000028 Implementation details:
29 This is a leaf class in an abstract class hierarchy, it must
30 implement the unimplemented methods in parent classes.
31 """
mbligh119c12a2007-11-12 22:13:44 +000032
jadmanski0afbb632008-06-06 21:10:57 +000033 def __init__(self, host = None):
34 self.host = host
35 self.got = False
36 self.installed = False
37 self.serverdir = utils.get_server_dir()
38 super(BaseAutotest, self).__init__()
mblighc8949b82007-07-23 16:33:58 +000039
mblighdc735a22007-08-02 16:54:37 +000040
jadmanskif22fea82008-11-26 20:57:07 +000041 install_in_tmpdir = False
42 @classmethod
43 def set_install_in_tmpdir(cls, flag):
44 """ Sets a flag that controls whether or not Autotest should by
45 default be installed in a "standard" directory (e.g.
46 /home/autotest, /usr/local/autotest) or a temporary directory. """
47 cls.install_in_tmpdir = flag
48
49
50 def _get_install_dir(self, host):
51 """ Determines the location where autotest should be installed on
52 host. If self.install_in_tmpdir is set, it will return a unique
53 temporary directory that autotest can be installed in. """
54 try:
55 autodir = _get_autodir(host)
56 except error.AutotestRunError:
57 autodir = '/usr/local/autotest'
58 if self.install_in_tmpdir:
59 autodir = host.get_tmp_dir(parent=autodir)
60 return autodir
61
62
mbligh1b3b3762008-09-25 02:46:34 +000063 @log.record
mblighb3c0c912008-11-27 00:32:45 +000064 def install(self, host=None, autodir=None):
65 self._install(host=host, autodir=autodir)
jadmanski54f90af2008-10-10 16:20:55 +000066
67
jadmanski27b52912009-08-14 17:09:15 +000068 def _install(self, host=None, autodir=None):
jadmanski0afbb632008-06-06 21:10:57 +000069 """
70 Install autotest. If get() was not called previously, an
71 attempt will be made to install from the autotest svn
72 repository.
mbligh9a3f5e52008-05-28 21:21:43 +000073
jadmanski0afbb632008-06-06 21:10:57 +000074 Args:
jadmanski54f90af2008-10-10 16:20:55 +000075 host: a Host instance on which autotest will be installed
76 autodir: location on the remote host to install to
mbligh9a3f5e52008-05-28 21:21:43 +000077
jadmanski0afbb632008-06-06 21:10:57 +000078 Raises:
jadmanski54f90af2008-10-10 16:20:55 +000079 AutoservError: if a tarball was not specified and
80 the target host does not have svn installed in its path"""
jadmanski0afbb632008-06-06 21:10:57 +000081 if not host:
82 host = self.host
83 if not self.got:
84 self.get()
85 host.wait_up(timeout=30)
86 host.setup()
showardb18134f2009-03-20 20:52:18 +000087 logging.info("Installing autotest on %s", host.hostname)
mbligh40f122a2007-11-03 23:08:46 +000088
jadmanski54f90af2008-10-10 16:20:55 +000089 # set up the autotest directory on the remote machine
90 if not autodir:
jadmanskif22fea82008-11-26 20:57:07 +000091 autodir = self._get_install_dir(host)
mbligh0562e652008-08-20 20:11:45 +000092 host.set_autodir(autodir)
jadmanski3c236942009-03-04 17:51:26 +000093 host.run('mkdir -p %s' % utils.sh_escape(autodir))
mbligh40f122a2007-11-03 23:08:46 +000094
jadmanski1c3c07b2009-03-03 23:29:36 +000095 # make sure there are no files in $AUTODIR/results
96 results_path = os.path.join(autodir, 'results')
jadmanski3c236942009-03-04 17:51:26 +000097 host.run('rm -rf %s/*' % utils.sh_escape(results_path),
jadmanski1c3c07b2009-03-03 23:29:36 +000098 ignore_status=True)
99
mblighc5ddfd12008-08-04 17:15:00 +0000100 # Fetch the autotest client from the nearest repository
101 try:
102 c = global_config.global_config
103 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list)
mbligh76d19f72008-10-15 16:24:43 +0000104 pkgmgr = packages.PackageManager(autodir, hostname=host.hostname,
105 repo_urls=repos,
mbligh1e3b0992008-10-14 16:29:54 +0000106 do_locking=False,
107 run_function=host.run,
108 run_function_dargs=dict(timeout=600))
mblighc5ddfd12008-08-04 17:15:00 +0000109 # The packages dir is used to store all the packages that
110 # are fetched on that client. (for the tests,deps etc.
111 # too apart from the client)
112 pkg_dir = os.path.join(autodir, 'packages')
113 # clean up the autodir except for the packages directory
114 host.run('cd %s && ls | grep -v "^packages$"'
115 ' | xargs rm -rf && rm -rf .[^.]*' % autodir)
116 pkgmgr.install_pkg('autotest', 'client', pkg_dir, autodir,
117 preserve_install_dir=True)
118 self.installed = True
119 return
120 except global_config.ConfigError, e:
mblighd0e94982009-07-11 00:15:18 +0000121 logging.error("Could not install autotest using the packaging "
showardb18134f2009-03-20 20:52:18 +0000122 "system: %s", e)
jadmanskib1a51132009-08-07 16:45:50 +0000123 except (error.PackageInstallError, error.AutoservRunError), e:
showardb18134f2009-03-20 20:52:18 +0000124 logging.error("Could not install autotest from %s", repos)
mblighc5ddfd12008-08-04 17:15:00 +0000125
jadmanski0afbb632008-06-06 21:10:57 +0000126 # try to install from file or directory
127 if self.source_material:
128 if os.path.isdir(self.source_material):
jadmanski27b52912009-08-14 17:09:15 +0000129 c = global_config.global_config
130 supports_autoserv_packaging = c.get_config_value(
131 "PACKAGES", "serve_packages_from_autoserv", type=bool)
jadmanski0afbb632008-06-06 21:10:57 +0000132 # Copy autotest recursively
jadmanski27b52912009-08-14 17:09:15 +0000133 if supports_autoserv_packaging:
jadmanski54f90af2008-10-10 16:20:55 +0000134 dirs_to_exclude = set(["tests", "site_tests", "deps",
jadmanski27b52912009-08-14 17:09:15 +0000135 "profilers"])
jadmanski54f90af2008-10-10 16:20:55 +0000136 light_files = [os.path.join(self.source_material, f)
137 for f in os.listdir(self.source_material)
138 if f not in dirs_to_exclude]
mbligh89e258d2008-10-24 13:58:08 +0000139 host.send_file(light_files, autodir, delete_dest=True)
jadmanski54f90af2008-10-10 16:20:55 +0000140
141 # create empty dirs for all the stuff we excluded
142 commands = []
143 for path in dirs_to_exclude:
144 abs_path = os.path.join(autodir, path)
145 abs_path = utils.sh_escape(abs_path)
146 commands.append("mkdir -p '%s'" % abs_path)
147 host.run(';'.join(commands))
148 else:
mbligh89e258d2008-10-24 13:58:08 +0000149 host.send_file(self.source_material, autodir,
150 delete_dest=True)
jadmanski0afbb632008-06-06 21:10:57 +0000151 else:
152 # Copy autotest via tarball
153 e_msg = 'Installation method not yet implemented!'
154 raise NotImplementedError(e_msg)
showardb18134f2009-03-20 20:52:18 +0000155 logging.info("Installation of autotest completed")
jadmanski0afbb632008-06-06 21:10:57 +0000156 self.installed = True
157 return
mbligh91334902007-09-28 01:47:59 +0000158
jadmanski0afbb632008-06-06 21:10:57 +0000159 # if that fails try to install using svn
160 if utils.run('which svn').exit_status:
mbligh78bf5352008-07-11 20:27:36 +0000161 raise error.AutoservError('svn not found on target machine: %s'
162 % host.name)
jadmanski0afbb632008-06-06 21:10:57 +0000163 try:
mbligh78bf5352008-07-11 20:27:36 +0000164 host.run('svn checkout %s %s' % (AUTOTEST_SVN, autodir))
jadmanski0afbb632008-06-06 21:10:57 +0000165 except error.AutoservRunError, e:
mbligh78bf5352008-07-11 20:27:36 +0000166 host.run('svn checkout %s %s' % (AUTOTEST_HTTP, autodir))
showardb18134f2009-03-20 20:52:18 +0000167 logging.info("Installation of autotest completed")
jadmanski0afbb632008-06-06 21:10:57 +0000168 self.installed = True
mbligh91334902007-09-28 01:47:59 +0000169
170
jadmanski7c7aff32009-03-25 22:43:07 +0000171 def uninstall(self, host=None):
172 """
173 Uninstall (i.e. delete) autotest. Removes the autotest client install
174 from the specified host.
175
176 @params host a Host instance from which the client will be removed
177 """
178 if not self.installed:
179 return
180 if not host:
181 host = self.host
182 autodir = host.get_autodir()
183 if not autodir:
184 return
185
186 # perform the actual uninstall
187 host.run("rm -rf %s" % utils.sh_escape(autodir), ignore_status=True)
188 host.set_autodir(None)
189 self.installed = False
190
191
jadmanski0afbb632008-06-06 21:10:57 +0000192 def get(self, location = None):
193 if not location:
194 location = os.path.join(self.serverdir, '../client')
195 location = os.path.abspath(location)
196 # If there's stuff run on our client directory already, it
197 # can cause problems. Try giving it a quick clean first.
198 cwd = os.getcwd()
199 os.chdir(location)
200 os.system('tools/make_clean')
201 os.chdir(cwd)
202 super(BaseAutotest, self).get(location)
203 self.got = True
mblighdcd57a82007-07-11 23:06:47 +0000204
205
mblighe7d9c602009-07-02 19:02:33 +0000206 def run(self, control_file, results_dir='.', host=None, timeout=None,
207 tag=None, parallel_flag=False, background=False,
208 client_disconnect_timeout=1800, job_tag=''):
jadmanski0afbb632008-06-06 21:10:57 +0000209 """
210 Run an autotest job on the remote machine.
mbligh9a3f5e52008-05-28 21:21:43 +0000211
mblighe7d9c602009-07-02 19:02:33 +0000212 @param control_file: An open file-like-obj of the control file.
213 @param results_dir: A str path where the results should be stored
214 on the local filesystem.
215 @param host: A Host instance on which the control file should
216 be run.
217 @param timeout: Maximum number of seconds to wait for the run or None.
218 @param tag: Tag name for the client side instance of autotest.
219 @param parallel_flag: Flag set when multiple jobs are run at the
220 same time.
221 @param background: Indicates that the client should be launched as
222 a background job; the code calling run will be responsible
223 for monitoring the client and collecting the results.
224 @param client_disconnect_timeout: Seconds to wait for the remote host
225 to come back after a reboot. [default: 30 minutes]
226 @param job_tag: The scheduler's execution tag for this particular job
227 to pass on to the clients. 'job#-owner/hostgroupname'
228
229 @raises AutotestRunError: If there is a problem executing
230 the control file.
jadmanski0afbb632008-06-06 21:10:57 +0000231 """
232 host = self._get_host_and_setup(host)
233 results_dir = os.path.abspath(results_dir)
mblighc1cbc992008-05-27 20:01:45 +0000234
jadmanski0afbb632008-06-06 21:10:57 +0000235 if tag:
236 results_dir = os.path.join(results_dir, tag)
mblighc1cbc992008-05-27 20:01:45 +0000237
mblighb3c0c912008-11-27 00:32:45 +0000238 atrun = _Run(host, results_dir, tag, parallel_flag, background)
jadmanski6dadd832009-02-05 23:39:27 +0000239 self._do_run(control_file, results_dir, host, atrun, timeout,
mblighe7d9c602009-07-02 19:02:33 +0000240 client_disconnect_timeout, job_tag)
mblighd8b39252008-03-20 21:15:03 +0000241
242
jadmanski0afbb632008-06-06 21:10:57 +0000243 def _get_host_and_setup(self, host):
244 if not host:
245 host = self.host
246 if not self.installed:
247 self.install(host)
mbligh91334902007-09-28 01:47:59 +0000248
jadmanski0afbb632008-06-06 21:10:57 +0000249 host.wait_up(timeout=30)
250 return host
mblighd8b39252008-03-20 21:15:03 +0000251
252
jadmanski6dadd832009-02-05 23:39:27 +0000253 def _do_run(self, control_file, results_dir, host, atrun, timeout,
mblighe7d9c602009-07-02 19:02:33 +0000254 client_disconnect_timeout, job_tag):
jadmanski0afbb632008-06-06 21:10:57 +0000255 try:
256 atrun.verify_machine()
257 except:
showardb18134f2009-03-20 20:52:18 +0000258 logging.error("Verify failed on %s. Reinstalling autotest",
259 host.hostname)
jadmanski0afbb632008-06-06 21:10:57 +0000260 self.install(host)
261 atrun.verify_machine()
262 debug = os.path.join(results_dir, 'debug')
263 try:
264 os.makedirs(debug)
mbligh09108442008-10-15 16:27:38 +0000265 except Exception:
jadmanski0afbb632008-06-06 21:10:57 +0000266 pass
mbligh9a3f5e52008-05-28 21:21:43 +0000267
mbligh09108442008-10-15 16:27:38 +0000268 delete_file_list = [atrun.remote_control_file,
269 atrun.remote_control_file + '.state',
270 atrun.manual_control_file,
271 atrun.manual_control_file + '.state']
272 cmd = ';'.join('rm -f ' + control for control in delete_file_list)
273 host.run(cmd, ignore_status=True)
mbligh9a3f5e52008-05-28 21:21:43 +0000274
jadmanski0afbb632008-06-06 21:10:57 +0000275 tmppath = utils.get(control_file)
mblighc5ddfd12008-08-04 17:15:00 +0000276
jadmanskicb0e1612009-02-27 18:03:10 +0000277 # build up the initialization prologue for the control file
278 prologue_lines = []
279 prologue_lines.append("job.default_boot_tag(%r)\n"
280 % host.job.last_boot_tag)
281 prologue_lines.append("job.default_test_cleanup(%r)\n"
282 % host.job.run_test_cleanup)
mblighe7d9c602009-07-02 19:02:33 +0000283 if job_tag:
284 prologue_lines.append("job.default_tag(%r)\n" % job_tag)
jadmanski23afbec2008-09-17 18:12:07 +0000285
mbligh09108442008-10-15 16:27:38 +0000286 # If the packaging system is being used, add the repository list.
mblighc5ddfd12008-08-04 17:15:00 +0000287 try:
mblighc5ddfd12008-08-04 17:15:00 +0000288 c = global_config.global_config
289 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list)
jadmanskiede7e242009-08-10 15:43:33 +0000290 repos.reverse() # high priority packages should be added last
mbligh76d19f72008-10-15 16:24:43 +0000291 pkgmgr = packages.PackageManager('autotest', hostname=host.hostname,
292 repo_urls=repos)
jadmanskib1a51132009-08-07 16:45:50 +0000293 prologue_lines.append('job.add_repository(%s)\n' % repos)
mblighc5ddfd12008-08-04 17:15:00 +0000294 except global_config.ConfigError, e:
295 pass
296
jadmanskie2eef7b2009-03-03 23:55:13 +0000297 # on full-size installs, turn on any profilers the server is using
jadmanski27b52912009-08-14 17:09:15 +0000298 if not atrun.background:
jadmanskie2eef7b2009-03-03 23:55:13 +0000299 running_profilers = host.job.profilers.add_log.iteritems()
300 for profiler, (args, dargs) in running_profilers:
301 call_args = [repr(profiler)]
302 call_args += [repr(arg) for arg in args]
303 call_args += ["%s=%r" % item for item in dargs.iteritems()]
304 prologue_lines.append("job.profilers.add(%s)\n"
305 % ", ".join(call_args))
306 cfile = "".join(prologue_lines)
307
mbligh09108442008-10-15 16:27:38 +0000308 cfile += open(tmppath).read()
309 open(tmppath, "w").write(cfile)
mblighc5ddfd12008-08-04 17:15:00 +0000310
jadmanskic09fc152008-10-15 17:56:59 +0000311 # Create and copy state file to remote_control_file + '.state'
312 sysinfo_state = {"__sysinfo": host.job.sysinfo.serialize()}
313 state_file = self._create_state_file(host.job, sysinfo_state)
314 host.send_file(state_file, atrun.remote_control_file + '.state')
315 os.remove(state_file)
316
mblighc5ddfd12008-08-04 17:15:00 +0000317 # Copy control_file to remote_control_file on the host
jadmanski0afbb632008-06-06 21:10:57 +0000318 host.send_file(tmppath, atrun.remote_control_file)
319 if os.path.abspath(tmppath) != os.path.abspath(control_file):
320 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000321
jadmanski6bb32d72009-03-19 20:25:24 +0000322 atrun.execute_control(
jadmanski6dadd832009-02-05 23:39:27 +0000323 timeout=timeout,
324 client_disconnect_timeout=client_disconnect_timeout)
jadmanski23afbec2008-09-17 18:12:07 +0000325
326
jadmanskic09fc152008-10-15 17:56:59 +0000327 def _create_state_file(self, job, state_dict):
328 """ Create a state file from a dictionary. Returns the path of the
329 state file. """
330 fd, path = tempfile.mkstemp(dir=job.tmpdir)
331 state_file = os.fdopen(fd, "w")
332 pickle.dump(state_dict, state_file)
333 state_file.close()
334 return path
335
336
jadmanski0afbb632008-06-06 21:10:57 +0000337 def run_timed_test(self, test_name, results_dir='.', host=None,
jadmanskic98c4702009-01-05 15:50:06 +0000338 timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000339 """
340 Assemble a tiny little control file to just run one test,
341 and run it as an autotest client-side test
342 """
343 if not host:
344 host = self.host
345 if not self.installed:
346 self.install(host)
347 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
348 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
349 control = "job.run_test(%s)\n" % cmd
jadmanskic98c4702009-01-05 15:50:06 +0000350 self.run(control, results_dir, host, timeout=timeout)
mbligh0e4613b2007-10-29 16:55:07 +0000351
352
jadmanskic98c4702009-01-05 15:50:06 +0000353 def run_test(self, test_name, results_dir='.', host=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000354 self.run_timed_test(test_name, results_dir, host, timeout=None,
jadmanskic98c4702009-01-05 15:50:06 +0000355 *args, **dargs)
mblighd54832b2007-07-25 16:46:56 +0000356
357
mblighdcd57a82007-07-11 23:06:47 +0000358class _Run(object):
jadmanski0afbb632008-06-06 21:10:57 +0000359 """
360 Represents a run of autotest control file. This class maintains
361 all the state necessary as an autotest control file is executed.
mblighdcd57a82007-07-11 23:06:47 +0000362
jadmanski0afbb632008-06-06 21:10:57 +0000363 It is not intended to be used directly, rather control files
364 should be run using the run method in Autotest.
365 """
mblighb3c0c912008-11-27 00:32:45 +0000366 def __init__(self, host, results_dir, tag, parallel_flag, background):
jadmanski0afbb632008-06-06 21:10:57 +0000367 self.host = host
368 self.results_dir = results_dir
369 self.env = host.env
370 self.tag = tag
371 self.parallel_flag = parallel_flag
mblighb3c0c912008-11-27 00:32:45 +0000372 self.background = background
jadmanski0afbb632008-06-06 21:10:57 +0000373 self.autodir = _get_autodir(self.host)
mbligh78bf5352008-07-11 20:27:36 +0000374 control = os.path.join(self.autodir, 'control')
jadmanski0afbb632008-06-06 21:10:57 +0000375 if tag:
mbligh78bf5352008-07-11 20:27:36 +0000376 control += '.' + tag
377 self.manual_control_file = control
378 self.remote_control_file = control + '.autoserv'
mblighdc735a22007-08-02 16:54:37 +0000379
380
jadmanski0afbb632008-06-06 21:10:57 +0000381 def verify_machine(self):
382 binary = os.path.join(self.autodir, 'bin/autotest')
383 try:
384 self.host.run('ls %s > /dev/null 2>&1' % binary)
385 except:
386 raise "Autotest does not appear to be installed"
mblighdc735a22007-08-02 16:54:37 +0000387
jadmanski0afbb632008-06-06 21:10:57 +0000388 if not self.parallel_flag:
389 tmpdir = os.path.join(self.autodir, 'tmp')
390 download = os.path.join(self.autodir, 'tests/download')
391 self.host.run('umount %s' % tmpdir, ignore_status=True)
392 self.host.run('umount %s' % download, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000393
jadmanski6dadd832009-02-05 23:39:27 +0000394
395 def get_base_cmd_args(self, section):
showard234b2962009-07-28 20:02:30 +0000396 args = ['--verbose']
jadmanski0afbb632008-06-06 21:10:57 +0000397 if section > 0:
jadmanski6dadd832009-02-05 23:39:27 +0000398 args.append('-c')
jadmanski0afbb632008-06-06 21:10:57 +0000399 if self.tag:
jadmanski6dadd832009-02-05 23:39:27 +0000400 args.append('-t %s' % self.tag)
jadmanski0afbb632008-06-06 21:10:57 +0000401 if self.host.job.use_external_logging():
jadmanski6dadd832009-02-05 23:39:27 +0000402 args.append('-l')
mblighce955fc2009-08-24 21:59:02 +0000403 if self.host.hostname:
404 args.append('--hostname=%s' % self.host.hostname)
405
jadmanski6dadd832009-02-05 23:39:27 +0000406 args.append(self.remote_control_file)
407 return args
408
409
410 def get_background_cmd(self, section):
411 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotest_client')]
412 cmd += self.get_base_cmd_args(section)
413 cmd.append('>/dev/null 2>/dev/null &')
414 return ' '.join(cmd)
415
416
417 def get_daemon_cmd(self, section, monitor_dir):
418 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotestd'),
419 monitor_dir, '-H autoserv']
420 cmd += self.get_base_cmd_args(section)
421 cmd.append('>/dev/null 2>/dev/null </dev/null &')
422 return ' '.join(cmd)
423
424
425 def get_monitor_cmd(self, monitor_dir, stdout_read, stderr_read):
426 cmd = [os.path.join(self.autodir, 'bin', 'autotestd_monitor'),
427 monitor_dir, str(stdout_read), str(stderr_read)]
jadmanski0afbb632008-06-06 21:10:57 +0000428 return ' '.join(cmd)
mblighadf2aab2007-11-29 18:16:43 +0000429
mblighd8b39252008-03-20 21:15:03 +0000430
jadmanski0afbb632008-06-06 21:10:57 +0000431 def get_client_log(self, section):
jadmanskie0c7fb62008-12-16 20:51:16 +0000432 """ Find what the "next" client.log.* file should be and open it. """
433 debug_dir = os.path.join(self.results_dir, "debug")
434 client_logs = glob.glob(os.path.join(debug_dir, "client.log.*"))
435 next_log = os.path.join(debug_dir, "client.log.%d" % len(client_logs))
436 return open(next_log, "w", 0)
mblighd8b39252008-03-20 21:15:03 +0000437
438
jadmanskib264ed02009-01-12 23:54:27 +0000439 @staticmethod
440 def is_client_job_finished(last_line):
441 return bool(re.match(r'^END .*\t----\t----\t.*$', last_line))
442
443
444 @staticmethod
445 def is_client_job_rebooting(last_line):
446 return bool(re.match(r'^\t*GOOD\t----\treboot\.start.*$', last_line))
447
448
jadmanskia61edad2009-05-21 22:17:49 +0000449 def log_unexpected_abort(self, stderr_redirector):
450 stderr_redirector.flush_all_buffers()
jadmanskib264ed02009-01-12 23:54:27 +0000451 msg = "Autotest client terminated unexpectedly"
452 self.host.job.record("END ABORT", None, None, msg)
453
454
jadmanski6dadd832009-02-05 23:39:27 +0000455 def _execute_in_background(self, section, timeout):
456 full_cmd = self.get_background_cmd(section)
457 devnull = open(os.devnull, "w")
mblighd8b39252008-03-20 21:15:03 +0000458
jadmanski6dadd832009-02-05 23:39:27 +0000459 old_resultdir = self.host.job.resultdir
jadmanski0afbb632008-06-06 21:10:57 +0000460 try:
jadmanski0afbb632008-06-06 21:10:57 +0000461 self.host.job.resultdir = self.results_dir
462 result = self.host.run(full_cmd, ignore_status=True,
463 timeout=timeout,
jadmanski6dadd832009-02-05 23:39:27 +0000464 stdout_tee=devnull,
465 stderr_tee=devnull)
jadmanski0afbb632008-06-06 21:10:57 +0000466 finally:
jadmanski0afbb632008-06-06 21:10:57 +0000467 self.host.job.resultdir = old_resultdir
jadmanski6dadd832009-02-05 23:39:27 +0000468
469 return result
470
471
472 @staticmethod
473 def _strip_stderr_prologue(stderr):
474 """Strips the 'standard' prologue that get pre-pended to every
475 remote command and returns the text that was actually written to
476 stderr by the remote command."""
477 stderr_lines = stderr.split("\n")[1:]
478 if not stderr_lines:
479 return ""
480 elif stderr_lines[0].startswith("NOTE: autotestd_monitor"):
481 del stderr_lines[0]
482 return "\n".join(stderr_lines)
483
484
485 def _execute_daemon(self, section, timeout, stderr_redirector,
486 client_disconnect_timeout):
487 monitor_dir = self.host.get_tmp_dir()
488 daemon_cmd = self.get_daemon_cmd(section, monitor_dir)
489 client_log = self.get_client_log(section)
490
491 stdout_read = stderr_read = 0
492 old_resultdir = self.host.job.resultdir
493 try:
jadmanski29a4c702009-03-03 23:30:59 +0000494 self.host.job.resultdir = self.results_dir
jadmanski6dadd832009-02-05 23:39:27 +0000495 self.host.run(daemon_cmd, ignore_status=True, timeout=timeout)
jadmanski91d56a92009-04-01 15:20:40 +0000496 disconnect_warnings = []
jadmanski6dadd832009-02-05 23:39:27 +0000497 while True:
498 monitor_cmd = self.get_monitor_cmd(monitor_dir, stdout_read,
499 stderr_read)
500 try:
501 result = self.host.run(monitor_cmd, ignore_status=True,
502 timeout=timeout,
503 stdout_tee=client_log,
504 stderr_tee=stderr_redirector)
505 except error.AutoservRunError, e:
506 result = e.result_obj
507 result.exit_status = None
jadmanski91d56a92009-04-01 15:20:40 +0000508 disconnect_warnings.append(e.description)
509
jadmanski6dadd832009-02-05 23:39:27 +0000510 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000511 "Autotest client was disconnected: %s" % e.description,
512 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000513 except error.AutoservSSHTimeout:
514 result = utils.CmdResult(monitor_cmd, "", "", None, 0)
515 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000516 "Attempt to connect to Autotest client timed out",
517 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000518
519 stdout_read += len(result.stdout)
520 stderr_read += len(self._strip_stderr_prologue(result.stderr))
521
522 if result.exit_status is not None:
523 return result
524 elif not self.host.wait_up(client_disconnect_timeout):
525 raise error.AutoservSSHTimeout(
526 "client was disconnected, reconnect timed out")
527 finally:
528 self.host.job.resultdir = old_resultdir
529
530
531 def execute_section(self, section, timeout, stderr_redirector,
532 client_disconnect_timeout):
showardb18134f2009-03-20 20:52:18 +0000533 logging.info("Executing %s/bin/autotest %s/control phase %d",
534 self.autodir, self.autodir, section)
jadmanski6dadd832009-02-05 23:39:27 +0000535
536 if self.background:
537 result = self._execute_in_background(section, timeout)
538 else:
539 result = self._execute_daemon(section, timeout, stderr_redirector,
540 client_disconnect_timeout)
541
542 last_line = stderr_redirector.last_line
mbligh2bf2db62007-11-27 00:53:18 +0000543
jadmanskib264ed02009-01-12 23:54:27 +0000544 # check if we failed hard enough to warrant an exception
jadmanski0afbb632008-06-06 21:10:57 +0000545 if result.exit_status == 1:
jadmanskib264ed02009-01-12 23:54:27 +0000546 err = error.AutotestRunError("client job was aborted")
547 elif not self.background and not result.stderr:
548 err = error.AutotestRunError(
jadmanskie4130532009-03-17 18:01:28 +0000549 "execute_section %s failed to return anything\n"
550 "stdout:%s\n" % (section, result.stdout))
jadmanskib264ed02009-01-12 23:54:27 +0000551 else:
552 err = None
mbligh0e4613b2007-10-29 16:55:07 +0000553
jadmanskib264ed02009-01-12 23:54:27 +0000554 # log something if the client failed AND never finished logging
555 if err and not self.is_client_job_finished(last_line):
jadmanskia61edad2009-05-21 22:17:49 +0000556 self.log_unexpected_abort(stderr_redirector)
jadmanskib264ed02009-01-12 23:54:27 +0000557
558 if err:
559 raise err
560 else:
561 return stderr_redirector.last_line
jadmanski4600e342008-10-29 22:54:00 +0000562
563
564 def _wait_for_reboot(self):
showardb18134f2009-03-20 20:52:18 +0000565 logging.info("Client is rebooting")
566 logging.info("Waiting for client to halt")
jadmanski4600e342008-10-29 22:54:00 +0000567 if not self.host.wait_down(HALT_TIME):
568 err = "%s failed to shutdown after %d"
569 err %= (self.host.hostname, HALT_TIME)
570 raise error.AutotestRunError(err)
showardb18134f2009-03-20 20:52:18 +0000571 logging.info("Client down, waiting for restart")
jadmanski4600e342008-10-29 22:54:00 +0000572 if not self.host.wait_up(BOOT_TIME):
573 # since reboot failed
574 # hardreset the machine once if possible
575 # before failing this control file
576 warning = "%s did not come back up, hard resetting"
577 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000578 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000579 try:
580 self.host.hardreset(wait=False)
mblighd99d3b272008-12-22 14:41:27 +0000581 except (AttributeError, error.AutoservUnsupportedError):
jadmanski4600e342008-10-29 22:54:00 +0000582 warning = "Hard reset unsupported on %s"
583 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000584 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000585 raise error.AutotestRunError("%s failed to boot after %ds" %
586 (self.host.hostname, BOOT_TIME))
587 self.host.reboot_followup()
mblighdc735a22007-08-02 16:54:37 +0000588
589
jadmanski6bb32d72009-03-19 20:25:24 +0000590 def _process_client_state_file(self):
591 state_file = os.path.basename(self.remote_control_file) + ".state"
592 state_path = os.path.join(self.results_dir, state_file)
593 try:
594 state_dict = pickle.load(open(state_path))
595 except Exception, e:
596 msg = "Ignoring error while loading client job state file: %s" % e
showard372ce3e2009-04-07 18:12:03 +0000597 logging.warning(msg)
jadmanski6bb32d72009-03-19 20:25:24 +0000598 state_dict = {}
599
600 # clear out the state file
601 # TODO: stash the file away somewhere useful instead
602 try:
603 os.remove(state_path)
604 except Exception:
605 pass
606
mblighe9633f52009-07-02 19:01:09 +0000607 logging.debug("Persistent state variables pulled back from %s: %s",
608 self.host.hostname, state_dict)
jadmanski6bb32d72009-03-19 20:25:24 +0000609
610 if "__run_test_cleanup" in state_dict:
611 if state_dict["__run_test_cleanup"]:
612 self.host.job.enable_test_cleanup()
613 else:
614 self.host.job.disable_test_cleanup()
615
616 if "__last_boot_tag" in state_dict:
617 self.host.job.last_boot_tag = state_dict["__last_boot_tag"]
618
619 if "__sysinfo" in state_dict:
620 self.host.job.sysinfo.deserialize(state_dict["__sysinfo"])
621
622
jadmanski6dadd832009-02-05 23:39:27 +0000623 def execute_control(self, timeout=None, client_disconnect_timeout=None):
jadmanski6bb32d72009-03-19 20:25:24 +0000624 if not self.background:
625 collector = log_collector(self.host, self.tag, self.results_dir)
626 hostname = self.host.hostname
627 remote_results = collector.client_results_dir
628 local_results = collector.server_results_dir
629 self.host.job.add_client_log(hostname, remote_results,
630 local_results)
631
jadmanski0afbb632008-06-06 21:10:57 +0000632 section = 0
jadmanski4600e342008-10-29 22:54:00 +0000633 start_time = time.time()
634
jadmanski043e1132008-11-19 17:10:32 +0000635 logger = client_logger(self.host, self.tag, self.results_dir)
jadmanski4600e342008-10-29 22:54:00 +0000636 try:
637 while not timeout or time.time() < start_time + timeout:
638 if timeout:
639 section_timeout = start_time + timeout - time.time()
640 else:
641 section_timeout = None
642 last = self.execute_section(section, section_timeout,
jadmanski6dadd832009-02-05 23:39:27 +0000643 logger, client_disconnect_timeout)
mblighb3c0c912008-11-27 00:32:45 +0000644 if self.background:
645 return
jadmanski4600e342008-10-29 22:54:00 +0000646 section += 1
jadmanskib264ed02009-01-12 23:54:27 +0000647 if self.is_client_job_finished(last):
showardb18134f2009-03-20 20:52:18 +0000648 logging.info("Client complete")
jadmanski4600e342008-10-29 22:54:00 +0000649 return
jadmanskib264ed02009-01-12 23:54:27 +0000650 elif self.is_client_job_rebooting(last):
jadmanski79ab9282008-11-11 17:53:12 +0000651 try:
652 self._wait_for_reboot()
653 except error.AutotestRunError, e:
jadmanski043e1132008-11-19 17:10:32 +0000654 self.host.job.record("ABORT", None, "reboot", str(e))
655 self.host.job.record("END ABORT", None, None, str(e))
jadmanski79ab9282008-11-11 17:53:12 +0000656 raise
jadmanski4600e342008-10-29 22:54:00 +0000657 continue
658
659 # if we reach here, something unexpected happened
jadmanskia61edad2009-05-21 22:17:49 +0000660 self.log_unexpected_abort(logger)
jadmanski4600e342008-10-29 22:54:00 +0000661
662 # give the client machine a chance to recover from a crash
663 self.host.wait_up(CRASH_RECOVERY_TIME)
664 msg = ("Aborting - unexpected final status message from "
665 "client: %s\n") % last
666 raise error.AutotestRunError(msg)
667 finally:
jadmanski043e1132008-11-19 17:10:32 +0000668 logger.close()
jadmanski6bb32d72009-03-19 20:25:24 +0000669 if not self.background:
670 collector.collect_client_job_results()
671 self._process_client_state_file()
672 self.host.job.remove_client_log(hostname, remote_results,
673 local_results)
mblighdcd57a82007-07-11 23:06:47 +0000674
jadmanski0afbb632008-06-06 21:10:57 +0000675 # should only get here if we timed out
676 assert timeout
677 raise error.AutotestTimeoutError()
mbligh0e4613b2007-10-29 16:55:07 +0000678
mblighdcd57a82007-07-11 23:06:47 +0000679
680def _get_autodir(host):
mbligh3c7a1502008-07-24 18:08:47 +0000681 autodir = host.get_autodir()
682 if autodir:
jadmanski8297cf52009-05-21 22:23:02 +0000683 logging.debug('Using existing host autodir: %s', autodir)
mbligh3c7a1502008-07-24 18:08:47 +0000684 return autodir
mbligh483d1da2009-08-24 22:07:14 +0000685 for path in CLIENT_AUTODIR_PATHS:
jadmanski0afbb632008-06-06 21:10:57 +0000686 try:
mbligh483d1da2009-08-24 22:07:14 +0000687 host.run('test -d %s' % utils.sh_escape(path))
jadmanski8297cf52009-05-21 22:23:02 +0000688 logging.debug('Found autodir at %s', path)
jadmanski0afbb632008-06-06 21:10:57 +0000689 return path
690 except error.AutoservRunError:
mbligh483d1da2009-08-24 22:07:14 +0000691 logging.debug('%s does not exist on %s', path, host.hostname)
jadmanski8297cf52009-05-21 22:23:02 +0000692 raise error.AutotestRunError('Cannot figure out autotest directory')
mblighd8b39252008-03-20 21:15:03 +0000693
694
jadmanski043e1132008-11-19 17:10:32 +0000695class log_collector(object):
696 def __init__(self, host, client_tag, results_dir):
697 self.host = host
698 if not client_tag:
699 client_tag = "default"
700 self.client_results_dir = os.path.join(host.get_autodir(), "results",
701 client_tag)
702 self.server_results_dir = results_dir
703
704
705 def collect_client_job_results(self):
706 """ A method that collects all the current results of a running
707 client job into the results dir. By default does nothing as no
708 client job is running, but when running a client job you can override
709 this with something that will actually do something. """
710
711 # make an effort to wait for the machine to come up
712 try:
713 self.host.wait_up(timeout=30)
714 except error.AutoservError:
715 # don't worry about any errors, we'll try and
716 # get the results anyway
717 pass
718
jadmanski043e1132008-11-19 17:10:32 +0000719 # Copy all dirs in default to results_dir
720 try:
jadmanski043e1132008-11-19 17:10:32 +0000721 self.host.get_file(self.client_results_dir + '/',
mbligh45561782009-05-11 21:14:34 +0000722 self.server_results_dir, preserve_symlinks=True)
jadmanski043e1132008-11-19 17:10:32 +0000723 except Exception:
724 # well, don't stop running just because we couldn't get logs
showardb18134f2009-03-20 20:52:18 +0000725 e_msg = "Unexpected error copying test result logs, continuing ..."
726 logging.error(e_msg)
jadmanski043e1132008-11-19 17:10:32 +0000727 traceback.print_exc(file=sys.stdout)
728
729
jadmanski043e1132008-11-19 17:10:32 +0000730# a file-like object for catching stderr from an autotest client and
731# extracting status logs from it
732class client_logger(object):
733 """Partial file object to write to both stdout and
734 the status log file. We only implement those methods
735 utils.run() actually calls.
736
737 Note that this class is fairly closely coupled with server_job, as it
738 uses special job._ methods to actually carry out the loggging.
739 """
740 status_parser = re.compile(r"^AUTOTEST_STATUS:([^:]*):(.*)$")
741 test_complete_parser = re.compile(r"^AUTOTEST_TEST_COMPLETE:(.*)$")
jadmanskib1a51132009-08-07 16:45:50 +0000742 fetch_package_parser = re.compile(
743 r"^AUTOTEST_FETCH_PACKAGE:([^:]*):([^:]*):(.*)$")
jadmanski043e1132008-11-19 17:10:32 +0000744 extract_indent = re.compile(r"^(\t*).*$")
jadmanskiefe4ebf2009-05-21 22:12:30 +0000745 extract_timestamp = re.compile(r".*\ttimestamp=(\d+)\t.*$")
jadmanski043e1132008-11-19 17:10:32 +0000746
747 def __init__(self, host, tag, server_results_dir):
748 self.host = host
749 self.job = host.job
750 self.log_collector = log_collector(host, tag, server_results_dir)
751 self.leftover = ""
752 self.last_line = ""
jadmanskiefe4ebf2009-05-21 22:12:30 +0000753 self.newest_timestamp = float("-inf")
jadmanski043e1132008-11-19 17:10:32 +0000754 self.logs = {}
jadmanski6dadd832009-02-05 23:39:27 +0000755 self.server_warnings = []
jadmanski043e1132008-11-19 17:10:32 +0000756
757
jadmanskiefe4ebf2009-05-21 22:12:30 +0000758 def _update_timestamp(self, line):
759 match = self.extract_timestamp.search(line)
760 if match:
761 self.newest_timestamp = max(self.newest_timestamp,
762 int(match.group(1)))
763
764
jadmanski043e1132008-11-19 17:10:32 +0000765 def _process_log_dict(self, log_dict):
766 log_list = log_dict.pop("logs", [])
767 for key in sorted(log_dict.iterkeys()):
768 log_list += self._process_log_dict(log_dict.pop(key))
769 return log_list
770
771
772 def _process_logs(self):
773 """Go through the accumulated logs in self.log and print them
774 out to stdout and the status log. Note that this processes
775 logs in an ordering where:
776
777 1) logs to different tags are never interleaved
778 2) logs to x.y come before logs to x.y.z for all z
779 3) logs to x.y come before x.z whenever y < z
780
781 Note that this will in general not be the same as the
782 chronological ordering of the logs. However, if a chronological
783 ordering is desired that one can be reconstructed from the
784 status log by looking at timestamp lines."""
785 log_list = self._process_log_dict(self.logs)
786 for line in log_list:
787 self.job._record_prerendered(line + '\n')
788 if log_list:
789 self.last_line = log_list[-1]
790
791
792 def _process_quoted_line(self, tag, line):
793 """Process a line quoted with an AUTOTEST_STATUS flag. If the
794 tag is blank then we want to push out all the data we've been
795 building up in self.logs, and then the newest line. If the
796 tag is not blank, then push the line into the logs for handling
797 later."""
showardb18134f2009-03-20 20:52:18 +0000798 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +0000799 if tag == "":
800 self._process_logs()
801 self.job._record_prerendered(line + '\n')
802 self.last_line = line
803 else:
804 tag_parts = [int(x) for x in tag.split(".")]
805 log_dict = self.logs
806 for part in tag_parts:
807 log_dict = log_dict.setdefault(part, {})
808 log_list = log_dict.setdefault("logs", [])
809 log_list.append(line)
810
811
jadmanskif37df842009-02-11 00:03:26 +0000812 def _process_info_line(self, line):
813 """Check if line is an INFO line, and if it is, interpret any control
814 messages (e.g. enabling/disabling warnings) that it may contain."""
815 match = re.search(r"^\t*INFO\t----\t----(.*)\t[^\t]*$", line)
816 if not match:
817 return # not an INFO line
818 for field in match.group(1).split('\t'):
819 if field.startswith("warnings.enable="):
jadmanski16a7ff72009-04-01 18:19:53 +0000820 func = self.job.warning_manager.enable_warnings
jadmanskif37df842009-02-11 00:03:26 +0000821 elif field.startswith("warnings.disable="):
jadmanski16a7ff72009-04-01 18:19:53 +0000822 func = self.job.warning_manager.disable_warnings
jadmanskif37df842009-02-11 00:03:26 +0000823 else:
824 continue
825 warning_type = field.split("=", 1)[1]
jadmanski16a7ff72009-04-01 18:19:53 +0000826 func(warning_type)
jadmanskif37df842009-02-11 00:03:26 +0000827
828
jadmanski043e1132008-11-19 17:10:32 +0000829 def _process_line(self, line):
830 """Write out a line of data to the appropriate stream. Status
831 lines sent by autotest will be prepended with
832 "AUTOTEST_STATUS", and all other lines are ssh error
833 messages."""
834 status_match = self.status_parser.search(line)
835 test_complete_match = self.test_complete_parser.search(line)
jadmanskib1a51132009-08-07 16:45:50 +0000836 fetch_package_match = self.fetch_package_parser.search(line)
jadmanski043e1132008-11-19 17:10:32 +0000837 if status_match:
838 tag, line = status_match.groups()
jadmanskif37df842009-02-11 00:03:26 +0000839 self._process_info_line(line)
jadmanski043e1132008-11-19 17:10:32 +0000840 self._process_quoted_line(tag, line)
841 elif test_complete_match:
jadmanskifcc0d5d2009-02-12 21:52:54 +0000842 self._process_logs()
jadmanski043e1132008-11-19 17:10:32 +0000843 fifo_path, = test_complete_match.groups()
844 self.log_collector.collect_client_job_results()
845 self.host.run("echo A > %s" % fifo_path)
jadmanskib1a51132009-08-07 16:45:50 +0000846 elif fetch_package_match:
847 pkg_name, dest_path, fifo_path = fetch_package_match.groups()
jadmanskiede7e242009-08-10 15:43:33 +0000848 serve_packages = global_config.global_config.get_config_value(
849 "PACKAGES", "serve_packages_from_autoserv", type=bool)
850 if serve_packages and pkg_name.endswith(".tar.bz2"):
851 try:
852 self._send_tarball(pkg_name, dest_path)
853 except Exception:
854 msg = "Package tarball creation failed, continuing anyway"
855 logging.exception(msg)
jadmanskib1a51132009-08-07 16:45:50 +0000856 self.host.run("echo B > %s" % fifo_path)
jadmanski043e1132008-11-19 17:10:32 +0000857 else:
showardb18134f2009-03-20 20:52:18 +0000858 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +0000859
860
jadmanskiede7e242009-08-10 15:43:33 +0000861 def _send_tarball(self, pkg_name, remote_dest):
862 name, pkg_type = self.job.pkgmgr.parse_tarball_name(pkg_name)
863 src_dirs = []
864 if pkg_type == 'test':
865 src_dirs += [os.path.join(self.job.clientdir, 'site_tests', name),
866 os.path.join(self.job.clientdir, 'tests', name)]
867 elif pkg_type == 'profiler':
868 src_dirs += [os.path.join(self.job.clientdir, 'profilers', name)]
869 elif pkg_type == 'dep':
870 src_dirs += [os.path.join(self.job.clientdir, 'deps', name)]
871 elif pkg_type == 'client':
872 return # you must already have a client to hit this anyway
873 else:
874 return # no other types are supported
875
876 # iterate over src_dirs until we find one that exists, then tar it
877 for src_dir in src_dirs:
878 if os.path.exists(src_dir):
879 try:
880 logging.info('Bundling %s into %s', src_dir, pkg_name)
881 temp_dir = autotemp.tempdir(unique_id='autoserv-packager',
882 dir=self.job.tmpdir)
883 tarball_path = self.job.pkgmgr.tar_package(
884 pkg_name, src_dir, temp_dir.name, ' .')
885 self.host.send_file(tarball_path, remote_dest)
886 finally:
887 temp_dir.clean()
888 return
889
890
jadmanski043e1132008-11-19 17:10:32 +0000891 def _format_warnings(self, last_line, warnings):
892 # use the indentation of whatever the last log line was
893 indent = self.extract_indent.match(last_line).group(1)
894 # if the last line starts a new group, add an extra indent
895 if last_line.lstrip('\t').startswith("START\t"):
896 indent += '\t'
897 return [self.job._render_record("WARN", None, None, msg,
898 timestamp, indent).rstrip('\n')
899 for timestamp, msg in warnings]
900
901
902 def _process_warnings(self, last_line, log_dict, warnings):
903 if log_dict.keys() in ([], ["logs"]):
904 # there are no sub-jobs, just append the warnings here
905 warnings = self._format_warnings(last_line, warnings)
906 log_list = log_dict.setdefault("logs", [])
907 log_list += warnings
908 for warning in warnings:
909 sys.stdout.write(warning + '\n')
910 else:
911 # there are sub-jobs, so put the warnings in there
912 log_list = log_dict.get("logs", [])
913 if log_list:
914 last_line = log_list[-1]
915 for key in sorted(log_dict.iterkeys()):
916 if key != "logs":
917 self._process_warnings(last_line,
918 log_dict[key],
919 warnings)
920
jadmanskif37df842009-02-11 00:03:26 +0000921
jadmanski91d56a92009-04-01 15:20:40 +0000922 def log_warning(self, msg, warning_type):
jadmanski6dadd832009-02-05 23:39:27 +0000923 """Injects a WARN message into the current status logging stream."""
jadmanski91d56a92009-04-01 15:20:40 +0000924 timestamp = int(time.time())
925 if self.job.warning_manager.is_valid(timestamp, warning_type):
926 self.server_warnings.append((timestamp, msg))
jadmanski6dadd832009-02-05 23:39:27 +0000927
jadmanski043e1132008-11-19 17:10:32 +0000928
929 def write(self, data):
930 # first check for any new console warnings
jadmanski6dadd832009-02-05 23:39:27 +0000931 warnings = self.job._read_warnings() + self.server_warnings
932 warnings.sort() # sort into timestamp order
jadmanski043e1132008-11-19 17:10:32 +0000933 # now process the newest data written out
934 data = self.leftover + data
935 lines = data.split("\n")
936 # process every line but the last one
937 for line in lines[:-1]:
jadmanskiefe4ebf2009-05-21 22:12:30 +0000938 self._update_timestamp(line)
939 # output any warnings between now and the next status line
940 old_warnings = [(timestamp, msg) for timestamp, msg in warnings
941 if timestamp < self.newest_timestamp]
942 self._process_warnings(self.last_line, self.logs, warnings)
943 del warnings[:len(old_warnings)]
jadmanski043e1132008-11-19 17:10:32 +0000944 self._process_line(line)
jadmanskiefe4ebf2009-05-21 22:12:30 +0000945 # save off any warnings not yet logged for later processing
946 self.server_warnings = warnings
jadmanski043e1132008-11-19 17:10:32 +0000947 # save the last line for later processing
948 # since we may not have the whole line yet
949 self.leftover = lines[-1]
950
951
952 def flush(self):
953 sys.stdout.flush()
954
955
jadmanskia61edad2009-05-21 22:17:49 +0000956 def flush_all_buffers(self):
jadmanski043e1132008-11-19 17:10:32 +0000957 if self.leftover:
958 self._process_line(self.leftover)
jadmanskia61edad2009-05-21 22:17:49 +0000959 self.leftover = ""
jadmanskiefe4ebf2009-05-21 22:12:30 +0000960 self._process_warnings(self.last_line, self.logs, self.server_warnings)
jadmanski043e1132008-11-19 17:10:32 +0000961 self._process_logs()
962 self.flush()
963
964
jadmanskia61edad2009-05-21 22:17:49 +0000965 def close(self):
966 self.flush_all_buffers()
967
968
mbligha7007722009-01-13 00:37:11 +0000969SiteAutotest = client_utils.import_site_class(
970 __file__, "autotest_lib.server.site_autotest", "SiteAutotest",
971 BaseAutotest)
mblighd8b39252008-03-20 21:15:03 +0000972
973class Autotest(SiteAutotest):
jadmanski0afbb632008-06-06 21:10:57 +0000974 pass