services.py 35 KB

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