blob: 697597d3b7b0b13c2adaadc7e23b4ffbad37da32 [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
24
25import installable_object
26import errors
27import utils
28
29
30AUTOTEST_SVN = 'svn://test.kernel.org/autotest/trunk/client'
31AUTOTEST_HTTP = 'http://test.kernel.org/svn/autotest/trunk/client'
32
33# Timeouts for powering down and up respectively
34HALT_TIME = 300
35BOOT_TIME = 300
36
37
38class AutotestRunError(errors.AutoservRunError):
39 pass
40
41
42class Autotest(installable_object.InstallableObject):
mblighdc735a22007-08-02 16:54:37 +000043 """
44 This class represents the Autotest program.
mblighdcd57a82007-07-11 23:06:47 +000045
46 Autotest is used to run tests automatically and collect the results.
47 It also supports profilers.
48
49 Implementation details:
50 This is a leaf class in an abstract class hierarchy, it must
51 implement the unimplemented methods in parent classes.
52 """
mblighdd81ef02007-08-10 01:18:40 +000053 def __init__(self, host = None):
54 self.host = host
mblighdcd57a82007-07-11 23:06:47 +000055 super(Autotest, self).__init__()
mblighc8949b82007-07-23 16:33:58 +000056
mblighdc735a22007-08-02 16:54:37 +000057
mblighdd81ef02007-08-10 01:18:40 +000058 def install(self, host = None):
mblighdc735a22007-08-02 16:54:37 +000059 """
60 Install autotest. If get() was not called previously, an
mblighc8949b82007-07-23 16:33:58 +000061 attempt will be made to install from the autotest svn
62 repository.
mblighdcd57a82007-07-11 23:06:47 +000063
64 Args:
65 host: a Host instance on which autotest will be
66 installed
67
68 Raises:
69 AutoservError: if a tarball was not specified and
70 the target host does not have svn installed in its path
mblighc8949b82007-07-23 16:33:58 +000071
72 TODO(poirier): check dependencies
73 autotest needs:
74 bzcat
75 liboptdev (oprofile)
76 binutils-dev (oprofile)
77 make
mblighdcd57a82007-07-11 23:06:47 +000078 """
mblighdd81ef02007-08-10 01:18:40 +000079 if not host:
80 host = self.host
mblighb84a1cf2007-08-09 23:08:13 +000081 print "Installing autotest on %s" % host.hostname
mblighdcd57a82007-07-11 23:06:47 +000082 # try to install from file or directory
mblighc8949b82007-07-23 16:33:58 +000083 if self.source_material:
84 if os.path.isdir(self.source_material):
mblighdcd57a82007-07-11 23:06:47 +000085 # Copy autotest recursively
86 autodir = _get_autodir(host)
mblighf238b452007-07-25 21:11:14 +000087 host.run('mkdir -p "%s"' %
88 utils.sh_escape(autodir))
mblighc8949b82007-07-23 16:33:58 +000089 host.send_file(self.source_material,
mblighdcd57a82007-07-11 23:06:47 +000090 autodir)
91 else:
92 # Copy autotest via tarball
93 raise "Not yet implemented!"
94 return
mblighc8949b82007-07-23 16:33:58 +000095
mblighdcd57a82007-07-11 23:06:47 +000096 # if that fails try to install using svn
97 if utils.run('which svn').exit_status:
98 raise AutoservError('svn not found in path on \
99 target machine: %s' % host.name)
100 try:
101 host.run('svn checkout %s %s' %
102 (AUTOTEST_SVN, _get_autodir(host)))
103 except errors.AutoservRunError, e:
104 host.run('svn checkout %s %s' %
105 (AUTOTEST_HTTP, _get_autodir(host)))
106
107
mblighdd81ef02007-08-10 01:18:40 +0000108 def run(self, control_file, results_dir, host = None):
mblighdcd57a82007-07-11 23:06:47 +0000109 """
110 Run an autotest job on the remote machine.
mblighc8949b82007-07-23 16:33:58 +0000111
mblighdcd57a82007-07-11 23:06:47 +0000112 Args:
113 control_file: an open file-like-obj of the control file
114 results_dir: a str path where the results should be stored
115 on the local filesystem
116 host: a Host instance on which the control file should
117 be run
118
119 Raises:
120 AutotestRunError: if there is a problem executing
121 the control file
122 """
mblighdd81ef02007-08-10 01:18:40 +0000123 if not host:
124 host = self.host
mblighdbe4a382007-07-26 19:41:28 +0000125 host.ensure_up()
126
mblighdcd57a82007-07-11 23:06:47 +0000127 atrun = _Run(host, results_dir)
128 atrun.verify_machine()
mblighc8949b82007-07-23 16:33:58 +0000129 if os.path.isdir(results_dir):
130 shutil.rmtree(results_dir)
mblighdcd57a82007-07-11 23:06:47 +0000131 debug = os.path.join(results_dir, 'debug')
mblighc8949b82007-07-23 16:33:58 +0000132 os.makedirs(debug)
133
mblighdcd57a82007-07-11 23:06:47 +0000134 # Ready .... Aim ....
135 host.run('rm -f ' + atrun.remote_control_file)
136 host.run('rm -f ' + atrun.remote_control_file + '.state')
mblighc8949b82007-07-23 16:33:58 +0000137
mblighdcd57a82007-07-11 23:06:47 +0000138 # Copy control_file to remote_control_file on the host
139 tmppath = utils.get(control_file)
140 host.send_file(tmppath, atrun.remote_control_file)
141 os.remove(tmppath)
mblighc8949b82007-07-23 16:33:58 +0000142
mblighdcd57a82007-07-11 23:06:47 +0000143 atrun.execute_control()
mblighc8949b82007-07-23 16:33:58 +0000144
mblighdcd57a82007-07-11 23:06:47 +0000145 # retrive results
146 results = os.path.join(atrun.autodir, 'results', 'default')
147 # Copy all dirs in default to results_dir
148 host.get_file(results + '/', results_dir)
149
150
mblighdd81ef02007-08-10 01:18:40 +0000151 def run_test(self, test_name, results_dir, host = None, **options):
mblighd54832b2007-07-25 16:46:56 +0000152 """
153 Assemble a tiny little control file to just run one test,
154 and run it as an autotest client-side test
155 """
mblighdd81ef02007-08-10 01:18:40 +0000156 if not host:
157 host = self.host
mblighd54832b2007-07-25 16:46:56 +0000158 args = ["%s=%s" % (o[0], repr(o[1])) for o in options.items()]
159 args = ", ".join([repr(test_name)] + args)
160 control = "job.run_test(%s)" % args
161 self.run(control, results_dir, host)
162
163
mblighdcd57a82007-07-11 23:06:47 +0000164class _Run(object):
165 """
166 Represents a run of autotest control file. This class maintains
167 all the state necessary as an autotest control file is executed.
168
169 It is not intended to be used directly, rather control files
170 should be run using the run method in Autotest.
171 """
172 def __init__(self, host, results_dir):
173 self.host = host
174 self.results_dir = results_dir
mbligh3ceb7202007-07-27 21:10:26 +0000175 self.env = ''
mbligh928c0362007-07-31 17:59:49 +0000176 if hasattr(host, 'env'):
mbligh3ceb7202007-07-27 21:10:26 +0000177 self.env = host.env
mblighc8949b82007-07-23 16:33:58 +0000178
mblighdcd57a82007-07-11 23:06:47 +0000179 self.autodir = _get_autodir(self.host)
180 self.remote_control_file = os.path.join(self.autodir, 'control')
mblighdc735a22007-08-02 16:54:37 +0000181
182
mblighdcd57a82007-07-11 23:06:47 +0000183 def verify_machine(self):
184 binary = os.path.join(self.autodir, 'bin/autotest')
mblighdd81ef02007-08-10 01:18:40 +0000185 try:
186 self.host.run('ls ' + binary)
187 except:
mbligh8d7e3472007-08-10 01:35:18 +0000188 raise "Autotest does not appear to be installed"
189 tmpdir = os.path.join(self.autodir, 'tmp')
190 self.host.run('umount %s' % tmpdir, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000191
192
mblighdcd57a82007-07-11 23:06:47 +0000193 def __execute_section(self, section):
194 print "Executing %s/bin/autotest %s/control phase %d" % \
195 (self.autodir, self.autodir,
196 section)
197 logfile = "%s/debug/client.log.%d" % (self.results_dir,
198 section)
199 client_log = open(logfile, 'w')
200 if section > 0:
201 cont = '-c'
202 else:
203 cont = ''
204 client = os.path.join(self.autodir, 'bin/autotest_client')
205 ssh = "ssh -q %s@%s" % (self.host.user, self.host.hostname)
206 cmd = "%s %s %s" % (client, cont, self.remote_control_file)
mbligh3ceb7202007-07-27 21:10:26 +0000207 print "%s '%s %s'" % (ssh, self.env, cmd)
mblighdcd57a82007-07-11 23:06:47 +0000208 # Use Popen here, not m.ssh, as we want it in the background
mbligh3ceb7202007-07-27 21:10:26 +0000209 p = subprocess.Popen("%s '%s %s'" % (ssh, self.env, cmd),
210 shell=True,
211 stdout=client_log,
212 stderr=subprocess.PIPE)
mblighdcd57a82007-07-11 23:06:47 +0000213 line = None
214 for line in iter(p.stderr.readline, ''):
215 print line,
216 sys.stdout.flush()
217 if not line:
218 raise AutotestRunError("execute_section: %s '%s' \
219 failed to return anything" % (ssh, cmd))
220 return line
mblighdc735a22007-08-02 16:54:37 +0000221
222
mblighdcd57a82007-07-11 23:06:47 +0000223 def execute_control(self):
224 section = 0
225 while True:
226 last = self.__execute_section(section)
227 section += 1
228 if re.match('DONE', last):
229 print "Client complete"
230 return
231 elif re.match('REBOOT', last):
232 print "Client is rebooting"
233 print "Waiting for client to halt"
234 if not self.host.wait_down(HALT_TIME):
235 raise AutotestRunError("%s \
236 failed to shutdown after %ds" %
mblighc8949b82007-07-23 16:33:58 +0000237 (self.host.hostname,
mblighdcd57a82007-07-11 23:06:47 +0000238 HALT_TIME))
239 print "Client down, waiting for restart"
240 if not self.host.wait_up(BOOT_TIME):
241 # since reboot failed
242 # hardreset the machine once if possible
243 # before failing this control file
244 if hasattr(self.host, 'hardreset'):
mblighc8949b82007-07-23 16:33:58 +0000245 print "Hardresetting %s" % (
mbligh928c0362007-07-31 17:59:49 +0000246 self.host.hostname,)
mblighdcd57a82007-07-11 23:06:47 +0000247 self.host.hardreset()
mblighc8949b82007-07-23 16:33:58 +0000248 raise AutotestRunError("%s failed to "
249 "boot after %ds" % (
250 self.host.hostname,
251 BOOT_TIME,))
mblighdcd57a82007-07-11 23:06:47 +0000252 continue
mblighc8949b82007-07-23 16:33:58 +0000253 raise AutotestRunError("Aborting - unknown "
254 "return code: %s\n" % last)
mblighdcd57a82007-07-11 23:06:47 +0000255
256
257def _get_autodir(host):
258 try:
259 atdir = host.run(
mblighc8949b82007-07-23 16:33:58 +0000260 'grep "autodir *=" /etc/autotest.conf').stdout.strip()
mblighdcd57a82007-07-11 23:06:47 +0000261 if atdir:
262 m = re.search(r'autodir *= *[\'"]?([^\'"]*)[\'"]?',
263 atdir)
264 return m.group(1)
265 except errors.AutoservRunError:
266 pass
267 for path in ['/usr/local/autotest', '/home/autotest']:
268 try:
269 host.run('ls ' + path)
270 return path
271 except errors.AutoservRunError:
272 pass
273 raise AutotestRunError("Cannot figure out autotest directory")