From cfec395b8f00e9aff8cc3a370a3cda1a66cd2af3 Mon Sep 17 00:00:00 2001 From: Shashank Kumar Shankar Date: Tue, 4 Oct 2016 13:28:10 -0500 Subject: [PATCH] Integration of Port Binding Level OVO This patch integrates Port Binding Level OVO in /plugin/ml2/db.py and introduces context instead of session for usage in object operations. Change-Id: Ifa779f5f70a7502bd96b34d64a84d272af2a6886 Partially-Implements: blueprint adopt-oslo-versioned-objects-for-db Co-Authored-By: Anindita Das Co-Authored-By: Slawek Kaplonski --- neutron/objects/ports.py | 10 ++++ neutron/plugins/ml2/db.py | 38 +++++++++----- neutron/plugins/ml2/driver_context.py | 7 +-- neutron/plugins/ml2/managers.py | 17 ++++--- neutron/plugins/ml2/plugin.py | 49 ++++++++++--------- neutron/tests/unit/agent/test_rpc.py | 1 + neutron/tests/unit/objects/test_ports.py | 8 +-- neutron/tests/unit/plugins/ml2/test_plugin.py | 2 +- ...ding_levels-function-84012e104ac572a1.yaml | 7 +++ 9 files changed, 88 insertions(+), 51 deletions(-) create mode 100644 releasenotes/notes/deprecate-get_binding_levels-function-84012e104ac572a1.yaml diff --git a/neutron/objects/ports.py b/neutron/objects/ports.py index 18f275a7c74..2b4e0d9e6b0 100644 --- a/neutron/objects/ports.py +++ b/neutron/objects/ports.py @@ -407,6 +407,16 @@ class Port(base.NeutronDbObject): return super(Port, cls).get_objects(context, _pager, validate_filters, **kwargs) + @classmethod + def get_port_ids_filter_by_segment_id(cls, context, segment_id): + query = context.session.query(models_v2.Port.id) + query = query.join( + ml2_models.PortBindingLevel, + ml2_models.PortBindingLevel.port_id == models_v2.Port.id) + query = query.filter( + ml2_models.PortBindingLevel.segment_id == segment_id) + return [p.id for p in query] + @classmethod def modify_fields_to_db(cls, fields): result = super(Port, cls).modify_fields_to_db(fields) diff --git a/neutron/plugins/ml2/db.py b/neutron/plugins/ml2/db.py index 83866a79149..57de8eb76f1 100644 --- a/neutron/plugins/ml2/db.py +++ b/neutron/plugins/ml2/db.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +from debtcollector import removals from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import events from neutron_lib.callbacks import registry @@ -30,6 +31,7 @@ from neutron._i18n import _ from neutron.db import api as db_api from neutron.db.models import securitygroup as sg_models from neutron.db import models_v2 +from neutron.objects import base as objects_base from neutron.objects import ports as port_obj from neutron.plugins.ml2 import models from neutron.services.segments import exceptions as seg_exc @@ -53,7 +55,7 @@ def add_port_binding(context, port_id): def set_binding_levels(context, levels): if levels: for level in levels: - level.persist_state_to_session(context.session) + level.create() LOG.debug("For port %(port_id)s, host %(host)s, " "set binding levels %(levels)s", {'port_id': levels[0].port_id, @@ -63,6 +65,10 @@ def set_binding_levels(context, levels): LOG.debug("Attempted to set empty binding levels") +@removals.remove( + version="Stein", removal_version="T", + message="Function get_binding_levels is deprecated. Please use " + "get_binding_level_objs instead as it makes use of OVOs.") @db_api.context_manager.reader def get_binding_levels(context, port_id, host): if host: @@ -78,12 +84,25 @@ def get_binding_levels(context, port_id, host): return result +@db_api.context_manager.reader +def get_binding_level_objs(context, port_id, host): + if host: + pager = objects_base.Pager(sorts=[('level', True)]) + port_bl_objs = port_obj.PortBindingLevel.get_objects( + context, _pager=pager, port_id=port_id, host=host) + LOG.debug("For port %(port_id)s, host %(host)s, " + "got binding levels %(levels)s", + {'port_id': port_id, + 'host': host, + 'levels': port_bl_objs}) + return port_bl_objs + + @db_api.context_manager.writer def clear_binding_levels(context, port_id, host): if host: - for l in (context.session.query(models.PortBindingLevel). - filter_by(port_id=port_id, host=host)): - context.session.delete(l) + port_obj.PortBindingLevel.delete_objects( + context, port_id=port_id, host=host) LOG.debug("For port %(port_id)s, host %(host)s, " "cleared binding levels", {'port_id': port_id, @@ -322,20 +341,15 @@ def _prevent_segment_delete_with_port_bound(resource, event, trigger, return with db_api.context_manager.reader.using(context): - segment_id = segment['id'] - query = context.session.query(models_v2.Port.id) - query = query.join( - models.PortBindingLevel, - models.PortBindingLevel.port_id == models_v2.Port.id) - query = query.filter(models.PortBindingLevel.segment_id == segment_id) - port_ids = [p.id for p in query] + port_ids = port_obj.Port.get_port_ids_filter_by_segment_id( + context, segment_id=segment['id']) # There are still some ports in the segment, segment should not be deleted # TODO(xiaohhui): Should we delete the dhcp port automatically here? if port_ids: reason = _("The segment is still bound with port(s) " "%s") % ", ".join(port_ids) - raise seg_exc.SegmentInUse(segment_id=segment_id, reason=reason) + raise seg_exc.SegmentInUse(segment_id=segment['id'], reason=reason) def subscribe(): diff --git a/neutron/plugins/ml2/driver_context.py b/neutron/plugins/ml2/driver_context.py index 91e1c41c5aa..c61982729e3 100644 --- a/neutron/plugins/ml2/driver_context.py +++ b/neutron/plugins/ml2/driver_context.py @@ -132,8 +132,7 @@ class PortContext(MechanismDriverContext, api.PortContext): # NOTE(kevinbenton): InstanceSnapshot can go away once we are working # with OVO objects instead of native SQLA objects. self._binding = InstanceSnapshot(binding) - self._binding_levels = [InstanceSnapshot(l) - for l in (binding_levels or [])] + self._binding_levels = binding_levels or [] self._segments_to_bind = None self._new_bound_segment = None self._next_segments_to_bind = None @@ -159,7 +158,9 @@ class PortContext(MechanismDriverContext, api.PortContext): self._binding_levels = [] def _push_binding_level(self, binding_level): - self._binding_levels.append(InstanceSnapshot(binding_level)) + # NOTE(slaweq): binding_level should be always OVO with no reference + # to DB object + self._binding_levels.append(binding_level) def _pop_binding_level(self): return self._binding_levels.pop() diff --git a/neutron/plugins/ml2/managers.py b/neutron/plugins/ml2/managers.py index 501552d72d5..954a19942fd 100644 --- a/neutron/plugins/ml2/managers.py +++ b/neutron/plugins/ml2/managers.py @@ -32,8 +32,8 @@ from neutron._i18n import _ from neutron.conf.plugins.ml2 import config from neutron.db import api as db_api from neutron.db import segments_db +from neutron.objects import ports from neutron.plugins.ml2.common import exceptions as ml2_exc -from neutron.plugins.ml2 import models LOG = log.getLogger(__name__) @@ -783,12 +783,15 @@ class MechanismManager(stevedore.named.NamedExtensionManager): driver.obj.bind_port(context) segment = context._new_bound_segment if segment: - context._push_binding_level( - models.PortBindingLevel(port_id=port_id, - host=context.host, - level=level, - driver=driver.name, - segment_id=segment)) + pbl_obj = ports.PortBindingLevel( + context._plugin_context, + port_id=port_id, + host=context.host, + level=level, + driver=driver.name, + segment_id=segment + ) + context._push_binding_level(pbl_obj) next_segments = context._next_segments_to_bind if next_segments: # Continue binding another level. diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py index 05e11c68dc7..4c4c1d2f3a6 100644 --- a/neutron/plugins/ml2/plugin.py +++ b/neutron/plugins/ml2/plugin.py @@ -487,7 +487,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, return new_context def _commit_port_binding(self, orig_context, bind_context, - need_notify, try_again): + need_notify, try_again, + update_binding_levels=True): port_id = orig_context.current['id'] plugin_context = orig_context._plugin_context orig_binding = orig_context._binding @@ -584,10 +585,11 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, else: cur_context_binding.vif_type = new_binding.vif_type cur_context_binding.vif_details = new_binding.vif_details - db.clear_binding_levels(plugin_context, port_id, - cur_binding.host) - db.set_binding_levels(plugin_context, - bind_context._binding_levels) + if update_binding_levels: + db.clear_binding_levels(plugin_context, port_id, + cur_binding.host) + db.set_binding_levels(plugin_context, + bind_context._binding_levels) # refresh context with a snapshot of updated state cur_context._binding = driver_context.InstanceSnapshot( cur_context_binding) @@ -1375,7 +1377,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, network = self.get_network(context, original_port['network_id']) need_port_update_notify |= self._update_extra_dhcp_opts_on_port( context, id, port, updated_port) - levels = db.get_binding_levels(context, id, binding.host) + levels = db.get_binding_level_objs(context, id, binding.host) # one of the operations above may have altered the model call # _make_port_dict again to ensure latest state is reflected so mech # drivers, callback handlers, and the API caller see latest state. @@ -1411,8 +1413,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, dist_binding_list = db.get_distributed_port_bindings(context, id) for dist_binding in dist_binding_list: - levels = db.get_binding_levels(context, id, - dist_binding.host) + levels = db.get_binding_level_objs(context, id, + dist_binding.host) dist_mech_context = driver_context.PortContext( self, context, updated_port, network, dist_binding, levels, original_port=original_port) @@ -1517,7 +1519,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, context, id, host, router_id=device_id) network = self.get_network(context, orig_port['network_id']) - levels = db.get_binding_levels(context, id, host) + levels = db.get_binding_level_objs(context, id, host) mech_context = driver_context.PortContext(self, context, orig_port, network, binding, levels, original_port=orig_port) @@ -1581,8 +1583,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, bindings = db.get_distributed_port_bindings(context, id) for bind in bindings: - levels = db.get_binding_levels(context, id, - bind.host) + levels = db.get_binding_level_objs(context, id, bind.host) kwargs['bind'] = bind kwargs['levels'] = levels registry.notify(resources.PORT, events.PRECOMMIT_DELETE, @@ -1592,8 +1593,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, self.mechanism_manager.delete_port_precommit(mech_context) bound_mech_contexts.append(mech_context) else: - levels = db.get_binding_levels(context, id, - binding.host) + levels = db.get_binding_level_objs(context, id, binding.host) kwargs['bind'] = None kwargs['levels'] = levels registry.notify(resources.PORT, events.PRECOMMIT_DELETE, @@ -1669,8 +1669,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, LOG.error("Binding info for DVR port %s not found", port_id) return None - levels = db.get_binding_levels(plugin_context, - port_db.id, host) + levels = db.get_binding_level_objs( + plugin_context, port_db.id, host) port_context = driver_context.PortContext( self, plugin_context, port, network, binding, levels) else: @@ -1685,8 +1685,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, "it might have been deleted already.", port_id) return - levels = db.get_binding_levels(plugin_context, port_db.id, - binding.host) + levels = db.get_binding_level_objs( + plugin_context, port_db.id, binding.host) port_context = driver_context.PortContext( self, plugin_context, port, network, binding, levels) @@ -1811,7 +1811,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, binding = p_utils.get_port_binding_by_status_and_host( port.port_bindings, const.ACTIVE, raise_if_not_found=True, port_id=port_id) - levels = db.get_binding_levels(context, port.id, binding.host) + levels = db.get_binding_level_objs( + context, port.id, binding.host) mech_context = driver_context.PortContext( self, context, updated_port, network, binding, levels, original_port=original_port) @@ -1840,7 +1841,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, port.status = db.generate_distributed_port_status(context, port['id']) updated_port = self._make_port_dict(port) - levels = db.get_binding_levels(context, port_id, host) + levels = db.get_binding_level_objs(context, port_id, host) mech_context = (driver_context.PortContext( self, context, updated_port, network, binding, levels, original_port=original_port)) @@ -2184,8 +2185,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, raise n_exc.PortBindingNotFound(port_id=port_id, host=host) network = self.get_network(context, port_db['network_id']) port_dict = self._make_port_dict(port_db) - levels = db.get_binding_levels(context, port_id, - active_binding.host) + levels = db.get_binding_level_objs(context, port_id, + active_binding.host) original_context = driver_context.PortContext(self, context, port_dict, network, active_binding, @@ -2197,15 +2198,15 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, context, port_dict['id'], {port_def.RESOURCE_NAME: {'status': const.PORT_STATUS_DOWN}}) - levels = db.get_binding_levels(context, port_id, - inactive_binding.host) + levels = db.get_binding_level_objs(context, port_id, + inactive_binding.host) bind_context = driver_context.PortContext(self, context, port_dict, network, inactive_binding, levels) for count in range(MAX_BIND_TRIES): cur_context, _, try_again = self._commit_port_binding( original_context, bind_context, need_notify=True, - try_again=True) + try_again=True, update_binding_levels=False) if not try_again: self.notifier.binding_deactivate(context, port_id, active_binding.host, diff --git a/neutron/tests/unit/agent/test_rpc.py b/neutron/tests/unit/agent/test_rpc.py index 71315c5c8f4..fe31d20d20f 100644 --- a/neutron/tests/unit/agent/test_rpc.py +++ b/neutron/tests/unit/agent/test_rpc.py @@ -199,6 +199,7 @@ class TestCacheBackedPluginApi(base.BaseTestCase): profile={})], binding_levels=[ports.PortBindingLevel(port_id=self._port_id, host='host1', + level=0, segment=self._segment)]) def test__legacy_notifier_resource_delete(self): diff --git a/neutron/tests/unit/objects/test_ports.py b/neutron/tests/unit/objects/test_ports.py index 4fe820c0653..ed87e24240b 100644 --- a/neutron/tests/unit/objects/test_ports.py +++ b/neutron/tests/unit/objects/test_ports.py @@ -197,9 +197,6 @@ class PortBindingLevelIfaceObjTestCase( super(PortBindingLevelIfaceObjTestCase, self).setUp() self.pager_map[self._test_class.obj_name()] = ( obj_base.Pager(sorts=[('port_id', True), ('level', True)])) - self.pager_map[network.NetworkSegment.obj_name()] = ( - obj_base.Pager( - sorts=[('network_id', True), ('segment_index', True)])) class PortBindingLevelDbObjectTestCase( @@ -232,10 +229,13 @@ class PortDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, def setUp(self): super(PortDbObjectTestCase, self).setUp() network_id = self._create_test_network_id() + segment_id = self._create_test_segment_id(network_id) subnet_id = self._create_test_subnet_id(network_id) self.update_obj_fields( {'network_id': network_id, - 'fixed_ips': {'subnet_id': subnet_id, 'network_id': network_id}}) + 'fixed_ips': {'subnet_id': subnet_id, + 'network_id': network_id}, + 'binding_levels': {'segment_id': segment_id}}) def test_security_group_ids(self): groups = [] diff --git a/neutron/tests/unit/plugins/ml2/test_plugin.py b/neutron/tests/unit/plugins/ml2/test_plugin.py index bfff0580cee..fb345db5913 100644 --- a/neutron/tests/unit/plugins/ml2/test_plugin.py +++ b/neutron/tests/unit/plugins/ml2/test_plugin.py @@ -858,7 +858,7 @@ class TestMl2PortsV2(test_plugin.TestPortsV2, Ml2PluginV2TestCase): ctx = context.get_admin_context() plugin = directory.get_plugin() with self.port() as port: - with mock.patch.object(ml2_db, 'get_binding_levels', + with mock.patch.object(ml2_db, 'get_binding_level_objs', return_value=[]) as mock_gbl: port_id = port['port']['id'] short_id = port_id[:11] diff --git a/releasenotes/notes/deprecate-get_binding_levels-function-84012e104ac572a1.yaml b/releasenotes/notes/deprecate-get_binding_levels-function-84012e104ac572a1.yaml new file mode 100644 index 00000000000..e4fdfdbe1fc --- /dev/null +++ b/releasenotes/notes/deprecate-get_binding_levels-function-84012e104ac572a1.yaml @@ -0,0 +1,7 @@ +--- +deprecations: + - | + Function ``get_binding_levels`` from ``neutron.plugins.ml2.db`` module is + deprecated and will be removed in the future. + New function ``get_binding_levels_objs`` should be used instead. + This new function returns ``PortBindingLevel`` OVO objects.