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 131b7d88d71..c7d17f8993a 100644 --- a/neutron/tests/unit/agent/l3/test_agent.py +++ b/neutron/tests/unit/agent/l3/test_agent.py @@ -1870,6 +1870,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()