Add Evoque model framework

Fisrtly you should install evoque:
    `python setup.py install develop`

1. tox -egenconfig
2. mkdir /etc/evoquea && cp etc/evoque/evoque.conf /etc/evoquea
3. create database `evoque` and grant privileges for someone
4. update the database connection of /etc/evoque/evoque.conf
5. evoque-manage db_sync

Change-Id: Ice7fddeb93d1c524ba4ab09c26e14170eeca70f9
This commit is contained in:
lawrancejing 2015-11-05 09:28:35 +00:00
parent 16e5e156cd
commit 10f6fb8070
17 changed files with 433 additions and 2 deletions

View File

@ -3,3 +3,4 @@ output_file = etc/evoque/evoque.conf
wrap_width = 79
namespace = evoque
namespace = oslo.log
namespace = oslo.db

82
evoque/cmd/manage.py Normal file
View File

@ -0,0 +1,82 @@
# 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.
"""
Glance Management Utility
"""
import os
import sys
from oslo_config import cfg
from oslo_log import log as logging
from evoque.db import api
from evoque import version
CONF = cfg.CONF
# If ../evoque/__init__.py exists, add ../ to Python search path, so that
# it will override what happens to be installed in /usr/(local/)lib/python...
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
os.pardir,
os.pardir))
if os.path.exists(os.path.join(possible_topdir, 'evoque', '__init__.py')):
sys.path.insert(0, possible_topdir)
def do_db_version():
'''Print database's current migration level.'''
print(api.db_version(api.get_engine()))
def do_db_sync():
'''Place a database under migration control and upgrade.
DB is created first if necessary.
'''
api.db_sync(api.get_engine(), CONF.command.version)
def add_command_parsers(subparsers):
parser = subparsers.add_parser('db_version')
parser.set_defaults(func=do_db_version)
parser = subparsers.add_parser('db_sync')
parser.set_defaults(func=do_db_sync)
parser.add_argument('version', nargs='?')
parser.add_argument('current_version', nargs='?')
command_opt = cfg.SubCommandOpt('command',
title='Commands',
help='Show available commands.',
handler=add_command_parsers)
def main():
logging.register_options(CONF)
logging.setup(CONF, 'evoque-manage')
CONF.register_cli_opt(command_opt)
try:
default_config_files = cfg.find_config_files('evoque', 'evoque-manage')
CONF(sys.argv[1:], project='evoque', prog='evoque-manage',
version=version.version_info.version_string(),
default_config_files=default_config_files)
except RuntimeError as e:
sys.exit("ERROR: %s" % e)
try:
CONF.command.func()
except Exception as e:
sys.exit("ERROR: %s" % e)

0
evoque/db/__init__.py Normal file
View File

46
evoque/db/api.py Normal file
View File

@ -0,0 +1,46 @@
# 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.
'''
Interface for database access.
SQLAlchemy is currently the only supported backend.
'''
from oslo_config import cfg
from oslo_db import api
CONF = cfg.CONF
_BACKEND_MAPPING = {'sqlalchemy': 'evoque.db.sqlalchemy.api'}
IMPL = api.DBAPI.from_config(CONF, backend_mapping=_BACKEND_MAPPING)
def get_engine():
return IMPL.get_engine()
# Tickets
def ticket_create(context, values):
return IMPL.ticket_create(context, values)
# Utils
def db_sync(engine, version=None):
"""Migrate the database to `version` or the most recent version."""
return IMPL.db_sync(engine, version=version)
def db_version(engine):
"""Display the current database version."""
return IMPL.db_version(engine)

View File

View File

