summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Benton <kevin@benton.pub>2017-03-03 08:51:33 -0800
committerKevin Benton <kevin@benton.pub>2017-04-02 04:09:52 -0700
commitfc563eaabec392281be90bd2472bed4f6506cb40 (patch)
tree1f017cb98dd627b160d642015946b446ee35733d
parentaeee7a5c908122074dc9c5117d5a5747fc4784d2 (diff)
deepcopy binding and binding levels avoid expiration
Perform a deepcopy on the sqla objects passed into the PortContext so we get detached versions of them safe to reference forever. This is necessary because the PortContexts outlive the transaction context managers they are creating in which means an object can be expired and result in a query after a commit (e.g. in bind_port_if_needed) that will fail and result in an exception. This required a few additional explicit session.merge calls to deal with cases where touching the mech context was implicitly expected to modify the DB state on the next commit. Closes-Bug: #1669528 Change-Id: Ib5ba2daa80acba53c082bade1f61a3ee44ca41fc
Notes
Notes (review): Verified+1: Mellanox CI <mlnx-openstack-ci@dev.mellanox.co.il> Code-Review+2: garyk <gkotton@vmware.com> Code-Review+1: Rossella Sblendido <rsblendido@suse.com> Code-Review+1: Manjeet Singh Bhatia <manjeet.s.bhatia@intel.com> Code-Review+2: Miguel Lavalle <malavall@us.ibm.com> Workflow+1: Miguel Lavalle <malavall@us.ibm.com> Verified+2: Jenkins Submitted-by: Jenkins Submitted-at: Fri, 07 Apr 2017 23:54:37 +0000 Reviewed-on: https://review.openstack.org/441231 Project: openstack/neutron Branch: refs/heads/master
-rw-r--r--neutron/plugins/ml2/driver_context.py8
-rw-r--r--neutron/plugins/ml2/plugin.py13
-rw-r--r--neutron/tests/unit/plugins/ml2/test_plugin.py7
-rw-r--r--neutron/tests/unit/plugins/ml2/test_port_binding.py3
4 files changed, 24 insertions, 7 deletions
diff --git a/neutron/plugins/ml2/driver_context.py b/neutron/plugins/ml2/driver_context.py
index 1286ccf..860647d 100644
--- a/neutron/plugins/ml2/driver_context.py
+++ b/neutron/plugins/ml2/driver_context.py
@@ -13,6 +13,8 @@
13# License for the specific language governing permissions and limitations 13# License for the specific language governing permissions and limitations
14# under the License. 14# under the License.
15 15
16import copy
17
16from neutron_lib.api.definitions import portbindings 18from neutron_lib.api.definitions import portbindings
17from neutron_lib import constants 19from neutron_lib import constants
18from oslo_log import log 20from oslo_log import log
@@ -95,8 +97,10 @@ class PortContext(MechanismDriverContext, api.PortContext):
95 self._original_port = original_port 97 self._original_port = original_port
96 self._network_context = NetworkContext(plugin, plugin_context, 98 self._network_context = NetworkContext(plugin, plugin_context,
97 network) if network else None 99 network) if network else None
98 self._binding = binding 100 # NOTE(kevinbenton): these copys can go away once we are working with
99 self._binding_levels = binding_levels 101 # OVO objects here instead of native SQLA objects.
102 self._binding = copy.deepcopy(binding)
103 self._binding_levels = copy.deepcopy(binding_levels)
100 self._segments_to_bind = None 104 self._segments_to_bind = None
101 self._new_bound_segment = None 105 self._new_bound_segment = None
102 self._next_segments_to_bind = None 106 self._next_segments_to_bind = None
diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py
index 17a729d..a02f54b 100644
--- a/neutron/plugins/ml2/plugin.py
+++ b/neutron/plugins/ml2/plugin.py
@@ -13,6 +13,8 @@
13# License for the specific language governing permissions and limitations 13# License for the specific language governing permissions and limitations
14# under the License. 14# under the License.
15 15
16import copy
17
16from eventlet import greenthread 18from eventlet import greenthread
17from neutron_lib.api.definitions import portbindings 19from neutron_lib.api.definitions import portbindings
18from neutron_lib.api.definitions import provider_net 20from neutron_lib.api.definitions import provider_net
@@ -339,6 +341,10 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
339 binding.host = '' 341 binding.host = ''
340 342
341 self._update_port_dict_binding(port, binding) 343 self._update_port_dict_binding(port, binding)
344 # merging here brings binding changes into the session so they can be
345 # committed since the binding attached to the context is detached from
346 # the session
347 plugin_context.session.merge(binding)
342 return changes 348 return changes
343 349
344 @db_api.retry_db_errors 350 @db_api.retry_db_errors
@@ -507,6 +513,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
507 cur_binding.host) 513 cur_binding.host)
508 db.set_binding_levels(plugin_context, 514 db.set_binding_levels(plugin_context,
509 bind_context._binding_levels) 515 bind_context._binding_levels)
516 # refresh context with a snapshot of updated state
517 cur_context._binding = copy.deepcopy(cur_binding)
510 cur_context._binding_levels = bind_context._binding_levels 518 cur_context._binding_levels = bind_context._binding_levels
511 519
512 # Update PortContext's port dictionary to reflect the 520 # Update PortContext's port dictionary to reflect the
@@ -1337,6 +1345,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
1337 self._update_port_dict_binding(port, binding) 1345 self._update_port_dict_binding(port, binding)
1338 binding.host = attrs and attrs.get(portbindings.HOST_ID) 1346 binding.host = attrs and attrs.get(portbindings.HOST_ID)
1339 binding.router_id = attrs and attrs.get('device_id') 1347 binding.router_id = attrs and attrs.get('device_id')
1348 # merge into session to reflect changes
1349 plugin_context.session.merge(binding)
1340 1350
1341 @utils.transaction_guard 1351 @utils.transaction_guard
1342 @db_api.retry_if_session_inactive() 1352 @db_api.retry_if_session_inactive()
@@ -1557,8 +1567,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
1557 context, port['id'], host) 1567 context, port['id'], host)
1558 if not binding: 1568 if not binding:
1559 return 1569 return
1560 binding['status'] = status 1570 binding.status = status
1561 binding.update(binding)
1562 updated = True 1571 updated = True
1563 1572
1564 if (updated and 1573 if (updated and
diff --git a/neutron/tests/unit/plugins/ml2/test_plugin.py b/neutron/tests/unit/plugins/ml2/test_plugin.py
index 880ae4b..e33014b 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,
1606 plugin = directory.get_plugin() 1606 plugin = directory.get_plugin()
1607 binding = plugin._get_port( 1607 binding = plugin._get_port(
1608 self.context, port['port']['id']).port_binding 1608 self.context, port['port']['id']).port_binding
1609 binding['host'] = 'test' 1609 with self.context.session.begin(subtransactions=True):
1610 binding.host = 'test'
1610 mech_context = driver_context.PortContext( 1611 mech_context = driver_context.PortContext(
1611 plugin, self.context, port['port'], 1612 plugin, self.context, port['port'],
1612 plugin.get_network(self.context, port['port']['network_id']), 1613 plugin.get_network(self.context, port['port']['network_id']),
@@ -1614,7 +1615,9 @@ class TestMl2PortBinding(Ml2PluginV2TestCase,
1614 with mock.patch('neutron.plugins.ml2.plugin.Ml2Plugin.' 1615 with mock.patch('neutron.plugins.ml2.plugin.Ml2Plugin.'
1615 '_update_port_dict_binding') as update_mock: 1616 '_update_port_dict_binding') as update_mock:
1616 attrs = {portbindings.HOST_ID: None} 1617 attrs = {portbindings.HOST_ID: None}
1617 plugin._process_port_binding(mech_context, attrs) 1618 self.assertEqual('test', binding.host)
1619 with self.context.session.begin(subtransactions=True):
1620 plugin._process_port_binding(mech_context, attrs)
1618 self.assertTrue(update_mock.mock_calls) 1621 self.assertTrue(update_mock.mock_calls)
1619 self.assertEqual('', binding.host) 1622 self.assertEqual('', binding.host)
1620 1623
diff --git a/neutron/tests/unit/plugins/ml2/test_port_binding.py b/neutron/tests/unit/plugins/ml2/test_port_binding.py
index f773104..3007c11 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
18from neutron_lib import constants as const 18from neutron_lib import constants as const
19from neutron_lib import context 19from neutron_lib import context
20from neutron_lib.plugins import directory 20from neutron_lib.plugins import directory
21from oslo_serialization import jsonutils
21 22
22from neutron.conf.plugins.ml2.drivers import driver_type 23from neutron.conf.plugins.ml2.drivers import driver_type
23from neutron.plugins.ml2 import config 24from neutron.plugins.ml2 import config
@@ -201,7 +202,7 @@ class PortBindingTestCase(test_plugin.NeutronDbPluginV2TestCase):
201 port_id=original_port['id'], 202 port_id=original_port['id'],
202 host=original_port['binding:host_id'], 203 host=original_port['binding:host_id'],
203 vnic_type=original_port['binding:vnic_type'], 204 vnic_type=original_port['binding:vnic_type'],
204 profile=original_port['binding:profile'], 205 profile=jsonutils.dumps(original_port['binding:profile']),
205 vif_type=original_port['binding:vif_type'], 206 vif_type=original_port['binding:vif_type'],
206 vif_details=original_port['binding:vif_details']) 207 vif_details=original_port['binding:vif_details'])
207 levels = 1 208 levels = 1