Merge "Implementation of Brocade Vyatta VPNaaS Plugin"

This commit is contained in:
Jenkins 2015-02-19 02:54:27 +00:00 committed by Gerrit Code Review
commit dfad4a1969
11 changed files with 920 additions and 0 deletions

View File

@ -8,4 +8,6 @@
service_provider=VPN:openswan:neutron_vpnaas.services.vpn.service_drivers.ipsec.IPsecVPNDriver:default
# Uncomment the following line (and comment out the OpenSwan VPN line) to enable Cisco's VPN driver.
# service_provider=VPN:cisco:neutron_vpnaas.services.vpn.service_drivers.cisco_ipsec.CiscoCsrIPsecVPNDriver:default
# Uncomment the following line (and comment out the OpenSwan VPN line) to enable Brocade Vyatta's VPN driver.
# service_provider=VPN:vyatta:neutron_vpnaas.services.vpn.service_drivers.vyatta_ipsec.VyattaIPsecDriver:default

View File

@ -7,6 +7,7 @@
# If we want to use multiple drivers, we need to define this option multiple times.
# vpn_device_driver=neutron.services.vpn.device_drivers.ipsec.OpenSwanDriver
# vpn_device_driver=neutron.services.vpn.device_drivers.cisco_ipsec.CiscoCsrIPsecDriver
# vpn_device_driver=neutron.services.vpn.device_drivers.vyatta_ipsec.VyattaIPSecDriver
# vpn_device_driver=another_driver
[ipsec]

View File

@ -18,3 +18,5 @@ IPSEC_DRIVER_TOPIC = 'ipsec_driver'
IPSEC_AGENT_TOPIC = 'ipsec_agent'
CISCO_IPSEC_DRIVER_TOPIC = 'cisco_csr_ipsec_driver'
CISCO_IPSEC_AGENT_TOPIC = 'cisco_csr_ipsec_agent'
BROCADE_IPSEC_DRIVER_TOPIC = 'brocade_vyatta_ipsec_driver'
BROCADE_IPSEC_AGENT_TOPIC = 'brocade_vyatta_ipsec_agent'

View File