@ -0,0 +1,72 @@
# 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.
'''
Implementation of SQLAlchemy backend.
'''
import sys
from oslo_config import cfg
from oslo_db.sqlalchemy import session as db_session
from evoque.db.sqlalchemy import migration
from evoque.db.sqlalchemy import models
CONF = cfg.CONF
_facade = None
def get_facade():
global _facade
if not _facade:
_facade = db_session.EngineFacade.from_config(CONF)
return _facade
get_engine = lambda: get_facade().get_engine()
get_session = lambda: get_facade().get_session()
def get_backend():
"""The backend is this module itself."""
return sys.modules[__name__]
def model_query(context, *args):
session = _session(context)
query = session.query(*args)
return query
def _session(context):
return (context and context.session) or get_session()
# Tickets
def cluster_create(context, values):
cluster_ref = models.Cluster()
cluster_ref.update(values)
cluster_ref.save(_session(context))
return cluster_ref
# Utils
def db_sync(engine, version=None):
"""Migrate the database to `version` or the most recent version."""
return migration.db_sync(engine, version=version)
def db_version(engine):
"""Display the current database version."""
return migration.db_version(engine)

View File

@ -0,0 +1,4 @@
This is a database migration repository.
More information at
http://code.google.com/p/sqlalchemy-migrate/

View File

@ -0,0 +1,5 @@
#!/usr/bin/env python
from migrate.versioning.shell import main
if __name__ == '__main__':
main(debug='False')

View File

@ -0,0 +1,25 @@
[db_settings]
# Used to identify which repository this database is versioned under.
# You can use the name of your project.
repository_id=evoque
# The name of the database table used to track the schema version.
# This name shouldn't already be used by your project.
# If this is changed once a database is under version control, you'll need to
# change the table name in each database too.
version_table=migrate_version
# When committing a change script, Migrate will attempt to generate the
# sql for all supported databases; normally, if one of them fails - probably
# because you don't have that database installed - it is ignored and the
# commit continues, perhaps ending successfully.
# Databases in this list MUST compile successfully during a commit, or the
# entire commit will fail. List the databases your application will actually
# be using to ensure your updates to that database work properly.
# This must be a list; example: ['postgres','sqlite']
required_dbs=[]
# When creating new change scripts, Migrate will stamp the new script with
# a version number. By default this is latest_version + 1. You can set this
# to 'true' to tell Migrate to use the UTC timestamp instead.
use_timestamp_numbering=False

View File

@ -0,0 +1,60 @@
# 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 sqlalchemy
from evoque.db.sqlalchemy import types
def upgrade(migrate_engine):
meta = sqlalchemy.MetaData()
meta.bind = migrate_engine
ticket = sqlalchemy.Table(
'ticket', meta,
sqlalchemy.Column('id', sqlalchemy.String(36),
primary_key=True, nullable=False),
sqlalchemy.Column('name', sqlalchemy.String(255)),
sqlalchemy.Column('type', sqlalchemy.String(255)),
sqlalchemy.Column('status', sqlalchemy.String(255)),
sqlalchemy.Column('meta_data', types.Dict),
sqlalchemy.Column('user', sqlalchemy.String(32), nullable=False),
sqlalchemy.Column('project', sqlalchemy.String(32), nullable=False),
sqlalchemy.Column('domain', sqlalchemy.String(32)),
sqlalchemy.Column('user_id', sqlalchemy.String(255), nullable=False),
sqlalchemy.Column('project_id', sqlalchemy.String(255),
nullable=False),
sqlalchemy.Column('domain_id', sqlalchemy.String(255)),
sqlalchemy.Column('created_time', sqlalchemy.DateTime),
sqlalchemy.Column('updated_time', sqlalchemy.DateTime),
sqlalchemy.Column('deleted_time', sqlalchemy.DateTime),
mysql_engine='InnoDB',
mysql_charset='utf8'
)
tables = (
ticket,
)
for index, table in enumerate(tables):
try:
table.create()
except Exception:
# If an error occurs, drop all tables created so far to return
# to the previously existing state.
meta.drop_all(tables=tables[:index])
raise
def downgrade(migrate_engine):
raise NotImplementedError('Database downgrade not supported - '
'would drop all tables')

