From dcc9840684de11835625730aeca10aeaf416929b Mon Sep 17 00:00:00 2001 From: Eugene Nikanorov Date: Sat, 9 May 2015 22:56:44 +0400 Subject: [PATCH] Catch ObjectDeletedError and skip port or subnet removal When network is deleted service ports are deleted in the scope of delete_network. Service ports could also be deleted by other entities such as DHCP agent releasing dhcp port. That could rarely lead to a race condition when port object used in _delete_ports helper is already deleted causing ObjectDeletedError exception. Need to handle it and prevent object deletion in that case. Change-Id: I531251d3211545c82a5bb7a471b7915da9b763b7 Closes-Bug: #1454408 --- neutron/plugins/ml2/plugin.py | 6 ++++-- neutron/tests/unit/plugins/ml2/test_plugin.py | 11 +++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py index 2f209db77..b796d853e 100644 --- a/neutron/plugins/ml2/plugin.py +++ b/neutron/plugins/ml2/plugin.py @@ -685,7 +685,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, for port in ports: try: self.delete_port(context, port.id) - except exc.PortNotFound: + except (exc.PortNotFound, sa_exc.ObjectDeletedError): + context.session.expunge(port) # concurrent port deletion can be performed by # release_dhcp_port caused by concurrent subnet_delete LOG.info(_LI("Port %s was deleted concurrently"), port.id) @@ -698,7 +699,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, for subnet in subnets: try: self.delete_subnet(context, subnet.id) - except exc.SubnetNotFound: + except (exc.SubnetNotFound, sa_exc.ObjectDeletedError): + context.session.expunge(subnet) LOG.info(_LI("Subnet %s was deleted concurrently"), subnet.id) except Exception: diff --git a/neutron/tests/unit/plugins/ml2/test_plugin.py b/neutron/tests/unit/plugins/ml2/test_plugin.py index 21b90976a..af8c44042 100644 --- a/neutron/tests/unit/plugins/ml2/test_plugin.py +++ b/neutron/tests/unit/plugins/ml2/test_plugin.py @@ -23,6 +23,7 @@ import webob import fixtures from oslo_db import exception as db_exc +from sqlalchemy.orm import exc as sqla_exc from neutron.callbacks import registry from neutron.common import constants @@ -186,13 +187,19 @@ class TestMl2NetworksV2(test_plugin.TestNetworksV2, plugin = manager.NeutronManager.get_plugin() with mock.patch.object(plugin, "delete_port", side_effect=exc.PortNotFound(port_id="123")): - plugin._delete_ports(None, [mock.MagicMock()]) + plugin._delete_ports(mock.MagicMock(), [mock.MagicMock()]) + with mock.patch.object(plugin, "delete_port", + side_effect=sqla_exc.ObjectDeletedError(None)): + plugin._delete_ports(mock.MagicMock(), [mock.MagicMock()]) def test_subnet_delete_helper_tolerates_failure(self): plugin = manager.NeutronManager.get_plugin() with mock.patch.object(plugin, "delete_subnet", side_effect=exc.SubnetNotFound(subnet_id="1")): - plugin._delete_subnets(None, [mock.MagicMock()]) + plugin._delete_subnets(mock.MagicMock(), [mock.MagicMock()]) + with mock.patch.object(plugin, "delete_subnet", + side_effect=sqla_exc.ObjectDeletedError(None)): + plugin._delete_subnets(mock.MagicMock(), [mock.MagicMock()]) def _create_and_verify_networks(self, networks): for net_idx, net in enumerate(networks): -- 2.45.2