Merge "Populate binding levels when concurrent ops fail" into stable/rocky
This commit is contained in:
commit
2cd3480fb4
|
@ -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
|
||||||
|
|
|
@ -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}
|
||||||
|
|
Loading…
Reference in New Issue