From 4d18154aa8b075363ac52914e3f330db7cc04308 Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Mon, 17 Mar 2014 08:46:39 -0700 Subject: [PATCH] Ignore PortNotFound exceptions on lockless delete Modifies the delete_ports_by_device_id method to ignore PortNotFound exceptions because it has no protection against concurrent operations deleting the same ports that it's trying to delete. Closes-Bug: #1293657 Change-Id: Icbcded149364a0e231ae811a440a691518bf20ad --- neutron/db/db_base_plugin_v2.py | 8 +++++- .../tests/unit/cisco/test_network_plugin.py | 4 +++ neutron/tests/unit/test_db_plugin.py | 25 +++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/neutron/db/db_base_plugin_v2.py b/neutron/db/db_base_plugin_v2.py index b7ad1ecff..3bb3205d3 100644 --- a/neutron/db/db_base_plugin_v2.py +++ b/neutron/db/db_base_plugin_v2.py @@ -1357,7 +1357,13 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2, query = query.filter(models_v2.Port.network_id == network_id) port_ids = [p[0] for p in query] for port_id in port_ids: - self.delete_port(context, port_id) + try: + self.delete_port(context, port_id) + except n_exc.PortNotFound: + # Don't raise if something else concurrently deleted the port + LOG.debug(_("Ignoring PortNotFound when deleting port '%s'. " + "The port has already been deleted."), + port_id) def _delete_port(self, context, id): query = (context.session.query(models_v2.Port). diff --git a/neutron/tests/unit/cisco/test_network_plugin.py b/neutron/tests/unit/cisco/test_network_plugin.py index fd3fac031..a3ccc8c65 100644 --- a/neutron/tests/unit/cisco/test_network_plugin.py +++ b/neutron/tests/unit/cisco/test_network_plugin.py @@ -863,6 +863,10 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase, plugin_ref = self._get_plugin_ref() self._test_delete_ports_by_device_id_second_call_failure(plugin_ref) + def test_delete_ports_ignores_port_not_found(self): + plugin_ref = self._get_plugin_ref() + self._test_delete_ports_ignores_port_not_found(plugin_ref) + class TestCiscoNetworksV2(CiscoNetworkPluginV2TestCase, test_db_plugin.TestNetworksV2): diff --git a/neutron/tests/unit/test_db_plugin.py b/neutron/tests/unit/test_db_plugin.py index c7e780ac0..0dd198749 100644 --- a/neutron/tests/unit/test_db_plugin.py +++ b/neutron/tests/unit/test_db_plugin.py @@ -1705,6 +1705,31 @@ fixed_ips=ip_address%%3D%s&fixed_ips=ip_address%%3D%s&fixed_ips=subnet_id%%3D%s plugin = NeutronManager.get_plugin() self._test_delete_ports_by_device_id_second_call_failure(plugin) + def _test_delete_ports_ignores_port_not_found(self, plugin): + ctx = context.get_admin_context() + with self.subnet() as subnet: + with contextlib.nested( + self.port(subnet=subnet, device_id='owner1'), + mock.patch.object(plugin, 'delete_port') + ) as (p, del_port): + del_port.side_effect = n_exc.PortNotFound( + port_id=p['port']['id'] + ) + network_id = subnet['subnet']['network_id'] + try: + plugin.delete_ports_by_device_id(ctx, 'owner1', + network_id) + except n_exc.PortNotFound: + self.fail("delete_ports_by_device_id unexpectedly raised " + "a PortNotFound exception. It should ignore " + "this exception because it is often called at " + "the same time other concurrent operations are " + "deleting some of the same ports.") + + def test_delete_ports_ignores_port_not_found(self): + plugin = NeutronManager.get_plugin() + self._test_delete_ports_ignores_port_not_found(plugin) + class TestNetworksV2(NeutronDbPluginV2TestCase): # NOTE(cerberus): successful network update and delete are -- 2.45.2