Fix NetworkInterface PrivateIP support and EIP association (#4288)
This commit is contained in:
parent
45b2684eb6
commit
f84ba7d6ec
@ -131,6 +131,7 @@ from .utils import (
|
||||
random_internet_gateway_id,
|
||||
random_egress_only_internet_gateway_id,
|
||||
random_ip,
|
||||
random_mac_address,
|
||||
random_ipv6_cidr,
|
||||
random_transit_gateway_attachment_id,
|
||||
random_transit_gateway_route_table_id,
|
||||
@ -277,15 +278,16 @@ class NetworkInterface(TaggedEC2Resource, CloudFormationModel):
|
||||
private_ip_address,
|
||||
private_ip_addresses=None,
|
||||
device_index=0,
|
||||
public_ip_auto_assign=True,
|
||||
public_ip_auto_assign=False,
|
||||
group_ids=None,
|
||||
description=None,
|
||||
tags=None,
|
||||
):
|
||||
self.ec2_backend = ec2_backend
|
||||
self.id = random_eni_id()
|
||||
self.device_index = device_index
|
||||
self.private_ip_address = private_ip_address or random_private_ip()
|
||||
self.private_ip_addresses = private_ip_addresses
|
||||
self.private_ip_address = private_ip_address or None
|
||||
self.private_ip_addresses = private_ip_addresses or []
|
||||
self.subnet = subnet
|
||||
self.instance = None
|
||||
self.attachment_id = None
|
||||
@ -295,13 +297,35 @@ class NetworkInterface(TaggedEC2Resource, CloudFormationModel):
|
||||
self.public_ip = None
|
||||
self.public_ip_auto_assign = public_ip_auto_assign
|
||||
self.start()
|
||||
|
||||
self.add_tags(tags or {})
|
||||
self.status = "available"
|
||||
self.attachments = []
|
||||
|
||||
self.mac_address = random_mac_address()
|
||||
self.interface_type = "interface"
|
||||
# Local set to the ENI. When attached to an instance, @property group_set
|
||||
# returns groups for both self and the attached instance.
|
||||
self._group_set = []
|
||||
|
||||
if not self.private_ip_address:
|
||||
if self.private_ip_addresses:
|
||||
for ip in self.private_ip_addresses:
|
||||
if isinstance(ip, list) and ip.get("Primary", False) in [
|
||||
"true",
|
||||
True,
|
||||
"True",
|
||||
]:
|
||||
self.private_ip_address = ip.get("PrivateIpAddress")
|
||||
if isinstance(ip, str):
|
||||
self.private_ip_address = self.private_ip_addresses[0]
|
||||
break
|
||||
else:
|
||||
self.private_ip_address = random_private_ip()
|
||||
|
||||
if not self.private_ip_addresses and self.private_ip_address:
|
||||
self.private_ip_addresses.append(
|
||||
{"Primary": True, "PrivateIpAddress": self.private_ip_address}
|
||||
)
|
||||
|
||||
group = None
|
||||
if group_ids:
|
||||
for group_id in group_ids:
|
||||
@ -319,6 +343,21 @@ class NetworkInterface(TaggedEC2Resource, CloudFormationModel):
|
||||
if group:
|
||||
self._group_set.append(group)
|
||||
|
||||
@property
|
||||
def owner_id(self):
|
||||
return ACCOUNT_ID
|
||||
|
||||
@property
|
||||
def association(self):
|
||||
association = {}
|
||||
if self.public_ip:
|
||||
eips = self.ec2_backend.address_by_ip([self.public_ip])
|
||||
eip = eips[0] if len(eips) > 0 else None
|
||||
if eip:
|
||||
association["allocationId"] = eip.allocation_id or None
|
||||
association["associationId"] = eip.association_id or None
|
||||
return association
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
@ -430,10 +469,9 @@ class NetworkInterfaceBackend(object):
|
||||
private_ip_addresses,
|
||||
group_ids=group_ids,
|
||||
description=description,
|
||||
tags=tags,
|
||||
**kwargs
|
||||
)
|
||||
if tags:
|
||||
eni.add_tags(tags)
|
||||
self.enis[eni.id] = eni
|
||||
return eni
|
||||
|
||||
@ -4031,9 +4069,7 @@ class Subnet(TaggedEC2Resource, CloudFormationModel):
|
||||
for eni in self.ec2_backend.get_all_network_interfaces()
|
||||
if eni.subnet.id == self.id
|
||||
]
|
||||
addresses_taken = [
|
||||
eni.private_ip_address for eni in enis if eni.private_ip_address
|
||||
]
|
||||
addresses_taken = []
|
||||
for eni in enis:
|
||||
if eni.private_ip_addresses:
|
||||
addresses_taken.extend(eni.private_ip_addresses)
|
||||
@ -7704,8 +7740,9 @@ class NatGateway(CloudFormationModel, TaggedEC2Resource):
|
||||
|
||||
@property
|
||||
def public_ip(self):
|
||||
eips = self._backend.address_by_allocation([self.allocation_id])
|
||||
return eips[0].public_ip
|
||||
if self.allocation_id:
|
||||
eips = self._backend.address_by_allocation([self.allocation_id])
|
||||
return eips[0].public_ip if self.allocation_id else None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
|
@ -1,6 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
from moto.core.responses import BaseResponse
|
||||
from moto.ec2.utils import filters_from_querystring
|
||||
from moto.ec2.utils import filters_from_querystring, add_tag_specification
|
||||
|
||||
|
||||
class ElasticNetworkInterfaces(BaseResponse):
|
||||
@ -11,9 +11,9 @@ class ElasticNetworkInterfaces(BaseResponse):
|
||||
groups = self._get_multi_param("SecurityGroupId")
|
||||
subnet = self.ec2_backend.get_subnet(subnet_id)
|
||||
description = self._get_param("Description")
|
||||
tags = self._parse_tag_specification("TagSpecification").get(
|
||||
"network-interface"
|
||||
)
|
||||
tags = self._get_multi_param("TagSpecification")
|
||||
tags = add_tag_specification(tags)
|
||||
|
||||
if self.is_not_dryrun("CreateNetworkInterface"):
|
||||
eni = self.ec2_backend.create_network_interface(
|
||||
subnet,
|
||||
@ -84,19 +84,20 @@ CREATE_NETWORK_INTERFACE_RESPONSE = """
|
||||
<CreateNetworkInterfaceResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
|
||||
<requestId>2c6021ec-d705-445a-9780-420d0c7ab793</requestId>
|
||||
<networkInterface>
|
||||
<association></association>
|
||||
<attachment></attachment>
|
||||
<networkInterfaceId>{{ eni.id }}</networkInterfaceId>
|
||||
<subnetId>{{ eni.subnet.id }}</subnetId>
|
||||
<vpcId>{{ eni.subnet.vpc_id }}</vpcId>
|
||||
<availabilityZone>us-west-2a</availabilityZone>
|
||||
<availabilityZone>{{ eni.subnet.availability_zone }}</availabilityZone>
|
||||
{% if eni.description %}
|
||||
<description>{{ eni.description }}</description>
|
||||
{% else %}
|
||||
<description/>
|
||||
{% endif %}
|
||||
<ownerId>498654062920</ownerId>
|
||||
<requesterManaged>false</requesterManaged>
|
||||
<status>pending</status>
|
||||
<macAddress>02:07:a9:b6:12:51</macAddress>
|
||||
<ownerId>{{ eni.owner_id }}</ownerId>
|
||||
<requesterId>AIDARCSPW2WNREUEN7XFM</requesterId>
|
||||
<requesterManaged>False</requesterManaged>
|
||||
<status>{{ eni.status }}</status>
|
||||
<macAddress>{{ eni.mac_address }}</macAddress>
|
||||
{% if eni.private_ip_address %}
|
||||
<privateIpAddress>{{ eni.private_ip_address }}</privateIpAddress>
|
||||
{% endif %}
|
||||
@ -109,17 +110,36 @@ CREATE_NETWORK_INTERFACE_RESPONSE = """
|
||||
</item>
|
||||
{% endfor %}
|
||||
</groupSet>
|
||||
<tagSet/>
|
||||
{% if eni.private_ip_address %}
|
||||
<privateIpAddressesSet>
|
||||
<item>
|
||||
<privateIpAddress>{{ eni.private_ip_address }}</privateIpAddress>
|
||||
<primary>true</primary>
|
||||
</item>
|
||||
</privateIpAddressesSet>
|
||||
{% else %}
|
||||
<privateIpAddressesSet/>
|
||||
{% if eni.association %}
|
||||
<association>
|
||||
<publicIp>{{ eni.public_ip }}</publicIp>
|
||||
<ipOwnerId>{{ eni.owner_id }}</ipOwnerId>
|
||||
<allocationId>{{ eni.association.allocationId }}</allocationId>
|
||||
<associationId>{{ eni.association.associationId }}</associationId>
|
||||
<natEnabled>true</natEnabled>
|
||||
</association>
|
||||
{% endif %}
|
||||
<tagSet>
|
||||
{% for tag in eni.get_tags() %}
|
||||
<item>
|
||||
<key>{{ tag.key }}</key>
|
||||
<value>{{ tag.value }}</value>
|
||||
</item>
|
||||
{% endfor %}
|
||||
</tagSet>
|
||||
<privateIpAddressesSet>
|
||||
{% for address in eni.private_ip_addresses %}
|
||||
<item>
|
||||
<privateIpAddress>{{ address.PrivateIpAddress }}</privateIpAddress>
|
||||
{% if address.privateDnsName %}
|
||||
<privateDnsName>{{ address.privateDnsName }}</privateDnsName>
|
||||
{% endif %}
|
||||
<primary>{{ address.Primary }}</primary>
|
||||
</item>
|
||||
{% endfor %}
|
||||
</privateIpAddressesSet>
|
||||
<ipv6AddressesSet/>
|
||||
<interfaceType>{{ eni.interface_type }}</interfaceType>
|
||||
</networkInterface>
|
||||
</CreateNetworkInterfaceResponse>
|
||||
"""
|
||||
@ -129,74 +149,60 @@ DESCRIBE_NETWORK_INTERFACES_RESPONSE = """<DescribeNetworkInterfacesResponse xml
|
||||
<networkInterfaceSet>
|
||||
{% for eni in enis %}
|
||||
<item>
|
||||
<networkInterfaceId>{{ eni.id }}</networkInterfaceId>
|
||||
<subnetId>{{ eni.subnet.id }}</subnetId>
|
||||
<vpcId>{{ eni.subnet.vpc_id }}</vpcId>
|
||||
<availabilityZone>us-west-2a</availabilityZone>
|
||||
<description>{{ eni.description or "" }}</description>
|
||||
<ownerId>190610284047</ownerId>
|
||||
<requesterManaged>false</requesterManaged>
|
||||
{% if eni.attachment_id %}
|
||||
<status>in-use</status>
|
||||
{% else %}
|
||||
<status>available</status>
|
||||
{% endif %}
|
||||
<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>{{ eni.source_dest_check }}</sourceDestCheck>
|
||||
<groupSet>
|
||||
{% for group in eni.group_set %}
|
||||
<item>
|
||||
<groupId>{{ group.id }}</groupId>
|
||||
<groupName>{{ group.name }}</groupName>
|
||||
<networkInterfaceId>{{ eni.id }}</networkInterfaceId>
|
||||
<subnetId>{{ eni.subnet.id }}</subnetId>
|
||||
<vpcId>{{ eni.subnet.vpc_id }}</vpcId>
|
||||
<availabilityZone>{{ eni.subnet.availability_zone }}</availabilityZone>
|
||||
{% if eni.description %}
|
||||
<description>{{ eni.description }}</description>
|
||||
{% endif %}
|
||||
<ownerId>{{ eni.owner_id }}</ownerId>
|
||||
<requesterId>AIDARCSPW2WNREUEN7XFM</requesterId>
|
||||
<requesterManaged>False</requesterManaged>
|
||||
<status>{{ eni.status }}</status>
|
||||
<macAddress>{{ eni.mac_address }}</macAddress>
|
||||
{% if eni.private_ip_address %}
|
||||
<privateIpAddress>{{ eni.private_ip_address }}</privateIpAddress>
|
||||
{% endif %}
|
||||
<sourceDestCheck>{{ eni.source_dest_check }}</sourceDestCheck>
|
||||
<groupSet>
|
||||
{% for group in eni.group_set %}
|
||||
<item>
|
||||
<groupId>{{ group.id }}</groupId>
|
||||
<groupName>{{ group.name }}</groupName>
|
||||
</item>
|
||||
{% endfor %}
|
||||
</groupSet>
|
||||
{% if eni.association %}
|
||||
<association>
|
||||
<publicIp>{{ eni.public_ip }}</publicIp>
|
||||
<ipOwnerId>{{ eni.owner_id }}</ipOwnerId>
|
||||
<allocationId>{{ eni.association.allocationId }}</allocationId>
|
||||
<associationId>{{ eni.association.associationId }}</associationId>
|
||||
<natEnabled>true</natEnabled>
|
||||
</association>
|
||||
{% endif %}
|
||||
<tagSet>
|
||||
{% for tag in eni.get_tags() %}
|
||||
<item>
|
||||
<key>{{ tag.key }}</key>
|
||||
<value>{{ tag.value }}</value>
|
||||
</item>
|
||||
{% endfor %}
|
||||
</groupSet>
|
||||
<tagSet>
|
||||
{% for tag in eni.get_tags() %}
|
||||
<item>
|
||||
<key>{{ tag.key }}</key>
|
||||
<value>{{ tag.value }}</value>
|
||||
</item>
|
||||
{% endfor %}
|
||||
</tagSet>
|
||||
{% if eni.instance %}
|
||||
<attachment>
|
||||
<attachmentId>{{ eni.attachment_id }}</attachmentId>
|
||||
<instanceId>{{ eni.instance.id }}</instanceId>
|
||||
<instanceOwnerId>190610284047</instanceOwnerId>
|
||||
<deviceIndex>{{ eni.device_index }}</deviceIndex>
|
||||
<status>attached</status>
|
||||
<attachTime>2013-10-04T17:38:53.000Z</attachTime>
|
||||
<deleteOnTermination>true</deleteOnTermination>
|
||||
</attachment>
|
||||
{% endif %}
|
||||
<association>
|
||||
<publicIp>{{ eni.public_ip }}</publicIp>
|
||||
<publicDnsName>ec2-54-200-86-47.us-west-2.compute.amazonaws.com</publicDnsName>
|
||||
<ipOwnerId>amazon</ipOwnerId>
|
||||
</association>
|
||||
{% 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 %}
|
||||
</tagSet>
|
||||
<privateIpAddressesSet>
|
||||
{% for address in eni.private_ip_addresses %}
|
||||
<item>
|
||||
<privateIpAddress>{{ address.PrivateIpAddress }}</privateIpAddress>
|
||||
{% if address.privateDnsName %}
|
||||
<privateDnsName>{{ address.privateDnsName }}</privateDnsName>
|
||||
{% endif %}
|
||||
<primary>{{ address.Primary }}</primary>
|
||||
</item>
|
||||
{% endfor %}
|
||||
</privateIpAddressesSet>
|
||||
<ipv6AddressesSet/>
|
||||
<interfaceType>{{ eni.interface_type }}</interfaceType>
|
||||
</item>
|
||||
{% endfor %}
|
||||
</networkInterfaceSet>
|
||||
|
@ -241,6 +241,14 @@ def random_ip():
|
||||
)
|
||||
|
||||
|
||||
def random_mac_address():
|
||||
return "02:00:00:%02x:%02x:%02x" % (
|
||||
random.randint(0, 255),
|
||||
random.randint(0, 255),
|
||||
random.randint(0, 255),
|
||||
)
|
||||
|
||||
|
||||
def randor_ipv4_cidr():
|
||||
return "10.0.{}.{}/16".format(random.randint(0, 255), random.randint(0, 255))
|
||||
|
||||
|
@ -7,3 +7,4 @@ TestAccAWSIAMRolePolicy
|
||||
TestAccAWSSecurityGroup_forceRevokeRules_
|
||||
TestAccAWSSSMDocument_package
|
||||
TestAccAWSDefaultSecurityGroup_Classic_
|
||||
TestAccDataSourceAwsNetworkInterface_CarrierIPAssociation
|
||||
|
@ -124,4 +124,5 @@ TestAccAWSSecurityGroupRule_
|
||||
TestAccAWSVpnGateway
|
||||
TestAccAWSVpnGatewayAttachment
|
||||
TestAccAWSEc2CarrierGateway
|
||||
TestAccDataSourceAwsNetworkInterface_
|
||||
TestAccAWSNatGateway
|
||||
|
@ -618,14 +618,14 @@ def validate_subnet_details_after_creating_eni(
|
||||
],
|
||||
)["NetworkInterface"]
|
||||
enis_created.append(eni)
|
||||
ip_addresses_assigned = ip_addresses_assigned + nr_of_ip_addresses + 1 #
|
||||
ip_addresses_assigned = ip_addresses_assigned + nr_of_ip_addresses #
|
||||
|
||||
# Verify that the nr of available IP addresses takes these ENIs into account
|
||||
updated_subnet = client.describe_subnets(SubnetIds=[subnet["SubnetId"]])["Subnets"][
|
||||
0
|
||||
]
|
||||
private_addresses = [
|
||||
eni["PrivateIpAddress"] for eni in enis_created if eni["PrivateIpAddress"]
|
||||
]
|
||||
|
||||
private_addresses = []
|
||||
for eni in enis_created:
|
||||
private_addresses.extend(
|
||||
[address["PrivateIpAddress"] for address in eni["PrivateIpAddresses"]]
|
||||
|
Loading…
Reference in New Issue
Block a user