diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py index b64f2e6d2bc..f0fd292380e 100644 --- a/neutron/plugins/ml2/plugin.py +++ b/neutron/plugins/ml2/plugin.py @@ -1282,21 +1282,23 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, binding.vif_type == portbindings.VIF_TYPE_BINDING_FAILED or router_id != device_id) if update_required: - with session.begin(subtransactions=True): - try: - orig_port = super(Ml2Plugin, self).get_port(context, id) - except exc.PortNotFound: - LOG.debug("DVR Port %s has been deleted concurrently", id) - return - if not binding: - binding = db.ensure_dvr_port_binding( - session, id, host, router_id=device_id) - network = self.get_network(context, orig_port['network_id']) - levels = db.get_binding_levels(session, id, host) - mech_context = driver_context.PortContext(self, - context, orig_port, network, - binding, levels, original_port=orig_port) - self._process_dvr_port_binding(mech_context, context, attrs) + try: + with session.begin(subtransactions=True): + orig_port = self.get_port(context, id) + if not binding: + binding = db.ensure_dvr_port_binding( + session, id, host, router_id=device_id) + network = self.get_network(context, + orig_port['network_id']) + levels = db.get_binding_levels(session, id, host) + mech_context = driver_context.PortContext(self, + context, orig_port, network, + binding, levels, original_port=orig_port) + self._process_dvr_port_binding(mech_context, context, + attrs) + except (os_db_exception.DBReferenceError, exc.PortNotFound): + LOG.debug("DVR Port %s has been deleted concurrently", id) + return self._bind_port_if_needed(mech_context) def _pre_delete_port(self, context, port_id, port_check): diff --git a/neutron/tests/unit/plugins/ml2/test_plugin.py b/neutron/tests/unit/plugins/ml2/test_plugin.py index 750c83acd09..6a3ef743446 100644 --- a/neutron/tests/unit/plugins/ml2/test_plugin.py +++ b/neutron/tests/unit/plugins/ml2/test_plugin.py @@ -1171,6 +1171,18 @@ class TestMl2PortBinding(Ml2PluginV2TestCase, mech_context._binding.router_id) self.assertEqual(host_id, mech_context._binding.host) + def test_update_dvr_port_binding_on_concurrent_port_delete(self): + plugin = manager.NeutronManager.get_plugin() + with self.port() as port: + port = { + 'id': port['port']['id'], + portbindings.HOST_ID: 'foo_host', + } + with mock.patch.object(plugin, 'get_port', new=plugin.delete_port): + res = plugin.update_dvr_port_binding( + self.context, 'foo_port_id', {'port': port}) + self.assertIsNone(res) + def test_update_dvr_port_binding_on_non_existent_port(self): plugin = manager.NeutronManager.get_plugin() port = {