From 74c671ba8721e59c1673fcfac078719d884801e0 Mon Sep 17 00:00:00 2001 From: Oleg Bondarev Date: Thu, 5 Nov 2020 10:41:15 +0400 Subject: [PATCH] Auto-remove floating agent gw ports on net/subnet delete fip agent gw ports may be left in DB after router removal due to race condition between l3 agent and server: when server processes "router delete" - l3 agent is still processing "router add" and creates fip agent gw port after server already removed the router. The patch also adds handling of external network delete event to cleanup fip namespaces left on agents due to same race condition. Change-Id: Ib2f3aca08946e584156d092c37e1ea5ed5ca81a6 Closes-Bug: #1902998 (cherry picked from commit b97a8eb488aec0ee9afed0ceeb3d45caeb37ffdb) --- neutron/common/_constants.py | 3 ++- neutron/db/l3_dvr_db.py | 10 ++++++++++ neutron/tests/unit/db/test_db_base_plugin_v2.py | 12 ++++++++++-- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/neutron/common/_constants.py b/neutron/common/_constants.py index 2260441c1e0..7639fad10a2 100644 --- a/neutron/common/_constants.py +++ b/neutron/common/_constants.py @@ -75,4 +75,5 @@ IDPOOL_SELECT_SIZE = 100 # finds out that all existing IP Allocations are associated with ports # with these owners, it will allow subnet deletion to proceed with the # IP allocations being cleaned up by cascade. -AUTO_DELETE_PORT_OWNERS = [constants.DEVICE_OWNER_DHCP] +AUTO_DELETE_PORT_OWNERS = [constants.DEVICE_OWNER_DHCP, + constants.DEVICE_OWNER_AGENT_GW] diff --git a/neutron/db/l3_dvr_db.py b/neutron/db/l3_dvr_db.py index 9bca31c6ca5..c874649ddde 100644 --- a/neutron/db/l3_dvr_db.py +++ b/neutron/db/l3_dvr_db.py @@ -13,6 +13,7 @@ # under the License. import collections +from neutron_lib.api.definitions import external_net as extnet_apidef from neutron_lib.api.definitions import l3 as l3_apidef from neutron_lib.api.definitions import portbindings from neutron_lib.api.definitions import portbindings_extended @@ -387,6 +388,15 @@ class DVRResourceOperationHandler(object): if host_id: return + @registry.receives(resources.NETWORK, [events.AFTER_DELETE]) + def delete_fip_namespaces_for_ext_net(self, rtype, event, trigger, + context, network, **kwargs): + if network.get(extnet_apidef.EXTERNAL): + # Send the information to all the L3 Agent hosts + # to clean up the fip namespace as it is no longer required. + self.l3plugin.l3_rpc_notifier.delete_fipnamespace_for_ext_net( + context, network['id']) + def _get_ports_for_allowed_address_pair_ip(self, context, network_id, fixed_ip): """Return all active ports associated with the allowed_addr_pair ip.""" diff --git a/neutron/tests/unit/db/test_db_base_plugin_v2.py b/neutron/tests/unit/db/test_db_base_plugin_v2.py index 5b7e5d41f8a..56e16c83a7e 100644 --- a/neutron/tests/unit/db/test_db_base_plugin_v2.py +++ b/neutron/tests/unit/db/test_db_base_plugin_v2.py @@ -1612,17 +1612,25 @@ fixed_ips=ip_address%%3D%s&fixed_ips=ip_address%%3D%s&fixed_ips=subnet_id%%3D%s res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) - def test_delete_network_port_exists_owned_by_network(self): + def _test_delete_network_port_exists_owned_by_network(self, device_owner): res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) network_id = network['network']['id'] self._create_port(self.fmt, network_id, - device_owner=constants.DEVICE_OWNER_DHCP) + device_owner=device_owner) req = self.new_delete_request('networks', network_id) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPNoContent.code, res.status_int) + def test_test_delete_network_port_exists_dhcp(self): + self._test_delete_network_port_exists_owned_by_network( + constants.DEVICE_OWNER_DHCP) + + def test_test_delete_network_port_exists_fip_gw(self): + self._test_delete_network_port_exists_owned_by_network( + constants.DEVICE_OWNER_AGENT_GW) + def test_delete_network_port_exists_owned_by_network_race(self): res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True)