blob: 2672afb6b8d93c4e8779c1a3221f8f297126ae2f [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
8 Autotest: software to run tests automatically
9"""
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
17import re
18import os
19import sys
20import subprocess
21import urllib
22import tempfile
23import shutil
mbligh0e4613b2007-10-29 16:55:07 +000024import time
mblighdcd57a82007-07-11 23:06:47 +000025
26import installable_object
mblighdcd57a82007-07-11 23:06:47 +000027import utils
mblighdab39662008-02-27 16:47:55 +000028import server_job
mbligh119c12a2007-11-12 22:13:44 +000029from common import logging
mbligh03f4fc72007-11-29 20:56:14 +000030from common.error import *
mblighdcd57a82007-07-11 23:06:47 +000031
32
33AUTOTEST_SVN = 'svn://test.kernel.org/autotest/trunk/client'
34AUTOTEST_HTTP = 'http://test.kernel.org/svn/autotest/trunk/client'
35
36# Timeouts for powering down and up respectively
37HALT_TIME = 300
mbligh07c1eac2007-11-05 18:39:29 +000038BOOT_TIME = 1800
mblighdcd57a82007-07-11 23:06:47 +000039
40
mblighdcd57a82007-07-11 23:06:47 +000041
mbligh0e4613b2007-10-29 16:55:07 +000042
mblighdcd57a82007-07-11 23:06:47 +000043
mblighd8b39252008-03-20 21:15:03 +000044class BaseAutotest(installable_object.InstallableObject):
mblighdc735a22007-08-02 16:54:37 +000045 """
46 This class represents the Autotest program.
mblighdcd57a82007-07-11 23:06:47 +000047
48 Autotest is used to run tests automatically and collect the results.
49 It also supports profilers.
50
51 Implementation details:
52 This is a leaf class in an abstract class hierarchy, it must
53 implement the unimplemented methods in parent classes.
54 """
mbligh119c12a2007-11-12 22:13:44 +000055 job = None
56
57
mblighdd81ef02007-08-10 01:18:40 +000058 def __init__(self, host = None):
59 self.host = host
mbligh91334902007-09-28 01:47:59 +000060 self.got = False
61 self.installed = False
mbligh9708f732007-10-18 03:18:54 +000062 self.serverdir = utils.get_server_dir()
mblighd8b39252008-03-20 21:15:03 +000063 super(BaseAutotest, self).__init__()
mblighc8949b82007-07-23 16:33:58 +000064
mblighdc735a22007-08-02 16:54:37 +000065
mbligh119c12a2007-11-12 22:13:44 +000066 @logging.record
mblighdd81ef02007-08-10 01:18:40 +000067 def install(self, host = None):
mblighdc735a22007-08-02 16:54:37 +000068 """
69 Install autotest. If get() was not called previously, an
mblighc8949b82007-07-23 16:33:58 +000070 attempt will be made to install from the autotest svn
71 repository.
mblighdcd57a82007-07-11 23:06:47 +000072
73 Args:
74 host: a Host instance on which autotest will be
75 installed
76
77 Raises:
78 AutoservError: if a tarball was not specified and
79 the target host does not have svn installed in its path
mblighc8949b82007-07-23 16:33:58 +000080
81 TODO(poirier): check dependencies
82 autotest needs:
83 bzcat
84 liboptdev (oprofile)
85 binutils-dev (oprofile)
86 make
mbligh629e39e2007-08-10 19:32:00 +000087 psutils (netperf)
mblighdcd57a82007-07-11 23:06:47 +000088 """
mblighdd81ef02007-08-10 01:18:40 +000089 if not host:
90 host = self.host
mbligh91334902007-09-28 01:47:59 +000091 if not self.got:
92 self.get()
mbligh9e787d22008-02-11 17:56:37 +000093 host.wait_up(timeout=30)
mbligh91334902007-09-28 01:47:59 +000094 host.setup()
mblighb84a1cf2007-08-09 23:08:13 +000095 print "Installing autotest on %s" % host.hostname
mbligh40f122a2007-11-03 23:08:46 +000096
97 autodir = _get_autodir(host)
98 host.run('mkdir -p "%s"' % utils.sh_escape(autodir))
99
100 if getattr(host, 'site_install_autotest', None):
101 if host.site_install_autotest():
102 self.installed = True
103 return
104
mblighdcd57a82007-07-11 23:06:47 +0000105 # try to install from file or directory
mblighc8949b82007-07-23 16:33:58 +0000106 if self.source_material:
107 if os.path.isdir(self.source_material):
mblighdcd57a82007-07-11 23:06:47 +0000108 # Copy autotest recursively
mbligh40f122a2007-11-03 23:08:46 +0000109 host.send_file(self.source_material, autodir)
mblighdcd57a82007-07-11 23:06:47 +0000110 else:
111 # Copy autotest via tarball
mbligh4d6feff2008-01-14 16:48:56 +0000112 e_msg = 'Installation method not yet implemented!'
113 raise NotImplementedError(e_msg)
mbligh91334902007-09-28 01:47:59 +0000114 print "Installation of autotest completed"
115 self.installed = True
mblighdcd57a82007-07-11 23:06:47 +0000116 return
mbligh91334902007-09-28 01:47:59 +0000117
mblighdcd57a82007-07-11 23:06:47 +0000118 # if that fails try to install using svn
119 if utils.run('which svn').exit_status:
120 raise AutoservError('svn not found in path on \
121 target machine: %s' % host.name)
122 try:
123 host.run('svn checkout %s %s' %
mbligh3850f892007-11-05 22:49:24 +0000124 (AUTOTEST_SVN, autodir))
mbligh03f4fc72007-11-29 20:56:14 +0000125 except AutoservRunError, e:
mblighdcd57a82007-07-11 23:06:47 +0000126 host.run('svn checkout %s %s' %
mbligh3850f892007-11-05 22:49:24 +0000127 (AUTOTEST_HTTP, autodir))
mbligh91334902007-09-28 01:47:59 +0000128 print "Installation of autotest completed"
129 self.installed = True
130
131
132 def get(self, location = None):
133 if not location:
134 location = os.path.join(self.serverdir, '../client')
135 location = os.path.abspath(location)
mbligh8fc0e5a2007-10-11 18:39:03 +0000136 # If there's stuff run on our client directory already, it
137 # can cause problems. Try giving it a quick clean first.
138 cwd = os.getcwd()
139 os.chdir(location)
140 os.system('tools/make_clean')
141 os.chdir(cwd)
mblighd8b39252008-03-20 21:15:03 +0000142 super(BaseAutotest, self).get(location)
mbligh91334902007-09-28 01:47:59 +0000143 self.got = True
mblighdcd57a82007-07-11 23:06:47 +0000144
145
mbligh0e4613b2007-10-29 16:55:07 +0000146 def run(self, control_file, results_dir = '.', host = None,
147 timeout=None):
mblighdcd57a82007-07-11 23:06:47 +0000148 """
149 Run an autotest job on the remote machine.
mblighc8949b82007-07-23 16:33:58 +0000150
mblighdcd57a82007-07-11 23:06:47 +0000151 Args:
152 control_file: an open file-like-obj of the control file
153 results_dir: a str path where the results should be stored
154 on the local filesystem
155 host: a Host instance on which the control file should
156 be run
157
158 Raises:
159 AutotestRunError: if there is a problem executing
160 the control file
161 """
mblighd8b39252008-03-20 21:15:03 +0000162 host = self._get_host_and_setup(host)
mbligh55a2a3b2007-09-30 01:27:55 +0000163 results_dir = os.path.abspath(results_dir)
mblighd8b39252008-03-20 21:15:03 +0000164 atrun = _Run(host, results_dir)
165 self._do_run(control_file, results_dir, host, atrun, timeout)
166
167
168 def _get_host_and_setup(self, host):
mblighdd81ef02007-08-10 01:18:40 +0000169 if not host:
170 host = self.host
mbligh91334902007-09-28 01:47:59 +0000171 if not self.installed:
mbligh7fdd1692007-10-02 19:16:53 +0000172 self.install(host)
mbligh91334902007-09-28 01:47:59 +0000173
mbligh9e787d22008-02-11 17:56:37 +0000174 host.wait_up(timeout=30)
mblighd8b39252008-03-20 21:15:03 +0000175 return host
176
177
178 def _do_run(self, control_file, results_dir, host, atrun, timeout):
mblighe4f01512007-08-10 01:42:50 +0000179 try:
180 atrun.verify_machine()
181 except:
182 print "Verify machine failed on %s. Reinstalling" % \
183 host.hostname
184 self.install(host)
mblighdcd57a82007-07-11 23:06:47 +0000185 atrun.verify_machine()
186 debug = os.path.join(results_dir, 'debug')
mbligh55a2a3b2007-09-30 01:27:55 +0000187 try:
188 os.makedirs(debug)
189 except:
190 pass
mblighc8949b82007-07-23 16:33:58 +0000191
mblighdcd57a82007-07-11 23:06:47 +0000192 # Ready .... Aim ....
mbligha4bece12007-11-24 19:37:14 +0000193 for control in [atrun.remote_control_file,
194 atrun.remote_control_file + '.state',
195 atrun.manual_control_file,
196 atrun.manual_control_file + '.state']:
197 host.run('rm -f ' + control)
mblighc8949b82007-07-23 16:33:58 +0000198
mblighdcd57a82007-07-11 23:06:47 +0000199 # Copy control_file to remote_control_file on the host
200 tmppath = utils.get(control_file)
201 host.send_file(tmppath, atrun.remote_control_file)
mbligh4918bdb2007-12-13 16:06:09 +0000202 if os.path.abspath(tmppath) != os.path.abspath(control_file):
203 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000204
mbligh0e4613b2007-10-29 16:55:07 +0000205 try:
206 atrun.execute_control(timeout=timeout)
mbligh35b225c2007-12-10 17:17:27 +0000207 finally:
mbligh1f194902007-12-20 01:31:43 +0000208 # make an effort to wait for the machine to come up
209 try:
mbligh9e787d22008-02-11 17:56:37 +0000210 host.wait_up(timeout=30)
mbligh1f194902007-12-20 01:31:43 +0000211 except AutoservError:
212 # don't worry about any errors, we'll try and
213 # get the results anyway
214 pass
215
mbligh35b225c2007-12-10 17:17:27 +0000216 # get the results
217 results = os.path.join(atrun.autodir, 'results',
218 'default')
219 # Copy all dirs in default to results_dir
220 host.get_file(results + '/', results_dir)
mblighdcd57a82007-07-11 23:06:47 +0000221
mbligh0e4613b2007-10-29 16:55:07 +0000222
223 def run_timed_test(self, test_name, results_dir = '.', host = None,
224 timeout=None, *args, **dargs):
mblighd54832b2007-07-25 16:46:56 +0000225 """
226 Assemble a tiny little control file to just run one test,
227 and run it as an autotest client-side test
228 """
mblighdd81ef02007-08-10 01:18:40 +0000229 if not host:
230 host = self.host
mbligh91334902007-09-28 01:47:59 +0000231 if not self.installed:
mbligh7fdd1692007-10-02 19:16:53 +0000232 self.install(host)
mbligh271d5af2007-08-10 19:54:01 +0000233 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
234 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
mblighf1c52842007-10-16 15:21:38 +0000235 control = "job.run_test(%s)\n" % cmd
mbligh0e4613b2007-10-29 16:55:07 +0000236 self.run(control, results_dir, host, timeout=timeout)
237
238
239 def run_test(self, test_name, results_dir = '.', host = None,
240 *args, **dargs):
241 self.run_timed_test(test_name, results_dir, host, None,
242 *args, **dargs)
mblighd54832b2007-07-25 16:46:56 +0000243
244
mblighdcd57a82007-07-11 23:06:47 +0000245class _Run(object):
246 """
247 Represents a run of autotest control file. This class maintains
248 all the state necessary as an autotest control file is executed.
249
250 It is not intended to be used directly, rather control files
251 should be run using the run method in Autotest.
252 """
253 def __init__(self, host, results_dir):
254 self.host = host
255 self.results_dir = results_dir
mblighcc53b352007-10-24 21:15:30 +0000256 self.env = host.env
mblighd528d302007-12-19 16:19:05 +0000257
mblighdcd57a82007-07-11 23:06:47 +0000258 self.autodir = _get_autodir(self.host)
mbligha4bece12007-11-24 19:37:14 +0000259 self.manual_control_file = os.path.join(self.autodir, 'control')
260 self.remote_control_file = os.path.join(self.autodir,
261 'control.autoserv')
mblighdc735a22007-08-02 16:54:37 +0000262
263
mblighdcd57a82007-07-11 23:06:47 +0000264 def verify_machine(self):
265 binary = os.path.join(self.autodir, 'bin/autotest')
mblighdd81ef02007-08-10 01:18:40 +0000266 try:
mbligh09f45692008-01-26 22:53:56 +0000267 self.host.run('ls %s > /dev/null 2>&1' % binary)
mblighdd81ef02007-08-10 01:18:40 +0000268 except:
mbligh8d7e3472007-08-10 01:35:18 +0000269 raise "Autotest does not appear to be installed"
270 tmpdir = os.path.join(self.autodir, 'tmp')
271 self.host.run('umount %s' % tmpdir, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000272
273
mblighd8b39252008-03-20 21:15:03 +0000274 def get_full_cmd(self, section):
mblighadf2aab2007-11-29 18:16:43 +0000275 # build up the full command we want to run over the host
276 cmd = [os.path.join(self.autodir, 'bin/autotest_client')]
277 if section > 0:
278 cmd.append('-c')
279 cmd.append(self.remote_control_file)
mblighd8b39252008-03-20 21:15:03 +0000280 return ' '.join(cmd)
mblighadf2aab2007-11-29 18:16:43 +0000281
mblighd8b39252008-03-20 21:15:03 +0000282
283 def get_client_log(self, section):
mblighd528d302007-12-19 16:19:05 +0000284 # open up the files we need for our logging
285 client_log_file = os.path.join(self.results_dir, 'debug',
286 'client.log.%d' % section)
mblighd8b39252008-03-20 21:15:03 +0000287 return open(client_log_file, 'w', 0)
288
289
290 def execute_section(self, section, timeout):
291 print "Executing %s/bin/autotest %s/control phase %d" % \
292 (self.autodir, self.autodir,
293 section)
294
295 full_cmd = self.get_full_cmd(section)
296 client_log = self.get_client_log(section)
297 redirector = server_job.client_logger(self.host.job)
mblighd528d302007-12-19 16:19:05 +0000298
299 try:
mblighd528d302007-12-19 16:19:05 +0000300 result = self.host.run(full_cmd, ignore_status=True,
301 timeout=timeout,
302 stdout_tee=client_log,
303 stderr_tee=redirector)
304 finally:
305 redirector.close()
mbligh2bf2db62007-11-27 00:53:18 +0000306
mblighcf732d12007-11-21 18:15:03 +0000307 if result.exit_status == 1:
mblighfaf0cd42007-11-19 16:00:24 +0000308 self.host.job.aborted = True
mbligh0e4613b2007-10-29 16:55:07 +0000309 if not result.stderr:
310 raise AutotestRunError(
311 "execute_section: %s failed to return anything\n"
312 "stdout:%s\n" % (full_cmd, result.stdout))
313
mbligh2bf2db62007-11-27 00:53:18 +0000314 return redirector.last_line
mblighdc735a22007-08-02 16:54:37 +0000315
316
mbligh0e4613b2007-10-29 16:55:07 +0000317 def execute_control(self, timeout=None):
mblighdcd57a82007-07-11 23:06:47 +0000318 section = 0
mbligh0e4613b2007-10-29 16:55:07 +0000319 time_left = None
320 if timeout:
321 end_time = time.time() + timeout
322 time_left = end_time - time.time()
323 while not timeout or time_left > 0:
mblighd8b39252008-03-20 21:15:03 +0000324 last = self.execute_section(section, time_left)
mbligh0e4613b2007-10-29 16:55:07 +0000325 if timeout:
326 time_left = end_time - time.time()
327 if time_left <= 0:
328 break
mblighdcd57a82007-07-11 23:06:47 +0000329 section += 1
mblighc3430162007-11-14 23:57:19 +0000330 if re.match(r'^END .*\t----\t----\t.*$', last):
mblighdcd57a82007-07-11 23:06:47 +0000331 print "Client complete"
332 return
mblighc3430162007-11-14 23:57:19 +0000333 elif re.match('^\t*GOOD\t----\treboot\.start.*$', last):
mblighdcd57a82007-07-11 23:06:47 +0000334 print "Client is rebooting"
335 print "Waiting for client to halt"
336 if not self.host.wait_down(HALT_TIME):
337 raise AutotestRunError("%s \
338 failed to shutdown after %ds" %
mblighc8949b82007-07-23 16:33:58 +0000339 (self.host.hostname,
mblighdcd57a82007-07-11 23:06:47 +0000340 HALT_TIME))
341 print "Client down, waiting for restart"
342 if not self.host.wait_up(BOOT_TIME):
343 # since reboot failed
344 # hardreset the machine once if possible
345 # before failing this control file
mblighba81c682007-10-25 15:35:59 +0000346 print "Hardresetting %s" % (
347 self.host.hostname,)
348 try:
349 self.host.hardreset(wait=False)
mbligh03f4fc72007-11-29 20:56:14 +0000350 except AutoservUnsupportedError:
mblighba81c682007-10-25 15:35:59 +0000351 print "Hardreset unsupported on %s" % (
352 self.host.hostname,)
mblighc8949b82007-07-23 16:33:58 +0000353 raise AutotestRunError("%s failed to "
354 "boot after %ds" % (
355 self.host.hostname,
356 BOOT_TIME,))
mblighdcd57a82007-07-11 23:06:47 +0000357 continue
mbligh215c55a2008-03-25 14:56:13 +0000358 self.host.job.record("ABORT", None, None,
359 "Autotest client terminated " +
360 "unexpectedly")
mblighc8949b82007-07-23 16:33:58 +0000361 raise AutotestRunError("Aborting - unknown "
362 "return code: %s\n" % last)
mblighdcd57a82007-07-11 23:06:47 +0000363
mbligh0e4613b2007-10-29 16:55:07 +0000364 # should only get here if we timed out
365 assert timeout
366 raise AutotestTimeoutError()
367
mblighdcd57a82007-07-11 23:06:47 +0000368
369def _get_autodir(host):
mblighda13d542008-01-03 16:28:34 +0000370 dir = host.get_autodir()
371 if dir:
372 return dir
mblighdcd57a82007-07-11 23:06:47 +0000373 try:
mblighe988aa52007-11-24 19:40:41 +0000374 # There's no clean way to do this. readlink may not exist
mbligh09f45692008-01-26 22:53:56 +0000375 cmd = "python -c 'import os,sys; print os.readlink(sys.argv[1])' /etc/autotest.conf 2> /dev/null"
mblighe988aa52007-11-24 19:40:41 +0000376 dir = os.path.dirname(host.run(cmd).stdout)
377 if dir:
378 return dir
mbligh03f4fc72007-11-29 20:56:14 +0000379 except AutoservRunError:
mblighdcd57a82007-07-11 23:06:47 +0000380 pass
381 for path in ['/usr/local/autotest', '/home/autotest']:
382 try:
mbligh09f45692008-01-26 22:53:56 +0000383 host.run('ls %s > /dev/null 2>&1' % \
mbligh548197f2007-12-12 15:36:22 +0000384 os.path.join(path, 'bin/autotest'))
mblighdcd57a82007-07-11 23:06:47 +0000385 return path
mbligh03f4fc72007-11-29 20:56:14 +0000386 except AutoservRunError:
mblighdcd57a82007-07-11 23:06:47 +0000387 pass
388 raise AutotestRunError("Cannot figure out autotest directory")
mblighd8b39252008-03-20 21:15:03 +0000389
390
391# site_autotest.py may be non-existant or empty, make sure that an appropriate
392# SiteAutotest class is created nevertheless
393try:
394 from site_autotest import SiteAutotest
395except ImportError:
396 class SiteAutotest(BaseAutotest):
397 pass
398
399
400class Autotest(SiteAutotest):
401 pass