[DVR] Fix update of the MTU in the SNAT namespace

When network's MTU is changed, Neutron sends notification about it
to the L3 agents. In case of DVR (and DVR HA) MTU is then changed in
the qrouter- namespace but should be also changed on snat interfaces
in the snat namespace. And that part was missing.

This patch adds special implementation of the internal_network_updated()
method in the DvrEdgeRouter class so it can configure MTU also for
in the snat namespace.

This patch also removed passing attributes "interface_name",
"ip_cidrs" and "mtu" to the internal_network_updated() method and adds
"port" dict to be passed there. It is consistent with what is already
done in e.g. internal_network_added() method and "port" dict is actually
necessary to configure properly snat internal interface in the snat
namespace.

This patch adds also functional test of update network mtu for all types
of routers as there was no such test at all.

There is additional issue with DVR-HA which isn't fixed with that patch
and for which follow up will be proposed. Because of that this patch is
marked as partial fix for the related bug.

Conflicts:
    neutron/tests/functional/agent/l3/test_dvr_router.py

Related-Bug: #1933273
Change-Id: I200acfcaaae7f056ea9a563fead9ff2de8464971
(cherry picked from commit b5dd6efdca)
This commit is contained in:
Slawek Kaplonski 2021-07-02 17:55:26 +02:00
parent 5c3c81426e
commit 763a65d963
6 changed files with 161 additions and 8 deletions

View File

@ -129,6 +129,23 @@ class DvrEdgeRouter(dvr_local_router.DvrLocalRouter):
lib_constants.SNAT_INT_DEV_PREFIX,
mtu=sn_port.get('mtu'))
def internal_network_updated(self, port):
super(DvrEdgeRouter, self).internal_network_updated(port)
if not port:
return
if not self._is_this_snat_host():
return
sn_port = self.get_snat_port_for_internal_port(port)
if not sn_port:
return
ns_name = dvr_snat_ns.SnatNamespace.get_snat_ns_name(self.router['id'])
interface_name = self._get_snat_int_device_name(sn_port['id'])
self.driver.set_mtu(interface_name, port['mtu'], namespace=ns_name,
prefix=lib_constants.SNAT_INT_DEV_PREFIX)
def _dvr_internal_network_removed(self, port):
super(DvrEdgeRouter, self)._dvr_internal_network_removed(port)

View File

@ -359,7 +359,10 @@ class HaRouter(router.RouterInfo):
if device.addr.list(to=to):
super(HaRouter, self).remove_floating_ip(device, ip_cidr)
def internal_network_updated(self, interface_name, ip_cidrs, mtu):
def internal_network_updated(self, port):
interface_name = self.get_internal_device_name(port['id'])
ip_cidrs = common_utils.fixed_ip_cidrs(port['fixed_ips'])
mtu = port['mtu']
self.driver.set_mtu(interface_name, mtu, namespace=self.ns_name,
prefix=router.INTERNAL_DEV_PREFIX)
self._clear_vips(interface_name)

View File

@ -589,7 +589,10 @@ class RouterInfo(BaseRouterInfo):
self.router_id)
self.radvd.disable()
def internal_network_updated(self, interface_name, ip_cidrs, mtu):
def internal_network_updated(self, port):
interface_name = self.get_internal_device_name(port['id'])
ip_cidrs = common_utils.fixed_ip_cidrs(port['fixed_ips'])
mtu = port['mtu']
self.driver.set_mtu(interface_name, mtu, namespace=self.ns_name,
prefix=INTERNAL_DEV_PREFIX)
self.driver.init_router_port(
@ -648,12 +651,8 @@ class RouterInfo(BaseRouterInfo):
updated_cidrs = []
for p in updated_ports:
self._update_internal_ports_cache(p)
interface_name = self.get_internal_device_name(p['id'])
ip_cidrs = common_utils.fixed_ip_cidrs(p['fixed_ips'])
LOG.debug("updating internal network for port %s", p)
updated_cidrs += ip_cidrs
self.internal_network_updated(
interface_name, ip_cidrs, p['mtu'])
updated_cidrs += common_utils.fixed_ip_cidrs(p['fixed_ips'])
self.internal_network_updated(p)
enable_ra = enable_ra or self._port_has_ipv6_subnet(p)
# Check if there is any pd prefix update

View File

@ -2245,3 +2245,69 @@ class TestDvrRouter(DvrRouterTestFramework, framework.L3AgentTestFramework):
in fip_agent_gw_port['extra_subnets'])
routes_cidr = set(route['cidr'] for route in routes)
self.assertEqual(extra_subnet_cidr, routes_cidr)
def _test_router_interface_mtu_update(self, ha):
original_mtu = 1450
router_info = self.generate_dvr_router_info(
enable_ha=ha, enable_snat=True)
router_info['_interfaces'][0]['mtu'] = original_mtu
router_info['gw_port']['mtu'] = original_mtu
router_info[lib_constants.SNAT_ROUTER_INTF_KEY][0]['mtu'] = (
original_mtu)
router = self.manage_router(self.agent, router_info)
if ha:
utils.wait_until_true(lambda: router.ha_state == 'primary')
# Keepalived notifies of a state transition when it starts,
# not when it ends. Thus, we have to wait until keepalived finishes
# configuring everything. We verify this by waiting until the last
# device has an IP address.
device = router.router[lib_constants.INTERFACE_KEY][-1]
device_exists = functools.partial(
self.device_exists_with_ips_and_mac,
device,
router.get_internal_device_name,
router.ns_name)
utils.wait_until_true(device_exists)
interface_name = router.get_internal_device_name(
router_info['_interfaces'][0]['id'])
gw_interface_name = router.get_external_device_name(
router_info['gw_port']['id'])
snat_internal_port = router_info[lib_constants.SNAT_ROUTER_INTF_KEY]
snat_interface_name = router._get_snat_int_device_name(
snat_internal_port[0]['id'])
snat_namespace = dvr_snat_ns.SnatNamespace.get_snat_ns_name(
router_info['id'])
self.assertEqual(
original_mtu,
ip_lib.IPDevice(interface_name, router.ns_name).link.mtu)
self.assertEqual(
original_mtu,
ip_lib.IPDevice(gw_interface_name, snat_namespace).link.mtu)
self.assertEqual(
original_mtu,
ip_lib.IPDevice(snat_interface_name, snat_namespace).link.mtu)
updated_mtu = original_mtu + 1
router_info_copy = copy.deepcopy(router_info)
router_info_copy['_interfaces'][0]['mtu'] = updated_mtu
router_info_copy['gw_port']['mtu'] = updated_mtu
router_info_copy[lib_constants.SNAT_ROUTER_INTF_KEY][0]['mtu'] = (
updated_mtu)
self.agent._process_updated_router(router_info_copy)
self.assertEqual(
updated_mtu,
ip_lib.IPDevice(interface_name, router.ns_name).link.mtu)
self.assertEqual(
updated_mtu,
ip_lib.IPDevice(gw_interface_name, snat_namespace).link.mtu)
self.assertEqual(
updated_mtu,
ip_lib.IPDevice(snat_interface_name, snat_namespace).link.mtu)
def test_dvr_router_interface_mtu_update(self):
self._test_router_interface_mtu_update(ha=False)

