Use oslo database code
Bring in the new database code from oslo. Uses get_session() from oslo as well as changing NovaBase to derive from a common class. Remove test_sqlalchemy.py now that this code is test in oslo. Implements blueprint db-common. Change-Id: I090754981c871250dd981cbbe1a08e7181440120
This commit is contained in:
parent
47bbf12a6c
commit
cd0c765ced
@ -80,6 +80,7 @@ from nova.db import migration
|
||||
from nova import exception
|
||||
from nova.openstack.common import cfg
|
||||
from nova.openstack.common import cliutils
|
||||
from nova.openstack.common.db.sqlalchemy import session as db_session
|
||||
from nova.openstack.common import importutils
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.openstack.common import rpc
|
||||
@ -831,7 +832,7 @@ class InstanceTypeCommands(object):
|
||||
except exception.InstanceTypeNotFound:
|
||||
print _("Valid instance type name is required")
|
||||
sys.exit(1)
|
||||
except exception.DBError, e:
|
||||
except db_session.DBError, e:
|
||||
print _("DB Error: %s") % e
|
||||
sys.exit(2)
|
||||
except Exception:
|
||||
@ -848,7 +849,7 @@ class InstanceTypeCommands(object):
|
||||
inst_types = instance_types.get_all_types()
|
||||
else:
|
||||
inst_types = instance_types.get_instance_type_by_name(name)
|
||||
except exception.DBError, e:
|
||||
except db_session.DBError, e:
|
||||
_db_error(e)
|
||||
if isinstance(inst_types.values()[0], dict):
|
||||
for k, v in inst_types.iteritems():
|
||||
@ -879,7 +880,7 @@ class InstanceTypeCommands(object):
|
||||
ext_spec)
|
||||
print _("Key %(key)s set to %(value)s on instance"
|
||||
" type %(name)s") % locals()
|
||||
except exception.DBError, e:
|
||||
except db_session.DBError, e:
|
||||
_db_error(e)
|
||||
|
||||
@args('--name', dest='name', metavar='<name>',
|
||||
@ -902,7 +903,7 @@ class InstanceTypeCommands(object):
|
||||
key)
|
||||
|
||||
print _("Key %(key)s on instance type %(name)s unset") % locals()
|
||||
except exception.DBError, e:
|
||||
except db_session.DBError, e:
|
||||
_db_error(e)
|
||||
|
||||
|
||||
|
@ -27,6 +27,7 @@ from nova import context
|
||||
from nova import db
|
||||
from nova import exception
|
||||
from nova.openstack.common import cfg
|
||||
from nova.openstack.common.db.sqlalchemy import session as db_session
|
||||
from nova.openstack.common import log as logging
|
||||
from nova import utils
|
||||
|
||||
@ -110,7 +111,7 @@ def create(name, memory, vcpus, root_gb, ephemeral_gb=None, flavorid=None,
|
||||
|
||||
try:
|
||||
return db.instance_type_create(context.get_admin_context(), kwargs)
|
||||
except exception.DBError, e:
|
||||
except db_session.DBError, e:
|
||||
LOG.exception(_('DB error: %s') % e)
|
||||
raise exception.InstanceTypeCreateFailed()
|
||||
|
||||
|
@ -18,10 +18,16 @@
|
||||
# under the License.
|
||||
|
||||
from nova.openstack.common import cfg
|
||||
from nova.openstack.common.db.sqlalchemy import session as db_session
|
||||
from nova.openstack.common import rpc
|
||||
from nova import paths
|
||||
|
||||
_DEFAULT_SQL_CONNECTION = 'sqlite:///' + paths.state_path_def('$sqlite_db')
|
||||
|
||||
|
||||
def parse_args(argv, default_config_files=None):
|
||||
db_session.set_defaults(sql_connection=_DEFAULT_SQL_CONNECTION,
|
||||
sqlite_db='nova.sqlite')
|
||||
rpc.set_defaults(control_exchange='nova')
|
||||
cfg.CONF(argv[1:],
|
||||
project='nova',
|
||||
|
@ -35,14 +35,14 @@ from sqlalchemy.sql.expression import desc
|
||||
from sqlalchemy.sql import func
|
||||
|
||||
from nova import block_device
|
||||
from nova.common import sqlalchemyutils
|
||||
from nova.compute import task_states
|
||||
from nova.compute import vm_states
|
||||
from nova import db
|
||||
from nova.db.sqlalchemy import models
|
||||
from nova.db.sqlalchemy.session import get_session
|
||||
from nova import exception
|
||||
from nova.openstack.common import cfg
|
||||
from nova.openstack.common.db.sqlalchemy import session as db_session
|
||||
from nova.openstack.common.db.sqlalchemy import utils as sqlalchemyutils
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.openstack.common import timeutils
|
||||
from nova.openstack.common import uuidutils
|
||||
@ -58,10 +58,13 @@ db_opts = [
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(db_opts)
|
||||
CONF.import_opt('compute_topic', 'nova.compute.rpcapi')
|
||||
CONF.import_opt('sql_connection', 'nova.db.sqlalchemy.session')
|
||||
CONF.import_opt('sql_connection',
|
||||
'nova.openstack.common.db.sqlalchemy.session')
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
get_session = db_session.get_session
|
||||
|
||||
|
||||
def is_user_context(context):
|
||||
"""Indicates if the request context is a normal user."""
|
||||
@ -1251,7 +1254,7 @@ def virtual_interface_create(context, values):
|
||||
vif_ref = models.VirtualInterface()
|
||||
vif_ref.update(values)
|
||||
vif_ref.save()
|
||||
except exception.DBError:
|
||||
except db_session.DBError:
|
||||
raise exception.VirtualInterfaceCreateException()
|
||||
|
||||
return vif_ref
|
||||
@ -3535,7 +3538,7 @@ def instance_type_create(context, values):
|
||||
instance_type_ref.update(values)
|
||||
instance_type_ref.save(session=session)
|
||||
except Exception, e:
|
||||
raise exception.DBError(e)
|
||||
raise db_session.DBError(e)
|
||||
return _dict_with_extra_specs(instance_type_ref)
|
||||
|
||||
|
||||
@ -4238,7 +4241,7 @@ def s3_image_create(context, image_uuid):
|
||||
s3_image_ref.update({'uuid': image_uuid})
|
||||
s3_image_ref.save()
|
||||
except Exception, e:
|
||||
raise exception.DBError(e)
|
||||
raise db_session.DBError(e)
|
||||
|
||||
return s3_image_ref
|
||||
|
||||
|
@ -20,8 +20,8 @@ import distutils.version as dist_version
|
||||
import os
|
||||
|
||||
from nova.db import migration
|
||||
from nova.db.sqlalchemy.session import get_engine
|
||||
from nova import exception
|
||||
from nova.openstack.common.db.sqlalchemy import session as db_session
|
||||
from nova.openstack.common import log as logging
|
||||
|
||||
|
||||
@ -62,6 +62,8 @@ from migrate.versioning.repository import Repository
|
||||
|
||||
_REPOSITORY = None
|
||||
|
||||
get_engine = db_session.get_engine
|
||||
|
||||
|
||||
def db_sync(version=None):
|
||||
if version is not None:
|
||||
|
@ -26,9 +26,9 @@ from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy import ForeignKey, DateTime, Boolean, Text, Float
|
||||
from sqlalchemy.orm import relationship, backref, object_mapper
|
||||
|
||||
from nova.db.sqlalchemy.session import get_session
|
||||
from nova.db.sqlalchemy import types
|
||||
from nova.openstack.common import cfg
|
||||
from nova.openstack.common.db.sqlalchemy import models
|
||||
from nova.openstack.common import timeutils
|
||||
|
||||
|
||||
@ -36,74 +36,8 @@ CONF = cfg.CONF
|
||||
BASE = declarative_base()
|
||||
|
||||
|
||||
class NovaBase(object):
|
||||
"""Base class for Nova Models."""
|
||||
__table_initialized__ = False
|
||||
created_at = Column(DateTime, default=timeutils.utcnow)
|
||||
updated_at = Column(DateTime, onupdate=timeutils.utcnow)
|
||||
deleted_at = Column(DateTime)
|
||||
deleted = Column(Integer, default=0)
|
||||
metadata = None
|
||||
|
||||
def save(self, session=None):
|
||||
"""Save this object."""
|
||||
if not session:
|
||||
session = get_session()
|
||||
# NOTE(boris-42): This part of code should be look like:
|
||||
# sesssion.add(self)
|
||||
# session.flush()
|
||||
# But there is a bug in sqlalchemy and eventlet that
|
||||
# raises NoneType exception if there is no running
|
||||
# transaction and rollback is called. As long as
|
||||
# sqlalchemy has this bug we have to create transaction
|
||||
# explicity.
|
||||
with session.begin(subtransactions=True):
|
||||
session.add(self)
|
||||
session.flush()
|
||||
|
||||
def soft_delete(self, session=None):
|
||||
"""Mark this object as deleted."""
|
||||
self.deleted = self.id
|
||||
self.deleted_at = timeutils.utcnow()
|
||||
self.save(session=session)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
setattr(self, key, value)
|
||||
|
||||
def __getitem__(self, key):
|
||||
return getattr(self, key)
|
||||
|
||||
def get(self, key, default=None):
|
||||
return getattr(self, key, default)
|
||||
|
||||
def __iter__(self):
|
||||
columns = dict(object_mapper(self).columns).keys()
|
||||
# NOTE(russellb): Allow models to specify other keys that can be looked
|
||||
# up, beyond the actual db columns. An example would be the 'name'
|
||||
# property for an Instance.
|
||||
if hasattr(self, '_extra_keys'):
|
||||
columns.extend(self._extra_keys())
|
||||
self._i = iter(columns)
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
n = self._i.next()
|
||||
return n, getattr(self, n)
|
||||
|
||||
def update(self, values):
|
||||
"""Make the model object behave like a dict."""
|
||||
for k, v in values.iteritems():
|
||||
setattr(self, k, v)
|
||||
|
||||
def iteritems(self):
|
||||
"""Make the model object behave like a dict.
|
||||
|
||||
Includes attributes from joins."""
|
||||
local = dict(self)
|
||||
joined = dict([(k, v) for k, v in self.__dict__.iteritems()
|
||||
if not k[0] == '_'])
|
||||
local.update(joined)
|
||||
return local.iteritems()
|
||||
class NovaBase(models.SoftDeleteMixin, models.ModelBase):
|
||||
pass
|
||||
|
||||
|
||||
class Service(BASE, NovaBase):
|
||||
|
@ -164,20 +164,6 @@ class EC2APIError(NovaException):
|
||||
super(EC2APIError, self).__init__(outstr)
|
||||
|
||||
|
||||
class DBError(NovaException):
|
||||
"""Wraps an implementation specific exception."""
|
||||
def __init__(self, inner_exception=None):
|
||||
self.inner_exception = inner_exception
|
||||
super(DBError, self).__init__(str(inner_exception))
|
||||
|
||||
|
||||
class DBDuplicateEntry(DBError):
|
||||
"""Wraps an implementation specific exception."""
|
||||
def __init__(self, columns=[], inner_exception=None):
|
||||
self.columns = columns
|
||||
super(DBDuplicateEntry, self).__init__(inner_exception)
|
||||
|
||||
|
||||
class EncryptionFailure(NovaException):
|
||||
message = _("Failed to encrypt text: %(reason)s")
|
||||
|
||||
|
16
nova/openstack/common/db/__init__.py
Normal file
16
nova/openstack/common/db/__init__.py
Normal file
@ -0,0 +1,16 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 Cloudscaling Group, Inc
|
||||
# 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.
|
16
nova/openstack/common/db/sqlalchemy/__init__.py
Normal file
16
nova/openstack/common/db/sqlalchemy/__init__.py
Normal file
@ -0,0 +1,16 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 Cloudscaling Group, Inc
|
||||
# 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.
|
103
nova/openstack/common/db/sqlalchemy/models.py
Normal file
103
nova/openstack/common/db/sqlalchemy/models.py
Normal file
@ -0,0 +1,103 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# Copyright 2011 Piston Cloud Computing, Inc.
|
||||
# Copyright 2012 Cloudscaling Group, Inc.
|
||||
# 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.
|
||||
"""
|
||||
SQLAlchemy models.
|
||||
"""
|
||||
|
||||
from sqlalchemy import Column, Integer
|
||||
from sqlalchemy import DateTime
|
||||
from sqlalchemy.orm import object_mapper
|
||||
|
||||
from nova.openstack.common.db.sqlalchemy.session import get_session
|
||||
from nova.openstack.common import timeutils
|
||||
|
||||
|
||||
class ModelBase(object):
|
||||
"""Base class for models."""
|
||||
__table_initialized__ = False
|
||||
created_at = Column(DateTime, default=timeutils.utcnow)
|
||||
updated_at = Column(DateTime, onupdate=timeutils.utcnow)
|
||||
metadata = None
|
||||
|
||||
def save(self, session=None):
|
||||
"""Save this object."""
|
||||
if not session:
|
||||
session = get_session()
|
||||
# NOTE(boris-42): This part of code should be look like:
|
||||
# sesssion.add(self)
|
||||
# session.flush()
|
||||
# But there is a bug in sqlalchemy and eventlet that
|
||||
# raises NoneType exception if there is no running
|
||||
# transaction and rollback is called. As long as
|
||||
# sqlalchemy has this bug we have to create transaction
|
||||
# explicity.
|
||||
with session.begin(subtransactions=True):
|
||||
session.add(self)
|
||||
session.flush()
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
setattr(self, key, value)
|
||||
|
||||
def __getitem__(self, key):
|
||||
return getattr(self, key)
|
||||
|
||||
def get(self, key, default=None):
|
||||
return getattr(self, key, default)
|
||||
|
||||
def __iter__(self):
|
||||
columns = dict(object_mapper(self).columns).keys()
|
||||
# NOTE(russellb): Allow models to specify other keys that can be looked
|
||||
# up, beyond the actual db columns. An example would be the 'name'
|
||||
# property for an Instance.
|
||||
if hasattr(self, '_extra_keys'):
|
||||
columns.extend(self._extra_keys())
|
||||
self._i = iter(columns)
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
n = self._i.next()
|
||||
return n, getattr(self, n)
|
||||
|
||||
def update(self, values):
|
||||
"""Make the model object behave like a dict."""
|
||||
for k, v in values.iteritems():
|
||||
setattr(self, k, v)
|
||||
|
||||
def iteritems(self):
|
||||
"""Make the model object behave like a dict.
|
||||
|
||||
Includes attributes from joins."""
|
||||
local = dict(self)
|
||||
joined = dict([(k, v) for k, v in self.__dict__.iteritems()
|
||||
if not k[0] == '_'])
|
||||
local.update(joined)
|
||||
return local.iteritems()
|
||||
|
||||
|
||||
class SoftDeleteMixin(object):
|
||||
deleted_at = Column(DateTime)
|
||||
deleted = Column(Integer, default=0)
|
||||
|
||||
def soft_delete(self, session=None):
|
||||
"""Mark this object as deleted."""
|
||||
self.deleted = self.id
|
||||
self.deleted_at = timeutils.utcnow()
|
||||
self.save(session=session)
|
@ -18,6 +18,16 @@
|
||||
|
||||
"""Session Handling for SQLAlchemy backend.
|
||||
|
||||
Initializing:
|
||||
|
||||
* Call set_defaults with the minimal of the following kwargs:
|
||||
sql_connection, sqlite_db
|
||||
|
||||
Example:
|
||||
|
||||
session.set_defaults(sql_connection="sqlite:///var/lib/nova/sqlite.db",
|
||||
sqlite_db="/var/lib/nova/sqlite.db")
|
||||
|
||||
Recommended ways to use sessions within this framework:
|
||||
|
||||
* Don't use them explicitly; this is like running with AUTOCOMMIT=1.
|
||||
@ -159,6 +169,15 @@ There are some things which it is best to avoid:
|
||||
proper UNIQUE constraints are added to the tables.
|
||||
|
||||
|
||||
Enabling soft deletes:
|
||||
|
||||
* To use/enable soft-deletes, the SoftDeleteMixin must be added
|
||||
to your model class. For example:
|
||||
|
||||
class NovaBase(models.SoftDeleteMixin, models.ModelBase):
|
||||
pass
|
||||
|
||||
|
||||
Efficient use of soft deletes:
|
||||
|
||||
* There are two possible ways to mark a record as deleted:
|
||||
@ -221,6 +240,7 @@ Efficient use of soft deletes:
|
||||
# This will produce count(bar_refs) db requests.
|
||||
"""
|
||||
|
||||
import os.path
|
||||
import re
|
||||
import time
|
||||
|
||||
@ -238,16 +258,17 @@ import sqlalchemy.orm
|
||||
from sqlalchemy.pool import NullPool, StaticPool
|
||||
from sqlalchemy.sql.expression import literal_column
|
||||
|
||||
import nova.exception
|
||||
from nova.openstack.common import cfg
|
||||
import nova.openstack.common.log as logging
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.openstack.common.gettextutils import _
|
||||
from nova.openstack.common import timeutils
|
||||
from nova import paths
|
||||
|
||||
|
||||
sql_opts = [
|
||||
cfg.StrOpt('sql_connection',
|
||||
default='sqlite:///' + paths.state_path_def('$sqlite_db'),
|
||||
default='sqlite:///' +
|
||||
os.path.abspath(os.path.join(os.path.dirname(__file__),
|
||||
'../', '$sqlite_db')),
|
||||
help='The SQLAlchemy connection string used to connect to the '
|
||||
'database'),
|
||||
cfg.StrOpt('sqlite_db',
|
||||
@ -262,11 +283,11 @@ sql_opts = [
|
||||
cfg.IntOpt('sql_min_pool_size',
|
||||
default=1,
|
||||
help='Minimum number of SQL connections to keep open in a '
|
||||
'pool'),
|
||||
'pool'),
|
||||
cfg.IntOpt('sql_max_pool_size',
|
||||
default=5,
|
||||
help='Maximum number of SQL connections to keep open in a '
|
||||
'pool'),
|
||||
'pool'),
|
||||
cfg.IntOpt('sql_max_retries',
|
||||
default=10,
|
||||
help='maximum db connection retries during startup. '
|
||||
@ -297,6 +318,13 @@ _ENGINE = None
|
||||
_MAKER = None
|
||||
|
||||
|
||||
def set_defaults(sql_connection, sqlite_db):
|
||||
"""Set defaults for configuration variables."""
|
||||
cfg.set_defaults(sql_opts,
|
||||
sql_connection=sql_connection,
|
||||
sqlite_db=sqlite_db)
|
||||
|
||||
|
||||
def get_session(autocommit=True, expire_on_commit=False):
|
||||
"""Return a SQLAlchemy session."""
|
||||
global _MAKER
|
||||
@ -309,6 +337,25 @@ def get_session(autocommit=True, expire_on_commit=False):
|
||||
return session
|
||||
|
||||
|
||||
class DBError(Exception):
|
||||
"""Wraps an implementation specific exception."""
|
||||
def __init__(self, inner_exception=None):
|
||||
self.inner_exception = inner_exception
|
||||
super(DBError, self).__init__(str(inner_exception))
|
||||
|
||||
|
||||
class DBDuplicateEntry(DBError):
|
||||
"""Wraps an implementation specific exception."""
|
||||
def __init__(self, columns=[], inner_exception=None):
|
||||
self.columns = columns
|
||||
super(DBDuplicateEntry, self).__init__(inner_exception)
|
||||
|
||||
|
||||
class InvalidUnicodeParameter(Exception):
|
||||
message = _("Invalid Parameter: "
|
||||
"Unicode is not supported by the current database.")
|
||||
|
||||
|
||||
# note(boris-42): In current versions of DB backends unique constraint
|
||||
# violation messages follow the structure:
|
||||
#
|
||||
@ -362,7 +409,7 @@ def raise_if_duplicate_entry_error(integrity_error, engine_name):
|
||||
columns = columns.strip().split(", ")
|
||||
else:
|
||||
columns = get_columns_from_uniq_cons_or_name(columns)
|
||||
raise nova.exception.DBDuplicateEntry(columns, integrity_error)
|
||||
raise DBDuplicateEntry(columns, integrity_error)
|
||||
|
||||
|
||||
def wrap_db_error(f):
|
||||
@ -370,7 +417,7 @@ def wrap_db_error(f):
|
||||
try:
|
||||
return f(*args, **kwargs)
|
||||
except UnicodeEncodeError:
|
||||
raise nova.exception.InvalidUnicodeParameter()
|
||||
raise InvalidUnicodeParameter()
|
||||
# note(boris-42): We should catch unique constraint violation and
|
||||
# wrap it by our own DBDuplicateEntry exception. Unique constraint
|
||||
# violation is wrapped by IntegrityError.
|
||||
@ -381,10 +428,10 @@ def wrap_db_error(f):
|
||||
# means we should get names of columns, which values violate
|
||||
# unique constraint, from error message.
|
||||
raise_if_duplicate_entry_error(e, get_engine().name)
|
||||
raise nova.exception.DBError(e)
|
||||
raise DBError(e)
|
||||
except Exception, e:
|
||||
LOG.exception(_('DB exception wrapped.'))
|
||||
raise nova.exception.DBError(e)
|
||||
raise DBError(e)
|
||||
_wrap.func_name = f.func_name
|
||||
return _wrap
|
||||
|
||||
@ -473,19 +520,19 @@ def create_engine(sql_connection):
|
||||
engine_args["poolclass"] = StaticPool
|
||||
engine_args["connect_args"] = {'check_same_thread': False}
|
||||
elif all((CONF.sql_dbpool_enable, MySQLdb,
|
||||
"mysql" in connection_dict.drivername)):
|
||||
"mysql" in connection_dict.drivername)):
|
||||
LOG.info(_("Using mysql/eventlet db_pool."))
|
||||
# MySQLdb won't accept 'None' in the password field
|
||||
password = connection_dict.password or ''
|
||||
pool_args = {
|
||||
'db': connection_dict.database,
|
||||
'passwd': password,
|
||||
'host': connection_dict.host,
|
||||
'user': connection_dict.username,
|
||||
'min_size': CONF.sql_min_pool_size,
|
||||
'max_size': CONF.sql_max_pool_size,
|
||||
'max_idle': CONF.sql_idle_timeout,
|
||||
'client_flag': mysql_client_constants.FOUND_ROWS}
|
||||
'db': connection_dict.database,
|
||||
'passwd': password,
|
||||
'host': connection_dict.host,
|
||||
'user': connection_dict.username,
|
||||
'min_size': CONF.sql_min_pool_size,
|
||||
'max_size': CONF.sql_max_pool_size,
|
||||
'max_idle': CONF.sql_idle_timeout,
|
||||
'client_flag': mysql_client_constants.FOUND_ROWS}
|
||||
|
||||
pool = db_pool.ConnectionPool(MySQLdb, **pool_args)
|
||||
|
||||
@ -540,7 +587,7 @@ def create_engine(sql_connection):
|
||||
break
|
||||
except OperationalError, e:
|
||||
if (remaining != 'infinite' and remaining == 0) or \
|
||||
not is_db_connection_error(e.args[0]):
|
||||
not is_db_connection_error(e.args[0]):
|
||||
raise
|
||||
return engine
|
||||
|
||||
@ -599,15 +646,15 @@ def patch_mysqldb_with_stacktrace_comments():
|
||||
continue
|
||||
if file.endswith('exception.py') and method == '_wrap':
|
||||
continue
|
||||
# nova/db/api is just a wrapper around nova/db/sqlalchemy/api
|
||||
if file.endswith('nova/db/api.py'):
|
||||
# db/api is just a wrapper around db/sqlalchemy/api
|
||||
if file.endswith('db/api.py'):
|
||||
continue
|
||||
# only trace inside nova
|
||||
index = file.rfind('nova')
|
||||
if index == -1:
|
||||
continue
|
||||
stack += "File:%s:%s Method:%s() Line:%s | " \
|
||||
% (file[index:], line, method, function)
|
||||
% (file[index:], line, method, function)
|
||||
|
||||
# strip trailing " | " from stack
|
||||
if stack:
|
@ -22,13 +22,17 @@
|
||||
|
||||
import sqlalchemy
|
||||
|
||||
from nova import exception
|
||||
from nova.openstack.common.gettextutils import _
|
||||
from nova.openstack.common import log as logging
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class InvalidSortKey(Exception):
|
||||
message = _("Sort key supplied was not valid.")
|
||||
|
||||
|
||||
# copy from glance/db/sqlalchemy/api.py
|
||||
def paginate_query(query, model, limit, sort_keys, marker=None,
|
||||
sort_dir=None, sort_dirs=None):
|
||||
@ -89,7 +93,7 @@ def paginate_query(query, model, limit, sort_keys, marker=None,
|
||||
try:
|
||||
sort_key_attr = getattr(model, current_sort_key)
|
||||
except AttributeError:
|
||||
raise exception.InvalidSortKey()
|
||||
raise InvalidSortKey()
|
||||
query = query.order_by(sort_dir_func(sort_key_attr))
|
||||
|
||||
# Add pagination
|
@ -37,9 +37,9 @@ import testtools
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova.db import migration
|
||||
from nova.db.sqlalchemy import session
|
||||
from nova.network import manager as network_manager
|
||||
from nova.openstack.common import cfg
|
||||
from nova.openstack.common.db.sqlalchemy import session
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.openstack.common import timeutils
|
||||
from nova import paths
|
||||
@ -56,8 +56,9 @@ test_opts = [
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(test_opts)
|
||||
CONF.import_opt('sql_connection', 'nova.db.sqlalchemy.session')
|
||||
CONF.import_opt('sqlite_db', 'nova.db.sqlalchemy.session')
|
||||
CONF.import_opt('sql_connection',
|
||||
'nova.openstack.common.db.sqlalchemy.session')
|
||||
CONF.import_opt('sqlite_db', 'nova.openstack.common.db.sqlalchemy.session')
|
||||
CONF.set_override('use_stderr', False)
|
||||
|
||||
logging.setup('nova')
|
||||
|
@ -18,6 +18,7 @@ Bare-metal DB testcase for BareMetalInterface
|
||||
"""
|
||||
|
||||
from nova import exception
|
||||
from nova.openstack.common.db.sqlalchemy import session as db_session
|
||||
from nova.tests.baremetal.db import base
|
||||
from nova.virt.baremetal import db
|
||||
|
||||
@ -27,7 +28,7 @@ class BareMetalInterfaceTestCase(base.BMDBTestCase):
|
||||
def test_unique_address(self):
|
||||
pif1_id = db.bm_interface_create(self.context, 1, '11:11:11:11:11:11',
|
||||
'0x1', 1)
|
||||
self.assertRaises(exception.DBError,
|
||||
self.assertRaises(db_session.DBError,
|
||||
db.bm_interface_create,
|
||||
self.context, 2, '11:11:11:11:11:11', '0x2', 2)
|
||||
# succeed after delete pif1
|
||||
|
@ -18,6 +18,7 @@ Bare-metal DB testcase for BareMetalPxeIp
|
||||
"""
|
||||
|
||||
from nova import exception
|
||||
from nova.openstack.common.db.sqlalchemy import session as db_session
|
||||
from nova.tests.baremetal.db import base
|
||||
from nova.tests.baremetal.db import utils
|
||||
from nova.virt.baremetal import db
|
||||
@ -50,14 +51,14 @@ class BareMetalPxeIpTestCase(base.BMDBTestCase):
|
||||
# address duplicates
|
||||
i = utils.new_bm_pxe_ip(address='10.1.1.1',
|
||||
server_address='10.1.1.201')
|
||||
self.assertRaises(exception.DBError,
|
||||
self.assertRaises(db_session.DBError,
|
||||
db.bm_pxe_ip_create_direct,
|
||||
self.context, i)
|
||||
|
||||
# server_address duplicates
|
||||
i = utils.new_bm_pxe_ip(address='10.1.1.3',
|
||||
server_address='10.1.1.101')
|
||||
self.assertRaises(exception.DBError,
|
||||
self.assertRaises(db_session.DBError,
|
||||
db.bm_pxe_ip_create_direct,
|
||||
self.context, i)
|
||||
|
||||
|
@ -25,6 +25,7 @@ from testtools import matchers
|
||||
|
||||
from nova import exception
|
||||
from nova.openstack.common import cfg
|
||||
from nova.openstack.common.db.sqlalchemy import session as db_session
|
||||
from nova.tests.baremetal.db import base as bm_db_base
|
||||
from nova.tests.baremetal.db import utils as bm_db_utils
|
||||
from nova.tests.image import fake as fake_image
|
||||
@ -521,7 +522,7 @@ class PXEPublicMethodsTestCase(BareMetalPXETestCase):
|
||||
AndRaise(exception.NovaException)
|
||||
bm_utils.unlink_without_raise(pxe_path)
|
||||
self.driver._collect_mac_addresses(self.context, self.node).\
|
||||
AndRaise(exception.DBError)
|
||||
AndRaise(db_session.DBError)
|
||||
bm_utils.rmtree_without_raise(
|
||||
os.path.join(CONF.baremetal.tftp_root, 'fake-uuid'))
|
||||
self.mox.ReplayAll()
|
||||
|
@ -29,6 +29,7 @@ from nova.network import linux_net
|
||||
from nova.network import manager as network_manager
|
||||
from nova.network import model as net_model
|
||||
from nova.openstack.common import cfg
|
||||
from nova.openstack.common.db.sqlalchemy import session as db_session
|
||||
from nova.openstack.common import importutils
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.openstack.common import rpc
|
||||
@ -2046,7 +2047,7 @@ class FloatingIPTestCase(test.TestCase):
|
||||
# address column, so fake the collision-avoidance here
|
||||
def fake_vif_save(vif):
|
||||
if vif.address == crash_test_dummy_vif['address']:
|
||||
raise exception.DBError("If you're smart, you'll retry!")
|
||||
raise db_session.DBError("If you're smart, you'll retry!")
|
||||
self.stubs.Set(models.VirtualInterface, 'save', fake_vif_save)
|
||||
|
||||
# Attempt to add another and make sure that both MACs are consumed
|
||||
|
@ -21,8 +21,8 @@ from nova.compute import instance_types
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova.db.sqlalchemy import models
|
||||
from nova.db.sqlalchemy import session as sql_session
|
||||
from nova import exception
|
||||
from nova.openstack.common.db.sqlalchemy import session as sql_session
|
||||
from nova.openstack.common import log as logging
|
||||
from nova import test
|
||||
|
||||
|
@ -1,129 +0,0 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
# Copyright (c) 2012 Rackspace Hosting
|
||||
# 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.
|
||||
|
||||
"""Unit tests for SQLAlchemy specific code."""
|
||||
|
||||
from eventlet import db_pool
|
||||
try:
|
||||
import MySQLdb
|
||||
except ImportError:
|
||||
MySQLdb = None
|
||||
|
||||
from sqlalchemy import Column, MetaData, Table, UniqueConstraint
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy import DateTime, Integer
|
||||
|
||||
from nova import context
|
||||
from nova.db.sqlalchemy import models
|
||||
from nova.db.sqlalchemy import session
|
||||
from nova import exception
|
||||
from nova import test
|
||||
|
||||
|
||||
class DbPoolTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
super(DbPoolTestCase, self).setUp()
|
||||
self.flags(sql_dbpool_enable=True)
|
||||
self.user_id = 'fake'
|
||||
self.project_id = 'fake'
|
||||
self.context = context.RequestContext(self.user_id, self.project_id)
|
||||
if not MySQLdb:
|
||||
self.skipTest("Unable to test due to lack of MySQLdb")
|
||||
|
||||
def test_db_pool_option(self):
|
||||
self.flags(sql_idle_timeout=11, sql_min_pool_size=21,
|
||||
sql_max_pool_size=42)
|
||||
|
||||
info = {}
|
||||
|
||||
class FakeConnectionPool(db_pool.ConnectionPool):
|
||||
def __init__(self, mod_name, **kwargs):
|
||||
info['module'] = mod_name
|
||||
info['kwargs'] = kwargs
|
||||
super(FakeConnectionPool, self).__init__(mod_name,
|
||||
**kwargs)
|
||||
|
||||
def connect(self, *args, **kwargs):
|
||||
raise test.TestingException()
|
||||
|
||||
self.stubs.Set(db_pool, 'ConnectionPool',
|
||||
FakeConnectionPool)
|
||||
|
||||
sql_connection = 'mysql://user:pass@127.0.0.1/nova'
|
||||
self.assertRaises(test.TestingException, session.create_engine,
|
||||
sql_connection)
|
||||
|
||||
self.assertEqual(info['module'], MySQLdb)
|
||||
self.assertEqual(info['kwargs']['max_idle'], 11)
|
||||
self.assertEqual(info['kwargs']['min_size'], 21)
|
||||
self.assertEqual(info['kwargs']['max_size'], 42)
|
||||
|
||||
|
||||
BASE = declarative_base()
|
||||
_TABLE_NAME = '__tmp__test__tmp__'
|
||||
|
||||
|
||||
class TmpTable(BASE, models.NovaBase):
|
||||
__tablename__ = _TABLE_NAME
|
||||
id = Column(Integer, primary_key=True)
|
||||
foo = Column(Integer)
|
||||
|
||||
|
||||
class SessionErrorWrapperTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
super(SessionErrorWrapperTestCase, self).setUp()
|
||||
meta = MetaData()
|
||||
meta.bind = session.get_engine()
|
||||
test_table = Table(_TABLE_NAME, meta,
|
||||
Column('id', Integer, primary_key=True,
|
||||
nullable=False),
|
||||
Column('deleted', Integer, default=0),
|
||||
Column('deleted_at', DateTime),
|
||||
Column('updated_at', DateTime),
|
||||
Column('created_at', DateTime),
|
||||
Column('foo', Integer),
|
||||
UniqueConstraint('foo', name='uniq_foo'))
|
||||
test_table.create()
|
||||
|
||||
def tearDown(self):
|
||||
super(SessionErrorWrapperTestCase, self).tearDown()
|
||||
meta = MetaData()
|
||||
meta.bind = session.get_engine()
|
||||
test_table = Table(_TABLE_NAME, meta, autoload=True)
|
||||
test_table.drop()
|
||||
|
||||
def test_flush_wrapper(self):
|
||||
tbl = TmpTable()
|
||||
tbl.update({'foo': 10})
|
||||
tbl.save()
|
||||
|
||||
tbl2 = TmpTable()
|
||||
tbl2.update({'foo': 10})
|
||||
self.assertRaises(exception.DBDuplicateEntry, tbl2.save)
|
||||
|
||||
def test_execute_wrapper(self):
|
||||
_session = session.get_session()
|
||||
with _session.begin():
|
||||
for i in [10, 20]:
|
||||
tbl = TmpTable()
|
||||
tbl.update({'foo': i})
|
||||
tbl.save(session=_session)
|
||||
|
||||
method = _session.query(TmpTable).\
|
||||
filter_by(foo=10).\
|
||||
update
|
||||
self.assertRaises(exception.DBDuplicateEntry,
|
||||
method, {'foo': 20})
|
@ -351,7 +351,7 @@ def bm_interface_set_vif_uuid(context, if_id, vif_uuid):
|
||||
try:
|
||||
session.add(bm_interface)
|
||||
session.flush()
|
||||
except exception.DBError, e:
|
||||
except db_session.DBError, e:
|
||||
# TODO(deva): clean up when db layer raises DuplicateKeyError
|
||||
if str(e).find('IntegrityError') != -1:
|
||||
raise exception.NovaException(_("Baremetal interface %s "
|
||||
|
@ -19,8 +19,8 @@
|
||||
|
||||
"""Session Handling for SQLAlchemy backend."""
|
||||
|
||||
from nova.db.sqlalchemy import session as nova_session
|
||||
from nova.openstack.common import cfg
|
||||
from nova.openstack.common.db.sqlalchemy import session as nova_session
|
||||
from nova import paths
|
||||
|
||||
opts = [
|
||||
@ -38,11 +38,13 @@ CONF = cfg.CONF
|
||||
CONF.register_group(baremetal_group)
|
||||
CONF.register_opts(opts, baremetal_group)
|
||||
|
||||
CONF.import_opt('sqlite_db', 'nova.db.sqlalchemy.session')
|
||||
CONF.import_opt('sqlite_db', 'nova.openstack.common.db.sqlalchemy.session')
|
||||
|
||||
_ENGINE = None
|
||||
_MAKER = None
|
||||
|
||||
DBError = nova_session.DBError
|
||||
|
||||
|
||||
def get_session(autocommit=True, expire_on_commit=False):
|
||||
"""Return a SQLAlchemy session."""
|
||||
|
@ -25,6 +25,7 @@ from nova.compute import power_state
|
||||
from nova import context as nova_context
|
||||
from nova import exception
|
||||
from nova.openstack.common import cfg
|
||||
from nova.openstack.common.db.sqlalchemy import session as db_session
|
||||
from nova.openstack.common import importutils
|
||||
from nova.openstack.common import log as logging
|
||||
from nova import paths
|
||||
@ -266,7 +267,7 @@ class BareMetalDriver(driver.ComputeDriver):
|
||||
pm.state = baremetal_states.ERROR
|
||||
try:
|
||||
_update_state(context, node, instance, pm.state)
|
||||
except exception.DBError, e:
|
||||
except db_session.DBError, e:
|
||||
LOG.warning(_("Failed to update state record for "
|
||||
"baremetal node %s") % instance['uuid'])
|
||||
|
||||
|
@ -25,6 +25,7 @@ import os
|
||||
from nova.compute import instance_types
|
||||
from nova import exception
|
||||
from nova.openstack.common import cfg
|
||||
from nova.openstack.common.db.sqlalchemy import session as db_session
|
||||
from nova.openstack.common import fileutils
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.virt.baremetal import base
|
||||
@ -411,7 +412,7 @@ class PXE(base.NodeDriver):
|
||||
bm_utils.unlink_without_raise(get_pxe_config_file_path(instance))
|
||||
try:
|
||||
macs = self._collect_mac_addresses(context, node)
|
||||
except exception.DBError:
|
||||
except db_session.DBError:
|
||||
pass
|
||||
else:
|
||||
for mac in macs:
|
||||
|
@ -1,7 +1,7 @@
|
||||
[DEFAULT]
|
||||
|
||||
# The list of modules to copy from openstack-common
|
||||
modules=cfg,cliutils,context,excutils,eventlet_backdoor,fileutils,gettextutils,importutils,iniparser,jsonutils,local,lockutils,log,network_utils,notifier,plugin,policy,rootwrap,setup,timeutils,rpc,uuidutils,install_venv_common,flakes
|
||||
modules=cfg,cliutils,context,db,db.sqlalchemy,excutils,eventlet_backdoor,fileutils,gettextutils,importutils,iniparser,jsonutils,local,lockutils,log,network_utils,notifier,plugin,policy,rootwrap,setup,timeutils,rpc,uuidutils,install_venv_common,flakes
|
||||
|
||||
# The base module to hold the copy of openstack.common
|
||||
base=nova
|
||||
|
Loading…
Reference in New Issue
Block a user