blob: e9a1e338a70c4f3f63c8939bf2a899d939fed4ea [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 """
53 def __init__(self):
54 super(Autotest, self).__init__()
mblighc8949b82007-07-23 16:33:58 +000055
mblighdc735a22007-08-02 16:54:37 +000056
mblighdcd57a82007-07-11 23:06:47 +000057 def install(self, host):
mblighdc735a22007-08-02 16:54:37 +000058 """
59 Install autotest. If get() was not called previously, an
mblighc8949b82007-07-23 16:33:58 +000060 attempt will be made to install from the autotest svn
61 repository.
mblighdcd57a82007-07-11 23:06:47 +000062
63 Args:
64 host: a Host instance on which autotest will be
65 installed
66
67 Raises:
68 AutoservError: if a tarball was not specified and
69 the target host does not have svn installed in its path
mblighc8949b82007-07-23 16:33:58 +000070
71 TODO(poirier): check dependencies
72 autotest needs:
73 bzcat
74 liboptdev (oprofile)
75 binutils-dev (oprofile)
76 make
mblighdcd57a82007-07-11 23:06:47 +000077 """
mblighb84a1cf2007-08-09 23:08:13 +000078 print "Installing autotest on %s" % host.hostname
mblighdcd57a82007-07-11 23:06:47 +000079 # try to install from file or directory
mblighc8949b82007-07-23 16:33:58 +000080 if self.source_material:
81 if os.path.isdir(self.source_material):
mblighdcd57a82007-07-11 23:06:47 +000082 # Copy autotest recursively
83 autodir = _get_autodir(host)
mblighf238b452007-07-25 21:11:14 +000084 host.run('mkdir -p "%s"' %
85 utils.sh_escape(autodir))
mblighc8949b82007-07-23 16:33:58 +000086 host.send_file(self.source_material,
mblighdcd57a82007-07-11 23:06:47 +000087 autodir)
88 else:
89 # Copy autotest via tarball
90 raise "Not yet implemented!"
91 return
mblighc8949b82007-07-23 16:33:58 +000092
mblighdcd57a82007-07-11 23:06:47 +000093 # if that fails try to install using svn
94 if utils.run('which svn').exit_status:
95 raise AutoservError('svn not found in path on \
96 target machine: %s' % host.name)
97 try:
98 host.run('svn checkout %s %s' %
99 (AUTOTEST_SVN, _get_autodir(host)))
100 except errors.AutoservRunError, e:
101 host.run('svn checkout %s %s' %
102 (AUTOTEST_HTTP, _get_autodir(host)))
103
104
105 def run(self, control_file, results_dir, host):
106 """
107 Run an autotest job on the remote machine.
mblighc8949b82007-07-23 16:33:58 +0000108
mblighdcd57a82007-07-11 23:06:47 +0000109 Args:
110 control_file: an open file-like-obj of the control file
111 results_dir: a str path where the results should be stored
112 on the local filesystem
113 host: a Host instance on which the control file should
114 be run
115
116 Raises:
117 AutotestRunError: if there is a problem executing
118 the control file
119 """
mblighdbe4a382007-07-26 19:41:28 +0000120 host.ensure_up()
121
mblighdcd57a82007-07-11 23:06:47 +0000122 atrun = _Run(host, results_dir)
123 atrun.verify_machine()
mblighc8949b82007-07-23 16:33:58 +0000124 if os.path.isdir(results_dir):
125 shutil.rmtree(results_dir)
mblighdcd57a82007-07-11 23:06:47 +0000126 debug = os.path.join(results_dir, 'debug')
mblighc8949b82007-07-23 16:33:58 +0000127 os.makedirs(debug)
128
mblighdcd57a82007-07-11 23:06:47 +0000129 # Ready .... Aim ....
130 host.run('rm -f ' + atrun.remote_control_file)
131 host.run('rm -f ' + atrun.remote_control_file + '.state')
mblighc8949b82007-07-23 16:33:58 +0000132
mblighdcd57a82007-07-11 23:06:47 +0000133 # Copy control_file to remote_control_file on the host
134 tmppath = utils.get(control_file)
135 host.send_file(tmppath, atrun.remote_control_file)
136 os.remove(tmppath)
mblighc8949b82007-07-23 16:33:58 +0000137
mblighdcd57a82007-07-11 23:06:47 +0000138 atrun.execute_control()
mblighc8949b82007-07-23 16:33:58 +0000139
mblighdcd57a82007-07-11 23:06:47 +0000140 # retrive results
141 results = os.path.join(atrun.autodir, 'results', 'default')
142 # Copy all dirs in default to results_dir
143 host.get_file(results + '/', results_dir)
144
145
mblighd54832b2007-07-25 16:46:56 +0000146 def run_test(self, test_name, results_dir, host, **options):
147 """
148 Assemble a tiny little control file to just run one test,
149 and run it as an autotest client-side test
150 """
151 args = ["%s=%s" % (o[0], repr(o[1])) for o in options.items()]
152 args = ", ".join([repr(test_name)] + args)
153 control = "job.run_test(%s)" % args
154 self.run(control, results_dir, host)
155
156
mblighdcd57a82007-07-11 23:06:47 +0000157class _Run(object):
158 """
159 Represents a run of autotest control file. This class maintains
160 all the state necessary as an autotest control file is executed.
161
162 It is not intended to be used directly, rather control files
163 should be run using the run method in Autotest.
164 """
165 def __init__(self, host, results_dir):
166 self.host = host
167 self.results_dir = results_dir
mbligh3ceb7202007-07-27 21:10:26 +0000168 self.env = ''
mbligh928c0362007-07-31 17:59:49 +0000169 if hasattr(host, 'env'):
mbligh3ceb7202007-07-27 21:10:26 +0000170 self.env = host.env
mblighc8949b82007-07-23 16:33:58 +0000171
mblighdcd57a82007-07-11 23:06:47 +0000172 self.autodir = _get_autodir(self.host)
173 self.remote_control_file = os.path.join(self.autodir, 'control')
mblighdc735a22007-08-02 16:54:37 +0000174
175
mblighdcd57a82007-07-11 23:06:47 +0000176 def verify_machine(self):
177 binary = os.path.join(self.autodir, 'bin/autotest')
178 self.host.run('ls ' + binary)
mblighdc735a22007-08-02 16:54:37 +0000179
180
mblighdcd57a82007-07-11 23:06:47 +0000181 def __execute_section(self, section):
182 print "Executing %s/bin/autotest %s/control phase %d" % \
183 (self.autodir, self.autodir,
184 section)
185 logfile = "%s/debug/client.log.%d" % (self.results_dir,
186 section)
187 client_log = open(logfile, 'w')
188 if section > 0:
189 cont = '-c'
190 else:
191 cont = ''
192 client = os.path.join(self.autodir, 'bin/autotest_client')
193 ssh = "ssh -q %s@%s" % (self.host.user, self.host.hostname)
194 cmd = "%s %s %s" % (client, cont, self.remote_control_file)
mbligh3ceb7202007-07-27 21:10:26 +0000195 print "%s '%s %s'" % (ssh, self.env, cmd)
mblighdcd57a82007-07-11 23:06:47 +0000196 # Use Popen here, not m.ssh, as we want it in the background
mbligh3ceb7202007-07-27 21:10:26 +0000197 p = subprocess.Popen("%s '%s %s'" % (ssh, self.env, cmd),
198 shell=True,
199 stdout=client_log,
200 stderr=subprocess.PIPE)
mblighdcd57a82007-07-11 23:06:47 +0000201 line = None
202 for line in iter(p.stderr.readline, ''):
203 print line,
204 sys.stdout.flush()
205 if not line:
206 raise AutotestRunError("execute_section: %s '%s' \
207 failed to return anything" % (ssh, cmd))
208 return line
mblighdc735a22007-08-02 16:54:37 +0000209
210
mblighdcd57a82007-07-11 23:06:47 +0000211 def execute_control(self):
212 section = 0
213 while True:
214 last = self.__execute_section(section)
215 section += 1
216 if re.match('DONE', last):
217 print "Client complete"
218 return
219 elif re.match('REBOOT', last):
220 print "Client is rebooting"
221 print "Waiting for client to halt"
222 if not self.host.wait_down(HALT_TIME):
223 raise AutotestRunError("%s \
224 failed to shutdown after %ds" %
mblighc8949b82007-07-23 16:33:58 +0000225 (self.host.hostname,
mblighdcd57a82007-07-11 23:06:47 +0000226 HALT_TIME))
227 print "Client down, waiting for restart"
228 if not self.host.wait_up(BOOT_TIME):
229 # since reboot failed
230 # hardreset the machine once if possible
231 # before failing this control file
232 if hasattr(self.host, 'hardreset'):
mblighc8949b82007-07-23 16:33:58 +0000233 print "Hardresetting %s" % (
mbligh928c0362007-07-31 17:59:49 +0000234 self.host.hostname,)
mblighdcd57a82007-07-11 23:06:47 +0000235 self.host.hardreset()
mblighc8949b82007-07-23 16:33:58 +0000236 raise AutotestRunError("%s failed to "
237 "boot after %ds" % (
238 self.host.hostname,
239 BOOT_TIME,))
mblighdcd57a82007-07-11 23:06:47 +0000240 continue
mblighc8949b82007-07-23 16:33:58 +0000241 raise AutotestRunError("Aborting - unknown "
242 "return code: %s\n" % last)
mblighdcd57a82007-07-11 23:06:47 +0000243
244
245def _get_autodir(host):
246 try:
247 atdir = host.run(
mblighc8949b82007-07-23 16:33:58 +0000248 'grep "autodir *=" /etc/autotest.conf').stdout.strip()
mblighdcd57a82007-07-11 23:06:47 +0000249 if atdir:
250 m = re.search(r'autodir *= *[\'"]?([^\'"]*)[\'"]?',
251 atdir)
252 return m.group(1)
253 except errors.AutoservRunError:
254 pass
255 for path in ['/usr/local/autotest', '/home/autotest']:
256 try:
257 host.run('ls ' + path)
258 return path
259 except errors.AutoservRunError:
260 pass
261 raise AutotestRunError("Cannot figure out autotest directory")