blob: 0b6f6af48ba2759ca11c5f2d5e44f4fff8373d2d [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
showardb18134f2009-03-20 20:52:18 +00005from autotest_lib.client.common_lib import log, error
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
12# Timeouts for powering down and up respectively
13HALT_TIME = 300
mbligh07c1eac2007-11-05 18:39:29 +000014BOOT_TIME = 1800
jadmanskiec859142008-05-29 21:33:39 +000015CRASH_RECOVERY_TIME = 9000
mbligh0e4613b2007-10-29 16:55:07 +000016
mblighdcd57a82007-07-11 23:06:47 +000017
mblighd8b39252008-03-20 21:15:03 +000018class BaseAutotest(installable_object.InstallableObject):
jadmanski0afbb632008-06-06 21:10:57 +000019 """
20 This class represents the Autotest program.
mblighdcd57a82007-07-11 23:06:47 +000021
jadmanski0afbb632008-06-06 21:10:57 +000022 Autotest is used to run tests automatically and collect the results.
23 It also supports profilers.
mblighdcd57a82007-07-11 23:06:47 +000024
jadmanski0afbb632008-06-06 21:10:57 +000025 Implementation details:
26 This is a leaf class in an abstract class hierarchy, it must
27 implement the unimplemented methods in parent classes.
28 """
mbligh119c12a2007-11-12 22:13:44 +000029
jadmanski0afbb632008-06-06 21:10:57 +000030 def __init__(self, host = None):
31 self.host = host
32 self.got = False
33 self.installed = False
jadmanskie2eef7b2009-03-03 23:55:13 +000034 self.lightweight = False
jadmanski0afbb632008-06-06 21:10:57 +000035 self.serverdir = utils.get_server_dir()
36 super(BaseAutotest, self).__init__()
mblighc8949b82007-07-23 16:33:58 +000037
mblighdc735a22007-08-02 16:54:37 +000038
jadmanskif22fea82008-11-26 20:57:07 +000039 install_in_tmpdir = False
40 @classmethod
41 def set_install_in_tmpdir(cls, flag):
42 """ Sets a flag that controls whether or not Autotest should by
43 default be installed in a "standard" directory (e.g.
44 /home/autotest, /usr/local/autotest) or a temporary directory. """
45 cls.install_in_tmpdir = flag
46
47
48 def _get_install_dir(self, host):
49 """ Determines the location where autotest should be installed on
50 host. If self.install_in_tmpdir is set, it will return a unique
51 temporary directory that autotest can be installed in. """
52 try:
53 autodir = _get_autodir(host)
54 except error.AutotestRunError:
55 autodir = '/usr/local/autotest'
56 if self.install_in_tmpdir:
57 autodir = host.get_tmp_dir(parent=autodir)
58 return autodir
59
60
mbligh1b3b3762008-09-25 02:46:34 +000061 @log.record
mblighb3c0c912008-11-27 00:32:45 +000062 def install(self, host=None, autodir=None):
63 self._install(host=host, autodir=autodir)
jadmanski54f90af2008-10-10 16:20:55 +000064
65
66 def install_base(self, host=None, autodir=None):
67 """ Performs a lightweight autotest install. Useful for when you
68 want to run some client-side code but don't want to pay the cost
69 of a full installation. """
70 self._install(host=host, autodir=autodir, lightweight=True)
71
72
73 def _install(self, host=None, autodir=None, lightweight=False):
jadmanski0afbb632008-06-06 21:10:57 +000074 """
75 Install autotest. If get() was not called previously, an
76 attempt will be made to install from the autotest svn
77 repository.
mbligh9a3f5e52008-05-28 21:21:43 +000078
jadmanski0afbb632008-06-06 21:10:57 +000079 Args:
jadmanski54f90af2008-10-10 16:20:55 +000080 host: a Host instance on which autotest will be installed
81 autodir: location on the remote host to install to
82 lightweight: exclude tests, deps and profilers, if possible
mbligh9a3f5e52008-05-28 21:21:43 +000083
jadmanski0afbb632008-06-06 21:10:57 +000084 Raises:
jadmanski54f90af2008-10-10 16:20:55 +000085 AutoservError: if a tarball was not specified and
86 the target host does not have svn installed in its path"""
jadmanski0afbb632008-06-06 21:10:57 +000087 if not host:
88 host = self.host
89 if not self.got:
90 self.get()
91 host.wait_up(timeout=30)
92 host.setup()
showardb18134f2009-03-20 20:52:18 +000093 logging.info("Installing autotest on %s", host.hostname)
mbligh40f122a2007-11-03 23:08:46 +000094
jadmanski54f90af2008-10-10 16:20:55 +000095 # set up the autotest directory on the remote machine
96 if not autodir:
jadmanskif22fea82008-11-26 20:57:07 +000097 autodir = self._get_install_dir(host)
mbligh0562e652008-08-20 20:11:45 +000098 host.set_autodir(autodir)
jadmanski3c236942009-03-04 17:51:26 +000099 host.run('mkdir -p %s' % utils.sh_escape(autodir))
mbligh40f122a2007-11-03 23:08:46 +0000100
jadmanski1c3c07b2009-03-03 23:29:36 +0000101 # make sure there are no files in $AUTODIR/results
102 results_path = os.path.join(autodir, 'results')
jadmanski3c236942009-03-04 17:51:26 +0000103 host.run('rm -rf %s/*' % utils.sh_escape(results_path),
jadmanski1c3c07b2009-03-03 23:29:36 +0000104 ignore_status=True)
105
mblighc5ddfd12008-08-04 17:15:00 +0000106 # Fetch the autotest client from the nearest repository
107 try:
108 c = global_config.global_config
109 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list)
mbligh76d19f72008-10-15 16:24:43 +0000110 pkgmgr = packages.PackageManager(autodir, hostname=host.hostname,
111 repo_urls=repos,
mbligh1e3b0992008-10-14 16:29:54 +0000112 do_locking=False,
113 run_function=host.run,
114 run_function_dargs=dict(timeout=600))
mblighc5ddfd12008-08-04 17:15:00 +0000115 # The packages dir is used to store all the packages that
116 # are fetched on that client. (for the tests,deps etc.
117 # too apart from the client)
118 pkg_dir = os.path.join(autodir, 'packages')
119 # clean up the autodir except for the packages directory
120 host.run('cd %s && ls | grep -v "^packages$"'
121 ' | xargs rm -rf && rm -rf .[^.]*' % autodir)
122 pkgmgr.install_pkg('autotest', 'client', pkg_dir, autodir,
123 preserve_install_dir=True)
124 self.installed = True
jadmanskie2eef7b2009-03-03 23:55:13 +0000125 self.lightweight = lightweight
mblighc5ddfd12008-08-04 17:15:00 +0000126 return
127 except global_config.ConfigError, e:
showardb18134f2009-03-20 20:52:18 +0000128 logging.error("Could not install autotest using the packaging"
129 "system: %s", e)
mblighc5ddfd12008-08-04 17:15:00 +0000130 except (packages.PackageInstallError, error.AutoservRunError), e:
showardb18134f2009-03-20 20:52:18 +0000131 logging.error("Could not install autotest from %s", repos)
mblighc5ddfd12008-08-04 17:15:00 +0000132
jadmanski0afbb632008-06-06 21:10:57 +0000133 # try to install from file or directory
134 if self.source_material:
135 if os.path.isdir(self.source_material):
136 # Copy autotest recursively
jadmanski54f90af2008-10-10 16:20:55 +0000137 if lightweight:
138 dirs_to_exclude = set(["tests", "site_tests", "deps",
139 "tools", "profilers"])
140 light_files = [os.path.join(self.source_material, f)
141 for f in os.listdir(self.source_material)
142 if f not in dirs_to_exclude]
mbligh89e258d2008-10-24 13:58:08 +0000143 host.send_file(light_files, autodir, delete_dest=True)
jadmanski54f90af2008-10-10 16:20:55 +0000144
145 # create empty dirs for all the stuff we excluded
146 commands = []
147 for path in dirs_to_exclude:
148 abs_path = os.path.join(autodir, path)
149 abs_path = utils.sh_escape(abs_path)
150 commands.append("mkdir -p '%s'" % abs_path)
151 host.run(';'.join(commands))
152 else:
mbligh89e258d2008-10-24 13:58:08 +0000153 host.send_file(self.source_material, autodir,
154 delete_dest=True)
jadmanski0afbb632008-06-06 21:10:57 +0000155 else:
156 # Copy autotest via tarball
157 e_msg = 'Installation method not yet implemented!'
158 raise NotImplementedError(e_msg)
showardb18134f2009-03-20 20:52:18 +0000159 logging.info("Installation of autotest completed")
jadmanski0afbb632008-06-06 21:10:57 +0000160 self.installed = True
jadmanskie2eef7b2009-03-03 23:55:13 +0000161 self.lightweight = lightweight
jadmanski0afbb632008-06-06 21:10:57 +0000162 return
mbligh91334902007-09-28 01:47:59 +0000163
jadmanski0afbb632008-06-06 21:10:57 +0000164 # if that fails try to install using svn
165 if utils.run('which svn').exit_status:
mbligh78bf5352008-07-11 20:27:36 +0000166 raise error.AutoservError('svn not found on target machine: %s'
167 % host.name)
jadmanski0afbb632008-06-06 21:10:57 +0000168 try:
mbligh78bf5352008-07-11 20:27:36 +0000169 host.run('svn checkout %s %s' % (AUTOTEST_SVN, autodir))
jadmanski0afbb632008-06-06 21:10:57 +0000170 except error.AutoservRunError, e:
mbligh78bf5352008-07-11 20:27:36 +0000171 host.run('svn checkout %s %s' % (AUTOTEST_HTTP, autodir))
showardb18134f2009-03-20 20:52:18 +0000172 logging.info("Installation of autotest completed")
jadmanski0afbb632008-06-06 21:10:57 +0000173 self.installed = True
jadmanskie2eef7b2009-03-03 23:55:13 +0000174 self.lightweight = lightweight
mbligh91334902007-09-28 01:47:59 +0000175
176
jadmanski7c7aff32009-03-25 22:43:07 +0000177 def uninstall(self, host=None):
178 """
179 Uninstall (i.e. delete) autotest. Removes the autotest client install
180 from the specified host.
181
182 @params host a Host instance from which the client will be removed
183 """
184 if not self.installed:
185 return
186 if not host:
187 host = self.host
188 autodir = host.get_autodir()
189 if not autodir:
190 return
191
192 # perform the actual uninstall
193 host.run("rm -rf %s" % utils.sh_escape(autodir), ignore_status=True)
194 host.set_autodir(None)
195 self.installed = False
196
197
jadmanski0afbb632008-06-06 21:10:57 +0000198 def get(self, location = None):
199 if not location:
200 location = os.path.join(self.serverdir, '../client')
201 location = os.path.abspath(location)
202 # If there's stuff run on our client directory already, it
203 # can cause problems. Try giving it a quick clean first.
204 cwd = os.getcwd()
205 os.chdir(location)
206 os.system('tools/make_clean')
207 os.chdir(cwd)
208 super(BaseAutotest, self).get(location)
209 self.got = True
mblighdcd57a82007-07-11 23:06:47 +0000210
211
mblighe7d9c602009-07-02 19:02:33 +0000212 def run(self, control_file, results_dir='.', host=None, timeout=None,
213 tag=None, parallel_flag=False, background=False,
214 client_disconnect_timeout=1800, job_tag=''):
jadmanski0afbb632008-06-06 21:10:57 +0000215 """
216 Run an autotest job on the remote machine.
mbligh9a3f5e52008-05-28 21:21:43 +0000217
mblighe7d9c602009-07-02 19:02:33 +0000218 @param control_file: An open file-like-obj of the control file.
219 @param results_dir: A str path where the results should be stored
220 on the local filesystem.
221 @param host: A Host instance on which the control file should
222 be run.
223 @param timeout: Maximum number of seconds to wait for the run or None.
224 @param tag: Tag name for the client side instance of autotest.
225 @param parallel_flag: Flag set when multiple jobs are run at the
226 same time.
227 @param background: Indicates that the client should be launched as
228 a background job; the code calling run will be responsible
229 for monitoring the client and collecting the results.
230 @param client_disconnect_timeout: Seconds to wait for the remote host
231 to come back after a reboot. [default: 30 minutes]
232 @param job_tag: The scheduler's execution tag for this particular job
233 to pass on to the clients. 'job#-owner/hostgroupname'
234
235 @raises AutotestRunError: If there is a problem executing
236 the control file.
jadmanski0afbb632008-06-06 21:10:57 +0000237 """
238 host = self._get_host_and_setup(host)
239 results_dir = os.path.abspath(results_dir)
mblighc1cbc992008-05-27 20:01:45 +0000240
jadmanski0afbb632008-06-06 21:10:57 +0000241 if tag:
242 results_dir = os.path.join(results_dir, tag)
mblighc1cbc992008-05-27 20:01:45 +0000243
mblighb3c0c912008-11-27 00:32:45 +0000244 atrun = _Run(host, results_dir, tag, parallel_flag, background)
jadmanski6dadd832009-02-05 23:39:27 +0000245 self._do_run(control_file, results_dir, host, atrun, timeout,
mblighe7d9c602009-07-02 19:02:33 +0000246 client_disconnect_timeout, job_tag)
mblighd8b39252008-03-20 21:15:03 +0000247
248
jadmanski0afbb632008-06-06 21:10:57 +0000249 def _get_host_and_setup(self, host):
250 if not host:
251 host = self.host
252 if not self.installed:
253 self.install(host)
mbligh91334902007-09-28 01:47:59 +0000254
jadmanski0afbb632008-06-06 21:10:57 +0000255 host.wait_up(timeout=30)
256 return host
mblighd8b39252008-03-20 21:15:03 +0000257
258
jadmanski6dadd832009-02-05 23:39:27 +0000259 def _do_run(self, control_file, results_dir, host, atrun, timeout,
mblighe7d9c602009-07-02 19:02:33 +0000260 client_disconnect_timeout, job_tag):
jadmanski0afbb632008-06-06 21:10:57 +0000261 try:
262 atrun.verify_machine()
263 except:
showardb18134f2009-03-20 20:52:18 +0000264 logging.error("Verify failed on %s. Reinstalling autotest",
265 host.hostname)
jadmanski0afbb632008-06-06 21:10:57 +0000266 self.install(host)
267 atrun.verify_machine()
268 debug = os.path.join(results_dir, 'debug')
269 try:
270 os.makedirs(debug)
mbligh09108442008-10-15 16:27:38 +0000271 except Exception:
jadmanski0afbb632008-06-06 21:10:57 +0000272 pass
mbligh9a3f5e52008-05-28 21:21:43 +0000273
mbligh09108442008-10-15 16:27:38 +0000274 delete_file_list = [atrun.remote_control_file,
275 atrun.remote_control_file + '.state',
276 atrun.manual_control_file,
277 atrun.manual_control_file + '.state']
278 cmd = ';'.join('rm -f ' + control for control in delete_file_list)
279 host.run(cmd, ignore_status=True)
mbligh9a3f5e52008-05-28 21:21:43 +0000280
jadmanski0afbb632008-06-06 21:10:57 +0000281 tmppath = utils.get(control_file)
mblighc5ddfd12008-08-04 17:15:00 +0000282
jadmanskicb0e1612009-02-27 18:03:10 +0000283 # build up the initialization prologue for the control file
284 prologue_lines = []
285 prologue_lines.append("job.default_boot_tag(%r)\n"
286 % host.job.last_boot_tag)
287 prologue_lines.append("job.default_test_cleanup(%r)\n"
288 % host.job.run_test_cleanup)
mblighe7d9c602009-07-02 19:02:33 +0000289 if job_tag:
290 prologue_lines.append("job.default_tag(%r)\n" % job_tag)
jadmanski23afbec2008-09-17 18:12:07 +0000291
mbligh09108442008-10-15 16:27:38 +0000292 # If the packaging system is being used, add the repository list.
mblighc5ddfd12008-08-04 17:15:00 +0000293 try:
mblighc5ddfd12008-08-04 17:15:00 +0000294 c = global_config.global_config
295 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list)
mbligh76d19f72008-10-15 16:24:43 +0000296 pkgmgr = packages.PackageManager('autotest', hostname=host.hostname,
297 repo_urls=repos)
jadmanskie2eef7b2009-03-03 23:55:13 +0000298 prologue_lines.append('job.add_repository(%s)\n'
299 % pkgmgr.repo_urls)
mblighc5ddfd12008-08-04 17:15:00 +0000300 except global_config.ConfigError, e:
301 pass
302
jadmanskie2eef7b2009-03-03 23:55:13 +0000303 # on full-size installs, turn on any profilers the server is using
jadmanski6fc7ff52009-05-04 19:59:56 +0000304 if not self.lightweight and not atrun.background:
jadmanskie2eef7b2009-03-03 23:55:13 +0000305 running_profilers = host.job.profilers.add_log.iteritems()
306 for profiler, (args, dargs) in running_profilers:
307 call_args = [repr(profiler)]
308 call_args += [repr(arg) for arg in args]
309 call_args += ["%s=%r" % item for item in dargs.iteritems()]
310 prologue_lines.append("job.profilers.add(%s)\n"
311 % ", ".join(call_args))
312 cfile = "".join(prologue_lines)
313
mbligh09108442008-10-15 16:27:38 +0000314 cfile += open(tmppath).read()
315 open(tmppath, "w").write(cfile)
mblighc5ddfd12008-08-04 17:15:00 +0000316
jadmanskic09fc152008-10-15 17:56:59 +0000317 # Create and copy state file to remote_control_file + '.state'
318 sysinfo_state = {"__sysinfo": host.job.sysinfo.serialize()}
319 state_file = self._create_state_file(host.job, sysinfo_state)
320 host.send_file(state_file, atrun.remote_control_file + '.state')
321 os.remove(state_file)
322
mblighc5ddfd12008-08-04 17:15:00 +0000323 # Copy control_file to remote_control_file on the host
jadmanski0afbb632008-06-06 21:10:57 +0000324 host.send_file(tmppath, atrun.remote_control_file)
325 if os.path.abspath(tmppath) != os.path.abspath(control_file):
326 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000327
jadmanski6bb32d72009-03-19 20:25:24 +0000328 atrun.execute_control(
jadmanski6dadd832009-02-05 23:39:27 +0000329 timeout=timeout,
330 client_disconnect_timeout=client_disconnect_timeout)
jadmanski23afbec2008-09-17 18:12:07 +0000331
332
jadmanskic09fc152008-10-15 17:56:59 +0000333 def _create_state_file(self, job, state_dict):
334 """ Create a state file from a dictionary. Returns the path of the
335 state file. """
336 fd, path = tempfile.mkstemp(dir=job.tmpdir)
337 state_file = os.fdopen(fd, "w")
338 pickle.dump(state_dict, state_file)
339 state_file.close()
340 return path
341
342
jadmanski0afbb632008-06-06 21:10:57 +0000343 def run_timed_test(self, test_name, results_dir='.', host=None,
jadmanskic98c4702009-01-05 15:50:06 +0000344 timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000345 """
346 Assemble a tiny little control file to just run one test,
347 and run it as an autotest client-side test
348 """
349 if not host:
350 host = self.host
351 if not self.installed:
352 self.install(host)
353 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
354 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
355 control = "job.run_test(%s)\n" % cmd
jadmanskic98c4702009-01-05 15:50:06 +0000356 self.run(control, results_dir, host, timeout=timeout)
mbligh0e4613b2007-10-29 16:55:07 +0000357
358
jadmanskic98c4702009-01-05 15:50:06 +0000359 def run_test(self, test_name, results_dir='.', host=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000360 self.run_timed_test(test_name, results_dir, host, timeout=None,
jadmanskic98c4702009-01-05 15:50:06 +0000361 *args, **dargs)
mblighd54832b2007-07-25 16:46:56 +0000362
363
mblighdcd57a82007-07-11 23:06:47 +0000364class _Run(object):
jadmanski0afbb632008-06-06 21:10:57 +0000365 """
366 Represents a run of autotest control file. This class maintains
367 all the state necessary as an autotest control file is executed.
mblighdcd57a82007-07-11 23:06:47 +0000368
jadmanski0afbb632008-06-06 21:10:57 +0000369 It is not intended to be used directly, rather control files
370 should be run using the run method in Autotest.
371 """
mblighb3c0c912008-11-27 00:32:45 +0000372 def __init__(self, host, results_dir, tag, parallel_flag, background):
jadmanski0afbb632008-06-06 21:10:57 +0000373 self.host = host
374 self.results_dir = results_dir
375 self.env = host.env
376 self.tag = tag
377 self.parallel_flag = parallel_flag
mblighb3c0c912008-11-27 00:32:45 +0000378 self.background = background
jadmanski0afbb632008-06-06 21:10:57 +0000379 self.autodir = _get_autodir(self.host)
mbligh78bf5352008-07-11 20:27:36 +0000380 control = os.path.join(self.autodir, 'control')
jadmanski0afbb632008-06-06 21:10:57 +0000381 if tag:
mbligh78bf5352008-07-11 20:27:36 +0000382 control += '.' + tag
383 self.manual_control_file = control
384 self.remote_control_file = control + '.autoserv'
mblighdc735a22007-08-02 16:54:37 +0000385
386
jadmanski0afbb632008-06-06 21:10:57 +0000387 def verify_machine(self):
388 binary = os.path.join(self.autodir, 'bin/autotest')
389 try:
390 self.host.run('ls %s > /dev/null 2>&1' % binary)
391 except:
392 raise "Autotest does not appear to be installed"
mblighdc735a22007-08-02 16:54:37 +0000393
jadmanski0afbb632008-06-06 21:10:57 +0000394 if not self.parallel_flag:
395 tmpdir = os.path.join(self.autodir, 'tmp')
396 download = os.path.join(self.autodir, 'tests/download')
397 self.host.run('umount %s' % tmpdir, ignore_status=True)
398 self.host.run('umount %s' % download, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000399
jadmanski6dadd832009-02-05 23:39:27 +0000400
401 def get_base_cmd_args(self, section):
402 args = []
jadmanski0afbb632008-06-06 21:10:57 +0000403 if section > 0:
jadmanski6dadd832009-02-05 23:39:27 +0000404 args.append('-c')
jadmanski0afbb632008-06-06 21:10:57 +0000405 if self.tag:
jadmanski6dadd832009-02-05 23:39:27 +0000406 args.append('-t %s' % self.tag)
jadmanski0afbb632008-06-06 21:10:57 +0000407 if self.host.job.use_external_logging():
jadmanski6dadd832009-02-05 23:39:27 +0000408 args.append('-l')
409 args.append(self.remote_control_file)
410 return args
411
412
413 def get_background_cmd(self, section):
414 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotest_client')]
415 cmd += self.get_base_cmd_args(section)
416 cmd.append('>/dev/null 2>/dev/null &')
417 return ' '.join(cmd)
418
419
420 def get_daemon_cmd(self, section, monitor_dir):
421 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotestd'),
422 monitor_dir, '-H autoserv']
423 cmd += self.get_base_cmd_args(section)
424 cmd.append('>/dev/null 2>/dev/null </dev/null &')
425 return ' '.join(cmd)
426
427
428 def get_monitor_cmd(self, monitor_dir, stdout_read, stderr_read):
429 cmd = [os.path.join(self.autodir, 'bin', 'autotestd_monitor'),
430 monitor_dir, str(stdout_read), str(stderr_read)]
jadmanski0afbb632008-06-06 21:10:57 +0000431 return ' '.join(cmd)
mblighadf2aab2007-11-29 18:16:43 +0000432
mblighd8b39252008-03-20 21:15:03 +0000433
jadmanski0afbb632008-06-06 21:10:57 +0000434 def get_client_log(self, section):
jadmanskie0c7fb62008-12-16 20:51:16 +0000435 """ Find what the "next" client.log.* file should be and open it. """
436 debug_dir = os.path.join(self.results_dir, "debug")
437 client_logs = glob.glob(os.path.join(debug_dir, "client.log.*"))
438 next_log = os.path.join(debug_dir, "client.log.%d" % len(client_logs))
439 return open(next_log, "w", 0)
mblighd8b39252008-03-20 21:15:03 +0000440
441
jadmanskib264ed02009-01-12 23:54:27 +0000442 @staticmethod
443 def is_client_job_finished(last_line):
444 return bool(re.match(r'^END .*\t----\t----\t.*$', last_line))
445
446
447 @staticmethod
448 def is_client_job_rebooting(last_line):
449 return bool(re.match(r'^\t*GOOD\t----\treboot\.start.*$', last_line))
450
451
jadmanskia61edad2009-05-21 22:17:49 +0000452 def log_unexpected_abort(self, stderr_redirector):
453 stderr_redirector.flush_all_buffers()
jadmanskib264ed02009-01-12 23:54:27 +0000454 msg = "Autotest client terminated unexpectedly"
455 self.host.job.record("END ABORT", None, None, msg)
456
457
jadmanski6dadd832009-02-05 23:39:27 +0000458 def _execute_in_background(self, section, timeout):
459 full_cmd = self.get_background_cmd(section)
460 devnull = open(os.devnull, "w")
mblighd8b39252008-03-20 21:15:03 +0000461
jadmanski6dadd832009-02-05 23:39:27 +0000462 old_resultdir = self.host.job.resultdir
jadmanski0afbb632008-06-06 21:10:57 +0000463 try:
jadmanski0afbb632008-06-06 21:10:57 +0000464 self.host.job.resultdir = self.results_dir
465 result = self.host.run(full_cmd, ignore_status=True,
466 timeout=timeout,
jadmanski6dadd832009-02-05 23:39:27 +0000467 stdout_tee=devnull,
468 stderr_tee=devnull)
jadmanski0afbb632008-06-06 21:10:57 +0000469 finally:
jadmanski0afbb632008-06-06 21:10:57 +0000470 self.host.job.resultdir = old_resultdir
jadmanski6dadd832009-02-05 23:39:27 +0000471
472 return result
473
474
475 @staticmethod
476 def _strip_stderr_prologue(stderr):
477 """Strips the 'standard' prologue that get pre-pended to every
478 remote command and returns the text that was actually written to
479 stderr by the remote command."""
480 stderr_lines = stderr.split("\n")[1:]
481 if not stderr_lines:
482 return ""
483 elif stderr_lines[0].startswith("NOTE: autotestd_monitor"):
484 del stderr_lines[0]
485 return "\n".join(stderr_lines)
486
487
488 def _execute_daemon(self, section, timeout, stderr_redirector,
489 client_disconnect_timeout):
490 monitor_dir = self.host.get_tmp_dir()
491 daemon_cmd = self.get_daemon_cmd(section, monitor_dir)
492 client_log = self.get_client_log(section)
493
494 stdout_read = stderr_read = 0
495 old_resultdir = self.host.job.resultdir
496 try:
jadmanski29a4c702009-03-03 23:30:59 +0000497 self.host.job.resultdir = self.results_dir
jadmanski6dadd832009-02-05 23:39:27 +0000498 self.host.run(daemon_cmd, ignore_status=True, timeout=timeout)
jadmanski91d56a92009-04-01 15:20:40 +0000499 disconnect_warnings = []
jadmanski6dadd832009-02-05 23:39:27 +0000500 while True:
501 monitor_cmd = self.get_monitor_cmd(monitor_dir, stdout_read,
502 stderr_read)
503 try:
504 result = self.host.run(monitor_cmd, ignore_status=True,
505 timeout=timeout,
506 stdout_tee=client_log,
507 stderr_tee=stderr_redirector)
508 except error.AutoservRunError, e:
509 result = e.result_obj
510 result.exit_status = None
jadmanski91d56a92009-04-01 15:20:40 +0000511 disconnect_warnings.append(e.description)
512
jadmanski6dadd832009-02-05 23:39:27 +0000513 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000514 "Autotest client was disconnected: %s" % e.description,
515 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000516 except error.AutoservSSHTimeout:
517 result = utils.CmdResult(monitor_cmd, "", "", None, 0)
518 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000519 "Attempt to connect to Autotest client timed out",
520 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000521
522 stdout_read += len(result.stdout)
523 stderr_read += len(self._strip_stderr_prologue(result.stderr))
524
525 if result.exit_status is not None:
526 return result
527 elif not self.host.wait_up(client_disconnect_timeout):
528 raise error.AutoservSSHTimeout(
529 "client was disconnected, reconnect timed out")
530 finally:
531 self.host.job.resultdir = old_resultdir
532
533
534 def execute_section(self, section, timeout, stderr_redirector,
535 client_disconnect_timeout):
showardb18134f2009-03-20 20:52:18 +0000536 logging.info("Executing %s/bin/autotest %s/control phase %d",
537 self.autodir, self.autodir, section)
jadmanski6dadd832009-02-05 23:39:27 +0000538
539 if self.background:
540 result = self._execute_in_background(section, timeout)
541 else:
542 result = self._execute_daemon(section, timeout, stderr_redirector,
543 client_disconnect_timeout)
544
545 last_line = stderr_redirector.last_line
mbligh2bf2db62007-11-27 00:53:18 +0000546
jadmanskib264ed02009-01-12 23:54:27 +0000547 # check if we failed hard enough to warrant an exception
jadmanski0afbb632008-06-06 21:10:57 +0000548 if result.exit_status == 1:
jadmanskib264ed02009-01-12 23:54:27 +0000549 err = error.AutotestRunError("client job was aborted")
550 elif not self.background and not result.stderr:
551 err = error.AutotestRunError(
jadmanskie4130532009-03-17 18:01:28 +0000552 "execute_section %s failed to return anything\n"
553 "stdout:%s\n" % (section, result.stdout))
jadmanskib264ed02009-01-12 23:54:27 +0000554 else:
555 err = None
mbligh0e4613b2007-10-29 16:55:07 +0000556
jadmanskib264ed02009-01-12 23:54:27 +0000557 # log something if the client failed AND never finished logging
558 if err and not self.is_client_job_finished(last_line):
jadmanskia61edad2009-05-21 22:17:49 +0000559 self.log_unexpected_abort(stderr_redirector)
jadmanskib264ed02009-01-12 23:54:27 +0000560
561 if err:
562 raise err
563 else:
564 return stderr_redirector.last_line
jadmanski4600e342008-10-29 22:54:00 +0000565
566
567 def _wait_for_reboot(self):
showardb18134f2009-03-20 20:52:18 +0000568 logging.info("Client is rebooting")
569 logging.info("Waiting for client to halt")
jadmanski4600e342008-10-29 22:54:00 +0000570 if not self.host.wait_down(HALT_TIME):
571 err = "%s failed to shutdown after %d"
572 err %= (self.host.hostname, HALT_TIME)
573 raise error.AutotestRunError(err)
showardb18134f2009-03-20 20:52:18 +0000574 logging.info("Client down, waiting for restart")
jadmanski4600e342008-10-29 22:54:00 +0000575 if not self.host.wait_up(BOOT_TIME):
576 # since reboot failed
577 # hardreset the machine once if possible
578 # before failing this control file
579 warning = "%s did not come back up, hard resetting"
580 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000581 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000582 try:
583 self.host.hardreset(wait=False)
mblighd99d3b272008-12-22 14:41:27 +0000584 except (AttributeError, error.AutoservUnsupportedError):
jadmanski4600e342008-10-29 22:54:00 +0000585 warning = "Hard reset unsupported on %s"
586 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000587 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000588 raise error.AutotestRunError("%s failed to boot after %ds" %
589 (self.host.hostname, BOOT_TIME))
590 self.host.reboot_followup()
mblighdc735a22007-08-02 16:54:37 +0000591
592
jadmanski6bb32d72009-03-19 20:25:24 +0000593 def _process_client_state_file(self):
594 state_file = os.path.basename(self.remote_control_file) + ".state"
595 state_path = os.path.join(self.results_dir, state_file)
596 try:
597 state_dict = pickle.load(open(state_path))
598 except Exception, e:
599 msg = "Ignoring error while loading client job state file: %s" % e
showard372ce3e2009-04-07 18:12:03 +0000600 logging.warning(msg)
jadmanski6bb32d72009-03-19 20:25:24 +0000601 state_dict = {}
602
603 # clear out the state file
604 # TODO: stash the file away somewhere useful instead
605 try:
606 os.remove(state_path)
607 except Exception:
608 pass
609
mblighe9633f52009-07-02 19:01:09 +0000610 logging.debug("Persistent state variables pulled back from %s: %s",
611 self.host.hostname, state_dict)
jadmanski6bb32d72009-03-19 20:25:24 +0000612
613 if "__run_test_cleanup" in state_dict:
614 if state_dict["__run_test_cleanup"]:
615 self.host.job.enable_test_cleanup()
616 else:
617 self.host.job.disable_test_cleanup()
618
619 if "__last_boot_tag" in state_dict:
620 self.host.job.last_boot_tag = state_dict["__last_boot_tag"]
621
622 if "__sysinfo" in state_dict:
623 self.host.job.sysinfo.deserialize(state_dict["__sysinfo"])
624
625
jadmanski6dadd832009-02-05 23:39:27 +0000626 def execute_control(self, timeout=None, client_disconnect_timeout=None):
jadmanski6bb32d72009-03-19 20:25:24 +0000627 if not self.background:
628 collector = log_collector(self.host, self.tag, self.results_dir)
629 hostname = self.host.hostname
630 remote_results = collector.client_results_dir
631 local_results = collector.server_results_dir
632 self.host.job.add_client_log(hostname, remote_results,
633 local_results)
634
jadmanski0afbb632008-06-06 21:10:57 +0000635 section = 0
jadmanski4600e342008-10-29 22:54:00 +0000636 start_time = time.time()
637
jadmanski043e1132008-11-19 17:10:32 +0000638 logger = client_logger(self.host, self.tag, self.results_dir)
jadmanski4600e342008-10-29 22:54:00 +0000639 try:
640 while not timeout or time.time() < start_time + timeout:
641 if timeout:
642 section_timeout = start_time + timeout - time.time()
643 else:
644 section_timeout = None
645 last = self.execute_section(section, section_timeout,
jadmanski6dadd832009-02-05 23:39:27 +0000646 logger, client_disconnect_timeout)
mblighb3c0c912008-11-27 00:32:45 +0000647 if self.background:
648 return
jadmanski4600e342008-10-29 22:54:00 +0000649 section += 1
jadmanskib264ed02009-01-12 23:54:27 +0000650 if self.is_client_job_finished(last):
showardb18134f2009-03-20 20:52:18 +0000651 logging.info("Client complete")
jadmanski4600e342008-10-29 22:54:00 +0000652 return
jadmanskib264ed02009-01-12 23:54:27 +0000653 elif self.is_client_job_rebooting(last):
jadmanski79ab9282008-11-11 17:53:12 +0000654 try:
655 self._wait_for_reboot()
656 except error.AutotestRunError, e:
jadmanski043e1132008-11-19 17:10:32 +0000657 self.host.job.record("ABORT", None, "reboot", str(e))
658 self.host.job.record("END ABORT", None, None, str(e))
jadmanski79ab9282008-11-11 17:53:12 +0000659 raise
jadmanski4600e342008-10-29 22:54:00 +0000660 continue
661
662 # if we reach here, something unexpected happened
jadmanskia61edad2009-05-21 22:17:49 +0000663 self.log_unexpected_abort(logger)
jadmanski4600e342008-10-29 22:54:00 +0000664
665 # give the client machine a chance to recover from a crash
666 self.host.wait_up(CRASH_RECOVERY_TIME)
667 msg = ("Aborting - unexpected final status message from "
668 "client: %s\n") % last
669 raise error.AutotestRunError(msg)
670 finally:
jadmanski043e1132008-11-19 17:10:32 +0000671 logger.close()
jadmanski6bb32d72009-03-19 20:25:24 +0000672 if not self.background:
673 collector.collect_client_job_results()
674 self._process_client_state_file()
675 self.host.job.remove_client_log(hostname, remote_results,
676 local_results)
mblighdcd57a82007-07-11 23:06:47 +0000677
jadmanski0afbb632008-06-06 21:10:57 +0000678 # should only get here if we timed out
679 assert timeout
680 raise error.AutotestTimeoutError()
mbligh0e4613b2007-10-29 16:55:07 +0000681
mblighdcd57a82007-07-11 23:06:47 +0000682
683def _get_autodir(host):
mbligh3c7a1502008-07-24 18:08:47 +0000684 autodir = host.get_autodir()
685 if autodir:
jadmanski8297cf52009-05-21 22:23:02 +0000686 logging.debug('Using existing host autodir: %s', autodir)
mbligh3c7a1502008-07-24 18:08:47 +0000687 return autodir
jadmanski0afbb632008-06-06 21:10:57 +0000688 try:
689 # There's no clean way to do this. readlink may not exist
jadmanski8297cf52009-05-21 22:23:02 +0000690 cmd = ("python -c '%s' /etc/autotest.conf 2> /dev/null"
691 % "import os,sys; print os.readlink(sys.argv[1])")
mbligh3c7a1502008-07-24 18:08:47 +0000692 autodir = os.path.dirname(host.run(cmd).stdout)
693 if autodir:
jadmanski8297cf52009-05-21 22:23:02 +0000694 logging.debug('Using autodir from /etc/autotest.conf: %s', autodir)
mbligh3c7a1502008-07-24 18:08:47 +0000695 return autodir
jadmanski0afbb632008-06-06 21:10:57 +0000696 except error.AutoservRunError:
697 pass
698 for path in ['/usr/local/autotest', '/home/autotest']:
699 try:
jadmanski8297cf52009-05-21 22:23:02 +0000700 host.run('ls %s > /dev/null 2>&1' % path)
701 logging.debug('Found autodir at %s', path)
jadmanski0afbb632008-06-06 21:10:57 +0000702 return path
703 except error.AutoservRunError:
jadmanski8297cf52009-05-21 22:23:02 +0000704 logging.debug('%s does not exist', path)
705 raise error.AutotestRunError('Cannot figure out autotest directory')
mblighd8b39252008-03-20 21:15:03 +0000706
707
jadmanski043e1132008-11-19 17:10:32 +0000708class log_collector(object):
709 def __init__(self, host, client_tag, results_dir):
710 self.host = host
711 if not client_tag:
712 client_tag = "default"
713 self.client_results_dir = os.path.join(host.get_autodir(), "results",
714 client_tag)
715 self.server_results_dir = results_dir
716
717
718 def collect_client_job_results(self):
719 """ A method that collects all the current results of a running
720 client job into the results dir. By default does nothing as no
721 client job is running, but when running a client job you can override
722 this with something that will actually do something. """
723
724 # make an effort to wait for the machine to come up
725 try:
726 self.host.wait_up(timeout=30)
727 except error.AutoservError:
728 # don't worry about any errors, we'll try and
729 # get the results anyway
730 pass
731
jadmanski043e1132008-11-19 17:10:32 +0000732 # Copy all dirs in default to results_dir
733 try:
jadmanski043e1132008-11-19 17:10:32 +0000734 self.host.get_file(self.client_results_dir + '/',
mbligh45561782009-05-11 21:14:34 +0000735 self.server_results_dir, preserve_symlinks=True)
jadmanski043e1132008-11-19 17:10:32 +0000736 except Exception:
737 # well, don't stop running just because we couldn't get logs
showardb18134f2009-03-20 20:52:18 +0000738 e_msg = "Unexpected error copying test result logs, continuing ..."
739 logging.error(e_msg)
jadmanski043e1132008-11-19 17:10:32 +0000740 traceback.print_exc(file=sys.stdout)
741
742
jadmanski043e1132008-11-19 17:10:32 +0000743# a file-like object for catching stderr from an autotest client and
744# extracting status logs from it
745class client_logger(object):
746 """Partial file object to write to both stdout and
747 the status log file. We only implement those methods
748 utils.run() actually calls.
749
750 Note that this class is fairly closely coupled with server_job, as it
751 uses special job._ methods to actually carry out the loggging.
752 """
753 status_parser = re.compile(r"^AUTOTEST_STATUS:([^:]*):(.*)$")
754 test_complete_parser = re.compile(r"^AUTOTEST_TEST_COMPLETE:(.*)$")
755 extract_indent = re.compile(r"^(\t*).*$")
jadmanskiefe4ebf2009-05-21 22:12:30 +0000756 extract_timestamp = re.compile(r".*\ttimestamp=(\d+)\t.*$")
jadmanski043e1132008-11-19 17:10:32 +0000757
758 def __init__(self, host, tag, server_results_dir):
759 self.host = host
760 self.job = host.job
761 self.log_collector = log_collector(host, tag, server_results_dir)
762 self.leftover = ""
763 self.last_line = ""
jadmanskiefe4ebf2009-05-21 22:12:30 +0000764 self.newest_timestamp = float("-inf")
jadmanski043e1132008-11-19 17:10:32 +0000765 self.logs = {}
jadmanski6dadd832009-02-05 23:39:27 +0000766 self.server_warnings = []
jadmanski043e1132008-11-19 17:10:32 +0000767
768
jadmanskiefe4ebf2009-05-21 22:12:30 +0000769 def _update_timestamp(self, line):
770 match = self.extract_timestamp.search(line)
771 if match:
772 self.newest_timestamp = max(self.newest_timestamp,
773 int(match.group(1)))
774
775
jadmanski043e1132008-11-19 17:10:32 +0000776 def _process_log_dict(self, log_dict):
777 log_list = log_dict.pop("logs", [])
778 for key in sorted(log_dict.iterkeys()):
779 log_list += self._process_log_dict(log_dict.pop(key))
780 return log_list
781
782
783 def _process_logs(self):
784 """Go through the accumulated logs in self.log and print them
785 out to stdout and the status log. Note that this processes
786 logs in an ordering where:
787
788 1) logs to different tags are never interleaved
789 2) logs to x.y come before logs to x.y.z for all z
790 3) logs to x.y come before x.z whenever y < z
791
792 Note that this will in general not be the same as the
793 chronological ordering of the logs. However, if a chronological
794 ordering is desired that one can be reconstructed from the
795 status log by looking at timestamp lines."""
796 log_list = self._process_log_dict(self.logs)
797 for line in log_list:
798 self.job._record_prerendered(line + '\n')
799 if log_list:
800 self.last_line = log_list[-1]
801
802
803 def _process_quoted_line(self, tag, line):
804 """Process a line quoted with an AUTOTEST_STATUS flag. If the
805 tag is blank then we want to push out all the data we've been
806 building up in self.logs, and then the newest line. If the
807 tag is not blank, then push the line into the logs for handling
808 later."""
showardb18134f2009-03-20 20:52:18 +0000809 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +0000810 if tag == "":
811 self._process_logs()
812 self.job._record_prerendered(line + '\n')
813 self.last_line = line
814 else:
815 tag_parts = [int(x) for x in tag.split(".")]
816 log_dict = self.logs
817 for part in tag_parts:
818 log_dict = log_dict.setdefault(part, {})
819 log_list = log_dict.setdefault("logs", [])
820 log_list.append(line)
821
822
jadmanskif37df842009-02-11 00:03:26 +0000823 def _process_info_line(self, line):
824 """Check if line is an INFO line, and if it is, interpret any control
825 messages (e.g. enabling/disabling warnings) that it may contain."""
826 match = re.search(r"^\t*INFO\t----\t----(.*)\t[^\t]*$", line)
827 if not match:
828 return # not an INFO line
829 for field in match.group(1).split('\t'):
830 if field.startswith("warnings.enable="):
jadmanski16a7ff72009-04-01 18:19:53 +0000831 func = self.job.warning_manager.enable_warnings
jadmanskif37df842009-02-11 00:03:26 +0000832 elif field.startswith("warnings.disable="):
jadmanski16a7ff72009-04-01 18:19:53 +0000833 func = self.job.warning_manager.disable_warnings
jadmanskif37df842009-02-11 00:03:26 +0000834 else:
835 continue
836 warning_type = field.split("=", 1)[1]
jadmanski16a7ff72009-04-01 18:19:53 +0000837 func(warning_type)
jadmanskif37df842009-02-11 00:03:26 +0000838
839
jadmanski043e1132008-11-19 17:10:32 +0000840 def _process_line(self, line):
841 """Write out a line of data to the appropriate stream. Status
842 lines sent by autotest will be prepended with
843 "AUTOTEST_STATUS", and all other lines are ssh error
844 messages."""
845 status_match = self.status_parser.search(line)
846 test_complete_match = self.test_complete_parser.search(line)
847 if status_match:
848 tag, line = status_match.groups()
jadmanskif37df842009-02-11 00:03:26 +0000849 self._process_info_line(line)
jadmanski043e1132008-11-19 17:10:32 +0000850 self._process_quoted_line(tag, line)
851 elif test_complete_match:
jadmanskifcc0d5d2009-02-12 21:52:54 +0000852 self._process_logs()
jadmanski043e1132008-11-19 17:10:32 +0000853 fifo_path, = test_complete_match.groups()
854 self.log_collector.collect_client_job_results()
855 self.host.run("echo A > %s" % fifo_path)
856 else:
showardb18134f2009-03-20 20:52:18 +0000857 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +0000858
859
860 def _format_warnings(self, last_line, warnings):
861 # use the indentation of whatever the last log line was
862 indent = self.extract_indent.match(last_line).group(1)
863 # if the last line starts a new group, add an extra indent
864 if last_line.lstrip('\t').startswith("START\t"):
865 indent += '\t'
866 return [self.job._render_record("WARN", None, None, msg,
867 timestamp, indent).rstrip('\n')
868 for timestamp, msg in warnings]
869
870
871 def _process_warnings(self, last_line, log_dict, warnings):
872 if log_dict.keys() in ([], ["logs"]):
873 # there are no sub-jobs, just append the warnings here
874 warnings = self._format_warnings(last_line, warnings)
875 log_list = log_dict.setdefault("logs", [])
876 log_list += warnings
877 for warning in warnings:
878 sys.stdout.write(warning + '\n')
879 else:
880 # there are sub-jobs, so put the warnings in there
881 log_list = log_dict.get("logs", [])
882 if log_list:
883 last_line = log_list[-1]
884 for key in sorted(log_dict.iterkeys()):
885 if key != "logs":
886 self._process_warnings(last_line,
887 log_dict[key],
888 warnings)
889
jadmanskif37df842009-02-11 00:03:26 +0000890
jadmanski91d56a92009-04-01 15:20:40 +0000891 def log_warning(self, msg, warning_type):
jadmanski6dadd832009-02-05 23:39:27 +0000892 """Injects a WARN message into the current status logging stream."""
jadmanski91d56a92009-04-01 15:20:40 +0000893 timestamp = int(time.time())
894 if self.job.warning_manager.is_valid(timestamp, warning_type):
895 self.server_warnings.append((timestamp, msg))
jadmanski6dadd832009-02-05 23:39:27 +0000896
jadmanski043e1132008-11-19 17:10:32 +0000897
898 def write(self, data):
899 # first check for any new console warnings
jadmanski6dadd832009-02-05 23:39:27 +0000900 warnings = self.job._read_warnings() + self.server_warnings
901 warnings.sort() # sort into timestamp order
jadmanski043e1132008-11-19 17:10:32 +0000902 # now process the newest data written out
903 data = self.leftover + data
904 lines = data.split("\n")
905 # process every line but the last one
906 for line in lines[:-1]:
jadmanskiefe4ebf2009-05-21 22:12:30 +0000907 self._update_timestamp(line)
908 # output any warnings between now and the next status line
909 old_warnings = [(timestamp, msg) for timestamp, msg in warnings
910 if timestamp < self.newest_timestamp]
911 self._process_warnings(self.last_line, self.logs, warnings)
912 del warnings[:len(old_warnings)]
jadmanski043e1132008-11-19 17:10:32 +0000913 self._process_line(line)
jadmanskiefe4ebf2009-05-21 22:12:30 +0000914 # save off any warnings not yet logged for later processing
915 self.server_warnings = warnings
jadmanski043e1132008-11-19 17:10:32 +0000916 # save the last line for later processing
917 # since we may not have the whole line yet
918 self.leftover = lines[-1]
919
920
921 def flush(self):
922 sys.stdout.flush()
923
924
jadmanskia61edad2009-05-21 22:17:49 +0000925 def flush_all_buffers(self):
jadmanski043e1132008-11-19 17:10:32 +0000926 if self.leftover:
927 self._process_line(self.leftover)
jadmanskia61edad2009-05-21 22:17:49 +0000928 self.leftover = ""
jadmanskiefe4ebf2009-05-21 22:12:30 +0000929 self._process_warnings(self.last_line, self.logs, self.server_warnings)
jadmanski043e1132008-11-19 17:10:32 +0000930 self._process_logs()
931 self.flush()
932
933
jadmanskia61edad2009-05-21 22:17:49 +0000934 def close(self):
935 self.flush_all_buffers()
936
937
mbligha7007722009-01-13 00:37:11 +0000938SiteAutotest = client_utils.import_site_class(
939 __file__, "autotest_lib.server.site_autotest", "SiteAutotest",
940 BaseAutotest)
mblighd8b39252008-03-20 21:15:03 +0000941
942class Autotest(SiteAutotest):
jadmanski0afbb632008-06-06 21:10:57 +0000943 pass