summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2017-05-24 17:53:14 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2017-05-24 18:21:14 -0400
commit2166490a058c653a726372415cf0df03579e5542 (patch)
tree952b70662185f7a79b142614310013a588a20295
parentc23e571ff544c4d1e76d569c916842aa7d598c90 (diff)
downloadoslo-db-2166490a058c653a726372415cf0df03579e5542.tar.gz
Warn on URL without a drivername
Older installations may still be running with URLs of the form "mysql://user:pass@host/dbname", which defaults to the native MySQL driver that is not supported by eventlet. Warn that URLs should be qualified with an explicit driver, and in the case of MySQL that PyMySQL should be used. Change-Id: Ie973a43c8d056778d02703cf75bfc52a90027dea
-rw-r--r--oslo_db/sqlalchemy/engines.py20
-rw-r--r--oslo_db/tests/sqlalchemy/test_sqlalchemy.py46
2 files changed, 66 insertions, 0 deletions
diff --git a/oslo_db/sqlalchemy/engines.py b/oslo_db/sqlalchemy/engines.py
index 601be76..a9ac662 100644
--- a/oslo_db/sqlalchemy/engines.py
+++ b/oslo_db/sqlalchemy/engines.py
@@ -100,6 +100,24 @@ def _setup_logging(connection_debug=0):
logger.setLevel(logging.WARNING)
+def _vet_url(url):
+ if "+" not in url.drivername and not url.drivername.startswith("sqlite"):
+ if url.drivername.startswith("mysql"):
+ LOG.warning(
+ "URL %r does not contain a '+drivername' portion, "
+ "and will make use of a default driver. "
+ "A full dbname+drivername:// protocol is recommended. "
+ "For MySQL, it is strongly recommended that mysql+pymysql:// "
+ "be specified for maximum service compatibility", url
+ )
+ else:
+ LOG.warning(
+ "URL %r does not contain a '+drivername' portion, "
+ "and will make use of a default driver. "
+ "A full dbname+drivername:// protocol is recommended.", url
+ )
+
+
def create_engine(sql_connection, sqlite_fk=False, mysql_sql_mode=None,
idle_timeout=3600,
connection_debug=0, max_pool_size=None, max_overflow=None,
@@ -112,6 +130,8 @@ def create_engine(sql_connection, sqlite_fk=False, mysql_sql_mode=None,
url = sqlalchemy.engine.url.make_url(sql_connection)
+ _vet_url(url)
+
engine_args = {
"pool_recycle": idle_timeout,
'convert_unicode': True,
diff --git a/oslo_db/tests/sqlalchemy/test_sqlalchemy.py b/oslo_db/tests/sqlalchemy/test_sqlalchemy.py
index a37cdc6..bcbad45 100644
--- a/oslo_db/tests/sqlalchemy/test_sqlalchemy.py
+++ b/oslo_db/tests/sqlalchemy/test_sqlalchemy.py
@@ -664,6 +664,52 @@ class CreateEngineTest(oslo_test.BaseTestCase):
engines._thread_yield
)
+ def test_warn_on_missing_driver(self):
+
+ warnings = mock.Mock()
+
+ def warn_interpolate(msg, args):
+ # test the interpolation itself to ensure the password
+ # is concealed
+ warnings.warning(msg % args)
+
+ with mock.patch(
+ "oslo_db.sqlalchemy.engines.LOG.warning",
+ warn_interpolate):
+
+ engines._vet_url(
+ url.make_url("mysql://scott:tiger@some_host/some_db"))
+ engines._vet_url(url.make_url(
+ "mysql+mysqldb://scott:tiger@some_host/some_db"))
+ engines._vet_url(url.make_url(
+ "mysql+pymysql://scott:tiger@some_host/some_db"))
+ engines._vet_url(url.make_url(
+ "postgresql+psycopg2://scott:tiger@some_host/some_db"))
+ engines._vet_url(url.make_url(
+ "postgresql://scott:tiger@some_host/some_db"))
+
+ self.assertEqual(
+ [
+ mock.call.warning(
+ "URL mysql://scott:***@some_host/some_db does not contain "
+ "a '+drivername' portion, "
+ "and will make use of a default driver. "
+ "A full dbname+drivername:// protocol is recommended. "
+ "For MySQL, it is strongly recommended that "
+ "mysql+pymysql:// "
+ "be specified for maximum service compatibility",
+
+ ),
+ mock.call.warning(
+ "URL postgresql://scott:***@some_host/some_db does not "
+ "contain a '+drivername' portion, "
+ "and will make use of a default driver. "
+ "A full dbname+drivername:// protocol is recommended."
+ )
+ ],
+ warnings.mock_calls
+ )
+
class ProcessGuardTest(test_base.DbTestCase):
def test_process_guard(self):