Fix VPN Service for Distributed Routers

With Distributed Routers the ipsec vpn
reference implementation was not fully
functional since the namespaces in the
service node where handled different from
the legacy centralized routers.

Legacy centralized router namespace
hosts the snat service, but in the case
of Distributed Routers the snat service
is on a different namespace than the
router namespace.

This patch address it by running the
ipsec vpn service in the snat namespace
for the dvr routers and only in dvr_snat
l3agent mode.

Closes-Bug: #1356467

DocImpact

Change-Id: I1ed4f8b3c3624a1c0f22af427f1827404fbf8fab
This commit is contained in:
Swaminathan Vasudevan 2014-12-17 22:55:57 -08:00
parent 30fe57e4a8
commit bd12e68b64
3 changed files with 78 additions and 7 deletions

View File

@ -619,7 +619,13 @@ class VPNPluginRpcDbMixin():
plugin = manager.NeutronManager.get_plugin()
agent = plugin._get_agent_by_type_and_host(
context, n_constants.AGENT_TYPE_L3, host)
if not agent.admin_state_up:
agent_conf = plugin.get_configuration_dict(agent)
# Retreive the agent_mode to check if this is the
# right agent to deploy the vpn service. In the
# case of distributed the vpn service should reside
# only on a dvr_snat node.
agent_mode = agent_conf.get('agent_mode', 'legacy')
if not agent.admin_state_up or agent_mode == 'dvr':
return []
query = context.session.query(VPNService)
query = query.join(IPsecSiteConnection)

View File

