provider.py 5.7 KB

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