389-ds-base/0034-Issue-7223-Add-upgrade-function-to-remove-ancestorid.patch
2026-07-21 07:46:19 -04:00

314 lines
12 KiB
Diff

From 41670301ccad5558296a3380a4974f7c0d4baede Mon Sep 17 00:00:00 2001
From: Viktor Ashirov <vashirov@redhat.com>
Date: Thu, 5 Feb 2026 12:17:06 +0100
Subject: [PATCH] Issue 7223 - Add upgrade function to remove ancestorid index
config entry
Description:
Add `upgrade_remove_ancestorid_index_config()` function that removes:
* ancestorid from `cn=default indexes`
* ancestorid index config entries from each backend's `cn=index`
Also remove ancestorid index configuration from template-dse.ldif.
Relates: https://github.com/389ds/389-ds-base/issues/7223
Reviewed by: @progier389, @tbordaz, @droideck (Thanks!)
---
.../healthcheck/health_system_indexes_test.py | 85 +++++++++++
ldap/ldif/template-dse.ldif.in | 8 --
ldap/servers/slapd/upgrade.c | 133 +++++++++++++++++-
3 files changed, 214 insertions(+), 12 deletions(-)
diff --git a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
index aea88e0e2..eb727b902 100644
--- a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
+++ b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
@@ -504,6 +504,91 @@ def test_upgrade_removes_parentid_scanlimit(topology_st):
log.info("Upgrade successfully removed nsIndexIDListScanLimit from parentid index")
+ # Verify idempotency - restart again and ensure no errors
+ log.info("Restart server again to verify idempotency (no errors on second run)")
+ standalone.restart()
+ # Verify the attribute is still absent
+ scanlimit_after_second = parentid_index.get_attr_vals_utf8("nsIndexIDListScanLimit")
+ assert not scanlimit_after_second, \
+ f"nsIndexIDListScanLimit should still be absent after second restart but found: {scanlimit_after_second}"
+ log.info("Idempotency verified - no issues on second restart")
+
+
+def test_upgrade_removes_ancestorid_index_config(topology_st):
+ """Check if upgrade function removes ancestorid index config entry
+
+ :id: 3f3d6e9b-75ac-4f0d-b2ce-7204e6eacd0a
+ :setup: Standalone instance
+ :steps:
+ 1. Create DS instance
+ 2. Stop the server
+ 3. Use DSEldif to add an ancestorid index config entry
+ 4. Start the server (triggers upgrade)
+ 5. Verify ancestorid index config entry is removed
+ :expectedresults:
+ 1. Success
+ 2. Success
+ 3. Success
+ 4. Success
+ 5. ancestorid index config entry is no longer present
+ """
+ from lib389.dseldif import DSEldif
+
+ standalone = topology_st.standalone
+ ANCESTORID_DN = "cn=ancestorid,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config"
+
+ log.info("Stop the server")
+ standalone.stop()
+
+ log.info("Add ancestorid index config entry using DSEldif")
+ dse_ldif = DSEldif(standalone)
+
+ # Create a fake ancestorid index entry
+ ancestorid_entry = [
+ "dn: {}\n".format(ANCESTORID_DN),
+ "objectClass: top\n",
+ "objectClass: nsIndex\n",
+ "cn: ancestorid\n",
+ "nsSystemIndex: true\n",
+ "nsIndexType: eq\n",
+ "nsMatchingRule: integerOrderingMatch\n",
+ "\n"
+ ]
+ dse_ldif.add_entry(ancestorid_entry)
+
+ # Verify it was added by re-reading dse.ldif
+ dse_ldif2 = DSEldif(standalone)
+ cn_value = dse_ldif2.get(ANCESTORID_DN, "cn")
+ assert cn_value is not None, "Failed to add ancestorid index config entry"
+ log.info(f"Added ancestorid index entry with cn: {cn_value}")
+
+ log.info("Start the server (triggers upgrade)")
+ standalone.start()
+
+ log.info("Verify ancestorid index config entry was removed by upgrade")
+ # Check via LDAP - the upgrade should have removed the entry
+ try:
+ ancestorid_index = Index(standalone, ANCESTORID_DN)
+ # If we can get the entry, it wasn't removed - this is a failure
+ cn_after = ancestorid_index.get_attr_vals_utf8("cn")
+ assert False, f"ancestorid index config entry should have been removed but still exists: {cn_after}"
+ except Exception as e:
+ # Entry should not exist - this is expected
+ log.info(f"ancestorid index config entry correctly removed (got exception: {e})")
+
+ log.info("Upgrade successfully removed ancestorid index config entry")
+
+ # Verify idempotency - restart again and ensure no errors
+ log.info("Restart server again to verify idempotency (no errors on second run)")
+ standalone.restart()
+ # Verify the entry is still absent
+ try:
+ ancestorid_index = Index(standalone, ANCESTORID_DN)
+ cn_after_second = ancestorid_index.get_attr_vals_utf8("cn")
+ assert False, f"ancestorid index config entry should still be absent after second restart but found: {cn_after_second}"
+ except Exception as e:
+ log.info(f"Idempotency verified - ancestorid still absent after second restart (got exception: {e})")
+
if __name__ == "__main__":
# Run isolated
diff --git a/ldap/ldif/template-dse.ldif.in b/ldap/ldif/template-dse.ldif.in
index bb8c71cd9..b6ab6f6c6 100644
--- a/ldap/ldif/template-dse.ldif.in
+++ b/ldap/ldif/template-dse.ldif.in
@@ -998,14 +998,6 @@ cn: aci
nssystemindex: true
nsindextype: pres
-dn: cn=ancestorid,cn=default indexes, cn=config,cn=ldbm database,cn=plugins,cn=config
-objectclass: top
-objectclass: nsIndex
-cn: ancestorid
-nssystemindex: true
-nsindextype: eq
-nsmatchingrule: integerOrderingMatch
-
dn: cn=cn,cn=default indexes, cn=config,cn=ldbm database,cn=plugins,cn=config
objectclass: top
objectclass: nsIndex
diff --git a/ldap/servers/slapd/upgrade.c b/ldap/servers/slapd/upgrade.c
index dcd16940b..6b1b012da 100644
--- a/ldap/servers/slapd/upgrade.c
+++ b/ldap/servers/slapd/upgrade.c
@@ -431,6 +431,126 @@ upgrade_remove_index_scanlimit(void)
return uresult;
}
+/*
+ * Remove ancestorid index configuration entry if present.
+ *
+ * The ancestorid index is special - it has no corresponding attribute type
+ * and should not have a DSE config entry. If an entry exists, remove it.
+ *
+ * This function removes:
+ * 1. The ancestorid entry from cn=default indexes (to prevent re-creation on startup)
+ * 2. The ancestorid entry from each backend's cn=index (if it exists)
+ */
+static upgrade_status
+upgrade_remove_ancestorid_index_config(void)
+{
+ struct slapi_pblock *pb = slapi_pblock_new();
+ Slapi_Entry **backends = NULL;
+ const char *be_base_dn = "cn=ldbm database,cn=plugins,cn=config";
+ const char *be_filter = "(objectclass=nsBackendInstance)";
+ upgrade_status uresult = UPGRADE_SUCCESS;
+ int rc;
+
+ /*
+ * First, remove ancestorid from cn=default indexes to prevent
+ * ldbm_instance_create_default_user_indexes() from re-creating it.
+ */
+ {
+ Slapi_PBlock *def_pb = slapi_pblock_new();
+ char *def_idx_dn = slapi_create_dn_string(
+ "cn=ancestorid,cn=default indexes,cn=config,%s", be_base_dn);
+
+ if (def_idx_dn) {
+ slapi_delete_internal_set_pb(
+ def_pb, def_idx_dn, NULL, NULL,
+ plugin_get_default_component_id(), 0);
+ slapi_delete_internal_pb(def_pb);
+ slapi_pblock_get(def_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+
+ if (rc == LDAP_SUCCESS) {
+ slapi_log_err(SLAPI_LOG_NOTICE, "upgrade_remove_ancestorid_index_config",
+ "Removed 'ancestorid' from default indexes.\n");
+ } else if (rc != LDAP_NO_SUCH_OBJECT) {
+ slapi_log_err(SLAPI_LOG_ERR, "upgrade_remove_ancestorid_index_config",
+ "Failed to remove 'ancestorid' from default indexes: error %d\n", rc);
+ }
+
+ slapi_ch_free_string(&def_idx_dn);
+ }
+ slapi_pblock_destroy(def_pb);
+ }
+
+ /* Search for all backend instances */
+ slapi_search_internal_set_pb(
+ pb, be_base_dn,
+ LDAP_SCOPE_ONELEVEL,
+ be_filter, NULL, 0, NULL, NULL,
+ plugin_get_default_component_id(), 0);
+ slapi_search_internal_pb(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &backends);
+
+ if (backends) {
+ for (size_t be_idx = 0; backends[be_idx] != NULL; be_idx++) {
+ const char *be_dn = slapi_entry_get_dn_const(backends[be_idx]);
+ const char *be_name = slapi_entry_attr_get_ref(backends[be_idx], "cn");
+ if (!be_dn || !be_name) {
+ continue;
+ }
+
+ struct slapi_pblock *idx_pb = slapi_pblock_new();
+ Slapi_Entry **idx_entries = NULL;
+ char *idx_dn = slapi_create_dn_string("cn=ancestorid,cn=index,%s",
+ be_dn);
+ char *idx_filter = "(objectclass=nsIndex)";
+
+ if (!idx_dn) {
+ slapi_pblock_destroy(idx_pb);
+ continue;
+ }
+
+ slapi_search_internal_set_pb(
+ idx_pb, idx_dn,
+ LDAP_SCOPE_BASE,
+ idx_filter, NULL, 0, NULL, NULL,
+ plugin_get_default_component_id(), 0);
+ slapi_search_internal_pb(idx_pb);
+ slapi_pblock_get(idx_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &idx_entries);
+
+ if (idx_entries && idx_entries[0]) {
+ /* ancestorid index entry exists - delete it */
+ Slapi_PBlock *del_pb = slapi_pblock_new();
+
+ slapi_delete_internal_set_pb(
+ del_pb, idx_dn, NULL, NULL,
+ plugin_get_default_component_id(), 0);
+ slapi_delete_internal_pb(del_pb);
+ slapi_pblock_get(del_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+
+ if (rc == LDAP_SUCCESS) {
+ slapi_log_err(SLAPI_LOG_NOTICE, "upgrade_remove_ancestorid_index_config",
+ "Removed 'ancestorid' index config entry in backend '%s'.\n",
+ be_name);
+ } else if (rc != LDAP_NO_SUCH_OBJECT) {
+ slapi_log_err(SLAPI_LOG_ERR, "upgrade_remove_ancestorid_index_config",
+ "Failed to remove 'ancestorid' index config entry in backend '%s': error %d\n",
+ be_name, rc);
+ }
+
+ slapi_pblock_destroy(del_pb);
+ }
+
+ slapi_ch_free_string(&idx_dn);
+ slapi_free_search_results_internal(idx_pb);
+ slapi_pblock_destroy(idx_pb);
+ }
+ }
+
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+
+ return uresult;
+}
+
/*
* Check if parentid/ancestorid indexes are missing the integerOrderingMatch
* matching rule.
@@ -445,7 +565,7 @@ upgrade_check_id_index_matching_rule(void)
Slapi_Entry **backends = NULL;
const char *be_base_dn = "cn=ldbm database,cn=plugins,cn=config";
const char *be_filter = "(objectclass=nsBackendInstance)";
- const char *attrs_to_check[] = {"parentid", "ancestorid", NULL};
+ const char *attrs_to_check[] = {"parentid", NULL};
upgrade_status uresult = UPGRADE_SUCCESS;
/* Search for all backend instances */
@@ -459,8 +579,9 @@ upgrade_check_id_index_matching_rule(void)
if (backends) {
for (size_t be_idx = 0; backends[be_idx] != NULL; be_idx++) {
+ const char *be_dn = slapi_entry_get_dn_const(backends[be_idx]);
const char *be_name = slapi_entry_attr_get_ref(backends[be_idx], "cn");
- if (!be_name) {
+ if (!be_dn || !be_name) {
continue;
}
@@ -469,8 +590,8 @@ upgrade_check_id_index_matching_rule(void)
const char *attr_name = attrs_to_check[attr_idx];
struct slapi_pblock *idx_pb = slapi_pblock_new();
Slapi_Entry **idx_entries = NULL;
- char *idx_dn = slapi_create_dn_string("cn=%s,cn=index,cn=%s,%s",
- attr_name, be_name, be_base_dn);
+ char *idx_dn = slapi_create_dn_string("cn=%s,cn=index,%s",
+ attr_name, be_dn);
char *idx_filter = "(objectclass=nsIndex)";
PRBool has_matching_rule = PR_FALSE;
@@ -754,6 +875,10 @@ upgrade_server(void)
return UPGRADE_FAILURE;
}
+ if (upgrade_remove_ancestorid_index_config() != UPGRADE_SUCCESS) {
+ return UPGRADE_FAILURE;
+ }
+
if (upgrade_check_id_index_matching_rule() != UPGRADE_SUCCESS) {
return UPGRADE_FAILURE;
}
--
2.52.0