389-ds-base/SOURCES/Issue-5789-Improve-ds-replc...

260 lines
12 KiB
Diff

From 4c16b43c714b0b3d1d7ba6cb65be00bd42a27f3f Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Tue, 6 Jun 2023 12:49:50 -0400
Subject: [PATCH] Issue 5789 - Improve ds-replcheck error handling
Description: When replication is not fully configured the tool outputs vague
messages. These should be cleaned up to indicate that
replication was not initialized. Also added healthcheck.
Relates: https://github.com/389ds/389-ds-base/issues/5789
Reviewed by: tbordaz, spichugi, progier (Thanks!!!)
---
ldap/admin/src/scripts/ds-replcheck | 35 +++++++++++--------
.../src/lib/replication/replTasks.jsx | 2 +-
src/cockpit/389-console/src/replication.jsx | 3 +-
src/lib389/lib389/cli_conf/replication.py | 7 +++-
src/lib389/lib389/config.py | 2 +-
src/lib389/lib389/lint.py | 10 ++++++
src/lib389/lib389/replica.py | 20 +++++++++--
7 files changed, 58 insertions(+), 21 deletions(-)
diff --git a/ldap/admin/src/scripts/ds-replcheck b/ldap/admin/src/scripts/ds-replcheck
index f411f357aa..efa13ffe81 100755
--- a/ldap/admin/src/scripts/ds-replcheck
+++ b/ldap/admin/src/scripts/ds-replcheck
@@ -1,7 +1,7 @@
#!/usr/bin/python3
# --- BEGIN COPYRIGHT BLOCK ---
-# Copyright (C) 2021 Red Hat, Inc.
+# Copyright (C) 2023 Red Hat, Inc.
# All rights reserved.
#
# License: GPL (version 3 or any later version).
@@ -216,17 +216,17 @@ def get_ruv_state(opts):
mtime = get_ruv_time(opts['supplier_ruv'], opts['rid'])
rtime = get_ruv_time(opts['replica_ruv'], opts['rid'])
if mtime == -1:
- repl_state = "Replication State: Replica ID ({}) not found in Supplier's RUV".format(opts['rid'])
+ repl_state = f"Replication State: Replica ID ({opts['rid']}) not found in Supplier's RUV"
elif rtime == -1:
- repl_state = "Replication State: Replica ID ({}) not found in Replica's RUV (not initialized?)".format(opts['rid'])
+ repl_state = f"Replication State: Replica ID ({opts['rid']}) not found in Replica's RUV (not initialized?)"
elif mtime == 0:
repl_state = "Replication State: Supplier has not seen any updates"
elif rtime == 0:
repl_state = "Replication State: Replica has not seen any changes from the Supplier"
elif mtime > rtime:
- repl_state = "Replication State: Replica is behind Supplier by: {} seconds".format(mtime - rtime)
+ repl_state = f"Replication State: Replica is behind Supplier by: {mtime - rtime} seconds"
elif mtime < rtime:
- repl_state = "Replication State: Replica is ahead of Supplier by: {} seconds".format(rtime - mtime)
+ repl_state = f"Replication State: Replica is ahead of Supplier by: {rtime - mtime} seconds"
else:
repl_state = "Replication State: Supplier and Replica are in perfect synchronization"
@@ -928,7 +928,7 @@ def check_for_diffs(mentries, mglue, rentries, rglue, report, opts):
return report
-def validate_suffix(ldapnode, suffix, hostname):
+def validate_suffix(ldapnode, suffix, hostname, port):
"""Validate that the suffix exists
:param ldapnode - The LDAP object
:param suffix - The suffix to validate
@@ -938,10 +938,11 @@ def validate_suffix(ldapnode, suffix, hostname):
try:
ldapnode.search_s(suffix, ldap.SCOPE_BASE)
except ldap.NO_SUCH_OBJECT:
- print("Error: Failed to validate suffix in {}. {} does not exist.".format(hostname, suffix))
+ print(f"Error: Failed to validate suffix in {hostname}:{port}. {suffix} " +
+ "does not exist. Replica might need to be initialized.")
return False
except ldap.LDAPError as e:
- print("Error: failed to validate suffix in {} ({}). ".format(hostname, str(e)))
+ print(f"Error: failed to validate suffix in {hostname}:{port} ({str(e)}). ")
return False
# Check suffix is replicated
@@ -949,10 +950,10 @@ def validate_suffix(ldapnode, suffix, hostname):
replica_filter = "(&(objectclass=nsds5replica)(nsDS5ReplicaRoot=%s))" % suffix
supplier_replica = ldapnode.search_s("cn=config",ldap.SCOPE_SUBTREE,replica_filter)
if (len(supplier_replica) != 1):
- print("Error: Failed to validate suffix in {}. {} is not replicated.".format(hostname, suffix))
+ print(f"Error: Failed to validate suffix in {hostname}:{port}. {suffix} is not replicated.")
return False
except ldap.LDAPError as e:
- print("Error: failed to validate suffix in {} ({}). ".format(hostname, str(e)))
+ print(f"Error: failed to validate suffix in {hostname}:{port} ({str(e)}). ")
return False
return True
@@ -1034,10 +1035,10 @@ def connect_to_replicas(opts):
# Validate suffix
if opts['verbose']:
print ("Validating suffix ...")
- if not validate_suffix(supplier, opts['suffix'], opts['mhost']):
+ if not validate_suffix(supplier, opts['suffix'], opts['mhost'], opts['mport']):
sys.exit(1)
- if not validate_suffix(replica,opts['suffix'], opts['rhost']):
+ if not validate_suffix(replica,opts['suffix'], opts['rhost'], opts['rport']):
sys.exit(1)
# Get the RUVs
@@ -1048,8 +1049,11 @@ def connect_to_replicas(opts):
if len(supplier_ruv) > 0:
opts['supplier_ruv'] = ensure_list_str(supplier_ruv[0][1]['nsds50ruv'])
else:
- print("Error: Supplier does not have an RUV entry")
+ print("Error: Supplier does not have an RUV entry. It might need to be initialized.")
sys.exit(1)
+ except ldap.NO_SUCH_OBJECT:
+ print("Error: Supplier does not have an RUV entry. It might need to be initialized.")
+ sys.exit(1)
except ldap.LDAPError as e:
print("Error: Failed to get Supplier RUV entry: {}".format(str(e)))
sys.exit(1)
@@ -1061,8 +1065,11 @@ def connect_to_replicas(opts):
if len(replica_ruv) > 0:
opts['replica_ruv'] = ensure_list_str(replica_ruv[0][1]['nsds50ruv'])
else:
- print("Error: Replica does not have an RUV entry")
+ print("Error: Replica does not have an RUV entry. It might need to be initialized.")
sys.exit(1)
+ except ldap.NO_SUCH_OBJECT:
+ print("Error: Replica does not have an RUV entry. It might need to be initialized.")
+ sys.exit(1)
except ldap.LDAPError as e:
print("Error: Failed to get Replica RUV entry: {}".format(str(e)))
sys.exit(1)
diff --git a/src/cockpit/389-console/src/lib/replication/replTasks.jsx b/src/cockpit/389-console/src/lib/replication/replTasks.jsx
index 9387af3258..d592995f88 100644
--- a/src/cockpit/389-console/src/lib/replication/replTasks.jsx
+++ b/src/cockpit/389-console/src/lib/replication/replTasks.jsx
@@ -345,7 +345,7 @@ export class ReplRUV extends React.Component {
if (localRID == "") {
localRUV =
- <div className="ds-indent">
+ <div className="ds-indent ds-margin-top">
<i>
There is no local RUV, the database might not have been initialized yet.
</i>
diff --git a/src/cockpit/389-console/src/replication.jsx b/src/cockpit/389-console/src/replication.jsx
index c2598c1181..76b8da486a 100644
--- a/src/cockpit/389-console/src/replication.jsx
+++ b/src/cockpit/389-console/src/replication.jsx
@@ -880,7 +880,8 @@ export class Replication extends React.Component {
})
.fail(err => {
const errMsg = JSON.parse(err);
- if (errMsg.desc !== "No such object") {
+ if (errMsg.desc !== "No such object" &&
+ !errMsg.desc.includes('There is no RUV for suffix')) {
this.props.addNotification(
"error",
`Error loading suffix RUV - ${errMsg.desc}`
diff --git a/src/lib389/lib389/cli_conf/replication.py b/src/lib389/lib389/cli_conf/replication.py
index 8a919da989..2c9501cdef 100644
--- a/src/lib389/lib389/cli_conf/replication.py
+++ b/src/lib389/lib389/cli_conf/replication.py
@@ -115,10 +115,15 @@ def _args_to_attrs(args):
#
def get_ruv(inst, basedn, log, args):
replicas = Replicas(inst)
- replica = replicas.get(args.suffix)
+ try:
+ replica = replicas.get(args.suffix)
+ except ldap.NO_SUCH_OBJECT:
+ raise ValueError(f"Suffix '{args.suffix}' is not configured for replication.")
ruv = replica.get_ruv()
ruv_dict = ruv.format_ruv()
ruvs = ruv_dict['ruvs']
+ if len(ruvs) == 0:
+ raise ValueError(f"There is no RUV for suffix {args.suffix}. Replica is not initialized.")
if args and args.json:
log.info(json.dumps({"type": "list", "items": ruvs}, indent=4))
else:
diff --git a/src/lib389/lib389/config.py b/src/lib389/lib389/config.py
index c178eb02f0..00d38463a0 100644
--- a/src/lib389/lib389/config.py
+++ b/src/lib389/lib389/config.py
@@ -209,7 +209,7 @@ def _lint_hr_timestamp(self):
yield report
def _lint_passwordscheme(self):
- allowed_schemes = ['SSHA512', 'PBKDF2_SHA256', 'GOST_YESCRYPT']
+ allowed_schemes = ['PBKDF2-SHA512', 'PBKDF2_SHA256', 'PBKDF2_SHA512', 'GOST_YESCRYPT']
u_password_scheme = self.get_attr_val_utf8('passwordStorageScheme')
u_root_scheme = self.get_attr_val_utf8('nsslapd-rootpwstoragescheme')
if u_root_scheme not in allowed_schemes or u_password_scheme not in allowed_schemes:
diff --git a/src/lib389/lib389/lint.py b/src/lib389/lib389/lint.py
index ce23d5c128..0219801f48 100644
--- a/src/lib389/lib389/lint.py
+++ b/src/lib389/lib389/lint.py
@@ -310,6 +310,16 @@
'fix': """Check if the consumer is running, and also check the errors log for more information."""
}
+DSREPLLE0006 = {
+ 'dsle': 'DSREPLLE0006',
+ 'severity': 'MEDIUM',
+ 'description': 'Replication has not been initilaized',
+ 'items': ['Replication'],
+ 'detail': """The replication for "SUFFIX" does not appear to be initialzied,
+because there is no RUV found for the suffix.""",
+ 'fix': """Initialize this replica from a primary supplier replica"""
+}
+
# Replication changelog
DSCLLE0001 = {
'dsle': 'DSCLLE0001',
diff --git a/src/lib389/lib389/replica.py b/src/lib389/lib389/replica.py
index f0c71cbebd..8b02433454 100644
--- a/src/lib389/lib389/replica.py
+++ b/src/lib389/lib389/replica.py
@@ -45,7 +45,7 @@
from lib389.idm.organizationalunit import OrganizationalUnits
from lib389.conflicts import ConflictEntries
from lib389.lint import (DSREPLLE0001, DSREPLLE0002, DSREPLLE0003, DSREPLLE0004,
- DSREPLLE0005, DSCLLE0001)
+ DSREPLLE0005, DSREPLLE0006, DSCLLE0001)
class ReplicaLegacy(object):
@@ -1207,6 +1207,20 @@ def _lint_conflicts(self):
report['check'] = f'replication:conflicts'
yield report
+ def _lint_no_ruv(self):
+ # No RUV means replica has not been initialized
+ replicas = Replicas(self._instance).list()
+ for replica in replicas:
+ ruv = replica.get_ruv()
+ ruv_dict = ruv.format_ruv()
+ ruvs = ruv_dict['ruvs']
+ suffix = replica.get_suffix()
+ if len(ruvs) == 0:
+ report = copy.deepcopy(DSREPLLE0006)
+ report['detail'] = report['detail'].replace('SUFFIX', suffix)
+ report['check'] = 'replication'
+ yield report
+
def _validate(self, rdn, properties, basedn):
(tdn, str_props) = super(Replica, self)._validate(rdn, properties, basedn)
# We override the tdn here. We use the MT for the suffix.
@@ -1587,8 +1601,8 @@ def get_ruv(self):
serverctrls=self._server_controls, clientctrls=self._client_controls,
escapehatch='i am sure')[0]
data = ensure_list_str(ent.getValues('nsds50ruv'))
- except IndexError:
- # There is no ruv entry, it's okay
+ except (IndexError, ldap.NO_SUCH_OBJECT):
+ # There are no ruv elements, it's okay
pass
return RUV(data)