Add standard_attrs to address group
Need the revision_number attribute to support address group update in rpc resource_cache. Change-Id: I6355c9394c23f7d94496e9c06e061d6d3fd4128d Implements: blueprint address-groups-in-sg-rules
This commit is contained in:
parent
23738d0df7
commit
6db15a004d
@ -1 +1 @@
|
|||||||
a964d94b4677
|
26d1e9f5c766
|
||||||
|
@ -0,0 +1,95 @@
|
|||||||
|
# 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
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
"""Add standard attributes to address group
|
||||||
|
|
||||||
|
Revision ID: 26d1e9f5c766
|
||||||
|
Revises: a964d94b4677
|
||||||
|
Create Date: 2020-12-02 17:38:45.331048
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '26d1e9f5c766'
|
||||||
|
down_revision = 'a964d94b4677'
|
||||||
|
|
||||||
|
|
||||||
|
TABLE = 'address_groups'
|
||||||
|
|
||||||
|
TABLE_MODEL = sa.Table(TABLE, sa.MetaData(),
|
||||||
|
sa.Column('id', sa.String(length=36), nullable=False),
|
||||||
|
sa.Column('description', sa.String(length=255),
|
||||||
|
nullable=True),
|
||||||
|
sa.Column('standard_attr_id', sa.BigInteger(),
|
||||||
|
nullable=True))
|
||||||
|
|
||||||
|
standardattrs = sa.Table(
|
||||||
|
'standardattributes', sa.MetaData(),
|
||||||
|
sa.Column('id', sa.BigInteger(), primary_key=True, autoincrement=True),
|
||||||
|
sa.Column('resource_type', sa.String(length=255), nullable=False),
|
||||||
|
sa.Column('description', sa.String(length=255), nullable=True))
|
||||||
|
|
||||||
|
|
||||||
|
def generate_records_for_existing():
|
||||||
|
session = sa.orm.Session(bind=op.get_bind())
|
||||||
|
with session.begin(subtransactions=True):
|
||||||
|
for row in session.query(TABLE_MODEL):
|
||||||
|
res = session.execute(
|
||||||
|
standardattrs.insert().values(resource_type=TABLE,
|
||||||
|
description=row[1])
|
||||||
|
)
|
||||||
|
session.execute(
|
||||||
|
TABLE_MODEL.update().values(
|
||||||
|
standard_attr_id=res.inserted_primary_key[0]).where(
|
||||||
|
TABLE_MODEL.c.id == row[0])
|
||||||
|
)
|
||||||
|
# this commit is necessary to allow further operations
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.add_column(TABLE, sa.Column('standard_attr_id', sa.BigInteger(),
|
||||||
|
nullable=True))
|
||||||
|
generate_records_for_existing()
|
||||||
|
|
||||||
|
# add the constraint now that everything is populated on that table
|
||||||
|
op.create_foreign_key(
|
||||||
|
constraint_name=None, source_table=TABLE,
|
||||||
|
referent_table='standardattributes',
|
||||||
|
local_cols=['standard_attr_id'], remote_cols=['id'],
|
||||||
|
ondelete='CASCADE')
|
||||||
|
op.alter_column(TABLE, 'standard_attr_id', nullable=False,
|
||||||
|
existing_type=sa.BigInteger(), existing_nullable=True,
|
||||||
|
existing_server_default=False)
|
||||||
|
op.create_unique_constraint(
|
||||||
|
constraint_name='uniq_%s0standard_attr_id' % TABLE,
|
||||||
|
table_name=TABLE, columns=['standard_attr_id'])
|
||||||
|
op.drop_column(TABLE, 'description')
|
||||||
|
|
||||||
|
|
||||||
|
def expand_drop_exceptions():
|
||||||
|
"""Drop the description column for table address_groups
|
||||||
|
|
||||||
|
Drop the existing description column in address_groups table since
|
||||||
|
address_groups are now associated with standard_attributes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return {
|
||||||
|
sa.Column: ['%s.description' % TABLE]
|
||||||
|
}
|
@ -10,11 +10,14 @@
|
|||||||
# 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.api.definitions import address_group as ag
|
||||||
from neutron_lib.db import constants as db_const
|
from neutron_lib.db import constants as db_const
|
||||||
from neutron_lib.db import model_base
|
from neutron_lib.db import model_base
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy import orm
|
from sqlalchemy import orm
|
||||||
|
|
||||||
|
from neutron.db import standard_attr
|
||||||
|
|
||||||
|
|
||||||
class AddressAssociation(model_base.BASEV2):
|
class AddressAssociation(model_base.BASEV2):
|
||||||
"""Represents a neutron address group's address association."""
|
"""Represents a neutron address group's address association."""
|
||||||
@ -26,16 +29,18 @@ class AddressAssociation(model_base.BASEV2):
|
|||||||
sa.ForeignKey("address_groups.id",
|
sa.ForeignKey("address_groups.id",
|
||||||
ondelete="CASCADE"),
|
ondelete="CASCADE"),
|
||||||
nullable=False, primary_key=True)
|
nullable=False, primary_key=True)
|
||||||
|
revises_on_change = ('address_groups',)
|
||||||
|
|
||||||
|
|
||||||
class AddressGroup(model_base.BASEV2, model_base.HasId, model_base.HasProject):
|
class AddressGroup(standard_attr.HasStandardAttributes,
|
||||||
|
model_base.BASEV2, model_base.HasId, model_base.HasProject):
|
||||||
"""Represents a neutron address group."""
|
"""Represents a neutron address group."""
|
||||||
__tablename__ = "address_groups"
|
__tablename__ = "address_groups"
|
||||||
|
|
||||||
name = sa.Column(sa.String(db_const.NAME_FIELD_SIZE))
|
name = sa.Column(sa.String(db_const.NAME_FIELD_SIZE))
|
||||||
description = sa.Column(sa.String(db_const.LONG_DESCRIPTION_FIELD_SIZE))
|
|
||||||
addresses = orm.relationship(AddressAssociation,
|
addresses = orm.relationship(AddressAssociation,
|
||||||
backref=orm.backref('address_groups',
|
backref=orm.backref('address_groups',
|
||||||
load_on_pending=True),
|
load_on_pending=True),
|
||||||
lazy='subquery',
|
lazy='subquery',
|
||||||
cascade='all, delete-orphan')
|
cascade='all, delete-orphan')
|
||||||
|
api_collections = [ag.ALIAS]
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
import netaddr
|
import netaddr
|
||||||
from neutron_lib.objects import common_types
|
from neutron_lib.objects import common_types
|
||||||
|
from oslo_utils import versionutils
|
||||||
from oslo_versionedobjects import fields as obj_fields
|
from oslo_versionedobjects import fields as obj_fields
|
||||||
|
|
||||||
from neutron.db.models import address_group as models
|
from neutron.db.models import address_group as models
|
||||||
@ -22,19 +23,27 @@ from neutron.objects import base
|
|||||||
class AddressGroup(base.NeutronDbObject):
|
class AddressGroup(base.NeutronDbObject):
|
||||||
# Version 1.0: Initial version
|
# Version 1.0: Initial version
|
||||||
VERSION = '1.0'
|
VERSION = '1.0'
|
||||||
|
# Version 1.1: Added standard attributes
|
||||||
|
VERSION = '1.1'
|
||||||
|
|
||||||
db_model = models.AddressGroup
|
db_model = models.AddressGroup
|
||||||
|
|
||||||
fields = {
|
fields = {
|
||||||
'id': common_types.UUIDField(),
|
'id': common_types.UUIDField(),
|
||||||
'name': obj_fields.StringField(nullable=True),
|
'name': obj_fields.StringField(nullable=True),
|
||||||
'description': obj_fields.StringField(nullable=True),
|
|
||||||
'project_id': obj_fields.StringField(),
|
'project_id': obj_fields.StringField(),
|
||||||
'addresses': obj_fields.ListOfObjectsField('AddressAssociation',
|
'addresses': obj_fields.ListOfObjectsField('AddressAssociation',
|
||||||
nullable=True)
|
nullable=True)
|
||||||
}
|
}
|
||||||
synthetic_fields = ['addresses']
|
synthetic_fields = ['addresses']
|
||||||
|
|
||||||
|
def obj_make_compatible(self, primitive, target_version):
|
||||||
|
_target_version = versionutils.convert_version_to_tuple(target_version)
|
||||||
|
if _target_version < (1, 1):
|
||||||
|
standard_fields = ['revision_number', 'created_at', 'updated_at']
|
||||||
|
for f in standard_fields:
|
||||||
|
primitive.pop(f, None)
|
||||||
|
|
||||||
|
|
||||||
@base.NeutronObjectRegistry.register
|
@base.NeutronObjectRegistry.register
|
||||||
class AddressAssociation(base.NeutronDbObject):
|
class AddressAssociation(base.NeutronDbObject):
|
||||||
|
@ -272,7 +272,7 @@ class _TestModelsMigrations(test_migrations.ModelsMigrationsSync):
|
|||||||
"""Identify excepted operations that are allowed for the branch."""
|
"""Identify excepted operations that are allowed for the branch."""
|
||||||
# For alembic the clause is AddColumn or DropColumn
|
# For alembic the clause is AddColumn or DropColumn
|
||||||
column = clauseelement.column.name
|
column = clauseelement.column.name
|
||||||
table = clauseelement.column.table.name
|
table = clauseelement.table_name
|
||||||
element_name = '.'.join([table, column])
|
element_name = '.'.join([table, column])
|
||||||
for alembic_type, excepted_names in exceptions.items():
|
for alembic_type, excepted_names in exceptions.items():
|
||||||
if alembic_type == sqlalchemy.Column:
|
if alembic_type == sqlalchemy.Column:
|
||||||
|
@ -29,6 +29,23 @@ class AddressGroupDbObjectTestCase(
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(AddressGroupDbObjectTestCase, self).setUp()
|
super(AddressGroupDbObjectTestCase, self).setUp()
|
||||||
|
|
||||||
|
def _create_test_address_group(self):
|
||||||
|
self.objs[0].create()
|
||||||
|
return self.objs[0]
|
||||||
|
|
||||||
|
def test_object_version_degradation_1_1_to_1_0_no_standard_attrs(self):
|
||||||
|
ag_obj = self._create_test_address_group()
|
||||||
|
ag_obj_1_0 = ag_obj.obj_to_primitive('1.0')
|
||||||
|
self.assertNotIn('revision_number',
|
||||||
|
ag_obj_1_0['versioned_object.data'])
|
||||||
|
self.assertNotIn('created_at',
|
||||||
|
ag_obj_1_0['versioned_object.data'])
|
||||||
|
self.assertNotIn('updated_at',
|
||||||
|
ag_obj_1_0['versioned_object.data'])
|
||||||
|
# description filed was added to initial version separately
|
||||||
|
self.assertIn('description',
|
||||||
|
ag_obj_1_0['versioned_object.data'])
|
||||||
|
|
||||||
|
|
||||||
class AddressAssociationIfaceObjectTestCase(
|
class AddressAssociationIfaceObjectTestCase(
|
||||||
obj_test_base.BaseObjectIfaceTestCase):
|
obj_test_base.BaseObjectIfaceTestCase):
|
||||||
|
@ -27,7 +27,7 @@ from neutron.tests import base as test_base
|
|||||||
# alphabetic order.
|
# alphabetic order.
|
||||||
object_data = {
|
object_data = {
|
||||||
'AddressAssociation': '1.0-b92160a3dd2fb7b951adcd2e6ae1665a',
|
'AddressAssociation': '1.0-b92160a3dd2fb7b951adcd2e6ae1665a',
|
||||||
'AddressGroup': '1.0-a402a66e35d25e9381eab40e1e709907',
|
'AddressGroup': '1.1-78c35b6ac495407be56b8fcdbeda4d67',
|
||||||
'AddressScope': '1.1-dd0dfdb67775892d3adc090e28e43bd8',
|
'AddressScope': '1.1-dd0dfdb67775892d3adc090e28e43bd8',
|
||||||
'AddressScopeRBAC': '1.0-192845c5ed0718e1c54fac36936fcd7d',
|
'AddressScopeRBAC': '1.0-192845c5ed0718e1c54fac36936fcd7d',
|
||||||
'Agent': '1.1-64b670752d57b3c7602cb136e0338507',
|
'Agent': '1.1-64b670752d57b3c7602cb136e0338507',
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
upgrade:
|
||||||
|
- |
|
||||||
|
Address group now has standard attributes. In the alembic migration,
|
||||||
|
the original ``description`` column of ``address_groups`` is dropped after
|
||||||
|
data migrated to the ``standardattributes`` table. The ``description``
|
||||||
|
field is also removed from the address group object and DB model. This
|
||||||
|
change requires a restart of ``neutron-server`` service after the DB
|
||||||
|
migration otherwise users will get server errors when making calls to
|
||||||
|
address group APIs.
|
Loading…
Reference in New Issue
Block a user