diff --git a/moto/ec2/models.py b/moto/ec2/models.py index 47f90dae6..40277a660 100644 --- a/moto/ec2/models.py +++ b/moto/ec2/models.py @@ -1750,7 +1750,11 @@ class VPC(TaggedEC2Resource): self.cidr_block = cidr_block self.dhcp_options = None self.state = 'available' - self.is_default = is_default + self.is_default = 'true' if is_default else 'false' + self.enable_dns_support = 'true' + # This attribute is set to 'true' only for default VPCs + # or VPCs created using the wizard of the VPC console + self.enable_dns_hostnames = 'true' if is_default else 'false' @classmethod def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): @@ -1847,6 +1851,20 @@ class VPCBackend(object): vpc.dhcp_options = None return vpc + def describe_vpc_attribute(self, vpc_id, attr_name): + vpc = self.get_vpc(vpc_id) + if attr_name in ('enable_dns_support', 'enable_dns_hostnames'): + return getattr(vpc, attr_name) + else: + raise InvalidParameterValueError(attr_name) + + def modify_vpc_attribute(self, vpc_id, attr_name, attr_value): + vpc = self.get_vpc(vpc_id) + if attr_name in ('enable_dns_support', 'enable_dns_hostnames'): + setattr(vpc, attr_name, attr_value) + else: + raise InvalidParameterValueError(attr_name) + class VPCPeeringConnectionStatus(object): def __init__(self, code='initiating-request', message=''): @@ -1936,14 +1954,14 @@ class VPCPeeringConnectionBackend(object): class Subnet(TaggedEC2Resource): - def __init__(self, ec2_backend, subnet_id, vpc_id, cidr_block, availability_zone, defaultForAz, + def __init__(self, ec2_backend, subnet_id, vpc_id, cidr_block, availability_zone, default_for_az, map_public_ip_on_launch): self.ec2_backend = ec2_backend self.id = subnet_id self.vpc_id = vpc_id self.cidr_block = cidr_block self._availability_zone = availability_zone - self.defaultForAz = defaultForAz + self.default_for_az = default_for_az self.map_public_ip_on_launch = map_public_ip_on_launch @classmethod @@ -1996,7 +2014,7 @@ class Subnet(TaggedEC2Resource): Taken from: http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSubnets.html """ - if filter_name in ['cidr', 'cidrBlock', 'cidr-block']: + if filter_name in ('cidr', 'cidrBlock', 'cidr-block'): return self.cidr_block elif filter_name == 'vpc-id': return self.vpc_id @@ -2004,8 +2022,8 @@ class Subnet(TaggedEC2Resource): return self.id elif filter_name == 'availabilityZone': return self.availability_zone - elif filter_name == 'defaultForAz': - return self.defaultForAz + elif filter_name in ('defaultForAz', 'default-for-az'): + return self.default_for_az filter_value = super(Subnet, self).get_filter_value(filter_name) @@ -2035,9 +2053,9 @@ class SubnetBackend(object): def create_subnet(self, vpc_id, cidr_block, availability_zone=None): subnet_id = random_subnet_id() vpc = self.get_vpc(vpc_id) # Validate VPC exists - defaultForAz = "true" if vpc.is_default else "false" - map_public_ip_on_launch = "true" if vpc.is_default else "false" - subnet = Subnet(self, subnet_id, vpc_id, cidr_block, availability_zone, defaultForAz, map_public_ip_on_launch) + default_for_az = vpc.is_default + map_public_ip_on_launch = vpc.is_default + subnet = Subnet(self, subnet_id, vpc_id, cidr_block, availability_zone, default_for_az, map_public_ip_on_launch) # AWS associates a new subnet with the default Network ACL self.associate_default_network_acl_with_subnet(subnet_id) diff --git a/moto/ec2/responses/subnets.py b/moto/ec2/responses/subnets.py index 0aa789554..af5ec7605 100644 --- a/moto/ec2/responses/subnets.py +++ b/moto/ec2/responses/subnets.py @@ -79,7 +79,7 @@ DESCRIBE_SUBNETS_RESPONSE = """ {{ subnet.cidr_block }} 251 {{ subnet.availability_zone }} - {{ subnet.defaultForAz }} + {{ subnet.default_for_az }} {{ subnet.map_public_ip_on_launch }} {% for tag in subnet.get_tags() %} diff --git a/moto/ec2/responses/vpcs.py b/moto/ec2/responses/vpcs.py index 7e1199980..86df332aa 100644 --- a/moto/ec2/responses/vpcs.py +++ b/moto/ec2/responses/vpcs.py @@ -1,5 +1,6 @@ from __future__ import unicode_literals from moto.core.responses import BaseResponse +from moto.core.utils import camelcase_to_underscores from moto.ec2.utils import filters_from_querystring, vpc_ids_from_querystring @@ -23,6 +24,24 @@ class VPCs(BaseResponse): template = self.response_template(DESCRIBE_VPCS_RESPONSE) return template.render(vpcs=vpcs) + def describe_vpc_attribute(self): + vpc_id = self.querystring.get('VpcId')[0] + attribute = self.querystring.get('Attribute')[0] + attr_name = camelcase_to_underscores(attribute) + value = self.ec2_backend.describe_vpc_attribute(vpc_id, attr_name) + template = self.response_template(DESCRIBE_VPC_ATTRIBUTE_RESPONSE) + return template.render(vpc_id=vpc_id, attribute=attribute, value=value) + + def modify_vpc_attribute(self): + vpc_id = self.querystring.get('VpcId')[0] + + for attribute in ('EnableDnsSupport', 'EnableDnsHostnames'): + if self.querystring.get('%s.Value' % attribute): + attr_name = camelcase_to_underscores(attribute) + attr_value = self.querystring.get('%s.Value' % attribute)[0] + self.ec2_backend.modify_vpc_attribute(vpc_id, attr_name, attr_value) + return MODIFY_VPC_ATTRIBUTE_RESPONSE + CREATE_VPC_RESPONSE = """ @@ -79,3 +98,18 @@ DELETE_VPC_RESPONSE = """ true """ + +DESCRIBE_VPC_ATTRIBUTE_RESPONSE = """ + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + {{ vpc_id }} + <{{ attribute }}> + {{ value }} + +""" + +MODIFY_VPC_ATTRIBUTE_RESPONSE = """ + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + true +""" diff --git a/tests/test_ec2/test_subnets.py b/tests/test_ec2/test_subnets.py index 6cc06a559..8ca17232d 100644 --- a/tests/test_ec2/test_subnets.py +++ b/tests/test_ec2/test_subnets.py @@ -71,19 +71,59 @@ def test_subnet_should_have_proper_availability_zone_set(): subnetA = conn.create_subnet(vpcA.id, "10.0.0.0/24", availability_zone='us-west-1b') subnetA.availability_zone.should.equal('us-west-1b') + +@mock_ec2 +def test_default_subnet(): + ec2 = boto3.resource('ec2', region_name='us-west-1') + + # Create the default VPC + default_vpc = ec2.create_vpc(CidrBlock='172.31.0.0/16') + default_vpc.reload() + default_vpc.is_default.should.be.ok + + subnet = ec2.create_subnet(VpcId=default_vpc.id, CidrBlock='172.31.0.0/20', AvailabilityZone='us-west-1a') + subnet.reload() + subnet.map_public_ip_on_launch.should.be.ok + + +@mock_ec2 +def test_non_default_subnet(): + ec2 = boto3.resource('ec2', region_name='us-west-1') + + # Create the default VPC + ec2.create_vpc(CidrBlock='172.31.0.0/16') + + # Create the non default VPC + vpc = ec2.create_vpc(CidrBlock='10.0.0.0/16') + vpc.reload() + vpc.is_default.shouldnt.be.ok + + subnet = ec2.create_subnet(VpcId=vpc.id, CidrBlock='10.0.0.0/24', AvailabilityZone='us-west-1a') + subnet.reload() + subnet.map_public_ip_on_launch.shouldnt.be.ok + + @mock_ec2 def test_modify_subnet_attribute(): ec2 = boto3.resource('ec2', region_name='us-west-1') client = boto3.client('ec2', region_name='us-west-1') + + # Create the default VPC + ec2.create_vpc(CidrBlock='172.31.0.0/16') + vpc = ec2.create_vpc(CidrBlock='10.0.0.0/16') subnet = ec2.create_subnet(VpcId=vpc.id, CidrBlock='10.0.0.0/24', AvailabilityZone='us-west-1a') - subnet.map_public_ip_on_launch.should.be.false + # 'map_public_ip_on_launch' is set when calling 'DescribeSubnets' action + subnet.reload() + + # For non default subnet, attribute value should be 'False' + subnet.map_public_ip_on_launch.shouldnt.be.ok client.modify_subnet_attribute(SubnetId=subnet.id, MapPublicIpOnLaunch={'Value': True}) subnet.reload() - subnet.map_public_ip_on_launch.should.be.true + subnet.map_public_ip_on_launch.should.be.ok @mock_ec2 def test_modify_subnet_attribute_validation(): diff --git a/tests/test_ec2/test_vpcs.py b/tests/test_ec2/test_vpcs.py index e4148625c..8f9bdc140 100644 --- a/tests/test_ec2/test_vpcs.py +++ b/tests/test_ec2/test_vpcs.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import tests.backport_assert_raises # flake8: noqa from nose.tools import assert_raises +import boto3 import boto from boto.exception import EC2ResponseError import sure # noqa @@ -208,3 +209,86 @@ def test_vpc_get_by_tag_value_subset(): vpc_ids = tuple(map(lambda v: v.id, vpcs)) vpc1.id.should.be.within(vpc_ids) vpc2.id.should.be.within(vpc_ids) + + +@mock_ec2 +def test_default_vpc(): + ec2 = boto3.resource('ec2', region_name='us-west-1') + + # Create the default VPC + default_vpc = ec2.create_vpc(CidrBlock='172.31.0.0/16') + default_vpc.reload() + default_vpc.is_default.should.be.ok + + # Test default values for VPC attributes + response = default_vpc.describe_attribute(Attribute='enableDnsSupport') + attr = response.get('EnableDnsSupport') + attr.get('Value').should.be.ok + + response = default_vpc.describe_attribute(Attribute='enableDnsHostnames') + attr = response.get('EnableDnsHostnames') + attr.get('Value').should.be.ok + + +@mock_ec2 +def test_non_default_vpc(): + ec2 = boto3.resource('ec2', region_name='us-west-1') + + # Create the default VPC + ec2.create_vpc(CidrBlock='172.31.0.0/16') + + # Create the non default VPC + vpc = ec2.create_vpc(CidrBlock='10.0.0.0/16') + vpc.reload() + vpc.is_default.shouldnt.be.ok + + # Test default values for VPC attributes + response = vpc.describe_attribute(Attribute='enableDnsSupport') + attr = response.get('EnableDnsSupport') + attr.get('Value').should.be.ok + + response = vpc.describe_attribute(Attribute='enableDnsHostnames') + attr = response.get('EnableDnsHostnames') + attr.get('Value').shouldnt.be.ok + + +@mock_ec2 +def test_vpc_modify_enable_dns_support(): + ec2 = boto3.resource('ec2', region_name='us-west-1') + + # Create the default VPC + ec2.create_vpc(CidrBlock='172.31.0.0/16') + + vpc = ec2.create_vpc(CidrBlock='10.0.0.0/16') + + # Test default values for VPC attributes + response = vpc.describe_attribute(Attribute='enableDnsSupport') + attr = response.get('EnableDnsSupport') + attr.get('Value').should.be.ok + + vpc.modify_attribute(EnableDnsSupport={'Value': False}) + + response = vpc.describe_attribute(Attribute='enableDnsSupport') + attr = response.get('EnableDnsSupport') + attr.get('Value').shouldnt.be.ok + + +@mock_ec2 +def test_vpc_modify_enable_dns_hostnames(): + ec2 = boto3.resource('ec2', region_name='us-west-1') + + # Create the default VPC + ec2.create_vpc(CidrBlock='172.31.0.0/16') + + vpc = ec2.create_vpc(CidrBlock='10.0.0.0/16') + + # Test default values for VPC attributes + response = vpc.describe_attribute(Attribute='enableDnsHostnames') + attr = response.get('EnableDnsHostnames') + attr.get('Value').shouldnt.be.ok + + vpc.modify_attribute(EnableDnsHostnames={'Value': True}) + + response = vpc.describe_attribute(Attribute='enableDnsHostnames') + attr = response.get('EnableDnsHostnames') + attr.get('Value').should.be.ok