4 # Copyright 2016 RIFT.IO Inc
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
10 # http://www.apache.org/licenses/LICENSE-2.0
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
21 from . import aws_table
22 from . import exceptions
25 logger
= logging
.getLogger('rwcal.aws.drv')
26 logger
.setLevel(logging
.DEBUG
)
28 class AWSDriver(object):
32 def __init__(self
, key
, secret
, region
,ssh_key
=None,vpcid
= None,availability_zone
= None,default_subnet_id
= None):
34 Constructor for AWSDriver
36 key : AWS user access key
37 secret : AWS user access secret
39 ssh_key: Name of key pair to connect to EC2 instance
40 vpcid : VPC ID for the resources
41 availability_zone: Avaialbility zone to allocate EC2 instance.
42 default_subnet_id: Default subnet id to be used for the EC2 instance interfaces at instance creation time
43 Returns: AWS Driver Object
45 self
._access
_key
= key
46 self
._access
_secret
= secret
48 self
._availability
_zone
= availability_zone
49 self
._ssh
_key
= ssh_key
51 self
._sess
= boto3
.session
.Session(aws_access_key_id
= self
._access
_key
,
52 aws_secret_access_key
= self
._access
_secret
,
53 region_name
= self
._region
)
54 self
._ec
2_resource
_handle
= self
._sess
.resource(service_name
= 'ec2')
55 self
._s
3_handle
= self
._sess
.resource(service_name
= 's3')
56 self
._iam
_handle
= self
._sess
.resource(service_name
= 'iam')
58 self
._acct
_arn
= self
._iam
_handle
.CurrentUser().arn
59 self
._account
_id
= self
._acct
_arn
.split(':')[4]
60 # If VPC id is not passed; use default VPC for the account
62 self
._vpcid
= self
._default
_vpc
_id
66 self
._default
_subnet
_id
= default_subnet_id
67 # If default_subnet_is is not passed; get default subnet for AZ.
68 # We use this to create first network interface during instance creation time. This subnet typically should have associate public address
69 # to get public address.
70 if default_subnet_id
is None:
71 self
._default
_subnet
_id
= self
._get
_default
_subnet
_id
_for
_az
75 def default_subnet_id(self
):
77 Returns default subnet id for account
79 return self
._default
_subnet
_id
82 def _ec2_client_handle(self
):
84 Low level EC2 client connection handle
86 Returns: EC2 Client Connection Handle
88 return self
._ec
2_resource
_handle
.meta
.client
91 def _default_vpc_id(self
):
93 Method to get Default VPC ID
95 Returns: Default EC2.Vpc Resource ID for AWS account
97 return self
._default
_vpc
.vpc_id
100 def _default_vpc(self
):
102 Method to get Default VPC Resource Object
104 Returns: Default EC2.Vpc Resource for AWS account
107 response
= list(self
._ec
2_resource
_handle
.vpcs
.all())
108 except Exception as e
:
109 logger
.error("AWSDriver: Get of Default VPC failed with exception: %s" %(repr(e
)))
111 default_vpc
= [vpc
for vpc
in response
if vpc
.is_default
]
112 assert(len(default_vpc
) == 1)
113 return default_vpc
[0]
115 def _get_vpc_info(self
,VpcId
):
117 Get Vpc resource for specificed VpcId
119 - VpcId (String) : VPC ID
120 Returns: EC2.Vpc Resouce
124 response
= list(self
._ec
2_resource
_handle
.vpcs
.filter(
127 assert(len(response
) == 1)
132 def upload_image(self
, **kwargs
):
135 Arguments: **kwargs -- dictionary
137 'image_path' : File location for the image,
138 'image_prefix' : Name-Prefix of the image on S3
139 'public_key' : The path to the user's PEM encoded RSA public key certificate file,
140 'private_key' : The path to the user's PEM encoded RSA private key file,
141 'arch' : One of ["i386", "x86_64"],
142 's3_bucket' : Name of S3 bucket where this image should be uploaded
143 (e.g. 'Rift.Cal' or 'Rift.VNF' or 'Rift.3rdPartyVM' etc)
144 'kernelId' : Id of the default kernel to launch the AMI with (OPTIONAL)
145 'ramdiskId' : Id of the default ramdisk to launch the AMI with (OPTIONAL)
146 'block_device_mapping : block_device_mapping string (OPTIONAL)
147 Default block-device-mapping scheme to launch the AMI with. This scheme
148 defines how block devices may be exposed to an EC2 instance of this AMI
149 if the instance-type of the instance is entitled to the specified device.
150 The scheme is a comma-separated list of key=value pairs, where each key
151 is a "virtual-name" and each value, the corresponding native device name
152 desired. Possible virtual-names are:
153 - "ami": denotes the root file system device, as seen by the instance.
154 - "root": denotes the root file system device, as seen by the kernel.
155 - "swap": denotes the swap device, if present.
156 - "ephemeralN": denotes Nth ephemeral store; N is a non-negative integer.
157 Note that the contents of the AMI form the root file system. Samples of
158 block-device-mappings are:
159 '"ami=sda1","root=/dev/sda1","ephemeral0=sda2","swap=sda3"'
160 '"ami=0","root=/dev/dsk/c0d0s0","ephemeral0=1"'
169 CREATE_BUNDLE_CMD
= 'ec2-bundle-image --cert {public_key} --privatekey {private_key} --user {account_id} --image {image_path} --prefix {image_prefix} --arch {arch}'
170 UPLOAD_BUNDLE_CMD
= 'ec2-upload-bundle --bucket {bucket} --access-key {key} --secret-key {secret} --manifest {manifest} --region {region} --retry'
172 cmdline
= CREATE_BUNDLE_CMD
.format(public_key
= kwargs
['public_key'],
173 private_key
= kwargs
['private_key'],
174 account_id
= self
._account
_id
,
175 image_path
= kwargs
['image_path'],
176 image_prefix
= kwargs
['image_prefix'],
177 arch
= kwargs
['arch'])
179 if 'kernelId' in kwargs
:
180 cmdline
+= (' --kernel ' + kwargs
['kernelId'])
182 if 'ramdiskId' in kwargs
:
183 cmdline
+= (' --ramdisk ' + kwargs
['ramdiskId'])
185 if 'block_device_mapping' in kwargs
:
186 cmdline
+= ' --block-device-mapping ' + kwargs
['block_device_mapping']
188 ### Create Temporary Directory
190 tmp_dir
= tempfile
.mkdtemp()
191 except Exception as e
:
192 logger
.error("Failed to create temporary directory. Exception Details: %s" %(repr(e
)))
195 cmdline
+= (" --destination " + tmp_dir
)
196 logger
.info('AWSDriver: Executing ec2-bundle-image command. Target directory name: %s. This command may take a while...\n' %(tmp_dir))
197 result
= subprocess
.call(cmdline
.split())
199 logger
.info('AWSDriver: ec2-bundle-image command succeeded')
201 logger
.error('AWSDriver: ec2-bundle-image command failed. Return code %d. CMD: %s'%(result
, cmdline
))
202 raise OSError('AWSDriver: ec2-bundle-image command failed. Return code %d' %(result))
204 logger
.info('AWSDriver: Initiating image upload. This may take a while...')
206 cmdline
= UPLOAD_BUNDLE_CMD
.format(bucket
= kwargs
['s3_bucket'],
207 key
= self
._access
_key
,
208 secret
= self
._access
_secret
,
209 manifest
= tmp_dir
+'/'+kwargs
['image_prefix']+'.manifest.xml',
210 region
= self
._region
)
211 result
= subprocess
.call(cmdline
.split())
213 logger
.info('AWSDriver: ec2-upload-bundle command succeeded')
215 logger
.error('AWSDriver: ec2-upload-bundle command failed. Return code %d. CMD: %s'%(result
, cmdline
))
216 raise OSError('AWSDriver: ec2-upload-bundle command failed. Return code %d' %(result))
217 ### Delete the temporary directory
218 logger
.info('AWSDriver: Deleting temporary directory and other software artifacts')
219 shutil
.rmtree(tmp_dir
, ignore_errors
= True)
222 def register_image(self
, **kwargs
):
224 Registers an image uploaded to S3 with EC2
225 Arguments: **kwargs -- dictionary
227 Name (string) : Name of the image
228 ImageLocation(string) : Location of image manifest file in S3 (e.g. 'rift.cal.images/test-img.manifest.xml')
229 Description(string) : Description for the image (OPTIONAL)
230 Architecture (string) : Possible values 'i386' or 'x86_64' (OPTIONAL)
231 KernelId(string) : Kernel-ID Refer: http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/UserProvidedKernels.html#AmazonKernelImageIDs (OPTIONAL)
232 RamdiskId(string) : Ramdisk-ID Refer: http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/UserProvidedKernels.html#AmazonKernelImageIDs (OPTIONAL)
233 RootDeviceName(string): The name of the root device (for example, /dev/sda1 , or /dev/xvda ) (OPTIONAL)
234 BlockDeviceMappings(list) : List of dictionary of block device mapping (OPTIONAL)
237 'VirtualName': 'string',
238 'DeviceName': 'string',
240 'SnapshotId': 'string',
242 'DeleteOnTermination': True|False,
243 'VolumeType': 'standard'|'io1'|'gp2',
245 'Encrypted': True|False
250 VirtualizationType(string): The type of virtualization (OPTIONAL)
252 SriovNetSupport(string): (OPTIONAL)
253 Set to ``simple`` to enable enhanced networking for the AMI and any instances that are launched from the AMI.
254 This option is supported only for HVM AMIs. Specifying this option with a PV AMI can make instances launched from the AMI unreachable.
257 image_id: UUID of the image
260 kwargs
['DryRun'] = False
262 response
= self
._ec
2_client
_handle
.register_image(**kwargs
)
263 except Exception as e
:
264 logger
.error("AWSDriver: List image operation failed with exception: %s" %(repr(e
)))
266 assert response
['ResponseMetadata']['HTTPStatusCode'] == 200
267 return response
['ImageId']
270 def deregister_image(self
, ImageId
):
272 DeRegisters image from EC2.
274 - ImageId (string): ImageId generated by AWS in register_image call
278 response
= self
._ec
2_client
_handle
.deregister_image(
280 except Exception as e
:
281 logger
.error("AWSDriver: deregister_image operation failed with exception: %s" %(repr(e
)))
283 assert response
['ResponseMetadata']['HTTPStatusCode'] == 200
285 def get_image(self
, ImageId
):
287 Returns a dictionary object describing the Image identified by ImageId
290 response
= list(self
._ec
2_resource
_handle
.images
.filter(ImageIds
= [ImageId
]))
291 except Exception as e
:
292 logger
.error("AWSDriver: List image operation failed with exception: %s" %(repr(e
)))
296 def list_images(self
):
298 Returns list of dictionaries. Each dictionary contains attributes associated with image
300 Returns: List of dictionaries.
303 response
= list(self
._ec
2_resource
_handle
.images
.filter(Owners
= [self
._account
_id
]))
304 except Exception as e
:
305 logger
.error("AWSDriver: List image operation failed with exception: %s" %(repr(e
)))
309 def create_image_from_instance(self
,InstanceId
,ImageName
,VolumeSize
= 16):
311 Creates AWS AMI from the instance root device Volume and registers the same
312 Caller is expected to stop the instance and restart the instance if required
314 - InstanceId (String) : AWS EC2 Instance Id
315 - ImageName (String) : Name for AMI
321 inst
= self
.get_instance(InstanceId
)
322 # Find Volume Id of Root Device
323 if inst
.root_device_type
== 'ebs':
324 for dev
in inst
.block_device_mappings
:
325 if inst
.root_device_name
== dev
['DeviceName']:
326 volume_id
= dev
['Ebs']['VolumeId']
329 rsp
=self
._ec
2_resource
_handle
.create_snapshot(VolumeId
=volume_id
)
332 #Wait for the snapshot to be completed
336 attempts
= attempts
+ 1
337 waiter
= self
._ec
2_client
_handle
.get_waiter('snapshot_completed')
338 waiter
.wait(SnapshotIds
=[snapshot_id
])
339 except botocore
.exceptions
.WaiterError
as e
:
340 logger
.error("AWSDriver: Create Snapshot for image still not completed. Will wait for another iteration")
342 except Exception as e
:
343 logger
.error("AWSDriver: Createing Snapshot for instance failed during image creation: %s", (repr(e
)))
347 logger
.debug("AWSDriver: Snapshot %s completed successfully from instance %s",snapshot_id
,InstanceId
)
348 image_id
= self
.register_image(Name
=ImageName
,VirtualizationType
='hvm',
349 RootDeviceName
='/dev/sda1',SriovNetSupport
='simple',
350 BlockDeviceMappings
=[{'DeviceName':'/dev/sda1',
351 'Ebs':{'SnapshotId':snapshot_id
,'VolumeSize': VolumeSize
,
352 'VolumeType': 'standard', 'DeleteOnTermination': True}}],
353 Architecture
='x86_64')
356 logger
.error("AWSDriver: Create Image failed as Instance Root device Type should be ebs to create image")
357 raise exceptions
.RWErrorFailure("AWSDriver: Create Image failed as Instance Root device Type should be ebs to create image")
358 except Exception as e
:
359 logger
.error("AWSDriver: Createing image from instance failed with exception: %s", (repr(e
)))
362 def list_instances(self
):
364 Returns list of resource object representing EC2 instance.
366 Returns: List of EC2.Instance object
370 # Skip Instances in terminated state
371 response
= self
._ec
2_resource
_handle
.instances
.filter(
373 { 'Name': 'instance-state-name',
374 'Values': ['pending',
381 except Exception as e
:
382 logger
.error("AWSDriver: List instances operation failed with exception: %s" %(repr(e
)))
384 for instance
in response
:
385 instance_list
.append(instance
)
388 def get_instance(self
, InstanceId
):
390 Returns a EC2 resource Object describing the Instance identified by InstanceId
392 - InstnaceId (String) : MANDATORY, EC2 Instance Id
393 Returns: EC2.Instance object
397 instance
= list(self
._ec
2_resource
_handle
.instances
.filter(
398 InstanceIds
= [InstanceId
]))
399 except Exception as e
:
400 logger
.error("AWSDriver: Get instances operation failed with exception: %s" %(repr(e
)))
402 if len(instance
) == 0:
403 logger
.error("AWSDriver: instance with id %s not avaialble" %InstanceId
)
404 raise exceptions
.RWErrorNotFound("AWSDriver: instance with id %s not avaialble" %InstanceId
)
405 elif len(instance
) > 1:
406 logger
.error("AWSDriver: Duplicate instances with id %s is avaialble" %InstanceId
)
407 raise exceptions
.RWErrorDuplicate("AWSDriver: Duplicate instances with id %s is avaialble" %InstanceId
)
410 def create_instance(self
,**kwargs
):
412 Create an EC2instance.
413 Arguments: **kwargs -- dictionary
415 ImageId (string): MANDATORY, Id of AMI to create instance
416 SubetId (string): Id of Subnet to start EC2 instance. EC2 instance will be started in VPC subnet resides.
417 Default subnet from account used if not present
418 InstanceType(string): AWS Instance Type name. Default: t2.micro
419 SecurityGroupIds: AWS Security Group Id to associate with the instance. Default from VPC used if not present
420 KeyName (string): Key pair name. Default key pair from account used if not present
421 MinCount (Integer): Minimum number of instance to start. Default: 1
422 MaxCount (Integer): Maximum number of instance to start. Default: 1
423 Placement (Dict) : Dictionary having Placement group details
424 {AvailabilityZone (String): AZ to create the instance}
425 UserData (string) : cloud-init config file
427 Returns: List of EC2.Instance object
430 if 'ImageId' not in kwargs
:
431 logger
.error("AWSDriver: Mandatory parameter ImageId not available during create_instance")
432 raise AttributeError("Mandatory parameter ImageId not available during create_instance")
434 #Validate image exists and is avaialble
436 image_res
= self
._ec
2_resource
_handle
.Image(kwargs
['ImageId'])
438 except Exception as e
:
439 logger
.error("AWSDriver: Image with id %s not available and failed with exception: %s",kwargs
['ImageId'],(repr(e
)))
440 raise AttributeError("AWSDriver: Image with id %s not available and failed with exception: %s",kwargs
['ImageId'],(repr(e
)))
441 if image_res
.state
!= 'available':
442 logger
.error("AWSDriver: Image state is not available for image with id %s; Current state is %s",
443 image_res
.id,image_res
.state
)
444 raise AttributeError("ImageId is not valid")
446 # If MinCount or MaxCount is not passed set them to default of 1
447 if 'MinCount' not in kwargs
:
448 kwargs
['MinCount'] = 1
449 if 'MaxCount' not in kwargs
:
450 kwargs
['MaxCount'] = kwargs
['MinCount']
452 if 'KeyName' not in kwargs
:
453 if not self
._ssh
_key
:
454 logger
.error("AWSDriver: Key not available during create_instance to allow SSH")
456 kwargs
['KeyName'] = self
._ssh
_key
458 if 'Placement' not in kwargs
and self
._availability
_zone
is not None:
459 placement
= {'AvailabilityZone':self
._availability
_zone
}
460 kwargs
['Placement'] = placement
462 if 'SubnetId' not in kwargs
and 'NetworkInterfaces' not in kwargs
:
463 if self
._default
_subnet
_id
:
464 kwargs
['SubnetId'] = self
._default
_subnet
_id
466 logger
.error("AWSDriver: Valid subnetid not present during create instance")
467 raise AttributeError("Valid subnet not present during create instance")
469 if self
._availability
_zone
and 'SubnetId' in kwargs
:
470 subnet
= self
.get_subnet(SubnetId
= kwargs
['SubnetId'])
472 logger
.error("AWSDriver: Valid subnet not found for subnetid %s",kwargs
['SubnetId'])
473 raise AttributeError("Valid subnet not found for subnetid %s",kwargs
['SubnetId'])
474 if subnet
.availability_zone
!= self
._availability
_zone
:
475 logger
.error("AWSDriver: AZ of Subnet %s %s doesnt match account AZ %s",kwargs
['SubnetId'],
476 subnet
.availability_zone
,self
._availability
_zone
)
477 raise AttributeError("AWSDriver: AZ of Subnet %s %s doesnt match account AZ %s",kwargs
['SubnetId'],
478 subnet
.availability_zone
,self
._availability
_zone
)
480 # If instance type is not passed; use t2.micro as default
481 if 'InstanceType' not in kwargs
or kwargs
['InstanceType'] is None:
482 kwargs
['InstanceType'] = 't2.micro'
483 inst_type
= kwargs
['InstanceType']
484 if inst_type
not in aws_table
.INSTANCE_TYPES
.keys():
485 logger
.error("AWSDriver: Invalid instance type %s used",inst_type
)
486 raise AttributeError('InstanceType %s is not valid' %inst
_type
)
488 #validate instance_type for AMI
489 if image_res
.sriov_net_support
== 'simple':
490 if image_res
.virtualization_type
!= 'hvm':
491 logger
.error("AWSDriver: Image with id %s has SRIOV net support but virtualization type is not hvm",kwargs
['ImageId'])
492 raise AttributeError('Invalid Image with id %s' %kwargs
['ImageId'])
493 if aws_table
.INSTANCE_TYPES
[inst_type
]['sriov'] is False:
494 logger
.warning("AWSDriver: Image %s support SR-IOV but instance type %s does not support HVM",kwargs
['ImageId'],inst_type
)
496 if image_res
.virtualization_type
== 'paravirtual' and aws_table
.INSTANCE_TYPES
[inst_type
]['paravirt'] is False: # Need to check virt type str for PV
497 logger
.error("AWSDriver: Image %s requires PV support but instance %s does not support PV",kwargs
['ImageId'],inst_type
)
498 raise AttributeError('Image %s requires PV support but instance %s does not support PV',kwargs
['ImageId'],inst_type
)
500 if image_res
.root_device_type
== 'instance-store' and aws_table
.INSTANCE_TYPES
[inst_type
]['disk'] == 0:
501 logger
.error("AWSDriver: Image %s uses instance-store root device type that is not supported by instance type %s",kwargs
['ImageId'],inst_type
)
502 raise AttributeError("AWSDriver: Image %s uses instance-store root device type that is not supported by instance type %s",kwargs
['ImageId'],inst_type
)
505 # Support of instance type varies across regions and also based on account. So we are not validating it
506 #if inst_type not in aws_table.REGION_DETAILS[self._region]['instance_types']:
507 # logger.error("AWSDriver: instance type %s not supported in region %s",inst_type,self._region)
508 # raise AttributeError("AWSDriver: instance type %s not supported in region %s",inst_type,self._region)
511 instances
= self
._ec
2_resource
_handle
.create_instances(**kwargs
)
512 except Exception as e
:
513 logger
.error("AWSDriver: Creating instance failed with exception: %s" %(repr(e
)))
517 def terminate_instance(self
,InstanceId
):
519 Termintae an EC2 instance
521 - InstanceId (String): ID of EC2 instance
525 InstanceIds
= InstanceId
526 if type(InstanceIds
) is not list:
528 InstanceIds
.append(InstanceId
)
531 response
= self
._ec
2_client
_handle
.terminate_instances(InstanceIds
=InstanceIds
)
532 except Exception as e
:
533 logger
.error("AWSDriver: Terminate instance failed with exception: %s" %(repr(e
)))
537 def stop_instance(self
,InstanceId
):
539 Stop an EC2 instance. Stop is supported only for EBS backed instance
541 - InstanceId (String): ID of EC2 instance
545 InstanceIds
= InstanceId
546 if type(InstanceIds
) is not list:
548 InstanceIds
.append(InstanceId
)
551 response
= self
._ec
2_client
_handle
.stop_instances(InstanceIds
=InstanceIds
)
552 except Exception as e
:
553 logger
.error("AWSDriver: Stop for instance %s failed with exception: %s",InstanceId
,repr(e
))
557 def start_instance(self
,InstanceId
):
559 Start an EC2 instance. Start is supported only for EBS backed instance
561 - InstanceId (String): ID of EC2 instance
565 InstanceIds
= InstanceId
566 if type(InstanceIds
) is not list:
568 InstanceIds
.append(InstanceId
)
571 response
= self
._ec
2_client
_handle
.start_instances(InstanceIds
=InstanceIds
)
572 except Exception as e
:
573 logger
.error("AWSDriver: Start for instance %s failed with exception: %s",InstanceId
,repr(e
))
578 def _get_default_subnet_id_for_az(self
):
580 Get default subnet id for AWS Driver registered Availability Zone
582 Returns: SubnetId (String)
585 if self
._availability
_zone
:
586 subnet
= self
._get
_default
_subnet
_for
_az
(self
._availability
_zone
)
591 def _get_default_subnet_for_az(self
,AvailabilityZone
):
593 Get default Subnet for Avaialbility Zone
595 - AvailabilityZone (String) : EC2 AZ
596 Returns: EC2.Subnet object
599 AvailabilityZones
= [AvailabilityZone
]
601 response
= list(self
._ec
2_resource
_handle
.subnets
.filter(
603 {'Name':'availability-zone',
604 'Values': AvailabilityZones
}]))
605 except Exception as e
:
606 logger
.error("AWSDriver: Get default subnet for Availability zone failed with exception: %s" %(repr(e
)))
608 default_subnet
= [subnet
for subnet
in response
if subnet
.default_for_az
is True and subnet
.vpc_id
== self
._vpcid
]
609 assert(len(default_subnet
) == 1)
610 return default_subnet
[0]
612 def get_subnet_list(self
,VpcId
=None):
616 - VpcId (String) - VPC ID to filter the subnet list
617 Returns: List of EC2.Subnet Object
622 if VpcId
is not None:
623 if type(VpcIds
) is not list:
626 response
= list(self
._ec
2_resource
_handle
.subnets
.filter(
631 response
= list(self
._ec
2_resource
_handle
.subnets
.all())
632 except Exception as e
:
633 logger
.error("AWSDriver: List subnets operation failed with exception: %s" %(repr(e
)))
637 def get_subnet(self
,SubnetId
):
639 Get the subnet for specified SubnetId
641 - SubnetId (String) - MANDATORY
642 Returns: EC2.Subnet Object
646 response
= list(self
._ec
2_resource
_handle
.subnets
.filter(SubnetIds
=[SubnetId
]))
647 except botocore
.exceptions
.ClientError
as e
:
648 if e
.response
['Error']['Code'] == 'InvalidSubnetID.NotFound':
649 logger
.error("AWSDriver: Get Subnet Invalid SubnetID %s",SubnetId
)
650 raise exceptions
.RWErrorNotFound("AWSDriver: Delete Subnet Invalid SubnetID %s",SubnetId
)
652 logger
.error("AWSDriver: Creating network interface failed with exception: %s",(repr(e
)))
654 except Exception as e
:
655 logger
.error("AWSDriver: Get subnet operation failed with exception: %s" %(repr(e
)))
657 if len(response
) == 0:
658 logger
.error("AWSDriver: subnet with id %s is not avaialble" %SubnetId
)
659 raise exceptions
.RWErrorNotFoun("AWSDriver: subnet with id %s is not avaialble" %SubnetId
)
660 elif len(response
) > 1:
661 logger
.error("AWSDriver: Duplicate subnet with id %s is avaialble" %SubnetId
)
662 raise exceptions
.RWErrorDuplicate("AWSDriver: Duplicate subnet with id %s is avaialble" %SubnetId
)
665 def create_subnet(self
,**kwargs
):
667 Create a EC2 subnet based on specified CIDR
669 - CidrBlock (String): MANDATORY. CIDR for subnet. CIDR should be within VPC CIDR
670 - VpcId (String): VPC ID to create the subnet. Default AZ from AWS Driver registration used if not present.
671 - AvailabilityZone (String): Availability zone to create subnet. Default AZ from AWS Driver registration used
673 Returns: EC2.Subnet Object
676 if 'CidrBlock' not in kwargs
:
677 logger
.error("AWSDriver: Insufficent params for create_subnet. CidrBlock is mandatory parameter")
678 raise AttributeError("AWSDriver: Insufficent params for create_subnet. CidrBlock is mandatory parameter")
680 if 'VpcId' not in kwargs
:
681 kwargs
['VpcId'] = self
._vpcid
682 if 'AvailabilityZone' not in kwargs
and self
._availability
_zone
is not None:
683 kwargs
['AvailabilityZone'] = self
._availability
_zone
685 vpc
= self
._get
_vpc
_info
(kwargs
['VpcId'])
687 logger
.error("AWSDriver: Subnet creation failed as VpcId %s does not exist", kwargs
['VpcId'])
688 raise exceptions
.RWErrorNotFound("AWSDriver: Subnet creation failed as VpcId %s does not exist", kwargs
['VpcId'])
689 if vpc
.state
!= 'available':
690 logger
.error("AWSDriver: Subnet creation failed as VpcId %s is not in available state. Current state is %s", kwargs
['VpcId'],vpc
.state
)
691 raise exceptions
.RWErrorNotConnected("AWSDriver: Subnet creation failed as VpcId %s is not in available state. Current state is %s", kwargs
['VpcId'],vpc
.state
)
694 subnet
= self
._ec
2_resource
_handle
.create_subnet(**kwargs
)
695 except botocore
.exceptions
.ClientError
as e
:
696 if e
.response
['Error']['Code'] == 'InvalidSubnet.Conflict':
697 logger
.error("AWSDriver: Create Subnet for ip %s failed due to overalp with existing subnet in VPC %s",kwargs
['CidrBlock'],kwargs
['VpcId'])
698 raise exceptions
.RWErrorExists("AWSDriver: Create Subnet for ip %s failed due to overalp with existing subnet in VPC %s",kwargs
['CidrBlock'],kwargs
['VpcId'])
699 elif e
.response
['Error']['Code'] == 'InvalidSubnet.Range':
700 logger
.error("AWSDriver: Create Subnet for ip %s failed as it is not in VPC CIDR range for VPC %s",kwargs
['CidrBlock'],kwargs
['VpcId'])
701 raise AttributeError("AWSDriver: Create Subnet for ip %s failed as it is not in VPC CIDR range for VPC %s",kwargs
['CidrBlock'],kwargs
['VpcId'])
703 logger
.error("AWSDriver: Creating subnet failed with exception: %s",(repr(e
)))
705 except Exception as e
:
706 logger
.error("AWSDriver: Creating subnet failed with exception: %s" %(repr(e
)))
710 def modify_subnet(self
,SubnetId
,MapPublicIpOnLaunch
):
714 - SubnetId (String): MANDATORY, EC2 Subnet ID
715 - MapPublicIpOnLaunch (Boolean): Flag to indicate if subnet is associated with public IP
719 response
= self
._ec
2_client
_handle
.modify_subnet_attribute(SubnetId
=SubnetId
,MapPublicIpOnLaunch
={'Value':MapPublicIpOnLaunch
})
720 except botocore
.exceptions
.ClientError
as e
:
721 if e
.response
['Error']['Code'] == 'InvalidSubnetID.NotFound':
722 logger
.error("AWSDriver: Modify Subnet Invalid SubnetID %s",SubnetId
)
723 raise exceptions
.RWErrorNotFound("AWSDriver: Modify Subnet Invalid SubnetID %s",SubnetId
)
725 logger
.error("AWSDriver: Modify subnet failed with exception: %s",(repr(e
)))
727 except Exception as e
:
728 logger
.error("AWSDriver: Modify subnet failed with exception: %s",(repr(e
)))
732 def delete_subnet(self
,SubnetId
):
736 - SubnetId (String): MANDATORY, EC2 Subnet ID
741 response
= self
._ec
2_client
_handle
.delete_subnet(SubnetId
=SubnetId
)
742 except botocore
.exceptions
.ClientError
as e
:
743 if e
.response
['Error']['Code'] == 'InvalidSubnetID.NotFound':
744 logger
.error("AWSDriver: Delete Subnet Invalid SubnetID %s",SubnetId
)
745 raise exceptions
.RWErrorNotFound("AWSDriver: Delete Subnet Invalid SubnetID %s",SubnetId
)
747 logger
.error("AWSDriver: Delete subnet failed with exception: %s",(repr(e
)))
749 except Exception as e
:
750 logger
.error("AWSDriver: Delete subnet failed with exception: %s",(repr(e
)))
753 def get_network_interface_list(self
,SubnetId
=None,VpcId
=None,InstanceId
= None):
755 List all the network interfaces
759 - InstanceId (String)
760 Returns List of EC2.NetworkInterface
764 if InstanceId
is not None:
765 InstanceIds
= [InstanceId
]
766 response
= list(self
._ec
2_resource
_handle
.network_interfaces
.filter(
768 { 'Name': 'attachment.instance-id',
769 'Values': InstanceIds
}]))
770 elif SubnetId
is not None:
772 if type(SubnetId
) is not list:
774 SubnetIds
.append(SubnetId
)
775 response
= list(self
._ec
2_resource
_handle
.network_interfaces
.filter(
777 { 'Name': 'subnet-id',
778 'Values': SubnetIds
}]))
779 elif VpcId
is not None:
781 if type(VpcIds
) is not list:
784 response
= list(self
._ec
2_resource
_handle
.network_interfaces
.filter(
789 response
= list(self
._ec
2_resource
_handle
.network_interfaces
.all())
790 except Exception as e
:
791 logger
.error("AWSDriver: List network interfaces operation failed with exception: %s" %(repr(e
)))
795 def get_network_interface(self
,NetworkInterfaceId
):
797 Get the network interface
799 NetworkInterfaceId (String): MANDATORY, EC2 Network Interface Id
800 Returns: EC2.NetworkInterface Object
804 response
= list(self
._ec
2_resource
_handle
.network_interfaces
.filter(NetworkInterfaceIds
=[NetworkInterfaceId
]))
805 except Exception as e
:
806 logger
.error("AWSDriver: List Network Interfaces operation failed with exception: %s" %(repr(e
)))
808 if len(response
) == 0:
809 logger
.error("AWSDriver: Network interface with id %s is not avaialble" %NetworkInterfaceId
)
810 raise exceptions
.RWErrorNotFound("AWSDriver: Network interface with id %s is not avaialble" %NetworkInterfaceId
)
811 elif len(response
) > 1:
812 logger
.error("AWSDriver: Duplicate Network interface with id %s is avaialble" %NetworkInterfaceId
)
813 raise exceptions
.RWErrorDuplicate("AWSDriver: Duplicate Network interface with id %s is avaialble" %NetworkInterfaceId
)
816 def create_network_interface(self
,**kwargs
):
818 Create a network interface in specified subnet
820 - SubnetId (String): MANDATORY, Subnet to create network interface
821 Returns: EC2.NetworkInterface Object
824 if 'SubnetId' not in kwargs
:
825 logger
.error("AWSDriver: Insufficent params for create_network_inteface . SubnetId is mandatory parameters")
826 raise AttributeError("AWSDriver: Insufficent params for create_network_inteface . SubnetId is mandatory parameters")
829 interface
= self
._ec
2_resource
_handle
.create_network_interface(**kwargs
)
830 except botocore
.exceptions
.ClientError
as e
:
831 if e
.response
['Error']['Code'] == 'InvalidSubnetID.NotFound':
832 logger
.error("AWSDriver: Create Network interface failed as subnet %s is not found",kwargs
['SubnetId'])
833 raise exceptions
.RWErrorNotFound("AWSDriver: Create Network interface failed as subnet %s is not found",kwargs
['SubnetId'])
835 logger
.error("AWSDriver: Creating network interface failed with exception: %s",(repr(e
)))
837 except Exception as e
:
838 logger
.error("AWSDriver: Creating network interface failed with exception: %s" %(repr(e
)))
842 def delete_network_interface(self
,NetworkInterfaceId
):
844 Delete a network interface
846 - NetworkInterfaceId(String): MANDATORY
850 response
= self
._ec
2_client
_handle
.delete_network_interface(NetworkInterfaceId
=NetworkInterfaceId
)
851 except botocore
.exceptions
.ClientError
as e
:
852 if e
.response
['Error']['Code'] == 'InvalidNetworkInterfaceID.NotFound':
853 logger
.error("AWSDriver: Delete Network interface not found for interface ID %s",NetworkInterfaceId
)
854 raise exceptions
.RWErrorNotFound("AWSDriver: Delete Network interface not found for interface ID %s",NetworkInterfaceId
)
856 logger
.error("AWSDriver: Delete network interface failed with exception: %s",(repr(e
)))
858 except Exception as e
:
859 logger
.error("AWSDriver: Delete network interface failed with exception: %s",(repr(e
)))
862 def associate_public_ip_to_network_interface(self
,NetworkInterfaceId
):
864 Allocate a Elastic IP and associate to network interface
866 NetworkInterfaceId (String): MANDATORY
870 response
= self
._ec
2_client
_handle
.allocate_address(Domain
='vpc')
871 self
._ec
2_client
_handle
.associate_address(NetworkInterfaceId
=NetworkInterfaceId
,AllocationId
= response
['AllocationId'])
872 except Exception as e
:
873 logger
.error("AWSDriver: Associating Public IP to network interface %s failed with exception: %s",NetworkInterfaceId
,(repr(e
)))
877 def disassociate_public_ip_from_network_interface(self
,NetworkInterfaceId
):
879 Disassociate a Elastic IP from network interface and release the same
881 NetworkInterfaceId (String): MANDATORY
885 interface
= self
.get_network_interface(NetworkInterfaceId
=NetworkInterfaceId
)
886 if interface
and interface
.association
and 'AssociationId' in interface
.association
:
887 self
._ec
2_client
_handle
.disassociate_address(AssociationId
= interface
.association
['AssociationId'])
888 self
._ec
2_client
_handle
.release_address(AllocationId
=interface
.association
['AllocationId'])
889 except Exception as e
:
890 logger
.error("AWSDriver: Associating Public IP to network interface %s failed with exception: %s",NetworkInterfaceId
,(repr(e
)))
893 def attach_network_interface(self
,**kwargs
):
895 Attach network interface to running EC2 instance. Used to add additional interfaces to instance
897 - NetworkInterfaceId (String): MANDATORY,
898 - InstanceId(String) : MANDATORY
899 - DeviceIndex (Integer): MANDATORY
900 Returns: Dict with AttachmentId which is string
903 if 'NetworkInterfaceId' not in kwargs
or 'InstanceId' not in kwargs
or 'DeviceIndex' not in kwargs
:
904 logger
.error('AWSDriver: Attach network interface to instance requires NetworkInterfaceId and InstanceId as mandatory parameters')
905 raise AttributeError('AWSDriver: Attach network interface to instance requires NetworkInterfaceId and InstanceId as mandatory parameters')
908 response
= self
._ec
2_client
_handle
.attach_network_interface(**kwargs
)
909 except Exception as e
:
910 logger
.error("AWSDriver: Attach network interface failed with exception: %s",(repr(e
)))
914 def detach_network_interface(self
,**kwargs
):
916 Detach network interface from instance
918 - AttachmentId (String)
922 if 'AttachmentId' not in kwargs
:
923 logger
.error('AWSDriver: Detach network interface from instance requires AttachmentId as mandatory parameters')
924 raise AttributeError('AWSDriver: Detach network interface from instance requires AttachmentId as mandatory parameters')
927 response
= self
._ec
2_client
_handle
.detach_network_interface(**kwargs
)
928 except Exception as e
:
929 logger
.error("AWSDriver: Detach network interface failed with exception: %s",(repr(e
)))
932 def map_flavor_to_instance_type(self
,ram
,vcpus
,disk
,inst_types
= None):
934 Method to find a EC2 instance type matching the requested params
936 - ram (Integer) : RAM size in MB
937 - vcpus (Integer): VPCU count
938 - disk (Integer): Storage size in GB
939 - inst_types (List): List of string having list of EC2 instance types to choose from
940 assumed to be in order of resource size
942 InstanceType (String) - EC2 Instance Type
944 if inst_types
is None:
945 inst_types
= ['c3.large','c3.xlarge','c3.2xlarge', 'c3.4xlarge', 'c3.8xlarge']
947 for inst
in inst_types
:
948 if inst
in aws_table
.INSTANCE_TYPES
:
949 if ( aws_table
.INSTANCE_TYPES
[inst
]['ram'] > ram
and
950 aws_table
.INSTANCE_TYPES
[inst
]['vcpu'] > vcpus
and
951 aws_table
.INSTANCE_TYPES
[inst
]['disk'] > disk
):
955 def upload_ssh_key(self
,key_name
,public_key
):
957 Method to upload Public Key to AWS
959 - keyname (String): Name for the key pair
960 - public_key (String): Base 64 encoded public key
963 self
._ec
2_resource
_handle
.import_key_pair(KeyName
=key_name
,PublicKeyMaterial
=public_key
)
965 def delete_ssh_key(self
,key_name
):
967 Method to delete Public Key from AWS
969 - keyname (String): Name for the key pair
972 self
._ec
2_client
_handle
.delete_key_pair(KeyName
=key_name
)