summaryrefslogtreecommitdiffstats
path: root/openstack/common
diff options
context:
space:
mode:
authorChris Behrens <cbehrens@codestud.com>2013-02-22 16:13:27 +0000
committerChris Behrens <cbehrens@codestud.com>2013-02-22 16:47:30 +0000
commit743f15e0c72e6c5b51b5543f89ee2533d1d94845 (patch)
treefe0d862c3e73a6add5b41ccc4947105f8fcf73b7 /openstack/common
parent2b418be864a5aa5ba135f7651e83051cf3bf9ce6 (diff)
downloadoslo-743f15e0c72e6c5b51b5543f89ee2533d1d94845.tar.gz
oslo-743f15e0c72e6c5b51b5543f89ee2533d1d94845.tar.xz
oslo-743f15e0c72e6c5b51b5543f89ee2533d1d94845.zip
Clean up sqlalchemy exception code
Moves DB exceptions that can be shared between DB implementations into their own module. Adds DBDeadlock() exception wrapping. Nova has its own code for determining Deadlock and it's better to consolidate it with DBDuplicateKey checking. Change-Id: I108bd0da2a14d62e460a997b1472f0b65bfc9b95
Diffstat (limited to 'openstack/common')
-rw-r--r--openstack/common/db/exception.py45
-rw-r--r--openstack/common/db/sqlalchemy/session.py72
2 files changed, 87 insertions, 30 deletions
diff --git a/openstack/common/db/exception.py b/openstack/common/db/exception.py
new file mode 100644
index 0000000..8588d85
--- /dev/null
+++ b/openstack/common/db/exception.py
@@ -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 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.")
diff --git a/openstack/common/db/sqlalchemy/session.py b/openstack/common/db/sqlalchemy/session.py
index 96f582f..cbf40fd 100644
--- a/openstack/common/db/sqlalchemy/session.py
+++ b/openstack/common/db/sqlalchemy/session.py
@@ -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 openstack.common.db import exception
from openstack.common import log as logging
from openstack.common.gettextutils import _
from 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