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 <anindita.das@intel.com>
Co-Authored-By: Slawek Kaplonski <slawek@kaplonski.pl>
This commit is contained in:
Shashank Kumar Shankar
2016-10-04 13:28:10 -05:00
committed by Slawek Kaplonski
parent 317cdbf408
commit cfec395b8f
9 changed files with 88 additions and 51 deletions

View File

@@ -407,6 +407,16 @@ class Port(base.NeutronDbObject):
return super(Port, cls).get_objects(context, _pager, validate_filters, return super(Port, cls).get_objects(context, _pager, validate_filters,
**kwargs) **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 @classmethod
def modify_fields_to_db(cls, fields): def modify_fields_to_db(cls, fields):
result = super(Port, cls).modify_fields_to_db(fields) result = super(Port, cls).modify_fields_to_db(fields)

View File

@@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from debtcollector import removals
from neutron_lib.api.definitions import portbindings from neutron_lib.api.definitions import portbindings
from neutron_lib.callbacks import events from neutron_lib.callbacks import events
from neutron_lib.callbacks import registry 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 import api as db_api
from neutron.db.models import securitygroup as sg_models from neutron.db.models import securitygroup as sg_models
from neutron.db import models_v2 from neutron.db import models_v2
from neutron.objects import base as objects_base
from neutron.objects import ports as port_obj from neutron.objects import ports as port_obj
from neutron.plugins.ml2 import models from neutron.plugins.ml2 import models
from neutron.services.segments import exceptions as seg_exc 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): def set_binding_levels(context, levels):
if levels: if levels:
for level in levels: for level in levels:
level.persist_state_to_session(context.session) level.create()
LOG.debug("For port %(port_id)s, host %(host)s, " LOG.debug("For port %(port_id)s, host %(host)s, "
"set binding levels %(levels)s", "set binding levels %(levels)s",
{'port_id': levels[0].port_id, {'port_id': levels[0].port_id,
@@ -63,6 +65,10 @@ def set_binding_levels(context, levels):
LOG.debug("Attempted to set empty binding 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 @db_api.context_manager.reader
def get_binding_levels(context, port_id, host): def get_binding_levels(context, port_id, host):
if host: if host:
@@ -78,12 +84,25 @@ def get_binding_levels(context, port_id, host):
return result 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 @db_api.context_manager.writer
def clear_binding_levels(context, port_id, host): def clear_binding_levels(context, port_id, host):
if host: if host:
for l in (context.session.query(models.PortBindingLevel). port_obj.PortBindingLevel.delete_objects(
filter_by(port_id=port_id, host=host)): context, port_id=port_id, host=host)
context.session.delete(l)
LOG.debug("For port %(port_id)s, host %(host)s, " LOG.debug("For port %(port_id)s, host %(host)s, "
"cleared binding levels", "cleared binding levels",
{'port_id': port_id, {'port_id': port_id,
@@ -322,20 +341,15 @@ def _prevent_segment_delete_with_port_bound(resource, event, trigger,
return return
with db_api.context_manager.reader.using(context): with db_api.context_manager.reader.using(context):
segment_id = segment['id'] port_ids = port_obj.Port.get_port_ids_filter_by_segment_id(
query = context.session.query(models_v2.Port.id) context, segment_id=segment['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]
# There are still some ports in the segment, segment should not be deleted # There are still some ports in the segment, segment should not be deleted
# TODO(xiaohhui): Should we delete the dhcp port automatically here? # TODO(xiaohhui): Should we delete the dhcp port automatically here?
if port_ids: if port_ids:
reason = _("The segment is still bound with port(s) " reason = _("The segment is still bound with port(s) "
"%s") % ", ".join(port_ids) "%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(): def subscribe():

View File

@@ -132,8 +132,7 @@ class PortContext(MechanismDriverContext, api.PortContext):
# NOTE(kevinbenton): InstanceSnapshot can go away once we are working # NOTE(kevinbenton): InstanceSnapshot can go away once we are working
# with OVO objects instead of native SQLA objects. # with OVO objects instead of native SQLA objects.
self._binding = InstanceSnapshot(binding) self._binding = InstanceSnapshot(binding)
self._binding_levels = [InstanceSnapshot(l) self._binding_levels = binding_levels or []
for l in (binding_levels or [])]
self._segments_to_bind = None self._segments_to_bind = None
self._new_bound_segment = None self._new_bound_segment = None
self._next_segments_to_bind = None self._next_segments_to_bind = None
@@ -159,7 +158,9 @@ class PortContext(MechanismDriverContext, api.PortContext):
self._binding_levels = [] self._binding_levels = []
def _push_binding_level(self, binding_level): 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): def _pop_binding_level(self):
return self._binding_levels.pop() return self._binding_levels.pop()

View File

@@ -32,8 +32,8 @@ from neutron._i18n import _
from neutron.conf.plugins.ml2 import config from neutron.conf.plugins.ml2 import config
from neutron.db import api as db_api from neutron.db import api as db_api
from neutron.db import segments_db 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.common import exceptions as ml2_exc
from neutron.plugins.ml2 import models
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
@@ -783,12 +783,15 @@ class MechanismManager(stevedore.named.NamedExtensionManager):
driver.obj.bind_port(context) driver.obj.bind_port(context)
segment = context._new_bound_segment segment = context._new_bound_segment
if segment: if segment:
context._push_binding_level( pbl_obj = ports.PortBindingLevel(
models.PortBindingLevel(port_id=port_id, context._plugin_context,
port_id=port_id,
host=context.host, host=context.host,
level=level, level=level,
driver=driver.name, driver=driver.name,
segment_id=segment)) segment_id=segment
)
context._push_binding_level(pbl_obj)
next_segments = context._next_segments_to_bind next_segments = context._next_segments_to_bind
if next_segments: if next_segments:
# Continue binding another level. # Continue binding another level.

View File

@@ -487,7 +487,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
return new_context return new_context
def _commit_port_binding(self, orig_context, bind_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'] port_id = orig_context.current['id']
plugin_context = orig_context._plugin_context plugin_context = orig_context._plugin_context
orig_binding = orig_context._binding orig_binding = orig_context._binding
@@ -584,6 +585,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
else: else:
cur_context_binding.vif_type = new_binding.vif_type cur_context_binding.vif_type = new_binding.vif_type
cur_context_binding.vif_details = new_binding.vif_details cur_context_binding.vif_details = new_binding.vif_details
if update_binding_levels:
db.clear_binding_levels(plugin_context, port_id, db.clear_binding_levels(plugin_context, port_id,
cur_binding.host) cur_binding.host)
db.set_binding_levels(plugin_context, db.set_binding_levels(plugin_context,
@@ -1375,7 +1377,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
network = self.get_network(context, original_port['network_id']) network = self.get_network(context, original_port['network_id'])
need_port_update_notify |= self._update_extra_dhcp_opts_on_port( need_port_update_notify |= self._update_extra_dhcp_opts_on_port(
context, id, port, updated_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 # one of the operations above may have altered the model call
# _make_port_dict again to ensure latest state is reflected so mech # _make_port_dict again to ensure latest state is reflected so mech
# drivers, callback handlers, and the API caller see latest state. # drivers, callback handlers, and the API caller see latest state.
@@ -1411,7 +1413,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
dist_binding_list = db.get_distributed_port_bindings(context, dist_binding_list = db.get_distributed_port_bindings(context,
id) id)
for dist_binding in dist_binding_list: for dist_binding in dist_binding_list:
levels = db.get_binding_levels(context, id, levels = db.get_binding_level_objs(context, id,
dist_binding.host) dist_binding.host)
dist_mech_context = driver_context.PortContext( dist_mech_context = driver_context.PortContext(
self, context, updated_port, network, self, context, updated_port, network,
@@ -1517,7 +1519,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
context, id, host, router_id=device_id) context, id, host, router_id=device_id)
network = self.get_network(context, network = self.get_network(context,
orig_port['network_id']) 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, mech_context = driver_context.PortContext(self,
context, orig_port, network, context, orig_port, network,
binding, levels, original_port=orig_port) binding, levels, original_port=orig_port)
@@ -1581,8 +1583,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
bindings = db.get_distributed_port_bindings(context, bindings = db.get_distributed_port_bindings(context,
id) id)
for bind in bindings: for bind in bindings:
levels = db.get_binding_levels(context, id, levels = db.get_binding_level_objs(context, id, bind.host)
bind.host)
kwargs['bind'] = bind kwargs['bind'] = bind
kwargs['levels'] = levels kwargs['levels'] = levels
registry.notify(resources.PORT, events.PRECOMMIT_DELETE, 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) self.mechanism_manager.delete_port_precommit(mech_context)
bound_mech_contexts.append(mech_context) bound_mech_contexts.append(mech_context)
else: else:
levels = db.get_binding_levels(context, id, levels = db.get_binding_level_objs(context, id, binding.host)
binding.host)
kwargs['bind'] = None kwargs['bind'] = None
kwargs['levels'] = levels kwargs['levels'] = levels
registry.notify(resources.PORT, events.PRECOMMIT_DELETE, 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", LOG.error("Binding info for DVR port %s not found",
port_id) port_id)
return None return None
levels = db.get_binding_levels(plugin_context, levels = db.get_binding_level_objs(
port_db.id, host) plugin_context, port_db.id, host)
port_context = driver_context.PortContext( port_context = driver_context.PortContext(
self, plugin_context, port, network, binding, levels) self, plugin_context, port, network, binding, levels)
else: else:
@@ -1685,8 +1685,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
"it might have been deleted already.", "it might have been deleted already.",
port_id) port_id)
return return
levels = db.get_binding_levels(plugin_context, port_db.id, levels = db.get_binding_level_objs(
binding.host) plugin_context, port_db.id, binding.host)
port_context = driver_context.PortContext( port_context = driver_context.PortContext(
self, plugin_context, port, network, binding, levels) 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( binding = p_utils.get_port_binding_by_status_and_host(
port.port_bindings, const.ACTIVE, raise_if_not_found=True, port.port_bindings, const.ACTIVE, raise_if_not_found=True,
port_id=port_id) 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( mech_context = driver_context.PortContext(
self, context, updated_port, network, binding, levels, self, context, updated_port, network, binding, levels,
original_port=original_port) original_port=original_port)
@@ -1840,7 +1841,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
port.status = db.generate_distributed_port_status(context, port.status = db.generate_distributed_port_status(context,
port['id']) port['id'])
updated_port = self._make_port_dict(port) 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( mech_context = (driver_context.PortContext(
self, context, updated_port, network, self, context, updated_port, network,
binding, levels, original_port=original_port)) binding, levels, original_port=original_port))
@@ -2184,7 +2185,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
raise n_exc.PortBindingNotFound(port_id=port_id, host=host) raise n_exc.PortBindingNotFound(port_id=port_id, host=host)
network = self.get_network(context, port_db['network_id']) network = self.get_network(context, port_db['network_id'])
port_dict = self._make_port_dict(port_db) port_dict = self._make_port_dict(port_db)
levels = db.get_binding_levels(context, port_id, levels = db.get_binding_level_objs(context, port_id,
active_binding.host) active_binding.host)
original_context = driver_context.PortContext(self, context, original_context = driver_context.PortContext(self, context,
port_dict, network, port_dict, network,
@@ -2197,7 +2198,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
context, port_dict['id'], context, port_dict['id'],
{port_def.RESOURCE_NAME: {port_def.RESOURCE_NAME:
{'status': const.PORT_STATUS_DOWN}}) {'status': const.PORT_STATUS_DOWN}})
levels = db.get_binding_levels(context, port_id, levels = db.get_binding_level_objs(context, port_id,
inactive_binding.host) inactive_binding.host)
bind_context = driver_context.PortContext(self, context, port_dict, bind_context = driver_context.PortContext(self, context, port_dict,
network, network,
@@ -2205,7 +2206,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
for count in range(MAX_BIND_TRIES): for count in range(MAX_BIND_TRIES):
cur_context, _, try_again = self._commit_port_binding( cur_context, _, try_again = self._commit_port_binding(
original_context, bind_context, need_notify=True, original_context, bind_context, need_notify=True,
try_again=True) try_again=True, update_binding_levels=False)
if not try_again: if not try_again:
self.notifier.binding_deactivate(context, port_id, self.notifier.binding_deactivate(context, port_id,
active_binding.host, active_binding.host,

View File

@@ -199,6 +199,7 @@ class TestCacheBackedPluginApi(base.BaseTestCase):
profile={})], profile={})],
binding_levels=[ports.PortBindingLevel(port_id=self._port_id, binding_levels=[ports.PortBindingLevel(port_id=self._port_id,
host='host1', host='host1',
level=0,
segment=self._segment)]) segment=self._segment)])
def test__legacy_notifier_resource_delete(self): def test__legacy_notifier_resource_delete(self):

View File

@@ -197,9 +197,6 @@ class PortBindingLevelIfaceObjTestCase(
super(PortBindingLevelIfaceObjTestCase, self).setUp() super(PortBindingLevelIfaceObjTestCase, self).setUp()
self.pager_map[self._test_class.obj_name()] = ( self.pager_map[self._test_class.obj_name()] = (
obj_base.Pager(sorts=[('port_id', True), ('level', True)])) 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( class PortBindingLevelDbObjectTestCase(
@@ -232,10 +229,13 @@ class PortDbObjectTestCase(obj_test_base.BaseDbObjectTestCase,
def setUp(self): def setUp(self):
super(PortDbObjectTestCase, self).setUp() super(PortDbObjectTestCase, self).setUp()
network_id = self._create_test_network_id() 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) subnet_id = self._create_test_subnet_id(network_id)
self.update_obj_fields( self.update_obj_fields(
{'network_id': network_id, {'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): def test_security_group_ids(self):
groups = [] groups = []

View File

@@ -858,7 +858,7 @@ class TestMl2PortsV2(test_plugin.TestPortsV2, Ml2PluginV2TestCase):
ctx = context.get_admin_context() ctx = context.get_admin_context()
plugin = directory.get_plugin() plugin = directory.get_plugin()
with self.port() as port: 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: return_value=[]) as mock_gbl:
port_id = port['port']['id'] port_id = port['port']['id']
short_id = port_id[:11] short_id = port_id[:11]

View File

@@ -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.