resources.py 28 KB

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