summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2016-08-11 12:52:34 +0000
committerGerrit Code Review <review@openstack.org>2016-08-11 12:52:34 +0000
commite9a0e9d568fca39d88c64f203eeff8fc1eefcbf8 (patch)
treea171b663fe0dd93d549aebebd5de350121abdc23
parent4ea972553cfca4816865971d4f58ab101ab3aa1d (diff)
parentaf27831a9e6eceda192f63c1368d0aa1cc70c17e (diff)
downloadoslo-db-e9a0e9d568fca39d88c64f203eeff8fc1eefcbf8.tar.gz
Merge "Display full reason for backend not available"
-rw-r--r--oslo_db/sqlalchemy/provision.py20
-rw-r--r--oslo_db/sqlalchemy/test_base.py21
-rw-r--r--oslo_db/tests/sqlalchemy/test_fixtures.py83
-rw-r--r--oslo_db/tests/sqlalchemy/test_provision.py58
-rw-r--r--oslo_db/tests/sqlalchemy/test_utils.py8
5 files changed, 176 insertions, 14 deletions
diff --git a/oslo_db/sqlalchemy/provision.py b/oslo_db/sqlalchemy/provision.py
index ff6ebc0..7a1eaee 100644
--- a/oslo_db/sqlalchemy/provision.py
+++ b/oslo_db/sqlalchemy/provision.py
@@ -157,7 +157,6 @@ class Backend(object):
self.engine = None
self.impl = BackendImpl.impl(database_type)
self.current_dbs = set()
- Backend.backends_by_database_type[database_type] = self
@classmethod
def backend_for_database_type(cls, database_type):
@@ -167,7 +166,8 @@ class Backend(object):
try:
backend = cls.backends_by_database_type[database_type]
except KeyError:
- raise exception.BackendNotAvailable(database_type)
+ raise exception.BackendNotAvailable(
+ "Backend '%s' is unavailable: No such backend" % database_type)
else:
return backend._verify()
@@ -197,14 +197,15 @@ class Backend(object):
if not self.verified:
try:
eng = self._ensure_backend_available(self.url)
- except exception.BackendNotAvailable:
+ except exception.BackendNotAvailable as bne:
+ self._no_engine_reason = str(bne)
raise
else:
self.engine = eng
finally:
self.verified = True
if self.engine is None:
- raise exception.BackendNotAvailable(self.database_type)
+ raise exception.BackendNotAvailable(self._no_engine_reason)
return self
@classmethod
@@ -219,7 +220,9 @@ class Backend(object):
LOG.info(
_LI("The %(dbapi)s backend is unavailable: %(err)s"),
dict(dbapi=url.drivername, err=i_e))
- raise exception.BackendNotAvailable("No DBAPI installed")
+ raise exception.BackendNotAvailable(
+ "Backend '%s' is unavailable: No DBAPI installed" %
+ url.drivername)
else:
try:
conn = eng.connect()
@@ -231,7 +234,9 @@ class Backend(object):
_LI("The %(dbapi)s backend is unavailable: %(err)s"),
dict(dbapi=url.drivername, err=d_e)
)
- raise exception.BackendNotAvailable("Could not connect")
+ raise exception.BackendNotAvailable(
+ "Backend '%s' is unavailable: Could not connect" %
+ url.drivername)
else:
conn.close()
return eng
@@ -312,7 +317,8 @@ class Backend(object):
url = sa_url.make_url(url_str)
m = re.match(r'([^+]+?)(?:\+(.+))?$', url.drivername)
database_type = m.group(1)
- Backend(database_type, url)
+ Backend.backends_by_database_type[database_type] = \
+ Backend(database_type, url)
@six.add_metaclass(abc.ABCMeta)
diff --git a/oslo_db/sqlalchemy/test_base.py b/oslo_db/sqlalchemy/test_base.py
index 40b9c30..f25d266 100644
--- a/oslo_db/sqlalchemy/test_base.py
+++ b/oslo_db/sqlalchemy/test_base.py
@@ -65,9 +65,10 @@ class DbFixture(fixtures.Fixture):
testresources.tearDownResources,
self.test, self.test.resources, testresources._get_result()
)
- if not hasattr(self.test, 'db'):
- msg = "backend '%s' unavailable" % self.DRIVER
- if self.skip_on_unavailable_db:
+
+ if not self.test._has_db_resource():
+ msg = self.test._get_db_resource_not_available_reason()
+ if self.test.SKIP_ON_UNAVAILABLE_DB:
self.test.skip(msg)
else:
self.test.fail(msg)
@@ -98,9 +99,17 @@ class DbTestCase(test_base.BaseTestCase):
SCHEMA_SCOPE = None
SKIP_ON_UNAVAILABLE_DB = True
+ _db_not_available = {}
_schema_resources = {}
_database_resources = {}
+ def _get_db_resource_not_available_reason(self):
+ return self._db_not_available.get(self.FIXTURE.DRIVER, None)
+
+ def _has_db_resource(self):
+ return self._database_resources.get(
+ self.FIXTURE.DRIVER, None) is not None
+
def _resources_for_driver(self, driver, schema_scope, generate_schema):
# testresources relies on the identity and state of the
# TestResourceManager objects in play to correctly manage
@@ -110,12 +119,14 @@ class DbTestCase(test_base.BaseTestCase):
# so we have to code the TestResourceManager logic into the
# .resources attribute and ensure that the same set of test
# variables always produces the same TestResourceManager objects.
+
if driver not in self._database_resources:
try:
self._database_resources[driver] = \
provision.DatabaseResource(driver)
- except exception.BackendNotAvailable:
+ except exception.BackendNotAvailable as bne:
self._database_resources[driver] = None
+ self._db_not_available[driver] = str(bne)
database_resource = self._database_resources[driver]
if database_resource is None:
@@ -200,7 +211,7 @@ def backend_specific(*dialects):
if self.engine.name not in dialects:
msg = ('The test "%s" can be run '
'only on %s. Current engine is %s.')
- args = (reflection.get_callable_name(f), ' '.join(dialects),
+ args = (reflection.get_callable_name(f), ', '.join(dialects),
self.engine.name)
self.skip(msg % args)
else:
diff --git a/oslo_db/tests/sqlalchemy/test_fixtures.py b/oslo_db/tests/sqlalchemy/test_fixtures.py
new file mode 100644
index 0000000..5d63d69
--- /dev/null
+++ b/oslo_db/tests/sqlalchemy/test_fixtures.py
@@ -0,0 +1,83 @@
+# 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.
+
+import mock
+
+from oslo_db.sqlalchemy import provision
+from oslo_db.sqlalchemy import test_base
+from oslotest import base as oslo_test_base
+
+
+class BackendSkipTest(oslo_test_base.BaseTestCase):
+
+ def test_skip_no_dbapi(self):
+
+ class FakeDatabaseOpportunisticFixture(test_base.DbFixture):
+ DRIVER = 'postgresql'
+
+ class SomeTest(test_base.DbTestCase):
+ FIXTURE = FakeDatabaseOpportunisticFixture
+
+ def runTest(self):
+ pass
+
+ st = SomeTest()
+
+ # patch in replacement lookup dictionaries to avoid
+ # leaking from/to other tests
+ with mock.patch(
+ "oslo_db.sqlalchemy.provision."
+ "Backend.backends_by_database_type", {
+ "postgresql":
+ provision.Backend("postgresql", "postgresql://")}):
+ st._database_resources = {}
+ st._db_not_available = {}
+ st._schema_resources = {}
+
+ with mock.patch(
+ "sqlalchemy.create_engine",
+ mock.Mock(side_effect=ImportError())):
+
+ self.assertEqual([], st.resources)
+
+ ex = self.assertRaises(
+ self.skipException,
+ st.setUp
+ )
+
+ self.assertEqual(
+ "Backend 'postgresql' is unavailable: No DBAPI installed",
+ str(ex)
+ )
+
+ def test_skip_no_such_backend(self):
+
+ class FakeDatabaseOpportunisticFixture(test_base.DbFixture):
+ DRIVER = 'postgresql+nosuchdbapi'
+
+ class SomeTest(test_base.DbTestCase):
+ FIXTURE = FakeDatabaseOpportunisticFixture
+
+ def runTest(self):
+ pass
+
+ st = SomeTest()
+
+ ex = self.assertRaises(
+ self.skipException,
+ st.setUp
+ )
+
+ self.assertEqual(
+ "Backend 'postgresql+nosuchdbapi' is unavailable: No such backend",
+ str(ex)
+ )
diff --git a/oslo_db/tests/sqlalchemy/test_provision.py b/oslo_db/tests/sqlalchemy/test_provision.py
index 1ad586d..53d2303 100644
--- a/oslo_db/tests/sqlalchemy/test_provision.py
+++ b/oslo_db/tests/sqlalchemy/test_provision.py
@@ -10,7 +10,9 @@
# License for the specific language governing permissions and limitations
# under the License.
+import mock
from oslotest import base as oslo_test_base
+from sqlalchemy import exc as sa_exc
from sqlalchemy import inspect
from sqlalchemy import schema
from sqlalchemy import types
@@ -73,6 +75,62 @@ class DropAllObjectsTest(test_base.DbTestCase):
)
+class BackendNotAvailableTest(oslo_test_base.BaseTestCase):
+ def test_no_dbapi(self):
+ backend = provision.Backend(
+ "postgresql", "postgresql+nosuchdbapi://hostname/dsn")
+
+ with mock.patch(
+ "sqlalchemy.create_engine",
+ mock.Mock(side_effect=ImportError("nosuchdbapi"))):
+
+ # NOTE(zzzeek): Call and test the _verify function twice, as it
+ # exercises a different code path on subsequent runs vs.
+ # the first run
+ ex = self.assertRaises(
+ exception.BackendNotAvailable,
+ backend._verify)
+ self.assertEqual(
+ "Backend 'postgresql+nosuchdbapi' is unavailable: "
+ "No DBAPI installed", str(ex))
+
+ ex = self.assertRaises(
+ exception.BackendNotAvailable,
+ backend._verify)
+ self.assertEqual(
+ "Backend 'postgresql+nosuchdbapi' is unavailable: "
+ "No DBAPI installed", str(ex))
+
+ def test_cant_connect(self):
+ backend = provision.Backend(
+ "postgresql", "postgresql+nosuchdbapi://hostname/dsn")
+
+ with mock.patch(
+ "sqlalchemy.create_engine",
+ mock.Mock(return_value=mock.Mock(connect=mock.Mock(
+ side_effect=sa_exc.OperationalError(
+ "can't connect", None, None))
+ ))
+ ):
+
+ # NOTE(zzzeek): Call and test the _verify function twice, as it
+ # exercises a different code path on subsequent runs vs.
+ # the first run
+ ex = self.assertRaises(
+ exception.BackendNotAvailable,
+ backend._verify)
+ self.assertEqual(
+ "Backend 'postgresql+nosuchdbapi' is unavailable: "
+ "Could not connect", str(ex))
+
+ ex = self.assertRaises(
+ exception.BackendNotAvailable,
+ backend._verify)
+ self.assertEqual(
+ "Backend 'postgresql+nosuchdbapi' is unavailable: "
+ "Could not connect", str(ex))
+
+
class MySQLDropAllObjectsTest(
DropAllObjectsTest, test_base.MySQLOpportunisticTestCase):
pass
diff --git a/oslo_db/tests/sqlalchemy/test_utils.py b/oslo_db/tests/sqlalchemy/test_utils.py
index b3723a7..19f78ce 100644
--- a/oslo_db/tests/sqlalchemy/test_utils.py
+++ b/oslo_db/tests/sqlalchemy/test_utils.py
@@ -785,7 +785,9 @@ class TestConnectionUtils(test_utils.BaseTestCase):
exception.BackendNotAvailable,
provision.Backend._ensure_backend_available, self.connect_string
)
- self.assertEqual("Could not connect", str(exc))
+ self.assertEqual(
+ "Backend 'postgresql' is unavailable: "
+ "Could not connect", str(exc))
self.assertEqual(
"The postgresql backend is unavailable: %s" % err,
log.output.strip())
@@ -802,7 +804,9 @@ class TestConnectionUtils(test_utils.BaseTestCase):
exception.BackendNotAvailable,
provision.Backend._ensure_backend_available, self.connect_string
)
- self.assertEqual("No DBAPI installed", str(exc))
+ self.assertEqual(
+ "Backend 'postgresql' is unavailable: "
+ "No DBAPI installed", str(exc))
self.assertEqual(
"The postgresql backend is unavailable: Can't import "
"DBAPI module foobar", log.output.strip())