blob: 1c1070eaec54077dbe2e52ccb70cc436dbb99c1a [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
jadmanski27b52912009-08-14 17:09:15 +000065 def _install(self, host=None, autodir=None):
jadmanski0afbb632008-06-06 21:10:57 +000066 """
67 Install autotest. If get() was not called previously, an
68 attempt will be made to install from the autotest svn
69 repository.
mbligh9a3f5e52008-05-28 21:21:43 +000070
jadmanski0afbb632008-06-06 21:10:57 +000071 Args:
jadmanski54f90af2008-10-10 16:20:55 +000072 host: a Host instance on which autotest will be installed
73 autodir: location on the remote host to install to
mbligh9a3f5e52008-05-28 21:21:43 +000074
jadmanski0afbb632008-06-06 21:10:57 +000075 Raises:
jadmanski54f90af2008-10-10 16:20:55 +000076 AutoservError: if a tarball was not specified and
77 the target host does not have svn installed in its path"""
jadmanski0afbb632008-06-06 21:10:57 +000078 if not host:
79 host = self.host
80 if not self.got:
81 self.get()
82 host.wait_up(timeout=30)
83 host.setup()
showardb18134f2009-03-20 20:52:18 +000084 logging.info("Installing autotest on %s", host.hostname)
mbligh40f122a2007-11-03 23:08:46 +000085
jadmanski54f90af2008-10-10 16:20:55 +000086 # set up the autotest directory on the remote machine
87 if not autodir:
jadmanskif22fea82008-11-26 20:57:07 +000088 autodir = self._get_install_dir(host)
mbligh0562e652008-08-20 20:11:45 +000089 host.set_autodir(autodir)
jadmanski3c236942009-03-04 17:51:26 +000090 host.run('mkdir -p %s' % utils.sh_escape(autodir))
mbligh40f122a2007-11-03 23:08:46 +000091
jadmanski1c3c07b2009-03-03 23:29:36 +000092 # make sure there are no files in $AUTODIR/results
93 results_path = os.path.join(autodir, 'results')
jadmanski3c236942009-03-04 17:51:26 +000094 host.run('rm -rf %s/*' % utils.sh_escape(results_path),
jadmanski1c3c07b2009-03-03 23:29:36 +000095 ignore_status=True)
96
mblighc5ddfd12008-08-04 17:15:00 +000097 # Fetch the autotest client from the nearest repository
98 try:
99 c = global_config.global_config
100 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list)
mbligh76d19f72008-10-15 16:24:43 +0000101 pkgmgr = packages.PackageManager(autodir, hostname=host.hostname,
102 repo_urls=repos,
mbligh1e3b0992008-10-14 16:29:54 +0000103 do_locking=False,
104 run_function=host.run,
105 run_function_dargs=dict(timeout=600))
mblighc5ddfd12008-08-04 17:15:00 +0000106 # The packages dir is used to store all the packages that
107 # are fetched on that client. (for the tests,deps etc.
108 # too apart from the client)
109 pkg_dir = os.path.join(autodir, 'packages')
110 # clean up the autodir except for the packages directory
111 host.run('cd %s && ls | grep -v "^packages$"'
112 ' | xargs rm -rf && rm -rf .[^.]*' % autodir)
113 pkgmgr.install_pkg('autotest', 'client', pkg_dir, autodir,
114 preserve_install_dir=True)
115 self.installed = True
116 return
117 except global_config.ConfigError, e:
mblighd0e94982009-07-11 00:15:18 +0000118 logging.error("Could not install autotest using the packaging "
showardb18134f2009-03-20 20:52:18 +0000119 "system: %s", e)
jadmanskib1a51132009-08-07 16:45:50 +0000120 except (error.PackageInstallError, error.AutoservRunError), e:
showardb18134f2009-03-20 20:52:18 +0000121 logging.error("Could not install autotest from %s", repos)
mblighc5ddfd12008-08-04 17:15:00 +0000122
jadmanski0afbb632008-06-06 21:10:57 +0000123 # try to install from file or directory
124 if self.source_material:
125 if os.path.isdir(self.source_material):
jadmanski27b52912009-08-14 17:09:15 +0000126 c = global_config.global_config
127 supports_autoserv_packaging = c.get_config_value(
128 "PACKAGES", "serve_packages_from_autoserv", type=bool)
jadmanski0afbb632008-06-06 21:10:57 +0000129 # Copy autotest recursively
jadmanski27b52912009-08-14 17:09:15 +0000130 if supports_autoserv_packaging:
jadmanski54f90af2008-10-10 16:20:55 +0000131 dirs_to_exclude = set(["tests", "site_tests", "deps",
jadmanski27b52912009-08-14 17:09:15 +0000132 "profilers"])
jadmanski54f90af2008-10-10 16:20:55 +0000133 light_files = [os.path.join(self.source_material, f)
134 for f in os.listdir(self.source_material)
135 if f not in dirs_to_exclude]
mbligh89e258d2008-10-24 13:58:08 +0000136 host.send_file(light_files, autodir, delete_dest=True)
jadmanski54f90af2008-10-10 16:20:55 +0000137
138 # create empty dirs for all the stuff we excluded
139 commands = []
140 for path in dirs_to_exclude:
141 abs_path = os.path.join(autodir, path)
142 abs_path = utils.sh_escape(abs_path)
143 commands.append("mkdir -p '%s'" % abs_path)
144 host.run(';'.join(commands))
145 else:
mbligh89e258d2008-10-24 13:58:08 +0000146 host.send_file(self.source_material, autodir,
147 delete_dest=True)
jadmanski0afbb632008-06-06 21:10:57 +0000148 else:
149 # Copy autotest via tarball
150 e_msg = 'Installation method not yet implemented!'
151 raise NotImplementedError(e_msg)
showardb18134f2009-03-20 20:52:18 +0000152 logging.info("Installation of autotest completed")
jadmanski0afbb632008-06-06 21:10:57 +0000153 self.installed = True
154 return
mbligh91334902007-09-28 01:47:59 +0000155
jadmanski0afbb632008-06-06 21:10:57 +0000156 # if that fails try to install using svn
157 if utils.run('which svn').exit_status:
mbligh78bf5352008-07-11 20:27:36 +0000158 raise error.AutoservError('svn not found on target machine: %s'
159 % host.name)
jadmanski0afbb632008-06-06 21:10:57 +0000160 try:
mbligh78bf5352008-07-11 20:27:36 +0000161 host.run('svn checkout %s %s' % (AUTOTEST_SVN, autodir))
jadmanski0afbb632008-06-06 21:10:57 +0000162 except error.AutoservRunError, e:
mbligh78bf5352008-07-11 20:27:36 +0000163 host.run('svn checkout %s %s' % (AUTOTEST_HTTP, autodir))
showardb18134f2009-03-20 20:52:18 +0000164 logging.info("Installation of autotest completed")
jadmanski0afbb632008-06-06 21:10:57 +0000165 self.installed = True
mbligh91334902007-09-28 01:47:59 +0000166
167
jadmanski7c7aff32009-03-25 22:43:07 +0000168 def uninstall(self, host=None):
169 """
170 Uninstall (i.e. delete) autotest. Removes the autotest client install
171 from the specified host.
172
173 @params host a Host instance from which the client will be removed
174 """
175 if not self.installed:
176 return
177 if not host:
178 host = self.host
179 autodir = host.get_autodir()
180 if not autodir:
181 return
182
183 # perform the actual uninstall
184 host.run("rm -rf %s" % utils.sh_escape(autodir), ignore_status=True)
185 host.set_autodir(None)
186 self.installed = False
187
188
jadmanski0afbb632008-06-06 21:10:57 +0000189 def get(self, location = None):
190 if not location:
191 location = os.path.join(self.serverdir, '../client')
192 location = os.path.abspath(location)
193 # If there's stuff run on our client directory already, it
194 # can cause problems. Try giving it a quick clean first.
195 cwd = os.getcwd()
196 os.chdir(location)
197 os.system('tools/make_clean')
198 os.chdir(cwd)
199 super(BaseAutotest, self).get(location)
200 self.got = True
mblighdcd57a82007-07-11 23:06:47 +0000201
202
mblighe7d9c602009-07-02 19:02:33 +0000203 def run(self, control_file, results_dir='.', host=None, timeout=None,
204 tag=None, parallel_flag=False, background=False,
205 client_disconnect_timeout=1800, job_tag=''):
jadmanski0afbb632008-06-06 21:10:57 +0000206 """
207 Run an autotest job on the remote machine.
mbligh9a3f5e52008-05-28 21:21:43 +0000208
mblighe7d9c602009-07-02 19:02:33 +0000209 @param control_file: An open file-like-obj of the control file.
210 @param results_dir: A str path where the results should be stored
211 on the local filesystem.
212 @param host: A Host instance on which the control file should
213 be run.
214 @param timeout: Maximum number of seconds to wait for the run or None.
215 @param tag: Tag name for the client side instance of autotest.
216 @param parallel_flag: Flag set when multiple jobs are run at the
217 same time.
218 @param background: Indicates that the client should be launched as
219 a background job; the code calling run will be responsible
220 for monitoring the client and collecting the results.
221 @param client_disconnect_timeout: Seconds to wait for the remote host
222 to come back after a reboot. [default: 30 minutes]
223 @param job_tag: The scheduler's execution tag for this particular job
224 to pass on to the clients. 'job#-owner/hostgroupname'
225
226 @raises AutotestRunError: If there is a problem executing
227 the control file.
jadmanski0afbb632008-06-06 21:10:57 +0000228 """
229 host = self._get_host_and_setup(host)
230 results_dir = os.path.abspath(results_dir)
mblighc1cbc992008-05-27 20:01:45 +0000231
jadmanski0afbb632008-06-06 21:10:57 +0000232 if tag:
233 results_dir = os.path.join(results_dir, tag)
mblighc1cbc992008-05-27 20:01:45 +0000234
mblighb3c0c912008-11-27 00:32:45 +0000235 atrun = _Run(host, results_dir, tag, parallel_flag, background)
jadmanski6dadd832009-02-05 23:39:27 +0000236 self._do_run(control_file, results_dir, host, atrun, timeout,
mblighe7d9c602009-07-02 19:02:33 +0000237 client_disconnect_timeout, job_tag)
mblighd8b39252008-03-20 21:15:03 +0000238
239
jadmanski0afbb632008-06-06 21:10:57 +0000240 def _get_host_and_setup(self, host):
241 if not host:
242 host = self.host
243 if not self.installed:
244 self.install(host)
mbligh91334902007-09-28 01:47:59 +0000245
jadmanski0afbb632008-06-06 21:10:57 +0000246 host.wait_up(timeout=30)
247 return host
mblighd8b39252008-03-20 21:15:03 +0000248
249
jadmanski6dadd832009-02-05 23:39:27 +0000250 def _do_run(self, control_file, results_dir, host, atrun, timeout,
mblighe7d9c602009-07-02 19:02:33 +0000251 client_disconnect_timeout, job_tag):
jadmanski0afbb632008-06-06 21:10:57 +0000252 try:
253 atrun.verify_machine()
254 except:
showardb18134f2009-03-20 20:52:18 +0000255 logging.error("Verify failed on %s. Reinstalling autotest",
256 host.hostname)
jadmanski0afbb632008-06-06 21:10:57 +0000257 self.install(host)
258 atrun.verify_machine()
259 debug = os.path.join(results_dir, 'debug')
260 try:
261 os.makedirs(debug)
mbligh09108442008-10-15 16:27:38 +0000262 except Exception:
jadmanski0afbb632008-06-06 21:10:57 +0000263 pass
mbligh9a3f5e52008-05-28 21:21:43 +0000264
mbligh09108442008-10-15 16:27:38 +0000265 delete_file_list = [atrun.remote_control_file,
266 atrun.remote_control_file + '.state',
267 atrun.manual_control_file,
268 atrun.manual_control_file + '.state']
269 cmd = ';'.join('rm -f ' + control for control in delete_file_list)
270 host.run(cmd, ignore_status=True)
mbligh9a3f5e52008-05-28 21:21:43 +0000271
jadmanski0afbb632008-06-06 21:10:57 +0000272 tmppath = utils.get(control_file)
mblighc5ddfd12008-08-04 17:15:00 +0000273
jadmanskicb0e1612009-02-27 18:03:10 +0000274 # build up the initialization prologue for the control file
275 prologue_lines = []
276 prologue_lines.append("job.default_boot_tag(%r)\n"
277 % host.job.last_boot_tag)
278 prologue_lines.append("job.default_test_cleanup(%r)\n"
279 % host.job.run_test_cleanup)
mblighe7d9c602009-07-02 19:02:33 +0000280 if job_tag:
281 prologue_lines.append("job.default_tag(%r)\n" % job_tag)
jadmanski23afbec2008-09-17 18:12:07 +0000282
mbligh09108442008-10-15 16:27:38 +0000283 # If the packaging system is being used, add the repository list.
mblighc5ddfd12008-08-04 17:15:00 +0000284 try:
mblighc5ddfd12008-08-04 17:15:00 +0000285 c = global_config.global_config
286 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list)
jadmanskiede7e242009-08-10 15:43:33 +0000287 repos.reverse() # high priority packages should be added last
mbligh76d19f72008-10-15 16:24:43 +0000288 pkgmgr = packages.PackageManager('autotest', hostname=host.hostname,
289 repo_urls=repos)
jadmanskib1a51132009-08-07 16:45:50 +0000290 prologue_lines.append('job.add_repository(%s)\n' % repos)
mblighc5ddfd12008-08-04 17:15:00 +0000291 except global_config.ConfigError, e:
292 pass
293
jadmanskie2eef7b2009-03-03 23:55:13 +0000294 # on full-size installs, turn on any profilers the server is using
jadmanski27b52912009-08-14 17:09:15 +0000295 if not atrun.background:
jadmanskie2eef7b2009-03-03 23:55:13 +0000296 running_profilers = host.job.profilers.add_log.iteritems()
297 for profiler, (args, dargs) in running_profilers:
298 call_args = [repr(profiler)]
299 call_args += [repr(arg) for arg in args]
300 call_args += ["%s=%r" % item for item in dargs.iteritems()]
301 prologue_lines.append("job.profilers.add(%s)\n"
302 % ", ".join(call_args))
303 cfile = "".join(prologue_lines)
304
mbligh09108442008-10-15 16:27:38 +0000305 cfile += open(tmppath).read()
306 open(tmppath, "w").write(cfile)
mblighc5ddfd12008-08-04 17:15:00 +0000307
jadmanskic09fc152008-10-15 17:56:59 +0000308 # Create and copy state file to remote_control_file + '.state'
309 sysinfo_state = {"__sysinfo": host.job.sysinfo.serialize()}
310 state_file = self._create_state_file(host.job, sysinfo_state)
311 host.send_file(state_file, atrun.remote_control_file + '.state')
312 os.remove(state_file)
313
mblighc5ddfd12008-08-04 17:15:00 +0000314 # Copy control_file to remote_control_file on the host
jadmanski0afbb632008-06-06 21:10:57 +0000315 host.send_file(tmppath, atrun.remote_control_file)
316 if os.path.abspath(tmppath) != os.path.abspath(control_file):
317 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000318
jadmanski6bb32d72009-03-19 20:25:24 +0000319 atrun.execute_control(
jadmanski6dadd832009-02-05 23:39:27 +0000320 timeout=timeout,
321 client_disconnect_timeout=client_disconnect_timeout)
jadmanski23afbec2008-09-17 18:12:07 +0000322
323
jadmanskic09fc152008-10-15 17:56:59 +0000324 def _create_state_file(self, job, state_dict):
325 """ Create a state file from a dictionary. Returns the path of the
326 state file. """
327 fd, path = tempfile.mkstemp(dir=job.tmpdir)
328 state_file = os.fdopen(fd, "w")
329 pickle.dump(state_dict, state_file)
330 state_file.close()
331 return path
332
333
jadmanski0afbb632008-06-06 21:10:57 +0000334 def run_timed_test(self, test_name, results_dir='.', host=None,
jadmanskic98c4702009-01-05 15:50:06 +0000335 timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000336 """
337 Assemble a tiny little control file to just run one test,
338 and run it as an autotest client-side test
339 """
340 if not host:
341 host = self.host
342 if not self.installed:
343 self.install(host)
344 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
345 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
346 control = "job.run_test(%s)\n" % cmd
jadmanskic98c4702009-01-05 15:50:06 +0000347 self.run(control, results_dir, host, timeout=timeout)
mbligh0e4613b2007-10-29 16:55:07 +0000348
349
jadmanskic98c4702009-01-05 15:50:06 +0000350 def run_test(self, test_name, results_dir='.', host=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000351 self.run_timed_test(test_name, results_dir, host, timeout=None,
jadmanskic98c4702009-01-05 15:50:06 +0000352 *args, **dargs)
mblighd54832b2007-07-25 16:46:56 +0000353
354
mblighdcd57a82007-07-11 23:06:47 +0000355class _Run(object):
jadmanski0afbb632008-06-06 21:10:57 +0000356 """
357 Represents a run of autotest control file. This class maintains
358 all the state necessary as an autotest control file is executed.
mblighdcd57a82007-07-11 23:06:47 +0000359
jadmanski0afbb632008-06-06 21:10:57 +0000360 It is not intended to be used directly, rather control files
361 should be run using the run method in Autotest.
362 """
mblighb3c0c912008-11-27 00:32:45 +0000363 def __init__(self, host, results_dir, tag, parallel_flag, background):
jadmanski0afbb632008-06-06 21:10:57 +0000364 self.host = host
365 self.results_dir = results_dir
366 self.env = host.env
367 self.tag = tag
368 self.parallel_flag = parallel_flag
mblighb3c0c912008-11-27 00:32:45 +0000369 self.background = background
jadmanski0afbb632008-06-06 21:10:57 +0000370 self.autodir = _get_autodir(self.host)
mbligh78bf5352008-07-11 20:27:36 +0000371 control = os.path.join(self.autodir, 'control')
jadmanski0afbb632008-06-06 21:10:57 +0000372 if tag:
mbligh78bf5352008-07-11 20:27:36 +0000373 control += '.' + tag
374 self.manual_control_file = control
375 self.remote_control_file = control + '.autoserv'
mblighdc735a22007-08-02 16:54:37 +0000376
377
jadmanski0afbb632008-06-06 21:10:57 +0000378 def verify_machine(self):
379 binary = os.path.join(self.autodir, 'bin/autotest')
380 try:
381 self.host.run('ls %s > /dev/null 2>&1' % binary)
382 except:
383 raise "Autotest does not appear to be installed"
mblighdc735a22007-08-02 16:54:37 +0000384
jadmanski0afbb632008-06-06 21:10:57 +0000385 if not self.parallel_flag:
386 tmpdir = os.path.join(self.autodir, 'tmp')
387 download = os.path.join(self.autodir, 'tests/download')
388 self.host.run('umount %s' % tmpdir, ignore_status=True)
389 self.host.run('umount %s' % download, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000390
jadmanski6dadd832009-02-05 23:39:27 +0000391
392 def get_base_cmd_args(self, section):
showard234b2962009-07-28 20:02:30 +0000393 args = ['--verbose']
jadmanski0afbb632008-06-06 21:10:57 +0000394 if section > 0:
jadmanski6dadd832009-02-05 23:39:27 +0000395 args.append('-c')
jadmanski0afbb632008-06-06 21:10:57 +0000396 if self.tag:
jadmanski6dadd832009-02-05 23:39:27 +0000397 args.append('-t %s' % self.tag)
jadmanski0afbb632008-06-06 21:10:57 +0000398 if self.host.job.use_external_logging():
jadmanski6dadd832009-02-05 23:39:27 +0000399 args.append('-l')
mblighce955fc2009-08-24 21:59:02 +0000400 if self.host.hostname:
401 args.append('--hostname=%s' % self.host.hostname)
402
jadmanski6dadd832009-02-05 23:39:27 +0000403 args.append(self.remote_control_file)
404 return args
405
406
407 def get_background_cmd(self, section):
408 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotest_client')]
409 cmd += self.get_base_cmd_args(section)
410 cmd.append('>/dev/null 2>/dev/null &')
411 return ' '.join(cmd)
412
413
414 def get_daemon_cmd(self, section, monitor_dir):
415 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotestd'),
416 monitor_dir, '-H autoserv']
417 cmd += self.get_base_cmd_args(section)
418 cmd.append('>/dev/null 2>/dev/null </dev/null &')
419 return ' '.join(cmd)
420
421
422 def get_monitor_cmd(self, monitor_dir, stdout_read, stderr_read):
423 cmd = [os.path.join(self.autodir, 'bin', 'autotestd_monitor'),
424 monitor_dir, str(stdout_read), str(stderr_read)]
jadmanski0afbb632008-06-06 21:10:57 +0000425 return ' '.join(cmd)
mblighadf2aab2007-11-29 18:16:43 +0000426
mblighd8b39252008-03-20 21:15:03 +0000427
jadmanski0afbb632008-06-06 21:10:57 +0000428 def get_client_log(self, section):
jadmanskie0c7fb62008-12-16 20:51:16 +0000429 """ Find what the "next" client.log.* file should be and open it. """
430 debug_dir = os.path.join(self.results_dir, "debug")
431 client_logs = glob.glob(os.path.join(debug_dir, "client.log.*"))
432 next_log = os.path.join(debug_dir, "client.log.%d" % len(client_logs))
433 return open(next_log, "w", 0)
mblighd8b39252008-03-20 21:15:03 +0000434
435
jadmanskib264ed02009-01-12 23:54:27 +0000436 @staticmethod
437 def is_client_job_finished(last_line):
438 return bool(re.match(r'^END .*\t----\t----\t.*$', last_line))
439
440
441 @staticmethod
442 def is_client_job_rebooting(last_line):
443 return bool(re.match(r'^\t*GOOD\t----\treboot\.start.*$', last_line))
444
445
jadmanskia61edad2009-05-21 22:17:49 +0000446 def log_unexpected_abort(self, stderr_redirector):
447 stderr_redirector.flush_all_buffers()
jadmanskib264ed02009-01-12 23:54:27 +0000448 msg = "Autotest client terminated unexpectedly"
449 self.host.job.record("END ABORT", None, None, msg)
450
451
jadmanski6dadd832009-02-05 23:39:27 +0000452 def _execute_in_background(self, section, timeout):
453 full_cmd = self.get_background_cmd(section)
454 devnull = open(os.devnull, "w")
mblighd8b39252008-03-20 21:15:03 +0000455
jadmanski6dadd832009-02-05 23:39:27 +0000456 old_resultdir = self.host.job.resultdir
jadmanski0afbb632008-06-06 21:10:57 +0000457 try:
jadmanski0afbb632008-06-06 21:10:57 +0000458 self.host.job.resultdir = self.results_dir
459 result = self.host.run(full_cmd, ignore_status=True,
460 timeout=timeout,
jadmanski6dadd832009-02-05 23:39:27 +0000461 stdout_tee=devnull,
462 stderr_tee=devnull)
jadmanski0afbb632008-06-06 21:10:57 +0000463 finally:
jadmanski0afbb632008-06-06 21:10:57 +0000464 self.host.job.resultdir = old_resultdir
jadmanski6dadd832009-02-05 23:39:27 +0000465
466 return result
467
468
469 @staticmethod
470 def _strip_stderr_prologue(stderr):
471 """Strips the 'standard' prologue that get pre-pended to every
472 remote command and returns the text that was actually written to
473 stderr by the remote command."""
474 stderr_lines = stderr.split("\n")[1:]
475 if not stderr_lines:
476 return ""
477 elif stderr_lines[0].startswith("NOTE: autotestd_monitor"):
478 del stderr_lines[0]
479 return "\n".join(stderr_lines)
480
481
482 def _execute_daemon(self, section, timeout, stderr_redirector,
483 client_disconnect_timeout):
484 monitor_dir = self.host.get_tmp_dir()
485 daemon_cmd = self.get_daemon_cmd(section, monitor_dir)
486 client_log = self.get_client_log(section)
487
488 stdout_read = stderr_read = 0
489 old_resultdir = self.host.job.resultdir
490 try:
jadmanski29a4c702009-03-03 23:30:59 +0000491 self.host.job.resultdir = self.results_dir
jadmanski6dadd832009-02-05 23:39:27 +0000492 self.host.run(daemon_cmd, ignore_status=True, timeout=timeout)
jadmanski91d56a92009-04-01 15:20:40 +0000493 disconnect_warnings = []
jadmanski6dadd832009-02-05 23:39:27 +0000494 while True:
495 monitor_cmd = self.get_monitor_cmd(monitor_dir, stdout_read,
496 stderr_read)
497 try:
498 result = self.host.run(monitor_cmd, ignore_status=True,
499 timeout=timeout,
500 stdout_tee=client_log,
501 stderr_tee=stderr_redirector)
502 except error.AutoservRunError, e:
503 result = e.result_obj
504 result.exit_status = None
jadmanski91d56a92009-04-01 15:20:40 +0000505 disconnect_warnings.append(e.description)
506
jadmanski6dadd832009-02-05 23:39:27 +0000507 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000508 "Autotest client was disconnected: %s" % e.description,
509 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000510 except error.AutoservSSHTimeout:
511 result = utils.CmdResult(monitor_cmd, "", "", None, 0)
512 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000513 "Attempt to connect to Autotest client timed out",
514 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000515
516 stdout_read += len(result.stdout)
517 stderr_read += len(self._strip_stderr_prologue(result.stderr))
518
519 if result.exit_status is not None:
520 return result
521 elif not self.host.wait_up(client_disconnect_timeout):
522 raise error.AutoservSSHTimeout(
523 "client was disconnected, reconnect timed out")
524 finally:
525 self.host.job.resultdir = old_resultdir
526
527
528 def execute_section(self, section, timeout, stderr_redirector,
529 client_disconnect_timeout):
showardb18134f2009-03-20 20:52:18 +0000530 logging.info("Executing %s/bin/autotest %s/control phase %d",
531 self.autodir, self.autodir, section)
jadmanski6dadd832009-02-05 23:39:27 +0000532
533 if self.background:
534 result = self._execute_in_background(section, timeout)
535 else:
536 result = self._execute_daemon(section, timeout, stderr_redirector,
537 client_disconnect_timeout)
538
539 last_line = stderr_redirector.last_line
mbligh2bf2db62007-11-27 00:53:18 +0000540
jadmanskib264ed02009-01-12 23:54:27 +0000541 # check if we failed hard enough to warrant an exception
jadmanski0afbb632008-06-06 21:10:57 +0000542 if result.exit_status == 1:
jadmanskib264ed02009-01-12 23:54:27 +0000543 err = error.AutotestRunError("client job was aborted")
544 elif not self.background and not result.stderr:
545 err = error.AutotestRunError(
jadmanskie4130532009-03-17 18:01:28 +0000546 "execute_section %s failed to return anything\n"
547 "stdout:%s\n" % (section, result.stdout))
jadmanskib264ed02009-01-12 23:54:27 +0000548 else:
549 err = None
mbligh0e4613b2007-10-29 16:55:07 +0000550
jadmanskib264ed02009-01-12 23:54:27 +0000551 # log something if the client failed AND never finished logging
552 if err and not self.is_client_job_finished(last_line):
jadmanskia61edad2009-05-21 22:17:49 +0000553 self.log_unexpected_abort(stderr_redirector)
jadmanskib264ed02009-01-12 23:54:27 +0000554
555 if err:
556 raise err
557 else:
558 return stderr_redirector.last_line
jadmanski4600e342008-10-29 22:54:00 +0000559
560
561 def _wait_for_reboot(self):
showardb18134f2009-03-20 20:52:18 +0000562 logging.info("Client is rebooting")
563 logging.info("Waiting for client to halt")
jadmanski4600e342008-10-29 22:54:00 +0000564 if not self.host.wait_down(HALT_TIME):
565 err = "%s failed to shutdown after %d"
566 err %= (self.host.hostname, HALT_TIME)
567 raise error.AutotestRunError(err)
showardb18134f2009-03-20 20:52:18 +0000568 logging.info("Client down, waiting for restart")
jadmanski4600e342008-10-29 22:54:00 +0000569 if not self.host.wait_up(BOOT_TIME):
570 # since reboot failed
571 # hardreset the machine once if possible
572 # before failing this control file
573 warning = "%s did not come back up, hard resetting"
574 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000575 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000576 try:
577 self.host.hardreset(wait=False)
mblighd99d3b272008-12-22 14:41:27 +0000578 except (AttributeError, error.AutoservUnsupportedError):
jadmanski4600e342008-10-29 22:54:00 +0000579 warning = "Hard reset unsupported on %s"
580 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000581 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000582 raise error.AutotestRunError("%s failed to boot after %ds" %
583 (self.host.hostname, BOOT_TIME))
584 self.host.reboot_followup()
mblighdc735a22007-08-02 16:54:37 +0000585
586
jadmanski6bb32d72009-03-19 20:25:24 +0000587 def _process_client_state_file(self):
588 state_file = os.path.basename(self.remote_control_file) + ".state"
589 state_path = os.path.join(self.results_dir, state_file)
590 try:
591 state_dict = pickle.load(open(state_path))
592 except Exception, e:
593 msg = "Ignoring error while loading client job state file: %s" % e
showard372ce3e2009-04-07 18:12:03 +0000594 logging.warning(msg)
jadmanski6bb32d72009-03-19 20:25:24 +0000595 state_dict = {}
596
597 # clear out the state file
598 # TODO: stash the file away somewhere useful instead
599 try:
600 os.remove(state_path)
601 except Exception:
602 pass
603
mblighe9633f52009-07-02 19:01:09 +0000604 logging.debug("Persistent state variables pulled back from %s: %s",
605 self.host.hostname, state_dict)
jadmanski6bb32d72009-03-19 20:25:24 +0000606
607 if "__run_test_cleanup" in state_dict:
608 if state_dict["__run_test_cleanup"]:
609 self.host.job.enable_test_cleanup()
610 else:
611 self.host.job.disable_test_cleanup()
612
613 if "__last_boot_tag" in state_dict:
614 self.host.job.last_boot_tag = state_dict["__last_boot_tag"]
615
616 if "__sysinfo" in state_dict:
617 self.host.job.sysinfo.deserialize(state_dict["__sysinfo"])
618
619
jadmanski6dadd832009-02-05 23:39:27 +0000620 def execute_control(self, timeout=None, client_disconnect_timeout=None):
jadmanski6bb32d72009-03-19 20:25:24 +0000621 if not self.background:
622 collector = log_collector(self.host, self.tag, self.results_dir)
623 hostname = self.host.hostname
624 remote_results = collector.client_results_dir
625 local_results = collector.server_results_dir
626 self.host.job.add_client_log(hostname, remote_results,
627 local_results)
628
jadmanski0afbb632008-06-06 21:10:57 +0000629 section = 0
jadmanski4600e342008-10-29 22:54:00 +0000630 start_time = time.time()
631
jadmanski043e1132008-11-19 17:10:32 +0000632 logger = client_logger(self.host, self.tag, self.results_dir)
jadmanski4600e342008-10-29 22:54:00 +0000633 try:
634 while not timeout or time.time() < start_time + timeout:
635 if timeout:
636 section_timeout = start_time + timeout - time.time()
637 else:
638 section_timeout = None
639 last = self.execute_section(section, section_timeout,
jadmanski6dadd832009-02-05 23:39:27 +0000640 logger, client_disconnect_timeout)
mblighb3c0c912008-11-27 00:32:45 +0000641 if self.background:
642 return
jadmanski4600e342008-10-29 22:54:00 +0000643 section += 1
jadmanskib264ed02009-01-12 23:54:27 +0000644 if self.is_client_job_finished(last):
showardb18134f2009-03-20 20:52:18 +0000645 logging.info("Client complete")
jadmanski4600e342008-10-29 22:54:00 +0000646 return
jadmanskib264ed02009-01-12 23:54:27 +0000647 elif self.is_client_job_rebooting(last):
jadmanski79ab9282008-11-11 17:53:12 +0000648 try:
649 self._wait_for_reboot()
650 except error.AutotestRunError, e:
jadmanski043e1132008-11-19 17:10:32 +0000651 self.host.job.record("ABORT", None, "reboot", str(e))
652 self.host.job.record("END ABORT", None, None, str(e))
jadmanski79ab9282008-11-11 17:53:12 +0000653 raise
jadmanski4600e342008-10-29 22:54:00 +0000654 continue
655
656 # if we reach here, something unexpected happened
jadmanskia61edad2009-05-21 22:17:49 +0000657 self.log_unexpected_abort(logger)
jadmanski4600e342008-10-29 22:54:00 +0000658
659 # give the client machine a chance to recover from a crash
660 self.host.wait_up(CRASH_RECOVERY_TIME)
661 msg = ("Aborting - unexpected final status message from "
662 "client: %s\n") % last
663 raise error.AutotestRunError(msg)
664 finally:
jadmanski043e1132008-11-19 17:10:32 +0000665 logger.close()
jadmanski6bb32d72009-03-19 20:25:24 +0000666 if not self.background:
667 collector.collect_client_job_results()
668 self._process_client_state_file()
669 self.host.job.remove_client_log(hostname, remote_results,
670 local_results)
mblighdcd57a82007-07-11 23:06:47 +0000671
jadmanski0afbb632008-06-06 21:10:57 +0000672 # should only get here if we timed out
673 assert timeout
674 raise error.AutotestTimeoutError()
mbligh0e4613b2007-10-29 16:55:07 +0000675
mblighdcd57a82007-07-11 23:06:47 +0000676
677def _get_autodir(host):
mbligh3c7a1502008-07-24 18:08:47 +0000678 autodir = host.get_autodir()
679 if autodir:
jadmanski8297cf52009-05-21 22:23:02 +0000680 logging.debug('Using existing host autodir: %s', autodir)
mbligh3c7a1502008-07-24 18:08:47 +0000681 return autodir
jadmanski0afbb632008-06-06 21:10:57 +0000682 try:
683 # There's no clean way to do this. readlink may not exist
jadmanski8297cf52009-05-21 22:23:02 +0000684 cmd = ("python -c '%s' /etc/autotest.conf 2> /dev/null"
685 % "import os,sys; print os.readlink(sys.argv[1])")
mbligh3c7a1502008-07-24 18:08:47 +0000686 autodir = os.path.dirname(host.run(cmd).stdout)
687 if autodir:
jadmanski8297cf52009-05-21 22:23:02 +0000688 logging.debug('Using autodir from /etc/autotest.conf: %s', autodir)
mbligh3c7a1502008-07-24 18:08:47 +0000689 return autodir
jadmanski0afbb632008-06-06 21:10:57 +0000690 except error.AutoservRunError:
691 pass
692 for path in ['/usr/local/autotest', '/home/autotest']:
693 try:
jadmanski8297cf52009-05-21 22:23:02 +0000694 host.run('ls %s > /dev/null 2>&1' % path)
695 logging.debug('Found autodir at %s', path)
jadmanski0afbb632008-06-06 21:10:57 +0000696 return path
697 except error.AutoservRunError:
jadmanski8297cf52009-05-21 22:23:02 +0000698 logging.debug('%s does not exist', path)
699 raise error.AutotestRunError('Cannot figure out autotest directory')
mblighd8b39252008-03-20 21:15:03 +0000700
701
jadmanski043e1132008-11-19 17:10:32 +0000702class log_collector(object):
703 def __init__(self, host, client_tag, results_dir):
704 self.host = host
705 if not client_tag:
706 client_tag = "default"
707 self.client_results_dir = os.path.join(host.get_autodir(), "results",
708 client_tag)
709 self.server_results_dir = results_dir
710
711
712 def collect_client_job_results(self):
713 """ A method that collects all the current results of a running
714 client job into the results dir. By default does nothing as no
715 client job is running, but when running a client job you can override
716 this with something that will actually do something. """
717
718 # make an effort to wait for the machine to come up
719 try:
720 self.host.wait_up(timeout=30)
721 except error.AutoservError:
722 # don't worry about any errors, we'll try and
723 # get the results anyway
724 pass
725
jadmanski043e1132008-11-19 17:10:32 +0000726 # Copy all dirs in default to results_dir
727 try:
jadmanski043e1132008-11-19 17:10:32 +0000728 self.host.get_file(self.client_results_dir + '/',
mbligh45561782009-05-11 21:14:34 +0000729 self.server_results_dir, preserve_symlinks=True)
jadmanski043e1132008-11-19 17:10:32 +0000730 except Exception:
731 # well, don't stop running just because we couldn't get logs
showardb18134f2009-03-20 20:52:18 +0000732 e_msg = "Unexpected error copying test result logs, continuing ..."
733 logging.error(e_msg)
jadmanski043e1132008-11-19 17:10:32 +0000734 traceback.print_exc(file=sys.stdout)
735
736
jadmanski043e1132008-11-19 17:10:32 +0000737# a file-like object for catching stderr from an autotest client and
738# extracting status logs from it
739class client_logger(object):
740 """Partial file object to write to both stdout and
741 the status log file. We only implement those methods
742 utils.run() actually calls.
743
744 Note that this class is fairly closely coupled with server_job, as it
745 uses special job._ methods to actually carry out the loggging.
746 """
747 status_parser = re.compile(r"^AUTOTEST_STATUS:([^:]*):(.*)$")
748 test_complete_parser = re.compile(r"^AUTOTEST_TEST_COMPLETE:(.*)$")
jadmanskib1a51132009-08-07 16:45:50 +0000749 fetch_package_parser = re.compile(
750 r"^AUTOTEST_FETCH_PACKAGE:([^:]*):([^:]*):(.*)$")
jadmanski043e1132008-11-19 17:10:32 +0000751 extract_indent = re.compile(r"^(\t*).*$")
jadmanskiefe4ebf2009-05-21 22:12:30 +0000752 extract_timestamp = re.compile(r".*\ttimestamp=(\d+)\t.*$")
jadmanski043e1132008-11-19 17:10:32 +0000753
754 def __init__(self, host, tag, server_results_dir):
755 self.host = host
756 self.job = host.job
757 self.log_collector = log_collector(host, tag, server_results_dir)
758 self.leftover = ""
759 self.last_line = ""
jadmanskiefe4ebf2009-05-21 22:12:30 +0000760 self.newest_timestamp = float("-inf")
jadmanski043e1132008-11-19 17:10:32 +0000761 self.logs = {}
jadmanski6dadd832009-02-05 23:39:27 +0000762 self.server_warnings = []
jadmanski043e1132008-11-19 17:10:32 +0000763
764
jadmanskiefe4ebf2009-05-21 22:12:30 +0000765 def _update_timestamp(self, line):
766 match = self.extract_timestamp.search(line)
767 if match:
768 self.newest_timestamp = max(self.newest_timestamp,
769 int(match.group(1)))
770
771
jadmanski043e1132008-11-19 17:10:32 +0000772 def _process_log_dict(self, log_dict):
773 log_list = log_dict.pop("logs", [])
774 for key in sorted(log_dict.iterkeys()):
775 log_list += self._process_log_dict(log_dict.pop(key))
776 return log_list
777
778
779 def _process_logs(self):
780 """Go through the accumulated logs in self.log and print them
781 out to stdout and the status log. Note that this processes
782 logs in an ordering where:
783
784 1) logs to different tags are never interleaved
785 2) logs to x.y come before logs to x.y.z for all z
786 3) logs to x.y come before x.z whenever y < z
787
788 Note that this will in general not be the same as the
789 chronological ordering of the logs. However, if a chronological
790 ordering is desired that one can be reconstructed from the
791 status log by looking at timestamp lines."""
792 log_list = self._process_log_dict(self.logs)
793 for line in log_list:
794 self.job._record_prerendered(line + '\n')
795 if log_list:
796 self.last_line = log_list[-1]
797
798
799 def _process_quoted_line(self, tag, line):
800 """Process a line quoted with an AUTOTEST_STATUS flag. If the
801 tag is blank then we want to push out all the data we've been
802 building up in self.logs, and then the newest line. If the
803 tag is not blank, then push the line into the logs for handling
804 later."""
showardb18134f2009-03-20 20:52:18 +0000805 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +0000806 if tag == "":
807 self._process_logs()
808 self.job._record_prerendered(line + '\n')
809 self.last_line = line
810 else:
811 tag_parts = [int(x) for x in tag.split(".")]
812 log_dict = self.logs
813 for part in tag_parts:
814 log_dict = log_dict.setdefault(part, {})
815 log_list = log_dict.setdefault("logs", [])
816 log_list.append(line)
817
818
jadmanskif37df842009-02-11 00:03:26 +0000819 def _process_info_line(self, line):
820 """Check if line is an INFO line, and if it is, interpret any control
821 messages (e.g. enabling/disabling warnings) that it may contain."""
822 match = re.search(r"^\t*INFO\t----\t----(.*)\t[^\t]*$", line)
823 if not match:
824 return # not an INFO line
825 for field in match.group(1).split('\t'):
826 if field.startswith("warnings.enable="):
jadmanski16a7ff72009-04-01 18:19:53 +0000827 func = self.job.warning_manager.enable_warnings
jadmanskif37df842009-02-11 00:03:26 +0000828 elif field.startswith("warnings.disable="):
jadmanski16a7ff72009-04-01 18:19:53 +0000829 func = self.job.warning_manager.disable_warnings
jadmanskif37df842009-02-11 00:03:26 +0000830 else:
831 continue
832 warning_type = field.split("=", 1)[1]
jadmanski16a7ff72009-04-01 18:19:53 +0000833 func(warning_type)
jadmanskif37df842009-02-11 00:03:26 +0000834
835
jadmanski043e1132008-11-19 17:10:32 +0000836 def _process_line(self, line):
837 """Write out a line of data to the appropriate stream. Status
838 lines sent by autotest will be prepended with
839 "AUTOTEST_STATUS", and all other lines are ssh error
840 messages."""
841 status_match = self.status_parser.search(line)
842 test_complete_match = self.test_complete_parser.search(line)
jadmanskib1a51132009-08-07 16:45:50 +0000843 fetch_package_match = self.fetch_package_parser.search(line)
jadmanski043e1132008-11-19 17:10:32 +0000844 if status_match:
845 tag, line = status_match.groups()
jadmanskif37df842009-02-11 00:03:26 +0000846 self._process_info_line(line)
jadmanski043e1132008-11-19 17:10:32 +0000847 self._process_quoted_line(tag, line)
848 elif test_complete_match:
jadmanskifcc0d5d2009-02-12 21:52:54 +0000849 self._process_logs()
jadmanski043e1132008-11-19 17:10:32 +0000850 fifo_path, = test_complete_match.groups()
851 self.log_collector.collect_client_job_results()
852 self.host.run("echo A > %s" % fifo_path)
jadmanskib1a51132009-08-07 16:45:50 +0000853 elif fetch_package_match:
854 pkg_name, dest_path, fifo_path = fetch_package_match.groups()
jadmanskiede7e242009-08-10 15:43:33 +0000855 serve_packages = global_config.global_config.get_config_value(
856 "PACKAGES", "serve_packages_from_autoserv", type=bool)
857 if serve_packages and pkg_name.endswith(".tar.bz2"):
858 try:
859 self._send_tarball(pkg_name, dest_path)
860 except Exception:
861 msg = "Package tarball creation failed, continuing anyway"
862 logging.exception(msg)
jadmanskib1a51132009-08-07 16:45:50 +0000863 self.host.run("echo B > %s" % fifo_path)
jadmanski043e1132008-11-19 17:10:32 +0000864 else:
showardb18134f2009-03-20 20:52:18 +0000865 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +0000866
867
jadmanskiede7e242009-08-10 15:43:33 +0000868 def _send_tarball(self, pkg_name, remote_dest):
869 name, pkg_type = self.job.pkgmgr.parse_tarball_name(pkg_name)
870 src_dirs = []
871 if pkg_type == 'test':
872 src_dirs += [os.path.join(self.job.clientdir, 'site_tests', name),
873 os.path.join(self.job.clientdir, 'tests', name)]
874 elif pkg_type == 'profiler':
875 src_dirs += [os.path.join(self.job.clientdir, 'profilers', name)]
876 elif pkg_type == 'dep':
877 src_dirs += [os.path.join(self.job.clientdir, 'deps', name)]
878 elif pkg_type == 'client':
879 return # you must already have a client to hit this anyway
880 else:
881 return # no other types are supported
882
883 # iterate over src_dirs until we find one that exists, then tar it
884 for src_dir in src_dirs:
885 if os.path.exists(src_dir):
886 try:
887 logging.info('Bundling %s into %s', src_dir, pkg_name)
888 temp_dir = autotemp.tempdir(unique_id='autoserv-packager',
889 dir=self.job.tmpdir)
890 tarball_path = self.job.pkgmgr.tar_package(
891 pkg_name, src_dir, temp_dir.name, ' .')
892 self.host.send_file(tarball_path, remote_dest)
893 finally:
894 temp_dir.clean()
895 return
896
897
jadmanski043e1132008-11-19 17:10:32 +0000898 def _format_warnings(self, last_line, warnings):
899 # use the indentation of whatever the last log line was
900 indent = self.extract_indent.match(last_line).group(1)
901 # if the last line starts a new group, add an extra indent
902 if last_line.lstrip('\t').startswith("START\t"):
903 indent += '\t'
904 return [self.job._render_record("WARN", None, None, msg,
905 timestamp, indent).rstrip('\n')
906 for timestamp, msg in warnings]
907
908
909 def _process_warnings(self, last_line, log_dict, warnings):
910 if log_dict.keys() in ([], ["logs"]):
911 # there are no sub-jobs, just append the warnings here
912 warnings = self._format_warnings(last_line, warnings)
913 log_list = log_dict.setdefault("logs", [])
914 log_list += warnings
915 for warning in warnings:
916 sys.stdout.write(warning + '\n')
917 else:
918 # there are sub-jobs, so put the warnings in there
919 log_list = log_dict.get("logs", [])
920 if log_list:
921 last_line = log_list[-1]
922 for key in sorted(log_dict.iterkeys()):
923 if key != "logs":
924 self._process_warnings(last_line,
925 log_dict[key],
926 warnings)
927
jadmanskif37df842009-02-11 00:03:26 +0000928
jadmanski91d56a92009-04-01 15:20:40 +0000929 def log_warning(self, msg, warning_type):
jadmanski6dadd832009-02-05 23:39:27 +0000930 """Injects a WARN message into the current status logging stream."""
jadmanski91d56a92009-04-01 15:20:40 +0000931 timestamp = int(time.time())
932 if self.job.warning_manager.is_valid(timestamp, warning_type):
933 self.server_warnings.append((timestamp, msg))
jadmanski6dadd832009-02-05 23:39:27 +0000934
jadmanski043e1132008-11-19 17:10:32 +0000935
936 def write(self, data):
937 # first check for any new console warnings
jadmanski6dadd832009-02-05 23:39:27 +0000938 warnings = self.job._read_warnings() + self.server_warnings
939 warnings.sort() # sort into timestamp order
jadmanski043e1132008-11-19 17:10:32 +0000940 # now process the newest data written out
941 data = self.leftover + data
942 lines = data.split("\n")
943 # process every line but the last one
944 for line in lines[:-1]:
jadmanskiefe4ebf2009-05-21 22:12:30 +0000945 self._update_timestamp(line)
946 # output any warnings between now and the next status line
947 old_warnings = [(timestamp, msg) for timestamp, msg in warnings
948 if timestamp < self.newest_timestamp]
949 self._process_warnings(self.last_line, self.logs, warnings)
950 del warnings[:len(old_warnings)]
jadmanski043e1132008-11-19 17:10:32 +0000951 self._process_line(line)
jadmanskiefe4ebf2009-05-21 22:12:30 +0000952 # save off any warnings not yet logged for later processing
953 self.server_warnings = warnings
jadmanski043e1132008-11-19 17:10:32 +0000954 # save the last line for later processing
955 # since we may not have the whole line yet
956 self.leftover = lines[-1]
957
958
959 def flush(self):
960 sys.stdout.flush()
961
962
jadmanskia61edad2009-05-21 22:17:49 +0000963 def flush_all_buffers(self):
jadmanski043e1132008-11-19 17:10:32 +0000964 if self.leftover:
965 self._process_line(self.leftover)
jadmanskia61edad2009-05-21 22:17:49 +0000966 self.leftover = ""
jadmanskiefe4ebf2009-05-21 22:12:30 +0000967 self._process_warnings(self.last_line, self.logs, self.server_warnings)
jadmanski043e1132008-11-19 17:10:32 +0000968 self._process_logs()
969 self.flush()
970
971
jadmanskia61edad2009-05-21 22:17:49 +0000972 def close(self):
973 self.flush_all_buffers()
974
975
mbligha7007722009-01-13 00:37:11 +0000976SiteAutotest = client_utils.import_site_class(
977 __file__, "autotest_lib.server.site_autotest", "SiteAutotest",
978 BaseAutotest)
mblighd8b39252008-03-20 21:15:03 +0000979
980class Autotest(SiteAutotest):
jadmanski0afbb632008-06-06 21:10:57 +0000981 pass