Merge "[OVN] Execute OVN migration transactions independently" into stable/xena

This commit is contained in:
Zuul 2021-11-22 22:30:52 +00:00 committed by Gerrit Code Review
commit aeb7d07fa0
3 changed files with 97 additions and 27 deletions

View File

@ -20,6 +20,7 @@ from neutron_lib.utils import net as net_utils
from oslo_log import log as logging
from oslo_utils import versionutils
from oslo_versionedobjects import fields as obj_fields
from sqlalchemy import and_
from neutron.common import _constants
from neutron.db.models import dns as dns_models
@ -84,6 +85,23 @@ class PortBinding(PortBindingBase):
primary_keys = ['port_id', 'host']
@classmethod
def get_port_id_and_host(cls, context, vif_type, vnic_type, status):
"""Returns only the port_id and the host of matching registers
This method returns only the primary keys of a "PortBinding" register,
reducing the query complexity and increasing the retrieval speed.
This query does not check the "PortBinding" owner or RBACs.
"""
with cls.db_context_reader(context):
query = context.session.query(cls.db_model.port_id,
cls.db_model.host)
query = query.filter(and_(
cls.db_model.vif_type == vif_type,
cls.db_model.vnic_type == vnic_type,
cls.db_model.status == status))
return query.all()
@base.NeutronObjectRegistry.register
class DistributedPortBinding(PortBindingBase):

View File

@ -131,3 +131,15 @@ class Trunk(base.NeutronDbObject):
_dict = super(Trunk, self).to_dict()
resource_extend.apply_funcs(trunk_def.TRUNKS, _dict, self.db_obj)
return _dict
@classmethod
def get_trunk_ids(cls, context):
"""Returns only the trunk IDs.
This method returns only the primary key ID, reducing the query
complexity and increasing the retrieval speed.
This query does not check the "Trunk" owner or RBACs.
"""
with cls.db_context_reader(context):
return [_id[0] for _id in
context.session.query(cls.db_model.id).all()]

View File

@ -16,6 +16,8 @@ from neutron_lib.api.definitions import portbindings as pb_api
from neutron_lib import context as n_context
from neutron_lib.db import api as db_api
from neutron_lib import exceptions
from oslo_log import log as logging
from sqlalchemy.orm import exc as sqla_exc
from neutron.db.models.plugins.ml2 import geneveallocation
from neutron.db.models.plugins.ml2 import vxlanallocation
@ -23,6 +25,9 @@ from neutron.objects import network as network_obj
from neutron.objects import ports as port_obj
from neutron.objects import trunk as trunk_obj
LOG = logging.getLogger(__name__)
VIF_DETAILS_TO_REMOVE = (
pb_api.OVS_HYBRID_PLUG,
pb_api.VIF_DETAILS_BRIDGE_NAME,
@ -54,37 +59,72 @@ def migrate_neutron_database_to_ovn(plugin):
vxlanallocation.VxlanAllocation.vxlan_vni ==
segment.segmentation_id).update({"allocated": False})
port_bindings = port_obj.PortBinding.get_objects(
# Update ``PortBinding`` objects.
pb_updated = set([])
pb_missed = set([])
while True:
pb_current = port_obj.PortBinding.get_port_id_and_host(
ctx, vif_type='ovs', vnic_type='normal', status='ACTIVE')
for pb in port_bindings:
if not pb.vif_details:
continue
vif_details = pb.vif_details.copy()
for detail in VIF_DETAILS_TO_REMOVE:
try:
del vif_details[detail]
except KeyError:
pass
if vif_details != pb.vif_details:
diff = set(pb_current).difference(pb_updated)
if not diff:
break
for port_id, host in diff:
with db_api.CONTEXT_WRITER.using(ctx):
pb = port_obj.PortBinding.get_object(ctx, port_id=port_id,
host=host)
if not pb or not pb.vif_details:
continue
vif_details = pb.vif_details.copy()
for detail in VIF_DETAILS_TO_REMOVE:
try:
del vif_details[detail]
except KeyError:
pass
if vif_details == pb.vif_details:
continue
pb.vif_details = vif_details
try:
pb.update()
except exceptions.ObjectNotFound:
# When Neutron server is running, it could happen that
# for example gateway port has been rescheduled to a
# different gateway chassis.
pass
except (exceptions.ObjectNotFound, sqla_exc.StaleDataError):
# The PortBinding register has been already modified.
pb_missed.add(port_id)
pb_updated.update(diff)
if pb_missed:
LOG.warning('The following ports did not update their port binding '
'records: %s', ', '.join(pb_missed))
# Update ``Trunk`` objects.
trunk_updated = set([])
while True:
trunk_current = trunk_obj.Trunk.get_trunk_ids(ctx)
diff = set(trunk_current).difference(trunk_updated)
if not diff:
break
for trunk_id in diff:
with db_api.CONTEXT_WRITER.using(ctx):
trunk = trunk_obj.Trunk.get_object(ctx, id=trunk_id)
if not trunk:
continue
for subport in trunk.sub_ports:
pbs = port_obj.PortBinding.get_objects(
ctx, port_id=subport.port_id)
for pb in pbs:
profile = {}
if pb.profile:
profile = pb.profile.copy()
profile['parent_name'] = trunk.port_id
profile['tag'] = subport.segmentation_id
if profile == pb.profile:
continue
for trunk in trunk_obj.Trunk.get_objects(ctx):
for subport in trunk.sub_ports:
pbs = port_obj.PortBinding.get_objects(
ctx, port_id=subport.port_id)
for pb in pbs:
profile = {}
if pb.profile:
profile = pb.profile.copy()
profile['parent_name'] = trunk.port_id
profile['tag'] = subport.segmentation_id
if profile != pb.profile:
pb.profile = profile
pb.update()
trunk_updated.update(diff)