From 5d730f7e9f1e857bc886556db0229607b8d536d2 Mon Sep 17 00:00:00 2001 From: tbordaz Date: Thu, 6 May 2021 18:54:20 +0200 Subject: [PATCH 01/12] Issue 4747 - Remove unstable/unstatus tests from PRCI (#4748) Bug description: Some tests (17) in the tests suite (dirsrvtest/tests/suites) are failing although there is no regression. It needs (long) investigations to status if failures are due to a bug in the tests or in DS core. Until those investigations are completes, test suites loose a large part of its value to detect regression. Indeed those failing tests may hide a real regression. Fix description: Flag failing tests with pytest.mark.flaky(max_runs=2, min_passes=1) Additional action will be to create upstream 17 ticket to status on each failing tests relates: https://github.com/389ds/389-ds-base/issues/4747 Reviewed by: Simon Pichugin, Viktor Ashirov (many thanks for your reviews and help) Platforms tested: F33 --- .github/workflows/pytest.yml | 84 +++++ dirsrvtests/tests/suites/acl/keywords_test.py | 16 +- .../tests/suites/clu/dsctl_acceptance_test.py | 56 --- .../tests/suites/clu/repl_monitor_test.py | 2 + .../dynamic_plugins/dynamic_plugins_test.py | 8 +- .../suites/fourwaymmr/fourwaymmr_test.py | 3 +- .../suites/healthcheck/health_config_test.py | 1 + .../suites/healthcheck/health_sync_test.py | 2 + .../tests/suites/import/import_test.py | 23 +- .../tests/suites/indexes/regression_test.py | 63 ++++ .../paged_results/paged_results_test.py | 3 +- .../tests/suites/password/regression_test.py | 2 + .../tests/suites/plugins/accpol_test.py | 20 +- .../suites/plugins/managed_entry_test.py | 351 ++++++++++++++++++ .../tests/suites/plugins/memberof_test.py | 3 +- .../suites/replication/cleanallruv_test.py | 8 +- .../suites/replication/encryption_cl5_test.py | 8 +- .../tests/suites/retrocl/basic_test.py | 292 --------------- 18 files changed, 576 insertions(+), 369 deletions(-) create mode 100644 .github/workflows/pytest.yml delete mode 100644 dirsrvtests/tests/suites/clu/dsctl_acceptance_test.py create mode 100644 dirsrvtests/tests/suites/plugins/managed_entry_test.py delete mode 100644 dirsrvtests/tests/suites/retrocl/basic_test.py diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml new file mode 100644 index 000000000..015794d96 --- /dev/null +++ b/.github/workflows/pytest.yml @@ -0,0 +1,84 @@ +name: Test + +on: [push, pull_request] + +jobs: + build: + name: Build + runs-on: ubuntu-20.04 + container: + image: quay.io/389ds/ci-images:test + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Get a list of all test suites + id: set-matrix + run: echo "::set-output name=matrix::$(python3 .github/scripts/generate_matrix.py)" + + - name: Build RPMs + run: cd $GITHUB_WORKSPACE && SKIP_AUDIT_CI=1 make -f rpm.mk dist-bz2 rpms + + - name: Tar build artifacts + run: tar -cvf dist.tar dist/ + + - name: Upload RPMs + uses: actions/upload-artifact@v2 + with: + name: rpms + path: dist.tar + + test: + name: Test + runs-on: ubuntu-20.04 + needs: build + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.build.outputs.matrix) }} + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install dependencies + run: | + sudo apt update -y + sudo apt install -y docker.io containerd runc + + sudo cp .github/daemon.json /etc/docker/daemon.json + + sudo systemctl unmask docker + sudo systemctl start docker + + - name: Download RPMs + uses: actions/download-artifact@master + with: + name: rpms + + - name: Extract RPMs + run: tar xvf dist.tar + + - name: Run pytest in a container + run: | + set -x + CID=$(sudo docker run -d -h server.example.com --privileged --rm -v /sys/fs/cgroup:/sys/fs/cgroup:rw,rslave -v ${PWD}:/workspace quay.io/389ds/ci-images:test) + sudo docker exec $CID sh -c "dnf install -y -v dist/rpms/*rpm" + sudo docker exec $CID py.test --suppress-no-test-exit-code -m "not flaky" --junit-xml=pytest.xml -v dirsrvtests/tests/suites/${{ matrix.suite }} + + - name: Make the results file readable by all + if: always() + run: + sudo chmod -f a+r pytest.xml + + - name: Sanitize filename + run: echo "PYTEST_SUITE=$(echo ${{ matrix.suite }} | sed -e 's#\/#-#g')" >> $GITHUB_ENV + + - name: Upload pytest test results + if: always() + uses: actions/upload-artifact@v2 + with: + name: pytest-${{ env.PYTEST_SUITE }} + path: pytest.xml + diff --git a/dirsrvtests/tests/suites/acl/keywords_test.py b/dirsrvtests/tests/suites/acl/keywords_test.py index 0174152e3..c5e989f3b 100644 --- a/dirsrvtests/tests/suites/acl/keywords_test.py +++ b/dirsrvtests/tests/suites/acl/keywords_test.py @@ -216,7 +216,8 @@ def test_user_binds_without_any_password_and_cannot_access_the_data(topo, add_us with pytest.raises(ldap.INSUFFICIENT_ACCESS): org.replace("seeAlso", "cn=1") - +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_user_can_access_the_data_when_connecting_from_any_machine( topo, add_user, aci_of_user ): @@ -245,6 +246,8 @@ def test_user_can_access_the_data_when_connecting_from_any_machine( OrganizationalUnit(conn, DNS_OU_KEY).replace("seeAlso", "cn=1") +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_user_can_access_the_data_when_connecting_from_internal_ds_network_only( topo, add_user, aci_of_user ): @@ -276,7 +279,8 @@ def test_user_can_access_the_data_when_connecting_from_internal_ds_network_only( # Perform Operation OrganizationalUnit(conn, DNS_OU_KEY).replace("seeAlso", "cn=1") - +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_user_can_access_the_data_when_connecting_from_some_network_only( topo, add_user, aci_of_user ): @@ -306,7 +310,8 @@ def test_user_can_access_the_data_when_connecting_from_some_network_only( # Perform Operation OrganizationalUnit(conn, DNS_OU_KEY).replace("seeAlso", "cn=1") - +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_from_an_unauthorized_network(topo, add_user, aci_of_user): """User cannot access the data when connecting from an unauthorized network as per the ACI. @@ -332,7 +337,8 @@ def test_from_an_unauthorized_network(topo, add_user, aci_of_user): # Perform Operation OrganizationalUnit(conn, DNS_OU_KEY).replace("seeAlso", "cn=1") - +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_user_cannot_access_the_data_when_connecting_from_an_unauthorized_network_2( topo, add_user, aci_of_user): """User cannot access the data when connecting from an unauthorized network as per the ACI. @@ -418,6 +424,8 @@ def test_dnsalias_keyword_test_nodns_cannot(topo, add_user, aci_of_user): with pytest.raises(ldap.INSUFFICIENT_ACCESS): org.replace("seeAlso", "cn=1") +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) @pytest.mark.ds50378 @pytest.mark.bz1710848 @pytest.mark.parametrize("ip_addr", ['127.0.0.1', "[::1]"]) diff --git a/dirsrvtests/tests/suites/clu/dsctl_acceptance_test.py b/dirsrvtests/tests/suites/clu/dsctl_acceptance_test.py deleted file mode 100644 index a0f89defd..000000000 --- a/dirsrvtests/tests/suites/clu/dsctl_acceptance_test.py +++ /dev/null @@ -1,56 +0,0 @@ -# --- BEGIN COPYRIGHT BLOCK --- -# Copyright (C) 2021 Red Hat, Inc. -# All rights reserved. -# -# License: GPL (version 3 or any later version). -# See LICENSE for details. -# --- END COPYRIGHT BLOCK --- - -import logging -import pytest -import os -from lib389._constants import * -from lib389.topologies import topology_st as topo - -log = logging.getLogger(__name__) - - -def test_custom_path(topo): - """Test that a custom path, backup directory, is correctly used by lib389 - when the server is stopped. - - :id: 8659e209-ee83-477e-8183-1d2f555669ea - :setup: Standalone Instance - :steps: - 1. Get the LDIF directory - 2. Change the server's backup directory to the LDIF directory - 3. Stop the server, and perform a backup - 4. Backup was written to LDIF directory - :expectedresults: - 1. Success - 2. Success - 3. Success - 4. Success - """ - - # Get LDIF dir - ldif_dir = topo.standalone.get_ldif_dir() - - # Set backup directory to LDIF directory - topo.standalone.config.replace('nsslapd-bakdir', ldif_dir) - - # Stop the server and take a backup - topo.standalone.stop() - topo.standalone.db2bak(None) - - # Verify backup was written to LDIF directory - backups = os.listdir(ldif_dir) - assert len(backups) - - -if __name__ == '__main__': - # Run isolated - # -s for DEBUG mode - CURRENT_FILE = os.path.realpath(__file__) - pytest.main(["-s", CURRENT_FILE]) - diff --git a/dirsrvtests/tests/suites/clu/repl_monitor_test.py b/dirsrvtests/tests/suites/clu/repl_monitor_test.py index 9428edb26..3cf6343c8 100644 --- a/dirsrvtests/tests/suites/clu/repl_monitor_test.py +++ b/dirsrvtests/tests/suites/clu/repl_monitor_test.py @@ -90,6 +90,8 @@ def get_hostnames_from_log(port1, port2): host_m2 = match.group(2) return (host_m1, host_m2) +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) @pytest.mark.ds50545 @pytest.mark.bz1739718 @pytest.mark.skipif(ds_is_older("1.4.0"), reason="Not implemented") diff --git a/dirsrvtests/tests/suites/dynamic_plugins/dynamic_plugins_test.py b/dirsrvtests/tests/suites/dynamic_plugins/dynamic_plugins_test.py index b61daed74..7558cc03d 100644 --- a/dirsrvtests/tests/suites/dynamic_plugins/dynamic_plugins_test.py +++ b/dirsrvtests/tests/suites/dynamic_plugins/dynamic_plugins_test.py @@ -68,7 +68,8 @@ def check_replicas(topology_m2): log.info('Data is consistent across the replicas.\n') - +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_acceptance(topology_m2): """Exercise each plugin and its main features, while changing the configuration without restarting the server. @@ -140,7 +141,8 @@ def test_acceptance(topology_m2): ############################################################################ check_replicas(topology_m2) - +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_memory_corruption(topology_m2): """Check the plugins for memory corruption issues while dynamic plugins option is enabled @@ -242,6 +244,8 @@ def test_memory_corruption(topology_m2): ############################################################################ check_replicas(topology_m2) +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) @pytest.mark.tier2 def test_stress(topology_m2): """Test plugins while under a big load. Perform the test 5 times diff --git a/dirsrvtests/tests/suites/fourwaymmr/fourwaymmr_test.py b/dirsrvtests/tests/suites/fourwaymmr/fourwaymmr_test.py index 5b0754a2e..c5a746ebb 100644 --- a/dirsrvtests/tests/suites/fourwaymmr/fourwaymmr_test.py +++ b/dirsrvtests/tests/suites/fourwaymmr/fourwaymmr_test.py @@ -144,7 +144,8 @@ def test_delete_a_few_entries_in_m4(topo_m4, _cleanupentris): topo_m4.ms["supplier4"], topo_m4.ms["supplier3"], 30 ) - +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_replicated_multivalued_entries(topo_m4): """ Replicated multivalued entries are ordered the same way on all consumers diff --git a/dirsrvtests/tests/suites/healthcheck/health_config_test.py b/dirsrvtests/tests/suites/healthcheck/health_config_test.py index 3d102e859..f470c05c6 100644 --- a/dirsrvtests/tests/suites/healthcheck/health_config_test.py +++ b/dirsrvtests/tests/suites/healthcheck/health_config_test.py @@ -337,6 +337,7 @@ def test_healthcheck_low_disk_space(topology_st): os.remove(file) +@pytest.mark.flaky(max_runs=2, min_passes=1) @pytest.mark.ds50791 @pytest.mark.bz1843567 @pytest.mark.xfail(ds_is_older("1.4.3.8"), reason="Not implemented") diff --git a/dirsrvtests/tests/suites/healthcheck/health_sync_test.py b/dirsrvtests/tests/suites/healthcheck/health_sync_test.py index 75bbfd35c..74df1b322 100644 --- a/dirsrvtests/tests/suites/healthcheck/health_sync_test.py +++ b/dirsrvtests/tests/suites/healthcheck/health_sync_test.py @@ -70,6 +70,8 @@ def run_healthcheck_and_flush_log(topology, instance, searched_code, json, searc @pytest.mark.ds50873 @pytest.mark.bz1685160 @pytest.mark.xfail(ds_is_older("1.4.1"), reason="Not implemented") +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_healthcheck_replication_out_of_sync_not_broken(topology_m3): """Check if HealthCheck returns DSREPLLE0003 code diff --git a/dirsrvtests/tests/suites/import/import_test.py b/dirsrvtests/tests/suites/import/import_test.py index defe447d5..119b097f1 100644 --- a/dirsrvtests/tests/suites/import/import_test.py +++ b/dirsrvtests/tests/suites/import/import_test.py @@ -14,6 +14,7 @@ import os import pytest import time import glob +import logging from lib389.topologies import topology_st as topo from lib389._constants import DEFAULT_SUFFIX, TaskWarning from lib389.dbgen import dbgen_users @@ -28,6 +29,12 @@ from lib389.idm.account import Accounts pytestmark = pytest.mark.tier1 +DEBUGGING = os.getenv("DEBUGGING", default=False) +if DEBUGGING: + logging.getLogger(__name__).setLevel(logging.DEBUG) +else: + logging.getLogger(__name__).setLevel(logging.INFO) +log = logging.getLogger(__name__) def _generate_ldif(topo, no_no): """ @@ -349,7 +356,8 @@ def _toggle_private_import_mem(request, topo): ('nsslapd-db-private-import-mem', 'off')) request.addfinalizer(finofaci) - +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_fast_slow_import(topo, _toggle_private_import_mem, _import_clean): """With nsslapd-db-private-import-mem: on is faster import. @@ -381,16 +389,19 @@ def test_fast_slow_import(topo, _toggle_private_import_mem, _import_clean): # Let's set nsslapd-db-private-import-mem:on, nsslapd-import-cache-autosize: 0 config = LDBMConfig(topo.standalone) # Measure offline import time duration total_time1 - total_time1 = _import_offline(topo, 20) + total_time1 = _import_offline(topo, 1000) # Now nsslapd-db-private-import-mem:off config.replace('nsslapd-db-private-import-mem', 'off') accounts = Accounts(topo.standalone, DEFAULT_SUFFIX) for i in accounts.filter('(uid=*)'): UserAccount(topo.standalone, i.dn).delete() # Measure offline import time duration total_time2 - total_time2 = _import_offline(topo, 20) + total_time2 = _import_offline(topo, 1000) # total_time1 < total_time2 + log.info("total_time1 = %f" % total_time1) + log.info("total_time2 = %f" % total_time2) assert total_time1 < total_time2 + # Set nsslapd-db-private-import-mem:on, nsslapd-import-cache-autosize: -1 config.replace_many( ('nsslapd-db-private-import-mem', 'on'), @@ -398,14 +409,16 @@ def test_fast_slow_import(topo, _toggle_private_import_mem, _import_clean): for i in accounts.filter('(uid=*)'): UserAccount(topo.standalone, i.dn).delete() # Measure offline import time duration total_time1 - total_time1 = _import_offline(topo, 20) + total_time1 = _import_offline(topo, 1000) # Now nsslapd-db-private-import-mem:off config.replace('nsslapd-db-private-import-mem', 'off') for i in accounts.filter('(uid=*)'): UserAccount(topo.standalone, i.dn).delete() # Measure offline import time duration total_time2 - total_time2 = _import_offline(topo, 20) + total_time2 = _import_offline(topo, 1000) # total_time1 < total_time2 + log.info("toral_time1 = %f" % total_time1) + log.info("total_time2 = %f" % total_time2) assert total_time1 < total_time2 diff --git a/dirsrvtests/tests/suites/indexes/regression_test.py b/dirsrvtests/tests/suites/indexes/regression_test.py index 1a71f16e9..ed0c8885f 100644 --- a/dirsrvtests/tests/suites/indexes/regression_test.py +++ b/dirsrvtests/tests/suites/indexes/regression_test.py @@ -19,6 +19,68 @@ from lib389.topologies import topology_st as topo pytestmark = pytest.mark.tier1 +@pytest.fixture(scope="function") +def add_a_group_with_users(request, topo): + """ + Add a group and users, which are members of this group. + """ + groups = Groups(topo.standalone, DEFAULT_SUFFIX, rdn=None) + group = groups.create(properties={'cn': 'test_group'}) + users_list = [] + users_num = 100 + users = UserAccounts(topo.standalone, DEFAULT_SUFFIX, rdn=None) + for num in range(users_num): + USER_NAME = f'test_{num}' + user = users.create(properties={ + 'uid': USER_NAME, + 'sn': USER_NAME, + 'cn': USER_NAME, + 'uidNumber': f'{num}', + 'gidNumber': f'{num}', + 'homeDirectory': f'/home/{USER_NAME}' + }) + users_list.append(user) + group.add_member(user.dn) + + def fin(): + """ + Removes group and users. + """ + # If the server crashed, start it again to do the cleanup + if not topo.standalone.status(): + topo.standalone.start() + for user in users_list: + user.delete() + group.delete() + + request.addfinalizer(fin) + + +@pytest.fixture(scope="function") +def set_small_idlistscanlimit(request, topo): + """ + Set nsslapd-idlistscanlimit to a smaller value to accelerate the reproducer + """ + db_cfg = DatabaseConfig(topo.standalone) + old_idlistscanlimit = db_cfg.get_attr_vals_utf8('nsslapd-idlistscanlimit') + db_cfg.set([('nsslapd-idlistscanlimit', '100')]) + topo.standalone.restart() + + def fin(): + """ + Set nsslapd-idlistscanlimit back to the default value + """ + # If the server crashed, start it again to do the cleanup + if not topo.standalone.status(): + topo.standalone.start() + db_cfg.set([('nsslapd-idlistscanlimit', old_idlistscanlimit)]) + topo.standalone.restart() + + request.addfinalizer(fin) + +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) +@pytest.mark.skipif(ds_is_older("1.4.4.4"), reason="Not implemented") def test_reindex_task_creates_abandoned_index_file(topo): """ Recreating an index for the same attribute but changing @@ -123,3 +185,4 @@ if __name__ == "__main__": # -s for DEBUG mode CURRENT_FILE = os.path.realpath(__file__) pytest.main("-s %s" % CURRENT_FILE) + diff --git a/dirsrvtests/tests/suites/paged_results/paged_results_test.py b/dirsrvtests/tests/suites/paged_results/paged_results_test.py index 9fdceb165..0b45b7d96 100644 --- a/dirsrvtests/tests/suites/paged_results/paged_results_test.py +++ b/dirsrvtests/tests/suites/paged_results/paged_results_test.py @@ -506,7 +506,8 @@ def test_search_with_timelimit(topology_st, create_user): finally: del_users(users_list) - +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) @pytest.mark.parametrize('aci_subject', ('dns = "{}"'.format(HOSTNAME), 'ip = "{}"'.format(IP_ADDRESS))) diff --git a/dirsrvtests/tests/suites/password/regression_test.py b/dirsrvtests/tests/suites/password/regression_test.py index 251834421..8f1facb6d 100644 --- a/dirsrvtests/tests/suites/password/regression_test.py +++ b/dirsrvtests/tests/suites/password/regression_test.py @@ -215,6 +215,8 @@ def test_global_vs_local(topo, passw_policy, create_user, user_pasw): # reset password create_user.set('userPassword', PASSWORD) +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) @pytest.mark.ds49789 def test_unhashed_pw_switch(topo_supplier): """Check that nsslapd-unhashed-pw-switch works corrently diff --git a/dirsrvtests/tests/suites/plugins/accpol_test.py b/dirsrvtests/tests/suites/plugins/accpol_test.py index 73e2e54d1..77975c747 100644 --- a/dirsrvtests/tests/suites/plugins/accpol_test.py +++ b/dirsrvtests/tests/suites/plugins/accpol_test.py @@ -520,7 +520,8 @@ def test_glinact_limit(topology_st, accpol_global): modify_attr(topology_st, ACCP_CONF, 'accountInactivityLimit', '12') del_users(topology_st, suffix, subtree, userid, nousrs) - +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_glnologin_attr(topology_st, accpol_global): """Verify if user account is inactivated based on createTimeStamp attribute, no lastLoginTime attribute present @@ -610,7 +611,8 @@ def test_glnologin_attr(topology_st, accpol_global): account_status(topology_st, suffix, subtree, userid, nousrs, 0, "Enabled") del_users(topology_st, suffix, subtree, userid, nousrs) - +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_glnoalt_stattr(topology_st, accpol_global): """Verify if user account can be inactivated based on lastLoginTime attribute, altstateattrname set to 1.1 @@ -656,6 +658,8 @@ def test_glnoalt_stattr(topology_st, accpol_global): del_users(topology_st, suffix, subtree, userid, nousrs) +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_glattr_modtime(topology_st, accpol_global): """Verify if user account can be inactivated based on modifyTimeStamp attribute @@ -705,6 +709,8 @@ def test_glattr_modtime(topology_st, accpol_global): del_users(topology_st, suffix, subtree, userid, nousrs) +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_glnoalt_nologin(topology_st, accpol_global): """Verify if account policy plugin works if we set altstateattrname set to 1.1 and alwaysrecordlogin to NO @@ -763,6 +769,8 @@ def test_glnoalt_nologin(topology_st, accpol_global): del_users(topology_st, suffix, subtree, userid, nousrs) +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_glinact_nsact(topology_st, accpol_global): """Verify if user account can be activated using ns-activate.pl script. @@ -812,6 +820,8 @@ def test_glinact_nsact(topology_st, accpol_global): del_users(topology_st, suffix, subtree, userid, nousrs) +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_glinact_acclock(topology_st, accpol_global): """Verify if user account is activated when account is unlocked by passwordlockoutduration. @@ -868,6 +878,8 @@ def test_glinact_acclock(topology_st, accpol_global): del_users(topology_st, suffix, subtree, userid, nousrs) +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_glnact_pwexp(topology_st, accpol_global): """Verify if user account is activated when password is reset after password is expired @@ -951,6 +963,8 @@ def test_glnact_pwexp(topology_st, accpol_global): del_users(topology_st, suffix, subtree, userid, nousrs) +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_locact_inact(topology_st, accpol_local): """Verify if user account is inactivated when accountInactivityLimit is exceeded. @@ -995,6 +1009,8 @@ def test_locact_inact(topology_st, accpol_local): del_users(topology_st, suffix, subtree, userid, nousrs) +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_locinact_modrdn(topology_st, accpol_local): """Verify if user account is inactivated when moved from ou=groups to ou=people subtree. diff --git a/dirsrvtests/tests/suites/plugins/managed_entry_test.py b/dirsrvtests/tests/suites/plugins/managed_entry_test.py new file mode 100644 index 000000000..662044ccd --- /dev/null +++ b/dirsrvtests/tests/suites/plugins/managed_entry_test.py @@ -0,0 +1,351 @@ +# --- BEGIN COPYRIGHT BLOCK --- +# Copyright (C) 2020 Red Hat, Inc. +# All rights reserved. +# +# License: GPL (version 3 or any later version). +# See LICENSE for details. +# --- END COPYRIGHT BLOCK --- +# +import pytest +import time +from lib389.topologies import topology_st as topo +from lib389.idm.user import UserAccount, UserAccounts +from lib389.idm.account import Account, Accounts +from lib389._constants import DEFAULT_SUFFIX +from lib389.idm.group import Groups +from lib389.config import Config +from lib389.idm.organizationalunit import OrganizationalUnits, OrganizationalUnit +from lib389.plugins import MEPTemplates, MEPConfigs, ManagedEntriesPlugin, MEPTemplate +from lib389.idm.nscontainer import nsContainers +from lib389.idm.domain import Domain +from lib389.tasks import Entry +import ldap + +pytestmark = pytest.mark.tier1 +USER_PASSWORD = 'password' + + +@pytest.fixture(scope="module") +def _create_inital(topo): + """ + Will create entries for this module + """ + meps = MEPTemplates(topo.standalone, DEFAULT_SUFFIX) + mep_template1 = meps.create( + properties={'cn': 'UPG Template', 'mepRDNAttr': 'cn', 'mepStaticAttr': 'objectclass: posixGroup', + 'mepMappedAttr': 'cn: $uid|gidNumber: $gidNumber|description: User private group for $uid'.split( + '|')}) + conf_mep = MEPConfigs(topo.standalone) + conf_mep.create(properties={'cn': 'UPG Definition1', 'originScope': f'cn=Users,{DEFAULT_SUFFIX}', + 'originFilter': 'objectclass=posixaccount', + 'managedBase': f'cn=Groups,{DEFAULT_SUFFIX}', + 'managedTemplate': mep_template1.dn}) + container = nsContainers(topo.standalone, DEFAULT_SUFFIX) + for cn in ['Users', 'Groups']: + container.create(properties={'cn': cn}) + + +def test_binddn_tracking(topo, _create_inital): + """Test Managed Entries basic functionality + + :id: ea2ddfd4-aaec-11ea-8416-8c16451d917b + :setup: Standalone Instance + :steps: + 1. Set nsslapd-plugin-binddn-tracking attribute under cn=config + 2. Add user + 3. Managed Entry Plugin runs against managed entries upon any update without validating + 4. verify creation of User Private Group with its time stamp value + 5. Modify the SN attribute which is not mapped with managed entry + 6. run ModRDN operation and check the User Private group + 7. Check the time stamp of UPG should be changed now + 8. Check the creatorsname should be user dn and internalCreatorsname should be plugin name + 9. Check if a managed group entry was created + :expected results: + 1. Success + 2. Success + 3. Success + 4. Success + 5. Success + 6. Success + 7. Success + 8. Success + 9. Success + """ + config = Config(topo.standalone) + # set nsslapd-plugin-binddn-tracking attribute under cn=config + config.replace('nsslapd-plugin-binddn-tracking', 'on') + # Add user + user = UserAccounts(topo.standalone, f'cn=Users,{DEFAULT_SUFFIX}', rdn=None).create_test_user() + assert user.get_attr_val_utf8('mepManagedEntry') == f'cn=test_user_1000,cn=Groups,{DEFAULT_SUFFIX}' + entry = Account(topo.standalone, f'cn=test_user_1000,cn=Groups,{DEFAULT_SUFFIX}') + # Managed Entry Plugin runs against managed entries upon any update without validating + # verify creation of User Private Group with its time stamp value + stamp1 = entry.get_attr_val_utf8('modifyTimestamp') + user.replace('sn', 'NewSN_modified') + stamp2 = entry.get_attr_val_utf8('modifyTimestamp') + # Modify the SN attribute which is not mapped with managed entry + # Check the time stamp of UPG should not be changed + assert stamp1 == stamp2 + time.sleep(1) + # run ModRDN operation and check the User Private group + user.rename(new_rdn='uid=UserNewRDN', newsuperior='cn=Users,dc=example,dc=com') + assert user.get_attr_val_utf8('mepManagedEntry') == f'cn=UserNewRDN,cn=Groups,{DEFAULT_SUFFIX}' + entry = Account(topo.standalone, f'cn=UserNewRDN,cn=Groups,{DEFAULT_SUFFIX}') + stamp3 = entry.get_attr_val_utf8('modifyTimestamp') + # Check the time stamp of UPG should be changed now + assert stamp2 != stamp3 + time.sleep(1) + user.replace('gidNumber', '1') + stamp4 = entry.get_attr_val_utf8('modifyTimestamp') + assert stamp4 != stamp3 + # Check the creatorsname should be user dn and internalCreatorsname should be plugin name + assert entry.get_attr_val_utf8('creatorsname') == 'cn=directory manager' + assert entry.get_attr_val_utf8('internalCreatorsname') == 'cn=Managed Entries,cn=plugins,cn=config' + assert entry.get_attr_val_utf8('modifiersname') == 'cn=directory manager' + user.delete() + config.replace('nsslapd-plugin-binddn-tracking', 'off') + + +class WithObjectClass(Account): + def __init__(self, instance, dn=None): + super(WithObjectClass, self).__init__(instance, dn) + self._rdn_attribute = 'uid' + self._create_objectclasses = ['top', 'person', 'inetorgperson'] + +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) +def test_mentry01(topo, _create_inital): + """Test Managed Entries basic functionality + + :id: 9b87493b-0493-46f9-8364-6099d0e5d806 + :setup: Standalone Instance + :steps: + 1. Check the plug-in status + 2. Add Template and definition entry + 3. Add our org units + 4. Add users with PosixAccount ObjectClass and verify creation of User Private Group + 5. Disable the plug-in and check the status + 6. Enable the plug-in and check the status the plug-in is disabled and creation of UPG should fail + 7. Add users with PosixAccount ObjectClass and verify creation of User Private Group + 8. Add users, run ModRDN operation and check the User Private group + 9. Add users, run LDAPMODIFY to change the gidNumber and check the User Private group + 10. Checking whether creation of User Private group fails for existing group entry + 11. Checking whether adding of posixAccount objectClass to existing user creates UPG + 12. Running ModRDN operation and checking the user private groups mepManagedBy attribute + 13. Deleting mepManagedBy attribute and running ModRDN operation to check if it creates a new UPG + 14. Change the RDN of template entry, DSA Unwilling to perform error expected + 15. Change the RDN of cn=Users to cn=TestUsers and check UPG are deleted + :expected results: + 1. Success + 2. Success + 3. Success + 4. Success + 5. Success + 6. Success + 7. Success + 8. Success + 9. Success + 10. Success + 11. Success + 12. Success + 13. Success + 14. Fail(Unwilling to perform ) + 15. Success + """ + # Check the plug-in status + mana = ManagedEntriesPlugin(topo.standalone) + assert mana.status() + # Add Template and definition entry + org1 = OrganizationalUnits(topo.standalone, DEFAULT_SUFFIX).create(properties={'ou': 'Users'}) + org2 = OrganizationalUnit(topo.standalone, f'ou=Groups,{DEFAULT_SUFFIX}') + meps = MEPTemplates(topo.standalone, DEFAULT_SUFFIX) + mep_template1 = meps.create(properties={ + 'cn': 'UPG Template1', + 'mepRDNAttr': 'cn', + 'mepStaticAttr': 'objectclass: posixGroup', + 'mepMappedAttr': 'cn: $uid|gidNumber: $gidNumber|description: User private group for $uid'.split('|')}) + conf_mep = MEPConfigs(topo.standalone) + mep_config = conf_mep.create(properties={ + 'cn': 'UPG Definition2', + 'originScope': org1.dn, + 'originFilter': 'objectclass=posixaccount', + 'managedBase': org2.dn, + 'managedTemplate': mep_template1.dn}) + # Add users with PosixAccount ObjectClass and verify creation of User Private Group + user = UserAccounts(topo.standalone, f'ou=Users,{DEFAULT_SUFFIX}', rdn=None).create_test_user() + assert user.get_attr_val_utf8('mepManagedEntry') == f'cn=test_user_1000,ou=Groups,{DEFAULT_SUFFIX}' + # Disable the plug-in and check the status + mana.disable() + user.delete() + topo.standalone.restart() + # Add users with PosixAccount ObjectClass when the plug-in is disabled and creation of UPG should fail + user = UserAccounts(topo.standalone, f'ou=Users,{DEFAULT_SUFFIX}', rdn=None).create_test_user() + assert not user.get_attr_val_utf8('mepManagedEntry') + # Enable the plug-in and check the status + mana.enable() + user.delete() + topo.standalone.restart() + # Add users with PosixAccount ObjectClass and verify creation of User Private Group + user = UserAccounts(topo.standalone, f'ou=Users,{DEFAULT_SUFFIX}', rdn=None).create_test_user() + assert user.get_attr_val_utf8('mepManagedEntry') == f'cn=test_user_1000,ou=Groups,{DEFAULT_SUFFIX}' + # Add users, run ModRDN operation and check the User Private group + # Add users, run LDAPMODIFY to change the gidNumber and check the User Private group + user.rename(new_rdn='uid=UserNewRDN', newsuperior='ou=Users,dc=example,dc=com') + assert user.get_attr_val_utf8('mepManagedEntry') == f'cn=UserNewRDN,ou=Groups,{DEFAULT_SUFFIX}' + user.replace('gidNumber', '20209') + entry = Account(topo.standalone, f'cn=UserNewRDN,ou=Groups,{DEFAULT_SUFFIX}') + assert entry.get_attr_val_utf8('gidNumber') == '20209' + user.replace_many(('sn', 'new_modified_sn'), ('gidNumber', '31309')) + assert entry.get_attr_val_utf8('gidNumber') == '31309' + user.delete() + # Checking whether creation of User Private group fails for existing group entry + grp = Groups(topo.standalone, f'ou=Groups,{DEFAULT_SUFFIX}', rdn=None).create(properties={'cn': 'MENTRY_14'}) + user = UserAccounts(topo.standalone, f'ou=Users,{DEFAULT_SUFFIX}', rdn=None).create_test_user() + with pytest.raises(ldap.NO_SUCH_OBJECT): + entry.status() + user.delete() + # Checking whether adding of posixAccount objectClass to existing user creates UPG + # Add Users without posixAccount objectClass + users = WithObjectClass(topo.standalone, f'uid=test_test, ou=Users,{DEFAULT_SUFFIX}') + user_properties1 = {'uid': 'test_test', 'cn': 'test', 'sn': 'test', 'mail': 'sasa@sasa.com', 'telephoneNumber': '123'} + user = users.create(properties=user_properties1) + assert not user.get_attr_val_utf8('mepManagedEntry') + # Add posixAccount objectClass + user.replace_many(('objectclass', ['top', 'person', 'inetorgperson', 'posixAccount']), + ('homeDirectory', '/home/ok'), + ('uidNumber', '61603'), ('gidNumber', '61603')) + assert not user.get_attr_val_utf8('mepManagedEntry') + user = UserAccounts(topo.standalone, f'ou=Users,{DEFAULT_SUFFIX}', rdn=None).create_test_user() + entry = Account(topo.standalone, 'cn=test_user_1000,ou=Groups,dc=example,dc=com') + # Add inetuser objectClass + user.replace_many( + ('objectclass', ['top', 'account', 'posixaccount', 'inetOrgPerson', + 'organizationalPerson', 'nsMemberOf', 'nsAccount', + 'person', 'mepOriginEntry', 'inetuser']), + ('memberOf', entry.dn)) + assert entry.status() + user.delete() + user = UserAccounts(topo.standalone, f'ou=Users,{DEFAULT_SUFFIX}', rdn=None).create_test_user() + entry = Account(topo.standalone, 'cn=test_user_1000,ou=Groups,dc=example,dc=com') + # Add groupofNames objectClass + user.replace_many( + ('objectclass', ['top', 'account', 'posixaccount', 'inetOrgPerson', + 'organizationalPerson', 'nsMemberOf', 'nsAccount', + 'person', 'mepOriginEntry', 'groupofNames']), + ('memberOf', user.dn)) + assert entry.status() + # Running ModRDN operation and checking the user private groups mepManagedBy attribute + user.replace('mepManagedEntry', f'uid=CheckModRDN,ou=Users,{DEFAULT_SUFFIX}') + user.rename(new_rdn='uid=UserNewRDN', newsuperior='ou=Users,dc=example,dc=com') + assert user.get_attr_val_utf8('mepManagedEntry') == f'uid=CheckModRDN,ou=Users,{DEFAULT_SUFFIX}' + # Deleting mepManagedBy attribute and running ModRDN operation to check if it creates a new UPG + user.remove('mepManagedEntry', f'uid=CheckModRDN,ou=Users,{DEFAULT_SUFFIX}') + user.rename(new_rdn='uid=UserNewRDN1', newsuperior='ou=Users,dc=example,dc=com') + assert user.get_attr_val_utf8('mepManagedEntry') == f'cn=UserNewRDN1,ou=Groups,{DEFAULT_SUFFIX}' + # Change the RDN of template entry, DSA Unwilling to perform error expected + mep = MEPTemplate(topo.standalone, f'cn=UPG Template,{DEFAULT_SUFFIX}') + with pytest.raises(ldap.UNWILLING_TO_PERFORM): + mep.rename(new_rdn='cn=UPG Template2', newsuperior='dc=example,dc=com') + # Change the RDN of cn=Users to cn=TestUsers and check UPG are deleted + before = user.get_attr_val_utf8('mepManagedEntry') + user.rename(new_rdn='uid=Anuj', newsuperior='ou=Users,dc=example,dc=com') + assert user.get_attr_val_utf8('mepManagedEntry') != before + + +def test_managed_entry_removal(topo): + """Check that we can't remove managed entry manually + + :id: cf9c5be5-97ef-46fc-b199-8346acf4c296 + :setup: Standalone Instance + :steps: + 1. Enable the plugin + 2. Restart the instance + 3. Add our org units + 4. Set up config entry and template entry for the org units + 5. Add an entry that meets the MEP scope + 6. Check if a managed group entry was created + 7. Try to remove the entry while bound as Admin (non-DM) + 8. Remove the entry while bound as DM + 9. Check that the managing entry can be deleted too + :expectedresults: + 1. Success + 2. Success + 3. Success + 4. Success + 5. Success + 6. Success + 7. Should fail + 8. Success + 9. Success + """ + + inst = topo.standalone + + # Add ACI so we can test that non-DM user can't delete managed entry + domain = Domain(inst, DEFAULT_SUFFIX) + ACI_TARGET = f"(target = \"ldap:///{DEFAULT_SUFFIX}\")" + ACI_TARGETATTR = "(targetattr = *)" + ACI_ALLOW = "(version 3.0; acl \"Admin Access\"; allow (all) " + ACI_SUBJECT = "(userdn = \"ldap:///anyone\");)" + ACI_BODY = ACI_TARGET + ACI_TARGETATTR + ACI_ALLOW + ACI_SUBJECT + domain.add('aci', ACI_BODY) + + # stop the plugin, and start it + plugin = ManagedEntriesPlugin(inst) + plugin.disable() + plugin.enable() + + # Add our org units + ous = OrganizationalUnits(inst, DEFAULT_SUFFIX) + ou_people = ous.create(properties={'ou': 'managed_people'}) + ou_groups = ous.create(properties={'ou': 'managed_groups'}) + + mep_templates = MEPTemplates(inst, DEFAULT_SUFFIX) + mep_template1 = mep_templates.create(properties={ + 'cn': 'MEP template', + 'mepRDNAttr': 'cn', + 'mepStaticAttr': 'objectclass: groupOfNames|objectclass: extensibleObject'.split('|'), + 'mepMappedAttr': 'cn: $cn|uid: $cn|gidNumber: $uidNumber'.split('|') + }) + mep_configs = MEPConfigs(inst) + mep_configs.create(properties={'cn': 'config', + 'originScope': ou_people.dn, + 'originFilter': 'objectclass=posixAccount', + 'managedBase': ou_groups.dn, + 'managedTemplate': mep_template1.dn}) + inst.restart() + + # Add an entry that meets the MEP scope + test_users_m1 = UserAccounts(inst, DEFAULT_SUFFIX, rdn='ou={}'.format(ou_people.rdn)) + managing_entry = test_users_m1.create_test_user(1001) + managing_entry.reset_password(USER_PASSWORD) + user_bound_conn = managing_entry.bind(USER_PASSWORD) + + # Get the managed entry + managed_groups = Groups(inst, ou_groups.dn, rdn=None) + managed_entry = managed_groups.get(managing_entry.rdn) + + # Check that the managed entry was created + assert managed_entry.exists() + + # Try to remove the entry while bound as Admin (non-DM) + managed_groups_user_conn = Groups(user_bound_conn, ou_groups.dn, rdn=None) + managed_entry_user_conn = managed_groups_user_conn.get(managed_entry.rdn) + with pytest.raises(ldap.UNWILLING_TO_PERFORM): + managed_entry_user_conn.delete() + assert managed_entry_user_conn.exists() + + # Remove the entry while bound as DM + managed_entry.delete() + assert not managed_entry.exists() + + # Check that the managing entry can be deleted too + managing_entry.delete() + assert not managing_entry.exists() + + +if __name__ == '__main__': + # Run isolated + # -s for DEBUG mode + CURRENT_FILE = os.path.realpath(__file__) + pytest.main("-s %s" % CURRENT_FILE) diff --git a/dirsrvtests/tests/suites/plugins/memberof_test.py b/dirsrvtests/tests/suites/plugins/memberof_test.py index bc99eef7d..d3b32c856 100644 --- a/dirsrvtests/tests/suites/plugins/memberof_test.py +++ b/dirsrvtests/tests/suites/plugins/memberof_test.py @@ -2655,7 +2655,8 @@ def test_complex_group_scenario_9(topology_st): verify_post_025(topology_st, memofegrp020_1, memofegrp020_2, memofegrp020_3, memofegrp020_4, memofegrp020_5, memofuser1, memofuser2, memofuser3, memofuser4) - +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_memberof_auto_add_oc(topology_st): """Test the auto add objectclass (OC) feature. The plugin should add a predefined objectclass that will allow memberOf to be added to an entry. diff --git a/dirsrvtests/tests/suites/replication/cleanallruv_test.py b/dirsrvtests/tests/suites/replication/cleanallruv_test.py index 5610e3c19..f0cd99cfc 100644 --- a/dirsrvtests/tests/suites/replication/cleanallruv_test.py +++ b/dirsrvtests/tests/suites/replication/cleanallruv_test.py @@ -223,7 +223,7 @@ def test_clean(topology_m4, m4rid): log.info('test_clean PASSED, restoring supplier 4...') - +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_clean_restart(topology_m4, m4rid): """Check that cleanallruv task works properly after a restart @@ -295,6 +295,7 @@ def test_clean_restart(topology_m4, m4rid): log.info('test_clean_restart PASSED, restoring supplier 4...') +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_clean_force(topology_m4, m4rid): """Check that multiple tasks with a 'force' option work properly @@ -353,6 +354,7 @@ def test_clean_force(topology_m4, m4rid): log.info('test_clean_force PASSED, restoring supplier 4...') +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_abort(topology_m4, m4rid): """Test the abort task basic functionality @@ -408,6 +410,7 @@ def test_abort(topology_m4, m4rid): log.info('test_abort PASSED, restoring supplier 4...') +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_abort_restart(topology_m4, m4rid): """Test the abort task can handle a restart, and then resume @@ -486,6 +489,7 @@ def test_abort_restart(topology_m4, m4rid): log.info('test_abort_restart PASSED, restoring supplier 4...') +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_abort_certify(topology_m4, m4rid): """Test the abort task with a replica-certify-all option @@ -555,6 +559,7 @@ def test_abort_certify(topology_m4, m4rid): log.info('test_abort_certify PASSED, restoring supplier 4...') +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_stress_clean(topology_m4, m4rid): """Put each server(m1 - m4) under a stress, and perform the entire clean process @@ -641,6 +646,7 @@ def test_stress_clean(topology_m4, m4rid): ldbm_config.set('nsslapd-readonly', 'off') +@pytest.mark.flaky(max_runs=2, min_passes=1) def test_multiple_tasks_with_force(topology_m4, m4rid): """Check that multiple tasks with a 'force' option work properly diff --git a/dirsrvtests/tests/suites/replication/encryption_cl5_test.py b/dirsrvtests/tests/suites/replication/encryption_cl5_test.py index 7ae7e1b13..b69863f53 100644 --- a/dirsrvtests/tests/suites/replication/encryption_cl5_test.py +++ b/dirsrvtests/tests/suites/replication/encryption_cl5_test.py @@ -73,10 +73,10 @@ def _check_unhashed_userpw_encrypted(inst, change_type, user_dn, user_pw, is_enc assert user_pw_attr in entry, 'Changelog entry does not contain clear text password' assert count, 'Operation type and DN of the entry not matched in changelog' - -@pytest.mark.parametrize("encryption", ["AES", "3DES"]) -def test_algorithm_unhashed(topology_with_tls, encryption): - """Check encryption algowithm AES and 3DES. +#unstable or unstatus tests, skipped for now +@pytest.mark.flaky(max_runs=2, min_passes=1) +def test_algorithm_unhashed(topology_with_tls): + """Check encryption algorithm AES And check unhashed#user#password attribute for encryption. :id: b7a37bf8-4b2e-4dbd-9891-70117d67558c diff --git a/dirsrvtests/tests/suites/retrocl/basic_test.py b/dirsrvtests/tests/suites/retrocl/basic_test.py deleted file mode 100644 index 112c73cb9..000000000 --- a/dirsrvtests/tests/suites/retrocl/basic_test.py +++ /dev/null @@ -1,292 +0,0 @@ -# --- BEGIN COPYRIGHT BLOCK --- -# Copyright (C) 2021 Red Hat, Inc. -# All rights reserved. -# -# License: GPL (version 3 or any later version). -# See LICENSE for details. -# --- END COPYRIGHT BLOCK --- - -import logging -import ldap -import time -import pytest -from lib389.topologies import topology_st -from lib389.plugins import RetroChangelogPlugin -from lib389._constants import * -from lib389.utils import * -from lib389.tasks import * -from lib389.cli_base import FakeArgs, connect_instance, disconnect_instance -from lib389.cli_base.dsrc import dsrc_arg_concat -from lib389.cli_conf.plugins.retrochangelog import retrochangelog_add -from lib389.idm.user import UserAccount, UserAccounts, nsUserAccounts - -pytestmark = pytest.mark.tier1 - -USER1_DN = 'uid=user1,ou=people,'+ DEFAULT_SUFFIX -USER2_DN = 'uid=user2,ou=people,'+ DEFAULT_SUFFIX -USER_PW = 'password' -ATTR_HOMEPHONE = 'homePhone' -ATTR_CARLICENSE = 'carLicense' - -log = logging.getLogger(__name__) - -def test_retrocl_exclude_attr_add(topology_st): - """ Test exclude attribute feature of the retrocl plugin for add operation - - :id: 3481650f-2070-45ef-9600-2500cfc51559 - - :setup: Standalone instance - - :steps: - 1. Enable dynamic plugins - 2. Confige retro changelog plugin - 3. Add an entry - 4. Ensure entry attrs are in the changelog - 5. Exclude an attr - 6. Add another entry - 7. Ensure excluded attr is not in the changelog - - :expectedresults: - 1. Success - 2. Success - 3. Success - 4. Success - 5. Success - 6. Success - 7. Success - """ - - st = topology_st.standalone - - log.info('Enable dynamic plugins') - try: - st.config.set('nsslapd-dynamic-plugins', 'on') - except ldap.LDAPError as e: - ldap.error('Failed to enable dynamic plugins ' + e.args[0]['desc']) - assert False - - log.info('Configure retrocl plugin') - rcl = RetroChangelogPlugin(st) - rcl.disable() - rcl.enable() - rcl.replace('nsslapd-attribute', 'nsuniqueid:targetUniqueId') - - log.info('Restarting instance') - try: - st.restart() - except ldap.LDAPError as e: - ldap.error('Failed to restart instance ' + e.args[0]['desc']) - assert False - - users = UserAccounts(st, DEFAULT_SUFFIX) - - log.info('Adding user1') - try: - user1 = users.create(properties={ - 'sn': '1', - 'cn': 'user 1', - 'uid': 'user1', - 'uidNumber': '11', - 'gidNumber': '111', - 'givenname': 'user1', - 'homePhone': '0861234567', - 'carLicense': '131D16674', - 'mail': 'user1@whereever.com', - 'homeDirectory': '/home/user1', - 'userpassword': USER_PW}) - except ldap.ALREADY_EXISTS: - pass - except ldap.LDAPError as e: - log.error("Failed to add user1") - - log.info('Verify homePhone and carLicense attrs are in the changelog changestring') - try: - cllist = st.search_s(RETROCL_SUFFIX, ldap.SCOPE_SUBTREE, '(targetDn=%s)' % USER1_DN) - except ldap.LDAPError as e: - log.fatal("Changelog search failed, error: " +str(e)) - assert False - assert len(cllist) > 0 - if cllist[0].hasAttr('changes'): - clstr = (cllist[0].getValue('changes')).decode() - assert ATTR_HOMEPHONE in clstr - assert ATTR_CARLICENSE in clstr - - log.info('Excluding attribute ' + ATTR_HOMEPHONE) - args = FakeArgs() - args.connections = [st.host + ':' + str(st.port) + ':' + DN_DM + ':' + PW_DM] - args.instance = 'standalone1' - args.basedn = None - args.binddn = None - args.starttls = False - args.pwdfile = None - args.bindpw = None - args.prompt = False - args.exclude_attrs = ATTR_HOMEPHONE - args.func = retrochangelog_add - dsrc_inst = dsrc_arg_concat(args, None) - inst = connect_instance(dsrc_inst, False, args) - result = args.func(inst, None, log, args) - disconnect_instance(inst) - assert result is None - - log.info("5s delay for retrocl plugin to restart") - time.sleep(5) - - log.info('Adding user2') - try: - user2 = users.create(properties={ - 'sn': '2', - 'cn': 'user 2', - 'uid': 'user2', - 'uidNumber': '22', - 'gidNumber': '222', - 'givenname': 'user2', - 'homePhone': '0879088363', - 'carLicense': '04WX11038', - 'mail': 'user2@whereever.com', - 'homeDirectory': '/home/user2', - 'userpassword': USER_PW}) - except ldap.ALREADY_EXISTS: - pass - except ldap.LDAPError as e: - log.error("Failed to add user2") - - log.info('Verify homePhone attr is not in the changelog changestring') - try: - cllist = st.search_s(RETROCL_SUFFIX, ldap.SCOPE_SUBTREE, '(targetDn=%s)' % USER2_DN) - assert len(cllist) > 0 - if cllist[0].hasAttr('changes'): - clstr = (cllist[0].getValue('changes')).decode() - assert ATTR_HOMEPHONE not in clstr - assert ATTR_CARLICENSE in clstr - except ldap.LDAPError as e: - log.fatal("Changelog search failed, error: " +str(e)) - assert False - -def test_retrocl_exclude_attr_mod(topology_st): - """ Test exclude attribute feature of the retrocl plugin for mod operation - - :id: f6bef689-685b-4f86-a98d-f7e6b1fcada3 - - :setup: Standalone instance - - :steps: - 1. Enable dynamic plugins - 2. Confige retro changelog plugin - 3. Add user1 entry - 4. Ensure entry attrs are in the changelog - 5. Exclude an attr - 6. Modify user1 entry - 7. Ensure excluded attr is not in the changelog - - :expectedresults: - 1. Success - 2. Success - 3. Success - 4. Success - 5. Success - 6. Success - 7. Success - """ - - st = topology_st.standalone - - log.info('Enable dynamic plugins') - try: - st.config.set('nsslapd-dynamic-plugins', 'on') - except ldap.LDAPError as e: - ldap.error('Failed to enable dynamic plugins ' + e.args[0]['desc']) - assert False - - log.info('Configure retrocl plugin') - rcl = RetroChangelogPlugin(st) - rcl.disable() - rcl.enable() - rcl.replace('nsslapd-attribute', 'nsuniqueid:targetUniqueId') - - log.info('Restarting instance') - try: - st.restart() - except ldap.LDAPError as e: - ldap.error('Failed to restart instance ' + e.args[0]['desc']) - assert False - - users = UserAccounts(st, DEFAULT_SUFFIX) - - log.info('Adding user1') - try: - user1 = users.create(properties={ - 'sn': '1', - 'cn': 'user 1', - 'uid': 'user1', - 'uidNumber': '11', - 'gidNumber': '111', - 'givenname': 'user1', - 'homePhone': '0861234567', - 'carLicense': '131D16674', - 'mail': 'user1@whereever.com', - 'homeDirectory': '/home/user1', - 'userpassword': USER_PW}) - except ldap.ALREADY_EXISTS: - pass - except ldap.LDAPError as e: - log.error("Failed to add user1") - - log.info('Verify homePhone and carLicense attrs are in the changelog changestring') - try: - cllist = st.search_s(RETROCL_SUFFIX, ldap.SCOPE_SUBTREE, '(targetDn=%s)' % USER1_DN) - except ldap.LDAPError as e: - log.fatal("Changelog search failed, error: " +str(e)) - assert False - assert len(cllist) > 0 - if cllist[0].hasAttr('changes'): - clstr = (cllist[0].getValue('changes')).decode() - assert ATTR_HOMEPHONE in clstr - assert ATTR_CARLICENSE in clstr - - log.info('Excluding attribute ' + ATTR_CARLICENSE) - args = FakeArgs() - args.connections = [st.host + ':' + str(st.port) + ':' + DN_DM + ':' + PW_DM] - args.instance = 'standalone1' - args.basedn = None - args.binddn = None - args.starttls = False - args.pwdfile = None - args.bindpw = None - args.prompt = False - args.exclude_attrs = ATTR_CARLICENSE - args.func = retrochangelog_add - dsrc_inst = dsrc_arg_concat(args, None) - inst = connect_instance(dsrc_inst, False, args) - result = args.func(inst, None, log, args) - disconnect_instance(inst) - assert result is None - - log.info("5s delay for retrocl plugin to restart") - time.sleep(5) - - log.info('Modify user1 carLicense attribute') - try: - st.modify_s(USER1_DN, [(ldap.MOD_REPLACE, ATTR_CARLICENSE, b"123WX321")]) - except ldap.LDAPError as e: - log.fatal('test_retrocl_exclude_attr_mod: Failed to update user1 attribute: error ' + e.message['desc']) - assert False - - log.info('Verify carLicense attr is not in the changelog changestring') - try: - cllist = st.search_s(RETROCL_SUFFIX, ldap.SCOPE_SUBTREE, '(targetDn=%s)' % USER1_DN) - assert len(cllist) > 0 - # There will be 2 entries in the changelog for this user, we are only - #interested in the second one, the modify operation. - if cllist[1].hasAttr('changes'): - clstr = (cllist[1].getValue('changes')).decode() - assert ATTR_CARLICENSE not in clstr - except ldap.LDAPError as e: - log.fatal("Changelog search failed, error: " +str(e)) - assert False - -if __name__ == '__main__': - # Run isolated - # -s for DEBUG mode - CURRENT_FILE = os.path.realpath(__file__) - pytest.main("-s %s" % CURRENT_FILE) -- 2.26.3