Revert "Integration of (Distributed) Port Binding OVO"

This reverts commit febeaf5d40.

This patch broke postgres tempest jobs, as well as introduced potential
race conditions in database layer because of mixed usage of old and new
engine facades.

Related-Bug: #1744829
Change-Id: Ic142ae7faf4e5f10cbdf761d7e6f3d442e94a3eb
This commit is contained in:
Ihar Hrachyshka 2018-01-23 17:46:40 +00:00 committed by Miguel Lavalle
parent 03b1cca794
commit 906eda44d2
10 changed files with 200 additions and 264 deletions

View File

@ -27,7 +27,6 @@ from oslo_versionedobjects import base as obj_base
from oslo_versionedobjects import exception as obj_exception from oslo_versionedobjects import exception as obj_exception
from oslo_versionedobjects import fields as obj_fields from oslo_versionedobjects import fields as obj_fields
import six import six
from sqlalchemy import exc as sql_exc
from neutron._i18n import _ from neutron._i18n import _
from neutron.db import api as db_api from neutron.db import api as db_api
@ -319,11 +318,7 @@ def _detach_db_obj(func):
# TODO(ihrachys) consider refreshing just changed attributes # TODO(ihrachys) consider refreshing just changed attributes
self.obj_context.session.refresh(self.db_obj) self.obj_context.session.refresh(self.db_obj)
# detach the model so that consequent fetches don't reuse it # detach the model so that consequent fetches don't reuse it
try: self.obj_context.session.expunge(self.db_obj)
self.obj_context.session.expunge(self.db_obj)
except sql_exc.InvalidRequestError:
# already detached
pass
return res return res
return decorator return decorator
@ -350,8 +345,6 @@ class DeclarativeObject(abc.ABCMeta):
if key in cls.fields or key in cls.obj_extra_fields: if key in cls.fields or key in cls.obj_extra_fields:
fields_no_update_set.add(key) fields_no_update_set.add(key)
cls.fields_no_update = list(fields_no_update_set) cls.fields_no_update = list(fields_no_update_set)
if name in ('PortBinding', 'DistributedPortBinding'):
cls.fields_no_update.remove('host')
model = getattr(cls, 'db_model', None) model = getattr(cls, 'db_model', None)
if model: if model:
@ -502,12 +495,7 @@ class NeutronDbObject(NeutronObject):
obj = cls(context) obj = cls(context)
obj.from_db_object(db_obj) obj.from_db_object(db_obj)
# detach the model so that consequent fetches don't reuse it # detach the model so that consequent fetches don't reuse it
# TODO(lujinluo): remove the try block when Port OVO is in place. context.session.expunge(obj.db_obj)
try:
context.session.expunge(obj.db_obj)
except sql_exc.InvalidRequestError:
# already detached
pass
return obj return obj
def obj_load_attr(self, attrname): def obj_load_attr(self, attrname):

View File

