Network Interfaces: Initial implementation.
This commit is contained in:
		
							parent
							
								
									22d9141122
								
							
						
					
					
						commit
						42f8cea5e6
					
				@ -72,6 +72,14 @@ class InvalidSubnetIdError(EC2ClientError):
 | 
			
		||||
            .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):
 | 
			
		||||
    def __init__(self, name):
 | 
			
		||||
        super(InvalidSecurityGroupDuplicateError, self).__init__(
 | 
			
		||||
 | 
			
		||||
@ -26,6 +26,7 @@ from .exceptions import (
 | 
			
		||||
    ResourceAlreadyAssociatedError,
 | 
			
		||||
    InvalidVPCIdError,
 | 
			
		||||
    InvalidSubnetIdError,
 | 
			
		||||
    InvalidNetworkInterfaceIdError,
 | 
			
		||||
    InvalidSecurityGroupDuplicateError,
 | 
			
		||||
    InvalidSecurityGroupNotFoundError,
 | 
			
		||||
    InvalidPermissionNotFoundError,
 | 
			
		||||
@ -48,11 +49,14 @@ from .utils import (
 | 
			
		||||
    random_dhcp_option_id,
 | 
			
		||||
    random_eip_allocation_id,
 | 
			
		||||
    random_eip_association_id,
 | 
			
		||||
    random_eni_attach_id,
 | 
			
		||||
    random_eni_id,
 | 
			
		||||
    random_internet_gateway_id,
 | 
			
		||||
    random_instance_id,
 | 
			
		||||
    random_internet_gateway_id,
 | 
			
		||||
    random_ip,
 | 
			
		||||
    random_key_pair,
 | 
			
		||||
    random_public_ip,
 | 
			
		||||
    random_reservation_id,
 | 
			
		||||
    random_route_table_id,
 | 
			
		||||
    random_security_group_id,
 | 
			
		||||
@ -77,6 +81,172 @@ class TaggedEC2Instance(object):
 | 
			
		||||
        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):
 | 
			
		||||
    def __init__(self, image_id, user_data, security_groups, **kwargs):
 | 
			
		||||
        super(Instance, self).__init__()
 | 
			
		||||
@ -108,6 +278,12 @@ class Instance(BotoInstance, TaggedEC2Instance):
 | 
			
		||||
                # string will have a "u" prefix -- need to get rid of it
 | 
			
		||||
                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
 | 
			
		||||
    def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
 | 
			
		||||
        properties = cloudformation_json['Properties']
 | 
			
		||||
@ -131,14 +307,23 @@ class Instance(BotoInstance, TaggedEC2Instance):
 | 
			
		||||
        return self.id
 | 
			
		||||
 | 
			
		||||
    def start(self, *args, **kwargs):
 | 
			
		||||
        for nic in self.nics.values():
 | 
			
		||||
            nic.start()
 | 
			
		||||
 | 
			
		||||
        self._state.name = "running"
 | 
			
		||||
        self._state.code = 16
 | 
			
		||||
 | 
			
		||||
    def stop(self, *args, **kwargs):
 | 
			
		||||
        for nic in self.nics.values():
 | 
			
		||||
            nic.stop()
 | 
			
		||||
 | 
			
		||||
        self._state.name = "stopped"
 | 
			
		||||
        self._state.code = 80
 | 
			
		||||
 | 
			
		||||
    def terminate(self, *args, **kwargs):
 | 
			
		||||
        for nic in self.nics.values():
 | 
			
		||||
            nic.stop()
 | 
			
		||||
 | 
			
		||||
        self._state.name = "terminated"
 | 
			
		||||
        self._state.code = 48
 | 
			
		||||
 | 
			
		||||
@ -146,6 +331,21 @@ class Instance(BotoInstance, TaggedEC2Instance):
 | 
			
		||||
        self._state.name = "running"
 | 
			
		||||
        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):
 | 
			
		||||
 | 
			
		||||
@ -511,6 +711,7 @@ class SecurityGroup(object):
 | 
			
		||||
        self.description = description
 | 
			
		||||
        self.ingress_rules = []
 | 
			
		||||
        self.egress_rules = []
 | 
			
		||||
        self.enis = {}
 | 
			
		||||
        self.vpc_id = vpc_id
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
@ -569,18 +770,23 @@ class SecurityGroupBackend(object):
 | 
			
		||||
    def describe_security_groups(self):
 | 
			
		||||
        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):
 | 
			
		||||
        if group_id:
 | 
			
		||||
            # loop over all the SGs, find the right one
 | 
			
		||||
            for vpc in self.groups.values():
 | 
			
		||||
                if group_id in vpc:
 | 
			
		||||
                    return vpc.pop(group_id)
 | 
			
		||||
            for vpc_id, groups in self.groups.items():
 | 
			
		||||
                if group_id in groups:
 | 
			
		||||
                    return self._delete_security_group(vpc_id, group_id)
 | 
			
		||||
            raise InvalidSecurityGroupNotFoundError(group_id)
 | 
			
		||||
        elif name:
 | 
			
		||||
            # Group Name.  Has to be in standard EC2, VPC needs to be identified by group_id
 | 
			
		||||
            group = self.get_security_group_from_name(name)
 | 
			
		||||
            if group:
 | 
			
		||||
                return self.groups[None].pop(group.id)
 | 
			
		||||
                return self._delete_security_group(None, group.id)
 | 
			
		||||
            raise InvalidSecurityGroupNotFoundError(name)
 | 
			
		||||
 | 
			
		||||
    def get_security_group_from_id(self, group_id):
 | 
			
		||||
