Implement customer gateway
Change-Id: I0e090f1d8dca70235615242f8993081c71ae6615
This commit is contained in:
parent
76b5766d0b
commit
54690d1095
|
@ -27,6 +27,7 @@ from oslo_log import log as logging
|
|||
|
||||
from ec2api.api import address
|
||||
from ec2api.api import availability_zone
|
||||
from ec2api.api import customer_gateway
|
||||
from ec2api.api import dhcp_options
|
||||
from ec2api.api import image
|
||||
from ec2api.api import instance
|
||||
|
@ -1775,3 +1776,58 @@ class VpcCloudController(CloudController):
|
|||
Returns:
|
||||
true if the request succeeds.
|
||||
"""
|
||||
|
||||
@module_and_param_types(customer_gateway, 'ip', 'vpn_connection_type',
|
||||
'int')
|
||||
def create_customer_gateway(self, context, ip_address, type,
|
||||
bgp_asn=None):
|
||||
"""Provides information to EC2 API about VPN customer gateway device.
|
||||
|
||||
Args:
|
||||
context (RequestContext): The request context.
|
||||
ip_address (str): The Internet-routable IP address for the
|
||||
customer gateway's outside interface.
|
||||
type (str): The type of VPN connection that this customer gateway
|
||||
supports (ipsec.1).
|
||||
bgp_asn (int): For devices that support BGP,
|
||||
the customer gateway's BGP ASN (65000 otherwise).
|
||||
|
||||
Returns:
|
||||
Information about the customer gateway.
|
||||
|
||||
You cannot create more than one customer gateway with the same VPN
|
||||
type, IP address, and BGP ASN parameter values. If you run an
|
||||
identical request more than one time, subsequent requests return
|
||||
information about the existing customer gateway.
|
||||
"""
|
||||
|
||||
@module_and_param_types(customer_gateway, 'cgw_id')
|
||||
def delete_customer_gateway(self, context, customer_gateway_id):
|
||||
"""Deletes the specified customer gateway.
|
||||
|
||||
Args:
|
||||
context (RequestContext): The request context.
|
||||
customer_gateway_id (str): The ID of the customer gateway.
|
||||
|
||||
Returns:
|
||||
true if the request succeeds.
|
||||
|
||||
You must delete the VPN connection before you can delete the customer
|
||||
gateway.
|
||||
"""
|
||||
|
||||
@module_and_param_types(customer_gateway, 'cgw_ids',
|
||||
'filter')
|
||||
def describe_customer_gateways(self, context, customer_gateway_id=None,
|
||||
filter=None):
|
||||
"""Describes one or more of your VPN customer gateways.
|
||||
|
||||
Args:
|
||||
context (RequestContext): The request context.
|
||||
customer_gateway_id (list of str): One or more customer gateway
|
||||
IDs.
|
||||
filter (list of filter dict): One or more filters.
|
||||
|
||||
Returns:
|
||||
Information about one or more customer gateways.
|
||||
"""
|
||||
|
|
|
@ -247,6 +247,12 @@ class Validator(object):
|
|||
def dopt_ids(self, ids):
|
||||
self.multi(ids, self.dopt_id)
|
||||
|
||||
def cgw_id(self, id):
|
||||
self.ec2_id(id, ['cgw'])
|
||||
|
||||
def cgw_ids(self, ids):
|
||||
self.multi(ids, self.cgw_id)
|
||||
|
||||
def security_group_str(self, value):
|
||||
validator.validate_security_group_str(value, self.param_name,
|
||||
self.params.get('vpc_id'))
|
||||
|
@ -254,8 +260,12 @@ class Validator(object):
|
|||
def security_group_strs(self, values):
|
||||
self.multi(values, self.security_group_str)
|
||||
|
||||
def vpn_connection_type(self, value):
|
||||
validator.validate_vpn_connection_type(value)
|
||||
|
||||
VPC_KINDS = ['vpc', 'igw', 'subnet', 'eni', 'dopt', 'eipalloc', 'sg', 'rtb']
|
||||
|
||||
VPC_KINDS = ['vpc', 'igw', 'subnet', 'eni', 'dopt', 'eipalloc', 'sg', 'rtb',
|
||||
'cgw']
|
||||
|
||||
|
||||
class UniversalDescriber(object):
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
# Copyright 2014
|
||||
# The Cloudscaling Group, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from ec2api.api import common
|
||||
from ec2api.api import ec2utils
|
||||
from ec2api.db import api as db_api
|
||||
from ec2api import exception
|
||||
from ec2api.i18n import _
|
||||
|
||||
|
||||
Validator = common.Validator
|
||||
|
||||
|
||||
DEFAULT_BGP_ASN = 65000
|
||||
|
||||
|
||||
def create_customer_gateway(context, ip_address, type, bgp_asn=None):
|
||||
if bgp_asn and bgp_asn != DEFAULT_BGP_ASN:
|
||||
raise exception.Unsupported("BGP dynamic routing is unsupported")
|
||||
customer_gateway = next((cgw for cgw in db_api.get_items(context, 'cgw')
|
||||
if cgw['ip_address'] == ip_address), None)
|
||||
if not customer_gateway:
|
||||
customer_gateway = db_api.add_item(context, 'cgw',
|
||||
{'ip_address': ip_address})
|
||||
return {'customerGateway': _format_customer_gateway(customer_gateway)}
|
||||
|
||||
|
||||
def delete_customer_gateway(context, customer_gateway_id):
|
||||
customer_gateway = ec2utils.get_db_item(context, customer_gateway_id)
|
||||
vpn_connections = db_api.get_items(context, 'vpn')
|
||||
if any(vpn['customer_gateway_id'] == customer_gateway['id']
|
||||
for vpn in vpn_connections):
|
||||
raise exception.IncorrectState(
|
||||
reason=_('The customer gateway is in use.'))
|
||||
db_api.delete_item(context, customer_gateway['id'])
|
||||
return True
|
||||
|
||||
|
||||
def describe_customer_gateways(context, customer_gateway_id=None,
|
||||
filter=None):
|
||||
formatted_cgws = CustomerGatewayDescriber().describe(
|
||||
context, ids=customer_gateway_id, filter=filter)
|
||||
return {'customerGatewaySet': formatted_cgws}
|
||||
|
||||
|
||||
class CustomerGatewayDescriber(common.TaggableItemsDescriber,
|
||||
common.NonOpenstackItemsDescriber):
|
||||
|
||||
KIND = 'cgw'
|
||||
FILTER_MAP = {'bgp-asn': 'bgpAsn',
|
||||
'customer-gateway-id': 'customerGatewayId',
|
||||
'ip-address': 'ipAddress',
|
||||
'state': 'state',
|
||||
'type': 'type'}
|
||||
|
||||
def format(self, customer_gateway):
|
||||
return _format_customer_gateway(customer_gateway)
|
||||
|
||||
|
||||
def _format_customer_gateway(customer_gateway):
|
||||
return {'customerGatewayId': customer_gateway['id'],
|
||||
'ipAddress': customer_gateway['ip_address'],
|
||||
'state': 'available',
|
||||
'type': 'ipsec.1',
|
||||
'bgpAsn': DEFAULT_BGP_ASN}
|
|
@ -189,6 +189,7 @@ NOT_FOUND_EXCEPTION_MAP = {
|
|||
'ami': exception.InvalidAMIIDNotFound,
|
||||
'aki': exception.InvalidAMIIDNotFound,
|
||||
'ari': exception.InvalidAMIIDNotFound,
|
||||
'cgw': exception.InvalidCustomerGatewayIDNotFound,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -224,3 +224,11 @@ def validate_security_group_str(value, parameter_name, vpc_id=None):
|
|||
if msg:
|
||||
raise exception.ValidationError(reason=msg)
|
||||
return True
|
||||
|
||||
|
||||
def validate_vpn_connection_type(value):
|
||||
if value != 'ipsec.1':
|
||||
raise exception.InvalidParameterValue(
|
||||
value=type, parameter='type',
|
||||
reason=_('Invalid VPN connection type.'))
|
||||
return True
|
||||
|
|
|
@ -400,6 +400,11 @@ class InvalidAvailabilityZoneNotFound(EC2NotFoundException):
|
|||
msg_fmt = _("Availability zone %(id)s not found")
|
||||
|
||||
|
||||
class InvalidCustomerGatewayIDNotFound(EC2NotFoundException):
|
||||
ec2_code = 'InvalidCustomerGatewayID.NotFound'
|
||||
msg_fmt = _("The customerGateway ID '%(id)s' does not exist")
|
||||
|
||||
|
||||
class ResourceLimitExceeded(EC2OverlimitException):
|
||||
msg_fmt = _('You have reached the limit of %(resource)s')
|
||||
|
||||
|
|
|
@ -234,6 +234,14 @@ FINGERPRINT_KEY_PAIR = (
|
|||
'2a:72:dd:aa:0d:a6:45:4d:27:4f:75:28:73:0d:a6:10:35:88:e1:ce')
|
||||
|
||||
|
||||
# customer gateway constants
|
||||
ID_EC2_CUSTOMER_GATEWAY_1 = random_ec2_id('cgw')
|
||||
ID_EC2_CUSTOMER_GATEWAY_2 = random_ec2_id('cgw')
|
||||
|
||||
IP_CUSTOMER_GATEWAY_ADDRESS_1 = '172.16.1.11'
|
||||
IP_CUSTOMER_GATEWAY_ADDRESS_2 = '172.31.2.22'
|
||||
|
||||
|
||||
# Object constants section
|
||||
# Constant name notation:
|
||||
# [<subtype>]<object_name>
|
||||
|
@ -1489,6 +1497,36 @@ EC2_KEY_PAIR = {'keyName': NAME_KEY_PAIR,
|
|||
'keyMaterial': PRIVATE_KEY_KEY_PAIR}
|
||||
|
||||
|
||||
# customer gateway objects
|
||||
DB_CUSTOMER_GATEWAY_1 = {
|
||||
'id': ID_EC2_CUSTOMER_GATEWAY_1,
|
||||
'ip_address': IP_CUSTOMER_GATEWAY_ADDRESS_1,
|
||||
'os_id': None,
|
||||
'vpc_id': None,
|
||||
}
|
||||
DB_CUSTOMER_GATEWAY_2 = {
|
||||
'id': ID_EC2_CUSTOMER_GATEWAY_2,
|
||||
'ip_address': IP_CUSTOMER_GATEWAY_ADDRESS_2,
|
||||
'os_id': None,
|
||||
'vpc_id': None,
|
||||
}
|
||||
|
||||
EC2_CUSTOMER_GATEWAY_1 = {
|
||||
'customerGatewayId': ID_EC2_CUSTOMER_GATEWAY_1,
|
||||
'ipAddress': IP_CUSTOMER_GATEWAY_ADDRESS_1,
|
||||
'state': 'available',
|
||||
'type': 'ipsec.1',
|
||||
'bgpAsn': 65000,
|
||||
}
|
||||
EC2_CUSTOMER_GATEWAY_2 = {
|
||||
'customerGatewayId': ID_EC2_CUSTOMER_GATEWAY_2,
|
||||
'ipAddress': IP_CUSTOMER_GATEWAY_ADDRESS_2,
|
||||
'state': 'available',
|
||||
'type': 'ipsec.1',
|
||||
'bgpAsn': 65000,
|
||||
}
|
||||
|
||||
|
||||
# Object generator functions section
|
||||
|
||||
# internet gateway generator functions
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
# Copyright 2014
|
||||
# The Cloudscaling Group, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
import mock
|
||||
|
||||
from ec2api.tests.unit import base
|
||||
from ec2api.tests.unit import fakes
|
||||
from ec2api.tests.unit import matchers
|
||||
from ec2api.tests.unit import tools
|
||||
|
||||
|
||||
class CustomerGatewayTestCase(base.ApiTestCase):
|
||||
|
||||
def test_create_customer_gateway(self):
|
||||
self.db_api.add_item.side_effect = (
|
||||
tools.get_db_api_add_item(fakes.ID_EC2_CUSTOMER_GATEWAY_2))
|
||||
|
||||
resp = self.execute('CreateCustomerGateway',
|
||||
{'IpAddress': fakes.IP_CUSTOMER_GATEWAY_ADDRESS_2,
|
||||
'Type': 'ipsec.1'})
|
||||
self.assertEqual({'customerGateway': fakes.EC2_CUSTOMER_GATEWAY_2},
|
||||
resp)
|
||||
self.db_api.add_item.assert_called_once_with(
|
||||
mock.ANY, 'cgw',
|
||||
{'ip_address': fakes.IP_CUSTOMER_GATEWAY_ADDRESS_2},
|
||||
project_id=None)
|
||||
|
||||
resp = self.execute('CreateCustomerGateway',
|
||||
{'IpAddress': fakes.IP_CUSTOMER_GATEWAY_ADDRESS_2,
|
||||
'Type': 'ipsec.1',
|
||||
'BgpAsn': '65000'})
|
||||
self.assertEqual({'customerGateway': fakes.EC2_CUSTOMER_GATEWAY_2},
|
||||
resp)
|
||||
|
||||
def test_create_customer_gateway_idempotent(self):
|
||||
self.set_mock_db_items(fakes.DB_CUSTOMER_GATEWAY_1)
|
||||
|
||||
resp = self.execute('CreateCustomerGateway',
|
||||
{'IpAddress': fakes.IP_CUSTOMER_GATEWAY_ADDRESS_1,
|
||||
'Type': 'ipsec.1'})
|
||||
self.assertEqual({'customerGateway': fakes.EC2_CUSTOMER_GATEWAY_1},
|
||||
resp)
|
||||
self.assertFalse(self.db_api.add_item.called)
|
||||
|
||||
resp = self.execute('CreateCustomerGateway',
|
||||
{'IpAddress': fakes.IP_CUSTOMER_GATEWAY_ADDRESS_1,
|
||||
'Type': 'ipsec.1',
|
||||
'BgpAsn': '65000'})
|
||||
self.assertEqual({'customerGateway': fakes.EC2_CUSTOMER_GATEWAY_1},
|
||||
resp)
|
||||
self.assertFalse(self.db_api.add_item.called)
|
||||
|
||||
def test_create_customer_gateway_invalid_parameters(self):
|
||||
self.assert_execution_error(
|
||||
'Unsupported',
|
||||
'CreateCustomerGateway',
|
||||
{'IpAddress': fakes.IP_CUSTOMER_GATEWAY_ADDRESS_1,
|
||||
'Type': 'ipsec.1',
|
||||
'BgpAsn': '456'})
|
||||
|
||||
def test_delete_customer_gateway(self):
|
||||
self.set_mock_db_items(fakes.DB_CUSTOMER_GATEWAY_2)
|
||||
|
||||
resp = self.execute(
|
||||
'DeleteCustomerGateway',
|
||||
{'CustomerGatewayId': fakes.ID_EC2_CUSTOMER_GATEWAY_2})
|
||||
|
||||
self.assertEqual({'return': True}, resp)
|
||||
self.db_api.delete_item.assert_called_once_with(
|
||||
mock.ANY, fakes.ID_EC2_CUSTOMER_GATEWAY_2)
|
||||
|
||||
def test_delete_customer_gateway_invalid_parameters(self):
|
||||
self.set_mock_db_items()
|
||||
self.assert_execution_error(
|
||||
'InvalidCustomerGatewayID.NotFound',
|
||||
'DeleteCustomerGateway',
|
||||
{'CustomerGatewayId': fakes.ID_EC2_CUSTOMER_GATEWAY_2})
|
||||
self.assertFalse(self.db_api.delete_item.called)
|
||||
|
||||
def test_describe_customer_gateways(self):
|
||||
self.set_mock_db_items(fakes.DB_CUSTOMER_GATEWAY_1,
|
||||
fakes.DB_CUSTOMER_GATEWAY_2)
|
||||
|
||||
resp = self.execute('DescribeCustomerGateways', {})
|
||||
self.assertThat(resp['customerGatewaySet'],
|
||||
matchers.ListMatches([fakes.EC2_CUSTOMER_GATEWAY_1,
|
||||
fakes.EC2_CUSTOMER_GATEWAY_2]))
|
||||
|
||||
resp = self.execute(
|
||||
'DescribeCustomerGateways',
|
||||
{'CustomerGatewayId.1': fakes.ID_EC2_CUSTOMER_GATEWAY_2})
|
||||
self.assertThat(
|
||||
resp['customerGatewaySet'],
|
||||
matchers.ListMatches([fakes.EC2_CUSTOMER_GATEWAY_2]))
|
||||
self.db_api.get_items_by_ids.assert_called_once_with(
|
||||
mock.ANY, set([fakes.ID_EC2_CUSTOMER_GATEWAY_2]))
|
||||
|
||||
self.check_filtering(
|
||||
'DescribeCustomerGateways', 'customerGatewaySet',
|
||||
[('bgp-asn', 65000),
|
||||
('customer-gateway-id', fakes.ID_EC2_CUSTOMER_GATEWAY_2),
|
||||
('ip-address', fakes.IP_CUSTOMER_GATEWAY_ADDRESS_2),
|
||||
('state', 'available'),
|
||||
('type', 'ipsec.1')])
|
||||
self.check_tag_support(
|
||||
'DescribeCustomerGateways', 'customerGatewaySet',
|
||||
fakes.ID_EC2_CUSTOMER_GATEWAY_2, 'customerGatewayId')
|
|
@ -76,6 +76,7 @@ class EC2ValidationTestCase(testtools.TestCase):
|
|||
validator.eipalloc_id('eipalloc-00000001')
|
||||
validator.eipassoc_id('eipassoc-00000001')
|
||||
validator.rtbassoc_id('rtbassoc-00000001')
|
||||
validator.cgw_id('cgw-00000001')
|
||||
|
||||
invalid_ids = ['1234', 'a-1111', '', 'i-1111', 'i-rrr', 'foobar']
|
||||
|
||||
|
@ -97,6 +98,7 @@ class EC2ValidationTestCase(testtools.TestCase):
|
|||
check_raise_invalid_parameters(validator.eipalloc_id)
|
||||
check_raise_invalid_parameters(validator.eipassoc_id)
|
||||
check_raise_invalid_parameters(validator.rtbassoc_id)
|
||||
check_raise_invalid_parameters(validator.cgw_id)
|
||||
|
||||
invalid_ids = ['1234', 'a-1111', '', 'vpc-1111', 'vpc-rrr', 'foobar']
|
||||
|
||||
|
@ -158,6 +160,16 @@ class EC2ValidationTestCase(testtools.TestCase):
|
|||
check_raise_validation_error('aa #^% -=99')
|
||||
check_raise_validation_error('x' * 256)
|
||||
|
||||
def test_validate_vpn_connection_type(self):
|
||||
validator = common.Validator()
|
||||
validator.vpn_connection_type('ipsec.1')
|
||||
|
||||
invalid_ids = ['1234', 'a-1111', '', 'vpc-1111', 'vpc-rrr', 'foobar',
|
||||
'ipsec1', 'openvpn', 'pptp', 'l2tp', 'freelan']
|
||||
for id in invalid_ids:
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
validator.vpn_connection_type, id)
|
||||
|
||||
|
||||
class EC2TimestampValidationTestCase(testtools.TestCase):
|
||||
"""Test case for EC2 request timestamp validation."""
|
||||
|
|
|
@ -67,6 +67,7 @@ class EC2UtilsTestCase(testtools.TestCase):
|
|||
check_not_found('ami', exception.InvalidAMIIDNotFound)
|
||||
check_not_found('ari', exception.InvalidAMIIDNotFound)
|
||||
check_not_found('aki', exception.InvalidAMIIDNotFound)
|
||||
check_not_found('cgw', exception.InvalidCustomerGatewayIDNotFound)
|
||||
|
||||
@mock.patch('ec2api.db.api.IMPL')
|
||||
def test_get_db_items(self, db_api):
|
||||
|
@ -121,6 +122,7 @@ class EC2UtilsTestCase(testtools.TestCase):
|
|||
check_not_found('ami', exception.InvalidAMIIDNotFound)
|
||||
check_not_found('aki', exception.InvalidAMIIDNotFound)
|
||||
check_not_found('ari', exception.InvalidAMIIDNotFound)
|
||||
check_not_found('cgw', exception.InvalidCustomerGatewayIDNotFound)
|
||||
|
||||
"""Unit test api xml conversion."""
|
||||
def test_number_conversion(self):
|
||||
|
|
Loading…
Reference in New Issue