blob: b0a83eb6ffed5ce597bce5cdc8e3f943c9b05f68 [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
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
34 self.serverdir = utils.get_server_dir()
35 super(BaseAutotest, self).__init__()
mblighc8949b82007-07-23 16:33:58 +000036
mblighdc735a22007-08-02 16:54:37 +000037
jadmanskif22fea82008-11-26 20:57:07 +000038 install_in_tmpdir = False
39 @classmethod
40 def set_install_in_tmpdir(cls, flag):
41 """ Sets a flag that controls whether or not Autotest should by
42 default be installed in a "standard" directory (e.g.
43 /home/autotest, /usr/local/autotest) or a temporary directory. """
44 cls.install_in_tmpdir = flag
45
46
47 def _get_install_dir(self, host):
48 """ Determines the location where autotest should be installed on
49 host. If self.install_in_tmpdir is set, it will return a unique
50 temporary directory that autotest can be installed in. """
51 try:
52 autodir = _get_autodir(host)
53 except error.AutotestRunError:
54 autodir = '/usr/local/autotest'
55 if self.install_in_tmpdir:
56 autodir = host.get_tmp_dir(parent=autodir)
57 return autodir
58
59
mbligh1b3b3762008-09-25 02:46:34 +000060 @log.record
mblighb3c0c912008-11-27 00:32:45 +000061 def install(self, host=None, autodir=None):
62 self._install(host=host, autodir=autodir)
jadmanski54f90af2008-10-10 16:20:55 +000063
64
mblighbccad482009-08-24 22:08:31 +000065 def install_no_autoserv(self, host=None, autodir=None):
66 self._install(host=host, autodir=autodir, no_autoserv=True)
67
68
mblighe1cea0a2009-09-03 20:20:16 +000069 def _install(self, host=None, autodir=None, no_autoserv=False):
jadmanski0afbb632008-06-06 21:10:57 +000070 """
71 Install autotest. If get() was not called previously, an
72 attempt will be made to install from the autotest svn
73 repository.
mbligh9a3f5e52008-05-28 21:21:43 +000074
mblighbccad482009-08-24 22:08:31 +000075 @param host A Host instance on which autotest will be installed
76 @param autodir Location on the remote host to install to
77 @param autoserv Disable install modes that depend on the client
78 running with the autoserv harness
mbligh9a3f5e52008-05-28 21:21:43 +000079
mblighbccad482009-08-24 22:08:31 +000080 @exception AutoservError if a tarball was not specified and
81 the target host does not have svn installed in its path
82 """
jadmanski0afbb632008-06-06 21:10:57 +000083 if not host:
84 host = self.host
85 if not self.got:
86 self.get()
87 host.wait_up(timeout=30)
88 host.setup()
showardb18134f2009-03-20 20:52:18 +000089 logging.info("Installing autotest on %s", host.hostname)
mbligh40f122a2007-11-03 23:08:46 +000090
jadmanski54f90af2008-10-10 16:20:55 +000091 # set up the autotest directory on the remote machine
92 if not autodir:
jadmanskif22fea82008-11-26 20:57:07 +000093 autodir = self._get_install_dir(host)
mbligh0562e652008-08-20 20:11:45 +000094 host.set_autodir(autodir)
jadmanski3c236942009-03-04 17:51:26 +000095 host.run('mkdir -p %s' % utils.sh_escape(autodir))
mbligh40f122a2007-11-03 23:08:46 +000096
jadmanski1c3c07b2009-03-03 23:29:36 +000097 # make sure there are no files in $AUTODIR/results
98 results_path = os.path.join(autodir, 'results')
jadmanski3c236942009-03-04 17:51:26 +000099 host.run('rm -rf %s/*' % utils.sh_escape(results_path),
jadmanski1c3c07b2009-03-03 23:29:36 +0000100 ignore_status=True)
101
mblighc5ddfd12008-08-04 17:15:00 +0000102 # Fetch the autotest client from the nearest repository
103 try:
104 c = global_config.global_config
105 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list)
mbligh76d19f72008-10-15 16:24:43 +0000106 pkgmgr = packages.PackageManager(autodir, hostname=host.hostname,
107 repo_urls=repos,
mbligh1e3b0992008-10-14 16:29:54 +0000108 do_locking=False,
109 run_function=host.run,
110 run_function_dargs=dict(timeout=600))
mblighc5ddfd12008-08-04 17:15:00 +0000111 # The packages dir is used to store all the packages that
112 # are fetched on that client. (for the tests,deps etc.
113 # too apart from the client)
114 pkg_dir = os.path.join(autodir, 'packages')
115 # clean up the autodir except for the packages directory
116 host.run('cd %s && ls | grep -v "^packages$"'
117 ' | xargs rm -rf && rm -rf .[^.]*' % autodir)
118 pkgmgr.install_pkg('autotest', 'client', pkg_dir, autodir,
119 preserve_install_dir=True)
120 self.installed = True
121 return
122 except global_config.ConfigError, e:
mbligh6a837772009-09-02 18:15:11 +0000123 logging.info("Could not install autotest using the packaging "
showardb18134f2009-03-20 20:52:18 +0000124 "system: %s", e)
jadmanskib1a51132009-08-07 16:45:50 +0000125 except (error.PackageInstallError, error.AutoservRunError), e:
showardb18134f2009-03-20 20:52:18 +0000126 logging.error("Could not install autotest from %s", repos)
mblighc5ddfd12008-08-04 17:15:00 +0000127
jadmanski0afbb632008-06-06 21:10:57 +0000128 # try to install from file or directory
129 if self.source_material:
130 if os.path.isdir(self.source_material):
jadmanski27b52912009-08-14 17:09:15 +0000131 c = global_config.global_config
132 supports_autoserv_packaging = c.get_config_value(
133 "PACKAGES", "serve_packages_from_autoserv", type=bool)
jadmanski0afbb632008-06-06 21:10:57 +0000134 # Copy autotest recursively
mblighbccad482009-08-24 22:08:31 +0000135 if supports_autoserv_packaging and not no_autoserv:
mbligh875f5352009-09-03 20:20:30 +0000136 dirs_to_exclude = set(["tests", "site_tests", "deps",
137 "profilers"])
jadmanski54f90af2008-10-10 16:20:55 +0000138 light_files = [os.path.join(self.source_material, f)
139 for f in os.listdir(self.source_material)
140 if f not in dirs_to_exclude]
mbligh89e258d2008-10-24 13:58:08 +0000141 host.send_file(light_files, autodir, delete_dest=True)
jadmanski54f90af2008-10-10 16:20:55 +0000142
143 # create empty dirs for all the stuff we excluded
144 commands = []
145 for path in dirs_to_exclude:
146 abs_path = os.path.join(autodir, path)
147 abs_path = utils.sh_escape(abs_path)
148 commands.append("mkdir -p '%s'" % abs_path)
mbligh875f5352009-09-03 20:20:30 +0000149 commands.append("touch '%s'/__init__.py" % abs_path)
jadmanski54f90af2008-10-10 16:20:55 +0000150 host.run(';'.join(commands))
151 else:
mbligh89e258d2008-10-24 13:58:08 +0000152 host.send_file(self.source_material, autodir,
153 delete_dest=True)
jadmanski0afbb632008-06-06 21:10:57 +0000154 else:
155 # Copy autotest via tarball
156 e_msg = 'Installation method not yet implemented!'
157 raise NotImplementedError(e_msg)
showardb18134f2009-03-20 20:52:18 +0000158 logging.info("Installation of autotest completed")
jadmanski0afbb632008-06-06 21:10:57 +0000159 self.installed = True
160 return
mbligh91334902007-09-28 01:47:59 +0000161
jadmanski0afbb632008-06-06 21:10:57 +0000162 # if that fails try to install using svn
163 if utils.run('which svn').exit_status:
mbligh78bf5352008-07-11 20:27:36 +0000164 raise error.AutoservError('svn not found on target machine: %s'
165 % host.name)
jadmanski0afbb632008-06-06 21:10:57 +0000166 try:
mbligh78bf5352008-07-11 20:27:36 +0000167 host.run('svn checkout %s %s' % (AUTOTEST_SVN, autodir))
jadmanski0afbb632008-06-06 21:10:57 +0000168 except error.AutoservRunError, e:
mbligh78bf5352008-07-11 20:27:36 +0000169 host.run('svn checkout %s %s' % (AUTOTEST_HTTP, autodir))
showardb18134f2009-03-20 20:52:18 +0000170 logging.info("Installation of autotest completed")
jadmanski0afbb632008-06-06 21:10:57 +0000171 self.installed = True
mbligh91334902007-09-28 01:47:59 +0000172
173
jadmanski7c7aff32009-03-25 22:43:07 +0000174 def uninstall(self, host=None):
175 """
176 Uninstall (i.e. delete) autotest. Removes the autotest client install
177 from the specified host.
178
179 @params host a Host instance from which the client will be removed
180 """
181 if not self.installed:
182 return
183 if not host:
184 host = self.host
185 autodir = host.get_autodir()
186 if not autodir:
187 return
188
189 # perform the actual uninstall
190 host.run("rm -rf %s" % utils.sh_escape(autodir), ignore_status=True)
191 host.set_autodir(None)
192 self.installed = False
193
194
jadmanski0afbb632008-06-06 21:10:57 +0000195 def get(self, location = None):
196 if not location:
197 location = os.path.join(self.serverdir, '../client')
198 location = os.path.abspath(location)
199 # If there's stuff run on our client directory already, it
200 # can cause problems. Try giving it a quick clean first.
201 cwd = os.getcwd()
202 os.chdir(location)
203 os.system('tools/make_clean')
204 os.chdir(cwd)
205 super(BaseAutotest, self).get(location)
206 self.got = True
mblighdcd57a82007-07-11 23:06:47 +0000207
208
mblighe7d9c602009-07-02 19:02:33 +0000209 def run(self, control_file, results_dir='.', host=None, timeout=None,
210 tag=None, parallel_flag=False, background=False,
211 client_disconnect_timeout=1800, job_tag=''):
jadmanski0afbb632008-06-06 21:10:57 +0000212 """
213 Run an autotest job on the remote machine.
mbligh9a3f5e52008-05-28 21:21:43 +0000214
mblighe7d9c602009-07-02 19:02:33 +0000215 @param control_file: An open file-like-obj of the control file.
216 @param results_dir: A str path where the results should be stored
217 on the local filesystem.
218 @param host: A Host instance on which the control file should
219 be run.
220 @param timeout: Maximum number of seconds to wait for the run or None.
221 @param tag: Tag name for the client side instance of autotest.
222 @param parallel_flag: Flag set when multiple jobs are run at the
223 same time.
224 @param background: Indicates that the client should be launched as
225 a background job; the code calling run will be responsible
226 for monitoring the client and collecting the results.
227 @param client_disconnect_timeout: Seconds to wait for the remote host
228 to come back after a reboot. [default: 30 minutes]
229 @param job_tag: The scheduler's execution tag for this particular job
230 to pass on to the clients. 'job#-owner/hostgroupname'
231
232 @raises AutotestRunError: If there is a problem executing
233 the control file.
jadmanski0afbb632008-06-06 21:10:57 +0000234 """
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,
mblighe7d9c602009-07-02 19:02:33 +0000243 client_disconnect_timeout, job_tag)
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,
mblighe7d9c602009-07-02 19:02:33 +0000257 client_disconnect_timeout, job_tag):
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)
mblighe7d9c602009-07-02 19:02:33 +0000286 if job_tag:
287 prologue_lines.append("job.default_tag(%r)\n" % job_tag)
jadmanski23afbec2008-09-17 18:12:07 +0000288
mbligh09108442008-10-15 16:27:38 +0000289 # If the packaging system is being used, add the repository list.
mblighc5ddfd12008-08-04 17:15:00 +0000290 try:
mblighc5ddfd12008-08-04 17:15:00 +0000291 c = global_config.global_config
292 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list)
jadmanskiede7e242009-08-10 15:43:33 +0000293 repos.reverse() # high priority packages should be added last
mbligh76d19f72008-10-15 16:24:43 +0000294 pkgmgr = packages.PackageManager('autotest', hostname=host.hostname,
295 repo_urls=repos)
jadmanskib1a51132009-08-07 16:45:50 +0000296 prologue_lines.append('job.add_repository(%s)\n' % repos)
mblighc5ddfd12008-08-04 17:15:00 +0000297 except global_config.ConfigError, e:
298 pass
299
jadmanskie2eef7b2009-03-03 23:55:13 +0000300 # on full-size installs, turn on any profilers the server is using
jadmanski27b52912009-08-14 17:09:15 +0000301 if not atrun.background:
jadmanskie2eef7b2009-03-03 23:55:13 +0000302 running_profilers = host.job.profilers.add_log.iteritems()
303 for profiler, (args, dargs) in running_profilers:
304 call_args = [repr(profiler)]
305 call_args += [repr(arg) for arg in args]
306 call_args += ["%s=%r" % item for item in dargs.iteritems()]
307 prologue_lines.append("job.profilers.add(%s)\n"
308 % ", ".join(call_args))
309 cfile = "".join(prologue_lines)
310
mbligh09108442008-10-15 16:27:38 +0000311 cfile += open(tmppath).read()
312 open(tmppath, "w").write(cfile)
mblighc5ddfd12008-08-04 17:15:00 +0000313
jadmanskic09fc152008-10-15 17:56:59 +0000314 # Create and copy state file to remote_control_file + '.state'
315 sysinfo_state = {"__sysinfo": host.job.sysinfo.serialize()}
316 state_file = self._create_state_file(host.job, sysinfo_state)
317 host.send_file(state_file, atrun.remote_control_file + '.state')
318 os.remove(state_file)
319
mblighc5ddfd12008-08-04 17:15:00 +0000320 # Copy control_file to remote_control_file on the host
jadmanski0afbb632008-06-06 21:10:57 +0000321 host.send_file(tmppath, atrun.remote_control_file)
322 if os.path.abspath(tmppath) != os.path.abspath(control_file):
323 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000324
jadmanski6bb32d72009-03-19 20:25:24 +0000325 atrun.execute_control(
jadmanski6dadd832009-02-05 23:39:27 +0000326 timeout=timeout,
327 client_disconnect_timeout=client_disconnect_timeout)
jadmanski23afbec2008-09-17 18:12:07 +0000328
329
jadmanskic09fc152008-10-15 17:56:59 +0000330 def _create_state_file(self, job, state_dict):
331 """ Create a state file from a dictionary. Returns the path of the
332 state file. """
333 fd, path = tempfile.mkstemp(dir=job.tmpdir)
334 state_file = os.fdopen(fd, "w")
335 pickle.dump(state_dict, state_file)
336 state_file.close()
337 return path
338
339
jadmanski0afbb632008-06-06 21:10:57 +0000340 def run_timed_test(self, test_name, results_dir='.', host=None,
jadmanskic98c4702009-01-05 15:50:06 +0000341 timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000342 """
343 Assemble a tiny little control file to just run one test,
344 and run it as an autotest client-side test
345 """
346 if not host:
347 host = self.host
348 if not self.installed:
349 self.install(host)
350 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
351 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
352 control = "job.run_test(%s)\n" % cmd
jadmanskic98c4702009-01-05 15:50:06 +0000353 self.run(control, results_dir, host, timeout=timeout)
mbligh0e4613b2007-10-29 16:55:07 +0000354
355
jadmanskic98c4702009-01-05 15:50:06 +0000356 def run_test(self, test_name, results_dir='.', host=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000357 self.run_timed_test(test_name, results_dir, host, timeout=None,
jadmanskic98c4702009-01-05 15:50:06 +0000358 *args, **dargs)
mblighd54832b2007-07-25 16:46:56 +0000359
360
mblighdcd57a82007-07-11 23:06:47 +0000361class _Run(object):
jadmanski0afbb632008-06-06 21:10:57 +0000362 """
363 Represents a run of autotest control file. This class maintains
364 all the state necessary as an autotest control file is executed.
mblighdcd57a82007-07-11 23:06:47 +0000365
jadmanski0afbb632008-06-06 21:10:57 +0000366 It is not intended to be used directly, rather control files
367 should be run using the run method in Autotest.
368 """
mblighb3c0c912008-11-27 00:32:45 +0000369 def __init__(self, host, results_dir, tag, parallel_flag, background):
jadmanski0afbb632008-06-06 21:10:57 +0000370 self.host = host
371 self.results_dir = results_dir
372 self.env = host.env
373 self.tag = tag
374 self.parallel_flag = parallel_flag
mblighb3c0c912008-11-27 00:32:45 +0000375 self.background = background
jadmanski0afbb632008-06-06 21:10:57 +0000376 self.autodir = _get_autodir(self.host)
mbligh78bf5352008-07-11 20:27:36 +0000377 control = os.path.join(self.autodir, 'control')
jadmanski0afbb632008-06-06 21:10:57 +0000378 if tag:
mbligh78bf5352008-07-11 20:27:36 +0000379 control += '.' + tag
380 self.manual_control_file = control
381 self.remote_control_file = control + '.autoserv'
mblighdc735a22007-08-02 16:54:37 +0000382
383
jadmanski0afbb632008-06-06 21:10:57 +0000384 def verify_machine(self):
385 binary = os.path.join(self.autodir, 'bin/autotest')
386 try:
387 self.host.run('ls %s > /dev/null 2>&1' % binary)
388 except:
389 raise "Autotest does not appear to be installed"
mblighdc735a22007-08-02 16:54:37 +0000390
jadmanski0afbb632008-06-06 21:10:57 +0000391 if not self.parallel_flag:
392 tmpdir = os.path.join(self.autodir, 'tmp')
393 download = os.path.join(self.autodir, 'tests/download')
394 self.host.run('umount %s' % tmpdir, ignore_status=True)
395 self.host.run('umount %s' % download, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000396
jadmanski6dadd832009-02-05 23:39:27 +0000397
398 def get_base_cmd_args(self, section):
showard234b2962009-07-28 20:02:30 +0000399 args = ['--verbose']
jadmanski0afbb632008-06-06 21:10:57 +0000400 if section > 0:
jadmanski6dadd832009-02-05 23:39:27 +0000401 args.append('-c')
jadmanski0afbb632008-06-06 21:10:57 +0000402 if self.tag:
jadmanski6dadd832009-02-05 23:39:27 +0000403 args.append('-t %s' % self.tag)
jadmanski0afbb632008-06-06 21:10:57 +0000404 if self.host.job.use_external_logging():
jadmanski6dadd832009-02-05 23:39:27 +0000405 args.append('-l')
mblighce955fc2009-08-24 21:59:02 +0000406 if self.host.hostname:
407 args.append('--hostname=%s' % self.host.hostname)
408
jadmanski6dadd832009-02-05 23:39:27 +0000409 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
mblighe2e0bef2009-09-03 20:29:18 +0000688 client_autodir_paths = global_config.global_config.get_config_value(
689 'AUTOSERV', 'client_autodir_paths', type=list,
690 default=['/usr/local/autotest', '/home/autotest'])
691 for path in client_autodir_paths:
jadmanski0afbb632008-06-06 21:10:57 +0000692 try:
mbligh483d1da2009-08-24 22:07:14 +0000693 host.run('test -d %s' % utils.sh_escape(path))
jadmanski8297cf52009-05-21 22:23:02 +0000694 logging.debug('Found autodir at %s', path)
jadmanski0afbb632008-06-06 21:10:57 +0000695 return path
696 except error.AutoservRunError:
mbligh483d1da2009-08-24 22:07:14 +0000697 logging.debug('%s does not exist on %s', path, host.hostname)
jadmanski8297cf52009-05-21 22:23:02 +0000698 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:(.*)$")
jadmanskib1a51132009-08-07 16:45:50 +0000748 fetch_package_parser = re.compile(
749 r"^AUTOTEST_FETCH_PACKAGE:([^:]*):([^:]*):(.*)$")
jadmanski043e1132008-11-19 17:10:32 +0000750 extract_indent = re.compile(r"^(\t*).*$")
jadmanskiefe4ebf2009-05-21 22:12:30 +0000751 extract_timestamp = re.compile(r".*\ttimestamp=(\d+)\t.*$")
jadmanski043e1132008-11-19 17:10:32 +0000752
753 def __init__(self, host, tag, server_results_dir):
754 self.host = host
755 self.job = host.job
756 self.log_collector = log_collector(host, tag, server_results_dir)
757 self.leftover = ""
758 self.last_line = ""
jadmanskiefe4ebf2009-05-21 22:12:30 +0000759 self.newest_timestamp = float("-inf")
jadmanski043e1132008-11-19 17:10:32 +0000760 self.logs = {}
jadmanski6dadd832009-02-05 23:39:27 +0000761 self.server_warnings = []
jadmanski043e1132008-11-19 17:10:32 +0000762
763
jadmanskiefe4ebf2009-05-21 22:12:30 +0000764 def _update_timestamp(self, line):
765 match = self.extract_timestamp.search(line)
766 if match:
767 self.newest_timestamp = max(self.newest_timestamp,
768 int(match.group(1)))
769
770
jadmanski043e1132008-11-19 17:10:32 +0000771 def _process_log_dict(self, log_dict):
772 log_list = log_dict.pop("logs", [])
773 for key in sorted(log_dict.iterkeys()):
774 log_list += self._process_log_dict(log_dict.pop(key))
775 return log_list
776
777
778 def _process_logs(self):
779 """Go through the accumulated logs in self.log and print them
780 out to stdout and the status log. Note that this processes
781 logs in an ordering where:
782
783 1) logs to different tags are never interleaved
784 2) logs to x.y come before logs to x.y.z for all z
785 3) logs to x.y come before x.z whenever y < z
786
787 Note that this will in general not be the same as the
788 chronological ordering of the logs. However, if a chronological
789 ordering is desired that one can be reconstructed from the
790 status log by looking at timestamp lines."""
791 log_list = self._process_log_dict(self.logs)
792 for line in log_list:
793 self.job._record_prerendered(line + '\n')
794 if log_list:
795 self.last_line = log_list[-1]
796
797
798 def _process_quoted_line(self, tag, line):
799 """Process a line quoted with an AUTOTEST_STATUS flag. If the
800 tag is blank then we want to push out all the data we've been
801 building up in self.logs, and then the newest line. If the
802 tag is not blank, then push the line into the logs for handling
803 later."""
showardb18134f2009-03-20 20:52:18 +0000804 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +0000805 if tag == "":
806 self._process_logs()
807 self.job._record_prerendered(line + '\n')
808 self.last_line = line
809 else:
810 tag_parts = [int(x) for x in tag.split(".")]
811 log_dict = self.logs
812 for part in tag_parts:
813 log_dict = log_dict.setdefault(part, {})
814 log_list = log_dict.setdefault("logs", [])
815 log_list.append(line)
816
817
jadmanskif37df842009-02-11 00:03:26 +0000818 def _process_info_line(self, line):
819 """Check if line is an INFO line, and if it is, interpret any control
820 messages (e.g. enabling/disabling warnings) that it may contain."""
821 match = re.search(r"^\t*INFO\t----\t----(.*)\t[^\t]*$", line)
822 if not match:
823 return # not an INFO line
824 for field in match.group(1).split('\t'):
825 if field.startswith("warnings.enable="):
jadmanski16a7ff72009-04-01 18:19:53 +0000826 func = self.job.warning_manager.enable_warnings
jadmanskif37df842009-02-11 00:03:26 +0000827 elif field.startswith("warnings.disable="):
jadmanski16a7ff72009-04-01 18:19:53 +0000828 func = self.job.warning_manager.disable_warnings
jadmanskif37df842009-02-11 00:03:26 +0000829 else:
830 continue
831 warning_type = field.split("=", 1)[1]
jadmanski16a7ff72009-04-01 18:19:53 +0000832 func(warning_type)
jadmanskif37df842009-02-11 00:03:26 +0000833
834
jadmanski043e1132008-11-19 17:10:32 +0000835 def _process_line(self, line):
836 """Write out a line of data to the appropriate stream. Status
837 lines sent by autotest will be prepended with
838 "AUTOTEST_STATUS", and all other lines are ssh error
839 messages."""
840 status_match = self.status_parser.search(line)
841 test_complete_match = self.test_complete_parser.search(line)
jadmanskib1a51132009-08-07 16:45:50 +0000842 fetch_package_match = self.fetch_package_parser.search(line)
jadmanski043e1132008-11-19 17:10:32 +0000843 if status_match:
844 tag, line = status_match.groups()
jadmanskif37df842009-02-11 00:03:26 +0000845 self._process_info_line(line)
jadmanski043e1132008-11-19 17:10:32 +0000846 self._process_quoted_line(tag, line)
847 elif test_complete_match:
jadmanskifcc0d5d2009-02-12 21:52:54 +0000848 self._process_logs()
jadmanski043e1132008-11-19 17:10:32 +0000849 fifo_path, = test_complete_match.groups()
850 self.log_collector.collect_client_job_results()
851 self.host.run("echo A > %s" % fifo_path)
jadmanskib1a51132009-08-07 16:45:50 +0000852 elif fetch_package_match:
853 pkg_name, dest_path, fifo_path = fetch_package_match.groups()
jadmanskiede7e242009-08-10 15:43:33 +0000854 serve_packages = global_config.global_config.get_config_value(
855 "PACKAGES", "serve_packages_from_autoserv", type=bool)
856 if serve_packages and pkg_name.endswith(".tar.bz2"):
857 try:
858 self._send_tarball(pkg_name, dest_path)
859 except Exception:
860 msg = "Package tarball creation failed, continuing anyway"
861 logging.exception(msg)
jadmanskib1a51132009-08-07 16:45:50 +0000862 self.host.run("echo B > %s" % fifo_path)
jadmanski043e1132008-11-19 17:10:32 +0000863 else:
showardb18134f2009-03-20 20:52:18 +0000864 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +0000865
866
jadmanskiede7e242009-08-10 15:43:33 +0000867 def _send_tarball(self, pkg_name, remote_dest):
868 name, pkg_type = self.job.pkgmgr.parse_tarball_name(pkg_name)
869 src_dirs = []
870 if pkg_type == 'test':
871 src_dirs += [os.path.join(self.job.clientdir, 'site_tests', name),
872 os.path.join(self.job.clientdir, 'tests', name)]
873 elif pkg_type == 'profiler':
874 src_dirs += [os.path.join(self.job.clientdir, 'profilers', name)]
875 elif pkg_type == 'dep':
876 src_dirs += [os.path.join(self.job.clientdir, 'deps', name)]
877 elif pkg_type == 'client':
878 return # you must already have a client to hit this anyway
879 else:
880 return # no other types are supported
881
882 # iterate over src_dirs until we find one that exists, then tar it
883 for src_dir in src_dirs:
884 if os.path.exists(src_dir):
885 try:
886 logging.info('Bundling %s into %s', src_dir, pkg_name)
887 temp_dir = autotemp.tempdir(unique_id='autoserv-packager',
888 dir=self.job.tmpdir)
889 tarball_path = self.job.pkgmgr.tar_package(
mblighbccad482009-08-24 22:08:31 +0000890 pkg_name, src_dir, temp_dir.name, " .")
jadmanskiede7e242009-08-10 15:43:33 +0000891 self.host.send_file(tarball_path, remote_dest)
892 finally:
893 temp_dir.clean()
894 return
895
896
jadmanski043e1132008-11-19 17:10:32 +0000897 def _format_warnings(self, last_line, warnings):
898 # use the indentation of whatever the last log line was
899 indent = self.extract_indent.match(last_line).group(1)
900 # if the last line starts a new group, add an extra indent
901 if last_line.lstrip('\t').startswith("START\t"):
902 indent += '\t'
903 return [self.job._render_record("WARN", None, None, msg,
904 timestamp, indent).rstrip('\n')
905 for timestamp, msg in warnings]
906
907
908 def _process_warnings(self, last_line, log_dict, warnings):
909 if log_dict.keys() in ([], ["logs"]):
910 # there are no sub-jobs, just append the warnings here
911 warnings = self._format_warnings(last_line, warnings)
912 log_list = log_dict.setdefault("logs", [])
913 log_list += warnings
914 for warning in warnings:
915 sys.stdout.write(warning + '\n')
916 else:
917 # there are sub-jobs, so put the warnings in there
918 log_list = log_dict.get("logs", [])
919 if log_list:
920 last_line = log_list[-1]
921 for key in sorted(log_dict.iterkeys()):
922 if key != "logs":
923 self._process_warnings(last_line,
924 log_dict[key],
925 warnings)
926
jadmanskif37df842009-02-11 00:03:26 +0000927
jadmanski91d56a92009-04-01 15:20:40 +0000928 def log_warning(self, msg, warning_type):
jadmanski6dadd832009-02-05 23:39:27 +0000929 """Injects a WARN message into the current status logging stream."""
jadmanski91d56a92009-04-01 15:20:40 +0000930 timestamp = int(time.time())
931 if self.job.warning_manager.is_valid(timestamp, warning_type):
932 self.server_warnings.append((timestamp, msg))
jadmanski6dadd832009-02-05 23:39:27 +0000933
jadmanski043e1132008-11-19 17:10:32 +0000934
935 def write(self, data):
936 # first check for any new console warnings
jadmanski6dadd832009-02-05 23:39:27 +0000937 warnings = self.job._read_warnings() + self.server_warnings
938 warnings.sort() # sort into timestamp order
jadmanski043e1132008-11-19 17:10:32 +0000939 # now process the newest data written out
940 data = self.leftover + data
941 lines = data.split("\n")
942 # process every line but the last one
943 for line in lines[:-1]:
jadmanskiefe4ebf2009-05-21 22:12:30 +0000944 self._update_timestamp(line)
945 # output any warnings between now and the next status line
946 old_warnings = [(timestamp, msg) for timestamp, msg in warnings
947 if timestamp < self.newest_timestamp]
948 self._process_warnings(self.last_line, self.logs, warnings)
949 del warnings[:len(old_warnings)]
jadmanski043e1132008-11-19 17:10:32 +0000950 self._process_line(line)
jadmanskiefe4ebf2009-05-21 22:12:30 +0000951 # save off any warnings not yet logged for later processing
952 self.server_warnings = warnings
jadmanski043e1132008-11-19 17:10:32 +0000953 # save the last line for later processing
954 # since we may not have the whole line yet
955 self.leftover = lines[-1]
956
957
958 def flush(self):
959 sys.stdout.flush()
960
961
jadmanskia61edad2009-05-21 22:17:49 +0000962 def flush_all_buffers(self):
jadmanski043e1132008-11-19 17:10:32 +0000963 if self.leftover:
964 self._process_line(self.leftover)
jadmanskia61edad2009-05-21 22:17:49 +0000965 self.leftover = ""
jadmanskiefe4ebf2009-05-21 22:12:30 +0000966 self._process_warnings(self.last_line, self.logs, self.server_warnings)
jadmanski043e1132008-11-19 17:10:32 +0000967 self._process_logs()
968 self.flush()
969
970
jadmanskia61edad2009-05-21 22:17:49 +0000971 def close(self):
972 self.flush_all_buffers()
973
974
mbligha7007722009-01-13 00:37:11 +0000975SiteAutotest = client_utils.import_site_class(
976 __file__, "autotest_lib.server.site_autotest", "SiteAutotest",
977 BaseAutotest)
mblighd8b39252008-03-20 21:15:03 +0000978
979class Autotest(SiteAutotest):
jadmanski0afbb632008-06-06 21:10:57 +0000980 pass