blob: c3144eddeead423aa354e553a697f7d2c632bfe1 [file] [log] [blame]
mblighdcd57a82007-07-11 23:06:47 +00001# Copyright 2007 Google Inc. Released under the GPL v2
2
showardb18134f2009-03-20 20:52:18 +00003import re, os, sys, traceback, subprocess, tempfile, time, pickle, glob, logging
jadmanski043e1132008-11-19 17:10:32 +00004from autotest_lib.server import installable_object, utils
jadmanskiede7e242009-08-10 15:43:33 +00005from autotest_lib.client.common_lib import log, error, autotemp
mbligh09108442008-10-15 16:27:38 +00006from autotest_lib.client.common_lib import global_config, packages
mbligha7007722009-01-13 00:37:11 +00007from autotest_lib.client.common_lib import utils as client_utils
mbligh3c7a1502008-07-24 18:08:47 +00008
mblighdcd57a82007-07-11 23:06:47 +00009AUTOTEST_SVN = 'svn://test.kernel.org/autotest/trunk/client'
10AUTOTEST_HTTP = 'http://test.kernel.org/svn/autotest/trunk/client'
11
mbligh483d1da2009-08-24 22:07:14 +000012# This should probably live in global_config.ini
13CLIENT_AUTODIR_PATHS = ('/usr/local/autotest', '/home/autotest')
14
mblighdcd57a82007-07-11 23:06:47 +000015# Timeouts for powering down and up respectively
16HALT_TIME = 300
mbligh07c1eac2007-11-05 18:39:29 +000017BOOT_TIME = 1800
jadmanskiec859142008-05-29 21:33:39 +000018CRASH_RECOVERY_TIME = 9000
mbligh0e4613b2007-10-29 16:55:07 +000019
mblighdcd57a82007-07-11 23:06:47 +000020
mblighd8b39252008-03-20 21:15:03 +000021class BaseAutotest(installable_object.InstallableObject):
jadmanski0afbb632008-06-06 21:10:57 +000022 """
23 This class represents the Autotest program.
mblighdcd57a82007-07-11 23:06:47 +000024
jadmanski0afbb632008-06-06 21:10:57 +000025 Autotest is used to run tests automatically and collect the results.
26 It also supports profilers.
mblighdcd57a82007-07-11 23:06:47 +000027
jadmanski0afbb632008-06-06 21:10:57 +000028 Implementation details:
29 This is a leaf class in an abstract class hierarchy, it must
30 implement the unimplemented methods in parent classes.
31 """
mbligh119c12a2007-11-12 22:13:44 +000032
jadmanski0afbb632008-06-06 21:10:57 +000033 def __init__(self, host = None):
34 self.host = host
35 self.got = False
36 self.installed = False
37 self.serverdir = utils.get_server_dir()
38 super(BaseAutotest, self).__init__()
mblighc8949b82007-07-23 16:33:58 +000039
mblighdc735a22007-08-02 16:54:37 +000040
jadmanskif22fea82008-11-26 20:57:07 +000041 install_in_tmpdir = False
42 @classmethod
43 def set_install_in_tmpdir(cls, flag):
44 """ Sets a flag that controls whether or not Autotest should by
45 default be installed in a "standard" directory (e.g.
46 /home/autotest, /usr/local/autotest) or a temporary directory. """
47 cls.install_in_tmpdir = flag
48
49
50 def _get_install_dir(self, host):
51 """ Determines the location where autotest should be installed on
52 host. If self.install_in_tmpdir is set, it will return a unique
53 temporary directory that autotest can be installed in. """
54 try:
55 autodir = _get_autodir(host)
56 except error.AutotestRunError:
57 autodir = '/usr/local/autotest'
58 if self.install_in_tmpdir:
59 autodir = host.get_tmp_dir(parent=autodir)
60 return autodir
61
62
mbligh1b3b3762008-09-25 02:46:34 +000063 @log.record
mblighb3c0c912008-11-27 00:32:45 +000064 def install(self, host=None, autodir=None):
65 self._install(host=host, autodir=autodir)
jadmanski54f90af2008-10-10 16:20:55 +000066
67
mblighbccad482009-08-24 22:08:31 +000068 def install_no_autoserv(self, host=None, autodir=None):
69 self._install(host=host, autodir=autodir, no_autoserv=True)
70
71
mblighe1cea0a2009-09-03 20:20:16 +000072 def _install(self, host=None, autodir=None, no_autoserv=False):
jadmanski0afbb632008-06-06 21:10:57 +000073 """
74 Install autotest. If get() was not called previously, an
75 attempt will be made to install from the autotest svn
76 repository.
mbligh9a3f5e52008-05-28 21:21:43 +000077
mblighbccad482009-08-24 22:08:31 +000078 @param host A Host instance on which autotest will be installed
79 @param autodir Location on the remote host to install to
80 @param autoserv Disable install modes that depend on the client
81 running with the autoserv harness
mbligh9a3f5e52008-05-28 21:21:43 +000082
mblighbccad482009-08-24 22:08:31 +000083 @exception AutoservError if a tarball was not specified and
84 the target host does not have svn installed in its path
85 """
jadmanski0afbb632008-06-06 21:10:57 +000086 if not host:
87 host = self.host
88 if not self.got:
89 self.get()
90 host.wait_up(timeout=30)
91 host.setup()
showardb18134f2009-03-20 20:52:18 +000092 logging.info("Installing autotest on %s", host.hostname)
mbligh40f122a2007-11-03 23:08:46 +000093
jadmanski54f90af2008-10-10 16:20:55 +000094 # set up the autotest directory on the remote machine
95 if not autodir:
jadmanskif22fea82008-11-26 20:57:07 +000096 autodir = self._get_install_dir(host)
mbligh0562e652008-08-20 20:11:45 +000097 host.set_autodir(autodir)
jadmanski3c236942009-03-04 17:51:26 +000098 host.run('mkdir -p %s' % utils.sh_escape(autodir))
mbligh40f122a2007-11-03 23:08:46 +000099
jadmanski1c3c07b2009-03-03 23:29:36 +0000100 # make sure there are no files in $AUTODIR/results
101 results_path = os.path.join(autodir, 'results')
jadmanski3c236942009-03-04 17:51:26 +0000102 host.run('rm -rf %s/*' % utils.sh_escape(results_path),
jadmanski1c3c07b2009-03-03 23:29:36 +0000103 ignore_status=True)
104
mblighc5ddfd12008-08-04 17:15:00 +0000105 # Fetch the autotest client from the nearest repository
106 try:
107 c = global_config.global_config
108 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list)
mbligh76d19f72008-10-15 16:24:43 +0000109 pkgmgr = packages.PackageManager(autodir, hostname=host.hostname,
110 repo_urls=repos,
mbligh1e3b0992008-10-14 16:29:54 +0000111 do_locking=False,
112 run_function=host.run,
113 run_function_dargs=dict(timeout=600))
mblighc5ddfd12008-08-04 17:15:00 +0000114 # The packages dir is used to store all the packages that
115 # are fetched on that client. (for the tests,deps etc.
116 # too apart from the client)
117 pkg_dir = os.path.join(autodir, 'packages')
118 # clean up the autodir except for the packages directory
119 host.run('cd %s && ls | grep -v "^packages$"'
120 ' | xargs rm -rf && rm -rf .[^.]*' % autodir)
121 pkgmgr.install_pkg('autotest', 'client', pkg_dir, autodir,
122 preserve_install_dir=True)
123 self.installed = True
124 return
125 except global_config.ConfigError, e:
mbligh6a837772009-09-02 18:15:11 +0000126 logging.info("Could not install autotest using the packaging "
showardb18134f2009-03-20 20:52:18 +0000127 "system: %s", e)
jadmanskib1a51132009-08-07 16:45:50 +0000128 except (error.PackageInstallError, error.AutoservRunError), e:
showardb18134f2009-03-20 20:52:18 +0000129 logging.error("Could not install autotest from %s", repos)
mblighc5ddfd12008-08-04 17:15:00 +0000130
jadmanski0afbb632008-06-06 21:10:57 +0000131 # try to install from file or directory
132 if self.source_material:
133 if os.path.isdir(self.source_material):
jadmanski27b52912009-08-14 17:09:15 +0000134 c = global_config.global_config
135 supports_autoserv_packaging = c.get_config_value(
136 "PACKAGES", "serve_packages_from_autoserv", type=bool)
jadmanski0afbb632008-06-06 21:10:57 +0000137 # Copy autotest recursively
mblighbccad482009-08-24 22:08:31 +0000138 if supports_autoserv_packaging and not no_autoserv:
mbligh875f5352009-09-03 20:20:30 +0000139 dirs_to_exclude = set(["tests", "site_tests", "deps",
140 "profilers"])
jadmanski54f90af2008-10-10 16:20:55 +0000141 light_files = [os.path.join(self.source_material, f)
142 for f in os.listdir(self.source_material)
143 if f not in dirs_to_exclude]
mbligh89e258d2008-10-24 13:58:08 +0000144 host.send_file(light_files, autodir, delete_dest=True)
jadmanski54f90af2008-10-10 16:20:55 +0000145
146 # create empty dirs for all the stuff we excluded
147 commands = []
148 for path in dirs_to_exclude:
149 abs_path = os.path.join(autodir, path)
150 abs_path = utils.sh_escape(abs_path)
151 commands.append("mkdir -p '%s'" % abs_path)
mbligh875f5352009-09-03 20:20:30 +0000152 commands.append("touch '%s'/__init__.py" % abs_path)
jadmanski54f90af2008-10-10 16:20:55 +0000153 host.run(';'.join(commands))
154 else:
mbligh89e258d2008-10-24 13:58:08 +0000155 host.send_file(self.source_material, autodir,
156 delete_dest=True)
jadmanski0afbb632008-06-06 21:10:57 +0000157 else:
158 # Copy autotest via tarball
159 e_msg = 'Installation method not yet implemented!'
160 raise NotImplementedError(e_msg)
showardb18134f2009-03-20 20:52:18 +0000161 logging.info("Installation of autotest completed")
jadmanski0afbb632008-06-06 21:10:57 +0000162 self.installed = True
163 return
mbligh91334902007-09-28 01:47:59 +0000164
jadmanski0afbb632008-06-06 21:10:57 +0000165 # if that fails try to install using svn
166 if utils.run('which svn').exit_status:
mbligh78bf5352008-07-11 20:27:36 +0000167 raise error.AutoservError('svn not found on target machine: %s'
168 % host.name)
jadmanski0afbb632008-06-06 21:10:57 +0000169 try:
mbligh78bf5352008-07-11 20:27:36 +0000170 host.run('svn checkout %s %s' % (AUTOTEST_SVN, autodir))
jadmanski0afbb632008-06-06 21:10:57 +0000171 except error.AutoservRunError, e:
mbligh78bf5352008-07-11 20:27:36 +0000172 host.run('svn checkout %s %s' % (AUTOTEST_HTTP, autodir))
showardb18134f2009-03-20 20:52:18 +0000173 logging.info("Installation of autotest completed")
jadmanski0afbb632008-06-06 21:10:57 +0000174 self.installed = True
mbligh91334902007-09-28 01:47:59 +0000175
176
jadmanski7c7aff32009-03-25 22:43:07 +0000177 def uninstall(self, host=None):
178 """
179 Uninstall (i.e. delete) autotest. Removes the autotest client install
180 from the specified host.
181
182 @params host a Host instance from which the client will be removed
183 """
184 if not self.installed:
185 return
186 if not host:
187 host = self.host
188 autodir = host.get_autodir()
189 if not autodir:
190 return
191
192 # perform the actual uninstall
193 host.run("rm -rf %s" % utils.sh_escape(autodir), ignore_status=True)
194 host.set_autodir(None)
195 self.installed = False
196
197
jadmanski0afbb632008-06-06 21:10:57 +0000198 def get(self, location = None):
199 if not location:
200 location = os.path.join(self.serverdir, '../client')
201 location = os.path.abspath(location)
202 # If there's stuff run on our client directory already, it
203 # can cause problems. Try giving it a quick clean first.
204 cwd = os.getcwd()
205 os.chdir(location)
206 os.system('tools/make_clean')
207 os.chdir(cwd)
208 super(BaseAutotest, self).get(location)
209 self.got = True
mblighdcd57a82007-07-11 23:06:47 +0000210
211
mblighe7d9c602009-07-02 19:02:33 +0000212 def run(self, control_file, results_dir='.', host=None, timeout=None,
213 tag=None, parallel_flag=False, background=False,
214 client_disconnect_timeout=1800, job_tag=''):
jadmanski0afbb632008-06-06 21:10:57 +0000215 """
216 Run an autotest job on the remote machine.
mbligh9a3f5e52008-05-28 21:21:43 +0000217
mblighe7d9c602009-07-02 19:02:33 +0000218 @param control_file: An open file-like-obj of the control file.
219 @param results_dir: A str path where the results should be stored
220 on the local filesystem.
221 @param host: A Host instance on which the control file should
222 be run.
223 @param timeout: Maximum number of seconds to wait for the run or None.
224 @param tag: Tag name for the client side instance of autotest.
225 @param parallel_flag: Flag set when multiple jobs are run at the
226 same time.
227 @param background: Indicates that the client should be launched as
228 a background job; the code calling run will be responsible
229 for monitoring the client and collecting the results.
230 @param client_disconnect_timeout: Seconds to wait for the remote host
231 to come back after a reboot. [default: 30 minutes]
232 @param job_tag: The scheduler's execution tag for this particular job
233 to pass on to the clients. 'job#-owner/hostgroupname'
234
235 @raises AutotestRunError: If there is a problem executing
236 the control file.
jadmanski0afbb632008-06-06 21:10:57 +0000237 """
238 host = self._get_host_and_setup(host)
239 results_dir = os.path.abspath(results_dir)
mblighc1cbc992008-05-27 20:01:45 +0000240
jadmanski0afbb632008-06-06 21:10:57 +0000241 if tag:
242 results_dir = os.path.join(results_dir, tag)
mblighc1cbc992008-05-27 20:01:45 +0000243
mblighb3c0c912008-11-27 00:32:45 +0000244 atrun = _Run(host, results_dir, tag, parallel_flag, background)
jadmanski6dadd832009-02-05 23:39:27 +0000245 self._do_run(control_file, results_dir, host, atrun, timeout,
mblighe7d9c602009-07-02 19:02:33 +0000246 client_disconnect_timeout, job_tag)
mblighd8b39252008-03-20 21:15:03 +0000247
248
jadmanski0afbb632008-06-06 21:10:57 +0000249 def _get_host_and_setup(self, host):
250 if not host:
251 host = self.host
252 if not self.installed:
253 self.install(host)
mbligh91334902007-09-28 01:47:59 +0000254
jadmanski0afbb632008-06-06 21:10:57 +0000255 host.wait_up(timeout=30)
256 return host
mblighd8b39252008-03-20 21:15:03 +0000257
258
jadmanski6dadd832009-02-05 23:39:27 +0000259 def _do_run(self, control_file, results_dir, host, atrun, timeout,
mblighe7d9c602009-07-02 19:02:33 +0000260 client_disconnect_timeout, job_tag):
jadmanski0afbb632008-06-06 21:10:57 +0000261 try:
262 atrun.verify_machine()
263 except:
showardb18134f2009-03-20 20:52:18 +0000264 logging.error("Verify failed on %s. Reinstalling autotest",
265 host.hostname)
jadmanski0afbb632008-06-06 21:10:57 +0000266 self.install(host)
267 atrun.verify_machine()
268 debug = os.path.join(results_dir, 'debug')
269 try:
270 os.makedirs(debug)
mbligh09108442008-10-15 16:27:38 +0000271 except Exception:
jadmanski0afbb632008-06-06 21:10:57 +0000272 pass
mbligh9a3f5e52008-05-28 21:21:43 +0000273
mbligh09108442008-10-15 16:27:38 +0000274 delete_file_list = [atrun.remote_control_file,
275 atrun.remote_control_file + '.state',
276 atrun.manual_control_file,
277 atrun.manual_control_file + '.state']
278 cmd = ';'.join('rm -f ' + control for control in delete_file_list)
279 host.run(cmd, ignore_status=True)
mbligh9a3f5e52008-05-28 21:21:43 +0000280
jadmanski0afbb632008-06-06 21:10:57 +0000281 tmppath = utils.get(control_file)
mblighc5ddfd12008-08-04 17:15:00 +0000282
jadmanskicb0e1612009-02-27 18:03:10 +0000283 # build up the initialization prologue for the control file
284 prologue_lines = []
285 prologue_lines.append("job.default_boot_tag(%r)\n"
286 % host.job.last_boot_tag)
287 prologue_lines.append("job.default_test_cleanup(%r)\n"
288 % host.job.run_test_cleanup)
mblighe7d9c602009-07-02 19:02:33 +0000289 if job_tag:
290 prologue_lines.append("job.default_tag(%r)\n" % job_tag)
jadmanski23afbec2008-09-17 18:12:07 +0000291
mbligh09108442008-10-15 16:27:38 +0000292 # If the packaging system is being used, add the repository list.
mblighc5ddfd12008-08-04 17:15:00 +0000293 try:
mblighc5ddfd12008-08-04 17:15:00 +0000294 c = global_config.global_config
295 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list)
jadmanskiede7e242009-08-10 15:43:33 +0000296 repos.reverse() # high priority packages should be added last
mbligh76d19f72008-10-15 16:24:43 +0000297 pkgmgr = packages.PackageManager('autotest', hostname=host.hostname,
298 repo_urls=repos)
jadmanskib1a51132009-08-07 16:45:50 +0000299 prologue_lines.append('job.add_repository(%s)\n' % repos)
mblighc5ddfd12008-08-04 17:15:00 +0000300 except global_config.ConfigError, e:
301 pass
302
jadmanskie2eef7b2009-03-03 23:55:13 +0000303 # on full-size installs, turn on any profilers the server is using
jadmanski27b52912009-08-14 17:09:15 +0000304 if not atrun.background:
jadmanskie2eef7b2009-03-03 23:55:13 +0000305 running_profilers = host.job.profilers.add_log.iteritems()
306 for profiler, (args, dargs) in running_profilers:
307 call_args = [repr(profiler)]
308 call_args += [repr(arg) for arg in args]
309 call_args += ["%s=%r" % item for item in dargs.iteritems()]
310 prologue_lines.append("job.profilers.add(%s)\n"
311 % ", ".join(call_args))
312 cfile = "".join(prologue_lines)
313
mbligh09108442008-10-15 16:27:38 +0000314 cfile += open(tmppath).read()
315 open(tmppath, "w").write(cfile)
mblighc5ddfd12008-08-04 17:15:00 +0000316
jadmanskic09fc152008-10-15 17:56:59 +0000317 # Create and copy state file to remote_control_file + '.state'
318 sysinfo_state = {"__sysinfo": host.job.sysinfo.serialize()}
319 state_file = self._create_state_file(host.job, sysinfo_state)
320 host.send_file(state_file, atrun.remote_control_file + '.state')
321 os.remove(state_file)
322
mblighc5ddfd12008-08-04 17:15:00 +0000323 # Copy control_file to remote_control_file on the host
jadmanski0afbb632008-06-06 21:10:57 +0000324 host.send_file(tmppath, atrun.remote_control_file)
325 if os.path.abspath(tmppath) != os.path.abspath(control_file):
326 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000327
jadmanski6bb32d72009-03-19 20:25:24 +0000328 atrun.execute_control(
jadmanski6dadd832009-02-05 23:39:27 +0000329 timeout=timeout,
330 client_disconnect_timeout=client_disconnect_timeout)
jadmanski23afbec2008-09-17 18:12:07 +0000331
332
jadmanskic09fc152008-10-15 17:56:59 +0000333 def _create_state_file(self, job, state_dict):
334 """ Create a state file from a dictionary. Returns the path of the
335 state file. """
336 fd, path = tempfile.mkstemp(dir=job.tmpdir)
337 state_file = os.fdopen(fd, "w")
338 pickle.dump(state_dict, state_file)
339 state_file.close()
340 return path
341
342
jadmanski0afbb632008-06-06 21:10:57 +0000343 def run_timed_test(self, test_name, results_dir='.', host=None,
jadmanskic98c4702009-01-05 15:50:06 +0000344 timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000345 """
346 Assemble a tiny little control file to just run one test,
347 and run it as an autotest client-side test
348 """
349 if not host:
350 host = self.host
351 if not self.installed:
352 self.install(host)
353 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
354 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
355 control = "job.run_test(%s)\n" % cmd
jadmanskic98c4702009-01-05 15:50:06 +0000356 self.run(control, results_dir, host, timeout=timeout)
mbligh0e4613b2007-10-29 16:55:07 +0000357
358
jadmanskic98c4702009-01-05 15:50:06 +0000359 def run_test(self, test_name, results_dir='.', host=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000360 self.run_timed_test(test_name, results_dir, host, timeout=None,
jadmanskic98c4702009-01-05 15:50:06 +0000361 *args, **dargs)
mblighd54832b2007-07-25 16:46:56 +0000362
363
mblighdcd57a82007-07-11 23:06:47 +0000364class _Run(object):
jadmanski0afbb632008-06-06 21:10:57 +0000365 """
366 Represents a run of autotest control file. This class maintains
367 all the state necessary as an autotest control file is executed.
mblighdcd57a82007-07-11 23:06:47 +0000368
jadmanski0afbb632008-06-06 21:10:57 +0000369 It is not intended to be used directly, rather control files
370 should be run using the run method in Autotest.
371 """
mblighb3c0c912008-11-27 00:32:45 +0000372 def __init__(self, host, results_dir, tag, parallel_flag, background):
jadmanski0afbb632008-06-06 21:10:57 +0000373 self.host = host
374 self.results_dir = results_dir
375 self.env = host.env
376 self.tag = tag
377 self.parallel_flag = parallel_flag
mblighb3c0c912008-11-27 00:32:45 +0000378 self.background = background
jadmanski0afbb632008-06-06 21:10:57 +0000379 self.autodir = _get_autodir(self.host)
mbligh78bf5352008-07-11 20:27:36 +0000380 control = os.path.join(self.autodir, 'control')
jadmanski0afbb632008-06-06 21:10:57 +0000381 if tag:
mbligh78bf5352008-07-11 20:27:36 +0000382 control += '.' + tag
383 self.manual_control_file = control
384 self.remote_control_file = control + '.autoserv'
mblighdc735a22007-08-02 16:54:37 +0000385
386
jadmanski0afbb632008-06-06 21:10:57 +0000387 def verify_machine(self):
388 binary = os.path.join(self.autodir, 'bin/autotest')
389 try:
390 self.host.run('ls %s > /dev/null 2>&1' % binary)
391 except:
392 raise "Autotest does not appear to be installed"
mblighdc735a22007-08-02 16:54:37 +0000393
jadmanski0afbb632008-06-06 21:10:57 +0000394 if not self.parallel_flag:
395 tmpdir = os.path.join(self.autodir, 'tmp')
396 download = os.path.join(self.autodir, 'tests/download')
397 self.host.run('umount %s' % tmpdir, ignore_status=True)
398 self.host.run('umount %s' % download, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000399
jadmanski6dadd832009-02-05 23:39:27 +0000400
401 def get_base_cmd_args(self, section):
showard234b2962009-07-28 20:02:30 +0000402 args = ['--verbose']
jadmanski0afbb632008-06-06 21:10:57 +0000403 if section > 0:
jadmanski6dadd832009-02-05 23:39:27 +0000404 args.append('-c')
jadmanski0afbb632008-06-06 21:10:57 +0000405 if self.tag:
jadmanski6dadd832009-02-05 23:39:27 +0000406 args.append('-t %s' % self.tag)
jadmanski0afbb632008-06-06 21:10:57 +0000407 if self.host.job.use_external_logging():
jadmanski6dadd832009-02-05 23:39:27 +0000408 args.append('-l')
mblighce955fc2009-08-24 21:59:02 +0000409 if self.host.hostname:
410 args.append('--hostname=%s' % self.host.hostname)
411
jadmanski6dadd832009-02-05 23:39:27 +0000412 args.append(self.remote_control_file)
413 return args
414
415
416 def get_background_cmd(self, section):
417 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotest_client')]
418 cmd += self.get_base_cmd_args(section)
419 cmd.append('>/dev/null 2>/dev/null &')
420 return ' '.join(cmd)
421
422
423 def get_daemon_cmd(self, section, monitor_dir):
424 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotestd'),
425 monitor_dir, '-H autoserv']
426 cmd += self.get_base_cmd_args(section)
427 cmd.append('>/dev/null 2>/dev/null </dev/null &')
428 return ' '.join(cmd)
429
430
431 def get_monitor_cmd(self, monitor_dir, stdout_read, stderr_read):
432 cmd = [os.path.join(self.autodir, 'bin', 'autotestd_monitor'),
433 monitor_dir, str(stdout_read), str(stderr_read)]
jadmanski0afbb632008-06-06 21:10:57 +0000434 return ' '.join(cmd)
mblighadf2aab2007-11-29 18:16:43 +0000435
mblighd8b39252008-03-20 21:15:03 +0000436
jadmanski0afbb632008-06-06 21:10:57 +0000437 def get_client_log(self, section):
jadmanskie0c7fb62008-12-16 20:51:16 +0000438 """ Find what the "next" client.log.* file should be and open it. """
439 debug_dir = os.path.join(self.results_dir, "debug")
440 client_logs = glob.glob(os.path.join(debug_dir, "client.log.*"))
441 next_log = os.path.join(debug_dir, "client.log.%d" % len(client_logs))
442 return open(next_log, "w", 0)
mblighd8b39252008-03-20 21:15:03 +0000443
444
jadmanskib264ed02009-01-12 23:54:27 +0000445 @staticmethod
446 def is_client_job_finished(last_line):
447 return bool(re.match(r'^END .*\t----\t----\t.*$', last_line))
448
449
450 @staticmethod
451 def is_client_job_rebooting(last_line):
452 return bool(re.match(r'^\t*GOOD\t----\treboot\.start.*$', last_line))
453
454
jadmanskia61edad2009-05-21 22:17:49 +0000455 def log_unexpected_abort(self, stderr_redirector):
456 stderr_redirector.flush_all_buffers()
jadmanskib264ed02009-01-12 23:54:27 +0000457 msg = "Autotest client terminated unexpectedly"
458 self.host.job.record("END ABORT", None, None, msg)
459
460
jadmanski6dadd832009-02-05 23:39:27 +0000461 def _execute_in_background(self, section, timeout):
462 full_cmd = self.get_background_cmd(section)
463 devnull = open(os.devnull, "w")
mblighd8b39252008-03-20 21:15:03 +0000464
jadmanski6dadd832009-02-05 23:39:27 +0000465 old_resultdir = self.host.job.resultdir
jadmanski0afbb632008-06-06 21:10:57 +0000466 try:
jadmanski0afbb632008-06-06 21:10:57 +0000467 self.host.job.resultdir = self.results_dir
468 result = self.host.run(full_cmd, ignore_status=True,
469 timeout=timeout,
jadmanski6dadd832009-02-05 23:39:27 +0000470 stdout_tee=devnull,
471 stderr_tee=devnull)
jadmanski0afbb632008-06-06 21:10:57 +0000472 finally:
jadmanski0afbb632008-06-06 21:10:57 +0000473 self.host.job.resultdir = old_resultdir
jadmanski6dadd832009-02-05 23:39:27 +0000474
475 return result
476
477
478 @staticmethod
479 def _strip_stderr_prologue(stderr):
480 """Strips the 'standard' prologue that get pre-pended to every
481 remote command and returns the text that was actually written to
482 stderr by the remote command."""
483 stderr_lines = stderr.split("\n")[1:]
484 if not stderr_lines:
485 return ""
486 elif stderr_lines[0].startswith("NOTE: autotestd_monitor"):
487 del stderr_lines[0]
488 return "\n".join(stderr_lines)
489
490
491 def _execute_daemon(self, section, timeout, stderr_redirector,
492 client_disconnect_timeout):
493 monitor_dir = self.host.get_tmp_dir()
494 daemon_cmd = self.get_daemon_cmd(section, monitor_dir)
495 client_log = self.get_client_log(section)
496
497 stdout_read = stderr_read = 0
498 old_resultdir = self.host.job.resultdir
499 try:
jadmanski29a4c702009-03-03 23:30:59 +0000500 self.host.job.resultdir = self.results_dir
jadmanski6dadd832009-02-05 23:39:27 +0000501 self.host.run(daemon_cmd, ignore_status=True, timeout=timeout)
jadmanski91d56a92009-04-01 15:20:40 +0000502 disconnect_warnings = []
jadmanski6dadd832009-02-05 23:39:27 +0000503 while True:
504 monitor_cmd = self.get_monitor_cmd(monitor_dir, stdout_read,
505 stderr_read)
506 try:
507 result = self.host.run(monitor_cmd, ignore_status=True,
508 timeout=timeout,
509 stdout_tee=client_log,
510 stderr_tee=stderr_redirector)
511 except error.AutoservRunError, e:
512 result = e.result_obj
513 result.exit_status = None
jadmanski91d56a92009-04-01 15:20:40 +0000514 disconnect_warnings.append(e.description)
515
jadmanski6dadd832009-02-05 23:39:27 +0000516 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000517 "Autotest client was disconnected: %s" % e.description,
518 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000519 except error.AutoservSSHTimeout:
520 result = utils.CmdResult(monitor_cmd, "", "", None, 0)
521 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000522 "Attempt to connect to Autotest client timed out",
523 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000524
525 stdout_read += len(result.stdout)
526 stderr_read += len(self._strip_stderr_prologue(result.stderr))
527
528 if result.exit_status is not None:
529 return result
530 elif not self.host.wait_up(client_disconnect_timeout):
531 raise error.AutoservSSHTimeout(
532 "client was disconnected, reconnect timed out")
533 finally:
534 self.host.job.resultdir = old_resultdir
535
536
537 def execute_section(self, section, timeout, stderr_redirector,
538 client_disconnect_timeout):
showardb18134f2009-03-20 20:52:18 +0000539 logging.info("Executing %s/bin/autotest %s/control phase %d",
540 self.autodir, self.autodir, section)
jadmanski6dadd832009-02-05 23:39:27 +0000541
542 if self.background:
543 result = self._execute_in_background(section, timeout)
544 else:
545 result = self._execute_daemon(section, timeout, stderr_redirector,
546 client_disconnect_timeout)
547
548 last_line = stderr_redirector.last_line
mbligh2bf2db62007-11-27 00:53:18 +0000549
jadmanskib264ed02009-01-12 23:54:27 +0000550 # check if we failed hard enough to warrant an exception
jadmanski0afbb632008-06-06 21:10:57 +0000551 if result.exit_status == 1:
jadmanskib264ed02009-01-12 23:54:27 +0000552 err = error.AutotestRunError("client job was aborted")
553 elif not self.background and not result.stderr:
554 err = error.AutotestRunError(
jadmanskie4130532009-03-17 18:01:28 +0000555 "execute_section %s failed to return anything\n"
556 "stdout:%s\n" % (section, result.stdout))
jadmanskib264ed02009-01-12 23:54:27 +0000557 else:
558 err = None
mbligh0e4613b2007-10-29 16:55:07 +0000559
jadmanskib264ed02009-01-12 23:54:27 +0000560 # log something if the client failed AND never finished logging
561 if err and not self.is_client_job_finished(last_line):
jadmanskia61edad2009-05-21 22:17:49 +0000562 self.log_unexpected_abort(stderr_redirector)
jadmanskib264ed02009-01-12 23:54:27 +0000563
564 if err:
565 raise err
566 else:
567 return stderr_redirector.last_line
jadmanski4600e342008-10-29 22:54:00 +0000568
569
570 def _wait_for_reboot(self):
showardb18134f2009-03-20 20:52:18 +0000571 logging.info("Client is rebooting")
572 logging.info("Waiting for client to halt")
jadmanski4600e342008-10-29 22:54:00 +0000573 if not self.host.wait_down(HALT_TIME):
574 err = "%s failed to shutdown after %d"
575 err %= (self.host.hostname, HALT_TIME)
576 raise error.AutotestRunError(err)
showardb18134f2009-03-20 20:52:18 +0000577 logging.info("Client down, waiting for restart")
jadmanski4600e342008-10-29 22:54:00 +0000578 if not self.host.wait_up(BOOT_TIME):
579 # since reboot failed
580 # hardreset the machine once if possible
581 # before failing this control file
582 warning = "%s did not come back up, hard resetting"
583 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000584 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000585 try:
586 self.host.hardreset(wait=False)
mblighd99d3b272008-12-22 14:41:27 +0000587 except (AttributeError, error.AutoservUnsupportedError):
jadmanski4600e342008-10-29 22:54:00 +0000588 warning = "Hard reset unsupported on %s"
589 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000590 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000591 raise error.AutotestRunError("%s failed to boot after %ds" %
592 (self.host.hostname, BOOT_TIME))
593 self.host.reboot_followup()
mblighdc735a22007-08-02 16:54:37 +0000594
595
jadmanski6bb32d72009-03-19 20:25:24 +0000596 def _process_client_state_file(self):
597 state_file = os.path.basename(self.remote_control_file) + ".state"
598 state_path = os.path.join(self.results_dir, state_file)
599 try:
600 state_dict = pickle.load(open(state_path))
601 except Exception, e:
602 msg = "Ignoring error while loading client job state file: %s" % e
showard372ce3e2009-04-07 18:12:03 +0000603 logging.warning(msg)
jadmanski6bb32d72009-03-19 20:25:24 +0000604 state_dict = {}
605
606 # clear out the state file
607 # TODO: stash the file away somewhere useful instead
608 try:
609 os.remove(state_path)
610 except Exception:
611 pass
612
mblighe9633f52009-07-02 19:01:09 +0000613 logging.debug("Persistent state variables pulled back from %s: %s",
614 self.host.hostname, state_dict)
jadmanski6bb32d72009-03-19 20:25:24 +0000615
616 if "__run_test_cleanup" in state_dict:
617 if state_dict["__run_test_cleanup"]:
618 self.host.job.enable_test_cleanup()
619 else:
620 self.host.job.disable_test_cleanup()
621
622 if "__last_boot_tag" in state_dict:
623 self.host.job.last_boot_tag = state_dict["__last_boot_tag"]
624
625 if "__sysinfo" in state_dict:
626 self.host.job.sysinfo.deserialize(state_dict["__sysinfo"])
627
628
jadmanski6dadd832009-02-05 23:39:27 +0000629 def execute_control(self, timeout=None, client_disconnect_timeout=None):
jadmanski6bb32d72009-03-19 20:25:24 +0000630 if not self.background:
631 collector = log_collector(self.host, self.tag, self.results_dir)
632 hostname = self.host.hostname
633 remote_results = collector.client_results_dir
634 local_results = collector.server_results_dir
635 self.host.job.add_client_log(hostname, remote_results,
636 local_results)
637
jadmanski0afbb632008-06-06 21:10:57 +0000638 section = 0
jadmanski4600e342008-10-29 22:54:00 +0000639 start_time = time.time()
640
jadmanski043e1132008-11-19 17:10:32 +0000641 logger = client_logger(self.host, self.tag, self.results_dir)
jadmanski4600e342008-10-29 22:54:00 +0000642 try:
643 while not timeout or time.time() < start_time + timeout:
644 if timeout:
645 section_timeout = start_time + timeout - time.time()
646 else:
647 section_timeout = None
648 last = self.execute_section(section, section_timeout,
jadmanski6dadd832009-02-05 23:39:27 +0000649 logger, client_disconnect_timeout)
mblighb3c0c912008-11-27 00:32:45 +0000650 if self.background:
651 return
jadmanski4600e342008-10-29 22:54:00 +0000652 section += 1
jadmanskib264ed02009-01-12 23:54:27 +0000653 if self.is_client_job_finished(last):
showardb18134f2009-03-20 20:52:18 +0000654 logging.info("Client complete")
jadmanski4600e342008-10-29 22:54:00 +0000655 return
jadmanskib264ed02009-01-12 23:54:27 +0000656 elif self.is_client_job_rebooting(last):
jadmanski79ab9282008-11-11 17:53:12 +0000657 try:
658 self._wait_for_reboot()
659 except error.AutotestRunError, e:
jadmanski043e1132008-11-19 17:10:32 +0000660 self.host.job.record("ABORT", None, "reboot", str(e))
661 self.host.job.record("END ABORT", None, None, str(e))
jadmanski79ab9282008-11-11 17:53:12 +0000662 raise
jadmanski4600e342008-10-29 22:54:00 +0000663 continue
664
665 # if we reach here, something unexpected happened
jadmanskia61edad2009-05-21 22:17:49 +0000666 self.log_unexpected_abort(logger)
jadmanski4600e342008-10-29 22:54:00 +0000667
668 # give the client machine a chance to recover from a crash
669 self.host.wait_up(CRASH_RECOVERY_TIME)
670 msg = ("Aborting - unexpected final status message from "
671 "client: %s\n") % last
672 raise error.AutotestRunError(msg)
673 finally:
jadmanski043e1132008-11-19 17:10:32 +0000674 logger.close()
jadmanski6bb32d72009-03-19 20:25:24 +0000675 if not self.background:
676 collector.collect_client_job_results()
677 self._process_client_state_file()
678 self.host.job.remove_client_log(hostname, remote_results,
679 local_results)
mblighdcd57a82007-07-11 23:06:47 +0000680
jadmanski0afbb632008-06-06 21:10:57 +0000681 # should only get here if we timed out
682 assert timeout
683 raise error.AutotestTimeoutError()
mbligh0e4613b2007-10-29 16:55:07 +0000684
mblighdcd57a82007-07-11 23:06:47 +0000685
686def _get_autodir(host):
mbligh3c7a1502008-07-24 18:08:47 +0000687 autodir = host.get_autodir()
688 if autodir:
jadmanski8297cf52009-05-21 22:23:02 +0000689 logging.debug('Using existing host autodir: %s', autodir)
mbligh3c7a1502008-07-24 18:08:47 +0000690 return autodir
mbligh483d1da2009-08-24 22:07:14 +0000691 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