Merge "Static routes not added to qrouter namespace for DVR" into stable/liberty

This commit is contained in:
Jenkins 2016-02-12 19:50:40 +00:00 committed by Gerrit Code Review
commit f67e0dc214
6 changed files with 82 additions and 37 deletions

View File

@ -19,6 +19,7 @@ from neutron.agent.l3 import dvr_snat_ns
from neutron.agent.l3 import router_info as router
from neutron.agent.linux import ip_lib
from neutron.agent.linux import iptables_manager
from neutron.i18n import _LE
LOG = logging.getLogger(__name__)
@ -35,6 +36,11 @@ class DvrEdgeRouter(dvr_local_router.DvrLocalRouter):
ex_gw_port, interface_name)
if self._is_this_snat_host():
self._create_dvr_gateway(ex_gw_port, interface_name)
# NOTE: When a router is created without a gateway the routes get
# added to the router namespace, but if we wanted to populate
# the same routes to the snat namespace after the gateway port
# is added, we need to call routes_updated here.
self.routes_updated([], self.router['routes'])
def external_gateway_updated(self, ex_gw_port, interface_name):
if not self._is_this_snat_host():
@ -175,7 +181,17 @@ class DvrEdgeRouter(dvr_local_router.DvrLocalRouter):
self._add_snat_rules(ex_gw_port, self.snat_iptables_manager,
interface_name)
def update_routing_table(self, operation, route, namespace=None):
ns_name = dvr_snat_ns.SnatNamespace.get_snat_ns_name(self.router['id'])
super(DvrEdgeRouter, self).update_routing_table(operation, route,
namespace=ns_name)
def update_routing_table(self, operation, route):
if self.get_ex_gw_port() and self._is_this_snat_host():
ns_name = dvr_snat_ns.SnatNamespace.get_snat_ns_name(
self.router['id'])
# NOTE: For now let us apply the static routes both in SNAT
# namespace and Router Namespace, to reduce the complexity.
ip_wrapper = ip_lib.IPWrapper(namespace=ns_name)
if ip_wrapper.netns.exists(ns_name):
super(DvrEdgeRouter, self)._update_routing_table(
operation, route, namespace=ns_name)
else:
LOG.error(_LE("The SNAT namespace %s does not exist for "
"the router."), ns_name)
super(DvrEdgeRouter, self).update_routing_table(operation, route)

View File

@ -175,15 +175,12 @@ class HaRouter(router.RouterInfo):
def get_router_cidrs(self, device):
return set(self._get_cidrs_from_keepalived(device.name))
def routes_updated(self):
new_routes = self.router['routes']
def routes_updated(self, old_routes, new_routes):
instance = self._get_keepalived_instance()
instance.virtual_routes.extra_routes = [
keepalived.KeepalivedVirtualRoute(
route['destination'], route['nexthop'])
for route in new_routes]
self.routes = new_routes
def _add_default_gw_virtual_route(self, ex_gw_port, interface_name):
gateway_ips = self._get_external_gw_ips(ex_gw_port)

View File

@ -116,15 +116,10 @@ class RouterInfo(object):
ip_wrapper = ip_lib.IPWrapper(namespace=namespace)
ip_wrapper.netns.execute(cmd, check_exit_code=False)
def update_routing_table(self, operation, route, namespace=None):
if namespace is None:
namespace = self.ns_name
self._update_routing_table(operation, route, namespace)
def update_routing_table(self, operation, route):
self._update_routing_table(operation, route, self.ns_name)
def routes_updated(self):
new_routes = self.router['routes']
old_routes = self.routes
def routes_updated(self, old_routes, new_routes):
adds, removes = common_utils.diff_list_of_dict(old_routes,
new_routes)
for route in adds:
@ -138,7 +133,6 @@ class RouterInfo(object):
for route in removes:
LOG.debug("Removed route entry is '%s'", route)
self.update_routing_table('delete', route)
self.routes = new_routes
def get_ex_gw_port(self):
return self.router.get('gw_port')
@ -692,7 +686,8 @@ class RouterInfo(object):
agent.pd.sync_router(self.router['id'])
self.process_external(agent)
# Process static routes for router
self.routes_updated()
self.routes_updated(self.routes, self.router['routes'])
self.routes = self.router['routes']
# Update ex_gw_port and enable_snat on the router info cache
self.ex_gw_port = self.get_ex_gw_port()

View File

