Fix NetworkInterface PrivateIP support and EIP association (#4288)

This commit is contained in:
Mohit Alonja 2021-09-17 02:37:18 +05:30 committed by GitHub
parent 45b2684eb6
commit f84ba7d6ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 156 additions and 103 deletions

View File

@ -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():

View File

@ -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>

View File

@ -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))

View File

@ -7,3 +7,4 @@ TestAccAWSIAMRolePolicy
TestAccAWSSecurityGroup_forceRevokeRules_
TestAccAWSSSMDocument_package
TestAccAWSDefaultSecurityGroup_Classic_
TestAccDataSourceAwsNetworkInterface_CarrierIPAssociation

View File

@ -124,4 +124,5 @@ TestAccAWSSecurityGroupRule_
TestAccAWSVpnGateway
TestAccAWSVpnGatewayAttachment
TestAccAWSEc2CarrierGateway
TestAccDataSourceAwsNetworkInterface_
TestAccAWSNatGateway

View File

@ -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"]]