services.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809
  1. """Services implemented by the AWS provider."""
  2. import logging
  3. import string
  4. from botocore.exceptions import ClientError
  5. from cloudbridge.cloud.base.resources import ClientPagedResultList
  6. from cloudbridge.cloud.base.services import BaseBucketService
  7. from cloudbridge.cloud.base.services import BaseComputeService
  8. from cloudbridge.cloud.base.services import BaseFloatingIPService
  9. from cloudbridge.cloud.base.services import BaseGatewayService
  10. from cloudbridge.cloud.base.services import BaseImageService
  11. from cloudbridge.cloud.base.services import BaseInstanceService
  12. from cloudbridge.cloud.base.services import BaseKeyPairService
  13. from cloudbridge.cloud.base.services import BaseNetworkService
  14. from cloudbridge.cloud.base.services import BaseNetworkingService
  15. from cloudbridge.cloud.base.services import BaseRegionService
  16. from cloudbridge.cloud.base.services import BaseRouterService
  17. from cloudbridge.cloud.base.services import BaseSecurityService
  18. from cloudbridge.cloud.base.services import BaseSnapshotService
  19. from cloudbridge.cloud.base.services import BaseStorageService
  20. from cloudbridge.cloud.base.services import BaseSubnetService
  21. from cloudbridge.cloud.base.services import BaseVMFirewallService
  22. from cloudbridge.cloud.base.services import BaseVMTypeService
  23. from cloudbridge.cloud.base.services import BaseVolumeService
  24. from cloudbridge.cloud.interfaces.exceptions \
  25. import InvalidConfigurationException
  26. from cloudbridge.cloud.interfaces.resources import KeyPair
  27. from cloudbridge.cloud.interfaces.resources import MachineImage
  28. from cloudbridge.cloud.interfaces.resources import PlacementZone
  29. from cloudbridge.cloud.interfaces.resources import Snapshot
  30. from cloudbridge.cloud.interfaces.resources import VMFirewall
  31. from cloudbridge.cloud.interfaces.resources import VMType
  32. from cloudbridge.cloud.interfaces.resources import Volume
  33. import requests
  34. from .helpers import BotoEC2Service
  35. from .helpers import BotoS3Service
  36. from .resources import AWSBucket
  37. from .resources import AWSFloatingIP
  38. from .resources import AWSInstance
  39. from .resources import AWSInternetGateway
  40. from .resources import AWSKeyPair
  41. from .resources import AWSLaunchConfig
  42. from .resources import AWSMachineImage
  43. from .resources import AWSNetwork
  44. from .resources import AWSRegion
  45. from .resources import AWSRouter
  46. from .resources import AWSSnapshot
  47. from .resources import AWSSubnet
  48. from .resources import AWSVMFirewall
  49. from .resources import AWSVMType
  50. from .resources import AWSVolume
  51. log = logging.getLogger(__name__)
  52. class AWSSecurityService(BaseSecurityService):
  53. def __init__(self, provider):
  54. super(AWSSecurityService, self).__init__(provider)
  55. # Initialize provider services
  56. self._key_pairs = AWSKeyPairService(provider)
  57. self._vm_firewalls = AWSVMFirewallService(provider)
  58. @property
  59. def key_pairs(self):
  60. return self._key_pairs
  61. @property
  62. def vm_firewalls(self):
  63. return self._vm_firewalls
  64. class AWSKeyPairService(BaseKeyPairService):
  65. def __init__(self, provider):
  66. super(AWSKeyPairService, self).__init__(provider)
  67. self.svc = BotoEC2Service(provider=self.provider,
  68. cb_resource=AWSKeyPair,
  69. boto_collection_name='key_pairs')
  70. def get(self, key_pair_id):
  71. log.debug("Getting Key Pair Service %s", key_pair_id)
  72. return self.svc.get(key_pair_id)
  73. def list(self, limit=None, marker=None):
  74. return self.svc.list(limit=limit, marker=marker)
  75. def find(self, name, limit=None, marker=None):
  76. log.debug("Searching for Key Pair %s with the params "
  77. "[Limit: %s Marker: %s]", name, limit, marker)
  78. return self.svc.find(filter_name='key-name', filter_value=name,
  79. limit=limit, marker=marker)
  80. def create(self, name):
  81. log.debug("Creating Key Pair Service %s", name)
  82. AWSKeyPair.assert_valid_resource_name(name)
  83. return self.svc.create('create_key_pair', KeyName=name)
  84. class AWSVMFirewallService(BaseVMFirewallService):
  85. def __init__(self, provider):
  86. super(AWSVMFirewallService, self).__init__(provider)
  87. self.svc = BotoEC2Service(provider=self.provider,
  88. cb_resource=AWSVMFirewall,
  89. boto_collection_name='security_groups')
  90. def get(self, firewall_id):
  91. log.debug("Getting Firewall Service with the id: %s", firewall_id)
  92. return self.svc.get(firewall_id)
  93. def list(self, limit=None, marker=None):
  94. return self.svc.list(limit=limit, marker=marker)
  95. def create(self, name, description, network_id):
  96. log.debug("Creating Firewall Service with the parameters "
  97. "[name: %s id: %s description: %s]", name, network_id,
  98. description)
  99. AWSVMFirewall.assert_valid_resource_name(name)
  100. return self.svc.create('create_security_group', GroupName=name,
  101. Description=description, VpcId=network_id)
  102. def find(self, name, limit=None, marker=None):
  103. log.debug("Searching for Firewall Service %s with the params "
  104. "[Limit: %s Marker: %s]", name, limit, marker)
  105. return self.svc.find(filter_name='group-name', filter_value=name,
  106. limit=limit, marker=marker)
  107. def delete(self, firewall_id):
  108. log.info("Deleting Firewall Service with the id %s", firewall_id)
  109. firewall = self.svc.get(firewall_id)
  110. if firewall:
  111. firewall.delete()
  112. class AWSStorageService(BaseStorageService):
  113. def __init__(self, provider):
  114. super(AWSStorageService, self).__init__(provider)
  115. # Initialize provider services
  116. self._volume_svc = AWSVolumeService(self.provider)
  117. self._snapshot_svc = AWSSnapshotService(self.provider)
  118. self._bucket_svc = AWSBucketService(self.provider)
  119. @property
  120. def volumes(self):
  121. return self._volume_svc
  122. @property
  123. def snapshots(self):
  124. return self._snapshot_svc
  125. @property
  126. def buckets(self):
  127. return self._bucket_svc
  128. class AWSVolumeService(BaseVolumeService):
  129. def __init__(self, provider):
  130. super(AWSVolumeService, self).__init__(provider)
  131. self.svc = BotoEC2Service(provider=self.provider,
  132. cb_resource=AWSVolume,
  133. boto_collection_name='volumes')
  134. def get(self, volume_id):
  135. log.debug("Getting AWS Volume Service with the id: %s",
  136. volume_id)
  137. return self.svc.get(volume_id)
  138. def find(self, name, limit=None, marker=None):
  139. log.debug("Searching for AWS Volume Service %s with "
  140. "the params [Limit: %s Marker: %s]", name,
  141. limit, marker)
  142. return self.svc.find(filter_name='tag:Name', filter_value=name,
  143. limit=limit, marker=marker)
  144. def list(self, limit=None, marker=None):
  145. return self.svc.list(limit=limit, marker=marker)
  146. def create(self, name, size, zone, snapshot=None, description=None):
  147. log.debug("Creating AWS Volume Service with the parameters "
  148. "[name: %s size: %s zone: %s snapshot: %s "
  149. "description: %s]", name, size, zone, snapshot,
  150. description)
  151. AWSVolume.assert_valid_resource_name(name)
  152. zone_id = zone.id if isinstance(zone, PlacementZone) else zone
  153. snapshot_id = snapshot.id if isinstance(
  154. snapshot, AWSSnapshot) and snapshot else snapshot
  155. cb_vol = self.svc.create('create_volume', Size=size,
  156. AvailabilityZone=zone_id,
  157. SnapshotId=snapshot_id)
  158. # Wait until ready to tag instance
  159. cb_vol.wait_till_ready()
  160. cb_vol.name = name
  161. if description:
  162. cb_vol.description = description
  163. return cb_vol
  164. class AWSSnapshotService(BaseSnapshotService):
  165. def __init__(self, provider):
  166. super(AWSSnapshotService, self).__init__(provider)
  167. self.svc = BotoEC2Service(provider=self.provider,
  168. cb_resource=AWSSnapshot,
  169. boto_collection_name='snapshots')
  170. def get(self, snapshot_id):
  171. log.debug("Getting AWS Snapshot Service with the id: %s",
  172. snapshot_id)
  173. return self.svc.get(snapshot_id)
  174. def find(self, name, limit=None, marker=None):
  175. log.debug("Searching for AWS Snapshot Service %s with "
  176. " the params [Limit: %s Marker: %s]", name,
  177. limit, marker)
  178. return self.svc.find(filter_name='tag:Name', filter_value=name,
  179. limit=limit, marker=marker)
  180. def list(self, limit=None, marker=None):
  181. return self.svc.list(limit=limit, marker=marker)
  182. def create(self, name, volume, description=None):
  183. """
  184. Creates a new snapshot of a given volume.
  185. """
  186. log.debug("Creating a new AWS snapshot Service with the "
  187. "parameters [name: %s volume: %s description: %s]",
  188. name, volume, description)
  189. AWSSnapshot.assert_valid_resource_name(name)
  190. volume_id = volume.id if isinstance(volume, AWSVolume) else volume
  191. cb_snap = self.svc.create('create_snapshot', VolumeId=volume_id)
  192. # Wait until ready to tag instance
  193. cb_snap.wait_till_ready()
  194. cb_snap.name = name
  195. if cb_snap.description:
  196. cb_snap.description = description
  197. return cb_snap
  198. class AWSBucketService(BaseBucketService):
  199. def __init__(self, provider):
  200. super(AWSBucketService, self).__init__(provider)
  201. self.svc = BotoS3Service(provider=self.provider,
  202. cb_resource=AWSBucket,
  203. boto_collection_name='buckets')
  204. def get(self, bucket_id):
  205. """
  206. Returns a bucket given its ID. Returns ``None`` if the bucket
  207. does not exist.
  208. """
  209. log.debug("Getting AWS Bucket Service with the id: %s", bucket_id)
  210. try:
  211. # Make a call to make sure the bucket exists. There's an edge case
  212. # where a 403 response can occur when the bucket exists but the
  213. # user simply does not have permissions to access it. See below.
  214. self.provider.s3_conn.meta.client.head_bucket(Bucket=bucket_id)
  215. return AWSBucket(self.provider,
  216. self.provider.s3_conn.Bucket(bucket_id))
  217. except ClientError as e:
  218. # If 403, it means the bucket exists, but the user does not have
  219. # permissions to access the bucket. However, limited operations
  220. # may be permitted (with a session token for example), so return a
  221. # Bucket instance to allow further operations.
  222. # http://stackoverflow.com/questions/32331456/using-boto-upload-file-to-s3-
  223. # sub-folder-when-i-have-no-permissions-on-listing-fo
  224. if e.response['Error']['Code'] == 403:
  225. log.warning("AWS Bucket %s already exists but user doesn't "
  226. "have enough permissions to access the bucket",
  227. bucket_id)
  228. bucket = self.provider.s3_conn.get_bucket(bucket_id,
  229. validate=False)
  230. return AWSBucket(self.provider, bucket)
  231. # For all other responses, it's assumed that the bucket does not exist.
  232. return None
  233. def find(self, name, limit=None, marker=None):
  234. log.debug("Searching for AWS Bucket %s with the params "
  235. "[Limit: %s Marker: %s]", name, limit, marker)
  236. buckets = [bucket
  237. for bucket in self
  238. if name == bucket.name]
  239. return ClientPagedResultList(self.provider, buckets,
  240. limit=limit, marker=marker)
  241. def list(self, limit=None, marker=None):
  242. return self.svc.list(limit=limit, marker=marker)
  243. def create(self, name, location=None):
  244. log.debug("Creating AWS Bucket with the params "
  245. "[name: %s id: %s description: %s]", name, location)
  246. AWSBucket.assert_valid_resource_name(name)
  247. loc_constraint = location or self.provider.region_name
  248. # Due to an API issue in S3, specifying us-east-1 as a
  249. # LocationConstraint results in an InvalidLocationConstraint.
  250. # Therefore, it must be special-cased and omitted altogether.
  251. # See: https://github.com/boto/boto3/issues/125
  252. if loc_constraint == 'us-east-1':
  253. return self.svc.create('create_bucket', Bucket=name)
  254. else:
  255. return self.svc.create('create_bucket', Bucket=name,
  256. CreateBucketConfiguration={
  257. 'LocationConstraint': loc_constraint
  258. })
  259. class AWSImageService(BaseImageService):
  260. def __init__(self, provider):
  261. super(AWSImageService, self).__init__(provider)
  262. self.svc = BotoEC2Service(provider=self.provider,
  263. cb_resource=AWSMachineImage,
  264. boto_collection_name='images')
  265. def get(self, image_id):
  266. log.debug("Getting AWS Image Service with the id: %s", image_id)
  267. return self.svc.get(image_id)
  268. def find(self, name, limit=None, marker=None):
  269. log.debug("Searching for AWS Image Service %s with the params "
  270. "[Limit: %s Marker: %s]", name, limit, marker)
  271. return self.svc.find(filter_name='name', filter_value=name,
  272. limit=limit, marker=marker)
  273. def list(self, filter_by_owner=True, limit=None, marker=None):
  274. return self.svc.list(Owners=['self'] if filter_by_owner else [],
  275. limit=limit, marker=marker)
  276. class AWSComputeService(BaseComputeService):
  277. def __init__(self, provider):
  278. super(AWSComputeService, self).__init__(provider)
  279. self._vm_type_svc = AWSVMTypeService(self.provider)
  280. self._instance_svc = AWSInstanceService(self.provider)
  281. self._region_svc = AWSRegionService(self.provider)
  282. self._images_svc = AWSImageService(self.provider)
  283. @property
  284. def images(self):
  285. return self._images_svc
  286. @property
  287. def vm_types(self):
  288. return self._vm_type_svc
  289. @property
  290. def instances(self):
  291. return self._instance_svc
  292. @property
  293. def regions(self):
  294. return self._region_svc
  295. class AWSInstanceService(BaseInstanceService):
  296. def __init__(self, provider):
  297. super(AWSInstanceService, self).__init__(provider)
  298. self.svc = BotoEC2Service(provider=self.provider,
  299. cb_resource=AWSInstance,
  300. boto_collection_name='instances')
  301. def create(self, name, image, vm_type, subnet, zone=None,
  302. key_pair=None, vm_firewalls=None, user_data=None,
  303. launch_config=None, **kwargs):
  304. log.debug("Creating AWS Instance Service with the params "
  305. "[name: %s image: %s type: %s subnet: %s zone: %s "
  306. "key pair: %s firewalls: %s user data: %s config %s "
  307. "others: %s]", name, image, vm_type, subnet, zone,
  308. key_pair, vm_firewalls, user_data, launch_config, **kwargs)
  309. AWSInstance.assert_valid_resource_name(name)
  310. image_id = image.id if isinstance(image, MachineImage) else image
  311. vm_size = vm_type.id if \
  312. isinstance(vm_type, VMType) else vm_type
  313. subnet = (self.provider.networking.subnets.get(subnet)
  314. if isinstance(subnet, str) else subnet)
  315. zone_id = zone.id if isinstance(zone, PlacementZone) else zone
  316. key_pair_name = key_pair.name if isinstance(
  317. key_pair,
  318. KeyPair) else key_pair
  319. if launch_config:
  320. bdm = self._process_block_device_mappings(launch_config)
  321. else:
  322. bdm = None
  323. subnet_id, zone_id, vm_firewall_ids = \
  324. self._resolve_launch_options(subnet, zone_id, vm_firewalls)
  325. placement = {'AvailabilityZone': zone_id} if zone_id else None
  326. inst = self.svc.create('create_instances',
  327. ImageId=image_id,
  328. MinCount=1,
  329. MaxCount=1,
  330. KeyName=key_pair_name,
  331. SecurityGroupIds=vm_firewall_ids or None,
  332. UserData=str(user_data) or None,
  333. InstanceType=vm_size,
  334. Placement=placement,
  335. BlockDeviceMappings=bdm,
  336. SubnetId=subnet_id
  337. )
  338. if inst and len(inst) == 1:
  339. # Wait until the resource exists
  340. # pylint:disable=protected-access
  341. inst[0]._wait_till_exists()
  342. # Tag the instance w/ the name
  343. inst[0].name = name
  344. return inst[0]
  345. raise ValueError(
  346. 'Expected a single object response, got a list: %s' % inst)
  347. def _resolve_launch_options(self, subnet=None, zone_id=None,
  348. vm_firewalls=None):
  349. """
  350. Work out interdependent launch options.
  351. Some launch options are required and interdependent so make sure
  352. they conform to the interface contract.
  353. :type subnet: ``Subnet``
  354. :param subnet: Subnet object within which to launch.
  355. :type zone_id: ``str``
  356. :param zone_id: ID of the zone where the launch should happen.
  357. :type vm_firewalls: ``list`` of ``id``
  358. :param vm_firewalls: List of firewall IDs.
  359. :rtype: triplet of ``str``
  360. :return: Subnet ID, zone ID and VM firewall IDs for launch.
  361. :raise ValueError: In case a conflicting combination is found.
  362. """
  363. if subnet:
  364. # subnet's zone takes precedence
  365. zone_id = subnet.zone.id
  366. if isinstance(vm_firewalls, list) and isinstance(
  367. vm_firewalls[0], VMFirewall):
  368. vm_firewall_ids = [fw.id for fw in vm_firewalls]
  369. else:
  370. vm_firewall_ids = vm_firewalls
  371. return subnet.id, zone_id, vm_firewall_ids
  372. def _process_block_device_mappings(self, launch_config):
  373. """
  374. Processes block device mapping information
  375. and returns a Boto BlockDeviceMapping object. If new volumes
  376. are requested (source is None and destination is VOLUME), they will be
  377. created and the relevant volume ids included in the mapping.
  378. """
  379. bdml = []
  380. # Assign letters from f onwards
  381. # http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/device_naming.html
  382. next_letter = iter(list(string.ascii_lowercase[6:]))
  383. # assign ephemeral devices from 0 onwards
  384. ephemeral_counter = 0
  385. for device in launch_config.block_devices:
  386. bdm = {}
  387. if device.is_volume:
  388. # Generate the device path
  389. bdm['DeviceName'] = \
  390. '/dev/sd' + ('a1' if device.is_root else next(next_letter))
  391. ebs_def = {}
  392. if isinstance(device.source, Snapshot):
  393. ebs_def['SnapshotId'] = device.source.id
  394. elif isinstance(device.source, Volume):
  395. # TODO: We could create a snapshot from the volume
  396. # and use that instead.
  397. # Not supported
  398. pass
  399. elif isinstance(device.source, MachineImage):
  400. # Not supported
  401. pass
  402. else:
  403. # source is None, but destination is volume, therefore
  404. # create a blank volume. This requires a size though.
  405. if not device.size:
  406. raise InvalidConfigurationException(
  407. "The source is none and the destination is a"
  408. " volume. Therefore, you must specify a size.")
  409. ebs_def['DeleteOnTermination'] = device.delete_on_terminate
  410. if device.size:
  411. ebs_def['VolumeSize'] = device.size
  412. if ebs_def:
  413. bdm['Ebs'] = ebs_def
  414. else: # device is ephemeral
  415. bdm['VirtualName'] = 'ephemeral%s' % ephemeral_counter
  416. # Append the config
  417. bdml.append(bdm)
  418. return bdml
  419. def create_launch_config(self):
  420. return AWSLaunchConfig(self.provider)
  421. def get(self, instance_id):
  422. return self.svc.get(instance_id)
  423. def find(self, name, limit=None, marker=None):
  424. return self.svc.find(filter_name='tag:Name', filter_value=name,
  425. limit=limit, marker=marker)
  426. def list(self, limit=None, marker=None):
  427. return self.svc.list(limit=limit, marker=marker)
  428. class AWSVMTypeService(BaseVMTypeService):
  429. def __init__(self, provider):
  430. super(AWSVMTypeService, self).__init__(provider)
  431. @property
  432. def instance_data(self):
  433. """
  434. Fetch info about the available instances.
  435. To update this information, update the file pointed to by the
  436. ``provider.AWS_INSTANCE_DATA_DEFAULT_URL`` above. The content for this
  437. file should be obtained from this repo:
  438. https://github.com/powdahound/ec2instances.info (in particular, this
  439. file: https://raw.githubusercontent.com/powdahound/ec2instances.info/
  440. master/www/instances.json).
  441. TODO: Needs a caching function with timeout
  442. """
  443. r = requests.get(self.provider.config.get(
  444. "aws_instance_info_url",
  445. self.provider.AWS_INSTANCE_DATA_DEFAULT_URL))
  446. return r.json()
  447. def list(self, limit=None, marker=None):
  448. vm_types = [AWSVMType(self.provider, vm_type)
  449. for vm_type in self.instance_data]
  450. return ClientPagedResultList(self.provider, vm_types,
  451. limit=limit, marker=marker)
  452. class AWSRegionService(BaseRegionService):
  453. def __init__(self, provider):
  454. super(AWSRegionService, self).__init__(provider)
  455. def get(self, region_id):
  456. log.debug("Getting AWS Region Service with the id: %s",
  457. region_id)
  458. region = [r for r in self if r.id == region_id]
  459. if region:
  460. return region[0]
  461. else:
  462. return None
  463. def list(self, limit=None, marker=None):
  464. regions = [
  465. AWSRegion(self.provider, region) for region in
  466. self.provider.ec2_conn.meta.client.describe_regions()
  467. .get('Regions', [])]
  468. return ClientPagedResultList(self.provider, regions,
  469. limit=limit, marker=marker)
  470. @property
  471. def current(self):
  472. return self.get(self._provider.region_name)
  473. class AWSNetworkingService(BaseNetworkingService):
  474. def __init__(self, provider):
  475. super(AWSNetworkingService, self).__init__(provider)
  476. self._network_service = AWSNetworkService(self.provider)
  477. self._subnet_service = AWSSubnetService(self.provider)
  478. self._fip_service = AWSFloatingIPService(self.provider)
  479. self._router_service = AWSRouterService(self.provider)
  480. self._gateway_service = AWSGatewayService(self.provider)
  481. @property
  482. def networks(self):
  483. return self._network_service
  484. @property
  485. def subnets(self):
  486. return self._subnet_service
  487. @property
  488. def floating_ips(self):
  489. return self._fip_service
  490. @property
  491. def routers(self):
  492. return self._router_service
  493. @property
  494. def gateways(self):
  495. return self._gateway_service
  496. class AWSNetworkService(BaseNetworkService):
  497. def __init__(self, provider):
  498. super(AWSNetworkService, self).__init__(provider)
  499. self.svc = BotoEC2Service(provider=self.provider,
  500. cb_resource=AWSNetwork,
  501. boto_collection_name='vpcs')
  502. def get(self, network_id):
  503. log.debug("Getting AWS Network Service with the id: %s",
  504. network_id)
  505. return self.svc.get(network_id)
  506. def list(self, limit=None, marker=None):
  507. return self.svc.list(limit=limit, marker=marker)
  508. def find(self, name, limit=None, marker=None):
  509. log.debug("Searching for AWS Network Service %s with the "
  510. " params [Limit: %s Marker: %s]", name, limit, marker)
  511. return self.svc.find(filter_name='tag:Name', filter_value=name,
  512. limit=limit, marker=marker)
  513. def create(self, name, cidr_block):
  514. log.debug("Creating AWS Network Service with the params "
  515. "[name: %s block: %s]", name, cidr_block)
  516. AWSNetwork.assert_valid_resource_name(name)
  517. cb_net = self.svc.create('create_vpc', CidrBlock=cidr_block)
  518. # Wait until ready to tag instance
  519. cb_net.wait_till_ready()
  520. if name:
  521. cb_net.name = name
  522. return cb_net
  523. class AWSSubnetService(BaseSubnetService):
  524. def __init__(self, provider):
  525. super(AWSSubnetService, self).__init__(provider)
  526. self.svc = BotoEC2Service(provider=self.provider,
  527. cb_resource=AWSSubnet,
  528. boto_collection_name='subnets')
  529. def get(self, subnet_id):
  530. log.debug("Getting AWS Subnet Service with the id: %s", subnet_id)
  531. return self.svc.get(subnet_id)
  532. def list(self, network=None, limit=None, marker=None):
  533. network_id = network.id if isinstance(network, AWSNetwork) else network
  534. if network_id:
  535. return self.svc.find(
  536. filter_name='VpcId', filter_value=network_id,
  537. limit=limit, marker=marker)
  538. else:
  539. return self.svc.list(limit=limit, marker=marker)
  540. def find(self, name, limit=None, marker=None):
  541. log.debug("Searching for AWS Subnet Service %s with the params "
  542. "[Limit: %s Marker: %s]", name, limit, marker)
  543. return self.svc.find(filter_name='tag:Name', filter_value=name,
  544. limit=limit, marker=marker)
  545. def create(self, name, network, cidr_block, zone=None):
  546. log.debug("Creating AWS Subnet Service with the params "
  547. "[name: %s network: %s block: %s zone: %s]",
  548. name, network, cidr_block, zone)
  549. AWSSubnet.assert_valid_resource_name(name)
  550. network_id = network.id if isinstance(network, AWSNetwork) else network
  551. subnet = self.svc.create('create_subnet',
  552. VpcId=network_id,
  553. CidrBlock=cidr_block,
  554. AvailabilityZone=zone)
  555. if name:
  556. subnet.name = name
  557. return subnet
  558. def get_or_create_default(self, zone=None):
  559. if zone:
  560. snl = self.svc.find('availabilityZone', zone)
  561. else:
  562. snl = self.svc.list()
  563. for sn in snl:
  564. # pylint:disable=protected-access
  565. if sn._subnet.default_for_az:
  566. return sn
  567. # No provider-default Subnet exists, look for a library-default one
  568. for sn in snl:
  569. # pylint:disable=protected-access
  570. for tag in sn._subnet.tags or {}:
  571. if (tag.get('Key') == 'Name' and
  572. tag.get('Value') == AWSSubnet.CB_DEFAULT_SUBNET_NAME):
  573. return sn
  574. # No provider-default Subnet exists, try to create it (net + subnets)
  575. default_net = self.provider.networking.networks.create(
  576. name=AWSNetwork.CB_DEFAULT_NETWORK_NAME, cidr_block='10.0.0.0/16')
  577. # Create a subnet in each of the region's zones
  578. region = self.provider.compute.regions.get(self.provider.region_name)
  579. default_sn = None
  580. for i, z in enumerate(region.zones):
  581. sn = self.create(AWSSubnet.CB_DEFAULT_SUBNET_NAME, default_net,
  582. '10.0.{0}.0/24'.format(i), z.name)
  583. if zone and zone == z.name:
  584. default_sn = sn
  585. # No specific zone was supplied; return the last created subnet
  586. if not default_sn:
  587. default_sn = sn
  588. return default_sn
  589. def delete(self, subnet):
  590. log.debug("Deleting AWS Subnet Service: %s", subnet)
  591. subnet_id = subnet.id if isinstance(subnet, AWSSubnet) else subnet
  592. self.svc.delete(subnet_id)
  593. class AWSFloatingIPService(BaseFloatingIPService):
  594. def __init__(self, provider):
  595. super(AWSFloatingIPService, self).__init__(provider)
  596. self.svc = BotoEC2Service(provider=self.provider,
  597. cb_resource=AWSFloatingIP,
  598. boto_collection_name='vpc_addresses')
  599. def get(self, router_id):
  600. log.debug("Getting AWS Floating IP Service with the id: %s",
  601. router_id)
  602. return self.svc.get(router_id)
  603. def list(self, limit=None, marker=None):
  604. return self.svc.list(limit=limit, marker=marker)
  605. def create(self):
  606. ip = self.provider.ec2_conn.meta.client.allocate_address(
  607. Domain='vpc')
  608. return AWSFloatingIP(
  609. self.provider,
  610. self.provider.ec2_conn.VpcAddress(ip.get('AllocationId')))
  611. class AWSRouterService(BaseRouterService):
  612. """For AWS, a CloudBridge router corresponds to an AWS Route Table."""
  613. def __init__(self, provider):
  614. super(AWSRouterService, self).__init__(provider)
  615. self.svc = BotoEC2Service(provider=self.provider,
  616. cb_resource=AWSRouter,
  617. boto_collection_name='route_tables')
  618. def get(self, router_id):
  619. log.debug("Getting AWS Router Service with the id: %s", router_id)
  620. return self.svc.get(router_id)
  621. def find(self, name, limit=None, marker=None):
  622. log.debug("Searching for AWS Router Service %s with the params "
  623. "[Limit: %s Marker: %s]", name, limit, marker)
  624. return self.svc.find(filter_name='tag:Name', filter_value=name,
  625. limit=limit, marker=marker)
  626. def list(self, limit=None, marker=None):
  627. return self.svc.list(limit=limit, marker=marker)
  628. def create(self, name, network):
  629. log.debug("Creating AWS Router Service with the params "
  630. "[name: %s network: %s]", name, network)
  631. AWSRouter.assert_valid_resource_name(name)
  632. network_id = network.id if isinstance(network, AWSNetwork) else network
  633. cb_router = self.svc.create('create_route_table', VpcId=network_id)
  634. if name:
  635. cb_router.name = name
  636. return cb_router
  637. class AWSGatewayService(BaseGatewayService):
  638. def __init__(self, provider):
  639. super(AWSGatewayService, self).__init__(provider)
  640. self.svc = BotoEC2Service(provider=self.provider,
  641. cb_resource=AWSInternetGateway,
  642. boto_collection_name='internet_gateways')
  643. def get_or_create_inet_gateway(self, name):
  644. AWSInternetGateway.assert_valid_resource_name(name)
  645. cb_gateway = self.svc.create('create_internet_gateway')
  646. cb_gateway.name = name
  647. return cb_gateway
  648. def delete(self, gateway_id):
  649. log.debug("Deleting AWS Gateway Service with the id %s", gateway_id)
  650. gateway = self.svc.get(gateway_id)
  651. if gateway:
  652. gateway.delete()
  653. def list(self, limit=None, marker=None):
  654. log.debug("Listing current AWS internet gateways.")
  655. return self.svc.list(limit=None, marker=None)