diff --git a/nova/db/sqlalchemy/migrate_repo/versions/318_resource_provider_name_aggregates.py b/nova/db/sqlalchemy/migrate_repo/versions/318_resource_provider_name_aggregates.py new file mode 100644 index 000000000000..f0f4cf3e9706 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/318_resource_provider_name_aggregates.py @@ -0,0 +1,93 @@ +# 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 migrate import UniqueConstraint +from oslo_db.sqlalchemy import utils +from sqlalchemy import Column +from sqlalchemy import DDL +from sqlalchemy import Index +from sqlalchemy import Integer +from sqlalchemy import MetaData +from sqlalchemy import Table +from sqlalchemy import Unicode + + +def upgrade(migrate_engine): + meta = MetaData(bind=migrate_engine) + + resource_providers = Table('resource_providers', meta, autoload=True) + + name = Column('name', Unicode(200), nullable=True) + generation = Column('generation', Integer, default=0) + can_host = Column('can_host', Integer, default=0) + + if not hasattr(resource_providers.c, 'name'): + # NOTE(cdent): The resource_providers table is defined as + # latin1 to be more efficient. Now we need the name column + # to be UTF8. First create the column, then modify it, + # otherwise the declarative handling in sqlalchemy gets + # confused. + resource_providers.create_column(name) + if migrate_engine.name == 'mysql': + name_col_ddl = DDL( + "ALTER TABLE resource_providers MODIFY name " + "VARCHAR(200) CHARACTER SET utf8") + conn = migrate_engine.connect() + conn.execute(name_col_ddl) + + uc = UniqueConstraint('name', table=resource_providers, + name='uniq_resource_providers0name') + uc.create() + + # DB2 automatically creates an index for the unique + # constraint above, so skip adding the index on DB2. + if migrate_engine.name != 'ibm_db_sa': + utils.add_index(migrate_engine, 'resource_providers', + 'resource_providers_name_idx', + ['name']) + + if not hasattr(resource_providers.c, 'generation'): + resource_providers.create_column(generation) + + if not hasattr(resource_providers.c, 'can_host'): + resource_providers.create_column(can_host) + + resource_provider_aggregates = Table( + 'resource_provider_aggregates', meta, + Column('resource_provider_id', Integer, primary_key=True, + nullable=False), + Column('aggregate_id', Integer, primary_key=True, nullable=False), + mysql_engine='InnoDB', + mysql_charset='latin1' + ) + Index('resource_provider_aggregates_aggregate_id_idx', + resource_provider_aggregates.c.aggregate_id) + resource_provider_aggregates.create(checkfirst=True) + + utils.add_index(migrate_engine, 'allocations', + 'allocations_resource_provider_class_used_idx', + ['resource_provider_id', 'resource_class_id', + 'used']) + utils.drop_index(migrate_engine, 'allocations', + 'allocations_resource_provider_class_id_idx') + + # Add a unique constraint so that any resource provider can have + # only one inventory for any given resource class. + inventories = Table('inventories', meta, autoload=True) + inventories_uc = UniqueConstraint( + 'resource_provider_id', 'resource_class_id', table=inventories, + name='uniq_inventories0resource_provider_resource_class') + inventories_uc.create() + if migrate_engine.name != 'ibm_db_sa': + utils.add_index(migrate_engine, 'inventories', + 'inventories_resource_provider_resource_class_idx', + ['resource_provider_id', 'resource_class_id']) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 47364c2651cc..0bcc4a786e4c 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -1450,10 +1450,16 @@ class ResourceProvider(BASE, models.ModelBase): Index('resource_providers_uuid_idx', 'uuid'), schema.UniqueConstraint('uuid', name='uniq_resource_providers0uuid'), - ) + Index('resource_providers_name_idx', 'name'), + schema.UniqueConstraint('name', + name='uniq_resource_providers0name') + ) id = Column(Integer, primary_key=True, nullable=False) uuid = Column(String(36), nullable=False) + name = Column(Unicode(200), nullable=True) + generation = Column(Integer, default=0) + can_host = Column(Integer, default=0) class Inventory(BASE, models.ModelBase): @@ -1465,6 +1471,10 @@ class Inventory(BASE, models.ModelBase): 'resource_provider_id'), Index('inventories_resource_class_id_idx', 'resource_class_id'), + Index('inventories_resource_provider_resource_class_idx', + 'resource_provider_id', 'resource_class_id'), + schema.UniqueConstraint('resource_provider_id', 'resource_class_id', + name='uniq_inventories0resource_provider_resource_class') ) id = Column(Integer, primary_key=True, nullable=False) @@ -1488,8 +1498,9 @@ class Allocation(BASE, models.ModelBase): __tablename__ = "allocations" __table_args__ = ( - Index('allocations_resource_provider_class_id_idx', - 'resource_provider_id', 'resource_class_id'), + Index('allocations_resource_provider_class_used_idx', + 'resource_provider_id', 'resource_class_id', + 'used'), Index('allocations_resource_class_id_idx', 'resource_class_id'), Index('allocations_consumer_id_idx', 'consumer_id') @@ -1500,3 +1511,16 @@ class Allocation(BASE, models.ModelBase): consumer_id = Column(String(36), nullable=False) resource_class_id = Column(Integer, nullable=False) used = Column(Integer, nullable=False) + + +class ResourceProviderAggregate(BASE, models.ModelBase): + """Assocate a resource provider with an aggregate.""" + + __tablename__ = 'resource_provider_aggregates' + __table_args__ = ( + Index('resource_provider_aggregates_aggregate_id_idx', + 'aggregate_id'), + ) + + resource_provider_id = Column(Integer, primary_key=True, nullable=False) + aggregate_id = Column(Integer, primary_key=True, nullable=False) diff --git a/nova/tests/unit/db/test_db_api.py b/nova/tests/unit/db/test_db_api.py index 12dd492873be..9d60702a22b7 100644 --- a/nova/tests/unit/db/test_db_api.py +++ b/nova/tests/unit/db/test_db_api.py @@ -8391,8 +8391,9 @@ class ArchiveTestCase(test.TestCase, ModelsObjectComparatorMixin): # NOTE(cdent): migration 314 introduced three new # ('resource_providers', 'allocations' and 'inventories') # with no shadow table and it's OK, so skip. + # 318 adds one more: 'resource_provider_aggregates'. if table_name in ['tags', 'resource_providers', 'allocations', - 'inventories']: + 'inventories', 'resource_provider_aggregates']: continue if table_name.startswith("shadow_"): diff --git a/nova/tests/unit/db/test_migrations.py b/nova/tests/unit/db/test_migrations.py index d46fcb30a937..16084293c1e8 100644 --- a/nova/tests/unit/db/test_migrations.py +++ b/nova/tests/unit/db/test_migrations.py @@ -855,6 +855,31 @@ class NovaMigrationsCheckers(test_migrations.ModelsMigrationsSync, self.assertColumnExists(engine, 'aggregates', 'uuid') self.assertColumnExists(engine, 'shadow_aggregates', 'uuid') + def _check_318(self, engine, data): + self.assertColumnExists(engine, 'resource_providers', 'name') + self.assertColumnExists(engine, 'resource_providers', 'generation') + self.assertColumnExists(engine, 'resource_providers', 'can_host') + self.assertIndexMembers(engine, 'resource_providers', + 'resource_providers_name_idx', + ['name']) + + self.assertColumnExists(engine, 'resource_provider_aggregates', + 'resource_provider_id') + self.assertColumnExists(engine, 'resource_provider_aggregates', + 'aggregate_id') + + self.assertIndexMembers(engine, 'resource_provider_aggregates', + 'resource_provider_aggregates_aggregate_id_idx', + ['aggregate_id']) + + self.assertIndexMembers(engine, 'resource_provider_aggregates', + 'resource_provider_aggregates_aggregate_id_idx', + ['aggregate_id']) + + self.assertIndexMembers(engine, 'inventories', + 'inventories_resource_provider_resource_class_idx', + ['resource_provider_id', 'resource_class_id']) + class TestNovaMigrationsSQLite(NovaMigrationsCheckers, test_base.DbTestCase,