blob: d6dc70f5445594cac03cdf5a196f1a3eda8c22fb [file] [log] [blame]
import datetime
import getpass
import glob
import os
import pickle
import re
import threading
import time
import image_chromeos
import machine_manager_singleton
import table_formatter
from utils import command_executer
from utils import logger
SCRATCH_DIR = "/home/%s/cros_scratch" % getpass.getuser()
PICKLE_FILE = "pickle.txt"
VERSION = "1"
def ConvertToFilename(text):
ret = text
ret = re.sub("/", "__", ret)
ret = re.sub(" ", "_", ret)
ret = re.sub("=", "", ret)
ret = re.sub("\"", "", ret)
return ret
class AutotestRun(threading.Thread):
def __init__(self, autotest, chromeos_root="", chromeos_image="",
board="", remote="", iteration=0, image_checksum="",
exact_remote=False, rerun=False, rerun_if_failed=False):
self.autotest = autotest
self.chromeos_root = chromeos_root
self.chromeos_image = chromeos_image
self.board = board
self.remote = remote
self.iteration = iteration
l = logger.GetLogger()
l.LogFatalIf(not image_checksum, "Checksum shouldn't be None")
self.image_checksum = image_checksum
self.results = {}
threading.Thread.__init__(self)
self.terminate = False
self.retval = None
self.status = "PENDING"
self.run_completed = False
self.exact_remote = exact_remote
self.rerun = rerun
self.rerun_if_failed = rerun_if_failed
self.results_dir = None
self.full_name = None
@staticmethod
def MeanExcludingSlowest(array):
mean = sum(array) / len(array)
array2 = []
for v in array:
if mean != 0 and abs(v - mean)/mean < 0.2:
array2.append(v)
if array2:
return sum(array2) / len(array2)
else:
return mean
@staticmethod
def AddComposite(results_dict):
composite_keys = []
composite_dict = {}
for key in results_dict:
mo = re.match("(.*){\d+}", key)
if mo:
composite_keys.append(mo.group(1))
for key in results_dict:
for composite_key in composite_keys:
if (key.count(composite_key) != 0 and
table_formatter.IsFloat(results_dict[key])):
if composite_key not in composite_dict:
composite_dict[composite_key] = []
composite_dict[composite_key].append(float(results_dict[key]))
break
for composite_key in composite_dict:
v = composite_dict[composite_key]
results_dict["%s[c]" % composite_key] = sum(v) / len(v)
mean_excluding_slowest = AutotestRun.MeanExcludingSlowest(v)
results_dict["%s[ce]" % composite_key] = mean_excluding_slowest
return results_dict
def ParseOutput(self):
p = re.compile("^-+.*?^-+", re.DOTALL|re.MULTILINE)
matches = p.findall(self.out)
for i in range(len(matches)):
results = matches[i]
results_dict = {}
for line in results.splitlines()[1:-1]:
mo = re.match("(.*\S)\s+\[\s+(PASSED|FAILED)\s+\]", line)
if mo:
results_dict[mo.group(1)] = mo.group(2)
continue
mo = re.match("(.*\S)\s+(.*)", line)
if mo:
results_dict[mo.group(1)] = mo.group(2)
# Add a composite keyval for tests like startup.
results_dict = AutotestRun.AddComposite(results_dict)
self.results = results_dict
# This causes it to not parse the table again
# Autotest recently added a secondary table
# That reports errors and screws up the final pretty output.
break
mo = re.search("Results placed in (\S+)", self.out)
if mo:
self.results_dir = mo.group(1)
self.full_name = os.path.basename(self.results_dir)
def GetCacheHashBase(self):
ret = ("%s %s %s" %
(self.image_checksum, self.autotest.name, self.iteration))
if self.autotest.args:
ret += " %s" % self.autotest.args
ret += "-%s" % VERSION
return ret
def GetLabel(self):
ret = "%s %s remote:%s" % (self.chromeos_image, self.autotest.name,
self.remote)
return ret
def TryToLoadFromCache(self):
base = self.GetCacheHashBase()
if self.exact_remote:
if not self.remote:
return False
cache_dir_glob = "%s_%s" % (ConvertToFilename(base), self.remote)
else:
cache_dir_glob = "%s*" % ConvertToFilename(base)
cache_path_glob = os.path.join(SCRATCH_DIR, cache_dir_glob)
matching_dirs = glob.glob(cache_path_glob)
if matching_dirs:
matching_dir = matching_dirs[0]
cache_file = os.path.join(matching_dir, PICKLE_FILE)
assert os.path.isfile(cache_file)
self._logger.LogOutput("Trying to read from cache file: %s" % cache_file)
return self.ReadFromCache(cache_file)
self._logger.LogOutput("Cache miss. AM going to run: %s for: %s" %
(self.autotest.name, self.chromeos_image))
return False
def ReadFromCache(self, cache_file):
with open(cache_file, "rb") as f:
self.retval = pickle.load(f)
self.out = pickle.load(f)
self.err = pickle.load(f)
self._logger.LogOutput(self.out)
return True
return False
def StoreToCache(self):
base = self.GetCacheHashBase()
self.cache_dir = os.path.join(SCRATCH_DIR, "%s_%s" % (
ConvertToFilename(base),
self.remote))
cache_file = os.path.join(self.cache_dir, PICKLE_FILE)
command = "mkdir -p %s" % os.path.dirname(cache_file)
ret = self._ce.RunCommand(command)
assert ret == 0, "Couldn't create cache dir"
with open(cache_file, "wb") as f:
pickle.dump(self.retval, f)
pickle.dump(self.out, f)
pickle.dump(self.err, f)
def run(self):
self._logger = logger.Logger(
os.path.dirname(__file__),
"%s.%s" % (os.path.basename(__file__),
self.name), True)
self._ce = command_executer.GetCommandExecuter(self._logger)
self.RunCached()
def RunCached(self):
self.status = "WAITING"
cache_hit = False
if not self.rerun:
cache_hit = self.TryToLoadFromCache()
else:
self._logger.LogOutput("--rerun passed. Not using cached results.")
if self.rerun_if_failed and self.retval:
self._logger.LogOutput("--rerun_if_failed passed and existing test "
"failed. Rerunning...")
cache_hit = False
if not cache_hit:
# Get machine
while True:
if self.terminate:
return 1
self.machine = (
machine_manager_singleton.MachineManagerSingleton().AcquireMachine(self.image_checksum))
if self.machine:
self._logger.LogOutput("%s: Machine %s acquired at %s" %
(self.name,
self.machine.name,
datetime.datetime.now()))
break
else:
sleep_duration = 10
time.sleep(sleep_duration)
try:
self.remote = self.machine.name
if self.machine.checksum != self.image_checksum:
self.retval = self.ImageTo(self.machine.name)
if self.retval: return self.retval
self.machine.checksum = self.image_checksum
self.machine.image = self.chromeos_image
self.status = "RUNNING: %s" % self.autotest.name
[self.retval, self.out, self.err] = self.RunTestOn(self.machine.name)
self.run_completed = True
finally:
self._logger.LogOutput("Releasing machine: %s" % self.machine.name)
machine_manager_singleton.MachineManagerSingleton().ReleaseMachine(self.machine)
self._logger.LogOutput("Released machine: %s" % self.machine.name)
self.StoreToCache()
if not self.retval:
self.status = "SUCCEEDED"
else:
self.status = "FAILED"
self.ParseOutput()
# Copy results directory to the scratch dir
if (not cache_hit and not self.retval and self.autotest.args and
"--profile" in self.autotest.args):
results_dir = os.path.join(self.chromeos_root, "chroot",
self.results_dir.lstrip("/"))
tarball = os.path.join(
self.cache_dir,
os.path.basename(os.path.dirname(self.results_dir)))
command = ("cd %s && tar cjf %s.tbz2 ." % (results_dir, tarball))
self._ce.RunCommand(command)
perf_data_file = os.path.join(self.results_dir, self.full_name,
"profiling/iteration.1/perf.data")
# Attempt to build a perf report and keep it with the results.
command = ("cd %s/src/scripts &&"
" cros_sdk -- /usr/sbin/perf report --symfs=/build/%s"
" -i %s --stdio" % (self.chromeos_root, self.board,
perf_data_file))
ret, out, err = self._ce.RunCommand(command, return_output=True)
with open(os.path.join(self.cache_dir, "perf.report"), "wb") as f:
f.write(out)
return self.retval
def ImageTo(self, machine_name):
image_args = [image_chromeos.__file__,
"--chromeos_root=%s" % self.chromeos_root,
"--image=%s" % self.chromeos_image,
"--remote=%s" % machine_name]
if self.board:
image_args.append("--board=%s" % self.board)
### devserver_port = 8080
### mo = re.search("\d+", self.name)
### if mo:
### to_add = int(mo.group(0))
### assert to_add < 100, "Too many threads launched!"
### devserver_port += to_add
### # I tried --noupdate_stateful, but that still fails when run in parallel.
### image_args.append("--image_to_live_args=\"--devserver_port=%s"
### " --noupdate_stateful\"" % devserver_port)
### image_args.append("--image_to_live_args=--devserver_port=%s" %
### devserver_port)
# Currently can't image two machines at once.
# So have to serialized on this lock.
self.status = "WAITING ON IMAGE_LOCK"
with machine_manager_singleton.MachineManagerSingleton().image_lock:
self.status = "IMAGING"
retval = self._ce.RunCommand(" ".join(["python"] + image_args))
machine_manager_singleton.MachineManagerSingleton().num_reimages += 1
if retval:
self.status = "ABORTED DUE TO IMAGE FAILURE"
return retval
def DoPowerdHack(self):
command = "sudo initctl stop powerd"
self._ce.CrosRunCommand(command, machine=self.machine.name,
chromeos_root=self.chromeos_root)
def RunTestOn(self, machine_name):
command = "cd %s/src/scripts" % self.chromeos_root
options = ""
if self.board:
options += " --board=%s" % self.board
if self.autotest.args:
options += " %s" % self.autotest.args
if "tegra2" in self.board:
self.DoPowerdHack()
command += ("&& cros_sdk -- ./run_remote_tests.sh --remote=%s %s %s" %
(machine_name,
options,
self.autotest.name))
return self._ce.RunCommand(command, True)