services.py 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920
  1. """
  2. Services implemented by the OpenStack provider.
  3. """
  4. import fnmatch
  5. import logging
  6. import re
  7. from cinderclient.exceptions import NotFound as CinderNotFound
  8. from cloudbridge.cloud.base.resources import BaseLaunchConfig
  9. from cloudbridge.cloud.base.resources import ClientPagedResultList
  10. from cloudbridge.cloud.base.services import BaseBucketService
  11. from cloudbridge.cloud.base.services import BaseComputeService
  12. from cloudbridge.cloud.base.services import BaseFloatingIPService
  13. from cloudbridge.cloud.base.services import BaseGatewayService
  14. from cloudbridge.cloud.base.services import BaseImageService
  15. from cloudbridge.cloud.base.services import BaseInstanceService
  16. from cloudbridge.cloud.base.services import BaseKeyPairService
  17. from cloudbridge.cloud.base.services import BaseNetworkService
  18. from cloudbridge.cloud.base.services import BaseNetworkingService
  19. from cloudbridge.cloud.base.services import BaseRegionService
  20. from cloudbridge.cloud.base.services import BaseRouterService
  21. from cloudbridge.cloud.base.services import BaseSecurityService
  22. from cloudbridge.cloud.base.services import BaseSnapshotService
  23. from cloudbridge.cloud.base.services import BaseStorageService
  24. from cloudbridge.cloud.base.services import BaseSubnetService
  25. from cloudbridge.cloud.base.services import BaseVMFirewallService
  26. from cloudbridge.cloud.base.services import BaseVMTypeService
  27. from cloudbridge.cloud.base.services import BaseVolumeService
  28. from cloudbridge.cloud.interfaces.exceptions import ProviderInternalException
  29. from cloudbridge.cloud.interfaces.resources import KeyPair
  30. from cloudbridge.cloud.interfaces.resources import MachineImage
  31. from cloudbridge.cloud.interfaces.resources import PlacementZone
  32. from cloudbridge.cloud.interfaces.resources import Snapshot
  33. from cloudbridge.cloud.interfaces.resources import Subnet
  34. from cloudbridge.cloud.interfaces.resources import VMFirewall
  35. from cloudbridge.cloud.interfaces.resources import VMType
  36. from cloudbridge.cloud.interfaces.resources import Volume
  37. from cloudbridge.cloud.providers.openstack import helpers as oshelpers
  38. from neutronclient.common.exceptions import NeutronClientException
  39. from novaclient.exceptions import NotFound as NovaNotFound
  40. from openstack.exceptions import ResourceNotFound
  41. from .resources import OpenStackBucket
  42. from .resources import OpenStackFloatingIP
  43. from .resources import OpenStackInstance
  44. from .resources import OpenStackInternetGateway
  45. from .resources import OpenStackKeyPair
  46. from .resources import OpenStackMachineImage
  47. from .resources import OpenStackNetwork
  48. from .resources import OpenStackRegion
  49. from .resources import OpenStackRouter
  50. from .resources import OpenStackSnapshot
  51. from .resources import OpenStackSubnet
  52. from .resources import OpenStackVMFirewall
  53. from .resources import OpenStackVMType
  54. from .resources import OpenStackVolume
  55. log = logging.getLogger(__name__)
  56. class OpenStackSecurityService(BaseSecurityService):
  57. def __init__(self, provider):
  58. super(OpenStackSecurityService, self).__init__(provider)
  59. # Initialize provider services
  60. self._key_pairs = OpenStackKeyPairService(provider)
  61. self._vm_firewalls = OpenStackVMFirewallService(provider)
  62. @property
  63. def key_pairs(self):
  64. """
  65. Provides access to key pairs for this provider.
  66. :rtype: ``object`` of :class:`.KeyPairService`
  67. :return: a KeyPairService object
  68. """
  69. return self._key_pairs
  70. @property
  71. def vm_firewalls(self):
  72. """
  73. Provides access to VM firewalls for this provider.
  74. :rtype: ``object`` of :class:`.VMFirewallService`
  75. :return: a VMFirewallService object
  76. """
  77. return self._vm_firewalls
  78. def get_or_create_ec2_credentials(self):
  79. """
  80. A provider specific method than returns the ec2 credentials for the
  81. current user, or creates a new pair if one doesn't exist.
  82. """
  83. keystone = self.provider.keystone
  84. if hasattr(keystone, 'ec2'):
  85. user_id = keystone.session.get_user_id()
  86. user_creds = [cred for cred in keystone.ec2.list(user_id) if
  87. cred.tenant_id == keystone.session.get_project_id()]
  88. if user_creds:
  89. return user_creds[0]
  90. else:
  91. return keystone.ec2.create(
  92. user_id, keystone.session.get_project_id())
  93. return None
  94. def get_ec2_endpoints(self):
  95. """
  96. A provider specific method than returns the ec2 endpoints if
  97. available.
  98. """
  99. keystone = self.provider.keystone
  100. ec2_url = keystone.session.get_endpoint(service_type='ec2')
  101. s3_url = keystone.session.get_endpoint(service_type='s3')
  102. return {'ec2_endpoint': ec2_url,
  103. 's3_endpoint': s3_url}
  104. class OpenStackKeyPairService(BaseKeyPairService):
  105. def __init__(self, provider):
  106. super(OpenStackKeyPairService, self).__init__(provider)
  107. def get(self, key_pair_id):
  108. """
  109. Returns a KeyPair given its id.
  110. """
  111. try:
  112. return OpenStackKeyPair(
  113. self.provider, self.provider.nova.keypairs.get(key_pair_id))
  114. except NovaNotFound:
  115. return None
  116. def list(self, limit=None, marker=None):
  117. """
  118. List all key pairs associated with this account.
  119. :rtype: ``list`` of :class:`.KeyPair`
  120. :return: list of KeyPair objects
  121. """
  122. keypairs = self.provider.nova.keypairs.list()
  123. results = [OpenStackKeyPair(self.provider, kp)
  124. for kp in keypairs]
  125. return ClientPagedResultList(self.provider, results,
  126. limit=limit, marker=marker)
  127. def find(self, name, limit=None, marker=None):
  128. """
  129. Searches for a key pair by a given list of attributes.
  130. """
  131. keypairs = self.provider.nova.keypairs.findall(name=name)
  132. results = [OpenStackKeyPair(self.provider, kp)
  133. for kp in keypairs]
  134. return ClientPagedResultList(self.provider, results,
  135. limit=limit, marker=marker)
  136. def create(self, name):
  137. """
  138. Create a new key pair or raise an exception if one already exists.
  139. :type name: str
  140. :param name: The name of the key pair to be created.
  141. :rtype: ``object`` of :class:`.KeyPair`
  142. :return: A key pair instance or ``None`` if one was not be created.
  143. """
  144. OpenStackKeyPair.assert_valid_resource_name(name)
  145. kp = self.provider.nova.keypairs.create(name)
  146. if kp:
  147. return OpenStackKeyPair(self.provider, kp)
  148. return None
  149. class OpenStackVMFirewallService(BaseVMFirewallService):
  150. def __init__(self, provider):
  151. super(OpenStackVMFirewallService, self).__init__(provider)
  152. def get(self, firewall_id):
  153. try:
  154. return OpenStackVMFirewall(
  155. self.provider,
  156. self.provider.os_conn.network.get_security_group(firewall_id))
  157. except ResourceNotFound:
  158. return None
  159. def list(self, limit=None, marker=None):
  160. firewalls = [
  161. OpenStackVMFirewall(self.provider, fw)
  162. for fw in self.provider.os_conn.network.security_groups()]
  163. return ClientPagedResultList(self.provider, firewalls,
  164. limit=limit, marker=marker)
  165. def create(self, name, description, network_id):
  166. OpenStackVMFirewall.assert_valid_resource_name(name)
  167. sg = self.provider.os_conn.network.create_security_group(
  168. name=name, description=description)
  169. if sg:
  170. return OpenStackVMFirewall(self.provider, sg)
  171. return None
  172. def find(self, name, limit=None, marker=None):
  173. sgs = [self.provider.os_conn.network.find_security_group(name)]
  174. results = [OpenStackVMFirewall(self.provider, sg)
  175. for sg in sgs if sg]
  176. return ClientPagedResultList(self.provider, results,
  177. limit=limit, marker=marker)
  178. def delete(self, group_id):
  179. firewall = self.get(group_id)
  180. if firewall:
  181. firewall.delete()
  182. return True
  183. class OpenStackImageService(BaseImageService):
  184. def __init__(self, provider):
  185. super(OpenStackImageService, self).__init__(provider)
  186. def get(self, image_id):
  187. """
  188. Returns an Image given its id
  189. """
  190. try:
  191. return OpenStackMachineImage(
  192. self.provider, self.provider.os_conn.image.get_image(image_id))
  193. except ResourceNotFound:
  194. return None
  195. def find(self, name, limit=None, marker=None):
  196. """
  197. Searches for an image by a given list of attributes
  198. """
  199. regex = fnmatch.translate(name)
  200. cb_images = [
  201. img
  202. for img in self
  203. if img.name and re.search(regex, img.name)]
  204. return oshelpers.to_server_paged_list(self.provider, cb_images, limit)
  205. def list(self, limit=None, marker=None):
  206. """
  207. List all images.
  208. """
  209. os_images = self.provider.os_conn.image.images(
  210. limit=oshelpers.os_result_limit(self.provider, limit),
  211. marker=marker)
  212. cb_images = [
  213. OpenStackMachineImage(self.provider, img)
  214. for img in os_images]
  215. return oshelpers.to_server_paged_list(self.provider, cb_images, limit)
  216. class OpenStackVMTypeService(BaseVMTypeService):
  217. def __init__(self, provider):
  218. super(OpenStackVMTypeService, self).__init__(provider)
  219. def list(self, limit=None, marker=None):
  220. cb_itypes = [
  221. OpenStackVMType(self.provider, obj)
  222. for obj in self.provider.nova.flavors.list(
  223. limit=oshelpers.os_result_limit(self.provider, limit),
  224. marker=marker)]
  225. return oshelpers.to_server_paged_list(self.provider, cb_itypes, limit)
  226. class OpenStackStorageService(BaseStorageService):
  227. def __init__(self, provider):
  228. super(OpenStackStorageService, self).__init__(provider)
  229. # Initialize provider services
  230. self._volume_svc = OpenStackVolumeService(self.provider)
  231. self._snapshot_svc = OpenStackSnapshotService(self.provider)
  232. self._bucket_svc = OpenStackBucketService(self.provider)
  233. @property
  234. def volumes(self):
  235. return self._volume_svc
  236. @property
  237. def snapshots(self):
  238. return self._snapshot_svc
  239. @property
  240. def buckets(self):
  241. return self._bucket_svc
  242. class OpenStackVolumeService(BaseVolumeService):
  243. def __init__(self, provider):
  244. super(OpenStackVolumeService, self).__init__(provider)
  245. def get(self, volume_id):
  246. """
  247. Returns a volume given its id.
  248. """
  249. try:
  250. return OpenStackVolume(
  251. self.provider, self.provider.cinder.volumes.get(volume_id))
  252. except CinderNotFound:
  253. return None
  254. def find(self, name, limit=None, marker=None):
  255. """
  256. Searches for a volume by a given list of attributes.
  257. """
  258. search_opts = {'name': name}
  259. cb_vols = [
  260. OpenStackVolume(self.provider, vol)
  261. for vol in self.provider.cinder.volumes.list(
  262. search_opts=search_opts,
  263. limit=oshelpers.os_result_limit(self.provider, limit),
  264. marker=marker)]
  265. return oshelpers.to_server_paged_list(self.provider, cb_vols, limit)
  266. def list(self, limit=None, marker=None):
  267. """
  268. List all volumes.
  269. """
  270. cb_vols = [
  271. OpenStackVolume(self.provider, vol)
  272. for vol in self.provider.cinder.volumes.list(
  273. limit=oshelpers.os_result_limit(self.provider, limit),
  274. marker=marker)]
  275. return oshelpers.to_server_paged_list(self.provider, cb_vols, limit)
  276. def create(self, name, size, zone, snapshot=None, description=None):
  277. """
  278. Creates a new volume.
  279. """
  280. OpenStackVolume.assert_valid_resource_name(name)
  281. zone_id = zone.id if isinstance(zone, PlacementZone) else zone
  282. snapshot_id = snapshot.id if isinstance(
  283. snapshot, OpenStackSnapshot) and snapshot else snapshot
  284. os_vol = self.provider.cinder.volumes.create(
  285. size, name=name, description=description,
  286. availability_zone=zone_id, snapshot_id=snapshot_id)
  287. return OpenStackVolume(self.provider, os_vol)
  288. class OpenStackSnapshotService(BaseSnapshotService):
  289. def __init__(self, provider):
  290. super(OpenStackSnapshotService, self).__init__(provider)
  291. def get(self, snapshot_id):
  292. """
  293. Returns a snapshot given its id.
  294. """
  295. try:
  296. return OpenStackSnapshot(
  297. self.provider,
  298. self.provider.cinder.volume_snapshots.get(snapshot_id))
  299. except CinderNotFound:
  300. return None
  301. def find(self, name, limit=None, marker=None):
  302. """
  303. Searches for a volume by a given list of attributes.
  304. """
  305. search_opts = {'name': name, # TODO: Cinder is ignoring name
  306. 'limit': oshelpers.os_result_limit(self.provider,
  307. limit),
  308. 'marker': marker}
  309. cb_snaps = [
  310. OpenStackSnapshot(self.provider, snap) for
  311. snap in self.provider.cinder.volume_snapshots.list(search_opts)
  312. if snap.name == name]
  313. return oshelpers.to_server_paged_list(self.provider, cb_snaps, limit)
  314. def list(self, limit=None, marker=None):
  315. """
  316. List all snapshot.
  317. """
  318. cb_snaps = [
  319. OpenStackSnapshot(self.provider, snap) for
  320. snap in self.provider.cinder.volume_snapshots.list(
  321. search_opts={'limit': oshelpers.os_result_limit(self.provider,
  322. limit),
  323. 'marker': marker})]
  324. return oshelpers.to_server_paged_list(self.provider, cb_snaps, limit)
  325. def create(self, name, volume, description=None):
  326. """
  327. Creates a new snapshot of a given volume.
  328. """
  329. OpenStackSnapshot.assert_valid_resource_name(name)
  330. volume_id = (volume.id if isinstance(volume, OpenStackVolume)
  331. else volume)
  332. os_snap = self.provider.cinder.volume_snapshots.create(
  333. volume_id, name=name,
  334. description=description)
  335. return OpenStackSnapshot(self.provider, os_snap)
  336. class OpenStackBucketService(BaseBucketService):
  337. def __init__(self, provider):
  338. super(OpenStackBucketService, self).__init__(provider)
  339. def get(self, bucket_id):
  340. """
  341. Returns a bucket given its ID. Returns ``None`` if the bucket
  342. does not exist.
  343. """
  344. _, container_list = self.provider.swift.get_account(
  345. prefix=bucket_id)
  346. if container_list:
  347. return OpenStackBucket(self.provider,
  348. next((c for c in container_list
  349. if c['name'] == bucket_id), None))
  350. else:
  351. return None
  352. def find(self, name, limit=None, marker=None):
  353. """
  354. Searches for a bucket by a given list of attributes.
  355. """
  356. _, container_list = self.provider.swift.get_account(
  357. limit=oshelpers.os_result_limit(self.provider, limit),
  358. marker=marker)
  359. cb_buckets = [OpenStackBucket(self.provider, c)
  360. for c in container_list
  361. if name in c.get("name")]
  362. return oshelpers.to_server_paged_list(self.provider, cb_buckets, limit)
  363. def list(self, limit=None, marker=None):
  364. """
  365. List all containers.
  366. """
  367. _, container_list = self.provider.swift.get_account(
  368. limit=oshelpers.os_result_limit(self.provider, limit),
  369. marker=marker)
  370. cb_buckets = [OpenStackBucket(self.provider, c)
  371. for c in container_list]
  372. return oshelpers.to_server_paged_list(self.provider, cb_buckets, limit)
  373. def create(self, name, location=None):
  374. """
  375. Create a new bucket.
  376. """
  377. OpenStackBucket.assert_valid_resource_name(name)
  378. self.provider.swift.put_container(name)
  379. return self.get(name)
  380. class OpenStackRegionService(BaseRegionService):
  381. def __init__(self, provider):
  382. super(OpenStackRegionService, self).__init__(provider)
  383. def get(self, region_id):
  384. region = (r for r in self.list() if r.id == region_id)
  385. return next(region, None)
  386. def list(self, limit=None, marker=None):
  387. # pylint:disable=protected-access
  388. if self.provider._keystone_version == 3:
  389. os_regions = [OpenStackRegion(self.provider, region)
  390. for region in self.provider.keystone.regions.list()]
  391. return ClientPagedResultList(self.provider, os_regions,
  392. limit=limit, marker=marker)
  393. else:
  394. # Keystone v3 onwards supports directly listing regions
  395. # but for v2, this convoluted method is necessary.
  396. regions = (
  397. endpoint.get('region') or endpoint.get('region_id')
  398. for svc in self.provider.keystone.service_catalog.get_data()
  399. for endpoint in svc.get('endpoints', [])
  400. )
  401. regions = set(region for region in regions if region)
  402. os_regions = [OpenStackRegion(self.provider, region)
  403. for region in regions]
  404. return ClientPagedResultList(self.provider, os_regions,
  405. limit=limit, marker=marker)
  406. @property
  407. def current(self):
  408. nova_region = self.provider.nova.client.region_name
  409. return self.get(nova_region) if nova_region else None
  410. class OpenStackComputeService(BaseComputeService):
  411. def __init__(self, provider):
  412. super(OpenStackComputeService, self).__init__(provider)
  413. self._vm_type_svc = OpenStackVMTypeService(self.provider)
  414. self._instance_svc = OpenStackInstanceService(self.provider)
  415. self._region_svc = OpenStackRegionService(self.provider)
  416. self._images_svc = OpenStackImageService(self.provider)
  417. @property
  418. def images(self):
  419. return self._images_svc
  420. @property
  421. def vm_types(self):
  422. return self._vm_type_svc
  423. @property
  424. def instances(self):
  425. return self._instance_svc
  426. @property
  427. def regions(self):
  428. return self._region_svc
  429. class OpenStackInstanceService(BaseInstanceService):
  430. def __init__(self, provider):
  431. super(OpenStackInstanceService, self).__init__(provider)
  432. def create(self, name, image, vm_type, subnet, zone=None,
  433. key_pair=None, vm_firewalls=None, user_data=None,
  434. launch_config=None,
  435. **kwargs):
  436. """Create a new virtual machine instance."""
  437. OpenStackInstance.assert_valid_resource_name(name)
  438. image_id = image.id if isinstance(image, MachineImage) else image
  439. vm_size = vm_type.id if \
  440. isinstance(vm_type, VMType) else \
  441. self.provider.compute.vm_types.find(
  442. name=vm_type)[0].id
  443. if isinstance(subnet, Subnet):
  444. subnet_id = subnet.id
  445. net_id = subnet.network_id
  446. else:
  447. subnet_id = subnet
  448. net_id = (self.provider.networking.subnets
  449. .get(subnet_id).network_id
  450. if subnet_id else None)
  451. zone_id = zone.id if isinstance(zone, PlacementZone) else zone
  452. key_pair_name = key_pair.name if \
  453. isinstance(key_pair, KeyPair) else key_pair
  454. bdm = None
  455. if launch_config:
  456. bdm = self._to_block_device_mapping(launch_config)
  457. # Security groups must be passed in as a list of IDs and attached to a
  458. # port if a port is being created. Otherwise, the security groups must
  459. # be passed in as a list of names to the servers.create() call.
  460. # OpenStack will respect the port's security groups first and then
  461. # fall-back to the named security groups.
  462. sg_name_list = []
  463. nics = None
  464. if subnet_id:
  465. log.debug("Creating network port for %s in subnet: %s",
  466. name, subnet_id)
  467. sg_list = []
  468. if vm_firewalls:
  469. if isinstance(vm_firewalls, list) and \
  470. isinstance(vm_firewalls[0], VMFirewall):
  471. sg_list = vm_firewalls
  472. else:
  473. sg_list = (self.provider.security.vm_firewalls
  474. .find(name=sg) for sg in vm_firewalls)
  475. sg_list = (sg[0] for sg in sg_list if sg)
  476. sg_id_list = [sg.id for sg in sg_list]
  477. port_def = {
  478. "port": {
  479. "admin_state_up": True,
  480. "name": name,
  481. "network_id": net_id,
  482. "fixed_ips": [{"subnet_id": subnet_id}],
  483. "security_groups": sg_id_list
  484. }
  485. }
  486. port_id = self.provider.neutron.create_port(port_def)['port']['id']
  487. nics = [{'net-id': net_id, 'port-id': port_id}]
  488. else:
  489. if vm_firewalls:
  490. if isinstance(vm_firewalls, list) and \
  491. isinstance(vm_firewalls[0], VMFirewall):
  492. sg_name_list = [sg.name for sg in vm_firewalls]
  493. else:
  494. sg_name_list = vm_firewalls
  495. log.debug("Launching in subnet %s", subnet_id)
  496. os_instance = self.provider.nova.servers.create(
  497. name,
  498. None if self._has_root_device(launch_config) else image_id,
  499. vm_size,
  500. min_count=1,
  501. max_count=1,
  502. availability_zone=zone_id,
  503. key_name=key_pair_name,
  504. security_groups=sg_name_list,
  505. userdata=user_data,
  506. block_device_mapping_v2=bdm,
  507. nics=nics)
  508. return OpenStackInstance(self.provider, os_instance)
  509. def _to_block_device_mapping(self, launch_config):
  510. """
  511. Extracts block device mapping information
  512. from a launch config and constructs a BlockDeviceMappingV2
  513. object.
  514. """
  515. bdm = []
  516. for device in launch_config.block_devices:
  517. bdm_dict = dict()
  518. if device.is_volume:
  519. bdm_dict['destination_type'] = 'volume'
  520. if device.is_root:
  521. bdm_dict['device_name'] = '/dev/sda'
  522. bdm_dict['boot_index'] = 0
  523. if isinstance(device.source, Snapshot):
  524. bdm_dict['source_type'] = 'snapshot'
  525. bdm_dict['uuid'] = device.source.id
  526. elif isinstance(device.source, Volume):
  527. bdm_dict['source_type'] = 'volume'
  528. bdm_dict['uuid'] = device.source.id
  529. elif isinstance(device.source, MachineImage):
  530. bdm_dict['source_type'] = 'image'
  531. bdm_dict['uuid'] = device.source.id
  532. else:
  533. bdm_dict['source_type'] = 'blank'
  534. if device.delete_on_terminate is not None:
  535. bdm_dict[
  536. 'delete_on_termination'] = device.delete_on_terminate
  537. if device.size:
  538. bdm_dict['volume_size'] = device.size
  539. else:
  540. bdm_dict['destination_type'] = 'local'
  541. bdm_dict['source_type'] = 'blank'
  542. bdm_dict['delete_on_termination'] = True
  543. bdm.append(bdm_dict)
  544. return bdm
  545. def _has_root_device(self, launch_config):
  546. if not launch_config:
  547. return False
  548. for device in launch_config.block_devices:
  549. if device.is_root:
  550. return True
  551. return False
  552. def create_launch_config(self):
  553. return BaseLaunchConfig(self.provider)
  554. def find(self, name, limit=None, marker=None):
  555. """
  556. Searches for an instance by a given list of attributes.
  557. """
  558. search_opts = {'name': name}
  559. cb_insts = [
  560. OpenStackInstance(self.provider, inst)
  561. for inst in self.provider.nova.servers.list(
  562. search_opts=search_opts,
  563. limit=oshelpers.os_result_limit(self.provider, limit),
  564. marker=marker)]
  565. return oshelpers.to_server_paged_list(self.provider, cb_insts, limit)
  566. def list(self, limit=None, marker=None):
  567. """
  568. List all instances.
  569. """
  570. cb_insts = [
  571. OpenStackInstance(self.provider, inst)
  572. for inst in self.provider.nova.servers.list(
  573. limit=oshelpers.os_result_limit(self.provider, limit),
  574. marker=marker)]
  575. return oshelpers.to_server_paged_list(self.provider, cb_insts, limit)
  576. def get(self, instance_id):
  577. """
  578. Returns an instance given its id.
  579. """
  580. try:
  581. os_instance = self.provider.nova.servers.get(instance_id)
  582. return OpenStackInstance(self.provider, os_instance)
  583. except NovaNotFound:
  584. return None
  585. class OpenStackNetworkingService(BaseNetworkingService):
  586. def __init__(self, provider):
  587. super(OpenStackNetworkingService, self).__init__(provider)
  588. self._network_service = OpenStackNetworkService(self.provider)
  589. self._subnet_service = OpenStackSubnetService(self.provider)
  590. self._fip_service = OpenStackFloatingIPService(self.provider)
  591. self._router_service = OpenStackRouterService(self.provider)
  592. self._gateway_service = OpenStackGatewayService(self.provider)
  593. @property
  594. def networks(self):
  595. return self._network_service
  596. @property
  597. def subnets(self):
  598. return self._subnet_service
  599. @property
  600. def floating_ips(self):
  601. return self._fip_service
  602. @property
  603. def routers(self):
  604. return self._router_service
  605. @property
  606. def gateways(self):
  607. return self._gateway_service
  608. class OpenStackNetworkService(BaseNetworkService):
  609. def __init__(self, provider):
  610. super(OpenStackNetworkService, self).__init__(provider)
  611. def get(self, network_id):
  612. network = (n for n in self if n.id == network_id)
  613. return next(network, None)
  614. def list(self, limit=None, marker=None):
  615. networks = [OpenStackNetwork(self.provider, network)
  616. for network in self.provider.neutron.list_networks()
  617. .get('networks') if network]
  618. return ClientPagedResultList(self.provider, networks,
  619. limit=limit, marker=marker)
  620. def find(self, name, limit=None, marker=None):
  621. networks = [OpenStackNetwork(self.provider, network)
  622. for network in self.provider.neutron.list_networks(
  623. name=name)
  624. .get('networks') if network]
  625. return ClientPagedResultList(self.provider, networks,
  626. limit=limit, marker=marker)
  627. def create(self, name, cidr_block):
  628. OpenStackNetwork.assert_valid_resource_name(name)
  629. net_info = {'name': name}
  630. network = self.provider.neutron.create_network({'network': net_info})
  631. return OpenStackNetwork(self.provider, network.get('network'))
  632. class OpenStackSubnetService(BaseSubnetService):
  633. def __init__(self, provider):
  634. super(OpenStackSubnetService, self).__init__(provider)
  635. def get(self, subnet_id):
  636. subnet = (s for s in self if s.id == subnet_id)
  637. return next(subnet, None)
  638. def list(self, network=None, limit=None, marker=None):
  639. if network:
  640. network_id = (network.id if isinstance(network, OpenStackNetwork)
  641. else network)
  642. subnets = [subnet for subnet in self.list() if network_id ==
  643. subnet.network_id]
  644. else:
  645. subnets = [OpenStackSubnet(self.provider, subnet) for subnet in
  646. self.provider.neutron.list_subnets().get('subnets', [])]
  647. return ClientPagedResultList(self.provider, subnets,
  648. limit=limit, marker=marker)
  649. def create(self, name, network, cidr_block, zone=None):
  650. """zone param is ignored."""
  651. OpenStackSubnet.assert_valid_resource_name(name)
  652. network_id = (network.id if isinstance(network, OpenStackNetwork)
  653. else network)
  654. subnet_info = {'name': name, 'network_id': network_id,
  655. 'cidr': cidr_block, 'ip_version': 4}
  656. subnet = (self.provider.neutron.create_subnet({'subnet': subnet_info})
  657. .get('subnet'))
  658. return OpenStackSubnet(self.provider, subnet)
  659. def get_or_create_default(self, zone=None):
  660. """
  661. Subnet zone is not supported by OpenStack and is thus ignored.
  662. """
  663. try:
  664. sn = self.find(name=OpenStackSubnet.CB_DEFAULT_SUBNET_NAME)
  665. if sn:
  666. return sn[0]
  667. # No default; create one
  668. net = self.provider.networking.networks.create(
  669. name=OpenStackNetwork.CB_DEFAULT_NETWORK_NAME,
  670. cidr_block='10.0.0.0/16')
  671. sn = net.create_subnet(name=OpenStackSubnet.CB_DEFAULT_SUBNET_NAME,
  672. cidr_block='10.0.0.0/24')
  673. router = self.provider.networking.routers.create(
  674. network=net, name=OpenStackRouter.CB_DEFAULT_ROUTER_NAME)
  675. router.attach_subnet(sn)
  676. gteway = (self.provider.networking.gateways
  677. .get_or_create_inet_gateway(
  678. OpenStackInternetGateway.CB_DEFAULT_INET_GATEWAY_NAME
  679. ))
  680. router.attach_gateway(gteway)
  681. return sn
  682. except NeutronClientException:
  683. return None
  684. def delete(self, subnet):
  685. subnet_id = (subnet.id if isinstance(subnet, OpenStackSubnet)
  686. else subnet)
  687. self.provider.neutron.delete_subnet(subnet_id)
  688. # Adhere to the interface docs
  689. if subnet_id not in self.list():
  690. return True
  691. return False
  692. class OpenStackFloatingIPService(BaseFloatingIPService):
  693. def __init__(self, provider):
  694. super(OpenStackFloatingIPService, self).__init__(provider)
  695. def get(self, fip_id):
  696. try:
  697. return OpenStackFloatingIP(
  698. self.provider, self.provider.os_conn.network.get_ip(fip_id))
  699. except ResourceNotFound:
  700. return None
  701. def list(self, limit=None, marker=None):
  702. fips = [OpenStackFloatingIP(self.provider, fip)
  703. for fip in self.provider.os_conn.network.ips()]
  704. return ClientPagedResultList(self.provider, fips,
  705. limit=limit, marker=marker)
  706. def create(self):
  707. # OpenStack requires a floating IP to be associated with an external,
  708. # network, so choose the first external network found
  709. for n in self.provider.networking.networks:
  710. if n.external:
  711. return OpenStackFloatingIP(
  712. self.provider, self.provider.os_conn.network.create_ip(
  713. floating_network_id=n.id))
  714. raise ProviderInternalException(
  715. "This OpenStack cloud has no designated external network")
  716. class OpenStackRouterService(BaseRouterService):
  717. def __init__(self, provider):
  718. super(OpenStackRouterService, self).__init__(provider)
  719. def get(self, router_id):
  720. router = (r for r in self if r.id == router_id)
  721. return next(router, None)
  722. def list(self, limit=None, marker=None):
  723. routers = self.provider.neutron.list_routers().get('routers')
  724. os_routers = [OpenStackRouter(self.provider, r) for r in routers]
  725. return ClientPagedResultList(self.provider, os_routers, limit=limit,
  726. marker=marker)
  727. def find(self, name, limit=None, marker=None):
  728. aws_routers = [r for r in self if r.name == name]
  729. return ClientPagedResultList(self.provider, aws_routers, limit=limit,
  730. marker=marker)
  731. def create(self, name, network):
  732. """
  733. ``network`` is not used by OpenStack.
  734. However, the API seems to indicate it is a (required) param?!
  735. https://developer.openstack.org/api-ref/networking/v2/
  736. ?expanded=delete-router-detail,create-router-detail#create-router
  737. """
  738. OpenStackRouter.assert_valid_resource_name(name)
  739. body = {'router': {'name': name}} if name else None
  740. router = self.provider.neutron.create_router(body)
  741. return OpenStackRouter(self.provider, router.get('router'))
  742. class OpenStackGatewayService(BaseGatewayService):
  743. def __init__(self, provider):
  744. super(OpenStackGatewayService, self).__init__(provider)
  745. def get_or_create_inet_gateway(self, name):
  746. OpenStackInternetGateway.assert_valid_resource_name(name)
  747. for n in self.provider.networking.networks:
  748. if n.external:
  749. return OpenStackInternetGateway(self.provider, n)
  750. return None
  751. def delete(self, gateway):
  752. gateway.delete()