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