services.py 20 KB

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