blob: 481a14766543337ef3d8afa20b6e1565ee5d903c [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
27import errors
28import utils
29
30
31AUTOTEST_SVN = 'svn://test.kernel.org/autotest/trunk/client'
32AUTOTEST_HTTP = 'http://test.kernel.org/svn/autotest/trunk/client'
33
34# Timeouts for powering down and up respectively
35HALT_TIME = 300
mbligh07c1eac2007-11-05 18:39:29 +000036BOOT_TIME = 1800
mblighdcd57a82007-07-11 23:06:47 +000037
38
39class AutotestRunError(errors.AutoservRunError):
40 pass
41
mbligh0e4613b2007-10-29 16:55:07 +000042class AutotestTimeoutError(errors.AutoservRunError):
43 """This exception is raised when an autotest test exceeds the timeout
44 parameter passed to run_timed_test and is killed.
45 """
46
mblighdcd57a82007-07-11 23:06:47 +000047
48class Autotest(installable_object.InstallableObject):
mblighdc735a22007-08-02 16:54:37 +000049 """
50 This class represents the Autotest program.
mblighdcd57a82007-07-11 23:06:47 +000051
52 Autotest is used to run tests automatically and collect the results.
53 It also supports profilers.
54
55 Implementation details:
56 This is a leaf class in an abstract class hierarchy, it must
57 implement the unimplemented methods in parent classes.
58 """
mblighdd81ef02007-08-10 01:18:40 +000059 def __init__(self, host = None):
60 self.host = host
mbligh91334902007-09-28 01:47:59 +000061 self.got = False
62 self.installed = False
mbligh9708f732007-10-18 03:18:54 +000063 self.serverdir = utils.get_server_dir()
mblighdcd57a82007-07-11 23:06:47 +000064 super(Autotest, self).__init__()
mblighc8949b82007-07-23 16:33:58 +000065
mblighdc735a22007-08-02 16:54:37 +000066
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()
mbligh7386abc2007-08-31 08:57:56 +000093 host.ensure_up()
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
112 raise "Not yet implemented!"
mbligh91334902007-09-28 01:47:59 +0000113 print "Installation of autotest completed"
114 self.installed = True
mblighdcd57a82007-07-11 23:06:47 +0000115 return
mbligh91334902007-09-28 01:47:59 +0000116
mblighdcd57a82007-07-11 23:06:47 +0000117 # if that fails try to install using svn
118 if utils.run('which svn').exit_status:
119 raise AutoservError('svn not found in path on \
120 target machine: %s' % host.name)
121 try:
122 host.run('svn checkout %s %s' %
mbligh3850f892007-11-05 22:49:24 +0000123 (AUTOTEST_SVN, autodir))
mblighdcd57a82007-07-11 23:06:47 +0000124 except errors.AutoservRunError, e:
125 host.run('svn checkout %s %s' %
mbligh3850f892007-11-05 22:49:24 +0000126 (AUTOTEST_HTTP, autodir))
mbligh91334902007-09-28 01:47:59 +0000127 print "Installation of autotest completed"
128 self.installed = True
129
130
131 def get(self, location = None):
132 if not location:
133 location = os.path.join(self.serverdir, '../client')
134 location = os.path.abspath(location)
mbligh8fc0e5a2007-10-11 18:39:03 +0000135 # If there's stuff run on our client directory already, it
136 # can cause problems. Try giving it a quick clean first.
137 cwd = os.getcwd()
138 os.chdir(location)
139 os.system('tools/make_clean')
140 os.chdir(cwd)
mbligh91334902007-09-28 01:47:59 +0000141 super(Autotest, self).get(location)
142 self.got = True
mblighdcd57a82007-07-11 23:06:47 +0000143
144
mbligh0e4613b2007-10-29 16:55:07 +0000145 def run(self, control_file, results_dir = '.', host = None,
146 timeout=None):
mblighdcd57a82007-07-11 23:06:47 +0000147 """
148 Run an autotest job on the remote machine.
mblighc8949b82007-07-23 16:33:58 +0000149
mblighdcd57a82007-07-11 23:06:47 +0000150 Args:
151 control_file: an open file-like-obj of the control file
152 results_dir: a str path where the results should be stored
153 on the local filesystem
154 host: a Host instance on which the control file should
155 be run
156
157 Raises:
158 AutotestRunError: if there is a problem executing
159 the control file
160 """
mbligh55a2a3b2007-09-30 01:27:55 +0000161 results_dir = os.path.abspath(results_dir)
mblighdd81ef02007-08-10 01:18:40 +0000162 if not host:
163 host = self.host
mbligh91334902007-09-28 01:47:59 +0000164 if not self.installed:
mbligh7fdd1692007-10-02 19:16:53 +0000165 self.install(host)
mbligh91334902007-09-28 01:47:59 +0000166
mblighdbe4a382007-07-26 19:41:28 +0000167 host.ensure_up()
168
mblighdcd57a82007-07-11 23:06:47 +0000169 atrun = _Run(host, results_dir)
mblighe4f01512007-08-10 01:42:50 +0000170 try:
171 atrun.verify_machine()
172 except:
173 print "Verify machine failed on %s. Reinstalling" % \
174 host.hostname
175 self.install(host)
mblighdcd57a82007-07-11 23:06:47 +0000176 atrun.verify_machine()
177 debug = os.path.join(results_dir, 'debug')
mbligh55a2a3b2007-09-30 01:27:55 +0000178 try:
179 os.makedirs(debug)
180 except:
181 pass
mblighc8949b82007-07-23 16:33:58 +0000182
mblighdcd57a82007-07-11 23:06:47 +0000183 # Ready .... Aim ....
184 host.run('rm -f ' + atrun.remote_control_file)
185 host.run('rm -f ' + atrun.remote_control_file + '.state')
mblighc8949b82007-07-23 16:33:58 +0000186
mblighdcd57a82007-07-11 23:06:47 +0000187 # Copy control_file to remote_control_file on the host
188 tmppath = utils.get(control_file)
189 host.send_file(tmppath, atrun.remote_control_file)
190 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000191
192 timeout_exc = None
193 try:
194 atrun.execute_control(timeout=timeout)
195 except AutotestTimeoutError, exc:
196 # on timeout, fall though and try to grab results,
197 # and then reraise
198 timeout_exc = exc
199
200 # try to retrive results, even if a timeout occured
201 results = os.path.join(atrun.autodir, 'results',
202 'default')
mblighdcd57a82007-07-11 23:06:47 +0000203 # Copy all dirs in default to results_dir
204 host.get_file(results + '/', results_dir)
205
mbligh0e4613b2007-10-29 16:55:07 +0000206 if timeout_exc:
207 raise timeout_exc
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)
246 self.remote_control_file = os.path.join(self.autodir, 'control')
mblighdc735a22007-08-02 16:54:37 +0000247
248
mblighdcd57a82007-07-11 23:06:47 +0000249 def verify_machine(self):
250 binary = os.path.join(self.autodir, 'bin/autotest')
mblighdd81ef02007-08-10 01:18:40 +0000251 try:
252 self.host.run('ls ' + binary)
253 except:
mbligh8d7e3472007-08-10 01:35:18 +0000254 raise "Autotest does not appear to be installed"
255 tmpdir = os.path.join(self.autodir, 'tmp')
256 self.host.run('umount %s' % tmpdir, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000257
258
mbligh0e4613b2007-10-29 16:55:07 +0000259 def __execute_section(self, section, timeout):
mblighdcd57a82007-07-11 23:06:47 +0000260 print "Executing %s/bin/autotest %s/control phase %d" % \
261 (self.autodir, self.autodir,
262 section)
263 logfile = "%s/debug/client.log.%d" % (self.results_dir,
264 section)
mblighd2fc50f2007-10-23 22:38:00 +0000265 client_log = open(logfile, 'w', 0)
mblighdcd57a82007-07-11 23:06:47 +0000266 if section > 0:
267 cont = '-c'
268 else:
269 cont = ''
270 client = os.path.join(self.autodir, 'bin/autotest_client')
271 ssh = "ssh -q %s@%s" % (self.host.user, self.host.hostname)
mblighcc53b352007-10-24 21:15:30 +0000272 env = ' '.join(['='.join(i) for i in self.env.iteritems()])
mblighdcd57a82007-07-11 23:06:47 +0000273 cmd = "%s %s %s" % (client, cont, self.remote_control_file)
mbligh0e4613b2007-10-29 16:55:07 +0000274 full_cmd = "%s '%s %s'" % (ssh, env, cmd)
275 print full_cmd
276
mblighd2fc50f2007-10-23 22:38:00 +0000277 status_log_file = os.path.join(self.results_dir, 'status.log')
278 status_log = open(status_log_file, 'a', 0)
mbligh0e4613b2007-10-29 16:55:07 +0000279
280 class StdErrRedirector(object):
281 """Partial file object to write to both stdout and
282 the status log file. We only implement those methods
283 utils.run() actually calls.
284 """
285 def write(self, str):
286 sys.stdout.write(str)
287 status_log.write(str)
288
289 def flush(self):
290 sys.stdout.flush()
291 status_log.flush()
292
293 result = utils.run(full_cmd, ignore_status=True,
294 timeout=timeout,
295 stdout_tee=client_log,
296 stderr_tee=StdErrRedirector())
297 if not result.stderr:
298 raise AutotestRunError(
299 "execute_section: %s failed to return anything\n"
300 "stdout:%s\n" % (full_cmd, result.stdout))
301
302 last_line = result.stderr.strip().rsplit('\n', 1).pop()
303 return last_line
mblighdc735a22007-08-02 16:54:37 +0000304
305
mbligh0e4613b2007-10-29 16:55:07 +0000306 def execute_control(self, timeout=None):
mblighdcd57a82007-07-11 23:06:47 +0000307 section = 0
mbligh0e4613b2007-10-29 16:55:07 +0000308 time_left = None
309 if timeout:
310 end_time = time.time() + timeout
311 time_left = end_time - time.time()
312 while not timeout or time_left > 0:
313 last = self.__execute_section(section, time_left)
314 if timeout:
315 time_left = end_time - time.time()
316 if time_left <= 0:
317 break
mblighdcd57a82007-07-11 23:06:47 +0000318 section += 1
319 if re.match('DONE', last):
320 print "Client complete"
321 return
322 elif re.match('REBOOT', last):
323 print "Client is rebooting"
324 print "Waiting for client to halt"
325 if not self.host.wait_down(HALT_TIME):
326 raise AutotestRunError("%s \
327 failed to shutdown after %ds" %
mblighc8949b82007-07-23 16:33:58 +0000328 (self.host.hostname,
mblighdcd57a82007-07-11 23:06:47 +0000329 HALT_TIME))
330 print "Client down, waiting for restart"
331 if not self.host.wait_up(BOOT_TIME):
332 # since reboot failed
333 # hardreset the machine once if possible
334 # before failing this control file
mblighba81c682007-10-25 15:35:59 +0000335 print "Hardresetting %s" % (
336 self.host.hostname,)
337 try:
338 self.host.hardreset(wait=False)
339 except errors.AutoservUnsupportedError:
340 print "Hardreset unsupported on %s" % (
341 self.host.hostname,)
mblighc8949b82007-07-23 16:33:58 +0000342 raise AutotestRunError("%s failed to "
343 "boot after %ds" % (
344 self.host.hostname,
345 BOOT_TIME,))
mblighdcd57a82007-07-11 23:06:47 +0000346 continue
mblighc8949b82007-07-23 16:33:58 +0000347 raise AutotestRunError("Aborting - unknown "
348 "return code: %s\n" % last)
mblighdcd57a82007-07-11 23:06:47 +0000349
mbligh0e4613b2007-10-29 16:55:07 +0000350 # should only get here if we timed out
351 assert timeout
352 raise AutotestTimeoutError()
353
mblighdcd57a82007-07-11 23:06:47 +0000354
355def _get_autodir(host):
356 try:
357 atdir = host.run(
mblighc8949b82007-07-23 16:33:58 +0000358 'grep "autodir *=" /etc/autotest.conf').stdout.strip()
mblighdcd57a82007-07-11 23:06:47 +0000359 if atdir:
360 m = re.search(r'autodir *= *[\'"]?([^\'"]*)[\'"]?',
361 atdir)
362 return m.group(1)
363 except errors.AutoservRunError:
364 pass
365 for path in ['/usr/local/autotest', '/home/autotest']:
366 try:
367 host.run('ls ' + path)
368 return path
369 except errors.AutoservRunError:
370 pass
371 raise AutotestRunError("Cannot figure out autotest directory")