blob: d843739354c49e065f79939059727092426dabd7 [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
mbligh119c12a2007-11-12 22:13:44 +000028from common import logging
mbligh03f4fc72007-11-29 20:56:14 +000029from common.error import *
mblighdcd57a82007-07-11 23:06:47 +000030
31
32AUTOTEST_SVN = 'svn://test.kernel.org/autotest/trunk/client'
33AUTOTEST_HTTP = 'http://test.kernel.org/svn/autotest/trunk/client'
34
35# Timeouts for powering down and up respectively
36HALT_TIME = 300
mbligh07c1eac2007-11-05 18:39:29 +000037BOOT_TIME = 1800
mblighdcd57a82007-07-11 23:06:47 +000038
39
mbligh03f4fc72007-11-29 20:56:14 +000040class AutotestRunError(AutoservRunError):
mblighdcd57a82007-07-11 23:06:47 +000041 pass
42
mbligh03f4fc72007-11-29 20:56:14 +000043class AutotestTimeoutError(AutoservRunError):
mbligh0e4613b2007-10-29 16:55:07 +000044 """This exception is raised when an autotest test exceeds the timeout
45 parameter passed to run_timed_test and is killed.
46 """
47
mblighdcd57a82007-07-11 23:06:47 +000048
49class Autotest(installable_object.InstallableObject):
mblighdc735a22007-08-02 16:54:37 +000050 """
51 This class represents the Autotest program.
mblighdcd57a82007-07-11 23:06:47 +000052
53 Autotest is used to run tests automatically and collect the results.
54 It also supports profilers.
55
56 Implementation details:
57 This is a leaf class in an abstract class hierarchy, it must
58 implement the unimplemented methods in parent classes.
59 """
mbligh119c12a2007-11-12 22:13:44 +000060 job = None
61
62
mblighdd81ef02007-08-10 01:18:40 +000063 def __init__(self, host = None):
64 self.host = host
mbligh91334902007-09-28 01:47:59 +000065 self.got = False
66 self.installed = False
mbligh9708f732007-10-18 03:18:54 +000067 self.serverdir = utils.get_server_dir()
mblighdcd57a82007-07-11 23:06:47 +000068 super(Autotest, self).__init__()
mblighc8949b82007-07-23 16:33:58 +000069
mblighdc735a22007-08-02 16:54:37 +000070
mbligh119c12a2007-11-12 22:13:44 +000071 @logging.record
mblighdd81ef02007-08-10 01:18:40 +000072 def install(self, host = None):
mblighdc735a22007-08-02 16:54:37 +000073 """
74 Install autotest. If get() was not called previously, an
mblighc8949b82007-07-23 16:33:58 +000075 attempt will be made to install from the autotest svn
76 repository.
mblighdcd57a82007-07-11 23:06:47 +000077
78 Args:
79 host: a Host instance on which autotest will be
80 installed
81
82 Raises:
83 AutoservError: if a tarball was not specified and
84 the target host does not have svn installed in its path
mblighc8949b82007-07-23 16:33:58 +000085
86 TODO(poirier): check dependencies
87 autotest needs:
88 bzcat
89 liboptdev (oprofile)
90 binutils-dev (oprofile)
91 make
mbligh629e39e2007-08-10 19:32:00 +000092 psutils (netperf)
mblighdcd57a82007-07-11 23:06:47 +000093 """
mblighdd81ef02007-08-10 01:18:40 +000094 if not host:
95 host = self.host
mbligh91334902007-09-28 01:47:59 +000096 if not self.got:
97 self.get()
mbligh7386abc2007-08-31 08:57:56 +000098 host.ensure_up()
mbligh91334902007-09-28 01:47:59 +000099 host.setup()
mblighb84a1cf2007-08-09 23:08:13 +0000100 print "Installing autotest on %s" % host.hostname
mbligh40f122a2007-11-03 23:08:46 +0000101
102 autodir = _get_autodir(host)
103 host.run('mkdir -p "%s"' % utils.sh_escape(autodir))
104
105 if getattr(host, 'site_install_autotest', None):
106 if host.site_install_autotest():
107 self.installed = True
108 return
109
mblighdcd57a82007-07-11 23:06:47 +0000110 # try to install from file or directory
mblighc8949b82007-07-23 16:33:58 +0000111 if self.source_material:
112 if os.path.isdir(self.source_material):
mblighdcd57a82007-07-11 23:06:47 +0000113 # Copy autotest recursively
mbligh40f122a2007-11-03 23:08:46 +0000114 host.send_file(self.source_material, autodir)
mblighdcd57a82007-07-11 23:06:47 +0000115 else:
116 # Copy autotest via tarball
117 raise "Not yet implemented!"
mbligh91334902007-09-28 01:47:59 +0000118 print "Installation of autotest completed"
119 self.installed = True
mblighdcd57a82007-07-11 23:06:47 +0000120 return
mbligh91334902007-09-28 01:47:59 +0000121
mblighdcd57a82007-07-11 23:06:47 +0000122 # if that fails try to install using svn
123 if utils.run('which svn').exit_status:
124 raise AutoservError('svn not found in path on \
125 target machine: %s' % host.name)
126 try:
127 host.run('svn checkout %s %s' %
mbligh3850f892007-11-05 22:49:24 +0000128 (AUTOTEST_SVN, autodir))
mbligh03f4fc72007-11-29 20:56:14 +0000129 except AutoservRunError, e:
mblighdcd57a82007-07-11 23:06:47 +0000130 host.run('svn checkout %s %s' %
mbligh3850f892007-11-05 22:49:24 +0000131 (AUTOTEST_HTTP, autodir))
mbligh91334902007-09-28 01:47:59 +0000132 print "Installation of autotest completed"
133 self.installed = True
134
135
136 def get(self, location = None):
137 if not location:
138 location = os.path.join(self.serverdir, '../client')
139 location = os.path.abspath(location)
mbligh8fc0e5a2007-10-11 18:39:03 +0000140 # If there's stuff run on our client directory already, it
141 # can cause problems. Try giving it a quick clean first.
142 cwd = os.getcwd()
143 os.chdir(location)
144 os.system('tools/make_clean')
145 os.chdir(cwd)
mbligh91334902007-09-28 01:47:59 +0000146 super(Autotest, self).get(location)
147 self.got = True
mblighdcd57a82007-07-11 23:06:47 +0000148
149
mbligh0e4613b2007-10-29 16:55:07 +0000150 def run(self, control_file, results_dir = '.', host = None,
151 timeout=None):
mblighdcd57a82007-07-11 23:06:47 +0000152 """
153 Run an autotest job on the remote machine.
mblighc8949b82007-07-23 16:33:58 +0000154
mblighdcd57a82007-07-11 23:06:47 +0000155 Args:
156 control_file: an open file-like-obj of the control file
157 results_dir: a str path where the results should be stored
158 on the local filesystem
159 host: a Host instance on which the control file should
160 be run
161
162 Raises:
163 AutotestRunError: if there is a problem executing
164 the control file
165 """
mbligh55a2a3b2007-09-30 01:27:55 +0000166 results_dir = os.path.abspath(results_dir)
mblighdd81ef02007-08-10 01:18:40 +0000167 if not host:
168 host = self.host
mbligh91334902007-09-28 01:47:59 +0000169 if not self.installed:
mbligh7fdd1692007-10-02 19:16:53 +0000170 self.install(host)
mbligh91334902007-09-28 01:47:59 +0000171
mblighdbe4a382007-07-26 19:41:28 +0000172 host.ensure_up()
173
mblighdcd57a82007-07-11 23:06:47 +0000174 atrun = _Run(host, results_dir)
mblighe4f01512007-08-10 01:42:50 +0000175 try:
176 atrun.verify_machine()
177 except:
178 print "Verify machine failed on %s. Reinstalling" % \
179 host.hostname
180 self.install(host)
mblighdcd57a82007-07-11 23:06:47 +0000181 atrun.verify_machine()
182 debug = os.path.join(results_dir, 'debug')
mbligh55a2a3b2007-09-30 01:27:55 +0000183 try:
184 os.makedirs(debug)
185 except:
186 pass
mblighc8949b82007-07-23 16:33:58 +0000187
mblighdcd57a82007-07-11 23:06:47 +0000188 # Ready .... Aim ....
mbligha4bece12007-11-24 19:37:14 +0000189 for control in [atrun.remote_control_file,
190 atrun.remote_control_file + '.state',
191 atrun.manual_control_file,
192 atrun.manual_control_file + '.state']:
193 host.run('rm -f ' + control)
mblighc8949b82007-07-23 16:33:58 +0000194
mblighdcd57a82007-07-11 23:06:47 +0000195 # Copy control_file to remote_control_file on the host
196 tmppath = utils.get(control_file)
197 host.send_file(tmppath, atrun.remote_control_file)
mbligh4918bdb2007-12-13 16:06:09 +0000198 if os.path.abspath(tmppath) != os.path.abspath(control_file):
199 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000200
mbligh0e4613b2007-10-29 16:55:07 +0000201 try:
202 atrun.execute_control(timeout=timeout)
mbligh35b225c2007-12-10 17:17:27 +0000203 finally:
204 # get the results
205 results = os.path.join(atrun.autodir, 'results',
206 'default')
207 # Copy all dirs in default to results_dir
208 host.get_file(results + '/', results_dir)
mblighdcd57a82007-07-11 23:06:47 +0000209
mbligh0e4613b2007-10-29 16:55:07 +0000210
211 def run_timed_test(self, test_name, results_dir = '.', host = None,
212 timeout=None, *args, **dargs):
mblighd54832b2007-07-25 16:46:56 +0000213 """
214 Assemble a tiny little control file to just run one test,
215 and run it as an autotest client-side test
216 """
mblighdd81ef02007-08-10 01:18:40 +0000217 if not host:
218 host = self.host
mbligh91334902007-09-28 01:47:59 +0000219 if not self.installed:
mbligh7fdd1692007-10-02 19:16:53 +0000220 self.install(host)
mbligh271d5af2007-08-10 19:54:01 +0000221 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
222 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
mblighf1c52842007-10-16 15:21:38 +0000223 control = "job.run_test(%s)\n" % cmd
mbligh0e4613b2007-10-29 16:55:07 +0000224 self.run(control, results_dir, host, timeout=timeout)
225
226
227 def run_test(self, test_name, results_dir = '.', host = None,
228 *args, **dargs):
229 self.run_timed_test(test_name, results_dir, host, None,
230 *args, **dargs)
mblighd54832b2007-07-25 16:46:56 +0000231
232
mblighdcd57a82007-07-11 23:06:47 +0000233class _Run(object):
234 """
235 Represents a run of autotest control file. This class maintains
236 all the state necessary as an autotest control file is executed.
237
238 It is not intended to be used directly, rather control files
239 should be run using the run method in Autotest.
240 """
241 def __init__(self, host, results_dir):
242 self.host = host
243 self.results_dir = results_dir
mblighcc53b352007-10-24 21:15:30 +0000244 self.env = host.env
mblighc8949b82007-07-23 16:33:58 +0000245
mblighdcd57a82007-07-11 23:06:47 +0000246 self.autodir = _get_autodir(self.host)
mbligha4bece12007-11-24 19:37:14 +0000247 self.manual_control_file = os.path.join(self.autodir, 'control')
248 self.remote_control_file = os.path.join(self.autodir,
249 'control.autoserv')
mblighdc735a22007-08-02 16:54:37 +0000250
251
mblighdcd57a82007-07-11 23:06:47 +0000252 def verify_machine(self):
253 binary = os.path.join(self.autodir, 'bin/autotest')
mblighdd81ef02007-08-10 01:18:40 +0000254 try:
mbligh548197f2007-12-12 15:36:22 +0000255 self.host.run('ls %s > /dev/null' % binary)
mblighdd81ef02007-08-10 01:18:40 +0000256 except:
mbligh8d7e3472007-08-10 01:35:18 +0000257 raise "Autotest does not appear to be installed"
258 tmpdir = os.path.join(self.autodir, 'tmp')
259 self.host.run('umount %s' % tmpdir, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000260
261
mbligh0e4613b2007-10-29 16:55:07 +0000262 def __execute_section(self, section, timeout):
mblighdcd57a82007-07-11 23:06:47 +0000263 print "Executing %s/bin/autotest %s/control phase %d" % \
264 (self.autodir, self.autodir,
265 section)
mbligh0e4613b2007-10-29 16:55:07 +0000266
mblighadf2aab2007-11-29 18:16:43 +0000267 # open up the files we need for our logging
268 client_log_file = os.path.join(self.results_dir, 'debug',
269 'client.log.%d' % section)
270 client_log = open(client_log_file, 'w', 0)
mblighd2fc50f2007-10-23 22:38:00 +0000271 status_log_file = os.path.join(self.results_dir, 'status.log')
272 status_log = open(status_log_file, 'a', 0)
mbligh0e4613b2007-10-29 16:55:07 +0000273
mblighadf2aab2007-11-29 18:16:43 +0000274 # create a file-like object for catching the stderr text
275 # from the autotest client and extracting status logs from it
mbligh0e4613b2007-10-29 16:55:07 +0000276 class StdErrRedirector(object):
277 """Partial file object to write to both stdout and
278 the status log file. We only implement those methods
279 utils.run() actually calls.
280 """
mbligh2bf2db62007-11-27 00:53:18 +0000281 def __init__(self):
282 self.leftover = ""
283 self.last_line = ""
mbligh0e4613b2007-10-29 16:55:07 +0000284
mbligh2bf2db62007-11-27 00:53:18 +0000285 def _process_line(self, line):
286 """Write out a line of data to the appropriate
287 stream. Status lines sent by autotest will be
288 prepended with "AUTOTEST_STATUS", and all other
289 lines are ssh error messages.
290 """
291 if line.startswith("AUTOTEST_STATUS:"):
292 line = line[16:] + "\n"
293 sys.stdout.write(line)
294 status_log.write(line)
295 self.last_line = line
296 else:
297 sys.stderr.write(line + "\n")
298
299 def write(self, data):
300 data = self.leftover + data
301 lines = data.split("\n")
302 # process every line but the last one
303 for line in lines[:-1]:
304 self._process_line(line)
305 # save the last line for later processing
306 # since we may not have the whole line yet
307 self.leftover = lines[-1]
mbligha4bece12007-11-24 19:37:14 +0000308
mbligh0e4613b2007-10-29 16:55:07 +0000309 def flush(self):
310 sys.stdout.flush()
mbligh2bf2db62007-11-27 00:53:18 +0000311 sys.stderr.flush()
mbligh0e4613b2007-10-29 16:55:07 +0000312 status_log.flush()
313
mbligh2bf2db62007-11-27 00:53:18 +0000314 def close(self):
315 if self.leftover:
316 self._process_line(self.leftover)
317 self.flush()
mbligh2bf2db62007-11-27 00:53:18 +0000318 redirector = StdErrRedirector()
mblighadf2aab2007-11-29 18:16:43 +0000319
320 # build up the full command we want to run over the host
321 cmd = [os.path.join(self.autodir, 'bin/autotest_client')]
322 if section > 0:
323 cmd.append('-c')
324 cmd.append(self.remote_control_file)
325 full_cmd = ' '.join(cmd)
326
327 result = self.host.run(full_cmd, ignore_status=True,
328 timeout=timeout,
329 stdout_tee=client_log,
330 stderr_tee=redirector)
mbligh2bf2db62007-11-27 00:53:18 +0000331 redirector.close()
332
mblighcf732d12007-11-21 18:15:03 +0000333 if result.exit_status == 1:
mblighfaf0cd42007-11-19 16:00:24 +0000334 self.host.job.aborted = True
mbligh0e4613b2007-10-29 16:55:07 +0000335 if not result.stderr:
336 raise AutotestRunError(
337 "execute_section: %s failed to return anything\n"
338 "stdout:%s\n" % (full_cmd, result.stdout))
339
mbligh2bf2db62007-11-27 00:53:18 +0000340 return redirector.last_line
mblighdc735a22007-08-02 16:54:37 +0000341
342
mbligh0e4613b2007-10-29 16:55:07 +0000343 def execute_control(self, timeout=None):
mblighdcd57a82007-07-11 23:06:47 +0000344 section = 0
mbligh0e4613b2007-10-29 16:55:07 +0000345 time_left = None
346 if timeout:
347 end_time = time.time() + timeout
348 time_left = end_time - time.time()
349 while not timeout or time_left > 0:
350 last = self.__execute_section(section, time_left)
351 if timeout:
352 time_left = end_time - time.time()
353 if time_left <= 0:
354 break
mblighdcd57a82007-07-11 23:06:47 +0000355 section += 1
mblighc3430162007-11-14 23:57:19 +0000356 if re.match(r'^END .*\t----\t----\t.*$', last):
mblighdcd57a82007-07-11 23:06:47 +0000357 print "Client complete"
358 return
mblighc3430162007-11-14 23:57:19 +0000359 elif re.match('^\t*GOOD\t----\treboot\.start.*$', last):
mblighdcd57a82007-07-11 23:06:47 +0000360 print "Client is rebooting"
361 print "Waiting for client to halt"
362 if not self.host.wait_down(HALT_TIME):
363 raise AutotestRunError("%s \
364 failed to shutdown after %ds" %
mblighc8949b82007-07-23 16:33:58 +0000365 (self.host.hostname,
mblighdcd57a82007-07-11 23:06:47 +0000366 HALT_TIME))
367 print "Client down, waiting for restart"
368 if not self.host.wait_up(BOOT_TIME):
369 # since reboot failed
370 # hardreset the machine once if possible
371 # before failing this control file
mblighba81c682007-10-25 15:35:59 +0000372 print "Hardresetting %s" % (
373 self.host.hostname,)
374 try:
375 self.host.hardreset(wait=False)
mbligh03f4fc72007-11-29 20:56:14 +0000376 except AutoservUnsupportedError:
mblighba81c682007-10-25 15:35:59 +0000377 print "Hardreset unsupported on %s" % (
378 self.host.hostname,)
mblighc8949b82007-07-23 16:33:58 +0000379 raise AutotestRunError("%s failed to "
380 "boot after %ds" % (
381 self.host.hostname,
382 BOOT_TIME,))
mblighdcd57a82007-07-11 23:06:47 +0000383 continue
mblighc8949b82007-07-23 16:33:58 +0000384 raise AutotestRunError("Aborting - unknown "
385 "return code: %s\n" % last)
mblighdcd57a82007-07-11 23:06:47 +0000386
mbligh0e4613b2007-10-29 16:55:07 +0000387 # should only get here if we timed out
388 assert timeout
389 raise AutotestTimeoutError()
390
mblighdcd57a82007-07-11 23:06:47 +0000391
392def _get_autodir(host):
393 try:
mblighe988aa52007-11-24 19:40:41 +0000394 # There's no clean way to do this. readlink may not exist
395 cmd = "python -c 'import os,sys; print os.readlink(sys.argv[1])' /etc/autotest.conf"
396 dir = os.path.dirname(host.run(cmd).stdout)
397 if dir:
398 return dir
mbligh03f4fc72007-11-29 20:56:14 +0000399 except AutoservRunError:
mblighdcd57a82007-07-11 23:06:47 +0000400 pass
401 for path in ['/usr/local/autotest', '/home/autotest']:
402 try:
mbligh548197f2007-12-12 15:36:22 +0000403 host.run('ls %s > /dev/null' % \
404 os.path.join(path, 'bin/autotest'))
mblighdcd57a82007-07-11 23:06:47 +0000405 return path
mbligh03f4fc72007-11-29 20:56:14 +0000406 except AutoservRunError:
mblighdcd57a82007-07-11 23:06:47 +0000407 pass
408 raise AutotestRunError("Cannot figure out autotest directory")