View File

@ -0,0 +1,31 @@
# 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
from oslo_db.sqlalchemy import migration as oslo_migration
INIT_VERSION = 000
def db_sync(engine, version=None):
path = os.path.join(os.path.abspath(os.path.dirname(__file__)),
'migrate_repo')
return oslo_migration.db_sync(engine, path, version,
init_version=INIT_VERSION)
def db_version(engine):
path = os.path.join(os.path.abspath(os.path.dirname(__file__)),
'migrate_repo')
return oslo_migration.db_version(engine, path, INIT_VERSION)

View File

@ -0,0 +1,67 @@
# 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.
"""
SQLAlchemy models for Evoque data.
"""
import uuid
from oslo_db.sqlalchemy import models
from oslo_utils import timeutils
import sqlalchemy
from sqlalchemy.ext import declarative
from evoque.db.sqlalchemy import types
BASE = declarative.declarative_base()
class EvoqueBase(models.TimestampMixin,
models.ModelBase):
"""Base class for Evoque Models."""
__table_args__ = {'mysql_engine': 'InnoDB'}
deleted_at = sqlalchemy.Column(sqlalchemy.DateTime)
deleted = sqlalchemy.Column(sqlalchemy.Boolean, default=False)
metadata = None
def delete(self, session):
"""Delete this object."""
self.deleted = True
self.deleted_at = timeutils.utcnow()
self.save(session=session)
class Ticket(BASE, EvoqueBase):
"""Represents a ticket created by the Evoque engine."""
__tablename__ = 'ticket'
id = sqlalchemy.Column('id', sqlalchemy.String(36), primary_key=True,
default=lambda: str(uuid.uuid4()))
name = sqlalchemy.Column('name', sqlalchemy.String(255))
type = sqlalchemy.Column(sqlalchemy.String(255))
status = sqlalchemy.Column(sqlalchemy.String(255))
meta_data = sqlalchemy.Column(types.Dict)
user = sqlalchemy.Column(sqlalchemy.String(32), nullable=False)
project = sqlalchemy.Column(sqlalchemy.String(32), nullable=False)
domain = sqlalchemy.Column(sqlalchemy.String(32))
user_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=False)
project_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=False)
domain_id = sqlalchemy.Column(sqlalchemy.String(255))
created_time = sqlalchemy.Column(sqlalchemy.DateTime)
updated_time = sqlalchemy.Column(sqlalchemy.DateTime)
deleted_time = sqlalchemy.Column(sqlalchemy.DateTime)

View File

@ -0,0 +1,34 @@
# 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 json
from sqlalchemy.dialects import mysql
from sqlalchemy import types
class Dict(types.TypeDecorator):
impl = types.Text
def load_dialect_impl(self, dialect):
if dialect.name == 'mysql':
return dialect.type_descriptor(mysql.LONGTEXT())
else:
return self.impl
def process_bind_param(self, value, dialect):
return json.dumps(value)
def process_result_value(self, value, dialect):
if value is None:
return None
return json.loads(value)

View File

@ -2,10 +2,13 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
oslo.i18n>=1.5.0
pbr>=1.6
oslo.config>=2.3.0 # Apache-2.0
oslo.config>=2.6.0 # Apache-2.0
oslo.db>=3.0.0 # Apache-2.0
oslo.i18n>=1.5.0 # Apache-2.0
oslo.log>=1.8.0 # Apache-2.0
pecan>=1.0.0
SQLAlchemy<1.1.0,>=0.9.9
sqlalchemy-migrate>=0.9.6
WebOb>=1.2.3
Werkzeug>=0.7 # BSD License

View File

@ -26,6 +26,7 @@ packages =
[entry_points]
console_scripts =
evoque-api = evoque.cmd.api:main
evoque-manage = evoque.cmd.manage:main
oslo.config.opts =
evoque = evoque.opts:list_opts