summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xbin/nova-manage9
-rw-r--r--nova/compute/instance_types.py3
-rw-r--r--nova/config.py6
-rw-r--r--nova/db/sqlalchemy/api.py15
-rw-r--r--nova/db/sqlalchemy/migration.py4
-rw-r--r--nova/db/sqlalchemy/models.py72
-rw-r--r--nova/exception.py14
-rw-r--r--nova/openstack/common/db/__init__.py16
-rw-r--r--nova/openstack/common/db/sqlalchemy/__init__.py16
-rw-r--r--nova/openstack/common/db/sqlalchemy/models.py103
-rw-r--r--nova/openstack/common/db/sqlalchemy/session.py (renamed from nova/db/sqlalchemy/session.py)93
-rw-r--r--nova/openstack/common/db/sqlalchemy/utils.py (renamed from nova/common/sqlalchemyutils.py)8
-rw-r--r--nova/test.py7
-rw-r--r--nova/tests/baremetal/db/test_bm_interface.py3
-rw-r--r--nova/tests/baremetal/db/test_bm_pxe_ip.py5
-rw-r--r--nova/tests/baremetal/test_pxe.py3
-rw-r--r--nova/tests/network/test_manager.py3
-rw-r--r--nova/tests/test_instance_types.py2
-rw-r--r--nova/tests/test_sqlalchemy.py129
-rw-r--r--nova/virt/baremetal/db/sqlalchemy/api.py2
-rw-r--r--nova/virt/baremetal/db/sqlalchemy/session.py6
-rw-r--r--nova/virt/baremetal/driver.py3
-rw-r--r--nova/virt/baremetal/pxe.py3
-rw-r--r--openstack-common.conf2
24 files changed, 263 insertions, 264 deletions
diff --git a/bin/nova-manage b/bin/nova-manage
index 82edf7389..c793fed16 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -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)
diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py
index 78129ee6b..30766fd9e 100644
--- a/nova/compute/instance_types.py
+++ b/nova/compute/instance_types.py
@@ -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()
diff --git a/nova/config.py b/nova/config.py
index 4095dba75..18147bdbb 100644
--- a/nova/config.py
+++ b/nova/config.py
@@ -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',
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 0154805ac..6a973e59f 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -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
diff --git a/nova/db/sqlalchemy/migration.py b/nova/db/sqlalchemy/migration.py
index dbc1ed432..421167bec 100644
--- a/nova/db/sqlalchemy/migration.py
+++ b/nova/db/sqlalchemy/migration.py
@@ -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:
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index b4c680ac0..78629e6c9 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -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):
diff --git a/nova/exception.py b/nova/exception.py
index 6bb8097c3..3b20b7e78 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -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")
diff --git a/nova/openstack/common/db/__init__.py b/nova/openstack/common/db/__init__.py
new file mode 100644
index 000000000..1b9b60dec
--- /dev/null
+++ b/nova/openstack/common/db/__init__.py
@@ -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.
diff --git a/nova/openstack/common/db/sqlalchemy/__init__.py b/nova/openstack/common/db/sqlalchemy/__init__.py
new file mode 100644
index 000000000..1b9b60dec
--- /dev/null
+++ b/nova/openstack/common/db/sqlalchemy/__init__.py
@@ -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.
diff --git a/nova/openstack/common/db/sqlalchemy/models.py b/nova/openstack/common/db/sqlalchemy/models.py
new file mode 100644
index 000000000..87ec7ccc3
--- /dev/null
+++ b/nova/openstack/common/db/sqlalchemy/models.py
@@ -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)
diff --git a/nova/db/sqlalchemy/session.py b/nova/openstack/common/db/sqlalchemy/session.py
index 28ec613c5..2125b006d 100644
--- a/nova/db/sqlalchemy/session.py
+++ b/nova/openstack/common/db/sqlalchemy/session.py
@@ -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:
diff --git a/nova/common/sqlalchemyutils.py b/nova/openstack/common/db/sqlalchemy/utils.py
index a186948ac..ef8af57ce 100644
--- a/nova/common/sqlalchemyutils.py
+++ b/nova/openstack/common/db/sqlalchemy/utils.py
@@ -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
diff --git a/nova/test.py b/nova/test.py
index b3f851dc4..e5c11081c 100644
--- a/nova/test.py
+++ b/nova/test.py
@@ -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')
diff --git a/nova/tests/baremetal/db/test_bm_interface.py b/nova/tests/baremetal/db/test_bm_interface.py
index 9f051ac9b..32beb1ce0 100644
--- a/nova/tests/baremetal/db/test_bm_interface.py
+++ b/nova/tests/baremetal/db/test_bm_interface.py
@@ -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
diff --git a/nova/tests/baremetal/db/test_bm_pxe_ip.py b/nova/tests/baremetal/db/test_bm_pxe_ip.py
index 9a93b46ad..9820f3af0 100644
--- a/nova/tests/baremetal/db/test_bm_pxe_ip.py
+++ b/nova/tests/baremetal/db/test_bm_pxe_ip.py
@@ -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)
diff --git a/nova/tests/baremetal/test_pxe.py b/nova/tests/baremetal/test_pxe.py
index 09f1079bf..e50462b0e 100644
--- a/nova/tests/baremetal/test_pxe.py
+++ b/nova/tests/baremetal/test_pxe.py
@@ -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()
diff --git a/nova/tests/network/test_manager.py b/nova/tests/network/test_manager.py
index 48183010f..9d467bcb1 100644
--- a/nova/tests/network/test_manager.py
+++ b/nova/tests/network/test_manager.py
@@ -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
diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py
index b70b96b7f..5abf62c3e 100644
--- a/nova/tests/test_instance_types.py
+++ b/nova/tests/test_instance_types.py
@@ -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
diff --git a/nova/tests/test_sqlalchemy.py b/nova/tests/test_sqlalchemy.py
deleted file mode 100644
index 5c7f4450b..000000000
--- a/nova/tests/test_sqlalchemy.py
+++ /dev/null
@@ -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})
diff --git a/nova/virt/baremetal/db/sqlalchemy/api.py b/nova/virt/baremetal/db/sqlalchemy/api.py
index 34bcd1229..198c06256 100644
--- a/nova/virt/baremetal/db/sqlalchemy/api.py
+++ b/nova/virt/baremetal/db/sqlalchemy/api.py
@@ -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 "
diff --git a/nova/virt/baremetal/db/sqlalchemy/session.py b/nova/virt/baremetal/db/sqlalchemy/session.py
index fcaf210a5..06d777354 100644
--- a/nova/virt/baremetal/db/sqlalchemy/session.py
+++ b/nova/virt/baremetal/db/sqlalchemy/session.py
@@ -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."""
diff --git a/nova/virt/baremetal/driver.py b/nova/virt/baremetal/driver.py
index 631a9a8c4..43af951fd 100644
--- a/nova/virt/baremetal/driver.py
+++ b/nova/virt/baremetal/driver.py
@@ -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'])
diff --git a/nova/virt/baremetal/pxe.py b/nova/virt/baremetal/pxe.py
index 5a6f58655..a169e13e5 100644
--- a/nova/virt/baremetal/pxe.py
+++ b/nova/virt/baremetal/pxe.py
@@ -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:
diff --git a/openstack-common.conf b/openstack-common.conf
index b0db41d51..29ed9d82f 100644
--- a/openstack-common.conf
+++ b/openstack-common.conf
@@ -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