services.py 34 KB

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