Merge pull request #468 from EarthmanT/add_support_for_vpn_connections

Adding Support for VPN Connections
This commit is contained in:
Steve Pulec 2015-12-21 15:18:05 -05:00
commit 39fef4d367
5 changed files with 430 additions and 8 deletions

View File

@ -84,6 +84,14 @@ class InvalidVpnGatewayIdError(EC2ClientError):
.format(network_acl_id)) .format(network_acl_id))
class InvalidVpnConnectionIdError(EC2ClientError):
def __init__(self, network_acl_id):
super(InvalidVpnConnectionIdError, self).__init__(
"InvalidVpnConnectionID.NotFound",
"The vpnConnection ID '{0}' does not exist"
.format(network_acl_id))
class InvalidNetworkInterfaceIdError(EC2ClientError): class InvalidNetworkInterfaceIdError(EC2ClientError):
def __init__(self, eni_id): def __init__(self, eni_id):
super(InvalidNetworkInterfaceIdError, self).__init__( super(InvalidNetworkInterfaceIdError, self).__init__(

View File

@ -54,7 +54,8 @@ from .exceptions import (
InvalidID, InvalidID,
InvalidCIDRSubnetError, InvalidCIDRSubnetError,
InvalidNetworkAclIdError, InvalidNetworkAclIdError,
InvalidVpnGatewayIdError InvalidVpnGatewayIdError,
InvalidVpnConnectionIdError
) )
from .utils import ( from .utils import (
EC2_RESOURCE_TO_PREFIX, EC2_RESOURCE_TO_PREFIX,
@ -93,6 +94,7 @@ from .utils import (
random_network_acl_id, random_network_acl_id,
random_network_acl_subnet_association_id, random_network_acl_subnet_association_id,
random_vpn_gateway_id, random_vpn_gateway_id,
random_vpn_connection_id,
is_tag_filter, is_tag_filter,
) )
@ -2526,6 +2528,70 @@ class DHCPOptionsSetBackend(object):
return True return True
class VPNConnection(TaggedEC2Resource):
def __init__(self, ec2_backend, id, type,
customer_gateway_id, vpn_gateway_id):
self.ec2_backend = ec2_backend
self.id = id
self.state = 'available'
self.customer_gateway_configuration = {}
self.type = type
self.customer_gateway_id = customer_gateway_id
self.vpn_gateway_id = vpn_gateway_id
self.tunnels = None
self.options = None
self.static_routes = None
class VPNConnectionBackend(object):
def __init__(self):
self.vpn_connections = {}
super(VPNConnectionBackend, self).__init__()
def create_vpn_connection(self, type, customer_gateway_id,
vpn_gateway_id,
static_routes_only=None):
vpn_connection_id = random_vpn_connection_id()
if static_routes_only:
pass
vpn_connection = VPNConnection(
self, id=vpn_connection_id, type=type,
customer_gateway_id=customer_gateway_id,
vpn_gateway_id=vpn_gateway_id
)
self.vpn_connections[vpn_connection.id] = vpn_connection
return vpn_connection
def delete_vpn_connection(self, vpn_connection_id):
if vpn_connection_id in self.vpn_connections:
self.vpn_connections.pop(vpn_connection_id)
else:
raise InvalidVpnConnectionIdError(vpn_connection_id)
return True
def describe_vpn_connections(self, vpn_connection_ids=None):
vpn_connections = []
for vpn_connection_id in vpn_connection_ids or []:
if vpn_connection_id in self.vpn_connections:
vpn_connections.append(self.vpn_connections[vpn_connection_id])
else:
raise InvalidVpnConnectionIdError(vpn_connection_id)
return vpn_connections or self.vpn_connections.values()
def get_all_vpn_connections(self, vpn_connection_ids=None, filters=None):
vpn_connections = self.vpn_connections.values()
if vpn_connection_ids:
vpn_connections = [vpn_connection for vpn_connection in vpn_connections
if vpn_connection.id in vpn_connection_ids]
if len(vpn_connections) != len(vpn_connection_ids):
invalid_id = list(set(vpn_connection_ids).difference(set([vpn_connection.id for vpn_connection in vpn_connections])))[0]
raise InvalidVpnConnectionIdError(invalid_id)
return generic_filter(filters, vpn_connections)
class NetworkAclBackend(object): class NetworkAclBackend(object):
def __init__(self): def __init__(self):
self.network_acls = {} self.network_acls = {}
@ -2724,7 +2790,7 @@ class VpnGatewayBackend(object):
class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend, class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend,
RegionsAndZonesBackend, SecurityGroupBackend, EBSBackend, RegionsAndZonesBackend, SecurityGroupBackend, EBSBackend,
VPCBackend, SubnetBackend, SubnetRouteTableAssociationBackend, VPCBackend, SubnetBackend, SubnetRouteTableAssociationBackend,
NetworkInterfaceBackend, NetworkInterfaceBackend, VPNConnectionBackend,
VPCPeeringConnectionBackend, VPCPeeringConnectionBackend,
RouteTableBackend, RouteBackend, InternetGatewayBackend, RouteTableBackend, RouteBackend, InternetGatewayBackend,
VPCGatewayAttachmentBackend, SpotRequestBackend, VPCGatewayAttachmentBackend, SpotRequestBackend,
@ -2786,7 +2852,7 @@ class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend,
elif resource_prefix == EC2_RESOURCE_TO_PREFIX['vpc-peering-connection']: elif resource_prefix == EC2_RESOURCE_TO_PREFIX['vpc-peering-connection']:
self.get_vpc_peering_connection(vpc_pcx_id=resource_id) self.get_vpc_peering_connection(vpc_pcx_id=resource_id)
elif resource_prefix == EC2_RESOURCE_TO_PREFIX['vpn-connection']: elif resource_prefix == EC2_RESOURCE_TO_PREFIX['vpn-connection']:
self.raise_not_implemented_error('DescribeVpnConnections') self.describe_vpn_connections(vpn_connection_ids=[resource_id])
elif resource_prefix == EC2_RESOURCE_TO_PREFIX['vpn-gateway']: elif resource_prefix == EC2_RESOURCE_TO_PREFIX['vpn-gateway']:
self.get_vpn_gateway(vpn_gateway_id=resource_id) self.get_vpn_gateway(vpn_gateway_id=resource_id)
return True return True

View File

@ -1,13 +1,321 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from moto.core.responses import BaseResponse from moto.core.responses import BaseResponse
from moto.ec2.utils import filters_from_querystring, sequence_from_querystring
class VPNConnections(BaseResponse): class VPNConnections(BaseResponse):
def create_vpn_connection(self): def create_vpn_connection(self):
raise NotImplementedError('VPNConnections(AmazonVPC).create_vpn_connection is not yet implemented') type = self.querystring.get("Type", [None])[0]
cgw_id = self.querystring.get("CustomerGatewayId", [None])[0]
vgw_id = self.querystring.get("VPNGatewayId", [None])[0]
static_routes = self.querystring.get("StaticRoutesOnly", [None])[0]
vpn_connection = self.ec2_backend.create_vpn_connection(type, cgw_id, vgw_id, static_routes_only=static_routes)
template = self.response_template(CREATE_VPN_CONNECTION_RESPONSE)
return template.render(vpn_connection=vpn_connection)
def delete_vpn_connection(self): def delete_vpn_connection(self):
raise NotImplementedError('VPNConnections(AmazonVPC).delete_vpn_connection is not yet implemented') vpn_connection_id = self.querystring.get('VpnConnectionId')[0]
vpn_connection = self.ec2_backend.delete_vpn_connection(vpn_connection_id)
template = self.response_template(DELETE_VPN_CONNECTION_RESPONSE)
return template.render(vpn_connection=vpn_connection)
def describe_vpn_connections(self): def describe_vpn_connections(self):
raise NotImplementedError('VPNConnections(AmazonVPC).describe_vpn_connections is not yet implemented') vpn_connection_ids = sequence_from_querystring('VpnConnectionId', self.querystring)
filters = filters_from_querystring(self.querystring)
vpn_connections = self.ec2_backend.get_all_vpn_connections(
vpn_connection_ids=vpn_connection_ids, filters=filters)
template = self.response_template(DESCRIBE_VPN_CONNECTION_RESPONSE)
return template.render(vpn_connections=vpn_connections)
CREATE_VPN_CONNECTION_RESPONSE = """
<CreateVpnConnectionResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
<requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
<vpnConnection>
<vpnConnectionId>{{ vpn_connection.id }}</vpnConnectionId>
<state>pending</state>
<customerGatewayConfiguration>
<vpn_connection id="{{ vpn_connection.id }}">
<customer_gateway_id>{{ vpn_connection.customer_gateway_id }}</customer_gateway_id>
<vpn_gateway_id>{{ vpn_connection.vpn_gateway_id }}</vpn_gateway_id>
<vpn_connection_type>ipsec.1</vpn_connection_type>
<ipsec_tunnel>
<customer_gateway>
<tunnel_outside_address>
<ip_address>12.1.2.3</ip_address>
</tunnel_outside_address>
<tunnel_inside_address>
<ip_address>169.254.44.42</ip_address>
<network_mask>255.255.255.252</network_mask>
<network_cidr>30</network_cidr>
</tunnel_inside_address>
<bgp>
<asn>65000</asn>
<hold_time>30</hold_time>
</bgp>
</customer_gateway>
<vpn_gateway>
<tunnel_outside_address>
<ip_address>52.2.144.13</ip_address>
</tunnel_outside_address>
<tunnel_inside_address>
<ip_address>169.254.44.41</ip_address>
<network_mask>255.255.255.252</network_mask>
<network_cidr>30</network_cidr>
</tunnel_inside_address>
<bgp>
<asn>7224</asn>
<hold_time>30</hold_time>
</bgp>
</vpn_gateway>
<ike>
<authentication_protocol>sha1</authentication_protocol>
<encryption_protocol>aes-128-cbc</encryption_protocol>
<lifetime>28800</lifetime>
<perfect_forward_secrecy>group2</perfect_forward_secrecy>
<mode>main</mode>
<pre_shared_key>Iw2IAN9XUsQeYUrkMGP3kP59ugFDkfHg</pre_shared_key>
</ike>
<ipsec>
<protocol>esp</protocol>
<authentication_protocol>hmac-sha1-96</authentication_protocol>
<encryption_protocol>aes-128-cbc</encryption_protocol>
<lifetime>3600</lifetime>
<perfect_forward_secrecy>group2</perfect_forward_secrecy>
<mode>tunnel</mode>
<clear_df_bit>true</clear_df_bit>
<fragmentation_before_encryption>true</fragmentation_before_encryption>
<tcp_mss_adjustment>1387</tcp_mss_adjustment>
<dead_peer_detection>
<interval>10</interval>
<retries>3</retries>
</dead_peer_detection>
</ipsec>
</ipsec_tunnel>
<ipsec_tunnel>
<customer_gateway>
<tunnel_outside_address>
<ip_address>12.1.2.3</ip_address>
</tunnel_outside_address>
<tunnel_inside_address>
<ip_address>169.254.44.42</ip_address>
<network_mask>255.255.255.252</network_mask>
<network_cidr>30</network_cidr>
</tunnel_inside_address>
<bgp>
<asn>65000</asn>
<hold_time>30</hold_time>
</bgp>
</customer_gateway>
<vpn_gateway>
<tunnel_outside_address>
<ip_address>52.2.144.13</ip_address>
</tunnel_outside_address>
<tunnel_inside_address>
<ip_address>169.254.44.41</ip_address>
<network_mask>255.255.255.252</network_mask>
<network_cidr>30</network_cidr>
</tunnel_inside_address>
<bgp>
<asn>7224</asn>
<hold_time>30</hold_time>
</bgp>
</vpn_gateway>
<ike>
<authentication_protocol>sha1</authentication_protocol>
<encryption_protocol>aes-128-cbc</encryption_protocol>
<lifetime>28800</lifetime>
<perfect_forward_secrecy>group2</perfect_forward_secrecy>
<mode>main</mode>
<pre_shared_key>Iw2IAN9XUsQeYUrkMGP3kP59ugFDkfHg</pre_shared_key>
</ike>
<ipsec>
<protocol>esp</protocol>
<authentication_protocol>hmac-sha1-96</authentication_protocol>
<encryption_protocol>aes-128-cbc</encryption_protocol>
<lifetime>3600</lifetime>
<perfect_forward_secrecy>group2</perfect_forward_secrecy>
<mode>tunnel</mode>
<clear_df_bit>true</clear_df_bit>
<fragmentation_before_encryption>true</fragmentation_before_encryption>
<tcp_mss_adjustment>1387</tcp_mss_adjustment>
<dead_peer_detection>
<interval>10</interval>
<retries>3</retries>
</dead_peer_detection>
</ipsec>
</ipsec_tunnel>
</vpn_connection>
</customerGatewayConfiguration>
<type>ipsec.1</type>
<customerGatewayId>{{ vpn_connection.customer_gateway_id }}</customerGatewayId>
<vpnGatewayId>{{ vpn_connection.vpn_gateway_id }}</vpnGatewayId>
<tagSet>
{% for tag in vpn_connection.get_tags() %}
<item>
<resourceId>{{ tag.resource_id }}</resourceId>
<resourceType>{{ tag.resource_type }}</resourceType>
<key>{{ tag.key }}</key>
<value>{{ tag.value }}</value>
</item>
{% endfor %}
</tagSet>
</vpnConnection>
</CreateVpnConnectionResponse>"""
CREATE_VPN_CONNECTION_ROUTE_RESPONSE = """
<CreateVpnConnectionRouteResponse xmlns="http://ec2.amazonaws.com/doc/2013-10- 15/">
<requestId>4f35a1b2-c2c3-4093-b51f-abb9d7311990</requestId>
<return>true</return>
</CreateVpnConnectionRouteResponse>"""
DELETE_VPN_CONNECTION_RESPONSE = """
<DeleteVpnConnectionResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
<requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
<return>true</return>
</DeleteVpnConnectionResponse>"""
DELETE_VPN_CONNECTION_ROUTE_RESPONSE = """
<DeleteVpnConnectionRouteResponse xmlns="http://ec2.amazonaws.com/doc/2013-10- 15/">
<requestId>4f35a1b2-c2c3-4093-b51f-abb9d7311990</requestId>
<return>true</return>
</DeleteVpnConnectionRouteResponse>"""
DESCRIBE_VPN_CONNECTION_RESPONSE = """
<DescribeVpnConnectionsResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
<requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
<vpnConnectionSet>
{% for vpn_connection in vpn_connections %}
<item>
<vpnConnectionId>{{ vpn_connection.id }}</vpnConnectionId>
<state>available</state>
<customerGatewayConfiguration>
<vpn_connection id="{{ vpn_connection.id }}">
<customer_gateway_id>{{ vpn_connection.customer_gateway_id }}</customer_gateway_id>
<vpn_gateway_id>{{ vpn_connection.vpn_gateway_id }}</vpn_gateway_id>
<vpn_connection_type>ipsec.1</vpn_connection_type>
<ipsec_tunnel>
<customer_gateway>
<tunnel_outside_address>
<ip_address>12.1.2.3</ip_address>
</tunnel_outside_address>
<tunnel_inside_address>
<ip_address>169.254.44.42</ip_address>
<network_mask>255.255.255.252</network_mask>
<network_cidr>30</network_cidr>
</tunnel_inside_address>
<bgp>
<asn>65000</asn>
<hold_time>30</hold_time>
</bgp>
</customer_gateway>
<vpn_gateway>
<tunnel_outside_address>
<ip_address>52.2.144.13</ip_address>
</tunnel_outside_address>
<tunnel_inside_address>
<ip_address>169.254.44.41</ip_address>
<network_mask>255.255.255.252</network_mask>
<network_cidr>30</network_cidr>
</tunnel_inside_address>
<bgp>
<asn>7224</asn>
<hold_time>30</hold_time>
</bgp>
</vpn_gateway>
<ike>
<authentication_protocol>sha1</authentication_protocol>
<encryption_protocol>aes-128-cbc</encryption_protocol>
<lifetime>28800</lifetime>
<perfect_forward_secrecy>group2</perfect_forward_secrecy>
<mode>main</mode>
<pre_shared_key>Iw2IAN9XUsQeYUrkMGP3kP59ugFDkfHg</pre_shared_key>
</ike>
<ipsec>
<protocol>esp</protocol>
<authentication_protocol>hmac-sha1-96</authentication_protocol>
<encryption_protocol>aes-128-cbc</encryption_protocol>
<lifetime>3600</lifetime>
<perfect_forward_secrecy>group2</perfect_forward_secrecy>
<mode>tunnel</mode>
<clear_df_bit>true</clear_df_bit>
<fragmentation_before_encryption>true</fragmentation_before_encryption>
<tcp_mss_adjustment>1387</tcp_mss_adjustment>
<dead_peer_detection>
<interval>10</interval>
<retries>3</retries>
</dead_peer_detection>
</ipsec>
</ipsec_tunnel>
<ipsec_tunnel>
<customer_gateway>
<tunnel_outside_address>
<ip_address>12.1.2.3</ip_address>
</tunnel_outside_address>
<tunnel_inside_address>
<ip_address>169.254.44.42</ip_address>
<network_mask>255.255.255.252</network_mask>
<network_cidr>30</network_cidr>
</tunnel_inside_address>
<bgp>
<asn>65000</asn>
<hold_time>30</hold_time>
</bgp>
</customer_gateway>
<vpn_gateway>
<tunnel_outside_address>
<ip_address>52.2.144.13</ip_address>
</tunnel_outside_address>
<tunnel_inside_address>
<ip_address>169.254.44.41</ip_address>
<network_mask>255.255.255.252</network_mask>
<network_cidr>30</network_cidr>
</tunnel_inside_address>
<bgp>
<asn>7224</asn>
<hold_time>30</hold_time>
</bgp>
</vpn_gateway>
<ike>
<authentication_protocol>sha1</authentication_protocol>
<encryption_protocol>aes-128-cbc</encryption_protocol>
<lifetime>28800</lifetime>
<perfect_forward_secrecy>group2</perfect_forward_secrecy>
<mode>main</mode>
<pre_shared_key>Iw2IAN9XUsQeYUrkMGP3kP59ugFDkfHg</pre_shared_key>
</ike>
<ipsec>
<protocol>esp</protocol>
<authentication_protocol>hmac-sha1-96</authentication_protocol>
<encryption_protocol>aes-128-cbc</encryption_protocol>
<lifetime>3600</lifetime>
<perfect_forward_secrecy>group2</perfect_forward_secrecy>
<mode>tunnel</mode>
<clear_df_bit>true</clear_df_bit>
<fragmentation_before_encryption>true</fragmentation_before_encryption>
<tcp_mss_adjustment>1387</tcp_mss_adjustment>
<dead_peer_detection>
<interval>10</interval>
<retries>3</retries>
</dead_peer_detection>
</ipsec>
</ipsec_tunnel>
</vpn_connection>
</customerGatewayConfiguration>
<type>ipsec.1</type>
<customerGatewayId>{{ vpn_connection.customer_gateway_id }}</customerGatewayId>
<vpnGatewayId>{{ vpn_connection.vpn_gateway_id }}</vpnGatewayId>
<tagSet>
{% for tag in vpn_connection.get_tags() %}
<item>
<resourceId>{{ tag.resource_id }}</resourceId>
<resourceType>{{ tag.resource_type }}</resourceType>
<key>{{ tag.key }}</key>
<value>{{ tag.value }}</value>
</item>
{% endfor %}
</tagSet>
</item>
{% endfor %}
</vpnConnectionSet>
</DescribeVpnConnectionsResponse>"""

View File

@ -85,6 +85,10 @@ def random_vpn_gateway_id():
return random_id(prefix=EC2_RESOURCE_TO_PREFIX['vpn-gateway']) return random_id(prefix=EC2_RESOURCE_TO_PREFIX['vpn-gateway'])
def random_vpn_connection_id():
return random_id(prefix=EC2_RESOURCE_TO_PREFIX['vpn-connection'])
def random_volume_id(): def random_volume_id():
return random_id(prefix=EC2_RESOURCE_TO_PREFIX['volume']) return random_id(prefix=EC2_RESOURCE_TO_PREFIX['volume'])

View File

@ -1,10 +1,46 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import boto import boto
from nose.tools import assert_raises
import sure # noqa import sure # noqa
from boto.exception import EC2ResponseError
from moto import mock_ec2 from moto import mock_ec2
@mock_ec2 @mock_ec2
def test_vpn_connections(): def test_create_vpn_connections():
pass conn = boto.connect_vpc('the_key', 'the_secret')
vpn_connection = conn.create_vpn_connection('ipsec.1', 'vgw-0123abcd', 'cgw-0123abcd')
vpn_connection.should_not.be.none
vpn_connection.id.should.match(r'vpn-\w+')
vpn_connection.type.should.equal('ipsec.1')
@mock_ec2
def test_delete_vpn_connections():
conn = boto.connect_vpc('the_key', 'the_secret')
vpn_connection = conn.create_vpn_connection('ipsec.1', 'vgw-0123abcd', 'cgw-0123abcd')
list_of_vpn_connections = conn.get_all_vpn_connections()
list_of_vpn_connections.should.have.length_of(1)
conn.delete_vpn_connection(vpn_connection.id)
list_of_vpn_connections = conn.get_all_vpn_connections()
list_of_vpn_connections.should.have.length_of(0)
@mock_ec2
def test_delete_vpn_connections_bad_id():
conn = boto.connect_vpc('the_key', 'the_secret')
with assert_raises(EC2ResponseError):
conn.delete_vpn_connection('vpn-0123abcd')
@mock_ec2
def test_describe_vpn_connections():
conn = boto.connect_vpc('the_key', 'the_secret')
list_of_vpn_connections = conn.get_all_vpn_connections()
list_of_vpn_connections.should.have.length_of(0)
conn.create_vpn_connection('ipsec.1', 'vgw-0123abcd', 'cgw-0123abcd')
list_of_vpn_connections = conn.get_all_vpn_connections()
list_of_vpn_connections.should.have.length_of(1)
vpn = conn.create_vpn_connection('ipsec.1', 'vgw-1234abcd', 'cgw-1234abcd')
list_of_vpn_connections = conn.get_all_vpn_connections()
list_of_vpn_connections.should.have.length_of(2)
list_of_vpn_connections = conn.get_all_vpn_connections(vpn.id)
list_of_vpn_connections.should.have.length_of(1)