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