@ -36,22 +36,6 @@ class PortBindingBase(base.NeutronDbObject):
'Port': {'port_id': 'id'}, 'Port': {'port_id': 'id'},
} }
def update(self):
"""Override to handle host update in Port Binding.
Delete old Port Binding entry, update the hostname and create new
Port Binding with all values saved in DB.
This is done due to host being a primary key, and OVO is not able
to update primary key fields.
"""
if self.db_obj and self.host != self.db_obj.host:
with self.obj_context.session.begin(subtransactions=True):
old_obj = self._load_object(self.obj_context, self.db_obj)
old_obj.delete()
self._changed_fields = set(self.fields.keys())
self.create()
else:
super(PortBindingBase, self).update()
@classmethod @classmethod
def modify_fields_to_db(cls, fields): def modify_fields_to_db(cls, fields):
result = super(PortBindingBase, cls).modify_fields_to_db(fields) result = super(PortBindingBase, cls).modify_fields_to_db(fields)
@ -85,7 +69,7 @@ class PortBinding(PortBindingBase):
fields = { fields = {
'port_id': common_types.UUIDField(), 'port_id': common_types.UUIDField(),
'host': obj_fields.StringField(default=''), 'host': obj_fields.StringField(),
'profile': common_types.DictOfMiscValuesField(), 'profile': common_types.DictOfMiscValuesField(),
'vif_type': obj_fields.StringField(), 'vif_type': obj_fields.StringField(),
'vif_details': common_types.DictOfMiscValuesField(nullable=True), 'vif_details': common_types.DictOfMiscValuesField(nullable=True),

View File

@ -18,8 +18,8 @@ from neutron_lib.callbacks import events
from neutron_lib.callbacks import registry from neutron_lib.callbacks import registry
from neutron_lib.callbacks import resources from neutron_lib.callbacks import resources
from neutron_lib import constants as n_const from neutron_lib import constants as n_const
from neutron_lib.objects import exceptions
from neutron_lib.plugins import directory from neutron_lib.plugins import directory
from oslo_db import exception as db_exc
from oslo_log import log from oslo_log import log
from oslo_utils import uuidutils from oslo_utils import uuidutils
import six import six
@ -31,7 +31,6 @@ 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 ports as port_obj from neutron.objects import ports as port_obj
from neutron.objects import utils as obj_utils
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
@ -43,10 +42,11 @@ MAX_PORTS_PER_QUERY = 500
@db_api.context_manager.writer @db_api.context_manager.writer
def add_port_binding(context, port_id): def add_port_binding(context, port_id):
binding = port_obj.PortBinding( record = models.PortBinding(
context, port_id=port_id, vif_type=portbindings.VIF_TYPE_UNBOUND) port_id=port_id,
binding.create() vif_type=portbindings.VIF_TYPE_UNBOUND)
return binding context.session.add(record)
return record
@db_api.context_manager.writer @db_api.context_manager.writer
@ -91,32 +91,35 @@ def clear_binding_levels(context, port_id, host):
def ensure_distributed_port_binding(context, port_id, host, router_id=None): def ensure_distributed_port_binding(context, port_id, host, router_id=None):
binding_obj = port_obj.DistributedPortBinding.get_object( with db_api.context_manager.reader.using(context):
context, port_id=port_id, host=host) record = (context.session.query(models.DistributedPortBinding).
if binding_obj: filter_by(port_id=port_id, host=host).first())
return binding_obj if record:
return record
try: try:
binding_obj = port_obj.DistributedPortBinding( with db_api.context_manager.writer.using(context):
context, record = models.DistributedPortBinding(
port_id=port_id, port_id=port_id,
host=host, host=host,
router_id=router_id, router_id=router_id,
vif_type=portbindings.VIF_TYPE_UNBOUND, vif_type=portbindings.VIF_TYPE_UNBOUND,
vnic_type=portbindings.VNIC_NORMAL, vnic_type=portbindings.VNIC_NORMAL,
status=n_const.PORT_STATUS_DOWN) status=n_const.PORT_STATUS_DOWN)
binding_obj.create() context.session.add(record)
return binding_obj return record
except exceptions.NeutronDbObjectDuplicateEntry: except db_exc.DBDuplicateEntry:
LOG.debug("Distributed Port %s already bound", port_id) LOG.debug("Distributed Port %s already bound", port_id)
return port_obj.DistributedPortBinding.get_object( with db_api.context_manager.reader.using(context):
context, port_id=port_id, host=host) return (context.session.query(models.DistributedPortBinding).
filter_by(port_id=port_id, host=host).one())
def delete_distributed_port_binding_if_stale(context, binding): def delete_distributed_port_binding_if_stale(context, binding):
if not binding.router_id and binding.status == n_const.PORT_STATUS_DOWN: if not binding.router_id and binding.status == n_const.PORT_STATUS_DOWN:
LOG.debug("Distributed port: Deleting binding %s", binding) with db_api.context_manager.writer.using(context):
binding.delete() LOG.debug("Distributed port: Deleting binding %s", binding)
context.session.delete(binding)
def get_port(context, port_id): def get_port(context, port_id):
@ -209,27 +212,29 @@ def make_port_dict_with_security_groups(port, sec_groups):
def get_port_binding_host(context, port_id): def get_port_binding_host(context, port_id):
binding = port_obj.PortBinding.get_objects( try:
context, port_id=obj_utils.StringStarts(port_id)) with db_api.context_manager.reader.using(context):
if not binding: query = (context.session.query(models.PortBinding).
filter(models.PortBinding.port_id.startswith(port_id)).
one())
except exc.NoResultFound:
LOG.debug("No binding found for port %(port_id)s", LOG.debug("No binding found for port %(port_id)s",
{'port_id': port_id}) {'port_id': port_id})
return return
if len(binding) > 1: except exc.MultipleResultsFound:
LOG.error("Multiple ports have port_id starting with %s", LOG.error("Multiple ports have port_id starting with %s",
port_id) port_id)
return return
return binding[0].host return query.host
@db_api.context_manager.reader @db_api.context_manager.reader
def generate_distributed_port_status(context, port_id): def generate_distributed_port_status(context, port_id):
# an OR'ed value of status assigned to parent port from the # an OR'ed value of status assigned to parent port from the
# distributedportbinding bucket # distributedportbinding bucket
query = context.session.query(models.DistributedPortBinding)
final_status = n_const.PORT_STATUS_BUILD final_status = n_const.PORT_STATUS_BUILD
bindings = port_obj.DistributedPortBinding.get_objects(context, for bind in query.filter(models.DistributedPortBinding.port_id == port_id):
port_id=port_id)
for bind in bindings:
if bind.status == n_const.PORT_STATUS_ACTIVE: if bind.status == n_const.PORT_STATUS_ACTIVE:
return bind.status return bind.status
elif bind.status == n_const.PORT_STATUS_DOWN: elif bind.status == n_const.PORT_STATUS_DOWN:
@ -238,10 +243,10 @@ def generate_distributed_port_status(context, port_id):
def get_distributed_port_binding_by_host(context, port_id, host): def get_distributed_port_binding_by_host(context, port_id, host):
bindings = port_obj.DistributedPortBinding.get_objects( with db_api.context_manager.reader.using(context):
context, port_id=obj_utils.StringStarts(port_id), host=host) binding = (context.session.query(models.DistributedPortBinding).
binding = bindings.pop() if bindings else None filter(models.DistributedPortBinding.port_id.startswith(port_id),
models.DistributedPortBinding.host == host).first())
if not binding: if not binding:
LOG.debug("No binding for distributed port %(port_id)s with host " LOG.debug("No binding for distributed port %(port_id)s with host "
"%(host)s", {'port_id': port_id, 'host': host}) "%(host)s", {'port_id': port_id, 'host': host})
@ -249,8 +254,10 @@ def get_distributed_port_binding_by_host(context, port_id, host):
def get_distributed_port_bindings(context, port_id): def get_distributed_port_bindings(context, port_id):
bindings = port_obj.DistributedPortBinding.get_objects( with db_api.context_manager.reader.using(context):
context, port_id=obj_utils.StringStarts(port_id)) bindings = (context.session.query(models.DistributedPortBinding).
filter(models.DistributedPortBinding.port_id.startswith(
port_id)).all())
if not bindings: if not bindings:
LOG.debug("No bindings for distributed port %s", port_id) LOG.debug("No bindings for distributed port %s", port_id)
return bindings return bindings

View File

@ -17,6 +17,7 @@ from neutron_lib.api.definitions import portbindings
from neutron_lib import constants from neutron_lib import constants
from neutron_lib.plugins.ml2 import api from neutron_lib.plugins.ml2 import api
from oslo_log import log from oslo_log import log
from oslo_serialization import jsonutils
import sqlalchemy import sqlalchemy
from neutron.db import segments_db from neutron.db import segments_db
@ -123,7 +124,9 @@ class PortContext(MechanismDriverContext, api.PortContext):
else: else:
self._network_context = NetworkContext( self._network_context = NetworkContext(
plugin, plugin_context, network) if network else None plugin, plugin_context, network) if network else None
self._binding = binding # 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) self._binding_levels = [InstanceSnapshot(l)
for l in (binding_levels or [])] for l in (binding_levels or [])]
self._segments_to_bind = None self._segments_to_bind = None
@ -292,7 +295,7 @@ class PortContext(MechanismDriverContext, api.PortContext):
# TODO(rkukura) Verify binding allowed, segment in network # TODO(rkukura) Verify binding allowed, segment in network
self._new_bound_segment = segment_id self._new_bound_segment = segment_id
self._binding.vif_type = vif_type self._binding.vif_type = vif_type
self._binding.vif_details = vif_details self._binding.vif_details = jsonutils.dumps(vif_details)
self._new_port_status = status self._new_port_status = status
def continue_binding(self, segment_id, next_segments_to_bind): def continue_binding(self, segment_id, next_segments_to_bind):

