Merge "Limit ml2_vlan_allocations.vlan_id value in DB backend"

This commit is contained in:
Zuul 2020-06-09 23:37:42 +00:00 committed by Gerrit Code Review
commit 0ae0e3d791
6 changed files with 128 additions and 3 deletions

View File

@ -18,10 +18,12 @@ from neutron_lib.db import model_query
from oslo_config import cfg from oslo_config import cfg
from oslo_serialization import jsonutils from oslo_serialization import jsonutils
from oslo_upgradecheck import upgradecheck from oslo_upgradecheck import upgradecheck
from sqlalchemy import or_
from neutron._i18n import _ from neutron._i18n import _
from neutron.cmd.upgrade_checks import base from neutron.cmd.upgrade_checks import base
from neutron.db.models import agent as agent_model from neutron.db.models import agent as agent_model
from neutron.db.models.plugins.ml2 import vlanallocation
from neutron.db import models_v2 from neutron.db import models_v2
@ -71,6 +73,15 @@ def get_ovn_db_revisions():
"SELECT version_num from %s;" % OVN_ALEMBIC_TABLE_NAME)] # nosec "SELECT version_num from %s;" % OVN_ALEMBIC_TABLE_NAME)] # nosec
def count_vlan_allocations_invalid_segmentation_id():
ctx = context.get_admin_context()
query = ctx.session.query(vlanallocation.VlanAllocation)
query = query.filter(or_(
vlanallocation.VlanAllocation.vlan_id < constants.MIN_VLAN_TAG,
vlanallocation.VlanAllocation.vlan_id > constants.MAX_VLAN_TAG))
return query.count()
class CoreChecks(base.BaseChecks): class CoreChecks(base.BaseChecks):
def get_checks(self): def get_checks(self):
@ -83,7 +94,9 @@ class CoreChecks(base.BaseChecks):
(_("Networking-ovn database revision"), (_("Networking-ovn database revision"),
self.ovn_db_revision_check), self.ovn_db_revision_check),
(_("NIC Switch agent check kernel"), (_("NIC Switch agent check kernel"),
self.nic_switch_agent_min_kernel_check) self.nic_switch_agent_min_kernel_check),
(_("VLAN allocations valid segmentation ID check"),
self.vlan_allocations_segid_check),
] ]
@staticmethod @staticmethod
@ -240,3 +253,31 @@ class CoreChecks(base.BaseChecks):
return upgradecheck.Result( return upgradecheck.Result(
upgradecheck.Code.SUCCESS, upgradecheck.Code.SUCCESS,
_("No NIC Switch agents detected.")) _("No NIC Switch agents detected."))
@staticmethod
def vlan_allocations_segid_check(checker):
"""Checks that "ml2_vlan_allocations.vlan_id" has a valid value
Database register column "ml2_vlan_allocations.vlan_id" must be between
1 and 4094.
"""
if not cfg.CONF.database.connection:
return upgradecheck.Result(
upgradecheck.Code.WARNING,
_("Database connection string is not set. Check for VLAN "
"allocations with invalid segmentation IDs can't be done."))
count = count_vlan_allocations_invalid_segmentation_id()
if count:
return upgradecheck.Result(
upgradecheck.Code.WARNING,
_("There are %(count)s registers in 'ml2_vlan_allocations' "
"table with an invalid segmentation ID. 'vlan_id' must be "
"between %(min_vlan)s and %(max_vlan)s") %
{'count': count, 'min_vlan': constants.MIN_VLAN_TAG,
'max_vlan': constants.MAX_VLAN_TAG})
return upgradecheck.Result(
upgradecheck.Code.SUCCESS,
_("All 'ml2_vlan_allocations' registers have a valid "
"segmentation ID."))

View File

@ -1 +1 @@
d8bdf05313f4 dfe425060830

View File