@ -0,0 +1,308 @@
# Copyright 2015 Brocade Communications System, Inc.
# 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.
import collections
import logging
import oslo_messaging as messaging
import pprint
import six
from neutron.common import rpc as n_rpc
from neutron import context as n_ctx
from neutron.i18n import _LE, _LW
from neutron.openstack.common import loopingcall
from neutron.openstack.common import periodic_task
from vyatta.common import exceptions as v_exc
from vyatta.common import vrouter_config
from vyatta.vpn import config as vyatta_vpn_config
from neutron_vpnaas.services.vpn.common import topics
from neutron_vpnaas.services.vpn import device_drivers
LOG = logging.getLogger(__name__)
_KEY_CONNECTIONS = 'ipsec_site_connections'
_KEY_IKEPOLICY = 'ikepolicy'
_KEY_ESPPOLICY = 'ipsecpolicy'
class _DriverRPCEndpoint(object):
"""
VPN device driver RPC endpoint (server > agent)
history
1.0 Initial version
"""
target = messaging.Target(version='1.0')
def __init__(self, driver):
self.driver = driver
def vpnservice_updated(self, context, **kwargs):
self.driver.sync(context, [])
class NeutronServerAPI(object):
"""
VPN service driver RPC endpoint (agent > server)
"""
def __init__(self, topic):
target = messaging.Target(topic=topic, version='1.0')
self.client = n_rpc.get_client(target)
def get_vpn_services_on_host(self, context, host):
# make RPC call to neutron server
cctxt = self.client.prepare()
data = cctxt.call(context, 'get_vpn_services_on_host', host=host)
vpn_services = list()
for svc in data:
try:
for conn in svc[_KEY_CONNECTIONS]:
vyatta_vpn_config.validate_svc_connection(conn)
except v_exc.InvalidVPNServiceError:
LOG.error(_LE('Invalid or incomplete VPN service data: '
'id={id}').format(id=svc.get('id')))
continue
vpn_services.append(svc)
# return transformed data to caller
return vpn_services
def update_status(self, context, status):
cctxt = self.client.prepare()
cctxt.cast(context, 'update_status', status=status)
class VyattaIPSecDriver(device_drivers.DeviceDriver):
"""
Vyatta VPN device driver
"""
rpc_endpoint_factory = _DriverRPCEndpoint
def __init__(self, vpn_service, host):
super(VyattaIPSecDriver, self).__init__(vpn_service, host)
self.vpn_service = vpn_service
self.host = host
# register RPC endpoint
conn = n_rpc.create_connection(new=True)
node_topic = '%s.%s' % (topics.BROCADE_IPSEC_AGENT_TOPIC,
self.host)
endpoints = [self.rpc_endpoint_factory(self)]
conn.create_consumer(node_topic, endpoints, fanout=False)
conn.consume_in_threads()
# initialize agent ot server RPC link
self.server_api = NeutronServerAPI(
topics.BROCADE_IPSEC_DRIVER_TOPIC)
# initialize VPN service cache (to keep service state)
self._svc_cache = list()
self._router_resources_cache = dict()
# setup periodic task. All periodic task require fully configured
# device driver. It will be called asynchronously, and soon, so it
# should be last, when all configuration is done.
self._periodic_tasks = periodic = _VyattaPeriodicTasks(self)
loop = loopingcall.DynamicLoopingCall(periodic)
loop.start(initial_delay=5)
def sync(self, context, processes):
"""
Called by _DriverRPCEndpoint instance.
"""
svc_update = self.server_api.get_vpn_services_on_host(
context, self.host)
to_del, to_change, to_add = self._svc_diff(
self._svc_cache, svc_update)
for svc in to_del:
resources = self.get_router_resources(svc['router_id'])
self._svc_delete(svc, resources)
for old, new in to_change:
resources = self.get_router_resources(old['router_id'])
self._svc_delete(old, resources)
self._svc_add(new, resources)
for svc in to_add:
resources = self.get_router_resources(svc['router_id'])
self._svc_add(svc, resources)
self._svc_cache = svc_update
def create_router(self, router_id):
vrouter = self.vpn_service.get_router_client(router_id)
config_raw = vrouter.get_vrouter_configuration()
resources = self.get_router_resources(router_id)
with resources.make_patch() as patch:
vrouter_svc = vyatta_vpn_config.parse_vrouter_config(
vrouter_config.parse_config(config_raw), patch)
for svc in vrouter_svc:
svc['router_id'] = router_id
self._svc_cache.extend(vrouter_svc)
def destroy_router(self, router_id):
to_del = list()
for idx, svc in enumerate(self._svc_cache):
if svc['router_id'] != router_id:
continue
resources = self.get_router_resources(svc['router_id'])
self._svc_delete(svc, resources)
to_del.insert(0, idx)
for idx in to_del:
del self._svc_cache[idx]
def _svc_add(self, svc, resources):
vrouter = self.vpn_service.get_router_client(svc['router_id'])
for conn in svc[_KEY_CONNECTIONS]:
with resources.make_patch() as patch:
iface = self._get_router_gw_iface(vrouter, svc['router_id'])
batch = vyatta_vpn_config.connect_setup_commands(
vrouter, iface, svc, conn, patch)
vrouter.exec_cmd_batch(batch)
def _svc_delete(self, svc, resources):
vrouter = self.vpn_service.get_router_client(svc['router_id'])
for conn in svc[_KEY_CONNECTIONS]:
with resources.make_patch() as patch:
iface = self._get_router_gw_iface(vrouter, svc['router_id'])
batch = vyatta_vpn_config.connect_remove_commands(
vrouter, iface, svc, conn, patch)
vrouter.exec_cmd_batch(batch)
def _svc_diff(self, svc_old, svc_new):
state_key = 'admin_state_up'
old_idnr = set(x['id'] for x in svc_old)
new_idnr = set(x['id'] for x in svc_new if x[state_key])
to_del = old_idnr - new_idnr
to_add = new_idnr - old_idnr
possible_change = old_idnr & new_idnr
svc_old = dict((x['id'], x) for x in svc_old)
svc_new = dict((x['id'], x) for x in svc_new)
to_del = [svc_old[x] for x in to_del]
to_add = [svc_new[x] for x in to_add]
to_change = list()
for idnr in possible_change:
old = svc_old[idnr]
new = svc_new[idnr]
assert old['router_id'] == new['router_id']
vrouter = self.vpn_service.get_router_client(old['router_id'])
gw_iface = self._get_router_gw_iface(vrouter, old['router_id'])
if vyatta_vpn_config.compare_vpn_services(
vrouter, gw_iface, old, new):
continue
to_change.append((old, new))
return to_del, to_change, to_add
def get_active_services(self):
return tuple(self._svc_cache)
def get_router_resources(self, router_id):
try:
res = self._router_resources_cache[router_id]
except KeyError:
res = vyatta_vpn_config.RouterResources(router_id)
self._router_resources_cache[router_id] = res
return res
def update_status(self, ctx, stat):
LOG.debug('STAT: %s', pprint.pformat(stat))
self.server_api.update_status(ctx, stat)
def _get_router_gw_iface(self, vrouter, router_id):
router_info = self.vpn_service.get_router(router_id)
try:
gw_interface = vrouter.get_ethernet_if_id(
router_info['gw_port']['mac_address'])
except KeyError:
raise v_exc.InvalidL3AgentStateError(description=_(
'Router id={0} have no external gateway.').format(
router_info['id']))
return gw_interface
class _VyattaPeriodicTasks(periodic_task.PeriodicTasks):
def __init__(self, driver):
super(_VyattaPeriodicTasks, self).__init__()
self.driver = driver
def __call__(self):
ctx_admin = n_ctx.get_admin_context()
return self.run_periodic_tasks(ctx_admin)
@periodic_task.periodic_task(spacing=5)
def grab_vpn_status(self, ctx):
LOG.debug('VPN device driver periodic task: grab_vpn_status.')
svc_by_vrouter = collections.defaultdict(list)
for svc in self.driver.get_active_services():
svc_by_vrouter[svc['router_id']].append(svc)
status = list()
for router_id, svc_set in six.iteritems(svc_by_vrouter):
vrouter = self.driver.vpn_service.get_router_client(router_id)
resources = self.driver.get_router_resources(router_id)
try:
ipsec_sa = vrouter.get_vpn_ipsec_sa()
except v_exc.VRouterOperationError as e:
LOG.warn(_LW('Failed to fetch tunnel stats from router '
'{0}: {1}').format(router_id, unicode(e)))
continue
conn_ok = vyatta_vpn_config.parse_vpn_connections(
ipsec_sa, resources)
for svc in svc_set:
svc_ok = True
conn_stat = dict()
for conn in svc[_KEY_CONNECTIONS]:
ok = conn['id'] in conn_ok
svc_ok = svc_ok and ok
conn_stat[conn['id']] = {
'status': 'ACTIVE' if ok else 'DOWN',
'updated_pending_status': True
}
status.append({
'id': svc['id'],
'status': 'ACTIVE' if svc_ok else 'DOWN',
'updated_pending_status': True,
'ipsec_site_connections': conn_stat
})
self.driver.update_status(ctx, status)

