Blame SOURCES/0010-Issue-4449-dsconf-replication-monitor-fails-to-retri.patch

27c138
From 2a2773d4bf8553ba64b396d567fe05506b22c94c Mon Sep 17 00:00:00 2001
27c138
From: progier389 <72748589+progier389@users.noreply.github.com>
27c138
Date: Tue, 24 Nov 2020 19:22:49 +0100
27c138
Subject: [PATCH] Issue 4449 - dsconf replication monitor fails to retrieve
27c138
 database RUV - consumer (Unavailable) (#4451)
27c138
27c138
Bug Description:
27c138
27c138
"dsconf replication monitor" fails to retrieve database RUV entry from consumer and this
27c138
appears into the Cockpit web UI too.
27c138
The problem is that the bind credentials are not rightly propagated when trying to get
27c138
the consumers agreement status.  Then supplier credntials are used instead  and RUV
27c138
is searched anonymously because there is no bind dn in ldapi case.
27c138
27c138
Fix Description:
27c138
27c138
- Propagates the bind credentials when computing agreement status
27c138
- Add a credential cache because now a replica password could get asked several times:
27c138
    when discovering the topology and
27c138
    when getting the agreement maxcsn
27c138
- No testcase in 1.4.3 branch as the file modfied in master does not exists
27c138
27c138
- Add a comment about nonlocal keyword
27c138
27c138
Relates: #4449
27c138
27c138
Reviewers:
27c138
  firstyear
27c138
  droideck
27c138
  mreynolds
27c138
27c138
Issue 4449: Add a comment about nonlocal keyword
27c138
27c138
(cherry picked from commit 73ee04fa12cd1de3a5e47c109e79e31c1aaaa2ab)
27c138
---
27c138
 src/lib389/lib389/cli_conf/replication.py | 13 +++++++++++--
27c138
 src/lib389/lib389/replica.py              | 16 ++++++++++++----
27c138
 2 files changed, 23 insertions(+), 6 deletions(-)
27c138
27c138
diff --git a/src/lib389/lib389/cli_conf/replication.py b/src/lib389/lib389/cli_conf/replication.py
27c138
index 9dbaa320a..248972cba 100644
27c138
--- a/src/lib389/lib389/cli_conf/replication.py
27c138
+++ b/src/lib389/lib389/cli_conf/replication.py
27c138
@@ -369,9 +369,16 @@ def set_repl_config(inst, basedn, log, args):
27c138
 
27c138
 def get_repl_monitor_info(inst, basedn, log, args):
27c138
     connection_data = dsrc_to_repl_monitor(DSRC_HOME, log)
27c138
+    credentials_cache = {}
27c138
 
27c138
     # Additional details for the connections to the topology
27c138
     def get_credentials(host, port):
27c138
+        # credentials_cache is nonlocal to refer to the instance
27c138
+        # from enclosing function (get_repl_monitor_info)`
27c138
+        nonlocal credentials_cache
27c138
+        key = f'{host}:{port}'
27c138
+        if key in credentials_cache:
27c138
+            return credentials_cache[key]
27c138
         found = False
27c138
         if args.connections:
27c138
             connections = args.connections
27c138
@@ -406,8 +413,10 @@ def get_repl_monitor_info(inst, basedn, log, args):
27c138
             binddn = input(f'\nEnter a bind DN for {host}:{port}: ').rstrip()
27c138
             bindpw = getpass(f"Enter a password for {binddn} on {host}:{port}: ").rstrip()
27c138
 
27c138
-        return {"binddn": binddn,
27c138
-                "bindpw": bindpw}
27c138
+        credentials = {"binddn": binddn,
27c138
+                       "bindpw": bindpw}
27c138
+        credentials_cache[key] = credentials
27c138
+        return credentials
27c138
 
27c138
     repl_monitor = ReplicationMonitor(inst)
27c138
     report_dict = repl_monitor.generate_report(get_credentials, args.json)
27c138
diff --git a/src/lib389/lib389/replica.py b/src/lib389/lib389/replica.py
27c138
index c2ad2104d..3d89e61fb 100644
27c138
--- a/src/lib389/lib389/replica.py
27c138
+++ b/src/lib389/lib389/replica.py
27c138
@@ -2487,9 +2487,10 @@ class ReplicationMonitor(object):
27c138
         else:
27c138
             self._log = logging.getLogger(__name__)
27c138
 
27c138
-    def _get_replica_status(self, instance, report_data, use_json):
27c138
+    def _get_replica_status(self, instance, report_data, use_json, get_credentials=None):
27c138
         """Load all of the status data to report
27c138
         and add new hostname:port pairs for future processing
27c138
+        :type get_credentials: function
27c138
         """
27c138
 
27c138
         replicas_status = []
27c138
@@ -2503,6 +2504,13 @@ class ReplicationMonitor(object):
27c138
             for agmt in agmts.list():
27c138
                 host = agmt.get_attr_val_utf8_l("nsds5replicahost")
27c138
                 port = agmt.get_attr_val_utf8_l("nsds5replicaport")
27c138
+                if get_credentials is not None:
27c138
+                    credentials = get_credentials(host, port)
27c138
+                    binddn = credentials["binddn"]
27c138
+                    bindpw = credentials["bindpw"]
27c138
+                else:
27c138
+                    binddn = instance.binddn
27c138
+                    bindpw = instance.bindpw
27c138
                 protocol = agmt.get_attr_val_utf8_l('nsds5replicatransportinfo')
27c138
                 # Supply protocol here because we need it only for connection
27c138
                 # and agreement status is already preformatted for the user output
27c138
@@ -2510,9 +2518,9 @@ class ReplicationMonitor(object):
27c138
                 if consumer not in report_data:
27c138
                     report_data[f"{consumer}:{protocol}"] = None
27c138
                 if use_json:
27c138
-                    agmts_status.append(json.loads(agmt.status(use_json=True)))
27c138
+                    agmts_status.append(json.loads(agmt.status(use_json=True, binddn=binddn, bindpw=bindpw)))
27c138
                 else:
27c138
-                    agmts_status.append(agmt.status())
27c138
+                    agmts_status.append(agmt.status(binddn=binddn, bindpw=bindpw))
27c138
             replicas_status.append({"replica_id": replica_id,
27c138
                                     "replica_root": replica_root,
27c138
                                     "replica_status": "Available",
27c138
@@ -2535,7 +2543,7 @@ class ReplicationMonitor(object):
27c138
         initial_inst_key = f"{self._instance.config.get_attr_val_utf8_l('nsslapd-localhost')}:{self._instance.config.get_attr_val_utf8_l('nsslapd-port')}"
27c138
         # Do this on an initial instance to get the agreements to other instances
27c138
         try:
27c138
-            report_data[initial_inst_key] = self._get_replica_status(self._instance, report_data, use_json)
27c138
+            report_data[initial_inst_key] = self._get_replica_status(self._instance, report_data, use_json, get_credentials)
27c138
         except ldap.LDAPError as e:
27c138
             self._log.debug(f"Connection to consumer ({supplier_hostname}:{supplier_port}) failed, error: {e}")
27c138
             report_data[initial_inst_key] = [{"replica_status": f"Unavailable - {e.args[0]['desc']}"}]
27c138
-- 
27c138
2.26.2
27c138