From 4439ffb535070bb43689fa15520f7212d658bc8e Mon Sep 17 00:00:00 2001 From: Swaminathan Vasudevan Date: Mon, 20 May 2013 07:00:21 -0700 Subject: [PATCH] VPN as a Service (VPNaaS) API and DataModel implements:blueprint VPNaaS-Python-API It provides the basic API and DataModel for the "VPN As A Service" feature that includes: * VPNService * IPsecSiteConnection * IKEPolicy * IPsecPolicy Change-Id: I059d4db05118a4eecd388b8e8db0d714a8f9cb8f --- neutron/db/vpn/__init__.py | 18 + neutron/db/vpn/vpn_db.py | 591 +++++++ neutron/services/vpn/__init__.py | 18 + neutron/services/vpn/plugin.py | 32 + neutron/tests/unit/db/vpn/__init__.py | 17 + neutron/tests/unit/db/vpn/test_db_vpnaas.py | 1541 +++++++++++++++++ neutron/tests/unit/services/vpn/__init__.py | 17 + .../services/vpn/test_vpnaas_extension.py | 592 +++++++ 8 files changed, 2826 insertions(+) create mode 100644 neutron/db/vpn/__init__.py create mode 100644 neutron/db/vpn/vpn_db.py create mode 100644 neutron/services/vpn/__init__.py create mode 100644 neutron/services/vpn/plugin.py create mode 100644 neutron/tests/unit/db/vpn/__init__.py create mode 100644 neutron/tests/unit/db/vpn/test_db_vpnaas.py create mode 100644 neutron/tests/unit/services/vpn/__init__.py create mode 100644 neutron/tests/unit/services/vpn/test_vpnaas_extension.py diff --git a/neutron/db/vpn/__init__.py b/neutron/db/vpn/__init__.py new file mode 100644 index 000000000..7f4f3b9f8 --- /dev/null +++ b/neutron/db/vpn/__init__.py @@ -0,0 +1,18 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. +# All Rights Reserved. +# +# 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. +# +# @author: Swaminathan Vasudevan, Hewlett-Packard. diff --git a/neutron/db/vpn/vpn_db.py b/neutron/db/vpn/vpn_db.py new file mode 100644 index 000000000..87298a4a9 --- /dev/null +++ b/neutron/db/vpn/vpn_db.py @@ -0,0 +1,591 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. +# All Rights Reserved. +# +# 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. +# +# @author: Swaminathan Vasudevan, Hewlett-Packard. + +import sqlalchemy as sa +from sqlalchemy import orm +from sqlalchemy.orm import exc + +from neutron.common import constants as q_constants +from neutron.db import agentschedulers_db as agent_db +from neutron.db import api as qdbapi +from neutron.db import db_base_plugin_v2 as base_db +from neutron.db import l3_db +from neutron.db import model_base +from neutron.db import models_v2 +from neutron.extensions import vpnaas +from neutron.extensions.vpnaas import VPNPluginBase +from neutron import manager +from neutron.openstack.common import log as logging +from neutron.openstack.common import uuidutils +from neutron.plugins.common import constants + +LOG = logging.getLogger(__name__) + + +class IPsecPeerCidr(model_base.BASEV2): + """Internal representation of a IPsec Peer Cidrs.""" + + cidr = sa.Column(sa.String(32), nullable=False, primary_key=True) + ipsec_site_connection_id = sa.Column( + sa.String(36), + sa.ForeignKey('ipsec_site_connections.id', + ondelete="CASCADE"), + primary_key=True) + + +class IPsecPolicy(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant): + """Represents a v2 IPsecPolicy Object.""" + __tablename__ = 'ipsecpolicies' + name = sa.Column(sa.String(255)) + description = sa.Column(sa.String(255)) + transform_protocol = sa.Column(sa.Enum("esp", "ah", "ah-esp", + name="ipsec_transform_protocols"), + nullable=False) + auth_algorithm = sa.Column(sa.Enum("sha1", + name="vpn_auth_algorithms"), + nullable=False) + encryption_algorithm = sa.Column(sa.Enum("3des", "aes-128", + "aes-256", "aes-192", + name="vpn_encrypt_algorithms"), + nullable=False) + encapsulation_mode = sa.Column(sa.Enum("tunnel", "transport", + name="ipsec_encapsulations"), + nullable=False) + lifetime_units = sa.Column(sa.Enum("seconds", "kilobytes", + name="vpn_lifetime_units"), + nullable=False) + lifetime_value = sa.Column(sa.Integer, nullable=False) + pfs = sa.Column(sa.Enum("group2", "group5", "group14", + name="vpn_pfs"), nullable=False) + + +class IKEPolicy(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant): + """Represents a v2 IKEPolicy Object.""" + __tablename__ = 'ikepolicies' + name = sa.Column(sa.String(255)) + description = sa.Column(sa.String(255)) + auth_algorithm = sa.Column(sa.Enum("sha1", + name="vpn_auth_algorithms"), + nullable=False) + encryption_algorithm = sa.Column(sa.Enum("3des", "aes-128", + "aes-256", "aes-192", + name="vpn_encrypt_algorithms"), + nullable=False) + phase1_negotiation_mode = sa.Column(sa.Enum("main", + name="ike_phase1_mode"), + nullable=False) + lifetime_units = sa.Column(sa.Enum("seconds", "kilobytes", + name="vpn_lifetime_units"), + nullable=False) + lifetime_value = sa.Column(sa.Integer, nullable=False) + ike_version = sa.Column(sa.Enum("v1", "v2", name="ike_versions"), + nullable=False) + pfs = sa.Column(sa.Enum("group2", "group5", "group14", + name="vpn_pfs"), nullable=False) + + +class IPsecSiteConnection(model_base.BASEV2, + models_v2.HasId, models_v2.HasTenant): + """Represents a IPsecSiteConnection Object.""" + __tablename__ = 'ipsec_site_connections' + name = sa.Column(sa.String(255)) + description = sa.Column(sa.String(255)) + peer_address = sa.Column(sa.String(64), nullable=False) + peer_id = sa.Column(sa.String(255), nullable=False) + route_mode = sa.Column(sa.String(8), nullable=False) + mtu = sa.Column(sa.Integer, nullable=False) + initiator = sa.Column(sa.Enum("bi-directional", "response-only", + name="vpn_initiators"), nullable=False) + auth_mode = sa.Column(sa.String(16), nullable=False) + psk = sa.Column(sa.String(255), nullable=False) + dpd_action = sa.Column(sa.Enum("hold", "clear", + "restart", "disabled", + "restart-by-peer", name="vpn_dpd_actions"), + nullable=False) + dpd_interval = sa.Column(sa.Integer, nullable=False) + dpd_timeout = sa.Column(sa.Integer, nullable=False) + status = sa.Column(sa.String(16), nullable=False) + admin_state_up = sa.Column(sa.Boolean(), nullable=False) + vpnservice_id = sa.Column(sa.String(36), + sa.ForeignKey('vpnservices.id'), + nullable=False) + ipsecpolicy_id = sa.Column(sa.String(36), + sa.ForeignKey('ipsecpolicies.id'), + nullable=False) + ikepolicy_id = sa.Column(sa.String(36), + sa.ForeignKey('ikepolicies.id'), + nullable=False) + ipsecpolicy = orm.relationship( + IPsecPolicy, backref='ipsec_site_connection') + ikepolicy = orm.relationship(IKEPolicy, backref='ipsec_site_connection') + peer_cidrs = orm.relationship(IPsecPeerCidr, + backref='ipsec_site_connection', + lazy='joined', + cascade='all, delete, delete-orphan') + + +class VPNService(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant): + """Represents a v2 VPNService Object.""" + name = sa.Column(sa.String(255)) + description = sa.Column(sa.String(255)) + status = sa.Column(sa.String(16), nullable=False) + admin_state_up = sa.Column(sa.Boolean(), nullable=False) + subnet_id = sa.Column(sa.String(36), sa.ForeignKey('subnets.id'), + nullable=False) + router_id = sa.Column(sa.String(36), sa.ForeignKey('routers.id'), + nullable=False) + subnet = orm.relationship(models_v2.Subnet) + router = orm.relationship(l3_db.Router) + ipsec_site_connections = orm.relationship( + IPsecSiteConnection, + backref='vpnservice', + cascade="all, delete-orphan") + + +class VPNPluginDb(VPNPluginBase, base_db.CommonDbMixin): + """VPN plugin database class using SQLAlchemy models.""" + + def __init__(self): + """Do the initialization for the vpn service plugin here.""" + qdbapi.register_models() + + def update_status(self, context, model, v_id, status): + with context.session.begin(subtransactions=True): + v_db = self._get_resource(context, model, v_id) + v_db.update({'status': status}) + + def _get_resource(self, context, model, v_id): + try: + r = self._get_by_id(context, model, v_id) + except exc.NoResultFound: + if issubclass(model, IPsecSiteConnection): + raise vpnaas.IPsecSiteConnectionNotFound( + ipsec_site_conn_id=v_id + ) + elif issubclass(model, IKEPolicy): + raise vpnaas.IKEPolicyNotFound(ikepolicy_id=v_id) + elif issubclass(model, IPsecPolicy): + raise vpnaas.IPsecPolicyNotFound(ipsecpolicy_id=v_id) + elif issubclass(model, VPNService): + raise vpnaas.VPNServiceNotFound(vpnservice_id=v_id) + else: + raise + return r + + def assert_update_allowed(self, obj): + status = getattr(obj, 'status', None) + if status != constants.ACTIVE: + raise vpnaas.VPNStateInvalid(id=id, state=status) + + def _make_ipsec_site_connection_dict(self, ipsec_site_conn, fields=None): + + res = {'id': ipsec_site_conn['id'], + 'tenant_id': ipsec_site_conn['tenant_id'], + 'name': ipsec_site_conn['name'], + 'description': ipsec_site_conn['description'], + 'peer_address': ipsec_site_conn['peer_address'], + 'peer_id': ipsec_site_conn['peer_id'], + 'route_mode': ipsec_site_conn['route_mode'], + 'mtu': ipsec_site_conn['mtu'], + 'auth_mode': ipsec_site_conn['auth_mode'], + 'psk': ipsec_site_conn['psk'], + 'initiator': ipsec_site_conn['initiator'], + 'dpd': { + 'action': ipsec_site_conn['dpd_action'], + 'interval': ipsec_site_conn['dpd_interval'], + 'timeout': ipsec_site_conn['dpd_timeout'] + }, + 'admin_state_up': ipsec_site_conn['admin_state_up'], + 'status': ipsec_site_conn['status'], + 'vpnservice_id': ipsec_site_conn['vpnservice_id'], + 'ikepolicy_id': ipsec_site_conn['ikepolicy_id'], + 'ipsecpolicy_id': ipsec_site_conn['ipsecpolicy_id'], + 'peer_cidrs': [pcidr['cidr'] + for pcidr in ipsec_site_conn['peer_cidrs']] + } + + return self._fields(res, fields) + + def create_ipsec_site_connection(self, context, ipsec_site_connection): + ipsec_sitecon = ipsec_site_connection['ipsec_site_connection'] + dpd = ipsec_sitecon['dpd'] + ipsec_sitecon['dpd_action'] = dpd.get('action', 'hold') + ipsec_sitecon['dpd_interval'] = dpd.get('interval', 30) + ipsec_sitecon['dpd_timeout'] = dpd.get('timeout', 120) + tenant_id = self._get_tenant_id_for_create(context, ipsec_sitecon) + if ipsec_sitecon['dpd_timeout'] < ipsec_sitecon['dpd_interval']: + raise vpnaas.IPsecSiteConnectionDpdIntervalValueError( + attribute_a='dpd_timeout') + with context.session.begin(subtransactions=True): + #Check permissions + self._get_resource(context, + VPNService, + ipsec_sitecon['vpnservice_id']) + self._get_resource(context, + IKEPolicy, + ipsec_sitecon['ikepolicy_id']) + self._get_resource(context, + IPsecPolicy, + ipsec_sitecon['ipsecpolicy_id']) + ipsec_site_conn_db = IPsecSiteConnection( + id=uuidutils.generate_uuid(), + tenant_id=tenant_id, + name=ipsec_sitecon['name'], + description=ipsec_sitecon['description'], + peer_address=ipsec_sitecon['peer_address'], + peer_id=ipsec_sitecon['peer_id'], + route_mode='static', + mtu=ipsec_sitecon['mtu'], + auth_mode='psk', + psk=ipsec_sitecon['psk'], + initiator=ipsec_sitecon['initiator'], + dpd_action=ipsec_sitecon['dpd_action'], + dpd_interval=ipsec_sitecon['dpd_interval'], + dpd_timeout=ipsec_sitecon['dpd_timeout'], + admin_state_up=ipsec_sitecon['admin_state_up'], + status=constants.PENDING_CREATE, + vpnservice_id=ipsec_sitecon['vpnservice_id'], + ikepolicy_id=ipsec_sitecon['ikepolicy_id'], + ipsecpolicy_id=ipsec_sitecon['ipsecpolicy_id'] + ) + context.session.add(ipsec_site_conn_db) + for cidr in ipsec_sitecon['peer_cidrs']: + peer_cidr_db = IPsecPeerCidr( + cidr=cidr, + ipsec_site_connection_id=ipsec_site_conn_db['id'] + ) + context.session.add(peer_cidr_db) + return self._make_ipsec_site_connection_dict(ipsec_site_conn_db) + + def update_ipsec_site_connection( + self, context, + ipsec_site_conn_id, ipsec_site_connection): + ipsec_sitecon = ipsec_site_connection['ipsec_site_connection'] + dpd = ipsec_sitecon.get('dpd', {}) + if dpd.get('action'): + ipsec_sitecon['dpd_action'] = dpd.get('action') + if dpd.get('interval'): + ipsec_sitecon['dpd_interval'] = dpd.get('interval') + if dpd.get('timeout'): + ipsec_sitecon['dpd_timeout'] = dpd.get('timeout') + changed_peer_cidrs = False + with context.session.begin(subtransactions=True): + ipsec_site_conn_db = self._get_resource( + context, + IPsecSiteConnection, + ipsec_site_conn_id) + self.assert_update_allowed(ipsec_site_conn_db) + if "peer_cidrs" in ipsec_sitecon: + changed_peer_cidrs = True + old_peer_cidr_list = ipsec_site_conn_db['peer_cidrs'] + old_peer_cidr_dict = dict( + (peer_cidr['cidr'], peer_cidr) + for peer_cidr in old_peer_cidr_list) + new_peer_cidr_set = set(ipsec_sitecon["peer_cidrs"]) + old_peer_cidr_set = set(old_peer_cidr_dict) + + new_peer_cidrs = list(new_peer_cidr_set) + for peer_cidr in old_peer_cidr_set - new_peer_cidr_set: + context.session.delete(old_peer_cidr_dict[peer_cidr]) + for peer_cidr in new_peer_cidr_set - old_peer_cidr_set: + pcidr = IPsecPeerCidr( + cidr=peer_cidr, + ipsec_site_connection_id=ipsec_site_conn_id) + context.session.add(pcidr) + del ipsec_sitecon["peer_cidrs"] + if ipsec_sitecon: + ipsec_site_conn_db.update(ipsec_sitecon) + result = self._make_ipsec_site_connection_dict(ipsec_site_conn_db) + if changed_peer_cidrs: + result['peer_cidrs'] = new_peer_cidrs + return result + + def delete_ipsec_site_connection(self, context, ipsec_site_conn_id): + with context.session.begin(subtransactions=True): + ipsec_site_conn_db = self._get_resource( + context, IPsecSiteConnection, ipsec_site_conn_id + ) + context.session.delete(ipsec_site_conn_db) + + def get_ipsec_site_connection(self, context, + ipsec_site_conn_id, fields=None): + ipsec_site_conn_db = self._get_resource( + context, IPsecSiteConnection, ipsec_site_conn_id + ) + return self._make_ipsec_site_connection_dict( + ipsec_site_conn_db, fields) + + def get_ipsec_site_connections(self, context, filters=None, fields=None): + return self._get_collection(context, IPsecSiteConnection, + self._make_ipsec_site_connection_dict, + filters=filters, fields=fields) + + def _make_ikepolicy_dict(self, ikepolicy, fields=None): + res = {'id': ikepolicy['id'], + 'tenant_id': ikepolicy['tenant_id'], + 'name': ikepolicy['name'], + 'description': ikepolicy['description'], + 'auth_algorithm': ikepolicy['auth_algorithm'], + 'encryption_algorithm': ikepolicy['encryption_algorithm'], + 'phase1_negotiation_mode': ikepolicy['phase1_negotiation_mode'], + 'lifetime': { + 'units': ikepolicy['lifetime_units'], + 'value': ikepolicy['lifetime_value'], + }, + 'ike_version': ikepolicy['ike_version'], + 'pfs': ikepolicy['pfs'] + } + + return self._fields(res, fields) + + def create_ikepolicy(self, context, ikepolicy): + ike = ikepolicy['ikepolicy'] + tenant_id = self._get_tenant_id_for_create(context, ike) + lifetime_info = ike.get('lifetime', []) + lifetime_units = lifetime_info.get('unit', 'seconds') + lifetime_value = lifetime_info.get('value', 3600) + + with context.session.begin(subtransactions=True): + ike_db = IKEPolicy( + id=uuidutils.generate_uuid(), + tenant_id=tenant_id, + name=ike['name'], + description=ike['description'], + auth_algorithm=ike['auth_algorithm'], + encryption_algorithm=ike['encryption_algorithm'], + phase1_negotiation_mode=ike['phase1_negotiation_mode'], + lifetime_units=lifetime_units, + lifetime_value=lifetime_value, + ike_version=ike['ike_version'], + pfs=ike['pfs'] + ) + + context.session.add(ike_db) + return self._make_ikepolicy_dict(ike_db) + + def update_ikepolicy(self, context, ikepolicy_id, ikepolicy): + ike = ikepolicy['ikepolicy'] + with context.session.begin(subtransactions=True): + ikepolicy = context.session.query(IPsecSiteConnection).filter_by( + ikepolicy_id=ikepolicy_id).first() + if ikepolicy: + raise vpnaas.IKEPolicyInUse(ikepolicy_id=ikepolicy_id) + ike_db = self._get_resource(context, IKEPolicy, ikepolicy_id) + if ike: + lifetime_info = ike.get('lifetime') + if lifetime_info: + if lifetime_info.get('units'): + ike['lifetime_units'] = lifetime_info['units'] + if lifetime_info.get('value'): + ike['lifetime_value'] = lifetime_info['value'] + ike_db.update(ike) + return self._make_ikepolicy_dict(ike_db) + + def delete_ikepolicy(self, context, ikepolicy_id): + with context.session.begin(subtransactions=True): + ikepolicy = context.session.query(IPsecSiteConnection).filter_by( + ikepolicy_id=ikepolicy_id).first() + if ikepolicy: + raise vpnaas.IKEPolicyInUse(ikepolicy_id=ikepolicy_id) + ike_db = self._get_resource(context, IKEPolicy, ikepolicy_id) + context.session.delete(ike_db) + + def get_ikepolicy(self, context, ikepolicy_id, fields=None): + ike_db = self._get_resource(context, IKEPolicy, ikepolicy_id) + return self._make_ikepolicy_dict(ike_db, fields) + + def get_ikepolicies(self, context, filters=None, fields=None): + return self._get_collection(context, IKEPolicy, + self._make_ikepolicy_dict, + filters=filters, fields=fields) + + def _make_ipsecpolicy_dict(self, ipsecpolicy, fields=None): + + res = {'id': ipsecpolicy['id'], + 'tenant_id': ipsecpolicy['tenant_id'], + 'name': ipsecpolicy['name'], + 'description': ipsecpolicy['description'], + 'transform_protocol': ipsecpolicy['transform_protocol'], + 'auth_algorithm': ipsecpolicy['auth_algorithm'], + 'encryption_algorithm': ipsecpolicy['encryption_algorithm'], + 'encapsulation_mode': ipsecpolicy['encapsulation_mode'], + 'lifetime': { + 'units': ipsecpolicy['lifetime_units'], + 'value': ipsecpolicy['lifetime_value'], + }, + 'pfs': ipsecpolicy['pfs'] + } + + return self._fields(res, fields) + + def create_ipsecpolicy(self, context, ipsecpolicy): + ipsecp = ipsecpolicy['ipsecpolicy'] + tenant_id = self._get_tenant_id_for_create(context, ipsecp) + lifetime_info = ipsecp['lifetime'] + lifetime_units = lifetime_info.get('units', 'seconds') + lifetime_value = lifetime_info.get('value', 3600) + + with context.session.begin(subtransactions=True): + ipsecp_db = IPsecPolicy(id=uuidutils.generate_uuid(), + tenant_id=tenant_id, + name=ipsecp['name'], + description=ipsecp['description'], + transform_protocol=ipsecp['transform_' + 'protocol'], + auth_algorithm=ipsecp['auth_algorithm'], + encryption_algorithm=ipsecp['encryption_' + 'algorithm'], + encapsulation_mode=ipsecp['encapsulation_' + 'mode'], + lifetime_units=lifetime_units, + lifetime_value=lifetime_value, + pfs=ipsecp['pfs']) + context.session.add(ipsecp_db) + return self._make_ipsecpolicy_dict(ipsecp_db) + + def update_ipsecpolicy(self, context, ipsecpolicy_id, ipsecpolicy): + ipsecp = ipsecpolicy['ipsecpolicy'] + with context.session.begin(subtransactions=True): + ipsecpolicy = context.session.query(IPsecSiteConnection).filter_by( + ipsecpolicy_id=ipsecpolicy_id).first() + if ipsecpolicy: + raise vpnaas.IPsecPolicyInUse(ipsecpolicy_id=ipsecpolicy_id) + ipsecp_db = self._get_resource(context, + IPsecPolicy, + ipsecpolicy_id) + if ipsecp: + lifetime_info = ipsecp.get('lifetime') + if lifetime_info: + if lifetime_info.get('units'): + ipsecp['lifetime_units'] = lifetime_info['units'] + if lifetime_info('value'): + ipsecp['lifetime_value'] = lifetime_info['value'] + ipsecp_db.update(ipsecp) + return self._make_ipsecpolicy_dict(ipsecp_db) + + def delete_ipsecpolicy(self, context, ipsecpolicy_id): + with context.session.begin(subtransactions=True): + ipsecpolicy = context.session.query(IPsecSiteConnection).filter_by( + ipsecpolicy_id=ipsecpolicy_id).first() + if ipsecpolicy: + raise vpnaas.IPsecPolicyInUse(ipsecpolicy_id=ipsecpolicy_id) + ipsec_db = self._get_resource(context, IPsecPolicy, ipsecpolicy_id) + context.session.delete(ipsec_db) + + def get_ipsecpolicy(self, context, ipsecpolicy_id, fields=None): + ipsec_db = self._get_resource(context, IPsecPolicy, ipsecpolicy_id) + return self._make_ipsecpolicy_dict(ipsec_db, fields) + + def get_ipsecpolicies(self, context, filters=None, fields=None): + return self._get_collection(context, IPsecPolicy, + self._make_ipsecpolicy_dict, + filters=filters, fields=fields) + + def _make_vpnservice_dict(self, vpnservice, fields=None): + res = {'id': vpnservice['id'], + 'name': vpnservice['name'], + 'description': vpnservice['description'], + 'tenant_id': vpnservice['tenant_id'], + 'subnet_id': vpnservice['subnet_id'], + 'router_id': vpnservice['router_id'], + 'admin_state_up': vpnservice['admin_state_up'], + 'status': vpnservice['status']} + return self._fields(res, fields) + + def create_vpnservice(self, context, vpnservice): + vpns = vpnservice['vpnservice'] + tenant_id = self._get_tenant_id_for_create(context, vpns) + with context.session.begin(subtransactions=True): + vpnservice_db = VPNService(id=uuidutils.generate_uuid(), + tenant_id=tenant_id, + name=vpns['name'], + description=vpns['description'], + subnet_id=vpns['subnet_id'], + router_id=vpns['router_id'], + admin_state_up=vpns['admin_state_up'], + status=constants.PENDING_CREATE) + context.session.add(vpnservice_db) + return self._make_vpnservice_dict(vpnservice_db) + + def update_vpnservice(self, context, vpnservice_id, vpnservice): + vpns = vpnservice['vpnservice'] + with context.session.begin(subtransactions=True): + vpnservice = context.session.query(IPsecSiteConnection).filter_by( + vpnservice_id=vpnservice_id).first() + if vpnservice: + raise vpnaas.VPNServiceInUse(vpnservice_id=vpnservice_id) + vpns_db = self._get_resource(context, VPNService, vpnservice_id) + self.assert_update_allowed(vpns_db) + if vpns: + vpns_db.update(vpns) + return self._make_vpnservice_dict(vpns_db) + + def delete_vpnservice(self, context, vpnservice_id): + with context.session.begin(subtransactions=True): + if context.session.query(IPsecSiteConnection).filter_by( + vpnservice_id=vpnservice_id + ).first(): + raise vpnaas.VPNServiceInUse(vpnservice_id=vpnservice_id) + vpns_db = self._get_resource(context, VPNService, vpnservice_id) + context.session.delete(vpns_db) + + def _get_vpnservice(self, context, vpnservice_id): + return self._get_resource(context, VPNService, vpnservice_id) + + def get_vpnservice(self, context, vpnservice_id, fields=None): + vpns_db = self._get_resource(context, VPNService, vpnservice_id) + return self._make_vpnservice_dict(vpns_db, fields) + + def get_vpnservices(self, context, filters=None, fields=None): + return self._get_collection(context, VPNService, + self._make_vpnservice_dict, + filters=filters, fields=fields) + + +class VPNPluginRpcDbMixin(): + def _get_agent_hosting_vpn_services(self, context, host): + + plugin = manager.NeutronManager.get_plugin() + agent = plugin._get_agent_by_type_and_host( + context, q_constants.AGENT_TYPE_L3, host) + if not agent.admin_state_up: + return [] + query = context.session.query(VPNService) + query = query.join(IPsecSiteConnection) + query = query.join(IKEPolicy) + query = query.join(IPsecPolicy) + query = query.join(IPsecPeerCidr) + query = query.join(agent_db.RouterL3AgentBinding, + agent_db.RouterL3AgentBinding.router_id == + VPNService.router_id) + query = query.filter( + agent_db.RouterL3AgentBinding.l3_agent_id == agent.id) + return query + + def update_status_on_host(self, context, host, active_services): + with context.session.begin(subtransactions=True): + vpnservices = self._get_agent_hosting_vpn_services( + context, host) + for vpnservice in vpnservices: + if vpnservice.id in active_services: + if vpnservice.status != constants.ACTIVE: + vpnservice.status = constants.ACTIVE + else: + if vpnservice.status != constants.ERROR: + vpnservice.status = constants.ERROR diff --git a/neutron/services/vpn/__init__.py b/neutron/services/vpn/__init__.py new file mode 100644 index 000000000..29c415d98 --- /dev/null +++ b/neutron/services/vpn/__init__.py @@ -0,0 +1,18 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. +# All Rights Reserved. +# +# 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. +# +# @author: Swaminathan Vasudevan, Hewlett-Packard diff --git a/neutron/services/vpn/plugin.py b/neutron/services/vpn/plugin.py new file mode 100644 index 000000000..6ba7c0933 --- /dev/null +++ b/neutron/services/vpn/plugin.py @@ -0,0 +1,32 @@ + +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. +# All Rights Reserved. +# +# 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. +# +# @author: Swaminathan Vasudevan, Hewlett-Packard + +from neutron.db.vpn import vpn_db + + +class VPNPlugin(vpn_db.VPNPluginDb): + + """Implementation of the VPN Service Plugin. + + This class manages the workflow of VPNaaS request/response. + Most DB related works are implemented in class + vpn_db.VPNPluginDb. + """ + supported_extension_aliases = ["vpnaas"] diff --git a/neutron/tests/unit/db/vpn/__init__.py b/neutron/tests/unit/db/vpn/__init__.py new file mode 100644 index 000000000..b936bbcb8 --- /dev/null +++ b/neutron/tests/unit/db/vpn/__init__.py @@ -0,0 +1,17 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. +# All Rights Reserved. +# +# 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. +# @author: Swaminathan Vasudevan, Hewlett-Packard. diff --git a/neutron/tests/unit/db/vpn/test_db_vpnaas.py b/neutron/tests/unit/db/vpn/test_db_vpnaas.py new file mode 100644 index 000000000..c17ea08be --- /dev/null +++ b/neutron/tests/unit/db/vpn/test_db_vpnaas.py @@ -0,0 +1,1541 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. +# All Rights Reserved. +# +# 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. +# +# @author: Swaminathan Vasudevan, Hewlett-Packard. + +import contextlib +import os + +import webob.exc + +from neutron.api.extensions import ExtensionMiddleware +from neutron.api.extensions import PluginAwareExtensionManager +from neutron.common import config +from neutron import context +from neutron.db import agentschedulers_db +from neutron.db.vpn import vpn_db +from neutron import extensions +from neutron.extensions import vpnaas +from neutron import manager +from neutron.plugins.common import constants +from neutron.scheduler import l3_agent_scheduler +from neutron.services.vpn import plugin as vpn_plugin +from neutron.tests.unit import test_db_plugin +from neutron.tests.unit import test_l3_plugin + +DB_CORE_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' +DB_VPN_PLUGIN_KLASS = "neutron.services.vpn.plugin.VPNPlugin" +ROOTDIR = os.path.normpath(os.path.join( + os.path.dirname(__file__), + '..', '..', '..', '..')) + +extensions_path = ':'.join(extensions.__path__) + + +class TestVpnCorePlugin(test_l3_plugin.TestL3NatPlugin, + agentschedulers_db.L3AgentSchedulerDbMixin, + agentschedulers_db.DhcpAgentSchedulerDbMixin): + def __init__(self, configfile=None): + super(TestVpnCorePlugin, self).__init__() + self.router_scheduler = l3_agent_scheduler.ChanceScheduler() + + +class VPNPluginDbTestCase(test_l3_plugin.L3NatTestCaseMixin, + test_db_plugin.NeutronDbPluginV2TestCase): + resource_prefix_map = dict( + (k.replace('_', '-'), + constants.COMMON_PREFIXES[constants.VPN]) + for k in vpnaas.RESOURCE_ATTRIBUTE_MAP + ) + + def setUp(self, core_plugin=None, vpnaas_plugin=DB_VPN_PLUGIN_KLASS): + service_plugins = {'vpnaas_plugin': vpnaas_plugin} + plugin_str = ('neutron.tests.unit.db.vpn.' + 'test_db_vpnaas.TestVpnCorePlugin') + + super(VPNPluginDbTestCase, self).setUp( + plugin_str, + service_plugins=service_plugins + ) + self._subnet_id = "0c798ed8-33ba-11e2-8b28-000c291c4d14" + self.core_plugin = TestVpnCorePlugin + self.plugin = vpn_plugin.VPNPlugin() + ext_mgr = PluginAwareExtensionManager( + extensions_path, + {constants.CORE: self.core_plugin, + constants.VPN: self.plugin} + ) + app = config.load_paste_app('extensions_test_app') + self.ext_api = ExtensionMiddleware(app, ext_mgr=ext_mgr) + + def _create_ikepolicy(self, fmt, + name='ikepolicy1', + auth_algorithm='sha1', + encryption_algorithm='aes-128', + phase1_negotiation_mode='main', + lifetime_units='seconds', + lifetime_value=3600, + ike_version='v1', + pfs='group5', + expected_res_status=None, **kwargs): + + data = {'ikepolicy': { + 'name': name, + 'auth_algorithm': auth_algorithm, + 'encryption_algorithm': encryption_algorithm, + 'phase1_negotiation_mode': phase1_negotiation_mode, + 'lifetime': { + 'units': lifetime_units, + 'value': lifetime_value}, + 'ike_version': ike_version, + 'pfs': pfs, + 'tenant_id': self._tenant_id + }} + for arg in ['description']: + if arg in kwargs and kwargs[arg] is not None: + data['ikepolicy'][arg] = kwargs[arg] + + ikepolicy_req = self.new_create_request('ikepolicies', data, fmt) + ikepolicy_res = ikepolicy_req.get_response(self.ext_api) + if expected_res_status: + self.assertEqual(ikepolicy_res.status_int, expected_res_status) + + return ikepolicy_res + + @contextlib.contextmanager + def ikepolicy(self, fmt=None, + name='ikepolicy1', + auth_algorithm='sha1', + encryption_algorithm='aes-128', + phase1_negotiation_mode='main', + lifetime_units='seconds', + lifetime_value=3600, + ike_version='v1', + pfs='group5', + no_delete=False, + **kwargs): + if not fmt: + fmt = self.fmt + res = self._create_ikepolicy(fmt, + name, + auth_algorithm, + encryption_algorithm, + phase1_negotiation_mode, + lifetime_units, + lifetime_value, + ike_version, + pfs, + **kwargs) + if res.status_int >= 400: + raise webob.exc.HTTPClientError(code=res.status_int) + try: + ikepolicy = self.deserialize(fmt or self.fmt, res) + yield ikepolicy + finally: + if not no_delete: + self._delete('ikepolicies', ikepolicy['ikepolicy']['id']) + + def _create_ipsecpolicy(self, fmt, + name='ipsecpolicy1', + auth_algorithm='sha1', + encryption_algorithm='aes-128', + encapsulation_mode='tunnel', + transform_protocol='esp', + lifetime_units='seconds', + lifetime_value=3600, + pfs='group5', + expected_res_status=None, + **kwargs): + + data = {'ipsecpolicy': {'name': name, + 'auth_algorithm': auth_algorithm, + 'encryption_algorithm': encryption_algorithm, + 'encapsulation_mode': encapsulation_mode, + 'transform_protocol': transform_protocol, + 'lifetime': {'units': lifetime_units, + 'value': lifetime_value}, + 'pfs': pfs, + 'tenant_id': self._tenant_id}} + for arg in ['description']: + if arg in kwargs and kwargs[arg] is not None: + data['ipsecpolicy'][arg] = kwargs[arg] + ipsecpolicy_req = self.new_create_request('ipsecpolicies', data, fmt) + ipsecpolicy_res = ipsecpolicy_req.get_response(self.ext_api) + if expected_res_status: + self.assertEqual(ipsecpolicy_res.status_int, expected_res_status) + + return ipsecpolicy_res + + @contextlib.contextmanager + def ipsecpolicy(self, fmt=None, + name='ipsecpolicy1', + auth_algorithm='sha1', + encryption_algorithm='aes-128', + encapsulation_mode='tunnel', + transform_protocol='esp', + lifetime_units='seconds', + lifetime_value=3600, + pfs='group5', + no_delete=False, **kwargs): + if not fmt: + fmt = self.fmt + res = self._create_ipsecpolicy(fmt, + name, + auth_algorithm, + encryption_algorithm, + encapsulation_mode, + transform_protocol, + lifetime_units, + lifetime_value, + pfs, + **kwargs) + if res.status_int >= 400: + raise webob.exc.HTTPClientError(code=res.status_int) + try: + ipsecpolicy = self.deserialize(fmt or self.fmt, res) + yield ipsecpolicy + finally: + if not no_delete: + self._delete('ipsecpolicies', ipsecpolicy['ipsecpolicy']['id']) + + def _create_vpnservice(self, fmt, name, + admin_state_up, + router_id, subnet_id, + expected_res_status=None, **kwargs): + data = {'vpnservice': {'name': name, + 'subnet_id': subnet_id, + 'router_id': router_id, + 'admin_state_up': admin_state_up, + 'tenant_id': self._tenant_id}} + for arg in ['description']: + if arg in kwargs and kwargs[arg] is not None: + data['vpnservice'][arg] = kwargs[arg] + vpnservice_req = self.new_create_request('vpnservices', data, fmt) + vpnservice_res = vpnservice_req.get_response(self.ext_api) + if expected_res_status: + self.assertEqual(vpnservice_res.status_int, expected_res_status) + return vpnservice_res + + @contextlib.contextmanager + def vpnservice(self, fmt=None, name='vpnservice1', + subnet=None, + router=None, + admin_state_up=True, + no_delete=False, **kwargs): + if not fmt: + fmt = self.fmt + with test_db_plugin.optional_ctx(subnet, self.subnet) as tmp_subnet: + with test_db_plugin.optional_ctx(router, + self.router) as tmp_router: + res = self._create_vpnservice(fmt, + name, + admin_state_up, + router_id=(tmp_router['router'] + ['id']), + subnet_id=(tmp_subnet['subnet'] + ['id']), + **kwargs) + if res.status_int >= 400: + raise webob.exc.HTTPClientError(code=res.status_int) + try: + vpnservice = self.deserialize(fmt or self.fmt, res) + yield vpnservice + finally: + if not no_delete: + self._delete('vpnservices', + vpnservice['vpnservice']['id']) + + def _create_ipsec_site_connection(self, fmt, name='test', + peer_address='192.168.1.10', + peer_id='192.168.1.10', + peer_cidrs=None, + mtu=1500, + psk='abcdefg', + initiator='bi-directional', + dpd_action='hold', + dpd_interval=30, + dpd_timeout=120, + vpnservice_id='fake_id', + ikepolicy_id='fake_id', + ipsecpolicy_id='fake_id', + admin_state_up=True, + expected_res_status=None, **kwargs): + data = { + 'ipsec_site_connection': {'name': name, + 'peer_address': peer_address, + 'peer_id': peer_id, + 'peer_cidrs': peer_cidrs, + 'mtu': mtu, + 'psk': psk, + 'initiator': initiator, + 'dpd': { + 'action': dpd_action, + 'interval': dpd_interval, + 'timeout': dpd_timeout, + }, + 'vpnservice_id': vpnservice_id, + 'ikepolicy_id': ikepolicy_id, + 'ipsecpolicy_id': ipsecpolicy_id, + 'admin_state_up': admin_state_up, + 'tenant_id': self._tenant_id} + } + for arg in ['description']: + if arg in kwargs and kwargs[arg] is not None: + data['ipsec_site_connection'][arg] = kwargs[arg] + + ipsec_site_connection_req = self.new_create_request( + 'ipsec-site-connections', data, fmt + ) + ipsec_site_connection_res = ipsec_site_connection_req.get_response( + self.ext_api + ) + if expected_res_status: + self.assertEqual( + ipsec_site_connection_res.status_int, expected_res_status + ) + + return ipsec_site_connection_res + + @contextlib.contextmanager + def ipsec_site_connection(self, fmt=None, name='ipsec_site_connection1', + peer_address='192.168.1.10', + peer_id='192.168.1.10', + peer_cidrs=None, + mtu=1500, + psk='abcdefg', + initiator='bi-directional', + dpd_action='hold', + dpd_interval=30, + dpd_timeout=120, + vpnservice=None, + ikepolicy=None, + ipsecpolicy=None, + admin_state_up=True, no_delete=False, + **kwargs): + if not fmt: + fmt = self.fmt + with contextlib.nested( + test_db_plugin.optional_ctx(vpnservice, + self.vpnservice), + test_db_plugin.optional_ctx(ikepolicy, + self.ikepolicy), + test_db_plugin.optional_ctx(ipsecpolicy, + self.ipsecpolicy) + ) as (tmp_vpnservice, tmp_ikepolicy, tmp_ipsecpolicy): + vpnservice_id = tmp_vpnservice['vpnservice']['id'] + ikepolicy_id = tmp_ikepolicy['ikepolicy']['id'] + ipsecpolicy_id = tmp_ipsecpolicy['ipsecpolicy']['id'] + res = self._create_ipsec_site_connection(fmt, + name, + peer_address, + peer_id, + peer_cidrs, + mtu, + psk, + initiator, + dpd_action, + dpd_interval, + dpd_timeout, + vpnservice_id, + ikepolicy_id, + ipsecpolicy_id, + admin_state_up, + **kwargs) + if res.status_int >= 400: + raise webob.exc.HTTPClientError(code=res.status_int) + try: + ipsec_site_connection = self.deserialize( + fmt or self.fmt, res + ) + yield ipsec_site_connection + finally: + if not no_delete: + self._delete( + 'ipsec-site-connections', + ipsec_site_connection[ + 'ipsec_site_connection']['id'] + ) + + +class TestVpnaas(VPNPluginDbTestCase): + + def _check_policy(self, policy, keys, lifetime): + for k, v in keys: + self.assertEqual(policy[k], v) + for k, v in lifetime.iteritems(): + self.assertEqual(policy['lifetime'][k], v) + + def test_create_ikepolicy(self): + """Test case to create an ikepolicy.""" + name = "ikepolicy1" + description = 'ipsec-ikepolicy' + keys = [('name', name), + ('description', 'ipsec-ikepolicy'), + ('auth_algorithm', 'sha1'), + ('encryption_algorithm', 'aes-128'), + ('phase1_negotiation_mode', 'main'), + ('ike_version', 'v1'), + ('pfs', 'group5'), + ('tenant_id', self._tenant_id)] + lifetime = { + 'units': 'seconds', + 'value': 3600} + with self.ikepolicy(name=name, description=description) as ikepolicy: + self._check_policy(ikepolicy['ikepolicy'], keys, lifetime) + + def test_delete_ikepolicy(self): + """Test case to delete an ikepolicy.""" + with self.ikepolicy(no_delete=True) as ikepolicy: + req = self.new_delete_request('ikepolicies', + ikepolicy['ikepolicy']['id']) + res = req.get_response(self.ext_api) + self.assertEqual(res.status_int, 204) + + def test_show_ikepolicy(self): + """Test case to show or get an ikepolicy.""" + name = "ikepolicy1" + description = 'ipsec-ikepolicy' + keys = [('name', name), + ('auth_algorithm', 'sha1'), + ('encryption_algorithm', 'aes-128'), + ('phase1_negotiation_mode', 'main'), + ('ike_version', 'v1'), + ('pfs', 'group5'), + ('tenant_id', self._tenant_id)] + lifetime = { + 'units': 'seconds', + 'value': 3600} + with self.ikepolicy(name=name, description=description) as ikepolicy: + req = self.new_show_request('ikepolicies', + ikepolicy['ikepolicy']['id'], + fmt=self.fmt) + res = self.deserialize(self.fmt, req.get_response(self.ext_api)) + self._check_policy(res['ikepolicy'], keys, lifetime) + + def test_list_ikepolicies(self): + """Test case to list all ikepolicies.""" + name = "ikepolicy_list" + keys = [('name', name), + ('auth_algorithm', 'sha1'), + ('encryption_algorithm', 'aes-128'), + ('phase1_negotiation_mode', 'main'), + ('ike_version', 'v1'), + ('pfs', 'group5'), + ('tenant_id', self._tenant_id)] + lifetime = { + 'units': 'seconds', + 'value': 3600} + with self.ikepolicy(name=name) as ikepolicy: + keys.append(('id', ikepolicy['ikepolicy']['id'])) + req = self.new_list_request('ikepolicies') + res = self.deserialize(self.fmt, req.get_response(self.ext_api)) + self.assertEqual(len(res), 1) + for k, v in keys: + self.assertEqual(res['ikepolicies'][0][k], v) + for k, v in lifetime.iteritems(): + self.assertEqual(res['ikepolicies'][0]['lifetime'][k], v) + + def test_list_ikepolicies_with_sort_emulated(self): + """Test case to list all ikepolicies.""" + with contextlib.nested(self.ikepolicy(name='ikepolicy1'), + self.ikepolicy(name='ikepolicy2'), + self.ikepolicy(name='ikepolicy3') + ) as (ikepolicy1, ikepolicy2, ikepolicy3): + self._test_list_with_sort('ikepolicy', (ikepolicy3, + ikepolicy2, + ikepolicy1), + [('name', 'desc')], + 'ikepolicies') + + def test_list_ikepolicies_with_pagination_emulated(self): + """Test case to list all ikepolicies with pagination.""" + with contextlib.nested(self.ikepolicy(name='ikepolicy1'), + self.ikepolicy(name='ikepolicy2'), + self.ikepolicy(name='ikepolicy3') + ) as (ikepolicy1, ikepolicy2, ikepolicy3): + self._test_list_with_pagination('ikepolicy', + (ikepolicy1, + ikepolicy2, + ikepolicy3), + ('name', 'asc'), 2, 2, + 'ikepolicies') + + def test_list_ikepolicies_with_pagination_reverse_emulated(self): + """Test case to list all ikepolicies with reverse pagination.""" + with contextlib.nested(self.ikepolicy(name='ikepolicy1'), + self.ikepolicy(name='ikepolicy2'), + self.ikepolicy(name='ikepolicy3') + ) as (ikepolicy1, ikepolicy2, ikepolicy3): + self._test_list_with_pagination_reverse('ikepolicy', + (ikepolicy1, + ikepolicy2, + ikepolicy3), + ('name', 'asc'), 2, 2, + 'ikepolicies') + + def test_update_ikepolicy(self): + """Test case to update an ikepolicy.""" + name = "new_ikepolicy1" + keys = [('name', name), + ('auth_algorithm', 'sha1'), + ('encryption_algorithm', 'aes-128'), + ('phase1_negotiation_mode', 'main'), + ('ike_version', 'v1'), + ('pfs', 'group5'), + ('tenant_id', self._tenant_id)] + with self.ikepolicy(name=name) as ikepolicy: + data = {'ikepolicy': {'name': name}} + req = self.new_update_request("ikepolicies", + data, + ikepolicy['ikepolicy']['id']) + res = self.deserialize(self.fmt, req.get_response(self.ext_api)) + for k, v in keys: + self.assertEqual(res['ikepolicy'][k], v) + + def test_create_ikepolicy_with_invalid_values(self): + """Test case to test invalid values.""" + name = 'ikepolicy1' + self._create_ikepolicy(name=name, + fmt=self.fmt, + auth_algorithm='md5', + expected_res_status=400) + self._create_ikepolicy(name=name, + fmt=self.fmt, + auth_algorithm=200, + expected_res_status=400) + self._create_ikepolicy(name=name, + fmt=self.fmt, + encryption_algorithm='des', + expected_res_status=400) + self._create_ikepolicy(name=name, + fmt=self.fmt, + encryption_algorithm=100, + expected_res_status=400) + self._create_ikepolicy(name=name, + fmt=self.fmt, + phase1_negotiation_mode='aggressive', + expected_res_status=400) + self._create_ikepolicy(name=name, + fmt=self.fmt, + phase1_negotiation_mode=-100, + expected_res_status=400) + self._create_ikepolicy(name=name, + fmt=self.fmt, + ike_version='v6', + expected_res_status=400) + self._create_ikepolicy(name=name, + fmt=self.fmt, + ike_version=500, + expected_res_status=400) + self._create_ikepolicy(name=name, + fmt=self.fmt, + pfs='group1', + expected_res_status=400) + self._create_ikepolicy(name=name, + fmt=self.fmt, + pfs=120, + expected_res_status=400) + self._create_ikepolicy(name=name, + fmt=self.fmt, + lifetime_units='Megabytes', + expected_res_status=400) + self._create_ikepolicy(name=name, + fmt=self.fmt, + lifetime_units=20000, + expected_res_status=400) + self._create_ikepolicy(name=name, + fmt=self.fmt, + lifetime_value=-20, + expected_res_status=400) + self._create_ikepolicy(name=name, + fmt=self.fmt, + lifetime_value='Megabytes', + expected_res_status=400) + + def test_create_ipsecpolicy(self): + """Test case to create an ipsecpolicy.""" + name = "ipsecpolicy1" + description = 'my-ipsecpolicy' + keys = [('name', name), + ('description', 'my-ipsecpolicy'), + ('auth_algorithm', 'sha1'), + ('encryption_algorithm', 'aes-128'), + ('encapsulation_mode', 'tunnel'), + ('transform_protocol', 'esp'), + ('pfs', 'group5'), + ('tenant_id', self._tenant_id)] + lifetime = { + 'units': 'seconds', + 'value': 3600} + with self.ipsecpolicy(name=name, + description=description) as ipsecpolicy: + self._check_policy(ipsecpolicy['ipsecpolicy'], keys, lifetime) + + def test_delete_ipsecpolicy(self): + """Test case to delete an ipsecpolicy.""" + with self.ipsecpolicy(no_delete=True) as ipsecpolicy: + req = self.new_delete_request('ipsecpolicies', + ipsecpolicy['ipsecpolicy']['id']) + res = req.get_response(self.ext_api) + self.assertEqual(res.status_int, 204) + + def test_show_ipsecpolicy(self): + """Test case to show or get an ipsecpolicy.""" + name = "ipsecpolicy1" + keys = [('name', name), + ('auth_algorithm', 'sha1'), + ('encryption_algorithm', 'aes-128'), + ('encapsulation_mode', 'tunnel'), + ('transform_protocol', 'esp'), + ('pfs', 'group5'), + ('tenant_id', self._tenant_id)] + lifetime = { + 'units': 'seconds', + 'value': 3600} + with self.ipsecpolicy(name=name) as ipsecpolicy: + req = self.new_show_request('ipsecpolicies', + ipsecpolicy['ipsecpolicy']['id'], + fmt=self.fmt) + res = self.deserialize(self.fmt, req.get_response(self.ext_api)) + self._check_policy(res['ipsecpolicy'], keys, lifetime) + + def test_list_ipsecpolicies(self): + """Test case to list all ipsecpolicies.""" + name = "ipsecpolicy_list" + keys = [('name', name), + ('auth_algorithm', 'sha1'), + ('encryption_algorithm', 'aes-128'), + ('encapsulation_mode', 'tunnel'), + ('transform_protocol', 'esp'), + ('pfs', 'group5'), + ('tenant_id', self._tenant_id)] + lifetime = { + 'units': 'seconds', + 'value': 3600} + with self.ipsecpolicy(name=name) as ipsecpolicy: + keys.append(('id', ipsecpolicy['ipsecpolicy']['id'])) + req = self.new_list_request('ipsecpolicies') + res = self.deserialize(self.fmt, req.get_response(self.ext_api)) + self.assertEqual(len(res), 1) + self._check_policy(res['ipsecpolicies'][0], keys, lifetime) + + def test_list_ipsecpolicies_with_sort_emulated(self): + """Test case to list all ipsecpolicies.""" + with contextlib.nested(self.ipsecpolicy(name='ipsecpolicy1'), + self.ipsecpolicy(name='ipsecpolicy2'), + self.ipsecpolicy(name='ipsecpolicy3') + ) as(ipsecpolicy1, ipsecpolicy2, ipsecpolicy3): + self._test_list_with_sort('ipsecpolicy', (ipsecpolicy3, + ipsecpolicy2, + ipsecpolicy1), + [('name', 'desc')], + 'ipsecpolicies') + + def test_list_ipsecpolicies_with_pagination_emulated(self): + """Test case to list all ipsecpolicies with pagination.""" + with contextlib.nested(self.ipsecpolicy(name='ipsecpolicy1'), + self.ipsecpolicy(name='ipsecpolicy2'), + self.ipsecpolicy(name='ipsecpolicy3') + ) as(ipsecpolicy1, ipsecpolicy2, ipsecpolicy3): + self._test_list_with_pagination('ipsecpolicy', + (ipsecpolicy1, + ipsecpolicy2, + ipsecpolicy3), + ('name', 'asc'), 2, 2, + 'ipsecpolicies') + + def test_list_ipsecpolicies_with_pagination_reverse_emulated(self): + """Test case to list all ipsecpolicies with reverse pagination.""" + with contextlib.nested(self.ipsecpolicy(name='ipsecpolicy1'), + self.ipsecpolicy(name='ipsecpolicy2'), + self.ipsecpolicy(name='ipsecpolicy3') + ) as(ipsecpolicy1, ipsecpolicy2, ipsecpolicy3): + self._test_list_with_pagination_reverse('ipsecpolicy', + (ipsecpolicy1, + ipsecpolicy2, + ipsecpolicy3), + ('name', 'asc'), 2, 2, + 'ipsecpolicies') + + def test_update_ipsecpolicy(self): + """Test case to update an ipsecpolicy.""" + name = "new_ipsecpolicy1" + keys = [('name', name), + ('auth_algorithm', 'sha1'), + ('encryption_algorithm', 'aes-128'), + ('encapsulation_mode', 'tunnel'), + ('transform_protocol', 'esp'), + ('pfs', 'group5'), + ('tenant_id', self._tenant_id)] + with self.ipsecpolicy(name=name) as ipsecpolicy: + data = {'ipsecpolicy': {'name': name}} + req = self.new_update_request("ipsecpolicies", + data, + ipsecpolicy['ipsecpolicy']['id']) + res = self.deserialize(self.fmt, req.get_response(self.ext_api)) + for k, v in keys: + self.assertEqual(res['ipsecpolicy'][k], v) + + def test_create_ipsecpolicy_with_invalid_values(self): + """Test case to test invalid values.""" + name = 'ipsecpolicy1' + + self._create_ipsecpolicy( + fmt=self.fmt, + name=name, auth_algorithm='md5', expected_res_status=400) + self._create_ipsecpolicy( + fmt=self.fmt, + name=name, auth_algorithm=100, expected_res_status=400) + + self._create_ipsecpolicy( + fmt=self.fmt, + name=name, encryption_algorithm='des', expected_res_status=400) + self._create_ipsecpolicy( + fmt=self.fmt, + name=name, encryption_algorithm=200, expected_res_status=400) + + self._create_ipsecpolicy( + fmt=self.fmt, + name=name, transform_protocol='abcd', expected_res_status=400) + self._create_ipsecpolicy( + fmt=self.fmt, + name=name, transform_protocol=500, expected_res_status=400) + + self._create_ipsecpolicy( + fmt=self.fmt, + name=name, + encapsulation_mode='unsupported', expected_res_status=400) + self._create_ipsecpolicy(name=name, + fmt=self.fmt, + encapsulation_mode=100, + expected_res_status=400) + + self._create_ipsecpolicy(name=name, + fmt=self.fmt, + pfs='group9', expected_res_status=400) + self._create_ipsecpolicy( + fmt=self.fmt, name=name, pfs=-1, expected_res_status=400) + + self._create_ipsecpolicy( + fmt=self.fmt, name=name, lifetime_units='minutes', + expected_res_status=400) + + self._create_ipsecpolicy(fmt=self.fmt, name=name, lifetime_units=100, + expected_res_status=400) + + self._create_ipsecpolicy(fmt=self.fmt, name=name, + lifetime_value=-800, expected_res_status=400) + self._create_ipsecpolicy(fmt=self.fmt, name=name, + lifetime_value='Megabytes', + expected_res_status=400) + + def test_create_vpnservice(self, **extras): + """Test case to create a vpnservice.""" + description = 'my-vpn-service' + expected = {'name': 'vpnservice1', + 'description': 'my-vpn-service', + 'admin_state_up': True, + 'status': 'PENDING_CREATE', + 'tenant_id': self._tenant_id, } + + expected.update(extras) + with self.subnet(cidr='10.2.0.0/24') as subnet: + with self.router() as router: + expected['router_id'] = router['router']['id'] + expected['subnet_id'] = subnet['subnet']['id'] + name = expected['name'] + with self.vpnservice(name=name, + subnet=subnet, + router=router, + description=description, + **extras) as vpnservice: + self.assertEqual(dict((k, v) for k, v in + vpnservice['vpnservice'].items() + if k in expected), + expected) + return vpnservice + + def _set_active(self, model, resource_id): + service_plugin = manager.NeutronManager.get_service_plugins()[ + constants.VPN] + adminContext = context.get_admin_context() + with adminContext.session.begin(subtransactions=True): + resource_db = service_plugin._get_resource( + adminContext, + model, + resource_id) + resource_db.status = constants.ACTIVE + + def test_update_vpnservice(self): + """Test case to update a vpnservice.""" + name = 'new_vpnservice1' + keys = [('name', name)] + with contextlib.nested( + self.subnet(cidr='10.2.0.0/24'), + self.router()) as (subnet, router): + with self.vpnservice(name=name, + subnet=subnet, + router=router) as vpnservice: + keys.append(('subnet_id', + vpnservice['vpnservice']['subnet_id'])) + keys.append(('router_id', + vpnservice['vpnservice']['router_id'])) + data = {'vpnservice': {'name': name}} + self._set_active(vpn_db.VPNService, + vpnservice['vpnservice']['id']) + req = self.new_update_request( + 'vpnservices', + data, + vpnservice['vpnservice']['id']) + res = self.deserialize(self.fmt, + req.get_response(self.ext_api)) + for k, v in keys: + self.assertEqual(res['vpnservice'][k], v) + + def test_update_vpnservice_with_invalid_state(self): + """Test case to update a vpnservice in invalid state .""" + name = 'new_vpnservice1' + keys = [('name', name)] + with contextlib.nested( + self.subnet(cidr='10.2.0.0/24'), + self.router()) as (subnet, router): + with self.vpnservice(name=name, + subnet=subnet, + router=router) as vpnservice: + keys.append(('subnet_id', + vpnservice['vpnservice']['subnet_id'])) + keys.append(('router_id', + vpnservice['vpnservice']['router_id'])) + data = {'vpnservice': {'name': name}} + req = self.new_update_request( + 'vpnservices', + data, + vpnservice['vpnservice']['id']) + res = req.get_response(self.ext_api) + self.assertEqual(400, res.status_int) + + def test_delete_vpnservice(self): + """Test case to delete a vpnservice.""" + with self.vpnservice(name='vpnserver', + no_delete=True) as vpnservice: + req = self.new_delete_request('vpnservices', + vpnservice['vpnservice']['id']) + res = req.get_response(self.ext_api) + self.assertEqual(res.status_int, 204) + + def test_show_vpnservice(self): + """Test case to show or get a vpnservice.""" + name = "vpnservice1" + keys = [('name', name), + ('description', ''), + ('admin_state_up', True), + ('status', 'PENDING_CREATE')] + with self.vpnservice(name=name) as vpnservice: + req = self.new_show_request('vpnservices', + vpnservice['vpnservice']['id']) + res = self.deserialize(self.fmt, req.get_response(self.ext_api)) + for k, v in keys: + self.assertEqual(res['vpnservice'][k], v) + + def test_list_vpnservices(self): + """Test case to list all vpnservices.""" + name = "vpnservice_list" + keys = [('name', name), + ('description', ''), + ('admin_state_up', True), + ('status', 'PENDING_CREATE')] + with self.vpnservice(name=name) as vpnservice: + keys.append(('subnet_id', vpnservice['vpnservice']['subnet_id'])) + keys.append(('router_id', vpnservice['vpnservice']['router_id'])) + req = self.new_list_request('vpnservices') + res = self.deserialize(self.fmt, req.get_response(self.ext_api)) + self.assertEqual(len(res), 1) + for k, v in keys: + self.assertEqual(res['vpnservices'][0][k], v) + + def test_list_vpnservices_with_sort_emulated(self): + """Test case to list all vpnservices with sorting.""" + with self.subnet() as subnet: + with self.router() as router: + with contextlib.nested( + self.vpnservice(name='vpnservice1', + subnet=subnet, + router=router), + self.vpnservice(name='vpnservice2', + subnet=subnet, + router=router), + self.vpnservice(name='vpnservice3', + subnet=subnet, + router=router) + ) as(vpnservice1, vpnservice2, vpnservice3): + self._test_list_with_sort('vpnservice', (vpnservice3, + vpnservice2, + vpnservice1), + [('name', 'desc')]) + + def test_list_vpnservice_with_pagination_emulated(self): + """Test case to list all vpnservices with pagination.""" + with self.subnet() as subnet: + with self.router() as router: + with contextlib.nested( + self.vpnservice(name='vpnservice1', + subnet=subnet, + router=router), + self.vpnservice(name='vpnservice2', + subnet=subnet, + router=router), + self.vpnservice(name='vpnservice3', + subnet=subnet, + router=router) + ) as(vpnservice1, vpnservice2, vpnservice3): + self._test_list_with_pagination('vpnservice', + (vpnservice1, + vpnservice2, + vpnservice3), + ('name', 'asc'), 2, 2) + + def test_list_vpnservice_with_pagination_reverse_emulated(self): + """Test case to list all vpnservices with reverse pagination.""" + with self.subnet() as subnet: + with self.router() as router: + with contextlib.nested( + self.vpnservice(name='vpnservice1', + subnet=subnet, + router=router), + self.vpnservice(name='vpnservice2', + subnet=subnet, + router=router), + self.vpnservice(name='vpnservice3', + subnet=subnet, + router=router) + ) as(vpnservice1, vpnservice2, vpnservice3): + self._test_list_with_pagination_reverse('vpnservice', + (vpnservice1, + vpnservice2, + vpnservice3), + ('name', 'asc'), + 2, 2) + + def test_create_ipsec_site_connection_with_invalid_values(self): + """Test case to create an ipsec_site_connection with invalid values.""" + name = 'connection1' + self._create_ipsec_site_connection( + fmt=self.fmt, + name=name, peer_cidrs='myname', expected_status_int=400) + self._create_ipsec_site_connection( + fmt=self.fmt, + name=name, mtu=-100, expected_status_int=400) + self._create_ipsec_site_connection( + fmt=self.fmt, + name=name, dpd_action='unsupported', expected_status_int=400) + self._create_ipsec_site_connection( + fmt=self.fmt, + name=name, dpd_interval=-1, expected_status_int=400) + self._create_ipsec_site_connection( + fmt=self.fmt, + name=name, dpd_timeout=-200, expected_status_int=400) + self._create_ipsec_site_connection( + fmt=self.fmt, + name=name, initiator='unsupported', expected_status_int=400) + self._create_ipsec_site_connection( + fmt=self.fmt, + name=name, + dpd_interval=30, + dpd_timeout=20, expected_status_int=400) + + def test_create_ipsec_site_connection(self, **extras): + """Test case to create an ipsec_site_connection.""" + ikename = "ikepolicy1" + ipsecname = "ipsecpolicy1" + vpnsname = "vpnservice1" + name = "connection1" + description = "my-ipsec-connection" + keys = {'name': name, + 'description': "my-ipsec-connection", + 'peer_address': '192.168.1.10', + 'peer_id': '192.168.1.10', + 'peer_cidrs': ['192.168.2.0/24', '192.168.3.0/24'], + 'initiator': 'bi-directional', + 'mtu': 1500, + 'tenant_id': self._tenant_id, + 'psk': 'abcd', + 'status': 'PENDING_CREATE', + 'admin_state_up': True} + dpd = {'action': 'hold', + 'interval': 40, + 'timeout': 120} + keys.update(extras) + with contextlib.nested( + self.ikepolicy(name=ikename), + self.ipsecpolicy(name=ipsecname), + self.subnet(), + self.router()) as ( + ikepolicy, ipsecpolicy, subnet, router): + with self.vpnservice(name=vpnsname, subnet=subnet, + router=router) as vpnservice1: + keys['ikepolicy_id'] = ikepolicy['ikepolicy']['id'] + keys['ipsecpolicy_id'] = ( + ipsecpolicy['ipsecpolicy']['id'] + ) + keys['vpnservice_id'] = ( + vpnservice1['vpnservice']['id'] + ) + with self.ipsec_site_connection( + self.fmt, + name, + keys['peer_address'], + keys['peer_id'], + keys['peer_cidrs'], + keys['mtu'], + keys['psk'], + keys['initiator'], + dpd['action'], + dpd['interval'], + dpd['timeout'], + vpnservice1, + ikepolicy, + ipsecpolicy, + keys['admin_state_up'], + description=description, + **extras + ) as ipsec_site_connection: + self._check_ipsec_site_connection( + ipsec_site_connection['ipsec_site_connection'], + keys, + dpd) + + def _check_ipsec_site_connection(self, ipsec_site_connection, keys, dpd): + self.assertEqual( + dict((k, v) for k, v + in ipsec_site_connection.items() + if k in keys), keys) + self.assertEqual( + dict((k, v) for k, v + in ipsec_site_connection['dpd'].items() + if k in dpd), dpd) + + def test_delete_ipsec_site_connection(self): + """Test case to delete a ipsec_site_connection.""" + with self.ipsec_site_connection( + no_delete=True) as ipsec_site_connection: + req = self.new_delete_request( + 'ipsec-site-connections', + ipsec_site_connection['ipsec_site_connection']['id'] + ) + res = req.get_response(self.ext_api) + self.assertEqual(res.status_int, 204) + + def test_update_ipsec_site_connection(self): + """Test case to update a ipsec_site_connection.""" + name = 'new_ipsec_site_connection' + ikename = 'ikepolicy1' + ipsecname = 'ipsecpolicy1' + vpnsname = 'vpnservice1' + description = 'my-ipsec-connection' + keys = {'name': 'new_ipsec_site_connection', + 'description': "my-ipsec-connection", + 'peer_address': '192.168.1.10', + 'peer_id': '192.168.1.10', + 'peer_cidrs': ['192.168.2.0/24', '192.168.3.0/24'], + 'initiator': 'bi-directional', + 'mtu': 1500, + 'tenant_id': self._tenant_id, + 'psk': 'abcd', + 'status': 'ACTIVE', + 'admin_state_up': True} + dpd = {'action': 'hold', + 'interval': 40, + 'timeout': 120} + with contextlib.nested( + self.ikepolicy(name=ikename), + self.ipsecpolicy(name=ipsecname), + self.subnet(cidr='10.2.0.0/24'), + self.router()) as ( + ikepolicy, ipsecpolicy, subnet, router): + with self.vpnservice(name=vpnsname, subnet=subnet, + router=router) as vpnservice1: + keys['vpnservice_id'] = ( + vpnservice1['vpnservice']['id'] + ) + keys['ikepolicy_id'] = ( + ikepolicy['ikepolicy']['id'] + ) + keys['ipsecpolicy_id'] = ( + ipsecpolicy['ipsecpolicy']['id'] + ) + with self.ipsec_site_connection( + self.fmt, + name, + keys['peer_address'], + keys['peer_id'], + keys['peer_cidrs'], + keys['mtu'], + keys['psk'], + keys['initiator'], + dpd['action'], + dpd['interval'], + dpd['timeout'], + vpnservice1, + ikepolicy, + ipsecpolicy, + keys['admin_state_up'], + description=description + ) as ipsec_site_connection: + data = {'ipsec_site_connection': {'name': name}} + self._set_active( + vpn_db.IPsecSiteConnection, + ipsec_site_connection['ipsec_site_connection']['id']) + req = self.new_update_request( + 'ipsec-site-connections', + data, + ipsec_site_connection['ipsec_site_connection']['id'] + ) + res = self.deserialize( + self.fmt, + req.get_response(self.ext_api) + ) + for k, v in keys.items(): + self.assertEqual(res['ipsec_site_connection'][k], v) + + def test_update_ipsec_site_connection_with_invalid_state(self): + """Test case to update an ipsec_site_connection in invalid state.""" + name = 'new_ipsec_site_connection' + ikename = 'ikepolicy1' + ipsecname = 'ipsecpolicy1' + vpnsname = 'vpnservice1' + description = 'my-ipsec-connection' + keys = {'name': 'new_ipsec_site_connection', + 'description': "my-ipsec-connection", + 'peer_address': '192.168.1.10', + 'peer_id': '192.168.1.10', + 'peer_cidrs': ['192.168.2.0/24', '192.168.3.0/24'], + 'initiator': 'bi-directional', + 'mtu': 1500, + 'tenant_id': self._tenant_id, + 'psk': 'abcd', + 'status': 'ACTIVE', + 'admin_state_up': True} + dpd = {'action': 'hold', + 'interval': 40, + 'timeout': 120} + with contextlib.nested( + self.ikepolicy(name=ikename), + self.ipsecpolicy(name=ipsecname), + self.subnet(cidr='10.2.0.0/24'), + self.router()) as ( + ikepolicy, ipsecpolicy, subnet, router): + with self.vpnservice(name=vpnsname, subnet=subnet, + router=router) as vpnservice1: + keys['vpnservice_id'] = ( + vpnservice1['vpnservice']['id'] + ) + keys['ikepolicy_id'] = ( + ikepolicy['ikepolicy']['id'] + ) + keys['ipsecpolicy_id'] = ( + ipsecpolicy['ipsecpolicy']['id'] + ) + with self.ipsec_site_connection( + self.fmt, + name, + keys['peer_address'], + keys['peer_id'], + keys['peer_cidrs'], + keys['mtu'], + keys['psk'], + keys['initiator'], + dpd['action'], + dpd['interval'], + dpd['timeout'], + vpnservice1, + ikepolicy, + ipsecpolicy, + keys['admin_state_up'], + description=description + ) as ipsec_site_connection: + data = {'ipsec_site_connection': {'name': name}} + req = self.new_update_request( + 'ipsec-site-connections', + data, + ipsec_site_connection['ipsec_site_connection']['id'] + ) + res = req.get_response(self.ext_api) + self.assertEqual(400, res.status_int) + + def test_update_ipsec_site_connection_peer_cidrs(self): + """Test case to update a ipsec_site_connection for peer_cidrs.""" + name = 'ipsec_site_connection' + ikename = 'ikepolicy1' + ipsecname = 'ipsecpolicy1' + vpnsname = 'vpnservice1' + description = 'my-ipsec-connection' + keys = {'name': 'ipsec_site_connection', + 'description': "my-ipsec-connection", + 'peer_address': '192.168.1.10', + 'peer_id': '192.168.1.10', + 'peer_cidrs': ['192.168.2.0/24', '192.168.3.0/24'], + 'initiator': 'bi-directional', + 'mtu': 1500, + 'tenant_id': self._tenant_id, + 'psk': 'abcd', + 'status': 'ACTIVE', + 'admin_state_up': True} + dpd = {'action': 'hold', + 'interval': 40, + 'timeout': 120} + with contextlib.nested( + self.ikepolicy(name=ikename), + self.ipsecpolicy(name=ipsecname), + self.subnet(cidr='10.2.0.0/24'), + self.router()) as ( + ikepolicy, ipsecpolicy, subnet, router): + with self.vpnservice(name=vpnsname, subnet=subnet, + router=router) as vpnservice1: + keys['vpnservice_id'] = vpnservice1['vpnservice']['id'] + keys['ikepolicy_id'] = ikepolicy['ikepolicy']['id'] + keys['ipsecpolicy_id'] = ipsecpolicy['ipsecpolicy']['id'] + with self.ipsec_site_connection( + self.fmt, + name, + keys['peer_address'], + keys['peer_id'], + keys['peer_cidrs'], + keys['mtu'], + keys['psk'], + keys['initiator'], + dpd['action'], + dpd['interval'], + dpd['timeout'], + vpnservice1, + ikepolicy, + ipsecpolicy, + keys['admin_state_up'], + description=description + ) as ipsec_site_connection: + data = {'ipsec_site_connection': { + 'peer_cidrs': ['192.168.2.0/24', + '192.168.3.0/24'] + }} + self._set_active( + vpn_db.IPsecSiteConnection, + ipsec_site_connection['ipsec_site_connection']['id']) + req = self.new_update_request( + 'ipsec-site-connections', + data, + ipsec_site_connection[ + 'ipsec_site_connection']['id'] + ) + res = self.deserialize( + self.fmt, + req.get_response(self.ext_api) + ) + self._check_ipsec_site_connection( + res['ipsec_site_connection'], + keys, + dpd) + + def test_show_ipsec_site_connection(self): + """Test case to show a ipsec_site_connection.""" + ikename = "ikepolicy1" + ipsecname = "ipsecpolicy1" + vpnsname = "vpnservice1" + name = "connection1" + description = "my-ipsec-connection" + keys = {'name': name, + 'description': "my-ipsec-connection", + 'peer_address': '192.168.1.10', + 'peer_id': '192.168.1.10', + 'peer_cidrs': ['192.168.2.0/24', '192.168.3.0/24'], + 'initiator': 'bi-directional', + 'mtu': 1500, + 'tenant_id': self._tenant_id, + 'psk': 'abcd', + 'status': 'PENDING_CREATE', + 'admin_state_up': True} + dpd = {'action': 'hold', + 'interval': 40, + 'timeout': 120} + with contextlib.nested( + self.ikepolicy(name=ikename), + self.ipsecpolicy(name=ipsecname), + self.subnet(), + self.router()) as ( + ikepolicy, ipsecpolicy, subnet, router): + with self.vpnservice(name=vpnsname, subnet=subnet, + router=router) as vpnservice1: + keys['ikepolicy_id'] = ikepolicy['ikepolicy']['id'] + keys['ipsecpolicy_id'] = ipsecpolicy['ipsecpolicy']['id'] + keys['vpnservice_id'] = vpnservice1['vpnservice']['id'] + with self.ipsec_site_connection( + self.fmt, + name, + keys['peer_address'], + keys['peer_id'], + keys['peer_cidrs'], + keys['mtu'], + keys['psk'], + keys['initiator'], + dpd['action'], + dpd['interval'], + dpd['timeout'], + vpnservice1, + ikepolicy, + ipsecpolicy, + keys['admin_state_up'], + description=description, + ) as ipsec_site_connection: + + req = self.new_show_request( + 'ipsec-site-connections', + ipsec_site_connection[ + 'ipsec_site_connection']['id'], + fmt=self.fmt + ) + res = self.deserialize( + self.fmt, + req.get_response(self.ext_api) + ) + + self._check_ipsec_site_connection( + res['ipsec_site_connection'], + keys, + dpd) + + def test_list_ipsec_site_connections_with_sort_emulated(self): + """Test case to list all ipsec_site_connections with sort.""" + with self.subnet(cidr='10.2.0.0/24') as subnet: + with self.router() as router: + with self.vpnservice(subnet=subnet, + router=router + ) as vpnservice: + with contextlib.nested( + self.ipsec_site_connection( + name='connection1', vpnservice=vpnservice + ), + self.ipsec_site_connection( + name='connection2', vpnservice=vpnservice + ), + self.ipsec_site_connection( + name='connection3', vpnservice=vpnservice + ) + ) as(ipsec_site_connection1, + ipsec_site_connection2, + ipsec_site_connection3): + self._test_list_with_sort('ipsec-site-connection', + (ipsec_site_connection3, + ipsec_site_connection2, + ipsec_site_connection1), + [('name', 'desc')]) + + def test_list_ipsec_site_connections_with_pagination_emulated(self): + """Test case to list all ipsec_site_connections with pagination.""" + with self.subnet(cidr='10.2.0.0/24') as subnet: + with self.router() as router: + with self.vpnservice(subnet=subnet, + router=router + ) as vpnservice: + with contextlib.nested( + self.ipsec_site_connection( + name='ipsec_site_connection1', + vpnservice=vpnservice + ), + self.ipsec_site_connection( + name='ipsec_site_connection1', + vpnservice=vpnservice + ), + self.ipsec_site_connection( + name='ipsec_site_connection1', + vpnservice=vpnservice + ) + ) as(ipsec_site_connection1, + ipsec_site_connection2, + ipsec_site_connection3): + self._test_list_with_pagination( + 'ipsec-site-connection', + (ipsec_site_connection1, + ipsec_site_connection2, + ipsec_site_connection3), + ('name', 'asc'), 2, 2) + + def test_list_ipsec_site_conns_with_pagination_reverse_emulated(self): + """Test to list all ipsec_site_connections with reverse pagination.""" + with self.subnet(cidr='10.2.0.0/24') as subnet: + with self.router() as router: + with self.vpnservice(subnet=subnet, + router=router + ) as vpnservice: + with contextlib.nested( + self.ipsec_site_connection( + name='connection1', vpnservice=vpnservice + ), + self.ipsec_site_connection( + name='connection2', vpnservice=vpnservice + ), + self.ipsec_site_connection( + name='connection3', vpnservice=vpnservice + ) + ) as(ipsec_site_connection1, + ipsec_site_connection2, + ipsec_site_connection3): + self._test_list_with_pagination_reverse( + 'ipsec-site-connection', + (ipsec_site_connection1, + ipsec_site_connection2, + ipsec_site_connection3), + ('name', 'asc'), 2, 2 + ) + + def test_create_vpn(self): + """Test case to create a vpn.""" + vpns_name = "vpnservice1" + ike_name = "ikepolicy1" + ipsec_name = "ipsecpolicy1" + name1 = "ipsec_site_connection1" + with contextlib.nested( + self.ikepolicy(name=ike_name), + self.ipsecpolicy(name=ipsec_name), + self.vpnservice(name=vpns_name)) as ( + ikepolicy, ipsecpolicy, vpnservice): + vpnservice_id = vpnservice['vpnservice']['id'] + ikepolicy_id = ikepolicy['ikepolicy']['id'] + ipsecpolicy_id = ipsecpolicy['ipsecpolicy']['id'] + with self.ipsec_site_connection( + self.fmt, + name1, + '192.168.1.10', + '192.168.1.10', + ['192.168.2.0/24', + '192.168.3.0/24'], + 1500, + 'abcdef', + 'bi-directional', + 'hold', + 30, + 120, + vpnservice, + ikepolicy, + ipsecpolicy, + True + ) as vpnconn1: + + vpnservice_req = self.new_show_request( + 'vpnservices', + vpnservice_id, + fmt=self.fmt) + vpnservice_updated = self.deserialize( + self.fmt, + vpnservice_req.get_response(self.ext_api) + ) + self.assertEqual( + vpnservice_updated['vpnservice']['id'], + vpnconn1['ipsec_site_connection']['vpnservice_id'] + ) + ikepolicy_req = self.new_show_request('ikepolicies', + ikepolicy_id, + fmt=self.fmt) + ikepolicy_res = self.deserialize( + self.fmt, + ikepolicy_req.get_response(self.ext_api) + ) + self.assertEqual( + ikepolicy_res['ikepolicy']['id'], + vpnconn1['ipsec_site_connection']['ikepolicy_id']) + ipsecpolicy_req = self.new_show_request( + 'ipsecpolicies', + ipsecpolicy_id, + fmt=self.fmt) + ipsecpolicy_res = self.deserialize( + self.fmt, + ipsecpolicy_req.get_response(self.ext_api) + ) + self.assertEqual( + ipsecpolicy_res['ipsecpolicy']['id'], + vpnconn1['ipsec_site_connection']['ipsecpolicy_id'] + ) + + def test_delete_ikepolicy_inuse(self): + """Test case to delete an ikepolicy, that is in use.""" + vpns_name = "vpnservice1" + ike_name = "ikepolicy1" + ipsec_name = "ipsecpolicy1" + name1 = "ipsec_site_connection1" + with self.ikepolicy(name=ike_name) as ikepolicy: + with self.ipsecpolicy(name=ipsec_name) as ipsecpolicy: + with self.vpnservice(name=vpns_name) as vpnservice: + with self.ipsec_site_connection( + self.fmt, + name1, + '192.168.1.10', + '192.168.1.10', + ['192.168.2.0/24', + '192.168.3.0/24'], + 1500, + 'abcdef', + 'bi-directional', + 'hold', + 30, + 120, + vpnservice, + ikepolicy, + ipsecpolicy, + True + ): + delete_req = self.new_delete_request( + 'ikepolicies', + ikepolicy['ikepolicy']['id'] + ) + delete_res = delete_req.get_response(self.ext_api) + self.assertEqual(409, delete_res.status_int) + + def test_delete_ipsecpolicy_inuse(self): + """Test case to delete an ipsecpolicy, that is in use.""" + vpns_name = "vpnservice1" + ike_name = "ikepolicy1" + ipsec_name = "ipsecpolicy1" + name1 = "ipsec_site_connection1" + with self.ikepolicy(name=ike_name) as ikepolicy: + with self.ipsecpolicy(name=ipsec_name) as ipsecpolicy: + with self.vpnservice(name=vpns_name) as vpnservice: + with self.ipsec_site_connection( + self.fmt, + name1, + '192.168.1.10', + '192.168.1.10', + ['192.168.2.0/24', + '192.168.3.0/24'], + 1500, + 'abcdef', + 'bi-directional', + 'hold', + 30, + 120, + vpnservice, + ikepolicy, + ipsecpolicy, + True + ): + + delete_req = self.new_delete_request( + 'ipsecpolicies', + ipsecpolicy['ipsecpolicy']['id'] + ) + delete_res = delete_req.get_response(self.ext_api) + self.assertEqual(409, delete_res.status_int) + + +class TestVpnaasXML(TestVpnaas): + fmt = 'xml' diff --git a/neutron/tests/unit/services/vpn/__init__.py b/neutron/tests/unit/services/vpn/__init__.py new file mode 100644 index 000000000..b936bbcb8 --- /dev/null +++ b/neutron/tests/unit/services/vpn/__init__.py @@ -0,0 +1,17 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. +# All Rights Reserved. +# +# 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. +# @author: Swaminathan Vasudevan, Hewlett-Packard. diff --git a/neutron/tests/unit/services/vpn/test_vpnaas_extension.py b/neutron/tests/unit/services/vpn/test_vpnaas_extension.py new file mode 100644 index 000000000..d1f895453 --- /dev/null +++ b/neutron/tests/unit/services/vpn/test_vpnaas_extension.py @@ -0,0 +1,592 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. +# All Rights Reserved. +# +# 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. +# +# @author: Swaminathan Vasudevan, Hewlett-Packard. + +import copy + +import mock +from oslo.config import cfg +from webob import exc +import webtest + +from neutron.api import extensions +from neutron.api.v2 import attributes +from neutron.common import config +from neutron.extensions import vpnaas +from neutron import manager +from neutron.openstack.common import uuidutils +from neutron.plugins.common import constants +from neutron.tests.unit import test_api_v2 +from neutron.tests.unit import test_extensions +from neutron.tests.unit import testlib_api + + +_uuid = uuidutils.generate_uuid +_get_path = test_api_v2._get_path + + +class VpnaasTestExtensionManager(object): + + def get_resources(self): + # Add the resources to the global attribute map + # This is done here as the setup process won't + # initialize the main API router which extends + # the global attribute map + attributes.RESOURCE_ATTRIBUTE_MAP.update( + vpnaas.RESOURCE_ATTRIBUTE_MAP) + return vpnaas.Vpnaas.get_resources() + + def get_actions(self): + return [] + + def get_request_extensions(self): + return [] + + +class VpnaasExtensionTestCase(testlib_api.WebTestCase): + fmt = 'json' + + def setUp(self): + super(VpnaasExtensionTestCase, self).setUp() + plugin = 'neutron.extensions.vpnaas.VPNPluginBase' + # Ensure 'stale' patched copies of the plugin are never returned + manager.NeutronManager._instance = None + + # Ensure existing ExtensionManager is not used + extensions.PluginAwareExtensionManager._instance = None + + # Create the default configurations + args = ['--config-file', test_api_v2.etcdir('neutron.conf.test')] + config.parse(args) + + #just stubbing core plugin with LoadBalancer plugin + cfg.CONF.set_override('core_plugin', plugin) + cfg.CONF.set_override('service_plugins', [plugin]) + + self._plugin_patcher = mock.patch(plugin, autospec=True) + self.plugin = self._plugin_patcher.start() + instance = self.plugin.return_value + instance.get_plugin_type.return_value = constants.VPN + + ext_mgr = VpnaasTestExtensionManager() + self.ext_mdw = test_extensions.setup_extensions_middleware(ext_mgr) + self.api = webtest.TestApp(self.ext_mdw) + super(VpnaasExtensionTestCase, self).setUp() + + def tearDown(self): + self._plugin_patcher.stop() + self.api = None + self.plugin = None + cfg.CONF.reset() + super(VpnaasExtensionTestCase, self).tearDown() + + def test_ikepolicy_create(self): + """Test case to create an ikepolicy.""" + ikepolicy_id = _uuid() + data = {'ikepolicy': {'name': 'ikepolicy1', + 'description': 'myikepolicy1', + 'auth_algorithm': 'sha1', + 'encryption_algorithm': 'aes-128', + 'phase1_negotiation_mode': 'main', + 'lifetime': { + 'units': 'seconds', + 'value': 3600}, + 'ike_version': 'v1', + 'pfs': 'group5', + 'tenant_id': _uuid()}} + + return_value = copy.copy(data['ikepolicy']) + return_value.update({'id': ikepolicy_id}) + + instance = self.plugin.return_value + instance.create_ikepolicy.return_value = return_value + res = self.api.post(_get_path('vpn/ikepolicies', fmt=self.fmt), + self.serialize(data), + content_type='application/%s' % self.fmt) + instance.create_ikepolicy.assert_called_with(mock.ANY, + ikepolicy=data) + self.assertEqual(res.status_int, exc.HTTPCreated.code) + res = self.deserialize(res) + self.assertIn('ikepolicy', res) + self.assertEqual(res['ikepolicy'], return_value) + + def test_ikepolicy_list(self): + """Test case to list all ikepolicies.""" + ikepolicy_id = _uuid() + return_value = [{'name': 'ikepolicy1', + 'auth_algorithm': 'sha1', + 'encryption_algorithm': 'aes-128', + 'pfs': 'group5', + 'ike_version': 'v1', + 'id': ikepolicy_id}] + + instance = self.plugin.return_value + instance.get_ikepolicies.return_value = return_value + + res = self.api.get(_get_path('vpn/ikepolicies', fmt=self.fmt)) + + instance.get_ikepolicies.assert_called_with(mock.ANY, + fields=mock.ANY, + filters=mock.ANY) + self.assertEqual(res.status_int, exc.HTTPOk.code) + + def test_ikepolicy_update(self): + """Test case to update an ikepolicy.""" + ikepolicy_id = _uuid() + update_data = {'ikepolicy': {'name': 'ikepolicy1', + 'encryption_algorithm': 'aes-256'}} + return_value = {'name': 'ikepolicy1', + 'auth_algorithm': 'sha1', + 'encryption_algorithm': 'aes-256', + 'phase1_negotiation_mode': 'main', + 'lifetime': { + 'units': 'seconds', + 'value': 3600}, + 'ike_version': 'v1', + 'pfs': 'group5', + 'tenant_id': _uuid(), + 'id': ikepolicy_id} + + instance = self.plugin.return_value + instance.update_ikepolicy.return_value = return_value + + res = self.api.put(_get_path('vpn/ikepolicies', id=ikepolicy_id, + fmt=self.fmt), + self.serialize(update_data)) + + instance.update_ikepolicy.assert_called_with(mock.ANY, ikepolicy_id, + ikepolicy=update_data) + self.assertEqual(res.status_int, exc.HTTPOk.code) + res = self.deserialize(res) + self.assertIn('ikepolicy', res) + self.assertEqual(res['ikepolicy'], return_value) + + def test_ikepolicy_get(self): + """Test case to get or show an ikepolicy.""" + ikepolicy_id = _uuid() + return_value = {'name': 'ikepolicy1', + 'auth_algorithm': 'sha1', + 'encryption_algorithm': 'aes-128', + 'phase1_negotiation_mode': 'main', + 'lifetime': { + 'units': 'seconds', + 'value': 3600}, + 'ike_version': 'v1', + 'pfs': 'group5', + 'tenant_id': _uuid(), + 'id': ikepolicy_id} + + instance = self.plugin.return_value + instance.get_ikepolicy.return_value = return_value + + res = self.api.get(_get_path('vpn/ikepolicies', id=ikepolicy_id, + fmt=self.fmt)) + + instance.get_ikepolicy.assert_called_with(mock.ANY, + ikepolicy_id, + fields=mock.ANY) + self.assertEqual(res.status_int, exc.HTTPOk.code) + res = self.deserialize(res) + self.assertIn('ikepolicy', res) + self.assertEqual(res['ikepolicy'], return_value) + + def test_ikepolicy_delete(self): + """Test case to delete an ikepolicy.""" + self._test_entity_delete('ikepolicy') + + def test_ipsecpolicy_create(self): + """Test case to create an ipsecpolicy.""" + ipsecpolicy_id = _uuid() + data = {'ipsecpolicy': {'name': 'ipsecpolicy1', + 'description': 'myipsecpolicy1', + 'auth_algorithm': 'sha1', + 'encryption_algorithm': 'aes-128', + 'encapsulation_mode': 'tunnel', + 'lifetime': { + 'units': 'seconds', + 'value': 3600}, + 'transform_protocol': 'esp', + 'pfs': 'group5', + 'tenant_id': _uuid()}} + return_value = copy.copy(data['ipsecpolicy']) + return_value.update({'id': ipsecpolicy_id}) + + instance = self.plugin.return_value + instance.create_ipsecpolicy.return_value = return_value + res = self.api.post(_get_path('vpn/ipsecpolicies', fmt=self.fmt), + self.serialize(data), + content_type='application/%s' % self.fmt) + instance.create_ipsecpolicy.assert_called_with(mock.ANY, + ipsecpolicy=data) + self.assertEqual(res.status_int, exc.HTTPCreated.code) + res = self.deserialize(res) + self.assertIn('ipsecpolicy', res) + self.assertEqual(res['ipsecpolicy'], return_value) + + def test_ipsecpolicy_list(self): + """Test case to list an ipsecpolicy.""" + ipsecpolicy_id = _uuid() + return_value = [{'name': 'ipsecpolicy1', + 'auth_algorithm': 'sha1', + 'encryption_algorithm': 'aes-128', + 'pfs': 'group5', + 'id': ipsecpolicy_id}] + + instance = self.plugin.return_value + instance.get_ipsecpolicies.return_value = return_value + + res = self.api.get(_get_path('vpn/ipsecpolicies', fmt=self.fmt)) + + instance.get_ipsecpolicies.assert_called_with(mock.ANY, + fields=mock.ANY, + filters=mock.ANY) + self.assertEqual(res.status_int, exc.HTTPOk.code) + + def test_ipsecpolicy_update(self): + """Test case to update an ipsecpolicy.""" + ipsecpolicy_id = _uuid() + update_data = {'ipsecpolicy': {'name': 'ipsecpolicy1', + 'encryption_algorithm': 'aes-256'}} + return_value = {'name': 'ipsecpolicy1', + 'auth_algorithm': 'sha1', + 'encryption_algorithm': 'aes-128', + 'encapsulation_mode': 'tunnel', + 'lifetime': { + 'units': 'seconds', + 'value': 3600}, + 'transform_protocol': 'esp', + 'pfs': 'group5', + 'tenant_id': _uuid(), + 'id': ipsecpolicy_id} + + instance = self.plugin.return_value + instance.update_ipsecpolicy.return_value = return_value + + res = self.api.put(_get_path('vpn/ipsecpolicies', + id=ipsecpolicy_id, + fmt=self.fmt), + self.serialize(update_data)) + + instance.update_ipsecpolicy.assert_called_with(mock.ANY, + ipsecpolicy_id, + ipsecpolicy=update_data) + self.assertEqual(res.status_int, exc.HTTPOk.code) + res = self.deserialize(res) + self.assertIn('ipsecpolicy', res) + self.assertEqual(res['ipsecpolicy'], return_value) + + def test_ipsecpolicy_get(self): + """Test case to get or show an ipsecpolicy.""" + ipsecpolicy_id = _uuid() + return_value = {'name': 'ipsecpolicy1', + 'auth_algorithm': 'sha1', + 'encryption_algorithm': 'aes-128', + 'encapsulation_mode': 'tunnel', + 'lifetime': { + 'units': 'seconds', + 'value': 3600}, + 'transform_protocol': 'esp', + 'pfs': 'group5', + 'tenant_id': _uuid(), + 'id': ipsecpolicy_id} + + instance = self.plugin.return_value + instance.get_ipsecpolicy.return_value = return_value + + res = self.api.get(_get_path('vpn/ipsecpolicies', + id=ipsecpolicy_id, + fmt=self.fmt)) + + instance.get_ipsecpolicy.assert_called_with(mock.ANY, + ipsecpolicy_id, + fields=mock.ANY) + self.assertEqual(res.status_int, exc.HTTPOk.code) + res = self.deserialize(res) + self.assertIn('ipsecpolicy', res) + self.assertEqual(res['ipsecpolicy'], return_value) + + def test_ipsecpolicy_delete(self): + """Test case to delete an ipsecpolicy.""" + self._test_entity_delete('ipsecpolicy') + + def test_vpnservice_create(self): + """Test case to create a vpnservice.""" + vpnservice_id = _uuid() + data = {'vpnservice': {'name': 'vpnservice1', + 'description': 'descr_vpn1', + 'subnet_id': _uuid(), + 'router_id': _uuid(), + 'admin_state_up': True, + 'tenant_id': _uuid()}} + return_value = copy.copy(data['vpnservice']) + return_value.update({'status': "ACTIVE", 'id': vpnservice_id}) + + instance = self.plugin.return_value + instance.create_vpnservice.return_value = return_value + res = self.api.post(_get_path('vpn/vpnservices', fmt=self.fmt), + self.serialize(data), + content_type='application/%s' % self.fmt) + instance.create_vpnservice.assert_called_with(mock.ANY, + vpnservice=data) + self.assertEqual(res.status_int, exc.HTTPCreated.code) + res = self.deserialize(res) + self.assertIn('vpnservice', res) + self.assertEqual(res['vpnservice'], return_value) + + def test_vpnservice_list(self): + """Test case to list all vpnservices.""" + vpnservice_id = _uuid() + return_value = [{'name': 'vpnservice1', + 'tenant_id': _uuid(), + 'status': 'ACTIVE', + 'id': vpnservice_id}] + + instance = self.plugin.return_value + instance.get_vpnservice.return_value = return_value + + res = self.api.get(_get_path('vpn/vpnservices', fmt=self.fmt)) + + instance.get_vpnservices.assert_called_with(mock.ANY, + fields=mock.ANY, + filters=mock.ANY) + self.assertEqual(res.status_int, exc.HTTPOk.code) + + def test_vpnservice_update(self): + """Test case to update a vpnservice.""" + vpnservice_id = _uuid() + update_data = {'vpnservice': {'admin_state_up': False}} + return_value = {'name': 'vpnservice1', + 'admin_state_up': False, + 'subnet_id': _uuid(), + 'router_id': _uuid(), + 'tenant_id': _uuid(), + 'status': "ACTIVE", + 'id': vpnservice_id} + + instance = self.plugin.return_value + instance.update_vpnservice.return_value = return_value + + res = self.api.put(_get_path('vpn/vpnservices', + id=vpnservice_id, + fmt=self.fmt), + self.serialize(update_data)) + + instance.update_vpnservice.assert_called_with(mock.ANY, + vpnservice_id, + vpnservice=update_data) + self.assertEqual(res.status_int, exc.HTTPOk.code) + res = self.deserialize(res) + self.assertIn('vpnservice', res) + self.assertEqual(res['vpnservice'], return_value) + + def test_vpnservice_get(self): + """Test case to get or show a vpnservice.""" + vpnservice_id = _uuid() + return_value = {'name': 'vpnservice1', + 'admin_state_up': True, + 'subnet_id': _uuid(), + 'router_id': _uuid(), + 'tenant_id': _uuid(), + 'status': "ACTIVE", + 'id': vpnservice_id} + + instance = self.plugin.return_value + instance.get_vpnservice.return_value = return_value + + res = self.api.get(_get_path('vpn/vpnservices', + id=vpnservice_id, + fmt=self.fmt)) + + instance.get_vpnservice.assert_called_with(mock.ANY, + vpnservice_id, + fields=mock.ANY) + self.assertEqual(res.status_int, exc.HTTPOk.code) + res = self.deserialize(res) + self.assertIn('vpnservice', res) + self.assertEqual(res['vpnservice'], return_value) + + def _test_entity_delete(self, entity): + """does the entity deletion based on naming convention.""" + entity_id = _uuid() + path_map = {'ipsecpolicy': 'vpn/ipsecpolicies', + 'ikepolicy': 'vpn/ikepolicies', + 'ipsec_site_connection': 'vpn/ipsec-site-connections'} + path = path_map.get(entity, 'vpn/' + entity + 's') + res = self.api.delete(_get_path(path, + id=entity_id, + fmt=self.fmt)) + delete_entity = getattr(self.plugin.return_value, "delete_" + entity) + delete_entity.assert_called_with(mock.ANY, entity_id) + self.assertEqual(res.status_int, exc.HTTPNoContent.code) + + def test_vpnservice_delete(self): + """Test case to delete a vpnservice.""" + self._test_entity_delete('vpnservice') + + def test_ipsec_site_connection_create(self): + """Test case to create a ipsec_site_connection.""" + ipsecsite_con_id = _uuid() + ikepolicy_id = _uuid() + ipsecpolicy_id = _uuid() + data = { + 'ipsec_site_connection': {'name': 'connection1', + 'description': 'Remote-connection1', + 'peer_address': '192.168.1.10', + 'peer_id': '192.168.1.10', + 'peer_cidrs': ['192.168.2.0/24', + '192.168.3.0/24'], + 'mtu': 1500, + 'psk': 'abcd', + 'initiator': 'bi-directional', + 'dpd': { + 'action': 'hold', + 'interval': 30, + 'timeout': 120}, + 'ikepolicy_id': ikepolicy_id, + 'ipsecpolicy_id': ipsecpolicy_id, + 'vpnservice_id': _uuid(), + 'admin_state_up': True, + 'tenant_id': _uuid()} + } + return_value = copy.copy(data['ipsec_site_connection']) + return_value.update({'status': "ACTIVE", 'id': ipsecsite_con_id}) + + instance = self.plugin.return_value + instance.create_ipsec_site_connection.return_value = return_value + res = self.api.post(_get_path('vpn/ipsec-site-connections', + fmt=self.fmt), + self.serialize(data), + content_type='application/%s' % self.fmt) + instance.create_ipsec_site_connection.assert_called_with( + mock.ANY, ipsec_site_connection=data + ) + self.assertEqual(res.status_int, exc.HTTPCreated.code) + res = self.deserialize(res) + self.assertIn('ipsec_site_connection', res) + self.assertEqual(res['ipsec_site_connection'], return_value) + + def test_ipsec_site_connection_list(self): + """Test case to list all ipsec_site_connections.""" + ipsecsite_con_id = _uuid() + return_value = [{'name': 'connection1', + 'peer_address': '192.168.1.10', + 'peer_cidrs': ['192.168.2.0/24', '192.168.3.0/24'], + 'route_mode': 'static', + 'auth_mode': 'psk', + 'tenant_id': _uuid(), + 'status': 'ACTIVE', + 'id': ipsecsite_con_id}] + + instance = self.plugin.return_value + instance.get_ipsec_site_connections.return_value = return_value + + res = self.api.get( + _get_path('vpn/ipsec-site-connections', fmt=self.fmt)) + + instance.get_ipsec_site_connections.assert_called_with( + mock.ANY, fields=mock.ANY, filters=mock.ANY + ) + self.assertEqual(res.status_int, exc.HTTPOk.code) + + def test_ipsec_site_connection_update(self): + """Test case to update a ipsec_site_connection.""" + ipsecsite_con_id = _uuid() + update_data = {'ipsec_site_connection': {'admin_state_up': False}} + return_value = {'name': 'connection1', + 'description': 'Remote-connection1', + 'peer_address': '192.168.1.10', + 'peer_id': '192.168.1.10', + 'peer_cidrs': ['192.168.2.0/24', '192.168.3.0/24'], + 'mtu': 1500, + 'psk': 'abcd', + 'initiator': 'bi-directional', + 'dpd': { + 'action': 'hold', + 'interval': 30, + 'timeout': 120}, + 'ikepolicy_id': _uuid(), + 'ipsecpolicy_id': _uuid(), + 'vpnservice_id': _uuid(), + 'admin_state_up': False, + 'tenant_id': _uuid(), + 'status': 'ACTIVE', + 'id': ipsecsite_con_id} + + instance = self.plugin.return_value + instance.update_ipsec_site_connection.return_value = return_value + + res = self.api.put(_get_path('vpn/ipsec-site-connections', + id=ipsecsite_con_id, + fmt=self.fmt), + self.serialize(update_data)) + + instance.update_ipsec_site_connection.assert_called_with( + mock.ANY, ipsecsite_con_id, ipsec_site_connection=update_data + ) + + self.assertEqual(res.status_int, exc.HTTPOk.code) + res = self.deserialize(res) + self.assertIn('ipsec_site_connection', res) + self.assertEqual(res['ipsec_site_connection'], return_value) + + def test_ipsec_site_connection_get(self): + """Test case to get or show a ipsec_site_connection.""" + ipsecsite_con_id = _uuid() + return_value = {'name': 'connection1', + 'description': 'Remote-connection1', + 'peer_address': '192.168.1.10', + 'peer_id': '192.168.1.10', + 'peer_cidrs': ['192.168.2.0/24', + '192.168.3.0/24'], + 'mtu': 1500, + 'psk': 'abcd', + 'initiator': 'bi-directional', + 'dpd': { + 'action': 'hold', + 'interval': 30, + 'timeout': 120}, + 'ikepolicy_id': _uuid(), + 'ipsecpolicy_id': _uuid(), + 'vpnservice_id': _uuid(), + 'admin_state_up': True, + 'tenant_id': _uuid(), + 'status': 'ACTIVE', + 'id': ipsecsite_con_id} + + instance = self.plugin.return_value + instance.get_ipsec_site_connection.return_value = return_value + + res = self.api.get(_get_path('vpn/ipsec-site-connections', + id=ipsecsite_con_id, + fmt=self.fmt)) + + instance.get_ipsec_site_connection.assert_called_with( + mock.ANY, ipsecsite_con_id, fields=mock.ANY + ) + self.assertEqual(res.status_int, exc.HTTPOk.code) + res = self.deserialize(res) + self.assertIn('ipsec_site_connection', res) + self.assertEqual(res['ipsec_site_connection'], return_value) + + def test_ipsec_site_connection_delete(self): + """Test case to delete a ipsec_site_connection.""" + self._test_entity_delete('ipsec_site_connection') + + +class VpnaasExtensionTestCaseXML(VpnaasExtensionTestCase): + fmt = 'xml'