View File

@ -0,0 +1,117 @@
# Copyright 2015 Brocade Communications System, Inc.
# 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.
#
import netaddr
from neutron.common import rpc as n_rpc
from neutron_vpnaas.services.vpn.common import topics
from neutron_vpnaas.services.vpn import service_drivers
from neutron_vpnaas.services.vpn.service_drivers import ipsec
IPSEC = 'ipsec'
BASE_IPSEC_VERSION = '1.0'
class VyattaIPsecDriver(service_drivers.VpnDriver):
def __init__(self, service_plugin):
super(VyattaIPsecDriver, self).__init__(service_plugin)
self.endpoints = [ipsec.IPsecVpnDriverCallBack(self)]
self.conn = n_rpc.create_connection(new=True)
self.conn.create_consumer(
topics.BROCADE_IPSEC_DRIVER_TOPIC, self.endpoints, fanout=False)
self.conn.consume_in_threads()
self.agent_rpc = ipsec.IPsecVpnAgentApi(
topics.BROCADE_IPSEC_AGENT_TOPIC, BASE_IPSEC_VERSION, self)
@property
def service_type(self):
return IPSEC
def create_ipsec_site_connection(self, context, ipsec_site_connection):
vpnservice = self.service_plugin._get_vpnservice(
context, ipsec_site_connection['vpnservice_id'])
self.agent_rpc.vpnservice_updated(context, vpnservice['router_id'])
def update_ipsec_site_connection(
self, context, old_ipsec_site_connection, ipsec_site_connection):
vpnservice = self.service_plugin._get_vpnservice(
context, ipsec_site_connection['vpnservice_id'])
self.agent_rpc.vpnservice_updated(context, vpnservice['router_id'])
def delete_ipsec_site_connection(self, context, ipsec_site_connection):
vpnservice = self.service_plugin._get_vpnservice(
context, ipsec_site_connection['vpnservice_id'])
self.agent_rpc.vpnservice_updated(context, vpnservice['router_id'])
def create_ikepolicy(self, context, ikepolicy):
pass
def delete_ikepolicy(self, context, ikepolicy):
pass
def update_ikepolicy(self, context, old_ikepolicy, ikepolicy):
pass
def create_ipsecpolicy(self, context, ipsecpolicy):
pass
def delete_ipsecpolicy(self, context, ipsecpolicy):
pass
def update_ipsecpolicy(self, context, old_ipsec_policy, ipsecpolicy):
pass
def create_vpnservice(self, context, vpnservice):
pass
def update_vpnservice(self, context, old_vpnservice, vpnservice):
self.agent_rpc.vpnservice_updated(context, vpnservice['router_id'])
def delete_vpnservice(self, context, vpnservice):
self.agent_rpc.vpnservice_updated(context, vpnservice['router_id'])
def _make_vpnservice_dict(self, vpnservice):
"""Convert vpnservice information for vpn agent.
also converting parameter name for vpn agent driver
"""
vpnservice_dict = dict(vpnservice)
vpnservice_dict['ipsec_site_connections'] = []
vpnservice_dict['subnet'] = dict(
vpnservice.subnet)
vpnservice_dict['external_ip'] = vpnservice.router.gw_port[
'fixed_ips'][0]['ip_address']
for ipsec_site_connection in vpnservice.ipsec_site_connections:
ipsec_site_connection_dict = dict(ipsec_site_connection)
try:
netaddr.IPAddress(ipsec_site_connection['peer_id'])
except netaddr.core.AddrFormatError:
ipsec_site_connection['peer_id'] = (
'@' + ipsec_site_connection['peer_id'])
ipsec_site_connection_dict['ikepolicy'] = dict(
ipsec_site_connection.ikepolicy)
ipsec_site_connection_dict['ipsecpolicy'] = dict(
ipsec_site_connection.ipsecpolicy)
vpnservice_dict['ipsec_site_connections'].append(
ipsec_site_connection_dict)
peer_cidrs = [
peer_cidr.cidr
for peer_cidr in ipsec_site_connection.peer_cidrs]
ipsec_site_connection_dict['peer_cidrs'] = peer_cidrs
return vpnservice_dict

