Force "network_id" in "subnet" DB registers

The "subnet" OVO does not allow to have an empty (None) "network_id" but
the DB "subnet" table allows to have this parameter empty (NULL) in the
database. In order to avoid any problem like the one described in the
bug, this patch ensures the database "subnet" register does always have
a "network_id" value and if a "network" register is being deleted, all
related "subnet" registers are checked first.

Change-Id: Iad210f0585b4201fdb87187b44a9b42267b58db4
Closes-Bug: #1839658
This commit is contained in:
Rodolfo Alonso Hernandez 2019-08-19 13:09:20 +00:00
parent 490471ebd3
commit 4b6c2246c0
5 changed files with 49 additions and 5 deletions

View File

@ -1 +1 @@
63fd95af7dcd
c613d0b82681

View File

@ -0,0 +1,35 @@
# Copyright 2019 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
import sqlalchemy as sa
"""subnet force network id
Revision ID: c613d0b82681
Revises: 63fd95af7dcd
Create Date: 2019-08-19 11:15:14.443244
"""
# revision identifiers, used by Alembic.
revision = 'c613d0b82681'
down_revision = '63fd95af7dcd'
def upgrade():
op.alter_column('subnets', 'network_id', nullable=False,
existing_type=sa.String(36))

View File

@ -155,7 +155,8 @@ class Subnet(standard_attr.HasStandardAttributes, model_base.BASEV2,
"""
name = sa.Column(sa.String(db_const.NAME_FIELD_SIZE))
network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id'))
network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id'),
nullable=False)
# Added by the segments service plugin
segment_id = sa.Column(sa.String(36), sa.ForeignKey('networksegments.id'))
subnetpool_id = sa.Column(sa.String(36), index=True)

View File

@ -54,7 +54,8 @@ class MigrationToPluggableIpamMixin(object):
cidr=cidr,
ip_version=ip_version,
standard_attr_id=self._gen_attr_id(engine,
'subnets'))
'subnets'),
network_id=network_id)
engine.execute(subnets.insert().values(subnet_dict))
if data[cidr].get('pools'):

View File

@ -29,6 +29,7 @@ import webob.exc
from neutron.db import ipam_backend_mixin
from neutron.db import ipam_pluggable_backend
from neutron.ipam import requests as ipam_req
from neutron.objects import network as network_obj
from neutron.objects import ports as port_obj
from neutron.objects import subnet as obj_subnet
from neutron.tests.unit.db import test_db_base_plugin_v2 as test_db_base
@ -764,11 +765,14 @@ class TestDbBasePluginIpam(test_db_base.NeutronDbPluginV2TestCase):
def test_update_db_subnet_unchanged_pools(self, pool_mock):
old_pools = [{'start': '192.1.1.2', 'end': '192.1.1.254'}]
context = self.admin_context
network_id = uuidutils.generate_uuid()
network_obj.Network(context, id=network_id).create()
subnet = {'id': uuidutils.generate_uuid(),
'ip_version': constants.IP_VERSION_4,
'cidr': netaddr.IPNetwork('192.1.1.0/24'),
'ipv6_address_mode': None,
'ipv6_ra_mode': None}
'ipv6_ra_mode': None,
'network_id': network_id}
subnet_with_pools = subnet.copy()
subnet_obj = obj_subnet.Subnet(context, **subnet_with_pools)
subnet_obj.create()
@ -782,11 +786,14 @@ class TestDbBasePluginIpam(test_db_base.NeutronDbPluginV2TestCase):
def test_update_db_subnet_new_pools(self, pool_mock):
old_pools = [{'start': '192.1.1.2', 'end': '192.1.1.254'}]
context = self.admin_context
network_id = uuidutils.generate_uuid()
network_obj.Network(context, id=network_id).create()
subnet = {'id': uuidutils.generate_uuid(),
'ip_version': constants.IP_VERSION_4,
'cidr': netaddr.IPNetwork('192.1.1.0/24'),
'ipv6_address_mode': None,
'ipv6_ra_mode': None}
'ipv6_ra_mode': None,
'network_id': network_id}
# make a copy of subnet for validation, since update_subnet changes
# incoming subnet dict
expected_subnet = subnet.copy()