]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Avoid ObjectDeletedError while accessing deleted binding
authorEugene Nikanorov <enikanorov@mirantis.com>
Tue, 24 Feb 2015 12:35:22 +0000 (15:35 +0300)
committerEugene Nikanorov <enikanorov@mirantis.com>
Thu, 26 Feb 2015 11:05:42 +0000 (14:05 +0300)
In some cases access to db object's attribute triggers its refresh.
When the object has been already removed from the database, it could
lead to ObjectDeletedError being thrown.
Unit test was added to cover code path where the issue occurs
however issue doesn't seem to be reproducible with sqlite.

Change-Id: I0d6f9a91572c89cf7da1b66b880aeaa6b4b1987e
Closes-Bug: #1424593

neutron/db/agentschedulers_db.py
neutron/tests/unit/test_dhcp_scheduler.py

index 11728e54e7dd2d15af56eee9cb8f4089151da552..4cc645d852fb3264eaf1c72e8419b793399a5f5d 100644 (file)
@@ -259,6 +259,10 @@ class DhcpAgentSchedulerDbMixin(dhcpagentscheduler
                      {'network': binding.network_id,
                       'agent': binding.dhcp_agent_id,
                       'dead_time': agent_dead_limit})
+            # save binding object to avoid ObjectDeletedError
+            # in case binding is concurrently deleted from the DB
+            saved_binding = {'net': binding.network_id,
+                             'agent': binding.dhcp_agent_id}
             try:
                 self.remove_network_from_dhcp_agent(context,
                                                     binding.dhcp_agent_id,
@@ -267,19 +271,17 @@ class DhcpAgentSchedulerDbMixin(dhcpagentscheduler
                 # measures against concurrent operation
                 LOG.debug("Network %(net)s already removed from DHCP agent "
                           "%(agent)s",
-                          {'net': binding.network_id,
-                           'agent': binding.dhcp_agent_id})
+                          saved_binding)
                 # still continue and allow concurrent scheduling attempt
             except Exception:
                 LOG.exception(_LE("Unexpected exception occured while "
                                   "removing network %(net)s from agent "
                                   "%(agent)s"),
-                              {'net': binding.network_id,
-                               'agent': binding.dhcp_agent_id})
+                              saved_binding)
 
             if cfg.CONF.network_auto_schedule:
                 self._schedule_network(
-                    context, binding.network_id, dhcp_notifier)
+                    context, saved_binding['net'], dhcp_notifier)
 
     def get_dhcp_agents_hosting_networks(
             self, context, network_ids, active=None):
index 1407102370010420fecbfdedabea13426300d6c2..0daf26b66912eb6e0a44dd6f3e2361a5f2c19174 100644 (file)
@@ -26,6 +26,7 @@ from neutron import context
 from neutron.db import agents_db
 from neutron.db import agentschedulers_db as sched_db
 from neutron.db import models_v2
+from neutron.extensions import dhcpagentscheduler
 from neutron.scheduler import dhcp_agent_scheduler
 from neutron.tests.unit import testlib_api
 
@@ -216,11 +217,13 @@ class TestNetworksFailover(TestDhcpSchedulerBaseTestCase,
             notifier.network_added_to_agent.assert_called_with(
                 mock.ANY, self.network_id, agents[1].host)
 
-    def test_reschedule_network_from_down_agent_failed(self):
+    def _test_failed_rescheduling(self, rn_side_effect=None):
         agents = self._create_and_set_agents_down(['host-a'], 1)
         self._test_schedule_bind_network([agents[0]], self.network_id)
         with contextlib.nested(
-            mock.patch.object(self, 'remove_network_from_dhcp_agent'),
+            mock.patch.object(
+                self, 'remove_network_from_dhcp_agent',
+                side_effect=rn_side_effect),
             mock.patch.object(self, 'schedule_network',
                               return_value=None),
             mock.patch.object(self, 'get_network', create=True,
@@ -233,6 +236,14 @@ class TestNetworksFailover(TestDhcpSchedulerBaseTestCase,
             sch.assert_called_with(mock.ANY, {'id': self.network_id})
             self.assertFalse(notifier.network_added_to_agent.called)
 
+    def test_reschedule_network_from_down_agent_failed(self):
+        self._test_failed_rescheduling()
+
+    def test_reschedule_network_from_down_agent_concurrent_removal(self):
+        self._test_failed_rescheduling(
+            rn_side_effect=dhcpagentscheduler.NetworkNotHostedByDhcpAgent(
+                network_id='foo', agent_id='bar'))
+
     def test_filter_bindings(self):
         bindings = [
             sched_db.NetworkDhcpAgentBinding(network_id='foo1',