From 9b2fc77a36156ea987dcea6e2043f8e4c4a6b259 Mon Sep 17 00:00:00 2001 From: progier389 Date: Tue, 18 Jun 2024 14:21:07 +0200 Subject: [PATCH] Issue 6224 - d2entry - Could not open id2entry err 0 - at startup when having sub-suffixes (#6225) Problem:: d2entry - Could not open id2entry err 0 is logged at startup when having sub-suffixes Reason: The slapi_exist_referral internal search access a backend that is not yet started. Solution: Limit the internal search to a single backend Issue: #6224 Reviewed by: @droideck Thanks! (cherry picked from commit 796f703021e961fdd8cbc53b4ad4e20258af0e96) --- .../tests/suites/ds_logs/ds_logs_test.py | 1 + .../suites/mapping_tree/regression_test.py | 161 +++++++++++++++++- ldap/servers/slapd/backend.c | 7 +- 3 files changed, 159 insertions(+), 10 deletions(-) diff --git a/dirsrvtests/tests/suites/ds_logs/ds_logs_test.py b/dirsrvtests/tests/suites/ds_logs/ds_logs_test.py index 812936c62..84a9c6ec8 100644 --- a/dirsrvtests/tests/suites/ds_logs/ds_logs_test.py +++ b/dirsrvtests/tests/suites/ds_logs/ds_logs_test.py @@ -1222,6 +1222,7 @@ def test_referral_check(topology_st, request): request.addfinalizer(fin) +<<<<<<< HEAD def test_referral_subsuffix(topology_st, request): """Test the results of an inverted parent suffix definition in the configuration. diff --git a/dirsrvtests/tests/suites/mapping_tree/regression_test.py b/dirsrvtests/tests/suites/mapping_tree/regression_test.py index 99d4a1d5f..689ff9f59 100644 --- a/dirsrvtests/tests/suites/mapping_tree/regression_test.py +++ b/dirsrvtests/tests/suites/mapping_tree/regression_test.py @@ -11,10 +11,14 @@ import ldap import logging import os import pytest +import time from lib389.backend import Backends, Backend +from lib389._constants import HOST_STANDALONE, PORT_STANDALONE, DN_DM, PW_DM from lib389.dbgen import dbgen_users from lib389.mappingTree import MappingTrees from lib389.topologies import topology_st +from lib389.referral import Referrals, Referral + try: from lib389.backend import BackendSuffixView @@ -31,14 +35,26 @@ else: logging.getLogger(__name__).setLevel(logging.INFO) log = logging.getLogger(__name__) +PARENT_SUFFIX = "dc=parent" +CHILD1_SUFFIX = f"dc=child1,{PARENT_SUFFIX}" +CHILD2_SUFFIX = f"dc=child2,{PARENT_SUFFIX}" + +PARENT_REFERRAL_DN = f"cn=ref,ou=People,{PARENT_SUFFIX}" +CHILD1_REFERRAL_DN = f"cn=ref,ou=people,{CHILD1_SUFFIX}" +CHILD2_REFERRAL_DN = f"cn=ref,ou=people,{CHILD2_SUFFIX}" + +REFERRAL_CHECK_PEDIOD = 7 + + + BESTRUCT = [ - { "bename" : "parent", "suffix": "dc=parent" }, - { "bename" : "child1", "suffix": "dc=child1,dc=parent" }, - { "bename" : "child2", "suffix": "dc=child2,dc=parent" }, + { "bename" : "parent", "suffix": PARENT_SUFFIX }, + { "bename" : "child1", "suffix": CHILD1_SUFFIX }, + { "bename" : "child2", "suffix": CHILD2_SUFFIX }, ] -@pytest.fixture(scope="function") +@pytest.fixture(scope="module") def topo(topology_st, request): bes = [] @@ -50,6 +66,9 @@ def topo(topology_st, request): request.addfinalizer(fin) inst = topology_st.standalone + # Reduce nsslapd-referral-check-period to accelerate test + topology_st.standalone.config.set("nsslapd-referral-check-period", str(REFERRAL_CHECK_PEDIOD)) + ldif_files = {} for d in BESTRUCT: bename = d['bename'] @@ -76,14 +95,13 @@ def topo(topology_st, request): inst.start() return topology_st -# Parameters for test_change_repl_passwd -EXPECTED_ENTRIES = (("dc=parent", 39), ("dc=child1,dc=parent", 13), ("dc=child2,dc=parent", 13)) +# Parameters for test_sub_suffixes @pytest.mark.parametrize( "orphan_param", [ - pytest.param( ( True, { "dc=parent": 2, "dc=child1,dc=parent":1, "dc=child2,dc=parent":1}), id="orphan-is-true" ), - pytest.param( ( False, { "dc=parent": 3, "dc=child1,dc=parent":1, "dc=child2,dc=parent":1}), id="orphan-is-false" ), - pytest.param( ( None, { "dc=parent": 3, "dc=child1,dc=parent":1, "dc=child2,dc=parent":1}), id="no-orphan" ), + pytest.param( ( True, { PARENT_SUFFIX: 2, CHILD1_SUFFIX:1, CHILD2_SUFFIX:1}), id="orphan-is-true" ), + pytest.param( ( False, { PARENT_SUFFIX: 3, CHILD1_SUFFIX:1, CHILD2_SUFFIX:1}), id="orphan-is-false" ), + pytest.param( ( None, { PARENT_SUFFIX: 3, CHILD1_SUFFIX:1, CHILD2_SUFFIX:1}), id="no-orphan" ), ], ) @@ -128,3 +146,128 @@ def test_sub_suffixes(topo, orphan_param): log.info('Test PASSED') +def test_one_level_search_on_sub_suffixes(topo): + """ Perform one level scoped search accross suffix and sub-suffix + + :id: 92f3139e-280e-11ef-a989-482ae39447e5 + :feature: mapping-tree + :setup: Standalone instance with 3 additional backends: + dc=parent, dc=child1,dc=parent, dc=childr21,dc=parent + :steps: + 1. Perform a ONE LEVEL search on dc=parent + 2. Check that all expected entries have been returned + 3. Check that only the expected entries have been returned + :expectedresults: + 1. Success + 2. each expected dn should be in the result set + 3. Number of returned entries should be the same as the number of expected entries + """ + expected_dns = ( 'dc=child1,dc=parent', + 'dc=child2,dc=parent', + 'ou=accounting,dc=parent', + 'ou=product development,dc=parent', + 'ou=product testing,dc=parent', + 'ou=human resources,dc=parent', + 'ou=payroll,dc=parent', + 'ou=people,dc=parent', + 'ou=groups,dc=parent', ) + entries = topo.standalone.search_s("dc=parent", ldap.SCOPE_ONELEVEL, "(objectClass=*)", + attrlist=("dc","ou"), escapehatch='i am sure') + log.info(f'one level search on dc=parent returned the following entries: {entries}') + dns = [ entry.dn for entry in entries ] + for dn in expected_dns: + assert dn in dns + assert len(entries) == len(expected_dns) + + +def test_sub_suffixes_errlog(topo): + """ check the entries found on suffix/sub-suffix + used int + + :id: 1db9d52e-28de-11ef-b286-482ae39447e5 + :feature: mapping-tree + :setup: Standalone instance with 3 additional backends: + dc=parent, dc=child1,dc=parent, dc=childr21,dc=parent + :steps: + 1. Check that id2entry error message is not in the error log. + :expectedresults: + 1. Success + """ + inst = topo.standalone + assert not inst.searchErrorsLog('id2entry - Could not open id2entry err 0') + + +# Parameters for test_referral_subsuffix: +# a tuple pair containing: +# - list of referral dn that must be created +# - dict of searches basedn: expected_number_of_referrals +@pytest.mark.parametrize( + "parameters", + [ + pytest.param( ((PARENT_REFERRAL_DN, CHILD1_REFERRAL_DN), {PARENT_SUFFIX: 2, CHILD1_SUFFIX:1, CHILD2_SUFFIX:0}), id="Both"), + pytest.param( ((PARENT_REFERRAL_DN,), {PARENT_SUFFIX: 1, CHILD1_SUFFIX:0, CHILD2_SUFFIX:0}) , id="Parent"), + pytest.param( ((CHILD1_REFERRAL_DN,), {PARENT_SUFFIX: 1, CHILD1_SUFFIX:1, CHILD2_SUFFIX:0}) , id="Child"), + pytest.param( ((), {PARENT_SUFFIX: 0, CHILD1_SUFFIX:0, CHILD2_SUFFIX:0}), id="None"), + ]) + +def test_referral_subsuffix(topo, request, parameters): + """Test the results of an inverted parent suffix definition in the configuration. + + For more details see: + https://www.port389.org/docs/389ds/design/mapping_tree_assembly.html + + :id: 4e111a22-2a5d-11ef-a890-482ae39447e5 + :feature: referrals + :setup: Standalone instance with 3 additional backends: + dc=parent, dc=child1,dc=parent, dc=childr21,dc=parent + + :setup: Standalone instance + :parametrized: yes + :steps: + refs,searches = referrals + + 1. Create the referrals according to the current parameter + 2. Wait enough time so they get detected + 3. For each search base dn, in the current parameter, perform the two following steps + 4. In 3. loop: Perform a search with provided base dn + 5. In 3. loop: Check that the number of returned referrals is the expected one. + + :expectedresults: + all steps succeeds + """ + inst = topo.standalone + + def fin(): + log.info('Deleting all referrals') + for ref in Referrals(inst, PARENT_SUFFIX).list(): + ref.delete() + + # Set cleanup callback + if DEBUGGING: + request.addfinalizer(fin) + + # Remove all referrals + fin() + # Add requested referrals + for dn in parameters[0]: + refs = Referral(inst, dn=dn) + refs.create(basedn=dn, properties={ 'cn': 'ref', 'ref': f'ldap://remote/{dn}'}) + # Wait that the internal search detects the referrals + time.sleep(REFERRAL_CHECK_PEDIOD + 1) + # Open a test connection + ldc = ldap.initialize(f"ldap://{HOST_STANDALONE}:{PORT_STANDALONE}") + ldc.set_option(ldap.OPT_REFERRALS,0) + ldc.simple_bind_s(DN_DM,PW_DM) + + # For each search base dn: + for basedn,nbref in parameters[1].items(): + log.info(f"Referrals are: {parameters[0]}") + # Perform a search with provided base dn + result = ldc.search_s(basedn, ldap.SCOPE_SUBTREE, filterstr="(ou=People)") + found_dns = [ dn for dn,entry in result if dn is not None ] + found_refs = [ entry for dn,entry in result if dn is None ] + log.info(f"Search on {basedn} returned {found_dns} and {found_refs}") + # Check that the number of returned referrals is the expected one. + log.info(f"Search returned {len(found_refs)} referrals. {nbref} are expected.") + assert len(found_refs) == nbref + ldc.unbind() diff --git a/ldap/servers/slapd/backend.c b/ldap/servers/slapd/backend.c index 498f683b1..f86b0b9b6 100644 --- a/ldap/servers/slapd/backend.c +++ b/ldap/servers/slapd/backend.c @@ -230,12 +230,17 @@ slapi_exist_referral(Slapi_Backend *be) /* search for ("smart") referral entries */ search_pb = slapi_pblock_new(); - server_ctrls = (LDAPControl **) slapi_ch_calloc(2, sizeof (LDAPControl *)); + server_ctrls = (LDAPControl **) slapi_ch_calloc(3, sizeof (LDAPControl *)); server_ctrls[0] = (LDAPControl *) slapi_ch_malloc(sizeof (LDAPControl)); server_ctrls[0]->ldctl_oid = slapi_ch_strdup(LDAP_CONTROL_MANAGEDSAIT); server_ctrls[0]->ldctl_value.bv_val = NULL; server_ctrls[0]->ldctl_value.bv_len = 0; server_ctrls[0]->ldctl_iscritical = '\0'; + server_ctrls[1] = (LDAPControl *) slapi_ch_malloc(sizeof (LDAPControl)); + server_ctrls[1]->ldctl_oid = slapi_ch_strdup(MTN_CONTROL_USE_ONE_BACKEND_EXT_OID); + server_ctrls[1]->ldctl_value.bv_val = NULL; + server_ctrls[1]->ldctl_value.bv_len = 0; + server_ctrls[1]->ldctl_iscritical = '\0'; slapi_search_internal_set_pb(search_pb, suffix, LDAP_SCOPE_SUBTREE, filter, NULL, 0, server_ctrls, NULL, (void *) plugin_get_default_component_id(), 0); -- 2.48.0