From 166fd69515c7da0f1e13d6fabb7787135ca822c5 Mon Sep 17 00:00:00 2001 From: Tyler Sanders Date: Tue, 2 Dec 2014 10:28:09 -0600 Subject: [PATCH] Virtual Private Gateway functionality --- moto/ec2/exceptions.py | 8 ++ moto/ec2/models.py | 61 +++++++++++- .../ec2/responses/virtual_private_gateways.py | 99 ++++++++++++++++++- moto/ec2/utils.py | 4 + .../test_ec2/test_virtual_private_gateways.py | 68 ++++++++++++- 5 files changed, 231 insertions(+), 9 deletions(-) diff --git a/moto/ec2/exceptions.py b/moto/ec2/exceptions.py index 76c90e706..599f0d00d 100644 --- a/moto/ec2/exceptions.py +++ b/moto/ec2/exceptions.py @@ -80,6 +80,14 @@ class InvalidNetworkAclIdError(EC2ClientError): .format(network_acl_id)) +class InvalidVpnGatewayIdError(EC2ClientError): + def __init__(self, network_acl_id): + super(InvalidVpnGatewayIdError, self).__init__( + "InvalidVpnGatewayID.NotFound", + "The virtual private gateway 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 f5b68da28..543960da0 100644 --- a/moto/ec2/models.py +++ b/moto/ec2/models.py @@ -53,7 +53,8 @@ from .exceptions import ( TagLimitExceeded, InvalidID, InvalidCIDRSubnetError, - InvalidNetworkAclIdError + InvalidNetworkAclIdError, + InvalidVpnGatewayIdError ) from .utils import ( EC2_RESOURCE_TO_PREFIX, @@ -89,7 +90,8 @@ from .utils import ( filter_internet_gateways, filter_reservations, random_network_acl_id, - random_network_acl_subnet_association_id) + random_network_acl_subnet_association_id, + random_vpn_gateway_id) def validate_resource_ids(resource_ids): @@ -2442,6 +2444,57 @@ class NetworkAclEntry(TaggedEC2Resource): self.port_range_to = port_range_to +class VpnGateway(TaggedEC2Resource): + def __init__(self, ec2_backend, id, type): + self.ec2_backend = ec2_backend + self.id = id + self.type = type + self.attachments = {} + super(VpnGateway, self).__init__() + + +class VpnGatewayAttachment(object): + def __init__(self, vpc_id, state): + self.vpc_id = vpc_id + self.state = state + super(VpnGatewayAttachment, self).__init__() + + +class VpnGatewayBackend(object): + def __init__(self): + self.vpn_gateways = {} + super(VpnGatewayBackend, self).__init__() + + def create_vpn_gateway(self, type='ipsec.1'): + vpn_gateway_id = random_vpn_gateway_id() + vpn_gateway = VpnGateway(self, vpn_gateway_id, type) + self.vpn_gateways[vpn_gateway_id] = vpn_gateway + return vpn_gateway + + def get_all_vpn_gateways(self, filters=None): + vpn_gateways = self.vpn_gateways.values() + return generic_filter(filters, vpn_gateways) + + def get_vpn_gateway(self, vpn_gateway_id): + vpn_gateway = self.vpn_gateways.get(vpn_gateway_id, None) + if not vpn_gateway: + raise InvalidVpnGatewayIdError(vpn_gateway_id) + return vpn_gateway + + def attach_vpn_gateway(self, vpn_gateway_id, vpc_id): + vpn_gateway = self.get_vpn_gateway(vpn_gateway_id) + self.get_vpc(vpc_id) + attachment = VpnGatewayAttachment(vpc_id, state='attached') + vpn_gateway.attachments[vpc_id] = attachment + return attachment + + def delete_vpn_gateway(self, vpn_gateway_id): + deleted = self.vpn_gateways.pop(vpn_gateway_id, None) + if not deleted: + raise InvalidVpnGatewayIdError(vpn_gateway_id) + return deleted + + class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend, RegionsAndZonesBackend, SecurityGroupBackend, EBSBackend, VPCBackend, SubnetBackend, SubnetRouteTableAssociationBackend, @@ -2450,7 +2503,7 @@ class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend, RouteTableBackend, RouteBackend, InternetGatewayBackend, VPCGatewayAttachmentBackend, SpotRequestBackend, ElasticAddressBackend, KeyPairBackend, DHCPOptionsSetBackend, - NetworkAclBackend): + NetworkAclBackend, VpnGatewayBackend): def __init__(self, region_name): super(EC2Backend, self).__init__() @@ -2509,7 +2562,7 @@ class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend, elif resource_prefix == EC2_RESOURCE_TO_PREFIX['vpn-connection']: self.raise_not_implemented_error('DescribeVpnConnections') elif resource_prefix == EC2_RESOURCE_TO_PREFIX['vpn-gateway']: - self.raise_not_implemented_error('DescribeVpnGateways') + self.get_vpn_gateway(vpn_gateway_id=resource_id) return True ec2_backends = {} diff --git a/moto/ec2/responses/virtual_private_gateways.py b/moto/ec2/responses/virtual_private_gateways.py index 18e1e99f1..94533943e 100644 --- a/moto/ec2/responses/virtual_private_gateways.py +++ b/moto/ec2/responses/virtual_private_gateways.py @@ -1,19 +1,110 @@ from __future__ import unicode_literals +from jinja2 import Template from moto.core.responses import BaseResponse +from moto.ec2.utils import filters_from_querystring class VirtualPrivateGateways(BaseResponse): def attach_vpn_gateway(self): - raise NotImplementedError('VirtualPrivateGateways(AmazonVPC).attach_vpn_gateway is not yet implemented') + vpn_gateway_id = self.querystring.get('VpnGatewayId')[0] + vpc_id = self.querystring.get('VpcId')[0] + attachment = self.ec2_backend.attach_vpn_gateway( + vpn_gateway_id, + vpc_id + ) + template = Template(ATTACH_VPN_GATEWAY_RESPONSE) + return template.render(attachment=attachment) def create_vpn_gateway(self): - raise NotImplementedError('VirtualPrivateGateways(AmazonVPC).create_vpn_gateway is not yet implemented') + type = self.querystring.get('Type', None)[0] + vpn_gateway = self.ec2_backend.create_vpn_gateway(type) + template = Template(CREATE_VPN_GATEWAY_RESPONSE) + return template.render(vpn_gateway=vpn_gateway) def delete_vpn_gateway(self): - raise NotImplementedError('VirtualPrivateGateways(AmazonVPC).delete_vpn_gateway is not yet implemented') + vpn_gateway_id = self.querystring.get('VpnGatewayId')[0] + vpn_gateway = self.ec2_backend.delete_vpn_gateway(vpn_gateway_id) + template = Template(DELETE_VPN_GATEWAY_RESPONSE) + return template.render(vpn_gateway=vpn_gateway) def describe_vpn_gateways(self): - raise NotImplementedError('VirtualPrivateGateways(AmazonVPC).describe_vpn_gateways is not yet implemented') + filters = filters_from_querystring(self.querystring) + vpn_gateways = self.ec2_backend.get_all_vpn_gateways(filters) + template = Template(DESCRIBE_VPN_GATEWAYS_RESPONSE) + return template.render(vpn_gateways=vpn_gateways) def detach_vpn_gateway(self): raise NotImplementedError('VirtualPrivateGateways(AmazonVPC).detach_vpn_gateway is not yet implemented') + + +CREATE_VPN_GATEWAY_RESPONSE = """ + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + + {{ vpn_gateway.id }} + available + {{ vpn_gateway.type }} + us-east-1a + + + {% for tag in vpn_gateway.get_tags() %} + + {{ tag.resource_id }} + {{ tag.resource_type }} + {{ tag.key }} + {{ tag.value }} + + {% endfor %} + + +""" + +DESCRIBE_VPN_GATEWAYS_RESPONSE = """ + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + + {% for vpn_gateway in vpn_gateways %} + + {{ vpn_gateway.id }} + available + {{ vpn_gateway.id }} + us-east-1a + + {% for attachment in vpn_gateway.attachments.values() %} + + {{ attachment.vpc_id }} + {{ attachment.state }} + + {% endfor %} + + + + {% for tag in vpn_gateway.get_tags() %} + + {{ tag.resource_id }} + {{ tag.resource_type }} + {{ tag.key }} + {{ tag.value }} + + {% endfor %} + + + {% endfor %} + +""" + +ATTACH_VPN_GATEWAY_RESPONSE = """ + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + + {{ attachment.vpc_id }} + {{ attachment.state }} + +""" + +DELETE_VPN_GATEWAY_RESPONSE = """ + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + true + +""" \ No newline at end of file diff --git a/moto/ec2/utils.py b/moto/ec2/utils.py index c2608b9c8..6ff04821c 100644 --- a/moto/ec2/utils.py +++ b/moto/ec2/utils.py @@ -81,6 +81,10 @@ def random_network_acl_subnet_association_id(): return random_id(prefix=EC2_RESOURCE_TO_PREFIX['network-acl-subnet-assoc']) +def random_vpn_gateway_id(): + return random_id(prefix=EC2_RESOURCE_TO_PREFIX['vpn-gateway']) + + def random_volume_id(): return random_id(prefix=EC2_RESOURCE_TO_PREFIX['volume']) diff --git a/tests/test_ec2/test_virtual_private_gateways.py b/tests/test_ec2/test_virtual_private_gateways.py index eea1e2222..40a03038e 100644 --- a/tests/test_ec2/test_virtual_private_gateways.py +++ b/tests/test_ec2/test_virtual_private_gateways.py @@ -7,4 +7,70 @@ from moto import mock_ec2 @mock_ec2 def test_virtual_private_gateways(): - pass + conn = boto.connect_vpc('the_key', 'the_secret') + + vpn_gateway = conn.create_vpn_gateway('ipsec.1', 'us-east-1a') + vpn_gateway.should_not.be.none + vpn_gateway.id.should.match(r'vgw-\w+') + vpn_gateway.type.should.equal('ipsec.1') + vpn_gateway.state.should.equal('available') + vpn_gateway.availability_zone.should.equal('us-east-1a') + +@mock_ec2 +def test_describe_vpn_gateway(): + conn = boto.connect_vpc('the_key', 'the_secret') + vpn_gateway = conn.create_vpn_gateway('ipsec.1', 'us-east-1a') + + vgws = conn.get_all_vpn_gateways() + vgws.should.have.length_of(1) + + gateway = vgws[0] + gateway.id.should.match(r'vgw-\w+') + gateway.id.should.equal(vpn_gateway.id) + vpn_gateway.type.should.equal('ipsec.1') + vpn_gateway.state.should.equal('available') + vpn_gateway.availability_zone.should.equal('us-east-1a') + + +@mock_ec2 +def test_vpn_gateway_vpc_attachment(): + conn = boto.connect_vpc('the_key', 'the_secret') + vpc = conn.create_vpc("10.0.0.0/16") + vpn_gateway = conn.create_vpn_gateway('ipsec.1', 'us-east-1a') + + conn.attach_vpn_gateway( + vpn_gateway_id=vpn_gateway.id, + vpc_id=vpc.id + ) + + gateway = conn.get_all_vpn_gateways()[0] + attachments = gateway.attachments + attachments.should.have.length_of(1) + attachments[0].vpc_id.should.equal(vpc.id) + attachments[0].state.should.equal('attached') + + +@mock_ec2 +def test_delete_vpn_gateway(): + conn = boto.connect_vpc('the_key', 'the_secret') + vpn_gateway = conn.create_vpn_gateway('ipsec.1', 'us-east-1a') + + conn.delete_vpn_gateway(vpn_gateway.id) + vgws = conn.get_all_vpn_gateways() + vgws.should.have.length_of(0) + + +@mock_ec2 +def test_vpn_gateway_tagging(): + conn = boto.connect_vpc('the_key', 'the_secret') + vpn_gateway = conn.create_vpn_gateway('ipsec.1', 'us-east-1a') + vpn_gateway.add_tag("a key", "some value") + + tag = conn.get_all_tags()[0] + tag.name.should.equal("a key") + tag.value.should.equal("some value") + + # Refresh the subnet + vpn_gateway = conn.get_all_vpn_gateways()[0] + vpn_gateway.tags.should.have.length_of(1) + vpn_gateway.tags["a key"].should.equal("some value")