View File

@ -125,6 +125,6 @@ class DistributedPortBinding(model_base.BASEV2):
models_v2.Port, models_v2.Port,
load_on_pending=True, load_on_pending=True,
backref=orm.backref("distributed_port_binding", backref=orm.backref("distributed_port_binding",
lazy='joined', lazy='subquery',
cascade='delete')) cascade='delete'))
revises_on_change = ('port', ) revises_on_change = ('port', )

View File

@ -83,7 +83,6 @@ from neutron.db import subnet_service_type_db_models as service_type_db
from neutron.db import vlantransparent_db from neutron.db import vlantransparent_db
from neutron.extensions import providernet as provider from neutron.extensions import providernet as provider
from neutron.extensions import vlantransparent from neutron.extensions import vlantransparent
from neutron.objects import ports as obj_port
from neutron.plugins.common import utils as p_utils from neutron.plugins.common import utils as p_utils
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 db from neutron.plugins.ml2 import db
@ -317,6 +316,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
port = mech_context.current port = mech_context.current
port_id = port['id'] port_id = port['id']
changes = False changes = False
host = const.ATTR_NOT_SPECIFIED host = const.ATTR_NOT_SPECIFIED
if attrs and portbindings.HOST_ID in attrs: if attrs and portbindings.HOST_ID in attrs:
host = attrs.get(portbindings.HOST_ID) or '' host = attrs.get(portbindings.HOST_ID) or ''
@ -340,9 +340,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
if profile not in (None, const.ATTR_NOT_SPECIFIED, if profile not in (None, const.ATTR_NOT_SPECIFIED,
self._get_profile(binding)): self._get_profile(binding)):
binding.profile = profile binding.profile = jsonutils.dumps(profile)
if (len(jsonutils.dumps(binding.profile)) > if len(binding.profile) > models.BINDING_PROFILE_LEN:
models.BINDING_PROFILE_LEN):
msg = _("binding:profile value too large") msg = _("binding:profile value too large")
raise exc.InvalidInput(error_message=msg) raise exc.InvalidInput(error_message=msg)
changes = True changes = True
@ -350,8 +349,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
# Unbind the port if needed. # Unbind the port if needed.
if changes: if changes:
binding.vif_type = portbindings.VIF_TYPE_UNBOUND binding.vif_type = portbindings.VIF_TYPE_UNBOUND
binding.vif_details = None binding.vif_details = ''
binding.update()
db.clear_binding_levels(plugin_context, port_id, original_host) db.clear_binding_levels(plugin_context, port_id, original_host)
mech_context._clear_binding_levels() mech_context._clear_binding_levels()
port['status'] = const.PORT_STATUS_DOWN port['status'] = const.PORT_STATUS_DOWN
@ -361,14 +359,13 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE: if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE:
binding.vif_type = portbindings.VIF_TYPE_UNBOUND binding.vif_type = portbindings.VIF_TYPE_UNBOUND
binding.vif_details = None binding.vif_details = ''
db.clear_binding_levels(plugin_context, port_id, original_host) db.clear_binding_levels(plugin_context, port_id, original_host)
mech_context._clear_binding_levels() mech_context._clear_binding_levels()
binding.host = '' binding.host = ''
binding.update()
self._update_port_dict_binding(port, binding) self._update_port_dict_binding(port, binding)
binding.update() binding.persist_state_to_session(plugin_context.session)
return changes return changes
@db_api.retry_db_errors @db_api.retry_db_errors
@ -440,15 +437,12 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
# transaction. # transaction.
port = orig_context.current port = orig_context.current
orig_binding = orig_context._binding orig_binding = orig_context._binding
profile = orig_binding.profile or {} new_binding = models.PortBinding(
new_binding = obj_port.PortBinding(
orig_context._plugin_context,
port_id=orig_binding.port_id,
host=orig_binding.host, host=orig_binding.host,
vnic_type=orig_binding.vnic_type, vnic_type=orig_binding.vnic_type,
profile=profile, profile=orig_binding.profile,
vif_type=portbindings.VIF_TYPE_UNBOUND, vif_type=portbindings.VIF_TYPE_UNBOUND,
vif_details=None vif_details=''
) )
self._update_port_dict_binding(port, new_binding) self._update_port_dict_binding(port, new_binding)
new_context = driver_context.PortContext( new_context = driver_context.PortContext(
@ -485,13 +479,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
# mechanism driver update_port_*commit() calls. # mechanism driver update_port_*commit() calls.
try: try:
port_db = self._get_port(plugin_context, port_id) port_db = self._get_port(plugin_context, port_id)
plugin_context.session.refresh(port_db) cur_binding = port_db.port_binding
# TODO(korzen) replace get_objects with port_obj.binding when
# Port OVO is integrated in _get_port
bindings = obj_port.PortBinding.get_objects(
plugin_context, port_id=port_db.id,
status=const.ACTIVE)
cur_binding = bindings.pop() if bindings else None
except exc.PortNotFound: except exc.PortNotFound:
port_db, cur_binding = None, None port_db, cur_binding = None, None
if not port_db or not cur_binding: if not port_db or not cur_binding:
@ -558,10 +546,10 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
cur_binding.host) cur_binding.host)
db.set_binding_levels(plugin_context, db.set_binding_levels(plugin_context,
bind_context._binding_levels) bind_context._binding_levels)
cur_context._binding = cur_binding # refresh context with a snapshot of updated state
cur_context._binding = driver_context.InstanceSnapshot(
cur_binding)
cur_context._binding_levels = bind_context._binding_levels cur_context._binding_levels = bind_context._binding_levels
cur_binding.update()
plugin_context.session.refresh(port_db)
# Update PortContext's port dictionary to reflect the # Update PortContext's port dictionary to reflect the
# updated binding state. # updated binding state.
@ -612,10 +600,6 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
def _get_vif_details(self, binding): def _get_vif_details(self, binding):
if binding.vif_details: if binding.vif_details:
try: try:
# TODO(lujinluo): remove isinstance check once we switch to
# objects for all operations.
if isinstance(binding.vif_details, dict):
return binding.vif_details
return jsonutils.loads(binding.vif_details) return jsonutils.loads(binding.vif_details)
except Exception: except Exception:
LOG.error("Serialized vif_details DB value '%(value)s' " LOG.error("Serialized vif_details DB value '%(value)s' "
@ -627,10 +611,6 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
def _get_profile(self, binding): def _get_profile(self, binding):
if binding.profile: if binding.profile:
try: try:
# TODO(lujinluo): remove isinstance check once we switch to
# objects for all operations.
if isinstance(binding.profile, dict):
return binding.profile
return jsonutils.loads(binding.profile) return jsonutils.loads(binding.profile)
except Exception: except Exception:
LOG.error("Serialized profile DB value '%(value)s' for " LOG.error("Serialized profile DB value '%(value)s' for "
@ -1314,12 +1294,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
original_port=original_port) original_port=original_port)
with db_api.context_manager.writer.using(context): with db_api.context_manager.writer.using(context):
port_db = self._get_port(context, id) port_db = self._get_port(context, id)
context.session.refresh(port_db) binding = port_db.port_binding
# TODO(korzen) replace _get_objects with port_obj.binding when
# Port OVO is integrated in _get_port
bindings = obj_port.PortBinding.get_objects(
context, port_id=port_db.id)
binding = bindings.pop() if bindings else None
if not binding: if not binding:
raise exc.PortNotFound(port_id=id) raise exc.PortNotFound(port_id=id)
mac_address_updated = self._check_mac_update_allowed( mac_address_updated = self._check_mac_update_allowed(
@ -1458,21 +1433,19 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
binding = mech_context._binding binding = mech_context._binding
port = mech_context.current port = mech_context.current
port_id = port['id'] port_id = port['id']
clear_host = None
if binding.vif_type != portbindings.VIF_TYPE_UNBOUND: if binding.vif_type != portbindings.VIF_TYPE_UNBOUND:
binding.vif_details = None binding.vif_details = ''
binding.vif_type = portbindings.VIF_TYPE_UNBOUND binding.vif_type = portbindings.VIF_TYPE_UNBOUND
if binding.host: if binding.host:
db.clear_binding_levels(plugin_context, port_id, binding.host) db.clear_binding_levels(plugin_context, port_id, binding.host)
clear_host = '' binding.host = ''
self._update_port_dict_binding(port, binding) self._update_port_dict_binding(port, binding)
new_host = attrs and attrs.get(portbindings.HOST_ID) or clear_host binding.host = attrs and attrs.get(portbindings.HOST_ID)
binding.router_id = attrs and attrs.get('device_id') binding.router_id = attrs and attrs.get('device_id')
if new_host: # merge into session to reflect changes
binding.host = new_host binding.persist_state_to_session(plugin_context.session)
binding.update()
@utils.transaction_guard @utils.transaction_guard
@db_api.retry_if_session_inactive() @db_api.retry_if_session_inactive()
@ -1543,11 +1516,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
with db_api.context_manager.writer.using(context): with db_api.context_manager.writer.using(context):
try: try:
port_db = self._get_port(context, id) port_db = self._get_port(context, id)
# TODO(korzen) replace get_objects with port_obj.binding when binding = port_db.port_binding
# Port OVO is integrated in _get_port
bindings = obj_port.PortBinding.get_objects(
context, port_id=port_db.id)
binding = bindings.pop() if bindings else None
except exc.PortNotFound: except exc.PortNotFound:
LOG.debug("The port '%s' was deleted", id) LOG.debug("The port '%s' was deleted", id)
return return
@ -1791,7 +1760,6 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
return return
if binding.status != status: if binding.status != status:
binding.status = status binding.status = status
binding.update()
updated = True updated = True
if (updated and if (updated and

View File

@ -62,7 +62,7 @@ object_data = {
'NetworkPortSecurity': '1.0-b30802391a87945ee9c07582b4ff95e3', 'NetworkPortSecurity': '1.0-b30802391a87945ee9c07582b4ff95e3',
'NetworkSegment': '1.0-57b7f2960971e3b95ded20cbc59244a8', 'NetworkSegment': '1.0-57b7f2960971e3b95ded20cbc59244a8',
'Port': '1.1-5bf48d12a7bf7f5b7a319e8003b437a5', 'Port': '1.1-5bf48d12a7bf7f5b7a319e8003b437a5',
'PortBinding': '1.0-0ad9727c4e72d609d5b4f70bcd3bc727', 'PortBinding': '1.0-3306deeaa6deb01e33af06777d48d578',
'PortBindingLevel': '1.0-de66a4c61a083b8f34319fa9dde5b060', 'PortBindingLevel': '1.0-de66a4c61a083b8f34319fa9dde5b060',
'PortDataPlaneStatus': '1.0-25be74bda46c749653a10357676c0ab2', 'PortDataPlaneStatus': '1.0-25be74bda46c749653a10357676c0ab2',
'PortDNS': '1.1-c5ca2dc172bdd5fafee3fc986d1d7023', 'PortDNS': '1.1-c5ca2dc172bdd5fafee3fc986d1d7023',

View File

@ -23,6 +23,7 @@ from neutron_lib import context
from neutron_lib.plugins.ml2 import api from neutron_lib.plugins.ml2 import api
from oslo_utils import uuidutils from oslo_utils import uuidutils
from sqlalchemy.orm import exc from sqlalchemy.orm import exc
from sqlalchemy.orm import query
from neutron.db import api as db_api from neutron.db import api as db_api
from neutron.db import db_base_plugin_v2 from neutron.db import db_base_plugin_v2
@ -32,6 +33,7 @@ from neutron.db import segments_db
from neutron.objects import network as network_obj from neutron.objects import network as network_obj
from neutron.objects import ports as port_obj from neutron.objects import ports as port_obj
from neutron.plugins.ml2 import db as ml2_db from neutron.plugins.ml2 import db as ml2_db
from neutron.plugins.ml2 import models
from neutron.tests.unit import testlib_api from neutron.tests.unit import testlib_api
@ -62,8 +64,10 @@ class Ml2DBTestCase(testlib_api.SqlTestCase):
return port return port
def _setup_neutron_portbinding(self, port_id, vif_type, host): def _setup_neutron_portbinding(self, port_id, vif_type, host):
port_obj.PortBinding( with db_api.context_manager.writer.using(self.ctx):
self.ctx, port_id=port_id, vif_type=vif_type, host=host).create() self.ctx.session.add(models.PortBinding(port_id=port_id,
vif_type=vif_type,
host=host))
@staticmethod @staticmethod
def _sort_segments(segments): def _sort_segments(segments):
@ -314,45 +318,44 @@ class Ml2DvrDBTestCase(testlib_api.SqlTestCase):
def _setup_distributed_binding(self, network_id, def _setup_distributed_binding(self, network_id,
port_id, router_id, host_id): port_id, router_id, host_id):
binding_obj = port_obj.DistributedPortBinding( with db_api.context_manager.writer.using(self.ctx):
self.ctx, record = models.DistributedPortBinding(
port_id=port_id, port_id=port_id,
host=host_id, host=host_id,
router_id=router_id, router_id=router_id,
vif_type=portbindings.VIF_TYPE_UNBOUND, vif_type=portbindings.VIF_TYPE_UNBOUND,
vnic_type=portbindings.VNIC_NORMAL, vnic_type=portbindings.VNIC_NORMAL,
status='DOWN') status='DOWN')
binding_obj.create() self.ctx.session.add(record)
return binding_obj return record
def test_ensure_distributed_port_binding_deals_with_db_duplicate(self): def test_ensure_distributed_port_binding_deals_with_db_duplicate(self):
network_id = uuidutils.generate_uuid() network_id = uuidutils.generate_uuid()
port_id = uuidutils.generate_uuid() port_id = uuidutils.generate_uuid()
router_id = uuidutils.generate_uuid() router_id = 'foo_router_id'
host_id = uuidutils.generate_uuid() host_id = 'foo_host_id'
self._setup_neutron_network(network_id, [port_id]) self._setup_neutron_network(network_id, [port_id])
dpb = self._setup_distributed_binding(network_id, port_id, self._setup_distributed_binding(network_id, port_id,
router_id, host_id) router_id, host_id)
with mock.patch.object(port_obj.DistributedPortBinding, with mock.patch.object(query.Query, 'first') as query_first:
'get_object') as get_object: query_first.return_value = []
get_object.side_effect = [None, dpb] with mock.patch.object(ml2_db.LOG, 'debug') as log_trace:
binding = ml2_db.ensure_distributed_port_binding( binding = ml2_db.ensure_distributed_port_binding(
self.ctx, port_id, host_id, router_id) self.ctx, port_id, host_id, router_id)
self.assertTrue(get_object.called) self.assertTrue(query_first.called)
self.assertTrue(log_trace.called)
self.assertEqual(port_id, binding.port_id) self.assertEqual(port_id, binding.port_id)
def test_ensure_distributed_port_binding(self): def test_ensure_distributed_port_binding(self):
network_id = uuidutils.generate_uuid() network_id = uuidutils.generate_uuid()
expected_port_id = uuidutils.generate_uuid() port_id = uuidutils.generate_uuid()
self._setup_neutron_network(network_id, [expected_port_id]) self._setup_neutron_network(network_id, [port_id])
router = self._setup_neutron_router() router = self._setup_neutron_router()
ml2_db.ensure_distributed_port_binding( ml2_db.ensure_distributed_port_binding(
self.ctx, expected_port_id, 'foo_host', router.id) self.ctx, port_id, 'foo_host', router.id)
actual_objs = port_obj.DistributedPortBinding.get_objects( expected = (self.ctx.session.query(models.DistributedPortBinding).
self.ctx, port_id=expected_port_id) filter_by(port_id=port_id).one())
self.assertEqual(1, len(actual_objs)) self.assertEqual(port_id, expected.port_id)
actual_obj = actual_objs.pop()
self.assertEqual(expected_port_id, actual_obj.port_id)
def test_ensure_distributed_port_binding_multiple_bindings(self): def test_ensure_distributed_port_binding_multiple_bindings(self):
network_id = uuidutils.generate_uuid() network_id = uuidutils.generate_uuid()
@ -363,9 +366,9 @@ class Ml2DvrDBTestCase(testlib_api.SqlTestCase):
self.ctx, port_id, 'foo_host_1', router.id) self.ctx, port_id, 'foo_host_1', router.id)
ml2_db.ensure_distributed_port_binding( ml2_db.ensure_distributed_port_binding(
self.ctx, port_id, 'foo_host_2', router.id) self.ctx, port_id, 'foo_host_2', router.id)
count_objs = port_obj.DistributedPortBinding.count( bindings = (self.ctx.session.query(models.DistributedPortBinding).
self.ctx, port_id=port_id) filter_by(port_id=port_id).all())
self.assertEqual(2, count_objs) self.assertEqual(2, len(bindings))
def test_delete_distributed_port_binding_if_stale(self): def test_delete_distributed_port_binding_if_stale(self):
network_id = uuidutils.generate_uuid() network_id = uuidutils.generate_uuid()
@ -374,23 +377,21 @@ class Ml2DvrDBTestCase(testlib_api.SqlTestCase):
binding = self._setup_distributed_binding( binding = self._setup_distributed_binding(
network_id, port_id, None, 'foo_host_id') network_id, port_id, None, 'foo_host_id')
ml2_db.delete_distributed_port_binding_if_stale(self.ctx, binding) ml2_db.delete_distributed_port_binding_if_stale(self.ctx,
binding)
obj_exists = port_obj.DistributedPortBinding.objects_exist( count = (self.ctx.session.query(models.DistributedPortBinding).
self.ctx, port_id=binding.port_id) filter_by(port_id=binding.port_id).count())
self.assertFalse(obj_exists) self.assertFalse(count)
def test_get_distributed_port_binding_by_host_not_found(self): def test_get_distributed_port_binding_by_host_not_found(self):
port_id = uuidutils.generate_uuid()
host_id = uuidutils.generate_uuid()
port = ml2_db.get_distributed_port_binding_by_host( port = ml2_db.get_distributed_port_binding_by_host(
self.ctx, port_id, host_id) self.ctx, 'foo_port_id', 'foo_host_id')
self.assertIsNone(port) self.assertIsNone(port)
def test_get_distributed_port_bindings_not_found(self): def test_get_distributed_port_bindings_not_found(self):
port = ml2_db.get_distributed_port_bindings(self.ctx, port = ml2_db.get_distributed_port_bindings(self.ctx,
uuidutils.generate_uuid()) 'foo_port_id')
self.assertEqual(0, len(port)) self.assertFalse(len(port))
def test_get_distributed_port_bindings(self): def test_get_distributed_port_bindings(self):
network_id = uuidutils.generate_uuid() network_id = uuidutils.generate_uuid()
@ -411,9 +412,8 @@ class Ml2DvrDBTestCase(testlib_api.SqlTestCase):
network_obj.Network(self.ctx, id=network_id).create() network_obj.Network(self.ctx, id=network_id).create()
with db_api.context_manager.writer.using(self.ctx): with db_api.context_manager.writer.using(self.ctx):
device_owner = constants.DEVICE_OWNER_DVR_INTERFACE device_owner = constants.DEVICE_OWNER_DVR_INTERFACE
port_id = uuidutils.generate_uuid()
port = models_v2.Port( port = models_v2.Port(
id=port_id, id='port_id',
network_id=network_id, network_id=network_id,
mac_address='00:11:22:33:44:55', mac_address='00:11:22:33:44:55',
admin_state_up=True, admin_state_up=True,
@ -421,22 +421,25 @@ class Ml2DvrDBTestCase(testlib_api.SqlTestCase):
device_id='device_id', device_id='device_id',
device_owner=device_owner) device_owner=device_owner)
self.ctx.session.add(port) self.ctx.session.add(port)
binding_kwarg = { binding_kwarg = {
'port_id': port_id, 'port_id': 'port_id',
'host': 'host', 'host': 'host',
'vif_type': portbindings.VIF_TYPE_UNBOUND, 'vif_type': portbindings.VIF_TYPE_UNBOUND,
'vnic_type': portbindings.VNIC_NORMAL, 'vnic_type': portbindings.VNIC_NORMAL,
'router_id': 'router_id', 'router_id': 'router_id',
'status': constants.PORT_STATUS_DOWN 'status': constants.PORT_STATUS_DOWN
} }
port_obj.DistributedPortBinding(self.ctx, **binding_kwarg).create() self.ctx.session.add(models.DistributedPortBinding(
binding_kwarg['host'] = 'another-host' **binding_kwarg))
port_obj.DistributedPortBinding(self.ctx, **binding_kwarg).create() binding_kwarg['host'] = 'another-host'
self.ctx.session.add(models.DistributedPortBinding(
**binding_kwarg))
with warnings.catch_warnings(record=True) as warning_list: with warnings.catch_warnings(record=True) as warning_list:
with db_api.context_manager.writer.using(self.ctx): with db_api.context_manager.writer.using(self.ctx):
self.ctx.session.delete(port) self.ctx.session.delete(port)
self.assertEqual( self.assertEqual(
[], warning_list, [], warning_list,
'Warnings: %s' % ';'.join([str(w) for w in warning_list])) 'Warnings: %s' % ';'.join([str(w) for w in warning_list]))
bindings = ml2_db.get_distributed_port_bindings(self.ctx, port_id) ports = ml2_db.get_distributed_port_bindings(self.ctx,
self.assertEqual(0, len(bindings)) 'port_id')
self.assertEqual(0, len(ports))

View File

@ -47,7 +47,6 @@ from neutron.db import provisioning_blocks
from neutron.db import segments_db from neutron.db import segments_db
from neutron.extensions import multiprovidernet as mpnet from neutron.extensions import multiprovidernet as mpnet
from neutron.objects import base as base_obj from neutron.objects import base as base_obj
from neutron.objects import ports as obj_port
from neutron.objects import router as l3_obj from neutron.objects import router as l3_obj
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 db as ml2_db from neutron.plugins.ml2 import db as ml2_db
@ -1692,10 +1691,9 @@ class TestMl2PortBinding(Ml2PluginV2TestCase,
# create a port and delete it so we have an expired mechanism context # create a port and delete it so we have an expired mechanism context
with self.port() as port: with self.port() as port:
plugin = directory.get_plugin() plugin = directory.get_plugin()
binding = obj_port.PortBinding.get_object( binding = plugin._get_port(self.context,
self.context, port_id=port['port']['id'], host='') port['port']['id']).port_binding
binding.host = 'test' binding['host'] = 'test'
binding.update()
mech_context = driver_context.PortContext( mech_context = driver_context.PortContext(
plugin, self.context, port['port'], plugin, self.context, port['port'],
plugin.get_network(self.context, port['port']['network_id']), plugin.get_network(self.context, port['port']['network_id']),
@ -1714,11 +1712,10 @@ class TestMl2PortBinding(Ml2PluginV2TestCase,
def _create_port_and_bound_context(self, port_vif_type, bound_vif_type): def _create_port_and_bound_context(self, port_vif_type, bound_vif_type):
with self.port() as port: with self.port() as port:
plugin = directory.get_plugin() plugin = directory.get_plugin()
binding = obj_port.PortBinding.get_object( binding = plugin._get_port(
self.context, port_id=port['port']['id'], host='') self.context, port['port']['id']).port_binding
binding.host = 'fake_host' binding['host'] = 'fake_host'
binding['vif_type'] = port_vif_type binding['vif_type'] = port_vif_type
binding.update()
# Generates port context to be used before the bind. # Generates port context to be used before the bind.
port_context = driver_context.PortContext( port_context = driver_context.PortContext(
plugin, self.context, port['port'], plugin, self.context, port['port'],
@ -1830,10 +1827,10 @@ class TestMl2PortBinding(Ml2PluginV2TestCase,
def test_update_port_binding_host_id_none(self): def test_update_port_binding_host_id_none(self):
with self.port() as port: with self.port() as port:
plugin = directory.get_plugin() plugin = directory.get_plugin()
binding = obj_port.PortBinding.get_object( binding = plugin._get_port(
self.context, port_id=port['port']['id'], host='') self.context, port['port']['id']).port_binding
binding.host = 'test' with self.context.session.begin(subtransactions=True):
binding.update() binding.host = 'test'
mech_context = driver_context.PortContext( mech_context = driver_context.PortContext(
plugin, self.context, port['port'], plugin, self.context, port['port'],
plugin.get_network(self.context, port['port']['network_id']), plugin.get_network(self.context, port['port']['network_id']),
@ -1844,18 +1841,15 @@ class TestMl2PortBinding(Ml2PluginV2TestCase,
self.assertEqual('test', binding.host) self.assertEqual('test', binding.host)
with self.context.session.begin(subtransactions=True): with self.context.session.begin(subtransactions=True):
plugin._process_port_binding(mech_context, attrs) plugin._process_port_binding(mech_context, attrs)
updated_binding = obj_port.PortBinding.get_objects(self.context,
port_id=port['port']['id']).pop()
self.assertTrue(update_mock.mock_calls) self.assertTrue(update_mock.mock_calls)
self.assertEqual('', updated_binding.host) self.assertEqual('', binding.host)
def test_update_port_binding_host_id_not_changed(self): def test_update_port_binding_host_id_not_changed(self):
with self.port() as port: with self.port() as port:
plugin = directory.get_plugin() plugin = directory.get_plugin()
binding = obj_port.PortBinding.get_object( binding = plugin._get_port(
self.context, port_id=port['port']['id'], host='') self.context, port['port']['id']).port_binding
binding.host = 'test' binding['host'] = 'test'
binding.update()
mech_context = driver_context.PortContext( mech_context = driver_context.PortContext(
plugin, self.context, port['port'], plugin, self.context, port['port'],
plugin.get_network(self.context, port['port']['network_id']), plugin.get_network(self.context, port['port']['network_id']),
@ -1868,34 +1862,30 @@ class TestMl2PortBinding(Ml2PluginV2TestCase,
self.assertEqual('test', binding.host) self.assertEqual('test', binding.host)
def test_process_distributed_port_binding_update_router_id(self): def test_process_distributed_port_binding_update_router_id(self):
with self.port() as port: host_id = 'host'
host_id = 'host' binding = models.DistributedPortBinding(
ctxt = context.get_admin_context() port_id='port_id',
binding_obj = obj_port.DistributedPortBinding( host=host_id,
ctxt, router_id='old_router_id',
port_id=port['port']['id'], vif_type=portbindings.VIF_TYPE_OVS,
host=host_id, vnic_type=portbindings.VNIC_NORMAL,
profile={}, status=constants.PORT_STATUS_DOWN)
router_id='old_router_id', plugin = directory.get_plugin()
vif_type=portbindings.VIF_TYPE_OVS, mock_network = {'id': 'net_id'}
vnic_type=portbindings.VNIC_NORMAL, mock_port = {'id': 'port_id'}
status=constants.PORT_STATUS_DOWN) ctxt = context.get_admin_context()
binding_obj.create() new_router_id = 'new_router'
plugin = directory.get_plugin() attrs = {'device_id': new_router_id, portbindings.HOST_ID: host_id}
mock_network = {'id': 'net_id'} with mock.patch.object(plugin, '_update_port_dict_binding'):
mock_port = {'id': 'port_id'} with mock.patch.object(segments_db, 'get_network_segments',
new_router_id = 'new_router' return_value=[]):
attrs = {'device_id': new_router_id, portbindings.HOST_ID: host_id} mech_context = driver_context.PortContext(
with mock.patch.object(plugin, '_update_port_dict_binding'): self, ctxt, mock_port, mock_network, binding, None)
with mock.patch.object(segments_db, 'get_network_segments', plugin._process_distributed_port_binding(mech_context,
return_value=[]): ctxt, attrs)
mech_context = driver_context.PortContext( self.assertEqual(new_router_id,
self, ctxt, mock_port, mock_network, binding_obj, None) mech_context._binding.router_id)
plugin._process_distributed_port_binding(mech_context, self.assertEqual(host_id, mech_context._binding.host)
ctxt, attrs)
self.assertEqual(new_router_id,
mech_context._binding.router_id)
self.assertEqual(host_id, mech_context._binding.host)
def test_update_distributed_port_binding_on_concurrent_port_delete(self): def test_update_distributed_port_binding_on_concurrent_port_delete(self):
plugin = directory.get_plugin() plugin = directory.get_plugin()
@ -1926,20 +1916,9 @@ class TestMl2PortBinding(Ml2PluginV2TestCase,
def test__bind_port_original_port_set(self): def test__bind_port_original_port_set(self):
plugin = directory.get_plugin() plugin = directory.get_plugin()
plugin.mechanism_manager = mock.Mock() plugin.mechanism_manager = mock.Mock()
mock_port = {'id': uuidutils.generate_uuid()} mock_port = {'id': 'port_id'}
context = mock.Mock() context = mock.Mock()
binding_obj = obj_port.DistributedPortBinding(
mock.MagicMock(),
port_id=mock_port['id'],
host='vm_host',
profile={},
router_id='old_router_id',
vif_type='',
vnic_type=portbindings.VNIC_NORMAL,
status=constants.PORT_STATUS_DOWN)
binding_obj.create()
context.network.current = {'id': 'net_id'} context.network.current = {'id': 'net_id'}
context._binding = binding_obj
context.original = mock_port context.original = mock_port
with mock.patch.object(plugin, '_update_port_dict_binding'), \ with mock.patch.object(plugin, '_update_port_dict_binding'), \
mock.patch.object(segments_db, 'get_network_segments', mock.patch.object(segments_db, 'get_network_segments',
@ -2615,15 +2594,13 @@ class TestFaultyMechansimDriver(Ml2PluginV2FaultyDriverTestCase):
def test_update_distributed_router_interface_port(self): def test_update_distributed_router_interface_port(self):
"""Test validate distributed router interface update succeeds.""" """Test validate distributed router interface update succeeds."""
host_id = 'host' host_id = 'host'
binding_obj = obj_port.DistributedPortBinding( binding = models.DistributedPortBinding(
mock.MagicMock(), port_id='port_id',
port_id=uuidutils.generate_uuid(), host=host_id,
host=host_id, router_id='old_router_id',
router_id='old_router_id', vif_type=portbindings.VIF_TYPE_OVS,
vif_type=portbindings.VIF_TYPE_OVS, vnic_type=portbindings.VNIC_NORMAL,
vnic_type=portbindings.VNIC_NORMAL, status=constants.PORT_STATUS_DOWN)
status=constants.PORT_STATUS_DOWN)
binding_obj.create()
with mock.patch.object( with mock.patch.object(
mech_test.TestMechanismDriver, mech_test.TestMechanismDriver,
'update_port_postcommit', 'update_port_postcommit',
@ -2633,7 +2610,7 @@ class TestFaultyMechansimDriver(Ml2PluginV2FaultyDriverTestCase):
'update_port_precommit') as port_pre,\ 'update_port_precommit') as port_pre,\
mock.patch.object( mock.patch.object(
ml2_db, 'get_distributed_port_bindings') as dist_bindings: ml2_db, 'get_distributed_port_bindings') as dist_bindings:
dist_bindings.return_value = [binding_obj] dist_bindings.return_value = [binding]
port_pre.return_value = True port_pre.return_value = True
with self.network() as network: with self.network() as network:
with self.subnet(network=network) as subnet: with self.subnet(network=network) as subnet:
@ -2856,10 +2833,9 @@ class TestML2Segments(Ml2PluginV2TestCase):
# add writer here to make sure that the following operations are # add writer here to make sure that the following operations are
# performed in the same session # performed in the same session
with db_api.context_manager.writer.using(self.context): with db_api.context_manager.writer.using(self.context):
binding = obj_port.PortBinding.get_object( binding = plugin._get_port(
self.context, port_id=port['port']['id'], host='') self.context, port['port']['id']).port_binding
binding.host = 'host-ovs-no_filter' binding['host'] = 'host-ovs-no_filter'
binding.update()
mech_context = driver_context.PortContext( mech_context = driver_context.PortContext(
plugin, self.context, port['port'], plugin, self.context, port['port'],
plugin.get_network(self.context, plugin.get_network(self.context,

View File

@ -19,10 +19,11 @@ from neutron_lib import constants as const
from neutron_lib import context from neutron_lib import context
from neutron_lib.plugins import directory from neutron_lib.plugins import directory
from oslo_config import cfg from oslo_config import cfg
from oslo_serialization import jsonutils
from neutron.conf.plugins.ml2.drivers import driver_type from neutron.conf.plugins.ml2.drivers import driver_type
from neutron.objects import ports as obj_port
from neutron.plugins.ml2 import driver_context from neutron.plugins.ml2 import driver_context
from neutron.plugins.ml2 import models as ml2_models
from neutron.tests.unit.db import test_db_base_plugin_v2 as test_plugin from neutron.tests.unit.db import test_db_base_plugin_v2 as test_plugin
@ -110,8 +111,10 @@ class PortBindingTestCase(test_plugin.NeutronDbPluginV2TestCase):
ctx = context.get_admin_context() ctx = context.get_admin_context()
with self.port(name='name') as port: with self.port(name='name') as port:
# emulating concurrent binding deletion # emulating concurrent binding deletion
obj_port.PortBinding.delete_objects( with ctx.session.begin():
ctx, port_id=port['port']['id']) for item in (ctx.session.query(ml2_models.PortBinding).
filter_by(port_id=port['port']['id'])):
ctx.session.delete(item)
self.assertIsNone( self.assertIsNone(
self.plugin.get_bound_port_context(ctx, port['port']['id'])) self.plugin.get_bound_port_context(ctx, port['port']['id']))
@ -188,9 +191,13 @@ class PortBindingTestCase(test_plugin.NeutronDbPluginV2TestCase):
attrs['binding:host_id'] = 'host2' attrs['binding:host_id'] = 'host2'
updated_port = attrs.copy() updated_port = attrs.copy()
network = {'id': attrs['network_id']} network = {'id': attrs['network_id']}
binding = obj_port.PortBinding.get_object( binding = ml2_models.PortBinding(
ctx, port_id=original_port['id'], port_id=original_port['id'],
host=original_port['binding:host_id']) host=original_port['binding:host_id'],
vnic_type=original_port['binding:vnic_type'],
profile=jsonutils.dumps(original_port['binding:profile']),
vif_type=original_port['binding:vif_type'],
vif_details=original_port['binding:vif_details'])
levels = [] levels = []
mech_context = driver_context.PortContext( mech_context = driver_context.PortContext(
plugin, ctx, updated_port, network, binding, levels, plugin, ctx, updated_port, network, binding, levels,