Add an option to randomize LDAP urls list
Since LDAP is now readonly, the current behavior might be
unexpected. By randomizing the list, we assure a more gradual
failure scenario if the first server on the list (as specified
by the user) fails.
Change-Id: I23f31bd85443784013a6aa158d80c7aeeb343993
Closes-Bug: #1953622
Resolves: rhbz#2024602
(cherry picked from commit 36d57d2a83
)
This commit is contained in:
parent
7852ca24a4
commit
0a318bef48
@ -68,14 +68,32 @@ Define the destination LDAP server in the ``/etc/keystone/keystone.conf`` file:
|
|||||||
suffix = dc=example,dc=org
|
suffix = dc=example,dc=org
|
||||||
|
|
||||||
|
|
||||||
Multiple LDAP servers can be supplied to ``url`` to provide high-availability
|
Although it's not recommended (see note below), multiple LDAP servers can be
|
||||||
support for a single LDAP backend. To specify multiple LDAP servers, simply
|
supplied to ``url`` to provide high-availability support for a single LDAP
|
||||||
change the ``url`` option in the ``[ldap]`` section to be a list, separated by
|
backend. By default, these will be tried in order of apperance, but an
|
||||||
commas:
|
additional option, ``randomize_urls`` can be set to true, to randomize the
|
||||||
|
list in each process (when it starts). To specify multiple LDAP servers,
|
||||||
|
simply change the ``url`` option in the ``[ldap]`` section to be a list,
|
||||||
|
separated by commas:
|
||||||
|
|
||||||
.. code-block:: ini
|
.. code-block:: ini
|
||||||
|
|
||||||
url = "ldap://localhost,ldap://backup.localhost"
|
url = "ldap://localhost,ldap://backup.localhost"
|
||||||
|
randomize_urls = true
|
||||||
|
|
||||||
|
.. NOTE::
|
||||||
|
|
||||||
|
Failover mechanisms in the LDAP backend can cause delays when switching
|
||||||
|
over to the next working LDAP server. Randomizing the order in which the
|
||||||
|
servers are tried only makes the failure behavior not dependent on which
|
||||||
|
of the ordered servers fail. Individual processes can still be delayed or
|
||||||
|
time out, so this doesn't fix the issue at hand, but only makes the
|
||||||
|
failure mode more gradual. This behavior cannot be easily fixed inside the
|
||||||
|
service, because keystone would have to monitor the status of each LDAP
|
||||||
|
server, which is in fact a task for a load balancer. Because of this, it
|
||||||
|
is recommended to use a load balancer in front of the LDAP servers,
|
||||||
|
which can monitor the state of the cluster and instantly redirect
|
||||||
|
connections to the working LDAP server.
|
||||||
|
|
||||||
**Additional LDAP integration settings**
|
**Additional LDAP integration settings**
|
||||||
|
|
||||||
|
@ -24,6 +24,18 @@ as a comma separated string. The first URL to successfully bind is used for the
|
|||||||
connection.
|
connection.
|
||||||
"""))
|
"""))
|
||||||
|
|
||||||
|
randomize_urls = cfg.BoolOpt(
|
||||||
|
'randomize_urls',
|
||||||
|
default=False,
|
||||||
|
help=utils.fmt("""
|
||||||
|
Randomize the order of URLs in each keystone process. This makes the failure
|
||||||
|
behavior more gradual, since if the first server is down, a process/thread
|
||||||
|
will wait for the specified timeout before attempting a connection to a
|
||||||
|
server further down the list. This defaults to False, for backward
|
||||||
|
compatibility.
|
||||||
|
"""))
|
||||||
|
|
||||||
|
|
||||||
user = cfg.StrOpt(
|
user = cfg.StrOpt(
|
||||||
'user',
|
'user',
|
||||||
help=utils.fmt("""
|
help=utils.fmt("""
|
||||||
@ -479,6 +491,7 @@ use_auth_pool` is also enabled.
|
|||||||
GROUP_NAME = __name__.split('.')[-1]
|
GROUP_NAME = __name__.split('.')[-1]
|
||||||
ALL_OPTS = [
|
ALL_OPTS = [
|
||||||
url,
|
url,
|
||||||
|
randomize_urls,
|
||||||
user,
|
user,
|
||||||
password,
|
password,
|
||||||
suffix,
|
suffix,
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
import abc
|
import abc
|
||||||
import codecs
|
import codecs
|
||||||
import os.path
|
import os.path
|
||||||
|
import random
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import uuid
|
import uuid
|
||||||
@ -1166,7 +1167,12 @@ class BaseLdap(object):
|
|||||||
tree_dn = None
|
tree_dn = None
|
||||||
|
|
||||||
def __init__(self, conf):
|
def __init__(self, conf):
|
||||||
self.LDAP_URL = conf.ldap.url
|
if conf.ldap.randomize_urls:
|
||||||
|
urls = re.split(r'[\s,]+', conf.ldap.url)
|
||||||
|
random.shuffle(urls)
|
||||||
|
self.LDAP_URL = ','.join(urls)
|
||||||
|
else:
|
||||||
|
self.LDAP_URL = conf.ldap.url
|
||||||
self.LDAP_USER = conf.ldap.user
|
self.LDAP_USER = conf.ldap.user
|
||||||
self.LDAP_PASSWORD = conf.ldap.password
|
self.LDAP_PASSWORD = conf.ldap.password
|
||||||
self.LDAP_SCOPE = ldap_scope(conf.ldap.query_scope)
|
self.LDAP_SCOPE = ldap_scope(conf.ldap.query_scope)
|
||||||
|
@ -245,6 +245,33 @@ class MultiURLTests(unit.TestCase):
|
|||||||
ldap_connection = base_ldap.get_connection()
|
ldap_connection = base_ldap.get_connection()
|
||||||
self.assertEqual(urls, ldap_connection.conn.conn_pool.uri)
|
self.assertEqual(urls, ldap_connection.conn.conn_pool.uri)
|
||||||
|
|
||||||
|
@mock.patch.object(common_ldap.KeystoneLDAPHandler, 'simple_bind_s')
|
||||||
|
def test_multiple_urls_with_comma_randomized(self, mock_ldap_bind):
|
||||||
|
urls = ('ldap://localhost1,ldap://localhost2,'
|
||||||
|
'ldap://localhost3,ldap://localhost4,'
|
||||||
|
'ldap://localhost5,ldap://localhost6,'
|
||||||
|
'ldap://localhost7,ldap://localhost8,'
|
||||||
|
'ldap://localhost9,ldap://localhost0')
|
||||||
|
self.config_fixture.config(group='ldap', url=urls,
|
||||||
|
randomize_urls=True)
|
||||||
|
base_ldap = common_ldap.BaseLdap(CONF)
|
||||||
|
ldap_connection = base_ldap.get_connection()
|
||||||
|
|
||||||
|
# Sanity check
|
||||||
|
self.assertEqual(len(urls.split(',')), 10)
|
||||||
|
|
||||||
|
# Check that the list is split into the same number of URIs
|
||||||
|
self.assertEqual(len(urls.split(',')),
|
||||||
|
len(ldap_connection.conn.conn_pool.uri.split(',')))
|
||||||
|
|
||||||
|
# Check that the list is randomized
|
||||||
|
self.assertNotEqual(urls.split(','),
|
||||||
|
ldap_connection.conn.conn_pool.uri.split(','))
|
||||||
|
|
||||||
|
# Check that the list contains the same URIs
|
||||||
|
self.assertEqual(set(urls.split(',')),
|
||||||
|
set(ldap_connection.conn.conn_pool.uri.split(',')))
|
||||||
|
|
||||||
|
|
||||||
class LDAPConnectionTimeoutTest(unit.TestCase):
|
class LDAPConnectionTimeoutTest(unit.TestCase):
|
||||||
"""Test for Network Connection timeout on LDAP URL connection."""
|
"""Test for Network Connection timeout on LDAP URL connection."""
|
||||||
|
6
releasenotes/notes/randomize_urls-c0c19f48b2bfa299.yaml
Normal file
6
releasenotes/notes/randomize_urls-c0c19f48b2bfa299.yaml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
A new option 'randomize_urls' can be used to randomize the order in which
|
||||||
|
keystone connects to the LDAP servers in [ldap] 'url' list.
|
||||||
|
It is false by default.
|
Loading…
Reference in New Issue
Block a user