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,
**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)

View File

@ -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():

View File

@ -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()

View File

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

View File

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

View File

@ -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):

View File

@ -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 = []

View File

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

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.