cascade_service: DB infrastructure
Base DAL implementation and DevStack integration. Our database schema is under design so the models may be changed later. Partially implements: blueprint implement-dal Change-Id: I8b16b3217e6b72e04bd8886d01d638f2d5a5c388
This commit is contained in:
		
							
								
								
									
										36
									
								
								cmd/manage.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								cmd/manage.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
# Copyright 2015 Huawei Technologies Co., Ltd.
 | 
			
		||||
# All Rights Reserved
 | 
			
		||||
#
 | 
			
		||||
#    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 sys
 | 
			
		||||
 | 
			
		||||
from oslo_config import cfg
 | 
			
		||||
 | 
			
		||||
from tricircle.db import core
 | 
			
		||||
import tricircle.db.migration_helpers as migration_helpers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main(argv=None, config_files=None):
 | 
			
		||||
    core.initialize()
 | 
			
		||||
    cfg.CONF(args=argv[2:],
 | 
			
		||||
             project='tricircle',
 | 
			
		||||
             default_config_files=config_files)
 | 
			
		||||
    migration_helpers.find_migrate_repo()
 | 
			
		||||
    migration_helpers.sync_repo(1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    config_file = sys.argv[1]
 | 
			
		||||
    main(argv=sys.argv, config_files=[config_file])
 | 
			
		||||
@@ -40,6 +40,9 @@ if [[ "$Q_ENABLE_TRICIRCLE" == "True" ]]; then
 | 
			
		||||
        configure_tricircle_plugin
 | 
			
		||||
        echo export PYTHONPATH=\$PYTHONPATH:$TRICIRCLE_DIR >> $RC_DIR/.localrc.auto
 | 
			
		||||
 | 
			
		||||
        recreate_database tricircle
 | 
			
		||||
        python "$TRICIRCLE_DIR/cmd/manage.py" "$TRICIRCLE_CASCADE_CONF"
 | 
			
		||||
 | 
			
		||||
    elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
 | 
			
		||||
        echo_summary "Initializing Cascading Service"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										63
									
								
								tricircle/context.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								tricircle/context.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
# Copyright 2015 Huawei Technologies Co., Ltd.
 | 
			
		||||
# All Rights Reserved
 | 
			
		||||
#
 | 
			
		||||
#    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 oslo_context import context as oslo_ctx
 | 
			
		||||
 | 
			
		||||
from tricircle.db import core
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ContextBase(oslo_ctx.RequestContext):
 | 
			
		||||
    def __init__(self, auth_token=None, user_id=None, tenant_id=None,
 | 
			
		||||
                 is_admin=False, request_id=None, overwrite=True,
 | 
			
		||||
                 user_name=None, tenant_name=None, **kwargs):
 | 
			
		||||
        super(ContextBase, self).__init__(
 | 
			
		||||
            auth_token=auth_token,
 | 
			
		||||
            user=user_id or kwargs.get('user', None),
 | 
			
		||||
            tenant=tenant_id or kwargs.get('tenant', None),
 | 
			
		||||
            domain=kwargs.get('domain', None),
 | 
			
		||||
            user_domain=kwargs.get('user_domain', None),
 | 
			
		||||
            project_domain=kwargs.get('project_domain', None),
 | 
			
		||||
            is_admin=is_admin,
 | 
			
		||||
            read_only=kwargs.get('read_only', False),
 | 
			
		||||
            show_deleted=kwargs.get('show_deleted', False),
 | 
			
		||||
            request_id=request_id,
 | 
			
		||||
            resource_uuid=kwargs.get('resource_uuid', None),
 | 
			
		||||
            overwrite=overwrite)
 | 
			
		||||
        self.user_name = user_name
 | 
			
		||||
        self.tenant_name = tenant_name
 | 
			
		||||
 | 
			
		||||
    def to_dict(self):
 | 
			
		||||
        ctx_dict = super(ContextBase, self).to_dict()
 | 
			
		||||
        ctx_dict.update({
 | 
			
		||||
            'user_name': self.user_name,
 | 
			
		||||
            'tenant_name': self.tenant_name
 | 
			
		||||
        })
 | 
			
		||||
        return ctx_dict
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def from_dict(cls, ctx):
 | 
			
		||||
        return cls(**ctx)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Context(ContextBase):
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
        super(Context, self).__init__(**kwargs)
 | 
			
		||||
        self._session = None
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def session(self):
 | 
			
		||||
        if not self._session:
 | 
			
		||||
            self._session = core.get_session()
 | 
			
		||||
        return self._session
 | 
			
		||||
							
								
								
									
										0
									
								
								tricircle/db/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tricircle/db/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										139
									
								
								tricircle/db/core.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								tricircle/db/core.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,139 @@
 | 
			
		||||
# Copyright 2015 Huawei Technologies Co., Ltd.
 | 
			
		||||
# All Rights Reserved
 | 
			
		||||
#
 | 
			
		||||
#    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 oslo_config import cfg
 | 
			
		||||
import oslo_db.options as db_options
 | 
			
		||||
from oslo_db.sqlalchemy import session as db_session
 | 
			
		||||
from oslo_utils import strutils
 | 
			
		||||
import sqlalchemy as sql
 | 
			
		||||
from sqlalchemy.ext import declarative
 | 
			
		||||
from sqlalchemy.inspection import inspect
 | 
			
		||||
 | 
			
		||||
import tricircle.db.exception as db_exception
 | 
			
		||||
 | 
			
		||||
_engine_facade = None
 | 
			
		||||
ModelBase = declarative.declarative_base()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _filter_query(model, query, filters):
 | 
			
		||||
    """Apply filter to query
 | 
			
		||||
    :param model:
 | 
			
		||||
    :param query:
 | 
			
		||||
    :param filters: list of filter dict with key 'key', 'comparator', 'value'
 | 
			
		||||
    like {'key': 'site_id', 'comparator': 'eq', 'value': 'test_site_uuid'}
 | 
			
		||||
    :return:
 | 
			
		||||
    """
 | 
			
		||||
    filter_dict = {}
 | 
			
		||||
    for query_filter in filters:
 | 
			
		||||
        # only eq filter supported at first
 | 
			
		||||
        if query_filter['comparator'] != 'eq':
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        key = query_filter['key']
 | 
			
		||||
        if key not in model.attributes:
 | 
			
		||||
            continue
 | 
			
		||||
        if isinstance(inspect(model).columns[key].type, sql.Boolean):
 | 
			
		||||
            filter_dict[key] = strutils.bool_from_string(query_filter['value'])
 | 
			
		||||
        else:
 | 
			
		||||
            filter_dict[key] = query_filter['value']
 | 
			
		||||
    if filter_dict:
 | 
			
		||||
        return query.filter_by(**filter_dict)
 | 
			
		||||
    else:
 | 
			
		||||
        return query
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _get_engine_facade():
 | 
			
		||||
    global _engine_facade
 | 
			
		||||
 | 
			
		||||
    if not _engine_facade:
 | 
			
		||||
        _engine_facade = db_session.EngineFacade.from_config(cfg.CONF)
 | 
			
		||||
 | 
			
		||||
    return _engine_facade
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _get_resource(context, model, pk_value):
 | 
			
		||||
    res_obj = context.session.query(model).get(pk_value)
 | 
			
		||||
    if not res_obj:
 | 
			
		||||
        raise db_exception.ResourceNotFound(model, pk_value)
 | 
			
		||||
    return res_obj
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_resource(context, model, res_dict):
 | 
			
		||||
    res_obj = model.from_dict(res_dict)
 | 
			
		||||
    context.session.add(res_obj)
 | 
			
		||||
    return res_obj.to_dict()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def delete_resource(context, model, pk_value):
 | 
			
		||||
    res_obj = _get_resource(context, model, pk_value)
 | 
			
		||||
    context.session.delete(res_obj)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_engine():
 | 
			
		||||
    return _get_engine_facade().get_engine()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_resource(context, model, pk_value):
 | 
			
		||||
    return _get_resource(context, model, pk_value).to_dict()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_session(expire_on_commit=False):
 | 
			
		||||
    return _get_engine_facade().get_session(expire_on_commit=expire_on_commit)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def initialize():
 | 
			
		||||
    db_options.set_defaults(
 | 
			
		||||
        cfg.CONF,
 | 
			
		||||
        connection='sqlite:///:memory:')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def query_resource(context, model, filters):
 | 
			
		||||
    query = context.session.query(model)
 | 
			
		||||
    objs = _filter_query(model, query, filters)
 | 
			
		||||
    return [obj.to_dict() for obj in objs]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def update_resource(context, model, pk_value, update_dict):
 | 
			
		||||
    res_obj = _get_resource(context, model, pk_value)
 | 
			
		||||
    for key in update_dict:
 | 
			
		||||
        if key not in model.attributes:
 | 
			
		||||
            continue
 | 
			
		||||
        skip = False
 | 
			
		||||
        for pkey in inspect(model).primary_key:
 | 
			
		||||
            if pkey.name == key:
 | 
			
		||||
                skip = True
 | 
			
		||||
                break
 | 
			
		||||
        if skip:
 | 
			
		||||
            continue
 | 
			
		||||
        setattr(res_obj, key, update_dict[key])
 | 
			
		||||
    return res_obj.to_dict()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DictBase(object):
 | 
			
		||||
    attributes = []
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def from_dict(cls, d):
 | 
			
		||||
        return cls(**d)
 | 
			
		||||
 | 
			
		||||
    def to_dict(self):
 | 
			
		||||
        d = {}
 | 
			
		||||
        for attr in self.__class__.attributes:
 | 
			
		||||
            d[attr] = getattr(self, attr)
 | 
			
		||||
        return d
 | 
			
		||||
 | 
			
		||||
    def __getitem__(self, key):
 | 
			
		||||
        return getattr(self, key)
 | 
			
		||||
							
								
								
									
										24
									
								
								tricircle/db/exception.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								tricircle/db/exception.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
# Copyright 2015 Huawei Technologies Co., Ltd.
 | 
			
		||||
# All Rights Reserved
 | 
			
		||||
#
 | 
			
		||||
#    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.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ResourceNotFound(Exception):
 | 
			
		||||
    def __init__(self, model, pk_value):
 | 
			
		||||
        res_type = model.__name__.lower()
 | 
			
		||||
        message = "Could not find %(res_type)s: %(pk_value)s" % {
 | 
			
		||||
            'res_type': res_type,
 | 
			
		||||
            'pk_value': pk_value
 | 
			
		||||
        }
 | 
			
		||||
        super(ResourceNotFound, self).__init__(message)
 | 
			
		||||
							
								
								
									
										17
									
								
								tricircle/db/migrate_repo/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tricircle/db/migrate_repo/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
# Copyright 2015 Huawei Technologies Co., Ltd.
 | 
			
		||||
# All Rights Reserved
 | 
			
		||||
#
 | 
			
		||||
#    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.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DB_INIT_VERSION = 0
 | 
			
		||||
							
								
								
									
										26
									
								
								tricircle/db/migrate_repo/migrate.cfg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								tricircle/db/migrate_repo/migrate.cfg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
[db_settings]
 | 
			
		||||
# Used to identify which repository this database is versioned under.
 | 
			
		||||
# You can use the name of your project.
 | 
			
		||||
repository_id=tricircle
 | 
			
		||||
 | 
			
		||||
# 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
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										73
									
								
								tricircle/db/migrate_repo/versions/001_init.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								tricircle/db/migrate_repo/versions/001_init.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,73 @@
 | 
			
		||||
# Copyright 2015 Huawei Technologies Co., Ltd.
 | 
			
		||||
# All Rights Reserved
 | 
			
		||||
#
 | 
			
		||||
#    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 migrate
 | 
			
		||||
import sqlalchemy as sql
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def upgrade(migrate_engine):
 | 
			
		||||
    meta = sql.MetaData()
 | 
			
		||||
    meta.bind = migrate_engine
 | 
			
		||||
 | 
			
		||||
    cascaded_sites = sql.Table(
 | 
			
		||||
        'cascaded_sites', meta,
 | 
			
		||||
        sql.Column('site_id', sql.String(length=64), primary_key=True),
 | 
			
		||||
        sql.Column('site_name', sql.String(length=64), unique=True,
 | 
			
		||||
                   nullable=False),
 | 
			
		||||
        sql.Column('az_id', sql.String(length=64), nullable=False),
 | 
			
		||||
        mysql_engine='InnoDB',
 | 
			
		||||
        mysql_charset='utf8')
 | 
			
		||||
    cascaded_site_service_configuration = sql.Table(
 | 
			
		||||
        'cascaded_site_service_configuration', meta,
 | 
			
		||||
        sql.Column('service_id', sql.String(length=64), primary_key=True),
 | 
			
		||||
        sql.Column('site_id', sql.String(length=64), nullable=False),
 | 
			
		||||
        sql.Column('service_name', sql.String(length=64), unique=True,
 | 
			
		||||
                   nullable=False),
 | 
			
		||||
        sql.Column('service_type', sql.String(length=64), nullable=False),
 | 
			
		||||
        sql.Column('service_url', sql.String(length=512), nullable=False),
 | 
			
		||||
        mysql_engine='InnoDB',
 | 
			
		||||
        mysql_charset='utf8')
 | 
			
		||||
    cascaded_service_types = sql.Table(
 | 
			
		||||
        'cascaded_service_types', meta,
 | 
			
		||||
        sql.Column('id', sql.Integer, primary_key=True),
 | 
			
		||||
        sql.Column('service_type', sql.String(length=64), unique=True),
 | 
			
		||||
        mysql_engine='InnoDB',
 | 
			
		||||
        mysql_charset='utf8')
 | 
			
		||||
    cascaded_site_services = sql.Table(
 | 
			
		||||
        'cascaded_site_services', meta,
 | 
			
		||||
        sql.Column('site_id', sql.String(length=64), primary_key=True),
 | 
			
		||||
        mysql_engine='InnoDB',
 | 
			
		||||
        mysql_charset='utf8')
 | 
			
		||||
 | 
			
		||||
    tables = [cascaded_sites, cascaded_site_service_configuration,
 | 
			
		||||
              cascaded_service_types, cascaded_site_services]
 | 
			
		||||
    for table in tables:
 | 
			
		||||
        table.create()
 | 
			
		||||
 | 
			
		||||
    fkeys = [
 | 
			
		||||
        {'columns': [cascaded_site_service_configuration.c.site_id],
 | 
			
		||||
         'references': [cascaded_sites.c.site_id]},
 | 
			
		||||
        {'columns': [cascaded_site_service_configuration.c.service_type],
 | 
			
		||||
         'references': [cascaded_service_types.c.service_type]}
 | 
			
		||||
    ]
 | 
			
		||||
    for fkey in fkeys:
 | 
			
		||||
        migrate.ForeignKeyConstraint(columns=fkey['columns'],
 | 
			
		||||
                                     refcolumns=fkey['references'],
 | 
			
		||||
                                     name=fkey.get('name')).create()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def downgrade(migrate_engine):
 | 
			
		||||
    raise NotImplementedError('can not downgrade from init repo.')
 | 
			
		||||
							
								
								
									
										0
									
								
								tricircle/db/migrate_repo/versions/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tricircle/db/migrate_repo/versions/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										38
									
								
								tricircle/db/migration_helpers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								tricircle/db/migration_helpers.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
# Copyright 2015 Huawei Technologies Co., Ltd.
 | 
			
		||||
# All Rights Reserved
 | 
			
		||||
#
 | 
			
		||||
#    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
 | 
			
		||||
 | 
			
		||||
from tricircle import db
 | 
			
		||||
from tricircle.db import core
 | 
			
		||||
from tricircle.db import migrate_repo
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def find_migrate_repo(package=None, repo_name='migrate_repo'):
 | 
			
		||||
    package = package or db
 | 
			
		||||
    path = os.path.abspath(os.path.join(
 | 
			
		||||
        os.path.dirname(package.__file__), repo_name))
 | 
			
		||||
    # TODO(zhiyuan) handle path not valid exception
 | 
			
		||||
    return path
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def sync_repo(version):
 | 
			
		||||
    repo_abs_path = find_migrate_repo()
 | 
			
		||||
    init_version = migrate_repo.DB_INIT_VERSION
 | 
			
		||||
    engine = core.get_engine()
 | 
			
		||||
    migration.db_sync(engine, repo_abs_path, version, init_version)
 | 
			
		||||
							
								
								
									
										97
									
								
								tricircle/db/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								tricircle/db/models.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,97 @@
 | 
			
		||||
# Copyright 2015 Huawei Technologies Co., Ltd.
 | 
			
		||||
# All Rights Reserved
 | 
			
		||||
#
 | 
			
		||||
#    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 as sql
 | 
			
		||||
 | 
			
		||||
from tricircle.db import core
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_site(context, site_dict):
 | 
			
		||||
    with context.session.begin():
 | 
			
		||||
        return core.create_resource(context, Site, site_dict)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def delete_site(context, site_id):
 | 
			
		||||
    with context.session.begin():
 | 
			
		||||
        return core.delete_resource(context, Site, site_id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_site(context, site_id):
 | 
			
		||||
    with context.session.begin():
 | 
			
		||||
        return core.get_resource(context, Site, site_id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def list_sites(context, filters):
 | 
			
		||||
    with context.session.begin():
 | 
			
		||||
        return core.query_resource(context, Site, filters)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def update_site(context, site_id, update_dict):
 | 
			
		||||
    with context.session.begin():
 | 
			
		||||
        return core.update_resource(context, Site, site_id, update_dict)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_service_type(context, type_dict):
 | 
			
		||||
    with context.session.begin():
 | 
			
		||||
        return core.create_resource(context, ServiceType, type_dict)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_site_service_configuration(context, config_dict):
 | 
			
		||||
    with context.session.begin():
 | 
			
		||||
        return core.create_resource(context, SiteServiceConfiguration,
 | 
			
		||||
                                    config_dict)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Site(core.ModelBase, core.DictBase):
 | 
			
		||||
    __tablename__ = 'cascaded_sites'
 | 
			
		||||
    attributes = ['site_id', 'site_name', 'az_id']
 | 
			
		||||
    site_id = sql.Column('site_id', sql.String(length=64), primary_key=True)
 | 
			
		||||
    site_name = sql.Column('site_name', sql.String(length=64), unique=True,
 | 
			
		||||
                           nullable=False)
 | 
			
		||||
    az_id = sql.Column('az_id', sql.String(length=64), nullable=False)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SiteServiceConfiguration(core.ModelBase, core.DictBase):
 | 
			
		||||
    __tablename__ = 'cascaded_site_service_configuration'
 | 
			
		||||
    attributes = ['service_id', 'site_id', 'service_name',
 | 
			
		||||
                  'service_type', 'service_url']
 | 
			
		||||
    service_id = sql.Column('service_id', sql.String(length=64),
 | 
			
		||||
                            primary_key=True)
 | 
			
		||||
    site_id = sql.Column('site_id', sql.String(length=64),
 | 
			
		||||
                         sql.ForeignKey('cascaded_sites.site_id'),
 | 
			
		||||
                         nullable=False)
 | 
			
		||||
    service_name = sql.Column('service_name', sql.String(length=64),
 | 
			
		||||
                              unique=True, nullable=False)
 | 
			
		||||
    service_type = sql.Column(
 | 
			
		||||
        'service_type', sql.String(length=64),
 | 
			
		||||
        sql.ForeignKey('cascaded_service_types.service_type'),
 | 
			
		||||
        nullable=False)
 | 
			
		||||
    service_url = sql.Column('service_url', sql.String(length=512),
 | 
			
		||||
                             nullable=False)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ServiceType(core.ModelBase, core.DictBase):
 | 
			
		||||
    __tablename__ = 'cascaded_service_types'
 | 
			
		||||
    attributes = ['id', 'service_type']
 | 
			
		||||
    id = sql.Column('id', sql.Integer, primary_key=True)
 | 
			
		||||
    service_type = sql.Column('service_type', sql.String(length=64),
 | 
			
		||||
                              unique=True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SiteService(core.ModelBase, core.DictBase):
 | 
			
		||||
    __tablename__ = 'cascaded_site_services'
 | 
			
		||||
    attributes = ['site_id']
 | 
			
		||||
    site_id = sql.Column('site_id', sql.String(length=64), primary_key=True)
 | 
			
		||||
							
								
								
									
										0
									
								
								tricircle/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tricircle/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								tricircle/tests/unit/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tricircle/tests/unit/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								tricircle/tests/unit/db/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tricircle/tests/unit/db/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										107
									
								
								tricircle/tests/unit/db/test_models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								tricircle/tests/unit/db/test_models.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,107 @@
 | 
			
		||||
# Copyright 2015 Huawei Technologies Co., Ltd.
 | 
			
		||||
# All Rights Reserved
 | 
			
		||||
#
 | 
			
		||||
#    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 unittest
 | 
			
		||||
 | 
			
		||||
from tricircle import context
 | 
			
		||||
from tricircle.db import core
 | 
			
		||||
from tricircle.db import exception
 | 
			
		||||
from tricircle.db import models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ModelsTest(unittest.TestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        core.initialize()
 | 
			
		||||
        core.ModelBase.metadata.create_all(core.get_engine())
 | 
			
		||||
        self.context = context.Context()
 | 
			
		||||
 | 
			
		||||
    def test_obj_to_dict(self):
 | 
			
		||||
        site = {'site_id': 'test_site_uuid',
 | 
			
		||||
                'site_name': 'test_site',
 | 
			
		||||
                'az_id': 'test_az_uuid'}
 | 
			
		||||
        site_obj = models.Site.from_dict(site)
 | 
			
		||||
        for attr in site_obj.attributes:
 | 
			
		||||
            self.assertEqual(getattr(site_obj, attr), site[attr])
 | 
			
		||||
 | 
			
		||||
    def test_create(self):
 | 
			
		||||
        site = {'site_id': 'test_site_uuid',
 | 
			
		||||
                'site_name': 'test_site',
 | 
			
		||||
                'az_id': 'test_az_uuid'}
 | 
			
		||||
        site_ret = models.create_site(self.context, site)
 | 
			
		||||
        self.assertEqual(site_ret, site)
 | 
			
		||||
 | 
			
		||||
        service_type = {'id': 1,
 | 
			
		||||
                        'service_type': 'nova'}
 | 
			
		||||
        type_ret = models.create_service_type(self.context, service_type)
 | 
			
		||||
        self.assertEqual(type_ret, service_type)
 | 
			
		||||
 | 
			
		||||
        configuration = {
 | 
			
		||||
            'service_id': 'test_config_uuid',
 | 
			
		||||
            'site_id': 'test_site_uuid',
 | 
			
		||||
            'service_name': 'nova_service',
 | 
			
		||||
            'service_type': 'nova',
 | 
			
		||||
            'service_url': 'http://test_url'
 | 
			
		||||
        }
 | 
			
		||||
        config_ret = models.create_site_service_configuration(self.context,
 | 
			
		||||
                                                              configuration)
 | 
			
		||||
        self.assertEqual(config_ret, configuration)
 | 
			
		||||
 | 
			
		||||
    def test_update(self):
 | 
			
		||||
        site = {'site_id': 'test_site_uuid',
 | 
			
		||||
                'site_name': 'test_site',
 | 
			
		||||
                'az_id': 'test_az1_uuid'}
 | 
			
		||||
        models.create_site(self.context, site)
 | 
			
		||||
        update_dict = {'site_id': 'fake_uuid',
 | 
			
		||||
                       'site_name': 'test_site2',
 | 
			
		||||
                       'az_id': 'test_az2_uuid'}
 | 
			
		||||
        ret = models.update_site(self.context, 'test_site_uuid', update_dict)
 | 
			
		||||
        # primary key value will not be updated
 | 
			
		||||
        self.assertEqual(ret['site_id'], 'test_site_uuid')
 | 
			
		||||
        self.assertEqual(ret['site_name'], 'test_site2')
 | 
			
		||||
        self.assertEqual(ret['az_id'], 'test_az2_uuid')
 | 
			
		||||
 | 
			
		||||
    def test_delete(self):
 | 
			
		||||
        site = {'site_id': 'test_site_uuid',
 | 
			
		||||
                'site_name': 'test_site',
 | 
			
		||||
                'az_id': 'test_az_uuid'}
 | 
			
		||||
        models.create_site(self.context, site)
 | 
			
		||||
        models.delete_site(self.context, 'test_site_uuid')
 | 
			
		||||
        self.assertRaises(exception.ResourceNotFound, models.get_site,
 | 
			
		||||
                          self.context, 'test_site_uuid')
 | 
			
		||||
 | 
			
		||||
    def test_query(self):
 | 
			
		||||
        site1 = {'site_id': 'test_site1_uuid',
 | 
			
		||||
                 'site_name': 'test_site1',
 | 
			
		||||
                 'az_id': 'test_az1_uuid'}
 | 
			
		||||
        site2 = {'site_id': 'test_site2_uuid',
 | 
			
		||||
                 'site_name': 'test_site2',
 | 
			
		||||
                 'az_id': 'test_az2_uuid'}
 | 
			
		||||
        models.create_site(self.context, site1)
 | 
			
		||||
        models.create_site(self.context, site2)
 | 
			
		||||
        filters = [{'key': 'site_name',
 | 
			
		||||
                    'comparator': 'eq',
 | 
			
		||||
                    'value': 'test_site2'}]
 | 
			
		||||
        sites = models.list_sites(self.context, filters)
 | 
			
		||||
        self.assertEqual(len(sites), 1)
 | 
			
		||||
        self.assertEqual(sites[0], site2)
 | 
			
		||||
        filters = [{'key': 'site_name',
 | 
			
		||||
                    'comparator': 'eq',
 | 
			
		||||
                    'value': 'test_site3'}]
 | 
			
		||||
        sites = models.list_sites(self.context, filters)
 | 
			
		||||
        self.assertEqual(len(sites), 0)
 | 
			
		||||
 | 
			
		||||
    def tearDown(self):
 | 
			
		||||
        core.ModelBase.metadata.drop_all(core.get_engine())
 | 
			
		||||
		Reference in New Issue
	
	Block a user