View File

@ -0,0 +1,62 @@
# Copyright 2015 Brocade Communications System, Inc.
# 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.
#
from neutron.agent import l3_agent as entry
from neutron.openstack.common import log as logging
from oslo_config import cfg
from vyatta.common import l3_agent as vyatta_l3
from neutron_vpnaas.services.vpn import vyatta_vpn_service
LOG = logging.getLogger(__name__)
vpn_agent_opts = [
cfg.MultiStrOpt(
'vpn_device_driver',
default=['neutron_vpnaas.services.vpn.device_drivers.'
'vyatta_ipsec.VyattaIPSecDriver'],
help=_("The vpn device drivers Neutron will use")),
]
cfg.CONF.register_opts(vpn_agent_opts, 'vpnagent')
class VyattaVPNAgent(vyatta_l3.L3AgentMiddleware):
def __init__(self, host, conf=None):
super(VyattaVPNAgent, self).__init__(host, conf)
self.service = vyatta_vpn_service.VyattaVPNService.instance(self)
self.event_observers.add(self.service)
self.devices = self.service.load_device_drivers(host)
def _router_added(self, router_id, router):
super(VyattaVPNAgent, self)._router_added(router_id, router)
for device in self.devices:
device.create_router(router_id)
def _router_removed(self, router_id):
for device in self.devices:
device.destroy_router(router_id)
super(VyattaVPNAgent, self)._router_removed(router_id)
def _process_router_if_compatible(self, router):
super(VyattaVPNAgent, self)._process_router_if_compatible(router)
for device in self.devices:
device.sync(self.context, None)
def main():
entry.main(
manager='neutron_vpnaas.services.vpn.vyatta_agent.VyattaVPNAgent')

