Chris Masone | 859fdec | 2012-01-30 08:38:09 -0800 | [diff] [blame] | 1 | # Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
Chris Masone | 6fed646 | 2011-10-20 16:36:43 -0700 | [diff] [blame] | 2 | # Use of this source code is governed by a BSD-style license that can be |
| 3 | # found in the LICENSE file. |
| 4 | |
| 5 | import common |
Chris Masone | fef2138 | 2012-01-17 11:16:32 -0800 | [diff] [blame] | 6 | import compiler, logging, os, random, re, time, urllib2 |
Chris Masone | 6fed646 | 2011-10-20 16:36:43 -0700 | [diff] [blame] | 7 | from autotest_lib.client.common_lib import control_data, error, utils |
Chris Masone | fef2138 | 2012-01-17 11:16:32 -0800 | [diff] [blame] | 8 | from autotest_lib.client.common_lib.cros import dev_server |
| 9 | |
| 10 | |
Chris Masone | f8b5306 | 2012-05-08 22:14:18 -0700 | [diff] [blame] | 11 | # Relevant CrosDynamicSuiteExceptions are defined in client/common_lib/error.py. |
Chris Masone | 6fed646 | 2011-10-20 16:36:43 -0700 | [diff] [blame] | 12 | |
| 13 | |
| 14 | class ControlFileGetter(object): |
| 15 | """ |
| 16 | Interface for classes that can list and fetch known control files. |
Chris Masone | 6fed646 | 2011-10-20 16:36:43 -0700 | [diff] [blame] | 17 | """ |
| 18 | |
Chris Masone | 6fed646 | 2011-10-20 16:36:43 -0700 | [diff] [blame] | 19 | def __init__(self): |
| 20 | pass |
| 21 | |
| 22 | |
| 23 | def get_control_file_list(self): |
| 24 | """ |
Chris Masone | fef2138 | 2012-01-17 11:16:32 -0800 | [diff] [blame] | 25 | Gather a list of paths to control files. |
Chris Masone | 6fed646 | 2011-10-20 16:36:43 -0700 | [diff] [blame] | 26 | |
Chris Masone | fef2138 | 2012-01-17 11:16:32 -0800 | [diff] [blame] | 27 | @return A list of file paths. |
| 28 | @throws NoControlFileList if there is an error while listing. |
Chris Masone | 6fed646 | 2011-10-20 16:36:43 -0700 | [diff] [blame] | 29 | """ |
| 30 | pass |
| 31 | |
| 32 | |
| 33 | def get_control_file_contents(self, test_path): |
| 34 | """ |
| 35 | Given a path to a control file, return its contents. |
| 36 | |
Chris Masone | fef2138 | 2012-01-17 11:16:32 -0800 | [diff] [blame] | 37 | @param test_path: the path to the control file. |
Chris Masone | 6fed646 | 2011-10-20 16:36:43 -0700 | [diff] [blame] | 38 | @return the contents of the control file specified by the path. |
Chris Masone | fef2138 | 2012-01-17 11:16:32 -0800 | [diff] [blame] | 39 | @throws ControlFileNotFound if the file cannot be retrieved. |
Chris Masone | 6fed646 | 2011-10-20 16:36:43 -0700 | [diff] [blame] | 40 | """ |
| 41 | pass |
| 42 | |
| 43 | |
| 44 | def get_control_file_contents_by_name(self, test_name): |
| 45 | """ |
| 46 | Given the name of a control file, return its contents. |
| 47 | |
Chris Masone | fef2138 | 2012-01-17 11:16:32 -0800 | [diff] [blame] | 48 | @param test_name: the name of the test whose control file is desired. |
| 49 | @return the contents of the control file specified by the name. |
| 50 | @throws ControlFileNotFound if the file cannot be retrieved. |
Chris Masone | 6fed646 | 2011-10-20 16:36:43 -0700 | [diff] [blame] | 51 | """ |
| 52 | pass |
| 53 | |
| 54 | |
Chris Masone | fef2138 | 2012-01-17 11:16:32 -0800 | [diff] [blame] | 55 | class CacheingControlFileGetter(ControlFileGetter): |
| 56 | """Wraps ControlFileGetter to cache the retrieved control file list.""" |
| 57 | def __init__(self): |
| 58 | super(CacheingControlFileGetter, self).__init__() |
| 59 | self._files = [] |
| 60 | |
| 61 | |
| 62 | def get_control_file_list(self): |
| 63 | """ |
| 64 | Gather a list of paths to control files. |
| 65 | |
| 66 | Gets a list of control files; populates |self._files| with that list |
| 67 | and then returns the paths to all useful files in the list. |
| 68 | |
| 69 | @return A list of file paths. |
| 70 | @throws NoControlFileList if there is an error while listing. |
| 71 | """ |
| 72 | self._files = self._get_control_file_list() |
| 73 | return self._files |
| 74 | |
| 75 | |
| 76 | def get_control_file_contents_by_name(self, test_name): |
| 77 | """ |
| 78 | Given the name of a control file, return its contents. |
| 79 | |
| 80 | Searches through previously-compiled list in |self._files| for a |
| 81 | test named |test_name| and returns the contents of the control file |
| 82 | for that test if it is found. |
| 83 | |
| 84 | @param test_name: the name of the test whose control file is desired. |
| 85 | @return the contents of the control file specified by the name. |
| 86 | @throws ControlFileNotFound if the file cannot be retrieved. |
| 87 | """ |
| 88 | if not self._files and not self.get_control_file_list(): |
Chris Masone | f8b5306 | 2012-05-08 22:14:18 -0700 | [diff] [blame] | 89 | raise error.ControlFileNotFound('No control files found.') |
Chris Masone | 859fdec | 2012-01-30 08:38:09 -0800 | [diff] [blame] | 90 | |
| 91 | if 'control' not in test_name: |
Chris Masone | a64fdc1 | 2012-05-16 13:19:46 -0700 | [diff] [blame] | 92 | regexp = re.compile(os.path.join(test_name, 'control$')) |
Chris Masone | 859fdec | 2012-01-30 08:38:09 -0800 | [diff] [blame] | 93 | else: |
Chris Masone | a64fdc1 | 2012-05-16 13:19:46 -0700 | [diff] [blame] | 94 | regexp = re.compile(test_name + '$') |
Chris Masone | fef2138 | 2012-01-17 11:16:32 -0800 | [diff] [blame] | 95 | candidates = filter(regexp.search, self._files) |
| 96 | if not candidates: |
Chris Masone | f8b5306 | 2012-05-08 22:14:18 -0700 | [diff] [blame] | 97 | raise error.ControlFileNotFound('No control file for ' + test_name) |
Chris Masone | fef2138 | 2012-01-17 11:16:32 -0800 | [diff] [blame] | 98 | if len(candidates) > 1: |
Chris Masone | f8b5306 | 2012-05-08 22:14:18 -0700 | [diff] [blame] | 99 | raise error.ControlFileNotFound(test_name + ' is not unique.') |
Chris Masone | fef2138 | 2012-01-17 11:16:32 -0800 | [diff] [blame] | 100 | return self.get_control_file_contents(candidates[0]) |
| 101 | |
| 102 | |
| 103 | class FileSystemGetter(CacheingControlFileGetter): |
| 104 | """ |
| 105 | Class that can list and fetch known control files from disk. |
| 106 | |
| 107 | @var _CONTROL_PATTERN: control file name format to match. |
| 108 | """ |
| 109 | |
| 110 | _CONTROL_PATTERN = '^control(?:\..+)?$' |
| 111 | |
| 112 | def __init__(self, paths): |
| 113 | """ |
| 114 | @param paths: base directories to start search. |
| 115 | """ |
| 116 | super(FileSystemGetter, self).__init__() |
| 117 | self._paths = paths |
| 118 | |
| 119 | |
Chris Masone | 6fed646 | 2011-10-20 16:36:43 -0700 | [diff] [blame] | 120 | def _is_useful_file(self, name): |
| 121 | return '__init__.py' not in name and '.svn' not in name |
| 122 | |
| 123 | |
Chris Masone | fef2138 | 2012-01-17 11:16:32 -0800 | [diff] [blame] | 124 | def _get_control_file_list(self): |
Chris Masone | 6fed646 | 2011-10-20 16:36:43 -0700 | [diff] [blame] | 125 | """ |
Chris Masone | fef2138 | 2012-01-17 11:16:32 -0800 | [diff] [blame] | 126 | Gather a list of paths to control files under |self._paths|. |
Chris Masone | 6fed646 | 2011-10-20 16:36:43 -0700 | [diff] [blame] | 127 | |
Chris Masone | fef2138 | 2012-01-17 11:16:32 -0800 | [diff] [blame] | 128 | Searches under |self._paths| for files that match |
| 129 | |self._CONTROL_PATTERN|. Populates |self._files| with that list |
| 130 | and then returns the paths to all useful files in the list. |
Chris Masone | 6fed646 | 2011-10-20 16:36:43 -0700 | [diff] [blame] | 131 | |
Chris Masone | fef2138 | 2012-01-17 11:16:32 -0800 | [diff] [blame] | 132 | @return A list of files that match |self._CONTROL_PATTERN|. |
| 133 | @throws NoControlFileList if we find no files. |
Chris Masone | 6fed646 | 2011-10-20 16:36:43 -0700 | [diff] [blame] | 134 | """ |
| 135 | regexp = re.compile(self._CONTROL_PATTERN) |
| 136 | directories = self._paths |
| 137 | while len(directories) > 0: |
| 138 | directory = directories.pop() |
| 139 | if not os.path.exists(directory): |
| 140 | continue |
| 141 | for name in os.listdir(directory): |
| 142 | fullpath = os.path.join(directory, name) |
| 143 | if os.path.isfile(fullpath): |
| 144 | if regexp.search(name): |
| 145 | # if we are a control file |
| 146 | self._files.append(fullpath) |
| 147 | elif os.path.isdir(fullpath): |
| 148 | directories.append(fullpath) |
Chris Masone | fef2138 | 2012-01-17 11:16:32 -0800 | [diff] [blame] | 149 | if not self._files: |
| 150 | msg = 'No control files under ' + ','.join(self._paths) |
Chris Masone | f8b5306 | 2012-05-08 22:14:18 -0700 | [diff] [blame] | 151 | raise error.NoControlFileList(msg) |
Chris Masone | 6fed646 | 2011-10-20 16:36:43 -0700 | [diff] [blame] | 152 | return [f for f in self._files if self._is_useful_file(f)] |
| 153 | |
| 154 | |
| 155 | def get_control_file_contents(self, test_path): |
Chris Masone | fef2138 | 2012-01-17 11:16:32 -0800 | [diff] [blame] | 156 | """ |
| 157 | Get the contents of the control file at |test_path|. |
| 158 | |
| 159 | @return The contents of the aforementioned file. |
| 160 | @throws ControlFileNotFound if the file cannot be retrieved. |
| 161 | """ |
| 162 | try: |
| 163 | return utils.read_file(test_path) |
| 164 | except EnvironmentError as (errno, strerror): |
| 165 | msg = "Can't retrieve {0}: {1} ({2})".format(test_path, |
| 166 | strerror, |
| 167 | errno) |
Chris Masone | f8b5306 | 2012-05-08 22:14:18 -0700 | [diff] [blame] | 168 | raise error.ControlFileNotFound(msg) |
Chris Masone | 6fed646 | 2011-10-20 16:36:43 -0700 | [diff] [blame] | 169 | |
| 170 | |
Chris Masone | fef2138 | 2012-01-17 11:16:32 -0800 | [diff] [blame] | 171 | class DevServerGetter(CacheingControlFileGetter): |
| 172 | def __init__(self, build, ds=None): |
| 173 | """ |
| 174 | @param build: The build from which to get control files. |
| 175 | @param ds: An existing dev_server.DevServer object to use. |
| 176 | """ |
| 177 | super(DevServerGetter, self).__init__() |
| 178 | self._dev_server = ds if ds else dev_server.DevServer() |
| 179 | self._build = build |
| 180 | |
| 181 | |
Chris Masone | 859fdec | 2012-01-30 08:38:09 -0800 | [diff] [blame] | 182 | @staticmethod |
| 183 | def create(build, ds=None): |
| 184 | """Wraps constructor. Can be mocked for testing purposes.""" |
| 185 | return DevServerGetter(build, ds) |
| 186 | |
| 187 | |
Chris Masone | fef2138 | 2012-01-17 11:16:32 -0800 | [diff] [blame] | 188 | def _get_control_file_list(self): |
| 189 | """ |
| 190 | Gather a list of paths to control files from |self._dev_server|. |
| 191 | |
| 192 | Get a listing of all the control files for |self._build| on |
| 193 | |self._dev_server|. Populates |self._files| with that list |
| 194 | and then returns paths (under the autotest dir) to them. |
| 195 | |
Chris Masone | f70650c | 2012-05-16 08:52:12 -0700 | [diff] [blame^] | 196 | @return A list of control file paths. |
Chris Masone | fef2138 | 2012-01-17 11:16:32 -0800 | [diff] [blame] | 197 | @throws NoControlFileList if there is an error while listing. |
| 198 | """ |
| 199 | try: |
| 200 | return self._dev_server.list_control_files(self._build) |
Chris Masone | f70650c | 2012-05-16 08:52:12 -0700 | [diff] [blame^] | 201 | except dev_server.DevServerException as e: |
Chris Masone | f8b5306 | 2012-05-08 22:14:18 -0700 | [diff] [blame] | 202 | raise error.NoControlFileList(e) |
Chris Masone | fef2138 | 2012-01-17 11:16:32 -0800 | [diff] [blame] | 203 | |
| 204 | |
| 205 | def get_control_file_contents(self, test_path): |
| 206 | """ |
| 207 | Return the contents of |test_path| from |self._dev_server|. |
| 208 | |
| 209 | Get the contents of the control file at |test_path| for |self._build| on |
| 210 | |self._dev_server|. |
| 211 | |
| 212 | @return The contents of |test_path|. None on failure. |
| 213 | @throws ControlFileNotFound if the file cannot be retrieved. |
| 214 | """ |
| 215 | try: |
| 216 | return self._dev_server.get_control_file(self._build, test_path) |
Chris Masone | f70650c | 2012-05-16 08:52:12 -0700 | [diff] [blame^] | 217 | except dev_server.DevServerException as e: |
Chris Masone | f8b5306 | 2012-05-08 22:14:18 -0700 | [diff] [blame] | 218 | raise error.ControlFileNotFound(e) |