Add IPv6 Prefix Delegation support for DVR
Unlike Legacy routers, DVR Edge Routers have their gateway interfaces in the SNAT namespace as opposed to the router namespace. Added a new method to the router_info class, get_gw_ns_name(), so callers can determine which namespace the gateway device lives in. This can then be over-ridden in the DVR Edge router class. The Prefix Delegation code will also now listen on the update_router event from the l3-agent and reset the namespace name if it changes. Closes-Bug: #1541406 Change-Id: If6ada5027d0483fac7fc3ff935fee1edfc6e2759
This commit is contained in:
parent
eeeee06bf0
commit
7b5f01c2e5
|
@ -33,6 +33,9 @@ class DvrEdgeRouter(dvr_local_router.DvrLocalRouter):
|
||||||
self.router_id, self.agent_conf, self.driver, self.use_ipv6)
|
self.router_id, self.agent_conf, self.driver, self.use_ipv6)
|
||||||
self.snat_iptables_manager = None
|
self.snat_iptables_manager = None
|
||||||
|
|
||||||
|
def get_gw_ns_name(self):
|
||||||
|
return self.snat_namespace.name
|
||||||
|
|
||||||
def external_gateway_added(self, ex_gw_port, interface_name):
|
def external_gateway_added(self, ex_gw_port, interface_name):
|
||||||
super(DvrEdgeRouter, self).external_gateway_added(
|
super(DvrEdgeRouter, self).external_gateway_added(
|
||||||
ex_gw_port, interface_name)
|
ex_gw_port, interface_name)
|
||||||
|
|
|
@ -122,6 +122,9 @@ class RouterInfo(object):
|
||||||
def get_external_device_interface_name(self, ex_gw_port):
|
def get_external_device_interface_name(self, ex_gw_port):
|
||||||
return self.get_external_device_name(ex_gw_port['id'])
|
return self.get_external_device_name(ex_gw_port['id'])
|
||||||
|
|
||||||
|
def get_gw_ns_name(self):
|
||||||
|
return self.ns_name
|
||||||
|
|
||||||
def _update_routing_table(self, operation, route, namespace):
|
def _update_routing_table(self, operation, route, namespace):
|
||||||
cmd = ['ip', 'route', operation, 'to', route['destination'],
|
cmd = ['ip', 'route', operation, 'to', route['destination'],
|
||||||
'via', route['nexthop']]
|
'via', route['nexthop']]
|
||||||
|
|
|
@ -24,7 +24,7 @@ from oslo_utils import netutils
|
||||||
import six
|
import six
|
||||||
from stevedore import driver
|
from stevedore import driver
|
||||||
|
|
||||||
from neutron._i18n import _
|
from neutron._i18n import _, _LE
|
||||||
from neutron.callbacks import events
|
from neutron.callbacks import events
|
||||||
from neutron.callbacks import registry
|
from neutron.callbacks import registry
|
||||||
from neutron.callbacks import resources
|
from neutron.callbacks import resources
|
||||||
|
@ -57,6 +57,9 @@ class PrefixDelegation(object):
|
||||||
registry.subscribe(add_router,
|
registry.subscribe(add_router,
|
||||||
resources.ROUTER,
|
resources.ROUTER,
|
||||||
events.BEFORE_CREATE)
|
events.BEFORE_CREATE)
|
||||||
|
registry.subscribe(update_router,
|
||||||
|
resources.ROUTER,
|
||||||
|
events.AFTER_UPDATE)
|
||||||
registry.subscribe(remove_router,
|
registry.subscribe(remove_router,
|
||||||
resources.ROUTER,
|
resources.ROUTER,
|
||||||
events.AFTER_DELETE)
|
events.AFTER_DELETE)
|
||||||
|
@ -329,12 +332,24 @@ def get_router_entry(ns_name):
|
||||||
def add_router(resource, event, l3_agent, **kwargs):
|
def add_router(resource, event, l3_agent, **kwargs):
|
||||||
added_router = kwargs['router']
|
added_router = kwargs['router']
|
||||||
router = l3_agent.pd.routers.get(added_router.router_id)
|
router = l3_agent.pd.routers.get(added_router.router_id)
|
||||||
|
gw_ns_name = added_router.get_gw_ns_name()
|
||||||
if not router:
|
if not router:
|
||||||
l3_agent.pd.routers[added_router.router_id] = (
|
l3_agent.pd.routers[added_router.router_id] = (
|
||||||
get_router_entry(added_router.ns_name))
|
get_router_entry(gw_ns_name))
|
||||||
else:
|
else:
|
||||||
# This will happen during l3 agent restart
|
# This will happen during l3 agent restart
|
||||||
router['ns_name'] = added_router.ns_name
|
router['ns_name'] = gw_ns_name
|
||||||
|
|
||||||
|
|
||||||
|
@utils.synchronized("l3-agent-pd")
|
||||||
|
def update_router(resource, event, l3_agent, **kwargs):
|
||||||
|
updated_router = kwargs['router']
|
||||||
|
router = l3_agent.pd.routers.get(updated_router.router_id)
|
||||||
|
if not router:
|
||||||
|
LOG.exception(_LE("Router to be updated is not in internal routers "
|
||||||
|
"list: %s"), updated_router.router_id)
|
||||||
|
else:
|
||||||
|
router['ns_name'] = updated_router.get_gw_ns_name()
|
||||||
|
|
||||||
|
|
||||||
class PDInfo(object):
|
class PDInfo(object):
|
||||||
|
|
|
@ -670,8 +670,10 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
|
||||||
self._set_ri_kwargs(mock.Mock(), router['id'], router)
|
self._set_ri_kwargs(mock.Mock(), router['id'], router)
|
||||||
ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs)
|
ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs)
|
||||||
# Make sure that ri.snat_namespace object is created when the
|
# Make sure that ri.snat_namespace object is created when the
|
||||||
# router is initialized
|
# router is initialized, and that it's name matches the gw
|
||||||
|
# namespace name
|
||||||
self.assertIsNotNone(ri.snat_namespace)
|
self.assertIsNotNone(ri.snat_namespace)
|
||||||
|
self.assertEqual(ri.snat_namespace.name, ri.get_gw_ns_name())
|
||||||
|
|
||||||
def test_ext_gw_updated_calling_snat_ns_delete_if_gw_port_host_none(
|
def test_ext_gw_updated_calling_snat_ns_delete_if_gw_port_host_none(
|
||||||
self):
|
self):
|
||||||
|
|
|
@ -164,6 +164,10 @@ class TestDvrRouterOperations(base.BaseTestCase):
|
||||||
self.ri_kwargs['router_id'] = router_id
|
self.ri_kwargs['router_id'] = router_id
|
||||||
self.ri_kwargs['router'] = router
|
self.ri_kwargs['router'] = router
|
||||||
|
|
||||||
|
def test_gw_ns_name(self):
|
||||||
|
ri = self._create_router()
|
||||||
|
self.assertEqual(ri.ns_name, ri.get_gw_ns_name())
|
||||||
|
|
||||||
def test_create_dvr_fip_interfaces_update(self):
|
def test_create_dvr_fip_interfaces_update(self):
|
||||||
ri = self._create_router()
|
ri = self._create_router()
|
||||||
fip_agent_port = {'subnets': []}
|
fip_agent_port = {'subnets': []}
|
||||||
|
|
|
@ -12,6 +12,9 @@
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
|
from neutron.agent.l3 import dvr_edge_router
|
||||||
|
from neutron.agent.l3 import dvr_local_router
|
||||||
|
from neutron.agent.l3 import legacy_router
|
||||||
from neutron.agent.linux import pd
|
from neutron.agent.linux import pd
|
||||||
from neutron.tests import base as tests_base
|
from neutron.tests import base as tests_base
|
||||||
|
|
||||||
|
@ -24,8 +27,68 @@ class FakeRouter(object):
|
||||||
class TestPrefixDelegation(tests_base.DietTestCase):
|
class TestPrefixDelegation(tests_base.DietTestCase):
|
||||||
def test_remove_router(self):
|
def test_remove_router(self):
|
||||||
l3_agent = mock.Mock()
|
l3_agent = mock.Mock()
|
||||||
router_id = 1
|
router_id = '1'
|
||||||
l3_agent.pd.routers = {router_id: pd.get_router_entry(None)}
|
l3_agent.pd.routers = {router_id: pd.get_router_entry(None)}
|
||||||
pd.remove_router(None, None, l3_agent, router=FakeRouter(router_id))
|
pd.remove_router(None, None, l3_agent, router=FakeRouter(router_id))
|
||||||
self.assertTrue(l3_agent.pd.delete_router_pd.called)
|
self.assertTrue(l3_agent.pd.delete_router_pd.called)
|
||||||
self.assertEqual({}, l3_agent.pd.routers)
|
self.assertEqual({}, l3_agent.pd.routers)
|
||||||
|
|
||||||
|
def _test_add_update_pd(self, l3_agent, router, ns_name):
|
||||||
|
# add entry
|
||||||
|
pd.add_router(None, None, l3_agent, router=router)
|
||||||
|
pd_router = l3_agent.pd.routers.get(router.router_id)
|
||||||
|
self.assertEqual(ns_name, pd_router.get('ns_name'))
|
||||||
|
|
||||||
|
# clear namespace name, update entry
|
||||||
|
pd_router['ns_name'] = None
|
||||||
|
pd.update_router(None, None, l3_agent, router=router)
|
||||||
|
pd_router = l3_agent.pd.routers.get(router.router_id)
|
||||||
|
self.assertEqual(ns_name, pd_router.get('ns_name'))
|
||||||
|
|
||||||
|
def test_add_update_dvr_edge_router(self):
|
||||||
|
l3_agent = mock.Mock()
|
||||||
|
l3_agent.pd.routers = {}
|
||||||
|
router_id = '1'
|
||||||
|
ri = dvr_edge_router.DvrEdgeRouter(l3_agent,
|
||||||
|
'host',
|
||||||
|
router_id,
|
||||||
|
mock.Mock(),
|
||||||
|
mock.Mock(),
|
||||||
|
mock.Mock())
|
||||||
|
ns_name = ri.snat_namespace.name
|
||||||
|
self._test_add_update_pd(l3_agent, ri, ns_name)
|
||||||
|
|
||||||
|
def test_add_update_dvr_local_router(self):
|
||||||
|
l3_agent = mock.Mock()
|
||||||
|
l3_agent.pd.routers = {}
|
||||||
|
router_id = '1'
|
||||||
|
ri = dvr_local_router.DvrLocalRouter(l3_agent,
|
||||||
|
'host',
|
||||||
|
router_id,
|
||||||
|
mock.Mock(),
|
||||||
|
mock.Mock(),
|
||||||
|
mock.Mock())
|
||||||
|
ns_name = ri.ns_name
|
||||||
|
self._test_add_update_pd(l3_agent, ri, ns_name)
|
||||||
|
|
||||||
|
def test_add_update_legacy_router(self):
|
||||||
|
l3_agent = mock.Mock()
|
||||||
|
l3_agent.pd.routers = {}
|
||||||
|
router_id = '1'
|
||||||
|
ri = legacy_router.LegacyRouter(l3_agent,
|
||||||
|
router_id,
|
||||||
|
mock.Mock(),
|
||||||
|
mock.Mock(),
|
||||||
|
mock.Mock())
|
||||||
|
ns_name = ri.ns_name
|
||||||
|
self._test_add_update_pd(l3_agent, ri, ns_name)
|
||||||
|
|
||||||
|
def test_update_no_router_exception(self):
|
||||||
|
l3_agent = mock.Mock()
|
||||||
|
l3_agent.pd.routers = {}
|
||||||
|
router = mock.Mock()
|
||||||
|
router.router_id = '1'
|
||||||
|
|
||||||
|
with mock.patch.object(pd.LOG, 'exception') as log:
|
||||||
|
pd.update_router(None, None, l3_agent, router=router)
|
||||||
|
self.assertTrue(log.called)
|
||||||
|
|
Loading…
Reference in New Issue