blob: e26dc012db83beb137c554575b9f7bb6fc9eddf4 [file] [log] [blame]
#!/usr/bin/python
#
# Copyright 2007 Google Inc. Released under the GPL v2
"""This module defines the Autotest class
Autotest: software to run tests automatically
"""
__author__ = """mbligh@google.com (Martin J. Bligh),
poirier@google.com (Benjamin Poirier),
stutsman@google.com (Ryan Stutsman)"""
import re
import os
import sys
import subprocess
import urllib
import tempfile
import shutil
import installable_object
import errors
import utils
AUTOTEST_SVN = 'svn://test.kernel.org/autotest/trunk/client'
AUTOTEST_HTTP = 'http://test.kernel.org/svn/autotest/trunk/client'
# Timeouts for powering down and up respectively
HALT_TIME = 300
BOOT_TIME = 300
class AutotestRunError(errors.AutoservRunError):
pass
class Autotest(installable_object.InstallableObject):
"""This class represents the Autotest program.
Autotest is used to run tests automatically and collect the results.
It also supports profilers.
Implementation details:
This is a leaf class in an abstract class hierarchy, it must
implement the unimplemented methods in parent classes.
"""
def __init__(self):
super(Autotest, self).__init__()
def install(self, host):
"""Install autotest. If get() was not called previously, an
attempt will be made to install from the autotest svn
repository.
Args:
host: a Host instance on which autotest will be
installed
Raises:
AutoservError: if a tarball was not specified and
the target host does not have svn installed in its path
TODO(poirier): check dependencies
autotest needs:
bzcat
liboptdev (oprofile)
binutils-dev (oprofile)
make
"""
# try to install from file or directory
if self.source_material:
if os.path.isdir(self.source_material):
# Copy autotest recursively
autodir = _get_autodir(host)
host.run('mkdir -p "%s"' %
utils.sh_escape(autodir))
host.send_file(self.source_material,
autodir)
else:
# Copy autotest via tarball
raise "Not yet implemented!"
return
# if that fails try to install using svn
if utils.run('which svn').exit_status:
raise AutoservError('svn not found in path on \
target machine: %s' % host.name)
try:
host.run('svn checkout %s %s' %
(AUTOTEST_SVN, _get_autodir(host)))
except errors.AutoservRunError, e:
host.run('svn checkout %s %s' %
(AUTOTEST_HTTP, _get_autodir(host)))
def run(self, control_file, results_dir, host):
"""
Run an autotest job on the remote machine.
Args:
control_file: an open file-like-obj of the control file
results_dir: a str path where the results should be stored
on the local filesystem
host: a Host instance on which the control file should
be run
Raises:
AutotestRunError: if there is a problem executing
the control file
"""
host.ensure_up()
atrun = _Run(host, results_dir)
atrun.verify_machine()
if os.path.isdir(results_dir):
shutil.rmtree(results_dir)
debug = os.path.join(results_dir, 'debug')
os.makedirs(debug)
# Ready .... Aim ....
host.run('rm -f ' + atrun.remote_control_file)
host.run('rm -f ' + atrun.remote_control_file + '.state')
# Copy control_file to remote_control_file on the host
tmppath = utils.get(control_file)
host.send_file(tmppath, atrun.remote_control_file)
os.remove(tmppath)
atrun.execute_control()
# retrive results
results = os.path.join(atrun.autodir, 'results', 'default')
# Copy all dirs in default to results_dir
host.get_file(results + '/', results_dir)
def run_test(self, test_name, results_dir, host, **options):
"""
Assemble a tiny little control file to just run one test,
and run it as an autotest client-side test
"""
args = ["%s=%s" % (o[0], repr(o[1])) for o in options.items()]
args = ", ".join([repr(test_name)] + args)
control = "job.run_test(%s)" % args
self.run(control, results_dir, host)
class _Run(object):
"""
Represents a run of autotest control file. This class maintains
all the state necessary as an autotest control file is executed.
It is not intended to be used directly, rather control files
should be run using the run method in Autotest.
"""
def __init__(self, host, results_dir):
self.host = host
self.results_dir = results_dir
self.env = ''
if hasattr(host, 'env'):
self.env = host.env
self.autodir = _get_autodir(self.host)
self.remote_control_file = os.path.join(self.autodir, 'control')
def verify_machine(self):
binary = os.path.join(self.autodir, 'bin/autotest')
self.host.run('ls ' + binary)
def __execute_section(self, section):
print "Executing %s/bin/autotest %s/control phase %d" % \
(self.autodir, self.autodir,
section)
logfile = "%s/debug/client.log.%d" % (self.results_dir,
section)
client_log = open(logfile, 'w')
if section > 0:
cont = '-c'
else:
cont = ''
client = os.path.join(self.autodir, 'bin/autotest_client')
ssh = "ssh -q %s@%s" % (self.host.user, self.host.hostname)
cmd = "%s %s %s" % (client, cont, self.remote_control_file)
print "%s '%s %s'" % (ssh, self.env, cmd)
# Use Popen here, not m.ssh, as we want it in the background
p = subprocess.Popen("%s '%s %s'" % (ssh, self.env, cmd),
shell=True,
stdout=client_log,
stderr=subprocess.PIPE)
line = None
for line in iter(p.stderr.readline, ''):
print line,
sys.stdout.flush()
if not line:
raise AutotestRunError("execute_section: %s '%s' \
failed to return anything" % (ssh, cmd))
return line
def execute_control(self):
section = 0
while True:
last = self.__execute_section(section)
section += 1
if re.match('DONE', last):
print "Client complete"
return
elif re.match('REBOOT', last):
print "Client is rebooting"
print "Waiting for client to halt"
if not self.host.wait_down(HALT_TIME):
raise AutotestRunError("%s \
failed to shutdown after %ds" %
(self.host.hostname,
HALT_TIME))
print "Client down, waiting for restart"
if not self.host.wait_up(BOOT_TIME):
# since reboot failed
# hardreset the machine once if possible
# before failing this control file
if hasattr(self.host, 'hardreset'):
print "Hardresetting %s" % (
self.host.hostname,)
self.host.hardreset()
raise AutotestRunError("%s failed to "
"boot after %ds" % (
self.host.hostname,
BOOT_TIME,))
continue
raise AutotestRunError("Aborting - unknown "
"return code: %s\n" % last)
def _get_autodir(host):
try:
atdir = host.run(
'grep "autodir *=" /etc/autotest.conf').stdout.strip()
if atdir:
m = re.search(r'autodir *= *[\'"]?([^\'"]*)[\'"]?',
atdir)
return m.group(1)
except errors.AutoservRunError:
pass
for path in ['/usr/local/autotest', '/home/autotest']:
try:
host.run('ls ' + path)
return path
except errors.AutoservRunError:
pass
raise AutotestRunError("Cannot figure out autotest directory")