blob: f80b5f74b4754151f1dbc351117a29548fc9bc24 [file] [log] [blame]
mblighdcd57a82007-07-11 23:06:47 +00001#!/usr/bin/python
2#
3# Copyright 2007 Google Inc. Released under the GPL v2
4
mblighdc735a22007-08-02 16:54:37 +00005"""
6This module defines the Autotest class
mblighdcd57a82007-07-11 23:06:47 +00007
jadmanski0afbb632008-06-06 21:10:57 +00008 Autotest: software to run tests automatically
mblighdcd57a82007-07-11 23:06:47 +00009"""
10
mblighdc735a22007-08-02 16:54:37 +000011__author__ = """
12mbligh@google.com (Martin J. Bligh),
mblighdcd57a82007-07-11 23:06:47 +000013poirier@google.com (Benjamin Poirier),
mblighdc735a22007-08-02 16:54:37 +000014stutsman@google.com (Ryan Stutsman)
15"""
mblighdcd57a82007-07-11 23:06:47 +000016
mbligh02ff2d52008-06-03 15:00:21 +000017import re, os, sys, traceback, subprocess, tempfile, shutil, time
mblighdcd57a82007-07-11 23:06:47 +000018
mblighccb9e182008-04-17 15:42:10 +000019from autotest_lib.server import installable_object, utils, server_job
20from autotest_lib.client.common_lib import logging
mblighc5ddfd12008-08-04 17:15:00 +000021from autotest_lib.client.common_lib import error, global_config, packages
mblighdcd57a82007-07-11 23:06:47 +000022
23
mbligh3c7a1502008-07-24 18:08:47 +000024
mblighdcd57a82007-07-11 23:06:47 +000025AUTOTEST_SVN = 'svn://test.kernel.org/autotest/trunk/client'
26AUTOTEST_HTTP = 'http://test.kernel.org/svn/autotest/trunk/client'
27
28# Timeouts for powering down and up respectively
29HALT_TIME = 300
mbligh07c1eac2007-11-05 18:39:29 +000030BOOT_TIME = 1800
jadmanskiec859142008-05-29 21:33:39 +000031CRASH_RECOVERY_TIME = 9000
mbligh0e4613b2007-10-29 16:55:07 +000032
mblighdcd57a82007-07-11 23:06:47 +000033
mblighd8b39252008-03-20 21:15:03 +000034class BaseAutotest(installable_object.InstallableObject):
jadmanski0afbb632008-06-06 21:10:57 +000035 """
36 This class represents the Autotest program.
mblighdcd57a82007-07-11 23:06:47 +000037
jadmanski0afbb632008-06-06 21:10:57 +000038 Autotest is used to run tests automatically and collect the results.
39 It also supports profilers.
mblighdcd57a82007-07-11 23:06:47 +000040
jadmanski0afbb632008-06-06 21:10:57 +000041 Implementation details:
42 This is a leaf class in an abstract class hierarchy, it must
43 implement the unimplemented methods in parent classes.
44 """
mbligh119c12a2007-11-12 22:13:44 +000045
jadmanski0afbb632008-06-06 21:10:57 +000046 def __init__(self, host = None):
47 self.host = host
48 self.got = False
49 self.installed = False
50 self.serverdir = utils.get_server_dir()
51 super(BaseAutotest, self).__init__()
mblighc8949b82007-07-23 16:33:58 +000052
mblighdc735a22007-08-02 16:54:37 +000053
jadmanski0afbb632008-06-06 21:10:57 +000054 @logging.record
55 def install(self, host = None):
56 """
57 Install autotest. If get() was not called previously, an
58 attempt will be made to install from the autotest svn
59 repository.
mbligh9a3f5e52008-05-28 21:21:43 +000060
jadmanski0afbb632008-06-06 21:10:57 +000061 Args:
62 host: a Host instance on which autotest will be
63 installed
mbligh9a3f5e52008-05-28 21:21:43 +000064
jadmanski0afbb632008-06-06 21:10:57 +000065 Raises:
66 AutoservError: if a tarball was not specified and
67 the target host does not have svn installed in its path
mbligh9a3f5e52008-05-28 21:21:43 +000068
jadmanski0afbb632008-06-06 21:10:57 +000069 TODO(poirier): check dependencies
70 autotest needs:
71 bzcat
72 liboptdev (oprofile)
73 binutils-dev (oprofile)
74 make
75 psutils (netperf)
76 """
77 if not host:
78 host = self.host
79 if not self.got:
80 self.get()
81 host.wait_up(timeout=30)
82 host.setup()
83 print "Installing autotest on %s" % host.hostname
mbligh40f122a2007-11-03 23:08:46 +000084
jadmanski0afbb632008-06-06 21:10:57 +000085 # Let's try to figure out where autotest is installed. If we can't,
86 # (autotest not installed) just assume '/usr/local/autotest' and
87 # proceed.
88 try:
89 autodir = _get_autodir(host)
90 except error.AutotestRunError:
91 autodir = '/usr/local/autotest'
mbligh29742302008-05-09 16:06:37 +000092
mbligh0562e652008-08-20 20:11:45 +000093 # Make the host object know as to where autotest is installed
94 host.set_autodir(autodir)
95
jadmanski0afbb632008-06-06 21:10:57 +000096 host.run('mkdir -p "%s"' % utils.sh_escape(autodir))
mbligh40f122a2007-11-03 23:08:46 +000097
mblighc5ddfd12008-08-04 17:15:00 +000098 # Fetch the autotest client from the nearest repository
99 try:
100 c = global_config.global_config
101 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list)
102 pkgmgr = packages.PackageManager(
103 autodir, repo_urls=repos, do_locking=False,
104 run_function=host.run,
105 run_function_dargs=dict(timeout=600))
106 # The packages dir is used to store all the packages that
107 # are fetched on that client. (for the tests,deps etc.
108 # too apart from the client)
109 pkg_dir = os.path.join(autodir, 'packages')
110 # clean up the autodir except for the packages directory
111 host.run('cd %s && ls | grep -v "^packages$"'
112 ' | xargs rm -rf && rm -rf .[^.]*' % autodir)
113 pkgmgr.install_pkg('autotest', 'client', pkg_dir, autodir,
114 preserve_install_dir=True)
115 self.installed = True
116 return
117 except global_config.ConfigError, e:
118 print ("Could not install autotest using the"
119 " packaging system %s" % e)
120 except (packages.PackageInstallError, error.AutoservRunError), e:
121 print "Could not install autotest from %s : %s " % (repos, e)
122
mbligh40f122a2007-11-03 23:08:46 +0000123
jadmanski0afbb632008-06-06 21:10:57 +0000124 # try to install from file or directory
125 if self.source_material:
126 if os.path.isdir(self.source_material):
127 # Copy autotest recursively
128 host.send_file(self.source_material, autodir)
129 else:
130 # Copy autotest via tarball
131 e_msg = 'Installation method not yet implemented!'
132 raise NotImplementedError(e_msg)
133 print "Installation of autotest completed"
134 self.installed = True
135 return
mbligh91334902007-09-28 01:47:59 +0000136
jadmanski0afbb632008-06-06 21:10:57 +0000137 # if that fails try to install using svn
138 if utils.run('which svn').exit_status:
mbligh78bf5352008-07-11 20:27:36 +0000139 raise error.AutoservError('svn not found on target machine: %s'
140 % host.name)
jadmanski0afbb632008-06-06 21:10:57 +0000141 try:
mbligh78bf5352008-07-11 20:27:36 +0000142 host.run('svn checkout %s %s' % (AUTOTEST_SVN, autodir))
jadmanski0afbb632008-06-06 21:10:57 +0000143 except error.AutoservRunError, e:
mbligh78bf5352008-07-11 20:27:36 +0000144 host.run('svn checkout %s %s' % (AUTOTEST_HTTP, autodir))
jadmanski0afbb632008-06-06 21:10:57 +0000145 print "Installation of autotest completed"
146 self.installed = True
mbligh91334902007-09-28 01:47:59 +0000147
148
jadmanski0afbb632008-06-06 21:10:57 +0000149 def get(self, location = None):
150 if not location:
151 location = os.path.join(self.serverdir, '../client')
152 location = os.path.abspath(location)
153 # If there's stuff run on our client directory already, it
154 # can cause problems. Try giving it a quick clean first.
155 cwd = os.getcwd()
156 os.chdir(location)
157 os.system('tools/make_clean')
158 os.chdir(cwd)
159 super(BaseAutotest, self).get(location)
160 self.got = True
mblighdcd57a82007-07-11 23:06:47 +0000161
162
jadmanski0afbb632008-06-06 21:10:57 +0000163 def run(self, control_file, results_dir = '.', host = None,
164 timeout=None, tag=None, parallel_flag=False):
165 """
166 Run an autotest job on the remote machine.
mbligh9a3f5e52008-05-28 21:21:43 +0000167
jadmanski0afbb632008-06-06 21:10:57 +0000168 Args:
169 control_file: an open file-like-obj of the control file
170 results_dir: a str path where the results should be stored
171 on the local filesystem
172 host: a Host instance on which the control file should
173 be run
174 tag: tag name for the client side instance of autotest
175 parallel_flag: flag set when multiple jobs are run at the
176 same time
177 Raises:
178 AutotestRunError: if there is a problem executing
179 the control file
180 """
181 host = self._get_host_and_setup(host)
182 results_dir = os.path.abspath(results_dir)
mblighc1cbc992008-05-27 20:01:45 +0000183
jadmanski0afbb632008-06-06 21:10:57 +0000184 if tag:
185 results_dir = os.path.join(results_dir, tag)
mblighc1cbc992008-05-27 20:01:45 +0000186
jadmanski0afbb632008-06-06 21:10:57 +0000187 atrun = _Run(host, results_dir, tag, parallel_flag)
188 self._do_run(control_file, results_dir, host, atrun, timeout)
mblighd8b39252008-03-20 21:15:03 +0000189
190
jadmanski0afbb632008-06-06 21:10:57 +0000191 def _get_host_and_setup(self, host):
192 if not host:
193 host = self.host
194 if not self.installed:
195 self.install(host)
mbligh91334902007-09-28 01:47:59 +0000196
jadmanski0afbb632008-06-06 21:10:57 +0000197 host.wait_up(timeout=30)
198 return host
mblighd8b39252008-03-20 21:15:03 +0000199
200
jadmanski0afbb632008-06-06 21:10:57 +0000201 def _do_run(self, control_file, results_dir, host, atrun, timeout):
202 try:
203 atrun.verify_machine()
204 except:
mbligh78bf5352008-07-11 20:27:36 +0000205 print "Verify machine failed on %s. Reinstalling" % host.hostname
jadmanski0afbb632008-06-06 21:10:57 +0000206 self.install(host)
207 atrun.verify_machine()
208 debug = os.path.join(results_dir, 'debug')
209 try:
210 os.makedirs(debug)
211 except:
212 pass
mbligh9a3f5e52008-05-28 21:21:43 +0000213
jadmanski0afbb632008-06-06 21:10:57 +0000214 # Ready .... Aim ....
215 for control in [atrun.remote_control_file,
216 atrun.remote_control_file + '.state',
217 atrun.manual_control_file,
218 atrun.manual_control_file + '.state']:
219 host.run('rm -f ' + control)
mbligh9a3f5e52008-05-28 21:21:43 +0000220
jadmanski0afbb632008-06-06 21:10:57 +0000221 tmppath = utils.get(control_file)
mblighc5ddfd12008-08-04 17:15:00 +0000222
223 # Insert the job.add_repository() lines in the control file
224 # if there are any repos defined in global_config.ini
225 try:
226 cfile = open(tmppath, 'r')
227 cfile_orig = cfile.read()
228 cfile.close()
229 c = global_config.global_config
230 repos = c.get_config_value("PACKAGES", 'fetch_location', type=list)
231 control_file_new = []
232 control_file_new.append('job.add_repository(%s)\n' % repos)
233 control_file_new.append(cfile_orig)
234
235 # Overwrite the control file with the new one
236 cfile = open(tmppath, 'w')
237 cfile.write('\n'.join(control_file_new))
238 cfile.close()
239 except global_config.ConfigError, e:
240 pass
241
242
243 # Copy control_file to remote_control_file on the host
jadmanski0afbb632008-06-06 21:10:57 +0000244 host.send_file(tmppath, atrun.remote_control_file)
245 if os.path.abspath(tmppath) != os.path.abspath(control_file):
246 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000247
jadmanski0afbb632008-06-06 21:10:57 +0000248 try:
249 atrun.execute_control(timeout=timeout)
250 finally:
jadmanskia1f3c202008-09-15 19:17:16 +0000251 collector = server_job.log_collector(host, atrun.tag, results_dir)
252 collector.collect_client_job_results()
mblighdcd57a82007-07-11 23:06:47 +0000253
mbligh0e4613b2007-10-29 16:55:07 +0000254
jadmanski0afbb632008-06-06 21:10:57 +0000255 def run_timed_test(self, test_name, results_dir='.', host=None,
256 timeout=None, tag=None, *args, **dargs):
257 """
258 Assemble a tiny little control file to just run one test,
259 and run it as an autotest client-side test
260 """
261 if not host:
262 host = self.host
263 if not self.installed:
264 self.install(host)
265 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
266 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
267 control = "job.run_test(%s)\n" % cmd
268 self.run(control, results_dir, host, timeout=timeout, tag=tag)
mbligh0e4613b2007-10-29 16:55:07 +0000269
270
jadmanski169ecad2008-09-12 15:49:44 +0000271 def run_test(self, test_name, results_dir='.', host=None, tag=None,
272 *args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000273 self.run_timed_test(test_name, results_dir, host, timeout=None,
274 tag=tag, *args, **dargs)
mblighd54832b2007-07-25 16:46:56 +0000275
276
mblighdcd57a82007-07-11 23:06:47 +0000277class _Run(object):
jadmanski0afbb632008-06-06 21:10:57 +0000278 """
279 Represents a run of autotest control file. This class maintains
280 all the state necessary as an autotest control file is executed.
mblighdcd57a82007-07-11 23:06:47 +0000281
jadmanski0afbb632008-06-06 21:10:57 +0000282 It is not intended to be used directly, rather control files
283 should be run using the run method in Autotest.
284 """
285 def __init__(self, host, results_dir, tag, parallel_flag):
286 self.host = host
287 self.results_dir = results_dir
288 self.env = host.env
289 self.tag = tag
290 self.parallel_flag = parallel_flag
291 self.autodir = _get_autodir(self.host)
mbligh78bf5352008-07-11 20:27:36 +0000292 control = os.path.join(self.autodir, 'control')
jadmanski0afbb632008-06-06 21:10:57 +0000293 if tag:
mbligh78bf5352008-07-11 20:27:36 +0000294 control += '.' + tag
295 self.manual_control_file = control
296 self.remote_control_file = control + '.autoserv'
mblighdc735a22007-08-02 16:54:37 +0000297
298
jadmanski0afbb632008-06-06 21:10:57 +0000299 def verify_machine(self):
300 binary = os.path.join(self.autodir, 'bin/autotest')
301 try:
302 self.host.run('ls %s > /dev/null 2>&1' % binary)
303 except:
304 raise "Autotest does not appear to be installed"
mblighdc735a22007-08-02 16:54:37 +0000305
jadmanski0afbb632008-06-06 21:10:57 +0000306 if not self.parallel_flag:
307 tmpdir = os.path.join(self.autodir, 'tmp')
308 download = os.path.join(self.autodir, 'tests/download')
309 self.host.run('umount %s' % tmpdir, ignore_status=True)
310 self.host.run('umount %s' % download, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000311
jadmanski0afbb632008-06-06 21:10:57 +0000312 def get_full_cmd(self, section):
313 # build up the full command we want to run over the host
jadmanskib6eb2f12008-09-12 16:39:36 +0000314 cmd = [os.path.join(self.autodir, 'bin/autotest_client'),
315 '-H autoserv']
jadmanski0afbb632008-06-06 21:10:57 +0000316 if section > 0:
317 cmd.append('-c')
318 if self.tag:
319 cmd.append('-t %s' % self.tag)
320 if self.host.job.use_external_logging():
321 cmd.append('-l')
322 cmd.append(self.remote_control_file)
323 return ' '.join(cmd)
mblighadf2aab2007-11-29 18:16:43 +0000324
mblighd8b39252008-03-20 21:15:03 +0000325
jadmanski0afbb632008-06-06 21:10:57 +0000326 def get_client_log(self, section):
327 # open up the files we need for our logging
328 client_log_file = os.path.join(self.results_dir, 'debug',
329 'client.log.%d' % section)
330 return open(client_log_file, 'w', 0)
mblighd8b39252008-03-20 21:15:03 +0000331
332
jadmanski0afbb632008-06-06 21:10:57 +0000333 def execute_section(self, section, timeout):
mbligh4e4961c2008-07-11 21:08:10 +0000334 print "Executing %s/bin/autotest %s/control phase %d" % \
335 (self.autodir, self.autodir, section)
mblighd8b39252008-03-20 21:15:03 +0000336
jadmanski0afbb632008-06-06 21:10:57 +0000337 full_cmd = self.get_full_cmd(section)
338 client_log = self.get_client_log(section)
jadmanskia1f3c202008-09-15 19:17:16 +0000339 redirector = server_job.client_logger(self.host, self.tag,
340 self.results_dir)
mblighd528d302007-12-19 16:19:05 +0000341
jadmanski0afbb632008-06-06 21:10:57 +0000342 try:
343 old_resultdir = self.host.job.resultdir
344 self.host.job.resultdir = self.results_dir
345 result = self.host.run(full_cmd, ignore_status=True,
346 timeout=timeout,
347 stdout_tee=client_log,
348 stderr_tee=redirector)
349 finally:
350 redirector.close()
351 self.host.job.resultdir = old_resultdir
mbligh2bf2db62007-11-27 00:53:18 +0000352
jadmanski0afbb632008-06-06 21:10:57 +0000353 if result.exit_status == 1:
354 self.host.job.aborted = True
jadmanski89476062008-07-01 23:57:21 +0000355 raise error.AutotestRunError("client job was aborted")
jadmanski0afbb632008-06-06 21:10:57 +0000356 if not result.stderr:
357 raise error.AutotestRunError(
358 "execute_section: %s failed to return anything\n"
359 "stdout:%s\n" % (full_cmd, result.stdout))
mbligh0e4613b2007-10-29 16:55:07 +0000360
jadmanski0afbb632008-06-06 21:10:57 +0000361 return redirector.last_line
mblighdc735a22007-08-02 16:54:37 +0000362
363
jadmanski0afbb632008-06-06 21:10:57 +0000364 def execute_control(self, timeout=None):
365 section = 0
366 time_left = None
367 if timeout:
368 end_time = time.time() + timeout
369 time_left = end_time - time.time()
370 while not timeout or time_left > 0:
371 last = self.execute_section(section, time_left)
372 if timeout:
373 time_left = end_time - time.time()
374 if time_left <= 0:
375 break
376 section += 1
377 if re.match(r'^END .*\t----\t----\t.*$', last):
378 print "Client complete"
379 return
380 elif re.match('^\t*GOOD\t----\treboot\.start.*$', last):
381 print "Client is rebooting"
382 print "Waiting for client to halt"
383 if not self.host.wait_down(HALT_TIME):
jadmanski169ecad2008-09-12 15:49:44 +0000384 err = "%s failed to shutdown after %d"
385 err %= (self.host.hostname, HALT_TIME)
mbligh78bf5352008-07-11 20:27:36 +0000386 raise error.AutotestRunError(err)
jadmanski0afbb632008-06-06 21:10:57 +0000387 print "Client down, waiting for restart"
388 if not self.host.wait_up(BOOT_TIME):
389 # since reboot failed
390 # hardreset the machine once if possible
391 # before failing this control file
mbligh78bf5352008-07-11 20:27:36 +0000392 print "Hardresetting %s" % self.host.hostname
jadmanski0afbb632008-06-06 21:10:57 +0000393 try:
394 self.host.hardreset(wait=False)
395 except error.AutoservUnsupportedError:
mbligh78bf5352008-07-11 20:27:36 +0000396 print "Hardreset unsupported on %s" % self.host.hostname
397 raise error.AutotestRunError("%s failed to boot after %ds" %
398 (self.host.hostname, BOOT_TIME))
jadmanski0afbb632008-06-06 21:10:57 +0000399 self.host.reboot_followup()
400 continue
jadmanskic66e93c2008-07-29 21:25:22 +0000401 self.host.job.record("END ABORT", None, None,
mbligh78bf5352008-07-11 20:27:36 +0000402 "Autotest client terminated unexpectedly")
jadmanski0afbb632008-06-06 21:10:57 +0000403 # give the client machine a chance to recover from
404 # possible crash
405 self.host.wait_up(CRASH_RECOVERY_TIME)
mbligh78bf5352008-07-11 20:27:36 +0000406 raise error.AutotestRunError("Aborting - unexpected final status "
407 "message from client: %s\n" % last)
mblighdcd57a82007-07-11 23:06:47 +0000408
jadmanski0afbb632008-06-06 21:10:57 +0000409 # should only get here if we timed out
410 assert timeout
411 raise error.AutotestTimeoutError()
mbligh0e4613b2007-10-29 16:55:07 +0000412
mblighdcd57a82007-07-11 23:06:47 +0000413
414def _get_autodir(host):
mbligh3c7a1502008-07-24 18:08:47 +0000415 autodir = host.get_autodir()
416 if autodir:
417 return autodir
jadmanski0afbb632008-06-06 21:10:57 +0000418 try:
419 # There's no clean way to do this. readlink may not exist
420 cmd = "python -c 'import os,sys; print os.readlink(sys.argv[1])' /etc/autotest.conf 2> /dev/null"
mbligh3c7a1502008-07-24 18:08:47 +0000421 autodir = os.path.dirname(host.run(cmd).stdout)
422 if autodir:
423 return autodir
jadmanski0afbb632008-06-06 21:10:57 +0000424 except error.AutoservRunError:
425 pass
426 for path in ['/usr/local/autotest', '/home/autotest']:
427 try:
jadmanski169ecad2008-09-12 15:49:44 +0000428 host.run('ls %s > /dev/null 2>&1' %
429 os.path.join(path, 'bin/autotest'))
jadmanski0afbb632008-06-06 21:10:57 +0000430 return path
431 except error.AutoservRunError:
432 pass
433 raise error.AutotestRunError("Cannot figure out autotest directory")
mblighd8b39252008-03-20 21:15:03 +0000434
435
436# site_autotest.py may be non-existant or empty, make sure that an appropriate
437# SiteAutotest class is created nevertheless
438try:
jadmanski0afbb632008-06-06 21:10:57 +0000439 from site_autotest import SiteAutotest
mblighd8b39252008-03-20 21:15:03 +0000440except ImportError:
jadmanski0afbb632008-06-06 21:10:57 +0000441 class SiteAutotest(BaseAutotest):
442 pass
mblighd8b39252008-03-20 21:15:03 +0000443
444
445class Autotest(SiteAutotest):
jadmanski0afbb632008-06-06 21:10:57 +0000446 pass