@ -0,0 +1,55 @@
# Copyright 2020 OpenStack Foundation
#
# 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 alembic import op
from neutron_lib import constants
"""limit vlan allocation id values
Revision ID: dfe425060830
Revises: d8bdf05313f4
Create Date: 2020-04-27 16:58:33.110194
"""
# revision identifiers, used by Alembic.
revision = 'dfe425060830'
down_revision = 'd8bdf05313f4'
# NOTE(ralonsoh): "CHECK CONSTRAINT" is a feature heterogeneously implemented
# in different database engines and versions. For example:
# - MySQL: since version 8.0.16 (April 2019)
# https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-16.html
# - MariaDB: since version 10.2.1 (July 2016)
# https://mariadb.com/kb/en/mariadb-1021-release-notes/
# - PostgreSQL: since version 9.4 (December 2014)
# https://www.postgresql.org/docs/9.4/ddl-constraints.html
#
# If the DB engine does not support yet this feature, it will be ignored. The
# VLAN tag constraint is enforced in the Neutron API. This extra enforcement
# in the DB engine wants to mimic the limitation imposed in the API side and
# avoid any spurious modification.
def upgrade():
# https://docs.sqlalchemy.org/en/13/core/constraints.html#check-constraint
constraint = ('vlan_id>=%(min_vlan)s AND vlan_id<=%(max_vlan)s' %
{'min_vlan': constants.MIN_VLAN_TAG,
'max_vlan': constants.MAX_VLAN_TAG})
op.create_check_constraint(
constraint_name='check_ml2_vlan_allocations0vlan_id',
table_name='ml2_vlan_allocations',
condition=constraint)

View File

@ -10,10 +10,17 @@
# 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 neutron_lib import constants
from neutron_lib.db import model_base from neutron_lib.db import model_base
import sqlalchemy as sa import sqlalchemy as sa
# https://docs.sqlalchemy.org/en/13/core/constraints.html#check-constraint
VLAN_CONSTRAINT = ('vlan_id>=%(min_vlan)s AND vlan_id<=%(max_vlan)s' %
{'min_vlan': constants.MIN_VLAN_TAG,
'max_vlan': constants.MAX_VLAN_TAG})
class VlanAllocation(model_base.BASEV2): class VlanAllocation(model_base.BASEV2):
"""Represent allocation state of a vlan_id on a physical network. """Represent allocation state of a vlan_id on a physical network.
@ -32,7 +39,10 @@ class VlanAllocation(model_base.BASEV2):
__table_args__ = ( __table_args__ = (
sa.Index('ix_ml2_vlan_allocations_physical_network_allocated', sa.Index('ix_ml2_vlan_allocations_physical_network_allocated',
'physical_network', 'allocated'), 'physical_network', 'allocated'),
model_base.BASEV2.__table_args__,) sa.CheckConstraint(sqltext=VLAN_CONSTRAINT,
name='check_ml2_vlan_allocations0vlan_id'),
model_base.BASEV2.__table_args__,
)
physical_network = sa.Column(sa.String(64), nullable=False, physical_network = sa.Column(sa.String(64), nullable=False,
primary_key=True) primary_key=True)

View File

@ -179,3 +179,14 @@ class TestChecks(base.BaseTestCase):
self.assertEqual(Code.WARNING, result.code) self.assertEqual(Code.WARNING, result.code)
self.assertIn('Host A', result.details) self.assertIn('Host A', result.details)
self.assertIn('Host B', result.details) self.assertIn('Host B', result.details)
def test_vlan_allocations_segid_check(self):
cases = ([0, Code.SUCCESS], [1, Code.WARNING])
with mock.patch.object(
checks, 'count_vlan_allocations_invalid_segmentation_id') \
as mock_count:
for count, returned_code in cases:
mock_count.return_value = count
result = checks.CoreChecks.vlan_allocations_segid_check(
mock.ANY)
self.assertEqual(returned_code, result.code)

View File

@ -0,0 +1,8 @@
---
upgrade:
- |
Limit the ML2 VLAN allocations to [1, 4094] values in the database engine.
This constraint, enforced in the database engine, could not be supported
yet. In this case, it will be ignored. For more information, see the note
in
``neutron.db.migration.alembic_migrations.versions.victoria.expand.dfe425060830_limit_vlan_allocation_id_values.py``.