Sanitize profile column of ml2_port_bindings table in the DB
With the introduction of port-resource-request-groups extension, format of binding-profile.allocation has changed. Since the DB, may contain port bindings that were created before the introduction of the new format, it's necessary to perform upgrade check and sanitize those rows that are still using an older format. Partial-Bug: #1922237 See-Also: https://review.opendev.org/785236 Change-Id: I95e9e1bc553ac499d75c9280e45dfea61d135279
This commit is contained in:
parent
8db15cb2f3
commit
d699a955cd
37
neutron/api/converters.py
Normal file
37
neutron/api/converters.py
Normal file
@ -0,0 +1,37 @@
|
||||
# Copyright (c) 2021 Ericsson Software Technology
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import uuid
|
||||
|
||||
from neutron_lib.placement import utils as pl_utils
|
||||
|
||||
|
||||
# TODO(przszc): Delete when https://review.opendev.org/813650 is released
|
||||
def convert_to_sanitized_binding_profile_allocation(allocation, port_id,
|
||||
min_bw_rules):
|
||||
"""Return binding-profile.allocation in the new format
|
||||
|
||||
:param allocation: binding-profile.allocation attribute containting a
|
||||
string with RP UUID
|
||||
:param port_id: ID of the port that is being sanitized
|
||||
:param min_bw_rules: A list of minimum bandwidth rules associated with the
|
||||
port.
|
||||
:return: A dict with allocation in {'<group_uuid>': '<rp_uuid>'} format.
|
||||
"""
|
||||
if isinstance(allocation, dict):
|
||||
return allocation
|
||||
|
||||
group_id = str(
|
||||
pl_utils.resource_request_group_uuid(uuid.UUID(port_id), min_bw_rules))
|
||||
return {group_id: allocation}
|
87
neutron/cmd/sanitize_port_binding_profile_allocation.py
Normal file
87
neutron/cmd/sanitize_port_binding_profile_allocation.py
Normal file
@ -0,0 +1,87 @@
|
||||
# Copyright (c) 2021 Ericsson Software Technology
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron_lib import context
|
||||
from neutron_lib.db import api as db_api
|
||||
from oslo_config import cfg
|
||||
from oslo_db import options as db_options
|
||||
from oslo_log import log as logging
|
||||
|
||||
from neutron.api import converters as n_converters
|
||||
from neutron.objects import ports as port_obj
|
||||
from neutron.objects.qos import binding as qos_binding_obj
|
||||
from neutron.objects.qos import rule as qos_rule_obj
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def setup_conf():
|
||||
conf = cfg.CONF
|
||||
db_group, neutron_db_opts = db_options.list_opts()[0]
|
||||
cfg.CONF.register_cli_opts(neutron_db_opts, db_group)
|
||||
conf()
|
||||
|
||||
|
||||
def main():
|
||||
"""Main method for sanitizing "ml2_port_bindings.profile" column.
|
||||
|
||||
This script will sanitize "ml2_port_bindings.profile" columns existing in
|
||||
the database. In Yoga release the format of this column has changed from:
|
||||
{'allocation': '<rp_uuid>'}
|
||||
to:
|
||||
{'allocation': {'<group_uuid>': '<rp_uuid>'}}
|
||||
|
||||
where group_uuid is generated based on port_id and ID of QoS rules
|
||||
belonging to that group.
|
||||
"""
|
||||
setup_conf()
|
||||
admin_ctx = context.get_admin_context()
|
||||
with db_api.CONTEXT_WRITER.using(admin_ctx):
|
||||
for port_binding in port_obj.PortBinding.get_objects(admin_ctx):
|
||||
# NOTE(przszc): Before minimum packet rate rule was introduced,
|
||||
# binding-profile.allocation attribute could contain only a single
|
||||
# RP UUID, responsible for providing minimum bandwidth resources.
|
||||
# Because of that, whenever we find allocation attribute that still
|
||||
# uses old format, we can safely assume that we need to generate
|
||||
# minimum bandwidth group UUID.
|
||||
allocation = port_binding.profile.get('allocation')
|
||||
if (not allocation or isinstance(allocation, dict)):
|
||||
continue
|
||||
|
||||
qos_port_binding = qos_binding_obj.QosPolicyPortBinding.get_object(
|
||||
admin_ctx, port_id=port_binding.port_id)
|
||||
if not qos_port_binding:
|
||||
LOG.error(
|
||||
'Failed to sanitize binding-profile.allocation attribute '
|
||||
'%s for port %s: Did not find associated QoS policy.',
|
||||
allocation, port_binding.port_id)
|
||||
continue
|
||||
|
||||
min_bw_rules = qos_rule_obj.QosMinimumBandwidthRule.get_objects(
|
||||
admin_ctx, qos_policy_id=qos_port_binding.policy_id)
|
||||
if not min_bw_rules:
|
||||
LOG.error(
|
||||
'Failed to sanitize binding-profile.allocation attribute '
|
||||
'%s for port %s: Associated QoS policy %s has no minimum '
|
||||
'bandwidth rules.', allocation, port_binding.port_id,
|
||||
qos_port_binding.policy_id)
|
||||
continue
|
||||
|
||||
port_binding.profile = {'allocation':
|
||||
n_converters.convert_to_sanitized_binding_profile_allocation(
|
||||
allocation, port_binding.port_id, min_bw_rules)}
|
||||
LOG.info('Port %s updated, New binding-profile.allocation format: '
|
||||
'%s', port_binding.port_id, port_binding.profile)
|
||||
port_binding.update()
|
@ -29,6 +29,7 @@ from neutron.db.models import agent as agent_model
|
||||
from neutron.db.models.plugins.ml2 import vlanallocation
|
||||
from neutron.db.models import segment
|
||||
from neutron.db import models_v2
|
||||
from neutron.objects import ports as port_obj
|
||||
|
||||
|
||||
OVN_ALEMBIC_TABLE_NAME = "ovn_alembic_version"
|
||||
@ -106,6 +107,12 @@ def get_duplicate_network_segment_count():
|
||||
return query.count()
|
||||
|
||||
|
||||
def port_binding_profiles():
|
||||
ctx = context.get_admin_context()
|
||||
return [port_binding.profile
|
||||
for port_binding in port_obj.PortBinding.get_objects(ctx)]
|
||||
|
||||
|
||||
class CoreChecks(base.BaseChecks):
|
||||
|
||||
def get_checks(self):
|
||||
@ -127,6 +134,8 @@ class CoreChecks(base.BaseChecks):
|
||||
self.port_mac_address_sanity),
|
||||
(_('NetworkSegments unique constraint check'),
|
||||
self.networksegments_unique_constraint_check),
|
||||
(_('Port Binding profile sanity check'),
|
||||
self.port_binding_profile_sanity),
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
@ -365,3 +374,31 @@ class CoreChecks(base.BaseChecks):
|
||||
upgradecheck.Code.SUCCESS,
|
||||
_("No networksegments sharing the same network_id, network_type "
|
||||
"and physical_network found."))
|
||||
|
||||
@staticmethod
|
||||
def port_binding_profile_sanity(checker):
|
||||
"""Checks that "ml2_port_bindings.profile" uses the new format
|
||||
|
||||
All allocation information should be stored in the following format:
|
||||
{'allocation': {'<group_uuid>': '<rp_uuid>'}}.
|
||||
"""
|
||||
if not cfg.CONF.database.connection:
|
||||
return upgradecheck.Result(
|
||||
upgradecheck.Code.WARNING,
|
||||
_("Database connection string is not set. Check for "
|
||||
"ml2_port_bindings.profile sanity can't be done."))
|
||||
|
||||
for profile in port_binding_profiles():
|
||||
allocation = profile.get('allocation')
|
||||
if (allocation and not isinstance(allocation, dict)):
|
||||
return upgradecheck.Result(
|
||||
upgradecheck.Code.FAILURE,
|
||||
_("ml2_port_bindings.profile rows are not correctly "
|
||||
"formated in the database. The script "
|
||||
"neutron-sanitize-port-binding-profile-allocation "
|
||||
"should be executed"))
|
||||
|
||||
return upgradecheck.Result(
|
||||
upgradecheck.Code.SUCCESS,
|
||||
_("All ml2_port_bindings.profile rows are correctly formated in "
|
||||
"the database."))
|
||||
|
@ -211,3 +211,19 @@ class TestChecks(base.BaseTestCase):
|
||||
result = checks.CoreChecks.\
|
||||
networksegments_unique_constraint_check(mock.ANY)
|
||||
self.assertEqual(returned_code, result.code)
|
||||
|
||||
def test_port_binding_profile_sanity(self):
|
||||
new_format = {"allocation":
|
||||
{"397aec7a-1f69-11ec-9f1a-7b14e597e275":
|
||||
"41d7391e-1f69-11ec-a899-8f9d6d950f8d"}}
|
||||
old_format = {"allocation": "41d7391e-1f69-11ec-a899-8f9d6d950f8d"}
|
||||
cases = (([new_format], Code.SUCCESS),
|
||||
([old_format], Code.FAILURE))
|
||||
with mock.patch.object(
|
||||
checks, 'port_binding_profiles') \
|
||||
as mock_port_binding_profiles:
|
||||
for profile, returned_code in cases:
|
||||
mock_port_binding_profiles.return_value = profile
|
||||
result = checks.CoreChecks.port_binding_profile_sanity(
|
||||
mock.ANY)
|
||||
self.assertEqual(returned_code, result.code)
|
||||
|
@ -0,0 +1,9 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Added a check to verify if all rows of ``ml2_port_bindings`` table in the
|
||||
DB are using the new format for ``profile`` column. This check is part of
|
||||
upgrade check, that can be executed with ``neutron-status upgrade check``
|
||||
command. If some rows are using obsolete format, they can be sanitized
|
||||
with a script that can be executed with
|
||||
``neutron-sanitize-port-binding-profile-allocation`` command.
|
@ -61,6 +61,7 @@ console_scripts =
|
||||
neutron-ovn-metadata-agent = neutron.cmd.eventlet.agents.ovn_metadata:main
|
||||
neutron-ovn-migration-mtu = neutron.cmd.ovn.migration_mtu:main
|
||||
neutron-ovn-db-sync-util = neutron.cmd.ovn.neutron_ovn_db_sync_util:main
|
||||
neutron-sanitize-port-binding-profile-allocation = neutron.cmd.sanitize_port_binding_profile_allocation:main
|
||||
neutron-sanitize-port-mac-addresses = neutron.cmd.sanitize_port_mac_addresses:main
|
||||
ml2ovn-trace = neutron.cmd.ovn.ml2ovn_trace:main
|
||||
neutron.core_plugins =
|
||||
|
Loading…
Reference in New Issue
Block a user