added subnet ipv6 cidr association; minor fixes (#4242)
This commit is contained in:
parent
cc568c1656
commit
c707ee002c
@ -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
|
||||
),
|
||||
)
|
||||
|
@ -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,
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
"""
|
||||
|
@ -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"])
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user