services.py 35 KB

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