From 0f18dd597cf419e15e8afb5bf5f1fd95ef9f634b Mon Sep 17 00:00:00 2001 From: Tyler Sanders Date: Wed, 12 Nov 2014 15:33:31 -0600 Subject: [PATCH] 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') + +