
The current patch proposes the addition of a new data source strategy for filtering the strategies by a given list of versions. Each data source exports a new API, called ``version()``, which should return the underlying version of the said data source. This implies that each data source module can export multiple versioned data sources and it's the duty of an external strategy to select the appropiate data source with regard to a particular set of versions. Change-Id: I6ef2fdd6520a53fc600232247f23bd22b848461e
99 lines
2.9 KiB
Python
99 lines
2.9 KiB
Python
# Copyright 2015 Canonical Ltd.
|
|
# This file is part of cloud-init. See LICENCE file for license information.
|
|
#
|
|
# vi: ts=4 expandtab
|
|
|
|
import abc
|
|
|
|
import six
|
|
|
|
from cloudinit import logging
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
@six.add_metaclass(abc.ABCMeta)
|
|
class BaseSearchStrategy(object):
|
|
"""Declare search strategies for data sources
|
|
|
|
A *search strategy* represents a decoupled way of choosing
|
|
one or more data sources from a list of data sources.
|
|
Each strategy can be used interchangeably and they can
|
|
be composed. For instance, once can apply a filtering strategy
|
|
over a parallel search strategy, which looks for the available
|
|
data sources.
|
|
"""
|
|
|
|
@abc.abstractmethod
|
|
def search_data_sources(self, data_sources):
|
|
"""Search the possible data sources for this strategy
|
|
|
|
The method should filter the data sources that can be
|
|
considered *valid* for the given strategy.
|
|
|
|
:param data_sources:
|
|
An iterator of data source instances, where the lookup
|
|
will be done.
|
|
"""
|
|
|
|
@staticmethod
|
|
def is_datasource_available(data_source):
|
|
"""Check if the given *data_source* is considered *available*
|
|
|
|
A data source is considered available if it can be loaded,
|
|
but other strategies could implement their own behaviour.
|
|
"""
|
|
try:
|
|
if data_source.load():
|
|
return True
|
|
except Exception:
|
|
LOG.error("Failed to load data source %r", data_source)
|
|
return False
|
|
|
|
|
|
class FilterNameStrategy(BaseSearchStrategy):
|
|
"""A strategy for filtering data sources by name
|
|
|
|
:param names:
|
|
A list of strings, where each string is a name for a possible
|
|
data source. Only the data sources that are in this list will
|
|
be loaded and filtered.
|
|
"""
|
|
|
|
def __init__(self, names=None):
|
|
self._names = names
|
|
super(FilterNameStrategy, self).__init__()
|
|
|
|
def search_data_sources(self, data_sources):
|
|
return (source for source in data_sources
|
|
if source.__class__.__name__ in self._names)
|
|
|
|
|
|
class SerialSearchStrategy(BaseSearchStrategy):
|
|
"""A strategy that chooses a data source in serial."""
|
|
|
|
def search_data_sources(self, data_sources):
|
|
for data_source in data_sources:
|
|
if self.is_datasource_available(data_source):
|
|
yield data_source
|
|
|
|
|
|
class FilterVersionStrategy(BaseSearchStrategy):
|
|
"""A strategy for filtering data sources by their version
|
|
|
|
:param versions:
|
|
A list of strings, where each strings is a possible
|
|
version that a data source can have.
|
|
"""
|
|
|
|
def __init__(self, versions=None):
|
|
if versions is None:
|
|
versions = []
|
|
self._versions = versions
|
|
super(FilterVersionStrategy, self).__init__()
|
|
|
|
def search_data_sources(self, data_sources):
|
|
return (source for source in data_sources
|
|
if source.version() in self._versions)
|