]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
ML2: move L3 cleanup out of network transaction
authorKevin Benton <blak111@gmail.com>
Tue, 30 Sep 2014 03:21:23 +0000 (20:21 -0700)
committerKevin Benton <blak111@gmail.com>
Tue, 30 Sep 2014 15:38:53 +0000 (08:38 -0700)
Move _process_l3_delete out of the delete_network
transaction to eliminate the semaphore deadlock that
occurs when it tries to delete the ports associated
with existing floating IPs.

It makes more sense to live outside of the transaction
anyway because the operations it performs cannot be
rolled back only in the database if the L3 plugin makes
external calls for floating IP creation/deletion.
e.g. if delete_floatingip is successful, it may have
deleted external resources and restoring the DB records
would make things inconsistent.

If a failure to delete the network does occur, any cleanup
done by _process_l3_delete will not be reversed.

Closes-Bug: #1374573
Change-Id: I3ae7bb269df9b9dcef94f48f13f1bde1e4106a80

neutron/plugins/ml2/plugin.py
neutron/tests/unit/ml2/test_ml2_plugin.py

index 6ef4d107c76b21e3159aade386783073253d961d..422cbff86ed9f34620f5fa2cfdc636db474cf3cb 100644 (file)
@@ -580,9 +580,15 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
                 # to prevent deadlock waiting to acquire a DB lock
                 # held by another thread in the same process, leading
                 # to 'lock wait timeout' errors.
+                #
+                # Process L3 first, since, depending on the L3 plugin, it may
+                # involve locking the db-access semaphore, sending RPC
+                # notifications, and/or calling delete_port on this plugin.
+                # Additionally, a rollback may not be enough to undo the
+                # deletion of a floating IP with certain L3 backends.
+                self._process_l3_delete(context, id)
                 with contextlib.nested(lockutils.lock('db-access'),
                                        session.begin(subtransactions=True)):
-                    self._process_l3_delete(context, id)
                     # Get ports to auto-delete.
                     ports = (session.query(models_v2.Port).
                              enable_eagerloads(False).
index be987827c11e09f210954dd0418dbbb0dd150e77..5cb6df44bcff35ae25e6cea675773597785698a9 100644 (file)
@@ -24,6 +24,7 @@ from neutron.common import exceptions as exc
 from neutron.common import utils
 from neutron import context
 from neutron.db import db_base_plugin_v2 as base_plugin
+from neutron.extensions import external_net as external_net
 from neutron.extensions import multiprovidernet as mpnet
 from neutron.extensions import portbindings
 from neutron.extensions import providernet as pnet
@@ -147,6 +148,22 @@ class TestMl2PortsV2(test_plugin.TestPortsV2, Ml2PluginV2TestCase):
                 mock.call(_("The port '%s' was deleted"), 'invalid-uuid')
             ])
 
+    def test_l3_cleanup_on_net_delete(self):
+        l3plugin = manager.NeutronManager.get_service_plugins().get(
+            service_constants.L3_ROUTER_NAT)
+        kwargs = {'arg_list': (external_net.EXTERNAL,),
+                  external_net.EXTERNAL: True}
+        with self.network(**kwargs) as n:
+            with self.subnet(network=n, cidr='200.0.0.0/22'):
+                l3plugin.create_floatingip(
+                    context.get_admin_context(),
+                    {'floatingip': {'floating_network_id': n['network']['id'],
+                                    'tenant_id': n['network']['tenant_id']}}
+                )
+        self._delete('networks', n['network']['id'])
+        flips = l3plugin.get_floatingips(context.get_admin_context())
+        self.assertFalse(flips)
+
     def test_delete_port_no_notify_in_disassociate_floatingips(self):
         ctx = context.get_admin_context()
         plugin = manager.NeutronManager.get_plugin()