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