blob: 5c754fcc0e4cbc975b9d252960ecfbf76162849f [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
mbligh2266b152009-09-09 00:51:45 +00004import getpass
jadmanski043e1132008-11-19 17:10:32 +00005from autotest_lib.server import installable_object, utils
jadmanskiede7e242009-08-10 15:43:33 +00006from autotest_lib.client.common_lib import log, error, autotemp
mbligh09108442008-10-15 16:27:38 +00007from autotest_lib.client.common_lib import global_config, packages
mbligha7007722009-01-13 00:37:11 +00008from autotest_lib.client.common_lib import utils as client_utils
mbligh3c7a1502008-07-24 18:08:47 +00009
mblighdcd57a82007-07-11 23:06:47 +000010AUTOTEST_SVN = 'svn://test.kernel.org/autotest/trunk/client'
11AUTOTEST_HTTP = 'http://test.kernel.org/svn/autotest/trunk/client'
12
13# Timeouts for powering down and up respectively
14HALT_TIME = 300
mbligh07c1eac2007-11-05 18:39:29 +000015BOOT_TIME = 1800
jadmanskiec859142008-05-29 21:33:39 +000016CRASH_RECOVERY_TIME = 9000
mbligh0e4613b2007-10-29 16:55:07 +000017
mblighdcd57a82007-07-11 23:06:47 +000018
mblighd8b39252008-03-20 21:15:03 +000019class BaseAutotest(installable_object.InstallableObject):
jadmanski0afbb632008-06-06 21:10:57 +000020 """
21 This class represents the Autotest program.
mblighdcd57a82007-07-11 23:06:47 +000022
jadmanski0afbb632008-06-06 21:10:57 +000023 Autotest is used to run tests automatically and collect the results.
24 It also supports profilers.
mblighdcd57a82007-07-11 23:06:47 +000025
jadmanski0afbb632008-06-06 21:10:57 +000026 Implementation details:
27 This is a leaf class in an abstract class hierarchy, it must
28 implement the unimplemented methods in parent classes.
29 """
mbligh119c12a2007-11-12 22:13:44 +000030
jadmanski0afbb632008-06-06 21:10:57 +000031 def __init__(self, host = None):
32 self.host = host
33 self.got = False
34 self.installed = False
35 self.serverdir = utils.get_server_dir()
36 super(BaseAutotest, self).__init__()
mblighc8949b82007-07-23 16:33:58 +000037
mblighdc735a22007-08-02 16:54:37 +000038
jadmanskif22fea82008-11-26 20:57:07 +000039 install_in_tmpdir = False
40 @classmethod
41 def set_install_in_tmpdir(cls, flag):
42 """ Sets a flag that controls whether or not Autotest should by
43 default be installed in a "standard" directory (e.g.
44 /home/autotest, /usr/local/autotest) or a temporary directory. """
45 cls.install_in_tmpdir = flag
46
47
48 def _get_install_dir(self, host):
49 """ Determines the location where autotest should be installed on
50 host. If self.install_in_tmpdir is set, it will return a unique
51 temporary directory that autotest can be installed in. """
52 try:
53 autodir = _get_autodir(host)
54 except error.AutotestRunError:
55 autodir = '/usr/local/autotest'
56 if self.install_in_tmpdir:
57 autodir = host.get_tmp_dir(parent=autodir)
58 return autodir
59
60
mbligh1b3b3762008-09-25 02:46:34 +000061 @log.record
mblighb3c0c912008-11-27 00:32:45 +000062 def install(self, host=None, autodir=None):
63 self._install(host=host, autodir=autodir)
jadmanski54f90af2008-10-10 16:20:55 +000064
65
mblighbccad482009-08-24 22:08:31 +000066 def install_no_autoserv(self, host=None, autodir=None):
67 self._install(host=host, autodir=autodir, no_autoserv=True)
68
69
mblighe1cea0a2009-09-03 20:20:16 +000070 def _install(self, host=None, autodir=None, no_autoserv=False):
jadmanski0afbb632008-06-06 21:10:57 +000071 """
72 Install autotest. If get() was not called previously, an
73 attempt will be made to install from the autotest svn
74 repository.
mbligh9a3f5e52008-05-28 21:21:43 +000075
mblighbccad482009-08-24 22:08:31 +000076 @param host A Host instance on which autotest will be installed
77 @param autodir Location on the remote host to install to
78 @param autoserv Disable install modes that depend on the client
79 running with the autoserv harness
mbligh9a3f5e52008-05-28 21:21:43 +000080
mblighbccad482009-08-24 22:08:31 +000081 @exception AutoservError if a tarball was not specified and
82 the target host does not have svn installed in its path
83 """
jadmanski0afbb632008-06-06 21:10:57 +000084 if not host:
85 host = self.host
86 if not self.got:
87 self.get()
88 host.wait_up(timeout=30)
89 host.setup()
showardb18134f2009-03-20 20:52:18 +000090 logging.info("Installing autotest on %s", host.hostname)
mbligh40f122a2007-11-03 23:08:46 +000091
jadmanski54f90af2008-10-10 16:20:55 +000092 # set up the autotest directory on the remote machine
93 if not autodir:
jadmanskif22fea82008-11-26 20:57:07 +000094 autodir = self._get_install_dir(host)
mbligh0562e652008-08-20 20:11:45 +000095 host.set_autodir(autodir)
jadmanski3c236942009-03-04 17:51:26 +000096 host.run('mkdir -p %s' % utils.sh_escape(autodir))
mbligh40f122a2007-11-03 23:08:46 +000097
jadmanski1c3c07b2009-03-03 23:29:36 +000098 # make sure there are no files in $AUTODIR/results
99 results_path = os.path.join(autodir, 'results')
jadmanski3c236942009-03-04 17:51:26 +0000100 host.run('rm -rf %s/*' % utils.sh_escape(results_path),
jadmanski1c3c07b2009-03-03 23:29:36 +0000101 ignore_status=True)
102
mblighc5ddfd12008-08-04 17:15:00 +0000103 # Fetch the autotest client from the nearest repository
104 try:
105 c = global_config.global_config
106 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list)
mbligh76d19f72008-10-15 16:24:43 +0000107 pkgmgr = packages.PackageManager(autodir, hostname=host.hostname,
108 repo_urls=repos,
mbligh1e3b0992008-10-14 16:29:54 +0000109 do_locking=False,
110 run_function=host.run,
111 run_function_dargs=dict(timeout=600))
mblighc5ddfd12008-08-04 17:15:00 +0000112 # The packages dir is used to store all the packages that
113 # are fetched on that client. (for the tests,deps etc.
114 # too apart from the client)
115 pkg_dir = os.path.join(autodir, 'packages')
116 # clean up the autodir except for the packages directory
117 host.run('cd %s && ls | grep -v "^packages$"'
118 ' | xargs rm -rf && rm -rf .[^.]*' % autodir)
119 pkgmgr.install_pkg('autotest', 'client', pkg_dir, autodir,
120 preserve_install_dir=True)
121 self.installed = True
122 return
123 except global_config.ConfigError, e:
mbligh6a837772009-09-02 18:15:11 +0000124 logging.info("Could not install autotest using the packaging "
showardb18134f2009-03-20 20:52:18 +0000125 "system: %s", e)
jadmanskib1a51132009-08-07 16:45:50 +0000126 except (error.PackageInstallError, error.AutoservRunError), e:
showardb18134f2009-03-20 20:52:18 +0000127 logging.error("Could not install autotest from %s", repos)
mblighc5ddfd12008-08-04 17:15:00 +0000128
jadmanski0afbb632008-06-06 21:10:57 +0000129 # try to install from file or directory
130 if self.source_material:
131 if os.path.isdir(self.source_material):
jadmanski27b52912009-08-14 17:09:15 +0000132 c = global_config.global_config
133 supports_autoserv_packaging = c.get_config_value(
134 "PACKAGES", "serve_packages_from_autoserv", type=bool)
jadmanski0afbb632008-06-06 21:10:57 +0000135 # Copy autotest recursively
mblighbccad482009-08-24 22:08:31 +0000136 if supports_autoserv_packaging and not no_autoserv:
mbligh875f5352009-09-03 20:20:30 +0000137 dirs_to_exclude = set(["tests", "site_tests", "deps",
138 "profilers"])
jadmanski54f90af2008-10-10 16:20:55 +0000139 light_files = [os.path.join(self.source_material, f)
140 for f in os.listdir(self.source_material)
141 if f not in dirs_to_exclude]
mbligh89e258d2008-10-24 13:58:08 +0000142 host.send_file(light_files, autodir, delete_dest=True)
jadmanski54f90af2008-10-10 16:20:55 +0000143
144 # create empty dirs for all the stuff we excluded
145 commands = []
146 for path in dirs_to_exclude:
147 abs_path = os.path.join(autodir, path)
148 abs_path = utils.sh_escape(abs_path)
149 commands.append("mkdir -p '%s'" % abs_path)
mbligh875f5352009-09-03 20:20:30 +0000150 commands.append("touch '%s'/__init__.py" % abs_path)
jadmanski54f90af2008-10-10 16:20:55 +0000151 host.run(';'.join(commands))
152 else:
mbligh89e258d2008-10-24 13:58:08 +0000153 host.send_file(self.source_material, autodir,
154 delete_dest=True)
jadmanski0afbb632008-06-06 21:10:57 +0000155 else:
156 # Copy autotest via tarball
157 e_msg = 'Installation method not yet implemented!'
158 raise NotImplementedError(e_msg)
showardb18134f2009-03-20 20:52:18 +0000159 logging.info("Installation of autotest completed")
jadmanski0afbb632008-06-06 21:10:57 +0000160 self.installed = True
161 return
mbligh91334902007-09-28 01:47:59 +0000162
jadmanski0afbb632008-06-06 21:10:57 +0000163 # if that fails try to install using svn
164 if utils.run('which svn').exit_status:
mbligh78bf5352008-07-11 20:27:36 +0000165 raise error.AutoservError('svn not found on target machine: %s'
166 % host.name)
jadmanski0afbb632008-06-06 21:10:57 +0000167 try:
mbligh78bf5352008-07-11 20:27:36 +0000168 host.run('svn checkout %s %s' % (AUTOTEST_SVN, autodir))
jadmanski0afbb632008-06-06 21:10:57 +0000169 except error.AutoservRunError, e:
mbligh78bf5352008-07-11 20:27:36 +0000170 host.run('svn checkout %s %s' % (AUTOTEST_HTTP, autodir))
showardb18134f2009-03-20 20:52:18 +0000171 logging.info("Installation of autotest completed")
jadmanski0afbb632008-06-06 21:10:57 +0000172 self.installed = True
mbligh91334902007-09-28 01:47:59 +0000173
174
jadmanski7c7aff32009-03-25 22:43:07 +0000175 def uninstall(self, host=None):
176 """
177 Uninstall (i.e. delete) autotest. Removes the autotest client install
178 from the specified host.
179
180 @params host a Host instance from which the client will be removed
181 """
182 if not self.installed:
183 return
184 if not host:
185 host = self.host
186 autodir = host.get_autodir()
187 if not autodir:
188 return
189
190 # perform the actual uninstall
191 host.run("rm -rf %s" % utils.sh_escape(autodir), ignore_status=True)
192 host.set_autodir(None)
193 self.installed = False
194
195
jadmanski0afbb632008-06-06 21:10:57 +0000196 def get(self, location = None):
197 if not location:
198 location = os.path.join(self.serverdir, '../client')
199 location = os.path.abspath(location)
200 # If there's stuff run on our client directory already, it
201 # can cause problems. Try giving it a quick clean first.
202 cwd = os.getcwd()
203 os.chdir(location)
204 os.system('tools/make_clean')
205 os.chdir(cwd)
206 super(BaseAutotest, self).get(location)
207 self.got = True
mblighdcd57a82007-07-11 23:06:47 +0000208
209
mblighe7d9c602009-07-02 19:02:33 +0000210 def run(self, control_file, results_dir='.', host=None, timeout=None,
211 tag=None, parallel_flag=False, background=False,
212 client_disconnect_timeout=1800, job_tag=''):
jadmanski0afbb632008-06-06 21:10:57 +0000213 """
214 Run an autotest job on the remote machine.
mbligh9a3f5e52008-05-28 21:21:43 +0000215
mblighe7d9c602009-07-02 19:02:33 +0000216 @param control_file: An open file-like-obj of the control file.
217 @param results_dir: A str path where the results should be stored
218 on the local filesystem.
219 @param host: A Host instance on which the control file should
220 be run.
221 @param timeout: Maximum number of seconds to wait for the run or None.
222 @param tag: Tag name for the client side instance of autotest.
223 @param parallel_flag: Flag set when multiple jobs are run at the
224 same time.
225 @param background: Indicates that the client should be launched as
226 a background job; the code calling run will be responsible
227 for monitoring the client and collecting the results.
228 @param client_disconnect_timeout: Seconds to wait for the remote host
229 to come back after a reboot. [default: 30 minutes]
230 @param job_tag: The scheduler's execution tag for this particular job
231 to pass on to the clients. 'job#-owner/hostgroupname'
232
233 @raises AutotestRunError: If there is a problem executing
234 the control file.
jadmanski0afbb632008-06-06 21:10:57 +0000235 """
236 host = self._get_host_and_setup(host)
237 results_dir = os.path.abspath(results_dir)
mblighc1cbc992008-05-27 20:01:45 +0000238
jadmanski0afbb632008-06-06 21:10:57 +0000239 if tag:
240 results_dir = os.path.join(results_dir, tag)
mblighc1cbc992008-05-27 20:01:45 +0000241
mblighb3c0c912008-11-27 00:32:45 +0000242 atrun = _Run(host, results_dir, tag, parallel_flag, background)
jadmanski6dadd832009-02-05 23:39:27 +0000243 self._do_run(control_file, results_dir, host, atrun, timeout,
mblighe7d9c602009-07-02 19:02:33 +0000244 client_disconnect_timeout, job_tag)
mblighd8b39252008-03-20 21:15:03 +0000245
246
jadmanski0afbb632008-06-06 21:10:57 +0000247 def _get_host_and_setup(self, host):
248 if not host:
249 host = self.host
250 if not self.installed:
251 self.install(host)
mbligh91334902007-09-28 01:47:59 +0000252
jadmanski0afbb632008-06-06 21:10:57 +0000253 host.wait_up(timeout=30)
254 return host
mblighd8b39252008-03-20 21:15:03 +0000255
256
jadmanski6dadd832009-02-05 23:39:27 +0000257 def _do_run(self, control_file, results_dir, host, atrun, timeout,
mblighe7d9c602009-07-02 19:02:33 +0000258 client_disconnect_timeout, job_tag):
jadmanski0afbb632008-06-06 21:10:57 +0000259 try:
260 atrun.verify_machine()
261 except:
showardb18134f2009-03-20 20:52:18 +0000262 logging.error("Verify failed on %s. Reinstalling autotest",
263 host.hostname)
jadmanski0afbb632008-06-06 21:10:57 +0000264 self.install(host)
265 atrun.verify_machine()
266 debug = os.path.join(results_dir, 'debug')
267 try:
268 os.makedirs(debug)
mbligh09108442008-10-15 16:27:38 +0000269 except Exception:
jadmanski0afbb632008-06-06 21:10:57 +0000270 pass
mbligh9a3f5e52008-05-28 21:21:43 +0000271
mbligh09108442008-10-15 16:27:38 +0000272 delete_file_list = [atrun.remote_control_file,
273 atrun.remote_control_file + '.state',
274 atrun.manual_control_file,
275 atrun.manual_control_file + '.state']
276 cmd = ';'.join('rm -f ' + control for control in delete_file_list)
277 host.run(cmd, ignore_status=True)
mbligh9a3f5e52008-05-28 21:21:43 +0000278
jadmanski0afbb632008-06-06 21:10:57 +0000279 tmppath = utils.get(control_file)
mblighc5ddfd12008-08-04 17:15:00 +0000280
jadmanskicb0e1612009-02-27 18:03:10 +0000281 # build up the initialization prologue for the control file
282 prologue_lines = []
283 prologue_lines.append("job.default_boot_tag(%r)\n"
284 % host.job.last_boot_tag)
285 prologue_lines.append("job.default_test_cleanup(%r)\n"
286 % host.job.run_test_cleanup)
mblighe7d9c602009-07-02 19:02:33 +0000287 if job_tag:
288 prologue_lines.append("job.default_tag(%r)\n" % job_tag)
jadmanski23afbec2008-09-17 18:12:07 +0000289
mbligh09108442008-10-15 16:27:38 +0000290 # If the packaging system is being used, add the repository list.
mblighc5ddfd12008-08-04 17:15:00 +0000291 try:
mblighc5ddfd12008-08-04 17:15:00 +0000292 c = global_config.global_config
293 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list)
jadmanskiede7e242009-08-10 15:43:33 +0000294 repos.reverse() # high priority packages should be added last
mbligh76d19f72008-10-15 16:24:43 +0000295 pkgmgr = packages.PackageManager('autotest', hostname=host.hostname,
296 repo_urls=repos)
jadmanskib1a51132009-08-07 16:45:50 +0000297 prologue_lines.append('job.add_repository(%s)\n' % repos)
mblighc5ddfd12008-08-04 17:15:00 +0000298 except global_config.ConfigError, e:
299 pass
300
jadmanskie2eef7b2009-03-03 23:55:13 +0000301 # on full-size installs, turn on any profilers the server is using
jadmanski27b52912009-08-14 17:09:15 +0000302 if not atrun.background:
jadmanskie2eef7b2009-03-03 23:55:13 +0000303 running_profilers = host.job.profilers.add_log.iteritems()
304 for profiler, (args, dargs) in running_profilers:
305 call_args = [repr(profiler)]
306 call_args += [repr(arg) for arg in args]
307 call_args += ["%s=%r" % item for item in dargs.iteritems()]
308 prologue_lines.append("job.profilers.add(%s)\n"
309 % ", ".join(call_args))
310 cfile = "".join(prologue_lines)
311
mbligh09108442008-10-15 16:27:38 +0000312 cfile += open(tmppath).read()
313 open(tmppath, "w").write(cfile)
mblighc5ddfd12008-08-04 17:15:00 +0000314
jadmanskic09fc152008-10-15 17:56:59 +0000315 # Create and copy state file to remote_control_file + '.state'
316 sysinfo_state = {"__sysinfo": host.job.sysinfo.serialize()}
317 state_file = self._create_state_file(host.job, sysinfo_state)
318 host.send_file(state_file, atrun.remote_control_file + '.state')
319 os.remove(state_file)
320
mblighc5ddfd12008-08-04 17:15:00 +0000321 # Copy control_file to remote_control_file on the host
jadmanski0afbb632008-06-06 21:10:57 +0000322 host.send_file(tmppath, atrun.remote_control_file)
323 if os.path.abspath(tmppath) != os.path.abspath(control_file):
324 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000325
jadmanski6bb32d72009-03-19 20:25:24 +0000326 atrun.execute_control(
jadmanski6dadd832009-02-05 23:39:27 +0000327 timeout=timeout,
328 client_disconnect_timeout=client_disconnect_timeout)
jadmanski23afbec2008-09-17 18:12:07 +0000329
330
jadmanskic09fc152008-10-15 17:56:59 +0000331 def _create_state_file(self, job, state_dict):
332 """ Create a state file from a dictionary. Returns the path of the
333 state file. """
334 fd, path = tempfile.mkstemp(dir=job.tmpdir)
335 state_file = os.fdopen(fd, "w")
336 pickle.dump(state_dict, state_file)
337 state_file.close()
338 return path
339
340
jadmanski0afbb632008-06-06 21:10:57 +0000341 def run_timed_test(self, test_name, results_dir='.', host=None,
jadmanskic98c4702009-01-05 15:50:06 +0000342 timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000343 """
344 Assemble a tiny little control file to just run one test,
345 and run it as an autotest client-side test
346 """
347 if not host:
348 host = self.host
349 if not self.installed:
350 self.install(host)
351 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
352 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
353 control = "job.run_test(%s)\n" % cmd
jadmanskic98c4702009-01-05 15:50:06 +0000354 self.run(control, results_dir, host, timeout=timeout)
mbligh0e4613b2007-10-29 16:55:07 +0000355
356
jadmanskic98c4702009-01-05 15:50:06 +0000357 def run_test(self, test_name, results_dir='.', host=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000358 self.run_timed_test(test_name, results_dir, host, timeout=None,
jadmanskic98c4702009-01-05 15:50:06 +0000359 *args, **dargs)
mblighd54832b2007-07-25 16:46:56 +0000360
361
mblighdcd57a82007-07-11 23:06:47 +0000362class _Run(object):
jadmanski0afbb632008-06-06 21:10:57 +0000363 """
364 Represents a run of autotest control file. This class maintains
365 all the state necessary as an autotest control file is executed.
mblighdcd57a82007-07-11 23:06:47 +0000366
jadmanski0afbb632008-06-06 21:10:57 +0000367 It is not intended to be used directly, rather control files
368 should be run using the run method in Autotest.
369 """
mblighb3c0c912008-11-27 00:32:45 +0000370 def __init__(self, host, results_dir, tag, parallel_flag, background):
jadmanski0afbb632008-06-06 21:10:57 +0000371 self.host = host
372 self.results_dir = results_dir
373 self.env = host.env
374 self.tag = tag
375 self.parallel_flag = parallel_flag
mblighb3c0c912008-11-27 00:32:45 +0000376 self.background = background
jadmanski0afbb632008-06-06 21:10:57 +0000377 self.autodir = _get_autodir(self.host)
mbligh78bf5352008-07-11 20:27:36 +0000378 control = os.path.join(self.autodir, 'control')
jadmanski0afbb632008-06-06 21:10:57 +0000379 if tag:
mbligh78bf5352008-07-11 20:27:36 +0000380 control += '.' + tag
381 self.manual_control_file = control
382 self.remote_control_file = control + '.autoserv'
mblighdc735a22007-08-02 16:54:37 +0000383
384
jadmanski0afbb632008-06-06 21:10:57 +0000385 def verify_machine(self):
386 binary = os.path.join(self.autodir, 'bin/autotest')
387 try:
388 self.host.run('ls %s > /dev/null 2>&1' % binary)
389 except:
390 raise "Autotest does not appear to be installed"
mblighdc735a22007-08-02 16:54:37 +0000391
jadmanski0afbb632008-06-06 21:10:57 +0000392 if not self.parallel_flag:
393 tmpdir = os.path.join(self.autodir, 'tmp')
394 download = os.path.join(self.autodir, 'tests/download')
395 self.host.run('umount %s' % tmpdir, ignore_status=True)
396 self.host.run('umount %s' % download, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000397
jadmanski6dadd832009-02-05 23:39:27 +0000398
399 def get_base_cmd_args(self, section):
showard234b2962009-07-28 20:02:30 +0000400 args = ['--verbose']
jadmanski0afbb632008-06-06 21:10:57 +0000401 if section > 0:
jadmanski6dadd832009-02-05 23:39:27 +0000402 args.append('-c')
jadmanski0afbb632008-06-06 21:10:57 +0000403 if self.tag:
jadmanski6dadd832009-02-05 23:39:27 +0000404 args.append('-t %s' % self.tag)
jadmanski0afbb632008-06-06 21:10:57 +0000405 if self.host.job.use_external_logging():
jadmanski6dadd832009-02-05 23:39:27 +0000406 args.append('-l')
mblighce955fc2009-08-24 21:59:02 +0000407 if self.host.hostname:
408 args.append('--hostname=%s' % self.host.hostname)
mbligh2266b152009-09-09 00:51:45 +0000409 args.append('--user=%s' % getpass.getuser())
mblighce955fc2009-08-24 21:59:02 +0000410
jadmanski6dadd832009-02-05 23:39:27 +0000411 args.append(self.remote_control_file)
412 return args
413
414
415 def get_background_cmd(self, section):
416 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotest_client')]
417 cmd += self.get_base_cmd_args(section)
418 cmd.append('>/dev/null 2>/dev/null &')
419 return ' '.join(cmd)
420
421
422 def get_daemon_cmd(self, section, monitor_dir):
423 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotestd'),
424 monitor_dir, '-H autoserv']
425 cmd += self.get_base_cmd_args(section)
426 cmd.append('>/dev/null 2>/dev/null </dev/null &')
427 return ' '.join(cmd)
428
429
430 def get_monitor_cmd(self, monitor_dir, stdout_read, stderr_read):
431 cmd = [os.path.join(self.autodir, 'bin', 'autotestd_monitor'),
432 monitor_dir, str(stdout_read), str(stderr_read)]
jadmanski0afbb632008-06-06 21:10:57 +0000433 return ' '.join(cmd)
mblighadf2aab2007-11-29 18:16:43 +0000434
mblighd8b39252008-03-20 21:15:03 +0000435
jadmanski0afbb632008-06-06 21:10:57 +0000436 def get_client_log(self, section):
jadmanskie0c7fb62008-12-16 20:51:16 +0000437 """ Find what the "next" client.log.* file should be and open it. """
438 debug_dir = os.path.join(self.results_dir, "debug")
439 client_logs = glob.glob(os.path.join(debug_dir, "client.log.*"))
440 next_log = os.path.join(debug_dir, "client.log.%d" % len(client_logs))
441 return open(next_log, "w", 0)
mblighd8b39252008-03-20 21:15:03 +0000442
443
jadmanskib264ed02009-01-12 23:54:27 +0000444 @staticmethod
445 def is_client_job_finished(last_line):
446 return bool(re.match(r'^END .*\t----\t----\t.*$', last_line))
447
448
449 @staticmethod
450 def is_client_job_rebooting(last_line):
451 return bool(re.match(r'^\t*GOOD\t----\treboot\.start.*$', last_line))
452
453
jadmanskia61edad2009-05-21 22:17:49 +0000454 def log_unexpected_abort(self, stderr_redirector):
455 stderr_redirector.flush_all_buffers()
jadmanskib264ed02009-01-12 23:54:27 +0000456 msg = "Autotest client terminated unexpectedly"
457 self.host.job.record("END ABORT", None, None, msg)
458
459
jadmanski6dadd832009-02-05 23:39:27 +0000460 def _execute_in_background(self, section, timeout):
461 full_cmd = self.get_background_cmd(section)
462 devnull = open(os.devnull, "w")
mblighd8b39252008-03-20 21:15:03 +0000463
jadmanski6dadd832009-02-05 23:39:27 +0000464 old_resultdir = self.host.job.resultdir
jadmanski0afbb632008-06-06 21:10:57 +0000465 try:
jadmanski0afbb632008-06-06 21:10:57 +0000466 self.host.job.resultdir = self.results_dir
467 result = self.host.run(full_cmd, ignore_status=True,
468 timeout=timeout,
jadmanski6dadd832009-02-05 23:39:27 +0000469 stdout_tee=devnull,
470 stderr_tee=devnull)
jadmanski0afbb632008-06-06 21:10:57 +0000471 finally:
jadmanski0afbb632008-06-06 21:10:57 +0000472 self.host.job.resultdir = old_resultdir
jadmanski6dadd832009-02-05 23:39:27 +0000473
474 return result
475
476
477 @staticmethod
478 def _strip_stderr_prologue(stderr):
479 """Strips the 'standard' prologue that get pre-pended to every
480 remote command and returns the text that was actually written to
481 stderr by the remote command."""
482 stderr_lines = stderr.split("\n")[1:]
483 if not stderr_lines:
484 return ""
485 elif stderr_lines[0].startswith("NOTE: autotestd_monitor"):
486 del stderr_lines[0]
487 return "\n".join(stderr_lines)
488
489
490 def _execute_daemon(self, section, timeout, stderr_redirector,
491 client_disconnect_timeout):
492 monitor_dir = self.host.get_tmp_dir()
493 daemon_cmd = self.get_daemon_cmd(section, monitor_dir)
494 client_log = self.get_client_log(section)
495
496 stdout_read = stderr_read = 0
497 old_resultdir = self.host.job.resultdir
498 try:
jadmanski29a4c702009-03-03 23:30:59 +0000499 self.host.job.resultdir = self.results_dir
jadmanski6dadd832009-02-05 23:39:27 +0000500 self.host.run(daemon_cmd, ignore_status=True, timeout=timeout)
jadmanski91d56a92009-04-01 15:20:40 +0000501 disconnect_warnings = []
jadmanski6dadd832009-02-05 23:39:27 +0000502 while True:
503 monitor_cmd = self.get_monitor_cmd(monitor_dir, stdout_read,
504 stderr_read)
505 try:
506 result = self.host.run(monitor_cmd, ignore_status=True,
507 timeout=timeout,
508 stdout_tee=client_log,
509 stderr_tee=stderr_redirector)
510 except error.AutoservRunError, e:
511 result = e.result_obj
512 result.exit_status = None
jadmanski91d56a92009-04-01 15:20:40 +0000513 disconnect_warnings.append(e.description)
514
jadmanski6dadd832009-02-05 23:39:27 +0000515 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000516 "Autotest client was disconnected: %s" % e.description,
517 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000518 except error.AutoservSSHTimeout:
519 result = utils.CmdResult(monitor_cmd, "", "", None, 0)
520 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000521 "Attempt to connect to Autotest client timed out",
522 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000523
524 stdout_read += len(result.stdout)
525 stderr_read += len(self._strip_stderr_prologue(result.stderr))
526
527 if result.exit_status is not None:
528 return result
529 elif not self.host.wait_up(client_disconnect_timeout):
530 raise error.AutoservSSHTimeout(
531 "client was disconnected, reconnect timed out")
532 finally:
533 self.host.job.resultdir = old_resultdir
534
535
536 def execute_section(self, section, timeout, stderr_redirector,
537 client_disconnect_timeout):
showardb18134f2009-03-20 20:52:18 +0000538 logging.info("Executing %s/bin/autotest %s/control phase %d",
539 self.autodir, self.autodir, section)
jadmanski6dadd832009-02-05 23:39:27 +0000540
541 if self.background:
542 result = self._execute_in_background(section, timeout)
543 else:
544 result = self._execute_daemon(section, timeout, stderr_redirector,
545 client_disconnect_timeout)
546
547 last_line = stderr_redirector.last_line
mbligh2bf2db62007-11-27 00:53:18 +0000548
jadmanskib264ed02009-01-12 23:54:27 +0000549 # check if we failed hard enough to warrant an exception
jadmanski0afbb632008-06-06 21:10:57 +0000550 if result.exit_status == 1:
jadmanskib264ed02009-01-12 23:54:27 +0000551 err = error.AutotestRunError("client job was aborted")
552 elif not self.background and not result.stderr:
553 err = error.AutotestRunError(
jadmanskie4130532009-03-17 18:01:28 +0000554 "execute_section %s failed to return anything\n"
555 "stdout:%s\n" % (section, result.stdout))
jadmanskib264ed02009-01-12 23:54:27 +0000556 else:
557 err = None
mbligh0e4613b2007-10-29 16:55:07 +0000558
jadmanskib264ed02009-01-12 23:54:27 +0000559 # log something if the client failed AND never finished logging
560 if err and not self.is_client_job_finished(last_line):
jadmanskia61edad2009-05-21 22:17:49 +0000561 self.log_unexpected_abort(stderr_redirector)
jadmanskib264ed02009-01-12 23:54:27 +0000562
563 if err:
564 raise err
565 else:
566 return stderr_redirector.last_line
jadmanski4600e342008-10-29 22:54:00 +0000567
568
569 def _wait_for_reboot(self):
showardb18134f2009-03-20 20:52:18 +0000570 logging.info("Client is rebooting")
571 logging.info("Waiting for client to halt")
jadmanski4600e342008-10-29 22:54:00 +0000572 if not self.host.wait_down(HALT_TIME):
573 err = "%s failed to shutdown after %d"
574 err %= (self.host.hostname, HALT_TIME)
575 raise error.AutotestRunError(err)
showardb18134f2009-03-20 20:52:18 +0000576 logging.info("Client down, waiting for restart")
jadmanski4600e342008-10-29 22:54:00 +0000577 if not self.host.wait_up(BOOT_TIME):
578 # since reboot failed
579 # hardreset the machine once if possible
580 # before failing this control file
581 warning = "%s did not come back up, hard resetting"
582 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000583 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000584 try:
585 self.host.hardreset(wait=False)
mblighd99d3b272008-12-22 14:41:27 +0000586 except (AttributeError, error.AutoservUnsupportedError):
jadmanski4600e342008-10-29 22:54:00 +0000587 warning = "Hard reset unsupported on %s"
588 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000589 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000590 raise error.AutotestRunError("%s failed to boot after %ds" %
591 (self.host.hostname, BOOT_TIME))
592 self.host.reboot_followup()
mblighdc735a22007-08-02 16:54:37 +0000593
594
jadmanski6bb32d72009-03-19 20:25:24 +0000595 def _process_client_state_file(self):
596 state_file = os.path.basename(self.remote_control_file) + ".state"
597 state_path = os.path.join(self.results_dir, state_file)
598 try:
599 state_dict = pickle.load(open(state_path))
600 except Exception, e:
601 msg = "Ignoring error while loading client job state file: %s" % e
showard372ce3e2009-04-07 18:12:03 +0000602 logging.warning(msg)
jadmanski6bb32d72009-03-19 20:25:24 +0000603 state_dict = {}
604
605 # clear out the state file
606 # TODO: stash the file away somewhere useful instead
607 try:
608 os.remove(state_path)
609 except Exception:
610 pass
611
mblighe9633f52009-07-02 19:01:09 +0000612 logging.debug("Persistent state variables pulled back from %s: %s",
613 self.host.hostname, state_dict)
jadmanski6bb32d72009-03-19 20:25:24 +0000614
615 if "__run_test_cleanup" in state_dict:
616 if state_dict["__run_test_cleanup"]:
617 self.host.job.enable_test_cleanup()
618 else:
619 self.host.job.disable_test_cleanup()
620
621 if "__last_boot_tag" in state_dict:
622 self.host.job.last_boot_tag = state_dict["__last_boot_tag"]
623
624 if "__sysinfo" in state_dict:
625 self.host.job.sysinfo.deserialize(state_dict["__sysinfo"])
626
627
jadmanski6dadd832009-02-05 23:39:27 +0000628 def execute_control(self, timeout=None, client_disconnect_timeout=None):
jadmanski6bb32d72009-03-19 20:25:24 +0000629 if not self.background:
630 collector = log_collector(self.host, self.tag, self.results_dir)
631 hostname = self.host.hostname
632 remote_results = collector.client_results_dir
633 local_results = collector.server_results_dir
634 self.host.job.add_client_log(hostname, remote_results,
635 local_results)
636
jadmanski0afbb632008-06-06 21:10:57 +0000637 section = 0
jadmanski4600e342008-10-29 22:54:00 +0000638 start_time = time.time()
639
jadmanski043e1132008-11-19 17:10:32 +0000640 logger = client_logger(self.host, self.tag, self.results_dir)
jadmanski4600e342008-10-29 22:54:00 +0000641 try:
642 while not timeout or time.time() < start_time + timeout:
643 if timeout:
644 section_timeout = start_time + timeout - time.time()
645 else:
646 section_timeout = None
647 last = self.execute_section(section, section_timeout,
jadmanski6dadd832009-02-05 23:39:27 +0000648 logger, client_disconnect_timeout)
mblighb3c0c912008-11-27 00:32:45 +0000649 if self.background:
650 return
jadmanski4600e342008-10-29 22:54:00 +0000651 section += 1
jadmanskib264ed02009-01-12 23:54:27 +0000652 if self.is_client_job_finished(last):
showardb18134f2009-03-20 20:52:18 +0000653 logging.info("Client complete")
jadmanski4600e342008-10-29 22:54:00 +0000654 return
jadmanskib264ed02009-01-12 23:54:27 +0000655 elif self.is_client_job_rebooting(last):
jadmanski79ab9282008-11-11 17:53:12 +0000656 try:
657 self._wait_for_reboot()
658 except error.AutotestRunError, e:
jadmanski043e1132008-11-19 17:10:32 +0000659 self.host.job.record("ABORT", None, "reboot", str(e))
660 self.host.job.record("END ABORT", None, None, str(e))
jadmanski79ab9282008-11-11 17:53:12 +0000661 raise
jadmanski4600e342008-10-29 22:54:00 +0000662 continue
663
664 # if we reach here, something unexpected happened
jadmanskia61edad2009-05-21 22:17:49 +0000665 self.log_unexpected_abort(logger)
jadmanski4600e342008-10-29 22:54:00 +0000666
667 # give the client machine a chance to recover from a crash
668 self.host.wait_up(CRASH_RECOVERY_TIME)
669 msg = ("Aborting - unexpected final status message from "
670 "client: %s\n") % last
671 raise error.AutotestRunError(msg)
672 finally:
jadmanski043e1132008-11-19 17:10:32 +0000673 logger.close()
jadmanski6bb32d72009-03-19 20:25:24 +0000674 if not self.background:
675 collector.collect_client_job_results()
676 self._process_client_state_file()
677 self.host.job.remove_client_log(hostname, remote_results,
678 local_results)
mblighdcd57a82007-07-11 23:06:47 +0000679
jadmanski0afbb632008-06-06 21:10:57 +0000680 # should only get here if we timed out
681 assert timeout
682 raise error.AutotestTimeoutError()
mbligh0e4613b2007-10-29 16:55:07 +0000683
mblighdcd57a82007-07-11 23:06:47 +0000684
685def _get_autodir(host):
mbligh3c7a1502008-07-24 18:08:47 +0000686 autodir = host.get_autodir()
687 if autodir:
jadmanski8297cf52009-05-21 22:23:02 +0000688 logging.debug('Using existing host autodir: %s', autodir)
mbligh3c7a1502008-07-24 18:08:47 +0000689 return autodir
mblighe2e0bef2009-09-03 20:29:18 +0000690 client_autodir_paths = global_config.global_config.get_config_value(
691 'AUTOSERV', 'client_autodir_paths', type=list,
692 default=['/usr/local/autotest', '/home/autotest'])
693 for path in client_autodir_paths:
jadmanski0afbb632008-06-06 21:10:57 +0000694 try:
mbligh483d1da2009-08-24 22:07:14 +0000695 host.run('test -d %s' % utils.sh_escape(path))
jadmanski8297cf52009-05-21 22:23:02 +0000696 logging.debug('Found autodir at %s', path)
jadmanski0afbb632008-06-06 21:10:57 +0000697 return path
698 except error.AutoservRunError:
mbligh483d1da2009-08-24 22:07:14 +0000699 logging.debug('%s does not exist on %s', path, host.hostname)
jadmanski8297cf52009-05-21 22:23:02 +0000700 raise error.AutotestRunError('Cannot figure out autotest directory')
mblighd8b39252008-03-20 21:15:03 +0000701
702
jadmanski043e1132008-11-19 17:10:32 +0000703class log_collector(object):
704 def __init__(self, host, client_tag, results_dir):
705 self.host = host
706 if not client_tag:
707 client_tag = "default"
708 self.client_results_dir = os.path.join(host.get_autodir(), "results",
709 client_tag)
710 self.server_results_dir = results_dir
711
712
713 def collect_client_job_results(self):
714 """ A method that collects all the current results of a running
715 client job into the results dir. By default does nothing as no
716 client job is running, but when running a client job you can override
717 this with something that will actually do something. """
718
719 # make an effort to wait for the machine to come up
720 try:
721 self.host.wait_up(timeout=30)
722 except error.AutoservError:
723 # don't worry about any errors, we'll try and
724 # get the results anyway
725 pass
726
jadmanski043e1132008-11-19 17:10:32 +0000727 # Copy all dirs in default to results_dir
728 try:
jadmanski043e1132008-11-19 17:10:32 +0000729 self.host.get_file(self.client_results_dir + '/',
mbligh45561782009-05-11 21:14:34 +0000730 self.server_results_dir, preserve_symlinks=True)
jadmanski043e1132008-11-19 17:10:32 +0000731 except Exception:
732 # well, don't stop running just because we couldn't get logs
showardb18134f2009-03-20 20:52:18 +0000733 e_msg = "Unexpected error copying test result logs, continuing ..."
734 logging.error(e_msg)
jadmanski043e1132008-11-19 17:10:32 +0000735 traceback.print_exc(file=sys.stdout)
736
737
jadmanski043e1132008-11-19 17:10:32 +0000738# a file-like object for catching stderr from an autotest client and
739# extracting status logs from it
740class client_logger(object):
741 """Partial file object to write to both stdout and
742 the status log file. We only implement those methods
743 utils.run() actually calls.
744
745 Note that this class is fairly closely coupled with server_job, as it
746 uses special job._ methods to actually carry out the loggging.
747 """
748 status_parser = re.compile(r"^AUTOTEST_STATUS:([^:]*):(.*)$")
749 test_complete_parser = re.compile(r"^AUTOTEST_TEST_COMPLETE:(.*)$")
jadmanskib1a51132009-08-07 16:45:50 +0000750 fetch_package_parser = re.compile(
751 r"^AUTOTEST_FETCH_PACKAGE:([^:]*):([^:]*):(.*)$")
jadmanski043e1132008-11-19 17:10:32 +0000752 extract_indent = re.compile(r"^(\t*).*$")
jadmanskiefe4ebf2009-05-21 22:12:30 +0000753 extract_timestamp = re.compile(r".*\ttimestamp=(\d+)\t.*$")
jadmanski043e1132008-11-19 17:10:32 +0000754
755 def __init__(self, host, tag, server_results_dir):
756 self.host = host
757 self.job = host.job
758 self.log_collector = log_collector(host, tag, server_results_dir)
759 self.leftover = ""
760 self.last_line = ""
jadmanskiefe4ebf2009-05-21 22:12:30 +0000761 self.newest_timestamp = float("-inf")
jadmanski043e1132008-11-19 17:10:32 +0000762 self.logs = {}
jadmanski6dadd832009-02-05 23:39:27 +0000763 self.server_warnings = []
jadmanski043e1132008-11-19 17:10:32 +0000764
765
jadmanskiefe4ebf2009-05-21 22:12:30 +0000766 def _update_timestamp(self, line):
767 match = self.extract_timestamp.search(line)
768 if match:
769 self.newest_timestamp = max(self.newest_timestamp,
770 int(match.group(1)))
771
772
jadmanski043e1132008-11-19 17:10:32 +0000773 def _process_log_dict(self, log_dict):
774 log_list = log_dict.pop("logs", [])
775 for key in sorted(log_dict.iterkeys()):
776 log_list += self._process_log_dict(log_dict.pop(key))
777 return log_list
778
779
780 def _process_logs(self):
781 """Go through the accumulated logs in self.log and print them
782 out to stdout and the status log. Note that this processes
783 logs in an ordering where:
784
785 1) logs to different tags are never interleaved
786 2) logs to x.y come before logs to x.y.z for all z
787 3) logs to x.y come before x.z whenever y < z
788
789 Note that this will in general not be the same as the
790 chronological ordering of the logs. However, if a chronological
791 ordering is desired that one can be reconstructed from the
792 status log by looking at timestamp lines."""
793 log_list = self._process_log_dict(self.logs)
794 for line in log_list:
795 self.job._record_prerendered(line + '\n')
796 if log_list:
797 self.last_line = log_list[-1]
798
799
800 def _process_quoted_line(self, tag, line):
801 """Process a line quoted with an AUTOTEST_STATUS flag. If the
802 tag is blank then we want to push out all the data we've been
803 building up in self.logs, and then the newest line. If the
804 tag is not blank, then push the line into the logs for handling
805 later."""
showardb18134f2009-03-20 20:52:18 +0000806 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +0000807 if tag == "":
808 self._process_logs()
809 self.job._record_prerendered(line + '\n')
810 self.last_line = line
811 else:
812 tag_parts = [int(x) for x in tag.split(".")]
813 log_dict = self.logs
814 for part in tag_parts:
815 log_dict = log_dict.setdefault(part, {})
816 log_list = log_dict.setdefault("logs", [])
817 log_list.append(line)
818
819
jadmanskif37df842009-02-11 00:03:26 +0000820 def _process_info_line(self, line):
821 """Check if line is an INFO line, and if it is, interpret any control
822 messages (e.g. enabling/disabling warnings) that it may contain."""
823 match = re.search(r"^\t*INFO\t----\t----(.*)\t[^\t]*$", line)
824 if not match:
825 return # not an INFO line
826 for field in match.group(1).split('\t'):
827 if field.startswith("warnings.enable="):
jadmanski16a7ff72009-04-01 18:19:53 +0000828 func = self.job.warning_manager.enable_warnings
jadmanskif37df842009-02-11 00:03:26 +0000829 elif field.startswith("warnings.disable="):
jadmanski16a7ff72009-04-01 18:19:53 +0000830 func = self.job.warning_manager.disable_warnings
jadmanskif37df842009-02-11 00:03:26 +0000831 else:
832 continue
833 warning_type = field.split("=", 1)[1]
jadmanski16a7ff72009-04-01 18:19:53 +0000834 func(warning_type)
jadmanskif37df842009-02-11 00:03:26 +0000835
836
jadmanski043e1132008-11-19 17:10:32 +0000837 def _process_line(self, line):
838 """Write out a line of data to the appropriate stream. Status
839 lines sent by autotest will be prepended with
840 "AUTOTEST_STATUS", and all other lines are ssh error
841 messages."""
842 status_match = self.status_parser.search(line)
843 test_complete_match = self.test_complete_parser.search(line)
jadmanskib1a51132009-08-07 16:45:50 +0000844 fetch_package_match = self.fetch_package_parser.search(line)
jadmanski043e1132008-11-19 17:10:32 +0000845 if status_match:
846 tag, line = status_match.groups()
jadmanskif37df842009-02-11 00:03:26 +0000847 self._process_info_line(line)
jadmanski043e1132008-11-19 17:10:32 +0000848 self._process_quoted_line(tag, line)
849 elif test_complete_match:
jadmanskifcc0d5d2009-02-12 21:52:54 +0000850 self._process_logs()
jadmanski043e1132008-11-19 17:10:32 +0000851 fifo_path, = test_complete_match.groups()
852 self.log_collector.collect_client_job_results()
853 self.host.run("echo A > %s" % fifo_path)
jadmanskib1a51132009-08-07 16:45:50 +0000854 elif fetch_package_match:
855 pkg_name, dest_path, fifo_path = fetch_package_match.groups()
jadmanskiede7e242009-08-10 15:43:33 +0000856 serve_packages = global_config.global_config.get_config_value(
857 "PACKAGES", "serve_packages_from_autoserv", type=bool)
858 if serve_packages and pkg_name.endswith(".tar.bz2"):
859 try:
860 self._send_tarball(pkg_name, dest_path)
861 except Exception:
862 msg = "Package tarball creation failed, continuing anyway"
863 logging.exception(msg)
jadmanskib1a51132009-08-07 16:45:50 +0000864 self.host.run("echo B > %s" % fifo_path)
jadmanski043e1132008-11-19 17:10:32 +0000865 else:
showardb18134f2009-03-20 20:52:18 +0000866 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +0000867
868
jadmanskiede7e242009-08-10 15:43:33 +0000869 def _send_tarball(self, pkg_name, remote_dest):
870 name, pkg_type = self.job.pkgmgr.parse_tarball_name(pkg_name)
871 src_dirs = []
872 if pkg_type == 'test':
873 src_dirs += [os.path.join(self.job.clientdir, 'site_tests', name),
874 os.path.join(self.job.clientdir, 'tests', name)]
875 elif pkg_type == 'profiler':
876 src_dirs += [os.path.join(self.job.clientdir, 'profilers', name)]
877 elif pkg_type == 'dep':
878 src_dirs += [os.path.join(self.job.clientdir, 'deps', name)]
879 elif pkg_type == 'client':
880 return # you must already have a client to hit this anyway
881 else:
882 return # no other types are supported
883
884 # iterate over src_dirs until we find one that exists, then tar it
885 for src_dir in src_dirs:
886 if os.path.exists(src_dir):
887 try:
888 logging.info('Bundling %s into %s', src_dir, pkg_name)
889 temp_dir = autotemp.tempdir(unique_id='autoserv-packager',
890 dir=self.job.tmpdir)
891 tarball_path = self.job.pkgmgr.tar_package(
mblighbccad482009-08-24 22:08:31 +0000892 pkg_name, src_dir, temp_dir.name, " .")
jadmanskiede7e242009-08-10 15:43:33 +0000893 self.host.send_file(tarball_path, remote_dest)
894 finally:
895 temp_dir.clean()
896 return
897
898
jadmanski043e1132008-11-19 17:10:32 +0000899 def _format_warnings(self, last_line, warnings):
900 # use the indentation of whatever the last log line was
901 indent = self.extract_indent.match(last_line).group(1)
902 # if the last line starts a new group, add an extra indent
903 if last_line.lstrip('\t').startswith("START\t"):
904 indent += '\t'
905 return [self.job._render_record("WARN", None, None, msg,
906 timestamp, indent).rstrip('\n')
907 for timestamp, msg in warnings]
908
909
910 def _process_warnings(self, last_line, log_dict, warnings):
911 if log_dict.keys() in ([], ["logs"]):
912 # there are no sub-jobs, just append the warnings here
913 warnings = self._format_warnings(last_line, warnings)
914 log_list = log_dict.setdefault("logs", [])
915 log_list += warnings
916 for warning in warnings:
917 sys.stdout.write(warning + '\n')
918 else:
919 # there are sub-jobs, so put the warnings in there
920 log_list = log_dict.get("logs", [])
921 if log_list:
922 last_line = log_list[-1]
923 for key in sorted(log_dict.iterkeys()):
924 if key != "logs":
925 self._process_warnings(last_line,
926 log_dict[key],
927 warnings)
928
jadmanskif37df842009-02-11 00:03:26 +0000929
jadmanski91d56a92009-04-01 15:20:40 +0000930 def log_warning(self, msg, warning_type):
jadmanski6dadd832009-02-05 23:39:27 +0000931 """Injects a WARN message into the current status logging stream."""
jadmanski91d56a92009-04-01 15:20:40 +0000932 timestamp = int(time.time())
933 if self.job.warning_manager.is_valid(timestamp, warning_type):
934 self.server_warnings.append((timestamp, msg))
jadmanski6dadd832009-02-05 23:39:27 +0000935
jadmanski043e1132008-11-19 17:10:32 +0000936
937 def write(self, data):
938 # first check for any new console warnings
jadmanski6dadd832009-02-05 23:39:27 +0000939 warnings = self.job._read_warnings() + self.server_warnings
940 warnings.sort() # sort into timestamp order
jadmanski043e1132008-11-19 17:10:32 +0000941 # now process the newest data written out
942 data = self.leftover + data
943 lines = data.split("\n")
944 # process every line but the last one
945 for line in lines[:-1]:
jadmanskiefe4ebf2009-05-21 22:12:30 +0000946 self._update_timestamp(line)
947 # output any warnings between now and the next status line
948 old_warnings = [(timestamp, msg) for timestamp, msg in warnings
949 if timestamp < self.newest_timestamp]
950 self._process_warnings(self.last_line, self.logs, warnings)
951 del warnings[:len(old_warnings)]
jadmanski043e1132008-11-19 17:10:32 +0000952 self._process_line(line)
jadmanskiefe4ebf2009-05-21 22:12:30 +0000953 # save off any warnings not yet logged for later processing
954 self.server_warnings = warnings
jadmanski043e1132008-11-19 17:10:32 +0000955 # save the last line for later processing
956 # since we may not have the whole line yet
957 self.leftover = lines[-1]
958
959
960 def flush(self):
961 sys.stdout.flush()
962
963
jadmanskia61edad2009-05-21 22:17:49 +0000964 def flush_all_buffers(self):
jadmanski043e1132008-11-19 17:10:32 +0000965 if self.leftover:
966 self._process_line(self.leftover)
jadmanskia61edad2009-05-21 22:17:49 +0000967 self.leftover = ""
jadmanskiefe4ebf2009-05-21 22:12:30 +0000968 self._process_warnings(self.last_line, self.logs, self.server_warnings)
jadmanski043e1132008-11-19 17:10:32 +0000969 self._process_logs()
970 self.flush()
971
972
jadmanskia61edad2009-05-21 22:17:49 +0000973 def close(self):
974 self.flush_all_buffers()
975
976
mbligha7007722009-01-13 00:37:11 +0000977SiteAutotest = client_utils.import_site_class(
978 __file__, "autotest_lib.server.site_autotest", "SiteAutotest",
979 BaseAutotest)
mblighd8b39252008-03-20 21:15:03 +0000980
981class Autotest(SiteAutotest):
jadmanski0afbb632008-06-06 21:10:57 +0000982 pass