blob: 2d1cb133a162e9d6198997c5a462d9ef3c33aa5f [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
5"""This module defines the Autotest class
6
7 Autotest: software to run tests automatically
8"""
9
10__author__ = """mbligh@google.com (Martin J. Bligh),
11poirier@google.com (Benjamin Poirier),
12stutsman@google.com (Ryan Stutsman)"""
13
14
15import re
16import os
17import sys
18import subprocess
19import urllib
20import tempfile
21import shutil
22
23import installable_object
24import errors
25import utils
26
27
28AUTOTEST_SVN = 'svn://test.kernel.org/autotest/trunk/client'
29AUTOTEST_HTTP = 'http://test.kernel.org/svn/autotest/trunk/client'
30
31# Timeouts for powering down and up respectively
32HALT_TIME = 300
33BOOT_TIME = 300
34
35
36class AutotestRunError(errors.AutoservRunError):
37 pass
38
39
40class Autotest(installable_object.InstallableObject):
41 """This class represents the Autotest program.
42
43 Autotest is used to run tests automatically and collect the results.
44 It also supports profilers.
45
46 Implementation details:
47 This is a leaf class in an abstract class hierarchy, it must
48 implement the unimplemented methods in parent classes.
49 """
50 def __init__(self):
51 super(Autotest, self).__init__()
mblighc8949b82007-07-23 16:33:58 +000052
mblighdcd57a82007-07-11 23:06:47 +000053
54 def install(self, host):
mblighc8949b82007-07-23 16:33:58 +000055 """Install autotest. If get() was not called previously, an
56 attempt will be made to install from the autotest svn
57 repository.
mblighdcd57a82007-07-11 23:06:47 +000058
59 Args:
60 host: a Host instance on which autotest will be
61 installed
62
63 Raises:
64 AutoservError: if a tarball was not specified and
65 the target host does not have svn installed in its path
mblighc8949b82007-07-23 16:33:58 +000066
67 TODO(poirier): check dependencies
68 autotest needs:
69 bzcat
70 liboptdev (oprofile)
71 binutils-dev (oprofile)
72 make
mblighdcd57a82007-07-11 23:06:47 +000073 """
74 # try to install from file or directory
mblighc8949b82007-07-23 16:33:58 +000075 if self.source_material:
76 if os.path.isdir(self.source_material):
mblighdcd57a82007-07-11 23:06:47 +000077 # Copy autotest recursively
78 autodir = _get_autodir(host)
79 host.run('mkdir -p %s' %
80 utils.scp_remote_escape(autodir))
mblighc8949b82007-07-23 16:33:58 +000081 host.send_file(self.source_material,
mblighdcd57a82007-07-11 23:06:47 +000082 autodir)
83 else:
84 # Copy autotest via tarball
85 raise "Not yet implemented!"
86 return
mblighc8949b82007-07-23 16:33:58 +000087
mblighdcd57a82007-07-11 23:06:47 +000088 # if that fails try to install using svn
89 if utils.run('which svn').exit_status:
90 raise AutoservError('svn not found in path on \
91 target machine: %s' % host.name)
92 try:
93 host.run('svn checkout %s %s' %
94 (AUTOTEST_SVN, _get_autodir(host)))
95 except errors.AutoservRunError, e:
96 host.run('svn checkout %s %s' %
97 (AUTOTEST_HTTP, _get_autodir(host)))
98
99
100 def run(self, control_file, results_dir, host):
101 """
102 Run an autotest job on the remote machine.
mblighc8949b82007-07-23 16:33:58 +0000103
mblighdcd57a82007-07-11 23:06:47 +0000104 Args:
105 control_file: an open file-like-obj of the control file
106 results_dir: a str path where the results should be stored
107 on the local filesystem
108 host: a Host instance on which the control file should
109 be run
110
111 Raises:
112 AutotestRunError: if there is a problem executing
113 the control file
114 """
115 atrun = _Run(host, results_dir)
116 atrun.verify_machine()
mblighc8949b82007-07-23 16:33:58 +0000117 if os.path.isdir(results_dir):
118 shutil.rmtree(results_dir)
mblighdcd57a82007-07-11 23:06:47 +0000119 debug = os.path.join(results_dir, 'debug')
mblighc8949b82007-07-23 16:33:58 +0000120 os.makedirs(debug)
121
mblighdcd57a82007-07-11 23:06:47 +0000122 # Ready .... Aim ....
123 host.run('rm -f ' + atrun.remote_control_file)
124 host.run('rm -f ' + atrun.remote_control_file + '.state')
mblighc8949b82007-07-23 16:33:58 +0000125
mblighdcd57a82007-07-11 23:06:47 +0000126 # Copy control_file to remote_control_file on the host
127 tmppath = utils.get(control_file)
128 host.send_file(tmppath, atrun.remote_control_file)
129 os.remove(tmppath)
mblighc8949b82007-07-23 16:33:58 +0000130
mblighdcd57a82007-07-11 23:06:47 +0000131 atrun.execute_control()
mblighc8949b82007-07-23 16:33:58 +0000132
mblighdcd57a82007-07-11 23:06:47 +0000133 # retrive results
134 results = os.path.join(atrun.autodir, 'results', 'default')
135 # Copy all dirs in default to results_dir
136 host.get_file(results + '/', results_dir)
137
138
mblighd54832b2007-07-25 16:46:56 +0000139 def run_test(self, test_name, results_dir, host, **options):
140 """
141 Assemble a tiny little control file to just run one test,
142 and run it as an autotest client-side test
143 """
144 args = ["%s=%s" % (o[0], repr(o[1])) for o in options.items()]
145 args = ", ".join([repr(test_name)] + args)
146 control = "job.run_test(%s)" % args
147 self.run(control, results_dir, host)
148
149
mblighdcd57a82007-07-11 23:06:47 +0000150class _Run(object):
151 """
152 Represents a run of autotest control file. This class maintains
153 all the state necessary as an autotest control file is executed.
154
155 It is not intended to be used directly, rather control files
156 should be run using the run method in Autotest.
157 """
158 def __init__(self, host, results_dir):
159 self.host = host
160 self.results_dir = results_dir
mblighc8949b82007-07-23 16:33:58 +0000161
mblighdcd57a82007-07-11 23:06:47 +0000162 self.autodir = _get_autodir(self.host)
163 self.remote_control_file = os.path.join(self.autodir, 'control')
mblighc8949b82007-07-23 16:33:58 +0000164
mblighdcd57a82007-07-11 23:06:47 +0000165 def verify_machine(self):
166 binary = os.path.join(self.autodir, 'bin/autotest')
167 self.host.run('ls ' + binary)
mblighc8949b82007-07-23 16:33:58 +0000168
mblighdcd57a82007-07-11 23:06:47 +0000169 def __execute_section(self, section):
170 print "Executing %s/bin/autotest %s/control phase %d" % \
171 (self.autodir, self.autodir,
172 section)
173 logfile = "%s/debug/client.log.%d" % (self.results_dir,
174 section)
175 client_log = open(logfile, 'w')
176 if section > 0:
177 cont = '-c'
178 else:
179 cont = ''
180 client = os.path.join(self.autodir, 'bin/autotest_client')
181 ssh = "ssh -q %s@%s" % (self.host.user, self.host.hostname)
182 cmd = "%s %s %s" % (client, cont, self.remote_control_file)
183 print "%s '%s'" % (ssh, cmd)
184 # Use Popen here, not m.ssh, as we want it in the background
185 p = subprocess.Popen("%s '%s'" % (ssh, cmd), shell=True, \
186 stdout=client_log, stderr=subprocess.PIPE)
187 line = None
188 for line in iter(p.stderr.readline, ''):
189 print line,
190 sys.stdout.flush()
191 if not line:
192 raise AutotestRunError("execute_section: %s '%s' \
193 failed to return anything" % (ssh, cmd))
194 return line
mblighc8949b82007-07-23 16:33:58 +0000195
mblighdcd57a82007-07-11 23:06:47 +0000196 def execute_control(self):
197 section = 0
198 while True:
199 last = self.__execute_section(section)
200 section += 1
201 if re.match('DONE', last):
202 print "Client complete"
203 return
204 elif re.match('REBOOT', last):
205 print "Client is rebooting"
206 print "Waiting for client to halt"
207 if not self.host.wait_down(HALT_TIME):
208 raise AutotestRunError("%s \
209 failed to shutdown after %ds" %
mblighc8949b82007-07-23 16:33:58 +0000210 (self.host.hostname,
mblighdcd57a82007-07-11 23:06:47 +0000211 HALT_TIME))
212 print "Client down, waiting for restart"
213 if not self.host.wait_up(BOOT_TIME):
214 # since reboot failed
215 # hardreset the machine once if possible
216 # before failing this control file
217 if hasattr(self.host, 'hardreset'):
mblighc8949b82007-07-23 16:33:58 +0000218 print "Hardresetting %s" % (
219 self.hostname,)
mblighdcd57a82007-07-11 23:06:47 +0000220 self.host.hardreset()
mblighc8949b82007-07-23 16:33:58 +0000221 raise AutotestRunError("%s failed to "
222 "boot after %ds" % (
223 self.host.hostname,
224 BOOT_TIME,))
mblighdcd57a82007-07-11 23:06:47 +0000225 continue
mblighc8949b82007-07-23 16:33:58 +0000226 raise AutotestRunError("Aborting - unknown "
227 "return code: %s\n" % last)
mblighdcd57a82007-07-11 23:06:47 +0000228
229
230def _get_autodir(host):
231 try:
232 atdir = host.run(
mblighc8949b82007-07-23 16:33:58 +0000233 'grep "autodir *=" /etc/autotest.conf').stdout.strip()
mblighdcd57a82007-07-11 23:06:47 +0000234 if atdir:
235 m = re.search(r'autodir *= *[\'"]?([^\'"]*)[\'"]?',
236 atdir)
237 return m.group(1)
238 except errors.AutoservRunError:
239 pass
240 for path in ['/usr/local/autotest', '/home/autotest']:
241 try:
242 host.run('ls ' + path)
243 return path
244 except errors.AutoservRunError:
245 pass
246 raise AutotestRunError("Cannot figure out autotest directory")