services.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. """
  2. Base implementation for services available through a provider
  3. """
  4. import logging
  5. import cloudbridge.cloud.base.helpers as cb_helpers
  6. from cloudbridge.cloud.base.events import execute
  7. from cloudbridge.cloud.base.resources import BaseBucket
  8. from cloudbridge.cloud.base.resources import BaseNetwork
  9. from cloudbridge.cloud.base.resources import BaseRouter
  10. from cloudbridge.cloud.base.resources import BaseSubnet
  11. from cloudbridge.cloud.interfaces.exceptions import \
  12. InvalidConfigurationException
  13. from cloudbridge.cloud.interfaces.resources import Network
  14. from cloudbridge.cloud.interfaces.resources import Router
  15. from cloudbridge.cloud.interfaces.services import BucketObjectService
  16. from cloudbridge.cloud.interfaces.services import BucketService
  17. from cloudbridge.cloud.interfaces.services import CloudService
  18. from cloudbridge.cloud.interfaces.services import ComputeService
  19. from cloudbridge.cloud.interfaces.services import ImageService
  20. from cloudbridge.cloud.interfaces.services import InstanceService
  21. from cloudbridge.cloud.interfaces.services import KeyPairService
  22. from cloudbridge.cloud.interfaces.services import NetworkService
  23. from cloudbridge.cloud.interfaces.services import NetworkingService
  24. from cloudbridge.cloud.interfaces.services import RegionService
  25. from cloudbridge.cloud.interfaces.services import RouterService
  26. from cloudbridge.cloud.interfaces.services import SecurityService
  27. from cloudbridge.cloud.interfaces.services import SnapshotService
  28. from cloudbridge.cloud.interfaces.services import StorageService
  29. from cloudbridge.cloud.interfaces.services import SubnetService
  30. from cloudbridge.cloud.interfaces.services import VMFirewallService
  31. from cloudbridge.cloud.interfaces.services import VMTypeService
  32. from cloudbridge.cloud.interfaces.services import VolumeService
  33. from .resources import BasePageableObjectMixin
  34. from .resources import ClientPagedResultList
  35. log = logging.getLogger(__name__)
  36. class BaseCloudService(CloudService):
  37. STANDARD_EVENT_PRIORITY = 2500
  38. def __init__(self, provider):
  39. self._service_event_pattern = provider.PROVIDER_ID
  40. self._provider = provider
  41. # discover and register all middleware
  42. provider.middleware.add(self)
  43. @property
  44. def provider(self):
  45. return self._provider
  46. def emit(self, sender, event, *args, **kwargs):
  47. return self._provider.events.emit(sender, event, *args, **kwargs)
  48. def _generate_event_pattern(self, func_name):
  49. return ".".join((self._service_event_pattern, func_name))
  50. def observe_function(self, func_name, priority, callback):
  51. event_pattern = self._generate_event_pattern(func_name)
  52. self.provider.events.observe(event_pattern, priority, callback)
  53. def intercept_function(self, func_name, priority, callback):
  54. event_pattern = self._generate_event_pattern(func_name)
  55. self.provider.events.intercept(event_pattern, priority, callback)
  56. def execute_function(self, func_name, priority, callback):
  57. event_pattern = self._generate_event_pattern(func_name)
  58. self.provider.events.execute(event_pattern, priority, callback)
  59. def emit_function(self, sender, func_name, *args, **kwargs):
  60. """
  61. Emits the event corresponding to the given function name for the
  62. current service
  63. :type sender: CloudService
  64. :param sender: The CloudBridge Service object sending the emit signal
  65. :type func_name: str
  66. :param func_name: The name of the function to be emitted. e.g.: 'get'
  67. :type args: CloudService
  68. :return: The return value resulting from the handler chain invocations
  69. """
  70. full_event_name = self._generate_event_pattern(func_name)
  71. return self._provider.events.emit(sender, full_event_name,
  72. *args, **kwargs)
  73. class BaseSecurityService(SecurityService, BaseCloudService):
  74. def __init__(self, provider):
  75. super(BaseSecurityService, self).__init__(provider)
  76. self._service_event_pattern += ".security"
  77. class BaseKeyPairService(
  78. BasePageableObjectMixin, KeyPairService, BaseSecurityService):
  79. def __init__(self, provider):
  80. super(BaseKeyPairService, self).__init__(provider)
  81. self._service_event_pattern += ".key_pairs"
  82. def delete(self, key_pair_id):
  83. """
  84. Delete an existing key pair.
  85. :type key_pair_id: str
  86. :param key_pair_id: The id of the key pair to be deleted.
  87. :rtype: ``bool``
  88. :return: ``True`` if the key does not exist. Note that this implies
  89. that the key may not have been deleted by this method but
  90. instead has not existed in the first place.
  91. """
  92. log.info("Deleting the existing key pair %s", key_pair_id)
  93. kp = self.get(key_pair_id)
  94. if kp:
  95. kp.delete()
  96. return True
  97. class BaseVMFirewallService(
  98. BasePageableObjectMixin, VMFirewallService, BaseSecurityService):
  99. def __init__(self, provider):
  100. super(BaseVMFirewallService, self).__init__(provider)
  101. self._service_event_pattern += ".vm_firewalls"
  102. def find(self, **kwargs):
  103. obj_list = self
  104. filters = ['label']
  105. matches = cb_helpers.generic_find(filters, kwargs, obj_list)
  106. # All kwargs should have been popped at this time.
  107. if len(kwargs) > 0:
  108. raise TypeError("Unrecognised parameters for search: %s."
  109. " Supported attributes: %s" % (kwargs,
  110. ", ".join(filters)))
  111. return ClientPagedResultList(self.provider,
  112. matches if matches else [])
  113. class BaseStorageService(StorageService, BaseCloudService):
  114. def __init__(self, provider):
  115. super(BaseStorageService, self).__init__(provider)
  116. self._service_event_pattern += ".storage"
  117. class BaseVolumeService(
  118. BasePageableObjectMixin, VolumeService, BaseStorageService):
  119. def __init__(self, provider):
  120. super(BaseVolumeService, self).__init__(provider)
  121. self._service_event_pattern += ".volumes"
  122. class BaseSnapshotService(
  123. BasePageableObjectMixin, SnapshotService, BaseStorageService):
  124. def __init__(self, provider):
  125. super(BaseSnapshotService, self).__init__(provider)
  126. self._service_event_pattern += ".snapshots"
  127. class BaseBucketService(
  128. BasePageableObjectMixin, BucketService, BaseStorageService):
  129. def __init__(self, provider):
  130. super(BaseBucketService, self).__init__(provider)
  131. self._service_event_pattern += ".buckets"
  132. # Generic find will be used for providers where we have not implemented
  133. # provider-specific querying for find method
  134. @execute(event_pattern="*.storage.buckets.find",
  135. priority=BaseCloudService.STANDARD_EVENT_PRIORITY)
  136. def _find(self, **kwargs):
  137. obj_list = self
  138. filters = ['name']
  139. matches = cb_helpers.generic_find(filters, kwargs, obj_list)
  140. # All kwargs should have been popped at this time.
  141. if len(kwargs) > 0:
  142. raise TypeError("Unrecognised parameters for search: %s."
  143. " Supported attributes: %s" % (kwargs,
  144. ", ".join(filters)))
  145. return ClientPagedResultList(self.provider,
  146. matches if matches else [])
  147. def get(self, bucket_id):
  148. """
  149. Returns a bucket given its ID. Returns ``None`` if the bucket
  150. does not exist.
  151. :type bucket_id: str
  152. :param bucket_id: The id of the desired bucket.
  153. :rtype: ``Bucket``
  154. :return: ``None`` is returned if the bucket does not exist, and
  155. the bucket's provider-specific CloudBridge object is
  156. returned if the bucket is found.
  157. """
  158. return self.emit_function(self, "get", bucket_id)
  159. def find(self, **kwargs):
  160. """
  161. Returns a list of buckets filtered by the given keyword arguments.
  162. Accepted search arguments are: 'name'
  163. """
  164. return self.emit_function(self, "find", **kwargs)
  165. def list(self, limit=None, marker=None):
  166. """
  167. List all buckets.
  168. """
  169. return self.emit_function(self, "list", limit=limit, marker=marker)
  170. def create(self, name, location=None):
  171. """
  172. Create a new bucket.
  173. :type name: str
  174. :param name: The name of the bucket to be created. Note that names
  175. must be unique, and are unchangeable.
  176. :rtype: ``Bucket``
  177. :return: The created bucket's provider-specific CloudBridge object.
  178. """
  179. BaseBucket.assert_valid_resource_name(name)
  180. return self.emit_function(self, "create", name, location=location)
  181. def delete(self, bucket_id):
  182. """
  183. Delete an existing bucket.
  184. :type bucket_id: str
  185. :param bucket_id: The ID of the bucket to be deleted.
  186. """
  187. return self.emit_function(self, "delete", bucket_id)
  188. class BaseBucketObjectService(
  189. BasePageableObjectMixin, BucketObjectService, BaseStorageService):
  190. def __init__(self, provider):
  191. super(BaseBucketObjectService, self).__init__(provider)
  192. self._service_event_pattern += ".bucket_objects"
  193. self._bucket = None
  194. def set_bucket(self, bucket):
  195. bucket = bucket if isinstance(bucket, BaseBucket) \
  196. else self.provider.storage.buckets.get(bucket)
  197. self._bucket = bucket
  198. def __iter__(self):
  199. if not self._bucket:
  200. message = "You must set a bucket before iterating through its " \
  201. "objects. We do not allow iterating through all " \
  202. "buckets at this time. In order to set a bucket, use: " \
  203. "`provider.storage.bucket_objects.set_bucket(my_bucket)`"
  204. raise InvalidConfigurationException(message)
  205. result_list = self.list(bucket=self._bucket)
  206. if result_list.supports_server_paging:
  207. for result in result_list:
  208. yield result
  209. while result_list.is_truncated:
  210. result_list = self.list(bucket=self._bucket,
  211. marker=result_list.marker)
  212. for result in result_list:
  213. yield result
  214. else:
  215. for result in result_list.data:
  216. yield result
  217. class BaseComputeService(ComputeService, BaseCloudService):
  218. def __init__(self, provider):
  219. super(BaseComputeService, self).__init__(provider)
  220. self._service_event_pattern += ".compute"
  221. class BaseImageService(
  222. BasePageableObjectMixin, ImageService, BaseComputeService):
  223. def __init__(self, provider):
  224. super(BaseImageService, self).__init__(provider)
  225. self._service_event_pattern += ".images"
  226. class BaseInstanceService(
  227. BasePageableObjectMixin, InstanceService, BaseComputeService):
  228. def __init__(self, provider):
  229. super(BaseInstanceService, self).__init__(provider)
  230. self._service_event_pattern += ".instances"
  231. class BaseVMTypeService(
  232. BasePageableObjectMixin, VMTypeService, BaseComputeService):
  233. def __init__(self, provider):
  234. super(BaseVMTypeService, self).__init__(provider)
  235. self._service_event_pattern += ".vm_types"
  236. def get(self, vm_type_id):
  237. vm_type = (t for t in self if t.id == vm_type_id)
  238. return next(vm_type, None)
  239. def find(self, **kwargs):
  240. obj_list = self
  241. filters = ['name']
  242. matches = cb_helpers.generic_find(filters, kwargs, obj_list)
  243. return ClientPagedResultList(self._provider, list(matches))
  244. class BaseRegionService(
  245. BasePageableObjectMixin, RegionService, BaseComputeService):
  246. def __init__(self, provider):
  247. super(BaseRegionService, self).__init__(provider)
  248. self._service_event_pattern += ".regions"
  249. def find(self, **kwargs):
  250. obj_list = self
  251. filters = ['name']
  252. matches = cb_helpers.generic_find(filters, kwargs, obj_list)
  253. return ClientPagedResultList(self._provider, list(matches))
  254. class BaseNetworkingService(NetworkingService, BaseCloudService):
  255. def __init__(self, provider):
  256. super(BaseNetworkingService, self).__init__(provider)
  257. self._service_event_pattern += ".networking"
  258. class BaseNetworkService(
  259. BasePageableObjectMixin, NetworkService, BaseNetworkingService):
  260. def __init__(self, provider):
  261. super(BaseNetworkService, self).__init__(provider)
  262. self._service_event_pattern += ".networks"
  263. @property
  264. def subnets(self):
  265. return [subnet for subnet in self.provider.subnets
  266. if subnet.network_id == self.id]
  267. def delete(self, network_id):
  268. network = self.get(network_id)
  269. if network:
  270. log.info("Deleting network %s", network_id)
  271. network.delete()
  272. def get_or_create_default(self):
  273. networks = self.provider.networking.networks.find(
  274. label=BaseNetwork.CB_DEFAULT_NETWORK_LABEL)
  275. if networks:
  276. return networks[0]
  277. else:
  278. log.info("Creating a CloudBridge-default network labeled %s",
  279. BaseNetwork.CB_DEFAULT_NETWORK_LABEL)
  280. return self.provider.networking.networks.create(
  281. BaseNetwork.CB_DEFAULT_NETWORK_LABEL, '10.0.0.0/16')
  282. class BaseSubnetService(
  283. BasePageableObjectMixin, SubnetService, BaseNetworkingService):
  284. def __init__(self, provider):
  285. super(BaseSubnetService, self).__init__(provider)
  286. self._service_event_pattern += ".subnets"
  287. def find(self, **kwargs):
  288. obj_list = self
  289. filters = ['label']
  290. matches = cb_helpers.generic_find(filters, kwargs, obj_list)
  291. return ClientPagedResultList(self._provider, list(matches))
  292. def get_or_create_default(self, zone):
  293. # Look for a CB-default subnet
  294. matches = self.find(label=BaseSubnet.CB_DEFAULT_SUBNET_LABEL)
  295. if matches:
  296. return matches[0]
  297. # No provider-default Subnet exists, try to create it (net + subnets)
  298. network = self.provider.networking.networks.get_or_create_default()
  299. subnet = self.create(BaseSubnet.CB_DEFAULT_SUBNET_LABEL, network,
  300. BaseSubnet.CB_DEFAULT_SUBNET_IPV4RANGE, zone)
  301. return subnet
  302. class BaseRouterService(
  303. BasePageableObjectMixin, RouterService, BaseNetworkingService):
  304. def __init__(self, provider):
  305. super(BaseRouterService, self).__init__(provider)
  306. self._service_event_pattern += ".routers"
  307. def delete(self, router):
  308. if isinstance(router, Router):
  309. log.info("Router %s successful deleted.", router)
  310. router.delete()
  311. else:
  312. log.info("Getting router %s", router)
  313. router = self.get(router)
  314. if router:
  315. log.info("Router %s successful deleted.", router)
  316. router.delete()
  317. def get_or_create_default(self, network):
  318. net_id = network.id if isinstance(network, Network) else network
  319. routers = self.provider.networking.routers.find(
  320. label=BaseRouter.CB_DEFAULT_ROUTER_LABEL)
  321. for router in routers:
  322. if router.network_id == net_id:
  323. return router
  324. else:
  325. return self.provider.networking.routers.create(
  326. network=net_id, label=BaseRouter.CB_DEFAULT_ROUTER_LABEL)