factory.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. from cloudbridge.cloud import providers
  2. from cloudbridge.cloud.interfaces import CloudProvider
  3. from cloudbridge.cloud.interfaces import TestMockHelperMixin
  4. from collections import defaultdict
  5. import importlib
  6. import inspect
  7. import logging
  8. import pkgutil
  9. log = logging.getLogger(__name__)
  10. class ProviderList(object):
  11. AWS = 'aws'
  12. OPENSTACK = 'openstack'
  13. GCE = 'gce'
  14. class CloudProviderFactory(object):
  15. """
  16. Get info and handle on the available cloud provider implementations.
  17. """
  18. def __init__(self):
  19. self.provider_list = defaultdict(dict)
  20. def register_provider_class(self, cls):
  21. """
  22. Registers a provider class with the factory. The class must
  23. inherit from cloudbridge.cloud.interfaces.CloudProvider
  24. and also have a class attribute named PROVIDER_ID.
  25. The PROVIDER_ID is a user friendly name for the cloud provider,
  26. such as 'aws'. The PROVIDER_ID must also be included in the
  27. cloudbridge.factory.ProviderList.
  28. :type cls: class
  29. :param cls: A class implementing the CloudProvider interface.
  30. Mock providers must also implement
  31. :py:class:`cloudbridge.cloud.base.helpers.
  32. TestMockHelperMixin`.
  33. """
  34. if isinstance(cls, type) and issubclass(cls, CloudProvider):
  35. if hasattr(cls, "PROVIDER_ID"):
  36. provider_id = getattr(cls, "PROVIDER_ID")
  37. if issubclass(cls, TestMockHelperMixin):
  38. if self.provider_list.get(provider_id, {}).get(
  39. 'mock_class'):
  40. log.warning("Mock provider with id: %s is already "
  41. "registered. Overriding with class: %s",
  42. provider_id, cls)
  43. self.provider_list[provider_id]['mock_class'] = cls
  44. else:
  45. if self.provider_list.get(provider_id, {}).get('class'):
  46. log.warning("Provider with id: %s is already "
  47. "registered. Overriding with class: %s",
  48. provider_id, cls)
  49. self.provider_list[provider_id]['class'] = cls
  50. else:
  51. log.warning("Provider class: %s implements CloudProvider but"
  52. " does not define PROVIDER_ID. Ignoring...", cls)
  53. else:
  54. log.debug("Class: %s does not implement the CloudProvider"
  55. " interface. Ignoring...", cls)
  56. def discover_providers(self):
  57. """
  58. Discover all available providers within the
  59. ``cloudbridge.cloud.providers`` package.
  60. Note that this methods does not guard against a failed import.
  61. """
  62. for _, modname, _ in pkgutil.iter_modules(providers.__path__):
  63. self._import_provider(modname)
  64. def _import_provider(self, module_name):
  65. """
  66. Imports and registers providers from the given module name.
  67. Raises an ImportError if the import does not succeed.
  68. """
  69. module = importlib.import_module(
  70. "{0}.{1}".format(providers.__name__,
  71. module_name))
  72. classes = inspect.getmembers(module, inspect.isclass)
  73. for _, cls in classes:
  74. self.register_provider_class(cls)
  75. def list_providers(self):
  76. """
  77. Get a list of available providers.
  78. It uses a simple automatic discovery system by iterating through all
  79. submodules in cloudbridge.cloud.providers.
  80. :rtype: dict
  81. :return: A dict of available providers and their implementations in the
  82. following format::
  83. {'aws': {'class': aws.provider.AWSCloudProvider,
  84. 'mock_class': aws.provider.MockAWSCloudProvider},
  85. 'openstack': {'class': openstack.provider.OpenStackCloudProvi
  86. der}
  87. }
  88. """
  89. if not self.provider_list:
  90. self.discover_providers()
  91. return self.provider_list
  92. def create_provider(self, name, config):
  93. """
  94. Searches all available providers for a CloudProvider interface with the
  95. given name, and instantiates it based on the given config dictionary,
  96. where the config dictionary is a dictionary understood by that
  97. cloud provider.
  98. :type name: str
  99. :param name: Cloud provider name: one of ``aws``, ``openstack``.
  100. :type config: an object with required fields
  101. :param config: This can be a Bunch or any other object whose fields can
  102. be accessed using dot notation. See specific provider
  103. implementation for the required fields.
  104. :return: a concrete provider instance
  105. :rtype: ``object`` of :class:`.CloudProvider`
  106. """
  107. provider_class = self.get_provider_class(name)
  108. if provider_class is None:
  109. raise NotImplementedError(
  110. 'A provider with name {0} could not be'
  111. ' found'.format(name))
  112. return provider_class(config)
  113. def get_provider_class(self, name, get_mock=False):
  114. """
  115. Return a class for the requested provider.
  116. :type get_mock: ``bool``
  117. :param get_mock: If True, returns a mock version of the provider
  118. if available, or the real version if not.
  119. :rtype: provider class or ``None``
  120. :return: A class corresponding to the requested provider or ``None``
  121. if the provider was not found.
  122. """
  123. impl = self.list_providers().get(name)
  124. if impl:
  125. if get_mock and impl.get("mock_class"):
  126. return impl["mock_class"]
  127. else:
  128. return impl["class"]
  129. else:
  130. return None
  131. def get_all_provider_classes(self, get_mock=False):
  132. """
  133. Returns a list of classes for all available provider implementations
  134. :type get_mock: ``bool``
  135. :param get_mock: If True, returns a mock version of the provider
  136. if available, or the real version if not.
  137. :rtype: type ``class`` or ``None``
  138. :return: A list of all available provider classes or an empty list
  139. if none found.
  140. """
  141. all_providers = []
  142. for impl in self.list_providers().values():
  143. if get_mock and impl.get("mock_class"):
  144. all_providers.append(impl["mock_class"])
  145. else:
  146. all_providers.append(impl["class"])
  147. return all_providers