Getting hooked up w/ the db.
* connection db and all that jazz * migrations are working * local DB models are working (only save)
This commit is contained in:
parent
5c641643d4
commit
0fe9b2323c
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
vagrant/
|
vagrant/
|
||||||
*.pyc
|
*.pyc
|
||||||
.idea
|
.idea
|
||||||
|
reddwarf_test.sqlite
|
||||||
|
@ -36,7 +36,7 @@ if os.path.exists(os.path.join(possible_topdir, 'reddwarf', '__init__.py')):
|
|||||||
from reddwarf import version
|
from reddwarf import version
|
||||||
from reddwarf.common import config
|
from reddwarf.common import config
|
||||||
from reddwarf.common import wsgi
|
from reddwarf.common import wsgi
|
||||||
|
from reddwarf.db import db_api
|
||||||
|
|
||||||
def create_options(parser):
|
def create_options(parser):
|
||||||
"""Sets up the CLI and config-file options
|
"""Sets up the CLI and config-file options
|
||||||
@ -60,6 +60,7 @@ if __name__ == '__main__':
|
|||||||
(options, args) = config.parse_options(oparser)
|
(options, args) = config.parse_options(oparser)
|
||||||
try:
|
try:
|
||||||
conf, app = config.Config.load_paste_app('reddwarf', options, args)
|
conf, app = config.Config.load_paste_app('reddwarf', options, args)
|
||||||
|
db_api.configure_db(conf)
|
||||||
server = wsgi.Server()
|
server = wsgi.Server()
|
||||||
server.start(app, options.get('port', conf['bind_port']),
|
server.start(app, options.get('port', conf['bind_port']),
|
||||||
conf['bind_host'])
|
conf['bind_host'])
|
||||||
|
@ -33,8 +33,7 @@ curl -d '{"auth":{"passwordCredentials":{"username": "reddwarf", "password": "RE
|
|||||||
# now get a list of instances, which connects over python-novaclient to nova
|
# now get a list of instances, which connects over python-novaclient to nova
|
||||||
# NOTE THIS AUTH TOKEN NEEDS TO BE CHANGED
|
# NOTE THIS AUTH TOKEN NEEDS TO BE CHANGED
|
||||||
# Also note that keystone uses the tenant id now and _not_ the name
|
# Also note that keystone uses the tenant id now and _not_ the name
|
||||||
#curl -H"content-type:application/xml" -H"X-Auth-Project-Id:$REDDWARF_TENANT" -H"X-Auth-User:reddwarf" \
|
# curl -H'X-Auth-Token:AUTH_TOKEN' http://0.0.0.0:8779/v0.1/$REDDWARF_TENANT/instances
|
||||||
# -H'X-Auth-Key:2a2c89c6a7284d32bcb94b4e56f0411c' http://0.0.0.0:8779/v2/$REDDWARF_TENANT/instances
|
|
||||||
|
|
||||||
# Also, you should start up the api node like this
|
# Also, you should start up the api node like this
|
||||||
# bin/reddwarf-api-os-database --flagfile=etc/nova/nova.conf.template
|
# bin/reddwarf-api-os-database --flagfile=etc/nova/nova.conf.template
|
||||||
|
@ -33,9 +33,9 @@ class ReddwarfError(openstack_exception.OpenstackException):
|
|||||||
super(ReddwarfError, self).__init__(**kwargs)
|
super(ReddwarfError, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
class ThisIsATestError(ReddwarfError):
|
class DBConstraintError(ReddwarfError):
|
||||||
|
|
||||||
message = _("Data Missing")
|
message = _("Failed to save %(model_name)s because: %(error)s")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,12 +16,33 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
"""I totally stole most of this from melange, thx guys!!!"""
|
"""I totally stole most of this from melange, thx guys!!!"""
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import inspect
|
||||||
|
import re
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from reddwarf.openstack.common import utils as openstack_utils
|
||||||
|
|
||||||
|
|
||||||
|
import_class = openstack_utils.import_class
|
||||||
|
import_object = openstack_utils.import_object
|
||||||
|
bool_from_string = openstack_utils.bool_from_string
|
||||||
|
|
||||||
|
|
||||||
def stringify_keys(dictionary):
|
def stringify_keys(dictionary):
|
||||||
if dictionary is None:
|
if dictionary is None:
|
||||||
return None
|
return None
|
||||||
return dict((str(key), value) for key, value in dictionary.iteritems())
|
return dict((str(key), value) for key, value in dictionary.iteritems())
|
||||||
|
|
||||||
|
|
||||||
|
def generate_uuid():
|
||||||
|
return str(uuid.uuid4())
|
||||||
|
|
||||||
|
|
||||||
|
def utcnow():
|
||||||
|
return datetime.datetime.utcnow()
|
||||||
|
|
||||||
|
|
||||||
class cached_property(object):
|
class cached_property(object):
|
||||||
"""A decorator that converts a function into a lazy property.
|
"""A decorator that converts a function into a lazy property.
|
||||||
|
|
||||||
@ -50,3 +71,42 @@ class cached_property(object):
|
|||||||
value = self.func(obj)
|
value = self.func(obj)
|
||||||
setattr(obj, self.__name__, value)
|
setattr(obj, self.__name__, value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
class MethodInspector(object):
|
||||||
|
|
||||||
|
def __init__(self, func):
|
||||||
|
self._func = func
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def required_args(self):
|
||||||
|
return self.args[0:self.required_args_count]
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def optional_args(self):
|
||||||
|
keys = self.args[self.required_args_count: len(self.args)]
|
||||||
|
return zip(keys, self.defaults)
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def defaults(self):
|
||||||
|
return self.argspec.defaults or ()
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def required_args_count(self):
|
||||||
|
return len(self.args) - len(self.defaults)
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def args(self):
|
||||||
|
args = self.argspec.args
|
||||||
|
if inspect.ismethod(self._func):
|
||||||
|
args.pop(0)
|
||||||
|
return args
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def argspec(self):
|
||||||
|
return inspect.getargspec(self._func)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
optionals = ["[{0}=<{0}>]".format(k) for k, v in self.optional_args]
|
||||||
|
required = ["{0}=<{0}>".format(arg) for arg in self.required_args]
|
||||||
|
args_str = ' '.join(required + optionals)
|
||||||
|
return "%s %s" % (self._func.__name__, args_str)
|
||||||
|
@ -19,7 +19,12 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import netaddr
|
import netaddr
|
||||||
|
|
||||||
|
from reddwarf import db
|
||||||
|
|
||||||
from reddwarf.common import config
|
from reddwarf.common import config
|
||||||
|
from reddwarf.common import exception
|
||||||
|
from reddwarf.common import utils
|
||||||
from novaclient.v1_1.client import Client
|
from novaclient.v1_1.client import Client
|
||||||
|
|
||||||
LOG = logging.getLogger('reddwarf.database.models')
|
LOG = logging.getLogger('reddwarf.database.models')
|
||||||
@ -41,6 +46,30 @@ class ModelBase(object):
|
|||||||
data_fields = self._data_fields + self._auto_generated_attrs
|
data_fields = self._data_fields + self._auto_generated_attrs
|
||||||
return dict([(field, self[field]) for field in data_fields])
|
return dict([(field, self[field]) for field in data_fields])
|
||||||
|
|
||||||
|
def is_valid(self):
|
||||||
|
self.errors = {}
|
||||||
|
# self._validate_columns_type()
|
||||||
|
# self._before_validate()
|
||||||
|
# self._validate()
|
||||||
|
return self.errors == {}
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
setattr(self, key, value)
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
return getattr(self, key)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if not hasattr(other, 'id'):
|
||||||
|
return False
|
||||||
|
return type(other) == type(self) and other.id == self.id
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return not self == other
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return self.id.__hash__()
|
||||||
|
|
||||||
|
|
||||||
class RemoteModelBase(ModelBase):
|
class RemoteModelBase(ModelBase):
|
||||||
|
|
||||||
@ -89,4 +118,49 @@ class Instances(Instance):
|
|||||||
|
|
||||||
|
|
||||||
class DatabaseModelBase(ModelBase):
|
class DatabaseModelBase(ModelBase):
|
||||||
_auto_generated_attrs = ["id", "created_at", "updated_at"]
|
_auto_generated_attrs = ['id']
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(cls, **values):
|
||||||
|
values['id'] = utils.generate_uuid()
|
||||||
|
print values
|
||||||
|
# values['created_at'] = utils.utcnow()
|
||||||
|
instance = cls(**values).save()
|
||||||
|
# instance._notify_fields("create")
|
||||||
|
return instance
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
if not self.is_valid():
|
||||||
|
raise InvalidModelError(self.errors)
|
||||||
|
# self._convert_columns_to_proper_type()
|
||||||
|
# self._before_save()
|
||||||
|
self['updated_at'] = utils.utcnow()
|
||||||
|
LOG.debug("Saving %s: %s" % (self.__class__.__name__, self.__dict__))
|
||||||
|
return db.db_api.save(self)
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.merge_attributes(kwargs)
|
||||||
|
|
||||||
|
def merge_attributes(self, values):
|
||||||
|
"""dict.update() behaviour."""
|
||||||
|
for k, v in values.iteritems():
|
||||||
|
self[k] = v
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class DBInstance(DatabaseModelBase):
|
||||||
|
_data_fields = ['name', 'status']
|
||||||
|
|
||||||
|
def persisted_models():
|
||||||
|
return {
|
||||||
|
'instance': DBInstance,
|
||||||
|
}
|
||||||
|
|
||||||
|
class InvalidModelError(exception.ReddwarfError):
|
||||||
|
|
||||||
|
message = _("The following values are invalid: %(errors)s")
|
||||||
|
|
||||||
|
def __init__(self, errors, message=None):
|
||||||
|
super(InvalidModelError, self).__init__(message, errors=errors)
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,6 +50,8 @@ class InstanceController(BaseController):
|
|||||||
def index(self, req, tenant_id):
|
def index(self, req, tenant_id):
|
||||||
"""Return all instances."""
|
"""Return all instances."""
|
||||||
servers = models.Instances(req.headers["X-Auth-Token"]).data()
|
servers = models.Instances(req.headers["X-Auth-Token"]).data()
|
||||||
|
# Test to check that the db code works. this will eventually be removed
|
||||||
|
models.DBInstance.create(name='foo', status='status_foo')
|
||||||
return wsgi.Result(views.InstancesView(servers).data(), 201)
|
return wsgi.Result(views.InstancesView(servers).data(), 201)
|
||||||
|
|
||||||
def show(self, req, tenant_id, id):
|
def show(self, req, tenant_id, id):
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
# Copyright 2011 OpenStack LLC.
|
# Copyright 2010-2011 OpenStack LLC.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
@ -14,3 +14,86 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import optparse
|
||||||
|
|
||||||
|
from reddwarf.common import utils
|
||||||
|
from reddwarf.common import config
|
||||||
|
|
||||||
|
|
||||||
|
db_api = utils.import_object(config.Config.get("db_api_implementation",
|
||||||
|
"reddwarf.db.sqlalchemy.api"))
|
||||||
|
|
||||||
|
|
||||||
|
class Query(object):
|
||||||
|
"""Mimics sqlalchemy query object.
|
||||||
|
|
||||||
|
This class allows us to store query conditions and use them with
|
||||||
|
bulk updates and deletes just like sqlalchemy query object.
|
||||||
|
Using this class makes the models independent of sqlalchemy
|
||||||
|
|
||||||
|
"""
|
||||||
|
def __init__(self, model, query_func, **conditions):
|
||||||
|
self._query_func = query_func
|
||||||
|
self._model = model
|
||||||
|
self._conditions = conditions
|
||||||
|
|
||||||
|
def all(self):
|
||||||
|
return db_api.list(self._query_func, self._model, **self._conditions)
|
||||||
|
|
||||||
|
def count(self):
|
||||||
|
return db_api.count(self._query_func, self._model, **self._conditions)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self.all())
|
||||||
|
|
||||||
|
def update(self, **values):
|
||||||
|
db_api.update_all(self._query_func, self._model, self._conditions,
|
||||||
|
values)
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
db_api.delete_all(self._query_func, self._model, **self._conditions)
|
||||||
|
|
||||||
|
def limit(self, limit=200, marker=None, marker_column=None):
|
||||||
|
return db_api.find_all_by_limit(self._query_func,
|
||||||
|
self._model,
|
||||||
|
self._conditions,
|
||||||
|
limit=limit,
|
||||||
|
marker=marker,
|
||||||
|
marker_column=marker_column)
|
||||||
|
|
||||||
|
def paginated_collection(self, limit=200, marker=None, marker_column=None):
|
||||||
|
collection = self.limit(int(limit) + 1, marker, marker_column)
|
||||||
|
if len(collection) > int(limit):
|
||||||
|
return (collection[0:-1], collection[-2]['id'])
|
||||||
|
return (collection, None)
|
||||||
|
|
||||||
|
|
||||||
|
class Queryable(object):
|
||||||
|
|
||||||
|
def __getattr__(self, item):
|
||||||
|
return lambda model, **conditions: Query(
|
||||||
|
model, query_func=getattr(db_api, item), **conditions)
|
||||||
|
|
||||||
|
db_query = Queryable()
|
||||||
|
|
||||||
|
|
||||||
|
def add_options(parser):
|
||||||
|
"""Adds any configuration options that the db layer might have.
|
||||||
|
|
||||||
|
:param parser: An optparse.OptionParser object
|
||||||
|
:retval None
|
||||||
|
|
||||||
|
"""
|
||||||
|
help_text = ("The following configuration options are specific to the "
|
||||||
|
"Reddwarf database.")
|
||||||
|
|
||||||
|
group = optparse.OptionGroup(parser,
|
||||||
|
"Registry Database Options",
|
||||||
|
help_text)
|
||||||
|
group.add_option('--sql-connection',
|
||||||
|
metavar="CONNECTION",
|
||||||
|
default=None,
|
||||||
|
help="A valid SQLAlchemy connection string for the "
|
||||||
|
"registry database. Default: %default")
|
||||||
|
parser.add_option_group(group)
|
||||||
|
16
reddwarf/db/sqlalchemy/__init__.py
Normal file
16
reddwarf/db/sqlalchemy/__init__.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2010-2011 OpenStack LLC.
|
||||||
|
# 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.
|
144
reddwarf/db/sqlalchemy/api.py
Normal file
144
reddwarf/db/sqlalchemy/api.py
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2011 OpenStack LLC.
|
||||||
|
# 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.exc
|
||||||
|
from sqlalchemy import and_
|
||||||
|
from sqlalchemy import or_
|
||||||
|
from sqlalchemy.orm import aliased
|
||||||
|
|
||||||
|
from reddwarf.common import exception
|
||||||
|
from reddwarf.common import utils
|
||||||
|
from reddwarf.db.sqlalchemy import migration
|
||||||
|
from reddwarf.db.sqlalchemy import mappers
|
||||||
|
from reddwarf.db.sqlalchemy import session
|
||||||
|
|
||||||
|
|
||||||
|
def list(query_func, *args, **kwargs):
|
||||||
|
return query_func(*args, **kwargs).all()
|
||||||
|
|
||||||
|
|
||||||
|
def count(query, *args, **kwargs):
|
||||||
|
return query(*args, **kwargs).count()
|
||||||
|
|
||||||
|
|
||||||
|
def find_all(model, **conditions):
|
||||||
|
return _query_by(model, **conditions)
|
||||||
|
|
||||||
|
|
||||||
|
def find_all_by_limit(query_func, model, conditions, limit, marker=None,
|
||||||
|
marker_column=None):
|
||||||
|
return _limits(query_func, model, conditions, limit, marker,
|
||||||
|
marker_column).all()
|
||||||
|
|
||||||
|
|
||||||
|
def find_by(model, **kwargs):
|
||||||
|
return _query_by(model, **kwargs).first()
|
||||||
|
|
||||||
|
|
||||||
|
def save(model):
|
||||||
|
try:
|
||||||
|
db_session = session.get_session()
|
||||||
|
model = db_session.merge(model)
|
||||||
|
db_session.flush()
|
||||||
|
return model
|
||||||
|
except sqlalchemy.exc.IntegrityError as error:
|
||||||
|
raise exception.DBConstraintError(model_name=model.__class__.__name__,
|
||||||
|
error=str(error.orig))
|
||||||
|
|
||||||
|
|
||||||
|
def delete(model):
|
||||||
|
db_session = session.get_session()
|
||||||
|
model = db_session.merge(model)
|
||||||
|
db_session.delete(model)
|
||||||
|
db_session.flush()
|
||||||
|
|
||||||
|
|
||||||
|
def delete_all(query_func, model, **conditions):
|
||||||
|
query_func(model, **conditions).delete()
|
||||||
|
|
||||||
|
|
||||||
|
def update(model, **values):
|
||||||
|
for k, v in values.iteritems():
|
||||||
|
model[k] = v
|
||||||
|
|
||||||
|
|
||||||
|
def update_all(query_func, model, conditions, values):
|
||||||
|
query_func(model, **conditions).update(values)
|
||||||
|
|
||||||
|
|
||||||
|
def configure_db(options, *plugins):
|
||||||
|
session.configure_db(options)
|
||||||
|
configure_db_for_plugins(options, *plugins)
|
||||||
|
|
||||||
|
|
||||||
|
def configure_db_for_plugins(options, *plugins):
|
||||||
|
for plugin in plugins:
|
||||||
|
session.configure_db(options, models_mapper=plugin.mapper)
|
||||||
|
|
||||||
|
|
||||||
|
def drop_db(options):
|
||||||
|
session.drop_db(options)
|
||||||
|
|
||||||
|
|
||||||
|
def clean_db():
|
||||||
|
session.clean_db()
|
||||||
|
|
||||||
|
|
||||||
|
def db_sync(options, version=None, repo_path=None):
|
||||||
|
migration.db_sync(options, version, repo_path)
|
||||||
|
|
||||||
|
|
||||||
|
def db_upgrade(options, version=None, repo_path=None):
|
||||||
|
migration.upgrade(options, version, repo_path)
|
||||||
|
|
||||||
|
|
||||||
|
def db_downgrade(options, version, repo_path=None):
|
||||||
|
migration.downgrade(options, version, repo_path)
|
||||||
|
|
||||||
|
|
||||||
|
def db_reset(options, *plugins):
|
||||||
|
drop_db(options)
|
||||||
|
db_sync(options)
|
||||||
|
db_reset_for_plugins(options, *plugins)
|
||||||
|
configure_db(options)
|
||||||
|
|
||||||
|
|
||||||
|
def db_reset_for_plugins(options, *plugins):
|
||||||
|
for plugin in plugins:
|
||||||
|
repo_path = plugin.migrate_repo_path()
|
||||||
|
if repo_path:
|
||||||
|
db_sync(options, repo_path=repo_path)
|
||||||
|
configure_db(options, *plugins)
|
||||||
|
|
||||||
|
|
||||||
|
def _base_query(cls):
|
||||||
|
return session.get_session().query(cls)
|
||||||
|
|
||||||
|
|
||||||
|
def _query_by(cls, **conditions):
|
||||||
|
query = _base_query(cls)
|
||||||
|
if conditions:
|
||||||
|
query = query.filter_by(**conditions)
|
||||||
|
return query
|
||||||
|
|
||||||
|
|
||||||
|
def _limits(query_func, model, conditions, limit, marker, marker_column=None):
|
||||||
|
query = query_func(model, **conditions)
|
||||||
|
marker_column = marker_column or model.id
|
||||||
|
if marker:
|
||||||
|
query = query.filter(marker_column > marker)
|
||||||
|
return query.order_by(marker_column).limit(limit)
|
37
reddwarf/db/sqlalchemy/mappers.py
Normal file
37
reddwarf/db/sqlalchemy/mappers.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2011 OpenStack LLC.
|
||||||
|
# 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 sqlalchemy import MetaData
|
||||||
|
from sqlalchemy import Table
|
||||||
|
from sqlalchemy import orm
|
||||||
|
from sqlalchemy.orm import exc as orm_exc
|
||||||
|
|
||||||
|
|
||||||
|
def map(engine, models):
|
||||||
|
meta = MetaData()
|
||||||
|
meta.bind = engine
|
||||||
|
if mapping_exists(models['instance']):
|
||||||
|
return
|
||||||
|
|
||||||
|
orm.mapper(models['instance'], Table('instances', meta, autoload=True))
|
||||||
|
|
||||||
|
def mapping_exists(model):
|
||||||
|
try:
|
||||||
|
orm.class_mapper(model)
|
||||||
|
return True
|
||||||
|
except orm_exc.UnmappedClassError:
|
||||||
|
return False
|
4
reddwarf/db/sqlalchemy/migrate_repo/README
Normal file
4
reddwarf/db/sqlalchemy/migrate_repo/README
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
This is a database migration repository.
|
||||||
|
|
||||||
|
More information at
|
||||||
|
http://code.google.com/p/sqlalchemy-migrate/
|
16
reddwarf/db/sqlalchemy/migrate_repo/__init__.py
Normal file
16
reddwarf/db/sqlalchemy/migrate_repo/__init__.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2011 OpenStack LLC.
|
||||||
|
# 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.
|
21
reddwarf/db/sqlalchemy/migrate_repo/manage.py
Normal file
21
reddwarf/db/sqlalchemy/migrate_repo/manage.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2011 OpenStack LLC.
|
||||||
|
# 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 migrate.versioning.shell import main
|
||||||
|
|
||||||
|
main(debug='False', repository='.')
|
21
reddwarf/db/sqlalchemy/migrate_repo/migrate.cfg
Normal file
21
reddwarf/db/sqlalchemy/migrate_repo/migrate.cfg
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
[db_settings]
|
||||||
|
# Used to identify which repository this database is versioned under.
|
||||||
|
# You can use the name of your project.
|
||||||
|
repository_id=Reddwarf Migrations
|
||||||
|
|
||||||
|
# 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=['mysql','postgres','sqlite']
|
63
reddwarf/db/sqlalchemy/migrate_repo/schema.py
Normal file
63
reddwarf/db/sqlalchemy/migrate_repo/schema.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2011 OpenStack LLC.
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Various conveniences used for migration scripts."""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import sqlalchemy.types
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger('reddwarf.db.sqlalchemy.migrate_repo.schema')
|
||||||
|
|
||||||
|
|
||||||
|
String = lambda length: sqlalchemy.types.String(
|
||||||
|
length=length, convert_unicode=False, assert_unicode=None,
|
||||||
|
unicode_error=None, _warn_on_bytestring=False)
|
||||||
|
|
||||||
|
|
||||||
|
Text = lambda: sqlalchemy.types.Text(
|
||||||
|
length=None, convert_unicode=False, assert_unicode=None,
|
||||||
|
unicode_error=None, _warn_on_bytestring=False)
|
||||||
|
|
||||||
|
|
||||||
|
Boolean = lambda: sqlalchemy.types.Boolean(create_constraint=True, name=None)
|
||||||
|
|
||||||
|
|
||||||
|
DateTime = lambda: sqlalchemy.types.DateTime(timezone=False)
|
||||||
|
|
||||||
|
|
||||||
|
Integer = lambda: sqlalchemy.types.Integer()
|
||||||
|
|
||||||
|
|
||||||
|
BigInteger = lambda: sqlalchemy.types.BigInteger()
|
||||||
|
|
||||||
|
|
||||||
|
def create_tables(tables):
|
||||||
|
for table in tables:
|
||||||
|
logger.info("creating table %(table)s" % locals())
|
||||||
|
table.create()
|
||||||
|
|
||||||
|
|
||||||
|
def drop_tables(tables):
|
||||||
|
for table in tables:
|
||||||
|
logger.info("dropping table %(table)s" % locals())
|
||||||
|
table.drop()
|
||||||
|
|
||||||
|
|
||||||
|
def Table(name, metadata, *args, **kwargs):
|
||||||
|
return sqlalchemy.schema.Table(name, metadata, *args,
|
||||||
|
mysql_engine='INNODB', **kwargs)
|
@ -0,0 +1,48 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2011 OpenStack LLC.
|
||||||
|
# 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 sqlalchemy import ForeignKey
|
||||||
|
from sqlalchemy.schema import Column
|
||||||
|
from sqlalchemy.schema import MetaData
|
||||||
|
from sqlalchemy.schema import UniqueConstraint
|
||||||
|
|
||||||
|
from reddwarf.db.sqlalchemy.migrate_repo.schema import Boolean
|
||||||
|
from reddwarf.db.sqlalchemy.migrate_repo.schema import create_tables
|
||||||
|
from reddwarf.db.sqlalchemy.migrate_repo.schema import DateTime
|
||||||
|
from reddwarf.db.sqlalchemy.migrate_repo.schema import drop_tables
|
||||||
|
from reddwarf.db.sqlalchemy.migrate_repo.schema import Integer
|
||||||
|
from reddwarf.db.sqlalchemy.migrate_repo.schema import BigInteger
|
||||||
|
from reddwarf.db.sqlalchemy.migrate_repo.schema import String
|
||||||
|
from reddwarf.db.sqlalchemy.migrate_repo.schema import Table
|
||||||
|
|
||||||
|
|
||||||
|
meta = MetaData()
|
||||||
|
|
||||||
|
instances = Table('instances', meta,
|
||||||
|
Column('id', String(36), primary_key=True, nullable=False),
|
||||||
|
Column('name', String(255)),
|
||||||
|
Column('status', String(255)))
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade(migrate_engine):
|
||||||
|
meta.bind = migrate_engine
|
||||||
|
create_tables([instances,])
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade(migrate_engine):
|
||||||
|
meta.bind = migrate_engine
|
||||||
|
drop_tables([instances,])
|
18
reddwarf/db/sqlalchemy/migrate_repo/versions/__init__.py
Normal file
18
reddwarf/db/sqlalchemy/migrate_repo/versions/__init__.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2011 OpenStack LLC.
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
# template repository default versions module
|
134
reddwarf/db/sqlalchemy/migration.py
Normal file
134
reddwarf/db/sqlalchemy/migration.py
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2011 OpenStack LLC.
|
||||||
|
# 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 logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
from migrate.versioning import api as versioning_api
|
||||||
|
# See LP bug #719834. sqlalchemy-migrate changed location of
|
||||||
|
# exceptions.py after 0.6.0.
|
||||||
|
try:
|
||||||
|
from migrate.versioning import exceptions as versioning_exceptions
|
||||||
|
except ImportError:
|
||||||
|
from migrate import exceptions as versioning_exceptions
|
||||||
|
|
||||||
|
from reddwarf.common import exception
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger('reddwarf.db.sqlalchemy.migration')
|
||||||
|
|
||||||
|
|
||||||
|
def db_version(options, repo_path=None):
|
||||||
|
"""Return the database's current migration number.
|
||||||
|
|
||||||
|
:param options: options dict
|
||||||
|
:retval version number
|
||||||
|
|
||||||
|
"""
|
||||||
|
repo_path = get_migrate_repo_path(repo_path)
|
||||||
|
sql_connection = options['sql_connection']
|
||||||
|
try:
|
||||||
|
return versioning_api.db_version(sql_connection, repo_path)
|
||||||
|
except versioning_exceptions.DatabaseNotControlledError:
|
||||||
|
msg = ("database '%(sql_connection)s' is not under migration control"
|
||||||
|
% locals())
|
||||||
|
raise exception.DatabaseMigrationError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade(options, version=None, repo_path=None):
|
||||||
|
"""Upgrade the database's current migration level.
|
||||||
|
|
||||||
|
:param options: options dict
|
||||||
|
:param version: version to upgrade (defaults to latest)
|
||||||
|
:retval version number
|
||||||
|
|
||||||
|
"""
|
||||||
|
db_version(options, repo_path) # Ensure db is under migration control
|
||||||
|
repo_path = get_migrate_repo_path(repo_path)
|
||||||
|
sql_connection = options['sql_connection']
|
||||||
|
version_str = version or 'latest'
|
||||||
|
logger.info("Upgrading %(sql_connection)s to version %(version_str)s" %
|
||||||
|
locals())
|
||||||
|
return versioning_api.upgrade(sql_connection, repo_path, version)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade(options, version, repo_path=None):
|
||||||
|
"""Downgrade the database's current migration level.
|
||||||
|
|
||||||
|
:param options: options dict
|
||||||
|
:param version: version to downgrade to
|
||||||
|
:retval version number
|
||||||
|
|
||||||
|
"""
|
||||||
|
db_version(options, repo_path) # Ensure db is under migration control
|
||||||
|
repo_path = get_migrate_repo_path(repo_path)
|
||||||
|
sql_connection = options['sql_connection']
|
||||||
|
logger.info("Downgrading %(sql_connection)s to version %(version)s" %
|
||||||
|
locals())
|
||||||
|
return versioning_api.downgrade(sql_connection, repo_path, version)
|
||||||
|
|
||||||
|
|
||||||
|
def version_control(options, repo_path=None):
|
||||||
|
"""Place a database under migration control.
|
||||||
|
|
||||||
|
:param options: options dict
|
||||||
|
|
||||||
|
"""
|
||||||
|
sql_connection = options['sql_connection']
|
||||||
|
try:
|
||||||
|
_version_control(options)
|
||||||
|
except versioning_exceptions.DatabaseAlreadyControlledError:
|
||||||
|
msg = ("database '%(sql_connection)s' is already under migration "
|
||||||
|
"control" % locals())
|
||||||
|
raise exception.DatabaseMigrationError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
def _version_control(options, repo_path):
|
||||||
|
"""Place a database under migration control.
|
||||||
|
|
||||||
|
:param options: options dict
|
||||||
|
|
||||||
|
"""
|
||||||
|
repo_path = get_migrate_repo_path(repo_path)
|
||||||
|
sql_connection = options['sql_connection']
|
||||||
|
return versioning_api.version_control(sql_connection, repo_path)
|
||||||
|
|
||||||
|
|
||||||
|
def db_sync(options, version=None, repo_path=None):
|
||||||
|
"""Place a database under migration control and perform an upgrade.
|
||||||
|
|
||||||
|
:param options: options dict
|
||||||
|
:param repo_path: used for plugin db migrations, defaults to main repo
|
||||||
|
:retval version number
|
||||||
|
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
_version_control(options, repo_path)
|
||||||
|
except versioning_exceptions.DatabaseAlreadyControlledError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
upgrade(options, version=version, repo_path=repo_path)
|
||||||
|
|
||||||
|
|
||||||
|
def get_migrate_repo_path(repo_path=None):
|
||||||
|
"""Get the path for the migrate repository."""
|
||||||
|
|
||||||
|
default_path = os.path.join(os.path.abspath(os.path.dirname(__file__)),
|
||||||
|
'migrate_repo')
|
||||||
|
repo_path = repo_path or default_path
|
||||||
|
assert os.path.exists(repo_path)
|
||||||
|
return repo_path
|
104
reddwarf/db/sqlalchemy/session.py
Normal file
104
reddwarf/db/sqlalchemy/session.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2011 OpenStack LLC.
|
||||||
|
# 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 contextlib
|
||||||
|
import logging
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy import MetaData
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
|
||||||
|
from reddwarf import database
|
||||||
|
from reddwarf.common import config
|
||||||
|
from reddwarf.db.sqlalchemy import mappers
|
||||||
|
|
||||||
|
_ENGINE = None
|
||||||
|
_MAKER = None
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger('reddwarf.db.sqlalchemy.session')
|
||||||
|
|
||||||
|
|
||||||
|
def configure_db(options, models_mapper=None):
|
||||||
|
configure_sqlalchemy_log(options)
|
||||||
|
global _ENGINE
|
||||||
|
if not _ENGINE:
|
||||||
|
_ENGINE = _create_engine(options)
|
||||||
|
if models_mapper:
|
||||||
|
models_mapper.map(_ENGINE)
|
||||||
|
else:
|
||||||
|
mappers.map(_ENGINE, database.models.persisted_models())
|
||||||
|
|
||||||
|
|
||||||
|
def configure_sqlalchemy_log(options):
|
||||||
|
debug = config.get_option(options, 'debug', type='bool', default=False)
|
||||||
|
verbose = config.get_option(options, 'verbose', type='bool', default=False)
|
||||||
|
logger = logging.getLogger('sqlalchemy.engine')
|
||||||
|
if debug:
|
||||||
|
logger.setLevel(logging.DEBUG)
|
||||||
|
elif verbose:
|
||||||
|
logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
|
||||||
|
def _create_engine(options):
|
||||||
|
engine_args = {
|
||||||
|
"pool_recycle": config.get_option(options,
|
||||||
|
'sql_idle_timeout',
|
||||||
|
type='int',
|
||||||
|
default=3600),
|
||||||
|
"echo": config.get_option(options,
|
||||||
|
'sql_query_log',
|
||||||
|
type='bool',
|
||||||
|
default=False),
|
||||||
|
}
|
||||||
|
LOG.info("Creating SQLAlchemy engine with args: %s" % engine_args)
|
||||||
|
return create_engine(options['sql_connection'], **engine_args)
|
||||||
|
|
||||||
|
|
||||||
|
def get_session(autocommit=True, expire_on_commit=False):
|
||||||
|
"""Helper method to grab session."""
|
||||||
|
|
||||||
|
global _MAKER, _ENGINE
|
||||||
|
if not _MAKER:
|
||||||
|
assert _ENGINE
|
||||||
|
_MAKER = sessionmaker(bind=_ENGINE,
|
||||||
|
autocommit=autocommit,
|
||||||
|
expire_on_commit=expire_on_commit)
|
||||||
|
return _MAKER()
|
||||||
|
|
||||||
|
|
||||||
|
def raw_query(model, autocommit=True, expire_on_commit=False):
|
||||||
|
return get_session(autocommit, expire_on_commit).query(model)
|
||||||
|
|
||||||
|
|
||||||
|
def clean_db():
|
||||||
|
global _ENGINE
|
||||||
|
meta = MetaData()
|
||||||
|
meta.reflect(bind=_ENGINE)
|
||||||
|
with contextlib.closing(_ENGINE.connect()) as con:
|
||||||
|
trans = con.begin()
|
||||||
|
for table in reversed(meta.sorted_tables):
|
||||||
|
if table.name != "migrate_version":
|
||||||
|
con.execute(table.delete())
|
||||||
|
trans.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def drop_db(options):
|
||||||
|
meta = MetaData()
|
||||||
|
engine = _create_engine(options)
|
||||||
|
meta.bind = engine
|
||||||
|
meta.reflect()
|
||||||
|
meta.drop_all()
|
Loading…
x
Reference in New Issue
Block a user