diff options
| author | Jenkins <jenkins@review.openstack.org> | 2013-05-11 00:19:04 +0000 |
|---|---|---|
| committer | Gerrit Code Review <review@openstack.org> | 2013-05-11 00:19:04 +0000 |
| commit | 7cb353e8d264fe51845b690df42d5597fe063293 (patch) | |
| tree | 9c0c075a56d37b0b1818112b4b6a7c8c2f0bbde3 | |
| parent | 260d7efce73d2f175a8f647e61d3ff5b71e0f885 (diff) | |
| parent | a9b8fbc0e963bc81c4f4dc47dfcc9f31f3d8ef2e (diff) | |
| download | nova-7cb353e8d264fe51845b690df42d5597fe063293.tar.gz nova-7cb353e8d264fe51845b690df42d5597fe063293.tar.xz nova-7cb353e8d264fe51845b690df42d5597fe063293.zip | |
Merge "Add sqlalchemy migration utils.check_shadow_table method"
| -rw-r--r-- | nova/db/sqlalchemy/api.py | 5 | ||||
| -rw-r--r-- | nova/db/sqlalchemy/utils.py | 40 | ||||
| -rw-r--r-- | nova/tests/test_migration_utils.py | 87 | ||||
| -rw-r--r-- | nova/tests/test_migrations.py | 7 |
4 files changed, 129 insertions, 10 deletions
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 4b869914f..730ca641c 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -80,6 +80,9 @@ get_engine = db_session.get_engine get_session = db_session.get_session +_SHADOW_TABLE_PREFIX = 'shadow_' + + def get_backend(): """The backend is this module itself.""" return sys.modules[__name__] @@ -5004,7 +5007,7 @@ def archive_deleted_rows_for_table(context, tablename, max_rows): metadata.bind = engine table = Table(tablename, metadata, autoload=True) default_deleted_value = _get_default_deleted_value(table) - shadow_tablename = "shadow_" + tablename + shadow_tablename = _SHADOW_TABLE_PREFIX + tablename rows_archived = 0 try: shadow_table = Table(shadow_tablename, metadata, autoload=True) diff --git a/nova/db/sqlalchemy/utils.py b/nova/db/sqlalchemy/utils.py index f8ccbb6f2..0d10f724e 100644 --- a/nova/db/sqlalchemy/utils.py +++ b/nova/db/sqlalchemy/utils.py @@ -24,7 +24,7 @@ from sqlalchemy.sql.expression import UpdateBase, literal_column from sqlalchemy.sql import select from sqlalchemy.types import NullType - +from nova.db.sqlalchemy import api as db from nova import exception from nova.openstack.common import log as logging from nova.openstack.common import timeutils @@ -166,3 +166,41 @@ def drop_old_duplicate_entries_from_table(migrate_engine, table_name, else: delete_statement = table.delete().where(delete_condition) migrate_engine.execute(delete_statement) + + +def check_shadow_table(migrate_engine, table_name): + """ + This method checks that table with ``table_name`` and corresponding shadow + table have same columns. + """ + meta = MetaData() + meta.bind = migrate_engine + + table = Table(table_name, meta, autoload=True) + shadow_table = Table(db._SHADOW_TABLE_PREFIX + table_name, meta, + autoload=True) + + columns = dict([(c.name, c) for c in table.columns]) + shadow_columns = dict([(c.name, c) for c in shadow_table.columns]) + + for name, column in columns.iteritems(): + if name not in shadow_columns: + raise exception.NovaException( + _("Missing column %(table)s.%(column)s in shadow table") + % {'column': name, 'table': shadow_table.name}) + shadow_column = shadow_columns[name] + + if not isinstance(shadow_column.type, type(column.type)): + raise exception.NovaException( + _("Different types in %(table)s.%(column)s and shadow table: " + "%(c_type)s %(shadow_c_type)s") + % {'column': name, 'table': table.name, + 'c_type': column.type, + 'shadow_c_type': shadow_column.type}) + + for name, column in shadow_columns.iteritems(): + if name not in columns: + raise exception.NovaException( + _("Extra column %(table)%.%(column)s in shadow table") + % {'column': name, 'table': shadow_table.name}) + return True diff --git a/nova/tests/test_migration_utils.py b/nova/tests/test_migration_utils.py index 1096be0d3..d98b3952b 100644 --- a/nova/tests/test_migration_utils.py +++ b/nova/tests/test_migration_utils.py @@ -18,16 +18,24 @@ from migrate.changeset import UniqueConstraint from sqlalchemy import Integer, DateTime, String from sqlalchemy import MetaData, Table, Column +from sqlalchemy.exc import NoSuchTableError from sqlalchemy.exc import SAWarning from sqlalchemy.sql import select from sqlalchemy.types import UserDefinedType +from nova.db.sqlalchemy import api as db from nova.db.sqlalchemy import utils from nova import exception from nova.tests import test_migrations import warnings +class CustomType(UserDefinedType): + """Dummy column type for testing unsupported types.""" + def get_col_spec(self): + return "CustomType" + + class TestMigrationUtils(test_migrations.BaseMigrationTestCase): """Class for testing utils that are used in db migrations.""" @@ -75,11 +83,6 @@ class TestMigrationUtils(test_migrations.BaseMigrationTestCase): def test_util_drop_unique_constraint_with_not_supported_sqlite_type(self): - class CustomType(UserDefinedType): - """Dummy column type for testing unsupported types.""" - def get_col_spec(self): - return "CustomType" - table_name = "__test_tmp_table__" uc_name = 'uniq_foo' values = [ @@ -235,3 +238,77 @@ class TestMigrationUtils(test_migrations.BaseMigrationTestCase): len(values) - len(row_ids)) for value in soft_deleted_values: self.assertTrue(value['id'] in deleted_rows_ids) + + def test_check_shadow_table(self): + table_name = 'abc' + for key, engine in self.engines.items(): + meta = MetaData() + meta.bind = engine + + table = Table(table_name, meta, + Column('id', Integer, primary_key=True), + Column('a', Integer), + Column('c', String(256))) + table.create() + + #check missing shadow table + self.assertRaises(NoSuchTableError, + utils.check_shadow_table, engine, table_name) + + shadow_table = Table(db._SHADOW_TABLE_PREFIX + table_name, meta, + Column('id', Integer), + Column('a', Integer)) + shadow_table.create() + + # check missing column + self.assertRaises(exception.NovaException, + utils.check_shadow_table, engine, table_name) + + # check when all is ok + c = Column('c', String(256)) + shadow_table.create_column(c) + self.assertTrue(utils.check_shadow_table(engine, table_name)) + + # check extra column + d = Column('d', Integer) + shadow_table.create_column(d) + self.assertRaises(exception.NovaException, + utils.check_shadow_table, engine, table_name) + + def test_check_shadow_table_different_types(self): + table_name = 'abc' + for key, engine in self.engines.items(): + meta = MetaData() + meta.bind = engine + + table = Table(table_name, meta, + Column('id', Integer, primary_key=True), + Column('a', Integer)) + table.create() + + shadow_table = Table(db._SHADOW_TABLE_PREFIX + table_name, meta, + Column('id', Integer, primary_key=True), + Column('a', String(256))) + shadow_table.create() + self.assertRaises(exception.NovaException, + utils.check_shadow_table, engine, table_name) + + def test_check_shadow_table_with_unsupported_type(self): + table_name = 'abc' + for key, engine in self.engines.items(): + meta = MetaData() + meta.bind = engine + + table = Table(table_name, meta, + Column('id', Integer, primary_key=True), + Column('a', Integer), + Column('c', CustomType)) + table.create() + + shadow_table = Table(db._SHADOW_TABLE_PREFIX + table_name, meta, + Column('id', Integer, primary_key=True), + Column('a', Integer), + Column('c', CustomType)) + shadow_table.create() + + self.assertTrue(utils.check_shadow_table(engine, table_name)) diff --git a/nova/tests/test_migrations.py b/nova/tests/test_migrations.py index d03e18160..b2319cc09 100644 --- a/nova/tests/test_migrations.py +++ b/nova/tests/test_migrations.py @@ -56,6 +56,7 @@ from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import sqlite import sqlalchemy.exc +from nova.db.sqlalchemy import api as db import nova.db.sqlalchemy.migrate_repo from nova.openstack.common import lockutils from nova.openstack.common import log as logging @@ -855,13 +856,13 @@ class TestNovaMigrations(BaseMigrationTestCase, CommonTestsMixIn): meta.reflect(engine) table_names = set(meta.tables.keys()) for table_name in table_names: - if table_name.startswith("shadow_"): + if table_name.startswith(db._SHADOW_TABLE_PREFIX): shadow_name = table_name - base_name = table_name.replace("shadow_", "") + base_name = table_name.replace(db._SHADOW_TABLE_PREFIX, "") self.assertIn(base_name, table_names) else: base_name = table_name - shadow_name = "shadow_" + table_name + shadow_name = db._SHADOW_TABLE_PREFIX + table_name self.assertIn(shadow_name, table_names) shadow_table = get_table(engine, shadow_name) base_table = get_table(engine, base_name) |