View File

@ -0,0 +1,47 @@
# Copyright 2015 Brocade Communications System, Inc.
# 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.
from neutron_vpnaas.services.vpn import vpn_service
class VyattaVPNService(vpn_service.VPNService):
"""Vyatta VPN Service handler."""
_instance = None
def __init__(self, l3_agent):
"""Creates a Vyatta VPN Service instance.
DO NOT CALL THIS DIRECTLY! Use the instance() class method to Create
a singleton instance of the service.
NOTE: Directly accessing l3_agent here is an interim solution
until we move to have a router object given down to device drivers
to access router related methods
"""
super(VyattaVPNService, self).__init__(l3_agent)
def get_router_client(self, router_id):
"""
Get Router RESTapi client
"""
return self.l3_agent.get_router_client(router_id)
def get_router(self, router_id):
"""
Get Router Object
"""
return self.l3_agent.get_router(router_id)

View File

@ -0,0 +1,222 @@
# Copyright 2015 Brocade Communications System, Inc.
# 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.
#
import contextlib
import sys
import mock
from neutron.openstack.common import uuidutils
from neutron_vpnaas.tests import base
with mock.patch.dict(sys.modules, {
'vyatta': mock.Mock(),
'vyatta.common': mock.Mock(),
'vyatta.vrouter': mock.Mock(),
'vyatta.vpn': mock.Mock(),
}):
from neutron_vpnaas.services.vpn.device_drivers import vyatta_ipsec
from vyatta.common import vrouter_config
from vyatta.vpn import config as vyatta_vpn_config
_uuid = uuidutils.generate_uuid
FAKE_HOST = 'fake_host'
class TestNeutronServerAPI(base.BaseTestCase):
def setUp(self):
super(TestNeutronServerAPI, self).setUp()
get_client_mock = mock.patch(
'neutron.common.rpc.get_client').start()
self.client = get_client_mock.return_value
self.api = vyatta_ipsec.NeutronServerAPI('fake-topic')
def test_get_vpn_services_on_host(self):
fake_context = mock.Mock()
svc_connections = [
self._make_svc_connection(),
self._make_svc_connection()
]
vpn_services_on_host = [{
vyatta_ipsec._KEY_CONNECTIONS: svc_connections
}]
cctxt = self.client.prepare.return_value
cctxt.call.return_value = vpn_services_on_host
vpn_services = self.api.get_vpn_services_on_host(
fake_context, FAKE_HOST)
cctxt.call.assert_called_with(
fake_context, 'get_vpn_services_on_host', host=FAKE_HOST)
validate_func = vyatta_vpn_config.validate_svc_connection
for connection in svc_connections:
validate_func.assert_any_call(connection)
self.assertEqual(len(vpn_services), len(vpn_services_on_host))
def test_update_status(self):
context = mock.Mock()
fake_status = 'fake-status'
cctxt = self.client.prepare.return_value
self.api.update_status(context, 'fake-status')
cctxt.cast.assert_called_once_with(
context, 'update_status', status=fake_status)
@staticmethod
def _make_svc_connection():
return {
vyatta_ipsec._KEY_IKEPOLICY: {
'encryption_algorithm': 'aes-256',
'lifetime_units': 'seconds',
},
vyatta_ipsec._KEY_ESPPOLICY: {
'encryption_algorithm': 'aes-256',
'lifetime_units': 'seconds',
'transform_protocol': 'esp',
'pfs': 'dh-group2',
'encapsulation_mode': 'tunnel'
},
'dpd_action': 'hold',
}
class TestVyattaDeviceDriver(base.BaseTestCase):
def setUp(self):
super(TestVyattaDeviceDriver, self).setUp()
mock.patch('neutron.openstack.common.loopingcall'
'.DynamicLoopingCall').start()
self.server_api = mock.patch(
'neutron_vpnaas.services.vpn.device_drivers'
'.vyatta_ipsec.NeutronServerAPI').start()
self.agent = mock.Mock()
self.driver = vyatta_ipsec.VyattaIPSecDriver(self.agent, FAKE_HOST)
def test_create_router(self):
router_id = _uuid()
vrouter_svc_list = [self._make_vrouter_svc()]
parse_vrouter_config = mock.Mock()
parse_vrouter_config.return_value = vrouter_svc_list
with contextlib.nested(
mock.patch.object(vrouter_config, 'parse_config'),
mock.patch.object(vyatta_vpn_config, 'parse_vrouter_config',
parse_vrouter_config),
mock.patch.object(self.driver, 'get_router_resources',
mock.MagicMock())
):
self.driver.create_router(router_id)
svc_cache = self.driver._svc_cache
self.assertEqual(len(svc_cache), 1)
self.assertEqual(svc_cache[0]['router_id'], router_id)
ipsec_connections = svc_cache[0]['ipsec_site_connections']
self.assertEqual(
ipsec_connections[0]['peer_address'],
'172.24.4.234')
def test_destroy_router(self):
router_id = _uuid()
get_router_resources = mock.Mock()
vrouter_svc = self._make_vrouter_svc()
vrouter_svc['router_id'] = router_id
svc_cache = [vrouter_svc]
svc_delete = mock.Mock()
with contextlib.nested(
mock.patch.object(self.driver, 'get_router_resources',
get_router_resources),
mock.patch.object(self.driver, '_svc_delete', svc_delete),
mock.patch.object(self.driver, '_svc_cache', svc_cache),
):
self.driver.destroy_router(router_id)
self.assertNotIn(vrouter_svc, svc_cache)
svc_delete.assert_called_with(vrouter_svc, mock.ANY)
def test_sync(self):
router_id = _uuid()
self.agent.router_info = {
router_id: mock.Mock()
}
to_del = [self._make_svc()]
to_change = [
(self._make_svc(), self._make_svc()),
]
to_add = [self._make_svc()]
svc_diff = mock.Mock()
svc_diff.return_value = (
to_del,
to_change,
to_add,
)
svc_delete = mock.Mock()
svc_add = mock.Mock()
with contextlib.nested(
mock.patch.object(self.driver, '_svc_diff', svc_diff),
mock.patch.object(self.driver, '_svc_delete', svc_delete),
mock.patch.object(self.driver, '_svc_add', svc_add),
):
self.driver.sync(mock.Mock(), None)
for svc in to_add:
svc_add.assert_any_call(svc, mock.ANY)
for svc in to_del:
svc_delete.assert_any_call(svc, mock.ANY)
for old, new in to_change:
svc_delete.assert_any_call(old, mock.ANY)
svc_add.assert_any_call(new, mock.ANY)
@staticmethod
def _make_vrouter_svc():
return {
'id': _uuid(),
vyatta_ipsec._KEY_CONNECTIONS: [{
'peer_address': '172.24.4.234',
}]
}
@staticmethod
def _make_svc():
return {
'router_id': _uuid()
}

