diff --git a/neutron/plugins/ml2/driver_context.py b/neutron/plugins/ml2/driver_context.py index 1286ccf48a2..860647d4461 100644 --- a/neutron/plugins/ml2/driver_context.py +++ b/neutron/plugins/ml2/driver_context.py @@ -13,6 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. +import copy + from neutron_lib.api.definitions import portbindings from neutron_lib import constants from oslo_log import log @@ -95,8 +97,10 @@ class PortContext(MechanismDriverContext, api.PortContext): self._original_port = original_port self._network_context = NetworkContext(plugin, plugin_context, network) if network else None - self._binding = binding - self._binding_levels = binding_levels + # NOTE(kevinbenton): these copys can go away once we are working with + # OVO objects here instead of native SQLA objects. + self._binding = copy.deepcopy(binding) + self._binding_levels = copy.deepcopy(binding_levels) self._segments_to_bind = None self._new_bound_segment = None self._next_segments_to_bind = None diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py index 17a729d67f8..a02f54b5aee 100644 --- a/neutron/plugins/ml2/plugin.py +++ b/neutron/plugins/ml2/plugin.py @@ -13,6 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. +import copy + from eventlet import greenthread from neutron_lib.api.definitions import portbindings from neutron_lib.api.definitions import provider_net @@ -339,6 +341,10 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, binding.host = '' self._update_port_dict_binding(port, binding) + # merging here brings binding changes into the session so they can be + # committed since the binding attached to the context is detached from + # the session + plugin_context.session.merge(binding) return changes @db_api.retry_db_errors @@ -507,6 +513,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, cur_binding.host) db.set_binding_levels(plugin_context, bind_context._binding_levels) + # refresh context with a snapshot of updated state + cur_context._binding = copy.deepcopy(cur_binding) cur_context._binding_levels = bind_context._binding_levels # Update PortContext's port dictionary to reflect the @@ -1337,6 +1345,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, self._update_port_dict_binding(port, binding) binding.host = attrs and attrs.get(portbindings.HOST_ID) binding.router_id = attrs and attrs.get('device_id') + # merge into session to reflect changes + plugin_context.session.merge(binding) @utils.transaction_guard @db_api.retry_if_session_inactive() @@ -1557,8 +1567,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, context, port['id'], host) if not binding: return - binding['status'] = status - binding.update(binding) + binding.status = status updated = True if (updated and diff --git a/neutron/tests/unit/plugins/ml2/test_plugin.py b/neutron/tests/unit/plugins/ml2/test_plugin.py index 880ae4baf51..e33014b1b2a 100644 --- a/neutron/tests/unit/plugins/ml2/test_plugin.py +++ b/neutron/tests/unit/plugins/ml2/test_plugin.py @@ -1606,7 +1606,8 @@ class TestMl2PortBinding(Ml2PluginV2TestCase, plugin = directory.get_plugin() binding = plugin._get_port( self.context, port['port']['id']).port_binding - binding['host'] = 'test' + with self.context.session.begin(subtransactions=True): + binding.host = 'test' mech_context = driver_context.PortContext( plugin, self.context, port['port'], plugin.get_network(self.context, port['port']['network_id']), @@ -1614,7 +1615,9 @@ class TestMl2PortBinding(Ml2PluginV2TestCase, with mock.patch('neutron.plugins.ml2.plugin.Ml2Plugin.' '_update_port_dict_binding') as update_mock: attrs = {portbindings.HOST_ID: None} - plugin._process_port_binding(mech_context, attrs) + self.assertEqual('test', binding.host) + with self.context.session.begin(subtransactions=True): + plugin._process_port_binding(mech_context, attrs) self.assertTrue(update_mock.mock_calls) self.assertEqual('', binding.host) diff --git a/neutron/tests/unit/plugins/ml2/test_port_binding.py b/neutron/tests/unit/plugins/ml2/test_port_binding.py index f7731042d2c..3007c114693 100644 --- a/neutron/tests/unit/plugins/ml2/test_port_binding.py +++ b/neutron/tests/unit/plugins/ml2/test_port_binding.py @@ -18,6 +18,7 @@ from neutron_lib.api.definitions import portbindings from neutron_lib import constants as const from neutron_lib import context from neutron_lib.plugins import directory +from oslo_serialization import jsonutils from neutron.conf.plugins.ml2.drivers import driver_type from neutron.plugins.ml2 import config @@ -201,7 +202,7 @@ class PortBindingTestCase(test_plugin.NeutronDbPluginV2TestCase): port_id=original_port['id'], host=original_port['binding:host_id'], vnic_type=original_port['binding:vnic_type'], - profile=original_port['binding:profile'], + profile=jsonutils.dumps(original_port['binding:profile']), vif_type=original_port['binding:vif_type'], vif_details=original_port['binding:vif_details']) levels = 1