Add base files for sqlalchemy implementation
These code is imported from Solum. Implements: blueprint db-object-definitions Change-Id: Idc5458bd6b758f3d4a62bf5f059ce4dd444a825a
This commit is contained in:
parent
5ab29b0557
commit
e8f4831406
@ -0,0 +1,63 @@
|
||||
# Copyright 2013 - Red Hat, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Common functionality for the application object model
|
||||
|
||||
The object model must be initialized at service start via
|
||||
|
||||
magnum.objects.load()
|
||||
|
||||
and all objects should be retrieved via
|
||||
|
||||
magnum.objects.registry.<class>
|
||||
|
||||
in application code.
|
||||
"""
|
||||
|
||||
from oslo.config import cfg
|
||||
from oslo.db import api
|
||||
|
||||
from magnum.objects import registry as registry_mod
|
||||
|
||||
db_opts = [
|
||||
cfg.StrOpt('schema_mode',
|
||||
default='new',
|
||||
help="The version of the schema that should be "
|
||||
"running: 'old', 'transition', 'new'")
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(db_opts, "database")
|
||||
|
||||
_BACKEND_MAPPING = {'sqlalchemy': 'magnum.objects.sqlalchemy'}
|
||||
IMPL = api.DBAPI.from_config(CONF, backend_mapping=_BACKEND_MAPPING)
|
||||
|
||||
|
||||
def transition_schema():
|
||||
"""Is the new schema in write-only mode."""
|
||||
return cfg.CONF.database.schema_mode == 'transition'
|
||||
|
||||
|
||||
def new_schema():
|
||||
"""Should objects be writing to the new schema."""
|
||||
return cfg.CONF.database.schema_mode != 'old'
|
||||
|
||||
|
||||
def load():
|
||||
"""Ensure that the object model is initialized."""
|
||||
global registry
|
||||
registry.clear()
|
||||
IMPL.load()
|
||||
|
||||
registry = registry_mod.Registry()
|
51
magnum/objects/sqlalchemy/__init__.py
Normal file
51
magnum/objects/sqlalchemy/__init__.py
Normal file
@ -0,0 +1,51 @@
|
||||
# Copyright 2013 - Red Hat, Inc.
|
||||
#
|
||||
# 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 oslo.db.sqlalchemy import session
|
||||
|
||||
|
||||
_FACADE = None
|
||||
|
||||
|
||||
def get_facade():
|
||||
global _FACADE
|
||||
|
||||
if not _FACADE:
|
||||
_FACADE = session.EngineFacade.from_config(cfg.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 cleanup():
|
||||
global _FACADE
|
||||
|
||||
if _FACADE:
|
||||
_FACADE._session_maker.close_all()
|
||||
_FACADE.get_engine().dispose()
|
||||
_FACADE = None
|
||||
|
||||
|
||||
def load():
|
||||
"""Activate the sqlalchemy backend."""
|
||||
pass
|
183
magnum/objects/sqlalchemy/models.py
Normal file
183
magnum/objects/sqlalchemy/models.py
Normal file
@ -0,0 +1,183 @@
|
||||
# Copyright 2013 - Red Hat, Inc.
|
||||
#
|
||||
# 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 application data.
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from oslo.config import cfg
|
||||
from oslo.db import exception as db_exc
|
||||
from oslo.db.sqlalchemy import models
|
||||
import six
|
||||
from six import moves
|
||||
from sqlalchemy.ext import declarative
|
||||
from sqlalchemy.orm import exc
|
||||
from sqlalchemy import types
|
||||
|
||||
from magnum.common import exception
|
||||
from magnum import objects
|
||||
from magnum.objects import sqlalchemy as object_sqla
|
||||
|
||||
|
||||
def table_args():
|
||||
cfg.CONF.import_opt('connection', 'oslo.db.options',
|
||||
group='database')
|
||||
if cfg.CONF.database.connection is None:
|
||||
# this is only within some object tests where
|
||||
# the object classes are directly imported.
|
||||
return None
|
||||
engine_name = moves.urllib.parse.urlparse(
|
||||
cfg.CONF.database.connection).scheme
|
||||
if engine_name == 'mysql':
|
||||
return {'mysql_engine': 'InnoDB',
|
||||
'mysql_charset': "utf8"}
|
||||
return None
|
||||
|
||||
|
||||
def model_query(context, model, *args, **kwargs):
|
||||
"""Query helper.
|
||||
|
||||
:param context: context to query under
|
||||
:param session: if present, the session to use
|
||||
"""
|
||||
|
||||
session = kwargs.get('session') or object_sqla.get_session()
|
||||
|
||||
query = session.query(model, *args)
|
||||
return query
|
||||
|
||||
|
||||
class MagnumBase(models.TimestampMixin, models.ModelBase):
|
||||
|
||||
metadata = None
|
||||
|
||||
@classmethod
|
||||
def obj_name(cls):
|
||||
return cls.__name__
|
||||
|
||||
def as_dict(self):
|
||||
d = {}
|
||||
for c in self.__table__.columns:
|
||||
d[c.name] = self[c.name]
|
||||
for k in self._extra_keys:
|
||||
d[k] = self[k]
|
||||
return d
|
||||
|
||||
@classmethod
|
||||
def get_session(cls):
|
||||
return object_sqla.get_session()
|
||||
|
||||
@classmethod
|
||||
def get_by_id(cls, context, item_id):
|
||||
try:
|
||||
session = MagnumBase.get_session()
|
||||
return session.query(cls).filter_by(id=item_id).one()
|
||||
except exc.NoResultFound:
|
||||
cls._raise_not_found(item_id)
|
||||
|
||||
@classmethod
|
||||
def get_by_uuid(cls, context, item_uuid):
|
||||
try:
|
||||
session = MagnumBase.get_session()
|
||||
return session.query(cls).filter_by(uuid=item_uuid).one()
|
||||
except exc.NoResultFound:
|
||||
cls._raise_not_found(item_uuid)
|
||||
|
||||
@classmethod
|
||||
def _raise_duplicate_object(cls):
|
||||
if hasattr(cls, '__resource__'):
|
||||
raise exception.ResourceExists(name=cls.__resource__)
|
||||
else:
|
||||
raise exception.ObjectNotUnique(name=cls.__tablename__)
|
||||
|
||||
def _non_updatable_fields(self):
|
||||
return set(('uuid', 'id'))
|
||||
|
||||
def _lazyhasattr(self, name):
|
||||
return any(name in d for d in (self.__dict__,
|
||||
self.__class__.__dict__))
|
||||
|
||||
def update(self, data):
|
||||
for field in set(six.iterkeys(data)) - self._non_updatable_fields():
|
||||
if self._lazyhasattr(field):
|
||||
setattr(self, field, data[field])
|
||||
|
||||
def save(self, context):
|
||||
if objects.transition_schema():
|
||||
self.add_forward_schema_changes()
|
||||
|
||||
session = MagnumBase.get_session()
|
||||
with session.begin():
|
||||
session.merge(self)
|
||||
|
||||
def create(self, context):
|
||||
session = MagnumBase.get_session()
|
||||
try:
|
||||
with session.begin():
|
||||
session.add(self)
|
||||
except (db_exc.DBDuplicateEntry):
|
||||
self.__class__._raise_duplicate_object()
|
||||
|
||||
def destroy(self, context):
|
||||
session = MagnumBase.get_session()
|
||||
with session.begin():
|
||||
session.query(self.__class__).filter_by(
|
||||
id=self.id).delete()
|
||||
|
||||
@classmethod
|
||||
def _raise_not_found(cls, item_id):
|
||||
"""Raise a not found exception."""
|
||||
if hasattr(cls, '__resource__'):
|
||||
raise exception.ResourceNotFound(name=cls.__resource__, id=item_id)
|
||||
else:
|
||||
raise exception.ObjectNotFound(name=cls.__tablename__, id=item_id)
|
||||
|
||||
|
||||
Base = declarative.declarative_base(cls=MagnumBase)
|
||||
|
||||
|
||||
class JsonEncodedType(types.TypeDecorator):
|
||||
"""Abstract base type serialized as json-encoded string in db."""
|
||||
type = None
|
||||
impl = types.TEXT
|
||||
|
||||
def process_bind_param(self, value, dialect):
|
||||
if value is None:
|
||||
# Save default value according to current type to keep the
|
||||
# interface the consistent.
|
||||
value = self.type()
|
||||
elif not isinstance(value, self.type):
|
||||
raise TypeError("%s supposes to store %s objects, but %s given"
|
||||
% (self.__class__.__name__,
|
||||
self.type.__name__,
|
||||
type(value).__name__))
|
||||
serialized_value = json.dumps(value)
|
||||
return serialized_value
|
||||
|
||||
def process_result_value(self, value, dialect):
|
||||
if value is not None:
|
||||
value = json.loads(value)
|
||||
return value
|
||||
|
||||
|
||||
class JSONEncodedDict(JsonEncodedType):
|
||||
"""Represents dict serialized as json-encoded string in db."""
|
||||
type = dict
|
||||
|
||||
|
||||
class JSONEncodedList(JsonEncodedType):
|
||||
"""Represents list serialized as json-encoded string in db."""
|
||||
type = list
|
@ -6,6 +6,7 @@ pbr>=0.6,!=0.7,<1.0
|
||||
Babel>=1.3
|
||||
|
||||
oslo.config>=1.4.0
|
||||
oslo.db>=0.2.0 # Apache-2.0
|
||||
oslo.messaging>=1.4.0
|
||||
oslo.serialization>=1.0.0
|
||||
oslo.utils>=1.0.0
|
||||
@ -13,5 +14,6 @@ pecan>=0.8.0
|
||||
python-keystoneclient>=0.11.1
|
||||
python-zaqarclient>=0.0.3
|
||||
six>=1.7.0
|
||||
SQLAlchemy>=0.8.4,!=0.9.5,<=0.9.99
|
||||
WSME>=0.6
|
||||
docker-py>=0.5.1
|
||||
|
Loading…
Reference in New Issue
Block a user