]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Call DVR VMARP notify outside of transaction
authorKevin Benton <blak111@gmail.com>
Sat, 11 Oct 2014 10:42:47 +0000 (03:42 -0700)
committerKevin Benton <blak111@gmail.com>
Tue, 14 Oct 2014 21:01:24 +0000 (14:01 -0700)
The dvr vmarp table update notification was being called inside
of the delete_port transaction in ML2, which can cause a yield
and lead to the glorious mysql/eventlet deadlock.

This patch moves it outside the transaction and adjusts it to
use an existing port dictionary rather than re-looking it up since
the port is now gone from the DB by the time it is called.

Closes-Bug: #1377241
Change-Id: I0b4dac61e49b2a926353f8478e421cd1a70be038

neutron/db/l3_dvr_db.py
neutron/plugins/ml2/plugin.py
neutron/plugins/ml2/rpc.py
neutron/tests/unit/ml2/test_ml2_plugin.py

index b6e826b8c1dad9cd72e50f0e997cb62cc1d1a898..1b39d5bed76214e9a769fbab4452e4a0fc4a37f7 100644 (file)
@@ -538,13 +538,12 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin,
             self._populate_subnet_for_ports(context, port_list)
         return port_list
 
-    def dvr_vmarp_table_update(self, context, port_id, action):
+    def dvr_vmarp_table_update(self, context, port_dict, action):
         """Notify the L3 agent of VM ARP table changes.
 
         Provide the details of the VM ARP to the L3 agent when
         a Nova instance gets created or deleted.
         """
-        port_dict = self._core_plugin._get_port(context, port_id)
         # Check this is a valid VM port
         if ("compute:" not in port_dict['device_owner'] or
             not port_dict['fixed_ips']):
index d744f19fe1f32a9b8a14b5cee7d7ff95622a37ec..b271af83f5c27d30bde56a2271d96f001823ea07 100644 (file)
@@ -1009,8 +1009,6 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
             if l3plugin:
                 router_ids = l3plugin.disassociate_floatingips(
                     context, id, do_notify=False)
-                if is_dvr_enabled:
-                    l3plugin.dvr_vmarp_table_update(context, id, "del")
 
             LOG.debug("Calling delete_port for %(port_id)s owned by %(owner)s"
                       % {"port_id": id, "owner": device_owner})
@@ -1018,6 +1016,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
 
         # now that we've left db transaction, we are safe to notify
         if l3plugin:
+            if is_dvr_enabled:
+                l3plugin.dvr_vmarp_table_update(context, port, "del")
             l3plugin.notify_routers_updated(context, router_ids)
             for router in removed_routers:
                 try:
index d9cb07efd46c7fbfe7d2bcf37cff041ac8df955f..d962c1d22865136e15882d71ac2744c0067fbdec 100644 (file)
@@ -165,7 +165,8 @@ class RpcCallbacks(n_rpc.RpcCallback,
             utils.is_extension_supported(l3plugin,
                                          q_const.L3_DISTRIBUTED_EXT_ALIAS)):
             try:
-                l3plugin.dvr_vmarp_table_update(rpc_context, port_id, "add")
+                port = plugin._get_port(rpc_context, port_id)
+                l3plugin.dvr_vmarp_table_update(rpc_context, port, "add")
             except exceptions.PortNotFound:
                 LOG.debug('Port %s not found during ARP update', port_id)
 
index 0d43145d2c976e49041488fe3fb63a1e406e389b..9bd8ccd9f61e96b29954f289902564b9f2b6d830 100644 (file)
@@ -988,9 +988,9 @@ class TestFaultyMechansimDriver(Ml2PluginV2FaultyDriverTestCase):
                     self._delete('ports', port['port']['id'])
 
 
-class TestMl2PluginCreateUpdatePort(base.BaseTestCase):
+class TestMl2PluginCreateUpdateDeletePort(base.BaseTestCase):
     def setUp(self):
-        super(TestMl2PluginCreateUpdatePort, self).setUp()
+        super(TestMl2PluginCreateUpdateDeletePort, self).setUp()
         self.context = mock.MagicMock()
 
     def _ensure_transaction_is_closed(self):
@@ -1043,3 +1043,24 @@ class TestMl2PluginCreateUpdatePort(base.BaseTestCase):
 
             plugin._notify_l3_agent_new_port.assert_called_once_with(
                 self.context, new_host_port)
+
+    def test_vmarp_table_update_outside_of_delete_transaction(self):
+        l3plugin = mock.Mock()
+        l3plugin.dvr_vmarp_table_update = (
+            lambda *args, **kwargs: self._ensure_transaction_is_closed())
+        l3plugin.dvr_deletens_if_no_port.return_value = []
+        l3plugin.supported_extension_aliases = [
+            'router', constants.L3_AGENT_SCHEDULER_EXT_ALIAS,
+            constants.L3_DISTRIBUTED_EXT_ALIAS
+        ]
+        with contextlib.nested(
+            mock.patch.object(ml2_plugin.Ml2Plugin, '__init__',
+                              return_value=None),
+            mock.patch.object(manager.NeutronManager,
+                              'get_service_plugins',
+                              return_value={'L3_ROUTER_NAT': l3plugin}),
+        ):
+            plugin = self._create_plugin_for_create_update_port(mock.Mock())
+            # deleting the port will call dvr_vmarp_table_update, which will
+            # run the transaction balancing function defined in this test
+            plugin.delete_port(self.context, 'fake_id')