services.py 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964
  1. """Services implemented by the AWS provider."""
  2. import string
  3. import time
  4. from boto.ec2.blockdevicemapping import BlockDeviceMapping
  5. from boto.ec2.blockdevicemapping import BlockDeviceType
  6. from boto.exception import EC2ResponseError, S3ResponseError
  7. from cloudbridge.cloud.base.resources import ClientPagedResultList
  8. from cloudbridge.cloud.base.resources import ServerPagedResultList
  9. from cloudbridge.cloud.base.services import BaseBlockStoreService
  10. from cloudbridge.cloud.base.services import BaseComputeService
  11. from cloudbridge.cloud.base.services import BaseGatewayService
  12. from cloudbridge.cloud.base.services import BaseImageService
  13. from cloudbridge.cloud.base.services import BaseInstanceService
  14. from cloudbridge.cloud.base.services import BaseInstanceTypesService
  15. from cloudbridge.cloud.base.services import BaseKeyPairService
  16. from cloudbridge.cloud.base.services import BaseNetworkService
  17. from cloudbridge.cloud.base.services import BaseNetworkingService
  18. from cloudbridge.cloud.base.services import BaseObjectStoreService
  19. from cloudbridge.cloud.base.services import BaseRegionService
  20. from cloudbridge.cloud.base.services import BaseRouterService
  21. from cloudbridge.cloud.base.services import BaseSecurityGroupService
  22. from cloudbridge.cloud.base.services import BaseSecurityService
  23. from cloudbridge.cloud.base.services import BaseSnapshotService
  24. from cloudbridge.cloud.base.services import BaseSubnetService
  25. from cloudbridge.cloud.base.services import BaseVolumeService
  26. from cloudbridge.cloud.interfaces.exceptions \
  27. import InvalidConfigurationException
  28. from cloudbridge.cloud.interfaces.resources import InstanceState
  29. from cloudbridge.cloud.interfaces.resources import InstanceType
  30. from cloudbridge.cloud.interfaces.resources import KeyPair
  31. from cloudbridge.cloud.interfaces.resources import MachineImage
  32. from cloudbridge.cloud.interfaces.resources import NetworkState
  33. from cloudbridge.cloud.interfaces.resources import PlacementZone
  34. from cloudbridge.cloud.interfaces.resources import SecurityGroup
  35. from cloudbridge.cloud.interfaces.resources import Snapshot
  36. from cloudbridge.cloud.interfaces.resources import SubnetState
  37. from cloudbridge.cloud.interfaces.resources import Volume
  38. import requests
  39. from .resources import AWSBucket
  40. from .resources import AWSFloatingIP
  41. from .resources import AWSInstance
  42. from .resources import AWSInstanceType
  43. from .resources import AWSInternetGateway
  44. from .resources import AWSKeyPair
  45. from .resources import AWSLaunchConfig
  46. from .resources import AWSMachineImage
  47. from .resources import AWSNetwork
  48. from .resources import AWSRegion
  49. from .resources import AWSRouter
  50. from .resources import AWSSecurityGroup
  51. from .resources import AWSSnapshot
  52. from .resources import AWSSubnet
  53. from .resources import AWSVolume
  54. # Uncomment to enable logging by default for this module
  55. # import cloudbridge as cb
  56. # cb.set_stream_logger(__name__)
  57. class AWSSecurityService(BaseSecurityService):
  58. def __init__(self, provider):
  59. super(AWSSecurityService, self).__init__(provider)
  60. # Initialize provider services
  61. self._key_pairs = AWSKeyPairService(provider)
  62. self._security_groups = AWSSecurityGroupService(provider)
  63. @property
  64. def key_pairs(self):
  65. """
  66. Provides access to key pairs for this provider.
  67. :rtype: ``object`` of :class:`.KeyPairService`
  68. :return: a KeyPairService object
  69. """
  70. return self._key_pairs
  71. @property
  72. def security_groups(self):
  73. """
  74. Provides access to security groups for this provider.
  75. :rtype: ``object`` of :class:`.SecurityGroupService`
  76. :return: a SecurityGroupService object
  77. """
  78. return self._security_groups
  79. class AWSKeyPairService(BaseKeyPairService):
  80. def __init__(self, provider):
  81. super(AWSKeyPairService, self).__init__(provider)
  82. def get(self, key_pair_id):
  83. """
  84. Returns a KeyPair given its ID.
  85. """
  86. try:
  87. kps = self.provider.ec2_conn.get_all_key_pairs(
  88. keynames=[key_pair_id])
  89. return AWSKeyPair(self.provider, kps[0])
  90. except EC2ResponseError as ec2e:
  91. if ec2e.code == 'InvalidKeyPair.NotFound':
  92. return None
  93. elif ec2e.code == 'InvalidParameterValue':
  94. return None
  95. else:
  96. raise ec2e
  97. def list(self, limit=None, marker=None):
  98. """
  99. List all key pairs associated with this account.
  100. :rtype: ``list`` of :class:`.KeyPair`
  101. :return: list of KeyPair objects
  102. """
  103. key_pairs = [AWSKeyPair(self.provider, kp)
  104. for kp in self.provider.ec2_conn.get_all_key_pairs()]
  105. return ClientPagedResultList(self.provider, key_pairs,
  106. limit=limit, marker=marker)
  107. def find(self, name, limit=None, marker=None):
  108. """
  109. Searches for a key pair by a given list of attributes.
  110. """
  111. try:
  112. key_pairs = [
  113. AWSKeyPair(self.provider, kp) for kp in
  114. self.provider.ec2_conn.get_all_key_pairs(keynames=[name])]
  115. return ClientPagedResultList(self.provider, key_pairs,
  116. limit=limit, marker=marker)
  117. except EC2ResponseError as ec2e:
  118. if ec2e.code == 'InvalidKeyPair.NotFound':
  119. return []
  120. elif ec2e.code == 'InvalidParameterValue':
  121. return []
  122. else:
  123. raise ec2e
  124. def create(self, name):
  125. """
  126. Create a new key pair or raise an exception if one already exists.
  127. :type name: str
  128. :param name: The name of the key pair to be created.
  129. :rtype: ``object`` of :class:`.KeyPair`
  130. :return: A key pair instance or ``None`` if one was not be created.
  131. """
  132. kp = self.provider.ec2_conn.create_key_pair(name)
  133. if kp:
  134. return AWSKeyPair(self.provider, kp)
  135. return None
  136. class AWSSecurityGroupService(BaseSecurityGroupService):
  137. def __init__(self, provider):
  138. super(AWSSecurityGroupService, self).__init__(provider)
  139. def get(self, sg_id):
  140. """
  141. Returns a SecurityGroup given its id.
  142. """
  143. try:
  144. sgs = self.provider.ec2_conn.get_all_security_groups(
  145. group_ids=[sg_id])
  146. return AWSSecurityGroup(self.provider, sgs[0]) if sgs else None
  147. except EC2ResponseError as ec2e:
  148. if ec2e.code == 'InvalidGroup.NotFound':
  149. return None
  150. elif ec2e.code == 'InvalidGroupId.Malformed':
  151. return None
  152. else:
  153. raise ec2e
  154. def list(self, limit=None, marker=None):
  155. """
  156. List all security groups associated with this account.
  157. :rtype: ``list`` of :class:`.SecurityGroup`
  158. :return: list of SecurityGroup objects
  159. """
  160. sgs = [AWSSecurityGroup(self.provider, sg)
  161. for sg in self.provider.ec2_conn.get_all_security_groups()]
  162. return ClientPagedResultList(self.provider, sgs,
  163. limit=limit, marker=marker)
  164. def create(self, name, description, network_id):
  165. """
  166. Create a new SecurityGroup.
  167. :type name: str
  168. :param name: The name of the new security group.
  169. :type description: str
  170. :param description: The description of the new security group.
  171. :type network_id: ``str``
  172. :param network_id: The ID of the VPC under which to create the security
  173. group.
  174. :rtype: ``object`` of :class:`.SecurityGroup`
  175. :return: A SecurityGroup instance or ``None`` if one was not created.
  176. """
  177. sg = self.provider.ec2_conn.create_security_group(name, description,
  178. network_id)
  179. if sg:
  180. return AWSSecurityGroup(self.provider, sg)
  181. return None
  182. def find(self, name, limit=None, marker=None):
  183. """
  184. Get all security groups associated with your account.
  185. """
  186. flters = {'group-name': name}
  187. ec2_sgs = self.provider.ec2_conn.get_all_security_groups(
  188. filters=flters)
  189. sgs = [AWSSecurityGroup(self.provider, sg) for sg in ec2_sgs]
  190. return ClientPagedResultList(self.provider, sgs,
  191. limit=limit, marker=marker)
  192. def delete(self, group_id):
  193. """
  194. Delete an existing SecurityGroup.
  195. :type group_id: str
  196. :param group_id: The security group ID to be deleted.
  197. :rtype: ``bool``
  198. :return: ``True`` if the security group does not exist, ``False``
  199. otherwise. Note that this implies that the group may not have
  200. been deleted by this method but instead has not existed in
  201. the first place.
  202. """
  203. sg = self.get(group_id)
  204. if sg:
  205. sg.delete()
  206. return True
  207. return False
  208. class AWSBlockStoreService(BaseBlockStoreService):
  209. def __init__(self, provider):
  210. super(AWSBlockStoreService, self).__init__(provider)
  211. # Initialize provider services
  212. self._volume_svc = AWSVolumeService(self.provider)
  213. self._snapshot_svc = AWSSnapshotService(self.provider)
  214. @property
  215. def volumes(self):
  216. return self._volume_svc
  217. @property
  218. def snapshots(self):
  219. return self._snapshot_svc
  220. class AWSVolumeService(BaseVolumeService):
  221. def __init__(self, provider):
  222. super(AWSVolumeService, self).__init__(provider)
  223. def get(self, volume_id):
  224. """
  225. Returns a volume given its id.
  226. """
  227. try:
  228. vols = self.provider.ec2_conn.get_all_volumes(
  229. volume_ids=[volume_id])
  230. return AWSVolume(self.provider, vols[0]) if vols else None
  231. except EC2ResponseError as ec2e:
  232. if ec2e.code == 'InvalidVolume.NotFound':
  233. return None
  234. elif ec2e.code == 'InvalidParameterValue':
  235. # Occurs if volume_id does not start with 'vol-...'
  236. return None
  237. raise ec2e
  238. def find(self, name, limit=None, marker=None):
  239. """
  240. Searches for a volume by a given list of attributes.
  241. """
  242. filtr = {'tag:Name': name}
  243. aws_vols = self.provider.ec2_conn.get_all_volumes(filters=filtr)
  244. cb_vols = [AWSVolume(self.provider, vol) for vol in aws_vols]
  245. return ClientPagedResultList(self.provider, cb_vols,
  246. limit=limit, marker=marker)
  247. def list(self, limit=None, marker=None):
  248. """
  249. List all volumes.
  250. """
  251. aws_vols = self.provider.ec2_conn.get_all_volumes()
  252. cb_vols = [AWSVolume(self.provider, vol) for vol in aws_vols]
  253. return ClientPagedResultList(self.provider, cb_vols,
  254. limit=limit, marker=marker)
  255. def create(self, name, size, zone, snapshot=None, description=None):
  256. """
  257. Creates a new volume.
  258. """
  259. zone_id = zone.id if isinstance(zone, PlacementZone) else zone
  260. snapshot_id = snapshot.id if isinstance(
  261. snapshot, AWSSnapshot) and snapshot else snapshot
  262. ec2_vol = self.provider.ec2_conn.create_volume(
  263. size,
  264. zone_id,
  265. snapshot=snapshot_id)
  266. cb_vol = AWSVolume(self.provider, ec2_vol)
  267. cb_vol.name = name
  268. if description:
  269. cb_vol.description = description
  270. return cb_vol
  271. class AWSSnapshotService(BaseSnapshotService):
  272. def __init__(self, provider):
  273. super(AWSSnapshotService, self).__init__(provider)
  274. def get(self, snapshot_id):
  275. """
  276. Returns a snapshot given its id.
  277. """
  278. try:
  279. snaps = self.provider.ec2_conn.get_all_snapshots(
  280. snapshot_ids=[snapshot_id])
  281. return AWSSnapshot(self.provider, snaps[0]) if snaps else None
  282. except EC2ResponseError as ec2e:
  283. if ec2e.code == 'InvalidSnapshot.NotFound':
  284. return None
  285. elif ec2e.code == 'InvalidParameterValue':
  286. # Occurs if snapshot_id does not start with 'snap-...'
  287. return None
  288. raise ec2e
  289. def find(self, name, limit=None, marker=None):
  290. """
  291. Searches for a snapshot by a given list of attributes.
  292. """
  293. filtr = {'tag-value': name}
  294. snaps = [AWSSnapshot(self.provider, snap) for snap in
  295. self.provider.ec2_conn.get_all_snapshots(filters=filtr)]
  296. return ClientPagedResultList(self.provider, snaps,
  297. limit=limit, marker=marker)
  298. def list(self, limit=None, marker=None):
  299. """
  300. List all snapshots.
  301. """
  302. snaps = [AWSSnapshot(self.provider, snap)
  303. for snap in self.provider.ec2_conn.get_all_snapshots(
  304. owner='self')]
  305. return ClientPagedResultList(self.provider, snaps,
  306. limit=limit, marker=marker)
  307. def create(self, name, volume, description=None):
  308. """
  309. Creates a new snapshot of a given volume.
  310. """
  311. volume_id = volume.id if isinstance(volume, AWSVolume) else volume
  312. ec2_snap = self.provider.ec2_conn.create_snapshot(
  313. volume_id,
  314. description=description)
  315. cb_snap = AWSSnapshot(self.provider, ec2_snap)
  316. cb_snap.name = name
  317. if description:
  318. cb_snap.description = description
  319. return cb_snap
  320. class AWSObjectStoreService(BaseObjectStoreService):
  321. def __init__(self, provider):
  322. super(AWSObjectStoreService, self).__init__(provider)
  323. def get(self, bucket_id):
  324. """
  325. Returns a bucket given its ID. Returns ``None`` if the bucket
  326. does not exist.
  327. """
  328. try:
  329. # Make a call to make sure the bucket exists. While this would
  330. # normally return a Bucket instance, there's an edge case where a
  331. # 403 response can occur when the bucket exists but the
  332. # user simply does not have permissions to access it. See below.
  333. bucket = self.provider.s3_conn.get_bucket(bucket_id)
  334. return AWSBucket(self.provider, bucket)
  335. except S3ResponseError as e:
  336. # If 403, it means the bucket exists, but the user does not have
  337. # permissions to access the bucket. However, limited operations
  338. # may be permitted (with a session token for example), so return a
  339. # Bucket instance to allow further operations.
  340. # http://stackoverflow.com/questions/32331456/using-boto-upload-file-to-s3-
  341. # sub-folder-when-i-have-no-permissions-on-listing-fo
  342. if e.status == 403:
  343. bucket = self.provider.s3_conn.get_bucket(bucket_id,
  344. validate=False)
  345. return AWSBucket(self.provider, bucket)
  346. # For all other responses, it's assumed that the bucket does not exist.
  347. return None
  348. def find(self, name, limit=None, marker=None):
  349. """
  350. Searches for a bucket by a given list of attributes.
  351. """
  352. buckets = [AWSBucket(self.provider, bucket)
  353. for bucket in self.provider.s3_conn.get_all_buckets()
  354. if name in bucket.name]
  355. return ClientPagedResultList(self.provider, buckets,
  356. limit=limit, marker=marker)
  357. def list(self, limit=None, marker=None):
  358. """
  359. List all containers.
  360. """
  361. buckets = [AWSBucket(self.provider, bucket)
  362. for bucket in self.provider.s3_conn.get_all_buckets()]
  363. return ClientPagedResultList(self.provider, buckets,
  364. limit=limit, marker=marker)
  365. def create(self, name, location=None):
  366. """
  367. Create a new bucket.
  368. """
  369. bucket = self.provider.s3_conn.create_bucket(
  370. name,
  371. location=location if location else '')
  372. return AWSBucket(self.provider, bucket)
  373. class AWSImageService(BaseImageService):
  374. def __init__(self, provider):
  375. super(AWSImageService, self).__init__(provider)
  376. def get(self, image_id):
  377. """
  378. Returns an Image given its id
  379. """
  380. try:
  381. image = self.provider.ec2_conn.get_image(image_id)
  382. return AWSMachineImage(self.provider, image) if image else None
  383. except EC2ResponseError as ec2e:
  384. if ec2e.code == 'InvalidAMIID.NotFound':
  385. return None
  386. elif ec2e.code == 'InvalidAMIID.Malformed':
  387. # Occurs if image_id does not start with 'ami-...'
  388. return None
  389. raise ec2e
  390. def find(self, name, limit=None, marker=None):
  391. """
  392. Searches for an image by a given list of attributes
  393. """
  394. filters = {'name': name}
  395. images = [AWSMachineImage(self.provider, image) for image in
  396. self.provider.ec2_conn.get_all_images(filters=filters)]
  397. return ClientPagedResultList(self.provider, images,
  398. limit=limit, marker=marker)
  399. def list(self, limit=None, marker=None):
  400. """
  401. List all images.
  402. """
  403. images = [AWSMachineImage(self.provider, image)
  404. for image in self.provider.ec2_conn.get_all_images()]
  405. return ClientPagedResultList(self.provider, images,
  406. limit=limit, marker=marker)
  407. class AWSComputeService(BaseComputeService):
  408. def __init__(self, provider):
  409. super(AWSComputeService, self).__init__(provider)
  410. self._instance_type_svc = AWSInstanceTypesService(self.provider)
  411. self._instance_svc = AWSInstanceService(self.provider)
  412. self._region_svc = AWSRegionService(self.provider)
  413. self._images_svc = AWSImageService(self.provider)
  414. @property
  415. def images(self):
  416. return self._images_svc
  417. @property
  418. def instance_types(self):
  419. return self._instance_type_svc
  420. @property
  421. def instances(self):
  422. return self._instance_svc
  423. @property
  424. def regions(self):
  425. return self._region_svc
  426. class AWSInstanceService(BaseInstanceService):
  427. def __init__(self, provider):
  428. super(AWSInstanceService, self).__init__(provider)
  429. def create(self, name, image, instance_type, subnet, zone=None,
  430. key_pair=None, security_groups=None, user_data=None,
  431. launch_config=None, **kwargs):
  432. image_id = image.id if isinstance(image, MachineImage) else image
  433. instance_size = instance_type.id if \
  434. isinstance(instance_type, InstanceType) else instance_type
  435. subnet = (self.provider.network.subnets.get(subnet)
  436. if isinstance(subnet, str) else subnet)
  437. zone_id = zone.id if isinstance(zone, PlacementZone) else zone
  438. key_pair_name = key_pair.name if isinstance(
  439. key_pair,
  440. KeyPair) else key_pair
  441. if launch_config:
  442. bdm = self._process_block_device_mappings(launch_config, zone_id)
  443. else:
  444. bdm = None
  445. subnet_id, zone_id, security_group_ids = \
  446. self._resolve_launch_options(subnet, zone_id, security_groups)
  447. reservation = self.provider.ec2_conn.run_instances(
  448. image_id=image_id, instance_type=instance_size,
  449. min_count=1, max_count=1, placement=zone_id,
  450. key_name=key_pair_name, security_group_ids=security_group_ids,
  451. user_data=user_data, block_device_map=bdm, subnet_id=subnet_id)
  452. instance = None
  453. if reservation:
  454. instance = AWSInstance(self.provider, reservation.instances[0])
  455. instance.wait_for(
  456. [InstanceState.PENDING, InstanceState.RUNNING],
  457. terminal_states=[InstanceState.TERMINATED,
  458. InstanceState.ERROR])
  459. instance.name = name
  460. return instance
  461. def _resolve_launch_options(self, subnet=None, zone_id=None,
  462. security_groups=None):
  463. """
  464. Work out interdependent launch options.
  465. Some launch options are required and interdependent so make sure
  466. they conform to the interface contract.
  467. :type subnet: ``Subnet``
  468. :param subnet: Subnet object within which to launch.
  469. :type zone_id: ``str``
  470. :param zone_id: ID of the zone where the launch should happen.
  471. :type security_groups: ``list`` of ``id``
  472. :param zone_id: List of security group IDs.
  473. :rtype: triplet of ``str``
  474. :return: Subnet ID, zone ID and security group IDs for launch.
  475. :raise ValueError: In case a conflicting combination is found.
  476. """
  477. if subnet:
  478. # subnet's zone takes precedence
  479. zone_id = subnet.zone.id
  480. if isinstance(security_groups, list) and isinstance(
  481. security_groups[0], SecurityGroup):
  482. security_group_ids = [sg.id for sg in security_groups]
  483. else:
  484. security_group_ids = security_groups
  485. return subnet.id, zone_id, security_group_ids
  486. def _process_block_device_mappings(self, launch_config, zone=None):
  487. """
  488. Processes block device mapping information
  489. and returns a Boto BlockDeviceMapping object. If new volumes
  490. are requested (source is None and destination is VOLUME), they will be
  491. created and the relevant volume ids included in the mapping.
  492. """
  493. bdm = BlockDeviceMapping()
  494. # Assign letters from f onwards
  495. # http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/device_naming.html
  496. next_letter = iter(list(string.ascii_lowercase[6:]))
  497. # assign ephemeral devices from 0 onwards
  498. ephemeral_counter = 0
  499. for device in launch_config.block_devices:
  500. bd_type = BlockDeviceType()
  501. if device.is_volume:
  502. if device.is_root:
  503. bdm['/dev/sda1'] = bd_type
  504. else:
  505. bdm['sd' + next(next_letter)] = bd_type
  506. if isinstance(device.source, Snapshot):
  507. bd_type.snapshot_id = device.source.id
  508. elif isinstance(device.source, Volume):
  509. bd_type.volume_id = device.source.id
  510. elif isinstance(device.source, MachineImage):
  511. # Not supported
  512. pass
  513. else:
  514. # source is None, but destination is volume, therefore
  515. # create a blank volume. If the Zone is None, this
  516. # could fail since the volume and instance may be created
  517. # in two different zones.
  518. if not zone:
  519. raise InvalidConfigurationException(
  520. "A zone must be specified when launching with a"
  521. " new blank volume block device mapping.")
  522. new_vol = self.provider.block_store.volumes.create(
  523. '',
  524. device.size,
  525. zone)
  526. bd_type.volume_id = new_vol.id
  527. bd_type.delete_on_terminate = device.delete_on_terminate
  528. if device.size:
  529. bd_type.size = device.size
  530. else: # device is ephemeral
  531. bd_type.ephemeral_name = 'ephemeral%s' % ephemeral_counter
  532. return bdm
  533. def create_launch_config(self):
  534. return AWSLaunchConfig(self.provider)
  535. def get(self, instance_id):
  536. """
  537. Returns an instance given its id. Returns None
  538. if the object does not exist.
  539. """
  540. try:
  541. reservation = self.provider.ec2_conn.get_all_reservations(
  542. instance_ids=[instance_id])
  543. return (AWSInstance(self.provider, reservation[0].instances[0])
  544. if reservation else None)
  545. except EC2ResponseError as ec2e:
  546. if ec2e.code == 'InvalidInstanceID.NotFound':
  547. return None
  548. elif ec2e.code == 'InvalidParameterValue':
  549. # Occurs if id does not start with 'inst-...'
  550. return None
  551. raise ec2e
  552. def find(self, name, limit=None, marker=None):
  553. """
  554. Searches for an instance by a given list of attributes.
  555. :rtype: ``object`` of :class:`.Instance`
  556. :return: an Instance object
  557. """
  558. filtr = {'tag:Name': name}
  559. reservations = self.provider.ec2_conn.get_all_reservations(
  560. filters=filtr,
  561. max_results=limit,
  562. next_token=marker)
  563. instances = [AWSInstance(self.provider, inst)
  564. for res in reservations
  565. for inst in res.instances]
  566. return ServerPagedResultList(reservations.is_truncated,
  567. reservations.next_token,
  568. False, data=instances)
  569. def list(self, limit=None, marker=None):
  570. """
  571. List all instances.
  572. """
  573. reservations = self.provider.ec2_conn.get_all_reservations(
  574. max_results=limit,
  575. next_token=marker)
  576. instances = [AWSInstance(self.provider, inst)
  577. for res in reservations
  578. for inst in res.instances]
  579. return ServerPagedResultList(reservations.is_truncated,
  580. reservations.next_token,
  581. False, data=instances)
  582. class AWSInstanceTypesService(BaseInstanceTypesService):
  583. def __init__(self, provider):
  584. super(AWSInstanceTypesService, self).__init__(provider)
  585. @property
  586. def instance_data(self):
  587. """
  588. Fetch info about the available instances.
  589. To update this information, update the file pointed to by the
  590. ``provider.AWS_INSTANCE_DATA_DEFAULT_URL`` above. The content for this
  591. file should be obtained from this repo:
  592. https://github.com/powdahound/ec2instances.info (in particular, this
  593. file: https://raw.githubusercontent.com/powdahound/ec2instances.info/
  594. master/www/instances.json).
  595. TODO: Needs a caching function with timeout
  596. """
  597. r = requests.get(self.provider.config.get(
  598. "aws_instance_info_url",
  599. self.provider.AWS_INSTANCE_DATA_DEFAULT_URL))
  600. return r.json()
  601. def list(self, limit=None, marker=None):
  602. inst_types = [AWSInstanceType(self.provider, inst_type)
  603. for inst_type in self.instance_data]
  604. return ClientPagedResultList(self.provider, inst_types,
  605. limit=limit, marker=marker)
  606. class AWSRegionService(BaseRegionService):
  607. def __init__(self, provider):
  608. super(AWSRegionService, self).__init__(provider)
  609. def get(self, region_id):
  610. region = [r for r in self if r.id == region_id]
  611. if region:
  612. return region[0]
  613. else:
  614. return None
  615. def list(self, limit=None, marker=None):
  616. regions = [AWSRegion(self.provider, region)
  617. for region in self.provider.ec2_conn.get_all_regions()]
  618. return ClientPagedResultList(self.provider, regions,
  619. limit=limit, marker=marker)
  620. @property
  621. def current(self):
  622. return self.get(self._provider.region_name)
  623. class AWSNetworkingService(BaseNetworkingService):
  624. def __init__(self, provider):
  625. super(AWSNetworkingService, self).__init__(provider)
  626. self._network_service = AWSNetworkService(self.provider)
  627. self._subnet_service = AWSSubnetService(self.provider)
  628. self._router_service = AWSRouterService(self.provider)
  629. self._gateway_service = AWSGatewayService(self.provider)
  630. @property
  631. def networks(self):
  632. return self._network_service
  633. @property
  634. def subnets(self):
  635. return self._subnet_service
  636. @property
  637. def routers(self):
  638. return self._router_service
  639. @property
  640. def gateways(self):
  641. return self._gateway_service
  642. class AWSNetworkService(BaseNetworkService):
  643. def __init__(self, provider):
  644. super(AWSNetworkService, self).__init__(provider)
  645. self._subnet_svc = AWSSubnetService(self.provider)
  646. def get(self, network_id):
  647. try:
  648. network = self.provider.vpc_conn.get_all_vpcs(vpc_ids=[network_id])
  649. return AWSNetwork(self.provider, network[0]) if network else None
  650. except EC2ResponseError as ec2e:
  651. if ec2e.code == 'InvalidVpcID.NotFound':
  652. return None
  653. elif ec2e.code == 'InvalidParameterValue':
  654. # Occurs if id does not start with 'vpc-...'
  655. return None
  656. raise ec2e
  657. def list(self, limit=None, marker=None):
  658. networks = [AWSNetwork(self.provider, network)
  659. for network in self.provider.vpc_conn.get_all_vpcs()]
  660. return ClientPagedResultList(self.provider, networks,
  661. limit=limit, marker=marker)
  662. def find(self, name, limit=None, marker=None):
  663. filtr = {'tag:Name': name}
  664. networks = [AWSNetwork(self.provider, network)
  665. for network in self.provider.vpc_conn.get_all_vpcs(
  666. filters=filtr)]
  667. return ClientPagedResultList(self.provider, networks,
  668. limit=limit, marker=marker)
  669. def create(self, name, cidr_block):
  670. network = self.provider.vpc_conn.create_vpc(cidr_block=cidr_block)
  671. cb_network = AWSNetwork(self.provider, network)
  672. if name:
  673. cb_network.wait_for(
  674. [NetworkState.PENDING, NetworkState.AVAILABLE],
  675. terminal_states=[NetworkState.ERROR])
  676. cb_network.name = name
  677. return cb_network
  678. @property
  679. def subnets(self):
  680. return self._subnet_svc
  681. @property
  682. def floating_ips(self):
  683. # fltrs = None
  684. # if network_id:
  685. # fltrs = {'network-interface-id': network_id}
  686. al = self.provider.vpc_conn.get_all_addresses()
  687. return [AWSFloatingIP(self.provider, a) for a in al]
  688. def create_floating_ip(self):
  689. ip = self.provider.ec2_conn.allocate_address(domain='vpc')
  690. return AWSFloatingIP(self.provider, ip)
  691. def routers(self):
  692. routers = self.provider.vpc_conn.get_all_internet_gateways()
  693. return [AWSRouter(self.provider, r) for r in routers]
  694. def create_router(self, name=None):
  695. router = self.provider.vpc_conn.create_internet_gateway()
  696. cb_router = AWSRouter(self.provider, router)
  697. if name:
  698. time.sleep(2) # Some time is required
  699. cb_router.name = name
  700. return cb_router
  701. class AWSSubnetService(BaseSubnetService):
  702. def __init__(self, provider):
  703. super(AWSSubnetService, self).__init__(provider)
  704. def get(self, subnet_id):
  705. try:
  706. subnets = self.provider.vpc_conn.get_all_subnets([subnet_id])
  707. return AWSSubnet(self.provider, subnets[0]) if subnets else None
  708. except EC2ResponseError as ec2e:
  709. if ec2e.code == 'InvalidSubnetID.NotFound':
  710. return None
  711. elif ec2e.code == 'InvalidParameterValue':
  712. # Occurs if id does not start with 'subnet-...'
  713. return None
  714. raise ec2e
  715. def list(self, network=None, limit=None, marker=None):
  716. fltr = None
  717. if network:
  718. network_id = (network.id if isinstance(network, AWSNetwork) else
  719. network)
  720. fltr = {'vpc-id': network_id}
  721. subnets = [AWSSubnet(self.provider, subnet) for subnet in
  722. self.provider.vpc_conn.get_all_subnets(filters=fltr)]
  723. return ClientPagedResultList(self.provider, subnets,
  724. limit=limit, marker=marker)
  725. def create(self, name, network, cidr_block, zone=None):
  726. network_id = network.id if isinstance(network, AWSNetwork) else network
  727. subnet = self.provider.vpc_conn.create_subnet(network_id, cidr_block,
  728. availability_zone=zone)
  729. cb_subnet = AWSSubnet(self.provider, subnet)
  730. if name:
  731. cb_subnet.wait_for(
  732. [SubnetState.PENDING, SubnetState.AVAILABLE],
  733. terminal_states=[SubnetState.ERROR])
  734. cb_subnet.name = name
  735. return cb_subnet
  736. def get_or_create_default(self, zone=None):
  737. filtr = {'availabilityZone': zone} if zone else None
  738. sns = self.provider.vpc_conn.get_all_subnets(filters=filtr)
  739. for sn in sns:
  740. if sn.defaultForAz:
  741. return AWSSubnet(self.provider, sn)
  742. # No provider-default Subnet exists, look for a library-default one
  743. for sn in sns:
  744. if sn.tags.get('Name') == AWSSubnet.CB_DEFAULT_SUBNET_NAME:
  745. return AWSSubnet(self.provider, sn)
  746. # No provider-default Subnet exists, try to create it (net + subnets)
  747. default_net = self.provider.network.create(
  748. name=AWSNetwork.CB_DEFAULT_NETWORK_NAME, cidr_block='10.0.0.0/16')
  749. # Create a subnet in each of the region's zones
  750. region = self.provider.compute.regions.get(
  751. self.provider.vpc_conn.region.name)
  752. default_sn = None
  753. for i, z in enumerate(region.zones):
  754. sn = self.create(AWSSubnet.CB_DEFAULT_SUBNET_NAME, default_net,
  755. '10.0.{0}.0/24'.format(i), z.name)
  756. if zone and zone == z.name:
  757. default_sn = sn
  758. # No specific zone was supplied; return the last created subnet
  759. if not default_sn:
  760. default_sn = sn
  761. return default_sn
  762. def delete(self, subnet):
  763. subnet_id = subnet.id if isinstance(subnet, AWSSubnet) else subnet
  764. return self.provider.vpc_conn.delete_subnet(subnet_id=subnet_id)
  765. class AWSRouterService(BaseRouterService):
  766. """For AWS, a CloudBridge router corresponds to an AWS Route Table."""
  767. def __init__(self, provider):
  768. super(AWSRouterService, self).__init__(provider)
  769. def get(self, router_id):
  770. try:
  771. routers = self.provider.vpc_conn.get_all_route_tables([router_id])
  772. return AWSRouter(self.provider, routers[0]) if routers else None
  773. except EC2ResponseError as ec2e:
  774. if ec2e.code == 'InvalidRouteTableID.NotFound':
  775. return None
  776. elif ec2e.code == 'InvalidParameterValue':
  777. # Occurs if id does not start with 'rtb-...'
  778. return None
  779. raise ec2e
  780. def find(self, name, limit=None, marker=None):
  781. filtr = {'tag:Name': name}
  782. routers = self.provider.vpc_conn.get_all_route_tables(filters=filtr)
  783. aws_routers = [AWSRouter(self.provider, r) for r in routers]
  784. return ClientPagedResultList(self.provider, aws_routers, limit=limit,
  785. marker=marker)
  786. def list(self, limit=None, marker=None):
  787. routers = self.provider.vpc_conn.get_all_route_tables()
  788. aws_routers = [AWSRouter(self.provider, r) for r in routers]
  789. return ClientPagedResultList(self.provider, aws_routers, limit=limit,
  790. marker=marker)
  791. def create(self, name, network):
  792. network_id = network.id if isinstance(network, AWSNetwork) else network
  793. router = self.provider.vpc_conn.create_route_table(vpc_id=network_id)
  794. cb_router = AWSRouter(self.provider, router)
  795. if name:
  796. time.sleep(2) # Some time is required
  797. cb_router.name = name
  798. return cb_router
  799. class AWSGatewayService(BaseGatewayService):
  800. def __init__(self, provider):
  801. super(AWSGatewayService, self).__init__(provider)
  802. def get_or_create_inet_gateway(self, name):
  803. gateway = self.provider.vpc_conn.create_internet_gateway()
  804. cb_gateway = AWSInternetGateway(self.provider, gateway)
  805. cb_gateway.wait_till_ready()
  806. cb_gateway.name = name
  807. return cb_gateway
  808. def delete(self, gateway):
  809. gateway.delete()