provider.py 6.3 KB

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