From dcd4c8b8de4972b8e5aff55e041df54f66ac81b3 Mon Sep 17 00:00:00 2001 From: Quan Tian Date: Wed, 21 Dec 2016 20:40:08 +0800 Subject: [PATCH] DVR: delete stale devices after router update After the external network change of router gateway, the l3 agent deletes stale external devices in the qrouter namespace only. This doesn't apply to distributed router. This patch extract a method named _delete_stale_external_devices from RouterInfo and override it in DvrEdgeRouter. Change-Id: I2602efd5be92ec57410e54cf9c26641330a52ee6 Partial-Bug: #1651813 --- neutron/agent/l3/dvr_edge_router.py | 15 ++++++++++++ neutron/agent/l3/router_info.py | 25 ++++++++++--------- neutron/tests/unit/agent/l3/test_agent.py | 29 +++++++++++++++++++++++ 3 files changed, 58 insertions(+), 11 deletions(-) diff --git a/neutron/agent/l3/dvr_edge_router.py b/neutron/agent/l3/dvr_edge_router.py index e7f63f40112..efdf1609e18 100644 --- a/neutron/agent/l3/dvr_edge_router.py +++ b/neutron/agent/l3/dvr_edge_router.py @@ -242,3 +242,18 @@ class DvrEdgeRouter(dvr_local_router.DvrLocalRouter): with self.snat_iptables_manager.defer_apply(): self._add_address_scope_mark( self.snat_iptables_manager, ports_scopemark) + + def _delete_stale_external_devices(self, interface_name): + if not self.snat_namespace.exists(): + return + + ns_ip = ip_lib.IPWrapper(namespace=self.snat_namespace.name) + for d in ns_ip.get_devices(exclude_loopback=True): + if (d.name.startswith(router.EXTERNAL_DEV_PREFIX) and + d.name != interface_name): + LOG.debug('Deleting stale external router device: %s', d.name) + self.driver.unplug( + d.name, + bridge=self.agent_conf.external_network_bridge, + namespace=self.snat_namespace.name, + prefix=router.EXTERNAL_DEV_PREFIX) diff --git a/neutron/agent/l3/router_info.py b/neutron/agent/l3/router_info.py index 7670984e71f..a7fe32de66d 100644 --- a/neutron/agent/l3/router_info.py +++ b/neutron/agent/l3/router_info.py @@ -713,6 +713,19 @@ class RouterInfo(object): def _gateway_ports_equal(port1, port2): return port1 == port2 + def _delete_stale_external_devices(self, interface_name): + existing_devices = self._get_existing_devices() + stale_devs = [dev for dev in existing_devices + if dev.startswith(EXTERNAL_DEV_PREFIX) + and dev != interface_name] + for stale_dev in stale_devs: + LOG.debug('Deleting stale external router device: %s', stale_dev) + self.agent.pd.remove_gw_interface(self.router['id']) + self.driver.unplug(stale_dev, + bridge=self.agent_conf.external_network_bridge, + namespace=self.ns_name, + prefix=EXTERNAL_DEV_PREFIX) + def _process_external_gateway(self, ex_gw_port): # TODO(Carl) Refactor to clarify roles of ex_gw_port vs self.ex_gw_port ex_gw_port_id = (ex_gw_port and ex_gw_port['id'] or @@ -736,17 +749,7 @@ class RouterInfo(object): interface_name = self.get_internal_device_name(p['id']) self.gateway_redirect_cleanup(interface_name) - existing_devices = self._get_existing_devices() - stale_devs = [dev for dev in existing_devices - if dev.startswith(EXTERNAL_DEV_PREFIX) - and dev != interface_name] - for stale_dev in stale_devs: - LOG.debug('Deleting stale external router device: %s', stale_dev) - self.agent.pd.remove_gw_interface(self.router['id']) - self.driver.unplug(stale_dev, - bridge=self.agent_conf.external_network_bridge, - namespace=self.ns_name, - prefix=EXTERNAL_DEV_PREFIX) + self._delete_stale_external_devices(interface_name) # Process SNAT rules for external gateway gw_port = self._router.get('gw_port') diff --git a/neutron/tests/unit/agent/l3/test_agent.py b/neutron/tests/unit/agent/l3/test_agent.py index 402150fba69..f34b3d6a16f 100644 --- a/neutron/tests/unit/agent/l3/test_agent.py +++ b/neutron/tests/unit/agent/l3/test_agent.py @@ -1871,6 +1871,35 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): namespace=ri.ns_name, prefix=namespaces.EXTERNAL_DEV_PREFIX) + def test_process_dvr_router_delete_stale_external_devices(self): + agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) + stale_devlist = [l3_test_common.FakeDev('qg-a1b2c3d4-e5')] + stale_devnames = [dev.name for dev in stale_devlist] + + router = l3_test_common.prepare_router_data(enable_snat=True, + num_internal_ports=1) + self._set_ri_kwargs(agent, router['id'], router) + ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) + self.mock_ip.get_devices.return_value = stale_devlist + + ri.process() + + self.mock_driver.unplug.assert_called_with( + stale_devnames[0], + bridge=agent.conf.external_network_bridge, + namespace=ri.snat_namespace.name, + prefix=namespaces.EXTERNAL_DEV_PREFIX) + + def test_process_dvr_router_delete_stale_external_devices_no_snat_ns(self): + agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) + router = l3_test_common.prepare_router_data(enable_gw=False, + num_internal_ports=1) + self._set_ri_kwargs(agent, router['id'], router) + ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) + self.mock_ip.netns.exists.return_value = False + ri._delete_stale_external_devices('qg-a1b2c3d4-e5') + self.assertFalse(self.mock_ip.get_devices.called) + def test_router_deleted(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) agent._queue = mock.Mock()