From cc5fc43a97e064488ddde729bb43a3dbb99b79b9 Mon Sep 17 00:00:00 2001 From: Vamsi Date: Wed, 11 Apr 2018 23:46:04 +0530 Subject: [PATCH] Work in progress (db cli) --- retailstore/db/migration/__init__.py | 0 retailstore/db/migration/alembic.ini | 55 ++++++++ .../migration/alembic_migrations/__init__.py | 0 .../db/migration/alembic_migrations/env.py | 67 ++++++++++ .../alembic_migrations/script.py.mako | 21 ++++ .../alembic_migrations/versions/__init__.py | 0 retailstore/db/migration/cli.py | 118 ++++++++++++++++++ setup.cfg | 2 + 8 files changed, 263 insertions(+) create mode 100644 retailstore/db/migration/__init__.py create mode 100644 retailstore/db/migration/alembic.ini create mode 100644 retailstore/db/migration/alembic_migrations/__init__.py create mode 100644 retailstore/db/migration/alembic_migrations/env.py create mode 100644 retailstore/db/migration/alembic_migrations/script.py.mako create mode 100644 retailstore/db/migration/alembic_migrations/versions/__init__.py create mode 100644 retailstore/db/migration/cli.py diff --git a/retailstore/db/migration/__init__.py b/retailstore/db/migration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/retailstore/db/migration/alembic.ini b/retailstore/db/migration/alembic.ini new file mode 100644 index 0000000..45de0e3 --- /dev/null +++ b/retailstore/db/migration/alembic.ini @@ -0,0 +1,55 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +script_location = %(here)s/alembic_migrations + +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# default to an empty string because the Tacker migration cli will +# extract the correct value and set it programatically before alemic is fully +# invoked. +sqlalchemy.url = postgresql+psycopg2://root:root@postgresql.ucp:5432/retail_store + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S + +[database] +connection = test \ No newline at end of file diff --git a/retailstore/db/migration/alembic_migrations/__init__.py b/retailstore/db/migration/alembic_migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/retailstore/db/migration/alembic_migrations/env.py b/retailstore/db/migration/alembic_migrations/env.py new file mode 100644 index 0000000..d13c5f4 --- /dev/null +++ b/retailstore/db/migration/alembic_migrations/env.py @@ -0,0 +1,67 @@ +from alembic import context +from sqlalchemy import create_engine, pool + +from retailstore.db.sqlalchemy import models + + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +app_config = config + +# set the target for 'autogenerate' support +target_metadata = models.BASE.metadata + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with either a URL + or an Engine. + + Calls to context.execute() here emit the given string to the + script output. + + """ + kwargs = dict() + if config.database.connection: + kwargs['url'] = app_config.database.connection + else: + kwargs['dialect_name'] = app_config.database.engine + context.configure(**kwargs) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + import pdb; pdb.set_trace() # breakpoint bbf1e71a // + + engine = create_engine( + app_config.database.connection, + poolclass=pool.NullPool) + + connection = engine.connect() + context.configure( + connection=connection, + target_metadata=target_metadata + ) + + try: + with context.begin_transaction(): + context.run_migrations() + finally: + connection.close() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/retailstore/db/migration/alembic_migrations/script.py.mako b/retailstore/db/migration/alembic_migrations/script.py.mako new file mode 100644 index 0000000..9404347 --- /dev/null +++ b/retailstore/db/migration/alembic_migrations/script.py.mako @@ -0,0 +1,21 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision} +Create Date: ${create_date} + +""" + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +from tacker.db import migration + + +def upgrade(active_plugins=None, options=None): + ${upgrades if upgrades else "pass"} diff --git a/retailstore/db/migration/alembic_migrations/versions/__init__.py b/retailstore/db/migration/alembic_migrations/versions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/retailstore/db/migration/cli.py b/retailstore/db/migration/cli.py new file mode 100644 index 0000000..ac114ce --- /dev/null +++ b/retailstore/db/migration/cli.py @@ -0,0 +1,118 @@ +import os + +from alembic import command as alembic_command +from alembic import config as alembic_config +from alembic import script as alembic_script +from alembic import util as alembic_util +from oslo_config import cfg + +import retailstore.conf.config as config + +config.config_mgr.register_options() +CONF = config.config_mgr.conf + +HEAD_FILENAME = 'HEAD' + +def do_alembic_command(config, cmd, *args, **kwargs): + try: + getattr(alembic_command, cmd)(config, *args, **kwargs) + except alembic_util.CommandError as e: + alembic_util.err(str(e)) + +def do_upgrade(config, cmd): + if not CONF.command.revision and not CONF.command.delta: + raise SystemExit('You must provide a revision or relative delta') + + revision = CONF.command.revision + + if CONF.command.delta: + revision = '+%s' % str(CONF.command.delta) + else: + revision = CONF.command.revision + + do_alembic_command(config, cmd, revision, sql=CONF.command.sql) + + +def update_head_file(config): + script = alembic_script.ScriptDirectory.from_config(config) + if len(script.get_heads()) > 1: + alembic_util.err('Timeline branches unable to generate timeline') + + head_path = os.path.join(script.versions, HEAD_FILENAME) + with open(head_path, 'w+') as f: + f.write(script.get_current_head()) + + +def do_revision(config, cmd): + do_alembic_command(config, cmd, + message=CONF.command.message, + autogenerate=CONF.command.autogenerate, + sql=CONF.command.sql) + update_head_file(config) + +def add_command_parsers(subparsers): + for name in ['current', 'history', 'branches']: + parser = subparsers.add_parser(name) + parser.set_defaults(func=do_alembic_command) + + parser = subparsers.add_parser('check_migration') + parser.set_defaults(func=do_check_migration) + + parser = subparsers.add_parser('upgrade') + parser.add_argument('--delta', type=int) + parser.add_argument('--sql', action='store_true') + parser.add_argument('revision', nargs='?') + parser.set_defaults(func=do_upgrade) + + parser = subparsers.add_parser('stamp') + parser.add_argument('--sql', action='store_true') + parser.add_argument('revision') + parser.set_defaults(func=do_stamp) + + parser = subparsers.add_parser('revision') + parser.add_argument('-m', '--message') + parser.add_argument('--autogenerate', action='store_true') + parser.add_argument('--sql', action='store_true') + parser.set_defaults(func=do_revision) + + parser = subparsers.add_parser('purge_deleted') + parser.set_defaults(func=purge_deleted) + # positional parameter + parser.add_argument( + 'resource', + choices=['all', 'events', 'vnf', 'vnfd', 'vims'], + help='Resource name for which deleted entries are to be purged.') + # optional parameter, can be skipped. default='90' + parser.add_argument('-a', '--age', nargs='?', default='90', + help='How long to preserve deleted data, ' + 'defaults to 90') + # optional parameter, can be skipped. default='days' + parser.add_argument( + '-g', '--granularity', default='days', + choices=['days', 'hours', 'minutes', 'seconds'], + help='Granularity to use for age argument, defaults to days.') + + +command_opt = cfg.SubCommandOpt('command', + title='Command', + help='Available commands', + handler=add_command_parsers) + +CONF.register_cli_opt(command_opt) + + +def main(): + config = alembic_config.Config( + os.path.join(os.path.dirname(__file__), 'alembic.ini') + ) + config.set_main_option('script_location', + 'retailstore.db.migration:alembic_migrations') + # env = os.environ + # dirname = env.get('CONFIG_DIR', '/etc/retailstore').strip() + # config_files = [os.path.join(dirname, 'retailstore.conf')] + + # CONF([], project='retailstore', default_config_files=config_files) + CONF() + config.store_config = CONF + + CONF.command.func(config, CONF.command.name) diff --git a/setup.cfg b/setup.cfg index 54dd008..ac12b3f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -17,5 +17,7 @@ packages = retailstore [entry_points] +console_scripts = + db-manage = retailstore.db.migration.cli:main oslo.config.opts = retailstore.conf = retailstore.conf.config:list_opts