View File

@ -0,0 +1,106 @@
# Copyright 2015 Brocade Communications System, Inc.
# 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.
#
import contextlib
import mock
from neutron import context as n_ctx
from neutron.openstack.common import uuidutils
from neutron.plugins.common import constants
from neutron_vpnaas.services.vpn.service_drivers import vyatta_ipsec
from neutron_vpnaas.tests import base
_uuid = uuidutils.generate_uuid
FAKE_HOST = 'fake_host'
FAKE_SERVICE_ID = _uuid()
FAKE_VPN_CONNECTION = {
'vpnservice_id': FAKE_SERVICE_ID
}
FAKE_ROUTER_ID = _uuid()
FAKE_VPN_SERVICE = {
'router_id': FAKE_ROUTER_ID
}
class TestVyattaDriver(base.BaseTestCase):
def setUp(self):
super(TestVyattaDriver, self).setUp()
mock.patch('neutron.common.rpc.create_connection').start()
l3_agent = mock.Mock()
l3_agent.host = FAKE_HOST
plugin = mock.Mock()
plugin.get_l3_agents_hosting_routers.return_value = [l3_agent]
plugin_p = mock.patch('neutron.manager.NeutronManager.get_plugin')
get_plugin = plugin_p.start()
get_plugin.return_value = plugin
service_plugin_p = mock.patch(
'neutron.manager.NeutronManager.get_service_plugins')
get_service_plugin = service_plugin_p.start()
get_service_plugin.return_value = {constants.L3_ROUTER_NAT: plugin}
service_plugin = mock.Mock()
service_plugin.get_l3_agents_hosting_routers.return_value = [l3_agent]
self._fake_vpn_router_id = _uuid()
service_plugin._get_vpnservice.return_value = {
'router_id': self._fake_vpn_router_id
}
self.driver = vyatta_ipsec.VyattaIPsecDriver(service_plugin)
def _test_update(self, func, args, additional_info=None):
ctxt = n_ctx.Context('', 'somebody')
with contextlib.nested(
mock.patch.object(self.driver.agent_rpc.client, 'cast'),
mock.patch.object(self.driver.agent_rpc.client, 'prepare'),
) as (
rpc_mock, prepare_mock
):
prepare_mock.return_value = self.driver.agent_rpc.client
func(ctxt, *args)
prepare_args = {'server': 'fake_host', 'version': '1.0'}
prepare_mock.assert_called_once_with(**prepare_args)
rpc_mock.assert_called_once_with(ctxt, 'vpnservice_updated',
**additional_info)
def test_create_ipsec_site_connection(self):
self._test_update(self.driver.create_ipsec_site_connection,
[FAKE_VPN_CONNECTION],
{'router': {'id': self._fake_vpn_router_id}})
def test_update_ipsec_site_connection(self):
self._test_update(self.driver.update_ipsec_site_connection,
[FAKE_VPN_CONNECTION, FAKE_VPN_CONNECTION],
{'router': {'id': self._fake_vpn_router_id}})
def test_delete_ipsec_site_connection(self):
self._test_update(self.driver.delete_ipsec_site_connection,
[FAKE_VPN_CONNECTION],
{'router': {'id': self._fake_vpn_router_id}})
def test_update_vpnservice(self):
self._test_update(self.driver.update_vpnservice,
[FAKE_VPN_SERVICE, FAKE_VPN_SERVICE],
{'router': {'id': FAKE_VPN_SERVICE['router_id']}})
def test_delete_vpnservice(self):
self._test_update(self.driver.delete_vpnservice,
[FAKE_VPN_SERVICE],
{'router': {'id': FAKE_VPN_SERVICE['router_id']}})

