From e76932a0730abfd98092b4b9cbadd3a90cb32fb6 Mon Sep 17 00:00:00 2001 From: Victor Sergeyev Date: Thu, 17 Jul 2014 13:13:20 +0300 Subject: [PATCH] Use metadata.create_all() to initialise DB schema There is no need to run database migration each time, when we want to get a new actual database schema. Running all migration scripts takes a long time on production databases and can be not suitable for SQLite because of Alembic limitation. Added create_schema() method, which supposes to be used for initial installation instead of upgrade('head'). Partial-Bug: #1347604 Change-Id: I039d955f051eda015bef2299122fd1f0fb5c46da --- ironic/cmd/dbsync.py | 8 +++++- ironic/db/migration.py | 4 +++ ironic/db/sqlalchemy/migration.py | 26 +++++++++++++++-- ironic/tests/db/sqlalchemy/test_migrations.py | 28 +++++++++++++++++-- 4 files changed, 61 insertions(+), 5 deletions(-) diff --git a/ironic/cmd/dbsync.py b/ironic/cmd/dbsync.py index d0d6ba6f04..57059ba11b 100644 --- a/ironic/cmd/dbsync.py +++ b/ironic/cmd/dbsync.py @@ -47,6 +47,9 @@ class DBCommand(object): def version(self): print(migration.version()) + def create_schema(self): + migration.create_schema() + def add_command_parsers(subparsers): command_object = DBCommand() @@ -71,6 +74,9 @@ def add_command_parsers(subparsers): parser = subparsers.add_parser('version') parser.set_defaults(func=command_object.version) + parser = subparsers.add_parser('create_schema') + parser.set_defaults(func=command_object.create_schema) + command_opt = cfg.SubCommandOpt('command', title='Command', @@ -85,7 +91,7 @@ def main(): # pls change it to ironic-dbsync upgrade valid_commands = set([ 'upgrade', 'downgrade', 'revision', - 'version', 'stamp' + 'version', 'stamp', 'create_schema', ]) if not set(sys.argv) & valid_commands: sys.argv.append('upgrade') diff --git a/ironic/db/migration.py b/ironic/db/migration.py index 35f19d9422..846a60e249 100644 --- a/ironic/db/migration.py +++ b/ironic/db/migration.py @@ -45,3 +45,7 @@ def stamp(version): def revision(message, autogenerate): return IMPL.revision(message, autogenerate) + + +def create_schema(): + return IMPL.create_schema() diff --git a/ironic/db/sqlalchemy/migration.py b/ironic/db/sqlalchemy/migration.py index 6ebb949f7f..60e3f53b90 100644 --- a/ironic/db/sqlalchemy/migration.py +++ b/ironic/db/sqlalchemy/migration.py @@ -19,8 +19,10 @@ import os import alembic from alembic import config as alembic_config import alembic.migration as alembic_migration +from oslo.db import exception as db_exc from ironic.db.sqlalchemy import api as sqla_api +from ironic.db.sqlalchemy import models def _alembic_config(): @@ -29,13 +31,14 @@ def _alembic_config(): return config -def version(config=None): +def version(config=None, engine=None): """Current database version. :returns: Database version :rtype: string """ - engine = sqla_api.get_engine() + if engine is None: + engine = sqla_api.get_engine() with engine.connect() as conn: context = alembic_migration.MigrationContext.configure(conn) return context.get_current_revision() @@ -53,6 +56,25 @@ def upgrade(revision, config=None): alembic.command.upgrade(config, revision or 'head') +def create_schema(config=None, engine=None): + """Create database schema from models description. + + Can be used for initial installation instead of upgrade('head'). + """ + if engine is None: + engine = sqla_api.get_engine() + + # NOTE(viktors): If we will use metadata.create_all() for non empty db + # schema, it will only add the new tables, but leave + # existing as is. So we should avoid of this situation. + if version(engine=engine) is not None: + raise db_exc.DbMigrationError("DB schema is already under version" + " control. Use upgrade() instead") + + models.Base.metadata.create_all(engine) + stamp('head', config=config) + + def downgrade(revision, config=None): """Used for downgrading database. diff --git a/ironic/tests/db/sqlalchemy/test_migrations.py b/ironic/tests/db/sqlalchemy/test_migrations.py index 1c2533593a..c6350f49c5 100644 --- a/ironic/tests/db/sqlalchemy/test_migrations.py +++ b/ironic/tests/db/sqlalchemy/test_migrations.py @@ -42,7 +42,7 @@ import contextlib from alembic import script import mock -from oslo.db import exception +from oslo.db import exception as db_exc from oslo.db.sqlalchemy import test_base from oslo.db.sqlalchemy import test_migrations from oslo.db.sqlalchemy import utils as db_utils @@ -315,9 +315,33 @@ class MigrationCheckersMixin(object): # Ironic will use oslo.db 0.4.0 or higher. # See bug #1214341 for details. self.assertRaises( - (sqlalchemy.exc.IntegrityError, exception.DBDuplicateEntry), + (sqlalchemy.exc.IntegrityError, db_exc.DBDuplicateEntry), nodes.insert().execute, data) + def test_upgrade_and_version(self): + with patch_with_engine(self.engine): + self.migration_api.upgrade('head') + self.assertIsNotNone(self.migration_api.version()) + + def test_create_schema_and_version(self): + with patch_with_engine(self.engine): + self.migration_api.create_schema() + self.assertIsNotNone(self.migration_api.version()) + + def test_upgrade_and_create_schema(self): + with patch_with_engine(self.engine): + self.migration_api.upgrade('31baaf680d2b') + self.assertRaises(db_exc.DbMigrationError, + self.migration_api.create_schema) + + def test_upgrade_twice(self): + with patch_with_engine(self.engine): + self.migration_api.upgrade('31baaf680d2b') + v1 = self.migration_api.version() + self.migration_api.upgrade('head') + v2 = self.migration_api.version() + self.assertNotEqual(v1, v2) + class TestMigrationsMySQL(MigrationCheckersMixin, WalkVersionsMixin,