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_internet_gateway_id,
random_egress_only_internet_gateway_id, random_egress_only_internet_gateway_id,
random_ip, random_ip,
random_mac_address,
random_ipv6_cidr, random_ipv6_cidr,
random_transit_gateway_attachment_id, random_transit_gateway_attachment_id,
random_transit_gateway_route_table_id, random_transit_gateway_route_table_id,
@ -277,15 +278,16 @@ class NetworkInterface(TaggedEC2Resource, CloudFormationModel):
private_ip_address, private_ip_address,
private_ip_addresses=None, private_ip_addresses=None,
device_index=0, device_index=0,
public_ip_auto_assign=True, public_ip_auto_assign=False,
group_ids=None, group_ids=None,
description=None, description=None,
tags=None,
): ):
self.ec2_backend = ec2_backend self.ec2_backend = ec2_backend
self.id = random_eni_id() self.id = random_eni_id()
self.device_index = device_index self.device_index = device_index
self.private_ip_address = private_ip_address or random_private_ip() self.private_ip_address = private_ip_address or None
self.private_ip_addresses = private_ip_addresses self.private_ip_addresses = private_ip_addresses or []
self.subnet = subnet self.subnet = subnet
self.instance = None self.instance = None
self.attachment_id = None self.attachment_id = None
@ -295,13 +297,35 @@ class NetworkInterface(TaggedEC2Resource, CloudFormationModel):
self.public_ip = None self.public_ip = None
self.public_ip_auto_assign = public_ip_auto_assign self.public_ip_auto_assign = public_ip_auto_assign
self.start() self.start()
self.add_tags(tags or {})
self.status = "available"
self.attachments = [] 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 # Local set to the ENI. When attached to an instance, @property group_set
# returns groups for both self and the attached instance. # returns groups for both self and the attached instance.
self._group_set = [] 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 group = None
if group_ids: if group_ids:
for group_id in group_ids: for group_id in group_ids:
@ -319,6 +343,21 @@ class NetworkInterface(TaggedEC2Resource, CloudFormationModel):
if group: if group:
self._group_set.append(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 @staticmethod
def cloudformation_name_type(): def cloudformation_name_type():
return None return None
@ -430,10 +469,9 @@ class NetworkInterfaceBackend(object):
private_ip_addresses, private_ip_addresses,
group_ids=group_ids, group_ids=group_ids,
description=description, description=description,
tags=tags,
**kwargs **kwargs
) )
if tags:
eni.add_tags(tags)
self.enis[eni.id] = eni self.enis[eni.id] = eni
return eni return eni
@ -4031,9 +4069,7 @@ class Subnet(TaggedEC2Resource, CloudFormationModel):
for eni in self.ec2_backend.get_all_network_interfaces() for eni in self.ec2_backend.get_all_network_interfaces()
if eni.subnet.id == self.id if eni.subnet.id == self.id
] ]
addresses_taken = [ addresses_taken = []
eni.private_ip_address for eni in enis if eni.private_ip_address
]
for eni in enis: for eni in enis:
if eni.private_ip_addresses: if eni.private_ip_addresses:
addresses_taken.extend(eni.private_ip_addresses) addresses_taken.extend(eni.private_ip_addresses)
@ -7704,8 +7740,9 @@ class NatGateway(CloudFormationModel, TaggedEC2Resource):
@property @property
def public_ip(self): def public_ip(self):
eips = self._backend.address_by_allocation([self.allocation_id]) if self.allocation_id:
return eips[0].public_ip eips = self._backend.address_by_allocation([self.allocation_id])
return eips[0].public_ip if self.allocation_id else None
@staticmethod @staticmethod
def cloudformation_name_type(): def cloudformation_name_type():

View File

