summaryrefslogtreecommitdiffstats
path: root/openstack/common
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2013-02-25 15:50:11 +0000
committerGerrit Code Review <review@openstack.org>2013-02-25 15:50:11 +0000
commit35e4f0696fbbcd1fd09aeb53c13d8bba82b60ad2 (patch)
treed3d989d922cfa5339806a7b99ed702ac049d2024 /openstack/common
parentc312fd6742c035677347a6987fb17b67e2c72087 (diff)
parent743f15e0c72e6c5b51b5543f89ee2533d1d94845 (diff)
downloadoslo-35e4f0696fbbcd1fd09aeb53c13d8bba82b60ad2.tar.gz
oslo-35e4f0696fbbcd1fd09aeb53c13d8bba82b60ad2.tar.xz
oslo-35e4f0696fbbcd1fd09aeb53c13d8bba82b60ad2.zip
Merge "Clean up sqlalchemy exception code"
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