services.py 30 KB

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