From 0f18dd597cf419e15e8afb5bf5f1fd95ef9f634b Mon Sep 17 00:00:00 2001 From: Tyler Sanders Date: Wed, 12 Nov 2014 15:33:31 -0600 Subject: [PATCH 1/5] Add basic Network ACL create, describe support --- moto/ec2/exceptions.py | 8 +++ moto/ec2/models.py | 76 ++++++++++++++++++++- moto/ec2/responses/network_acls.py | 100 ++++++++++++++++++++++++++-- moto/ec2/utils.py | 4 ++ tests/test_ec2/test_network_acls.py | 36 +++++++++- 5 files changed, 214 insertions(+), 10 deletions(-) diff --git a/moto/ec2/exceptions.py b/moto/ec2/exceptions.py index 306e898e0..f01087817 100644 --- a/moto/ec2/exceptions.py +++ b/moto/ec2/exceptions.py @@ -72,6 +72,14 @@ class InvalidSubnetIdError(EC2ClientError): .format(subnet_id)) +class InvalidNetworkAclIdError(EC2ClientError): + def __init__(self, network_acl_id): + super(InvalidNetworkAclIdError, self).__init__( + "InvalidNetworkAclID.NotFound", + "The network acl ID '{0}' does not exist" + .format(network_acl_id)) + + class InvalidNetworkInterfaceIdError(EC2ClientError): def __init__(self, eni_id): super(InvalidNetworkInterfaceIdError, self).__init__( diff --git a/moto/ec2/models.py b/moto/ec2/models.py index 377699164..a896300e5 100644 --- a/moto/ec2/models.py +++ b/moto/ec2/models.py @@ -51,7 +51,8 @@ from .exceptions import ( InvalidVPCPeeringConnectionStateTransitionError, TagLimitExceeded, InvalidID, - InvalidCIDRSubnetError + InvalidCIDRSubnetError, + InvalidNetworkAclIdError ) from .utils import ( EC2_RESOURCE_TO_PREFIX, @@ -86,6 +87,7 @@ from .utils import ( is_valid_cidr, filter_internet_gateways, filter_reservations, + random_network_acl_id, ) @@ -2268,6 +2270,75 @@ class DHCPOptionsSetBackend(object): return True +class NetworkAclBackend(object): + def __init__(self): + self.network_acls = {} + super(NetworkAclBackend, self).__init__() + + def get_network_acl(self, network_acl_id): + network_acl = self.network_acls.get(network_acl_id, None) + if not network_acl: + raise InvalidNetworkAclIdError(network_acl_id) + return network_acl + + def create_network_acl(self, vpc_id): + network_acl_id = random_network_acl_id() + network_acl = NetworkAcl(self, network_acl_id, vpc_id) + vpc = self.get_vpc(vpc_id) + self.network_acls[network_acl_id] = network_acl + return network_acl + + def get_all_network_acls(self, filters=None): + network_acls = self.network_acls.values() + return generic_filter(filters, network_acls) + + def delete_network_acl(self, network_acl_id): + deleted = self.network_acls.pop(network_acl_id, None) + if not deleted: + raise InvalidNetworkAclIdError(network_acl_id) + return deleted + + def create_network_acl_entry(self, network_acl_id, rule_number, + protocol, rule_action, egress, cidr_block, + icmp_code, icmp_type, port_range_from, + port_range_to): + + network_acl_entry = NetworkAclEntry(self, network_acl_id, rule_number, + protocol, rule_action, egress, + cidr_block, icmp_code, icmp_type, + port_range_from, port_range_to) + + network_acl = self.get_network_acl(network_acl_id) + network_acl.network_acl_entries.append(network_acl_entry) + return network_acl_entry + + +class NetworkAcl(TaggedEC2Resource): + def __init__(self, ec2_backend, network_acl_id, vpc_id): + self.ec2_backend = ec2_backend + self.id = network_acl_id + self.vpc_id = vpc_id + self.network_acl_entries = [] + + +class NetworkAclEntry(TaggedEC2Resource): + def __init__(self, ec2_backend, network_acl_id, rule_number, + protocol, rule_action, egress, cidr_block, + icmp_code, icmp_type, port_range_from, + port_range_to): + self.ec2_backend = ec2_backend + self.network_acl_id = network_acl_id + self.rule_number = rule_number + self.protocol = protocol + self.rule_action = rule_action + self.egress = egress + self.cidr_block = cidr_block + self.icmp_code = icmp_code + self.icmp_type = icmp_type + self.port_range_from = port_range_from + self.port_range_to = port_range_to + + class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend, RegionsAndZonesBackend, SecurityGroupBackend, EBSBackend, VPCBackend, SubnetBackend, SubnetRouteTableAssociationBackend, @@ -2275,7 +2346,8 @@ class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend, VPCPeeringConnectionBackend, RouteTableBackend, RouteBackend, InternetGatewayBackend, VPCGatewayAttachmentBackend, SpotRequestBackend, - ElasticAddressBackend, KeyPairBackend, DHCPOptionsSetBackend): + ElasticAddressBackend, KeyPairBackend, DHCPOptionsSetBackend, + NetworkAclBackend): # Use this to generate a proper error template response when in a response handler. def raise_error(self, code, message): diff --git a/moto/ec2/responses/network_acls.py b/moto/ec2/responses/network_acls.py index c0978c30d..4efdddd8f 100644 --- a/moto/ec2/responses/network_acls.py +++ b/moto/ec2/responses/network_acls.py @@ -1,25 +1,111 @@ from __future__ import unicode_literals +from jinja2 import Template from moto.core.responses import BaseResponse +from moto.ec2.utils import filters_from_querystring class NetworkACLs(BaseResponse): + def create_network_acl(self): - raise NotImplementedError('NetworkACLs(AmazonVPC).create_network_acl is not yet implemented') + vpc_id = self.querystring.get('VpcId')[0] + network_acl = self.ec2_backend.create_network_acl(vpc_id) + template = Template(CREATE_NETWORK_ACL_RESPONSE) + return template.render(network_acl=network_acl) def create_network_acl_entry(self): - raise NotImplementedError('NetworkACLs(AmazonVPC).create_network_acl_entry is not yet implemented') + network_acl_id = self.querystring.get('NetworkAclId')[0] + rule_number = self.querystring.get('RuleNumber')[0] + protocol = self.querystring.get('Protocol')[0] + rule_action = self.querystring.get('RuleAction')[0] + egress = self.querystring.get('Egress')[0] + cidr_block = self.querystring.get('CidrBlock')[0] + icmp_code = self.querystring.get('Icmp.Code', [None])[0] + icmp_type = self.querystring.get('Icmp.Type', [None])[0] + port_range_from = self.querystring.get('PortRange.From')[0] + port_range_to = self.querystring.get('PortRange.To')[0] + + network_acl_entry = self.ec2_backend.create_network_acl_entry( + network_acl_id, rule_number, protocol, rule_action, + egress, cidr_block, icmp_code, icmp_type, + port_range_from, port_range_to) + + template = Template(CREATE_NETWORK_ACL_ENTRY_RESPONSE) + return template.render(network_acl_entry=network_acl_entry) def delete_network_acl(self): - raise NotImplementedError('NetworkACLs(AmazonVPC).delete_network_acl is not yet implemented') + raise NotImplementedError( + 'NetworkACLs(AmazonVPC).delete_network_acl is not yet implemented') def delete_network_acl_entry(self): - raise NotImplementedError('NetworkACLs(AmazonVPC).delete_network_acl_entry is not yet implemented') + raise NotImplementedError( + 'NetworkACLs(AmazonVPC).delete_network_acl_entry is not yet implemented') def describe_network_acls(self): - raise NotImplementedError('NetworkACLs(AmazonVPC).describe_network_acls is not yet implemented') + filters = filters_from_querystring(self.querystring) + network_acls = self.ec2_backend.get_all_network_acls(filters) + template = Template(DESCRIBE_NETWORK_ACL_RESPONSE) + return template.render(network_acls=network_acls) def replace_network_acl_association(self): - raise NotImplementedError('NetworkACLs(AmazonVPC).replace_network_acl_association is not yet implemented') + raise NotImplementedError( + 'NetworkACLs(AmazonVPC).replace_network_acl_association is not yet implemented') def replace_network_acl_entry(self): - raise NotImplementedError('NetworkACLs(AmazonVPC).replace_network_acl_entry is not yet implemented') + raise NotImplementedError( + 'NetworkACLs(AmazonVPC).replace_network_acl_entry is not yet implemented') + + +CREATE_NETWORK_ACL_RESPONSE = """ + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + + {{ network_acl.id }} + {{ network_acl.vpc_id }} + false + + + + + +""" + +DESCRIBE_NETWORK_ACL_RESPONSE = """ + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + + {% for network_acl in network_acls %} + + {{ network_acl.id }} + {{ network_acl.vpc_id }} + true + + {% for entry in network_acl.network_acl_entries %} + + {{ entry.rule_number }} + {{ entry.protocol }} + {{ entry.rule_action }} + {{ entry.egress.lower() }} + {{ entry.cidr_block }} + {% if entry.port_range_from or entry.port_range_to %} + + {{ entry.port_range_from }} + {{ entry.port_range_to }} + + {% endif %} + + {% endfor %} + + + + + {% endfor %} + + +""" + +CREATE_NETWORK_ACL_ENTRY_RESPONSE = """ + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + true + +""" diff --git a/moto/ec2/utils.py b/moto/ec2/utils.py index bb15f7920..80362ae17 100644 --- a/moto/ec2/utils.py +++ b/moto/ec2/utils.py @@ -72,6 +72,10 @@ def random_subnet_association_id(): return random_id(prefix=EC2_RESOURCE_TO_PREFIX['route-table-association']) +def random_network_acl_id(): + return random_id(prefix=EC2_RESOURCE_TO_PREFIX['network-acl']) + + def random_volume_id(): return random_id(prefix=EC2_RESOURCE_TO_PREFIX['volume']) diff --git a/tests/test_ec2/test_network_acls.py b/tests/test_ec2/test_network_acls.py index 3fdd2f630..a8b0be16d 100644 --- a/tests/test_ec2/test_network_acls.py +++ b/tests/test_ec2/test_network_acls.py @@ -7,4 +7,38 @@ from moto import mock_ec2 @mock_ec2 def test_network_acls(): - pass + + conn = boto.connect_vpc('the_key', 'the secret') + vpc = conn.create_vpc("10.0.0.0/16") + + network_acl = conn.create_network_acl(vpc.id) + + all_network_acls = conn.get_all_network_acls() + all_network_acls.should.have.length_of(1) + + +@mock_ec2 +def test_network_acl_entries(): + + conn = boto.connect_vpc('the_key', 'the secret') + vpc = conn.create_vpc("10.0.0.0/16") + + network_acl = conn.create_network_acl(vpc.id) + + network_acl_entry = conn.create_network_acl_entry( + network_acl.id, 110, 6, + 'ALLOW', '0.0.0.0/0', False, + port_range_from='443', + port_range_to='443' + ) + + all_network_acls = conn.get_all_network_acls() + all_network_acls.should.have.length_of(1) + + entries = all_network_acls[0].network_acl_entries + entries.should.have.length_of(1) + entries[0].rule_number.should.equal('110') + entries[0].protocol.should.equal('6') + entries[0].rule_action.should.equal('ALLOW') + + From efc8caaf487731a0f5674dd4d15656b8fcbcbf44 Mon Sep 17 00:00:00 2001 From: Tyler Sanders Date: Wed, 12 Nov 2014 15:48:44 -0600 Subject: [PATCH 2/5] Test Network ACL describe --- moto/ec2/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moto/ec2/models.py b/moto/ec2/models.py index a896300e5..0632e10ba 100644 --- a/moto/ec2/models.py +++ b/moto/ec2/models.py @@ -2373,7 +2373,7 @@ class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend, elif resource_prefix == EC2_RESOURCE_TO_PREFIX['internet-gateway']: self.describe_internet_gateways(internet_gateway_ids=[resource_id]) elif resource_prefix == EC2_RESOURCE_TO_PREFIX['network-acl']: - self.raise_not_implemented_error('DescribeNetworkAcls') + self.get_all_network_acls() elif resource_prefix == EC2_RESOURCE_TO_PREFIX['network-interface']: self.describe_network_interfaces(filters={'network-interface-id': resource_id}) elif resource_prefix == EC2_RESOURCE_TO_PREFIX['reserved-instance']: From 400d12b175878161671306b2b658251ed83c2b53 Mon Sep 17 00:00:00 2001 From: Tyler Sanders Date: Fri, 14 Nov 2014 17:23:56 -0600 Subject: [PATCH 3/5] Add default Network ACL during VPC creation. Associate default Network ACL with a new subnet. Add support for associating a new Network ACL with a subnet. --- moto/ec2/models.py | 81 +++++++++++++++++++++++++++-- moto/ec2/responses/network_acls.py | 47 ++++++++++++++--- moto/ec2/utils.py | 13 +++++ tests/test_ec2/test_network_acls.py | 71 ++++++++++++++++++++++++- 4 files changed, 198 insertions(+), 14 deletions(-) diff --git a/moto/ec2/models.py b/moto/ec2/models.py index 0632e10ba..40e8ac179 100644 --- a/moto/ec2/models.py +++ b/moto/ec2/models.py @@ -88,7 +88,7 @@ from .utils import ( filter_internet_gateways, filter_reservations, random_network_acl_id, -) + random_network_acl_subnet_association_id) def validate_resource_ids(resource_ids): @@ -1412,6 +1412,9 @@ class VPCBackend(object): # AWS creates a default main route table and security group. main_route_table = self.create_route_table(vpc_id, main=True) + # AWS creates a default Network ACL + default_network_acl = self.create_network_acl(vpc_id, default=True) + default = self.get_security_group_from_name('default', vpc_id=vpc_id) if not default: self.create_security_group('default', 'default VPC security group', vpc_id=vpc_id) @@ -1603,6 +1606,10 @@ class SubnetBackend(object): def create_subnet(self, vpc_id, cidr_block): subnet_id = random_subnet_id() subnet = Subnet(self, subnet_id, vpc_id, cidr_block) + + # AWS associates a new subnet with the default Network ACL + self.associate_default_network_acl_with_subnet(subnet_id) + vpc = self.get_vpc(vpc_id) # Validate VPC exists self.subnets[subnet_id] = subnet return subnet @@ -2281,15 +2288,23 @@ class NetworkAclBackend(object): raise InvalidNetworkAclIdError(network_acl_id) return network_acl - def create_network_acl(self, vpc_id): + def create_network_acl(self, vpc_id, default=False): network_acl_id = random_network_acl_id() - network_acl = NetworkAcl(self, network_acl_id, vpc_id) vpc = self.get_vpc(vpc_id) + network_acl = NetworkAcl(self, network_acl_id, vpc_id, default) self.network_acls[network_acl_id] = network_acl return network_acl - def get_all_network_acls(self, filters=None): + def get_all_network_acls(self, network_acl_ids=None, filters=None): network_acls = self.network_acls.values() + + if network_acl_ids: + network_acls = [network_acl for network_acl in network_acls + if network_acl.id in network_acl_ids ] + if len(network_acls) != len(network_acl_ids): + invalid_id = list(set(network_acl_ids).difference(set([network_acl.id for network_acl in network_acls])))[0] + raise InvalidRouteTableIdError(invalid_id) + return generic_filter(filters, network_acls) def delete_network_acl(self, network_acl_id): @@ -2312,13 +2327,69 @@ class NetworkAclBackend(object): network_acl.network_acl_entries.append(network_acl_entry) return network_acl_entry + def replace_network_acl_association(self, association_id, + network_acl_id): + + # lookup existing association for subnet and delete it + default_acl = next(value for key, value in self.network_acls.iteritems() + if association_id in value.associations.keys()) + + subnet_id = None + for key, value in default_acl.associations.iteritems(): + if key == association_id: + subnet_id = default_acl.associations[key].subnet_id + del default_acl.associations[key] + break + + new_assoc_id = random_network_acl_subnet_association_id() + association = NetworkAclAssociation(self, + new_assoc_id, + subnet_id, + network_acl_id) + new_acl = self.get_network_acl(network_acl_id) + new_acl.associations[new_assoc_id] = association + return association + + def associate_default_network_acl_with_subnet(self, subnet_id): + association_id = random_network_acl_subnet_association_id() + acl = next(acl for acl in self.network_acls.values() if acl.default) + acl.associations[association_id] = NetworkAclAssociation(self, association_id, + subnet_id, acl.id) + + +class NetworkAclAssociation(object): + def __init__(self, ec2_backend, new_association_id, + subnet_id, network_acl_id): + self.ec2_backend = ec2_backend + self.id = new_association_id + self.subnet_id = subnet_id + self.network_acl_id = network_acl_id + super(NetworkAclAssociation, self).__init__() + class NetworkAcl(TaggedEC2Resource): - def __init__(self, ec2_backend, network_acl_id, vpc_id): + def __init__(self, ec2_backend, network_acl_id, vpc_id, default=False): self.ec2_backend = ec2_backend self.id = network_acl_id self.vpc_id = vpc_id self.network_acl_entries = [] + self.associations = {} + self.default = default + + def get_filter_value(self, filter_name): + if filter_name == "vpc-id": + return self.vpc_id + elif filter_name == "association.network-acl-id": + return self.id + elif filter_name == "association.subnet-id": + return [assoc.subnet_id for assoc in self.associations.values()] + + filter_value = super(NetworkAcl, self).get_filter_value(filter_name) + + if filter_value is None: + self.ec2_backend.raise_not_implemented_error("The filter '{0}' for DescribeNetworkAcls".format(filter_name)) + + return filter_value class NetworkAclEntry(TaggedEC2Resource): diff --git a/moto/ec2/responses/network_acls.py b/moto/ec2/responses/network_acls.py index 4efdddd8f..8ca447338 100644 --- a/moto/ec2/responses/network_acls.py +++ b/moto/ec2/responses/network_acls.py @@ -1,7 +1,8 @@ from __future__ import unicode_literals from jinja2 import Template from moto.core.responses import BaseResponse -from moto.ec2.utils import filters_from_querystring +from moto.ec2.utils import filters_from_querystring, \ + network_acl_ids_from_querystring class NetworkACLs(BaseResponse): @@ -33,22 +34,32 @@ class NetworkACLs(BaseResponse): return template.render(network_acl_entry=network_acl_entry) def delete_network_acl(self): - raise NotImplementedError( - 'NetworkACLs(AmazonVPC).delete_network_acl is not yet implemented') + network_acl_id = self.querystring.get('NetworkAclId')[0] + self.ec2_backend.delete_network_acl(network_acl_id) + template = Template(DELETE_NETWORK_ACL_ASSOCIATION) + return template.render() def delete_network_acl_entry(self): raise NotImplementedError( 'NetworkACLs(AmazonVPC).delete_network_acl_entry is not yet implemented') def describe_network_acls(self): + network_acl_ids = network_acl_ids_from_querystring(self.querystring) filters = filters_from_querystring(self.querystring) - network_acls = self.ec2_backend.get_all_network_acls(filters) + network_acls = self.ec2_backend.get_all_network_acls(network_acl_ids, filters) template = Template(DESCRIBE_NETWORK_ACL_RESPONSE) return template.render(network_acls=network_acls) def replace_network_acl_association(self): - raise NotImplementedError( - 'NetworkACLs(AmazonVPC).replace_network_acl_association is not yet implemented') + association_id = self.querystring.get('AssociationId')[0] + network_acl_id = self.querystring.get('NetworkAclId')[0] + + association = self.ec2_backend.replace_network_acl_association( + association_id, + network_acl_id + ) + template = Template(REPLACE_NETWORK_ACL_ASSOCIATION) + return template.render(association=association) def replace_network_acl_entry(self): raise NotImplementedError( @@ -95,7 +106,15 @@ DESCRIBE_NETWORK_ACL_RESPONSE = """ {% endfor %} - + + {% for association in network_acl.associations.values() %} + + {{ association.id }} + {{ association.network_acl_id }} + {{ association.subnet_id }} + + {% endfor %} + {% endfor %} @@ -109,3 +128,17 @@ CREATE_NETWORK_ACL_ENTRY_RESPONSE = """ true """ + +REPLACE_NETWORK_ACL_ASSOCIATION = """ + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + {{ association.new_association_id }} + +""" + +DELETE_NETWORK_ACL_ASSOCIATION = """ + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + true + +""" \ No newline at end of file diff --git a/moto/ec2/utils.py b/moto/ec2/utils.py index 80362ae17..587a9fa31 100644 --- a/moto/ec2/utils.py +++ b/moto/ec2/utils.py @@ -10,6 +10,7 @@ EC2_RESOURCE_TO_PREFIX = { 'instance': 'i', 'internet-gateway': 'igw', 'network-acl': 'acl', + 'network-acl-subnet-assoc': 'aclassoc', 'network-interface': 'eni', 'network-interface-attachment': 'eni-attach', 'reserved-instance': 'uuid4', @@ -76,6 +77,10 @@ def random_network_acl_id(): return random_id(prefix=EC2_RESOURCE_TO_PREFIX['network-acl']) +def random_network_acl_subnet_association_id(): + return random_id(prefix=EC2_RESOURCE_TO_PREFIX['network-acl-subnet-assoc']) + + def random_volume_id(): return random_id(prefix=EC2_RESOURCE_TO_PREFIX['volume']) @@ -162,6 +167,14 @@ def route_table_ids_from_querystring(querystring_dict): return route_table_ids +def network_acl_ids_from_querystring(querystring_dict): + network_acl_ids = [] + for key, value in querystring_dict.items(): + if 'NetworkAclId' in key: + network_acl_ids.append(value[0]) + return network_acl_ids + + def vpc_ids_from_querystring(querystring_dict): vpc_ids = [] for key, value in querystring_dict.items(): diff --git a/tests/test_ec2/test_network_acls.py b/tests/test_ec2/test_network_acls.py index a8b0be16d..7f5009008 100644 --- a/tests/test_ec2/test_network_acls.py +++ b/tests/test_ec2/test_network_acls.py @@ -5,6 +5,15 @@ import sure # noqa from moto import mock_ec2 +@mock_ec2 +def test_default_network_acl_created_with_vpc(): + + conn = boto.connect_vpc('the_key', 'the secret') + vpc = conn.create_vpc("10.0.0.0/16") + + all_network_acls = conn.get_all_network_acls() + all_network_acls.should.have.length_of(1) + @mock_ec2 def test_network_acls(): @@ -13,9 +22,22 @@ def test_network_acls(): network_acl = conn.create_network_acl(vpc.id) + all_network_acls = conn.get_all_network_acls() + all_network_acls.should.have.length_of(2) + +@mock_ec2 +def test_new_subnet_associates_with_default_network_acl(): + + conn = boto.connect_vpc('the_key', 'the secret') + vpc = conn.create_vpc("10.0.0.0/16") + + subnet = conn.create_subnet(vpc.id, "10.0.0.0/18") all_network_acls = conn.get_all_network_acls() all_network_acls.should.have.length_of(1) + acl = all_network_acls[0] + acl.associations.should.have.length_of(1) + acl.associations[0].subnet_id.should.equal(subnet.id) @mock_ec2 def test_network_acl_entries(): @@ -33,12 +55,57 @@ def test_network_acl_entries(): ) all_network_acls = conn.get_all_network_acls() - all_network_acls.should.have.length_of(1) + all_network_acls.should.have.length_of(2) - entries = all_network_acls[0].network_acl_entries + test_network_acl = next(na for na in all_network_acls + if na.id == network_acl.id) + entries = test_network_acl.network_acl_entries entries.should.have.length_of(1) entries[0].rule_number.should.equal('110') entries[0].protocol.should.equal('6') entries[0].rule_action.should.equal('ALLOW') +@mock_ec2 +def test_associate_new_network_acl_with_subnet(): + + conn = boto.connect_vpc('the_key', 'the secret') + vpc = conn.create_vpc("10.0.0.0/16") + subnet = conn.create_subnet(vpc.id, "10.0.0.0/18") + network_acl = conn.create_network_acl(vpc.id) + + conn.associate_network_acl(network_acl.id, subnet.id) + + all_network_acls = conn.get_all_network_acls() + all_network_acls.should.have.length_of(2) + + test_network_acl = next(na for na in all_network_acls + if na.id == network_acl.id) + + test_network_acl.associations.should.have.length_of(1) + test_network_acl.associations[0].subnet_id.should.equal(subnet.id) + + +@mock_ec2 +def test_delete_network_acl(): + + conn = boto.connect_vpc('the_key', 'the secret') + vpc = conn.create_vpc("10.0.0.0/16") + subnet = conn.create_subnet(vpc.id, "10.0.0.0/18") + network_acl = conn.create_network_acl(vpc.id) + + all_network_acls = conn.get_all_network_acls() + all_network_acls.should.have.length_of(2) + + any(acl.id == network_acl.id for acl in all_network_acls).should.be.ok + + conn.delete_network_acl(network_acl.id) + + updated_network_acls = conn.get_all_network_acls() + updated_network_acls.should.have.length_of(1) + + any(acl.id == network_acl.id for acl in updated_network_acls).shouldnt.be.ok + + + + From 6d2c9d9960bfe66d3d55e79d6d46e76936281ec4 Mon Sep 17 00:00:00 2001 From: Tyler Sanders Date: Fri, 14 Nov 2014 17:31:08 -0600 Subject: [PATCH 4/5] Move call to associate default Network ACL with a new subnet. --- moto/ec2/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moto/ec2/models.py b/moto/ec2/models.py index 40e8ac179..8c65553ad 100644 --- a/moto/ec2/models.py +++ b/moto/ec2/models.py @@ -1606,11 +1606,11 @@ class SubnetBackend(object): def create_subnet(self, vpc_id, cidr_block): subnet_id = random_subnet_id() subnet = Subnet(self, subnet_id, vpc_id, cidr_block) + vpc = self.get_vpc(vpc_id) # Validate VPC exists # AWS associates a new subnet with the default Network ACL self.associate_default_network_acl_with_subnet(subnet_id) - vpc = self.get_vpc(vpc_id) # Validate VPC exists self.subnets[subnet_id] = subnet return subnet From d89dce258337f27b20af2f10ce1ee158e2030a6e Mon Sep 17 00:00:00 2001 From: Tyler Sanders Date: Mon, 17 Nov 2014 08:54:06 -0600 Subject: [PATCH 5/5] Use items() instead of iteritems() for Python 3 compatibility --- moto/ec2/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/moto/ec2/models.py b/moto/ec2/models.py index 8c65553ad..63738695b 100644 --- a/moto/ec2/models.py +++ b/moto/ec2/models.py @@ -2331,11 +2331,11 @@ class NetworkAclBackend(object): network_acl_id): # lookup existing association for subnet and delete it - default_acl = next(value for key, value in self.network_acls.iteritems() + default_acl = next(value for key, value in self.network_acls.items() if association_id in value.associations.keys()) subnet_id = None - for key, value in default_acl.associations.iteritems(): + for key, value in default_acl.associations.items(): if key == association_id: subnet_id = default_acl.associations[key].subnet_id del default_acl.associations[key]