Limit ml2_vlan_allocations.vlan_id value in DB backend

Limit "ml2_vlan_allocations.vlan_id" values stored in the DB by
adding a check constraint in the DB engine. This check will
verify that vlan_id is in the interval [1, 4094].

Change-Id: Ie6453cb7a2ef0c43baf540c49a03079f4c8d3818
Closes-Bug: #1870400
This commit is contained in:
Rodolfo Alonso Hernandez 2020-04-28 09:07:07 +00:00
parent 673c5cb52b
commit 795aa6b9fa
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_serialization import jsonutils
from oslo_upgradecheck import upgradecheck
from sqlalchemy import or_
from neutron._i18n import _
from neutron.cmd.upgrade_checks import base
from neutron.db.models import agent as agent_model
from neutron.db.models.plugins.ml2 import vlanallocation
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
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):
def get_checks(self):
@ -83,7 +94,9 @@ class CoreChecks(base.BaseChecks):
(_("Networking-ovn database revision"),
self.ovn_db_revision_check),
(_("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
@ -240,3 +253,31 @@ class CoreChecks(base.BaseChecks):
return upgradecheck.Result(
upgradecheck.Code.SUCCESS,
_("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
# under the License.
from neutron_lib import constants
from neutron_lib.db import model_base
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):
"""Represent allocation state of a vlan_id on a physical network.
@ -32,7 +39,10 @@ class VlanAllocation(model_base.BASEV2):
__table_args__ = (
sa.Index('ix_ml2_vlan_allocations_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,
primary_key=True)

View File

@ -179,3 +179,14 @@ class TestChecks(base.BaseTestCase):
self.assertEqual(Code.WARNING, result.code)
self.assertIn('Host A', 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``.