@ -1003,6 +1209,12 @@ class SubnetBackend(object):
 | 
			
		||||
        self.subnets = {}
 | 
			
		||||
        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):
 | 
			
		||||
        subnet_id = random_subnet_id()
 | 
			
		||||
        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.domain = domain
 | 
			
		||||
        self.instance = None
 | 
			
		||||
        self.eni = None
 | 
			
		||||
        self.association_id = None
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
@ -1355,7 +1568,7 @@ class ElasticAddressBackend(object):
 | 
			
		||||
 | 
			
		||||
        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 = []
 | 
			
		||||
        if address:
 | 
			
		||||
            eips = self.address_by_ip([address])
 | 
			
		||||
@ -1363,13 +1576,20 @@ class ElasticAddressBackend(object):
 | 
			
		||||
            eips = self.address_by_allocation([allocation_id])
 | 
			
		||||
        eip = eips[0]
 | 
			
		||||
 | 
			
		||||
        if eip.instance and not reassociate:
 | 
			
		||||
            raise ResourceAlreadyAssociatedError(eip.public_ip)
 | 
			
		||||
        new_instance_association = bool(instance and (not eip.instance or eip.instance.id == instance.id))
 | 
			
		||||
        new_eni_association = bool(eni and (not eip.eni or eni.id == eip.eni.id))
 | 
			
		||||
 | 
			
		||||
        eip.instance = instance
 | 
			
		||||
        if eip.domain == "vpc":
 | 
			
		||||
            eip.association_id = random_eip_association_id()
 | 
			
		||||
        return eip
 | 
			
		||||
        if new_instance_association or new_eni_association or reassociate:
 | 
			
		||||
            eip.instance = instance
 | 
			
		||||
            eip.eni = eni
 | 
			
		||||
            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):
 | 
			
		||||
        return self.addresses
 | 
			
		||||
@ -1382,6 +1602,13 @@ class ElasticAddressBackend(object):
 | 
			
		||||
            eips = self.address_by_association([association_id])
 | 
			
		||||
        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.association_id = None
 | 
			
		||||
        return True
 | 
			
		||||
@ -1474,6 +1701,7 @@ class DHCPOptionsSetBackend(object):
 | 
			
		||||
class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend,
 | 
			
		||||
                 RegionsAndZonesBackend, SecurityGroupBackend, EBSBackend,
 | 
			
		||||
                 VPCBackend, SubnetBackend, SubnetRouteTableAssociationBackend,
 | 
			
		||||
                 NetworkInterfaceBackend,
 | 
			
		||||
                 VPCPeeringConnectionBackend,
 | 
			
		||||
                 RouteTableBackend, RouteBackend, InternetGatewayBackend,
 | 
			
		||||
                 VPCGatewayAttachmentBackend, SpotRequestBackend,
 | 
			
		||||
 | 
			
		||||
@ -17,10 +17,12 @@ class ElasticIPAddresses(BaseResponse):
 | 
			
		||||
        return template.render(address=address)
 | 
			
		||||
 | 
			
		||||
    def associate_address(self):
 | 
			
		||||
        instance = eni = None
 | 
			
		||||
 | 
			
		||||
        if "InstanceId" in self.querystring:
 | 
			
		||||
            instance = ec2_backend.get_instance(self.querystring['InstanceId'][0])
 | 
			
		||||
        elif "NetworkInterfaceId" in self.querystring:
 | 
			
		||||
            raise NotImplementedError("Lookup by allocation id not implemented")
 | 
			
		||||
            eni = ec2_backend.get_network_interface(self.querystring['NetworkInterfaceId'][0])
 | 
			
		||||
        else:
 | 
			
		||||
            ec2_backend.raise_error("MissingParameter", "Invalid request, expect InstanceId/NetworkId parameter.")
 | 
			
		||||
 | 
			
		||||
