blob: 0181b8e0f643f598760c925e56b253424669c1d6 [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:
188 self.
mblighdc735a22007-08-02 16:54:37 +0000189
190
mblighdcd57a82007-07-11 23:06:47 +0000191 def __execute_section(self, section):
192 print "Executing %s/bin/autotest %s/control phase %d" % \
193 (self.autodir, self.autodir,
194 section)
195 logfile = "%s/debug/client.log.%d" % (self.results_dir,
196 section)
197 client_log = open(logfile, 'w')
198 if section > 0:
199 cont = '-c'
200 else:
201 cont = ''
202 client = os.path.join(self.autodir, 'bin/autotest_client')
203 ssh = "ssh -q %s@%s" % (self.host.user, self.host.hostname)
204 cmd = "%s %s %s" % (client, cont, self.remote_control_file)
mbligh3ceb7202007-07-27 21:10:26 +0000205 print "%s '%s %s'" % (ssh, self.env, cmd)
mblighdcd57a82007-07-11 23:06:47 +0000206 # Use Popen here, not m.ssh, as we want it in the background
mbligh3ceb7202007-07-27 21:10:26 +0000207 p = subprocess.Popen("%s '%s %s'" % (ssh, self.env, cmd),
208 shell=True,
209 stdout=client_log,
210 stderr=subprocess.PIPE)
mblighdcd57a82007-07-11 23:06:47 +0000211 line = None
212 for line in iter(p.stderr.readline, ''):
213 print line,
214 sys.stdout.flush()
215 if not line:
216 raise AutotestRunError("execute_section: %s '%s' \
217 failed to return anything" % (ssh, cmd))
218 return line
mblighdc735a22007-08-02 16:54:37 +0000219
220
mblighdcd57a82007-07-11 23:06:47 +0000221 def execute_control(self):
222 section = 0
223 while True:
224 last = self.__execute_section(section)
225 section += 1
226 if re.match('DONE', last):
227 print "Client complete"
228 return
229 elif re.match('REBOOT', last):
230 print "Client is rebooting"
231 print "Waiting for client to halt"
232 if not self.host.wait_down(HALT_TIME):
233 raise AutotestRunError("%s \
234 failed to shutdown after %ds" %
mblighc8949b82007-07-23 16:33:58 +0000235 (self.host.hostname,
mblighdcd57a82007-07-11 23:06:47 +0000236 HALT_TIME))
237 print "Client down, waiting for restart"
238 if not self.host.wait_up(BOOT_TIME):
239 # since reboot failed
240 # hardreset the machine once if possible
241 # before failing this control file
242 if hasattr(self.host, 'hardreset'):
mblighc8949b82007-07-23 16:33:58 +0000243 print "Hardresetting %s" % (
mbligh928c0362007-07-31 17:59:49 +0000244 self.host.hostname,)
mblighdcd57a82007-07-11 23:06:47 +0000245 self.host.hardreset()
mblighc8949b82007-07-23 16:33:58 +0000246 raise AutotestRunError("%s failed to "
247 "boot after %ds" % (
248 self.host.hostname,
249 BOOT_TIME,))
mblighdcd57a82007-07-11 23:06:47 +0000250 continue
mblighc8949b82007-07-23 16:33:58 +0000251 raise AutotestRunError("Aborting - unknown "
252 "return code: %s\n" % last)
mblighdcd57a82007-07-11 23:06:47 +0000253
254
255def _get_autodir(host):
256 try:
257 atdir = host.run(
mblighc8949b82007-07-23 16:33:58 +0000258 'grep "autodir *=" /etc/autotest.conf').stdout.strip()
mblighdcd57a82007-07-11 23:06:47 +0000259 if atdir:
260 m = re.search(r'autodir *= *[\'"]?([^\'"]*)[\'"]?',
261 atdir)
262 return m.group(1)
263 except errors.AutoservRunError:
264 pass
265 for path in ['/usr/local/autotest', '/home/autotest']:
266 try:
267 host.run('ls ' + path)
268 return path
269 except errors.AutoservRunError:
270 pass
271 raise AutotestRunError("Cannot figure out autotest directory")