Merge "Allow connection query string to be passed separately."

This commit is contained in:
Zuul 2018-02-20 22:02:00 +00:00 committed by Gerrit Code Review
commit 9c6695969f
5 changed files with 110 additions and 2 deletions

View File

@ -146,6 +146,11 @@ database_opts = [
'error before error is '
'raised. Set to -1 to specify an infinite retry '
'count.'),
cfg.StrOpt('connection_parameters',
default='',
help='Optional URL parameters to append onto the connection '
'URL at connect time; specify as '
'param1=value1&param2=value2&...'),
]

View File

@ -149,7 +149,8 @@ class _TransactionFactory(object):
'thread_checkin': _Default(True),
'json_serializer': _Default(None),
'json_deserializer': _Default(None),
'logging_name': _Default(None)
'logging_name': _Default(None),
'connection_parameters': _Default(None)
}
self._maker_cfg = {
'expire_on_commit': _Default(False),
@ -219,6 +220,9 @@ class _TransactionFactory(object):
:param connection_debug: engine logging level, defaults to 0. set to
50 for INFO, 100 for DEBUG.
:param connection_parameters: additional parameters to append onto the
database URL query string, pass as "param1=value1&param2=value2&..."
:param max_pool_size: max size of connection pool, uses CONF for
default

View File

@ -104,6 +104,21 @@ def _setup_logging(connection_debug=0):
logger.setLevel(logging.WARNING)
def _extend_url_parameters(url, connection_parameters):
for key, value in six.moves.urllib.parse.parse_qs(
connection_parameters).items():
if key in url.query:
existing = url.query[key]
if not isinstance(existing, list):
url.query[key] = existing = utils.to_list(existing)
existing.extend(value)
value = existing
else:
url.query[key] = value
if len(value) == 1:
url.query[key] = value[0]
def _vet_url(url):
if "+" not in url.drivername and not url.drivername.startswith("sqlite"):
if url.drivername.startswith("mysql"):
@ -132,11 +147,14 @@ def create_engine(sql_connection, sqlite_fk=False, mysql_sql_mode=None,
connection_trace=False, max_retries=10, retry_interval=10,
thread_checkin=True, logging_name=None,
json_serializer=None,
json_deserializer=None):
json_deserializer=None, connection_parameters=None):
"""Return a new SQLAlchemy engine."""
url = sqlalchemy.engine.url.make_url(sql_connection)
if connection_parameters:
_extend_url_parameters(url, connection_parameters)
_vet_url(url)
engine_args = {

View File

@ -213,6 +213,79 @@ class FakeDB2Engine(object):
pass
class QueryParamTest(test_base.DbTestCase):
def _fixture(self):
from sqlalchemy import create_engine
def _mock_create_engine(*arg, **kw):
return create_engine("sqlite://")
return mock.patch(
"oslo_db.sqlalchemy.engines.sqlalchemy.create_engine",
side_effect=_mock_create_engine)
def test_add_assorted_params(self):
with self._fixture() as ce:
engines.create_engine(
"mysql+pymysql://foo:bar@bat",
connection_parameters="foo=bar&bat=hoho&bat=param2")
self.assertEqual(
ce.mock_calls[0][1][0].query,
{'bat': ['hoho', 'param2'], 'foo': 'bar'}
)
def test_add_no_params(self):
with self._fixture() as ce:
engines.create_engine(
"mysql+pymysql://foo:bar@bat")
self.assertEqual(
ce.mock_calls[0][1][0].query,
{}
)
def test_combine_params(self):
with self._fixture() as ce:
engines.create_engine(
"mysql+pymysql://foo:bar@bat/"
"?charset=utf8&param_file=tripleo.cnf",
connection_parameters="plugin=sqlalchemy_collectd&"
"collectd_host=127.0.0.1&"
"bind_host=192.168.1.5")
self.assertEqual(
ce.mock_calls[0][1][0].query,
{
'bind_host': '192.168.1.5',
'charset': 'utf8',
'collectd_host': '127.0.0.1',
'param_file': 'tripleo.cnf',
'plugin': 'sqlalchemy_collectd'
}
)
def test_combine_multi_params(self):
with self._fixture() as ce:
engines.create_engine(
"mysql+pymysql://foo:bar@bat/"
"?charset=utf8&param_file=tripleo.cnf&plugin=connmon",
connection_parameters="plugin=sqlalchemy_collectd&"
"collectd_host=127.0.0.1&"
"bind_host=192.168.1.5")
self.assertEqual(
ce.mock_calls[0][1][0].query,
{
'bind_host': '192.168.1.5',
'charset': 'utf8',
'collectd_host': '127.0.0.1',
'param_file': 'tripleo.cnf',
'plugin': ['connmon', 'sqlalchemy_collectd']
}
)
class MySQLDefaultModeTestCase(test_base.MySQLOpportunisticTestCase):
def test_default_is_traditional(self):
with self.engine.connect() as conn:
@ -357,6 +430,7 @@ class EngineFacadeTestCase(oslo_test.BaseTestCase):
thread_checkin=mock.ANY,
json_serializer=None,
json_deserializer=None,
connection_parameters='',
logging_name=mock.ANY,
)
get_maker.assert_called_once_with(engine=create_engine(),

View File

@ -0,0 +1,7 @@
---
features:
- |
Added new option connection_parameters which allows SQLAlchemy query
parameters to be stated separately from the URL itself, to allow
URL-persistence schemes like Nova cells to use controller-local
query parameters that aren't broadcast to all other servers.