base.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. """
  2. Implementation of common methods across cloud providers.
  3. """
  4. import logging
  5. import time
  6. from cloudbridge.providers.interfaces import CloudProvider
  7. from cloudbridge.providers.interfaces import Instance
  8. from cloudbridge.providers.interfaces import InstanceState
  9. from cloudbridge.providers.interfaces import InstanceType
  10. from cloudbridge.providers.interfaces import KeyPair
  11. from cloudbridge.providers.interfaces import MachineImage
  12. from cloudbridge.providers.interfaces import MachineImageState
  13. from cloudbridge.providers.interfaces import ObjectLifeCycleMixin
  14. from cloudbridge.providers.interfaces import Region
  15. from cloudbridge.providers.interfaces import SecurityGroup
  16. from cloudbridge.providers.interfaces import SecurityGroupRule
  17. from cloudbridge.providers.interfaces import Snapshot
  18. from cloudbridge.providers.interfaces import SnapshotState
  19. from cloudbridge.providers.interfaces import Volume
  20. from cloudbridge.providers.interfaces import VolumeState
  21. from cloudbridge.providers.interfaces import WaitStateException
  22. from cloudbridge.providers.interfaces.services import BlockStoreService
  23. from cloudbridge.providers.interfaces.services import ComputeService
  24. from cloudbridge.providers.interfaces.services import ImageService
  25. from cloudbridge.providers.interfaces.services import InstanceService
  26. from cloudbridge.providers.interfaces.services import InstanceTypesService
  27. from cloudbridge.providers.interfaces.services import KeyPairService
  28. from cloudbridge.providers.interfaces.services import ObjectStoreService
  29. from cloudbridge.providers.interfaces.services import ProviderService
  30. from cloudbridge.providers.interfaces.services import RegionService
  31. from cloudbridge.providers.interfaces.services import SecurityGroupService
  32. from cloudbridge.providers.interfaces.services import SecurityService
  33. from cloudbridge.providers.interfaces.services import SnapshotService
  34. from cloudbridge.providers.interfaces.services import VolumeService
  35. log = logging.getLogger(__name__)
  36. class BaseCloudProvider(CloudProvider):
  37. def __init__(self, config):
  38. self._config = config
  39. @property
  40. def config(self):
  41. return self._config
  42. @config.setter
  43. def config(self, config):
  44. self._config = config
  45. @property
  46. def name(self):
  47. return str(self.__class__.__name__)
  48. def has_service(self, service_type):
  49. """
  50. Checks whether this provider supports a given service.
  51. :type service_type: str or :class:``.CloudProviderServiceType``
  52. :param service_type: Type of service to check support for.
  53. :rtype: bool
  54. :return: ``True`` if the service type is supported.
  55. """
  56. try:
  57. if getattr(self, service_type):
  58. return True
  59. except AttributeError:
  60. pass # Undefined service type
  61. return False
  62. def _get_config_value(self, key, default_value):
  63. """
  64. A convenience method to extract a configuration value.
  65. :type key: str
  66. :param key: a field to look for in the ``self.config`` field
  67. :type default_value: anything
  68. : param default_value: the default value to return if a value for the
  69. ``key`` is not available
  70. :return: a configuration value for the supplied ``key``
  71. """
  72. if isinstance(self.config, dict):
  73. return self.config.get(key, default_value)
  74. else:
  75. return getattr(self.config, key) if hasattr(
  76. self.config, key) and getattr(self.config, key) else \
  77. default_value
  78. class BaseObjectLifeCycleMixin(ObjectLifeCycleMixin):
  79. """
  80. A base implementation of an ObjectLifeCycleMixin.
  81. This base implementation has an implementation of wait_till_ready
  82. which refreshes the object's state till the desired ready states
  83. are reached. Subclasses must implement two new properties - ready_states
  84. and terminal_states, which return a list of states to wait for.
  85. """
  86. @property
  87. def ready_states(self):
  88. raise NotImplementedError(
  89. "ready_states not implemented by this object. Subclasses must"
  90. " implement this method and return a valid set of ready states")
  91. @property
  92. def terminal_states(self):
  93. raise NotImplementedError(
  94. "terminal_states not implemented by this object. Subclasses must"
  95. " implement this method and return a valid set of terminal states")
  96. def wait_for(self, target_states, terminal_states=None, timeout=600,
  97. interval=5):
  98. assert timeout > 0
  99. assert timeout > interval
  100. end_time = time.time() + timeout
  101. while True:
  102. if self.state in target_states:
  103. log.debug(
  104. "Object: {0} successfully reached target state:"
  105. " {1}".format(self, self.state))
  106. return True
  107. elif self.state in terminal_states:
  108. raise WaitStateException(
  109. "Object: {0} is in state: {1} which is a terminal state"
  110. " and cannot be waited on.".format(self, self.state))
  111. else:
  112. log.debug(
  113. "Object {0} is in state: {1}. Waiting another {2}"
  114. " seconds to reach target state(s): {3}...".format(
  115. self,
  116. self.state,
  117. int(end_time - time.time()),
  118. target_states))
  119. time.sleep(interval)
  120. if time.time() > end_time:
  121. raise WaitStateException(
  122. "Waited too long for object: {0} to become ready. It's"
  123. " still in state: {1}".format(self, self.state))
  124. self.refresh()
  125. def wait_till_ready(self, timeout=600, interval=5):
  126. self.wait_for(
  127. self.ready_states,
  128. self.terminal_states,
  129. timeout,
  130. interval)
  131. class BaseInstanceType(InstanceType):
  132. @property
  133. def total_disk(self):
  134. return self.root_disk + self.ephemeral_disk
  135. def __repr__(self):
  136. return "<CB-{0}: {1}>".format(self.__class__.__name__, self.name)
  137. class BaseInstance(BaseObjectLifeCycleMixin, Instance):
  138. @property
  139. def ready_states(self):
  140. return [InstanceState.RUNNING]
  141. @property
  142. def terminal_states(self):
  143. return [InstanceState.TERMINATED, InstanceState.ERROR]
  144. class BaseMachineImage(BaseObjectLifeCycleMixin, MachineImage):
  145. @property
  146. def ready_states(self):
  147. return [MachineImageState.AVAILABLE]
  148. @property
  149. def terminal_states(self):
  150. return [MachineImageState.ERROR]
  151. def __repr__(self):
  152. return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__,
  153. self.image_id, self.name)
  154. class BaseVolume(BaseObjectLifeCycleMixin, Volume):
  155. @property
  156. def ready_states(self):
  157. return [VolumeState.AVAILABLE]
  158. @property
  159. def terminal_states(self):
  160. return [VolumeState.ERROR, VolumeState.DELETED]
  161. class BaseSnapshot(BaseObjectLifeCycleMixin, Snapshot):
  162. @property
  163. def ready_states(self):
  164. return [SnapshotState.AVAILABLE]
  165. @property
  166. def terminal_states(self):
  167. return [SnapshotState.ERROR]
  168. class BaseKeyPair(KeyPair):
  169. def __init__(self, provider, key_pair):
  170. self._provider = provider
  171. self._key_pair = key_pair
  172. @property
  173. def name(self):
  174. """
  175. Return the name of this key pair.
  176. """
  177. return self._key_pair.name
  178. def delete(self):
  179. """
  180. Delete this KeyPair.
  181. :rtype: bool
  182. :return: True if successful, otherwise False.
  183. """
  184. # This implementation assumes the `delete` method exists across
  185. # multiple providers.
  186. self._key_pair.delete()
  187. def __repr__(self):
  188. return "<CBKeyPair: {0}>".format(self.name)
  189. class BaseSecurityGroup(SecurityGroup):
  190. def __init__(self, provider, security_group):
  191. self._provider = provider
  192. self._security_group = security_group
  193. def __eq__(self, other):
  194. """
  195. Check if all the defined rules match across both security groups.
  196. """
  197. if isinstance(other, SecurityGroup) and \
  198. self._provider == other._provider and \
  199. len(self.rules) == len(other.rules):
  200. eq = True
  201. for rule in other.rules:
  202. eq = eq and self.rule_exists(self.rules, rule)
  203. return eq
  204. else:
  205. return False
  206. def __ne__(self, other):
  207. return not self.__eq__(other)
  208. def rule_exists(self, rules, rule):
  209. """
  210. Check if an authorization rule exists in a list of rules.
  211. :type rules: list of :class:``.SecurityGroupRule``
  212. :param rules: A list of rules to check against
  213. :type rule: :class:``.SecurityGroupRule``
  214. :param rule: A rule whose existence to check for
  215. :rtype: bool
  216. :return: ``True`` if an existing rule matches the supplied rule;
  217. ``False`` otherwise.
  218. """
  219. for r in rules:
  220. if r == rule:
  221. return True
  222. return False
  223. @property
  224. def id(self):
  225. """
  226. Get the ID of this security group.
  227. :rtype: str
  228. :return: Security group ID
  229. """
  230. return self._security_group.id
  231. @property
  232. def name(self):
  233. """
  234. Return the name of this security group.
  235. """
  236. return self._security_group.name
  237. @property
  238. def description(self):
  239. """
  240. Return the description of this security group.
  241. """
  242. return self._security_group.description
  243. def delete(self):
  244. """
  245. Delete this security group.
  246. """
  247. return self._security_group.delete()
  248. def __repr__(self):
  249. return "<CBSecurityGroup: {0}>".format(self.name)
  250. class BaseSecurityGroupRule(SecurityGroupRule):
  251. def __init__(self, provider, rule, parent):
  252. self._provider = provider
  253. self._rule = rule
  254. self.parent = parent
  255. def __repr__(self):
  256. return "<CBSecurityGroupRule: IP: {0}; from: {1}; to: {2}>".format(
  257. self.ip_protocol, self.from_port, self.to_port)
  258. def __eq__(self, other):
  259. return self.ip_protocol == other.ip_protocol and \
  260. self.from_port == other.from_port and \
  261. self.to_port == other.to_port and \
  262. self.cidr_ip == other.cidr_ip
  263. def __ne__(self, other):
  264. return not self.__eq__(other)
  265. class BaseRegion(Region):
  266. def __init__(self, provider, region):
  267. self._provider = provider
  268. self._region = region
  269. def __repr__(self):
  270. return "<CB-{0}: {1}>".format(self.__class__.__name__,
  271. self.name)
  272. def __eq__(self, other):
  273. if isinstance(other, Region):
  274. return self._provider == other._provider and \
  275. self.id == other.id
  276. class BaseProviderService(ProviderService):
  277. def __init__(self, provider):
  278. self._provider = provider
  279. @property
  280. def provider(self):
  281. return self._provider
  282. class BaseComputeService(ComputeService, BaseProviderService):
  283. def __init__(self, provider):
  284. super(BaseComputeService, self).__init__(provider)
  285. class BaseVolumeService(VolumeService, BaseProviderService):
  286. def __init__(self, provider):
  287. super(BaseVolumeService, self).__init__(provider)
  288. class BaseSnapshotService(SnapshotService, BaseProviderService):
  289. def __init__(self, provider):
  290. super(BaseSnapshotService, self).__init__(provider)
  291. class BaseBlockStoreService(BlockStoreService, BaseProviderService):
  292. def __init__(self, provider):
  293. super(BaseBlockStoreService, self).__init__(provider)
  294. class BaseImageService(ImageService, BaseProviderService):
  295. def __init__(self, provider):
  296. super(BaseImageService, self).__init__(provider)
  297. class BaseObjectStoreService(ObjectStoreService, BaseProviderService):
  298. def __init__(self, provider):
  299. super(BaseObjectStoreService, self).__init__(provider)
  300. class BaseSecurityService(SecurityService, BaseProviderService):
  301. def __init__(self, provider):
  302. super(BaseSecurityService, self).__init__(provider)
  303. class BaseKeyPairService(KeyPairService, BaseProviderService):
  304. def __init__(self, provider):
  305. super(BaseKeyPairService, self).__init__(provider)
  306. class BaseSecurityGroupService(SecurityGroupService, BaseProviderService):
  307. def __init__(self, provider):
  308. super(BaseSecurityGroupService, self).__init__(provider)
  309. class BaseInstanceTypesService(InstanceTypesService, BaseProviderService):
  310. def __init__(self, provider):
  311. super(BaseInstanceTypesService, self).__init__(provider)
  312. class BaseInstanceService(InstanceService, BaseProviderService):
  313. def __init__(self, provider):
  314. super(BaseInstanceService, self).__init__(provider)
  315. class BaseRegionService(RegionService, BaseProviderService):
  316. def __init__(self, provider):
  317. super(BaseRegionService, self).__init__(provider)