blob: fd5db098d21deeaf9b278a7e48cc052a333e10a1 [file] [log] [blame]
mblighdcd57a82007-07-11 23:06:47 +00001# Copyright 2007 Google Inc. Released under the GPL v2
2
jadmanski4d03cf62010-03-04 18:32:28 +00003import re, os, sys, traceback, subprocess, time, pickle, glob, tempfile
4import logging, getpass
mbligh1f572e52010-04-01 17:15:53 +00005from autotest_lib.server import installable_object, prebuild, utils
jadmanski2a89dac2010-06-11 14:32:58 +00006from autotest_lib.client.common_lib import base_job, 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
mbligh1f572e52010-04-01 17:15:53 +000019get_value = global_config.global_config.get_config_value
20autoserv_prebuild = get_value('AUTOSERV', 'enable_server_prebuild',
21 type=bool, default=False)
22
jadmanski2a89dac2010-06-11 14:32:58 +000023
showardad812bf2009-10-20 23:49:56 +000024class AutodirNotFoundError(Exception):
25 """No Autotest installation could be found."""
26
27
mblighd8b39252008-03-20 21:15:03 +000028class BaseAutotest(installable_object.InstallableObject):
jadmanski0afbb632008-06-06 21:10:57 +000029 """
30 This class represents the Autotest program.
mblighdcd57a82007-07-11 23:06:47 +000031
jadmanski0afbb632008-06-06 21:10:57 +000032 Autotest is used to run tests automatically and collect the results.
33 It also supports profilers.
mblighdcd57a82007-07-11 23:06:47 +000034
jadmanski0afbb632008-06-06 21:10:57 +000035 Implementation details:
36 This is a leaf class in an abstract class hierarchy, it must
37 implement the unimplemented methods in parent classes.
38 """
mbligh119c12a2007-11-12 22:13:44 +000039
jadmanski0afbb632008-06-06 21:10:57 +000040 def __init__(self, host = None):
41 self.host = host
42 self.got = False
43 self.installed = False
44 self.serverdir = utils.get_server_dir()
45 super(BaseAutotest, self).__init__()
mblighc8949b82007-07-23 16:33:58 +000046
mblighdc735a22007-08-02 16:54:37 +000047
jadmanskif22fea82008-11-26 20:57:07 +000048 install_in_tmpdir = False
49 @classmethod
50 def set_install_in_tmpdir(cls, flag):
51 """ Sets a flag that controls whether or not Autotest should by
52 default be installed in a "standard" directory (e.g.
53 /home/autotest, /usr/local/autotest) or a temporary directory. """
54 cls.install_in_tmpdir = flag
55
56
showardad812bf2009-10-20 23:49:56 +000057 @classmethod
58 def get_client_autodir_paths(cls, host):
59 return global_config.global_config.get_config_value(
60 'AUTOSERV', 'client_autodir_paths', type=list)
61
62
63 @classmethod
64 def get_installed_autodir(cls, host):
65 """
66 Find where the Autotest client is installed on the host.
67 @returns an absolute path to an installed Autotest client root.
68 @raises AutodirNotFoundError if no Autotest installation can be found.
69 """
70 autodir = host.get_autodir()
71 if autodir:
72 logging.debug('Using existing host autodir: %s', autodir)
73 return autodir
74
75 for path in Autotest.get_client_autodir_paths(host):
76 try:
77 autotest_binary = os.path.join(path, 'bin', 'autotest')
78 host.run('test -x %s' % utils.sh_escape(autotest_binary))
79 logging.debug('Found existing autodir at %s', path)
80 return path
81 except error.AutoservRunError:
82 logging.debug('%s does not exist on %s', autotest_binary,
83 host.hostname)
84 raise AutodirNotFoundError
85
86
87 @classmethod
88 def get_install_dir(cls, host):
89 """
90 Determines the location where autotest should be installed on
jadmanskif22fea82008-11-26 20:57:07 +000091 host. If self.install_in_tmpdir is set, it will return a unique
showardad812bf2009-10-20 23:49:56 +000092 temporary directory that autotest can be installed in. Otherwise, looks
93 for an existing installation to use; if none is found, looks for a
94 usable directory in the global config client_autodir_paths.
95 """
jadmanskif22fea82008-11-26 20:57:07 +000096 try:
lmr9dcf0832009-12-08 21:28:55 +000097 install_dir = cls.get_installed_autodir(host)
showardad812bf2009-10-20 23:49:56 +000098 except AutodirNotFoundError:
lmr9dcf0832009-12-08 21:28:55 +000099 install_dir = cls._find_installable_dir(host)
100
101 if cls.install_in_tmpdir:
102 return host.get_tmp_dir(parent=install_dir)
103 return install_dir
showardad812bf2009-10-20 23:49:56 +0000104
105
106 @classmethod
107 def _find_installable_dir(cls, host):
108 client_autodir_paths = cls.get_client_autodir_paths(host)
109 for path in client_autodir_paths:
110 try:
111 host.run('mkdir -p %s' % utils.sh_escape(path))
112 return path
113 except error.AutoservRunError:
114 logging.debug('Failed to create %s', path)
115 raise error.AutoservInstallError(
116 'Unable to find a place to install Autotest; tried %s',
117 ', '.join(client_autodir_paths))
jadmanskif22fea82008-11-26 20:57:07 +0000118
119
mbligh1b3b3762008-09-25 02:46:34 +0000120 @log.record
mblighb3c0c912008-11-27 00:32:45 +0000121 def install(self, host=None, autodir=None):
122 self._install(host=host, autodir=autodir)
jadmanski54f90af2008-10-10 16:20:55 +0000123
124
mblighb8aa75b2009-09-18 16:50:37 +0000125 @log.record
126 def install_full_client(self, host=None, autodir=None):
127 self._install(host=host, autodir=autodir, use_autoserv=False,
128 use_packaging=False)
129
130
mblighbccad482009-08-24 22:08:31 +0000131 def install_no_autoserv(self, host=None, autodir=None):
mblighb8aa75b2009-09-18 16:50:37 +0000132 self._install(host=host, autodir=autodir, use_autoserv=False)
mblighbccad482009-08-24 22:08:31 +0000133
134
mblighb8aa75b2009-09-18 16:50:37 +0000135 def _install_using_packaging(self, host, autodir):
136 c = global_config.global_config
jadmanski2315a7e2009-09-18 18:39:37 +0000137 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list,
138 default=[])
139 repos.reverse()
mblighb8aa75b2009-09-18 16:50:37 +0000140 if not repos:
141 raise error.PackageInstallError("No repos to install an "
142 "autotest client from")
143 pkgmgr = packages.PackageManager(autodir, hostname=host.hostname,
144 repo_urls=repos,
145 do_locking=False,
146 run_function=host.run,
147 run_function_dargs=dict(timeout=600))
148 # The packages dir is used to store all the packages that
149 # are fetched on that client. (for the tests,deps etc.
150 # too apart from the client)
151 pkg_dir = os.path.join(autodir, 'packages')
152 # clean up the autodir except for the packages directory
153 host.run('cd %s && ls | grep -v "^packages$"'
154 ' | xargs rm -rf && rm -rf .[^.]*' % autodir)
155 pkgmgr.install_pkg('autotest', 'client', pkg_dir, autodir,
156 preserve_install_dir=True)
157 self.installed = True
158
159
160 def _install_using_send_file(self, host, autodir):
161 dirs_to_exclude = set(["tests", "site_tests", "deps", "profilers"])
162 light_files = [os.path.join(self.source_material, f)
163 for f in os.listdir(self.source_material)
164 if f not in dirs_to_exclude]
165 host.send_file(light_files, autodir, delete_dest=True)
166
167 # create empty dirs for all the stuff we excluded
168 commands = []
169 for path in dirs_to_exclude:
170 abs_path = os.path.join(autodir, path)
171 abs_path = utils.sh_escape(abs_path)
172 commands.append("mkdir -p '%s'" % abs_path)
173 commands.append("touch '%s'/__init__.py" % abs_path)
174 host.run(';'.join(commands))
175
176
177 def _install(self, host=None, autodir=None, use_autoserv=True,
178 use_packaging=True):
jadmanski0afbb632008-06-06 21:10:57 +0000179 """
180 Install autotest. If get() was not called previously, an
181 attempt will be made to install from the autotest svn
182 repository.
mbligh9a3f5e52008-05-28 21:21:43 +0000183
mblighbccad482009-08-24 22:08:31 +0000184 @param host A Host instance on which autotest will be installed
185 @param autodir Location on the remote host to install to
mblighb8aa75b2009-09-18 16:50:37 +0000186 @param use_autoserv Enable install modes that depend on the client
mblighbccad482009-08-24 22:08:31 +0000187 running with the autoserv harness
mblighb8aa75b2009-09-18 16:50:37 +0000188 @param use_packaging Enable install modes that use the packaging system
mbligh9a3f5e52008-05-28 21:21:43 +0000189
mblighbccad482009-08-24 22:08:31 +0000190 @exception AutoservError if a tarball was not specified and
191 the target host does not have svn installed in its path
192 """
jadmanski0afbb632008-06-06 21:10:57 +0000193 if not host:
194 host = self.host
195 if not self.got:
196 self.get()
197 host.wait_up(timeout=30)
198 host.setup()
showardb18134f2009-03-20 20:52:18 +0000199 logging.info("Installing autotest on %s", host.hostname)
mbligh40f122a2007-11-03 23:08:46 +0000200
jadmanski54f90af2008-10-10 16:20:55 +0000201 # set up the autotest directory on the remote machine
202 if not autodir:
showardad812bf2009-10-20 23:49:56 +0000203 autodir = self.get_install_dir(host)
204 logging.info('Using installation dir %s', autodir)
mbligh0562e652008-08-20 20:11:45 +0000205 host.set_autodir(autodir)
jadmanski3c236942009-03-04 17:51:26 +0000206 host.run('mkdir -p %s' % utils.sh_escape(autodir))
mbligh40f122a2007-11-03 23:08:46 +0000207
jadmanski1c3c07b2009-03-03 23:29:36 +0000208 # make sure there are no files in $AUTODIR/results
209 results_path = os.path.join(autodir, 'results')
jadmanski3c236942009-03-04 17:51:26 +0000210 host.run('rm -rf %s/*' % utils.sh_escape(results_path),
jadmanski1c3c07b2009-03-03 23:29:36 +0000211 ignore_status=True)
212
mblighc5ddfd12008-08-04 17:15:00 +0000213 # Fetch the autotest client from the nearest repository
mblighb8aa75b2009-09-18 16:50:37 +0000214 if use_packaging:
215 try:
216 self._install_using_packaging(host, autodir)
217 return
218 except global_config.ConfigError, e:
219 logging.info("Could not install autotest using the packaging "
220 "system: %s", e)
221 except (error.PackageInstallError, error.AutoservRunError), e:
222 logging.error("Could not install autotest from repos")
mblighc5ddfd12008-08-04 17:15:00 +0000223
jadmanski0afbb632008-06-06 21:10:57 +0000224 # try to install from file or directory
225 if self.source_material:
226 if os.path.isdir(self.source_material):
jadmanski27b52912009-08-14 17:09:15 +0000227 c = global_config.global_config
228 supports_autoserv_packaging = c.get_config_value(
229 "PACKAGES", "serve_packages_from_autoserv", type=bool)
jadmanski0afbb632008-06-06 21:10:57 +0000230 # Copy autotest recursively
mblighb8aa75b2009-09-18 16:50:37 +0000231 if supports_autoserv_packaging and use_autoserv:
232 self._install_using_send_file(host, autodir)
jadmanski54f90af2008-10-10 16:20:55 +0000233 else:
mbligh89e258d2008-10-24 13:58:08 +0000234 host.send_file(self.source_material, autodir,
235 delete_dest=True)
jadmanski0afbb632008-06-06 21:10:57 +0000236 else:
237 # Copy autotest via tarball
238 e_msg = 'Installation method not yet implemented!'
239 raise NotImplementedError(e_msg)
showardb18134f2009-03-20 20:52:18 +0000240 logging.info("Installation of autotest completed")
jadmanski0afbb632008-06-06 21:10:57 +0000241 self.installed = True
242 return
mbligh91334902007-09-28 01:47:59 +0000243
jadmanski0afbb632008-06-06 21:10:57 +0000244 # if that fails try to install using svn
245 if utils.run('which svn').exit_status:
mbligh78bf5352008-07-11 20:27:36 +0000246 raise error.AutoservError('svn not found on target machine: %s'
247 % host.name)
jadmanski0afbb632008-06-06 21:10:57 +0000248 try:
mbligh78bf5352008-07-11 20:27:36 +0000249 host.run('svn checkout %s %s' % (AUTOTEST_SVN, autodir))
jadmanski0afbb632008-06-06 21:10:57 +0000250 except error.AutoservRunError, e:
mbligh78bf5352008-07-11 20:27:36 +0000251 host.run('svn checkout %s %s' % (AUTOTEST_HTTP, autodir))
showardb18134f2009-03-20 20:52:18 +0000252 logging.info("Installation of autotest completed")
jadmanski0afbb632008-06-06 21:10:57 +0000253 self.installed = True
mbligh91334902007-09-28 01:47:59 +0000254
255
jadmanski7c7aff32009-03-25 22:43:07 +0000256 def uninstall(self, host=None):
257 """
258 Uninstall (i.e. delete) autotest. Removes the autotest client install
259 from the specified host.
260
261 @params host a Host instance from which the client will be removed
262 """
263 if not self.installed:
264 return
265 if not host:
266 host = self.host
267 autodir = host.get_autodir()
268 if not autodir:
269 return
270
271 # perform the actual uninstall
272 host.run("rm -rf %s" % utils.sh_escape(autodir), ignore_status=True)
273 host.set_autodir(None)
274 self.installed = False
275
276
jadmanski0afbb632008-06-06 21:10:57 +0000277 def get(self, location = None):
278 if not location:
279 location = os.path.join(self.serverdir, '../client')
280 location = os.path.abspath(location)
281 # If there's stuff run on our client directory already, it
282 # can cause problems. Try giving it a quick clean first.
283 cwd = os.getcwd()
284 os.chdir(location)
showard4b976072009-10-20 23:50:08 +0000285 try:
286 utils.system('tools/make_clean', ignore_status=True)
287 finally:
288 os.chdir(cwd)
jadmanski0afbb632008-06-06 21:10:57 +0000289 super(BaseAutotest, self).get(location)
290 self.got = True
mblighdcd57a82007-07-11 23:06:47 +0000291
292
mblighe7d9c602009-07-02 19:02:33 +0000293 def run(self, control_file, results_dir='.', host=None, timeout=None,
294 tag=None, parallel_flag=False, background=False,
mbligh9de6ed72010-01-11 19:01:10 +0000295 client_disconnect_timeout=1800):
jadmanski0afbb632008-06-06 21:10:57 +0000296 """
297 Run an autotest job on the remote machine.
mbligh9a3f5e52008-05-28 21:21:43 +0000298
mblighe7d9c602009-07-02 19:02:33 +0000299 @param control_file: An open file-like-obj of the control file.
300 @param results_dir: A str path where the results should be stored
301 on the local filesystem.
302 @param host: A Host instance on which the control file should
303 be run.
304 @param timeout: Maximum number of seconds to wait for the run or None.
305 @param tag: Tag name for the client side instance of autotest.
306 @param parallel_flag: Flag set when multiple jobs are run at the
307 same time.
308 @param background: Indicates that the client should be launched as
309 a background job; the code calling run will be responsible
310 for monitoring the client and collecting the results.
311 @param client_disconnect_timeout: Seconds to wait for the remote host
312 to come back after a reboot. [default: 30 minutes]
mblighe7d9c602009-07-02 19:02:33 +0000313
314 @raises AutotestRunError: If there is a problem executing
315 the control file.
jadmanski0afbb632008-06-06 21:10:57 +0000316 """
317 host = self._get_host_and_setup(host)
318 results_dir = os.path.abspath(results_dir)
mblighc1cbc992008-05-27 20:01:45 +0000319
jadmanski0afbb632008-06-06 21:10:57 +0000320 if tag:
321 results_dir = os.path.join(results_dir, tag)
mblighc1cbc992008-05-27 20:01:45 +0000322
mblighb3c0c912008-11-27 00:32:45 +0000323 atrun = _Run(host, results_dir, tag, parallel_flag, background)
jadmanski6dadd832009-02-05 23:39:27 +0000324 self._do_run(control_file, results_dir, host, atrun, timeout,
mbligh9de6ed72010-01-11 19:01:10 +0000325 client_disconnect_timeout)
mblighd8b39252008-03-20 21:15:03 +0000326
327
jadmanski0afbb632008-06-06 21:10:57 +0000328 def _get_host_and_setup(self, host):
329 if not host:
330 host = self.host
331 if not self.installed:
332 self.install(host)
mbligh91334902007-09-28 01:47:59 +0000333
jadmanski0afbb632008-06-06 21:10:57 +0000334 host.wait_up(timeout=30)
335 return host
mblighd8b39252008-03-20 21:15:03 +0000336
337
jadmanski6dadd832009-02-05 23:39:27 +0000338 def _do_run(self, control_file, results_dir, host, atrun, timeout,
mbligh9de6ed72010-01-11 19:01:10 +0000339 client_disconnect_timeout):
jadmanski0afbb632008-06-06 21:10:57 +0000340 try:
341 atrun.verify_machine()
342 except:
showardb18134f2009-03-20 20:52:18 +0000343 logging.error("Verify failed on %s. Reinstalling autotest",
344 host.hostname)
jadmanski0afbb632008-06-06 21:10:57 +0000345 self.install(host)
346 atrun.verify_machine()
347 debug = os.path.join(results_dir, 'debug')
348 try:
349 os.makedirs(debug)
mbligh09108442008-10-15 16:27:38 +0000350 except Exception:
jadmanski0afbb632008-06-06 21:10:57 +0000351 pass
mbligh9a3f5e52008-05-28 21:21:43 +0000352
mbligh09108442008-10-15 16:27:38 +0000353 delete_file_list = [atrun.remote_control_file,
354 atrun.remote_control_file + '.state',
355 atrun.manual_control_file,
356 atrun.manual_control_file + '.state']
357 cmd = ';'.join('rm -f ' + control for control in delete_file_list)
358 host.run(cmd, ignore_status=True)
mbligh9a3f5e52008-05-28 21:21:43 +0000359
jadmanski0afbb632008-06-06 21:10:57 +0000360 tmppath = utils.get(control_file)
mblighc5ddfd12008-08-04 17:15:00 +0000361
jadmanskicb0e1612009-02-27 18:03:10 +0000362 # build up the initialization prologue for the control file
363 prologue_lines = []
jadmanski23afbec2008-09-17 18:12:07 +0000364
mbligh2f076832010-03-30 17:08:20 +0000365 # Add the additional user arguments
jadmanski808f4b12010-04-09 22:30:31 +0000366 prologue_lines.append("args = %r\n" % self.job.args)
mbligh2f076832010-03-30 17:08:20 +0000367
mbligh09108442008-10-15 16:27:38 +0000368 # If the packaging system is being used, add the repository list.
mblighddc9a402010-01-15 20:33:34 +0000369 repos = None
mblighc5ddfd12008-08-04 17:15:00 +0000370 try:
mblighc5ddfd12008-08-04 17:15:00 +0000371 c = global_config.global_config
372 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list)
jadmanskiede7e242009-08-10 15:43:33 +0000373 repos.reverse() # high priority packages should be added last
mbligh76d19f72008-10-15 16:24:43 +0000374 pkgmgr = packages.PackageManager('autotest', hostname=host.hostname,
375 repo_urls=repos)
jadmanskib1a51132009-08-07 16:45:50 +0000376 prologue_lines.append('job.add_repository(%s)\n' % repos)
mblighc5ddfd12008-08-04 17:15:00 +0000377 except global_config.ConfigError, e:
mblighddc9a402010-01-15 20:33:34 +0000378 # If repos is defined packaging is enabled so log the error
379 if repos:
380 logging.error(e)
mblighc5ddfd12008-08-04 17:15:00 +0000381
jadmanskie2eef7b2009-03-03 23:55:13 +0000382 # on full-size installs, turn on any profilers the server is using
jadmanski27b52912009-08-14 17:09:15 +0000383 if not atrun.background:
jadmanskie2eef7b2009-03-03 23:55:13 +0000384 running_profilers = host.job.profilers.add_log.iteritems()
385 for profiler, (args, dargs) in running_profilers:
386 call_args = [repr(profiler)]
387 call_args += [repr(arg) for arg in args]
388 call_args += ["%s=%r" % item for item in dargs.iteritems()]
389 prologue_lines.append("job.profilers.add(%s)\n"
390 % ", ".join(call_args))
391 cfile = "".join(prologue_lines)
392
mbligh09108442008-10-15 16:27:38 +0000393 cfile += open(tmppath).read()
394 open(tmppath, "w").write(cfile)
mblighc5ddfd12008-08-04 17:15:00 +0000395
jadmanskic09fc152008-10-15 17:56:59 +0000396 # Create and copy state file to remote_control_file + '.state'
mblighfc3da5b2010-01-06 18:37:22 +0000397 state_file = host.job.preprocess_client_state()
mblighfbf73ae2009-12-19 05:22:42 +0000398 host.send_file(state_file, atrun.remote_control_file + '.init.state')
jadmanskic09fc152008-10-15 17:56:59 +0000399 os.remove(state_file)
400
mblighc5ddfd12008-08-04 17:15:00 +0000401 # Copy control_file to remote_control_file on the host
jadmanski0afbb632008-06-06 21:10:57 +0000402 host.send_file(tmppath, atrun.remote_control_file)
403 if os.path.abspath(tmppath) != os.path.abspath(control_file):
404 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000405
jadmanski6bb32d72009-03-19 20:25:24 +0000406 atrun.execute_control(
jadmanski6dadd832009-02-05 23:39:27 +0000407 timeout=timeout,
408 client_disconnect_timeout=client_disconnect_timeout)
jadmanski23afbec2008-09-17 18:12:07 +0000409
410
jadmanski0afbb632008-06-06 21:10:57 +0000411 def run_timed_test(self, test_name, results_dir='.', host=None,
jadmanskic98c4702009-01-05 15:50:06 +0000412 timeout=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000413 """
414 Assemble a tiny little control file to just run one test,
415 and run it as an autotest client-side test
416 """
417 if not host:
418 host = self.host
419 if not self.installed:
420 self.install(host)
421 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
422 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
423 control = "job.run_test(%s)\n" % cmd
jadmanskic98c4702009-01-05 15:50:06 +0000424 self.run(control, results_dir, host, timeout=timeout)
mbligh0e4613b2007-10-29 16:55:07 +0000425
426
jadmanskic98c4702009-01-05 15:50:06 +0000427 def run_test(self, test_name, results_dir='.', host=None, *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000428 self.run_timed_test(test_name, results_dir, host, timeout=None,
jadmanskic98c4702009-01-05 15:50:06 +0000429 *args, **dargs)
mblighd54832b2007-07-25 16:46:56 +0000430
431
mblighdcd57a82007-07-11 23:06:47 +0000432class _Run(object):
jadmanski0afbb632008-06-06 21:10:57 +0000433 """
434 Represents a run of autotest control file. This class maintains
435 all the state necessary as an autotest control file is executed.
mblighdcd57a82007-07-11 23:06:47 +0000436
jadmanski0afbb632008-06-06 21:10:57 +0000437 It is not intended to be used directly, rather control files
438 should be run using the run method in Autotest.
439 """
mblighb3c0c912008-11-27 00:32:45 +0000440 def __init__(self, host, results_dir, tag, parallel_flag, background):
jadmanski0afbb632008-06-06 21:10:57 +0000441 self.host = host
442 self.results_dir = results_dir
443 self.env = host.env
444 self.tag = tag
445 self.parallel_flag = parallel_flag
mblighb3c0c912008-11-27 00:32:45 +0000446 self.background = background
showardad812bf2009-10-20 23:49:56 +0000447 self.autodir = Autotest.get_installed_autodir(self.host)
mbligh78bf5352008-07-11 20:27:36 +0000448 control = os.path.join(self.autodir, 'control')
jadmanski0afbb632008-06-06 21:10:57 +0000449 if tag:
mbligh78bf5352008-07-11 20:27:36 +0000450 control += '.' + tag
451 self.manual_control_file = control
452 self.remote_control_file = control + '.autoserv'
lmr6d08b3c2009-11-18 19:26:38 +0000453 self.config_file = os.path.join(self.autodir, 'global_config.ini')
mblighdc735a22007-08-02 16:54:37 +0000454
455
jadmanski0afbb632008-06-06 21:10:57 +0000456 def verify_machine(self):
457 binary = os.path.join(self.autodir, 'bin/autotest')
458 try:
459 self.host.run('ls %s > /dev/null 2>&1' % binary)
460 except:
lmrd6d27ed2009-12-08 19:58:33 +0000461 raise error.AutoservInstallError(
462 "Autotest does not appear to be installed")
mblighdc735a22007-08-02 16:54:37 +0000463
jadmanski0afbb632008-06-06 21:10:57 +0000464 if not self.parallel_flag:
465 tmpdir = os.path.join(self.autodir, 'tmp')
466 download = os.path.join(self.autodir, 'tests/download')
467 self.host.run('umount %s' % tmpdir, ignore_status=True)
468 self.host.run('umount %s' % download, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000469
jadmanski6dadd832009-02-05 23:39:27 +0000470
471 def get_base_cmd_args(self, section):
showard234b2962009-07-28 20:02:30 +0000472 args = ['--verbose']
jadmanski0afbb632008-06-06 21:10:57 +0000473 if section > 0:
jadmanski6dadd832009-02-05 23:39:27 +0000474 args.append('-c')
jadmanski0afbb632008-06-06 21:10:57 +0000475 if self.tag:
jadmanski6dadd832009-02-05 23:39:27 +0000476 args.append('-t %s' % self.tag)
jadmanski0afbb632008-06-06 21:10:57 +0000477 if self.host.job.use_external_logging():
jadmanski6dadd832009-02-05 23:39:27 +0000478 args.append('-l')
mblighce955fc2009-08-24 21:59:02 +0000479 if self.host.hostname:
480 args.append('--hostname=%s' % self.host.hostname)
mbligh0d0f67d2009-11-06 03:15:03 +0000481 args.append('--user=%s' % self.host.job.user)
mblighce955fc2009-08-24 21:59:02 +0000482
jadmanski6dadd832009-02-05 23:39:27 +0000483 args.append(self.remote_control_file)
484 return args
485
486
487 def get_background_cmd(self, section):
488 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotest_client')]
489 cmd += self.get_base_cmd_args(section)
490 cmd.append('>/dev/null 2>/dev/null &')
491 return ' '.join(cmd)
492
493
494 def get_daemon_cmd(self, section, monitor_dir):
495 cmd = ['nohup', os.path.join(self.autodir, 'bin/autotestd'),
496 monitor_dir, '-H autoserv']
497 cmd += self.get_base_cmd_args(section)
498 cmd.append('>/dev/null 2>/dev/null </dev/null &')
499 return ' '.join(cmd)
500
501
502 def get_monitor_cmd(self, monitor_dir, stdout_read, stderr_read):
503 cmd = [os.path.join(self.autodir, 'bin', 'autotestd_monitor'),
504 monitor_dir, str(stdout_read), str(stderr_read)]
jadmanski0afbb632008-06-06 21:10:57 +0000505 return ' '.join(cmd)
mblighadf2aab2007-11-29 18:16:43 +0000506
mblighd8b39252008-03-20 21:15:03 +0000507
jadmanski4d03cf62010-03-04 18:32:28 +0000508 def get_client_log(self):
509 """Find what the "next" client.* prefix should be
510
511 @returns A string of the form client.INTEGER that should be prefixed
512 to all client debug log files.
513 """
514 max_digit = -1
515 debug_dir = os.path.join(self.results_dir, 'debug')
516 client_logs = glob.glob(os.path.join(debug_dir, 'client.*.*'))
517 for log in client_logs:
518 _, number, _ = log.split('.', 2)
519 if number.isdigit():
520 max_digit = max(max_digit, int(number))
521 return 'client.%d' % (max_digit + 1)
522
523
524 def copy_client_config_file(self, client_log_prefix=None):
525 """
526 Create and copy the client config file based on the server config.
527
528 @param client_log_prefix: Optional prefix to prepend to log files.
529 """
530 client_config_file = self._create_client_config_file(client_log_prefix)
531 self.host.send_file(client_config_file, self.config_file)
532 os.remove(client_config_file)
533
534
535 def _create_client_config_file(self, client_log_prefix=None):
536 """
537 Create a temporary file with the [CLIENT] section configuration values
538 taken from the server global_config.ini.
539
540 @param client_log_prefix: Optional prefix to prepend to log files.
541
542 @return: Path of the temporary file generated.
543 """
544 config = global_config.global_config.get_section_values('CLIENT')
545 if client_log_prefix:
546 config.set('CLIENT', 'default_logging_name', client_log_prefix)
547 return self._create_aux_file(config.write)
548
549
550 def _create_aux_file(self, func, *args):
551 """
552 Creates a temporary file and writes content to it according to a
553 content creation function. The file object is appended to *args, which
554 is then passed to the content creation function
555
556 @param func: Function that will be used to write content to the
557 temporary file.
558 @param *args: List of parameters that func takes.
559 @return: Path to the temporary file that was created.
560 """
561 fd, path = tempfile.mkstemp(dir=self.host.job.tmpdir)
562 aux_file = os.fdopen(fd, "w")
563 try:
564 list_args = list(args)
565 list_args.append(aux_file)
566 func(*list_args)
567 finally:
568 aux_file.close()
569 return path
mblighd8b39252008-03-20 21:15:03 +0000570
571
jadmanskib264ed02009-01-12 23:54:27 +0000572 @staticmethod
573 def is_client_job_finished(last_line):
574 return bool(re.match(r'^END .*\t----\t----\t.*$', last_line))
575
576
577 @staticmethod
578 def is_client_job_rebooting(last_line):
579 return bool(re.match(r'^\t*GOOD\t----\treboot\.start.*$', last_line))
580
581
jadmanskia61edad2009-05-21 22:17:49 +0000582 def log_unexpected_abort(self, stderr_redirector):
583 stderr_redirector.flush_all_buffers()
jadmanskib264ed02009-01-12 23:54:27 +0000584 msg = "Autotest client terminated unexpectedly"
585 self.host.job.record("END ABORT", None, None, msg)
586
587
jadmanski6dadd832009-02-05 23:39:27 +0000588 def _execute_in_background(self, section, timeout):
589 full_cmd = self.get_background_cmd(section)
590 devnull = open(os.devnull, "w")
mblighd8b39252008-03-20 21:15:03 +0000591
jadmanski4d03cf62010-03-04 18:32:28 +0000592 self.copy_client_config_file(self.get_client_log())
593
mbligh0d0f67d2009-11-06 03:15:03 +0000594 self.host.job.push_execution_context(self.results_dir)
jadmanski0afbb632008-06-06 21:10:57 +0000595 try:
jadmanski0afbb632008-06-06 21:10:57 +0000596 result = self.host.run(full_cmd, ignore_status=True,
597 timeout=timeout,
jadmanski6dadd832009-02-05 23:39:27 +0000598 stdout_tee=devnull,
599 stderr_tee=devnull)
jadmanski0afbb632008-06-06 21:10:57 +0000600 finally:
mbligh0d0f67d2009-11-06 03:15:03 +0000601 self.host.job.pop_execution_context()
jadmanski6dadd832009-02-05 23:39:27 +0000602
603 return result
604
605
606 @staticmethod
607 def _strip_stderr_prologue(stderr):
608 """Strips the 'standard' prologue that get pre-pended to every
609 remote command and returns the text that was actually written to
610 stderr by the remote command."""
611 stderr_lines = stderr.split("\n")[1:]
612 if not stderr_lines:
613 return ""
614 elif stderr_lines[0].startswith("NOTE: autotestd_monitor"):
615 del stderr_lines[0]
616 return "\n".join(stderr_lines)
617
618
619 def _execute_daemon(self, section, timeout, stderr_redirector,
620 client_disconnect_timeout):
621 monitor_dir = self.host.get_tmp_dir()
622 daemon_cmd = self.get_daemon_cmd(section, monitor_dir)
jadmanski4d03cf62010-03-04 18:32:28 +0000623
624 # grab the location for the server-side client log file
625 client_log_prefix = self.get_client_log()
626 client_log_path = os.path.join(self.results_dir, 'debug',
627 client_log_prefix + '.log')
628 client_log = open(client_log_path, 'w', 0)
629 self.copy_client_config_file(client_log_prefix)
jadmanski6dadd832009-02-05 23:39:27 +0000630
631 stdout_read = stderr_read = 0
mbligh0d0f67d2009-11-06 03:15:03 +0000632 self.host.job.push_execution_context(self.results_dir)
jadmanski6dadd832009-02-05 23:39:27 +0000633 try:
634 self.host.run(daemon_cmd, ignore_status=True, timeout=timeout)
jadmanski91d56a92009-04-01 15:20:40 +0000635 disconnect_warnings = []
jadmanski6dadd832009-02-05 23:39:27 +0000636 while True:
637 monitor_cmd = self.get_monitor_cmd(monitor_dir, stdout_read,
638 stderr_read)
639 try:
640 result = self.host.run(monitor_cmd, ignore_status=True,
641 timeout=timeout,
642 stdout_tee=client_log,
643 stderr_tee=stderr_redirector)
644 except error.AutoservRunError, e:
645 result = e.result_obj
646 result.exit_status = None
jadmanski91d56a92009-04-01 15:20:40 +0000647 disconnect_warnings.append(e.description)
648
jadmanski6dadd832009-02-05 23:39:27 +0000649 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000650 "Autotest client was disconnected: %s" % e.description,
651 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000652 except error.AutoservSSHTimeout:
653 result = utils.CmdResult(monitor_cmd, "", "", None, 0)
654 stderr_redirector.log_warning(
jadmanski91d56a92009-04-01 15:20:40 +0000655 "Attempt to connect to Autotest client timed out",
656 "NETWORK")
jadmanski6dadd832009-02-05 23:39:27 +0000657
658 stdout_read += len(result.stdout)
659 stderr_read += len(self._strip_stderr_prologue(result.stderr))
660
661 if result.exit_status is not None:
662 return result
663 elif not self.host.wait_up(client_disconnect_timeout):
664 raise error.AutoservSSHTimeout(
665 "client was disconnected, reconnect timed out")
666 finally:
jadmanski4d03cf62010-03-04 18:32:28 +0000667 client_log.close()
mbligh0d0f67d2009-11-06 03:15:03 +0000668 self.host.job.pop_execution_context()
jadmanski6dadd832009-02-05 23:39:27 +0000669
670
671 def execute_section(self, section, timeout, stderr_redirector,
672 client_disconnect_timeout):
showardb18134f2009-03-20 20:52:18 +0000673 logging.info("Executing %s/bin/autotest %s/control phase %d",
674 self.autodir, self.autodir, section)
jadmanski6dadd832009-02-05 23:39:27 +0000675
676 if self.background:
677 result = self._execute_in_background(section, timeout)
678 else:
679 result = self._execute_daemon(section, timeout, stderr_redirector,
680 client_disconnect_timeout)
681
682 last_line = stderr_redirector.last_line
mbligh2bf2db62007-11-27 00:53:18 +0000683
jadmanskib264ed02009-01-12 23:54:27 +0000684 # check if we failed hard enough to warrant an exception
jadmanski0afbb632008-06-06 21:10:57 +0000685 if result.exit_status == 1:
jadmanskib264ed02009-01-12 23:54:27 +0000686 err = error.AutotestRunError("client job was aborted")
687 elif not self.background and not result.stderr:
688 err = error.AutotestRunError(
jadmanskie4130532009-03-17 18:01:28 +0000689 "execute_section %s failed to return anything\n"
690 "stdout:%s\n" % (section, result.stdout))
jadmanskib264ed02009-01-12 23:54:27 +0000691 else:
692 err = None
mbligh0e4613b2007-10-29 16:55:07 +0000693
jadmanskib264ed02009-01-12 23:54:27 +0000694 # log something if the client failed AND never finished logging
695 if err and not self.is_client_job_finished(last_line):
jadmanskia61edad2009-05-21 22:17:49 +0000696 self.log_unexpected_abort(stderr_redirector)
jadmanskib264ed02009-01-12 23:54:27 +0000697
698 if err:
699 raise err
700 else:
701 return stderr_redirector.last_line
jadmanski4600e342008-10-29 22:54:00 +0000702
703
jadmanskic0354912010-01-12 15:57:29 +0000704 def _wait_for_reboot(self, old_boot_id):
showardb18134f2009-03-20 20:52:18 +0000705 logging.info("Client is rebooting")
706 logging.info("Waiting for client to halt")
jadmanskic0354912010-01-12 15:57:29 +0000707 if not self.host.wait_down(HALT_TIME, old_boot_id=old_boot_id):
jadmanski4600e342008-10-29 22:54:00 +0000708 err = "%s failed to shutdown after %d"
709 err %= (self.host.hostname, HALT_TIME)
710 raise error.AutotestRunError(err)
showardb18134f2009-03-20 20:52:18 +0000711 logging.info("Client down, waiting for restart")
jadmanski4600e342008-10-29 22:54:00 +0000712 if not self.host.wait_up(BOOT_TIME):
713 # since reboot failed
714 # hardreset the machine once if possible
715 # before failing this control file
716 warning = "%s did not come back up, hard resetting"
717 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000718 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000719 try:
720 self.host.hardreset(wait=False)
mblighd99d3b272008-12-22 14:41:27 +0000721 except (AttributeError, error.AutoservUnsupportedError):
jadmanski4600e342008-10-29 22:54:00 +0000722 warning = "Hard reset unsupported on %s"
723 warning %= self.host.hostname
showardb18134f2009-03-20 20:52:18 +0000724 logging.warning(warning)
jadmanski4600e342008-10-29 22:54:00 +0000725 raise error.AutotestRunError("%s failed to boot after %ds" %
726 (self.host.hostname, BOOT_TIME))
727 self.host.reboot_followup()
mblighdc735a22007-08-02 16:54:37 +0000728
729
jadmanski6dadd832009-02-05 23:39:27 +0000730 def execute_control(self, timeout=None, client_disconnect_timeout=None):
jadmanski6bb32d72009-03-19 20:25:24 +0000731 if not self.background:
732 collector = log_collector(self.host, self.tag, self.results_dir)
733 hostname = self.host.hostname
734 remote_results = collector.client_results_dir
735 local_results = collector.server_results_dir
736 self.host.job.add_client_log(hostname, remote_results,
737 local_results)
jadmanski52053632010-06-11 21:08:10 +0000738 job_record_context = self.host.job.get_record_context()
jadmanski6bb32d72009-03-19 20:25:24 +0000739
jadmanski0afbb632008-06-06 21:10:57 +0000740 section = 0
jadmanski4600e342008-10-29 22:54:00 +0000741 start_time = time.time()
742
jadmanski043e1132008-11-19 17:10:32 +0000743 logger = client_logger(self.host, self.tag, self.results_dir)
jadmanski4600e342008-10-29 22:54:00 +0000744 try:
745 while not timeout or time.time() < start_time + timeout:
746 if timeout:
747 section_timeout = start_time + timeout - time.time()
748 else:
749 section_timeout = None
jadmanskic0354912010-01-12 15:57:29 +0000750 boot_id = self.host.get_boot_id()
jadmanski4600e342008-10-29 22:54:00 +0000751 last = self.execute_section(section, section_timeout,
jadmanski6dadd832009-02-05 23:39:27 +0000752 logger, client_disconnect_timeout)
mblighb3c0c912008-11-27 00:32:45 +0000753 if self.background:
754 return
jadmanski4600e342008-10-29 22:54:00 +0000755 section += 1
jadmanskib264ed02009-01-12 23:54:27 +0000756 if self.is_client_job_finished(last):
showardb18134f2009-03-20 20:52:18 +0000757 logging.info("Client complete")
jadmanski4600e342008-10-29 22:54:00 +0000758 return
jadmanskib264ed02009-01-12 23:54:27 +0000759 elif self.is_client_job_rebooting(last):
jadmanski79ab9282008-11-11 17:53:12 +0000760 try:
jadmanskic0354912010-01-12 15:57:29 +0000761 self._wait_for_reboot(boot_id)
jadmanski79ab9282008-11-11 17:53:12 +0000762 except error.AutotestRunError, e:
jadmanski043e1132008-11-19 17:10:32 +0000763 self.host.job.record("ABORT", None, "reboot", str(e))
764 self.host.job.record("END ABORT", None, None, str(e))
jadmanski79ab9282008-11-11 17:53:12 +0000765 raise
jadmanski4600e342008-10-29 22:54:00 +0000766 continue
767
768 # if we reach here, something unexpected happened
jadmanskia61edad2009-05-21 22:17:49 +0000769 self.log_unexpected_abort(logger)
jadmanski4600e342008-10-29 22:54:00 +0000770
771 # give the client machine a chance to recover from a crash
772 self.host.wait_up(CRASH_RECOVERY_TIME)
773 msg = ("Aborting - unexpected final status message from "
jadmanskib1de5492010-06-11 14:29:01 +0000774 "client on %s: %s\n") % (self.host.hostname, last)
jadmanski4600e342008-10-29 22:54:00 +0000775 raise error.AutotestRunError(msg)
776 finally:
jadmanski043e1132008-11-19 17:10:32 +0000777 logger.close()
jadmanski6bb32d72009-03-19 20:25:24 +0000778 if not self.background:
779 collector.collect_client_job_results()
jadmanski4d03cf62010-03-04 18:32:28 +0000780 collector.remove_redundant_client_logs()
mblighfc3da5b2010-01-06 18:37:22 +0000781 state_file = os.path.basename(self.remote_control_file
782 + '.state')
783 state_path = os.path.join(self.results_dir, state_file)
784 self.host.job.postprocess_client_state(state_path)
jadmanski6bb32d72009-03-19 20:25:24 +0000785 self.host.job.remove_client_log(hostname, remote_results,
786 local_results)
jadmanski52053632010-06-11 21:08:10 +0000787 job_record_context.restore()
mblighdcd57a82007-07-11 23:06:47 +0000788
jadmanski0afbb632008-06-06 21:10:57 +0000789 # should only get here if we timed out
790 assert timeout
791 raise error.AutotestTimeoutError()
mbligh0e4613b2007-10-29 16:55:07 +0000792
mblighdcd57a82007-07-11 23:06:47 +0000793
jadmanski043e1132008-11-19 17:10:32 +0000794class log_collector(object):
795 def __init__(self, host, client_tag, results_dir):
796 self.host = host
797 if not client_tag:
798 client_tag = "default"
799 self.client_results_dir = os.path.join(host.get_autodir(), "results",
800 client_tag)
801 self.server_results_dir = results_dir
802
803
804 def collect_client_job_results(self):
805 """ A method that collects all the current results of a running
806 client job into the results dir. By default does nothing as no
807 client job is running, but when running a client job you can override
808 this with something that will actually do something. """
809
810 # make an effort to wait for the machine to come up
811 try:
812 self.host.wait_up(timeout=30)
813 except error.AutoservError:
814 # don't worry about any errors, we'll try and
815 # get the results anyway
816 pass
817
jadmanski043e1132008-11-19 17:10:32 +0000818 # Copy all dirs in default to results_dir
819 try:
jadmanski043e1132008-11-19 17:10:32 +0000820 self.host.get_file(self.client_results_dir + '/',
mbligh45561782009-05-11 21:14:34 +0000821 self.server_results_dir, preserve_symlinks=True)
jadmanski043e1132008-11-19 17:10:32 +0000822 except Exception:
823 # well, don't stop running just because we couldn't get logs
showardb18134f2009-03-20 20:52:18 +0000824 e_msg = "Unexpected error copying test result logs, continuing ..."
825 logging.error(e_msg)
jadmanski043e1132008-11-19 17:10:32 +0000826 traceback.print_exc(file=sys.stdout)
827
828
jadmanski4d03cf62010-03-04 18:32:28 +0000829 def remove_redundant_client_logs(self):
830 """Remove client.*.log files in favour of client.*.DEBUG files."""
831 debug_dir = os.path.join(self.server_results_dir, 'debug')
832 debug_files = [f for f in os.listdir(debug_dir)
833 if re.search(r'^client\.\d+\.DEBUG$', f)]
834 for debug_file in debug_files:
835 log_file = debug_file.replace('DEBUG', 'log')
836 log_file = os.path.join(debug_dir, log_file)
837 if os.path.exists(log_file):
838 os.remove(log_file)
839
840
jadmanski043e1132008-11-19 17:10:32 +0000841# a file-like object for catching stderr from an autotest client and
842# extracting status logs from it
843class client_logger(object):
844 """Partial file object to write to both stdout and
845 the status log file. We only implement those methods
846 utils.run() actually calls.
jadmanski043e1132008-11-19 17:10:32 +0000847 """
848 status_parser = re.compile(r"^AUTOTEST_STATUS:([^:]*):(.*)$")
849 test_complete_parser = re.compile(r"^AUTOTEST_TEST_COMPLETE:(.*)$")
jadmanskib1a51132009-08-07 16:45:50 +0000850 fetch_package_parser = re.compile(
851 r"^AUTOTEST_FETCH_PACKAGE:([^:]*):([^:]*):(.*)$")
jadmanski043e1132008-11-19 17:10:32 +0000852 extract_indent = re.compile(r"^(\t*).*$")
jadmanskiefe4ebf2009-05-21 22:12:30 +0000853 extract_timestamp = re.compile(r".*\ttimestamp=(\d+)\t.*$")
jadmanski043e1132008-11-19 17:10:32 +0000854
855 def __init__(self, host, tag, server_results_dir):
856 self.host = host
857 self.job = host.job
858 self.log_collector = log_collector(host, tag, server_results_dir)
859 self.leftover = ""
860 self.last_line = ""
861 self.logs = {}
jadmanskiefe4ebf2009-05-21 22:12:30 +0000862
863
jadmanski043e1132008-11-19 17:10:32 +0000864 def _process_log_dict(self, log_dict):
865 log_list = log_dict.pop("logs", [])
866 for key in sorted(log_dict.iterkeys()):
867 log_list += self._process_log_dict(log_dict.pop(key))
868 return log_list
869
870
871 def _process_logs(self):
872 """Go through the accumulated logs in self.log and print them
873 out to stdout and the status log. Note that this processes
874 logs in an ordering where:
875
876 1) logs to different tags are never interleaved
877 2) logs to x.y come before logs to x.y.z for all z
878 3) logs to x.y come before x.z whenever y < z
879
880 Note that this will in general not be the same as the
881 chronological ordering of the logs. However, if a chronological
882 ordering is desired that one can be reconstructed from the
883 status log by looking at timestamp lines."""
884 log_list = self._process_log_dict(self.logs)
jadmanski2a89dac2010-06-11 14:32:58 +0000885 for entry in log_list:
886 self.job.record_entry(entry, log_in_subdir=False)
jadmanski043e1132008-11-19 17:10:32 +0000887 if log_list:
jadmanski2a89dac2010-06-11 14:32:58 +0000888 self.last_line = log_list[-1].render()
jadmanski043e1132008-11-19 17:10:32 +0000889
890
891 def _process_quoted_line(self, tag, line):
892 """Process a line quoted with an AUTOTEST_STATUS flag. If the
893 tag is blank then we want to push out all the data we've been
894 building up in self.logs, and then the newest line. If the
895 tag is not blank, then push the line into the logs for handling
896 later."""
jadmanski2a89dac2010-06-11 14:32:58 +0000897 entry = base_job.status_log_entry.parse(line)
898 if entry is None:
899 return # the line contains no status lines
jadmanski043e1132008-11-19 17:10:32 +0000900 if tag == "":
901 self._process_logs()
jadmanski2a89dac2010-06-11 14:32:58 +0000902 self.job.record_entry(entry, log_in_subdir=False)
jadmanski043e1132008-11-19 17:10:32 +0000903 self.last_line = line
904 else:
905 tag_parts = [int(x) for x in tag.split(".")]
906 log_dict = self.logs
907 for part in tag_parts:
908 log_dict = log_dict.setdefault(part, {})
909 log_list = log_dict.setdefault("logs", [])
jadmanski2a89dac2010-06-11 14:32:58 +0000910 log_list.append(entry)
jadmanski043e1132008-11-19 17:10:32 +0000911
912
jadmanskif37df842009-02-11 00:03:26 +0000913 def _process_info_line(self, line):
914 """Check if line is an INFO line, and if it is, interpret any control
915 messages (e.g. enabling/disabling warnings) that it may contain."""
916 match = re.search(r"^\t*INFO\t----\t----(.*)\t[^\t]*$", line)
917 if not match:
918 return # not an INFO line
919 for field in match.group(1).split('\t'):
920 if field.startswith("warnings.enable="):
jadmanski16a7ff72009-04-01 18:19:53 +0000921 func = self.job.warning_manager.enable_warnings
jadmanskif37df842009-02-11 00:03:26 +0000922 elif field.startswith("warnings.disable="):
jadmanski16a7ff72009-04-01 18:19:53 +0000923 func = self.job.warning_manager.disable_warnings
jadmanskif37df842009-02-11 00:03:26 +0000924 else:
925 continue
926 warning_type = field.split("=", 1)[1]
jadmanski16a7ff72009-04-01 18:19:53 +0000927 func(warning_type)
jadmanskif37df842009-02-11 00:03:26 +0000928
929
jadmanski043e1132008-11-19 17:10:32 +0000930 def _process_line(self, line):
931 """Write out a line of data to the appropriate stream. Status
932 lines sent by autotest will be prepended with
933 "AUTOTEST_STATUS", and all other lines are ssh error
934 messages."""
935 status_match = self.status_parser.search(line)
936 test_complete_match = self.test_complete_parser.search(line)
jadmanskib1a51132009-08-07 16:45:50 +0000937 fetch_package_match = self.fetch_package_parser.search(line)
jadmanski043e1132008-11-19 17:10:32 +0000938 if status_match:
939 tag, line = status_match.groups()
jadmanskif37df842009-02-11 00:03:26 +0000940 self._process_info_line(line)
jadmanski043e1132008-11-19 17:10:32 +0000941 self._process_quoted_line(tag, line)
942 elif test_complete_match:
jadmanskifcc0d5d2009-02-12 21:52:54 +0000943 self._process_logs()
jadmanski043e1132008-11-19 17:10:32 +0000944 fifo_path, = test_complete_match.groups()
mbligh060c4712009-12-29 02:43:35 +0000945 try:
946 self.log_collector.collect_client_job_results()
947 self.host.run("echo A > %s" % fifo_path)
948 except Exception:
949 msg = "Post-test log collection failed, continuing anyway"
950 logging.exception(msg)
jadmanskib1a51132009-08-07 16:45:50 +0000951 elif fetch_package_match:
952 pkg_name, dest_path, fifo_path = fetch_package_match.groups()
jadmanskiede7e242009-08-10 15:43:33 +0000953 serve_packages = global_config.global_config.get_config_value(
954 "PACKAGES", "serve_packages_from_autoserv", type=bool)
955 if serve_packages and pkg_name.endswith(".tar.bz2"):
956 try:
957 self._send_tarball(pkg_name, dest_path)
958 except Exception:
959 msg = "Package tarball creation failed, continuing anyway"
960 logging.exception(msg)
mbligh060c4712009-12-29 02:43:35 +0000961 try:
962 self.host.run("echo B > %s" % fifo_path)
963 except Exception:
964 msg = "Package tarball installation failed, continuing anyway"
965 logging.exception(msg)
jadmanski043e1132008-11-19 17:10:32 +0000966 else:
showardb18134f2009-03-20 20:52:18 +0000967 logging.info(line)
jadmanski043e1132008-11-19 17:10:32 +0000968
969
jadmanskiede7e242009-08-10 15:43:33 +0000970 def _send_tarball(self, pkg_name, remote_dest):
971 name, pkg_type = self.job.pkgmgr.parse_tarball_name(pkg_name)
972 src_dirs = []
973 if pkg_type == 'test':
mbligh1f572e52010-04-01 17:15:53 +0000974 for test_dir in ['site_tests', 'tests']:
975 src_dir = os.path.join(self.job.clientdir, test_dir, name)
976 if os.path.exists(src_dir):
977 src_dirs += [src_dir]
978 if autoserv_prebuild:
979 prebuild.setup(self.job.clientdir, src_dir)
980 break
jadmanskiede7e242009-08-10 15:43:33 +0000981 elif pkg_type == 'profiler':
982 src_dirs += [os.path.join(self.job.clientdir, 'profilers', name)]
mbligh1f572e52010-04-01 17:15:53 +0000983 if autoserv_prebuild:
984 prebuild.setup(self.job.clientdir, src_dir)
jadmanskiede7e242009-08-10 15:43:33 +0000985 elif pkg_type == 'dep':
986 src_dirs += [os.path.join(self.job.clientdir, 'deps', name)]
987 elif pkg_type == 'client':
988 return # you must already have a client to hit this anyway
989 else:
990 return # no other types are supported
991
992 # iterate over src_dirs until we find one that exists, then tar it
993 for src_dir in src_dirs:
994 if os.path.exists(src_dir):
995 try:
996 logging.info('Bundling %s into %s', src_dir, pkg_name)
997 temp_dir = autotemp.tempdir(unique_id='autoserv-packager',
998 dir=self.job.tmpdir)
999 tarball_path = self.job.pkgmgr.tar_package(
mblighbccad482009-08-24 22:08:31 +00001000 pkg_name, src_dir, temp_dir.name, " .")
jadmanskiede7e242009-08-10 15:43:33 +00001001 self.host.send_file(tarball_path, remote_dest)
1002 finally:
1003 temp_dir.clean()
1004 return
1005
1006
jadmanski91d56a92009-04-01 15:20:40 +00001007 def log_warning(self, msg, warning_type):
jadmanski6dadd832009-02-05 23:39:27 +00001008 """Injects a WARN message into the current status logging stream."""
jadmanski91d56a92009-04-01 15:20:40 +00001009 timestamp = int(time.time())
1010 if self.job.warning_manager.is_valid(timestamp, warning_type):
jadmanski2a89dac2010-06-11 14:32:58 +00001011 self.job.record('WARN', None, None, {}, msg)
jadmanski6dadd832009-02-05 23:39:27 +00001012
jadmanski043e1132008-11-19 17:10:32 +00001013
1014 def write(self, data):
jadmanski2a89dac2010-06-11 14:32:58 +00001015 # now start processing the existing buffer and the new data
jadmanski043e1132008-11-19 17:10:32 +00001016 data = self.leftover + data
mbligh060c4712009-12-29 02:43:35 +00001017 lines = data.split('\n')
1018 processed_lines = 0
1019 try:
1020 # process all the buffered data except the last line
1021 # ignore the last line since we may not have all of it yet
1022 for line in lines[:-1]:
mbligh060c4712009-12-29 02:43:35 +00001023 self._process_line(line)
1024 processed_lines += 1
1025 finally:
1026 # save any unprocessed lines for future processing
1027 self.leftover = '\n'.join(lines[processed_lines:])
jadmanski043e1132008-11-19 17:10:32 +00001028
1029
1030 def flush(self):
1031 sys.stdout.flush()
1032
1033
jadmanskia61edad2009-05-21 22:17:49 +00001034 def flush_all_buffers(self):
jadmanski043e1132008-11-19 17:10:32 +00001035 if self.leftover:
1036 self._process_line(self.leftover)
jadmanskia61edad2009-05-21 22:17:49 +00001037 self.leftover = ""
jadmanski043e1132008-11-19 17:10:32 +00001038 self._process_logs()
1039 self.flush()
1040
1041
jadmanskia61edad2009-05-21 22:17:49 +00001042 def close(self):
1043 self.flush_all_buffers()
1044
1045
mbligha7007722009-01-13 00:37:11 +00001046SiteAutotest = client_utils.import_site_class(
1047 __file__, "autotest_lib.server.site_autotest", "SiteAutotest",
1048 BaseAutotest)
mblighd8b39252008-03-20 21:15:03 +00001049
showardad812bf2009-10-20 23:49:56 +00001050
mblighd8b39252008-03-20 21:15:03 +00001051class Autotest(SiteAutotest):
jadmanski0afbb632008-06-06 21:10:57 +00001052 pass