View File

@ -0,0 +1,52 @@
# Copyright 2015 Brocade Communications System, Inc.
# 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.
import mock
from neutron.agent.common import config as agent_config
from neutron.openstack.common import uuidutils
from oslo_config import cfg
from neutron_vpnaas.services.vpn import vyatta_vpn_service
from neutron_vpnaas.tests import base
_uuid = uuidutils.generate_uuid
FAKE_ROUTER_ID = _uuid()
class TestVyattaVPNService(base.BaseTestCase):
def setUp(self):
super(TestVyattaVPNService, self).setUp()
self.conf = cfg.CONF
agent_config.register_root_helper(self.conf)
self.ri_kwargs = {'root_helper': self.conf.AGENT.root_helper,
'agent_conf': self.conf,
'interface_driver': mock.sentinel.interface_driver}
self.agent = mock.Mock()
self.vyatta_service = vyatta_vpn_service.VyattaVPNService.instance(
self.agent)
self.l3_agent = self.vyatta_service.l3_agent
def test_get_router_client(self):
self.vyatta_service.get_router_client(FAKE_ROUTER_ID)
self.l3_agent.get_router_client.assert_called_once_with(FAKE_ROUTER_ID)
def test_get_router(self):
self.vyatta_service.get_router(FAKE_ROUTER_ID)
self.l3_agent.get_router.assert_called_once_with(FAKE_ROUTER_ID)

View File

@ -38,6 +38,7 @@ console_scripts =
device_drivers =
neutron.services.vpn.device_drivers.ipsec.OpenSwanDriver = neutron_vpnaas.services.vpn.device_drivers.ipsec:OpenSwanDriver
neutron.services.vpn.device_drivers.cisco_ipsec.CiscoCsrIPsecDriver = neutron_vpnaas.services.vpn.device_drivers.cisco_ipsec:CiscoCsrIPsecDriver
neutron.services.vpn.device_drivers.vyatta_ipsec.VyattaIPsecDriver = neutron_vpnaas.services.vpn.device_drivers.vyatta_ipsec:VyattaIPsecDriver
[build_sphinx]
all_files = 1