259 lines
8.3 KiB
Python
259 lines
8.3 KiB
Python
#!/usr/bin/env python3
|
|
# Copyright 2012 OpenStack Foundation
|
|
#
|
|
# 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 copy
|
|
|
|
from alembic import command as alembic_command
|
|
from alembic import script as alembic_script
|
|
from alembic import util as alembic_util
|
|
from oslo_config import cfg
|
|
from oslo_log import log
|
|
import pbr.version
|
|
|
|
from keystone.common import sql
|
|
from keystone.common.sql import upgrades
|
|
import keystone.conf
|
|
from keystone.i18n import _
|
|
|
|
# We need to import all of these so the tables are registered. It would be
|
|
# easier if these were all in a central location :(
|
|
import keystone.application_credential.backends.sql # noqa: F401
|
|
import keystone.assignment.backends.sql # noqa: F401
|
|
import keystone.assignment.role_backends.sql_model # noqa: F401
|
|
import keystone.catalog.backends.sql # noqa: F401
|
|
import keystone.credential.backends.sql # noqa: F401
|
|
import keystone.endpoint_policy.backends.sql # noqa: F401
|
|
import keystone.federation.backends.sql # noqa: F401
|
|
import keystone.identity.backends.sql_model # noqa: F401
|
|
import keystone.identity.mapping_backends.sql # noqa: F401
|
|
import keystone.limit.backends.sql # noqa: F401
|
|
import keystone.oauth1.backends.sql # noqa: F401
|
|
import keystone.policy.backends.sql # noqa: F401
|
|
import keystone.resource.backends.sql_model # noqa: F401
|
|
import keystone.resource.config_backends.sql # noqa: F401
|
|
import keystone.revoke.backends.sql # noqa: F401
|
|
import keystone.trust.backends.sql # noqa: F401
|
|
|
|
CONF = keystone.conf.CONF
|
|
LOG = log.getLogger(__name__)
|
|
|
|
|
|
def do_alembic_command(config, cmd, revision=None, **kwargs):
|
|
args = []
|
|
if revision:
|
|
args.append(revision)
|
|
|
|
try:
|
|
getattr(alembic_command, cmd)(config, *args, **kwargs)
|
|
except alembic_util.CommandError as e:
|
|
alembic_util.err(str(e))
|
|
|
|
|
|
def do_generic_show(config, cmd):
|
|
kwargs = {'verbose': CONF.command.verbose}
|
|
do_alembic_command(config, cmd, **kwargs)
|
|
|
|
|
|
def do_validate(config, cmd):
|
|
do_alembic_command(config, 'branches')
|
|
# TODO(stephenfin): Implement these
|
|
# validate_revisions(config)
|
|
# TODO(stephenfin): Implement these
|
|
# validate_head_files(config)
|
|
|
|
|
|
def _find_milestone_revisions(config, milestone, branch=None):
|
|
"""Return the revision(s) for a given milestone."""
|
|
script = alembic_script.ScriptDirectory.from_config(config)
|
|
return [
|
|
(m.revision, label)
|
|
for m in _get_revisions(script)
|
|
for label in (m.branch_labels or [None])
|
|
if milestone in getattr(m.module, 'keystone_milestone', [])
|
|
and (branch is None or branch in m.branch_labels)
|
|
]
|
|
|
|
|
|
def _get_revisions(script):
|
|
return list(script.walk_revisions(base='base', head='heads'))
|
|
|
|
|
|
def do_upgrade(config, cmd):
|
|
branch = None
|
|
|
|
if (CONF.command.revision or CONF.command.delta) and (
|
|
CONF.command.expand or CONF.command.contract
|
|
):
|
|
msg = _('Phase upgrade options do not accept revision specification')
|
|
raise SystemExit(msg)
|
|
|
|
if CONF.command.expand:
|
|
branch = upgrades.EXPAND_BRANCH
|
|
revision = f'{upgrades.EXPAND_BRANCH}@head'
|
|
elif CONF.command.contract:
|
|
branch = upgrades.CONTRACT_BRANCH
|
|
revision = f'{upgrades.CONTRACT_BRANCH}@head'
|
|
elif not CONF.command.revision and not CONF.command.delta:
|
|
msg = _('You must provide a revision or relative delta')
|
|
raise SystemExit(msg)
|
|
else:
|
|
revision = CONF.command.revision or ''
|
|
if '-' in revision:
|
|
msg = _('Negative relative revision (downgrade) not supported')
|
|
raise SystemExit(msg)
|
|
|
|
delta = CONF.command.delta
|
|
if delta:
|
|
if '+' in revision:
|
|
msg = _('Use either --delta or relative revision, not both')
|
|
raise SystemExit(msg)
|
|
if delta < 0:
|
|
msg = _('Negative delta (downgrade) not supported')
|
|
raise SystemExit(msg)
|
|
revision = '%s+%d' % (revision, delta)
|
|
|
|
# leave branchless 'head' revision request backward compatible by
|
|
# applying all heads in all available branches.
|
|
if revision == 'head':
|
|
revision = 'heads'
|
|
|
|
if revision in upgrades.MILESTONES:
|
|
expand_revisions = _find_milestone_revisions(
|
|
config,
|
|
revision,
|
|
upgrades.EXPAND_BRANCH,
|
|
)
|
|
contract_revisions = _find_milestone_revisions(
|
|
config,
|
|
revision,
|
|
upgrades.CONTRACT_BRANCH,
|
|
)
|
|
# Expand revisions must be run before contract revisions
|
|
revisions = expand_revisions + contract_revisions
|
|
else:
|
|
revisions = [(revision, branch)]
|
|
|
|
for revision, branch in revisions:
|
|
# if not CONF.command.sql:
|
|
# run_sanity_checks(config, revision)
|
|
do_alembic_command(
|
|
config,
|
|
cmd,
|
|
revision=revision,
|
|
sql=CONF.command.sql,
|
|
)
|
|
|
|
|
|
def do_revision(config, cmd):
|
|
kwargs = {
|
|
'message': CONF.command.message,
|
|
'autogenerate': CONF.command.autogenerate,
|
|
'sql': CONF.command.sql,
|
|
}
|
|
branches = []
|
|
if CONF.command.expand:
|
|
kwargs['head'] = 'expand@head'
|
|
branches.append(upgrades.EXPAND_BRANCH)
|
|
elif CONF.command.contract:
|
|
kwargs['head'] = 'contract@head'
|
|
branches.append(upgrades.CONTRACT_BRANCH)
|
|
else:
|
|
branches = upgrades.MIGRATION_BRANCHES
|
|
|
|
if not CONF.command.autogenerate:
|
|
for branch in branches:
|
|
args = copy.copy(kwargs)
|
|
version_path = upgrades.get_version_branch_path(
|
|
release=upgrades.CURRENT_RELEASE,
|
|
branch=branch,
|
|
)
|
|
upgrades.check_bootstrap_new_branch(branch, version_path, args)
|
|
do_alembic_command(config, cmd, **args)
|
|
else: # CONF.command.autogenerate
|
|
# autogeneration code will take care of enforcing proper directories
|
|
do_alembic_command(config, cmd, **kwargs)
|
|
|
|
# TODO(stephenfin): Implement these
|
|
# update_head_files(config)
|
|
|
|
|
|
def add_branch_options(parser):
|
|
group = parser.add_mutually_exclusive_group()
|
|
group.add_argument('--expand', action='store_true')
|
|
group.add_argument('--contract', action='store_true')
|
|
return group
|
|
|
|
|
|
def add_alembic_subparser(sub, cmd):
|
|
return sub.add_parser(cmd, help=getattr(alembic_command, cmd).__doc__)
|
|
|
|
|
|
def add_command_parsers(subparsers):
|
|
for name in ['current', 'history', 'branches', 'heads']:
|
|
parser = add_alembic_subparser(subparsers, name)
|
|
parser.set_defaults(func=do_generic_show)
|
|
parser.add_argument(
|
|
'--verbose',
|
|
action='store_true',
|
|
help='Display more verbose output for the specified command',
|
|
)
|
|
|
|
parser = add_alembic_subparser(subparsers, 'upgrade')
|
|
parser.add_argument('--delta', type=int)
|
|
parser.add_argument('--sql', action='store_true')
|
|
parser.add_argument('revision', nargs='?')
|
|
add_branch_options(parser)
|
|
parser.set_defaults(func=do_upgrade)
|
|
|
|
parser = subparsers.add_parser(
|
|
'validate',
|
|
help=alembic_command.branches.__doc__ + ' and validate head file',
|
|
)
|
|
parser.set_defaults(func=do_validate)
|
|
|
|
parser = add_alembic_subparser(subparsers, 'revision')
|
|
parser.add_argument('-m', '--message')
|
|
parser.add_argument('--sql', action='store_true')
|
|
group = add_branch_options(parser)
|
|
group.add_argument('--autogenerate', action='store_true')
|
|
parser.set_defaults(func=do_revision)
|
|
|
|
|
|
command_opt = cfg.SubCommandOpt(
|
|
'command',
|
|
title='Command',
|
|
help=_('Available commands'),
|
|
handler=add_command_parsers,
|
|
)
|
|
|
|
|
|
def main():
|
|
CONF.register_cli_opt(command_opt)
|
|
|
|
keystone.conf.configure()
|
|
sql.initialize()
|
|
|
|
CONF(
|
|
project='keystone',
|
|
version=pbr.version.VersionInfo('keystone').version_string(),
|
|
)
|
|
config = upgrades.get_alembic_config()
|
|
|
|
return bool(CONF.command.func(config, CONF.command.name))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|