summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2013-05-11 00:19:04 +0000
committerGerrit Code Review <review@openstack.org>2013-05-11 00:19:04 +0000
commit7cb353e8d264fe51845b690df42d5597fe063293 (patch)
tree9c0c075a56d37b0b1818112b4b6a7c8c2f0bbde3
parent260d7efce73d2f175a8f647e61d3ff5b71e0f885 (diff)
parenta9b8fbc0e963bc81c4f4dc47dfcc9f31f3d8ef2e (diff)
downloadnova-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.py5
-rw-r--r--nova/db/sqlalchemy/utils.py40
-rw-r--r--nova/tests/test_migration_utils.py87
-rw-r--r--nova/tests/test_migrations.py7
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)