resources.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937
  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: bool
  516. :return: True if successful.
  517. """
  518. return 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. def to_json(self):
  526. attr = inspect.getmembers(self, lambda a: not(inspect.isroutine(a)))
  527. js = {k: v for(k, v) in attr if not k.startswith('_')}
  528. json_rules = [r.to_json() for r in self.rules]
  529. js['rules'] = [json.loads(r) for r in json_rules]
  530. return json.dumps(js, sort_keys=True)
  531. class AWSSecurityGroupRule(BaseSecurityGroupRule):
  532. def __init__(self, provider, rule, parent):
  533. super(AWSSecurityGroupRule, self).__init__(provider, rule, parent)
  534. @property
  535. def id(self):
  536. """
  537. AWS does not support rule IDs so compose one.
  538. """
  539. md5 = hashlib.md5()
  540. md5.update("{0}-{1}-{2}-{3}".format(
  541. self.ip_protocol, self.from_port, self.to_port, self.cidr_ip))
  542. return md5.hexdigest()
  543. @property
  544. def ip_protocol(self):
  545. return self._rule.ip_protocol
  546. @property
  547. def from_port(self):
  548. return self._rule.from_port
  549. @property
  550. def to_port(self):
  551. return self._rule.to_port
  552. @property
  553. def cidr_ip(self):
  554. if len(self._rule.grants) > 0:
  555. return self._rule.grants[0].cidr_ip
  556. return None
  557. @property
  558. def group(self):
  559. if len(self._rule.grants) > 0:
  560. if self._rule.grants[0].name:
  561. cg = self._provider.ec2_conn.get_all_security_groups(
  562. groupnames=[self._rule.grants[0].name])[0]
  563. return AWSSecurityGroup(self._provider, cg)
  564. return None
  565. def to_json(self):
  566. attr = inspect.getmembers(self, lambda a: not(inspect.isroutine(a)))
  567. js = {k: v for(k, v) in attr if not k.startswith('_')}
  568. js['group'] = self.group.id if self.group else ''
  569. js['parent'] = self.parent.id if self.parent else ''
  570. return json.dumps(js, sort_keys=True)
  571. def delete(self):
  572. if self.group:
  573. # pylint:disable=protected-access
  574. self.parent._security_group.revoke(
  575. src_group=self.group._security_group)
  576. else:
  577. # pylint:disable=protected-access
  578. self.parent._security_group.revoke(self.ip_protocol,
  579. self.from_port,
  580. self.to_port,
  581. self.cidr_ip)
  582. class AWSBucketObject(BaseBucketObject):
  583. def __init__(self, provider, key):
  584. super(AWSBucketObject, self).__init__(provider)
  585. self._key = key
  586. @property
  587. def id(self):
  588. return self._key.name
  589. @property
  590. def name(self):
  591. """
  592. Get this object's name.
  593. """
  594. return self._key.name
  595. def iter_content(self):
  596. """
  597. Returns this object's content as an
  598. iterable.
  599. """
  600. return self._key
  601. def upload(self, data):
  602. """
  603. Set the contents of this object to the data read from the source
  604. string.
  605. """
  606. self._key.set_contents_from_string(data)
  607. def delete(self):
  608. """
  609. Delete this object.
  610. :rtype: bool
  611. :return: True if successful
  612. """
  613. self._key.delete()
  614. class AWSBucket(BaseBucket):
  615. def __init__(self, provider, bucket):
  616. super(AWSBucket, self).__init__(provider)
  617. self._bucket = bucket
  618. @property
  619. def id(self):
  620. return self._bucket.name
  621. @property
  622. def name(self):
  623. """
  624. Get this bucket's name.
  625. """
  626. return self._bucket.name
  627. def get(self, key):
  628. """
  629. Retrieve a given object from this bucket.
  630. """
  631. key = Key(self._bucket, key)
  632. if key.exists():
  633. return AWSBucketObject(self._provider, key)
  634. return None
  635. def list(self, limit=None, marker=None):
  636. """
  637. List all objects within this bucket.
  638. :rtype: BucketObject
  639. :return: List of all available BucketObjects within this bucket.
  640. """
  641. objects = [AWSBucketObject(self._provider, obj)
  642. for obj in self._bucket.list()]
  643. return ClientPagedResultList(self._provider, objects,
  644. limit=limit, marker=marker)
  645. def delete(self, delete_contents=False):
  646. """
  647. Delete this bucket.
  648. """
  649. self._bucket.delete()
  650. def create_object(self, name):
  651. key = Key(self._bucket, name)
  652. return AWSBucketObject(self._provider, key)
  653. class AWSRegion(BaseRegion):
  654. def __init__(self, provider, aws_region):
  655. super(AWSRegion, self).__init__(provider)
  656. self._aws_region = aws_region
  657. @property
  658. def id(self):
  659. return self._aws_region.name
  660. @property
  661. def name(self):
  662. return self._aws_region.name
  663. @property
  664. def zones(self):
  665. """
  666. Accesss information about placement zones within this region.
  667. """
  668. if self.name == self._provider.region_name: # optimisation
  669. zones = self._provider.ec2_conn.get_all_zones()
  670. return [AWSPlacementZone(self._provider, zone.name,
  671. self._provider.region_name)
  672. for zone in zones]
  673. else:
  674. region = [region for region in
  675. self._provider.ec2_conn.get_all_regions()
  676. if self.name == region.name][0]
  677. conn = self._provider._conect_ec2_region(region)
  678. zones = conn.get_all_zones()
  679. return [AWSPlacementZone(self._provider, zone.name, region.name)
  680. for zone in zones]
  681. class AWSNetwork(BaseNetwork):
  682. # Ref:
  683. # docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVpcs.html
  684. _NETWORK_STATE_MAP = {
  685. 'pending': NetworkState.PENDING,
  686. 'available': VolumeState.AVAILABLE,
  687. }
  688. def __init__(self, provider, network):
  689. super(AWSNetwork, self).__init__(provider)
  690. self._vpc = network
  691. @property
  692. def id(self):
  693. return self._vpc.id
  694. @property
  695. def name(self):
  696. """
  697. Get the network name.
  698. .. note:: the network must have a (case sensitive) tag ``Name``
  699. """
  700. return self._vpc.tags.get('Name')
  701. @name.setter
  702. # pylint:disable=arguments-differ
  703. def name(self, value):
  704. """
  705. Set the network name.
  706. """
  707. self._vpc.add_tag('Name', value)
  708. @property
  709. def state(self):
  710. return AWSNetwork._NETWORK_STATE_MAP.get(
  711. self._vpc.update(), NetworkState.UNKNOWN)
  712. @property
  713. def cidr_block(self):
  714. return self._vpc.cidr_block
  715. def delete(self):
  716. return self._vpc.delete()
  717. def subnets(self):
  718. flter = {'vpc-id': self.id}
  719. subnets = self._provider.vpc_conn.get_all_subnets(filters=flter)
  720. return [AWSSubnet(self._provider, subnet) for subnet in subnets]
  721. def create_subnet(self, cidr_block, name=None):
  722. subnet = self._provider.vpc_conn.create_subnet(self.id, cidr_block)
  723. cb_subnet = AWSSubnet(self._provider, subnet)
  724. if name:
  725. cb_subnet.name = name
  726. return cb_subnet
  727. def refresh(self):
  728. """
  729. Refreshes the state of this instance by re-querying the cloud provider
  730. for its latest state.
  731. """
  732. return self.state
  733. class AWSSubnet(BaseSubnet):
  734. def __init__(self, provider, subnet):
  735. super(AWSSubnet, self).__init__(provider)
  736. self._subnet = subnet
  737. @property
  738. def id(self):
  739. return self._subnet.id
  740. @property
  741. def name(self):
  742. """
  743. Get the subnet name.
  744. .. note:: the subnet must have a (case sensitive) tag ``Name``
  745. """
  746. return self._subnet.tags.get('Name')
  747. @name.setter
  748. # pylint:disable=arguments-differ
  749. def name(self, value):
  750. """
  751. Set the subnet name.
  752. """
  753. self._subnet.add_tag('Name', value)
  754. @property
  755. def cidr_block(self):
  756. return self._subnet.cidr_block
  757. @property
  758. def network_id(self):
  759. return self._subnet.vpc_id
  760. def delete(self):
  761. return self._provider.vpc_conn.delete_subnet(subnet_id=self.id)