Home | History | Annotate | Download | only in dynamic_suite
      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 
      5 import common
      6 import logging, os, re
      7 from autotest_lib.client.common_lib import error, utils
      8 from autotest_lib.client.common_lib.cros import dev_server
      9 
     10 
     11 # Relevant CrosDynamicSuiteExceptions are defined in client/common_lib/error.py.
     12 
     13 
     14 class 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 
     56 class 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 
    112 class 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 
    194 class 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