services.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. """
  2. Services implemented by the OpenStack provider.
  3. """
  4. from cinderclient.exceptions import NotFound as CinderNotFound
  5. from novaclient.exceptions import NotFound as NovaNotFound
  6. from cloudbridge.cloud.base import BaseBlockStoreService
  7. from cloudbridge.cloud.base import BaseComputeService
  8. from cloudbridge.cloud.base import BaseImageService
  9. from cloudbridge.cloud.base import BaseInstanceService
  10. from cloudbridge.cloud.base import BaseInstanceTypesService
  11. from cloudbridge.cloud.base import BaseKeyPairService
  12. from cloudbridge.cloud.base import BaseLaunchConfig
  13. from cloudbridge.cloud.base import BaseObjectStoreService
  14. from cloudbridge.cloud.base import BaseRegionService
  15. from cloudbridge.cloud.base import BaseSecurityGroupService
  16. from cloudbridge.cloud.base import BaseSecurityService
  17. from cloudbridge.cloud.base import BaseSnapshotService
  18. from cloudbridge.cloud.base import BaseVolumeService
  19. from cloudbridge.cloud.interfaces.resources import InstanceType
  20. from cloudbridge.cloud.interfaces.resources import KeyPair
  21. from cloudbridge.cloud.interfaces.resources import MachineImage
  22. from cloudbridge.cloud.interfaces.resources import PlacementZone
  23. from cloudbridge.cloud.interfaces.resources import SecurityGroup
  24. from cloudbridge.cloud.interfaces.resources import Snapshot
  25. from cloudbridge.cloud.interfaces.resources import Volume
  26. from .resources import OpenStackContainer
  27. from .resources import OpenStackInstance
  28. from .resources import OpenStackInstanceType
  29. from .resources import OpenStackKeyPair
  30. from .resources import OpenStackMachineImage
  31. from .resources import OpenStackRegion
  32. from .resources import OpenStackSecurityGroup
  33. from .resources import OpenStackSnapshot
  34. from .resources import OpenStackVolume
  35. class OpenStackSecurityService(BaseSecurityService):
  36. def __init__(self, provider):
  37. super(OpenStackSecurityService, self).__init__(provider)
  38. # Initialize provider services
  39. self._key_pairs = OpenStackKeyPairService(provider)
  40. self._security_groups = OpenStackSecurityGroupService(provider)
  41. @property
  42. def key_pairs(self):
  43. """
  44. Provides access to key pairs for this provider.
  45. :rtype: ``object`` of :class:`.KeyPairService`
  46. :return: a KeyPairService object
  47. """
  48. return self._key_pairs
  49. @property
  50. def security_groups(self):
  51. """
  52. Provides access to security groups for this provider.
  53. :rtype: ``object`` of :class:`.SecurityGroupService`
  54. :return: a SecurityGroupService object
  55. """
  56. return self._security_groups
  57. class OpenStackKeyPairService(BaseKeyPairService):
  58. def __init__(self, provider):
  59. super(OpenStackKeyPairService, self).__init__(provider)
  60. def list(self):
  61. """
  62. List all key pairs associated with this account.
  63. :rtype: ``list`` of :class:`.KeyPair`
  64. :return: list of KeyPair objects
  65. """
  66. key_pairs = self._provider.nova.keypairs.list()
  67. return [OpenStackKeyPair(self._provider, kp) for kp in key_pairs]
  68. def create(self, name):
  69. """
  70. Create a new key pair.
  71. :type name: str
  72. :param name: The name of the key pair to be created.
  73. :rtype: ``object`` of :class:`.KeyPair`
  74. :return: A keypair instance or None if one was not be created.
  75. """
  76. kp = self._provider.nova.keypairs.create(name)
  77. if kp:
  78. return OpenStackKeyPair(self._provider, kp)
  79. return None
  80. def delete(self, name):
  81. """
  82. Delete an existing key pair.
  83. :type name: str
  84. :param name: The name of the key pair to be deleted.
  85. :rtype: ``bool``
  86. :return: ``True`` if the key does not exist, ``False`` otherwise. Note
  87. that this implies that the key may not have been deleted by
  88. this method but instead has not existed in the first place.
  89. """
  90. try:
  91. kp = self._provider.nova.keypairs.find(name=name)
  92. kp.delete()
  93. return True
  94. except NovaNotFound:
  95. return True
  96. class OpenStackSecurityGroupService(BaseSecurityGroupService):
  97. def __init__(self, provider):
  98. super(OpenStackSecurityGroupService, self).__init__(provider)
  99. def list(self):
  100. """
  101. List all security groups associated with this account.
  102. :rtype: ``list`` of :class:`.SecurityGroup`
  103. :return: list of SecurityGroup objects
  104. """
  105. groups = self._provider.nova.security_groups.list()
  106. return [OpenStackSecurityGroup(
  107. self._provider, group) for group in groups]
  108. def create(self, name, description):
  109. """
  110. Create a new security group under the current account.
  111. :type name: str
  112. :param name: The name of the new security group.
  113. :type description: str
  114. :param description: The description of the new security group.
  115. :rtype: ``object`` of :class:`.SecurityGroup`
  116. :return: a SecurityGroup object
  117. """
  118. sg = self._provider.nova.security_groups.create(name, description)
  119. if sg:
  120. return OpenStackSecurityGroup(self._provider, sg)
  121. return None
  122. def get(self, group_names=None, group_ids=None):
  123. """
  124. Get all security groups associated with your account.
  125. :type group_names: list
  126. :param group_names: A list of strings of the names of security groups
  127. to retrieve. If not provided, all security groups
  128. will be returned.
  129. :type group_ids: list
  130. :param group_ids: A list of string IDs of security groups to retrieve.
  131. If not provided, all security groups will be
  132. returned.
  133. :rtype: list of :class:`SecurityGroup`
  134. :return: A list of SecurityGroup objects or an empty list if none
  135. found.
  136. """
  137. if not group_names:
  138. group_names = []
  139. if not group_ids:
  140. group_ids = []
  141. security_groups = self._provider.nova.security_groups.list()
  142. filtered = []
  143. for sg in security_groups:
  144. if sg.name in group_names:
  145. filtered.append(sg)
  146. if sg.id in group_ids:
  147. filtered.append(sg)
  148. # If a filter was specified, use the filtered list; otherwise, get all
  149. return [OpenStackSecurityGroup(self._provider, sg)
  150. for sg in (filtered
  151. if (group_names or group_ids) else security_groups)]
  152. def delete(self, group_id):
  153. """
  154. Delete an existing SecurityGroup.
  155. :type group_id: str
  156. :param group_id: The security group ID to be deleted.
  157. :rtype: ``bool``
  158. :return: ``True`` if the security group does not exist, ``False``
  159. otherwise. Note that this implies that the group may not have
  160. been deleted by this method but instead has not existed in
  161. the first place.
  162. """
  163. sg = self.get(group_ids=[group_id])
  164. if sg:
  165. sg[0].delete()
  166. return True
  167. class OpenStackImageService(BaseImageService):
  168. def __init__(self, provider):
  169. super(OpenStackImageService, self).__init__(provider)
  170. def get(self, image_id):
  171. """
  172. Returns an Image given its id
  173. """
  174. try:
  175. return OpenStackMachineImage(
  176. self._provider, self._provider.nova.images.get(image_id))
  177. except NovaNotFound:
  178. return None
  179. def find(self, name):
  180. """
  181. Searches for an image by a given list of attributes
  182. """
  183. raise NotImplementedError(
  184. 'find_image not implemented by this provider')
  185. def list(self):
  186. """
  187. List all images.
  188. """
  189. images = self._provider.nova.images.list()
  190. return [OpenStackMachineImage(self._provider, image)
  191. for image in images]
  192. class OpenStackInstanceTypesService(BaseInstanceTypesService):
  193. def __init__(self, provider):
  194. super(OpenStackInstanceTypesService, self).__init__(provider)
  195. def list(self):
  196. return [OpenStackInstanceType(f)
  197. for f in self._provider.nova.flavors.list()]
  198. class OpenStackBlockStoreService(BaseBlockStoreService):
  199. def __init__(self, provider):
  200. super(OpenStackBlockStoreService, self).__init__(provider)
  201. # Initialize provider services
  202. self._volume_svc = OpenStackVolumeService(self._provider)
  203. self._snapshot_svc = OpenStackSnapshotService(self._provider)
  204. @property
  205. def volumes(self):
  206. return self._volume_svc
  207. @property
  208. def snapshots(self):
  209. return self._snapshot_svc
  210. class OpenStackVolumeService(BaseVolumeService):
  211. def __init__(self, provider):
  212. super(OpenStackVolumeService, self).__init__(provider)
  213. def get(self, volume_id):
  214. """
  215. Returns a volume given its id.
  216. """
  217. try:
  218. return OpenStackVolume(
  219. self._provider, self._provider.cinder.volumes.get(volume_id))
  220. except CinderNotFound:
  221. return None
  222. def find(self, name):
  223. """
  224. Searches for a volume by a given list of attributes.
  225. """
  226. raise NotImplementedError(
  227. 'find_volume not implemented by this provider')
  228. def list(self):
  229. """
  230. List all volumes.
  231. """
  232. return [OpenStackVolume(self._provider, vol)
  233. for vol in self._provider.cinder.volumes.list()]
  234. def create(self, name, size, zone, snapshot=None):
  235. """
  236. Creates a new volume.
  237. """
  238. zone_name = zone.name if isinstance(zone, PlacementZone) else zone
  239. snapshot_id = snapshot.id if isinstance(
  240. zone, OpenStackSnapshot) and snapshot else snapshot
  241. os_vol = self._provider.cinder.volumes.create(
  242. size, name=name, availability_zone=zone_name,
  243. snapshot_id=snapshot_id)
  244. return OpenStackVolume(self._provider, os_vol)
  245. class OpenStackSnapshotService(BaseSnapshotService):
  246. def __init__(self, provider):
  247. super(OpenStackSnapshotService, self).__init__(provider)
  248. def get(self, snapshot_id):
  249. """
  250. Returns a snapshot given its id.
  251. """
  252. try:
  253. return OpenStackSnapshot(
  254. self._provider,
  255. self._provider.cinder.volume_snapshots.get(snapshot_id))
  256. except CinderNotFound:
  257. return None
  258. def find(self, name):
  259. """
  260. Searches for a volume by a given list of attributes.
  261. """
  262. raise NotImplementedError(
  263. 'find_volume not implemented by this provider')
  264. def list(self):
  265. """
  266. List all snapshot.
  267. """
  268. return [OpenStackSnapshot(self._provider, snap)
  269. for snap in self._provider.cinder.volume_snapshots.list()]
  270. def create(self, name, volume, description=None):
  271. """
  272. Creates a new snapshot of a given volume.
  273. """
  274. volume_id = volume.id if \
  275. isinstance(volume, OpenStackVolume) else volume
  276. os_snap = self._provider.cinder.volume_snapshots.create(
  277. volume_id, name=name,
  278. description=description)
  279. return OpenStackSnapshot(self._provider, os_snap)
  280. class OpenStackObjectStoreService(BaseObjectStoreService):
  281. def __init__(self, provider):
  282. super(OpenStackObjectStoreService, self).__init__(provider)
  283. def get(self, container_id):
  284. """
  285. Returns a container given its id. Returns None if the container
  286. does not exist.
  287. """
  288. _, container_list = self._provider.swift.get_account(
  289. prefix=container_id)
  290. if container_list:
  291. return OpenStackContainer(self._provider, container_list[0])
  292. else:
  293. return None
  294. def find(self, name):
  295. """
  296. Searches for a container by a given list of attributes
  297. """
  298. raise NotImplementedError(
  299. 'find_container not implemented by this provider')
  300. def list(self):
  301. """
  302. List all containers.
  303. """
  304. _, container_list = self._provider.swift.get_account()
  305. return [
  306. OpenStackContainer(self._provider, c) for c in container_list]
  307. def create(self, name, location=None):
  308. """
  309. Create a new container.
  310. """
  311. self._provider.swift.put_container(name)
  312. return self.get(name)
  313. class OpenStackRegionService(BaseRegionService):
  314. def __init__(self, provider):
  315. super(OpenStackRegionService, self).__init__(provider)
  316. def get(self, region_id):
  317. region = (r for r in self.list() if r.id == region_id)
  318. return next(region, None)
  319. def list(self):
  320. regions = [endpoint.get('region') or endpoint.get('region_id')
  321. for svc in self.provider.keystone.service_catalog.get_data()
  322. for endpoint in svc.get('endpoints', [])]
  323. regions = [region for region in regions if region]
  324. return [OpenStackRegion(self.provider, region)
  325. for region in regions]
  326. class OpenStackComputeService(BaseComputeService):
  327. def __init__(self, provider):
  328. super(OpenStackComputeService, self).__init__(provider)
  329. self._instance_type_svc = OpenStackInstanceTypesService(self._provider)
  330. self._instance_svc = OpenStackInstanceService(self._provider)
  331. self._region_svc = OpenStackRegionService(self.provider)
  332. self._images_svc = OpenStackImageService(self.provider)
  333. @property
  334. def images(self):
  335. return self._images_svc
  336. @property
  337. def instance_types(self):
  338. return self._instance_type_svc
  339. @property
  340. def instances(self):
  341. return self._instance_svc
  342. @property
  343. def regions(self):
  344. return self._region_svc
  345. class OpenStackInstanceService(BaseInstanceService):
  346. def __init__(self, provider):
  347. super(OpenStackInstanceService, self).__init__(provider)
  348. def create(self, name, image, instance_type, zone=None,
  349. keypair=None, security_groups=None, user_data=None,
  350. launch_config=None,
  351. **kwargs):
  352. """
  353. Creates a new virtual machine instance.
  354. """
  355. image_id = image.id if isinstance(image, MachineImage) else image
  356. instance_size = instance_type.id if \
  357. isinstance(instance_type, InstanceType) else \
  358. next(
  359. self.provider.compute.instance_types.find(
  360. name=instance_type)).id
  361. zone_name = zone.name if isinstance(zone, PlacementZone) else zone
  362. keypair_name = keypair.name if \
  363. isinstance(keypair, KeyPair) else keypair
  364. if security_groups:
  365. if isinstance(security_groups, list) and \
  366. isinstance(security_groups[0], SecurityGroup):
  367. security_groups_list = [sg.name for sg in security_groups]
  368. else:
  369. security_groups_list = security_groups
  370. else:
  371. security_groups_list = None
  372. if launch_config:
  373. bdm = self._to_block_device_mapping(launch_config)
  374. nics = self._format_nics(launch_config)
  375. else:
  376. bdm = nics = None
  377. os_instance = self._provider.nova.servers.create(
  378. name,
  379. image_id,
  380. instance_size,
  381. min_count=1,
  382. max_count=1,
  383. availability_zone=zone_name,
  384. key_name=keypair_name,
  385. security_groups=security_groups_list,
  386. userdata=user_data,
  387. block_device_mapping_v2=bdm,
  388. nics=nics)
  389. return OpenStackInstance(self._provider, os_instance)
  390. def _to_block_device_mapping(self, launch_config):
  391. """
  392. Extracts block device mapping information
  393. from a launch config and constructs a BlockDeviceMappingV2
  394. object.
  395. """
  396. bdm = []
  397. for device in launch_config.block_devices:
  398. bdm_dict = {}
  399. # Let openstack auto assign device name
  400. bdm_dict['device_name'] = None
  401. if device.is_volume:
  402. bdm_dict['destination_type'] = 'volume'
  403. if device.is_root:
  404. bdm_dict['device_name'] = '/dev/sda'
  405. if isinstance(device.source, Snapshot):
  406. bdm_dict['source_type'] = 'snapshot'
  407. bdm_dict['uuid'] = device.source.id
  408. elif isinstance(device.source, Volume):
  409. bdm_dict['source_type'] = 'volume'
  410. bdm_dict['uuid'] = device.source.id
  411. elif isinstance(device.source, MachineImage):
  412. bdm_dict['source_type'] = 'image'
  413. bdm_dict['uuid'] = device.source.id
  414. else:
  415. bdm_dict['source_type'] = 'blank'
  416. if device.delete_on_terminate is not None:
  417. bdm_dict[
  418. 'delete_on_termination'] = device.delete_on_terminate
  419. if device.size:
  420. bdm_dict['volume_size'] = device.size
  421. else:
  422. bdm_dict['destination_type'] = 'local'
  423. bdm_dict['source_type'] = 'blank'
  424. bdm_dict['delete_on_termination'] = True
  425. bdm.append(bdm_dict)
  426. return bdm
  427. def _format_nics(self, launch_config):
  428. """
  429. Format network IDs for the API call.
  430. """
  431. nics = []
  432. for net_id in launch_config.net_ids:
  433. nics.append({'net-id': net_id})
  434. return nics
  435. def create_launch_config(self):
  436. return BaseLaunchConfig(self.provider)
  437. def find(self, name):
  438. """
  439. Searches for an instance by a given list of attributes.
  440. """
  441. raise NotImplementedError(
  442. 'find_instance not implemented by this provider')
  443. def list(self):
  444. """
  445. List all instances.
  446. """
  447. instances = self._provider.nova.servers.list()
  448. return [OpenStackInstance(self._provider, instance)
  449. for instance in instances]
  450. def get(self, instance_id):
  451. """
  452. Returns an instance given its id.
  453. """
  454. try:
  455. os_instance = self._provider.nova.servers.get(instance_id)
  456. return OpenStackInstance(self._provider, os_instance)
  457. except NovaNotFound:
  458. return None