View File

@ -384,6 +384,40 @@ class L3HATestCase(framework.L3AgentTestFramework):
common_utils.wait_until_true(lambda: router.ha_state == 'primary')
self._wait_until_ipv6_forwarding_has_state(router.ns_name, 'all', 1)
def test_router_interface_mtu_update(self):
original_mtu = 1450
router_info = self.generate_router_info(False)
router_info['_interfaces'][0]['mtu'] = original_mtu
router_info['gw_port']['mtu'] = original_mtu
router = self.manage_router(self.agent, router_info)
interface_name = router.get_internal_device_name(
router_info['_interfaces'][0]['id'])
gw_interface_name = router.get_external_device_name(
router_info['gw_port']['id'])
self.assertEqual(
original_mtu,
ip_lib.IPDevice(interface_name, router.ns_name).link.mtu)
self.assertEqual(
original_mtu,
ip_lib.IPDevice(gw_interface_name, router.ns_name).link.mtu)
updated_mtu = original_mtu + 1
router_info_copy = copy.deepcopy(router_info)
router_info_copy['_interfaces'][0]['mtu'] = updated_mtu
router_info_copy['gw_port']['mtu'] = updated_mtu
self.agent._process_updated_router(router_info_copy)
self.assertEqual(
updated_mtu,
ip_lib.IPDevice(interface_name, router.ns_name).link.mtu)
self.assertEqual(
updated_mtu,
ip_lib.IPDevice(gw_interface_name, router.ns_name).link.mtu)
class L3HATestFailover(framework.L3AgentTestFramework):

View File

@ -436,3 +436,37 @@ class L3AgentTestCase(framework.L3AgentTestFramework):
# networks that are in the same address scope as external network
net_helpers.assert_ping(machine_diff_scope.namespace,
machine_same_scope.ip)
def test_router_interface_mtu_update(self):
original_mtu = 1450
router_info = self.generate_router_info(False)
router_info['_interfaces'][0]['mtu'] = original_mtu
router_info['gw_port']['mtu'] = original_mtu
router = self.manage_router(self.agent, router_info)
interface_name = router.get_internal_device_name(
router_info['_interfaces'][0]['id'])
gw_interface_name = router.get_external_device_name(
router_info['gw_port']['id'])
self.assertEqual(
original_mtu,
ip_lib.IPDevice(interface_name, router.ns_name).link.mtu)
self.assertEqual(
original_mtu,
ip_lib.IPDevice(gw_interface_name, router.ns_name).link.mtu)
updated_mtu = original_mtu + 1
router_info_copy = copy.deepcopy(router_info)
router_info_copy['_interfaces'][0]['mtu'] = updated_mtu
router_info_copy['gw_port']['mtu'] = updated_mtu
self.agent._process_updated_router(router_info_copy)
self.assertEqual(
updated_mtu,
ip_lib.IPDevice(interface_name, router.ns_name).link.mtu)
self.assertEqual(
updated_mtu,
ip_lib.IPDevice(gw_interface_name, router.ns_name).link.mtu)