diff --git a/neutron/agent/l3_agent.py b/neutron/agent/l3_agent.py index aed9bd0f8c6..5b7d709d6bf 100644 --- a/neutron/agent/l3_agent.py +++ b/neutron/agent/l3_agent.py @@ -414,12 +414,11 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager): prefixlen = netaddr.IPNetwork(port['subnet']['cidr']).prefixlen port['ip_cidr'] = "%s/%s" % (ips[0]['ip_address'], prefixlen) - def _get_existing_internal_devices(self, ri): + def _get_existing_devices(self, ri): ip_wrapper = ip_lib.IPWrapper(root_helper=self.root_helper, namespace=ri.ns_name()) ip_devs = ip_wrapper.get_devices(exclude_loopback=True) - return [ip_dev.name for ip_dev in ip_devs if - ip_dev.name.startswith(INTERNAL_DEV_PREFIX)] + return [ip_dev.name for ip_dev in ip_devs] def process_router(self, ri): ri.iptables_manager.defer_apply_on() @@ -443,7 +442,9 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager): self.internal_network_removed(ri, p['id'], p['ip_cidr']) ri.internal_ports.remove(p) - current_internal_devs = set(self._get_existing_internal_devices(ri)) + existing_devices = self._get_existing_devices(ri) + current_internal_devs = set([n for n in existing_devices + if n.startswith(INTERNAL_DEV_PREFIX)]) current_port_devs = set([self.get_internal_device_name(id) for id in current_port_ids]) stale_devs = current_internal_devs - current_port_devs @@ -471,6 +472,17 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager): self.external_gateway_removed(ri, ri.ex_gw_port, interface_name, internal_cidrs) + 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.driver.unplug(stale_dev, + bridge=self.conf.external_network_bridge, + namespace=ri.ns_name(), + prefix=EXTERNAL_DEV_PREFIX) + # Process static routes for router self.routes_updated(ri) # Process SNAT rules for external gateway diff --git a/neutron/tests/unit/test_l3_agent.py b/neutron/tests/unit/test_l3_agent.py index 89250439ad5..d390ac29bd6 100644 --- a/neutron/tests/unit/test_l3_agent.py +++ b/neutron/tests/unit/test_l3_agent.py @@ -796,8 +796,7 @@ class TestBasicRouterOperations(base.BaseTestCase): FakeDev('qr-b2c3d4e5-f6')] stale_devnames = [dev.name for dev in stale_devlist] - get_devices_return = [FakeDev('qg-a1b2c3d4-e5'), - FakeDev('qg-b2c3d4e5-f6')] + get_devices_return = [] get_devices_return.extend(stale_devlist) self.mock_ip.get_devices.return_value = get_devices_return @@ -844,6 +843,33 @@ class TestBasicRouterOperations(base.BaseTestCase): for stale_devname in stale_devnames] self.mock_driver.unplug.assert_has_calls(calls, any_order=True) + def test_process_router_delete_stale_external_devices(self): + class FakeDev(object): + def __init__(self, name): + self.name = name + + agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) + stale_devlist = [FakeDev('qg-a1b2c3d4-e5')] + stale_devnames = [dev.name for dev in stale_devlist] + + router = self._prepare_router_data(enable_snat=True, + num_internal_ports=1) + del router['gw_port'] + ri = l3_agent.RouterInfo(router['id'], + self.conf.root_helper, + self.conf.use_namespaces, + router=router) + + self.mock_ip.get_devices.return_value = stale_devlist + + agent.process_router(ri) + + self.mock_driver.unplug.assert_called_with( + stale_devnames[0], + bridge="br-ex", + namespace=ri.ns_name(), + prefix=l3_agent.EXTERNAL_DEV_PREFIX) + def test_routers_with_admin_state_down(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self.plugin_api.get_external_network_id.return_value = None