Check for concurrent port binding deletion before binding the port
When agent tries to update port binding (DVR or regular), the port might have already been deleted via API call. This is not an error condition but should be handled to avoid traces in the logs. Change-Id: Ie9436172151f0ecd5b3e4667328910b09f8ef141 Closes-Bug: #1370570
This commit is contained in:
parent
18a238c841
commit
36b7ceb050
20
neutron/plugins/ml2/plugin.py
Normal file → Executable file
20
neutron/plugins/ml2/plugin.py
Normal file → Executable file
@ -51,6 +51,7 @@ from neutron.extensions import portbindings
|
|||||||
from neutron.extensions import providernet as provider
|
from neutron.extensions import providernet as provider
|
||||||
from neutron import manager
|
from neutron import manager
|
||||||
from neutron.openstack.common import excutils
|
from neutron.openstack.common import excutils
|
||||||
|
from neutron.openstack.common.gettextutils import _LI
|
||||||
from neutron.openstack.common import importutils
|
from neutron.openstack.common import importutils
|
||||||
from neutron.openstack.common import jsonutils
|
from neutron.openstack.common import jsonutils
|
||||||
from neutron.openstack.common import lockutils
|
from neutron.openstack.common import lockutils
|
||||||
@ -948,6 +949,11 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
cur_binding = db.get_dvr_port_binding_by_host(session,
|
cur_binding = db.get_dvr_port_binding_by_host(session,
|
||||||
port_id,
|
port_id,
|
||||||
host)
|
host)
|
||||||
|
if not cur_binding:
|
||||||
|
LOG.info(_LI("Binding info for port %s was not found, "
|
||||||
|
"it might have been deleted already."),
|
||||||
|
port_id)
|
||||||
|
return
|
||||||
# Commit our binding results only if port has not been
|
# Commit our binding results only if port has not been
|
||||||
# successfully bound concurrently by another thread or
|
# successfully bound concurrently by another thread or
|
||||||
# process and no binding inputs have been changed.
|
# process and no binding inputs have been changed.
|
||||||
@ -1055,6 +1061,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
filter(models_v2.Port.id.startswith(port_id)).
|
filter(models_v2.Port.id.startswith(port_id)).
|
||||||
one())
|
one())
|
||||||
except sa_exc.NoResultFound:
|
except sa_exc.NoResultFound:
|
||||||
|
LOG.debug("No ports have port_id starting with %s",
|
||||||
|
port_id)
|
||||||
return
|
return
|
||||||
except exc.MultipleResultsFound:
|
except exc.MultipleResultsFound:
|
||||||
LOG.error(_("Multiple ports have port_id starting with %s"),
|
LOG.error(_("Multiple ports have port_id starting with %s"),
|
||||||
@ -1072,8 +1080,18 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
port_context = driver_context.DvrPortContext(
|
port_context = driver_context.DvrPortContext(
|
||||||
self, plugin_context, port, network, binding)
|
self, plugin_context, port, network, binding)
|
||||||
else:
|
else:
|
||||||
|
# since eager loads are disabled in port_db query
|
||||||
|
# related attribute port_binding could disappear in
|
||||||
|
# concurrent port deletion.
|
||||||
|
# It's not an error condition.
|
||||||
|
binding = port_db.port_binding
|
||||||
|
if not binding:
|
||||||
|
LOG.info(_LI("Binding info for port %s was not found, "
|
||||||
|
"it might have been deleted already."),
|
||||||
|
port_id)
|
||||||
|
return
|
||||||
port_context = driver_context.PortContext(
|
port_context = driver_context.PortContext(
|
||||||
self, plugin_context, port, network, port_db.port_binding)
|
self, plugin_context, port, network, binding)
|
||||||
|
|
||||||
return self._bind_port_if_needed(port_context)
|
return self._bind_port_if_needed(port_context)
|
||||||
|
|
||||||
|
24
neutron/tests/unit/ml2/test_port_binding.py
Normal file → Executable file
24
neutron/tests/unit/ml2/test_port_binding.py
Normal file → Executable file
@ -19,6 +19,7 @@ from neutron import context
|
|||||||
from neutron.extensions import portbindings
|
from neutron.extensions import portbindings
|
||||||
from neutron import manager
|
from neutron import manager
|
||||||
from neutron.plugins.ml2 import config as config
|
from neutron.plugins.ml2 import config as config
|
||||||
|
from neutron.plugins.ml2 import models as ml2_models
|
||||||
from neutron.tests.unit import test_db_plugin as test_plugin
|
from neutron.tests.unit import test_db_plugin as test_plugin
|
||||||
|
|
||||||
|
|
||||||
@ -98,6 +99,29 @@ class PortBindingTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
|||||||
portbindings.VIF_TYPE_OVS,
|
portbindings.VIF_TYPE_OVS,
|
||||||
True, True, 'ACTIVE')
|
True, True, 'ACTIVE')
|
||||||
|
|
||||||
|
def test_update_port_binding_no_binding(self):
|
||||||
|
ctx = context.get_admin_context()
|
||||||
|
with self.port(name='name') as port:
|
||||||
|
# emulating concurrent binding deletion
|
||||||
|
(ctx.session.query(ml2_models.PortBinding).
|
||||||
|
filter_by(port_id=port['port']['id']).delete())
|
||||||
|
self.assertIsNone(
|
||||||
|
self.plugin.get_bound_port_context(ctx, port['port']['id']))
|
||||||
|
|
||||||
|
def test_commit_dvr_port_binding(self):
|
||||||
|
ctx = context.get_admin_context()
|
||||||
|
|
||||||
|
class MechContext(object):
|
||||||
|
pass
|
||||||
|
|
||||||
|
mctx = MechContext()
|
||||||
|
mctx._binding = None
|
||||||
|
# making a shortcut: calling private method directly to
|
||||||
|
# avoid bothering with "concurrent" port binding deletion
|
||||||
|
res = self.plugin._commit_dvr_port_binding(ctx, 'anyUUID',
|
||||||
|
'HostA', mctx)
|
||||||
|
self.assertIsNone(res)
|
||||||
|
|
||||||
def _test_update_port_binding(self, host, new_host=None):
|
def _test_update_port_binding(self, host, new_host=None):
|
||||||
with mock.patch.object(self.plugin,
|
with mock.patch.object(self.plugin,
|
||||||
'_notify_port_updated') as notify_mock:
|
'_notify_port_updated') as notify_mock:
|
||||||
|
Loading…
Reference in New Issue
Block a user