services.py 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033
  1. """
  2. Services implemented by the OpenStack provider.
  3. """
  4. import logging
  5. from cinderclient.exceptions import NotFound as CinderNotFound
  6. from neutronclient.common.exceptions import NeutronClientException
  7. from neutronclient.common.exceptions import PortNotFoundClient
  8. from novaclient.exceptions import NotFound as NovaNotFound
  9. from openstack.exceptions import NotFoundException
  10. from openstack.exceptions import ResourceNotFound
  11. from swiftclient import ClientException as SwiftClientException
  12. import cloudbridge.cloud.base.helpers as cb_helpers
  13. from cloudbridge.cloud.base.middleware import implement
  14. from cloudbridge.cloud.base.resources import BaseLaunchConfig
  15. from cloudbridge.cloud.base.resources import ClientPagedResultList
  16. from cloudbridge.cloud.base.services import BaseBucketObjectService
  17. from cloudbridge.cloud.base.services import BaseBucketService
  18. from cloudbridge.cloud.base.services import BaseComputeService
  19. from cloudbridge.cloud.base.services import BaseImageService
  20. from cloudbridge.cloud.base.services import BaseInstanceService
  21. from cloudbridge.cloud.base.services import BaseKeyPairService
  22. from cloudbridge.cloud.base.services import BaseNetworkService
  23. from cloudbridge.cloud.base.services import BaseNetworkingService
  24. from cloudbridge.cloud.base.services import BaseRegionService
  25. from cloudbridge.cloud.base.services import BaseRouterService
  26. from cloudbridge.cloud.base.services import BaseSecurityService
  27. from cloudbridge.cloud.base.services import BaseSnapshotService
  28. from cloudbridge.cloud.base.services import BaseStorageService
  29. from cloudbridge.cloud.base.services import BaseSubnetService
  30. from cloudbridge.cloud.base.services import BaseVMFirewallService
  31. from cloudbridge.cloud.base.services import BaseVMTypeService
  32. from cloudbridge.cloud.base.services import BaseVolumeService
  33. from cloudbridge.cloud.interfaces.exceptions \
  34. import DuplicateResourceException
  35. from cloudbridge.cloud.interfaces.resources import KeyPair
  36. from cloudbridge.cloud.interfaces.resources import MachineImage
  37. from cloudbridge.cloud.interfaces.resources import Network
  38. from cloudbridge.cloud.interfaces.resources import PlacementZone
  39. from cloudbridge.cloud.interfaces.resources import Snapshot
  40. from cloudbridge.cloud.interfaces.resources import Subnet
  41. from cloudbridge.cloud.interfaces.resources import VMFirewall
  42. from cloudbridge.cloud.interfaces.resources import VMType
  43. from cloudbridge.cloud.interfaces.resources import Volume
  44. from cloudbridge.cloud.providers.openstack import helpers as oshelpers
  45. from .resources import OpenStackBucket
  46. from .resources import OpenStackBucketObject
  47. from .resources import OpenStackInstance
  48. from .resources import OpenStackKeyPair
  49. from .resources import OpenStackMachineImage
  50. from .resources import OpenStackNetwork
  51. from .resources import OpenStackRegion
  52. from .resources import OpenStackRouter
  53. from .resources import OpenStackSnapshot
  54. from .resources import OpenStackSubnet
  55. from .resources import OpenStackVMFirewall
  56. from .resources import OpenStackVMType
  57. from .resources import OpenStackVolume
  58. log = logging.getLogger(__name__)
  59. class OpenStackSecurityService(BaseSecurityService):
  60. def __init__(self, provider):
  61. super(OpenStackSecurityService, self).__init__(provider)
  62. # Initialize provider services
  63. self._key_pairs = OpenStackKeyPairService(provider)
  64. self._vm_firewalls = OpenStackVMFirewallService(provider)
  65. @property
  66. def key_pairs(self):
  67. """
  68. Provides access to key pairs for this provider.
  69. :rtype: ``object`` of :class:`.KeyPairService`
  70. :return: a KeyPairService object
  71. """
  72. return self._key_pairs
  73. @property
  74. def vm_firewalls(self):
  75. """
  76. Provides access to VM firewalls for this provider.
  77. :rtype: ``object`` of :class:`.VMFirewallService`
  78. :return: a VMFirewallService object
  79. """
  80. return self._vm_firewalls
  81. def get_or_create_ec2_credentials(self):
  82. """
  83. A provider specific method than returns the ec2 credentials for the
  84. current user, or creates a new pair if one doesn't exist.
  85. """
  86. keystone = self.provider.keystone
  87. if hasattr(keystone, 'ec2'):
  88. user_id = keystone.session.get_user_id()
  89. user_creds = [cred for cred in keystone.ec2.list(user_id) if
  90. cred.tenant_id == keystone.session.get_project_id()]
  91. if user_creds:
  92. return user_creds[0]
  93. else:
  94. return keystone.ec2.create(
  95. user_id, keystone.session.get_project_id())
  96. return None
  97. def get_ec2_endpoints(self):
  98. """
  99. A provider specific method than returns the ec2 endpoints if
  100. available.
  101. """
  102. keystone = self.provider.keystone
  103. ec2_url = keystone.session.get_endpoint(service_type='ec2')
  104. s3_url = keystone.session.get_endpoint(service_type='s3')
  105. return {'ec2_endpoint': ec2_url,
  106. 's3_endpoint': s3_url}
  107. class OpenStackKeyPairService(BaseKeyPairService):
  108. def __init__(self, provider):
  109. super(OpenStackKeyPairService, self).__init__(provider)
  110. @implement(event_pattern="provider.security.key_pairs.get",
  111. priority=BaseKeyPairService.STANDARD_EVENT_PRIORITY)
  112. def _get(self, key_pair_id):
  113. """
  114. Returns a KeyPair given its id.
  115. """
  116. log.debug("Returning KeyPair with the id %s", key_pair_id)
  117. try:
  118. return OpenStackKeyPair(
  119. self.provider, self.provider.nova.keypairs.get(key_pair_id))
  120. except NovaNotFound:
  121. log.debug("KeyPair %s was not found.", key_pair_id)
  122. return None
  123. @implement(event_pattern="provider.security.key_pairs.list",
  124. priority=BaseKeyPairService.STANDARD_EVENT_PRIORITY)
  125. def _list(self, limit=None, marker=None):
  126. """
  127. List all key pairs associated with this account.
  128. :rtype: ``list`` of :class:`.KeyPair`
  129. :return: list of KeyPair objects
  130. """
  131. keypairs = self.provider.nova.keypairs.list()
  132. results = [OpenStackKeyPair(self.provider, kp)
  133. for kp in keypairs]
  134. log.debug("Listing all key pairs associated with OpenStack "
  135. "Account: %s", results)
  136. return ClientPagedResultList(self.provider, results,
  137. limit=limit, marker=marker)
  138. @implement(event_pattern="provider.security.key_pairs.find",
  139. priority=BaseKeyPairService.STANDARD_EVENT_PRIORITY)
  140. def _find(self, **kwargs):
  141. name = kwargs.pop('name', None)
  142. # All kwargs should have been popped at this time.
  143. if len(kwargs) > 0:
  144. raise TypeError("Unrecognised parameters for search: %s."
  145. " Supported attributes: %s" % (kwargs, 'name'))
  146. keypairs = self.provider.nova.keypairs.findall(name=name)
  147. results = [OpenStackKeyPair(self.provider, kp)
  148. for kp in keypairs]
  149. log.debug("Searching for %s in: %s", name, keypairs)
  150. return ClientPagedResultList(self.provider, results)
  151. @implement(event_pattern="provider.security.key_pairs.create",
  152. priority=BaseKeyPairService.STANDARD_EVENT_PRIORITY)
  153. def _create(self, name, public_key_material=None):
  154. existing_kp = self.find(name=name)
  155. if existing_kp:
  156. raise DuplicateResourceException(
  157. 'Keypair already exists with name {0}'.format(name))
  158. private_key = None
  159. if not public_key_material:
  160. public_key_material, private_key = cb_helpers.generate_key_pair()
  161. kp = self.provider.nova.keypairs.create(name,
  162. public_key=public_key_material)
  163. cb_kp = OpenStackKeyPair(self.provider, kp)
  164. cb_kp.material = private_key
  165. return cb_kp
  166. @implement(event_pattern="provider.security.key_pairs.delete",
  167. priority=BaseKeyPairService.STANDARD_EVENT_PRIORITY)
  168. def _delete(self, key_pair_id):
  169. os_kp = self.provider.nova.keypairs.get(key_pair_id)
  170. os_kp.delete()
  171. class OpenStackVMFirewallService(BaseVMFirewallService):
  172. def __init__(self, provider):
  173. super(OpenStackVMFirewallService, self).__init__(provider)
  174. @implement(event_pattern="provider.security.vm_firewalls.get",
  175. priority=BaseVMFirewallService.STANDARD_EVENT_PRIORITY)
  176. def _get(self, vm_firewall_id):
  177. try:
  178. return OpenStackVMFirewall(
  179. self.provider,
  180. self.provider.os_conn.network
  181. .get_security_group(vm_firewall_id))
  182. except (ResourceNotFound, NotFoundException):
  183. log.debug("Firewall %s not found.", vm_firewall_id)
  184. return None
  185. @implement(event_pattern="provider.security.vm_firewalls.list",
  186. priority=BaseVMFirewallService.STANDARD_EVENT_PRIORITY)
  187. def _list(self, limit=None, marker=None):
  188. firewalls = [
  189. OpenStackVMFirewall(self.provider, fw)
  190. for fw in self.provider.os_conn.network.security_groups()]
  191. return ClientPagedResultList(self.provider, firewalls,
  192. limit=limit, marker=marker)
  193. @cb_helpers.deprecated_alias(network_id='network')
  194. @implement(event_pattern="provider.security.vm_firewalls.create",
  195. priority=BaseVMFirewallService.STANDARD_EVENT_PRIORITY)
  196. def _create(self, label, network, description=None):
  197. net = network.id if isinstance(network, Network) else network
  198. # We generally simulate a network being associated with a firewall
  199. # by storing the supplied value in the firewall description field that
  200. # is not modifiable after creation; however, because of some networking
  201. # specificity in Nectar, we must also allow an empty network id value.
  202. if not net:
  203. net = ""
  204. if not description:
  205. description = ""
  206. description += " [{}{}]".format(OpenStackVMFirewall._network_id_tag,
  207. net)
  208. sg = self.provider.os_conn.network.create_security_group(
  209. name=label, description=description)
  210. if sg:
  211. return OpenStackVMFirewall(self.provider, sg)
  212. return None
  213. @implement(event_pattern="provider.security.vm_firewalls.delete",
  214. priority=BaseVMFirewallService.STANDARD_EVENT_PRIORITY)
  215. def _delete(self, vm_firewall_id):
  216. try:
  217. os_fw = self.provider.os_conn.network.get_security_group(
  218. vm_firewall_id)
  219. os_fw.delete(self.provider.os_conn.session)
  220. return True
  221. except (ResourceNotFound, NotFoundException):
  222. log.debug("Firewall %s not found.", vm_firewall_id)
  223. return True
  224. class OpenStackStorageService(BaseStorageService):
  225. def __init__(self, provider):
  226. super(OpenStackStorageService, self).__init__(provider)
  227. # Initialize provider services
  228. self._volume_svc = OpenStackVolumeService(self.provider)
  229. self._snapshot_svc = OpenStackSnapshotService(self.provider)
  230. self._bucket_svc = OpenStackBucketService(self.provider)
  231. self._bucket_obj_svc = OpenStackBucketObjectService(self.provider)
  232. @property
  233. def volumes(self):
  234. return self._volume_svc
  235. @property
  236. def snapshots(self):
  237. return self._snapshot_svc
  238. @property
  239. def buckets(self):
  240. return self._bucket_svc
  241. @property
  242. def bucket_objects(self):
  243. return self._bucket_obj_svc
  244. class OpenStackVolumeService(BaseVolumeService):
  245. def __init__(self, provider):
  246. super(OpenStackVolumeService, self).__init__(provider)
  247. @implement(event_pattern="provider.storage.volumes.get",
  248. priority=BaseVolumeService.STANDARD_EVENT_PRIORITY)
  249. def _get(self, volume_id):
  250. try:
  251. return OpenStackVolume(
  252. self.provider, self.provider.cinder.volumes.get(volume_id))
  253. except CinderNotFound:
  254. log.debug("Volume %s was not found.", volume_id)
  255. return None
  256. @implement(event_pattern="provider.storage.volumes.find",
  257. priority=BaseVolumeService.STANDARD_EVENT_PRIORITY)
  258. def _find(self, **kwargs):
  259. label = kwargs.pop('label', None)
  260. # All kwargs should have been popped at this time.
  261. if len(kwargs) > 0:
  262. raise TypeError("Unrecognised parameters for search: %s."
  263. " Supported attributes: %s" % (kwargs, 'label'))
  264. log.debug("Searching for an OpenStack Volume with the label %s", label)
  265. search_opts = {'name': label}
  266. cb_vols = [
  267. OpenStackVolume(self.provider, vol)
  268. for vol in self.provider.cinder.volumes.list(
  269. search_opts=search_opts,
  270. limit=oshelpers.os_result_limit(self.provider),
  271. marker=None)]
  272. return oshelpers.to_server_paged_list(self.provider, cb_vols)
  273. @implement(event_pattern="provider.storage.volumes.list",
  274. priority=BaseVolumeService.STANDARD_EVENT_PRIORITY)
  275. def _list(self, limit=None, marker=None):
  276. cb_vols = [
  277. OpenStackVolume(self.provider, vol)
  278. for vol in self.provider.cinder.volumes.list(
  279. limit=oshelpers.os_result_limit(self.provider, limit),
  280. marker=marker)]
  281. return oshelpers.to_server_paged_list(self.provider, cb_vols, limit)
  282. @implement(event_pattern="provider.storage.volumes.create",
  283. priority=BaseVolumeService.STANDARD_EVENT_PRIORITY)
  284. def _create(self, label, size, zone, snapshot=None, description=None):
  285. zone_id = zone.id if isinstance(zone, PlacementZone) else zone
  286. snapshot_id = snapshot.id if isinstance(
  287. snapshot, OpenStackSnapshot) and snapshot else snapshot
  288. os_vol = self.provider.cinder.volumes.create(
  289. size, name=label, description=description,
  290. availability_zone=zone_id, snapshot_id=snapshot_id)
  291. return OpenStackVolume(self.provider, os_vol)
  292. @implement(event_pattern="provider.storage.volumes.delete",
  293. priority=BaseVolumeService.STANDARD_EVENT_PRIORITY)
  294. def _delete(self, volume_id):
  295. os_vol = self.provider.cinder.volumes.get(volume_id)
  296. os_vol.delete()
  297. class OpenStackSnapshotService(BaseSnapshotService):
  298. def __init__(self, provider):
  299. super(OpenStackSnapshotService, self).__init__(provider)
  300. @implement(event_pattern="provider.storage.snapshots.get",
  301. priority=BaseSnapshotService.STANDARD_EVENT_PRIORITY)
  302. def _get(self, snapshot_id):
  303. try:
  304. return OpenStackSnapshot(
  305. self.provider,
  306. self.provider.cinder.volume_snapshots.get(snapshot_id))
  307. except CinderNotFound:
  308. log.debug("Snapshot %s was not found.", snapshot_id)
  309. return None
  310. @implement(event_pattern="provider.storage.snapshots.find",
  311. priority=BaseSnapshotService.STANDARD_EVENT_PRIORITY)
  312. def _find(self, **kwargs):
  313. label = kwargs.pop('label', None)
  314. # All kwargs should have been popped at this time.
  315. if len(kwargs) > 0:
  316. raise TypeError("Unrecognised parameters for search: %s."
  317. " Supported attributes: %s" % (kwargs, 'label'))
  318. search_opts = {'name': label, # TODO: Cinder is ignoring name
  319. 'limit': oshelpers.os_result_limit(self.provider),
  320. 'marker': None}
  321. log.debug("Searching for an OpenStack snapshot with the following "
  322. "params: %s", search_opts)
  323. cb_snaps = [
  324. OpenStackSnapshot(self.provider, snap) for
  325. snap in self.provider.cinder.volume_snapshots.list(search_opts)
  326. if snap.name == label]
  327. return oshelpers.to_server_paged_list(self.provider, cb_snaps)
  328. @implement(event_pattern="provider.storage.snapshots.list",
  329. priority=BaseSnapshotService.STANDARD_EVENT_PRIORITY)
  330. def _list(self, limit=None, marker=None):
  331. cb_snaps = [
  332. OpenStackSnapshot(self.provider, snap) for
  333. snap in self.provider.cinder.volume_snapshots.list(
  334. search_opts={'limit': oshelpers.os_result_limit(self.provider,
  335. limit),
  336. 'marker': marker})]
  337. return oshelpers.to_server_paged_list(self.provider, cb_snaps, limit)
  338. @implement(event_pattern="provider.storage.snapshots.create",
  339. priority=BaseSnapshotService.STANDARD_EVENT_PRIORITY)
  340. def _create(self, label, volume, description=None):
  341. volume_id = (volume.id if isinstance(volume, OpenStackVolume)
  342. else volume)
  343. os_snap = self.provider.cinder.volume_snapshots.create(
  344. volume_id, name=label,
  345. description=description)
  346. return OpenStackSnapshot(self.provider, os_snap)
  347. @implement(event_pattern="provider.storage.snapshots.delete",
  348. priority=BaseSnapshotService.STANDARD_EVENT_PRIORITY)
  349. def _delete(self, snapshot_id):
  350. os_snap = self.provider.cinder.volume_snapshots.get(snapshot_id)
  351. os_snap.delete()
  352. class OpenStackBucketService(BaseBucketService):
  353. def __init__(self, provider):
  354. super(OpenStackBucketService, self).__init__(provider)
  355. @implement(event_pattern="provider.storage.buckets.get",
  356. priority=BaseBucketService.STANDARD_EVENT_PRIORITY)
  357. def _get(self, bucket_id):
  358. """
  359. Returns a bucket given its ID. Returns ``None`` if the bucket
  360. does not exist.
  361. """
  362. _, container_list = self.provider.swift.get_account(
  363. prefix=bucket_id)
  364. if container_list:
  365. return OpenStackBucket(self.provider,
  366. next((c for c in container_list
  367. if c['name'] == bucket_id), None))
  368. else:
  369. log.debug("Bucket %s was not found.", bucket_id)
  370. return None
  371. @implement(event_pattern="provider.storage.buckets.find",
  372. priority=BaseBucketService.STANDARD_EVENT_PRIORITY)
  373. def _find(self, **kwargs):
  374. name = kwargs.pop('name', None)
  375. # All kwargs should have been popped at this time.
  376. if len(kwargs) > 0:
  377. raise TypeError("Unrecognised parameters for search: %s."
  378. " Supported attributes: %s" % (kwargs, 'name'))
  379. _, container_list = self.provider.swift.get_account()
  380. cb_buckets = [OpenStackBucket(self.provider, c)
  381. for c in container_list
  382. if name in c.get("name")]
  383. return oshelpers.to_server_paged_list(self.provider, cb_buckets)
  384. @implement(event_pattern="provider.storage.buckets.list",
  385. priority=BaseBucketService.STANDARD_EVENT_PRIORITY)
  386. def _list(self, limit, marker):
  387. _, container_list = self.provider.swift.get_account(
  388. limit=oshelpers.os_result_limit(self.provider, limit),
  389. marker=marker)
  390. cb_buckets = [OpenStackBucket(self.provider, c)
  391. for c in container_list]
  392. return oshelpers.to_server_paged_list(self.provider, cb_buckets, limit)
  393. @implement(event_pattern="provider.storage.buckets.create",
  394. priority=BaseBucketService.STANDARD_EVENT_PRIORITY)
  395. def _create(self, name, location):
  396. OpenStackBucket.assert_valid_resource_name(name)
  397. location = location or self.provider.region_name
  398. try:
  399. self.provider.swift.head_container(name)
  400. raise DuplicateResourceException(
  401. 'Bucket already exists with name {0}'.format(name))
  402. except SwiftClientException:
  403. self.provider.swift.put_container(name)
  404. return self.get(name)
  405. @implement(event_pattern="provider.storage.buckets.delete",
  406. priority=BaseBucketService.STANDARD_EVENT_PRIORITY)
  407. def _delete(self, bucket_id):
  408. self.provider.swift.delete_container(bucket_id)
  409. class OpenStackBucketObjectService(BaseBucketObjectService):
  410. def __init__(self, provider):
  411. super(OpenStackBucketObjectService, self).__init__(provider)
  412. def get(self, bucket, name):
  413. """
  414. Retrieve a given object from this bucket.
  415. """
  416. # Swift always returns a reference for the container first,
  417. # followed by a list containing references to objects.
  418. _, object_list = self.provider.swift.get_container(
  419. bucket.name, prefix=name)
  420. # Loop through list of objects looking for an exact name vs. a prefix
  421. for obj in object_list:
  422. if obj.get('name') == name:
  423. return OpenStackBucketObject(self.provider,
  424. bucket,
  425. obj)
  426. return None
  427. def list(self, bucket, limit=None, marker=None, prefix=None):
  428. """
  429. List all objects within this bucket.
  430. :rtype: BucketObject
  431. :return: List of all available BucketObjects within this bucket.
  432. """
  433. _, object_list = self.provider.swift.get_container(
  434. bucket.name,
  435. limit=oshelpers.os_result_limit(self.provider, limit),
  436. marker=marker, prefix=prefix)
  437. cb_objects = [OpenStackBucketObject(
  438. self.provider, bucket, obj) for obj in object_list]
  439. return oshelpers.to_server_paged_list(
  440. self.provider,
  441. cb_objects,
  442. limit)
  443. def find(self, bucket, **kwargs):
  444. _, obj_list = self.provider.swift.get_container(bucket.name)
  445. cb_objs = [OpenStackBucketObject(self.provider, bucket, obj)
  446. for obj in obj_list]
  447. filters = ['name']
  448. matches = cb_helpers.generic_find(filters, kwargs, cb_objs)
  449. return ClientPagedResultList(self.provider, list(matches))
  450. def create(self, bucket, object_name):
  451. self.provider.swift.put_object(bucket.name, object_name, None)
  452. return self.get(bucket, object_name)
  453. class OpenStackComputeService(BaseComputeService):
  454. def __init__(self, provider):
  455. super(OpenStackComputeService, self).__init__(provider)
  456. self._vm_type_svc = OpenStackVMTypeService(self.provider)
  457. self._instance_svc = OpenStackInstanceService(self.provider)
  458. self._region_svc = OpenStackRegionService(self.provider)
  459. self._images_svc = OpenStackImageService(self.provider)
  460. @property
  461. def images(self):
  462. return self._images_svc
  463. @property
  464. def vm_types(self):
  465. return self._vm_type_svc
  466. @property
  467. def instances(self):
  468. return self._instance_svc
  469. @property
  470. def regions(self):
  471. return self._region_svc
  472. class OpenStackImageService(BaseImageService):
  473. def __init__(self, provider):
  474. super(OpenStackImageService, self).__init__(provider)
  475. def get(self, image_id):
  476. """
  477. Returns an Image given its id
  478. """
  479. log.debug("Getting OpenStack Image with the id: %s", image_id)
  480. try:
  481. return OpenStackMachineImage(
  482. self.provider, self.provider.os_conn.image.get_image(image_id))
  483. except (NotFoundException, ResourceNotFound):
  484. log.debug("Image %s not found", image_id)
  485. return None
  486. def find(self, **kwargs):
  487. filters = ['label']
  488. obj_list = self
  489. return cb_helpers.generic_find(filters, kwargs, obj_list)
  490. def list(self, filter_by_owner=True, limit=None, marker=None):
  491. """
  492. List all images.
  493. """
  494. project_id = None
  495. if filter_by_owner:
  496. project_id = self.provider.os_conn.session.get_project_id()
  497. os_images = self.provider.os_conn.image.images(
  498. owner=project_id,
  499. limit=oshelpers.os_result_limit(self.provider, limit),
  500. marker=marker)
  501. cb_images = [
  502. OpenStackMachineImage(self.provider, img)
  503. for img in os_images]
  504. return oshelpers.to_server_paged_list(self.provider, cb_images, limit)
  505. class OpenStackInstanceService(BaseInstanceService):
  506. def __init__(self, provider):
  507. super(OpenStackInstanceService, self).__init__(provider)
  508. def create(self, label, image, vm_type, subnet, zone,
  509. key_pair=None, vm_firewalls=None, user_data=None,
  510. launch_config=None, **kwargs):
  511. """Create a new virtual machine instance."""
  512. OpenStackInstance.assert_valid_resource_label(label)
  513. image_id = image.id if isinstance(image, MachineImage) else image
  514. vm_size = vm_type.id if \
  515. isinstance(vm_type, VMType) else \
  516. self.provider.compute.vm_types.find(
  517. name=vm_type)[0].id
  518. if isinstance(subnet, Subnet):
  519. subnet_id = subnet.id
  520. net_id = subnet.network_id
  521. else:
  522. subnet_id = subnet
  523. net_id = (self.provider.networking.subnets
  524. .get(subnet_id).network_id
  525. if subnet_id else None)
  526. zone_id = zone.id if isinstance(zone, PlacementZone) else zone
  527. key_pair_name = key_pair.name if \
  528. isinstance(key_pair, KeyPair) else key_pair
  529. bdm = None
  530. if launch_config:
  531. bdm = self._to_block_device_mapping(launch_config)
  532. # Security groups must be passed in as a list of IDs and attached to a
  533. # port if a port is being created. Otherwise, the security groups must
  534. # be passed in as a list of names to the servers.create() call.
  535. # OpenStack will respect the port's security groups first and then
  536. # fall-back to the named security groups.
  537. sg_name_list = []
  538. nics = None
  539. if subnet_id:
  540. log.debug("Creating network port for %s in subnet: %s",
  541. label, subnet_id)
  542. sg_list = []
  543. if vm_firewalls:
  544. if isinstance(vm_firewalls, list) and \
  545. isinstance(vm_firewalls[0], VMFirewall):
  546. sg_list = vm_firewalls
  547. else:
  548. sg_list = (self.provider.security.vm_firewalls
  549. .find(label=sg) for sg in vm_firewalls)
  550. sg_list = (sg[0] for sg in sg_list if sg)
  551. sg_id_list = [sg.id for sg in sg_list]
  552. port_def = {
  553. "port": {
  554. "admin_state_up": True,
  555. "name": OpenStackInstance._generate_name_from_label(
  556. label, 'cb-port'),
  557. "network_id": net_id,
  558. "fixed_ips": [{"subnet_id": subnet_id}],
  559. "security_groups": sg_id_list
  560. }
  561. }
  562. port_id = self.provider.neutron.create_port(port_def)['port']['id']
  563. nics = [{'net-id': net_id, 'port-id': port_id}]
  564. else:
  565. if vm_firewalls:
  566. if isinstance(vm_firewalls, list) and \
  567. isinstance(vm_firewalls[0], VMFirewall):
  568. sg_name_list = [sg.name for sg in vm_firewalls]
  569. else:
  570. sg_list = (self.provider.security.vm_firewalls.get(sg)
  571. for sg in vm_firewalls)
  572. sg_name_list = (sg[0].name for sg in sg_list if sg)
  573. log.debug("Launching in subnet %s", subnet_id)
  574. os_instance = self.provider.nova.servers.create(
  575. label,
  576. None if self._has_root_device(launch_config) else image_id,
  577. vm_size,
  578. min_count=1,
  579. max_count=1,
  580. availability_zone=zone_id,
  581. key_name=key_pair_name,
  582. security_groups=sg_name_list,
  583. userdata=str(user_data) or None,
  584. block_device_mapping_v2=bdm,
  585. nics=nics)
  586. return OpenStackInstance(self.provider, os_instance)
  587. def _to_block_device_mapping(self, launch_config):
  588. """
  589. Extracts block device mapping information
  590. from a launch config and constructs a BlockDeviceMappingV2
  591. object.
  592. """
  593. bdm = []
  594. for device in launch_config.block_devices:
  595. bdm_dict = dict()
  596. if device.is_volume:
  597. bdm_dict['destination_type'] = 'volume'
  598. if device.is_root:
  599. bdm_dict['device_name'] = '/dev/sda'
  600. bdm_dict['boot_index'] = 0
  601. if isinstance(device.source, Snapshot):
  602. bdm_dict['source_type'] = 'snapshot'
  603. bdm_dict['uuid'] = device.source.id
  604. elif isinstance(device.source, Volume):
  605. bdm_dict['source_type'] = 'volume'
  606. bdm_dict['uuid'] = device.source.id
  607. elif isinstance(device.source, MachineImage):
  608. bdm_dict['source_type'] = 'image'
  609. bdm_dict['uuid'] = device.source.id
  610. else:
  611. bdm_dict['source_type'] = 'blank'
  612. if device.delete_on_terminate is not None:
  613. bdm_dict[
  614. 'delete_on_termination'] = device.delete_on_terminate
  615. if device.size:
  616. bdm_dict['volume_size'] = device.size
  617. else:
  618. bdm_dict['destination_type'] = 'local'
  619. bdm_dict['source_type'] = 'blank'
  620. bdm_dict['delete_on_termination'] = True
  621. bdm.append(bdm_dict)
  622. return bdm
  623. def _has_root_device(self, launch_config):
  624. if not launch_config:
  625. return False
  626. for device in launch_config.block_devices:
  627. if device.is_root:
  628. return True
  629. return False
  630. def create_launch_config(self):
  631. return BaseLaunchConfig(self.provider)
  632. def find(self, **kwargs):
  633. label = kwargs.pop('label', None)
  634. # All kwargs should have been popped at this time.
  635. if len(kwargs) > 0:
  636. raise TypeError("Unrecognised parameters for search: %s."
  637. " Supported attributes: %s" % (kwargs, 'label'))
  638. search_opts = {'name': label}
  639. cb_insts = [
  640. OpenStackInstance(self.provider, inst)
  641. for inst in self.provider.nova.servers.list(
  642. search_opts=search_opts,
  643. limit=oshelpers.os_result_limit(self.provider),
  644. marker=None)]
  645. return oshelpers.to_server_paged_list(self.provider, cb_insts)
  646. def list(self, limit=None, marker=None):
  647. """
  648. List all instances.
  649. """
  650. cb_insts = [
  651. OpenStackInstance(self.provider, inst)
  652. for inst in self.provider.nova.servers.list(
  653. limit=oshelpers.os_result_limit(self.provider, limit),
  654. marker=marker)]
  655. return oshelpers.to_server_paged_list(self.provider, cb_insts, limit)
  656. def get(self, instance_id):
  657. """
  658. Returns an instance given its id.
  659. """
  660. try:
  661. os_instance = self.provider.nova.servers.get(instance_id)
  662. return OpenStackInstance(self.provider, os_instance)
  663. except NovaNotFound:
  664. log.debug("Instance %s was not found.", instance_id)
  665. return None
  666. class OpenStackVMTypeService(BaseVMTypeService):
  667. def __init__(self, provider):
  668. super(OpenStackVMTypeService, self).__init__(provider)
  669. def list(self, limit=None, marker=None):
  670. cb_itypes = [
  671. OpenStackVMType(self.provider, obj)
  672. for obj in self.provider.nova.flavors.list(
  673. limit=oshelpers.os_result_limit(self.provider, limit),
  674. marker=marker)]
  675. return oshelpers.to_server_paged_list(self.provider, cb_itypes, limit)
  676. class OpenStackRegionService(BaseRegionService):
  677. def __init__(self, provider):
  678. super(OpenStackRegionService, self).__init__(provider)
  679. def get(self, region_id):
  680. log.debug("Getting OpenStack Region with the id: %s", region_id)
  681. region = (r for r in self if r.id == region_id)
  682. return next(region, None)
  683. def list(self, limit=None, marker=None):
  684. # pylint:disable=protected-access
  685. if self.provider._keystone_version == 3:
  686. os_regions = [OpenStackRegion(self.provider, region)
  687. for region in self.provider.keystone.regions.list()]
  688. return ClientPagedResultList(self.provider, os_regions,
  689. limit=limit, marker=marker)
  690. else:
  691. # Keystone v3 onwards supports directly listing regions
  692. # but for v2, this convoluted method is necessary.
  693. regions = (
  694. endpoint.get('region') or endpoint.get('region_id')
  695. for svc in self.provider.keystone.service_catalog.get_data()
  696. for endpoint in svc.get('endpoints', [])
  697. )
  698. regions = set(region for region in regions if region)
  699. os_regions = [OpenStackRegion(self.provider, region)
  700. for region in regions]
  701. return ClientPagedResultList(self.provider, os_regions,
  702. limit=limit, marker=marker)
  703. @property
  704. def current(self):
  705. nova_region = self.provider.nova.client.region_name
  706. return self.get(nova_region) if nova_region else None
  707. class OpenStackNetworkingService(BaseNetworkingService):
  708. def __init__(self, provider):
  709. super(OpenStackNetworkingService, self).__init__(provider)
  710. self._network_service = OpenStackNetworkService(self.provider)
  711. self._subnet_service = OpenStackSubnetService(self.provider)
  712. self._router_service = OpenStackRouterService(self.provider)
  713. @property
  714. def networks(self):
  715. return self._network_service
  716. @property
  717. def subnets(self):
  718. return self._subnet_service
  719. @property
  720. def routers(self):
  721. return self._router_service
  722. class OpenStackNetworkService(BaseNetworkService):
  723. def __init__(self, provider):
  724. super(OpenStackNetworkService, self).__init__(provider)
  725. @implement(event_pattern="provider.networking.networks.get",
  726. priority=BaseNetworkService.STANDARD_EVENT_PRIORITY)
  727. def _get(self, network_id):
  728. network = (n for n in self if n.id == network_id)
  729. return next(network, None)
  730. @implement(event_pattern="provider.networking.networks.list",
  731. priority=BaseNetworkService.STANDARD_EVENT_PRIORITY)
  732. def _list(self, limit=None, marker=None):
  733. networks = [OpenStackNetwork(self.provider, network)
  734. for network in self.provider.neutron.list_networks()
  735. .get('networks') if network]
  736. return ClientPagedResultList(self.provider, networks,
  737. limit=limit, marker=marker)
  738. @implement(event_pattern="provider.networking.networks.find",
  739. priority=BaseNetworkService.STANDARD_EVENT_PRIORITY)
  740. def _find(self, **kwargs):
  741. label = kwargs.pop('label', None)
  742. # All kwargs should have been popped at this time.
  743. if len(kwargs) > 0:
  744. raise TypeError("Unrecognised parameters for search: %s."
  745. " Supported attributes: %s" % (kwargs, 'label'))
  746. log.debug("Searching for OpenStack Network with label: %s", label)
  747. networks = [OpenStackNetwork(self.provider, network)
  748. for network in self.provider.neutron.list_networks(
  749. name=label)
  750. .get('networks') if network]
  751. return ClientPagedResultList(self.provider, networks)
  752. @implement(event_pattern="provider.networking.networks.create",
  753. priority=BaseNetworkService.STANDARD_EVENT_PRIORITY)
  754. def _create(self, label, cidr_block):
  755. OpenStackNetwork.assert_valid_resource_label(label)
  756. net_info = {'name': label or ""}
  757. network = self.provider.neutron.create_network({'network': net_info})
  758. cb_net = OpenStackNetwork(self.provider, network.get('network'))
  759. if label:
  760. cb_net.label = label
  761. return cb_net
  762. @implement(event_pattern="provider.networking.networks.create",
  763. priority=BaseNetworkService.STANDARD_EVENT_PRIORITY)
  764. def _delete(self, network_id):
  765. network = self.get(network_id)
  766. if not network.external and network_id in str(
  767. self.provider.neutron.list_networks()):
  768. # If there are ports associated with the network, it won't delete
  769. ports = self.provider.neutron.list_ports(
  770. network_id=network_id).get('ports', [])
  771. for port in ports:
  772. try:
  773. self.provider.neutron.delete_port(port.get('id'))
  774. except PortNotFoundClient:
  775. # Ports could have already been deleted if instances
  776. # are terminated etc. so exceptions can be safely ignored
  777. pass
  778. self.provider.neutron.delete_network(network_id)
  779. class OpenStackSubnetService(BaseSubnetService):
  780. def __init__(self, provider):
  781. super(OpenStackSubnetService, self).__init__(provider)
  782. def get(self, subnet_id):
  783. log.debug("Getting OpenStack Subnet with the id: %s", subnet_id)
  784. subnet = (s for s in self if s.id == subnet_id)
  785. return next(subnet, None)
  786. def list(self, network=None, limit=None, marker=None):
  787. if network:
  788. network_id = (network.id if isinstance(network, OpenStackNetwork)
  789. else network)
  790. subnets = [subnet for subnet in self if network_id ==
  791. subnet.network_id]
  792. else:
  793. subnets = [OpenStackSubnet(self.provider, subnet) for subnet in
  794. self.provider.neutron.list_subnets().get('subnets', [])]
  795. return ClientPagedResultList(self.provider, subnets,
  796. limit=limit, marker=marker)
  797. def create(self, label, network, cidr_block, zone):
  798. """zone param is ignored."""
  799. log.debug("Creating OpenStack Subnet with the params: "
  800. "[Label: %s Network: %s Cinder Block: %s Zone: -ignored-]",
  801. label, network, cidr_block)
  802. OpenStackSubnet.assert_valid_resource_label(label)
  803. network_id = (network.id if isinstance(network, OpenStackNetwork)
  804. else network)
  805. subnet_info = {'name': label, 'network_id': network_id,
  806. 'cidr': cidr_block, 'ip_version': 4}
  807. subnet = (self.provider.neutron.create_subnet({'subnet': subnet_info})
  808. .get('subnet'))
  809. cb_subnet = OpenStackSubnet(self.provider, subnet)
  810. return cb_subnet
  811. def get_or_create_default(self, zone):
  812. """
  813. Subnet zone is not supported by OpenStack and is thus ignored.
  814. """
  815. try:
  816. sn = self.find(label=OpenStackSubnet.CB_DEFAULT_SUBNET_LABEL)
  817. if sn:
  818. return sn[0]
  819. # No default subnet look for default network, then create subnet
  820. net = self.provider.networking.networks.get_or_create_default()
  821. sn = self.provider.networking.subnets.create(
  822. label=OpenStackSubnet.CB_DEFAULT_SUBNET_LABEL,
  823. cidr_block=OpenStackSubnet.CB_DEFAULT_SUBNET_IPV4RANGE,
  824. network=net)
  825. router = self.provider.networking.routers.get_or_create_default(
  826. net)
  827. router.attach_subnet(sn)
  828. gateway = net.gateways.get_or_create_inet_gateway()
  829. router.attach_gateway(gateway)
  830. return sn
  831. except NeutronClientException:
  832. return None
  833. def delete(self, subnet):
  834. log.debug("Deleting subnet: %s", subnet)
  835. subnet_id = (subnet.id if isinstance(subnet, OpenStackSubnet)
  836. else subnet)
  837. self.provider.neutron.delete_subnet(subnet_id)
  838. # Adhere to the interface docs
  839. if subnet_id not in self:
  840. return True
  841. return False
  842. class OpenStackRouterService(BaseRouterService):
  843. def __init__(self, provider):
  844. super(OpenStackRouterService, self).__init__(provider)
  845. def get(self, router_id):
  846. log.debug("Getting OpenStack Router with the id: %s", router_id)
  847. router = self.provider.os_conn.get_router(router_id)
  848. return OpenStackRouter(self.provider, router) if router else None
  849. def list(self, limit=None, marker=None):
  850. routers = self.provider.os_conn.list_routers()
  851. os_routers = [OpenStackRouter(self.provider, r) for r in routers]
  852. return ClientPagedResultList(self.provider, os_routers, limit=limit,
  853. marker=marker)
  854. def find(self, **kwargs):
  855. obj_list = self
  856. filters = ['label']
  857. matches = cb_helpers.generic_find(filters, kwargs, obj_list)
  858. return ClientPagedResultList(self._provider, list(matches))
  859. def create(self, label, network):
  860. """Parameter ``network`` is not used by OpenStack."""
  861. log.debug("Creating OpenStack Router with the label: %s", label)
  862. OpenStackRouter.assert_valid_resource_label(label)
  863. router = self.provider.os_conn.create_router(name=label)
  864. return OpenStackRouter(self.provider, router)