blob: 81885f5711929f037e2da39c4ca9730f0c760df2 [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)
198 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000199
mbligh0e4613b2007-10-29 16:55:07 +0000200 try:
201 atrun.execute_control(timeout=timeout)
mbligh35b225c2007-12-10 17:17:27 +0000202 finally:
203 # get the results
204 results = os.path.join(atrun.autodir, 'results',
205 'default')
206 # Copy all dirs in default to results_dir
207 host.get_file(results + '/', results_dir)
mblighdcd57a82007-07-11 23:06:47 +0000208
mbligh0e4613b2007-10-29 16:55:07 +0000209
210 def run_timed_test(self, test_name, results_dir = '.', host = None,
211 timeout=None, *args, **dargs):
mblighd54832b2007-07-25 16:46:56 +0000212 """
213 Assemble a tiny little control file to just run one test,
214 and run it as an autotest client-side test
215 """
mblighdd81ef02007-08-10 01:18:40 +0000216 if not host:
217 host = self.host
mbligh91334902007-09-28 01:47:59 +0000218 if not self.installed:
mbligh7fdd1692007-10-02 19:16:53 +0000219 self.install(host)
mbligh271d5af2007-08-10 19:54:01 +0000220 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
221 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
mblighf1c52842007-10-16 15:21:38 +0000222 control = "job.run_test(%s)\n" % cmd
mbligh0e4613b2007-10-29 16:55:07 +0000223 self.run(control, results_dir, host, timeout=timeout)
224
225
226 def run_test(self, test_name, results_dir = '.', host = None,
227 *args, **dargs):
228 self.run_timed_test(test_name, results_dir, host, None,
229 *args, **dargs)
mblighd54832b2007-07-25 16:46:56 +0000230
231
mblighdcd57a82007-07-11 23:06:47 +0000232class _Run(object):
233 """
234 Represents a run of autotest control file. This class maintains
235 all the state necessary as an autotest control file is executed.
236
237 It is not intended to be used directly, rather control files
238 should be run using the run method in Autotest.
239 """
240 def __init__(self, host, results_dir):
241 self.host = host
242 self.results_dir = results_dir
mblighcc53b352007-10-24 21:15:30 +0000243 self.env = host.env
mblighc8949b82007-07-23 16:33:58 +0000244
mblighdcd57a82007-07-11 23:06:47 +0000245 self.autodir = _get_autodir(self.host)
mbligha4bece12007-11-24 19:37:14 +0000246 self.manual_control_file = os.path.join(self.autodir, 'control')
247 self.remote_control_file = os.path.join(self.autodir,
248 'control.autoserv')
mblighdc735a22007-08-02 16:54:37 +0000249
250
mblighdcd57a82007-07-11 23:06:47 +0000251 def verify_machine(self):
252 binary = os.path.join(self.autodir, 'bin/autotest')
mblighdd81ef02007-08-10 01:18:40 +0000253 try:
254 self.host.run('ls ' + binary)
255 except:
mbligh8d7e3472007-08-10 01:35:18 +0000256 raise "Autotest does not appear to be installed"
257 tmpdir = os.path.join(self.autodir, 'tmp')
258 self.host.run('umount %s' % tmpdir, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000259
260
mbligh0e4613b2007-10-29 16:55:07 +0000261 def __execute_section(self, section, timeout):
mblighdcd57a82007-07-11 23:06:47 +0000262 print "Executing %s/bin/autotest %s/control phase %d" % \
263 (self.autodir, self.autodir,
264 section)
mbligh0e4613b2007-10-29 16:55:07 +0000265
mblighadf2aab2007-11-29 18:16:43 +0000266 # open up the files we need for our logging
267 client_log_file = os.path.join(self.results_dir, 'debug',
268 'client.log.%d' % section)
269 client_log = open(client_log_file, 'w', 0)
mblighd2fc50f2007-10-23 22:38:00 +0000270 status_log_file = os.path.join(self.results_dir, 'status.log')
271 status_log = open(status_log_file, 'a', 0)
mbligh0e4613b2007-10-29 16:55:07 +0000272
mblighadf2aab2007-11-29 18:16:43 +0000273 # create a file-like object for catching the stderr text
274 # from the autotest client and extracting status logs from it
mbligh0e4613b2007-10-29 16:55:07 +0000275 class StdErrRedirector(object):
276 """Partial file object to write to both stdout and
277 the status log file. We only implement those methods
278 utils.run() actually calls.
279 """
mbligh2bf2db62007-11-27 00:53:18 +0000280 def __init__(self):
281 self.leftover = ""
282 self.last_line = ""
mbligh0e4613b2007-10-29 16:55:07 +0000283
mbligh2bf2db62007-11-27 00:53:18 +0000284 def _process_line(self, line):
285 """Write out a line of data to the appropriate
286 stream. Status lines sent by autotest will be
287 prepended with "AUTOTEST_STATUS", and all other
288 lines are ssh error messages.
289 """
290 if line.startswith("AUTOTEST_STATUS:"):
291 line = line[16:] + "\n"
292 sys.stdout.write(line)
293 status_log.write(line)
294 self.last_line = line
295 else:
296 sys.stderr.write(line + "\n")
297
298 def write(self, data):
299 data = self.leftover + data
300 lines = data.split("\n")
301 # process every line but the last one
302 for line in lines[:-1]:
303 self._process_line(line)
304 # save the last line for later processing
305 # since we may not have the whole line yet
306 self.leftover = lines[-1]
mbligha4bece12007-11-24 19:37:14 +0000307
mbligh0e4613b2007-10-29 16:55:07 +0000308 def flush(self):
309 sys.stdout.flush()
mbligh2bf2db62007-11-27 00:53:18 +0000310 sys.stderr.flush()
mbligh0e4613b2007-10-29 16:55:07 +0000311 status_log.flush()
312
mbligh2bf2db62007-11-27 00:53:18 +0000313 def close(self):
314 if self.leftover:
315 self._process_line(self.leftover)
316 self.flush()
mbligh2bf2db62007-11-27 00:53:18 +0000317 redirector = StdErrRedirector()
mblighadf2aab2007-11-29 18:16:43 +0000318
319 # build up the full command we want to run over the host
320 cmd = [os.path.join(self.autodir, 'bin/autotest_client')]
321 if section > 0:
322 cmd.append('-c')
323 cmd.append(self.remote_control_file)
324 full_cmd = ' '.join(cmd)
325
326 result = self.host.run(full_cmd, ignore_status=True,
327 timeout=timeout,
328 stdout_tee=client_log,
329 stderr_tee=redirector)
mbligh2bf2db62007-11-27 00:53:18 +0000330 redirector.close()
331
mblighcf732d12007-11-21 18:15:03 +0000332 if result.exit_status == 1:
mblighfaf0cd42007-11-19 16:00:24 +0000333 self.host.job.aborted = True
mbligh0e4613b2007-10-29 16:55:07 +0000334 if not result.stderr:
335 raise AutotestRunError(
336 "execute_section: %s failed to return anything\n"
337 "stdout:%s\n" % (full_cmd, result.stdout))
338
mbligh2bf2db62007-11-27 00:53:18 +0000339 return redirector.last_line
mblighdc735a22007-08-02 16:54:37 +0000340
341
mbligh0e4613b2007-10-29 16:55:07 +0000342 def execute_control(self, timeout=None):
mblighdcd57a82007-07-11 23:06:47 +0000343 section = 0
mbligh0e4613b2007-10-29 16:55:07 +0000344 time_left = None
345 if timeout:
346 end_time = time.time() + timeout
347 time_left = end_time - time.time()
348 while not timeout or time_left > 0:
349 last = self.__execute_section(section, time_left)
350 if timeout:
351 time_left = end_time - time.time()
352 if time_left <= 0:
353 break
mblighdcd57a82007-07-11 23:06:47 +0000354 section += 1
mblighc3430162007-11-14 23:57:19 +0000355 if re.match(r'^END .*\t----\t----\t.*$', last):
mblighdcd57a82007-07-11 23:06:47 +0000356 print "Client complete"
357 return
mblighc3430162007-11-14 23:57:19 +0000358 elif re.match('^\t*GOOD\t----\treboot\.start.*$', last):
mblighdcd57a82007-07-11 23:06:47 +0000359 print "Client is rebooting"
360 print "Waiting for client to halt"
361 if not self.host.wait_down(HALT_TIME):
362 raise AutotestRunError("%s \
363 failed to shutdown after %ds" %
mblighc8949b82007-07-23 16:33:58 +0000364 (self.host.hostname,
mblighdcd57a82007-07-11 23:06:47 +0000365 HALT_TIME))
366 print "Client down, waiting for restart"
367 if not self.host.wait_up(BOOT_TIME):
368 # since reboot failed
369 # hardreset the machine once if possible
370 # before failing this control file
mblighba81c682007-10-25 15:35:59 +0000371 print "Hardresetting %s" % (
372 self.host.hostname,)
373 try:
374 self.host.hardreset(wait=False)
mbligh03f4fc72007-11-29 20:56:14 +0000375 except AutoservUnsupportedError:
mblighba81c682007-10-25 15:35:59 +0000376 print "Hardreset unsupported on %s" % (
377 self.host.hostname,)
mblighc8949b82007-07-23 16:33:58 +0000378 raise AutotestRunError("%s failed to "
379 "boot after %ds" % (
380 self.host.hostname,
381 BOOT_TIME,))
mblighdcd57a82007-07-11 23:06:47 +0000382 continue
mblighc8949b82007-07-23 16:33:58 +0000383 raise AutotestRunError("Aborting - unknown "
384 "return code: %s\n" % last)
mblighdcd57a82007-07-11 23:06:47 +0000385
mbligh0e4613b2007-10-29 16:55:07 +0000386 # should only get here if we timed out
387 assert timeout
388 raise AutotestTimeoutError()
389
mblighdcd57a82007-07-11 23:06:47 +0000390
391def _get_autodir(host):
392 try:
mblighe988aa52007-11-24 19:40:41 +0000393 # There's no clean way to do this. readlink may not exist
394 cmd = "python -c 'import os,sys; print os.readlink(sys.argv[1])' /etc/autotest.conf"
395 dir = os.path.dirname(host.run(cmd).stdout)
396 if dir:
397 return dir
mbligh03f4fc72007-11-29 20:56:14 +0000398 except AutoservRunError:
mblighdcd57a82007-07-11 23:06:47 +0000399 pass
400 for path in ['/usr/local/autotest', '/home/autotest']:
401 try:
mblighe988aa52007-11-24 19:40:41 +0000402 host.run('ls ' + os.path.join(path, 'bin/autotest'))
mblighdcd57a82007-07-11 23:06:47 +0000403 return path
mbligh03f4fc72007-11-29 20:56:14 +0000404 except AutoservRunError:
mblighdcd57a82007-07-11 23:06:47 +0000405 pass
406 raise AutotestRunError("Cannot figure out autotest directory")