blob: d72973f35999a5181a70f9f8bd1aad46d4a178cb [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
44class Autotest(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()
mblighdcd57a82007-07-11 23:06:47 +000063 super(Autotest, 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)
mbligh91334902007-09-28 01:47:59 +0000142 super(Autotest, self).get(location)
143 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 """
mbligh55a2a3b2007-09-30 01:27:55 +0000162 results_dir = os.path.abspath(results_dir)
mblighdd81ef02007-08-10 01:18:40 +0000163 if not host:
164 host = self.host
mbligh91334902007-09-28 01:47:59 +0000165 if not self.installed:
mbligh7fdd1692007-10-02 19:16:53 +0000166 self.install(host)
mbligh91334902007-09-28 01:47:59 +0000167
mbligh9e787d22008-02-11 17:56:37 +0000168 host.wait_up(timeout=30)
mblighdbe4a382007-07-26 19:41:28 +0000169
mblighdcd57a82007-07-11 23:06:47 +0000170 atrun = _Run(host, results_dir)
mblighe4f01512007-08-10 01:42:50 +0000171 try:
172 atrun.verify_machine()
173 except:
174 print "Verify machine failed on %s. Reinstalling" % \
175 host.hostname
176 self.install(host)
mblighdcd57a82007-07-11 23:06:47 +0000177 atrun.verify_machine()
178 debug = os.path.join(results_dir, 'debug')
mbligh55a2a3b2007-09-30 01:27:55 +0000179 try:
180 os.makedirs(debug)
181 except:
182 pass
mblighc8949b82007-07-23 16:33:58 +0000183
mblighdcd57a82007-07-11 23:06:47 +0000184 # Ready .... Aim ....
mbligha4bece12007-11-24 19:37:14 +0000185 for control in [atrun.remote_control_file,
186 atrun.remote_control_file + '.state',
187 atrun.manual_control_file,
188 atrun.manual_control_file + '.state']:
189 host.run('rm -f ' + control)
mblighc8949b82007-07-23 16:33:58 +0000190
mblighdcd57a82007-07-11 23:06:47 +0000191 # Copy control_file to remote_control_file on the host
192 tmppath = utils.get(control_file)
193 host.send_file(tmppath, atrun.remote_control_file)
mbligh4918bdb2007-12-13 16:06:09 +0000194 if os.path.abspath(tmppath) != os.path.abspath(control_file):
195 os.remove(tmppath)
mbligh0e4613b2007-10-29 16:55:07 +0000196
mbligh0e4613b2007-10-29 16:55:07 +0000197 try:
198 atrun.execute_control(timeout=timeout)
mbligh35b225c2007-12-10 17:17:27 +0000199 finally:
mbligh1f194902007-12-20 01:31:43 +0000200 # make an effort to wait for the machine to come up
201 try:
mbligh9e787d22008-02-11 17:56:37 +0000202 host.wait_up(timeout=30)
mbligh1f194902007-12-20 01:31:43 +0000203 except AutoservError:
204 # don't worry about any errors, we'll try and
205 # get the results anyway
206 pass
207
mbligh35b225c2007-12-10 17:17:27 +0000208 # get the results
209 results = os.path.join(atrun.autodir, 'results',
210 'default')
211 # Copy all dirs in default to results_dir
212 host.get_file(results + '/', results_dir)
mblighdcd57a82007-07-11 23:06:47 +0000213
mbligh0e4613b2007-10-29 16:55:07 +0000214
215 def run_timed_test(self, test_name, results_dir = '.', host = None,
216 timeout=None, *args, **dargs):
mblighd54832b2007-07-25 16:46:56 +0000217 """
218 Assemble a tiny little control file to just run one test,
219 and run it as an autotest client-side test
220 """
mblighdd81ef02007-08-10 01:18:40 +0000221 if not host:
222 host = self.host
mbligh91334902007-09-28 01:47:59 +0000223 if not self.installed:
mbligh7fdd1692007-10-02 19:16:53 +0000224 self.install(host)
mbligh271d5af2007-08-10 19:54:01 +0000225 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
226 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
mblighf1c52842007-10-16 15:21:38 +0000227 control = "job.run_test(%s)\n" % cmd
mbligh0e4613b2007-10-29 16:55:07 +0000228 self.run(control, results_dir, host, timeout=timeout)
229
230
231 def run_test(self, test_name, results_dir = '.', host = None,
232 *args, **dargs):
233 self.run_timed_test(test_name, results_dir, host, None,
234 *args, **dargs)
mblighd54832b2007-07-25 16:46:56 +0000235
236
mblighdcd57a82007-07-11 23:06:47 +0000237class _Run(object):
238 """
239 Represents a run of autotest control file. This class maintains
240 all the state necessary as an autotest control file is executed.
241
242 It is not intended to be used directly, rather control files
243 should be run using the run method in Autotest.
244 """
245 def __init__(self, host, results_dir):
246 self.host = host
247 self.results_dir = results_dir
mblighcc53b352007-10-24 21:15:30 +0000248 self.env = host.env
mblighd528d302007-12-19 16:19:05 +0000249
mblighdcd57a82007-07-11 23:06:47 +0000250 self.autodir = _get_autodir(self.host)
mbligha4bece12007-11-24 19:37:14 +0000251 self.manual_control_file = os.path.join(self.autodir, 'control')
252 self.remote_control_file = os.path.join(self.autodir,
253 'control.autoserv')
mblighdc735a22007-08-02 16:54:37 +0000254
255
mblighdcd57a82007-07-11 23:06:47 +0000256 def verify_machine(self):
257 binary = os.path.join(self.autodir, 'bin/autotest')
mblighdd81ef02007-08-10 01:18:40 +0000258 try:
mbligh09f45692008-01-26 22:53:56 +0000259 self.host.run('ls %s > /dev/null 2>&1' % binary)
mblighdd81ef02007-08-10 01:18:40 +0000260 except:
mbligh8d7e3472007-08-10 01:35:18 +0000261 raise "Autotest does not appear to be installed"
262 tmpdir = os.path.join(self.autodir, 'tmp')
263 self.host.run('umount %s' % tmpdir, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000264
265
mbligh0e4613b2007-10-29 16:55:07 +0000266 def __execute_section(self, section, timeout):
mblighdcd57a82007-07-11 23:06:47 +0000267 print "Executing %s/bin/autotest %s/control phase %d" % \
268 (self.autodir, self.autodir,
269 section)
mbligh0e4613b2007-10-29 16:55:07 +0000270
mblighadf2aab2007-11-29 18:16:43 +0000271 # build up the full command we want to run over the host
272 cmd = [os.path.join(self.autodir, 'bin/autotest_client')]
273 if section > 0:
274 cmd.append('-c')
275 cmd.append(self.remote_control_file)
276 full_cmd = ' '.join(cmd)
277
mblighd528d302007-12-19 16:19:05 +0000278 # open up the files we need for our logging
279 client_log_file = os.path.join(self.results_dir, 'debug',
280 'client.log.%d' % section)
281 client_log = open(client_log_file, 'w', 0)
mblighd528d302007-12-19 16:19:05 +0000282
283 try:
mblighdab39662008-02-27 16:47:55 +0000284 redirector = server_job.client_logger(self.host.job)
mblighd528d302007-12-19 16:19:05 +0000285 result = self.host.run(full_cmd, ignore_status=True,
286 timeout=timeout,
287 stdout_tee=client_log,
288 stderr_tee=redirector)
289 finally:
290 redirector.close()
mbligh2bf2db62007-11-27 00:53:18 +0000291
mblighcf732d12007-11-21 18:15:03 +0000292 if result.exit_status == 1:
mblighfaf0cd42007-11-19 16:00:24 +0000293 self.host.job.aborted = True
mbligh0e4613b2007-10-29 16:55:07 +0000294 if not result.stderr:
295 raise AutotestRunError(
296 "execute_section: %s failed to return anything\n"
297 "stdout:%s\n" % (full_cmd, result.stdout))
298
mbligh2bf2db62007-11-27 00:53:18 +0000299 return redirector.last_line
mblighdc735a22007-08-02 16:54:37 +0000300
301
mbligh0e4613b2007-10-29 16:55:07 +0000302 def execute_control(self, timeout=None):
mblighdcd57a82007-07-11 23:06:47 +0000303 section = 0
mbligh0e4613b2007-10-29 16:55:07 +0000304 time_left = None
305 if timeout:
306 end_time = time.time() + timeout
307 time_left = end_time - time.time()
308 while not timeout or time_left > 0:
309 last = self.__execute_section(section, time_left)
310 if timeout:
311 time_left = end_time - time.time()
312 if time_left <= 0:
313 break
mblighdcd57a82007-07-11 23:06:47 +0000314 section += 1
mblighc3430162007-11-14 23:57:19 +0000315 if re.match(r'^END .*\t----\t----\t.*$', last):
mblighdcd57a82007-07-11 23:06:47 +0000316 print "Client complete"
317 return
mblighc3430162007-11-14 23:57:19 +0000318 elif re.match('^\t*GOOD\t----\treboot\.start.*$', last):
mblighdcd57a82007-07-11 23:06:47 +0000319 print "Client is rebooting"
320 print "Waiting for client to halt"
321 if not self.host.wait_down(HALT_TIME):
322 raise AutotestRunError("%s \
323 failed to shutdown after %ds" %
mblighc8949b82007-07-23 16:33:58 +0000324 (self.host.hostname,
mblighdcd57a82007-07-11 23:06:47 +0000325 HALT_TIME))
326 print "Client down, waiting for restart"
327 if not self.host.wait_up(BOOT_TIME):
328 # since reboot failed
329 # hardreset the machine once if possible
330 # before failing this control file
mblighba81c682007-10-25 15:35:59 +0000331 print "Hardresetting %s" % (
332 self.host.hostname,)
333 try:
334 self.host.hardreset(wait=False)
mbligh03f4fc72007-11-29 20:56:14 +0000335 except AutoservUnsupportedError:
mblighba81c682007-10-25 15:35:59 +0000336 print "Hardreset unsupported on %s" % (
337 self.host.hostname,)
mblighc8949b82007-07-23 16:33:58 +0000338 raise AutotestRunError("%s failed to "
339 "boot after %ds" % (
340 self.host.hostname,
341 BOOT_TIME,))
mblighdcd57a82007-07-11 23:06:47 +0000342 continue
mblighc8949b82007-07-23 16:33:58 +0000343 raise AutotestRunError("Aborting - unknown "
344 "return code: %s\n" % last)
mblighdcd57a82007-07-11 23:06:47 +0000345
mbligh0e4613b2007-10-29 16:55:07 +0000346 # should only get here if we timed out
347 assert timeout
348 raise AutotestTimeoutError()
349
mblighdcd57a82007-07-11 23:06:47 +0000350
351def _get_autodir(host):
mblighda13d542008-01-03 16:28:34 +0000352 dir = host.get_autodir()
353 if dir:
354 return dir
mblighdcd57a82007-07-11 23:06:47 +0000355 try:
mblighe988aa52007-11-24 19:40:41 +0000356 # There's no clean way to do this. readlink may not exist
mbligh09f45692008-01-26 22:53:56 +0000357 cmd = "python -c 'import os,sys; print os.readlink(sys.argv[1])' /etc/autotest.conf 2> /dev/null"
mblighe988aa52007-11-24 19:40:41 +0000358 dir = os.path.dirname(host.run(cmd).stdout)
359 if dir:
360 return dir
mbligh03f4fc72007-11-29 20:56:14 +0000361 except AutoservRunError:
mblighdcd57a82007-07-11 23:06:47 +0000362 pass
363 for path in ['/usr/local/autotest', '/home/autotest']:
364 try:
mbligh09f45692008-01-26 22:53:56 +0000365 host.run('ls %s > /dev/null 2>&1' % \
mbligh548197f2007-12-12 15:36:22 +0000366 os.path.join(path, 'bin/autotest'))
mblighdcd57a82007-07-11 23:06:47 +0000367 return path
mbligh03f4fc72007-11-29 20:56:14 +0000368 except AutoservRunError:
mblighdcd57a82007-07-11 23:06:47 +0000369 pass
370 raise AutotestRunError("Cannot figure out autotest directory")