@ -1,6 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from moto.core.responses import BaseResponse 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): class ElasticNetworkInterfaces(BaseResponse):
@ -11,9 +11,9 @@ class ElasticNetworkInterfaces(BaseResponse):
groups = self._get_multi_param("SecurityGroupId") groups = self._get_multi_param("SecurityGroupId")
subnet = self.ec2_backend.get_subnet(subnet_id) subnet = self.ec2_backend.get_subnet(subnet_id)
description = self._get_param("Description") description = self._get_param("Description")
tags = self._parse_tag_specification("TagSpecification").get( tags = self._get_multi_param("TagSpecification")
"network-interface" tags = add_tag_specification(tags)
)
if self.is_not_dryrun("CreateNetworkInterface"): if self.is_not_dryrun("CreateNetworkInterface"):
eni = self.ec2_backend.create_network_interface( eni = self.ec2_backend.create_network_interface(
subnet, subnet,
@ -84,19 +84,20 @@ CREATE_NETWORK_INTERFACE_RESPONSE = """
<CreateNetworkInterfaceResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/"> <CreateNetworkInterfaceResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
<requestId>2c6021ec-d705-445a-9780-420d0c7ab793</requestId> <requestId>2c6021ec-d705-445a-9780-420d0c7ab793</requestId>
<networkInterface> <networkInterface>
<association></association>
<attachment></attachment>
<networkInterfaceId>{{ eni.id }}</networkInterfaceId> <networkInterfaceId>{{ eni.id }}</networkInterfaceId>
<subnetId>{{ eni.subnet.id }}</subnetId> <subnetId>{{ eni.subnet.id }}</subnetId>
<vpcId>{{ eni.subnet.vpc_id }}</vpcId> <vpcId>{{ eni.subnet.vpc_id }}</vpcId>
<availabilityZone>us-west-2a</availabilityZone> <availabilityZone>{{ eni.subnet.availability_zone }}</availabilityZone>
{% if eni.description %} {% if eni.description %}
<description>{{ eni.description }}</description> <description>{{ eni.description }}</description>
{% else %}
<description/>
{% endif %} {% endif %}
<ownerId>498654062920</ownerId> <ownerId>{{ eni.owner_id }}</ownerId>
<requesterManaged>false</requesterManaged> <requesterId>AIDARCSPW2WNREUEN7XFM</requesterId>
<status>pending</status> <requesterManaged>False</requesterManaged>
<macAddress>02:07:a9:b6:12:51</macAddress> <status>{{ eni.status }}</status>
<macAddress>{{ eni.mac_address }}</macAddress>
{% if eni.private_ip_address %} {% if eni.private_ip_address %}
<privateIpAddress>{{ eni.private_ip_address }}</privateIpAddress> <privateIpAddress>{{ eni.private_ip_address }}</privateIpAddress>
{% endif %} {% endif %}
@ -109,17 +110,36 @@ CREATE_NETWORK_INTERFACE_RESPONSE = """
</item> </item>
{% endfor %} {% endfor %}
</groupSet> </groupSet>
<tagSet/> {% if eni.association %}
{% if eni.private_ip_address %} <association>
<privateIpAddressesSet> <publicIp>{{ eni.public_ip }}</publicIp>
<item> <ipOwnerId>{{ eni.owner_id }}</ipOwnerId>
<privateIpAddress>{{ eni.private_ip_address }}</privateIpAddress> <allocationId>{{ eni.association.allocationId }}</allocationId>
<primary>true</primary> <associationId>{{ eni.association.associationId }}</associationId>
</item> <natEnabled>true</natEnabled>
</privateIpAddressesSet> </association>
{% else %}
<privateIpAddressesSet/>
{% endif %} {% 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> </networkInterface>
</CreateNetworkInterfaceResponse> </CreateNetworkInterfaceResponse>
""" """
@ -129,74 +149,60 @@ DESCRIBE_NETWORK_INTERFACES_RESPONSE = """<DescribeNetworkInterfacesResponse xml
<networkInterfaceSet> <networkInterfaceSet>
{% for eni in enis %} {% for eni in enis %}
<item> <item>
<networkInterfaceId>{{ eni.id }}</networkInterfaceId> <networkInterfaceId>{{ eni.id }}</networkInterfaceId>
<subnetId>{{ eni.subnet.id }}</subnetId> <subnetId>{{ eni.subnet.id }}</subnetId>
<vpcId>{{ eni.subnet.vpc_id }}</vpcId> <vpcId>{{ eni.subnet.vpc_id }}</vpcId>
<availabilityZone>us-west-2a</availabilityZone> <availabilityZone>{{ eni.subnet.availability_zone }}</availabilityZone>
<description>{{ eni.description or "" }}</description> {% if eni.description %}
<ownerId>190610284047</ownerId> <description>{{ eni.description }}</description>
<requesterManaged>false</requesterManaged> {% endif %}
{% if eni.attachment_id %} <ownerId>{{ eni.owner_id }}</ownerId>
<status>in-use</status> <requesterId>AIDARCSPW2WNREUEN7XFM</requesterId>
{% else %} <requesterManaged>False</requesterManaged>
<status>available</status> <status>{{ eni.status }}</status>
{% endif %} <macAddress>{{ eni.mac_address }}</macAddress>
<macAddress>0e:a3:a7:7b:95:a7</macAddress> {% if eni.private_ip_address %}
{% if eni.private_ip_address %} <privateIpAddress>{{ eni.private_ip_address }}</privateIpAddress>
<privateIpAddress>{{ eni.private_ip_address }}</privateIpAddress> {% endif %}
{% endif %} <sourceDestCheck>{{ eni.source_dest_check }}</sourceDestCheck>
<privateDnsName>ip-10-0-0-134.us-west-2.compute.internal</privateDnsName> <groupSet>
<sourceDestCheck>{{ eni.source_dest_check }}</sourceDestCheck> {% for group in eni.group_set %}
<groupSet> <item>
{% for group in eni.group_set %} <groupId>{{ group.id }}</groupId>
<item> <groupName>{{ group.name }}</groupName>
<groupId>{{ group.id }}</groupId> </item>
<groupName>{{ group.name }}</groupName> {% 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> </item>
{% endfor %} {% endfor %}
</groupSet> </tagSet>
<tagSet> <privateIpAddressesSet>
{% for tag in eni.get_tags() %} {% for address in eni.private_ip_addresses %}
<item> <item>
<key>{{ tag.key }}</key> <privateIpAddress>{{ address.PrivateIpAddress }}</privateIpAddress>
<value>{{ tag.value }}</value> {% if address.privateDnsName %}
</item> <privateDnsName>{{ address.privateDnsName }}</privateDnsName>
{% endfor %} {% endif %}
</tagSet> <primary>{{ address.Primary }}</primary>
{% if eni.instance %} </item>
<attachment> {% endfor %}
<attachmentId>{{ eni.attachment_id }}</attachmentId> </privateIpAddressesSet>
<instanceId>{{ eni.instance.id }}</instanceId> <ipv6AddressesSet/>
<instanceOwnerId>190610284047</instanceOwnerId> <interfaceType>{{ eni.interface_type }}</interfaceType>
<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 %}
</item> </item>
{% endfor %} {% endfor %}
</networkInterfaceSet> </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(): def randor_ipv4_cidr():
return "10.0.{}.{}/16".format(random.randint(0, 255), random.randint(0, 255)) return "10.0.{}.{}/16".format(random.randint(0, 255), random.randint(0, 255))

View File

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

View File

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

View File

@ -618,14 +618,14 @@ def validate_subnet_details_after_creating_eni(
], ],
)["NetworkInterface"] )["NetworkInterface"]
enis_created.append(eni) 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 # Verify that the nr of available IP addresses takes these ENIs into account
updated_subnet = client.describe_subnets(SubnetIds=[subnet["SubnetId"]])["Subnets"][ updated_subnet = client.describe_subnets(SubnetIds=[subnet["SubnetId"]])["Subnets"][
0 0
] ]
private_addresses = [
eni["PrivateIpAddress"] for eni in enis_created if eni["PrivateIpAddress"] private_addresses = []
]
for eni in enis_created: for eni in enis_created:
private_addresses.extend( private_addresses.extend(
[address["PrivateIpAddress"] for address in eni["PrivateIpAddresses"]] [address["PrivateIpAddress"] for address in eni["PrivateIpAddresses"]]