added subnet ipv6 cidr association; minor fixes (#4242)

This commit is contained in:
Macwan Nevil 2021-08-30 23:46:40 +05:30 committed by GitHub
parent cc568c1656
commit c707ee002c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 172 additions and 14 deletions

View File

@ -677,3 +677,21 @@ class InvalidTaggableResourceType(EC2ClientError):
resource_type
),
)
class GenericInvalidParameterValueError(EC2ClientError):
def __init__(self, attribute, value):
super(GenericInvalidParameterValueError, self).__init__(
"InvalidParameterValue",
"invalid value for parameter {0}: {1}".format(attribute, value),
)
class InvalidSubnetCidrBlockAssociationID(EC2ClientError):
def __init__(self, association_id):
super(InvalidSubnetCidrBlockAssociationID, self).__init__(
"InvalidSubnetCidrBlockAssociationID.NotFound",
"The subnet CIDR block with association ID '{0}' does not exist".format(
association_id
),
)

View File

@ -38,6 +38,7 @@ from os import listdir
from .exceptions import (
CidrLimitExceeded,
GenericInvalidParameterValueError,
UnsupportedTenancy,
DependencyViolationError,
EC2ClientError,
@ -95,6 +96,7 @@ from .exceptions import (
InvalidVPCRangeError,
InvalidVpnGatewayIdError,
InvalidVpnConnectionIdError,
InvalidSubnetCidrBlockAssociationID,
MalformedAMIIdError,
MalformedDHCPOptionsIdError,
MissingParameterError,
@ -172,6 +174,7 @@ from .utils import (
random_vpn_gateway_id,
random_vpn_connection_id,
random_customer_gateway_id,
random_subnet_ipv6_cidr_block_association_id,
is_tag_filter,
tag_filter_matches,
rsa_public_key_parse,
@ -284,6 +287,7 @@ class NetworkInterface(TaggedEC2Resource, CloudFormationModel):
self.instance = None
self.attachment_id = None
self.description = description
self.source_dest_check = True
self.public_ip = None
self.public_ip_auto_assign = public_ip_auto_assign
@ -478,10 +482,14 @@ class NetworkInterfaceBackend(object):
found_eni.instance.detach_eni(found_eni)
def modify_network_interface_attribute(self, eni_id, group_ids):
def modify_network_interface_attribute(
self, eni_id, group_ids, source_dest_check=None
):
eni = self.get_network_interface(eni_id)
groups = [self.get_security_group_from_id(group_id) for group_id in group_ids]
eni._group_set = groups
if source_dest_check:
eni.source_dest_check = source_dest_check
def get_all_network_interfaces(self, eni_ids=None, filters=None):
enis = self.enis.values()
@ -3633,6 +3641,7 @@ class Subnet(TaggedEC2Resource, CloudFormationModel):
subnet_id,
vpc_id,
cidr_block,
ipv6_cidr_block,
availability_zone,
default_for_az,
map_public_ip_on_launch,
@ -3650,7 +3659,9 @@ class Subnet(TaggedEC2Resource, CloudFormationModel):
self.default_for_az = default_for_az
self.map_public_ip_on_launch = map_public_ip_on_launch
self.assign_ipv6_address_on_creation = assign_ipv6_address_on_creation
self.ipv6_cidr_block_associations = []
self.ipv6_cidr_block_associations = {}
if ipv6_cidr_block:
self.attach_ipv6_cidr_block_associations(ipv6_cidr_block)
# Theory is we assign ip's as we go (as 16,777,214 usable IPs in a /8)
self._subnet_ip_generator = self.cidr.hosts()
@ -3802,6 +3813,22 @@ class Subnet(TaggedEC2Resource, CloudFormationModel):
except KeyError:
pass # Unknown IP
def attach_ipv6_cidr_block_associations(self, ipv6_cidr_block):
association = {
"associationId": random_subnet_ipv6_cidr_block_association_id(),
"ipv6CidrBlock": ipv6_cidr_block,
"ipv6CidrBlockState": "associated",
}
self.ipv6_cidr_block_associations[
association.get("associationId")
] = association
return association
def detach_subnet_cidr_block(self, association_id):
association = self.ipv6_cidr_block_associations.get(association_id)
association["ipv6CidrBlockState"] = "disassociated"
return association
class SubnetBackend(object):
def __init__(self):
@ -3819,6 +3846,7 @@ class SubnetBackend(object):
self,
vpc_id,
cidr_block,
ipv6_cidr_block=None,
availability_zone=None,
availability_zone_id=None,
context=None,
@ -3852,6 +3880,10 @@ class SubnetBackend(object):
if not subnet_in_vpc_cidr_range:
raise InvalidSubnetRangeError(cidr_block)
# The subnet size must use a /64 prefix length.
if ipv6_cidr_block and "::/64" not in ipv6_cidr_block:
raise GenericInvalidParameterValueError("ipv6-cidr-block", ipv6_cidr_block)
for subnet in self.get_all_subnets(filters={"vpc-id": vpc_id}):
if subnet.cidr.overlaps(subnet_cidr_block):
raise InvalidSubnetConflictError(cidr_block)
@ -3895,6 +3927,7 @@ class SubnetBackend(object):
subnet_id,
vpc_id,
cidr_block,
ipv6_cidr_block,
availability_zone_data,
default_for_az,
map_public_ip_on_launch,
@ -3937,6 +3970,27 @@ class SubnetBackend(object):
else:
raise InvalidParameterValueError(attr_name)
def get_subnet_from_ipv6_association(self, association_id):
subnet = None
for s in self.get_all_subnets():
if association_id in s.ipv6_cidr_block_associations:
subnet = s
return subnet
def associate_subnet_cidr_block(self, subnet_id, ipv6_cidr_block):
subnet = self.get_subnet(subnet_id)
if not subnet:
raise InvalidSubnetIdError(subnet_id)
association = subnet.attach_ipv6_cidr_block_associations(ipv6_cidr_block)
return association
def disassociate_subnet_cidr_block(self, association_id):
subnet = self.get_subnet_from_ipv6_association(association_id)
if not subnet:
raise InvalidSubnetCidrBlockAssociationID(association_id)
association = subnet.detach_subnet_cidr_block(association_id)
return subnet.id, association
class FlowLogs(TaggedEC2Resource, CloudFormationModel):
def __init__(
@ -4750,12 +4804,13 @@ class RouteBackend(object):
nat_gateway = None
transit_gateway = None
egress_only_igw = None
interface = None
route_table = self.get_route_table(route_table_id)
if interface_id:
# for validating interface Id whether it is valid or not.
self.get_network_interface(interface_id)
interface = self.get_network_interface(interface_id)
else:
if gateway_id:
@ -4787,7 +4842,7 @@ class RouteBackend(object):
nat_gateway=nat_gateway,
egress_only_igw=egress_only_igw,
transit_gateway=transit_gateway,
interface=None,
interface=interface,
vpc_pcx=self.get_vpc_peering_connection(vpc_peering_connection_id)
if vpc_peering_connection_id
else None,

View File

@ -66,8 +66,11 @@ class ElasticNetworkInterfaces(BaseResponse):
def modify_network_interface_attribute(self):
eni_id = self._get_param("NetworkInterfaceId")
group_ids = self._get_multi_param("SecurityGroupId")
source_dest_check = self._get_param("SourceDestCheck")
if self.is_not_dryrun("ModifyNetworkInterface"):
self.ec2_backend.modify_network_interface_attribute(eni_id, group_ids)
self.ec2_backend.modify_network_interface_attribute(
eni_id, group_ids, source_dest_check
)
return MODIFY_NETWORK_INTERFACE_ATTRIBUTE_RESPONSE
def reset_network_interface_attribute(self):
@ -97,9 +100,7 @@ CREATE_NETWORK_INTERFACE_RESPONSE = """
{% if eni.private_ip_address %}
<privateIpAddress>{{ eni.private_ip_address }}</privateIpAddress>
{% endif %}
{% if eni.instance %}
<sourceDestCheck>{{ eni.instance.source_dest_check }}</sourceDestCheck>
{% endif %}
<sourceDestCheck>{{ eni.source_dest_check }}</sourceDestCheck>
<groupSet>
{% for group in eni.group_set %}
<item>
@ -132,7 +133,7 @@ DESCRIBE_NETWORK_INTERFACES_RESPONSE = """<DescribeNetworkInterfacesResponse xml
<subnetId>{{ eni.subnet.id }}</subnetId>
<vpcId>{{ eni.subnet.vpc_id }}</vpcId>
<availabilityZone>us-west-2a</availabilityZone>
<description>{{ eni.description }}</description>
<description>{{ eni.description or "" }}</description>
<ownerId>190610284047</ownerId>
<requesterManaged>false</requesterManaged>
{% if eni.attachment_id %}
@ -145,9 +146,7 @@ DESCRIBE_NETWORK_INTERFACES_RESPONSE = """<DescribeNetworkInterfacesResponse xml
<privateIpAddress>{{ eni.private_ip_address }}</privateIpAddress>
{% endif %}
<privateDnsName>ip-10-0-0-134.us-west-2.compute.internal</privateDnsName>
{% if eni.instance %}
<sourceDestCheck>{{ eni.instance.source_dest_check }}</sourceDestCheck>
{% endif %}
<sourceDestCheck>{{ eni.source_dest_check }}</sourceDestCheck>
<groupSet>
{% for group in eni.group_set %}
<item>

View File

@ -210,6 +210,10 @@ DESCRIBE_ROUTE_TABLES_RESPONSE = """
<transitGatewayId>{{ route.transit_gateway.id }}</transitGatewayId>
<state>active</state>
{% endif %}
{% if route.interface %}
<networkInterfaceId>{{ route.interface.id }}</networkInterfaceId>
<state>active</state>
{% endif %}
</item>
{% endfor %}
</routeSet>

View File

@ -9,6 +9,7 @@ class Subnets(BaseResponse):
def create_subnet(self):
vpc_id = self._get_param("VpcId")
cidr_block = self._get_param("CidrBlock")
ipv6_cidr_block = self._get_param("Ipv6CidrBlock")
availability_zone = self._get_param("AvailabilityZone")
availability_zone_id = self._get_param("AvailabilityZoneId")
tags = self._get_multi_param("TagSpecification")
@ -22,6 +23,7 @@ class Subnets(BaseResponse):
subnet = self.ec2_backend.create_subnet(
vpc_id,
cidr_block,
ipv6_cidr_block,
availability_zone,
availability_zone_id,
context=self,
@ -55,6 +57,26 @@ class Subnets(BaseResponse):
)
return MODIFY_SUBNET_ATTRIBUTE_RESPONSE
def associate_subnet_cidr_block(self):
ipv6_cidr_block = self._get_param("Ipv6CidrBlock")
subnet_id = self._get_param("SubnetId")
association = self.ec2_backend.associate_subnet_cidr_block(
subnet_id, ipv6_cidr_block
)
template = self.response_template(ASSOCIATE_SUBNET_CIDR_BLOCK_RESPONSE)
return template.render(subnet_id=subnet_id, association=association)
def disassociate_subnet_cidr_block(self):
association_id = self._get_param("AssociationId")
subnet_id, association = self.ec2_backend.disassociate_subnet_cidr_block(
association_id
)
template = self.response_template(DISASSOCIATE_SUBNET_CIDR_BLOCK_RESPONSE)
result = template.render(subnet_id=subnet_id, association=association)
return result
CREATE_SUBNET_RESPONSE = """
<CreateSubnetResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
@ -71,7 +93,19 @@ CREATE_SUBNET_RESPONSE = """
<mapPublicIpOnLaunch>{{ subnet.map_public_ip_on_launch }}</mapPublicIpOnLaunch>
<ownerId>{{ subnet.owner_id }}</ownerId>
<assignIpv6AddressOnCreation>{{ 'false' if not subnet.assign_ipv6_address_on_creation or subnet.assign_ipv6_address_on_creation == 'false' else 'true'}}</assignIpv6AddressOnCreation>
<ipv6CidrBlockAssociationSet>{{ subnet.ipv6_cidr_block_associations }}</ipv6CidrBlockAssociationSet>
<ipv6CidrBlockAssociationSet>
{% for ipv6_association in subnet.ipv6_cidr_block_associations.values() %}
{% if ipv6_association.ipv6CidrBlockState == "associated" %}
<item>
<ipv6CidrBlock>{{ ipv6_association.ipv6CidrBlock }}</ipv6CidrBlock>
<associationId>{{ ipv6_association.associationId }}</associationId>
<ipv6CidrBlockState>
<state>{{ ipv6_association.ipv6CidrBlockState }}</state>
</ipv6CidrBlockState>
</item>
{% endif %}
{% endfor %}
</ipv6CidrBlockAssociationSet>
<subnetArn>arn:aws:ec2:{{ subnet._availability_zone.name[0:-1] }}:{{ subnet.owner_id }}:subnet/{{ subnet.id }}</subnetArn>
<tagSet>
{% for tag in subnet.get_tags() %}
@ -109,7 +143,19 @@ DESCRIBE_SUBNETS_RESPONSE = """
<mapPublicIpOnLaunch>{{ subnet.map_public_ip_on_launch }}</mapPublicIpOnLaunch>
<ownerId>{{ subnet.owner_id }}</ownerId>
<assignIpv6AddressOnCreation>{{ 'false' if not subnet.assign_ipv6_address_on_creation or subnet.assign_ipv6_address_on_creation == 'false' else 'true'}}</assignIpv6AddressOnCreation>
<ipv6CidrBlockAssociationSet>{{ subnet.ipv6_cidr_block_associations }}</ipv6CidrBlockAssociationSet>
<ipv6CidrBlockAssociationSet>
{% for ipv6_association in subnet.ipv6_cidr_block_associations.values() %}
{% if ipv6_association.ipv6CidrBlockState == "associated" %}
<item>
<ipv6CidrBlock>{{ ipv6_association.ipv6CidrBlock }}</ipv6CidrBlock>
<associationId>{{ ipv6_association.associationId }}</associationId>
<ipv6CidrBlockState>
<state>{{ ipv6_association.ipv6CidrBlockState }}</state>
</ipv6CidrBlockState>
</item>
{% endif %}
{% endfor %}
</ipv6CidrBlockAssociationSet>
<subnetArn>arn:aws:ec2:{{ subnet._availability_zone.name[0:-1] }}:{{ subnet.owner_id }}:subnet/{{ subnet.id }}</subnetArn>
{% if subnet.get_tags() %}
<tagSet>
@ -133,3 +179,31 @@ MODIFY_SUBNET_ATTRIBUTE_RESPONSE = """
<requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
<return>true</return>
</ModifySubnetAttributeResponse>"""
ASSOCIATE_SUBNET_CIDR_BLOCK_RESPONSE = """
<AssociateSubnetCidrBlock xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
<subnetId>{{ subnet_id }}</subnetId>
<ipv6CidrBlockAssociation>
<ipv6CidrBlock>{{ association.ipv6CidrBlock }}</ipv6CidrBlock>
<ipv6CidrBlockState>
<state>{{ association.ipv6CidrBlockState }}</state>
</ipv6CidrBlockState>
<associationId>{{ association.associationId }}</associationId>
</ipv6CidrBlockAssociation>
</AssociateSubnetCidrBlock>
"""
DISASSOCIATE_SUBNET_CIDR_BLOCK_RESPONSE = """
<DisassociateSubnetCidrBlockResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
<subnetId>{{ subnet_id }}</subnetId>
<ipv6CidrBlockAssociation>
<ipv6CidrBlock>{{ association.ipv6CidrBlock }}</ipv6CidrBlock>
<ipv6CidrBlockState>
<state>{{ association.ipv6CidrBlockState }}</state>
</ipv6CidrBlockState>
<associationId>{{ association.associationId }}</associationId>
</ipv6CidrBlockAssociation>
</DisassociateSubnetCidrBlockResponse>
"""

View File

@ -39,6 +39,7 @@ EC2_RESOURCE_TO_PREFIX = {
"spot-instance-request": "sir",
"spot-fleet-request": "sfr",
"subnet": "subnet",
"subnet-ipv6-cidr-block-association": "subnet-cidr-assoc",
"reservation": "r",
"volume": "vol",
"vpc": "vpc",
@ -107,6 +108,12 @@ def random_subnet_id():
return random_id(prefix=EC2_RESOURCE_TO_PREFIX["subnet"])
def random_subnet_ipv6_cidr_block_association_id():
return random_id(
prefix=EC2_RESOURCE_TO_PREFIX["subnet-ipv6-cidr-block-association"]
)
def random_subnet_association_id():
return random_id(prefix=EC2_RESOURCE_TO_PREFIX["route-table-association"])

View File

@ -88,6 +88,7 @@ TestAccAWSRouteTable_IPv4_To_NatGateway
TestAccAWSRouteTable_IPv4_To_TransitGateway
TestAccAWSRouteTable_IPv4_To_VpcPeeringConnection
TestAccAWSRouteTable_IPv6_To_EgressOnlyInternetGateway
TestAccAWSRouteTable_IPv6_To_NetworkInterface_Unattached
TestAccAWSRouteTable_disappears
TestAccAWSRouteTable_basic
TestAccAWSSsmDocumentDataSource