Add monasca_db command line tool
This commit adds a schema management tool for the Monasca configuration database. Apart from the usual OpenStack schema management tool subcommands (stamp, upgrade, version) it has two extra subcommands: * fingerprint: for computing a SHA1 fingerprint of the currently currently active database schema. * detect-revision: for identifiying the Alembic revision (if any) corresponding to a database schema that was created with one of the legacy SQL scripts. The data provided by the detect-revision subcommand can be used for stamping the database with Alembic version metadata when transitioning an existing Monasca configuration database to Alembic based database migrations. Story: 2001654 Task: 14341 Change-Id: Ibdd877a23ab5d6d1bbf8d83515c0197554098526
This commit is contained in:
parent
9f2e147308
commit
0250f81cdc
|
@ -4,3 +4,61 @@ Administration guide
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
|
Schema Setup
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
For setting up the Monasca configuration database, we provide ``monasca_db``,
|
||||||
|
an Alembic based database migration tool. Historically, the schema for the
|
||||||
|
configuration database was created by a SQL script. This SQL was changed a
|
||||||
|
couple of times, so ``monasca_db`` comes with a mechanism to detect the SQL
|
||||||
|
script revision being used to create it and stamp the database with the
|
||||||
|
matching Alembic revision.
|
||||||
|
|
||||||
|
Setting up a new database
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
If you are deploying Monasca from scratch, database setup is quite
|
||||||
|
straightforward:
|
||||||
|
|
||||||
|
1. Create a database and configure access credentials with ``ALL PRIVILEGES``
|
||||||
|
permission level on it in the Monasca API configuration file's
|
||||||
|
``[database]`` section.
|
||||||
|
|
||||||
|
2. Run schema migrations: ``monasca_db upgrade``. It will run all migrations up
|
||||||
|
to and including the most recent one (``head``) unless a revision to migrate
|
||||||
|
to is explicitly specified.
|
||||||
|
|
||||||
|
|
||||||
|
Upgrading Existing Database from Legacy Schema
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
|
If you have been running an older version of Monasca, you can attempt to
|
||||||
|
identify and stamp its database schema:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
monasca_db stamp --from-fingerprint
|
||||||
|
|
||||||
|
This command will generate a unique fingerprint for the database schema in
|
||||||
|
question and match that fingerprint with an in-code map of fingerprints to
|
||||||
|
database schema revisions. This should work for all official (shipped as part
|
||||||
|
of the ``monasca-api`` repository) schema scripts. If you used a custom
|
||||||
|
third-party schema script to set up the database, it may not be listed and
|
||||||
|
you'll get an error message similar to this one (the fingerprint hash will
|
||||||
|
vary):
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
Schema fingerprint 3d45493070e3b8e6fc492d2369e51423ca4cc1ac does not match any known legacy revision.
|
||||||
|
|
||||||
|
If this happens to you, please create a Storyboard story against the
|
||||||
|
`openstack/monasca-api project <https://storyboard.openstack.org/#!/project/863>`_.
|
||||||
|
Provide the following alongside the story:
|
||||||
|
|
||||||
|
1. A copy of or pointer to the schema SQL script being used to set up the
|
||||||
|
database.
|
||||||
|
|
||||||
|
2. The fingerprint shown in the error message.
|
||||||
|
|
||||||
|
3. The output of ``monasca_db fingerprint --raw``.
|
||||||
|
|
|
@ -95,6 +95,21 @@ If any of the following applies to the patch, a release note is required:
|
||||||
A release note is suggested if a long-standing or important bug is fixed.
|
A release note is suggested if a long-standing or important bug is fixed.
|
||||||
Otherwise, a release note is not required.
|
Otherwise, a release note is not required.
|
||||||
|
|
||||||
|
Database Migrations
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
As of the Rocky release, Monasca uses `Alembic <http://alembic.zzzcomputing.com/en/latest/>`_
|
||||||
|
migrations to set up its configuration database. If you need to change the
|
||||||
|
configuration database's schema, you need to create a migration to adjust the
|
||||||
|
database accordingly, as follows::
|
||||||
|
|
||||||
|
cd monasca_api/db/
|
||||||
|
alembic revision
|
||||||
|
|
||||||
|
This will create a new skeleton revision for you to edit. You will find
|
||||||
|
existing revisions to use for inspiration in the
|
||||||
|
``/monasca_api/db/alembic/versions/`` directory.
|
||||||
|
|
||||||
Developer reference
|
Developer reference
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,167 @@
|
||||||
|
# Copyright 2018 SUSE Linux GmbH
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
CLI interface for monasca database management.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
from oslo_db.sqlalchemy.migration_cli.ext_alembic import AlembicExtension
|
||||||
|
|
||||||
|
from monasca_api.common.repositories.sqla import sql_repository
|
||||||
|
from monasca_api import conf
|
||||||
|
from monasca_api.db.alembic import env
|
||||||
|
from monasca_api.db.fingerprint import Fingerprint
|
||||||
|
from monasca_api import version
|
||||||
|
|
||||||
|
import monasca_api.config
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
_FP_NOREVISION = ("Schema fingerprint %s does not match any known legacy "
|
||||||
|
"revision.")
|
||||||
|
|
||||||
|
migration_config = {'alembic_ini_path': env.ini_file_path}
|
||||||
|
|
||||||
|
|
||||||
|
def do_detect_revision():
|
||||||
|
fp = Fingerprint(sql_repository.get_engine())
|
||||||
|
|
||||||
|
if fp.revision is None:
|
||||||
|
print(_FP_NOREVISION % fp.sha1)
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
print(fp.revision)
|
||||||
|
|
||||||
|
|
||||||
|
def do_fingerprint():
|
||||||
|
fingerprint = Fingerprint(sql_repository.get_engine())
|
||||||
|
if CONF.command.raw:
|
||||||
|
print(fingerprint.schema_raw, end="")
|
||||||
|
else:
|
||||||
|
print(fingerprint.sha1)
|
||||||
|
|
||||||
|
|
||||||
|
def do_stamp():
|
||||||
|
rev = CONF.command.revision
|
||||||
|
from_fingerprint = CONF.command.from_fingerprint
|
||||||
|
|
||||||
|
engine = sql_repository.get_engine()
|
||||||
|
alembic_ext = AlembicExtension(engine, migration_config)
|
||||||
|
|
||||||
|
if rev is None:
|
||||||
|
if from_fingerprint is False:
|
||||||
|
print("No revision specified. Specify --from-fingerprint to "
|
||||||
|
"attempt a guess based on the current database schema's "
|
||||||
|
"fingerprint.")
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
fp = Fingerprint(engine)
|
||||||
|
if fp.revision is None:
|
||||||
|
print(_FP_NOREVISION % fp.sha1)
|
||||||
|
sys.exit(1)
|
||||||
|
rev = fp.revision
|
||||||
|
|
||||||
|
alembic_ext.stamp(rev)
|
||||||
|
|
||||||
|
|
||||||
|
def do_upgrade():
|
||||||
|
engine = sql_repository.get_engine()
|
||||||
|
alembic_ext = AlembicExtension(engine, migration_config)
|
||||||
|
|
||||||
|
rev = CONF.command.revision
|
||||||
|
db_rev = alembic_ext.version()
|
||||||
|
|
||||||
|
fp = Fingerprint(engine)
|
||||||
|
|
||||||
|
if fp.schema_raw != "" and db_rev is None:
|
||||||
|
print("Non-empty database schema without Alembic version metadata "
|
||||||
|
"detected. Please use the `stamp` subcommand to add version "
|
||||||
|
"metadata.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
alembic_ext.upgrade(rev)
|
||||||
|
|
||||||
|
|
||||||
|
def do_version():
|
||||||
|
engine = sql_repository.get_engine()
|
||||||
|
alembic_ext = AlembicExtension(engine, migration_config)
|
||||||
|
|
||||||
|
version = alembic_ext.version()
|
||||||
|
if version is None:
|
||||||
|
print("Cannot determine version. Check if this database has Alembic "
|
||||||
|
"version information. ")
|
||||||
|
sys.exit(1)
|
||||||
|
print(version)
|
||||||
|
|
||||||
|
|
||||||
|
def add_command_parsers(subparsers):
|
||||||
|
parser = subparsers.add_parser('fingerprint',
|
||||||
|
help="Compute SHA1 fingerprint of "
|
||||||
|
"current database schema ")
|
||||||
|
parser.add_argument('-r', '--raw', action='store_true',
|
||||||
|
help='Print raw schema dump used for '
|
||||||
|
'fingerprinting')
|
||||||
|
parser.set_defaults(func=do_fingerprint)
|
||||||
|
|
||||||
|
parser = subparsers.add_parser('detect-revision',
|
||||||
|
help="Attempt to detect revision "
|
||||||
|
"matching current database "
|
||||||
|
" schema ")
|
||||||
|
parser.set_defaults(func=do_detect_revision)
|
||||||
|
|
||||||
|
parser = subparsers.add_parser('stamp', help='Stamp database with an '
|
||||||
|
'Alembic revision')
|
||||||
|
parser.add_argument('revision', nargs='?', metavar='VERSION',
|
||||||
|
help='Revision to stamp database with',
|
||||||
|
default=None)
|
||||||
|
parser.add_argument('-f', '--from-fingerprint', action='store_true',
|
||||||
|
help='Try to determine VERSION from fingerprint')
|
||||||
|
parser.set_defaults(func=do_stamp)
|
||||||
|
|
||||||
|
parser = subparsers.add_parser('upgrade',
|
||||||
|
help='Upgrade database to given or '
|
||||||
|
'latest revision')
|
||||||
|
parser.add_argument('revision', metavar='VERSION', nargs='?',
|
||||||
|
help='Alembic revision to upgrade database to',
|
||||||
|
default='head')
|
||||||
|
parser.add_argument('-f', '--from-fingerprint', action='store_true',
|
||||||
|
help='Try to determine VERSION from fingerprint')
|
||||||
|
parser.set_defaults(func=do_upgrade)
|
||||||
|
|
||||||
|
parser = subparsers.add_parser('version', help="Show database's current Alembic version")
|
||||||
|
parser.set_defaults(func=do_version)
|
||||||
|
|
||||||
|
|
||||||
|
command_opt = cfg.SubCommandOpt('command',
|
||||||
|
title='Monasca DB manager',
|
||||||
|
help='Available commands',
|
||||||
|
handler=add_command_parsers)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
CONF.register_cli_opt(command_opt)
|
||||||
|
CONF(args=sys.argv[1:],
|
||||||
|
default_config_files=monasca_api.config.get_config_file(None),
|
||||||
|
prog='api',
|
||||||
|
project='monasca',
|
||||||
|
version=version.version_str)
|
||||||
|
|
||||||
|
conf.register_opts()
|
||||||
|
|
||||||
|
CONF.command.func()
|
|
@ -45,14 +45,12 @@ def parse_args(argv=None, config_file=None):
|
||||||
|
|
||||||
argv = (argv if argv is not None else sys.argv[1:])
|
argv = (argv if argv is not None else sys.argv[1:])
|
||||||
args = ([] if _is_running_under_gunicorn() else argv or [])
|
args = ([] if _is_running_under_gunicorn() else argv or [])
|
||||||
config_file = (_get_deprecated_config_file()
|
|
||||||
if config_file is None else config_file)
|
|
||||||
|
|
||||||
CONF(args=args,
|
CONF(args=args,
|
||||||
prog='api',
|
prog='api',
|
||||||
project='monasca',
|
project='monasca',
|
||||||
version=version.version_str,
|
version=version.version_str,
|
||||||
default_config_files=[config_file] if config_file else None,
|
default_config_files=get_config_file(config_file),
|
||||||
description='RESTful API for alarming in the cloud')
|
description='RESTful API for alarming in the cloud')
|
||||||
|
|
||||||
log.setup(CONF,
|
log.setup(CONF,
|
||||||
|
@ -64,6 +62,21 @@ def parse_args(argv=None, config_file=None):
|
||||||
_CONF_LOADED = True
|
_CONF_LOADED = True
|
||||||
|
|
||||||
|
|
||||||
|
def get_config_file(config_file):
|
||||||
|
"""Get config file in a format suitable for CONF constructor
|
||||||
|
|
||||||
|
Returns the config file name as a single element array. If a config file
|
||||||
|
was explicitly, specified, that file's name is returned. If there isn't and a
|
||||||
|
legacy config file is present that one is returned. Otherwise we return
|
||||||
|
None. This is what the CONF constructor expects for its
|
||||||
|
default_config_files keyword argument.
|
||||||
|
"""
|
||||||
|
if config_file is not None:
|
||||||
|
return [config_file]
|
||||||
|
|
||||||
|
return _get_deprecated_config_file()
|
||||||
|
|
||||||
|
|
||||||
def _is_running_under_gunicorn():
|
def _is_running_under_gunicorn():
|
||||||
"""Evaluates if api runs under gunicorn."""
|
"""Evaluates if api runs under gunicorn."""
|
||||||
content = filter(lambda x: x != sys.executable and _GUNICORN_MARKER in x,
|
content = filter(lambda x: x != sys.executable and _GUNICORN_MARKER in x,
|
||||||
|
@ -87,4 +100,5 @@ def _get_deprecated_config_file():
|
||||||
if old_files is not None and len(old_files) > 0:
|
if old_files is not None and len(old_files) > 0:
|
||||||
LOG.warning('Detected old location "/etc/monasca/api-config.conf" '
|
LOG.warning('Detected old location "/etc/monasca/api-config.conf" '
|
||||||
'of main configuration file')
|
'of main configuration file')
|
||||||
return old_files[0]
|
return [old_files[0]]
|
||||||
|
return None
|
||||||
|
|
|
@ -14,8 +14,8 @@
|
||||||
|
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
|
|
||||||
import monasca_api.config
|
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
from alembic import config as alembic_config
|
from alembic import config as alembic_config
|
||||||
from alembic import context
|
from alembic import context
|
||||||
|
@ -23,20 +23,22 @@ from logging.config import fileConfig
|
||||||
|
|
||||||
from monasca_api.common.repositories.sqla import models
|
from monasca_api.common.repositories.sqla import models
|
||||||
from monasca_api.common.repositories.sqla import sql_repository
|
from monasca_api.common.repositories.sqla import sql_repository
|
||||||
|
import monasca_api.config
|
||||||
|
|
||||||
ini_file_path = os.path.join(os.path.dirname(__file__), '..', 'alembic.ini')
|
ini_file_path = os.path.join(os.path.dirname(__file__), '..', 'alembic.ini')
|
||||||
|
|
||||||
# This indicates whether we are running with a viable Alembic
|
# This indicates whether we are running with a viable Alembic
|
||||||
# context (necessary to do skip run_migrations_online() below
|
# context (necessary to skip run_migrations_online() below
|
||||||
# if sphinx imports this file without a viable Alembic
|
# if sphinx imports this file without a viable Alembic
|
||||||
# context)
|
# context)
|
||||||
have_context = True
|
have_context = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
config = context.config
|
config = context.config
|
||||||
# FIXME: Move this to the monasca_db entry point later.
|
# Only load Monasca configuration if imported by alembic CLI tool (the
|
||||||
# Load monasca-api config (from files only)
|
# monasca_db command will handle this on its own).
|
||||||
monasca_api.config.parse_args(argv=[])
|
if os.path.basename(sys.argv[0]) == 'alembic':
|
||||||
|
monasca_api.config.parse_args(argv=[])
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
config = alembic_config.Config(ini_file_path)
|
config = alembic_config.Config(ini_file_path)
|
||||||
have_context = False
|
have_context = False
|
||||||
|
@ -76,14 +78,5 @@ def run_migrations_online():
|
||||||
with context.begin_transaction():
|
with context.begin_transaction():
|
||||||
context.run_migrations()
|
context.run_migrations()
|
||||||
|
|
||||||
|
|
||||||
def fingerprint_db():
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def stamp_db():
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
if have_context:
|
if have_context:
|
||||||
run_migrations_online()
|
run_migrations_online()
|
||||||
|
|
|
@ -401,26 +401,25 @@ def upgrade():
|
||||||
op.create_table(
|
op.create_table(
|
||||||
'sub_alarm',
|
'sub_alarm',
|
||||||
sa.Column('id',
|
sa.Column('id',
|
||||||
sa.dialects.mysql.VARCHAR(length=36, charset='utf8mb4',
|
sa.dialects.mysql.VARCHAR(length=36,
|
||||||
collation='utf8mb4_unicode_ci'),
|
collation='utf8mb4_unicode_ci'),
|
||||||
nullable=False),
|
nullable=False),
|
||||||
sa.Column('alarm_id',
|
sa.Column('alarm_id',
|
||||||
sa.dialects.mysql.VARCHAR(length=36, charset='utf8mb4',
|
sa.dialects.mysql.VARCHAR(length=36,
|
||||||
collation='utf8mb4_unicode_ci'),
|
collation='utf8mb4_unicode_ci'),
|
||||||
sa.ForeignKey('alarm.id', ondelete='CASCADE',
|
sa.ForeignKey('alarm.id', ondelete='CASCADE',
|
||||||
name='fk_sub_alarm'),
|
name='fk_sub_alarm'),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
server_default=''),
|
server_default=''),
|
||||||
sa.Column('sub_expression_id',
|
sa.Column('sub_expression_id',
|
||||||
sa.dialects.mysql.VARCHAR(length=36, charset='utf8mb4',
|
sa.dialects.mysql.VARCHAR(length=36,
|
||||||
collation='utf8mb4_unicode_ci'),
|
collation='utf8mb4_unicode_ci'),
|
||||||
sa.ForeignKey('sub_alarm_definition.id',
|
sa.ForeignKey('sub_alarm_definition.id',
|
||||||
name='fk_sub_alarm_expr'),
|
name='fk_sub_alarm_expr'),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
server_default=''),
|
server_default=''),
|
||||||
sa.Column('expression',
|
sa.Column('expression',
|
||||||
sa.dialects.mysql.LONGTEXT(charset='utf8mb4',
|
sa.dialects.mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
|
||||||
collation='utf8mb4_unicode_ci'),
|
|
||||||
nullable=False),
|
nullable=False),
|
||||||
sa.Column('created_at',
|
sa.Column('created_at',
|
||||||
sa.DateTime(),
|
sa.DateTime(),
|
||||||
|
@ -428,8 +427,7 @@ def upgrade():
|
||||||
sa.Column('updated_at',
|
sa.Column('updated_at',
|
||||||
sa.DateTime(),
|
sa.DateTime(),
|
||||||
nullable=False),
|
nullable=False),
|
||||||
sa.PrimaryKeyConstraint('id'),
|
sa.PrimaryKeyConstraint('id'))
|
||||||
mysql_charset='latin1')
|
|
||||||
|
|
||||||
op.create_table(
|
op.create_table(
|
||||||
'schema_migrations',
|
'schema_migrations',
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
# Copyright 2018 SUSE Linux GmbH
|
||||||
|
#
|
||||||
|
# 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 hashlib
|
||||||
|
|
||||||
|
from sqlalchemy import MetaData
|
||||||
|
|
||||||
|
# Map of SHA1 fingerprints to alembic revisions.
|
||||||
|
_REVS = {"43e5913b0272077321ab6f25ffbcda7149b6284b": "00597b5c8325",
|
||||||
|
"c4e5c870c705421faa4041405b5a895970faa434": "0cce983d957a",
|
||||||
|
"f7a79c4eea9c9d130277a64eb6d2d16587088dbb": "30181b42434b",
|
||||||
|
"529f266f7ed42929d5405616810546e4615153e8": "6b2b88f3cab4",
|
||||||
|
"857904f960af77c0554c4c38d73ed47df7c949b4": "8781a256f0c1",
|
||||||
|
"773489fb7bfa84bf2db0e1ff1ab96bce7fb4ecd7": "c2f85438d6f3",
|
||||||
|
"f29f18a30519a1bae9dcee85a604eb72886e34d3": "d8b801498850",
|
||||||
|
"dd47cb01f11cb5cd7fec6bda6a190bc10b4659a6": "f69cb3152a76",
|
||||||
|
|
||||||
|
# Database created with UTF8 default charset
|
||||||
|
"5dda7af1fd708095e6c9298976abb1242bbd1848": "8781a256f0c1",
|
||||||
|
"7fb1ce4a60f0065505096843bfd21f4ef4c5d1e0": "f69cb3152a76"}
|
||||||
|
|
||||||
|
|
||||||
|
class Fingerprint(object):
|
||||||
|
|
||||||
|
def __init__(self, engine):
|
||||||
|
metadata = MetaData(bind=engine, reflect=True)
|
||||||
|
|
||||||
|
schema_strings = []
|
||||||
|
|
||||||
|
for table in metadata.sorted_tables:
|
||||||
|
# Omit this table to maintain a consistent fingerprint when
|
||||||
|
# fingerprint a migrated schema is fingerprinted.
|
||||||
|
if table.name == "alembic_version":
|
||||||
|
continue
|
||||||
|
table.metadata = None
|
||||||
|
columns = []
|
||||||
|
for column in table.columns:
|
||||||
|
column.server_default = None
|
||||||
|
columns.append(repr(column))
|
||||||
|
table.columns = []
|
||||||
|
schema_strings.append(repr(table))
|
||||||
|
|
||||||
|
for column in columns:
|
||||||
|
schema_strings.append(" " + repr(column))
|
||||||
|
|
||||||
|
schema_strings.append("")
|
||||||
|
|
||||||
|
self.schema_raw = "\n".join(schema_strings)
|
||||||
|
self.sha1 = hashlib.sha1(self.schema_raw).hexdigest()
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.revision = _REVS[self.sha1]
|
||||||
|
except KeyError:
|
||||||
|
# Fingerprint does not match any revisions
|
||||||
|
self.revision = None
|
|
@ -35,6 +35,7 @@ cassandra =
|
||||||
[entry_points]
|
[entry_points]
|
||||||
console_scripts =
|
console_scripts =
|
||||||
monasca-api = monasca_api.api.server:launch
|
monasca-api = monasca_api.api.server:launch
|
||||||
|
monasca_db = monasca_api.cmd.monasca_db:main
|
||||||
|
|
||||||
wsgi_scripts =
|
wsgi_scripts =
|
||||||
monasca-api-wsgi = monasca_api.api.wsgi:main
|
monasca-api-wsgi = monasca_api.api.wsgi:main
|
||||||
|
|
Loading…
Reference in New Issue