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/
|
||||
*.pyc
|
||||
.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.common import config
|
||||
from reddwarf.common import wsgi
|
||||
|
||||
from reddwarf.db import db_api
|
||||
|
||||
def create_options(parser):
|
||||
"""Sets up the CLI and config-file options
|
||||
@ -60,6 +60,7 @@ if __name__ == '__main__':
|
||||
(options, args) = config.parse_options(oparser)
|
||||
try:
|
||||
conf, app = config.Config.load_paste_app('reddwarf', options, args)
|
||||
db_api.configure_db(conf)
|
||||
server = wsgi.Server()
|
||||
server.start(app, options.get('port', conf['bind_port']),
|
||||
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
|
||||
# NOTE THIS AUTH TOKEN NEEDS TO BE CHANGED
|
||||
# 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" \
|
||||
# -H'X-Auth-Key:2a2c89c6a7284d32bcb94b4e56f0411c' http://0.0.0.0:8779/v2/$REDDWARF_TENANT/instances
|
||||
# curl -H'X-Auth-Token:AUTH_TOKEN' http://0.0.0.0:8779/v0.1/$REDDWARF_TENANT/instances
|
||||
|
||||
# Also, you should start up the api node like this
|
||||
# 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)
|
||||
|
||||
|
||||
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.
|
||||
"""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):
|
||||
if dictionary is None:
|
||||
return None
|
||||
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):
|
||||
"""A decorator that converts a function into a lazy property.
|
||||
|
||||
@ -50,3 +71,42 @@ class cached_property(object):
|
||||
value = self.func(obj)
|
||||
setattr(obj, self.__name__, 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 netaddr
|
||||
|
||||
from reddwarf import db
|
||||
|
||||
from reddwarf.common import config
|
||||
from reddwarf.common import exception
|
||||
from reddwarf.common import utils
|
||||
from novaclient.v1_1.client import Client
|
||||
|
||||
LOG = logging.getLogger('reddwarf.database.models')
|
||||
@ -41,6 +46,30 @@ class ModelBase(object):
|
||||
data_fields = self._data_fields + self._auto_generated_attrs
|
||||
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):
|
||||
|
||||
@ -89,4 +118,49 @@ class Instances(Instance):
|
||||
|
||||
|
||||
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):
|
||||
"""Return all instances."""
|
||||
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)
|
||||
|
||||
def show(self, req, tenant_id, id):
|
||||
|
@ -1,6 +1,6 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
# Copyright 2010-2011 OpenStack LLC.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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
|
||||
# License for the specific language governing permissions and limitations
|
||||
# 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…
Reference in New Issue
Block a user