blob: cec499aeb92052e4fab97081ca5ba2789cdf725a [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:
139 dirs_to_exclude = set(["tests", "site_tests", "deps"])
140 profiler_dir = os.path.join(self.source_material,
141 "profilers")
142 for f in os.listdir(profiler_dir):
143 if os.path.isdir(os.path.join(profiler_dir, f)):
144 dirs_to_exclude.add(f)
jadmanski54f90af2008-10-10 16:20:55 +0000145 light_files = [os.path.join(self.source_material, f)
146 for f in os.listdir(self.source_material)
147 if f not in dirs_to_exclude]
mbligh89e258d2008-10-24 13:58:08 +0000148 host.send_file(light_files, autodir, delete_dest=True)
jadmanski54f90af2008-10-10 16:20:55 +0000149
150 # create empty dirs for all the stuff we excluded
151 commands = []
152 for path in dirs_to_exclude:
153 abs_path = os.path.join(autodir, path)
154 abs_path = utils.sh_escape(abs_path)
155 commands.append("mkdir -p '%s'" % abs_path)
156 host.run(';'.join(commands))
157 else:
mbligh89e258d2008-10-24 13:58:08 +0000158 host.send_file(self.source_material, autodir,
159 delete_dest=True)
jadmanski0afbb632008-06-06 21:10:57 +0000160 else:
161 # Copy autotest via tarball
162 e_msg = 'Installation method not yet implemented!'
163 raise NotImplementedError(e_msg)
showardb18134f2009-03-20 20:52:18 +0000164 logging.info("Installation of autotest completed")
jadmanski0afbb632008-06-06 21:10:57 +0000165 self.installed = True
166 return
mbligh91334902007-09-28 01:47:59 +0000167
jadmanski0afbb632008-06-06 21:10:57 +0000168 # if that fails try to install using svn
169 if utils.run('which svn').exit_status:
mbligh78bf5352008-07-11 20:27:36 +0000170 raise error.AutoservError('svn not found on target machine: %s'
171 % host.name)
jadmanski0afbb632008-06-06 21:10:57 +0000172 try:
mbligh78bf5352008-07-11 20:27:36 +0000173 host.run('svn checkout %s %s' % (AUTOTEST_SVN, autodir))
jadmanski0afbb632008-06-06 21:10:57 +0000174 except error.AutoservRunError, e:
mbligh78bf5352008-07-11 20:27:36 +0000175 host.run('svn checkout %s %s' % (AUTOTEST_HTTP, autodir))
showardb18134f2009-03-20 20:52:18 +0000176 logging.info("Installation of autotest completed")
jadmanski0afbb632008-06-06 21:10:57 +0000177 self.installed = True
mbligh91334902007-09-28 01:47:59 +0000178
179
jadmanski7c7aff32009-03-25 22:43:07 +0000180 def uninstall(self, host=None):
181 """
182 Uninstall (i.e. delete) autotest. Removes the autotest client install
183 from the specified host.
184
185 @params host a Host instance from which the client will be removed
186 """
187 if not self.installed:
188 return
189 if not host:
190 host = self.host
191 autodir = host.get_autodir()
192 if not autodir:
193 return
194
195 # perform the actual uninstall
196 host.run("rm -rf %s" % utils.sh_escape(autodir), ignore_status=True)
197 host.set_autodir(None)
198 self.installed = False
199
200
jadmanski0afbb632008-06-06 21:10:57 +0000201 def get(self, location = None):
202 if not location:
203 location = os.path.join(self.serverdir, '../client')
204 location = os.path.abspath(location)
205 # If there's stuff run on our client directory already, it
206 # can cause problems. Try giving it a quick clean first.
207 cwd = os.getcwd()
208 os.chdir(location)
209 os.system('tools/make_clean')
210 os.chdir(cwd)
211 super(BaseAutotest, self).get(location)
212 self.got = True
mblighdcd57a82007-07-11 23:06:47 +0000213
214
mblighe7d9c602009-07-02 19:02:33 +0000215 def run(self, control_file, results_dir='.', host=None, timeout=None,
216 tag=None, parallel_flag=False, background=False,
217 client_disconnect_timeout=1800, job_tag=''):
jadmanski0afbb632008-06-06 21:10:57 +0000218 """
219 Run an autotest job on the remote machine.
mbligh9a3f5e52008-05-28 21:21:43 +0000220
mblighe7d9c602009-07-02 19:02:33 +0000221 @param control_file: An open file-like-obj of the control file.
222 @param results_dir: A str path where the results should be stored
223 on the local filesystem.
224 @param host: A Host instance on which the control file should
225 be run.
226 @param timeout: Maximum number of seconds to wait for the run or None.
227 @param tag: Tag name for the client side instance of autotest.
228 @param parallel_flag: Flag set when multiple jobs are run at the
229 same time.
230 @param background: Indicates that the client should be launched as
231 a background job; the code calling run will be responsible
232 for monitoring the client and collecting the results.
233 @param client_disconnect_timeout: Seconds to wait for the remote host
234 to come back after a reboot. [default: 30 minutes]
235 @param job_tag: The scheduler's execution tag for this particular job
236 to pass on to the clients. 'job#-owner/hostgroupname'
237
238 @raises AutotestRunError: If there is a problem executing
239 the control file.
jadmanski0afbb632008-06-06 21:10:57 +0000240 """
241 host = self._get_host_and_setup(host)
242 results_dir = os.path.abspath(results_dir)
mblighc1cbc992008-05-27 20:01:45 +0000243
jadmanski0afbb632008-06-06 21:10:57 +0000244 if tag:
245 results_dir = os.path.join(results_dir, tag)
mblighc1cbc992008-05-27 20:01:45 +0000246
mblighb3c0c912008-11-27 00:32:45 +0000247 atrun = _Run(host, results_dir, tag, parallel_flag, background)
jadmanski6dadd832009-02-05 23:39:27 +0000248 self._do_run(control_file, results_dir, host, atrun, timeout,
mblighe7d9c602009-07-02 19:02:33 +0000249 client_disconnect_timeout, job_tag)
mblighd8b39252008-03-20 21:15:03 +0000250
251
jadmanski0afbb632008-06-06 21:10:57 +0000252 def _get_host_and_setup(self, host):
253 if not host:
254 host = self.host
255 if not self.installed:
256 self.install(host)
mbligh91334902007-09-28 01:47:59 +0000257
jadmanski0afbb632008-06-06 21:10:57 +0000258 host.wait_up(timeout=30)
259 return host
mblighd8b39252008-03-20 21:15:03 +0000260
261
jadmanski6dadd832009-02-05 23:39:27 +0000262 def _do_run(self, control_file, results_dir, host, atrun, timeout,
mblighe7d9c602009-07-02 19:02:33 +0000263 client_disconnect_timeout, job_tag):
jadmanski0afbb632008-06-06 21:10:57 +0000264 try:
265 atrun.verify_machine()
266 except:
showardb18134f2009-03-20 20:52:18 +0000267 logging.error("Verify failed on %s. Reinstalling autotest",
268 host.hostname)
jadmanski0afbb632008-06-06 21:10:57 +0000269 self.install(host)
270 atrun.verify_machine()
271 debug = os.path.join(results_dir, 'debug')
272 try:
273 os.makedirs(debug)
mbligh09108442008-10-15 16:27:38 +0000274 except Exception:
jadmanski0afbb632008-06-06 21:10:57 +0000275 pass
mbligh9a3f5e52008-05-28 21:21:43 +0000276
mbligh09108442008-10-15 16:27:38 +0000277 delete_file_list = [atrun.remote_control_file,
278 atrun.remote_control_file + '.state',
279 atrun.manual_control_file,
280 atrun.manual_control_file + '.state']
281 cmd = ';'.join('rm -f ' + control for control in delete_file_list)
282 host.run(cmd, ignore_status=True)
mbligh9a3f5e52008-05-28 21:21:43 +0000283
jadmanski0afbb632008-06-06 21:10:57 +0000284 tmppath = utils.get(control_file)
mblighc5ddfd12008-08-04 17:15:00 +0000285
jadmanskicb0e1612009-02-27 18:03:10 +0000286 # build up the initialization prologue for the control file
287 prologue_lines = []
288 prologue_lines.append("job.default_boot_tag(%r)\n"
289 % host.job.last_boot_tag)
290 prologue_lines.append("job.default_test_cleanup(%r)\n"
291 % host.job.run_test_cleanup)
mblighe7d9c602009-07-02 19:02:33 +0000292 if job_tag:
293 prologue_lines.append("job.default_tag(%r)\n" % job_tag)
jadmanski23afbec2008-09-17 18:12:07 +0000294
mbligh09108442008-10-15 16:27:38 +0000295 # If the packaging system is being used, add the repository list.
mblighc5ddfd12008-08-04 17:15:00 +0000296 try:
mblighc5ddfd12008-08-04 17:15:00 +0000297 c = global_config.global_config
298 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list)
jadmanskiede7e242009-08-10 15:43:33 +0000299 repos.reverse() # high priority packages should be added last
mbligh76d19f72008-10-15 16:24:43 +0000300 pkgmgr = packages.PackageManager('autotest', hostname=host.hostname,
301 repo_urls=repos)
jadmanskib1a51132009-08-07 16:45:50 +0000302 prologue_lines.append('job.add_repository(%s)\n' % repos)
mblighc5ddfd12008-08-04 17:15:00 +0000303 except global_config.ConfigError, e:
304 pass
305
jadmanskie2eef7b2009-03-03 23:55:13 +0000306 # on full-size installs, turn on any profilers the server is using
jadmanski27b52912009-08-14 17:09:15 +0000307 if not atrun.background:
jadmanskie2eef7b2009-03-03 23:55:13 +0000308 running_profilers = host.job.profilers.add_log.iteritems()
309 for profiler, (args, dargs) in running_profilers:
310 call_args = [repr(profiler)]
311 call_args += [repr(arg) for arg in args]
312 call_args += ["%s=%r" % item for item in dargs.iteritems()]
313 prologue_lines.append("job.profilers.add(%s)\n"
314 % ", ".join(call_args))
315 cfile = "".join(prologue_lines)
316
mbligh09108442008-10-15 16:27:38 +0000317 cfile += open(tmppath).read()
318 open(tmppath, "w").write(cfile)
mblighc5ddfd12008-08-04 17:15:00 +0000319
jadmanskic09fc152008-10-15 17:56:59 +0000320 # Create and copy state file to remote_control_file + '.state'
321 sysinfo_state = {"__sysinfo": host.job.sysinfo.serialize()}
322 state_file = self._create_state_file(host.job, sysinfo_state)
323 host.send_file(state_file, atrun.remote_control_file + '.state')
324 os.remove(state_file)
325
mblighc5ddfd12008-08-04 17:15:00 +0000326 # Copy control_file to remote_control_file on the host
jadmanski0afbb632008-06-06 21:10:57 +0000327 host.send_file(tmppath, atrun.remote_control_file)
328 if os.path.abspath(tmppath) != os.path.abspath(control_file):
329 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000330
jadmanski6bb32d72009-03-19 20:25:24 +0000331 atrun.execute_control(
jadmanski6dadd832009-02-05 23:39:27 +0000332 timeout=timeout,
333 client_disconnect_timeout=client_disconnect_timeout)
jadmanski23afbec2008-09-17 18:12:07 +0000334
335
jadmanskic09fc152008-10-15 17:56:59 +0000336 def _create_state_file(self, job, state_dict):
337 """ Create a state file from a dictionary. Returns the path of the
338 state file. """
339 fd, path = tempfile.mkstemp(dir=job.tmpdir)
340 state_file = os.fdopen(fd, "w")
341 pickle.dump(state_dict, state_file)
342 state_file.close()
343 return path
344
345
jadmanski0afbb632008-06-06 21:10:57 +0000346 def run_timed_test(self, test_name, results_dir='.', host=None,
jadmanskic98c4702009-01-05 15:50:06 +0000347 timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000348 """
349 Assemble a tiny little control file to just run one test,
350 and run it as an autotest client-side test
351 """
352 if not host:
353 host = self.host
354 if not self.installed:
355 self.install(host)
356 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
357 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
358 control = "job.run_test(%s)\n" % cmd
jadmanskic98c4702009-01-05 15:50:06 +0000359 self.run(control, results_dir, host, timeout=timeout)
mbligh0e4613b2007-10-29 16:55:07 +0000360
361
jadmanskic98c4702009-01-05 15:50:06 +0000362 def run_test(self, test_name, results_dir='.', host=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000363 self.run_timed_test(test_name, results_dir, host, timeout=None,
jadmanskic98c4702009-01-05 15:50:06 +0000364 *args, **dargs)
mblighd54832b2007-07-25 16:46:56 +0000365
366
mblighdcd57a82007-07-11 23:06:47 +0000367class _Run(object):
jadmanski0afbb632008-06-06 21:10:57 +0000368 """
369 Represents a run of autotest control file. This class maintains
370 all the state necessary as an autotest control file is executed.
mblighdcd57a82007-07-11 23:06:47 +0000371
jadmanski0afbb632008-06-06 21:10:57 +0000372 It is not intended to be used directly, rather control files
373 should be run using the run method in Autotest.
374 """
mblighb3c0c912008-11-27 00:32:45 +0000375 def __init__(self, host, results_dir, tag, parallel_flag, background):
jadmanski0afbb632008-06-06 21:10:57 +0000376 self.host = host
377 self.results_dir = results_dir
378 self.env = host.env
379 self.tag = tag
380 self.parallel_flag = parallel_flag
mblighb3c0c912008-11-27 00:32:45 +0000381 self.background = background
jadmanski0afbb632008-06-06 21:10:57 +0000382 self.autodir = _get_autodir(self.host)
mbligh78bf5352008-07-11 20:27:36 +0000383 control = os.path.join(self.autodir, 'control')
jadmanski0afbb632008-06-06 21:10:57 +0000384 if tag:
mbligh78bf5352008-07-11 20:27:36 +0000385 control += '.' + tag
386 self.manual_control_file = control
387 self.remote_control_file = control + '.autoserv'
mblighdc735a22007-08-02 16:54:37 +0000388
389
jadmanski0afbb632008-06-06 21:10:57 +0000390 def verify_machine(self):
391 binary = os.path.join(self.autodir, 'bin/autotest')
392 try:
393 self.host.run('ls %s > /dev/null 2>&1' % binary)
394 except:
395 raise "Autotest does not appear to be installed"
mblighdc735a22007-08-02 16:54:37 +0000396
jadmanski0afbb632008-06-06 21:10:57 +0000397 if not self.parallel_flag:
398 tmpdir = os.path.join(self.autodir, 'tmp')
399 download = os.path.join(self.autodir, 'tests/download')
400 self.host.run('umount %s' % tmpdir, ignore_status=True)
401 self.host.run('umount %s' % download, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000402
jadmanski6dadd832009-02-05 23:39:27 +0000403
404 def get_base_cmd_args(self, section):
showard234b2962009-07-28 20:02:30 +0000405 args = ['--verbose']
jadmanski0afbb632008-06-06 21:10:57 +0000406 if section > 0:
jadmanski6dadd832009-02-05 23:39:27 +0000407 args.append('-c')
jadmanski0afbb632008-06-06 21:10:57 +0000408 if self.tag:
jadmanski6dadd832009-02-05 23:39:27 +0000409 args.append('-t %s' % self.tag)
jadmanski0afbb632008-06-06 21:10:57 +0000410 if self.host.job.use_external_logging():
jadmanski6dadd832009-02-05 23:39:27 +0000411 args.append('-l')
mblighce955fc2009-08-24 21:59:02 +0000412 if self.host.hostname:
413 args.append('--hostname=%s' % self.host.hostname)
414
jadmanski6dadd832009-02-05 23:39:27 +0000415 args.append(self.remote_control_file)
416 return args
417
418
419 def get_background_cmd(self, section):
420 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotest_client')]
421 cmd += self.get_base_cmd_args(section)
422 cmd.append('>/dev/null 2>/dev/null &')
423 return ' '.join(cmd)
424
425
426 def get_daemon_cmd(self, section, monitor_dir):
427 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotestd'),
428 monitor_dir, '-H autoserv']
429 cmd += self.get_base_cmd_args(section)
430 cmd.append('>/dev/null 2>/dev/null </dev/null &')
431 return ' '.join(cmd)
432
433
434 def get_monitor_cmd(self, monitor_dir, stdout_read, stderr_read):
435 cmd = [os.path.join(self.autodir, 'bin', 'autotestd_monitor'),
436 monitor_dir, str(stdout_read), str(stderr_read)]
jadmanski0afbb632008-06-06 21:10:57 +0000437 return ' '.join(cmd)
mblighadf2aab2007-11-29 18:16:43 +0000438
mblighd8b39252008-03-20 21:15:03 +0000439
jadmanski0afbb632008-06-06 21:10:57 +0000440 def get_client_log(self, section):
jadmanskie0c7fb62008-12-16 20:51:16 +0000441 """ Find what the "next" client.log.* file should be and open it. """
442 debug_dir = os.path.join(self.results_dir, "debug")
443 client_logs = glob.glob(os.path.join(debug_dir, "client.log.*"))
444 next_log = os.path.join(debug_dir, "client.log.%d" % len(client_logs))
445 return open(next_log, "w", 0)
mblighd8b39252008-03-20 21:15:03 +0000446
447
jadmanskib264ed02009-01-12 23:54:27 +0000448 @staticmethod
449 def is_client_job_finished(last_line):
450 return bool(re.match(r'^END .*\t----\t----\t.*$', last_line))
451
452
453 @staticmethod
454 def is_client_job_rebooting(last_line):
455 return bool(re.match(r'^\t*GOOD\t----\treboot\.start.*$', last_line))
456
457
jadmanskia61edad2009-05-21 22:17:49 +0000458 def log_unexpected_abort(self, stderr_redirector):
459 stderr_redirector.flush_all_buffers()
jadmanskib264ed02009-01-12 23:54:27 +0000460 msg = "Autotest client terminated unexpectedly"
461 self.host.job.record("END ABORT", None, None, msg)
462
463
jadmanski6dadd832009-02-05 23:39:27 +0000464 def _execute_in_background(self, section, timeout):
465 full_cmd = self.get_background_cmd(section)
466 devnull = open(os.devnull, "w")
mblighd8b39252008-03-20 21:15:03 +0000467
jadmanski6dadd832009-02-05 23:39:27 +0000468 old_resultdir = self.host.job.resultdir
jadmanski0afbb632008-06-06 21:10:57 +0000469 try:
jadmanski0afbb632008-06-06 21:10:57 +0000470 self.host.job.resultdir = self.results_dir
471 result = self.host.run(full_cmd, ignore_status=True,
472 timeout=timeout,
jadmanski6dadd832009-02-05 23:39:27 +0000473 stdout_tee=devnull,
474 stderr_tee=devnull)
jadmanski0afbb632008-06-06 21:10:57 +0000475 finally:
jadmanski0afbb632008-06-06 21:10:57 +0000476 self.host.job.resultdir = old_resultdir
jadmanski6dadd832009-02-05 23:39:27 +0000477
478 return result
479
480
481 @staticmethod
482 def _strip_stderr_prologue(stderr):
483 """Strips the 'standard' prologue that get pre-pended to every
484 remote command and returns the text that was actually written to
485 stderr by the remote command."""
486 stderr_lines = stderr.split("\n")[1:]
487 if not stderr_lines:
488 return ""
489 elif stderr_lines[0].startswith("NOTE: autotestd_monitor"):
490 del stderr_lines[0]
491 return "\n".join(stderr_lines)
492
493
494 def _execute_daemon(self, section, timeout, stderr_redirector,
495 client_disconnect_timeout):
496 monitor_dir = self.host.get_tmp_dir()
497 daemon_cmd = self.get_daemon_cmd(section, monitor_dir)
498 client_log = self.get_client_log(section)
499
500 stdout_read = stderr_read = 0
501 old_resultdir = self.host.job.resultdir
502 try:
jadmanski29a4c702009-03-03 23:30:59 +0000503 self.host.job.resultdir = self.results_dir
jadmanski6dadd832009-02-05 23:39:27 +0000504 self.host.run(daemon_cmd, ignore_status=True, timeout=timeout)
jadmanski91d56a92009-04-01 15:20:40 +0000505 disconnect_warnings = []
jadmanski6dadd832009-02-05 23:39:27 +0000506 while True:
507 monitor_cmd = self.get_monitor_cmd(monitor_dir, stdout_read,
508 stderr_read)
509 try:
510 result = self.host.run(monitor_cmd, ignore_status=True,
511 timeout=timeout,
512 stdout_tee=client_log,
513 stderr_tee=stderr_redirector)
514 except error.AutoservRunError, e:
515 result = e.result_obj
516 result.exit_status = None
jadmanski91d56a92009-04-01 15:20:40 +0000517 disconnect_warnings.append(e.description)
518
jadmanski6dadd832009-02-05 23:39:27 +0000519 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000520 "Autotest client was disconnected: %s" % e.description,
521 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000522 except error.AutoservSSHTimeout:
523 result = utils.CmdResult(monitor_cmd, "", "", None, 0)
524 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000525 "Attempt to connect to Autotest client timed out",
526 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000527
528 stdout_read += len(result.stdout)
529 stderr_read += len(self._strip_stderr_prologue(result.stderr))
530
531 if result.exit_status is not None:
532 return result
533 elif not self.host.wait_up(client_disconnect_timeout):
534 raise error.AutoservSSHTimeout(
535 "client was disconnected, reconnect timed out")
536 finally:
537 self.host.job.resultdir = old_resultdir
538
539
540 def execute_section(self, section, timeout, stderr_redirector,
541 client_disconnect_timeout):
showardb18134f2009-03-20 20:52:18 +0000542 logging.info("Executing %s/bin/autotest %s/control phase %d",
543 self.autodir, self.autodir, section)
jadmanski6dadd832009-02-05 23:39:27 +0000544
545 if self.background:
546 result = self._execute_in_background(section, timeout)
547 else:
548 result = self._execute_daemon(section, timeout, stderr_redirector,
549 client_disconnect_timeout)
550
551 last_line = stderr_redirector.last_line
mbligh2bf2db62007-11-27 00:53:18 +0000552
jadmanskib264ed02009-01-12 23:54:27 +0000553 # check if we failed hard enough to warrant an exception
jadmanski0afbb632008-06-06 21:10:57 +0000554 if result.exit_status == 1:
jadmanskib264ed02009-01-12 23:54:27 +0000555 err = error.AutotestRunError("client job was aborted")
556 elif not self.background and not result.stderr:
557 err = error.AutotestRunError(
jadmanskie4130532009-03-17 18:01:28 +0000558 "execute_section %s failed to return anything\n"
559 "stdout:%s\n" % (section, result.stdout))
jadmanskib264ed02009-01-12 23:54:27 +0000560 else:
561 err = None
mbligh0e4613b2007-10-29 16:55:07 +0000562
jadmanskib264ed02009-01-12 23:54:27 +0000563 # log something if the client failed AND never finished logging
564 if err and not self.is_client_job_finished(last_line):
jadmanskia61edad2009-05-21 22:17:49 +0000565 self.log_unexpected_abort(stderr_redirector)
jadmanskib264ed02009-01-12 23:54:27 +0000566
567 if err:
568 raise err
569 else:
570 return stderr_redirector.last_line
jadmanski4600e342008-10-29 22:54:00 +0000571
572
573 def _wait_for_reboot(self):
showardb18134f2009-03-20 20:52:18 +0000574 logging.info("Client is rebooting")
575 logging.info("Waiting for client to halt")
jadmanski4600e342008-10-29 22:54:00 +0000576 if not self.host.wait_down(HALT_TIME):
577 err = "%s failed to shutdown after %d"
578 err %= (self.host.hostname, HALT_TIME)
579 raise error.AutotestRunError(err)
showardb18134f2009-03-20 20:52:18 +0000580 logging.info("Client down, waiting for restart")
jadmanski4600e342008-10-29 22:54:00 +0000581 if not self.host.wait_up(BOOT_TIME):
582 # since reboot failed
583 # hardreset the machine once if possible
584 # before failing this control file
585 warning = "%s did not come back up, hard resetting"
586 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000587 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000588 try:
589 self.host.hardreset(wait=False)
mblighd99d3b272008-12-22 14:41:27 +0000590 except (AttributeError, error.AutoservUnsupportedError):
jadmanski4600e342008-10-29 22:54:00 +0000591 warning = "Hard reset unsupported on %s"
592 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000593 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000594 raise error.AutotestRunError("%s failed to boot after %ds" %
595 (self.host.hostname, BOOT_TIME))
596 self.host.reboot_followup()
mblighdc735a22007-08-02 16:54:37 +0000597
598
jadmanski6bb32d72009-03-19 20:25:24 +0000599 def _process_client_state_file(self):
600 state_file = os.path.basename(self.remote_control_file) + ".state"
601 state_path = os.path.join(self.results_dir, state_file)
602 try:
603 state_dict = pickle.load(open(state_path))
604 except Exception, e:
605 msg = "Ignoring error while loading client job state file: %s" % e
showard372ce3e2009-04-07 18:12:03 +0000606 logging.warning(msg)
jadmanski6bb32d72009-03-19 20:25:24 +0000607 state_dict = {}
608
609 # clear out the state file
610 # TODO: stash the file away somewhere useful instead
611 try:
612 os.remove(state_path)
613 except Exception:
614 pass
615
mblighe9633f52009-07-02 19:01:09 +0000616 logging.debug("Persistent state variables pulled back from %s: %s",
617 self.host.hostname, state_dict)
jadmanski6bb32d72009-03-19 20:25:24 +0000618
619 if "__run_test_cleanup" in state_dict:
620 if state_dict["__run_test_cleanup"]:
621 self.host.job.enable_test_cleanup()
622 else:
623 self.host.job.disable_test_cleanup()
624
625 if "__last_boot_tag" in state_dict:
626 self.host.job.last_boot_tag = state_dict["__last_boot_tag"]
627
628 if "__sysinfo" in state_dict:
629 self.host.job.sysinfo.deserialize(state_dict["__sysinfo"])
630
631
jadmanski6dadd832009-02-05 23:39:27 +0000632 def execute_control(self, timeout=None, client_disconnect_timeout=None):
jadmanski6bb32d72009-03-19 20:25:24 +0000633 if not self.background:
634 collector = log_collector(self.host, self.tag, self.results_dir)
635 hostname = self.host.hostname
636 remote_results = collector.client_results_dir
637 local_results = collector.server_results_dir
638 self.host.job.add_client_log(hostname, remote_results,
639 local_results)
640
jadmanski0afbb632008-06-06 21:10:57 +0000641 section = 0
jadmanski4600e342008-10-29 22:54:00 +0000642 start_time = time.time()
643
jadmanski043e1132008-11-19 17:10:32 +0000644 logger = client_logger(self.host, self.tag, self.results_dir)
jadmanski4600e342008-10-29 22:54:00 +0000645 try:
646 while not timeout or time.time() < start_time + timeout:
647 if timeout:
648 section_timeout = start_time + timeout - time.time()
649 else:
650 section_timeout = None
651 last = self.execute_section(section, section_timeout,
jadmanski6dadd832009-02-05 23:39:27 +0000652 logger, client_disconnect_timeout)
mblighb3c0c912008-11-27 00:32:45 +0000653 if self.background:
654 return
jadmanski4600e342008-10-29 22:54:00 +0000655 section += 1
jadmanskib264ed02009-01-12 23:54:27 +0000656 if self.is_client_job_finished(last):
showardb18134f2009-03-20 20:52:18 +0000657 logging.info("Client complete")
jadmanski4600e342008-10-29 22:54:00 +0000658 return
jadmanskib264ed02009-01-12 23:54:27 +0000659 elif self.is_client_job_rebooting(last):
jadmanski79ab9282008-11-11 17:53:12 +0000660 try:
661 self._wait_for_reboot()
662 except error.AutotestRunError, e:
jadmanski043e1132008-11-19 17:10:32 +0000663 self.host.job.record("ABORT", None, "reboot", str(e))
664 self.host.job.record("END ABORT", None, None, str(e))
jadmanski79ab9282008-11-11 17:53:12 +0000665 raise
jadmanski4600e342008-10-29 22:54:00 +0000666 continue
667
668 # if we reach here, something unexpected happened
jadmanskia61edad2009-05-21 22:17:49 +0000669 self.log_unexpected_abort(logger)
jadmanski4600e342008-10-29 22:54:00 +0000670
671 # give the client machine a chance to recover from a crash
672 self.host.wait_up(CRASH_RECOVERY_TIME)
673 msg = ("Aborting - unexpected final status message from "
674 "client: %s\n") % last
675 raise error.AutotestRunError(msg)
676 finally:
jadmanski043e1132008-11-19 17:10:32 +0000677 logger.close()
jadmanski6bb32d72009-03-19 20:25:24 +0000678 if not self.background:
679 collector.collect_client_job_results()
680 self._process_client_state_file()
681 self.host.job.remove_client_log(hostname, remote_results,
682 local_results)
mblighdcd57a82007-07-11 23:06:47 +0000683
jadmanski0afbb632008-06-06 21:10:57 +0000684 # should only get here if we timed out
685 assert timeout
686 raise error.AutotestTimeoutError()
mbligh0e4613b2007-10-29 16:55:07 +0000687
mblighdcd57a82007-07-11 23:06:47 +0000688
689def _get_autodir(host):
mbligh3c7a1502008-07-24 18:08:47 +0000690 autodir = host.get_autodir()
691 if autodir:
jadmanski8297cf52009-05-21 22:23:02 +0000692 logging.debug('Using existing host autodir: %s', autodir)
mbligh3c7a1502008-07-24 18:08:47 +0000693 return autodir
mbligh483d1da2009-08-24 22:07:14 +0000694 for path in CLIENT_AUTODIR_PATHS:
jadmanski0afbb632008-06-06 21:10:57 +0000695 try:
mbligh483d1da2009-08-24 22:07:14 +0000696 host.run('test -d %s' % utils.sh_escape(path))
jadmanski8297cf52009-05-21 22:23:02 +0000697 logging.debug('Found autodir at %s', path)
jadmanski0afbb632008-06-06 21:10:57 +0000698 return path
699 except error.AutoservRunError:
mbligh483d1da2009-08-24 22:07:14 +0000700 logging.debug('%s does not exist on %s', path, host.hostname)
jadmanski8297cf52009-05-21 22:23:02 +0000701 raise error.AutotestRunError('Cannot figure out autotest directory')
mblighd8b39252008-03-20 21:15:03 +0000702
703
jadmanski043e1132008-11-19 17:10:32 +0000704class log_collector(object):
705 def __init__(self, host, client_tag, results_dir):
706 self.host = host
707 if not client_tag:
708 client_tag = "default"
709 self.client_results_dir = os.path.join(host.get_autodir(), "results",
710 client_tag)
711 self.server_results_dir = results_dir
712
713
714 def collect_client_job_results(self):
715 """ A method that collects all the current results of a running
716 client job into the results dir. By default does nothing as no
717 client job is running, but when running a client job you can override
718 this with something that will actually do something. """
719
720 # make an effort to wait for the machine to come up
721 try:
722 self.host.wait_up(timeout=30)
723 except error.AutoservError:
724 # don't worry about any errors, we'll try and
725 # get the results anyway
726 pass
727
jadmanski043e1132008-11-19 17:10:32 +0000728 # Copy all dirs in default to results_dir
729 try:
jadmanski043e1132008-11-19 17:10:32 +0000730 self.host.get_file(self.client_results_dir + '/',
mbligh45561782009-05-11 21:14:34 +0000731 self.server_results_dir, preserve_symlinks=True)
jadmanski043e1132008-11-19 17:10:32 +0000732 except Exception:
733 # well, don't stop running just because we couldn't get logs
showardb18134f2009-03-20 20:52:18 +0000734 e_msg = "Unexpected error copying test result logs, continuing ..."
735 logging.error(e_msg)
jadmanski043e1132008-11-19 17:10:32 +0000736 traceback.print_exc(file=sys.stdout)
737
738
jadmanski043e1132008-11-19 17:10:32 +0000739# a file-like object for catching stderr from an autotest client and
740# extracting status logs from it
741class client_logger(object):
742 """Partial file object to write to both stdout and
743 the status log file. We only implement those methods
744 utils.run() actually calls.
745
746 Note that this class is fairly closely coupled with server_job, as it
747 uses special job._ methods to actually carry out the loggging.
748 """
749 status_parser = re.compile(r"^AUTOTEST_STATUS:([^:]*):(.*)$")
750 test_complete_parser = re.compile(r"^AUTOTEST_TEST_COMPLETE:(.*)$")
jadmanskib1a51132009-08-07 16:45:50 +0000751 fetch_package_parser = re.compile(
752 r"^AUTOTEST_FETCH_PACKAGE:([^:]*):([^:]*):(.*)$")
jadmanski043e1132008-11-19 17:10:32 +0000753 extract_indent = re.compile(r"^(\t*).*$")
jadmanskiefe4ebf2009-05-21 22:12:30 +0000754 extract_timestamp = re.compile(r".*\ttimestamp=(\d+)\t.*$")
jadmanski043e1132008-11-19 17:10:32 +0000755
756 def __init__(self, host, tag, server_results_dir):
757 self.host = host
758 self.job = host.job
759 self.log_collector = log_collector(host, tag, server_results_dir)
760 self.leftover = ""
761 self.last_line = ""
jadmanskiefe4ebf2009-05-21 22:12:30 +0000762 self.newest_timestamp = float("-inf")
jadmanski043e1132008-11-19 17:10:32 +0000763 self.logs = {}
jadmanski6dadd832009-02-05 23:39:27 +0000764 self.server_warnings = []
jadmanski043e1132008-11-19 17:10:32 +0000765
766
jadmanskiefe4ebf2009-05-21 22:12:30 +0000767 def _update_timestamp(self, line):
768 match = self.extract_timestamp.search(line)
769 if match:
770 self.newest_timestamp = max(self.newest_timestamp,
771 int(match.group(1)))
772
773
jadmanski043e1132008-11-19 17:10:32 +0000774 def _process_log_dict(self, log_dict):
775 log_list = log_dict.pop("logs", [])
776 for key in sorted(log_dict.iterkeys()):
777 log_list += self._process_log_dict(log_dict.pop(key))
778 return log_list
779
780
781 def _process_logs(self):
782 """Go through the accumulated logs in self.log and print them
783 out to stdout and the status log. Note that this processes
784 logs in an ordering where:
785
786 1) logs to different tags are never interleaved
787 2) logs to x.y come before logs to x.y.z for all z
788 3) logs to x.y come before x.z whenever y < z
789
790 Note that this will in general not be the same as the
791 chronological ordering of the logs. However, if a chronological
792 ordering is desired that one can be reconstructed from the
793 status log by looking at timestamp lines."""
794 log_list = self._process_log_dict(self.logs)
795 for line in log_list:
796 self.job._record_prerendered(line + '\n')
797 if log_list:
798 self.last_line = log_list[-1]
799
800
801 def _process_quoted_line(self, tag, line):
802 """Process a line quoted with an AUTOTEST_STATUS flag. If the
803 tag is blank then we want to push out all the data we've been
804 building up in self.logs, and then the newest line. If the
805 tag is not blank, then push the line into the logs for handling
806 later."""
showardb18134f2009-03-20 20:52:18 +0000807 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +0000808 if tag == "":
809 self._process_logs()
810 self.job._record_prerendered(line + '\n')
811 self.last_line = line
812 else:
813 tag_parts = [int(x) for x in tag.split(".")]
814 log_dict = self.logs
815 for part in tag_parts:
816 log_dict = log_dict.setdefault(part, {})
817 log_list = log_dict.setdefault("logs", [])
818 log_list.append(line)
819
820
jadmanskif37df842009-02-11 00:03:26 +0000821 def _process_info_line(self, line):
822 """Check if line is an INFO line, and if it is, interpret any control
823 messages (e.g. enabling/disabling warnings) that it may contain."""
824 match = re.search(r"^\t*INFO\t----\t----(.*)\t[^\t]*$", line)
825 if not match:
826 return # not an INFO line
827 for field in match.group(1).split('\t'):
828 if field.startswith("warnings.enable="):
jadmanski16a7ff72009-04-01 18:19:53 +0000829 func = self.job.warning_manager.enable_warnings
jadmanskif37df842009-02-11 00:03:26 +0000830 elif field.startswith("warnings.disable="):
jadmanski16a7ff72009-04-01 18:19:53 +0000831 func = self.job.warning_manager.disable_warnings
jadmanskif37df842009-02-11 00:03:26 +0000832 else:
833 continue
834 warning_type = field.split("=", 1)[1]
jadmanski16a7ff72009-04-01 18:19:53 +0000835 func(warning_type)
jadmanskif37df842009-02-11 00:03:26 +0000836
837
jadmanski043e1132008-11-19 17:10:32 +0000838 def _process_line(self, line):
839 """Write out a line of data to the appropriate stream. Status
840 lines sent by autotest will be prepended with
841 "AUTOTEST_STATUS", and all other lines are ssh error
842 messages."""
843 status_match = self.status_parser.search(line)
844 test_complete_match = self.test_complete_parser.search(line)
jadmanskib1a51132009-08-07 16:45:50 +0000845 fetch_package_match = self.fetch_package_parser.search(line)
jadmanski043e1132008-11-19 17:10:32 +0000846 if status_match:
847 tag, line = status_match.groups()
jadmanskif37df842009-02-11 00:03:26 +0000848 self._process_info_line(line)
jadmanski043e1132008-11-19 17:10:32 +0000849 self._process_quoted_line(tag, line)
850 elif test_complete_match:
jadmanskifcc0d5d2009-02-12 21:52:54 +0000851 self._process_logs()
jadmanski043e1132008-11-19 17:10:32 +0000852 fifo_path, = test_complete_match.groups()
853 self.log_collector.collect_client_job_results()
854 self.host.run("echo A > %s" % fifo_path)
jadmanskib1a51132009-08-07 16:45:50 +0000855 elif fetch_package_match:
856 pkg_name, dest_path, fifo_path = fetch_package_match.groups()
jadmanskiede7e242009-08-10 15:43:33 +0000857 serve_packages = global_config.global_config.get_config_value(
858 "PACKAGES", "serve_packages_from_autoserv", type=bool)
859 if serve_packages and pkg_name.endswith(".tar.bz2"):
860 try:
861 self._send_tarball(pkg_name, dest_path)
862 except Exception:
863 msg = "Package tarball creation failed, continuing anyway"
864 logging.exception(msg)
jadmanskib1a51132009-08-07 16:45:50 +0000865 self.host.run("echo B > %s" % fifo_path)
jadmanski043e1132008-11-19 17:10:32 +0000866 else:
showardb18134f2009-03-20 20:52:18 +0000867 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +0000868
869
jadmanskiede7e242009-08-10 15:43:33 +0000870 def _send_tarball(self, pkg_name, remote_dest):
871 name, pkg_type = self.job.pkgmgr.parse_tarball_name(pkg_name)
872 src_dirs = []
873 if pkg_type == 'test':
874 src_dirs += [os.path.join(self.job.clientdir, 'site_tests', name),
875 os.path.join(self.job.clientdir, 'tests', name)]
876 elif pkg_type == 'profiler':
877 src_dirs += [os.path.join(self.job.clientdir, 'profilers', name)]
878 elif pkg_type == 'dep':
879 src_dirs += [os.path.join(self.job.clientdir, 'deps', name)]
880 elif pkg_type == 'client':
881 return # you must already have a client to hit this anyway
882 else:
883 return # no other types are supported
884
885 # iterate over src_dirs until we find one that exists, then tar it
886 for src_dir in src_dirs:
887 if os.path.exists(src_dir):
888 try:
889 logging.info('Bundling %s into %s', src_dir, pkg_name)
890 temp_dir = autotemp.tempdir(unique_id='autoserv-packager',
891 dir=self.job.tmpdir)
892 tarball_path = self.job.pkgmgr.tar_package(
mblighbccad482009-08-24 22:08:31 +0000893 pkg_name, src_dir, temp_dir.name, " .")
jadmanskiede7e242009-08-10 15:43:33 +0000894 self.host.send_file(tarball_path, remote_dest)
895 finally:
896 temp_dir.clean()
897 return
898
899
jadmanski043e1132008-11-19 17:10:32 +0000900 def _format_warnings(self, last_line, warnings):
901 # use the indentation of whatever the last log line was
902 indent = self.extract_indent.match(last_line).group(1)
903 # if the last line starts a new group, add an extra indent
904 if last_line.lstrip('\t').startswith("START\t"):
905 indent += '\t'
906 return [self.job._render_record("WARN", None, None, msg,
907 timestamp, indent).rstrip('\n')
908 for timestamp, msg in warnings]
909
910
911 def _process_warnings(self, last_line, log_dict, warnings):
912 if log_dict.keys() in ([], ["logs"]):
913 # there are no sub-jobs, just append the warnings here
914 warnings = self._format_warnings(last_line, warnings)
915 log_list = log_dict.setdefault("logs", [])
916 log_list += warnings
917 for warning in warnings:
918 sys.stdout.write(warning + '\n')
919 else:
920 # there are sub-jobs, so put the warnings in there
921 log_list = log_dict.get("logs", [])
922 if log_list:
923 last_line = log_list[-1]
924 for key in sorted(log_dict.iterkeys()):
925 if key != "logs":
926 self._process_warnings(last_line,
927 log_dict[key],
928 warnings)
929
jadmanskif37df842009-02-11 00:03:26 +0000930
jadmanski91d56a92009-04-01 15:20:40 +0000931 def log_warning(self, msg, warning_type):
jadmanski6dadd832009-02-05 23:39:27 +0000932 """Injects a WARN message into the current status logging stream."""
jadmanski91d56a92009-04-01 15:20:40 +0000933 timestamp = int(time.time())
934 if self.job.warning_manager.is_valid(timestamp, warning_type):
935 self.server_warnings.append((timestamp, msg))
jadmanski6dadd832009-02-05 23:39:27 +0000936
jadmanski043e1132008-11-19 17:10:32 +0000937
938 def write(self, data):
939 # first check for any new console warnings
jadmanski6dadd832009-02-05 23:39:27 +0000940 warnings = self.job._read_warnings() + self.server_warnings
941 warnings.sort() # sort into timestamp order
jadmanski043e1132008-11-19 17:10:32 +0000942 # now process the newest data written out
943 data = self.leftover + data
944 lines = data.split("\n")
945 # process every line but the last one
946 for line in lines[:-1]:
jadmanskiefe4ebf2009-05-21 22:12:30 +0000947 self._update_timestamp(line)
948 # output any warnings between now and the next status line
949 old_warnings = [(timestamp, msg) for timestamp, msg in warnings
950 if timestamp < self.newest_timestamp]
951 self._process_warnings(self.last_line, self.logs, warnings)
952 del warnings[:len(old_warnings)]
jadmanski043e1132008-11-19 17:10:32 +0000953 self._process_line(line)
jadmanskiefe4ebf2009-05-21 22:12:30 +0000954 # save off any warnings not yet logged for later processing
955 self.server_warnings = warnings
jadmanski043e1132008-11-19 17:10:32 +0000956 # save the last line for later processing
957 # since we may not have the whole line yet
958 self.leftover = lines[-1]
959
960
961 def flush(self):
962 sys.stdout.flush()
963
964
jadmanskia61edad2009-05-21 22:17:49 +0000965 def flush_all_buffers(self):
jadmanski043e1132008-11-19 17:10:32 +0000966 if self.leftover:
967 self._process_line(self.leftover)
jadmanskia61edad2009-05-21 22:17:49 +0000968 self.leftover = ""
jadmanskiefe4ebf2009-05-21 22:12:30 +0000969 self._process_warnings(self.last_line, self.logs, self.server_warnings)
jadmanski043e1132008-11-19 17:10:32 +0000970 self._process_logs()
971 self.flush()
972
973
jadmanskia61edad2009-05-21 22:17:49 +0000974 def close(self):
975 self.flush_all_buffers()
976
977
mbligha7007722009-01-13 00:37:11 +0000978SiteAutotest = client_utils.import_site_class(
979 __file__, "autotest_lib.server.site_autotest", "SiteAutotest",
980 BaseAutotest)
mblighd8b39252008-03-20 21:15:03 +0000981
982class Autotest(SiteAutotest):
jadmanski0afbb632008-06-06 21:10:57 +0000983 pass