blob: 271443c1a8b1b2f7da9bb1e0f4bf9ea9fb7e476e [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
mbligh91334902007-09-28 01:47:59 +000055 self.got = False
56 self.installed = False
mbligh9708f732007-10-18 03:18:54 +000057 self.serverdir = utils.get_server_dir()
mblighdcd57a82007-07-11 23:06:47 +000058 super(Autotest, self).__init__()
mblighc8949b82007-07-23 16:33:58 +000059
mblighdc735a22007-08-02 16:54:37 +000060
mblighdd81ef02007-08-10 01:18:40 +000061 def install(self, host = None):
mblighdc735a22007-08-02 16:54:37 +000062 """
63 Install autotest. If get() was not called previously, an
mblighc8949b82007-07-23 16:33:58 +000064 attempt will be made to install from the autotest svn
65 repository.
mblighdcd57a82007-07-11 23:06:47 +000066
67 Args:
68 host: a Host instance on which autotest will be
69 installed
70
71 Raises:
72 AutoservError: if a tarball was not specified and
73 the target host does not have svn installed in its path
mblighc8949b82007-07-23 16:33:58 +000074
75 TODO(poirier): check dependencies
76 autotest needs:
77 bzcat
78 liboptdev (oprofile)
79 binutils-dev (oprofile)
80 make
mbligh629e39e2007-08-10 19:32:00 +000081 psutils (netperf)
mblighdcd57a82007-07-11 23:06:47 +000082 """
mblighdd81ef02007-08-10 01:18:40 +000083 if not host:
84 host = self.host
mbligh91334902007-09-28 01:47:59 +000085 if not self.got:
86 self.get()
mbligh7386abc2007-08-31 08:57:56 +000087 host.ensure_up()
mbligh91334902007-09-28 01:47:59 +000088 host.setup()
mblighb84a1cf2007-08-09 23:08:13 +000089 print "Installing autotest on %s" % host.hostname
mblighdcd57a82007-07-11 23:06:47 +000090 # try to install from file or directory
mblighc8949b82007-07-23 16:33:58 +000091 if self.source_material:
92 if os.path.isdir(self.source_material):
mblighdcd57a82007-07-11 23:06:47 +000093 # Copy autotest recursively
94 autodir = _get_autodir(host)
mblighf238b452007-07-25 21:11:14 +000095 host.run('mkdir -p "%s"' %
mbligh91334902007-09-28 01:47:59 +000096 utils.sh_escape(autodir))
mblighc8949b82007-07-23 16:33:58 +000097 host.send_file(self.source_material,
mbligh91334902007-09-28 01:47:59 +000098 autodir)
mblighdcd57a82007-07-11 23:06:47 +000099 else:
100 # Copy autotest via tarball
101 raise "Not yet implemented!"
mbligh91334902007-09-28 01:47:59 +0000102 print "Installation of autotest completed"
103 self.installed = True
mblighdcd57a82007-07-11 23:06:47 +0000104 return
mbligh91334902007-09-28 01:47:59 +0000105
mblighdcd57a82007-07-11 23:06:47 +0000106 # if that fails try to install using svn
107 if utils.run('which svn').exit_status:
108 raise AutoservError('svn not found in path on \
109 target machine: %s' % host.name)
110 try:
111 host.run('svn checkout %s %s' %
112 (AUTOTEST_SVN, _get_autodir(host)))
113 except errors.AutoservRunError, e:
114 host.run('svn checkout %s %s' %
115 (AUTOTEST_HTTP, _get_autodir(host)))
mbligh91334902007-09-28 01:47:59 +0000116 print "Installation of autotest completed"
117 self.installed = True
118
119
120 def get(self, location = None):
121 if not location:
122 location = os.path.join(self.serverdir, '../client')
123 location = os.path.abspath(location)
mbligh8fc0e5a2007-10-11 18:39:03 +0000124 # If there's stuff run on our client directory already, it
125 # can cause problems. Try giving it a quick clean first.
126 cwd = os.getcwd()
127 os.chdir(location)
128 os.system('tools/make_clean')
129 os.chdir(cwd)
mbligh91334902007-09-28 01:47:59 +0000130 super(Autotest, self).get(location)
131 self.got = True
mblighdcd57a82007-07-11 23:06:47 +0000132
133
mbligh55a2a3b2007-09-30 01:27:55 +0000134 def run(self, control_file, results_dir = '.', host = None):
mblighdcd57a82007-07-11 23:06:47 +0000135 """
136 Run an autotest job on the remote machine.
mblighc8949b82007-07-23 16:33:58 +0000137
mblighdcd57a82007-07-11 23:06:47 +0000138 Args:
139 control_file: an open file-like-obj of the control file
140 results_dir: a str path where the results should be stored
141 on the local filesystem
142 host: a Host instance on which the control file should
143 be run
144
145 Raises:
146 AutotestRunError: if there is a problem executing
147 the control file
148 """
mbligh55a2a3b2007-09-30 01:27:55 +0000149 results_dir = os.path.abspath(results_dir)
mblighdd81ef02007-08-10 01:18:40 +0000150 if not host:
151 host = self.host
mbligh91334902007-09-28 01:47:59 +0000152 if not self.installed:
mbligh7fdd1692007-10-02 19:16:53 +0000153 self.install(host)
mbligh91334902007-09-28 01:47:59 +0000154
mblighdbe4a382007-07-26 19:41:28 +0000155 host.ensure_up()
156
mblighdcd57a82007-07-11 23:06:47 +0000157 atrun = _Run(host, results_dir)
mblighe4f01512007-08-10 01:42:50 +0000158 try:
159 atrun.verify_machine()
160 except:
161 print "Verify machine failed on %s. Reinstalling" % \
162 host.hostname
163 self.install(host)
mblighdcd57a82007-07-11 23:06:47 +0000164 atrun.verify_machine()
165 debug = os.path.join(results_dir, 'debug')
mbligh55a2a3b2007-09-30 01:27:55 +0000166 try:
167 os.makedirs(debug)
168 except:
169 pass
mblighc8949b82007-07-23 16:33:58 +0000170
mblighdcd57a82007-07-11 23:06:47 +0000171 # Ready .... Aim ....
172 host.run('rm -f ' + atrun.remote_control_file)
173 host.run('rm -f ' + atrun.remote_control_file + '.state')
mblighc8949b82007-07-23 16:33:58 +0000174
mblighdcd57a82007-07-11 23:06:47 +0000175 # Copy control_file to remote_control_file on the host
176 tmppath = utils.get(control_file)
177 host.send_file(tmppath, atrun.remote_control_file)
178 os.remove(tmppath)
mblighc8949b82007-07-23 16:33:58 +0000179
mblighdcd57a82007-07-11 23:06:47 +0000180 atrun.execute_control()
mblighc8949b82007-07-23 16:33:58 +0000181
mblighdcd57a82007-07-11 23:06:47 +0000182 # retrive results
183 results = os.path.join(atrun.autodir, 'results', 'default')
184 # Copy all dirs in default to results_dir
185 host.get_file(results + '/', results_dir)
186
187
mbligh55a2a3b2007-09-30 01:27:55 +0000188 def run_test(self, test_name, results_dir = '.', host = None, \
189 *args, **dargs):
mblighd54832b2007-07-25 16:46:56 +0000190 """
191 Assemble a tiny little control file to just run one test,
192 and run it as an autotest client-side test
193 """
mblighdd81ef02007-08-10 01:18:40 +0000194 if not host:
195 host = self.host
mbligh91334902007-09-28 01:47:59 +0000196 if not self.installed:
mbligh7fdd1692007-10-02 19:16:53 +0000197 self.install(host)
mbligh271d5af2007-08-10 19:54:01 +0000198 opts = ["%s=%s" % (o[0], repr(o[1])) for o in dargs.items()]
199 cmd = ", ".join([repr(test_name)] + map(repr, args) + opts)
mblighf1c52842007-10-16 15:21:38 +0000200 control = "job.run_test(%s)\n" % cmd
mblighd54832b2007-07-25 16:46:56 +0000201 self.run(control, results_dir, host)
202
203
mblighdcd57a82007-07-11 23:06:47 +0000204class _Run(object):
205 """
206 Represents a run of autotest control file. This class maintains
207 all the state necessary as an autotest control file is executed.
208
209 It is not intended to be used directly, rather control files
210 should be run using the run method in Autotest.
211 """
212 def __init__(self, host, results_dir):
213 self.host = host
214 self.results_dir = results_dir
mbligh3ceb7202007-07-27 21:10:26 +0000215 self.env = ''
mbligh928c0362007-07-31 17:59:49 +0000216 if hasattr(host, 'env'):
mbligh3ceb7202007-07-27 21:10:26 +0000217 self.env = host.env
mblighc8949b82007-07-23 16:33:58 +0000218
mblighdcd57a82007-07-11 23:06:47 +0000219 self.autodir = _get_autodir(self.host)
220 self.remote_control_file = os.path.join(self.autodir, 'control')
mblighdc735a22007-08-02 16:54:37 +0000221
222
mblighdcd57a82007-07-11 23:06:47 +0000223 def verify_machine(self):
224 binary = os.path.join(self.autodir, 'bin/autotest')
mblighdd81ef02007-08-10 01:18:40 +0000225 try:
226 self.host.run('ls ' + binary)
227 except:
mbligh8d7e3472007-08-10 01:35:18 +0000228 raise "Autotest does not appear to be installed"
229 tmpdir = os.path.join(self.autodir, 'tmp')
230 self.host.run('umount %s' % tmpdir, ignore_status=True)
mblighdc735a22007-08-02 16:54:37 +0000231
232
mblighdcd57a82007-07-11 23:06:47 +0000233 def __execute_section(self, section):
234 print "Executing %s/bin/autotest %s/control phase %d" % \
235 (self.autodir, self.autodir,
236 section)
237 logfile = "%s/debug/client.log.%d" % (self.results_dir,
238 section)
239 client_log = open(logfile, 'w')
240 if section > 0:
241 cont = '-c'
242 else:
243 cont = ''
244 client = os.path.join(self.autodir, 'bin/autotest_client')
245 ssh = "ssh -q %s@%s" % (self.host.user, self.host.hostname)
246 cmd = "%s %s %s" % (client, cont, self.remote_control_file)
mbligh3ceb7202007-07-27 21:10:26 +0000247 print "%s '%s %s'" % (ssh, self.env, cmd)
mblighdcd57a82007-07-11 23:06:47 +0000248 # Use Popen here, not m.ssh, as we want it in the background
mbligh3ceb7202007-07-27 21:10:26 +0000249 p = subprocess.Popen("%s '%s %s'" % (ssh, self.env, cmd),
250 shell=True,
251 stdout=client_log,
252 stderr=subprocess.PIPE)
mblighdcd57a82007-07-11 23:06:47 +0000253 line = None
254 for line in iter(p.stderr.readline, ''):
255 print line,
256 sys.stdout.flush()
257 if not line:
258 raise AutotestRunError("execute_section: %s '%s' \
259 failed to return anything" % (ssh, cmd))
260 return line
mblighdc735a22007-08-02 16:54:37 +0000261
262
mblighdcd57a82007-07-11 23:06:47 +0000263 def execute_control(self):
264 section = 0
265 while True:
266 last = self.__execute_section(section)
267 section += 1
268 if re.match('DONE', last):
269 print "Client complete"
270 return
271 elif re.match('REBOOT', last):
272 print "Client is rebooting"
273 print "Waiting for client to halt"
274 if not self.host.wait_down(HALT_TIME):
275 raise AutotestRunError("%s \
276 failed to shutdown after %ds" %
mblighc8949b82007-07-23 16:33:58 +0000277 (self.host.hostname,
mblighdcd57a82007-07-11 23:06:47 +0000278 HALT_TIME))
279 print "Client down, waiting for restart"
280 if not self.host.wait_up(BOOT_TIME):
281 # since reboot failed
282 # hardreset the machine once if possible
283 # before failing this control file
284 if hasattr(self.host, 'hardreset'):
mblighc8949b82007-07-23 16:33:58 +0000285 print "Hardresetting %s" % (
mbligh928c0362007-07-31 17:59:49 +0000286 self.host.hostname,)
mblighdcd57a82007-07-11 23:06:47 +0000287 self.host.hardreset()
mblighc8949b82007-07-23 16:33:58 +0000288 raise AutotestRunError("%s failed to "
289 "boot after %ds" % (
290 self.host.hostname,
291 BOOT_TIME,))
mblighdcd57a82007-07-11 23:06:47 +0000292 continue
mblighc8949b82007-07-23 16:33:58 +0000293 raise AutotestRunError("Aborting - unknown "
294 "return code: %s\n" % last)
mblighdcd57a82007-07-11 23:06:47 +0000295
296
297def _get_autodir(host):
298 try:
299 atdir = host.run(
mblighc8949b82007-07-23 16:33:58 +0000300 'grep "autodir *=" /etc/autotest.conf').stdout.strip()
mblighdcd57a82007-07-11 23:06:47 +0000301 if atdir:
302 m = re.search(r'autodir *= *[\'"]?([^\'"]*)[\'"]?',
303 atdir)
304 return m.group(1)
305 except errors.AutoservRunError:
306 pass
307 for path in ['/usr/local/autotest', '/home/autotest']:
308 try:
309 host.run('ls ' + path)
310 return path
311 except errors.AutoservRunError:
312 pass
313 raise AutotestRunError("Cannot figure out autotest directory")