blob: bf8f90d73a318bb3665b7d4e0fbc2c9cbe31dbfc [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
jadmanski0afbb632008-06-06 21:10:57 +0000212 def run(self, control_file, results_dir = '.', host = None,
jadmanski6dadd832009-02-05 23:39:27 +0000213 timeout=None, tag=None, parallel_flag=False, background=False,
214 client_disconnect_timeout=1800):
jadmanski0afbb632008-06-06 21:10:57 +0000215 """
216 Run an autotest job on the remote machine.
mbligh9a3f5e52008-05-28 21:21:43 +0000217
jadmanski0afbb632008-06-06 21:10:57 +0000218 Args:
219 control_file: an open file-like-obj of the control file
220 results_dir: a str path where the results should be stored
221 on the local filesystem
222 host: a Host instance on which the control file should
223 be run
224 tag: tag name for the client side instance of autotest
225 parallel_flag: flag set when multiple jobs are run at the
226 same time
mblighb3c0c912008-11-27 00:32:45 +0000227 background: indicates that the client should be launched as
228 a background job; the code calling run will be
229 responsible for monitoring the client and
230 collecting the results
jadmanski0afbb632008-06-06 21:10:57 +0000231 Raises:
232 AutotestRunError: if there is a problem executing
233 the control file
234 """
235 host = self._get_host_and_setup(host)
236 results_dir = os.path.abspath(results_dir)
mblighc1cbc992008-05-27 20:01:45 +0000237
jadmanski0afbb632008-06-06 21:10:57 +0000238 if tag:
239 results_dir = os.path.join(results_dir, tag)
mblighc1cbc992008-05-27 20:01:45 +0000240
mblighb3c0c912008-11-27 00:32:45 +0000241 atrun = _Run(host, results_dir, tag, parallel_flag, background)
jadmanski6dadd832009-02-05 23:39:27 +0000242 self._do_run(control_file, results_dir, host, atrun, timeout,
243 client_disconnect_timeout)
mblighd8b39252008-03-20 21:15:03 +0000244
245
jadmanski0afbb632008-06-06 21:10:57 +0000246 def _get_host_and_setup(self, host):
247 if not host:
248 host = self.host
249 if not self.installed:
250 self.install(host)
mbligh91334902007-09-28 01:47:59 +0000251
jadmanski0afbb632008-06-06 21:10:57 +0000252 host.wait_up(timeout=30)
253 return host
mblighd8b39252008-03-20 21:15:03 +0000254
255
jadmanski6dadd832009-02-05 23:39:27 +0000256 def _do_run(self, control_file, results_dir, host, atrun, timeout,
257 client_disconnect_timeout):
jadmanski0afbb632008-06-06 21:10:57 +0000258 try:
259 atrun.verify_machine()
260 except:
showardb18134f2009-03-20 20:52:18 +0000261 logging.error("Verify failed on %s. Reinstalling autotest",
262 host.hostname)
jadmanski0afbb632008-06-06 21:10:57 +0000263 self.install(host)
264 atrun.verify_machine()
265 debug = os.path.join(results_dir, 'debug')
266 try:
267 os.makedirs(debug)
mbligh09108442008-10-15 16:27:38 +0000268 except Exception:
jadmanski0afbb632008-06-06 21:10:57 +0000269 pass
mbligh9a3f5e52008-05-28 21:21:43 +0000270
mbligh09108442008-10-15 16:27:38 +0000271 delete_file_list = [atrun.remote_control_file,
272 atrun.remote_control_file + '.state',
273 atrun.manual_control_file,
274 atrun.manual_control_file + '.state']
275 cmd = ';'.join('rm -f ' + control for control in delete_file_list)
276 host.run(cmd, ignore_status=True)
mbligh9a3f5e52008-05-28 21:21:43 +0000277
jadmanski0afbb632008-06-06 21:10:57 +0000278 tmppath = utils.get(control_file)
mblighc5ddfd12008-08-04 17:15:00 +0000279
jadmanskicb0e1612009-02-27 18:03:10 +0000280 # build up the initialization prologue for the control file
281 prologue_lines = []
282 prologue_lines.append("job.default_boot_tag(%r)\n"
283 % host.job.last_boot_tag)
284 prologue_lines.append("job.default_test_cleanup(%r)\n"
285 % host.job.run_test_cleanup)
jadmanski23afbec2008-09-17 18:12:07 +0000286
mbligh09108442008-10-15 16:27:38 +0000287 # If the packaging system is being used, add the repository list.
mblighc5ddfd12008-08-04 17:15:00 +0000288 try:
mblighc5ddfd12008-08-04 17:15:00 +0000289 c = global_config.global_config
290 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list)
mbligh76d19f72008-10-15 16:24:43 +0000291 pkgmgr = packages.PackageManager('autotest', hostname=host.hostname,
292 repo_urls=repos)
jadmanskie2eef7b2009-03-03 23:55:13 +0000293 prologue_lines.append('job.add_repository(%s)\n'
294 % pkgmgr.repo_urls)
mblighc5ddfd12008-08-04 17:15:00 +0000295 except global_config.ConfigError, e:
296 pass
297
jadmanskie2eef7b2009-03-03 23:55:13 +0000298 # on full-size installs, turn on any profilers the server is using
jadmanski6fc7ff52009-05-04 19:59:56 +0000299 if not self.lightweight and not atrun.background:
jadmanskie2eef7b2009-03-03 23:55:13 +0000300 running_profilers = host.job.profilers.add_log.iteritems()
301 for profiler, (args, dargs) in running_profilers:
302 call_args = [repr(profiler)]
303 call_args += [repr(arg) for arg in args]
304 call_args += ["%s=%r" % item for item in dargs.iteritems()]
305 prologue_lines.append("job.profilers.add(%s)\n"
306 % ", ".join(call_args))
307 cfile = "".join(prologue_lines)
308
mbligh09108442008-10-15 16:27:38 +0000309 cfile += open(tmppath).read()
310 open(tmppath, "w").write(cfile)
mblighc5ddfd12008-08-04 17:15:00 +0000311
jadmanskic09fc152008-10-15 17:56:59 +0000312 # Create and copy state file to remote_control_file + '.state'
313 sysinfo_state = {"__sysinfo": host.job.sysinfo.serialize()}
314 state_file = self._create_state_file(host.job, sysinfo_state)
315 host.send_file(state_file, atrun.remote_control_file + '.state')
316 os.remove(state_file)
317
mblighc5ddfd12008-08-04 17:15:00 +0000318 # Copy control_file to remote_control_file on the host
jadmanski0afbb632008-06-06 21:10:57 +0000319 host.send_file(tmppath, atrun.remote_control_file)
320 if os.path.abspath(tmppath) != os.path.abspath(control_file):
321 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000322
jadmanski6bb32d72009-03-19 20:25:24 +0000323 atrun.execute_control(
jadmanski6dadd832009-02-05 23:39:27 +0000324 timeout=timeout,
325 client_disconnect_timeout=client_disconnect_timeout)
jadmanski23afbec2008-09-17 18:12:07 +0000326
327
jadmanskic09fc152008-10-15 17:56:59 +0000328 def _create_state_file(self, job, state_dict):
329 """ Create a state file from a dictionary. Returns the path of the
330 state file. """
331 fd, path = tempfile.mkstemp(dir=job.tmpdir)
332 state_file = os.fdopen(fd, "w")
333 pickle.dump(state_dict, state_file)
334 state_file.close()
335 return path
336
337
jadmanski0afbb632008-06-06 21:10:57 +0000338 def run_timed_test(self, test_name, results_dir='.', host=None,
jadmanskic98c4702009-01-05 15:50:06 +0000339 timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000340 """
341 Assemble a tiny little control file to just run one test,
342 and run it as an autotest client-side test
343 """
344 if not host:
345 host = self.host
346 if not self.installed:
347 self.install(host)
348 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
349 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
350 control = "job.run_test(%s)\n" % cmd
jadmanskic98c4702009-01-05 15:50:06 +0000351 self.run(control, results_dir, host, timeout=timeout)
mbligh0e4613b2007-10-29 16:55:07 +0000352
353
jadmanskic98c4702009-01-05 15:50:06 +0000354 def run_test(self, test_name, results_dir='.', host=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000355 self.run_timed_test(test_name, results_dir, host, timeout=None,
jadmanskic98c4702009-01-05 15:50:06 +0000356 *args, **dargs)
mblighd54832b2007-07-25 16:46:56 +0000357
358
mblighdcd57a82007-07-11 23:06:47 +0000359class _Run(object):
jadmanski0afbb632008-06-06 21:10:57 +0000360 """
361 Represents a run of autotest control file. This class maintains
362 all the state necessary as an autotest control file is executed.
mblighdcd57a82007-07-11 23:06:47 +0000363
jadmanski0afbb632008-06-06 21:10:57 +0000364 It is not intended to be used directly, rather control files
365 should be run using the run method in Autotest.
366 """
mblighb3c0c912008-11-27 00:32:45 +0000367 def __init__(self, host, results_dir, tag, parallel_flag, background):
jadmanski0afbb632008-06-06 21:10:57 +0000368 self.host = host
369 self.results_dir = results_dir
370 self.env = host.env
371 self.tag = tag
372 self.parallel_flag = parallel_flag
mblighb3c0c912008-11-27 00:32:45 +0000373 self.background = background
jadmanski0afbb632008-06-06 21:10:57 +0000374 self.autodir = _get_autodir(self.host)
mbligh78bf5352008-07-11 20:27:36 +0000375 control = os.path.join(self.autodir, 'control')
jadmanski0afbb632008-06-06 21:10:57 +0000376 if tag:
mbligh78bf5352008-07-11 20:27:36 +0000377 control += '.' + tag
378 self.manual_control_file = control
379 self.remote_control_file = control + '.autoserv'
mblighdc735a22007-08-02 16:54:37 +0000380
381
jadmanski0afbb632008-06-06 21:10:57 +0000382 def verify_machine(self):
383 binary = os.path.join(self.autodir, 'bin/autotest')
384 try:
385 self.host.run('ls %s > /dev/null 2>&1' % binary)
386 except:
387 raise "Autotest does not appear to be installed"
mblighdc735a22007-08-02 16:54:37 +0000388
jadmanski0afbb632008-06-06 21:10:57 +0000389 if not self.parallel_flag:
390 tmpdir = os.path.join(self.autodir, 'tmp')
391 download = os.path.join(self.autodir, 'tests/download')
392 self.host.run('umount %s' % tmpdir, ignore_status=True)
393 self.host.run('umount %s' % download, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000394
jadmanski6dadd832009-02-05 23:39:27 +0000395
396 def get_base_cmd_args(self, section):
397 args = []
jadmanski0afbb632008-06-06 21:10:57 +0000398 if section > 0:
jadmanski6dadd832009-02-05 23:39:27 +0000399 args.append('-c')
jadmanski0afbb632008-06-06 21:10:57 +0000400 if self.tag:
jadmanski6dadd832009-02-05 23:39:27 +0000401 args.append('-t %s' % self.tag)
jadmanski0afbb632008-06-06 21:10:57 +0000402 if self.host.job.use_external_logging():
jadmanski6dadd832009-02-05 23:39:27 +0000403 args.append('-l')
404 args.append(self.remote_control_file)
405 return args
406
407
408 def get_background_cmd(self, section):
409 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotest_client')]
410 cmd += self.get_base_cmd_args(section)
411 cmd.append('>/dev/null 2>/dev/null &')
412 return ' '.join(cmd)
413
414
415 def get_daemon_cmd(self, section, monitor_dir):
416 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotestd'),
417 monitor_dir, '-H autoserv']
418 cmd += self.get_base_cmd_args(section)
419 cmd.append('>/dev/null 2>/dev/null </dev/null &')
420 return ' '.join(cmd)
421
422
423 def get_monitor_cmd(self, monitor_dir, stdout_read, stderr_read):
424 cmd = [os.path.join(self.autodir, 'bin', 'autotestd_monitor'),
425 monitor_dir, str(stdout_read), str(stderr_read)]
jadmanski0afbb632008-06-06 21:10:57 +0000426 return ' '.join(cmd)
mblighadf2aab2007-11-29 18:16:43 +0000427
mblighd8b39252008-03-20 21:15:03 +0000428
jadmanski0afbb632008-06-06 21:10:57 +0000429 def get_client_log(self, section):
jadmanskie0c7fb62008-12-16 20:51:16 +0000430 """ Find what the "next" client.log.* file should be and open it. """
431 debug_dir = os.path.join(self.results_dir, "debug")
432 client_logs = glob.glob(os.path.join(debug_dir, "client.log.*"))
433 next_log = os.path.join(debug_dir, "client.log.%d" % len(client_logs))
434 return open(next_log, "w", 0)
mblighd8b39252008-03-20 21:15:03 +0000435
436
jadmanskib264ed02009-01-12 23:54:27 +0000437 @staticmethod
438 def is_client_job_finished(last_line):
439 return bool(re.match(r'^END .*\t----\t----\t.*$', last_line))
440
441
442 @staticmethod
443 def is_client_job_rebooting(last_line):
444 return bool(re.match(r'^\t*GOOD\t----\treboot\.start.*$', last_line))
445
446
jadmanskia61edad2009-05-21 22:17:49 +0000447 def log_unexpected_abort(self, stderr_redirector):
448 stderr_redirector.flush_all_buffers()
jadmanskib264ed02009-01-12 23:54:27 +0000449 msg = "Autotest client terminated unexpectedly"
450 self.host.job.record("END ABORT", None, None, msg)
451
452
jadmanski6dadd832009-02-05 23:39:27 +0000453 def _execute_in_background(self, section, timeout):
454 full_cmd = self.get_background_cmd(section)
455 devnull = open(os.devnull, "w")
mblighd8b39252008-03-20 21:15:03 +0000456
jadmanski6dadd832009-02-05 23:39:27 +0000457 old_resultdir = self.host.job.resultdir
jadmanski0afbb632008-06-06 21:10:57 +0000458 try:
jadmanski0afbb632008-06-06 21:10:57 +0000459 self.host.job.resultdir = self.results_dir
460 result = self.host.run(full_cmd, ignore_status=True,
461 timeout=timeout,
jadmanski6dadd832009-02-05 23:39:27 +0000462 stdout_tee=devnull,
463 stderr_tee=devnull)
jadmanski0afbb632008-06-06 21:10:57 +0000464 finally:
jadmanski0afbb632008-06-06 21:10:57 +0000465 self.host.job.resultdir = old_resultdir
jadmanski6dadd832009-02-05 23:39:27 +0000466
467 return result
468
469
470 @staticmethod
471 def _strip_stderr_prologue(stderr):
472 """Strips the 'standard' prologue that get pre-pended to every
473 remote command and returns the text that was actually written to
474 stderr by the remote command."""
475 stderr_lines = stderr.split("\n")[1:]
476 if not stderr_lines:
477 return ""
478 elif stderr_lines[0].startswith("NOTE: autotestd_monitor"):
479 del stderr_lines[0]
480 return "\n".join(stderr_lines)
481
482
483 def _execute_daemon(self, section, timeout, stderr_redirector,
484 client_disconnect_timeout):
485 monitor_dir = self.host.get_tmp_dir()
486 daemon_cmd = self.get_daemon_cmd(section, monitor_dir)
487 client_log = self.get_client_log(section)
488
489 stdout_read = stderr_read = 0
490 old_resultdir = self.host.job.resultdir
491 try:
jadmanski29a4c702009-03-03 23:30:59 +0000492 self.host.job.resultdir = self.results_dir
jadmanski6dadd832009-02-05 23:39:27 +0000493 self.host.run(daemon_cmd, ignore_status=True, timeout=timeout)
jadmanski91d56a92009-04-01 15:20:40 +0000494 disconnect_warnings = []
jadmanski6dadd832009-02-05 23:39:27 +0000495 while True:
496 monitor_cmd = self.get_monitor_cmd(monitor_dir, stdout_read,
497 stderr_read)
498 try:
499 result = self.host.run(monitor_cmd, ignore_status=True,
500 timeout=timeout,
501 stdout_tee=client_log,
502 stderr_tee=stderr_redirector)
503 except error.AutoservRunError, e:
504 result = e.result_obj
505 result.exit_status = None
jadmanski91d56a92009-04-01 15:20:40 +0000506 disconnect_warnings.append(e.description)
507
jadmanski6dadd832009-02-05 23:39:27 +0000508 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000509 "Autotest client was disconnected: %s" % e.description,
510 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000511 except error.AutoservSSHTimeout:
512 result = utils.CmdResult(monitor_cmd, "", "", None, 0)
513 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000514 "Attempt to connect to Autotest client timed out",
515 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000516
517 stdout_read += len(result.stdout)
518 stderr_read += len(self._strip_stderr_prologue(result.stderr))
519
520 if result.exit_status is not None:
521 return result
522 elif not self.host.wait_up(client_disconnect_timeout):
523 raise error.AutoservSSHTimeout(
524 "client was disconnected, reconnect timed out")
525 finally:
526 self.host.job.resultdir = old_resultdir
527
528
529 def execute_section(self, section, timeout, stderr_redirector,
530 client_disconnect_timeout):
showardb18134f2009-03-20 20:52:18 +0000531 logging.info("Executing %s/bin/autotest %s/control phase %d",
532 self.autodir, self.autodir, section)
jadmanski6dadd832009-02-05 23:39:27 +0000533
534 if self.background:
535 result = self._execute_in_background(section, timeout)
536 else:
537 result = self._execute_daemon(section, timeout, stderr_redirector,
538 client_disconnect_timeout)
539
540 last_line = stderr_redirector.last_line
mbligh2bf2db62007-11-27 00:53:18 +0000541
jadmanskib264ed02009-01-12 23:54:27 +0000542 # check if we failed hard enough to warrant an exception
jadmanski0afbb632008-06-06 21:10:57 +0000543 if result.exit_status == 1:
jadmanskib264ed02009-01-12 23:54:27 +0000544 err = error.AutotestRunError("client job was aborted")
545 elif not self.background and not result.stderr:
546 err = error.AutotestRunError(
jadmanskie4130532009-03-17 18:01:28 +0000547 "execute_section %s failed to return anything\n"
548 "stdout:%s\n" % (section, result.stdout))
jadmanskib264ed02009-01-12 23:54:27 +0000549 else:
550 err = None
mbligh0e4613b2007-10-29 16:55:07 +0000551
jadmanskib264ed02009-01-12 23:54:27 +0000552 # log something if the client failed AND never finished logging
553 if err and not self.is_client_job_finished(last_line):
jadmanskia61edad2009-05-21 22:17:49 +0000554 self.log_unexpected_abort(stderr_redirector)
jadmanskib264ed02009-01-12 23:54:27 +0000555
556 if err:
557 raise err
558 else:
559 return stderr_redirector.last_line
jadmanski4600e342008-10-29 22:54:00 +0000560
561
562 def _wait_for_reboot(self):
showardb18134f2009-03-20 20:52:18 +0000563 logging.info("Client is rebooting")
564 logging.info("Waiting for client to halt")
jadmanski4600e342008-10-29 22:54:00 +0000565 if not self.host.wait_down(HALT_TIME):
566 err = "%s failed to shutdown after %d"
567 err %= (self.host.hostname, HALT_TIME)
568 raise error.AutotestRunError(err)
showardb18134f2009-03-20 20:52:18 +0000569 logging.info("Client down, waiting for restart")
jadmanski4600e342008-10-29 22:54:00 +0000570 if not self.host.wait_up(BOOT_TIME):
571 # since reboot failed
572 # hardreset the machine once if possible
573 # before failing this control file
574 warning = "%s did not come back up, hard resetting"
575 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000576 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000577 try:
578 self.host.hardreset(wait=False)
mblighd99d3b272008-12-22 14:41:27 +0000579 except (AttributeError, error.AutoservUnsupportedError):
jadmanski4600e342008-10-29 22:54:00 +0000580 warning = "Hard reset unsupported on %s"
581 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000582 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000583 raise error.AutotestRunError("%s failed to boot after %ds" %
584 (self.host.hostname, BOOT_TIME))
585 self.host.reboot_followup()
mblighdc735a22007-08-02 16:54:37 +0000586
587
jadmanski6bb32d72009-03-19 20:25:24 +0000588 def _process_client_state_file(self):
589 state_file = os.path.basename(self.remote_control_file) + ".state"
590 state_path = os.path.join(self.results_dir, state_file)
591 try:
592 state_dict = pickle.load(open(state_path))
593 except Exception, e:
594 msg = "Ignoring error while loading client job state file: %s" % e
showard372ce3e2009-04-07 18:12:03 +0000595 logging.warning(msg)
jadmanski6bb32d72009-03-19 20:25:24 +0000596 state_dict = {}
597
598 # clear out the state file
599 # TODO: stash the file away somewhere useful instead
600 try:
601 os.remove(state_path)
602 except Exception:
603 pass
604
605 msg = "Persistent state variables pulled back from %s: %s"
606 msg %= (self.host.hostname, state_dict)
607 print msg
608
609 if "__run_test_cleanup" in state_dict:
610 if state_dict["__run_test_cleanup"]:
611 self.host.job.enable_test_cleanup()
612 else:
613 self.host.job.disable_test_cleanup()
614
615 if "__last_boot_tag" in state_dict:
616 self.host.job.last_boot_tag = state_dict["__last_boot_tag"]
617
618 if "__sysinfo" in state_dict:
619 self.host.job.sysinfo.deserialize(state_dict["__sysinfo"])
620
621
jadmanski6dadd832009-02-05 23:39:27 +0000622 def execute_control(self, timeout=None, client_disconnect_timeout=None):
jadmanski6bb32d72009-03-19 20:25:24 +0000623 if not self.background:
624 collector = log_collector(self.host, self.tag, self.results_dir)
625 hostname = self.host.hostname
626 remote_results = collector.client_results_dir
627 local_results = collector.server_results_dir
628 self.host.job.add_client_log(hostname, remote_results,
629 local_results)
630
jadmanski0afbb632008-06-06 21:10:57 +0000631 section = 0
jadmanski4600e342008-10-29 22:54:00 +0000632 start_time = time.time()
633
jadmanski043e1132008-11-19 17:10:32 +0000634 logger = client_logger(self.host, self.tag, self.results_dir)
jadmanski4600e342008-10-29 22:54:00 +0000635 try:
636 while not timeout or time.time() < start_time + timeout:
637 if timeout:
638 section_timeout = start_time + timeout - time.time()
639 else:
640 section_timeout = None
641 last = self.execute_section(section, section_timeout,
jadmanski6dadd832009-02-05 23:39:27 +0000642 logger, client_disconnect_timeout)
mblighb3c0c912008-11-27 00:32:45 +0000643 if self.background:
644 return
jadmanski4600e342008-10-29 22:54:00 +0000645 section += 1
jadmanskib264ed02009-01-12 23:54:27 +0000646 if self.is_client_job_finished(last):
showardb18134f2009-03-20 20:52:18 +0000647 logging.info("Client complete")
jadmanski4600e342008-10-29 22:54:00 +0000648 return
jadmanskib264ed02009-01-12 23:54:27 +0000649 elif self.is_client_job_rebooting(last):
jadmanski79ab9282008-11-11 17:53:12 +0000650 try:
651 self._wait_for_reboot()
652 except error.AutotestRunError, e:
jadmanski043e1132008-11-19 17:10:32 +0000653 self.host.job.record("ABORT", None, "reboot", str(e))
654 self.host.job.record("END ABORT", None, None, str(e))
jadmanski79ab9282008-11-11 17:53:12 +0000655 raise
jadmanski4600e342008-10-29 22:54:00 +0000656 continue
657
658 # if we reach here, something unexpected happened
jadmanskia61edad2009-05-21 22:17:49 +0000659 self.log_unexpected_abort(logger)
jadmanski4600e342008-10-29 22:54:00 +0000660
661 # give the client machine a chance to recover from a crash
662 self.host.wait_up(CRASH_RECOVERY_TIME)
663 msg = ("Aborting - unexpected final status message from "
664 "client: %s\n") % last
665 raise error.AutotestRunError(msg)
666 finally:
jadmanski043e1132008-11-19 17:10:32 +0000667 logger.close()
jadmanski6bb32d72009-03-19 20:25:24 +0000668 if not self.background:
669 collector.collect_client_job_results()
670 self._process_client_state_file()
671 self.host.job.remove_client_log(hostname, remote_results,
672 local_results)
mblighdcd57a82007-07-11 23:06:47 +0000673
jadmanski0afbb632008-06-06 21:10:57 +0000674 # should only get here if we timed out
675 assert timeout
676 raise error.AutotestTimeoutError()
mbligh0e4613b2007-10-29 16:55:07 +0000677
mblighdcd57a82007-07-11 23:06:47 +0000678
679def _get_autodir(host):
mbligh3c7a1502008-07-24 18:08:47 +0000680 autodir = host.get_autodir()
681 if autodir:
682 return autodir
jadmanski0afbb632008-06-06 21:10:57 +0000683 try:
684 # There's no clean way to do this. readlink may not exist
685 cmd = "python -c 'import os,sys; print os.readlink(sys.argv[1])' /etc/autotest.conf 2> /dev/null"
mbligh3c7a1502008-07-24 18:08:47 +0000686 autodir = os.path.dirname(host.run(cmd).stdout)
687 if autodir:
688 return autodir
jadmanski0afbb632008-06-06 21:10:57 +0000689 except error.AutoservRunError:
690 pass
691 for path in ['/usr/local/autotest', '/home/autotest']:
692 try:
jadmanski169ecad2008-09-12 15:49:44 +0000693 host.run('ls %s > /dev/null 2>&1' %
694 os.path.join(path, 'bin/autotest'))
jadmanski0afbb632008-06-06 21:10:57 +0000695 return path
696 except error.AutoservRunError:
697 pass
698 raise error.AutotestRunError("Cannot figure out autotest directory")
mblighd8b39252008-03-20 21:15:03 +0000699
700
jadmanski043e1132008-11-19 17:10:32 +0000701class log_collector(object):
702 def __init__(self, host, client_tag, results_dir):
703 self.host = host
704 if not client_tag:
705 client_tag = "default"
706 self.client_results_dir = os.path.join(host.get_autodir(), "results",
707 client_tag)
708 self.server_results_dir = results_dir
709
710
711 def collect_client_job_results(self):
712 """ A method that collects all the current results of a running
713 client job into the results dir. By default does nothing as no
714 client job is running, but when running a client job you can override
715 this with something that will actually do something. """
716
717 # make an effort to wait for the machine to come up
718 try:
719 self.host.wait_up(timeout=30)
720 except error.AutoservError:
721 # don't worry about any errors, we'll try and
722 # get the results anyway
723 pass
724
jadmanski043e1132008-11-19 17:10:32 +0000725 # Copy all dirs in default to results_dir
726 try:
jadmanski043e1132008-11-19 17:10:32 +0000727 self.host.get_file(self.client_results_dir + '/',
mbligh45561782009-05-11 21:14:34 +0000728 self.server_results_dir, preserve_symlinks=True)
jadmanski043e1132008-11-19 17:10:32 +0000729 except Exception:
730 # well, don't stop running just because we couldn't get logs
showardb18134f2009-03-20 20:52:18 +0000731 e_msg = "Unexpected error copying test result logs, continuing ..."
732 logging.error(e_msg)
jadmanski043e1132008-11-19 17:10:32 +0000733 traceback.print_exc(file=sys.stdout)
734
735
jadmanski043e1132008-11-19 17:10:32 +0000736# a file-like object for catching stderr from an autotest client and
737# extracting status logs from it
738class client_logger(object):
739 """Partial file object to write to both stdout and
740 the status log file. We only implement those methods
741 utils.run() actually calls.
742
743 Note that this class is fairly closely coupled with server_job, as it
744 uses special job._ methods to actually carry out the loggging.
745 """
746 status_parser = re.compile(r"^AUTOTEST_STATUS:([^:]*):(.*)$")
747 test_complete_parser = re.compile(r"^AUTOTEST_TEST_COMPLETE:(.*)$")
748 extract_indent = re.compile(r"^(\t*).*$")
jadmanskiefe4ebf2009-05-21 22:12:30 +0000749 extract_timestamp = re.compile(r".*\ttimestamp=(\d+)\t.*$")
jadmanski043e1132008-11-19 17:10:32 +0000750
751 def __init__(self, host, tag, server_results_dir):
752 self.host = host
753 self.job = host.job
754 self.log_collector = log_collector(host, tag, server_results_dir)
755 self.leftover = ""
756 self.last_line = ""
jadmanskiefe4ebf2009-05-21 22:12:30 +0000757 self.newest_timestamp = float("-inf")
jadmanski043e1132008-11-19 17:10:32 +0000758 self.logs = {}
jadmanski6dadd832009-02-05 23:39:27 +0000759 self.server_warnings = []
jadmanski043e1132008-11-19 17:10:32 +0000760
761
jadmanskiefe4ebf2009-05-21 22:12:30 +0000762 def _update_timestamp(self, line):
763 match = self.extract_timestamp.search(line)
764 if match:
765 self.newest_timestamp = max(self.newest_timestamp,
766 int(match.group(1)))
767
768
jadmanski043e1132008-11-19 17:10:32 +0000769 def _process_log_dict(self, log_dict):
770 log_list = log_dict.pop("logs", [])
771 for key in sorted(log_dict.iterkeys()):
772 log_list += self._process_log_dict(log_dict.pop(key))
773 return log_list
774
775
776 def _process_logs(self):
777 """Go through the accumulated logs in self.log and print them
778 out to stdout and the status log. Note that this processes
779 logs in an ordering where:
780
781 1) logs to different tags are never interleaved
782 2) logs to x.y come before logs to x.y.z for all z
783 3) logs to x.y come before x.z whenever y < z
784
785 Note that this will in general not be the same as the
786 chronological ordering of the logs. However, if a chronological
787 ordering is desired that one can be reconstructed from the
788 status log by looking at timestamp lines."""
789 log_list = self._process_log_dict(self.logs)
790 for line in log_list:
791 self.job._record_prerendered(line + '\n')
792 if log_list:
793 self.last_line = log_list[-1]
794
795
796 def _process_quoted_line(self, tag, line):
797 """Process a line quoted with an AUTOTEST_STATUS flag. If the
798 tag is blank then we want to push out all the data we've been
799 building up in self.logs, and then the newest line. If the
800 tag is not blank, then push the line into the logs for handling
801 later."""
showardb18134f2009-03-20 20:52:18 +0000802 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +0000803 if tag == "":
804 self._process_logs()
805 self.job._record_prerendered(line + '\n')
806 self.last_line = line
807 else:
808 tag_parts = [int(x) for x in tag.split(".")]
809 log_dict = self.logs
810 for part in tag_parts:
811 log_dict = log_dict.setdefault(part, {})
812 log_list = log_dict.setdefault("logs", [])
813 log_list.append(line)
814
815
jadmanskif37df842009-02-11 00:03:26 +0000816 def _process_info_line(self, line):
817 """Check if line is an INFO line, and if it is, interpret any control
818 messages (e.g. enabling/disabling warnings) that it may contain."""
819 match = re.search(r"^\t*INFO\t----\t----(.*)\t[^\t]*$", line)
820 if not match:
821 return # not an INFO line
822 for field in match.group(1).split('\t'):
823 if field.startswith("warnings.enable="):
jadmanski16a7ff72009-04-01 18:19:53 +0000824 func = self.job.warning_manager.enable_warnings
jadmanskif37df842009-02-11 00:03:26 +0000825 elif field.startswith("warnings.disable="):
jadmanski16a7ff72009-04-01 18:19:53 +0000826 func = self.job.warning_manager.disable_warnings
jadmanskif37df842009-02-11 00:03:26 +0000827 else:
828 continue
829 warning_type = field.split("=", 1)[1]
jadmanski16a7ff72009-04-01 18:19:53 +0000830 func(warning_type)
jadmanskif37df842009-02-11 00:03:26 +0000831
832
jadmanski043e1132008-11-19 17:10:32 +0000833 def _process_line(self, line):
834 """Write out a line of data to the appropriate stream. Status
835 lines sent by autotest will be prepended with
836 "AUTOTEST_STATUS", and all other lines are ssh error
837 messages."""
838 status_match = self.status_parser.search(line)
839 test_complete_match = self.test_complete_parser.search(line)
840 if status_match:
841 tag, line = status_match.groups()
jadmanskif37df842009-02-11 00:03:26 +0000842 self._process_info_line(line)
jadmanski043e1132008-11-19 17:10:32 +0000843 self._process_quoted_line(tag, line)
844 elif test_complete_match:
jadmanskifcc0d5d2009-02-12 21:52:54 +0000845 self._process_logs()
jadmanski043e1132008-11-19 17:10:32 +0000846 fifo_path, = test_complete_match.groups()
847 self.log_collector.collect_client_job_results()
848 self.host.run("echo A > %s" % fifo_path)
849 else:
showardb18134f2009-03-20 20:52:18 +0000850 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +0000851
852
853 def _format_warnings(self, last_line, warnings):
854 # use the indentation of whatever the last log line was
855 indent = self.extract_indent.match(last_line).group(1)
856 # if the last line starts a new group, add an extra indent
857 if last_line.lstrip('\t').startswith("START\t"):
858 indent += '\t'
859 return [self.job._render_record("WARN", None, None, msg,
860 timestamp, indent).rstrip('\n')
861 for timestamp, msg in warnings]
862
863
864 def _process_warnings(self, last_line, log_dict, warnings):
865 if log_dict.keys() in ([], ["logs"]):
866 # there are no sub-jobs, just append the warnings here
867 warnings = self._format_warnings(last_line, warnings)
868 log_list = log_dict.setdefault("logs", [])
869 log_list += warnings
870 for warning in warnings:
871 sys.stdout.write(warning + '\n')
872 else:
873 # there are sub-jobs, so put the warnings in there
874 log_list = log_dict.get("logs", [])
875 if log_list:
876 last_line = log_list[-1]
877 for key in sorted(log_dict.iterkeys()):
878 if key != "logs":
879 self._process_warnings(last_line,
880 log_dict[key],
881 warnings)
882
jadmanskif37df842009-02-11 00:03:26 +0000883
jadmanski91d56a92009-04-01 15:20:40 +0000884 def log_warning(self, msg, warning_type):
jadmanski6dadd832009-02-05 23:39:27 +0000885 """Injects a WARN message into the current status logging stream."""
jadmanski91d56a92009-04-01 15:20:40 +0000886 timestamp = int(time.time())
887 if self.job.warning_manager.is_valid(timestamp, warning_type):
888 self.server_warnings.append((timestamp, msg))
jadmanski6dadd832009-02-05 23:39:27 +0000889
jadmanski043e1132008-11-19 17:10:32 +0000890
891 def write(self, data):
892 # first check for any new console warnings
jadmanski6dadd832009-02-05 23:39:27 +0000893 warnings = self.job._read_warnings() + self.server_warnings
894 warnings.sort() # sort into timestamp order
jadmanski043e1132008-11-19 17:10:32 +0000895 # now process the newest data written out
896 data = self.leftover + data
897 lines = data.split("\n")
898 # process every line but the last one
899 for line in lines[:-1]:
jadmanskiefe4ebf2009-05-21 22:12:30 +0000900 self._update_timestamp(line)
901 # output any warnings between now and the next status line
902 old_warnings = [(timestamp, msg) for timestamp, msg in warnings
903 if timestamp < self.newest_timestamp]
904 self._process_warnings(self.last_line, self.logs, warnings)
905 del warnings[:len(old_warnings)]
jadmanski043e1132008-11-19 17:10:32 +0000906 self._process_line(line)
jadmanskiefe4ebf2009-05-21 22:12:30 +0000907 # save off any warnings not yet logged for later processing
908 self.server_warnings = warnings
jadmanski043e1132008-11-19 17:10:32 +0000909 # save the last line for later processing
910 # since we may not have the whole line yet
911 self.leftover = lines[-1]
912
913
914 def flush(self):
915 sys.stdout.flush()
916
917
jadmanskia61edad2009-05-21 22:17:49 +0000918 def flush_all_buffers(self):
jadmanski043e1132008-11-19 17:10:32 +0000919 if self.leftover:
920 self._process_line(self.leftover)
jadmanskia61edad2009-05-21 22:17:49 +0000921 self.leftover = ""
jadmanskiefe4ebf2009-05-21 22:12:30 +0000922 self._process_warnings(self.last_line, self.logs, self.server_warnings)
jadmanski043e1132008-11-19 17:10:32 +0000923 self._process_logs()
924 self.flush()
925
926
jadmanskia61edad2009-05-21 22:17:49 +0000927 def close(self):
928 self.flush_all_buffers()
929
930
mbligha7007722009-01-13 00:37:11 +0000931SiteAutotest = client_utils.import_site_class(
932 __file__, "autotest_lib.server.site_autotest", "SiteAutotest",
933 BaseAutotest)
mblighd8b39252008-03-20 21:15:03 +0000934
935class Autotest(SiteAutotest):
jadmanski0afbb632008-06-06 21:10:57 +0000936 pass