provider.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. """Base implementation of a provider interface."""
  2. import functools
  3. import logging
  4. import os
  5. from os.path import expanduser
  6. try:
  7. from configparser import ConfigParser
  8. except ImportError: # Python 2
  9. from ConfigParser import SafeConfigParser as ConfigParser
  10. from pyeventsystem.middleware import SimpleMiddlewareManager
  11. import six
  12. from ..base.middleware import ExceptionWrappingMiddleware
  13. from ..interfaces import CloudProvider
  14. from ..interfaces.exceptions import ProviderConnectionException
  15. from ..interfaces.resources import Configuration
  16. log = logging.getLogger(__name__)
  17. DEFAULT_RESULT_LIMIT = 50
  18. DEFAULT_WAIT_TIMEOUT = 600
  19. DEFAULT_WAIT_INTERVAL = 5
  20. # By default, use two locations for CloudBridge configuration
  21. CloudBridgeConfigPath = '/etc/cloudbridge.ini'
  22. CloudBridgeConfigLocations = [CloudBridgeConfigPath]
  23. UserConfigPath = os.path.join(expanduser('~'), '.cloudbridge')
  24. CloudBridgeConfigLocations.append(UserConfigPath)
  25. class BaseConfiguration(Configuration):
  26. def __init__(self, user_config):
  27. self.update(user_config)
  28. @property
  29. def default_result_limit(self):
  30. """
  31. Get the maximum number of results to return for a
  32. list method
  33. :rtype: ``int``
  34. :return: The maximum number of results to return
  35. """
  36. log.debug("Maximum number of results for list methods %s",
  37. DEFAULT_RESULT_LIMIT)
  38. return self.get('default_result_limit', DEFAULT_RESULT_LIMIT)
  39. @property
  40. def default_wait_timeout(self):
  41. """
  42. Gets the default wait timeout for LifeCycleObjects.
  43. """
  44. log.debug("Default wait timeout for LifeCycleObjects %s",
  45. DEFAULT_WAIT_TIMEOUT)
  46. return self.get('default_wait_timeout', DEFAULT_WAIT_TIMEOUT)
  47. @property
  48. def default_wait_interval(self):
  49. """
  50. Gets the default wait interval for LifeCycleObjects.
  51. """
  52. log.debug("Default wait interfal for LifeCycleObjects %s",
  53. DEFAULT_WAIT_INTERVAL)
  54. return self.get('default_wait_interval', DEFAULT_WAIT_INTERVAL)
  55. @property
  56. def debug_mode(self):
  57. """
  58. A flag indicating whether CloudBridge is in debug mode. Setting
  59. this to True will cause the underlying provider's debug
  60. output to be turned on.
  61. The flag can be toggled by sending in the cb_debug value via
  62. the config dictionary, or setting the CB_DEBUG environment variable.
  63. :rtype: ``bool``
  64. :return: Whether debug mode is on.
  65. """
  66. return self.get('cb_debug', os.environ.get('CB_DEBUG', False))
  67. class BaseCloudProvider(CloudProvider):
  68. def __init__(self, config):
  69. self._config = BaseConfiguration(config)
  70. self._config_parser = ConfigParser()
  71. self._config_parser.read(CloudBridgeConfigLocations)
  72. self._middleware = SimpleMiddlewareManager()
  73. self.add_required_middleware()
  74. self._region_name = None
  75. self._zone_name = None
  76. @property
  77. def region_name(self):
  78. return self._region_name
  79. @property
  80. def zone_name(self):
  81. if not self._zone_name:
  82. region = self.compute.regions.get(self.region_name)
  83. # TODO: Default zone
  84. self._zone_name = next(iter(region.zones))
  85. return self._zone_name
  86. @property
  87. def config(self):
  88. return self._config
  89. @property
  90. def name(self):
  91. return str(self.__class__.__name__)
  92. @property
  93. def middleware(self):
  94. return self._middleware
  95. def add_required_middleware(self):
  96. """
  97. Adds common middleware that is essential for cloudbridge to function.
  98. Any other extra middleware can be added through the provider factory.
  99. """
  100. self.middleware.add(ExceptionWrappingMiddleware())
  101. def authenticate(self):
  102. """
  103. A basic implementation which simply runs a low impact command to
  104. check whether cloud credentials work. Providers should override with
  105. more efficient implementations.
  106. """
  107. log.debug("Checking if cloud credential works...")
  108. try:
  109. self.security.key_pairs.list()
  110. return True
  111. except Exception as e:
  112. log.exception("ProviderConnectionException occurred")
  113. raise ProviderConnectionException(
  114. "Authentication with cloud provider failed: %s" % (e,))
  115. def _deepgetattr(self, obj, attr):
  116. """Recurses through an attribute chain to get the ultimate value."""
  117. return functools.reduce(getattr, attr.split('.'), obj)
  118. def has_service(self, service_type):
  119. """
  120. Checks whether this provider supports a given service.
  121. :type service_type: str or :class:``.CloudServiceType``
  122. :param service_type: Type of service to check support for.
  123. :rtype: bool
  124. :return: ``True`` if the service type is supported.
  125. """
  126. log.info("Checking if provider supports %s", service_type)
  127. try:
  128. if self._deepgetattr(self, service_type):
  129. log.info("This provider supports %s",
  130. service_type)
  131. return True
  132. except AttributeError:
  133. pass # Undefined service type
  134. except NotImplementedError:
  135. pass # service not implemented
  136. log.info("This provider doesn't support %s",
  137. service_type)
  138. return False
  139. def _get_config_value(self, key, default_value=None):
  140. """
  141. A convenience method to extract a configuration value.
  142. :type key: str
  143. :param key: a field to look for in the ``self.config`` field
  144. :type default_value: anything
  145. :param default_value: the default value to return if a value for the
  146. ``key`` is not available
  147. :return: a configuration value for the supplied ``key``
  148. """
  149. log.debug("Getting config key %s, with supplied default value: %s",
  150. key, default_value)
  151. value = default_value
  152. if isinstance(self.config, dict) and self.config.get(key):
  153. value = self.config.get(key, default_value)
  154. elif hasattr(self.config, key) and getattr(self.config, key):
  155. value = getattr(self.config, key)
  156. elif (self._config_parser.has_option(self.PROVIDER_ID, key) and
  157. self._config_parser.get(self.PROVIDER_ID, key)):
  158. value = self._config_parser.get(self.PROVIDER_ID, key)
  159. if isinstance(value, six.string_types) and not isinstance(
  160. value, six.text_type):
  161. return six.u(value)
  162. return value