@ -277,8 +277,10 @@ class L3AgentTestFramework(base.BaseSudoTestCase):
self.assertTrue(self.device_exists_with_ips_and_mac(
device, router.get_internal_device_name, router.ns_name))
def _assert_extra_routes(self, router):
routes = ip_lib.get_routing_table(4, namespace=router.ns_name)
def _assert_extra_routes(self, router, namespace=None):
if namespace is None:
namespace = router.ns_name
routes = ip_lib.get_routing_table(4, namespace=namespace)
routes = [{'nexthop': route['nexthop'],
'destination': route['destination']} for route in routes]
@ -1116,7 +1118,7 @@ class TestDvrRouter(L3AgentTestFramework):
# We get the router info particular to a dvr router
router_info = self.generate_dvr_router_info(
enable_ha, enable_snat)
enable_ha, enable_snat, extra_routes=True)
# We need to mock the get_agent_gateway_port return value
# because the whole L3PluginApi is mocked and we need the port
@ -1145,19 +1147,27 @@ class TestDvrRouter(L3AgentTestFramework):
self._assert_snat_chains(router)
self._assert_floating_ip_chains(router)
self._assert_metadata_chains(router)
self._assert_extra_routes(router)
self._assert_rfp_fpr_mtu(router, custom_mtu)
if enable_snat:
snat_ns_name = dvr_snat_ns.SnatNamespace.get_snat_ns_name(
router.router_id)
self._assert_extra_routes(router, namespace=snat_ns_name)
self._delete_router(self.agent, router.router_id)
self._assert_interfaces_deleted_from_ovs()
self._assert_router_does_not_exist(router)
def generate_dvr_router_info(
self, enable_ha=False, enable_snat=False, **kwargs):
def generate_dvr_router_info(self,
enable_ha=False,
enable_snat=False,
extra_routes=False,
**kwargs):
router = l3_test_common.prepare_router_data(
enable_snat=enable_snat,
enable_floating_ip=True,
enable_ha=enable_ha,
extra_routes=extra_routes,
num_internal_ports=2,
**kwargs)
internal_ports = router.get(l3_constants.INTERFACE_KEY, [])
router['distributed'] = True
@ -1530,3 +1540,21 @@ class TestDvrRouter(L3AgentTestFramework):
self.agent.context,
floating_agent_gw_port[0]['network_id'])
self.assertFalse(self._namespace_exists(fip_ns))
def test_dvr_router_static_routes(self):
"""Test to validate the extra routes on dvr routers."""
self.agent.conf.agent_mode = 'dvr_snat'
router_info = self.generate_dvr_router_info(enable_snat=True)
router1 = self.manage_router(self.agent, router_info)
self.assertTrue(self._namespace_exists(router1.ns_name))
self._assert_snat_namespace_exists(router1)
snat_ns_name = dvr_snat_ns.SnatNamespace.get_snat_ns_name(
router1.router_id)
# Now try to add routes that are suitable for both the
# router namespace and the snat namespace.
router1.router['routes'] = [{'destination': '8.8.4.0/24',
'nexthop': '35.4.0.20'}]
self.agent._process_updated_router(router1.router)
router_updated = self.agent.router_info[router_info['id']]
self._assert_extra_routes(router_updated, namespace=snat_ns_name)
self._assert_extra_routes(router_updated)

View File

@ -1112,14 +1112,14 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
router)
self.assertEqual(self.send_adv_notif.call_count, 1)
def test_update_routing_table(self):
# Just verify the correct namespace was used in the call
def _test_update_routing_table(self, is_snat_host=True):
router = l3_test_common.prepare_router_data()
uuid = router['id']
netns = 'snat-' + uuid
s_netns = 'snat-' + uuid
q_netns = 'qrouter-' + uuid
fake_route1 = {'destination': '135.207.0.0/16',
'nexthop': '1.2.3.4'}
'nexthop': '19.4.4.200'}
calls = [mock.call('replace', fake_route1, q_netns)]
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
ri = dvr_router.DvrEdgeRouter(
agent,
@ -1129,10 +1129,19 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
**self.ri_kwargs)
ri._update_routing_table = mock.Mock()
ri.update_routing_table('replace', fake_route1)
ri._update_routing_table.assert_called_once_with('replace',
fake_route1,
netns)
with mock.patch.object(ri, '_is_this_snat_host') as snat_host:
snat_host.return_value = is_snat_host
ri.update_routing_table('replace', fake_route1)
if is_snat_host:
ri._update_routing_table('replace', fake_route1, s_netns)
calls += [mock.call('replace', fake_route1, s_netns)]
ri._update_routing_table.assert_has_calls(calls, any_order=True)
def test_process_update_snat_routing_table(self):
self._test_update_routing_table()
def test_process_not_update_snat_routing_table(self):
self._test_update_routing_table(is_snat_host=False)
def test_process_router_interface_added(self):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)

View File

@ -98,7 +98,7 @@ class TestRouterInfo(base.BaseTestCase):
'nexthop': "10.100.10.30"}]
ri.routes = fake_old_routes
ri.router['routes'] = fake_new_routes
ri.routes_updated()
ri.routes_updated(fake_old_routes, fake_new_routes)
expected = [['ip', 'route', 'replace', 'to', '110.100.30.0/24',
'via', '10.100.10.30'],
@ -106,18 +106,18 @@ class TestRouterInfo(base.BaseTestCase):
'via', '10.100.10.30']]
self._check_agent_method_called(expected)
ri.routes = fake_new_routes
fake_new_routes = [{'destination': "110.100.30.0/24",
'nexthop': "10.100.10.30"}]
ri.router['routes'] = fake_new_routes
ri.routes_updated()
ri.routes_updated(ri.routes, fake_new_routes)
expected = [['ip', 'route', 'delete', 'to', '110.100.31.0/24',
'via', '10.100.10.30']]
self._check_agent_method_called(expected)
fake_new_routes = []
ri.router['routes'] = fake_new_routes
ri.routes_updated()
ri.routes_updated(ri.routes, fake_new_routes)
expected = [['ip', 'route', 'delete', 'to', '110.100.30.0/24',
'via', '10.100.10.30']]