@ -28,12 +30,15 @@ class ElasticIPAddresses(BaseResponse):
 | 
			
		||||
        if "AllowReassociation" in self.querystring:
 | 
			
		||||
            reassociate = self.querystring['AllowReassociation'][0] == "true"
 | 
			
		||||
 | 
			
		||||
        if "PublicIp" in self.querystring:
 | 
			
		||||
            eip = ec2_backend.associate_address(instance, address=self.querystring['PublicIp'][0], reassociate=reassociate)
 | 
			
		||||
        elif "AllocationId" in self.querystring:
 | 
			
		||||
            eip = ec2_backend.associate_address(instance, allocation_id=self.querystring['AllocationId'][0], reassociate=reassociate)
 | 
			
		||||
        if instance or eni:
 | 
			
		||||
            if "PublicIp" in self.querystring:
 | 
			
		||||
                eip = ec2_backend.associate_address(instance=instance, eni=eni, address=self.querystring['PublicIp'][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:
 | 
			
		||||
            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)
 | 
			
		||||
        return template.render(address=eip)
 | 
			
		||||
@ -103,6 +108,11 @@ DESCRIBE_ADDRESS_RESPONSE = """<DescribeAddressesResponse xmlns="http://ec2.amaz
 | 
			
		||||
          {% else %}
 | 
			
		||||
            <instanceId/>
 | 
			
		||||
          {% endif %}
 | 
			
		||||
          {% if address.eni %}
 | 
			
		||||
            <networkInterfaceId>{{ address.eni.id }}</networkInterfaceId>
 | 
			
		||||
          {% else %}
 | 
			
		||||
            <networkInterfaceId/>
 | 
			
		||||
          {% endif %}
 | 
			
		||||
          {% if address.allocation_id %}
 | 
			
		||||
            <allocationId>{{ address.allocation_id }}</allocationId>
 | 
			
		||||
          {% endif %}
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,9 @@
 | 
			
		||||
from __future__ import unicode_literals
 | 
			
		||||
from jinja2 import Template
 | 
			
		||||
 | 
			
		||||
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):
 | 
			
		||||
@ -7,22 +11,157 @@ class ElasticNetworkInterfaces(BaseResponse):
 | 
			
		||||
        raise NotImplementedError('ElasticNetworkInterfaces(AmazonVPC).attach_network_interface is not yet implemented')
 | 
			
		||||
 | 
			
		||||
    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):
 | 
			
		||||
        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):
 | 
			
		||||
        raise NotImplementedError('ElasticNetworkInterfaces(AmazonVPC).describe_network_interface_attribute is not yet implemented')
 | 
			
		||||
 | 
			
		||||
    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):
 | 
			
		||||
        raise NotImplementedError('ElasticNetworkInterfaces(AmazonVPC).detach_network_interface is not yet implemented')
 | 
			
		||||
 | 
			
		||||
    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):
 | 
			
		||||
        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.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):
 | 
			
		||||
@ -26,13 +26,19 @@ class InstanceResponse(BaseResponse):
 | 
			
		||||
        user_data = self.querystring.get('UserData')
 | 
			
		||||
        security_group_names = self._get_multi_param('SecurityGroup')
 | 
			
		||||
        security_group_ids = self._get_multi_param('SecurityGroupId')
 | 
			
		||||
        nics = dict_from_querystring("NetworkInterface", self.querystring)
 | 
			
		||||
        instance_type = self.querystring.get("InstanceType", ["m1.small"])[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]
 | 
			
		||||
 | 
			
		||||
        new_reservation = self.ec2_backend.add_instances(
 | 
			
		||||
            image_id, min_count, user_data, security_group_names,
 | 
			
		||||
            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)
 | 
			
		||||
        return template.render(reservation=new_reservation)
 | 
			
		||||
 | 
			
		||||
@ -189,10 +195,19 @@ EC2_RUN_INSTANCES = """<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc
 | 
			
		||||
          <monitoring>
 | 
			
		||||
            <state>enabled</state>
 | 
			
		||||
          </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>
 | 
			
		||||
          <groupSet>
 | 
			
		||||
             {% for group in instance.security_groups %}
 | 
			
		||||
             {% for group in instance.dynamic_group_list %}
 | 
			
		||||
             <item>
 | 
			
		||||
                <groupId>{{ group.id }}</groupId>
 | 
			
		||||
                <groupName>{{ group.name }}</groupName>
 | 
			
		||||
@ -208,6 +223,54 @@ EC2_RUN_INSTANCES = """<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc
 | 
			
		||||
          <clientToken/>
 | 
			
		||||
          <hypervisor>xen</hypervisor>
 | 
			
		||||
          <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>
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
  </instancesSet>
 | 
			
		||||
@ -220,7 +283,14 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns='http://ec2.amazona
 | 
			
		||||
          <item>
 | 
			
		||||
            <reservationId>{{ reservation.id }}</reservationId>
 | 
			
		||||
            <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>
 | 
			
		||||
                {% for instance in reservation.instances %}
 | 
			
		||||
                  <item>
 | 
			
		||||
@ -249,13 +319,17 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns='http://ec2.amazona
 | 
			
		||||
                    <monitoring>
 | 
			
		||||
                      <state>disabled</state>
 | 
			
		||||
                    </monitoring>
 | 
			
		||||
                    <subnetId>{{ instance.subnet_id }}</subnetId>
 | 
			
		||||
                    <vpcId>vpc-1a2b3c4d</vpcId>
 | 
			
		||||
                    <privateIpAddress>10.0.0.12</privateIpAddress>
 | 
			
		||||
                    <ipAddress>46.51.219.63</ipAddress>
 | 
			
		||||
                    {% 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 %}
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                    <sourceDestCheck>true</sourceDestCheck>
 | 
			
		||||
                    <groupSet>
 | 
			
		||||
                      {% for group in instance.security_groups %}
 | 
			
		||||
                      {% for group in instance.dynamic_group_list %}
 | 
			
		||||
                      <item>
 | 
			
		||||
                        <groupId>{{ group.id }}</groupId>
 | 
			
		||||
                        <groupName>{{ group.name }}</groupName>
 | 
			
		||||
@ -280,7 +354,54 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns='http://ec2.amazona
 | 
			
		||||
                      {% endfor %}
 | 
			
		||||
                    </tagSet>
 | 
			
		||||
                    <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>
 | 
			
		||||
                {% endfor %}
 | 
			
		||||
            </instancesSet>
 | 
			
		||||
 | 
			
		||||
@ -72,6 +72,18 @@ def random_dhcp_option_id():
 | 
			
		||||
    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():
 | 
			
		||||
    return "127.{0}.{1}.{2}".format(
 | 
			
		||||
        random.randint(0, 255),
 | 
			
		||||
@ -172,6 +184,21 @@ def filters_from_querystring(querystring_dict):
 | 
			
		||||
    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):
 | 
			
		||||
    keypair_names = []
 | 
			
		||||
    for key, value in querystring_dict.items():
 | 
			
		||||
 | 
			
		||||
@ -110,29 +110,89 @@ def test_eip_associate_vpc():
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
def test_eip_reassociate():
 | 
			
		||||
    """reassociate EIP"""
 | 
			
		||||
    conn = boto.connect_ec2('the_key', 'the_secret')
 | 
			
		||||
 | 
			
		||||
    reservation = conn.run_instances('ami-1234abcd')
 | 
			
		||||
    instance = reservation.instances[0]
 | 
			
		||||
    reservation = conn.run_instances('ami-1234abcd', min_count=2)
 | 
			
		||||
    instance1, instance2 = reservation.instances
 | 
			
		||||
 | 
			
		||||
    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:
 | 
			
		||||
        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.status.should.equal(400)
 | 
			
		||||
    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 = 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
 | 
			
		||||
def test_eip_associate_invalid_args():
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,10 @@
 | 
			
		||||
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
 | 
			
		||||
from boto.exception import EC2ResponseError
 | 
			
		||||
import sure  # noqa
 | 
			
		||||
 | 
			
		||||
from moto import mock_ec2
 | 
			
		||||
@ -7,4 +12,115 @@ from moto import mock_ec2
 | 
			
		||||
 | 
			
		||||
@mock_ec2
 | 
			
		||||
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
 | 
			
		||||
def test_run_instance_with_subnet():
 | 
			
		||||
    conn = boto.connect_ec2('the_key', 'the_secret')
 | 
			
		||||
    reservation = conn.run_instances('ami-1234abcd',
 | 
			
		||||
                                     subnet_id="subnet-abcd1234")
 | 
			
		||||
    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")
 | 
			
		||||
    reservation = conn.run_instances('ami-1234abcd', subnet_id=subnet.id)
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user