diff --git a/dirsrvtests/tests/suites/indexes/entryrdn_test.py b/dirsrvtests/tests/suites/indexes/entryrdn_test.py index 49535f8abe..0f1f8f79ff 100644 --- a/dirsrvtests/tests/suites/indexes/entryrdn_test.py +++ b/dirsrvtests/tests/suites/indexes/entryrdn_test.py @@ -11,11 +11,16 @@ import pytest import ldap import logging +from lib389 import Entry from lib389._constants import DEFAULT_BENAME, DEFAULT_SUFFIX -from lib389.backend import Backends -from lib389.idm.user import UserAccounts +from lib389.backend import Backends, Backend +from lib389.mappingTree import MappingTrees +from lib389.configurations.sample import create_base_domain +from lib389.idm.domain import Domain +from lib389.idm.user import UserAccounts, UserAccount from lib389.idm.organizationalunit import OrganizationalUnits from lib389.topologies import topology_m2 as topo_m2 +from lib389.topologies import topology_st from lib389.agreement import Agreements from lib389.utils import ds_is_older, ensure_bytes from lib389.tasks import Tasks,ExportTask, ImportTask @@ -140,7 +145,107 @@ def test_tombstone(topo_m2): assert error is False checkdbscancount(s1, 'nsuniqueid', EXPECTED_NB_NSNIQUEID) +def test_entry_rdn_same_as_suffix(topology_st, request): + """ + Test that a reindex is successful even if an entry + has a RDN that is identical to the suffix + :id: 7f5a38e9-b979-4664-b132-81df0e60f38a + :setup: standalone + :steps: + 1. Create a new backend with suffix 'dc=dup_rdn' (ID 1) + 2. Create a dummy entry 'ou=my_org,dc=dup_rdn' (ID 2) + 3. Create the problematic entry 'dc=dup_rdn,dc=dup_rdn' (ID 3) + 4. Create a dummy entry 'ou=my_org,dc=dup_rdn,dc=dup_rdn' (ID 4) + 5. Do an offline reindex + 6. Check that entryrdn contains the key P3 (parent of ID 3) + 7. Check that error log does not contain 'entryrdn_insert_key - Same DN' + :expectedresults: + 1. Should succeed + 2. Should succeed + 3. Should succeed + 4. Should succeed + 5. Should succeed + 6. Should succeed + 7. Should succeed + """ + inst = topology_st.standalone + + # Create a suffix 'dc=dup_rdn' + be_name = 'domain' + dc_value = 'dup_rdn' + suffix = 'dc=' + dc_value + rdn = 'my_org' + be1 = Backend(inst) + be1.create(properties={ + 'cn': be_name, + 'nsslapd-suffix': suffix, + }, + create_mapping_tree=False + ) + + mts = MappingTrees(inst) + mt = mts.create(properties={ + 'cn': suffix, + 'nsslapd-state': 'backend', + 'nsslapd-backend': be_name, + }) + + # Create the domain entry 'dc=dup_rdn' + create_base_domain(inst, suffix) + + # Create the org ou=my_org,dc=dup_rdn + ous = OrganizationalUnits(inst, suffix) + ou = ous.create(properties={ 'ou': rdn }) + + # when reindexing entryrdn the following entry + # (dc=dup_rdn,dc=dup_rdn) Triggers + # this message. + # This is because its RDN (dc=dup_rdn) is also + # the suffix DN + info_message = 'entryrdn_insert_key - Same DN (dn: %s) is already in the entryrdn file with different' % (ou.dn) + log.info("In case if the bug still exist this line should be in the error log") + log.info(" --> " + info_message) + + # Create the domain entry 'dc=dup_rdn,dc=dup_rdn' + trigger_entry = suffix + "," + suffix + domain = Domain(inst, dn=trigger_entry) + domain.create(properties={ + 'dc': dc_value, + 'description': 'Entry with RDN identical to suffix' + }) + + # Create the org ou=my_org,dc=dup_rdn,dc=dup_rdn + ous = OrganizationalUnits(inst, trigger_entry) + ou = ous.create(properties={ 'ou': rdn }) + + + # Trigger an offline reindex + log.info('Offline reindex, stopping the server') + topology_st.standalone.stop() + + log.info('Reindex all the suffix') + topology_st.standalone.db2index(bename=be_name) + + # make sure the key 'P3' (parent of 'dc=dup_rdn,dc=dup_rdn') exists + dbscanout = topology_st.standalone.dbscan(bename=be_name, index='entryrdn') + log.info(dbscanout) + assert(ensure_bytes('P3') in ensure_bytes(dbscanout)) + + # make sure there is no failure detected/logged in error logs + if topology_st.standalone.get_db_lib() == "mdb": + pattern_str = ".*Inconsistent id2entry database.*" + else: + pattern_str = ".*entryrdn_insert_key - Same DN.*is already in the entryrdn file with different.*$" + assert not topology_st.standalone.ds_error_log.match(pattern_str) + + + def fin(): + topology_st.standalone.restart() + mt.delete() + be1.delete() + + request.addfinalizer(fin) if __name__ == "__main__": # Run isolated diff --git a/ldap/servers/slapd/back-ldbm/db-mdb/mdb_import_threads.c b/ldap/servers/slapd/back-ldbm/db-mdb/mdb_import_threads.c index 44a250e1c5..b89151650e 100644 --- a/ldap/servers/slapd/back-ldbm/db-mdb/mdb_import_threads.c +++ b/ldap/servers/slapd/back-ldbm/db-mdb/mdb_import_threads.c @@ -721,7 +721,7 @@ get_entry_type(WorkerQueueData_t *wqelmt, Slapi_DN *sdn) int len = SLAPI_ATTR_UNIQUEID_LENGTH; const char *ndn = slapi_sdn_get_ndn(sdn); - if (slapi_be_issuffix(be, sdn)) { + if (slapi_be_issuffix(be, sdn) && (wqelmt->wait_id == 1)) { return DNRC_SUFFIX; } if (PL_strncasecmp(ndn, SLAPI_ATTR_UNIQUEID, len) || ndn[len] != '=') { diff --git a/ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c b/ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c index 9b8763d5ed..4de293199c 100644 --- a/ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c +++ b/ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c @@ -1166,7 +1166,16 @@ entryrdn_lookup_dn(backend *be, } goto bail; } - maybesuffix = 1; + if (workid == 1) { + /* The loop (workid) iterates from the starting 'id' + * up to the suffix ID (i.e. '1'). + * A corner case (#6417) is if an entry, on the path + * 'id' -> suffix, has the same RDN than the suffix. + * In order to erroneously believe the loop hits the suffix + * we need to check that 'workid' is '1' (suffix) + */ + maybesuffix = 1; + } } else { _entryrdn_cursor_print_error("entryrdn_lookup_dn", key.data, data.size, data.ulen, rc);