resources.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957
  1. """
  2. DataTypes used by this provider
  3. """
  4. from cloudbridge.cloud.base.resources import BaseAttachmentInfo
  5. from cloudbridge.cloud.base.resources import BaseBucket
  6. from cloudbridge.cloud.base.resources import BaseBucketObject
  7. from cloudbridge.cloud.base.resources import BaseInstance
  8. from cloudbridge.cloud.base.resources import BaseInstanceType
  9. from cloudbridge.cloud.base.resources import BaseKeyPair
  10. from cloudbridge.cloud.base.resources import BaseMachineImage
  11. from cloudbridge.cloud.base.resources import BaseNetwork
  12. from cloudbridge.cloud.base.resources import BasePlacementZone
  13. from cloudbridge.cloud.base.resources import BaseRegion
  14. from cloudbridge.cloud.base.resources import BaseSecurityGroup
  15. from cloudbridge.cloud.base.resources import BaseSecurityGroupRule
  16. from cloudbridge.cloud.base.resources import BaseSnapshot
  17. from cloudbridge.cloud.base.resources import BaseSubnet
  18. from cloudbridge.cloud.base.resources import BaseVolume
  19. from cloudbridge.cloud.base.resources import ClientPagedResultList
  20. from cloudbridge.cloud.interfaces.resources import InstanceState
  21. from cloudbridge.cloud.interfaces.resources import MachineImageState
  22. from cloudbridge.cloud.interfaces.resources import NetworkState
  23. from cloudbridge.cloud.interfaces.resources import SnapshotState
  24. from cloudbridge.cloud.interfaces.resources import VolumeState
  25. import hashlib
  26. import inspect
  27. import json
  28. from boto.exception import EC2ResponseError
  29. from boto.s3.key import Key
  30. from retrying import retry
  31. class AWSMachineImage(BaseMachineImage):
  32. IMAGE_STATE_MAP = {
  33. 'pending': MachineImageState.PENDING,
  34. 'available': MachineImageState.AVAILABLE,
  35. 'failed': MachineImageState.ERROR
  36. }
  37. def __init__(self, provider, image):
  38. super(AWSMachineImage, self).__init__(provider)
  39. if isinstance(image, AWSMachineImage):
  40. # pylint:disable=protected-access
  41. self._ec2_image = image._ec2_image
  42. else:
  43. self._ec2_image = image
  44. @property
  45. def id(self):
  46. """
  47. Get the image identifier.
  48. :rtype: ``str``
  49. :return: ID for this instance as returned by the cloud middleware.
  50. """
  51. return self._ec2_image.id
  52. @property
  53. def name(self):
  54. """
  55. Get the image name.
  56. :rtype: ``str``
  57. :return: Name for this image as returned by the cloud middleware.
  58. """
  59. return self._ec2_image.name
  60. @property
  61. def description(self):
  62. """
  63. Get the image description.
  64. :rtype: ``str``
  65. :return: Description for this image as returned by the cloud middleware
  66. """
  67. return self._ec2_image.description
  68. def delete(self):
  69. """
  70. Delete this image
  71. """
  72. self._ec2_image.deregister(delete_snapshot=True)
  73. @property
  74. def state(self):
  75. return AWSMachineImage.IMAGE_STATE_MAP.get(
  76. self._ec2_image.state, MachineImageState.UNKNOWN)
  77. def refresh(self):
  78. """
  79. Refreshes the state of this instance by re-querying the cloud provider
  80. for its latest state.
  81. """
  82. image = self._provider.compute.images.get(self.id)
  83. if image:
  84. # pylint:disable=protected-access
  85. self._ec2_image = image._ec2_image
  86. else:
  87. # image no longer exists
  88. self._ec2_image.state = "unknown"
  89. class AWSPlacementZone(BasePlacementZone):
  90. def __init__(self, provider, zone, region):
  91. super(AWSPlacementZone, self).__init__(provider)
  92. if isinstance(zone, AWSPlacementZone):
  93. # pylint:disable=protected-access
  94. self._aws_zone = zone._aws_zone
  95. self._aws_region = zone._aws_region
  96. else:
  97. self._aws_zone = zone
  98. self._aws_region = region
  99. @property
  100. def id(self):
  101. """
  102. Get the zone id
  103. :rtype: ``str``
  104. :return: ID for this zone as returned by the cloud middleware.
  105. """
  106. return self._aws_zone
  107. @property
  108. def name(self):
  109. """
  110. Get the zone name.
  111. :rtype: ``str``
  112. :return: Name for this zone as returned by the cloud middleware.
  113. """
  114. return self._aws_zone
  115. @property
  116. def region_name(self):
  117. """
  118. Get the region that this zone belongs to.
  119. :rtype: ``str``
  120. :return: Name of this zone's region as returned by the cloud middleware
  121. """
  122. return self._aws_region
  123. class AWSInstanceType(BaseInstanceType):
  124. def __init__(self, provider, instance_dict):
  125. super(AWSInstanceType, self).__init__(provider)
  126. self._inst_dict = instance_dict
  127. @property
  128. def id(self):
  129. return str(self._inst_dict['instance_type'])
  130. @property
  131. def name(self):
  132. return self._inst_dict['instance_type']
  133. @property
  134. def family(self):
  135. return self._inst_dict.get('family')
  136. @property
  137. def vcpus(self):
  138. return self._inst_dict.get('vCPU')
  139. @property
  140. def ram(self):
  141. return self._inst_dict.get('memory')
  142. @property
  143. def size_root_disk(self):
  144. return 0
  145. @property
  146. def size_ephemeral_disks(self):
  147. storage = self._inst_dict.get('storage')
  148. if storage:
  149. return storage.get('size') * storage.get("devices")
  150. else:
  151. return 0
  152. @property
  153. def num_ephemeral_disks(self):
  154. storage = self._inst_dict.get('storage')
  155. if storage:
  156. return storage.get("devices")
  157. else:
  158. return 0
  159. @property
  160. def extra_data(self):
  161. return {key: val for key, val in enumerate(self._inst_dict)
  162. if key not in ["instance_type", "family", "vCPU", "memory"]}
  163. class AWSInstance(BaseInstance):
  164. # ref:
  165. # http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-lifecycle.html
  166. INSTANCE_STATE_MAP = {
  167. 'pending': InstanceState.PENDING,
  168. 'running': InstanceState.RUNNING,
  169. 'shutting-down': InstanceState.CONFIGURING,
  170. 'terminated': InstanceState.TERMINATED,
  171. 'stopping': InstanceState.CONFIGURING,
  172. 'stopped': InstanceState.STOPPED
  173. }
  174. def __init__(self, provider, ec2_instance):
  175. super(AWSInstance, self).__init__(provider)
  176. self._ec2_instance = ec2_instance
  177. @property
  178. def id(self):
  179. """
  180. Get the instance identifier.
  181. """
  182. return self._ec2_instance.id
  183. @property
  184. def name(self):
  185. """
  186. Get the instance name.
  187. .. note:: an instance must have a (case sensitive) tag ``Name``
  188. """
  189. return self._ec2_instance.tags.get('Name')
  190. @name.setter
  191. # pylint:disable=arguments-differ
  192. def name(self, value):
  193. """
  194. Set the instance name.
  195. """
  196. self._ec2_instance.add_tag('Name', value)
  197. @property
  198. def public_ips(self):
  199. """
  200. Get all the public IP addresses for this instance.
  201. """
  202. return [self._ec2_instance.ip_address]
  203. @property
  204. def private_ips(self):
  205. """
  206. Get all the private IP addresses for this instance.
  207. """
  208. return [self._ec2_instance.private_ip_address]
  209. @property
  210. def instance_type_id(self):
  211. """
  212. Get the instance type name.
  213. """
  214. return self._ec2_instance.instance_type
  215. @property
  216. def instance_type(self):
  217. """
  218. Get the instance type.
  219. """
  220. return self._provider.compute.instance_types.find(
  221. name=self._ec2_instance.instance_type)[0]
  222. def reboot(self):
  223. """
  224. Reboot this instance (using the cloud middleware API).
  225. """
  226. self._ec2_instance.reboot()
  227. def terminate(self):
  228. """
  229. Permanently terminate this instance.
  230. """
  231. self._ec2_instance.terminate()
  232. @property
  233. def image_id(self):
  234. """
  235. Get the image ID for this insance.
  236. """
  237. return self._ec2_instance.image_id
  238. @property
  239. def zone_id(self):
  240. """
  241. Get the placement zone id where this instance is running.
  242. """
  243. return self._ec2_instance.placement
  244. @property
  245. def security_groups(self):
  246. """
  247. Get the security groups associated with this instance.
  248. """
  249. # boto instance.groups field returns a ``Group`` object so need to
  250. # convert that into a ``SecurityGroup`` object before creating a
  251. # cloudbridge SecurityGroup object
  252. return [self._provider.security.security_groups.get(group.id)
  253. for group in self._ec2_instance.groups]
  254. @property
  255. def security_group_ids(self):
  256. """
  257. Get the security groups IDs associated with this instance.
  258. """
  259. return [group.id for group in self._ec2_instance.groups]
  260. @property
  261. def key_pair_name(self):
  262. """
  263. Get the name of the key pair associated with this instance.
  264. """
  265. return self._ec2_instance.key_name
  266. def create_image(self, name):
  267. """
  268. Create a new image based on this instance.
  269. """
  270. image_id = self._ec2_instance.create_image(name)
  271. # Sometimes, the image takes a while to register, so retry a few times
  272. # if the image cannot be found
  273. retry_decorator = retry(retry_on_result=lambda result: result is None,
  274. stop_max_attempt_number=3, wait_fixed=1000)
  275. image = retry_decorator(self._provider.compute.images.get)(image_id)
  276. return image
  277. def add_floating_ip(self, ip_address):
  278. """
  279. Add an elastic IP address to this instance.
  280. """
  281. return self._ec2_instance.use_ip(ip_address)
  282. def remove_floating_ip(self, ip_address):
  283. """
  284. Remove a elastic IP address from this instance.
  285. """
  286. raise NotImplementedError(
  287. 'remove_floating_ip not implemented by this provider.')
  288. @property
  289. def state(self):
  290. return AWSInstance.INSTANCE_STATE_MAP.get(
  291. self._ec2_instance.state, InstanceState.UNKNOWN)
  292. def refresh(self):
  293. """
  294. Refreshes the state of this instance by re-querying the cloud provider
  295. for its latest state.
  296. """
  297. try:
  298. self._ec2_instance.update(validate=True)
  299. except (EC2ResponseError, ValueError):
  300. # The volume no longer exists and cannot be refreshed.
  301. # set the status to unknown
  302. self._ec2_instance.status = 'unknown'
  303. class AWSVolume(BaseVolume):
  304. # Ref:
  305. # http://docs.aws.amazon.com/AWSEC2/latest/CommandLineReference/
  306. # ApiReference-cmd-DescribeVolumes.html
  307. VOLUME_STATE_MAP = {
  308. 'creating': VolumeState.CREATING,
  309. 'available': VolumeState.AVAILABLE,
  310. 'in-use': VolumeState.IN_USE,
  311. 'deleting': VolumeState.CONFIGURING,
  312. 'deleted': VolumeState.DELETED,
  313. 'error': VolumeState.ERROR
  314. }
  315. def __init__(self, provider, volume):
  316. super(AWSVolume, self).__init__(provider)
  317. self._volume = volume
  318. @property
  319. def id(self):
  320. return self._volume.id
  321. @property
  322. def name(self):
  323. """
  324. Get the volume name.
  325. .. note:: an instance must have a (case sensitive) tag ``Name``
  326. """
  327. return self._volume.tags.get('Name')
  328. @name.setter
  329. # pylint:disable=arguments-differ
  330. def name(self, value):
  331. """
  332. Set the volume name.
  333. """
  334. self._volume.add_tag('Name', value)
  335. @property
  336. def description(self):
  337. return self._volume.tags.get('Description')
  338. @description.setter
  339. def description(self, value):
  340. self._volume.add_tag('Description', value)
  341. @property
  342. def size(self):
  343. return self._volume.size
  344. @property
  345. def create_time(self):
  346. return self._volume.create_time
  347. @property
  348. def zone_id(self):
  349. return self._volume.zone
  350. @property
  351. def source(self):
  352. if self._volume.snapshot_id:
  353. return self._provider.block_store.snapshots.get(
  354. self._volume.snapshot_id)
  355. return None
  356. @property
  357. def attachments(self):
  358. if self._volume.attach_data and self._volume.attach_data.id:
  359. return BaseAttachmentInfo(self,
  360. self._volume.attach_data.instance_id,
  361. self._volume.attach_data.device)
  362. else:
  363. return None
  364. def attach(self, instance, device):
  365. """
  366. Attach this volume to an instance.
  367. """
  368. instance_id = instance.id if isinstance(
  369. instance,
  370. AWSInstance) else instance
  371. self._volume.attach(instance_id, device)
  372. def detach(self, force=False):
  373. """
  374. Detach this volume from an instance.
  375. """
  376. self._volume.detach()
  377. def create_snapshot(self, name, description=None):
  378. """
  379. Create a snapshot of this Volume.
  380. """
  381. snap = AWSSnapshot(
  382. self._provider,
  383. self._volume.create_snapshot(
  384. description=description))
  385. snap.name = name
  386. return snap
  387. def delete(self):
  388. """
  389. Delete this volume.
  390. """
  391. self._volume.delete()
  392. @property
  393. def state(self):
  394. return AWSVolume.VOLUME_STATE_MAP.get(
  395. self._volume.status, VolumeState.UNKNOWN)
  396. def refresh(self):
  397. """
  398. Refreshes the state of this volume by re-querying the cloud provider
  399. for its latest state.
  400. """
  401. try:
  402. self._volume.update(validate=True)
  403. except (EC2ResponseError, ValueError):
  404. # The volume no longer exists and cannot be refreshed.
  405. # set the status to unknown
  406. self._volume.status = 'unknown'
  407. class AWSSnapshot(BaseSnapshot):
  408. # Ref: http://docs.aws.amazon.com/AWSEC2/latest/CommandLineReference/
  409. # ApiReference-cmd-DescribeSnapshots.html
  410. SNAPSHOT_STATE_MAP = {
  411. 'pending': SnapshotState.PENDING,
  412. 'completed': SnapshotState.AVAILABLE,
  413. 'error': SnapshotState.ERROR
  414. }
  415. def __init__(self, provider, snapshot):
  416. super(AWSSnapshot, self).__init__(provider)
  417. self._snapshot = snapshot
  418. @property
  419. def id(self):
  420. return self._snapshot.id
  421. @property
  422. def name(self):
  423. """
  424. Get the snapshot name.
  425. .. note:: an instance must have a (case sensitive) tag ``Name``
  426. """
  427. return self._snapshot.tags.get('Name')
  428. @name.setter
  429. # pylint:disable=arguments-differ
  430. def name(self, value):
  431. """
  432. Set the snapshot name.
  433. """
  434. self._snapshot.add_tag('Name', value)
  435. @property
  436. def description(self):
  437. return self._snapshot.tags.get('Description')
  438. @description.setter
  439. def description(self, value):
  440. self._snapshot.add_tag('Description', value)
  441. @property
  442. def size(self):
  443. return self._snapshot.volume_size
  444. @property
  445. def volume_id(self):
  446. return self._snapshot.volume_id
  447. @property
  448. def create_time(self):
  449. return self._snapshot.start_time
  450. @property
  451. def state(self):
  452. return AWSSnapshot.SNAPSHOT_STATE_MAP.get(
  453. self._snapshot.status, SnapshotState.UNKNOWN)
  454. def refresh(self):
  455. """
  456. Refreshes the state of this snapshot by re-querying the cloud provider
  457. for its latest state.
  458. """
  459. try:
  460. self._snapshot.update(validate=True)
  461. except (EC2ResponseError, ValueError):
  462. # The snapshot no longer exists and cannot be refreshed.
  463. # set the status to unknown
  464. self._snapshot.status = 'unknown'
  465. def delete(self):
  466. """
  467. Delete this snapshot.
  468. """
  469. self._snapshot.delete()
  470. def create_volume(self, placement, size=None, volume_type=None, iops=None):
  471. """
  472. Create a new Volume from this Snapshot.
  473. """
  474. ec2_vol = self._snapshot.create_volume(placement, size, volume_type,
  475. iops)
  476. cb_vol = AWSVolume(self._provider, ec2_vol)
  477. cb_vol.name = "Created from {0} ({1})".format(self.id, self.name)
  478. return cb_vol
  479. class AWSKeyPair(BaseKeyPair):
  480. def __init__(self, provider, key_pair):
  481. super(AWSKeyPair, self).__init__(provider, key_pair)
  482. @property
  483. def material(self):
  484. """
  485. Unencrypted private key.
  486. :rtype: str
  487. :return: Unencrypted private key or ``None`` if not available.
  488. """
  489. return self._key_pair.material
  490. class AWSSecurityGroup(BaseSecurityGroup):
  491. def __init__(self, provider, security_group):
  492. super(AWSSecurityGroup, self).__init__(provider, security_group)
  493. @property
  494. def rules(self):
  495. return [AWSSecurityGroupRule(self._provider, r, self)
  496. for r in self._security_group.rules]
  497. def add_rule(self, ip_protocol=None, from_port=None, to_port=None,
  498. cidr_ip=None, src_group=None):
  499. """
  500. Create a security group rule.
  501. You need to pass in either ``src_group`` OR ``ip_protocol``,
  502. ``from_port``, ``to_port``, and ``cidr_ip``. In other words, either
  503. you are authorizing another group or you are authorizing some
  504. ip-based rule.
  505. :type ip_protocol: str
  506. :param ip_protocol: Either ``tcp`` | ``udp`` | ``icmp``
  507. :type from_port: int
  508. :param from_port: The beginning port number you are enabling
  509. :type to_port: int
  510. :param to_port: The ending port number you are enabling
  511. :type cidr_ip: str or list of strings
  512. :param cidr_ip: The CIDR block you are providing access to.
  513. :type src_group: ``object`` of :class:`.SecurityGroup`
  514. :param src_group: The Security Group you are granting access to.
  515. :rtype: :class:``.SecurityGroupRule``
  516. :return: Rule object if successful or ``None``.
  517. """
  518. if self._security_group.authorize(
  519. ip_protocol=ip_protocol,
  520. from_port=from_port,
  521. to_port=to_port,
  522. cidr_ip=cidr_ip,
  523. # pylint:disable=protected-access
  524. src_group=src_group._security_group if src_group else None):
  525. return self.get_rule(ip_protocol, from_port, to_port, cidr_ip,
  526. src_group)
  527. return None
  528. def get_rule(self, ip_protocol=None, from_port=None, to_port=None,
  529. cidr_ip=None, src_group=None):
  530. for rule in self._security_group.rules:
  531. if (rule.ip_protocol == ip_protocol and
  532. rule.from_port == from_port and
  533. rule.to_port == to_port and
  534. rule.grants[0].cidr_ip == cidr_ip) or \
  535. (rule.grants[0].name == src_group.name if src_group and
  536. hasattr(rule.grants[0], 'name') else False):
  537. return AWSSecurityGroupRule(self._provider, rule, self)
  538. return None
  539. def to_json(self):
  540. attr = inspect.getmembers(self, lambda a: not(inspect.isroutine(a)))
  541. js = {k: v for(k, v) in attr if not k.startswith('_')}
  542. json_rules = [r.to_json() for r in self.rules]
  543. js['rules'] = [json.loads(r) for r in json_rules]
  544. return json.dumps(js, sort_keys=True)
  545. class AWSSecurityGroupRule(BaseSecurityGroupRule):
  546. def __init__(self, provider, rule, parent):
  547. super(AWSSecurityGroupRule, self).__init__(provider, rule, parent)
  548. @property
  549. def id(self):
  550. """
  551. AWS does not support rule IDs so compose one.
  552. """
  553. md5 = hashlib.md5()
  554. md5.update("{0}-{1}-{2}-{3}".format(
  555. self.ip_protocol, self.from_port, self.to_port, self.cidr_ip)
  556. .encode('ascii'))
  557. return md5.hexdigest()
  558. @property
  559. def ip_protocol(self):
  560. return self._rule.ip_protocol
  561. @property
  562. def from_port(self):
  563. if str(self._rule.from_port).isdigit():
  564. return int(self._rule.from_port)
  565. return 0
  566. @property
  567. def to_port(self):
  568. if str(self._rule.to_port).isdigit():
  569. return int(self._rule.to_port)
  570. return 0
  571. @property
  572. def cidr_ip(self):
  573. if len(self._rule.grants) > 0:
  574. return self._rule.grants[0].cidr_ip
  575. return None
  576. @property
  577. def group(self):
  578. if len(self._rule.grants) > 0:
  579. if self._rule.grants[0].name:
  580. cg = self._provider.ec2_conn.get_all_security_groups(
  581. groupnames=[self._rule.grants[0].name])[0]
  582. return AWSSecurityGroup(self._provider, cg)
  583. return None
  584. def to_json(self):
  585. attr = inspect.getmembers(self, lambda a: not(inspect.isroutine(a)))
  586. js = {k: v for(k, v) in attr if not k.startswith('_')}
  587. js['group'] = self.group.id if self.group else ''
  588. js['parent'] = self.parent.id if self.parent else ''
  589. return json.dumps(js, sort_keys=True)
  590. def delete(self):
  591. if self.group:
  592. # pylint:disable=protected-access
  593. self.parent._security_group.revoke(
  594. src_group=self.group._security_group)
  595. else:
  596. # pylint:disable=protected-access
  597. self.parent._security_group.revoke(self.ip_protocol,
  598. self.from_port,
  599. self.to_port,
  600. self.cidr_ip)
  601. class AWSBucketObject(BaseBucketObject):
  602. def __init__(self, provider, key):
  603. super(AWSBucketObject, self).__init__(provider)
  604. self._key = key
  605. @property
  606. def id(self):
  607. return self._key.name
  608. @property
  609. def name(self):
  610. """
  611. Get this object's name.
  612. """
  613. return self._key.name
  614. def iter_content(self):
  615. """
  616. Returns this object's content as an
  617. iterable.
  618. """
  619. return self._key
  620. def upload(self, data):
  621. """
  622. Set the contents of this object to the data read from the source
  623. string.
  624. """
  625. self._key.set_contents_from_string(data)
  626. def delete(self):
  627. """
  628. Delete this object.
  629. :rtype: bool
  630. :return: True if successful
  631. """
  632. self._key.delete()
  633. class AWSBucket(BaseBucket):
  634. def __init__(self, provider, bucket):
  635. super(AWSBucket, self).__init__(provider)
  636. self._bucket = bucket
  637. @property
  638. def id(self):
  639. return self._bucket.name
  640. @property
  641. def name(self):
  642. """
  643. Get this bucket's name.
  644. """
  645. return self._bucket.name
  646. def get(self, key):
  647. """
  648. Retrieve a given object from this bucket.
  649. """
  650. key = Key(self._bucket, key)
  651. if key.exists():
  652. return AWSBucketObject(self._provider, key)
  653. return None
  654. def list(self, limit=None, marker=None):
  655. """
  656. List all objects within this bucket.
  657. :rtype: BucketObject
  658. :return: List of all available BucketObjects within this bucket.
  659. """
  660. objects = [AWSBucketObject(self._provider, obj)
  661. for obj in self._bucket.list()]
  662. return ClientPagedResultList(self._provider, objects,
  663. limit=limit, marker=marker)
  664. def delete(self, delete_contents=False):
  665. """
  666. Delete this bucket.
  667. """
  668. self._bucket.delete()
  669. def create_object(self, name):
  670. key = Key(self._bucket, name)
  671. return AWSBucketObject(self._provider, key)
  672. class AWSRegion(BaseRegion):
  673. def __init__(self, provider, aws_region):
  674. super(AWSRegion, self).__init__(provider)
  675. self._aws_region = aws_region
  676. @property
  677. def id(self):
  678. return self._aws_region.name
  679. @property
  680. def name(self):
  681. return self._aws_region.name
  682. @property
  683. def zones(self):
  684. """
  685. Accesss information about placement zones within this region.
  686. """
  687. if self.name == self._provider.region_name: # optimisation
  688. zones = self._provider.ec2_conn.get_all_zones()
  689. return [AWSPlacementZone(self._provider, zone.name,
  690. self._provider.region_name)
  691. for zone in zones]
  692. else:
  693. region = [region for region in
  694. self._provider.ec2_conn.get_all_regions()
  695. if self.name == region.name][0]
  696. conn = self._provider._conect_ec2_region(region)
  697. zones = conn.get_all_zones()
  698. return [AWSPlacementZone(self._provider, zone.name, region.name)
  699. for zone in zones]
  700. class AWSNetwork(BaseNetwork):
  701. # Ref:
  702. # docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVpcs.html
  703. _NETWORK_STATE_MAP = {
  704. 'pending': NetworkState.PENDING,
  705. 'available': VolumeState.AVAILABLE,
  706. }
  707. def __init__(self, provider, network):
  708. super(AWSNetwork, self).__init__(provider)
  709. self._vpc = network
  710. @property
  711. def id(self):
  712. return self._vpc.id
  713. @property
  714. def name(self):
  715. """
  716. Get the network name.
  717. .. note:: the network must have a (case sensitive) tag ``Name``
  718. """
  719. return self._vpc.tags.get('Name')
  720. @name.setter
  721. # pylint:disable=arguments-differ
  722. def name(self, value):
  723. """
  724. Set the network name.
  725. """
  726. self._vpc.add_tag('Name', value)
  727. @property
  728. def state(self):
  729. return AWSNetwork._NETWORK_STATE_MAP.get(
  730. self._vpc.update(), NetworkState.UNKNOWN)
  731. @property
  732. def cidr_block(self):
  733. return self._vpc.cidr_block
  734. def delete(self):
  735. return self._vpc.delete()
  736. def subnets(self):
  737. flter = {'vpc-id': self.id}
  738. subnets = self._provider.vpc_conn.get_all_subnets(filters=flter)
  739. return [AWSSubnet(self._provider, subnet) for subnet in subnets]
  740. def create_subnet(self, cidr_block, name=None):
  741. subnet = self._provider.vpc_conn.create_subnet(self.id, cidr_block)
  742. cb_subnet = AWSSubnet(self._provider, subnet)
  743. if name:
  744. cb_subnet.name = name
  745. return cb_subnet
  746. def refresh(self):
  747. """
  748. Refreshes the state of this instance by re-querying the cloud provider
  749. for its latest state.
  750. """
  751. return self.state
  752. class AWSSubnet(BaseSubnet):
  753. def __init__(self, provider, subnet):
  754. super(AWSSubnet, self).__init__(provider)
  755. self._subnet = subnet
  756. @property
  757. def id(self):
  758. return self._subnet.id
  759. @property
  760. def name(self):
  761. """
  762. Get the subnet name.
  763. .. note:: the subnet must have a (case sensitive) tag ``Name``
  764. """
  765. return self._subnet.tags.get('Name')
  766. @name.setter
  767. # pylint:disable=arguments-differ
  768. def name(self, value):
  769. """
  770. Set the subnet name.
  771. """
  772. self._subnet.add_tag('Name', value)
  773. @property
  774. def cidr_block(self):
  775. return self._subnet.cidr_block
  776. @property
  777. def network_id(self):
  778. return self._subnet.vpc_id
  779. def delete(self):
  780. return self._provider.vpc_conn.delete_subnet(subnet_id=self.id)