Network Interfaces: Initial implementation.
This commit is contained in:
parent
22d9141122
commit
42f8cea5e6
@ -72,6 +72,14 @@ class InvalidSubnetIdError(EC2ClientError):
|
|||||||
.format(subnet_id))
|
.format(subnet_id))
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidNetworkInterfaceIdError(EC2ClientError):
|
||||||
|
def __init__(self, eni_id):
|
||||||
|
super(InvalidNetworkInterfaceIdError, self).__init__(
|
||||||
|
"InvalidNetworkInterfaceID.NotFound",
|
||||||
|
"The network interface ID '{0}' does not exist"
|
||||||
|
.format(eni_id))
|
||||||
|
|
||||||
|
|
||||||
class InvalidSecurityGroupDuplicateError(EC2ClientError):
|
class InvalidSecurityGroupDuplicateError(EC2ClientError):
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
super(InvalidSecurityGroupDuplicateError, self).__init__(
|
super(InvalidSecurityGroupDuplicateError, self).__init__(
|
||||||
|
@ -26,6 +26,7 @@ from .exceptions import (
|
|||||||
ResourceAlreadyAssociatedError,
|
ResourceAlreadyAssociatedError,
|
||||||
InvalidVPCIdError,
|
InvalidVPCIdError,
|
||||||
InvalidSubnetIdError,
|
InvalidSubnetIdError,
|
||||||
|
InvalidNetworkInterfaceIdError,
|
||||||
InvalidSecurityGroupDuplicateError,
|
InvalidSecurityGroupDuplicateError,
|
||||||
InvalidSecurityGroupNotFoundError,
|
InvalidSecurityGroupNotFoundError,
|
||||||
InvalidPermissionNotFoundError,
|
InvalidPermissionNotFoundError,
|
||||||
@ -48,11 +49,14 @@ from .utils import (
|
|||||||
random_dhcp_option_id,
|
random_dhcp_option_id,
|
||||||
random_eip_allocation_id,
|
random_eip_allocation_id,
|
||||||
random_eip_association_id,
|
random_eip_association_id,
|
||||||
|
random_eni_attach_id,
|
||||||
|
random_eni_id,
|
||||||
random_internet_gateway_id,
|
random_internet_gateway_id,
|
||||||
random_instance_id,
|
random_instance_id,
|
||||||
random_internet_gateway_id,
|
random_internet_gateway_id,
|
||||||
random_ip,
|
random_ip,
|
||||||
random_key_pair,
|
random_key_pair,
|
||||||
|
random_public_ip,
|
||||||
random_reservation_id,
|
random_reservation_id,
|
||||||
random_route_table_id,
|
random_route_table_id,
|
||||||
random_security_group_id,
|
random_security_group_id,
|
||||||
@ -77,6 +81,172 @@ class TaggedEC2Instance(object):
|
|||||||
return tags
|
return tags
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkInterface(object):
|
||||||
|
def __init__(self, subnet, private_ip_address, device_index=0, public_ip_auto_assign=True, group_ids=None):
|
||||||
|
self.id = random_eni_id()
|
||||||
|
self.device_index = device_index
|
||||||
|
self.private_ip_address = private_ip_address
|
||||||
|
self.subnet = subnet
|
||||||
|
self.instance = None
|
||||||
|
|
||||||
|
self.public_ip = None
|
||||||
|
self.public_ip_auto_assign = public_ip_auto_assign
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
self.attachments = []
|
||||||
|
self.group_set = []
|
||||||
|
|
||||||
|
group = None
|
||||||
|
if group_ids:
|
||||||
|
for group_id in group_ids:
|
||||||
|
group = ec2_backend.get_security_group_from_id(group_id)
|
||||||
|
if not group:
|
||||||
|
# Create with specific group ID.
|
||||||
|
group = SecurityGroup(group_id, group_id, group_id, vpc_id=subnet.vpc_id)
|
||||||
|
ec2_backend.groups[subnet.vpc_id][group_id] = group
|
||||||
|
if group:
|
||||||
|
self.group_set.append(group)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||||
|
properties = cloudformation_json['Properties']
|
||||||
|
|
||||||
|
security_group_ids = properties.get('SecurityGroups', [])
|
||||||
|
|
||||||
|
subnet_id = properties['SubnetId']
|
||||||
|
subnet = ec2_backend.get_subnet(subnet_id)
|
||||||
|
|
||||||
|
private_ip_address = properties.get('PrivateIpAddress', None)
|
||||||
|
|
||||||
|
network_interface = ec2_backend.create_network_interface(
|
||||||
|
subnet,
|
||||||
|
private_ip_address,
|
||||||
|
group_ids=security_group_ids
|
||||||
|
)
|
||||||
|
return network_interface
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
if self.public_ip_auto_assign:
|
||||||
|
self.public_ip = None
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self.check_auto_public_ip()
|
||||||
|
|
||||||
|
def check_auto_public_ip(self):
|
||||||
|
if self.public_ip_auto_assign:
|
||||||
|
self.public_ip = random_public_ip()
|
||||||
|
|
||||||
|
def attach(self, instance_id, device_index):
|
||||||
|
attachment = {'attachmentId': random_eni_attach_id(),
|
||||||
|
'instanceId': instance_id,
|
||||||
|
'deviceIndex': device_index}
|
||||||
|
self.attachments.append(attachment)
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkInterfaceBackend(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.enis = {}
|
||||||
|
super(NetworkInterfaceBackend, self).__init__()
|
||||||
|
|
||||||
|
def create_network_interface(self, subnet, private_ip_address, group_ids=None, **kwargs):
|
||||||
|
eni = NetworkInterface(subnet, private_ip_address, group_ids=group_ids)
|
||||||
|
self.enis[eni.id] = eni
|
||||||
|
return eni
|
||||||
|
|
||||||
|
def get_network_interface(self, eni_id):
|
||||||
|
for eni in self.enis.values():
|
||||||
|
if eni_id == eni.id:
|
||||||
|
return eni
|
||||||
|
raise InvalidNetworkInterfaceIdError(eni_id)
|
||||||
|
|
||||||
|
def delete_network_interface(self, eni_id):
|
||||||
|
deleted = self.enis.pop(eni_id, None)
|
||||||
|
if not deleted:
|
||||||
|
raise InvalidNetworkInterfaceIdError(eni_id)
|
||||||
|
return deleted
|
||||||
|
|
||||||
|
def describe_network_interfaces(self, filters=None):
|
||||||
|
enis = self.enis.values()
|
||||||
|
|
||||||
|
if filters:
|
||||||
|
for (_filter, _filter_value) in filters.items():
|
||||||
|
if _filter == 'network-interface-id':
|
||||||
|
_filter = 'id'
|
||||||
|
enis = [ eni for eni in enis if getattr(eni, _filter) in _filter_value ]
|
||||||
|
elif _filter == 'group-id':
|
||||||
|
original_enis = enis
|
||||||
|
enis = []
|
||||||
|
for eni in original_enis:
|
||||||
|
group_ids = []
|
||||||
|
for group in eni.group_set:
|
||||||
|
if group.id in _filter_value:
|
||||||
|
enis.append(eni)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
ec2_backend.raise_not_implemented_error("The filter '{0}' for DescribeNetworkInterfaces".format(_filter))
|
||||||
|
return enis
|
||||||
|
|
||||||
|
def modify_network_interface_attribute(self, eni_id, group_id):
|
||||||
|
eni = self.get_network_interface(eni_id)
|
||||||
|
group = self.get_security_group_from_id(group_id)
|
||||||
|
eni.group_set = [group]
|
||||||
|
|
||||||
|
def prep_nics_for_instance(self, instance, nic_spec, subnet_id=None, private_ip=None, associate_public_ip=None):
|
||||||
|
nics = {}
|
||||||
|
|
||||||
|
# Primary NIC defaults
|
||||||
|
primary_nic = {'SubnetId': subnet_id,
|
||||||
|
'PrivateIpAddress': private_ip,
|
||||||
|
'AssociatePublicIpAddress': associate_public_ip}
|
||||||
|
primary_nic = dict((k,v) for k, v in primary_nic.items() if v)
|
||||||
|
|
||||||
|
# If empty NIC spec but primary NIC values provided, create NIC from them.
|
||||||
|
if primary_nic and not nic_spec:
|
||||||
|
nic_spec[0] = primary_nic
|
||||||
|
nic_spec[0]['DeviceIndex'] = 0
|
||||||
|
|
||||||
|
# Flesh out data structures and associations
|
||||||
|
for nic in nic_spec.values():
|
||||||
|
use_eni = None
|
||||||
|
security_group_ids = []
|
||||||
|
|
||||||
|
device_index = int(nic.get('DeviceIndex'))
|
||||||
|
|
||||||
|
nic_id = nic.get('NetworkInterfaceId', None)
|
||||||
|
if nic_id:
|
||||||
|
# If existing NIC found, use it.
|
||||||
|
use_nic = ec2_backend.get_network_interface(nic_id)
|
||||||
|
use_nic.device_index = device_index
|
||||||
|
use_nic.public_ip_auto_assign = False
|
||||||
|
|
||||||
|
else:
|
||||||
|
# If primary NIC values provided, use them for the primary NIC.
|
||||||
|
if device_index == 0 and primary_nic:
|
||||||
|
nic.update(primary_nic)
|
||||||
|
|
||||||
|
subnet = ec2_backend.get_subnet(nic['SubnetId'])
|
||||||
|
|
||||||
|
group_id = nic.get('SecurityGroupId',None)
|
||||||
|
group_ids = [group_id] if group_id else []
|
||||||
|
|
||||||
|
use_nic = ec2_backend.create_network_interface(subnet,
|
||||||
|
nic.get('PrivateIpAddress',None),
|
||||||
|
device_index=device_index,
|
||||||
|
public_ip_auto_assign=nic.get('AssociatePublicIpAddress',False),
|
||||||
|
group_ids=group_ids)
|
||||||
|
|
||||||
|
use_nic.instance = instance # This is used upon associate/disassociate public IP.
|
||||||
|
|
||||||
|
if use_nic.instance.security_groups:
|
||||||
|
use_nic.group_set.extend(use_nic.instance.security_groups)
|
||||||
|
|
||||||
|
use_nic.attach(instance.id, device_index)
|
||||||
|
|
||||||
|
nics[device_index] = use_nic
|
||||||
|
|
||||||
|
return nics
|
||||||
|
|
||||||
|
|
||||||
class Instance(BotoInstance, TaggedEC2Instance):
|
class Instance(BotoInstance, TaggedEC2Instance):
|
||||||
def __init__(self, image_id, user_data, security_groups, **kwargs):
|
def __init__(self, image_id, user_data, security_groups, **kwargs):
|
||||||
super(Instance, self).__init__()
|
super(Instance, self).__init__()
|
||||||
@ -108,6 +278,12 @@ class Instance(BotoInstance, TaggedEC2Instance):
|
|||||||
# string will have a "u" prefix -- need to get rid of it
|
# string will have a "u" prefix -- need to get rid of it
|
||||||
self.user_data[0] = self.user_data[0].encode('utf-8')
|
self.user_data[0] = self.user_data[0].encode('utf-8')
|
||||||
|
|
||||||
|
self.nics = ec2_backend.prep_nics_for_instance(self,
|
||||||
|
kwargs.get("nics", {}),
|
||||||
|
subnet_id=kwargs.get("subnet_id",None),
|
||||||
|
private_ip=kwargs.get("private_ip",None),
|
||||||
|
associate_public_ip=kwargs.get("associate_public_ip",None))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||||
properties = cloudformation_json['Properties']
|
properties = cloudformation_json['Properties']
|
||||||
@ -131,14 +307,23 @@ class Instance(BotoInstance, TaggedEC2Instance):
|
|||||||
return self.id
|
return self.id
|
||||||
|
|
||||||
def start(self, *args, **kwargs):
|
def start(self, *args, **kwargs):
|
||||||
|
for nic in self.nics.values():
|
||||||
|
nic.start()
|
||||||
|
|
||||||
self._state.name = "running"
|
self._state.name = "running"
|
||||||
self._state.code = 16
|
self._state.code = 16
|
||||||
|
|
||||||
def stop(self, *args, **kwargs):
|
def stop(self, *args, **kwargs):
|
||||||
|
for nic in self.nics.values():
|
||||||
|
nic.stop()
|
||||||
|
|
||||||
self._state.name = "stopped"
|
self._state.name = "stopped"
|
||||||
self._state.code = 80
|
self._state.code = 80
|
||||||
|
|
||||||
def terminate(self, *args, **kwargs):
|
def terminate(self, *args, **kwargs):
|
||||||
|
for nic in self.nics.values():
|
||||||
|
nic.stop()
|
||||||
|
|
||||||
self._state.name = "terminated"
|
self._state.name = "terminated"
|
||||||
self._state.code = 48
|
self._state.code = 48
|
||||||
|
|
||||||
@ -146,6 +331,21 @@ class Instance(BotoInstance, TaggedEC2Instance):
|
|||||||
self._state.name = "running"
|
self._state.name = "running"
|
||||||
self._state.code = 16
|
self._state.code = 16
|
||||||
|
|
||||||
|
def get_tags(self):
|
||||||
|
tags = ec2_backend.describe_tags(self.id)
|
||||||
|
return tags
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dynamic_group_list(self):
|
||||||
|
if self.nics:
|
||||||
|
groups = []
|
||||||
|
for nic in self.nics.values():
|
||||||
|
for group in nic.group_set:
|
||||||
|
groups.append(group)
|
||||||
|
return groups
|
||||||
|
else:
|
||||||
|
return self.security_groups
|
||||||
|
|
||||||
|
|
||||||
class InstanceBackend(object):
|
class InstanceBackend(object):
|
||||||
|
|
||||||
@ -511,6 +711,7 @@ class SecurityGroup(object):
|
|||||||
self.description = description
|
self.description = description
|
||||||
self.ingress_rules = []
|
self.ingress_rules = []
|
||||||
self.egress_rules = []
|
self.egress_rules = []
|
||||||
|
self.enis = {}
|
||||||
self.vpc_id = vpc_id
|
self.vpc_id = vpc_id
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -569,18 +770,23 @@ class SecurityGroupBackend(object):
|
|||||||
def describe_security_groups(self):
|
def describe_security_groups(self):
|
||||||
return itertools.chain(*[x.values() for x in self.groups.values()])
|
return itertools.chain(*[x.values() for x in self.groups.values()])
|
||||||
|
|
||||||
|
def _delete_security_group(self, vpc_id, group_id):
|
||||||
|
if self.groups[vpc_id][group_id].enis:
|
||||||
|
raise DependencyViolationError("{0} is being utilized by {1}".format(group_id, 'ENIs'))
|
||||||
|
return self.groups[vpc_id].pop(group_id)
|
||||||
|
|
||||||
def delete_security_group(self, name=None, group_id=None):
|
def delete_security_group(self, name=None, group_id=None):
|
||||||
if group_id:
|
if group_id:
|
||||||
# loop over all the SGs, find the right one
|
# loop over all the SGs, find the right one
|
||||||
for vpc in self.groups.values():
|
for vpc_id, groups in self.groups.items():
|
||||||
if group_id in vpc:
|
if group_id in groups:
|
||||||
return vpc.pop(group_id)
|
return self._delete_security_group(vpc_id, group_id)
|
||||||
raise InvalidSecurityGroupNotFoundError(group_id)
|
raise InvalidSecurityGroupNotFoundError(group_id)
|
||||||
elif name:
|
elif name:
|
||||||
# Group Name. Has to be in standard EC2, VPC needs to be identified by group_id
|
# Group Name. Has to be in standard EC2, VPC needs to be identified by group_id
|
||||||
group = self.get_security_group_from_name(name)
|
group = self.get_security_group_from_name(name)
|
||||||
if group:
|
if group:
|
||||||
return self.groups[None].pop(group.id)
|
return self._delete_security_group(None, group.id)
|
||||||
raise InvalidSecurityGroupNotFoundError(name)
|
raise InvalidSecurityGroupNotFoundError(name)
|
||||||
|
|
||||||
def get_security_group_from_id(self, group_id):
|
def get_security_group_from_id(self, group_id):
|
||||||
@ -1003,6 +1209,12 @@ class SubnetBackend(object):
|
|||||||
self.subnets = {}
|
self.subnets = {}
|
||||||
super(SubnetBackend, self).__init__()
|
super(SubnetBackend, self).__init__()
|
||||||
|
|
||||||
|
def get_subnet(self, subnet_id):
|
||||||
|
subnet = self.subnets.get(subnet_id, None)
|
||||||
|
if not subnet:
|
||||||
|
raise InvalidSubnetIdError(subnet_id)
|
||||||
|
return subnet
|
||||||
|
|
||||||
def create_subnet(self, vpc_id, cidr_block):
|
def create_subnet(self, vpc_id, cidr_block):
|
||||||
subnet_id = random_subnet_id()
|
subnet_id = random_subnet_id()
|
||||||
subnet = Subnet(subnet_id, vpc_id, cidr_block)
|
subnet = Subnet(subnet_id, vpc_id, cidr_block)
|
||||||
@ -1289,6 +1501,7 @@ class ElasticAddress(object):
|
|||||||
self.allocation_id = random_eip_allocation_id() if domain == "vpc" else None
|
self.allocation_id = random_eip_allocation_id() if domain == "vpc" else None
|
||||||
self.domain = domain
|
self.domain = domain
|
||||||
self.instance = None
|
self.instance = None
|
||||||
|
self.eni = None
|
||||||
self.association_id = None
|
self.association_id = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -1355,7 +1568,7 @@ class ElasticAddressBackend(object):
|
|||||||
|
|
||||||
return eips
|
return eips
|
||||||
|
|
||||||
def associate_address(self, instance, address=None, allocation_id=None, reassociate=False):
|
def associate_address(self, instance=None, eni=None, address=None, allocation_id=None, reassociate=False):
|
||||||
eips = []
|
eips = []
|
||||||
if address:
|
if address:
|
||||||
eips = self.address_by_ip([address])
|
eips = self.address_by_ip([address])
|
||||||
@ -1363,13 +1576,20 @@ class ElasticAddressBackend(object):
|
|||||||
eips = self.address_by_allocation([allocation_id])
|
eips = self.address_by_allocation([allocation_id])
|
||||||
eip = eips[0]
|
eip = eips[0]
|
||||||
|
|
||||||
if eip.instance and not reassociate:
|
new_instance_association = bool(instance and (not eip.instance or eip.instance.id == instance.id))
|
||||||
raise ResourceAlreadyAssociatedError(eip.public_ip)
|
new_eni_association = bool(eni and (not eip.eni or eni.id == eip.eni.id))
|
||||||
|
|
||||||
eip.instance = instance
|
if new_instance_association or new_eni_association or reassociate:
|
||||||
if eip.domain == "vpc":
|
eip.instance = instance
|
||||||
eip.association_id = random_eip_association_id()
|
eip.eni = eni
|
||||||
return eip
|
if eip.eni:
|
||||||
|
eip.eni.public_ip = eip.public_ip
|
||||||
|
if eip.domain == "vpc":
|
||||||
|
eip.association_id = random_eip_association_id()
|
||||||
|
|
||||||
|
return eip
|
||||||
|
|
||||||
|
raise ResourceAlreadyAssociatedError(eip.public_ip)
|
||||||
|
|
||||||
def describe_addresses(self):
|
def describe_addresses(self):
|
||||||
return self.addresses
|
return self.addresses
|
||||||
@ -1382,6 +1602,13 @@ class ElasticAddressBackend(object):
|
|||||||
eips = self.address_by_association([association_id])
|
eips = self.address_by_association([association_id])
|
||||||
eip = eips[0]
|
eip = eips[0]
|
||||||
|
|
||||||
|
if eip.eni:
|
||||||
|
if eip.eni.instance and eip.eni.instance._state.name == "running":
|
||||||
|
eip.eni.check_auto_public_ip()
|
||||||
|
else:
|
||||||
|
eip.eni.public_ip = None
|
||||||
|
eip.eni = None
|
||||||
|
|
||||||
eip.instance = None
|
eip.instance = None
|
||||||
eip.association_id = None
|
eip.association_id = None
|
||||||
return True
|
return True
|
||||||
@ -1474,6 +1701,7 @@ class DHCPOptionsSetBackend(object):
|
|||||||
class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend,
|
class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend,
|
||||||
RegionsAndZonesBackend, SecurityGroupBackend, EBSBackend,
|
RegionsAndZonesBackend, SecurityGroupBackend, EBSBackend,
|
||||||
VPCBackend, SubnetBackend, SubnetRouteTableAssociationBackend,
|
VPCBackend, SubnetBackend, SubnetRouteTableAssociationBackend,
|
||||||
|
NetworkInterfaceBackend,
|
||||||
VPCPeeringConnectionBackend,
|
VPCPeeringConnectionBackend,
|
||||||
RouteTableBackend, RouteBackend, InternetGatewayBackend,
|
RouteTableBackend, RouteBackend, InternetGatewayBackend,
|
||||||
VPCGatewayAttachmentBackend, SpotRequestBackend,
|
VPCGatewayAttachmentBackend, SpotRequestBackend,
|
||||||
|
@ -17,10 +17,12 @@ class ElasticIPAddresses(BaseResponse):
|
|||||||
return template.render(address=address)
|
return template.render(address=address)
|
||||||
|
|
||||||
def associate_address(self):
|
def associate_address(self):
|
||||||
|
instance = eni = None
|
||||||
|
|
||||||
if "InstanceId" in self.querystring:
|
if "InstanceId" in self.querystring:
|
||||||
instance = ec2_backend.get_instance(self.querystring['InstanceId'][0])
|
instance = ec2_backend.get_instance(self.querystring['InstanceId'][0])
|
||||||
elif "NetworkInterfaceId" in self.querystring:
|
elif "NetworkInterfaceId" in self.querystring:
|
||||||
raise NotImplementedError("Lookup by allocation id not implemented")
|
eni = ec2_backend.get_network_interface(self.querystring['NetworkInterfaceId'][0])
|
||||||
else:
|
else:
|
||||||
ec2_backend.raise_error("MissingParameter", "Invalid request, expect InstanceId/NetworkId parameter.")
|
ec2_backend.raise_error("MissingParameter", "Invalid request, expect InstanceId/NetworkId parameter.")
|
||||||
|
|
||||||
@ -28,12 +30,15 @@ class ElasticIPAddresses(BaseResponse):
|
|||||||
if "AllowReassociation" in self.querystring:
|
if "AllowReassociation" in self.querystring:
|
||||||
reassociate = self.querystring['AllowReassociation'][0] == "true"
|
reassociate = self.querystring['AllowReassociation'][0] == "true"
|
||||||
|
|
||||||
if "PublicIp" in self.querystring:
|
if instance or eni:
|
||||||
eip = ec2_backend.associate_address(instance, address=self.querystring['PublicIp'][0], reassociate=reassociate)
|
if "PublicIp" in self.querystring:
|
||||||
elif "AllocationId" in self.querystring:
|
eip = ec2_backend.associate_address(instance=instance, eni=eni, address=self.querystring['PublicIp'][0], reassociate=reassociate)
|
||||||
eip = ec2_backend.associate_address(instance, allocation_id=self.querystring['AllocationId'][0], reassociate=reassociate)
|
elif "AllocationId" in self.querystring:
|
||||||
|
eip = ec2_backend.associate_address(instance=instance, eni=eni, allocation_id=self.querystring['AllocationId'][0], reassociate=reassociate)
|
||||||
|
else:
|
||||||
|
ec2_backend.raise_error("MissingParameter", "Invalid request, expect PublicIp/AllocationId parameter.")
|
||||||
else:
|
else:
|
||||||
ec2_backend.raise_error("MissingParameter", "Invalid request, expect PublicIp/AllocationId parameter.")
|
ec2_backend.raise_error("MissingParameter", "Invalid request, expect either instance or ENI.")
|
||||||
|
|
||||||
template = Template(ASSOCIATE_ADDRESS_RESPONSE)
|
template = Template(ASSOCIATE_ADDRESS_RESPONSE)
|
||||||
return template.render(address=eip)
|
return template.render(address=eip)
|
||||||
@ -103,6 +108,11 @@ DESCRIBE_ADDRESS_RESPONSE = """<DescribeAddressesResponse xmlns="http://ec2.amaz
|
|||||||
{% else %}
|
{% else %}
|
||||||
<instanceId/>
|
<instanceId/>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if address.eni %}
|
||||||
|
<networkInterfaceId>{{ address.eni.id }}</networkInterfaceId>
|
||||||
|
{% else %}
|
||||||
|
<networkInterfaceId/>
|
||||||
|
{% endif %}
|
||||||
{% if address.allocation_id %}
|
{% if address.allocation_id %}
|
||||||
<allocationId>{{ address.allocation_id }}</allocationId>
|
<allocationId>{{ address.allocation_id }}</allocationId>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
from jinja2 import Template
|
||||||
|
|
||||||
from moto.core.responses import BaseResponse
|
from moto.core.responses import BaseResponse
|
||||||
|
from moto.ec2.models import ec2_backend
|
||||||
|
from moto.ec2.utils import sequence_from_querystring, resource_ids_from_querystring, filters_from_querystring
|
||||||
|
|
||||||
|
|
||||||
class ElasticNetworkInterfaces(BaseResponse):
|
class ElasticNetworkInterfaces(BaseResponse):
|
||||||
@ -7,22 +11,157 @@ class ElasticNetworkInterfaces(BaseResponse):
|
|||||||
raise NotImplementedError('ElasticNetworkInterfaces(AmazonVPC).attach_network_interface is not yet implemented')
|
raise NotImplementedError('ElasticNetworkInterfaces(AmazonVPC).attach_network_interface is not yet implemented')
|
||||||
|
|
||||||
def create_network_interface(self):
|
def create_network_interface(self):
|
||||||
raise NotImplementedError('ElasticNetworkInterfaces(AmazonVPC).create_network_interface is not yet implemented')
|
subnet_id = self.querystring.get('SubnetId')[0]
|
||||||
|
private_ip_address = self.querystring.get('PrivateIpAddress', [None])[0]
|
||||||
|
groups = sequence_from_querystring('SecurityGroupId', self.querystring)
|
||||||
|
subnet = ec2_backend.get_subnet(subnet_id)
|
||||||
|
eni = ec2_backend.create_network_interface(subnet, private_ip_address, groups)
|
||||||
|
template = Template(CREATE_NETWORK_INTERFACE_RESPONSE)
|
||||||
|
return template.render(eni=eni)
|
||||||
|
|
||||||
def delete_network_interface(self):
|
def delete_network_interface(self):
|
||||||
raise NotImplementedError('ElasticNetworkInterfaces(AmazonVPC).delete_network_interface is not yet implemented')
|
eni_id = self.querystring.get('NetworkInterfaceId')[0]
|
||||||
|
eni = ec2_backend.delete_network_interface(eni_id)
|
||||||
|
template = Template(DELETE_NETWORK_INTERFACE_RESPONSE)
|
||||||
|
return template.render()
|
||||||
|
|
||||||
def describe_network_interface_attribute(self):
|
def describe_network_interface_attribute(self):
|
||||||
raise NotImplementedError('ElasticNetworkInterfaces(AmazonVPC).describe_network_interface_attribute is not yet implemented')
|
raise NotImplementedError('ElasticNetworkInterfaces(AmazonVPC).describe_network_interface_attribute is not yet implemented')
|
||||||
|
|
||||||
def describe_network_interfaces(self):
|
def describe_network_interfaces(self):
|
||||||
raise NotImplementedError('ElasticNetworkInterfaces(AmazonVPC).describe_network_interfaces is not yet implemented')
|
#Partially implemented. Supports only network-interface-id and group-id filters
|
||||||
|
filters = filters_from_querystring(self.querystring)
|
||||||
|
enis = ec2_backend.describe_network_interfaces(filters)
|
||||||
|
template = Template(DESCRIBE_NETWORK_INTERFACES_RESPONSE)
|
||||||
|
return template.render(enis=enis)
|
||||||
|
|
||||||
def detach_network_interface(self):
|
def detach_network_interface(self):
|
||||||
raise NotImplementedError('ElasticNetworkInterfaces(AmazonVPC).detach_network_interface is not yet implemented')
|
raise NotImplementedError('ElasticNetworkInterfaces(AmazonVPC).detach_network_interface is not yet implemented')
|
||||||
|
|
||||||
def modify_network_interface_attribute(self):
|
def modify_network_interface_attribute(self):
|
||||||
raise NotImplementedError('ElasticNetworkInterfaces(AmazonVPC).modify_network_interface_attribute is not yet implemented')
|
#Currently supports modifying one and only one security group
|
||||||
|
eni_id = self.querystring.get('NetworkInterfaceId')[0]
|
||||||
|
group_id = self.querystring.get('SecurityGroupId.1')[0]
|
||||||
|
ec2_backend.modify_network_interface_attribute(eni_id, group_id)
|
||||||
|
return MODIFY_NETWORK_INTERFACE_ATTRIBUTE_RESPONSE
|
||||||
|
|
||||||
def reset_network_interface_attribute(self):
|
def reset_network_interface_attribute(self):
|
||||||
raise NotImplementedError('ElasticNetworkInterfaces(AmazonVPC).reset_network_interface_attribute is not yet implemented')
|
raise NotImplementedError('ElasticNetworkInterfaces(AmazonVPC).reset_network_interface_attribute is not yet implemented')
|
||||||
|
|
||||||
|
CREATE_NETWORK_INTERFACE_RESPONSE = """
|
||||||
|
<CreateNetworkInterfaceResponse xmlns="http://ec2.amazonaws.com/doc/2013-07-15/">
|
||||||
|
<requestId>2c6021ec-d705-445a-9780-420d0c7ab793</requestId>
|
||||||
|
<networkInterface>
|
||||||
|
<networkInterfaceId>{{ eni.id }}</networkInterfaceId>
|
||||||
|
<subnetId>{{ eni.subnet.id }}</subnetId>
|
||||||
|
<vpcId>{{ eni.subnet.vpc_id }}</vpcId>
|
||||||
|
<availabilityZone>us-west-2a</availabilityZone>
|
||||||
|
<description/>
|
||||||
|
<ownerId>498654062920</ownerId>
|
||||||
|
<requesterManaged>false</requesterManaged>
|
||||||
|
<status>pending</status>
|
||||||
|
<macAddress>02:07:a9:b6:12:51</macAddress>
|
||||||
|
{% if eni.private_ip_address %}
|
||||||
|
<privateIpAddress>{{ eni.private_ip_address }}</privateIpAddress>
|
||||||
|
{% endif %}
|
||||||
|
<sourceDestCheck>true</sourceDestCheck>
|
||||||
|
<groupSet>
|
||||||
|
{% for group in eni.group_set %}
|
||||||
|
<item>
|
||||||
|
<groupId>{{ group.id }}</groupId>
|
||||||
|
<groupName>{{ group.name }}</groupName>
|
||||||
|
</item>
|
||||||
|
{% endfor %}
|
||||||
|
</groupSet>
|
||||||
|
<tagSet/>
|
||||||
|
{% if eni.private_ip_address %}
|
||||||
|
<privateIpAddressesSet>
|
||||||
|
<item>
|
||||||
|
<privateIpAddress>{{ eni.private_ip_address }}</privateIpAddress>
|
||||||
|
<primary>true</primary>
|
||||||
|
</item>
|
||||||
|
</privateIpAddressesSet>
|
||||||
|
{% else %}
|
||||||
|
<privateIpAddressesSet/>
|
||||||
|
{% endif %}
|
||||||
|
</networkInterface>
|
||||||
|
</CreateNetworkInterfaceResponse>
|
||||||
|
"""
|
||||||
|
|
||||||
|
DESCRIBE_NETWORK_INTERFACES_RESPONSE = """<DescribeNetworkInterfacesResponse xmlns="http://ec2.amazonaws.com/doc/2013-07-15/">
|
||||||
|
<requestId>ddb0aaf1-8b65-4f0a-94fa-654b18b8a204</requestId>
|
||||||
|
<networkInterfaceSet>
|
||||||
|
{% for eni in enis %}
|
||||||
|
<item>
|
||||||
|
<networkInterfaceId>{{ eni.id }}</networkInterfaceId>
|
||||||
|
<subnetId>{{ eni.subnet.id }}</subnetId>
|
||||||
|
<vpcId>vpc-9367a6f8</vpcId>
|
||||||
|
<availabilityZone>us-west-2a</availabilityZone>
|
||||||
|
<description>Primary network interface</description>
|
||||||
|
<ownerId>190610284047</ownerId>
|
||||||
|
<requesterManaged>false</requesterManaged>
|
||||||
|
<status>in-use</status>
|
||||||
|
<macAddress>0e:a3:a7:7b:95:a7</macAddress>
|
||||||
|
{% if eni.private_ip_address %}
|
||||||
|
<privateIpAddress>{{ eni.private_ip_address }}</privateIpAddress>
|
||||||
|
{% endif %}
|
||||||
|
<privateDnsName>ip-10-0-0-134.us-west-2.compute.internal</privateDnsName>
|
||||||
|
<sourceDestCheck>true</sourceDestCheck>
|
||||||
|
<groupSet>
|
||||||
|
{% for group in eni.group_set %}
|
||||||
|
<item>
|
||||||
|
<groupId>{{ group.id }}</groupId>
|
||||||
|
<groupName>{{ group.name }}</groupName>
|
||||||
|
</item>
|
||||||
|
{% endfor %}
|
||||||
|
</groupSet>
|
||||||
|
{% for attachment in eni.attachments %}
|
||||||
|
<attachment>
|
||||||
|
<attachmentId>{{ attachment['attachmentId'] }}</attachmentId>
|
||||||
|
<instanceId>{{ attachment['instanceId'] }}</instanceId>
|
||||||
|
<instanceOwnerId>190610284047</instanceOwnerId>
|
||||||
|
<deviceIndex>{{ attachment['deviceIndex'] }}</deviceIndex>
|
||||||
|
<status>attached</status>
|
||||||
|
<attachTime>2013-10-04T17:38:53.000Z</attachTime>
|
||||||
|
<deleteOnTermination>true</deleteOnTermination>
|
||||||
|
</attachment>
|
||||||
|
{% endfor %}
|
||||||
|
<association>
|
||||||
|
<publicIp>{{ eni.public_ip }}</publicIp>
|
||||||
|
<publicDnsName>ec2-54-200-86-47.us-west-2.compute.amazonaws.com</publicDnsName>
|
||||||
|
<ipOwnerId>amazon</ipOwnerId>
|
||||||
|
</association>
|
||||||
|
<tagSet/>
|
||||||
|
{% if eni.private_ip_address %}
|
||||||
|
<privateIpAddressesSet>
|
||||||
|
<item>
|
||||||
|
<privateIpAddress>{{ eni.private_ip_address }}</privateIpAddress>
|
||||||
|
<privateDnsName>ip-10-0-0-134.us-west-2.compute.internal</privateDnsName>
|
||||||
|
<primary>true</primary>
|
||||||
|
{% if eni.public_ip %}
|
||||||
|
<association>
|
||||||
|
<publicIp>{{ eni.public_ip }}</publicIp>
|
||||||
|
<publicDnsName>ec2-54-200-86-47.us-west-2.compute.amazonaws.com</publicDnsName>
|
||||||
|
<ipOwnerId>amazon</ipOwnerId>
|
||||||
|
</association>
|
||||||
|
{% endif %}
|
||||||
|
</item>
|
||||||
|
</privateIpAddressesSet>
|
||||||
|
{% else %}
|
||||||
|
<privateIpAddressesSet/>
|
||||||
|
{% endif %}
|
||||||
|
</item>
|
||||||
|
{% endfor %}
|
||||||
|
</networkInterfaceSet>
|
||||||
|
</DescribeNetworkInterfacesResponse>"""
|
||||||
|
|
||||||
|
MODIFY_NETWORK_INTERFACE_ATTRIBUTE_RESPONSE = """<ModifyNetworkInterfaceAttributeResponse xmlns="http://ec2.amazonaws.com/doc/2012-12-01/">
|
||||||
|
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
|
||||||
|
<return>true</return>
|
||||||
|
</ModifyNetworkInterfaceAttributeResponse>"""
|
||||||
|
|
||||||
|
DELETE_NETWORK_INTERFACE_RESPONSE = """
|
||||||
|
<DeleteNetworkInterfaceResponse xmlns="http://ec2.amazonaws.com/doc/2013-07-15/">
|
||||||
|
<requestId>34b5b3b4-d0c5-49b9-b5e2-a468ef6adcd8</requestId>
|
||||||
|
<return>true</return>
|
||||||
|
</DeleteNetworkInterfaceResponse>"""
|
||||||
|
@ -3,7 +3,7 @@ from jinja2 import Template
|
|||||||
|
|
||||||
from moto.core.responses import BaseResponse
|
from moto.core.responses import BaseResponse
|
||||||
from moto.core.utils import camelcase_to_underscores
|
from moto.core.utils import camelcase_to_underscores
|
||||||
from moto.ec2.utils import instance_ids_from_querystring, filters_from_querystring, filter_reservations
|
from moto.ec2.utils import instance_ids_from_querystring, filters_from_querystring, filter_reservations, dict_from_querystring
|
||||||
|
|
||||||
|
|
||||||
class InstanceResponse(BaseResponse):
|
class InstanceResponse(BaseResponse):
|
||||||
@ -26,13 +26,19 @@ class InstanceResponse(BaseResponse):
|
|||||||
user_data = self.querystring.get('UserData')
|
user_data = self.querystring.get('UserData')
|
||||||
security_group_names = self._get_multi_param('SecurityGroup')
|
security_group_names = self._get_multi_param('SecurityGroup')
|
||||||
security_group_ids = self._get_multi_param('SecurityGroupId')
|
security_group_ids = self._get_multi_param('SecurityGroupId')
|
||||||
|
nics = dict_from_querystring("NetworkInterface", self.querystring)
|
||||||
instance_type = self.querystring.get("InstanceType", ["m1.small"])[0]
|
instance_type = self.querystring.get("InstanceType", ["m1.small"])[0]
|
||||||
subnet_id = self.querystring.get("SubnetId", [None])[0]
|
subnet_id = self.querystring.get("SubnetId", [None])[0]
|
||||||
|
private_ip = self.querystring.get("PrivateIpAddress", [None])[0]
|
||||||
|
associate_public_ip = self.querystring.get("AssociatePublicIpAddress", [None])[0]
|
||||||
key_name = self.querystring.get("KeyName", [None])[0]
|
key_name = self.querystring.get("KeyName", [None])[0]
|
||||||
|
|
||||||
new_reservation = self.ec2_backend.add_instances(
|
new_reservation = self.ec2_backend.add_instances(
|
||||||
image_id, min_count, user_data, security_group_names,
|
image_id, min_count, user_data, security_group_names,
|
||||||
instance_type=instance_type, subnet_id=subnet_id,
|
instance_type=instance_type, subnet_id=subnet_id,
|
||||||
key_name=key_name, security_group_ids=security_group_ids)
|
key_name=key_name, security_group_ids=security_group_ids,
|
||||||
|
nics=nics, private_ip=private_ip, associate_public_ip=associate_public_ip)
|
||||||
|
|
||||||
template = Template(EC2_RUN_INSTANCES)
|
template = Template(EC2_RUN_INSTANCES)
|
||||||
return template.render(reservation=new_reservation)
|
return template.render(reservation=new_reservation)
|
||||||
|
|
||||||
@ -189,10 +195,19 @@ EC2_RUN_INSTANCES = """<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc
|
|||||||
<monitoring>
|
<monitoring>
|
||||||
<state>enabled</state>
|
<state>enabled</state>
|
||||||
</monitoring>
|
</monitoring>
|
||||||
<subnetId>{{ instance.subnet_id }}</subnetId>
|
{% if instance.nics %}
|
||||||
|
<subnetId>{{ instance.nics[0].subnet.id }}</subnetId>
|
||||||
|
<vpcId>{{ instance.nics[0].subnet.vpc_id }}</vpcId>
|
||||||
|
<privateIpAddress>{{ instance.nics[0].private_ip_address }}</privateIpAddress>
|
||||||
|
{% if instance.nics[0].public_ip %}
|
||||||
|
<ipAddress>46.51.219.63</ipAddress>
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<subnetId>{{ instance.subnet_id }}</subnetId>
|
||||||
|
{% endif %}
|
||||||
<sourceDestCheck>true</sourceDestCheck>
|
<sourceDestCheck>true</sourceDestCheck>
|
||||||
<groupSet>
|
<groupSet>
|
||||||
{% for group in instance.security_groups %}
|
{% for group in instance.dynamic_group_list %}
|
||||||
<item>
|
<item>
|
||||||
<groupId>{{ group.id }}</groupId>
|
<groupId>{{ group.id }}</groupId>
|
||||||
<groupName>{{ group.name }}</groupName>
|
<groupName>{{ group.name }}</groupName>
|
||||||
@ -208,6 +223,54 @@ EC2_RUN_INSTANCES = """<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc
|
|||||||
<clientToken/>
|
<clientToken/>
|
||||||
<hypervisor>xen</hypervisor>
|
<hypervisor>xen</hypervisor>
|
||||||
<ebsOptimized>false</ebsOptimized>
|
<ebsOptimized>false</ebsOptimized>
|
||||||
|
<networkInterfaceSet>
|
||||||
|
{% for nic in instance.nics.values() %}
|
||||||
|
<item>
|
||||||
|
<networkInterfaceId>{{ nic.id }}</networkInterfaceId>
|
||||||
|
<subnetId>{{ nic.subnet.id }}</subnetId>
|
||||||
|
<vpcId>{{ nic.subnet.vpc_id }}</vpcId>
|
||||||
|
<description>Primary network interface</description>
|
||||||
|
<ownerId>111122223333</ownerId>
|
||||||
|
<status>in-use</status>
|
||||||
|
<macAddress>1b:2b:3c:4d:5e:6f</macAddress>
|
||||||
|
<privateIpAddress>{{ nic.private_ip_address }}</privateIpAddress>
|
||||||
|
<sourceDestCheck>true</sourceDestCheck>
|
||||||
|
<groupSet>
|
||||||
|
{% for group in nic.group_set %}
|
||||||
|
<item>
|
||||||
|
<groupId>{{ group.id }}</groupId>
|
||||||
|
<groupName>{{ group.name }}</groupName>
|
||||||
|
</item>
|
||||||
|
{% endfor %}
|
||||||
|
</groupSet>
|
||||||
|
<attachment>
|
||||||
|
<attachmentId>eni-attach-1a2b3c4d</attachmentId>
|
||||||
|
<deviceIndex>{{ nic.device_index }}</deviceIndex>
|
||||||
|
<status>attached</status>
|
||||||
|
<attachTime>YYYY-MM-DDTHH:MM:SS+0000</attachTime>
|
||||||
|
<deleteOnTermination>true</deleteOnTermination>
|
||||||
|
</attachment>
|
||||||
|
{% if nic.public_ip %}
|
||||||
|
<association>
|
||||||
|
<publicIp>{{ nic.public_ip }}</publicIp>
|
||||||
|
<ipOwnerId>111122223333</ipOwnerId>
|
||||||
|
</association>
|
||||||
|
{% endif %}
|
||||||
|
<privateIpAddressesSet>
|
||||||
|
<item>
|
||||||
|
<privateIpAddress>{{ nic.private_ip_address }}</privateIpAddress>
|
||||||
|
<primary>true</primary>
|
||||||
|
{% if nic.public_ip %}
|
||||||
|
<association>
|
||||||
|
<publicIp>{{ nic.public_ip }}</publicIp>
|
||||||
|
<ipOwnerId>111122223333</ipOwnerId>
|
||||||
|
</association>
|
||||||
|
{% endif %}
|
||||||
|
</item>
|
||||||
|
</privateIpAddressesSet>
|
||||||
|
</item>
|
||||||
|
{% endfor %}
|
||||||
|
</networkInterfaceSet>
|
||||||
</item>
|
</item>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</instancesSet>
|
</instancesSet>
|
||||||
@ -220,7 +283,14 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns='http://ec2.amazona
|
|||||||
<item>
|
<item>
|
||||||
<reservationId>{{ reservation.id }}</reservationId>
|
<reservationId>{{ reservation.id }}</reservationId>
|
||||||
<ownerId>111122223333</ownerId>
|
<ownerId>111122223333</ownerId>
|
||||||
<groupSet></groupSet>
|
<groupSet>
|
||||||
|
{% for group in reservation.dynamic_group_list %}
|
||||||
|
<item>
|
||||||
|
<groupId>{{ group.id }}</groupId>
|
||||||
|
<groupName>{{ group.name }}</groupName>
|
||||||
|
</item>
|
||||||
|
{% endfor %}
|
||||||
|
</groupSet>
|
||||||
<instancesSet>
|
<instancesSet>
|
||||||
{% for instance in reservation.instances %}
|
{% for instance in reservation.instances %}
|
||||||
<item>
|
<item>
|
||||||
@ -249,13 +319,17 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns='http://ec2.amazona
|
|||||||
<monitoring>
|
<monitoring>
|
||||||
<state>disabled</state>
|
<state>disabled</state>
|
||||||
</monitoring>
|
</monitoring>
|
||||||
<subnetId>{{ instance.subnet_id }}</subnetId>
|
{% if instance.nics %}
|
||||||
<vpcId>vpc-1a2b3c4d</vpcId>
|
<subnetId>{{ instance.nics[0].subnet.id }}</subnetId>
|
||||||
<privateIpAddress>10.0.0.12</privateIpAddress>
|
<vpcId>{{ instance.nics[0].subnet.vpc_id }}</vpcId>
|
||||||
<ipAddress>46.51.219.63</ipAddress>
|
<privateIpAddress>{{ instance.nics[0].private_ip_address }}</privateIpAddress>
|
||||||
|
{% if instance.nics[0].public_ip %}
|
||||||
|
<ipAddress>46.51.219.63</ipAddress>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
<sourceDestCheck>true</sourceDestCheck>
|
<sourceDestCheck>true</sourceDestCheck>
|
||||||
<groupSet>
|
<groupSet>
|
||||||
{% for group in instance.security_groups %}
|
{% for group in instance.dynamic_group_list %}
|
||||||
<item>
|
<item>
|
||||||
<groupId>{{ group.id }}</groupId>
|
<groupId>{{ group.id }}</groupId>
|
||||||
<groupName>{{ group.name }}</groupName>
|
<groupName>{{ group.name }}</groupName>
|
||||||
@ -280,7 +354,54 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns='http://ec2.amazona
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tagSet>
|
</tagSet>
|
||||||
<hypervisor>xen</hypervisor>
|
<hypervisor>xen</hypervisor>
|
||||||
<networkInterfaceSet />
|
<networkInterfaceSet>
|
||||||
|
{% for nic in instance.nics.values() %}
|
||||||
|
<item>
|
||||||
|
<networkInterfaceId>{{ nic.id }}</networkInterfaceId>
|
||||||
|
<subnetId>{{ nic.subnet.id }}</subnetId>
|
||||||
|
<vpcId>{{ nic.subnet.vpc_id }}</vpcId>
|
||||||
|
<description>Primary network interface</description>
|
||||||
|
<ownerId>111122223333</ownerId>
|
||||||
|
<status>in-use</status>
|
||||||
|
<macAddress>1b:2b:3c:4d:5e:6f</macAddress>
|
||||||
|
<privateIpAddress>{{ nic.private_ip_address }}</privateIpAddress>
|
||||||
|
<sourceDestCheck>true</sourceDestCheck>
|
||||||
|
<groupSet>
|
||||||
|
{% for group in nic.group_set %}
|
||||||
|
<item>
|
||||||
|
<groupId>{{ group.id }}</groupId>
|
||||||
|
<groupName>{{ group.name }}</groupName>
|
||||||
|
</item>
|
||||||
|
{% endfor %}
|
||||||
|
</groupSet>
|
||||||
|
<attachment>
|
||||||
|
<attachmentId>eni-attach-1a2b3c4d</attachmentId>
|
||||||
|
<deviceIndex>{{ nic.device_index }}</deviceIndex>
|
||||||
|
<status>attached</status>
|
||||||
|
<attachTime>YYYY-MM-DDTHH:MM:SS+0000</attachTime>
|
||||||
|
<deleteOnTermination>true</deleteOnTermination>
|
||||||
|
</attachment>
|
||||||
|
{% if nic.public_ip %}
|
||||||
|
<association>
|
||||||
|
<publicIp>{{ nic.public_ip }}</publicIp>
|
||||||
|
<ipOwnerId>111122223333</ipOwnerId>
|
||||||
|
</association>
|
||||||
|
{% endif %}
|
||||||
|
<privateIpAddressesSet>
|
||||||
|
<item>
|
||||||
|
<privateIpAddress>{{ nic.private_ip_address }}</privateIpAddress>
|
||||||
|
<primary>true</primary>
|
||||||
|
{% if nic.public_ip %}
|
||||||
|
<association>
|
||||||
|
<publicIp>{{ nic.public_ip }}</publicIp>
|
||||||
|
<ipOwnerId>111122223333</ipOwnerId>
|
||||||
|
</association>
|
||||||
|
{% endif %}
|
||||||
|
</item>
|
||||||
|
</privateIpAddressesSet>
|
||||||
|
</item>
|
||||||
|
{% endfor %}
|
||||||
|
</networkInterfaceSet>
|
||||||
</item>
|
</item>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</instancesSet>
|
</instancesSet>
|
||||||
|
@ -72,6 +72,18 @@ def random_dhcp_option_id():
|
|||||||
return random_id(prefix='dopt')
|
return random_id(prefix='dopt')
|
||||||
|
|
||||||
|
|
||||||
|
def random_eni_id():
|
||||||
|
return random_id(prefix='eni')
|
||||||
|
|
||||||
|
|
||||||
|
def random_eni_attach_id():
|
||||||
|
return random_id(prefix='eni-attach')
|
||||||
|
|
||||||
|
|
||||||
|
def random_public_ip():
|
||||||
|
return '54.214.{0}.{1}'.format(random.choice(range(255)),
|
||||||
|
random.choice(range(255)))
|
||||||
|
|
||||||
def random_ip():
|
def random_ip():
|
||||||
return "127.{0}.{1}.{2}".format(
|
return "127.{0}.{1}.{2}".format(
|
||||||
random.randint(0, 255),
|
random.randint(0, 255),
|
||||||
@ -172,6 +184,21 @@ def filters_from_querystring(querystring_dict):
|
|||||||
return response_values
|
return response_values
|
||||||
|
|
||||||
|
|
||||||
|
def dict_from_querystring(parameter, querystring_dict):
|
||||||
|
use_dict={}
|
||||||
|
for key, value in querystring_dict.items():
|
||||||
|
match = re.search(r"{0}.(\d).(\w+)".format(parameter), key)
|
||||||
|
if match:
|
||||||
|
use_dict_index = match.groups()[0]
|
||||||
|
use_dict_element_property = match.groups()[1]
|
||||||
|
|
||||||
|
if not use_dict.get(use_dict_index):
|
||||||
|
use_dict[use_dict_index] = {}
|
||||||
|
use_dict[use_dict_index][use_dict_element_property]=value[0]
|
||||||
|
|
||||||
|
return use_dict
|
||||||
|
|
||||||
|
|
||||||
def keypair_names_from_querystring(querystring_dict):
|
def keypair_names_from_querystring(querystring_dict):
|
||||||
keypair_names = []
|
keypair_names = []
|
||||||
for key, value in querystring_dict.items():
|
for key, value in querystring_dict.items():
|
||||||
|
@ -110,29 +110,89 @@ def test_eip_associate_vpc():
|
|||||||
|
|
||||||
instance.terminate()
|
instance.terminate()
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_eip_associate_network_interface():
|
||||||
|
"""Associate/Disassociate EIP to NIC"""
|
||||||
|
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||||
|
vpc = conn.create_vpc("10.0.0.0/16")
|
||||||
|
subnet = conn.create_subnet(vpc.id, "10.0.0.0/18")
|
||||||
|
eni = conn.create_network_interface(subnet.id)
|
||||||
|
|
||||||
|
eip = conn.allocate_address(domain='vpc')
|
||||||
|
eip.network_interface_id.should.be.none
|
||||||
|
|
||||||
|
with assert_raises(EC2ResponseError) as cm:
|
||||||
|
conn.associate_address(network_interface_id=eni.id)
|
||||||
|
cm.exception.code.should.equal('MissingParameter')
|
||||||
|
cm.exception.status.should.equal(400)
|
||||||
|
cm.exception.request_id.should_not.be.none
|
||||||
|
|
||||||
|
conn.associate_address(network_interface_id=eni.id, allocation_id=eip.allocation_id)
|
||||||
|
eip = conn.get_all_addresses(addresses=[eip.public_ip])[0] # no .update() on address ):
|
||||||
|
eip.network_interface_id.should.be.equal(eni.id)
|
||||||
|
conn.disassociate_address(association_id=eip.association_id)
|
||||||
|
eip = conn.get_all_addresses(addresses=[eip.public_ip])[0] # no .update() on address ):
|
||||||
|
eip.network_interface_id.should.be.equal(u'')
|
||||||
|
eip.association_id.should.be.none
|
||||||
|
eip.release()
|
||||||
|
eip = None
|
||||||
|
|
||||||
@mock_ec2
|
@mock_ec2
|
||||||
def test_eip_reassociate():
|
def test_eip_reassociate():
|
||||||
"""reassociate EIP"""
|
"""reassociate EIP"""
|
||||||
conn = boto.connect_ec2('the_key', 'the_secret')
|
conn = boto.connect_ec2('the_key', 'the_secret')
|
||||||
|
|
||||||
reservation = conn.run_instances('ami-1234abcd')
|
reservation = conn.run_instances('ami-1234abcd', min_count=2)
|
||||||
instance = reservation.instances[0]
|
instance1, instance2 = reservation.instances
|
||||||
|
|
||||||
eip = conn.allocate_address()
|
eip = conn.allocate_address()
|
||||||
conn.associate_address(instance_id=instance.id, public_ip=eip.public_ip)
|
conn.associate_address(instance_id=instance1.id, public_ip=eip.public_ip)
|
||||||
|
|
||||||
|
# Same ID is idempotent
|
||||||
|
conn.associate_address(instance_id=instance1.id, public_ip=eip.public_ip)
|
||||||
|
|
||||||
|
# Different ID detects resource association
|
||||||
with assert_raises(EC2ResponseError) as cm:
|
with assert_raises(EC2ResponseError) as cm:
|
||||||
conn.associate_address(instance_id=instance.id, public_ip=eip.public_ip, allow_reassociation=False)
|
conn.associate_address(instance_id=instance2.id, public_ip=eip.public_ip, allow_reassociation=False)
|
||||||
cm.exception.code.should.equal('Resource.AlreadyAssociated')
|
cm.exception.code.should.equal('Resource.AlreadyAssociated')
|
||||||
cm.exception.status.should.equal(400)
|
cm.exception.status.should.equal(400)
|
||||||
cm.exception.request_id.should_not.be.none
|
cm.exception.request_id.should_not.be.none
|
||||||
|
|
||||||
conn.associate_address.when.called_with(instance_id=instance.id, public_ip=eip.public_ip, allow_reassociation=True).should_not.throw(EC2ResponseError)
|
conn.associate_address.when.called_with(instance_id=instance2.id, public_ip=eip.public_ip, allow_reassociation=True).should_not.throw(EC2ResponseError)
|
||||||
|
|
||||||
eip.release()
|
eip.release()
|
||||||
eip = None
|
eip = None
|
||||||
|
|
||||||
instance.terminate()
|
instance1.terminate()
|
||||||
|
instance2.terminate()
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_eip_reassociate_nic():
|
||||||
|
"""reassociate EIP"""
|
||||||
|
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||||
|
|
||||||
|
vpc = conn.create_vpc("10.0.0.0/16")
|
||||||
|
subnet = conn.create_subnet(vpc.id, "10.0.0.0/18")
|
||||||
|
eni1 = conn.create_network_interface(subnet.id)
|
||||||
|
eni2 = conn.create_network_interface(subnet.id)
|
||||||
|
|
||||||
|
eip = conn.allocate_address()
|
||||||
|
conn.associate_address(network_interface_id=eni1.id, public_ip=eip.public_ip)
|
||||||
|
|
||||||
|
# Same ID is idempotent
|
||||||
|
conn.associate_address(network_interface_id=eni1.id, public_ip=eip.public_ip)
|
||||||
|
|
||||||
|
# Different ID detects resource association
|
||||||
|
with assert_raises(EC2ResponseError) as cm:
|
||||||
|
conn.associate_address(network_interface_id=eni2.id, public_ip=eip.public_ip)
|
||||||
|
cm.exception.code.should.equal('Resource.AlreadyAssociated')
|
||||||
|
cm.exception.status.should.equal(400)
|
||||||
|
cm.exception.request_id.should_not.be.none
|
||||||
|
|
||||||
|
conn.associate_address.when.called_with(network_interface_id=eni2.id, public_ip=eip.public_ip, allow_reassociation=True).should_not.throw(EC2ResponseError)
|
||||||
|
|
||||||
|
eip.release()
|
||||||
|
eip = None
|
||||||
|
|
||||||
@mock_ec2
|
@mock_ec2
|
||||||
def test_eip_associate_invalid_args():
|
def test_eip_associate_invalid_args():
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
# Ensure 'assert_raises' context manager support for Python 2.6
|
||||||
|
import tests.backport_assert_raises
|
||||||
|
from nose.tools import assert_raises
|
||||||
|
|
||||||
import boto
|
import boto
|
||||||
|
from boto.exception import EC2ResponseError
|
||||||
import sure # noqa
|
import sure # noqa
|
||||||
|
|
||||||
from moto import mock_ec2
|
from moto import mock_ec2
|
||||||
@ -7,4 +12,115 @@ from moto import mock_ec2
|
|||||||
|
|
||||||
@mock_ec2
|
@mock_ec2
|
||||||
def test_elastic_network_interfaces():
|
def test_elastic_network_interfaces():
|
||||||
pass
|
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||||
|
vpc = conn.create_vpc("10.0.0.0/16")
|
||||||
|
subnet = conn.create_subnet(vpc.id, "10.0.0.0/18")
|
||||||
|
eni = conn.create_network_interface(subnet.id)
|
||||||
|
|
||||||
|
all_enis = conn.get_all_network_interfaces()
|
||||||
|
all_enis.should.have.length_of(1)
|
||||||
|
eni = all_enis[0]
|
||||||
|
eni.groups.should.have.length_of(0)
|
||||||
|
eni.private_ip_addresses.should.have.length_of(0)
|
||||||
|
|
||||||
|
conn.delete_network_interface(eni.id)
|
||||||
|
|
||||||
|
all_enis = conn.get_all_network_interfaces()
|
||||||
|
all_enis.should.have.length_of(0)
|
||||||
|
|
||||||
|
with assert_raises(EC2ResponseError) as cm:
|
||||||
|
conn.delete_network_interface(eni.id)
|
||||||
|
cm.exception.code.should.equal('InvalidNetworkInterfaceID.NotFound')
|
||||||
|
cm.exception.status.should.equal(400)
|
||||||
|
cm.exception.request_id.should_not.be.none
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_elastic_network_interfaces_subnet_validation():
|
||||||
|
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||||
|
|
||||||
|
with assert_raises(EC2ResponseError) as cm:
|
||||||
|
conn.create_network_interface("subnet-abcd1234")
|
||||||
|
cm.exception.code.should.equal('InvalidSubnetID.NotFound')
|
||||||
|
cm.exception.status.should.equal(400)
|
||||||
|
cm.exception.request_id.should_not.be.none
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_elastic_network_interfaces_with_private_ip():
|
||||||
|
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||||
|
vpc = conn.create_vpc("10.0.0.0/16")
|
||||||
|
subnet = conn.create_subnet(vpc.id, "10.0.0.0/18")
|
||||||
|
private_ip = "54.0.0.1"
|
||||||
|
eni = conn.create_network_interface(subnet.id, private_ip)
|
||||||
|
|
||||||
|
all_enis = conn.get_all_network_interfaces()
|
||||||
|
all_enis.should.have.length_of(1)
|
||||||
|
|
||||||
|
eni = all_enis[0]
|
||||||
|
eni.groups.should.have.length_of(0)
|
||||||
|
|
||||||
|
eni.private_ip_addresses.should.have.length_of(1)
|
||||||
|
eni.private_ip_addresses[0].private_ip_address.should.equal(private_ip)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_elastic_network_interfaces_with_groups():
|
||||||
|
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||||
|
vpc = conn.create_vpc("10.0.0.0/16")
|
||||||
|
subnet = conn.create_subnet(vpc.id, "10.0.0.0/18")
|
||||||
|
security_group1 = conn.create_security_group('test security group #1', 'this is a test security group')
|
||||||
|
security_group2 = conn.create_security_group('test security group #2', 'this is a test security group')
|
||||||
|
conn.create_network_interface(subnet.id, groups=[security_group1.id,security_group2.id])
|
||||||
|
|
||||||
|
all_enis = conn.get_all_network_interfaces()
|
||||||
|
all_enis.should.have.length_of(1)
|
||||||
|
|
||||||
|
eni = all_enis[0]
|
||||||
|
eni.groups.should.have.length_of(2)
|
||||||
|
set([group.id for group in eni.groups]).should.equal(set([security_group1.id,security_group2.id]))
|
||||||
|
|
||||||
|
conn.modify_network_interface_attribute(eni.id, 'groupset', [security_group1.id])
|
||||||
|
|
||||||
|
all_enis = conn.get_all_network_interfaces()
|
||||||
|
all_enis.should.have.length_of(1)
|
||||||
|
|
||||||
|
eni = all_enis[0]
|
||||||
|
eni.groups.should.have.length_of(1)
|
||||||
|
eni.groups[0].id.should.equal(security_group1.id)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_elastic_network_interfaces_filtering():
|
||||||
|
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||||
|
vpc = conn.create_vpc("10.0.0.0/16")
|
||||||
|
subnet = conn.create_subnet(vpc.id, "10.0.0.0/18")
|
||||||
|
|
||||||
|
security_group1 = conn.create_security_group('test security group #1', 'this is a test security group')
|
||||||
|
security_group2 = conn.create_security_group('test security group #2', 'this is a test security group')
|
||||||
|
|
||||||
|
eni1 = conn.create_network_interface(subnet.id, groups=[security_group1.id,security_group2.id])
|
||||||
|
eni2 = conn.create_network_interface(subnet.id, groups=[security_group1.id])
|
||||||
|
eni3 = conn.create_network_interface(subnet.id)
|
||||||
|
|
||||||
|
all_enis = conn.get_all_network_interfaces()
|
||||||
|
all_enis.should.have.length_of(3)
|
||||||
|
|
||||||
|
# Filter by ENI ID
|
||||||
|
enis_by_id = conn.get_all_network_interfaces(filters={'network-interface-id':eni1.id})
|
||||||
|
enis_by_id.should.have.length_of(1)
|
||||||
|
set([eni.id for eni in enis_by_id]).should.equal(set([eni1.id]))
|
||||||
|
|
||||||
|
# Filter by Security Group
|
||||||
|
enis_by_group = conn.get_all_network_interfaces(filters={'group-id':security_group1.id})
|
||||||
|
enis_by_group.should.have.length_of(2)
|
||||||
|
set([eni.id for eni in enis_by_group]).should.equal(set([eni1.id,eni2.id]))
|
||||||
|
|
||||||
|
# Filter by ENI ID and Security Group
|
||||||
|
enis_by_group = conn.get_all_network_interfaces(filters={'network-interface-id':eni1.id, 'group-id':security_group1.id})
|
||||||
|
enis_by_group.should.have.length_of(1)
|
||||||
|
set([eni.id for eni in enis_by_group]).should.equal(set([eni1.id]))
|
||||||
|
|
||||||
|
# Unsupported filter
|
||||||
|
conn.get_all_network_interfaces.when.called_with(filters={'not-implemented-filter': 'foobar'}).should.throw(NotImplementedError)
|
||||||
|
|
||||||
|
@ -233,12 +233,86 @@ def test_run_instance_with_instance_type():
|
|||||||
|
|
||||||
@mock_ec2
|
@mock_ec2
|
||||||
def test_run_instance_with_subnet():
|
def test_run_instance_with_subnet():
|
||||||
conn = boto.connect_ec2('the_key', 'the_secret')
|
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||||
reservation = conn.run_instances('ami-1234abcd',
|
vpc = conn.create_vpc("10.0.0.0/16")
|
||||||
subnet_id="subnet-abcd1234")
|
subnet = conn.create_subnet(vpc.id, "10.0.0.0/18")
|
||||||
|
reservation = conn.run_instances('ami-1234abcd', subnet_id=subnet.id)
|
||||||
instance = reservation.instances[0]
|
instance = reservation.instances[0]
|
||||||
|
|
||||||
instance.subnet_id.should.equal("subnet-abcd1234")
|
instance.subnet_id.should.equal(subnet.id)
|
||||||
|
|
||||||
|
all_enis = conn.get_all_network_interfaces()
|
||||||
|
all_enis.should.have.length_of(1)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_run_instance_with_nic_autocreated():
|
||||||
|
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||||
|
vpc = conn.create_vpc("10.0.0.0/16")
|
||||||
|
subnet = conn.create_subnet(vpc.id, "10.0.0.0/18")
|
||||||
|
security_group1 = conn.create_security_group('test security group #1', 'this is a test security group')
|
||||||
|
security_group2 = conn.create_security_group('test security group #2', 'this is a test security group')
|
||||||
|
private_ip = "54.0.0.1"
|
||||||
|
|
||||||
|
reservation = conn.run_instances('ami-1234abcd', subnet_id=subnet.id,
|
||||||
|
security_groups=[security_group1.name],
|
||||||
|
security_group_ids=[security_group2.id],
|
||||||
|
private_ip_address=private_ip)
|
||||||
|
instance = reservation.instances[0]
|
||||||
|
|
||||||
|
all_enis = conn.get_all_network_interfaces()
|
||||||
|
all_enis.should.have.length_of(1)
|
||||||
|
eni = all_enis[0]
|
||||||
|
|
||||||
|
instance.interfaces.should.have.length_of(1)
|
||||||
|
instance.interfaces[0].id.should.equal(eni.id)
|
||||||
|
|
||||||
|
instance.subnet_id.should.equal(subnet.id)
|
||||||
|
instance.groups.should.have.length_of(2)
|
||||||
|
set([group.id for group in instance.groups]).should.equal(set([security_group1.id,security_group2.id]))
|
||||||
|
|
||||||
|
eni.subnet_id.should.equal(subnet.id)
|
||||||
|
eni.groups.should.have.length_of(2)
|
||||||
|
set([group.id for group in eni.groups]).should.equal(set([security_group1.id,security_group2.id]))
|
||||||
|
eni.private_ip_addresses.should.have.length_of(1)
|
||||||
|
eni.private_ip_addresses[0].private_ip_address.should.equal(private_ip)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_run_instance_with_nic_preexisting():
|
||||||
|
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||||
|
vpc = conn.create_vpc("10.0.0.0/16")
|
||||||
|
subnet = conn.create_subnet(vpc.id, "10.0.0.0/18")
|
||||||
|
security_group1 = conn.create_security_group('test security group #1', 'this is a test security group')
|
||||||
|
security_group2 = conn.create_security_group('test security group #2', 'this is a test security group')
|
||||||
|
private_ip = "54.0.0.1"
|
||||||
|
eni = conn.create_network_interface(subnet.id, private_ip, groups=[security_group1.id])
|
||||||
|
|
||||||
|
# Boto requires NetworkInterfaceCollection of NetworkInterfaceSpecifications...
|
||||||
|
# annoying, but generates the desired querystring.
|
||||||
|
from boto.ec2.networkinterface import NetworkInterfaceSpecification, NetworkInterfaceCollection
|
||||||
|
interface = NetworkInterfaceSpecification(network_interface_id=eni.id, device_index=0)
|
||||||
|
interfaces = NetworkInterfaceCollection(interface)
|
||||||
|
# end Boto objects
|
||||||
|
|
||||||
|
reservation = conn.run_instances('ami-1234abcd', network_interfaces=interfaces,
|
||||||
|
security_group_ids=[security_group2.id])
|
||||||
|
instance = reservation.instances[0]
|
||||||
|
|
||||||
|
instance.subnet_id.should.equal(subnet.id)
|
||||||
|
|
||||||
|
all_enis = conn.get_all_network_interfaces()
|
||||||
|
all_enis.should.have.length_of(1)
|
||||||
|
|
||||||
|
instance.interfaces.should.have.length_of(1)
|
||||||
|
instance_eni = instance.interfaces[0]
|
||||||
|
instance_eni.id.should.equal(eni.id)
|
||||||
|
|
||||||
|
instance_eni.subnet_id.should.equal(subnet.id)
|
||||||
|
instance_eni.groups.should.have.length_of(2)
|
||||||
|
set([group.id for group in instance_eni.groups]).should.equal(set([security_group1.id,security_group2.id]))
|
||||||
|
instance_eni.private_ip_addresses.should.have.length_of(1)
|
||||||
|
instance_eni.private_ip_addresses[0].private_ip_address.should.equal(private_ip)
|
||||||
|
|
||||||
|
|
||||||
@mock_ec2
|
@mock_ec2
|
||||||
|
Loading…
Reference in New Issue
Block a user