results_cache.py revision ee988c87971c07b7e6767adb63d151ea37e363f2
1#!/usr/bin/python
2#
3# Copyright 2011 Google Inc. All Rights Reserved.
4
5import getpass
6import glob
7import hashlib
8import os
9import pickle
10import re
11
12from image_checksummer import ImageChecksummer
13from perf_processor import PerfProcessor
14from utils import command_executer
15from utils import logger
16from utils import misc
17
18
19SCRATCH_DIR = "/home/%s/cros_scratch" % getpass.getuser()
20RESULTS_FILE = "results.txt"
21AUTOTEST_TARBALL = "autotest.tbz2"
22PERF_RESULTS_FILE = "perf-results.txt"
23
24
25class Result(object):
26  def __init__(self, out, err, retval, keyvals):
27    self.out = out
28    self.err = err
29    self.retval = retval
30    self.keyvals = keyvals
31
32
33class CacheConditions(object):
34  # Cache hit only if the result file exists.
35  CACHE_FILE_EXISTS = 0
36
37  # Cache hit if the ip address of the cached result and the new run match.
38  REMOTES_MATCH = 1
39
40  # Cache hit if the image checksum of the cached result and the new run match.
41  CHECKSUMS_MATCH = 2
42
43  # Cache hit only if the cached result was successful
44  RUN_SUCCEEDED = 3
45
46  # Never a cache hit.
47  FALSE = 4
48
49  # Cache hit if the image path matches the cached image path.
50  IMAGE_PATH_MATCH = 5
51
52
53class ResultsCache(object):
54  CACHE_VERSION = 2
55  def Init(self, chromeos_image, chromeos_root, autotest_name, iteration,
56           autotest_args, remote, board, cache_conditions,
57           logger_to_use):
58    self.chromeos_image = chromeos_image
59    self.chromeos_root = chromeos_root
60    self.autotest_name = autotest_name
61    self.iteration = iteration
62    self.autotest_args = autotest_args,
63    self.remote = remote
64    self.board = board
65    self.cache_conditions = cache_conditions
66    self._logger = logger_to_use
67    self._ce = command_executer.GetCommandExecuter(self._logger)
68
69  def _GetCacheDirForRead(self):
70    glob_path = self._FormCacheDir(self._GetCacheKeyList(True))
71    matching_dirs = glob.glob(glob_path)
72
73    if matching_dirs:
74      # Cache file found.
75      if len(matching_dirs) > 1:
76        self._logger.LogError("Multiple compatible cache files: %s." %
77                              " ".join(matching_dirs))
78      return matching_dirs[0]
79    else:
80      return None
81
82  def _GetCacheDirForWrite(self):
83    return self._FormCacheDir(self._GetCacheKeyList(False))
84
85  def _FormCacheDir(self, list_of_strings):
86    cache_key = " ".join(list_of_strings)
87    cache_dir = misc.GetFilenameFromString(cache_key)
88    cache_path = os.path.join(SCRATCH_DIR, cache_dir)
89    return cache_path
90
91  def _GetCacheKeyList(self, read):
92    if read and CacheConditions.REMOTES_MATCH not in self.cache_conditions:
93      remote = "*"
94    else:
95      remote = self.remote
96    if read and CacheConditions.CHECKSUMS_MATCH not in self.cache_conditions:
97      checksum = "*"
98    else:
99      checksum = ImageChecksummer().Checksum(self.chromeos_image)
100
101    if read and CacheConditions.IMAGE_PATH_MATCH not in self.cache_conditions:
102      image_path_checksum = "*"
103    else:
104      image_path_checksum = hashlib.md5(self.chromeos_image).hexdigest()
105
106    autotest_args_checksum = hashlib.md5(
107                             "".join(self.autotest_args)).hexdigest()
108
109    return (image_path_checksum,
110            self.autotest_name, str(self.iteration),
111            autotest_args_checksum,
112            checksum,
113            remote,
114            str(self.CACHE_VERSION))
115
116  def ReadResult(self):
117    if CacheConditions.FALSE in self.cache_conditions:
118      return None
119    cache_dir = self._GetCacheDirForRead()
120
121    if not cache_dir:
122      return None
123
124    try:
125      cache_file = os.path.join(cache_dir, RESULTS_FILE)
126
127      self._logger.LogOutput("Trying to read from cache file: %s" % cache_file)
128
129      with open(cache_file, "rb") as f:
130        result = pickle.load(f)
131
132        if (result.retval == 0 or
133            CacheConditions.RUN_SUCCEEDED not in self.cache_conditions):
134          return result
135
136    except Exception, e:
137      if CacheConditions.CACHE_FILE_EXISTS not in self.cache_conditions:
138        # Cache file not found but just return a failure.
139        return Result("", "", 1, {})
140      raise e
141
142  def StoreResult(self, result):
143    cache_dir = self._GetCacheDirForWrite()
144    cache_file = os.path.join(cache_dir, RESULTS_FILE)
145    command = "mkdir -p %s" % cache_dir
146    ret = self._ce.RunCommand(command)
147    assert ret == 0, "Couldn't create cache dir"
148    with open(cache_file, "wb") as f:
149      pickle.dump(result, f)
150
151  def StoreAutotestOutput(self, results_dir):
152    host_results_dir = os.path.join(self.chromeos_root, "chroot",
153                                    results_dir[1:])
154    tarball = os.path.join(self._GetCacheDirForWrite(), AUTOTEST_TARBALL)
155    command = ("cd %s && tar cjf %s ." % (host_results_dir, tarball))
156    ret = self._ce.RunCommand(command)
157    if ret:
158      raise Exception("Couldn't store autotest output directory.")
159
160  def ReadAutotestOutput(self, destination):
161    cache_dir = self._GetCacheDirForRead()
162    tarball = os.path.join(cache_dir, AUTOTEST_TARBALL)
163    if not os.path.exists(tarball):
164      raise Exception("Cached autotest tarball does not exist at '%s'." %
165                      tarball)
166    command = ("cd %s && tar xjf %s ." % (destination, tarball))
167    ret = self._ce.RunCommand(command)
168    if ret:
169      raise Exception("Couldn't read autotest output directory.")
170
171  def StorePerfResults(self, perf):
172    perf_path = os.path.join(self._GetCacheDirForWrite(), PERF_RESULTS_FILE)
173    with open(perf_path, "wb") as f:
174      pickle.dump(perf.report, f)
175      pickle.dump(perf.output, f)
176
177  def ReadPerfResults(self):
178    cache_dir = self._GetCacheDirForRead()
179    perf_path = os.path.join(cache_dir, PERF_RESULTS_FILE)
180    with open(perf_path, "rb") as f:
181      report = pickle.load(f)
182      output = pickle.load(f)
183
184    return PerfProcessor.PerfResults(report, output)
185
186
187class MockResultsCache(object):
188  def Init(self, *args):
189    pass
190
191  def ReadResult(self):
192    return Result("Results placed in /tmp/test", "", 0)
193
194  def StoreResult(self, result):
195    pass
196
197  def StoreAutotestOutput(self, results_dir):
198    pass
199
200  def ReadAutotestOutput(self, destination):
201    pass
202
203  def StorePerfResults(self, perf):
204    pass
205
206  def ReadPerfResults(self):
207    return PerfProcessor.PerfResults("", "")
208