Sync nova with oslo DB exception cleanup.
DB exceptions have moved to openstack/common/db/exception module so that they can be shared with multiple DB implementations. Deadlock checking was also added to oslo to consolidate with DuplicateKey checking. This allows us to clean up our _retry_on_deadlock decorator in sqlalchemy/api.py Fixes unrelated pep8 issue with duplicate test in test_compute also. Change-Id: I7e985b384d1ef345e0d67c919b84b4faff869699
This commit is contained in:
@@ -80,7 +80,7 @@ from nova import db
|
||||
from nova.db import migration
|
||||
from nova import exception
|
||||
from nova.openstack.common import cliutils
|
||||
from nova.openstack.common.db.sqlalchemy import session as db_session
|
||||
from nova.openstack.common.db import exception as db_exc
|
||||
from nova.openstack.common import importutils
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.openstack.common import rpc
|
||||
@@ -861,7 +861,7 @@ class InstanceTypeCommands(object):
|
||||
except exception.InstanceTypeNotFound:
|
||||
print _("Valid instance type name is required")
|
||||
sys.exit(1)
|
||||
except db_session.DBError, e:
|
||||
except db_exc.DBError, e:
|
||||
print _("DB Error: %s") % e
|
||||
sys.exit(2)
|
||||
except Exception:
|
||||
@@ -878,7 +878,7 @@ class InstanceTypeCommands(object):
|
||||
inst_types = instance_types.get_all_types()
|
||||
else:
|
||||
inst_types = instance_types.get_instance_type_by_name(name)
|
||||
except db_session.DBError, e:
|
||||
except db_exc.DBError, e:
|
||||
_db_error(e)
|
||||
if isinstance(inst_types.values()[0], dict):
|
||||
for k, v in inst_types.iteritems():
|
||||
@@ -909,7 +909,7 @@ class InstanceTypeCommands(object):
|
||||
ext_spec)
|
||||
print _("Key %(key)s set to %(value)s on instance"
|
||||
" type %(name)s") % locals()
|
||||
except db_session.DBError, e:
|
||||
except db_exc.DBError, e:
|
||||
_db_error(e)
|
||||
|
||||
@args('--name', dest='name', metavar='<name>',
|
||||
@@ -932,7 +932,7 @@ class InstanceTypeCommands(object):
|
||||
key)
|
||||
|
||||
print _("Key %(key)s on instance type %(name)s unset") % locals()
|
||||
except db_session.DBError, e:
|
||||
except db_exc.DBError, e:
|
||||
_db_error(e)
|
||||
|
||||
|
||||
|
||||
45
nova/openstack/common/db/exception.py
Normal file
45
nova/openstack/common/db/exception.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# 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.
|
||||
|
||||
"""DB related custom exceptions."""
|
||||
|
||||
from nova.openstack.common.gettextutils import _
|
||||
|
||||
|
||||
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 DBDeadlock(DBError):
|
||||
def __init__(self, inner_exception=None):
|
||||
super(DBDeadlock, self).__init__(inner_exception)
|
||||
|
||||
|
||||
class DBInvalidUnicodeParameter(Exception):
|
||||
message = _("Invalid Parameter: "
|
||||
"Unicode is not supported by the current database.")
|
||||
@@ -246,12 +246,13 @@ import time
|
||||
|
||||
from eventlet import greenthread
|
||||
from oslo.config import cfg
|
||||
from sqlalchemy.exc import DisconnectionError, OperationalError, IntegrityError
|
||||
from sqlalchemy import exc as sqla_exc
|
||||
import sqlalchemy.interfaces
|
||||
import sqlalchemy.orm
|
||||
from sqlalchemy.pool import NullPool, StaticPool
|
||||
from sqlalchemy.sql.expression import literal_column
|
||||
|
||||
from nova.openstack.common.db import exception
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.openstack.common.gettextutils import _
|
||||
from nova.openstack.common import timeutils
|
||||
@@ -327,25 +328,6 @@ 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:
|
||||
#
|
||||
@@ -364,7 +346,7 @@ class InvalidUnicodeParameter(Exception):
|
||||
# 'c1'")
|
||||
# N columns - (IntegrityError) (1062, "Duplicate entry 'values joined
|
||||
# with -' for key 'name_of_our_constraint'")
|
||||
_RE_DB = {
|
||||
_DUP_KEY_RE_DB = {
|
||||
"sqlite": re.compile(r"^.*columns?([^)]+)(is|are)\s+not\s+unique$"),
|
||||
"postgresql": re.compile(r"^.*duplicate\s+key.*\"([^\"]+)\"\s*\n.*$"),
|
||||
"mysql": re.compile(r"^.*\(1062,.*'([^\']+)'\"\)$")
|
||||
@@ -390,7 +372,7 @@ def raise_if_duplicate_entry_error(integrity_error, engine_name):
|
||||
if engine_name not in ["mysql", "sqlite", "postgresql"]:
|
||||
return
|
||||
|
||||
m = _RE_DB[engine_name].match(integrity_error.message)
|
||||
m = _DUP_KEY_RE_DB[engine_name].match(integrity_error.message)
|
||||
if not m:
|
||||
return
|
||||
columns = m.group(1)
|
||||
@@ -399,7 +381,32 @@ 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 DBDuplicateEntry(columns, integrity_error)
|
||||
raise exception.DBDuplicateEntry(columns, integrity_error)
|
||||
|
||||
|
||||
# NOTE(comstud): In current versions of DB backends, Deadlock violation
|
||||
# messages follow the structure:
|
||||
#
|
||||
# mysql:
|
||||
# (OperationalError) (1213, 'Deadlock found when trying to get lock; try '
|
||||
# 'restarting transaction') <query_str> <query_args>
|
||||
_DEADLOCK_RE_DB = {
|
||||
"mysql": re.compile(r"^.*\(1213, 'Deadlock.*")
|
||||
}
|
||||
|
||||
|
||||
def raise_if_deadlock_error(operational_error, engine_name):
|
||||
"""
|
||||
Raise DBDeadlock exception if OperationalError contains a Deadlock
|
||||
condition.
|
||||
"""
|
||||
re = _DEADLOCK_RE_DB.get(engine_name)
|
||||
if re is None:
|
||||
return
|
||||
m = re.match(operational_error.message)
|
||||
if not m:
|
||||
return
|
||||
raise exception.DBDeadlock(operational_error)
|
||||
|
||||
|
||||
def wrap_db_error(f):
|
||||
@@ -407,21 +414,26 @@ def wrap_db_error(f):
|
||||
try:
|
||||
return f(*args, **kwargs)
|
||||
except UnicodeEncodeError:
|
||||
raise InvalidUnicodeParameter()
|
||||
raise exception.DBInvalidUnicodeParameter()
|
||||
# note(boris-42): We should catch unique constraint violation and
|
||||
# wrap it by our own DBDuplicateEntry exception. Unique constraint
|
||||
# violation is wrapped by IntegrityError.
|
||||
except IntegrityError, e:
|
||||
except sqla_exc.OperationalError, e:
|
||||
raise_if_deadlock_error(e, get_engine().name)
|
||||
# NOTE(comstud): A lot of code is checking for OperationalError
|
||||
# so let's not wrap it for now.
|
||||
raise
|
||||
except sqla_exc.IntegrityError, e:
|
||||
# note(boris-42): SqlAlchemy doesn't unify errors from different
|
||||
# DBs so we must do this. Also in some tables (for example
|
||||
# instance_types) there are more than one unique constraint. This
|
||||
# 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 DBError(e)
|
||||
raise exception.DBError(e)
|
||||
except Exception, e:
|
||||
LOG.exception(_('DB exception wrapped.'))
|
||||
raise DBError(e)
|
||||
raise exception.DBError(e)
|
||||
_wrap.func_name = f.func_name
|
||||
return _wrap
|
||||
|
||||
@@ -471,7 +483,7 @@ def ping_listener(dbapi_conn, connection_rec, connection_proxy):
|
||||
except dbapi_conn.OperationalError, ex:
|
||||
if ex.args[0] in (2006, 2013, 2014, 2045, 2055):
|
||||
LOG.warn(_('Got mysql server has gone away: %s'), ex)
|
||||
raise DisconnectionError("Database server went away")
|
||||
raise sqla_exc.DisconnectionError("Database server went away")
|
||||
else:
|
||||
raise
|
||||
|
||||
@@ -532,7 +544,7 @@ def create_engine(sql_connection):
|
||||
|
||||
try:
|
||||
engine.connect()
|
||||
except OperationalError, e:
|
||||
except sqla_exc.OperationalError, e:
|
||||
if not is_db_connection_error(e.args[0]):
|
||||
raise
|
||||
|
||||
@@ -548,7 +560,7 @@ def create_engine(sql_connection):
|
||||
try:
|
||||
engine.connect()
|
||||
break
|
||||
except OperationalError, e:
|
||||
except sqla_exc.OperationalError, e:
|
||||
if (remaining != 'infinite' and remaining == 0) or \
|
||||
not is_db_connection_error(e.args[0]):
|
||||
raise
|
||||
|
||||
@@ -18,7 +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.openstack.common.db import exception as db_exc
|
||||
from nova.tests.baremetal.db import base
|
||||
from nova.virt.baremetal import db
|
||||
|
||||
@@ -28,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(db_session.DBError,
|
||||
self.assertRaises(db_exc.DBError,
|
||||
db.bm_interface_create,
|
||||
self.context, 2, '11:11:11:11:11:11', '0x2', 2)
|
||||
# succeed after delete pif1
|
||||
|
||||
@@ -18,7 +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.openstack.common.db import exception as db_exc
|
||||
from nova.tests.baremetal.db import base
|
||||
from nova.tests.baremetal.db import utils
|
||||
from nova.virt.baremetal import db
|
||||
@@ -51,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(db_session.DBError,
|
||||
self.assertRaises(db_exc.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(db_session.DBError,
|
||||
self.assertRaises(db_exc.DBError,
|
||||
db.bm_pxe_ip_create_direct,
|
||||
self.context, i)
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ from oslo.config import cfg
|
||||
from testtools import matchers
|
||||
|
||||
from nova import exception
|
||||
from nova.openstack.common.db.sqlalchemy import session as db_session
|
||||
from nova.openstack.common.db import exception as db_exc
|
||||
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
|
||||
@@ -529,7 +529,7 @@ class PXEPublicMethodsTestCase(BareMetalPXETestCase):
|
||||
AndRaise(exception.NovaException)
|
||||
bm_utils.unlink_without_raise(pxe_path)
|
||||
self.driver._collect_mac_addresses(self.context, self.node).\
|
||||
AndRaise(db_session.DBError)
|
||||
AndRaise(db_exc.DBError)
|
||||
bm_utils.rmtree_without_raise(
|
||||
os.path.join(CONF.baremetal.tftp_root, 'fake-uuid'))
|
||||
self.mox.ReplayAll()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[DEFAULT]
|
||||
|
||||
# The list of modules to copy from openstack-common
|
||||
modules=cliutils,context,db,db.api,db.sqlalchemy,excutils,eventlet_backdoor,fileutils,gettextutils,importutils,jsonutils,local,lockutils,log,network_utils,notifier,plugin,policy,rootwrap,setup,timeutils,rpc,uuidutils,install_venv_common,flakes,version,processutils
|
||||
modules=cliutils,context,db,excutils,eventlet_backdoor,fileutils,gettextutils,importutils,jsonutils,local,lockutils,log,network_utils,notifier,plugin,policy,rootwrap,setup,timeutils,rpc,uuidutils,install_venv_common,flakes,version,processutils
|
||||
|
||||
# The base module to hold the copy of openstack.common
|
||||
base=nova
|
||||
|
||||
Reference in New Issue
Block a user