@ -84,7 +84,30 @@ class VPNService(advanced_service.AdvancedService):
router_info = self.l3_agent.router_info.get(router_id)
if not router_info:
return
return router_info.ns_name
# Added for handling the distributed Routers within SNAT namespace
if router_info.router['distributed']:
#return self.get_snat_ns_name(router_id)
return self.l3_agent.get_snat_ns_name(router_id)
else:
return router_info.ns_name
def get_router_based_iptables_manager(self, router_info):
"""Returns router based iptables manager
In DVR routers the IPsec VPN service should run inside
the snat namespace. So the iptables manager used for
snat namespace is different from the iptables manager
used for the qr namespace in a non dvr based router.
This function will check the router type and then will
return the right iptables manager. If DVR enabled router
it will return the snat_iptables_manager otherwise it will
return the legacy iptables_manager.
"""
if router_info.router['distributed']:
return router_info.snat_iptables_manager
else:
return router_info.iptables_manager
def add_nat_rule(self, router_id, chain, rule, top=False):
"""Add nat rule in namespace.
@ -99,8 +122,8 @@ class VPNService(advanced_service.AdvancedService):
router_info = self.l3_agent.router_info.get(router_id)
if not router_info:
return
router_info.iptables_manager.ipv4['nat'].add_rule(
chain, rule, top=top)
iptables_manager = self.get_router_based_iptables_manager(router_info)
iptables_manager.ipv4['nat'].add_rule(chain, rule, top=top)
def remove_nat_rule(self, router_id, chain, rule, top=False):
"""Remove nat rule in namespace.
@ -114,8 +137,8 @@ class VPNService(advanced_service.AdvancedService):
router_info = self.l3_agent.router_info.get(router_id)
if not router_info:
return
router_info.iptables_manager.ipv4['nat'].remove_rule(
chain, rule, top=top)
iptables_manager = self.get_router_based_iptables_manager(router_info)
iptables_manager.ipv4['nat'].remove_rule(chain, rule, top=top)
def iptables_apply(self, router_id):
"""Apply IPtables.
@ -126,4 +149,5 @@ class VPNService(advanced_service.AdvancedService):
router_info = self.l3_agent.router_info.get(router_id)
if not router_info:
return
router_info.iptables_manager.apply()
iptables_manager = self.get_router_based_iptables_manager(router_info)
iptables_manager.apply()

View File

@ -18,6 +18,7 @@ from oslo.config import cfg
from neutron.agent.common import config as agent_config
from neutron.agent.l3 import router_info
from neutron.agent.linux import iptables_manager
from neutron.extensions import vpnaas
from neutron.openstack.common import uuidutils
from neutron_vpnaas.services.vpn import agent as vpn_agent
@ -93,17 +94,38 @@ class TestVPNDeviceDriverCallsToService(base.BaseTestCase):
def _make_router_info_for_test(self, ns_name=None, iptables=None):
ri = router_info.RouterInfo(FAKE_ROUTER_ID, self.conf.root_helper,
{}, ns_name=ns_name)
ri.router['distributed'] = False
if iptables:
ri.iptables_manager.ipv4['nat'] = iptables
ri.iptables_manager.apply = self.apply_mock
self.service.l3_agent.router_info = {FAKE_ROUTER_ID: ri}
def _make_dvr_router_info_for_test(self, ns_name=None, iptables=None):
ri = router_info.RouterInfo(FAKE_ROUTER_ID, self.conf.root_helper,
{}, ns_name=ns_name)
ri.router['distributed'] = True
if iptables:
ri.snat_iptables_manager = iptables_manager.IptablesManager(
root_helper=mock.ANY,
namespace='snat-' + FAKE_ROUTER_ID,
use_ipv6=mock.ANY)
ri.snat_iptables_manager.ipv4['nat'] = iptables
ri.snat_iptables_manager.apply = self.apply_mock
self.service.l3_agent.router_info = {FAKE_ROUTER_ID: ri}
def test_get_namespace_for_router(self):
ns = "ns-" + FAKE_ROUTER_ID
self._make_router_info_for_test(ns_name=ns)
namespace = self.service.get_namespace(FAKE_ROUTER_ID)
self.assertTrue(namespace.endswith(FAKE_ROUTER_ID))
def test_get_namespace_for_dvr_router(self):
ns = "ns-" + FAKE_ROUTER_ID
self._make_dvr_router_info_for_test(ns_name=ns)
namespace = self.service.get_namespace(FAKE_ROUTER_ID)
self.assertTrue(namespace.startswith('snat'))
self.assertTrue(namespace.endswith(FAKE_ROUTER_ID))
def test_fail_getting_namespace_for_unknown_router(self):
self._make_router_info_for_test()
self.assertFalse(self.service.get_namespace('bogus_id'))
@ -115,6 +137,13 @@ class TestVPNDeviceDriverCallsToService(base.BaseTestCase):
self.iptables.add_rule.assert_called_once_with(
'fake_chain', 'fake_rule', top=True)
def test_add_nat_rule_with_dvr_router(self):
self._make_dvr_router_info_for_test(iptables=self.iptables)
self.service.add_nat_rule(FAKE_ROUTER_ID, 'fake_chain',
'fake_rule', True)
self.iptables.add_rule.assert_called_once_with(
'fake_chain', 'fake_rule', top=True)
def test_add_nat_rule_with_no_router(self):
self._make_router_info_for_test(iptables=self.iptables)
self.service.add_nat_rule(
@ -131,6 +160,13 @@ class TestVPNDeviceDriverCallsToService(base.BaseTestCase):
self.iptables.remove_rule.assert_called_once_with(
'fake_chain', 'fake_rule', top=True)
def test_remove_rule_with_dvr_router(self):
self._make_router_info_for_test(iptables=self.iptables)
self.service.remove_nat_rule(FAKE_ROUTER_ID, 'fake_chain',
'fake_rule', True)
self.iptables.remove_rule.assert_called_once_with(
'fake_chain', 'fake_rule', top=True)
def test_remove_rule_with_no_router(self):
self._make_router_info_for_test(iptables=self.iptables)
self.service.remove_nat_rule(
@ -144,6 +180,11 @@ class TestVPNDeviceDriverCallsToService(base.BaseTestCase):
self.service.iptables_apply(FAKE_ROUTER_ID)
self.apply_mock.assert_called_once_with()
def test_iptables_apply_with_dvr_router(self):
self._make_router_info_for_test(iptables=self.iptables)
self.service.iptables_apply(FAKE_ROUTER_ID)
self.apply_mock.assert_called_once_with()
def test_iptables_apply_with_no_router(self):
self._make_router_info_for_test(iptables=self.iptables)
self.service.iptables_apply('bogus_router_id')