Merge "Populate binding levels when concurrent ops fail" into stable/rocky

This commit is contained in:
Zuul 2019-08-15 02:09:24 +00:00 committed by Gerrit Code Review
commit 2cd3480fb4
2 changed files with 76 additions and 0 deletions

View File

@ -666,6 +666,22 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
# Call the mechanism driver precommit methods, commit # Call the mechanism driver precommit methods, commit
# the results, and call the postcommit methods. # the results, and call the postcommit methods.
self.mechanism_manager.update_port_precommit(cur_context) self.mechanism_manager.update_port_precommit(cur_context)
else:
# Try to populate the PortContext with the current binding
# levels so that the RPC notification won't get suppressed.
# This is to avoid leaving ports stuck in a DOWN state.
# For more information see bug:
# https://bugs.launchpad.net/neutron/+bug/1755810
LOG.warning("Concurrent port binding operations failed on "
"port %s", port_id)
levels = db.get_binding_levels(plugin_context, port_id,
cur_binding.host)
for level in levels:
cur_context._push_binding_level(level)
# refresh context with a snapshot of the current binding state
cur_context._binding = driver_context.InstanceSnapshot(
cur_binding)
if commit: if commit:
# Continue, using the port state as of the transaction that # Continue, using the port state as of the transaction that
# just finished, whether that transaction committed new # just finished, whether that transaction committed new

View File

@ -2090,6 +2090,66 @@ class TestMl2PortBinding(Ml2PluginV2TestCase,
# Successful binding should only be attempted once. # Successful binding should only be attempted once.
self.assertEqual(1, at_mock.call_count) self.assertEqual(1, at_mock.call_count)
def test__bind_port_if_needed_concurrent_calls(self):
port_vif_type = portbindings.VIF_TYPE_UNBOUND
bound_vif_type = portbindings.VIF_TYPE_OVS
plugin, port_context, bound_context = (
self._create_port_and_bound_context(port_vif_type,
bound_vif_type))
bound_context._binding_levels = [mock.Mock(
port_id="port_id",
level=0,
driver='fake_agent',
segment_id="11111111-2222-3333-4444-555555555555")]
# let _commit_port_binding replace the PortContext with a new instance
# which does not have any binding levels set to simulate the concurrent
# port binding operations fail
with mock.patch(
'neutron.plugins.ml2.plugin.Ml2Plugin._bind_port',
return_value=bound_context),\
mock.patch('neutron.plugins.ml2.plugin.Ml2Plugin.'
'_notify_port_updated') as npu_mock,\
mock.patch('neutron.plugins.ml2.plugin.Ml2Plugin.'
'_attempt_binding',
side_effect=plugin._attempt_binding) as ab_mock,\
mock.patch('neutron.plugins.ml2.plugin.Ml2Plugin.'
'_commit_port_binding', return_value=(
mock.MagicMock(), True, True)) as cpb_mock:
ret_context = plugin._bind_port_if_needed(port_context,
allow_notify=True)
# _attempt_binding will return without doing anything during
# the second iteration since _should_bind_port returns False
self.assertEqual(2, ab_mock.call_count)
self.assertEqual(1, cpb_mock.call_count)
# _notify_port_updated will still be called though it does
# nothing due to the missing binding levels
npu_mock.assert_called_once_with(ret_context)
def test__commit_port_binding_populating_with_binding_levels(self):
port_vif_type = portbindings.VIF_TYPE_OVS
bound_vif_type = portbindings.VIF_TYPE_OVS
plugin, port_context, bound_context = (
self._create_port_and_bound_context(port_vif_type,
bound_vif_type))
db_portbinding = port_obj.PortBindingLevel(
self.context,
port_id=uuidutils.generate_uuid(),
level=0,
driver='fake_agent',
segment_id="11111111-2222-3333-4444-555555555555")
bound_context.network.current = {'id': 'net_id'}
with mock.patch.object(ml2_db, 'get_binding_levels',
return_value=[db_portbinding]),\
mock.patch.object(driver_context.PortContext,
'_push_binding_level') as pbl_mock:
plugin._commit_port_binding(
port_context, bound_context, True, False)
pbl_mock.assert_called_once_with(db_portbinding)
def test_port_binding_profile_not_changed(self): def test_port_binding_profile_not_changed(self):
profile = {'e': 5} profile = {'e': 5}
profile_arg = {portbindings.PROFILE: profile} profile_arg = {portbindings.PROFILE: profile}