resources.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948
  1. """
  2. DataTypes used by this provider
  3. """
  4. import inspect
  5. import json
  6. import shutil
  7. import ipaddress
  8. from cloudbridge.cloud.base.resources import BaseAttachmentInfo
  9. from cloudbridge.cloud.base.resources import BaseBucket
  10. from cloudbridge.cloud.base.resources import BaseBucketObject
  11. from cloudbridge.cloud.base.resources import BaseInstance
  12. from cloudbridge.cloud.base.resources import BaseInstanceType
  13. from cloudbridge.cloud.base.resources import BaseKeyPair
  14. from cloudbridge.cloud.base.resources import BaseMachineImage
  15. from cloudbridge.cloud.base.resources import BaseNetwork
  16. from cloudbridge.cloud.base.resources import BasePlacementZone
  17. from cloudbridge.cloud.base.resources import BaseRegion
  18. from cloudbridge.cloud.base.resources import BaseSecurityGroup
  19. from cloudbridge.cloud.base.resources import BaseSecurityGroupRule
  20. from cloudbridge.cloud.base.resources import BaseSnapshot
  21. from cloudbridge.cloud.base.resources import BaseSubnet
  22. from cloudbridge.cloud.base.resources import BaseVolume
  23. from cloudbridge.cloud.interfaces.resources import InstanceState
  24. from cloudbridge.cloud.interfaces.resources import MachineImageState
  25. from cloudbridge.cloud.interfaces.resources import NetworkState
  26. from cloudbridge.cloud.interfaces.resources import SnapshotState
  27. from cloudbridge.cloud.interfaces.resources import VolumeState
  28. from cloudbridge.cloud.providers.openstack import helpers as oshelpers
  29. import novaclient.exceptions as novaex
  30. import swiftclient.exceptions as swiftex
  31. class OpenStackMachineImage(BaseMachineImage):
  32. # ref: http://docs.openstack.org/developer/glance/statuses.html
  33. IMAGE_STATE_MAP = {
  34. 'QUEUED': MachineImageState.PENDING,
  35. 'SAVING': MachineImageState.PENDING,
  36. 'ACTIVE': MachineImageState.AVAILABLE,
  37. 'KILLED': MachineImageState.ERROR,
  38. 'DELETED': MachineImageState.ERROR,
  39. 'PENDING_DELETE': MachineImageState.ERROR
  40. }
  41. def __init__(self, provider, os_image):
  42. super(OpenStackMachineImage, self).__init__(provider)
  43. if isinstance(os_image, OpenStackMachineImage):
  44. # pylint:disable=protected-access
  45. self._os_image = os_image._os_image
  46. else:
  47. self._os_image = os_image
  48. @property
  49. def id(self):
  50. """
  51. Get the image identifier.
  52. """
  53. return self._os_image.id
  54. @property
  55. def name(self):
  56. """
  57. Get the image name.
  58. """
  59. return self._os_image.name
  60. @property
  61. def description(self):
  62. """
  63. Get the image description.
  64. """
  65. return None
  66. def delete(self):
  67. """
  68. Delete this image
  69. """
  70. self._os_image.delete()
  71. @property
  72. def state(self):
  73. return OpenStackMachineImage.IMAGE_STATE_MAP.get(
  74. self._os_image.status, MachineImageState.UNKNOWN)
  75. def refresh(self):
  76. """
  77. Refreshes the state of this instance by re-querying the cloud provider
  78. for its latest state.
  79. """
  80. image = self._provider.compute.images.get(self.id)
  81. if image:
  82. self._os_image = image._os_image # pylint:disable=protected-access
  83. else:
  84. # The image no longer exists and cannot be refreshed.
  85. # set the status to unknown
  86. self._os_image.status = 'unknown'
  87. class OpenStackPlacementZone(BasePlacementZone):
  88. def __init__(self, provider, zone, region):
  89. super(OpenStackPlacementZone, self).__init__(provider)
  90. if isinstance(zone, OpenStackPlacementZone):
  91. self._os_zone = zone._os_zone # pylint:disable=protected-access
  92. self._os_region = zone._os_region
  93. else:
  94. self._os_zone = zone
  95. self._os_region = region
  96. @property
  97. def id(self):
  98. """
  99. Get the zone id
  100. :rtype: ``str``
  101. :return: ID for this zone as returned by the cloud middleware.
  102. """
  103. return self._os_zone
  104. @property
  105. def name(self):
  106. """
  107. Get the zone name.
  108. :rtype: ``str``
  109. :return: Name for this zone as returned by the cloud middleware.
  110. """
  111. # return self._os_zone.zoneName
  112. return self._os_zone
  113. @property
  114. def region_name(self):
  115. """
  116. Get the region that this zone belongs to.
  117. :rtype: ``str``
  118. :return: Name of this zone's region as returned by the cloud middleware
  119. """
  120. return self._os_region
  121. class OpenStackInstanceType(BaseInstanceType):
  122. def __init__(self, provider, os_flavor):
  123. super(OpenStackInstanceType, self).__init__(provider)
  124. self._os_flavor = os_flavor
  125. @property
  126. def id(self):
  127. return self._os_flavor.id
  128. @property
  129. def name(self):
  130. return self._os_flavor.name
  131. @property
  132. def family(self):
  133. # TODO: This may not be standardised accross openstack
  134. # but NeCTAR is using it this way
  135. return self.extra_data.get('flavor_class:name')
  136. @property
  137. def vcpus(self):
  138. return self._os_flavor.vcpus
  139. @property
  140. def ram(self):
  141. return self._os_flavor.ram
  142. @property
  143. def size_root_disk(self):
  144. return self._os_flavor.disk
  145. @property
  146. def size_ephemeral_disks(self):
  147. return 0 if self._os_flavor.ephemeral == 'N/A' else \
  148. self._os_flavor.ephemeral
  149. @property
  150. def num_ephemeral_disks(self):
  151. return 0 if self._os_flavor.ephemeral == 'N/A' else \
  152. self._os_flavor.ephemeral
  153. @property
  154. def extra_data(self):
  155. extras = self._os_flavor.get_keys()
  156. extras['rxtx_factor'] = self._os_flavor.rxtx_factor
  157. extras['swap'] = self._os_flavor.swap
  158. extras['is_public'] = self._os_flavor.is_public
  159. return extras
  160. class OpenStackInstance(BaseInstance):
  161. # ref: http://docs.openstack.org/developer/nova/v2/2.0_server_concepts.html
  162. # and http://developer.openstack.org/api-ref-compute-v2.html
  163. INSTANCE_STATE_MAP = {
  164. 'ACTIVE': InstanceState.RUNNING,
  165. 'BUILD': InstanceState.PENDING,
  166. 'DELETED': InstanceState.TERMINATED,
  167. 'ERROR': InstanceState.ERROR,
  168. 'HARD_REBOOT': InstanceState.REBOOTING,
  169. 'PASSWORD': InstanceState.PENDING,
  170. 'PAUSED': InstanceState.STOPPED,
  171. 'REBOOT': InstanceState.REBOOTING,
  172. 'REBUILD': InstanceState.CONFIGURING,
  173. 'RESCUE': InstanceState.CONFIGURING,
  174. 'RESIZE': InstanceState.CONFIGURING,
  175. 'REVERT_RESIZE': InstanceState.CONFIGURING,
  176. 'SOFT_DELETED': InstanceState.STOPPED,
  177. 'STOPPED': InstanceState.STOPPED,
  178. 'SUSPENDED': InstanceState.STOPPED,
  179. 'SHUTOFF': InstanceState.STOPPED,
  180. 'UNKNOWN': InstanceState.UNKNOWN,
  181. 'VERIFY_RESIZE': InstanceState.CONFIGURING
  182. }
  183. def __init__(self, provider, os_instance):
  184. super(OpenStackInstance, self).__init__(provider)
  185. self._os_instance = os_instance
  186. @property
  187. def id(self):
  188. """
  189. Get the instance identifier.
  190. """
  191. return self._os_instance.id
  192. @property
  193. def name(self):
  194. """
  195. Get the instance name.
  196. """
  197. return self._os_instance.name
  198. @name.setter
  199. # pylint:disable=arguments-differ
  200. def name(self, value):
  201. """
  202. Set the instance name.
  203. """
  204. self._os_instance.name = value
  205. self._os_instance.update()
  206. @property
  207. def public_ips(self):
  208. """
  209. Get all the public IP addresses for this instance.
  210. """
  211. # Openstack doesn't provide an easy way to figure our whether an ip is
  212. # public or private, since the returned ips are grouped by an arbitrary
  213. # network label. Therefore, it's necessary to parse the address and
  214. # determine whether it's public or private
  215. return [address
  216. for _, addresses in self._os_instance.networks.items()
  217. for address in addresses
  218. if not ipaddress.ip_address(address).is_private]
  219. @property
  220. def private_ips(self):
  221. """
  222. Get all the private IP addresses for this instance.
  223. """
  224. return [address
  225. for _, addresses in self._os_instance.networks.items()
  226. for address in addresses
  227. if ipaddress.ip_address(address).is_private]
  228. @property
  229. def instance_type(self):
  230. """
  231. Get the instance type.
  232. """
  233. flavor = self._provider.nova.flavors.get(
  234. self._os_instance.flavor.get('id'))
  235. return OpenStackInstanceType(self._provider, flavor)
  236. def reboot(self):
  237. """
  238. Reboot this instance (using the cloud middleware API).
  239. """
  240. self._os_instance.reboot()
  241. def terminate(self):
  242. """
  243. Permanently terminate this instance.
  244. """
  245. self._os_instance.delete()
  246. @property
  247. def image_id(self):
  248. """
  249. Get the image ID for this instance.
  250. """
  251. # In OpenStack, the Machine Image of a running instance may
  252. # be deleted, so make sure the image exists before attempting to
  253. # retrieve its id
  254. return (self._os_instance.image.get("id")
  255. if self._os_instance.image else "")
  256. @property
  257. def placement_zone(self):
  258. """
  259. Get the placement zone where this instance is running.
  260. """
  261. return OpenStackPlacementZone(
  262. self._provider,
  263. getattr(self._os_instance, 'OS-EXT-AZ:availability_zone', None),
  264. self._provider.region_name)
  265. @property
  266. def security_groups(self):
  267. """
  268. Get the security groups associated with this instance.
  269. """
  270. return [self._provider.security.security_groups.find(group['name'])[0]
  271. for group in self._os_instance.security_groups]
  272. @property
  273. def key_pair_name(self):
  274. """
  275. Get the name of the key pair associated with this instance.
  276. """
  277. return self._os_instance.key_name
  278. def create_image(self, name):
  279. """
  280. Create a new image based on this instance.
  281. """
  282. image_id = self._os_instance.create_image(name)
  283. return OpenStackMachineImage(
  284. self._provider, self._provider.compute.images.get(image_id))
  285. def add_floating_ip(self, ip_address):
  286. """
  287. Add a floating IP address to this instance.
  288. """
  289. self._os_instance.add_floating_ip(ip_address)
  290. def remove_floating_ip(self, ip_address):
  291. """
  292. Remove a floating IP address from this instance.
  293. """
  294. self._os_instance.remove_floating_ip(ip_address)
  295. @property
  296. def state(self):
  297. return OpenStackInstance.INSTANCE_STATE_MAP.get(
  298. self._os_instance.status, InstanceState.UNKNOWN)
  299. def refresh(self):
  300. """
  301. Refreshes the state of this instance by re-querying the cloud provider
  302. for its latest state.
  303. """
  304. instance = self._provider.compute.instances.get(
  305. self.id)
  306. if instance:
  307. # pylint:disable=protected-access
  308. self._os_instance = instance._os_instance
  309. else:
  310. # The instance no longer exists and cannot be refreshed.
  311. # set the status to unknown
  312. self._os_instance.status = 'unknown'
  313. class OpenStackRegion(BaseRegion):
  314. def __init__(self, provider, os_region):
  315. super(OpenStackRegion, self).__init__(provider)
  316. self._os_region = os_region
  317. @property
  318. def id(self):
  319. return self._os_region
  320. @property
  321. def name(self):
  322. return self._os_region
  323. @property
  324. def zones(self):
  325. # detailed must be set to ``False`` because the (default) ``True``
  326. # value requires Admin privileges
  327. if self.name == self._provider.region_name: # optimisation
  328. zones = self._provider.nova.availability_zones.list(detailed=False)
  329. else:
  330. try:
  331. region_nova = self._provider._connect_nova_region(self.name)
  332. zones = region_nova.availability_zones.list(detailed=False)
  333. except novaex.EndpointNotFound:
  334. # This region may not have a compute endpoint. If so just
  335. # return an empty list
  336. zones = []
  337. return [OpenStackPlacementZone(self._provider, z.zoneName,
  338. self._provider.region_name)
  339. for z in zones]
  340. class OpenStackVolume(BaseVolume):
  341. # Ref: http://developer.openstack.org/api-ref-blockstorage-v2.html
  342. VOLUME_STATE_MAP = {
  343. 'creating': VolumeState.CREATING,
  344. 'available': VolumeState.AVAILABLE,
  345. 'attaching': VolumeState.CONFIGURING,
  346. 'in-use': VolumeState.IN_USE,
  347. 'deleting': VolumeState.CONFIGURING,
  348. 'error': VolumeState.ERROR,
  349. 'error_deleting': VolumeState.ERROR,
  350. 'backing-up': VolumeState.CONFIGURING,
  351. 'restoring-backup': VolumeState.CONFIGURING,
  352. 'error_restoring': VolumeState.ERROR,
  353. 'error_extending': VolumeState.ERROR
  354. }
  355. def __init__(self, provider, volume):
  356. super(OpenStackVolume, self).__init__(provider)
  357. self._volume = volume
  358. @property
  359. def id(self):
  360. return self._volume.id
  361. @property
  362. def name(self):
  363. """
  364. Get the volume name.
  365. """
  366. return self._volume.name
  367. @name.setter
  368. def name(self, value): # pylint:disable=arguments-differ
  369. """
  370. Set the volume name.
  371. """
  372. self._volume.name = value
  373. self._volume.update(name=value)
  374. @property
  375. def description(self):
  376. return self._volume.description
  377. @description.setter
  378. def description(self, value):
  379. self._volume.description = value
  380. self._volume.update(description=value)
  381. @property
  382. def size(self):
  383. return self._volume.size
  384. @property
  385. def create_time(self):
  386. return self._volume.created_at
  387. @property
  388. def zone_id(self):
  389. return self._volume.availability_zone
  390. @property
  391. def source(self):
  392. if self._volume.snapshot_id:
  393. return self._provider.block_store.snapshots.get(
  394. self._volume.snapshot_id)
  395. return None
  396. @property
  397. def attachments(self):
  398. if self._volume.attachments:
  399. return BaseAttachmentInfo(
  400. self,
  401. self._volume.attachments[0].get('server_id'),
  402. self._volume.attachments[0].get('device'))
  403. else:
  404. return None
  405. def attach(self, instance, device):
  406. """
  407. Attach this volume to an instance.
  408. """
  409. instance_id = instance.id if isinstance(
  410. instance,
  411. OpenStackInstance) else instance
  412. self._volume.attach(instance_id, device)
  413. def detach(self, force=False):
  414. """
  415. Detach this volume from an instance.
  416. """
  417. self._volume.detach()
  418. def create_snapshot(self, name, description=None):
  419. """
  420. Create a snapshot of this Volume.
  421. """
  422. return self._provider.block_store.snapshots.create(
  423. name, self, description=description)
  424. def delete(self):
  425. """
  426. Delete this volume.
  427. """
  428. self._volume.delete()
  429. @property
  430. def state(self):
  431. return OpenStackVolume.VOLUME_STATE_MAP.get(
  432. self._volume.status, VolumeState.UNKNOWN)
  433. def refresh(self):
  434. """
  435. Refreshes the state of this volume by re-querying the cloud provider
  436. for its latest state.
  437. """
  438. vol = self._provider.block_store.volumes.get(
  439. self.id)
  440. if vol:
  441. self._volume = vol._volume # pylint:disable=protected-access
  442. else:
  443. # The volume no longer exists and cannot be refreshed.
  444. # set the status to unknown
  445. self._volume.status = 'unknown'
  446. class OpenStackSnapshot(BaseSnapshot):
  447. # Ref: http://developer.openstack.org/api-ref-blockstorage-v2.html
  448. SNAPSHOT_STATE_MAP = {
  449. 'creating': SnapshotState.PENDING,
  450. 'available': SnapshotState.AVAILABLE,
  451. 'deleting': SnapshotState.CONFIGURING,
  452. 'error': SnapshotState.ERROR,
  453. 'error_deleting': SnapshotState.ERROR
  454. }
  455. def __init__(self, provider, snapshot):
  456. super(OpenStackSnapshot, self).__init__(provider)
  457. self._snapshot = snapshot
  458. @property
  459. def id(self):
  460. return self._snapshot.id
  461. @property
  462. def name(self):
  463. """
  464. Get the snapshot name.
  465. """
  466. return self._snapshot.name
  467. @name.setter
  468. def name(self, value): # pylint:disable=arguments-differ
  469. """
  470. Set the snapshot name.
  471. """
  472. self._snapshot.name = value
  473. self._snapshot.update(name=value)
  474. @property
  475. def description(self):
  476. return self._snapshot.description
  477. @description.setter
  478. def description(self, value):
  479. self._snapshot.description = value
  480. self._snapshot.update(description=value)
  481. @property
  482. def size(self):
  483. return self._snapshot.size
  484. @property
  485. def volume_id(self):
  486. return self._snapshot.volume_id
  487. @property
  488. def create_time(self):
  489. return self._snapshot.created_at
  490. @property
  491. def state(self):
  492. return OpenStackSnapshot.SNAPSHOT_STATE_MAP.get(
  493. self._snapshot.status, SnapshotState.UNKNOWN)
  494. def refresh(self):
  495. """
  496. Refreshes the state of this snapshot by re-querying the cloud provider
  497. for its latest state.
  498. """
  499. snap = self._provider.block_store.snapshots.get(
  500. self.id)
  501. if snap:
  502. self._snapshot = snap._snapshot # pylint:disable=protected-access
  503. else:
  504. # The snapshot no longer exists and cannot be refreshed.
  505. # set the status to unknown
  506. self._snapshot.status = 'unknown'
  507. def delete(self):
  508. """
  509. Delete this snapshot.
  510. """
  511. self._snapshot.delete()
  512. def create_volume(self, placement, size=None, volume_type=None, iops=None):
  513. """
  514. Create a new Volume from this Snapshot.
  515. """
  516. vol_name = "Created from {0} ({1})".format(self.id, self.name)
  517. size = size if size else self._snapshot.size
  518. os_vol = self._provider.cinder.volumes.create(
  519. size, name=vol_name, availability_zone=placement,
  520. snapshot_id=self._snapshot.id)
  521. cb_vol = OpenStackVolume(self._provider, os_vol)
  522. cb_vol.name = vol_name
  523. return cb_vol
  524. class OpenStackNetwork(BaseNetwork):
  525. # Ref: https://github.com/openstack/neutron/blob/master/neutron/plugins/
  526. # common/constants.py
  527. _NETWORK_STATE_MAP = {
  528. 'PENDING_CREATE': NetworkState.PENDING,
  529. 'PENDING_UPDATE': NetworkState.PENDING,
  530. 'PENDING_DELETE': NetworkState.PENDING,
  531. 'CREATED': NetworkState.PENDING,
  532. 'INACTIVE': NetworkState.PENDING,
  533. 'DOWN': NetworkState.DOWN,
  534. 'ERROR': NetworkState.ERROR,
  535. 'ACTIVE': NetworkState.AVAILABLE
  536. }
  537. def __init__(self, provider, network):
  538. super(OpenStackNetwork, self).__init__(provider)
  539. self._network = network
  540. @property
  541. def id(self):
  542. return self._network.get('id', None)
  543. @property
  544. def name(self):
  545. return self._network.get('name', None)
  546. @property
  547. def state(self):
  548. return OpenStackNetwork._NETWORK_STATE_MAP.get(
  549. self._network.get('status', None),
  550. NetworkState.UNKNOWN)
  551. @property
  552. def cidr_block(self):
  553. # OpenStack does not define a CIDR block for networks
  554. return ''
  555. def delete(self):
  556. if self.id in str(self._provider.neutron.list_networks()):
  557. self._provider.neutron.delete_network(self.id)
  558. # Adhear to the interface docs
  559. if self.id not in str(self._provider.neutron.list_networks()):
  560. return True
  561. def subnets(self):
  562. subnets = (self._provider.neutron.list_subnets(network_id=self.id)
  563. .get('subnets', []))
  564. return [OpenStackSubnet(self._provider, subnet) for subnet in subnets]
  565. def create_subnet(self, cidr_block, name=''):
  566. subnet_info = {'name': name, 'network_id': self.id,
  567. 'cidr': cidr_block, 'ip_version': 4}
  568. subnet = (self._provider.neutron.create_subnet({'subnet': subnet_info})
  569. .get('subnet'))
  570. return OpenStackSubnet(self._provider, subnet)
  571. def refresh(self):
  572. """
  573. Refreshes the state of this network by re-querying the cloud provider
  574. for its latest state.
  575. """
  576. return self.state
  577. class OpenStackSubnet(BaseSubnet):
  578. def __init__(self, provider, subnet):
  579. super(OpenStackSubnet, self).__init__(provider)
  580. self._subnet = subnet
  581. @property
  582. def id(self):
  583. return self._subnet.get('id', None)
  584. @property
  585. def name(self):
  586. return self._subnet.get('name', None)
  587. @property
  588. def cidr_block(self):
  589. return self._subnet.get('cidr', None)
  590. @property
  591. def network_id(self):
  592. return self._subnet.get('network_id', None)
  593. def delete(self):
  594. if self.id in str(self._provider.neutron.list_subnets()):
  595. self._provider.neutron.delete_subnet(self.id)
  596. # Adhear to the interface docs
  597. if self.id not in str(self._provider.neutron.list_subnets()):
  598. return True
  599. class OpenStackKeyPair(BaseKeyPair):
  600. def __init__(self, provider, key_pair):
  601. super(OpenStackKeyPair, self).__init__(provider, key_pair)
  602. @property
  603. def material(self):
  604. """
  605. Unencrypted private key.
  606. :rtype: str
  607. :return: Unencrypted private key or ``None`` if not available.
  608. """
  609. return getattr(self._key_pair, 'private_key', None)
  610. class OpenStackSecurityGroup(BaseSecurityGroup):
  611. def __init__(self, provider, security_group):
  612. super(OpenStackSecurityGroup, self).__init__(provider, security_group)
  613. @property
  614. def rules(self):
  615. # Update SG object; otherwise, recenlty added rules do now show
  616. self._security_group = self._provider.nova.security_groups.get(
  617. self._security_group)
  618. return [OpenStackSecurityGroupRule(self._provider, r, self)
  619. for r in self._security_group.rules]
  620. def add_rule(self, ip_protocol=None, from_port=None, to_port=None,
  621. cidr_ip=None, src_group=None):
  622. """
  623. Create a security group rule.
  624. You need to pass in either ``src_group`` OR ``ip_protocol``,
  625. ``from_port``, ``to_port``, and ``cidr_ip``. In other words, either
  626. you are authorizing another group or you are authorizing some
  627. ip-based rule.
  628. :type ip_protocol: str
  629. :param ip_protocol: Either ``tcp`` | ``udp`` | ``icmp``
  630. :type from_port: int
  631. :param from_port: The beginning port number you are enabling
  632. :type to_port: int
  633. :param to_port: The ending port number you are enabling
  634. :type cidr_ip: str or list of strings
  635. :param cidr_ip: The CIDR block you are providing access to.
  636. :type src_group: ``object`` of :class:`.SecurityGroup`
  637. :param src_group: The Security Group you are granting access to.
  638. :rtype: bool
  639. :return: True if successful.
  640. """
  641. if src_group:
  642. for protocol in ['tcp', 'udp']:
  643. if self._provider.nova.security_group_rules.create(
  644. parent_group_id=self._security_group.id,
  645. ip_protocol=protocol,
  646. from_port=1,
  647. to_port=65535,
  648. group_id=src_group.id):
  649. return True
  650. else:
  651. if self._provider.nova.security_group_rules.create(
  652. parent_group_id=self._security_group.id,
  653. ip_protocol=ip_protocol,
  654. from_port=from_port,
  655. to_port=to_port,
  656. cidr=cidr_ip):
  657. return True
  658. def to_json(self):
  659. attr = inspect.getmembers(self, lambda a: not(inspect.isroutine(a)))
  660. js = {k: v for(k, v) in attr if not k.startswith('_')}
  661. json_rules = [r.to_json() for r in self.rules]
  662. js['rules'] = [json.loads(r) for r in json_rules]
  663. return json.dumps(js, sort_keys=True)
  664. class OpenStackSecurityGroupRule(BaseSecurityGroupRule):
  665. def __init__(self, provider, rule, parent):
  666. super(OpenStackSecurityGroupRule, self).__init__(
  667. provider, rule, parent)
  668. @property
  669. def ip_protocol(self):
  670. return self._rule.get('ip_protocol')
  671. @property
  672. def from_port(self):
  673. return self._rule.get('from_port')
  674. @property
  675. def to_port(self):
  676. return self._rule.get('to_port')
  677. @property
  678. def cidr_ip(self):
  679. return self._rule.get('ip_range', {}).get('cidr')
  680. @property
  681. def group(self):
  682. cg = self._rule.get('group', {}).get('name')
  683. if cg:
  684. security_groups = self._provider.nova.security_groups.list()
  685. for sg in security_groups:
  686. if sg.name == cg:
  687. return OpenStackSecurityGroup(self._provider, sg)
  688. return None
  689. def to_json(self):
  690. attr = inspect.getmembers(self, lambda a: not(inspect.isroutine(a)))
  691. js = {k: v for(k, v) in attr if not k.startswith('_')}
  692. js['group'] = self.group.id if self.group else ''
  693. js['parent'] = self.parent.id if self.parent else ''
  694. return json.dumps(js, sort_keys=True)
  695. class OpenStackBucketObject(BaseBucketObject):
  696. def __init__(self, provider, cbcontainer, obj):
  697. super(OpenStackBucketObject, self).__init__(provider)
  698. self.cbcontainer = cbcontainer
  699. self._obj = obj
  700. @property
  701. def id(self):
  702. return self._obj.get("name")
  703. @property
  704. def name(self):
  705. """
  706. Get this object's name.
  707. """
  708. return self._obj.get("name")
  709. def download(self, target_stream):
  710. """
  711. Download this object and write its
  712. contents to the target_stream.
  713. """
  714. _, content = self._provider.swift.get_object(
  715. self.cbcontainer.name, self.name, resp_chunk_size=65536)
  716. shutil.copyfileobj(content, target_stream)
  717. def upload(self, data):
  718. """
  719. Set the contents of this object to the data read from the source
  720. string.
  721. """
  722. self._provider.swift.put_object(self.cbcontainer.name, self.name,
  723. data)
  724. def delete(self):
  725. """
  726. Delete this object.
  727. :rtype: bool
  728. :return: True if successful
  729. """
  730. try:
  731. self._provider.swift.delete_object(self.cbcontainer.name,
  732. self.name)
  733. except swiftex.ClientException as err:
  734. if err.http_status == 404:
  735. return True
  736. return False
  737. class OpenStackBucket(BaseBucket):
  738. def __init__(self, provider, bucket):
  739. super(OpenStackBucket, self).__init__(provider)
  740. self._bucket = bucket
  741. @property
  742. def id(self):
  743. return self._bucket.get("name")
  744. @property
  745. def name(self):
  746. """
  747. Get this bucket's name.
  748. """
  749. return self._bucket.get("name")
  750. def get(self, key):
  751. """
  752. Retrieve a given object from this bucket.
  753. """
  754. _, object_list = self._provider.swift.get_container(
  755. self.name, prefix=key)
  756. if object_list:
  757. return OpenStackBucketObject(self._provider, self,
  758. object_list[0])
  759. else:
  760. return None
  761. def list(self, limit=None, marker=None):
  762. """
  763. List all objects within this bucket.
  764. :rtype: BucketObject
  765. :return: List of all available BucketObjects within this bucket.
  766. """
  767. _, object_list = self._provider.swift.get_container(
  768. self.name, limit=oshelpers.os_result_limit(self._provider, limit),
  769. marker=marker)
  770. cb_objects = [OpenStackBucketObject(
  771. self._provider, self, obj) for obj in object_list]
  772. return oshelpers.to_server_paged_list(
  773. self._provider,
  774. cb_objects,
  775. limit)
  776. def delete(self, delete_contents=False):
  777. """
  778. Delete this bucket.
  779. """
  780. self._provider.swift.delete_container(self.name)
  781. def create_object(self, object_name):
  782. self._provider.swift.put_object(self.name, object_name, None)
  783. return self.get(object_name)