diff options
author | Doug Hellmann <doug@doughellmann.com> | 2014-12-12 14:23:13 -0500 |
---|---|---|
committer | Roman Podoliaka <rpodolyaka@mirantis.com> | 2014-12-24 13:28:48 +0200 |
commit | 7063585c60205fe031e1c74289d88886705cfb57 (patch) | |
tree | 85d18890b25d9fb4e76365ce8dbbf282d8479711 /oslo_db/sqlalchemy/migration_cli | |
parent | 571433bfc4936d90602bfac4cbd7e9170c0a8d07 (diff) | |
download | oslo-db-7063585c60205fe031e1c74289d88886705cfb57.tar.gz |
Move files out of the namespace package
Move the public API out of oslo.db to oslo_db. Retain the ability to
import from the old namespace package for backwards compatibility for
this release cycle.
Blueprint: drop-namespace-packages
Change-Id: Ie96b482b9fbcb1d85203ad35bb65c1f43e912a44
Diffstat (limited to 'oslo_db/sqlalchemy/migration_cli')
-rw-r--r-- | oslo_db/sqlalchemy/migration_cli/README.rst | 9 | ||||
-rw-r--r-- | oslo_db/sqlalchemy/migration_cli/__init__.py | 0 | ||||
-rw-r--r-- | oslo_db/sqlalchemy/migration_cli/ext_alembic.py | 78 | ||||
-rw-r--r-- | oslo_db/sqlalchemy/migration_cli/ext_base.py | 79 | ||||
-rw-r--r-- | oslo_db/sqlalchemy/migration_cli/ext_migrate.py | 69 | ||||
-rw-r--r-- | oslo_db/sqlalchemy/migration_cli/manager.py | 71 |
6 files changed, 306 insertions, 0 deletions
diff --git a/oslo_db/sqlalchemy/migration_cli/README.rst b/oslo_db/sqlalchemy/migration_cli/README.rst new file mode 100644 index 0000000..ebbbdcb --- /dev/null +++ b/oslo_db/sqlalchemy/migration_cli/README.rst @@ -0,0 +1,9 @@ +This module could be used either for: +1. Smooth transition from migrate tool to alembic +2. As standalone alembic tool + +Core points: +1. Upgrade/downgrade database with usage of alembic/migrate migrations +or both +2. Compatibility with oslo.config +3. The way to autogenerate new revisions or stamps diff --git a/oslo_db/sqlalchemy/migration_cli/__init__.py b/oslo_db/sqlalchemy/migration_cli/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/oslo_db/sqlalchemy/migration_cli/__init__.py diff --git a/oslo_db/sqlalchemy/migration_cli/ext_alembic.py b/oslo_db/sqlalchemy/migration_cli/ext_alembic.py new file mode 100644 index 0000000..243ae47 --- /dev/null +++ b/oslo_db/sqlalchemy/migration_cli/ext_alembic.py @@ -0,0 +1,78 @@ +# 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. + +import os + +import alembic +from alembic import config as alembic_config +import alembic.migration as alembic_migration + +from oslo_db.sqlalchemy.migration_cli import ext_base +from oslo_db.sqlalchemy import session as db_session + + +class AlembicExtension(ext_base.MigrationExtensionBase): + + order = 2 + + @property + def enabled(self): + return os.path.exists(self.alembic_ini_path) + + def __init__(self, migration_config): + """Extension to provide alembic features. + + :param migration_config: Stores specific configuration for migrations + :type migration_config: dict + """ + self.alembic_ini_path = migration_config.get('alembic_ini_path', '') + self.config = alembic_config.Config(self.alembic_ini_path) + # option should be used if script is not in default directory + repo_path = migration_config.get('alembic_repo_path') + if repo_path: + self.config.set_main_option('script_location', repo_path) + self.db_url = migration_config['db_url'] + + def upgrade(self, version): + return alembic.command.upgrade(self.config, version or 'head') + + def downgrade(self, version): + if isinstance(version, int) or version is None or version.isdigit(): + version = 'base' + return alembic.command.downgrade(self.config, version) + + def version(self): + engine = db_session.create_engine(self.db_url) + with engine.connect() as conn: + context = alembic_migration.MigrationContext.configure(conn) + return context.get_current_revision() + + def revision(self, message='', autogenerate=False): + """Creates template for migration. + + :param message: Text that will be used for migration title + :type message: string + :param autogenerate: If True - generates diff based on current database + state + :type autogenerate: bool + """ + return alembic.command.revision(self.config, message=message, + autogenerate=autogenerate) + + def stamp(self, revision): + """Stamps database with provided revision. + + :param revision: Should match one from repository or head - to stamp + database with most recent revision + :type revision: string + """ + return alembic.command.stamp(self.config, revision=revision) diff --git a/oslo_db/sqlalchemy/migration_cli/ext_base.py b/oslo_db/sqlalchemy/migration_cli/ext_base.py new file mode 100644 index 0000000..205b7b2 --- /dev/null +++ b/oslo_db/sqlalchemy/migration_cli/ext_base.py @@ -0,0 +1,79 @@ +# 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. + +import abc + +import six + + +@six.add_metaclass(abc.ABCMeta) +class MigrationExtensionBase(object): + + # used to sort migration in logical order + order = 0 + + @property + def enabled(self): + """Used for availability verification of a plugin. + + :rtype: bool + """ + return False + + @abc.abstractmethod + def upgrade(self, version): + """Used for upgrading database. + + :param version: Desired database version + :type version: string + """ + + @abc.abstractmethod + def downgrade(self, version): + """Used for downgrading database. + + :param version: Desired database version + :type version: string + """ + + @abc.abstractmethod + def version(self): + """Current database version. + + :returns: Databse version + :rtype: string + """ + + def revision(self, *args, **kwargs): + """Used to generate migration script. + + In migration engines that support this feature, it should generate + new migration script. + + Accept arbitrary set of arguments. + """ + raise NotImplementedError() + + def stamp(self, *args, **kwargs): + """Stamps database based on plugin features. + + Accept arbitrary set of arguments. + """ + raise NotImplementedError() + + def __cmp__(self, other): + """Used for definition of plugin order. + + :param other: MigrationExtensionBase instance + :rtype: bool + """ + return self.order > other.order diff --git a/oslo_db/sqlalchemy/migration_cli/ext_migrate.py b/oslo_db/sqlalchemy/migration_cli/ext_migrate.py new file mode 100644 index 0000000..e31ee3d --- /dev/null +++ b/oslo_db/sqlalchemy/migration_cli/ext_migrate.py @@ -0,0 +1,69 @@ +# 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. + +import logging +import os + +from oslo_db._i18n import _LE +from oslo_db.sqlalchemy import migration +from oslo_db.sqlalchemy.migration_cli import ext_base +from oslo_db.sqlalchemy import session as db_session + + +LOG = logging.getLogger(__name__) + + +class MigrateExtension(ext_base.MigrationExtensionBase): + """Extension to provide sqlalchemy-migrate features. + + :param migration_config: Stores specific configuration for migrations + :type migration_config: dict + """ + + order = 1 + + def __init__(self, migration_config): + self.repository = migration_config.get('migration_repo_path', '') + self.init_version = migration_config.get('init_version', 0) + self.db_url = migration_config['db_url'] + self.engine = db_session.create_engine(self.db_url) + + @property + def enabled(self): + return os.path.exists(self.repository) + + def upgrade(self, version): + version = None if version == 'head' else version + return migration.db_sync( + self.engine, self.repository, version, + init_version=self.init_version) + + def downgrade(self, version): + try: + # version for migrate should be valid int - else skip + if version in ('base', None): + version = self.init_version + version = int(version) + return migration.db_sync( + self.engine, self.repository, version, + init_version=self.init_version) + except ValueError: + LOG.error( + _LE('Migration number for migrate plugin must be valid ' + 'integer or empty, if you want to downgrade ' + 'to initial state') + ) + raise + + def version(self): + return migration.db_version( + self.engine, self.repository, init_version=self.init_version) diff --git a/oslo_db/sqlalchemy/migration_cli/manager.py b/oslo_db/sqlalchemy/migration_cli/manager.py new file mode 100644 index 0000000..c8ab30e --- /dev/null +++ b/oslo_db/sqlalchemy/migration_cli/manager.py @@ -0,0 +1,71 @@ +# 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 stevedore import enabled + + +MIGRATION_NAMESPACE = 'oslo.db.migration' + + +def check_plugin_enabled(ext): + """Used for EnabledExtensionManager.""" + return ext.obj.enabled + + +class MigrationManager(object): + + def __init__(self, migration_config): + self._manager = enabled.EnabledExtensionManager( + MIGRATION_NAMESPACE, + check_plugin_enabled, + invoke_kwds={'migration_config': migration_config}, + invoke_on_load=True + ) + if not self._plugins: + raise ValueError('There must be at least one plugin active.') + + @property + def _plugins(self): + return sorted(ext.obj for ext in self._manager.extensions) + + def upgrade(self, revision): + """Upgrade database with all available backends.""" + results = [] + for plugin in self._plugins: + results.append(plugin.upgrade(revision)) + return results + + def downgrade(self, revision): + """Downgrade database with available backends.""" + # downgrading should be performed in reversed order + results = [] + for plugin in reversed(self._plugins): + results.append(plugin.downgrade(revision)) + return results + + def version(self): + """Return last version of db.""" + last = None + for plugin in self._plugins: + version = plugin.version() + if version: + last = version + return last + + def revision(self, message, autogenerate): + """Generate template or autogenerated revision.""" + # revision should be done only by last plugin + return self._plugins[-1].revision(message, autogenerate) + + def stamp(self, revision): + """Create stamp for a given revision.""" + return self._plugins[-1].stamp(revision) |