From dc7f1083c57da4a8826fd9ec36a4b7063a1bd4a6 Mon Sep 17 00:00:00 2001 From: Simon Pichugin Date: May 17 2018 12:55:35 +0000 Subject: Issue 49581 - Fix dynamic plugins test suite Description: Refactor plugins module and fix tests accordingly. Divide the tests into three functions: accaptance, memory corrruption and stress. Add absent Task and Plugin objects. Add docstrings for every fixed object. Move plugin acceptance tests to a separate module in plugins suite https://pagure.io/389-ds-base/issue/49581 Reviewed by: vashirov, mreynolds, wibrown (Thanks!) --- diff --git a/dirsrvtests/tests/suites/dynamic_plugins/dynamic_plugins_test.py b/dirsrvtests/tests/suites/dynamic_plugins/dynamic_plugins_test.py index 51c3a0f..03612d1 100644 --- a/dirsrvtests/tests/suites/dynamic_plugins/dynamic_plugins_test.py +++ b/dirsrvtests/tests/suites/dynamic_plugins/dynamic_plugins_test.py @@ -16,78 +16,83 @@ import logging import ldap.sasl import pytest from lib389.tasks import * -from lib389 import DirSrv -from lib389.properties import * -import plugin_tests -import stress_tests -from lib389.topologies import topology_st -from lib389._constants import (DN_CONFIG, DEFAULT_SUFFIX, DN_LDBM, defaultProperties, - PLUGIN_MEMBER_OF, PLUGIN_LINKED_ATTRS, PLUGIN_REFER_INTEGRITY, - ReplicaRole, REPLICATION_BIND_DN, REPLICATION_BIND_PW, - REPLICATION_BIND_METHOD, REPLICATION_TRANSPORT, - LOCALHOST, REPLICA_RUV_FILTER, args_instance, - RA_NAME, RA_BINDDN, RA_BINDPW, RA_METHOD, RA_TRANSPORT_PROT) +from lib389.replica import ReplicationManager +from lib389.config import LDBMConfig +from lib389._constants import * +from lib389.topologies import topology_m2 +from ..plugins import acceptance_test +from . import stress_tests log = logging.getLogger(__name__) -def repl_fail(replica): - """Remove replica instance, and assert failure""" +def check_replicas(topology_m2): + """Check that replication is in sync and working""" - replica.delete() - assert False + m1 = topology_m2.ms["master1"] + m2 = topology_m2.ms["master2"] + log.info('Checking if replication is in sync...') + repl = ReplicationManager(DEFAULT_SUFFIX) + repl.test_replication_topology(topology_m2) + # + # Verify the databases are identical. There should not be any "user, entry, employee" entries + # + log.info('Checking if the data is the same between the replicas...') -def test_dynamic_plugins(topology_st): - """ - Test Dynamic Plugins - exercise each plugin and its main features, while - changing the configuration without restarting the server. - - Need to test: functionality, stability, and stress. These tests need to run - with replication disabled, and with replication setup with a - second instance. Then test if replication is working, and we have - same entries on each side. - - Functionality - Make sure that as configuration changes are made they take - effect immediately. Cross plugin interaction (e.g. automember/memberOf) - needs to tested, as well as plugin tasks. Need to test plugin - config validation(dependencies, etc). - - Memory Corruption - Restart the plugins many times, and in different orders and test - functionality, and stability. This will excerise the internal - plugin linked lists, dse callbacks, and task handlers. - - Stress - Put the server under load that will trigger multiple plugins(MO, RI, DNA, etc) - Restart various plugins while these operations are going on. Perform this test - 5 times(stress_max_run). + # Check the master + try: + entries = m1.search_s(DEFAULT_SUFFIX, + ldap.SCOPE_SUBTREE, + "(|(uid=person*)(uid=entry*)(uid=employee*))") + if len(entries) > 0: + log.error('Master database has incorrect data set!\n') + assert False + except ldap.LDAPError as e: + log.fatal('Unable to search db on master: ' + e.message['desc']) + assert False + + # Check the consumer + try: + entries = m2.search_s(DEFAULT_SUFFIX, + ldap.SCOPE_SUBTREE, + "(|(uid=person*)(uid=entry*)(uid=employee*))") + if len(entries) > 0: + log.error('Consumer database in not consistent with master database') + assert False + except ldap.LDAPError as e: + log.fatal('Unable to search db on consumer: ' + e.message['desc']) + assert False + + log.info('Data is consistent across the replicas.\n') + + +def test_acceptance(topology_m2): + """Exercise each plugin and its main features, while + changing the configuration without restarting the server. + + Make sure that as configuration changes are made they take + effect immediately. Cross plugin interaction (e.g. automember/memberOf) + needs to tested, as well as plugin tasks. Need to test plugin + config validation(dependencies, etc). """ - REPLICA_PORT = 33334 - RUV_FILTER = REPLICA_RUV_FILTER - master_maxcsn = 0 - replica_maxcsn = 0 + m1 = topology_m2.ms["master1"] msg = ' (no replication)' replication_run = False - stress_max_runs = 5 + + # First part of the test should be without replication + topology_m2.pause_all_replicas() # First enable dynamic plugins - try: - topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-dynamic-plugins', 'on')]) - except ldap.LDAPError as e: - log.fatal('Failed to enable dynamic plugin!' + e.message['desc']) - assert False + m1.config.replace('nsslapd-dynamic-plugins', 'on') # Test that critical plugins can be updated even though the change might not be applied - try: - topology_st.standalone.modify_s(DN_LDBM, [(ldap.MOD_REPLACE, 'description', 'test')]) - except ldap.LDAPError as e: - log.fatal('Failed to apply change to critical plugin' + e.message['desc']) - assert False + ldbm_config = LDBMConfig(m1) + ldbm_config.replace('description', 'test') - while 1: - # + while True: # First run the tests with replication disabled, then rerun them with replication set up - # ############################################################################ # Test plugin functionality @@ -97,12 +102,53 @@ def test_dynamic_plugins(topology_st): log.info('Testing Dynamic Plugins Functionality' + msg + '...') log.info('####################################################################\n') - plugin_tests.test_all_plugins(topology_st.standalone) + acceptance_test.check_all_plugins(topology_m2) log.info('####################################################################') log.info('Successfully Tested Dynamic Plugins Functionality' + msg + '.') log.info('####################################################################\n') + if replication_run: + # We're done. + break + else: + log.info('Resume replication and run everything one more time') + topology_m2.resume_all_replicas() + + replication_run = True + msg = ' (replication enabled)' + time.sleep(1) + + ############################################################################ + # Check replication, and data are in sync + ############################################################################ + check_replicas(topology_m2) + + +def test_memory_corruption(topology_m2): + """Memory Corruption - Restart the plugins many times, and in different orders and test + functionality, and stability. This will excerise the internal + plugin linked lists, dse callbacks, and task handlers. + """ + + + m1 = topology_m2.ms["master1"] + msg = ' (no replication)' + replication_run = False + + # First part of the test should be without replication + topology_m2.pause_all_replicas() + + # First enable dynamic plugins + m1.config.replace('nsslapd-dynamic-plugins', 'on') + + # Test that critical plugins can be updated even though the change might not be applied + ldbm_config = LDBMConfig(m1) + ldbm_config.replace('description', 'test') + + while True: + # First run the tests with replication disabled, then rerun them with replication set up + ############################################################################ # Test the stability by exercising the internal lists, callabcks, and task handlers ############################################################################ @@ -113,24 +159,24 @@ def test_dynamic_plugins(topology_st): prev_plugin_test = None prev_prev_plugin_test = None - for plugin_test in plugin_tests.func_tests: + for plugin_test in acceptance_test.func_tests: # # Restart the plugin several times (and prev plugins) - work that linked list # - plugin_test(topology_st.standalone, "restart") + plugin_test(topology_m2, "restart") if prev_prev_plugin_test: - prev_prev_plugin_test(topology_st.standalone, "restart") + prev_prev_plugin_test(topology_m2, "restart") - plugin_test(topology_st.standalone, "restart") + plugin_test(topology_m2, "restart") if prev_plugin_test: - prev_plugin_test(topology_st.standalone, "restart") + prev_plugin_test(topology_m2, "restart") - plugin_test(topology_st.standalone, "restart") + plugin_test(topology_m2, "restart") # Now run the functional test - plugin_test(topology_st.standalone) + plugin_test(topology_m2, "dynamic") # Set the previous tests if prev_plugin_test: @@ -141,17 +187,58 @@ def test_dynamic_plugins(topology_st): log.info('Successfully Tested Dynamic Plugins for Memory Corruption' + msg + '.') log.info('####################################################################\n') - ############################################################################ - # Stress two plugins while restarting it, and while restarting other plugins. - # The goal is to not crash, and have the plugins work after stressing them. - ############################################################################ + if replication_run: + # We're done. + break + else: + log.info('Resume replication and run everything one more time') + topology_m2.resume_all_replicas() + + replication_run = True + msg = ' (replication enabled)' + time.sleep(1) + + ############################################################################ + # Check replication, and data are in sync + ############################################################################ + check_replicas(topology_m2) + + +def test_stress(topology_m2): + """Test dynamic plugins got + + Stress - Put the server under load that will trigger multiple plugins(MO, RI, DNA, etc) + Restart various plugins while these operations are going on. Perform this test + 5 times(stress_max_run). + """ + + m1 = topology_m2.ms["master1"] + msg = ' (no replication)' + replication_run = False + stress_max_runs = 5 + + # First part of the test should be without replication + topology_m2.pause_all_replicas() + + # First enable dynamic plugins + m1.config.replace('nsslapd-dynamic-plugins', 'on') + + # Test that critical plugins can be updated even though the change might not be applied + ldbm_config = LDBMConfig(m1) + ldbm_config.replace('description', 'test') + + while True: + # First run the tests with replication disabled, then rerun them with replication set up + + log.info('Do one run through all tests ' + msg + '...') + acceptance_test.check_all_plugins(topology_m2) log.info('####################################################################') log.info('Stressing Dynamic Plugins' + msg + '...') log.info('####################################################################\n') - stress_tests.configureMO(topology_st.standalone) - stress_tests.configureRI(topology_st.standalone) + stress_tests.configureMO(m1) + stress_tests.configureRI(m1) stress_count = 0 while stress_count < stress_max_runs: @@ -159,100 +246,94 @@ def test_dynamic_plugins(topology_st): log.info('Running stress test' + msg + '. Run (%d/%d)...' % (stress_count + 1, stress_max_runs)) log.info('####################################################################\n') - try: - # Launch three new threads to add a bunch of users - add_users = stress_tests.AddUsers(topology_st.standalone, 'employee', True) - add_users.start() - add_users2 = stress_tests.AddUsers(topology_st.standalone, 'entry', True) - add_users2.start() - add_users3 = stress_tests.AddUsers(topology_st.standalone, 'person', True) - add_users3.start() - time.sleep(1) - - # While we are adding users restart the MO plugin and an idle plugin - topology_st.standalone.plugins.disable(name=PLUGIN_MEMBER_OF) - topology_st.standalone.plugins.enable(name=PLUGIN_MEMBER_OF) - time.sleep(1) - topology_st.standalone.plugins.disable(name=PLUGIN_MEMBER_OF) - time.sleep(1) - topology_st.standalone.plugins.enable(name=PLUGIN_MEMBER_OF) - topology_st.standalone.plugins.disable(name=PLUGIN_LINKED_ATTRS) - topology_st.standalone.plugins.enable(name=PLUGIN_LINKED_ATTRS) - time.sleep(1) - topology_st.standalone.plugins.disable(name=PLUGIN_MEMBER_OF) - topology_st.standalone.plugins.enable(name=PLUGIN_MEMBER_OF) - time.sleep(2) - topology_st.standalone.plugins.disable(name=PLUGIN_MEMBER_OF) - time.sleep(1) - topology_st.standalone.plugins.enable(name=PLUGIN_MEMBER_OF) - topology_st.standalone.plugins.disable(name=PLUGIN_LINKED_ATTRS) - topology_st.standalone.plugins.enable(name=PLUGIN_LINKED_ATTRS) - topology_st.standalone.plugins.disable(name=PLUGIN_MEMBER_OF) - time.sleep(1) - topology_st.standalone.plugins.enable(name=PLUGIN_MEMBER_OF) - topology_st.standalone.plugins.disable(name=PLUGIN_MEMBER_OF) - topology_st.standalone.plugins.enable(name=PLUGIN_MEMBER_OF) - - # Wait for the 'adding' threads to complete - add_users.join() - add_users2.join() - add_users3.join() - - # Now launch three threads to delete the users - del_users = stress_tests.DelUsers(topology_st.standalone, 'employee') - del_users.start() - del_users2 = stress_tests.DelUsers(topology_st.standalone, 'entry') - del_users2.start() - del_users3 = stress_tests.DelUsers(topology_st.standalone, 'person') - del_users3.start() - time.sleep(1) - - # Restart both the MO, RI plugins during these deletes, and an idle plugin - topology_st.standalone.plugins.disable(name=PLUGIN_REFER_INTEGRITY) - topology_st.standalone.plugins.disable(name=PLUGIN_MEMBER_OF) - topology_st.standalone.plugins.enable(name=PLUGIN_MEMBER_OF) - topology_st.standalone.plugins.enable(name=PLUGIN_REFER_INTEGRITY) - time.sleep(1) - topology_st.standalone.plugins.disable(name=PLUGIN_REFER_INTEGRITY) - time.sleep(1) - topology_st.standalone.plugins.disable(name=PLUGIN_MEMBER_OF) - time.sleep(1) - topology_st.standalone.plugins.enable(name=PLUGIN_MEMBER_OF) - time.sleep(1) - topology_st.standalone.plugins.enable(name=PLUGIN_REFER_INTEGRITY) - topology_st.standalone.plugins.disable(name=PLUGIN_LINKED_ATTRS) - topology_st.standalone.plugins.enable(name=PLUGIN_LINKED_ATTRS) - topology_st.standalone.plugins.disable(name=PLUGIN_REFER_INTEGRITY) - topology_st.standalone.plugins.disable(name=PLUGIN_MEMBER_OF) - topology_st.standalone.plugins.enable(name=PLUGIN_MEMBER_OF) - topology_st.standalone.plugins.enable(name=PLUGIN_REFER_INTEGRITY) - time.sleep(2) - topology_st.standalone.plugins.disable(name=PLUGIN_REFER_INTEGRITY) - time.sleep(1) - topology_st.standalone.plugins.disable(name=PLUGIN_MEMBER_OF) - time.sleep(1) - topology_st.standalone.plugins.enable(name=PLUGIN_MEMBER_OF) - time.sleep(1) - topology_st.standalone.plugins.enable(name=PLUGIN_REFER_INTEGRITY) - topology_st.standalone.plugins.disable(name=PLUGIN_LINKED_ATTRS) - topology_st.standalone.plugins.enable(name=PLUGIN_LINKED_ATTRS) - - # Wait for the 'deleting' threads to complete - del_users.join() - del_users2.join() - del_users3.join() - - # Now make sure both the MO and RI plugins still work correctly - plugin_tests.func_tests[8](topology_st.standalone) # RI plugin - plugin_tests.func_tests[5](topology_st.standalone) # MO plugin - - # Cleanup the stress tests - stress_tests.cleanup(topology_st.standalone) - - except: - log.info('Stress test failed!') - if replication_run: - repl_fail(replica_inst) + # Launch three new threads to add a bunch of users + add_users = stress_tests.AddUsers(m1, 'employee', True) + add_users.start() + add_users2 = stress_tests.AddUsers(m1, 'entry', True) + add_users2.start() + add_users3 = stress_tests.AddUsers(m1, 'person', True) + add_users3.start() + time.sleep(1) + + # While we are adding users restart the MO plugin and an idle plugin + m1.plugins.disable(name=PLUGIN_MEMBER_OF) + m1.plugins.enable(name=PLUGIN_MEMBER_OF) + time.sleep(1) + m1.plugins.disable(name=PLUGIN_MEMBER_OF) + time.sleep(1) + m1.plugins.enable(name=PLUGIN_MEMBER_OF) + m1.plugins.disable(name=PLUGIN_LINKED_ATTRS) + m1.plugins.enable(name=PLUGIN_LINKED_ATTRS) + time.sleep(1) + m1.plugins.disable(name=PLUGIN_MEMBER_OF) + m1.plugins.enable(name=PLUGIN_MEMBER_OF) + time.sleep(2) + m1.plugins.disable(name=PLUGIN_MEMBER_OF) + time.sleep(1) + m1.plugins.enable(name=PLUGIN_MEMBER_OF) + m1.plugins.disable(name=PLUGIN_LINKED_ATTRS) + m1.plugins.enable(name=PLUGIN_LINKED_ATTRS) + m1.plugins.disable(name=PLUGIN_MEMBER_OF) + time.sleep(1) + m1.plugins.enable(name=PLUGIN_MEMBER_OF) + m1.plugins.disable(name=PLUGIN_MEMBER_OF) + m1.plugins.enable(name=PLUGIN_MEMBER_OF) + + # Wait for the 'adding' threads to complete + add_users.join() + add_users2.join() + add_users3.join() + + # Now launch three threads to delete the users + del_users = stress_tests.DelUsers(m1, 'employee') + del_users.start() + del_users2 = stress_tests.DelUsers(m1, 'entry') + del_users2.start() + del_users3 = stress_tests.DelUsers(m1, 'person') + del_users3.start() + time.sleep(1) + + # Restart both the MO, RI plugins during these deletes, and an idle plugin + m1.plugins.disable(name=PLUGIN_REFER_INTEGRITY) + m1.plugins.disable(name=PLUGIN_MEMBER_OF) + m1.plugins.enable(name=PLUGIN_MEMBER_OF) + m1.plugins.enable(name=PLUGIN_REFER_INTEGRITY) + time.sleep(1) + m1.plugins.disable(name=PLUGIN_REFER_INTEGRITY) + time.sleep(1) + m1.plugins.disable(name=PLUGIN_MEMBER_OF) + time.sleep(1) + m1.plugins.enable(name=PLUGIN_MEMBER_OF) + time.sleep(1) + m1.plugins.enable(name=PLUGIN_REFER_INTEGRITY) + m1.plugins.disable(name=PLUGIN_LINKED_ATTRS) + m1.plugins.enable(name=PLUGIN_LINKED_ATTRS) + m1.plugins.disable(name=PLUGIN_REFER_INTEGRITY) + m1.plugins.disable(name=PLUGIN_MEMBER_OF) + m1.plugins.enable(name=PLUGIN_MEMBER_OF) + m1.plugins.enable(name=PLUGIN_REFER_INTEGRITY) + time.sleep(2) + m1.plugins.disable(name=PLUGIN_REFER_INTEGRITY) + time.sleep(1) + m1.plugins.disable(name=PLUGIN_MEMBER_OF) + time.sleep(1) + m1.plugins.enable(name=PLUGIN_MEMBER_OF) + time.sleep(1) + m1.plugins.enable(name=PLUGIN_REFER_INTEGRITY) + m1.plugins.disable(name=PLUGIN_LINKED_ATTRS) + m1.plugins.enable(name=PLUGIN_LINKED_ATTRS) + + # Wait for the 'deleting' threads to complete + del_users.join() + del_users2.join() + del_users3.join() + + # Now make sure both the MO and RI plugins still work correctly + acceptance_test.func_tests[8](topology_m2, "dynamic") # RI plugin + acceptance_test.func_tests[5](topology_m2, "dynamic") # MO plugin + + # Cleanup the stress tests + stress_tests.cleanup(m1) stress_count += 1 log.info('####################################################################') @@ -264,166 +345,17 @@ def test_dynamic_plugins(topology_st): # We're done. break else: - # - # Enable replication and run everything one more time - # - log.info('Setting up replication, and rerunning the tests...\n') - - # Create replica instance - replica_inst = DirSrv(verbose=False) - args_instance[SER_HOST] = LOCALHOST - args_instance[SER_PORT] = REPLICA_PORT - args_instance[SER_SERVERID_PROP] = 'replica' - args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX - - args_replica_inst = args_instance.copy() - replica_inst.allocate(args_replica_inst) - replica_inst.create() - replica_inst.open() - - try: - topology_st.standalone.replica.enableReplication(suffix=DEFAULT_SUFFIX, - role=ReplicaRole.MASTER, - replicaId=1) - replica_inst.replica.enableReplication(suffix=DEFAULT_SUFFIX, - role=ReplicaRole.CONSUMER, - replicaId=65535) - properties = {RA_NAME: r'to_replica', - RA_BINDDN: defaultProperties[REPLICATION_BIND_DN], - RA_BINDPW: defaultProperties[REPLICATION_BIND_PW], - RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD], - RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]} - repl_agreement = topology_st.standalone.agreement.create(suffix=DEFAULT_SUFFIX, - host=LOCALHOST, - port=REPLICA_PORT, - properties=properties) - - if not repl_agreement: - log.fatal("Fail to create a replica agreement") - repl_fail(replica_inst) - topology_st.standalone.agreement.init(DEFAULT_SUFFIX, LOCALHOST, REPLICA_PORT) - topology_st.standalone.waitForReplInit(repl_agreement) - except: - log.info('Failed to setup replication!') - repl_fail(replica_inst) + log.info('Resume replication and run everything one more time') + topology_m2.resume_all_replicas() replication_run = True msg = ' (replication enabled)' time.sleep(1) ############################################################################ - # Check replication, and data are in sync, and remove the instance - ############################################################################ - - log.info('Checking if replication is in sync...') - - try: - # Grab master's max CSN - entry = topology_st.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, RUV_FILTER) - if not entry: - log.error('Failed to find db tombstone entry from master') - repl_fail(replica_inst) - elements = entry[0].getValues('nsds50ruv') - for ruv in elements: - if 'replica 1' in ruv: - parts = ruv.split() - if len(parts) == 5: - master_maxcsn = parts[4] - break - else: - log.error('RUV is incomplete') - repl_fail(replica_inst) - if master_maxcsn == 0: - log.error('Failed to find maxcsn on master') - repl_fail(replica_inst) - - except ldap.LDAPError as e: - log.fatal('Unable to search masterfor db tombstone: ' + e.message['desc']) - repl_fail(replica_inst) - - # Loop on the consumer - waiting for it to catch up - count = 0 - insync = False - while count < 60: - try: - # Grab master's max CSN - entry = replica_inst.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, RUV_FILTER) - if not entry: - log.error('Failed to find db tombstone entry on consumer') - repl_fail(replica_inst) - elements = entry[0].getValues('nsds50ruv') - for ruv in elements: - if 'replica 1' in ruv: - parts = ruv.split() - if len(parts) == 5: - replica_maxcsn = parts[4] - break - if replica_maxcsn == 0: - log.error('Failed to find maxcsn on consumer') - repl_fail(replica_inst) - except ldap.LDAPError as e: - log.fatal('Unable to search for db tombstone on consumer: ' + e.message['desc']) - repl_fail(replica_inst) - - if master_maxcsn == replica_maxcsn: - insync = True - log.info('Replication is in sync.\n') - break - count += 1 - time.sleep(1) - - # Report on replication status - if not insync: - log.error('Consumer not in sync with master!') - repl_fail(replica_inst) - - # - # Verify the databases are identical. There should not be any "user, entry, employee" entries - # - log.info('Checking if the data is the same between the replicas...') - - # Check the master - try: - entries = topology_st.standalone.search_s(DEFAULT_SUFFIX, - ldap.SCOPE_SUBTREE, - "(|(uid=person*)(uid=entry*)(uid=employee*))") - if len(entries) > 0: - log.error('Master database has incorrect data set!\n') - repl_fail(replica_inst) - except ldap.LDAPError as e: - log.fatal('Unable to search db on master: ' + e.message['desc']) - repl_fail(replica_inst) - - # Check the consumer - try: - entries = replica_inst.search_s(DEFAULT_SUFFIX, - ldap.SCOPE_SUBTREE, - "(|(uid=person*)(uid=entry*)(uid=employee*))") - if len(entries) > 0: - log.error('Consumer database in not consistent with master database') - repl_fail(replica_inst) - except ldap.LDAPError as e: - log.fatal('Unable to search db on consumer: ' + e.message['desc']) - repl_fail(replica_inst) - - log.info('Data is consistent across the replicas.\n') - - log.info('####################################################################') - log.info('Replication consistency test passed') - log.info('####################################################################\n') - - # Remove the replica instance - replica_inst.delete() - - ############################################################################ - # We made it to the end! + # Check replication, and data are in sync ############################################################################ - - log.info('#####################################################') - log.info('#####################################################') - log.info("Dynamic Plugins Testsuite: Completed Successfully!") - log.info('#####################################################') - log.info('#####################################################\n') + check_replicas(topology_m2) if __name__ == '__main__': diff --git a/dirsrvtests/tests/suites/dynamic_plugins/plugin_tests.py b/dirsrvtests/tests/suites/dynamic_plugins/plugin_tests.py deleted file mode 100644 index 293c167..0000000 --- a/dirsrvtests/tests/suites/dynamic_plugins/plugin_tests.py +++ /dev/null @@ -1,2485 +0,0 @@ -# --- BEGIN COPYRIGHT BLOCK --- -# Copyright (C) 2016 Red Hat, Inc. -# All rights reserved. -# -# License: GPL (version 3 or any later version). -# See LICENSE for details. -# --- END COPYRIGHT BLOCK --- -# -''' -Created on Dec 09, 2014 - -@author: mreynolds -''' -import logging -from lib389 import DirSrv -from lib389.tasks import * -from lib389.properties import * -from lib389._constants import (DEFAULT_SUFFIX, PLUGIN_ACCT_USABILITY, PLUGIN_ACCT_POLICY, - PLUGIN_ATTR_UNIQUENESS, PLUGIN_AUTOMEMBER, PLUGIN_DNA, - PLUGIN_LINKED_ATTRS, PLUGIN_MEMBER_OF, DN_CONFIG, - PLUGIN_MANAGED_ENTRY, PLUGIN_PASSTHRU, PLUGIN_REFER_INTEGRITY, - PLUGIN_RETRO_CHANGELOG, PLUGIN_ROOTDN_ACCESS, DN_DM, PASSWORD, - LOCALHOST, RETROCL_SUFFIX, args_instance) - -log = logging.getLogger(__name__) - -USER1_DN = 'uid=user1,' + DEFAULT_SUFFIX -USER2_DN = 'uid=user2,' + DEFAULT_SUFFIX -USER3_DN = 'uid=user3,' + DEFAULT_SUFFIX -BUSER1_DN = 'uid=user1,ou=branch1,' + DEFAULT_SUFFIX -BUSER2_DN = 'uid=user2,ou=branch2,' + DEFAULT_SUFFIX -BUSER3_DN = 'uid=user3,ou=branch2,' + DEFAULT_SUFFIX -BRANCH1_DN = 'ou=branch1,' + DEFAULT_SUFFIX -BRANCH2_DN = 'ou=branch2,' + DEFAULT_SUFFIX -GROUP_OU = 'ou=groups,' + DEFAULT_SUFFIX -PEOPLE_OU = 'ou=people,' + DEFAULT_SUFFIX -GROUP_DN = 'cn=group,' + DEFAULT_SUFFIX -CONFIG_AREA = 'nsslapd-pluginConfigArea' - -''' - Functional tests for each plugin - - Test: - plugin restarts (test when on and off) - plugin config validation - plugin dependencies - plugin functionality (including plugin tasks) -''' - - -################################################################################ -# -# Test Plugin Dependency -# -################################################################################ -def test_dependency(inst, plugin): - """ - Set the "account usabilty" plugin to depend on this plugin. This plugin - is generic, always enabled, and perfect for our testing - """ - - try: - inst.modify_s('cn=' + PLUGIN_ACCT_USABILITY + ',cn=plugins,cn=config', - [(ldap.MOD_REPLACE, 'nsslapd-plugin-depends-on-named', plugin)]) - - except ldap.LDAPError as e: - log.fatal('test_dependency: Failed to modify ' + PLUGIN_ACCT_USABILITY + ': error ' + e.message['desc']) - assert False - - try: - inst.modify_s('cn=' + plugin + ',cn=plugins,cn=config', - [(ldap.MOD_REPLACE, 'nsslapd-pluginenabled', 'off')]) - - except ldap.UNWILLING_TO_PERFORM: - # failed as expected - pass - else: - # Incorrectly succeeded - log.fatal('test_dependency: Plugin dependency check failed (%s)' % plugin) - assert False - - # Now undo the change - try: - inst.modify_s('cn=' + PLUGIN_ACCT_USABILITY + ',cn=plugins,cn=config', - [(ldap.MOD_DELETE, 'nsslapd-plugin-depends-on-named', None)]) - except ldap.LDAPError as e: - log.fatal('test_dependency: Failed to reset ' + plugin + ': error ' + e.message['desc']) - assert False - - -################################################################################ -# -# Wait for task to complete -# -################################################################################ -def wait_for_task(conn, task_dn): - finished = False - exitcode = 0 - count = 0 - while count < 60: - try: - task_entry = conn.search_s(task_dn, ldap.SCOPE_BASE, 'objectclass=*') - if not task_entry: - log.fatal('wait_for_task: Search failed to find task: ' + task_dn) - assert False - if task_entry[0].hasAttr('nstaskexitcode'): - # task is done - exitcode = task_entry[0].nsTaskExitCode - finished = True - break - except ldap.LDAPError as e: - log.fatal('wait_for_task: Search failed: ' + e.message['desc']) - assert False - - time.sleep(1) - count += 1 - if not finished: - log.fatal('wait_for_task: Task (%s) did not complete!' % task_dn) - assert False - - return exitcode - - -################################################################################ -# -# Test Account Policy Plugin (0) -# -################################################################################ -def test_acctpolicy(inst, args=None): - # stop the plugin, and start it - inst.plugins.disable(name=PLUGIN_ACCT_POLICY) - inst.plugins.enable(name=PLUGIN_ACCT_POLICY) - - if args == "restart": - return True - - CONFIG_DN = 'cn=config,cn=Account Policy Plugin,cn=plugins,cn=config' - - log.info('Testing ' + PLUGIN_ACCT_POLICY + '...') - - ############################################################################ - # Configure plugin - ############################################################################ - - # Add the config entry - try: - inst.add_s(Entry((CONFIG_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'cn': 'config', - 'alwaysrecordlogin': 'yes', - 'stateattrname': 'lastLoginTime' - }))) - except ldap.ALREADY_EXISTS: - try: - inst.modify_s(CONFIG_DN, - [(ldap.MOD_REPLACE, 'alwaysrecordlogin', 'yes'), - (ldap.MOD_REPLACE, 'stateattrname', 'lastLoginTime')]) - except ldap.LDAPError as e: - log.fatal('test_acctpolicy: Failed to modify config entry: error ' + e.message['desc']) - assert False - except ldap.LDAPError as e: - log.fatal('test_acctpolicy: Failed to add config entry: error ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin - ############################################################################ - - # Add an entry - time.sleep(1) - try: - inst.add_s(Entry((USER1_DN, {'objectclass': "top extensibleObject".split(), - 'sn': '1', - 'cn': 'user 1', - 'uid': 'user1', - 'userpassword': 'password'}))) - except ldap.LDAPError as e: - log.fatal('test_acctpolicy: Failed to add test user' + USER1_DN + ': error ' + e.message['desc']) - assert False - - # bind as user - try: - inst.simple_bind_s(USER1_DN, "password") - except ldap.LDAPError as e: - log.fatal('test_acctpolicy: Failed to bind as user1: ' + e.message['desc']) - assert False - - # Bind as Root DN - time.sleep(1) - try: - inst.simple_bind_s(DN_DM, PASSWORD) - except ldap.LDAPError as e: - log.fatal('test_acctpolicy: Failed to bind as rootDN: ' + e.message['desc']) - assert False - - # Check lastLoginTime of USER1 - try: - entries = inst.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, 'lastLoginTime=*') - if not entries: - log.fatal('test_acctpolicy: Search failed to find an entry with lastLoginTime.') - assert False - except ldap.LDAPError as e: - log.fatal('test_acctpolicy: Search failed: ' + e.message['desc']) - assert False - - ############################################################################ - # Change config - change the stateAttrName to a new attribute - ############################################################################ - - try: - inst.modify_s(CONFIG_DN, [(ldap.MOD_REPLACE, 'stateattrname', 'testLastLoginTime')]) - - except ldap.LDAPError as e: - log.fatal('test_acctpolicy: Failed to modify config entry: error ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin - ############################################################################ - - time.sleep(1) - # login as user - try: - inst.simple_bind_s(USER1_DN, "password") - except ldap.LDAPError as e: - log.fatal('test_acctpolicy: Failed to bind(2nd) as user1: ' + e.message['desc']) - assert False - - time.sleep(1) - # Bind as Root DN - try: - inst.simple_bind_s(DN_DM, PASSWORD) - except ldap.LDAPError as e: - log.fatal('test_acctpolicy: Failed to bind as rootDN: ' + e.message['desc']) - assert False - - # Check testLastLoginTime was added to USER1 - try: - entries = inst.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, '(testLastLoginTime=*)') - if not entries: - log.fatal('test_acctpolicy: Search failed to find an entry with testLastLoginTime.') - assert False - except ldap.LDAPError as e: - log.fatal('test_acctpolicy: Search failed: ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin dependency - ############################################################################ - - test_dependency(inst, PLUGIN_ACCT_POLICY) - - ############################################################################ - # Cleanup - ############################################################################ - - try: - inst.delete_s(USER1_DN) - except ldap.LDAPError as e: - log.fatal('test_acctpolicy: Failed to delete test entry: ' + e.message['desc']) - assert False - - ############################################################################ - # Test passed - ############################################################################ - - log.info('test_acctpolicy: PASS\n') - - return - - -################################################################################ -# -# Test Attribute Uniqueness Plugin (1) -# -################################################################################ -def test_attruniq(inst, args=None): - # stop the plugin, and start it - inst.plugins.disable(name=PLUGIN_ATTR_UNIQUENESS) - inst.plugins.enable(name=PLUGIN_ATTR_UNIQUENESS) - - if args == "restart": - return - - log.info('Testing ' + PLUGIN_ATTR_UNIQUENESS + '...') - - ############################################################################ - # Configure plugin - ############################################################################ - - try: - inst.modify_s('cn=' + PLUGIN_ATTR_UNIQUENESS + ',cn=plugins,cn=config', - [(ldap.MOD_REPLACE, 'uniqueness-attribute-name', 'uid')]) - - except ldap.LDAPError as e: - log.fatal('test_attruniq: Failed to configure plugin for "uid": error ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin - ############################################################################ - - # Add an entry - try: - inst.add_s(Entry((USER1_DN, {'objectclass': "top extensibleObject".split(), - 'sn': '1', - 'cn': 'user 1', - 'uid': 'user1', - 'mail': 'user1@example.com', - 'mailAlternateAddress': 'user1@alt.example.com', - 'userpassword': 'password'}))) - except ldap.LDAPError as e: - log.fatal('test_attruniq: Failed to add test user' + USER1_DN + ': error ' + e.message['desc']) - assert False - - # Add an entry with a duplicate "uid" - try: - inst.add_s(Entry((USER2_DN, {'objectclass': "top extensibleObject".split(), - 'sn': '2', - 'cn': 'user 2', - 'uid': 'user2', - 'uid': 'user1', - 'userpassword': 'password'}))) - - except ldap.CONSTRAINT_VIOLATION: - pass - else: - log.fatal('test_attruniq: Adding of 2nd entry(uid) incorrectly succeeded') - assert False - - ############################################################################ - # Change config to use "mail" instead of "uid" - ############################################################################ - - try: - inst.modify_s('cn=' + PLUGIN_ATTR_UNIQUENESS + ',cn=plugins,cn=config', - [(ldap.MOD_REPLACE, 'uniqueness-attribute-name', 'mail')]) - - except ldap.LDAPError as e: - log.fatal('test_attruniq: Failed to configure plugin for "mail": error ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin - Add an entry, that has a duplicate "mail" value - ############################################################################ - - try: - inst.add_s(Entry((USER2_DN, {'objectclass': "top extensibleObject".split(), - 'sn': '2', - 'cn': 'user 2', - 'uid': 'user2', - 'mail': 'user1@example.com', - 'userpassword': 'password'}))) - except ldap.CONSTRAINT_VIOLATION: - pass - else: - log.fatal('test_attruniq: Adding of 2nd entry(mail) incorrectly succeeded') - assert False - - ############################################################################ - # Reconfigure plugin for mail and mailAlternateAddress - ############################################################################ - - try: - inst.modify_s('cn=' + PLUGIN_ATTR_UNIQUENESS + ',cn=plugins,cn=config', - [(ldap.MOD_REPLACE, 'uniqueness-attribute-name', 'mail'), - (ldap.MOD_ADD, 'uniqueness-attribute-name', - 'mailAlternateAddress')]) - - except ldap.LDAPError as e: - log.error( - 'test_attruniq: Failed to reconfigure plugin for "mail mailAlternateAddress": error ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin - Add an entry, that has a duplicate "mail" value - ############################################################################ - - try: - inst.add_s(Entry((USER2_DN, {'objectclass': "top extensibleObject".split(), - 'sn': '2', - 'cn': 'user 2', - 'uid': 'user2', - 'mail': 'user1@example.com', - 'userpassword': 'password'}))) - except ldap.CONSTRAINT_VIOLATION: - pass - else: - log.error('test_attruniq: Adding of 3rd entry(mail) incorrectly succeeded') - assert False - - ############################################################################ - # Test plugin - Add an entry, that has a duplicate "mailAlternateAddress" value - ############################################################################ - - try: - inst.add_s(Entry((USER2_DN, {'objectclass': "top extensibleObject".split(), - 'sn': '2', - 'cn': 'user 2', - 'uid': 'user2', - 'mailAlternateAddress': 'user1@alt.example.com', - 'userpassword': 'password'}))) - except ldap.CONSTRAINT_VIOLATION: - pass - else: - log.error('test_attruniq: Adding of 4th entry(mailAlternateAddress) incorrectly succeeded') - assert False - - ############################################################################ - # Test plugin - Add an entry, that has a duplicate "mail" value conflicting mailAlternateAddress - ############################################################################ - - try: - inst.add_s(Entry((USER2_DN, {'objectclass': "top extensibleObject".split(), - 'sn': '2', - 'cn': 'user 2', - 'uid': 'user2', - 'mail': 'user1@alt.example.com', - 'userpassword': 'password'}))) - except ldap.CONSTRAINT_VIOLATION: - pass - else: - log.error('test_attruniq: Adding of 5th entry(mailAlternateAddress) incorrectly succeeded') - assert False - - ############################################################################ - # Test plugin - Add an entry, that has a duplicate "mailAlternateAddress" conflicting mail - ############################################################################ - - try: - inst.add_s(Entry((USER2_DN, {'objectclass': "top extensibleObject".split(), - 'sn': '2', - 'cn': 'user 2', - 'uid': 'user2', - 'mailAlternateAddress': 'user1@example.com', - 'userpassword': 'password'}))) - except ldap.CONSTRAINT_VIOLATION: - pass - else: - log.error('test_attruniq: Adding of 6th entry(mail) incorrectly succeeded') - assert False - - ############################################################################ - # Test plugin dependency - ############################################################################ - - test_dependency(inst, PLUGIN_ATTR_UNIQUENESS) - - ############################################################################ - # Cleanup - ############################################################################ - - try: - inst.delete_s(USER1_DN) - except ldap.LDAPError as e: - log.fatal('test_attruniq: Failed to delete test entry: ' + e.message['desc']) - assert False - - ############################################################################ - # Test passed - ############################################################################ - - log.info('test_attruniq: PASS\n') - return - - -################################################################################ -# -# Test Auto Membership Plugin (2) -# -################################################################################ -def test_automember(inst, args=None): - # stop the plugin, and start it - inst.plugins.disable(name=PLUGIN_AUTOMEMBER) - inst.plugins.enable(name=PLUGIN_AUTOMEMBER) - - if args == "restart": - return - - CONFIG_DN = 'cn=config,cn=' + PLUGIN_AUTOMEMBER + ',cn=plugins,cn=config' - - log.info('Testing ' + PLUGIN_AUTOMEMBER + '...') - - ############################################################################ - # Configure plugin - ############################################################################ - - # Add the automember group - try: - inst.add_s(Entry((GROUP_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'cn': 'group' - }))) - except ldap.LDAPError as e: - log.fatal('test_automember: Failed to add group: error ' + e.message['desc']) - assert False - - # Add ou=branch1 - try: - inst.add_s(Entry((BRANCH1_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'ou': 'branch1' - }))) - except ldap.LDAPError as e: - log.fatal('test_automember: Failed to add branch1: error ' + e.message['desc']) - assert False - - # Add ou=branch2 - try: - inst.add_s(Entry((BRANCH2_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'ou': 'branch2' - }))) - except ldap.LDAPError as e: - log.fatal('test_automember: Failed to add branch2: error ' + e.message['desc']) - assert False - - # Add the automember config entry - try: - inst.add_s(Entry((CONFIG_DN, { - 'objectclass': 'top autoMemberDefinition'.split(), - 'cn': 'config', - 'autoMemberScope': 'ou=branch1,' + DEFAULT_SUFFIX, - 'autoMemberFilter': 'objectclass=top', - 'autoMemberDefaultGroup': 'cn=group,' + DEFAULT_SUFFIX, - 'autoMemberGroupingAttr': 'member:dn' - }))) - except ldap.LDAPError as e: - log.fatal('test_automember: Failed to add config entry: error ' + e.message['desc']) - assert False - - ############################################################################ - # Test the plugin - ############################################################################ - - # Add a user that should get added to the group - try: - inst.add_s(Entry((BUSER1_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'uid': 'user1' - }))) - except ldap.LDAPError as e: - log.fatal('test_automember: Failed to add user: error ' + e.message['desc']) - assert False - - # Check the group - try: - entries = inst.search_s(GROUP_DN, ldap.SCOPE_BASE, - '(member=' + BUSER1_DN + ')') - if not entries: - log.fatal('test_automember: Search failed to find member user1') - assert False - except ldap.LDAPError as e: - log.fatal('test_automember: Search failed: ' + e.message['desc']) - assert False - - ############################################################################ - # Change config - ############################################################################ - - try: - inst.modify_s(CONFIG_DN, - [(ldap.MOD_REPLACE, 'autoMemberGroupingAttr', 'uniquemember:dn'), - (ldap.MOD_REPLACE, 'autoMemberScope', 'ou=branch2,' + DEFAULT_SUFFIX)]) - - except ldap.LDAPError as e: - log.fatal('test_automember: Failed to modify config entry: error ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin - ############################################################################ - - # Add a user that should get added to the group - try: - inst.add_s(Entry((BUSER2_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'uid': 'user2' - }))) - except ldap.LDAPError as e: - log.fatal('test_automember: Failed to user to branch2: error ' + e.message['desc']) - assert False - - # Check the group - try: - entries = inst.search_s(GROUP_DN, ldap.SCOPE_BASE, - '(uniquemember=' + BUSER2_DN + ')') - if not entries: - log.fatal('test_automember: Search failed to find uniquemember user2') - assert False - except ldap.LDAPError as e: - log.fatal('test_automember: Search failed: ' + e.message['desc']) - assert False - - ############################################################################ - # Test Task - ############################################################################ - - # Disable plugin - inst.plugins.disable(name=PLUGIN_AUTOMEMBER) - - # Add an entry that should be picked up by automember - verify it is not(yet) - try: - inst.add_s(Entry((BUSER3_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'uid': 'user3' - }))) - except ldap.LDAPError as e: - log.fatal('test_automember: Failed to user3 to branch2: error ' + e.message['desc']) - assert False - - # Check the group - uniquemember should not exist - try: - entries = inst.search_s(GROUP_DN, ldap.SCOPE_BASE, - '(uniquemember=' + BUSER3_DN + ')') - if entries: - log.fatal('test_automember: user3 was incorrectly added to the group') - assert False - except ldap.LDAPError as e: - log.fatal('test_automember: Search failed: ' + e.message['desc']) - assert False - - # Enable plugin - inst.plugins.enable(name=PLUGIN_AUTOMEMBER) - - TASK_DN = 'cn=task-' + str(int(time.time())) + ',cn=automember rebuild membership,cn=tasks,cn=config' - # Add the task - try: - inst.add_s(Entry((TASK_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'basedn': 'ou=branch2,' + DEFAULT_SUFFIX, - 'filter': 'objectclass=top'}))) - except ldap.LDAPError as e: - log.fatal('test_automember: Failed to add task: error ' + e.message['desc']) - assert False - - wait_for_task(inst, TASK_DN) - - # Verify the fixup task worked - try: - entries = inst.search_s(GROUP_DN, ldap.SCOPE_BASE, - '(uniquemember=' + BUSER3_DN + ')') - if not entries: - log.fatal('test_automember: user3 was not added to the group') - assert False - except ldap.LDAPError as e: - log.fatal('test_automember: Search failed: ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin dependency - ############################################################################ - - test_dependency(inst, PLUGIN_AUTOMEMBER) - - ############################################################################ - # Cleanup - ############################################################################ - - try: - inst.delete_s(BUSER1_DN) - except ldap.LDAPError as e: - log.fatal('test_automember: Failed to delete test entry1: ' + e.message['desc']) - assert False - - try: - inst.delete_s(BUSER2_DN) - except ldap.LDAPError as e: - log.fatal('test_automember: Failed to delete test entry2: ' + e.message['desc']) - assert False - - try: - inst.delete_s(BUSER3_DN) - except ldap.LDAPError as e: - log.fatal('test_automember: Failed to delete test entry3: ' + e.message['desc']) - assert False - - try: - inst.delete_s(BRANCH1_DN) - except ldap.LDAPError as e: - log.fatal('test_automember: Failed to delete branch1: ' + e.message['desc']) - assert False - - try: - inst.delete_s(BRANCH2_DN) - except ldap.LDAPError as e: - log.fatal('test_automember: Failed to delete test branch2: ' + e.message['desc']) - assert False - - try: - inst.delete_s(GROUP_DN) - except ldap.LDAPError as e: - log.fatal('test_automember: Failed to delete test group: ' + e.message['desc']) - assert False - - try: - inst.delete_s(CONFIG_DN) - except ldap.LDAPError as e: - log.fatal('test_automember: Failed to delete plugin config entry: ' + e.message['desc']) - assert False - - ############################################################################ - # Test passed - ############################################################################ - - log.info('test_automember: PASS\n') - return - - -################################################################################ -# -# Test DNA Plugin (3) -# -################################################################################ -def test_dna(inst, args=None): - # stop the plugin, and start it - inst.plugins.disable(name=PLUGIN_DNA) - inst.plugins.enable(name=PLUGIN_DNA) - - if args == "restart": - return - - CONFIG_DN = 'cn=config,cn=' + PLUGIN_DNA + ',cn=plugins,cn=config' - - log.info('Testing ' + PLUGIN_DNA + '...') - - ############################################################################ - # Configure plugin - ############################################################################ - - try: - inst.add_s(Entry((CONFIG_DN, { - 'objectclass': 'top dnaPluginConfig'.split(), - 'cn': 'config', - 'dnatype': 'uidNumber', - 'dnafilter': '(objectclass=top)', - 'dnascope': DEFAULT_SUFFIX, - 'dnaMagicRegen': '-1', - 'dnaMaxValue': '50000', - 'dnaNextValue': '1' - }))) - except ldap.ALREADY_EXISTS: - try: - inst.modify_s(CONFIG_DN, [(ldap.MOD_REPLACE, 'dnaNextValue', '1'), - (ldap.MOD_REPLACE, 'dnaMagicRegen', '-1')]) - except ldap.LDAPError as e: - log.fatal('test_dna: Failed to set the DNA plugin: error ' + e.message['desc']) - assert False - except ldap.LDAPError as e: - log.fatal('test_dna: Failed to add config entry: error ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin - ############################################################################ - - try: - inst.add_s(Entry((USER1_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'uid': 'user1' - }))) - except ldap.LDAPError as e: - log.fatal('test_dna: Failed to user1: error ' + e.message['desc']) - assert False - - # See if the entry now has the new uidNumber assignment - uidNumber=1 - try: - entries = inst.search_s(USER1_DN, ldap.SCOPE_BASE, '(uidNumber=1)') - if not entries: - log.fatal('test_dna: user1 was not updated - (looking for uidNumber: 1)') - assert False - except ldap.LDAPError as e: - log.fatal('test_dna: Search for user1 failed: ' + e.message['desc']) - assert False - - # Test the magic regen value - try: - inst.modify_s(USER1_DN, [(ldap.MOD_REPLACE, 'uidNumber', '-1')]) - except ldap.LDAPError as e: - log.fatal('test_dna: Failed to set the magic reg value: error ' + e.message['desc']) - assert False - - # See if the entry now has the new uidNumber assignment - uidNumber=2 - try: - entries = inst.search_s(USER1_DN, ldap.SCOPE_BASE, '(uidNumber=2)') - if not entries: - log.fatal('test_dna: user1 was not updated (looking for uidNumber: 2)') - assert False - except ldap.LDAPError as e: - log.fatal('test_dna: Search for user1 failed: ' + e.message['desc']) - assert False - - ################################################################################ - # Change the config - ################################################################################ - - try: - inst.modify_s(CONFIG_DN, [(ldap.MOD_REPLACE, 'dnaMagicRegen', '-2')]) - except ldap.LDAPError as e: - log.fatal('test_dna: Failed to set the magic reg value to -2: error ' + e.message['desc']) - assert False - - ################################################################################ - # Test plugin - ################################################################################ - - # Test the magic regen value - try: - inst.modify_s(USER1_DN, [(ldap.MOD_REPLACE, 'uidNumber', '-2')]) - except ldap.LDAPError as e: - log.fatal('test_dna: Failed to set the magic reg value: error ' + e.message['desc']) - assert False - - # See if the entry now has the new uidNumber assignment - uidNumber=3 - try: - entries = inst.search_s(USER1_DN, ldap.SCOPE_BASE, '(uidNumber=3)') - if not entries: - log.fatal('test_dna: user1 was not updated (looking for uidNumber: 3)') - assert False - except ldap.LDAPError as e: - log.fatal('test_dna: Search for user1 failed: ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin dependency - ############################################################################ - - test_dependency(inst, PLUGIN_AUTOMEMBER) - - ############################################################################ - # Cleanup - ############################################################################ - - try: - inst.delete_s(USER1_DN) - except ldap.LDAPError as e: - log.fatal('test_dna: Failed to delete test entry1: ' + e.message['desc']) - assert False - - inst.plugins.disable(name=PLUGIN_DNA) - - ############################################################################ - # Test passed - ############################################################################ - - log.info('test_dna: PASS\n') - - return - - -################################################################################ -# -# Test Linked Attrs Plugin (4) -# -################################################################################ -def test_linkedattrs(inst, args=None): - # stop the plugin, and start it - inst.plugins.disable(name=PLUGIN_LINKED_ATTRS) - inst.plugins.enable(name=PLUGIN_LINKED_ATTRS) - - if args == "restart": - return - - CONFIG_DN = 'cn=config,cn=' + PLUGIN_LINKED_ATTRS + ',cn=plugins,cn=config' - - log.info('Testing ' + PLUGIN_LINKED_ATTRS + '...') - - ############################################################################ - # Configure plugin - ############################################################################ - - # Add test entries - try: - inst.add_s(Entry((USER1_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'uid': 'user1' - }))) - except ldap.LDAPError as e: - log.fatal('test_linkedattrs: Failed to user1: error ' + e.message['desc']) - assert False - - try: - inst.add_s(Entry((USER2_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'uid': 'user2' - }))) - except ldap.LDAPError as e: - log.fatal('test_linkedattrs: Failed to user1: error ' + e.message['desc']) - assert False - - # Add the linked attrs config entry - try: - inst.add_s(Entry((CONFIG_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'cn': 'config', - 'linkType': 'directReport', - 'managedType': 'manager' - }))) - except ldap.LDAPError as e: - log.fatal('test_linkedattrs: Failed to add config entry: error ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin - ############################################################################ - - # Set "directReport" should add "manager" to the other entry - try: - inst.modify_s(USER1_DN, [(ldap.MOD_REPLACE, 'directReport', USER2_DN)]) - except ldap.LDAPError as e: - log.fatal('test_linkedattrs: Failed to add "directReport" to user1: error ' + e.message['desc']) - assert False - - # See if manager was added to the other entry - try: - entries = inst.search_s(USER2_DN, ldap.SCOPE_BASE, '(manager=*)') - if not entries: - log.fatal('test_linkedattrs: user2 missing "manager" attribute') - assert False - except ldap.LDAPError as e: - log.fatal('test_linkedattrs: Search for user1 failed: ' + e.message['desc']) - assert False - - # Remove "directReport" should remove "manager" to the other entry - try: - inst.modify_s(USER1_DN, [(ldap.MOD_DELETE, 'directReport', None)]) - except ldap.LDAPError as e: - log.fatal('test_linkedattrs: Failed to delete directReport: error ' + e.message['desc']) - assert False - - # See if manager was removed - try: - entries = inst.search_s(USER2_DN, ldap.SCOPE_BASE, '(manager=*)') - if entries: - log.fatal('test_linkedattrs: user2 "manager" attribute not removed') - assert False - except ldap.LDAPError as e: - log.fatal('test_linkedattrs: Search for user1 failed: ' + e.message['desc']) - assert False - - ############################################################################ - # Change the config - using linkType "indirectReport" now - ############################################################################ - - try: - inst.modify_s(CONFIG_DN, [(ldap.MOD_REPLACE, 'linkType', 'indirectReport')]) - except ldap.LDAPError as e: - log.error('test_linkedattrs: Failed to set linkTypee: error ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin - ############################################################################ - - # Make sure the old linkType(directManager) is not working - try: - inst.modify_s(USER1_DN, [(ldap.MOD_REPLACE, 'directReport', USER2_DN)]) - except ldap.LDAPError as e: - log.fatal('test_linkedattrs: Failed to add "directReport" to user1: error ' + e.message['desc']) - assert False - - # See if manager was added to the other entry, better not be... - try: - entries = inst.search_s(USER2_DN, ldap.SCOPE_BASE, '(manager=*)') - if entries: - log.fatal('test_linkedattrs: user2 had "manager" added unexpectedly') - assert False - except ldap.LDAPError as e: - log.fatal('test_linkedattrs: Search for user2 failed: ' + e.message['desc']) - assert False - - # Now, set the new linkType "indirectReport", which should add "manager" to the other entry - try: - inst.modify_s(USER1_DN, [(ldap.MOD_REPLACE, 'indirectReport', USER2_DN)]) - except ldap.LDAPError as e: - log.fatal('test_linkedattrs: Failed to add "indirectReport" to user1: error ' + e.message['desc']) - assert False - - # See if manager was added to the other entry, better not be - try: - entries = inst.search_s(USER2_DN, ldap.SCOPE_BASE, '(manager=*)') - if not entries: - log.fatal('test_linkedattrs: user2 missing "manager"') - assert False - except ldap.LDAPError as e: - log.fatal('test_linkedattrs: Search for user2 failed: ' + e.message['desc']) - assert False - - # Remove "indirectReport" should remove "manager" to the other entry - try: - inst.modify_s(USER1_DN, [(ldap.MOD_DELETE, 'indirectReport', None)]) - except ldap.LDAPError as e: - log.fatal('test_linkedattrs: Failed to delete directReport: error ' + e.message['desc']) - assert False - - # See if manager was removed - try: - entries = inst.search_s(USER2_DN, ldap.SCOPE_BASE, '(manager=*)') - if entries: - log.fatal('test_linkedattrs: user2 "manager" attribute not removed') - assert False - except ldap.LDAPError as e: - log.fatal('test_linkedattrs: Search for user1 failed: ' + e.message['desc']) - assert False - - ############################################################################ - # Test Fixup Task - ############################################################################ - - # Disable plugin and make some updates that would of triggered the plugin - inst.plugins.disable(name=PLUGIN_LINKED_ATTRS) - - try: - inst.modify_s(USER1_DN, [(ldap.MOD_REPLACE, 'indirectReport', USER2_DN)]) - except ldap.LDAPError as e: - log.fatal('test_linkedattrs: Failed to add "indirectReport" to user1: error ' + e.message['desc']) - assert False - - # The entry should not have a manager attribute - try: - entries = inst.search_s(USER2_DN, ldap.SCOPE_BASE, '(manager=*)') - if entries: - log.fatal('test_linkedattrs: user2 incorrectly has a "manager" attr') - assert False - except ldap.LDAPError as e: - log.fatal('test_linkedattrs: Search for user1 failed: ' + e.message['desc']) - assert False - - # Enable the plugin and rerun the task entry - inst.plugins.enable(name=PLUGIN_LINKED_ATTRS) - - # Add the task again - TASK_DN = 'cn=task-' + str(int(time.time())) + ',cn=fixup linked attributes,cn=tasks,cn=config' - try: - inst.add_s(Entry(('cn=task-' + str(int(time.time())) + ',cn=fixup linked attributes,cn=tasks,cn=config', { - 'objectclass': 'top extensibleObject'.split(), - 'basedn': DEFAULT_SUFFIX, - 'filter': 'objectclass=top'}))) - except ldap.LDAPError as e: - log.fatal('test_linkedattrs: Failed to add task: error ' + e.message['desc']) - assert False - - wait_for_task(inst, TASK_DN) - - # Check if user2 now has a manager attribute now - try: - entries = inst.search_s(USER2_DN, ldap.SCOPE_BASE, '(manager=*)') - if not entries: - log.fatal('test_linkedattrs: task failed: user2 missing "manager" attr') - assert False - except ldap.LDAPError as e: - log.fatal('test_linkedattrs: Search for user1 failed: ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin dependency - ############################################################################ - - test_dependency(inst, PLUGIN_LINKED_ATTRS) - - ############################################################################ - # Cleanup - ############################################################################ - - try: - inst.delete_s(USER1_DN) - except ldap.LDAPError as e: - log.fatal('test_linkedattrs: Failed to delete test entry1: ' + e.message['desc']) - assert False - - try: - inst.delete_s(USER2_DN) - except ldap.LDAPError as e: - log.fatal('test_linkedattrs: Failed to delete test entry2: ' + e.message['desc']) - assert False - - try: - inst.delete_s(CONFIG_DN) - except ldap.LDAPError as e: - log.fatal('test_linkedattrs: Failed to delete plugin config entry: ' + e.message['desc']) - assert False - - ############################################################################ - # Test passed - ############################################################################ - - log.info('test_linkedattrs: PASS\n') - return - - -################################################################################ -# -# Test MemberOf Plugin (5) -# -################################################################################ -def test_memberof(inst, args=None): - # stop the plugin, and start it - inst.plugins.disable(name=PLUGIN_MEMBER_OF) - inst.plugins.enable(name=PLUGIN_MEMBER_OF) - - if args == "restart": - return - - PLUGIN_DN = 'cn=' + PLUGIN_MEMBER_OF + ',cn=plugins,cn=config' - SHARED_CONFIG_DN = 'cn=memberOf Config,' + DEFAULT_SUFFIX - - log.info('Testing ' + PLUGIN_MEMBER_OF + '...') - - ############################################################################ - # Configure plugin - ############################################################################ - - try: - inst.modify_s(PLUGIN_DN, [(ldap.MOD_REPLACE, 'memberofgroupattr', 'member')]) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to update config(member): error ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin - ############################################################################ - - # Add our test entries - try: - inst.add_s(Entry((USER1_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'uid': 'user1' - }))) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to add user1: error ' + e.message['desc']) - assert False - - try: - inst.add_s(Entry((GROUP_DN, { - 'objectclass': 'top groupOfNames groupOfUniqueNames extensibleObject'.split(), - 'cn': 'group', - 'member': USER1_DN - }))) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to add group: error ' + e.message['desc']) - assert False - - try: - inst.add_s(Entry((SHARED_CONFIG_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'memberofgroupattr': 'member', - 'memberofattr': 'memberof' - }))) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to shared config entry: error ' + e.message['desc']) - assert False - - # Check if the user now has a "memberOf" attribute - try: - entries = inst.search_s(USER1_DN, ldap.SCOPE_BASE, '(memberOf=*)') - if not entries: - log.fatal('test_memberof: user1 missing memberOf') - assert False - except ldap.LDAPError as e: - log.fatal('test_memberof: Search for user1 failed: ' + e.message['desc']) - assert False - - # Remove "member" should remove "memberOf" from the entry - try: - inst.modify_s(GROUP_DN, [(ldap.MOD_DELETE, 'member', None)]) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to delete member: error ' + e.message['desc']) - assert False - - # Check that "memberOf" was removed - try: - entries = inst.search_s(USER1_DN, ldap.SCOPE_BASE, '(memberOf=*)') - if entries: - log.fatal('test_memberof: user1 incorrectly has memberOf attr') - assert False - except ldap.LDAPError as e: - log.fatal('test_memberof: Search for user1 failed: ' + e.message['desc']) - assert False - - ############################################################################ - # Change the config - ############################################################################ - - try: - inst.modify_s(PLUGIN_DN, [(ldap.MOD_REPLACE, 'memberofgroupattr', 'uniquemember')]) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to update config(uniquemember): error ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin - ############################################################################ - - try: - inst.modify_s(GROUP_DN, [(ldap.MOD_REPLACE, 'uniquemember', USER1_DN)]) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to add uniquemember: error ' + e.message['desc']) - assert False - - # Check if the user now has a "memberOf" attribute - try: - entries = inst.search_s(USER1_DN, ldap.SCOPE_BASE, '(memberOf=*)') - if not entries: - log.fatal('test_memberof: user1 missing memberOf') - assert False - except ldap.LDAPError as e: - log.fatal('test_memberof: Search for user1 failed: ' + e.message['desc']) - assert False - - # Remove "uniquemember" should remove "memberOf" from the entry - try: - inst.modify_s(GROUP_DN, [(ldap.MOD_DELETE, 'uniquemember', None)]) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to delete member: error ' + e.message['desc']) - assert False - - # Check that "memberOf" was removed - try: - entries = inst.search_s(USER1_DN, ldap.SCOPE_BASE, '(memberOf=*)') - if entries: - log.fatal('test_memberof: user1 incorrectly has memberOf attr') - assert False - except ldap.LDAPError as e: - log.fatal('test_memberof: Search for user1 failed: ' + e.message['desc']) - assert False - - ############################################################################ - # Set the shared config entry and test the plugin - ############################################################################ - - # The shared config entry uses "member" - the above test uses "uniquemember" - try: - inst.modify_s(PLUGIN_DN, [(ldap.MOD_REPLACE, CONFIG_AREA, SHARED_CONFIG_DN)]) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to set plugin area: error ' + e.message['desc']) - assert False - - # Delete the test entries then readd them to start with a clean slate - try: - inst.delete_s(USER1_DN) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to delete test entry1: ' + e.message['desc']) - assert False - - try: - inst.delete_s(GROUP_DN) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to delete test group: ' + e.message['desc']) - assert False - - try: - inst.add_s(Entry((USER1_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'uid': 'user1' - }))) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to add user1: error ' + e.message['desc']) - assert False - - try: - inst.add_s(Entry((GROUP_DN, { - 'objectclass': 'top groupOfNames groupOfUniqueNames extensibleObject'.split(), - 'cn': 'group', - 'member': USER1_DN - }))) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to add group: error ' + e.message['desc']) - assert False - - # Test the shared config - # Check if the user now has a "memberOf" attribute - try: - entries = inst.search_s(USER1_DN, ldap.SCOPE_BASE, '(memberOf=*)') - if not entries: - log.fatal('test_memberof: user1 missing memberOf') - assert False - except ldap.LDAPError as e: - log.fatal('test_memberof: Search for user1 failed: ' + e.message['desc']) - assert False - - # Remove "member" should remove "memberOf" from the entry - try: - inst.modify_s(GROUP_DN, [(ldap.MOD_DELETE, 'member', None)]) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to delete member: error ' + e.message['desc']) - assert False - - # Check that "memberOf" was removed - try: - entries = inst.search_s(USER1_DN, ldap.SCOPE_BASE, '(memberOf=*)') - if entries: - log.fatal('test_memberof: user1 incorrectly has memberOf attr') - assert False - except ldap.LDAPError as e: - log.fatal('test_memberof: Search for user1 failed: ' + e.message['desc']) - assert False - - ############################################################################ - # Change the shared config entry to use 'uniquemember' and test the plugin - ############################################################################ - - try: - inst.modify_s(SHARED_CONFIG_DN, [(ldap.MOD_REPLACE, 'memberofgroupattr', 'uniquemember')]) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to set shared plugin entry(uniquemember): error ' - + e.message['desc']) - assert False - - try: - inst.modify_s(GROUP_DN, [(ldap.MOD_REPLACE, 'uniquemember', USER1_DN)]) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to add uniquemember: error ' + e.message['desc']) - assert False - - # Check if the user now has a "memberOf" attribute - try: - entries = inst.search_s(USER1_DN, ldap.SCOPE_BASE, '(memberOf=*)') - if not entries: - log.fatal('test_memberof: user1 missing memberOf') - assert False - except ldap.LDAPError as e: - log.fatal('test_memberof: Search for user1 failed: ' + e.message['desc']) - assert False - - # Remove "uniquemember" should remove "memberOf" from the entry - try: - inst.modify_s(GROUP_DN, [(ldap.MOD_DELETE, 'uniquemember', None)]) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to delete member: error ' + e.message['desc']) - assert False - - # Check that "memberOf" was removed - try: - entries = inst.search_s(USER1_DN, ldap.SCOPE_BASE, '(memberOf=*)') - if entries: - log.fatal('test_memberof: user1 incorrectly has memberOf attr') - assert False - except ldap.LDAPError as e: - log.fatal('test_memberof: Search for user1 failed: ' + e.message['desc']) - assert False - - ############################################################################ - # Remove shared config from plugin, and retest - ############################################################################ - - # First change the plugin to use member before we move the shared config that uses uniquemember - try: - inst.modify_s(PLUGIN_DN, [(ldap.MOD_REPLACE, 'memberofgroupattr', 'member')]) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to update config(uniquemember): error ' + e.message['desc']) - assert False - - # Remove shared config from plugin - try: - inst.modify_s(PLUGIN_DN, [(ldap.MOD_DELETE, CONFIG_AREA, None)]) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to add uniquemember: error ' + e.message['desc']) - assert False - - try: - inst.modify_s(GROUP_DN, [(ldap.MOD_REPLACE, 'member', USER1_DN)]) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to add uniquemember: error ' + e.message['desc']) - assert False - - # Check if the user now has a "memberOf" attribute - try: - entries = inst.search_s(USER1_DN, ldap.SCOPE_BASE, '(memberOf=*)') - if not entries: - log.fatal('test_memberof: user1 missing memberOf') - assert False - except ldap.LDAPError as e: - log.fatal('test_memberof: Search for user1 failed: ' + e.message['desc']) - assert False - - # Remove "uniquemember" should remove "memberOf" from the entry - try: - inst.modify_s(GROUP_DN, [(ldap.MOD_DELETE, 'member', None)]) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to delete member: error ' + e.message['desc']) - assert False - - # Check that "memberOf" was removed - try: - entries = inst.search_s(USER1_DN, ldap.SCOPE_BASE, '(memberOf=*)') - if entries: - log.fatal('test_memberof: user1 incorrectly has memberOf attr') - assert False - except ldap.LDAPError as e: - log.fatal('test_memberof: Search for user1 failed: ' + e.message['desc']) - assert False - - ############################################################################ - # Test Fixup Task - ############################################################################ - - inst.plugins.disable(name=PLUGIN_MEMBER_OF) - - # First change the plugin to use uniquemember - try: - inst.modify_s(PLUGIN_DN, [(ldap.MOD_REPLACE, 'memberofgroupattr', 'uniquemember')]) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to update config(uniquemember): error ' + e.message['desc']) - assert False - - # Add uniquemember, should not update USER1 - try: - inst.modify_s(GROUP_DN, [(ldap.MOD_REPLACE, 'uniquemember', USER1_DN)]) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to add uniquemember: error ' + e.message['desc']) - assert False - - # Check for "memberOf" - try: - entries = inst.search_s(USER1_DN, ldap.SCOPE_BASE, '(memberOf=*)') - if entries: - log.fatal('test_memberof: user1 incorrect has memberOf attr') - assert False - except ldap.LDAPError as e: - log.fatal('test_memberof: Search for user1 failed: ' + e.message['desc']) - assert False - - # Enable memberof plugin - inst.plugins.enable(name=PLUGIN_MEMBER_OF) - - ############################################################# - # Test memberOf fixup arg validation: Test the DN and filter - ############################################################# - - # - # Test bad/nonexistant DN - # - TASK_DN = 'cn=task-' + str(int(time.time())) + ',' + DN_MBO_TASK - try: - inst.add_s(Entry((TASK_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'basedn': DEFAULT_SUFFIX + "bad", - 'filter': 'objectclass=top'}))) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to add task(bad dn): error ' + - e.message['desc']) - assert False - - exitcode = wait_for_task(inst, TASK_DN) - if exitcode == "0": - # We should an error - log.fatal('test_memberof: Task with invalid DN still reported success') - assert False - - # - # Test invalid DN syntax - # - TASK_DN = 'cn=task-' + str(int(time.time())) + ',' + DN_MBO_TASK - try: - inst.add_s(Entry((TASK_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'basedn': "bad", - 'filter': 'objectclass=top'}))) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to add task(invalid dn syntax): ' + - e.message['desc']) - assert False - - exitcode = wait_for_task(inst, TASK_DN) - if exitcode == "0": - # We should an error - log.fatal('test_memberof: Task with invalid DN syntax still reported' + - ' success') - assert False - - # - # Test bad filter (missing closing parenthesis) - # - TASK_DN = 'cn=task-' + str(int(time.time())) + ',' + DN_MBO_TASK - try: - inst.add_s(Entry((TASK_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'basedn': DEFAULT_SUFFIX, - 'filter': '(objectclass=top'}))) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to add task(bad filter: error ' + - e.message['desc']) - assert False - - exitcode = wait_for_task(inst, TASK_DN) - if exitcode == "0": - # We should an error - log.fatal('test_memberof: Task with invalid filter still reported ' + - 'success') - assert False - - #################################################### - # Test fixup works - #################################################### - - # - # Run the task and validate that it worked - # - TASK_DN = 'cn=task-' + str(int(time.time())) + ',' + DN_MBO_TASK - try: - inst.add_s(Entry((TASK_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'basedn': DEFAULT_SUFFIX, - 'filter': 'objectclass=top'}))) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to add task: error ' + e.message['desc']) - assert False - - wait_for_task(inst, TASK_DN) - - # Check for "memberOf" - try: - entries = inst.search_s(USER1_DN, ldap.SCOPE_BASE, '(memberOf=*)') - if not entries: - log.fatal('test_memberof: user1 missing memberOf attr') - assert False - except ldap.LDAPError as e: - log.fatal('test_memberof: Search for user1 failed: ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin dependency - ############################################################################ - - test_dependency(inst, PLUGIN_MEMBER_OF) - - ############################################################################ - # Cleanup - ############################################################################ - - try: - inst.delete_s(USER1_DN) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to delete test entry1: ' + e.message['desc']) - assert False - - try: - inst.delete_s(GROUP_DN) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to delete test group: ' + e.message['desc']) - assert False - - try: - inst.delete_s(SHARED_CONFIG_DN) - except ldap.LDAPError as e: - log.fatal('test_memberof: Failed to delete shared config entry: ' + e.message['desc']) - assert False - - ############################################################################ - # Test passed - ############################################################################ - - log.info('test_memberof: PASS\n') - - return - - -################################################################################ -# -# Test Managed Entry Plugin (6) -# -################################################################################ -def test_mep(inst, args=None): - # stop the plugin, and start it - inst.plugins.disable(name=PLUGIN_MANAGED_ENTRY) - inst.plugins.enable(name=PLUGIN_MANAGED_ENTRY) - - if args == "restart": - return - - USER_DN = 'uid=user1,ou=people,' + DEFAULT_SUFFIX - MEP_USER_DN = 'cn=user1,ou=groups,' + DEFAULT_SUFFIX - USER_DN2 = 'uid=user 1,ou=people,' + DEFAULT_SUFFIX - MEP_USER_DN2 = 'uid=user 1,ou=groups,' + DEFAULT_SUFFIX - CONFIG_DN = 'cn=config,cn=' + PLUGIN_MANAGED_ENTRY + ',cn=plugins,cn=config' - TEMPLATE_DN = 'cn=MEP Template,' + DEFAULT_SUFFIX - TEMPLATE_DN2 = 'cn=MEP Template2,' + DEFAULT_SUFFIX - - log.info('Testing ' + PLUGIN_MANAGED_ENTRY + '...') - - ############################################################################ - # Configure plugin - ############################################################################ - - # Add our org units - try: - inst.add_s(Entry((PEOPLE_OU, { - 'objectclass': 'top extensibleObject'.split(), - 'ou': 'people'}))) - except ldap.ALREADY_EXISTS: - pass - except ldap.LDAPError as e: - log.fatal('test_mep: Failed to add people org unit: error ' + e.message['desc']) - assert False - - try: - inst.add_s(Entry((GROUP_OU, { - 'objectclass': 'top extensibleObject'.split(), - 'ou': 'people'}))) - except ldap.ALREADY_EXISTS: - pass - except ldap.LDAPError as e: - log.fatal('test_mep: Failed to add people org unit: error ' + e.message['desc']) - assert False - - # Add the template entry - try: - inst.add_s(Entry((TEMPLATE_DN, { - 'objectclass': 'top mepTemplateEntry extensibleObject'.split(), - 'cn': 'MEP Template', - 'mepRDNAttr': 'cn', - 'mepStaticAttr': 'objectclass: posixGroup|objectclass: extensibleObject'.split('|'), - 'mepMappedAttr': 'cn: $cn|uid: $cn|gidNumber: $uidNumber'.split('|') - }))) - except ldap.LDAPError as e: - log.fatal('test_mep: Failed to add template entry: error ' + e.message['desc']) - assert False - - # Add the config entry - try: - inst.add_s(Entry((CONFIG_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'cn': 'config', - 'originScope': PEOPLE_OU, - 'originFilter': 'objectclass=posixAccount', - 'managedBase': GROUP_OU, - 'managedTemplate': TEMPLATE_DN - }))) - except ldap.LDAPError as e: - log.fatal('test_mep: Failed to add config entry: error ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin - ############################################################################ - - # Add an entry that meets the MEP scope - try: - inst.add_s(Entry((USER_DN, { - 'objectclass': 'top posixAccount extensibleObject'.split(), - 'uid': 'user1', - 'cn': 'user1', - 'uidNumber': '1', - 'gidNumber': '1', - 'homeDirectory': '/home/user1' - }))) - except ldap.LDAPError as e: - log.fatal('test_mep: Failed to user1: error ' + e.message['desc']) - assert False - - # Check if a managed group entry was created - try: - inst.search_s(MEP_USER_DN, ldap.SCOPE_BASE, '(objectclass=top)') - except ldap.LDAPError as e: - log.fatal('test_mep: Unable to find MEP entry: ' + e.message['desc']) - assert False - - ############################################################################ - # Change the config - ############################################################################ - - # Add a new template entry - try: - inst.add_s(Entry((TEMPLATE_DN2, { - 'objectclass': 'top mepTemplateEntry extensibleObject'.split(), - 'cn': 'MEP Template2', - 'mepRDNAttr': 'uid', - 'mepStaticAttr': 'objectclass: posixGroup|objectclass: extensibleObject'.split('|'), - 'mepMappedAttr': 'cn: $uid|uid: $cn|gidNumber: $gidNumber'.split('|') - }))) - except ldap.LDAPError as e: - log.fatal('test_mep: Failed to add template entry2: error ' + e.message['desc']) - assert False - - # Set the new template dn - try: - inst.modify_s(CONFIG_DN, [(ldap.MOD_REPLACE, 'managedTemplate', TEMPLATE_DN2)]) - except ldap.LDAPError as e: - log.fatal('test_mep: Failed to set mep plugin config: error ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin - ############################################################################ - - # Add an entry that meets the MEP scope - try: - inst.add_s(Entry((USER_DN2, { - 'objectclass': 'top posixAccount extensibleObject'.split(), - 'uid': 'user 1', - 'cn': 'user 1', - 'uidNumber': '1', - 'gidNumber': '1', - 'homeDirectory': '/home/user2' - }))) - except ldap.LDAPError as e: - log.fatal('test_mep: Failed to user2: error ' + e.message['desc']) - assert False - - # Check if a managed group entry was created - try: - inst.search_s(MEP_USER_DN2, ldap.SCOPE_BASE, '(objectclass=top)') - except ldap.LDAPError as e: - log.fatal('test_mep: Unable to find MEP entry2: ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin dependency - ############################################################################ - - test_dependency(inst, PLUGIN_MANAGED_ENTRY) - - ############################################################################ - # Cleanup - ############################################################################ - - try: - inst.delete_s(USER_DN) - except ldap.LDAPError as e: - log.fatal('test_mep: Failed to delete test user1: ' + e.message['desc']) - assert False - - try: - inst.delete_s(USER_DN2) - except ldap.LDAPError as e: - log.fatal('test_mep: Failed to delete test user 2: ' + e.message['desc']) - assert False - - try: - inst.delete_s(TEMPLATE_DN) - except ldap.LDAPError as e: - log.fatal('test_mep: Failed to delete template1: ' + e.message['desc']) - assert False - - inst.plugins.disable(name=PLUGIN_MANAGED_ENTRY) - - try: - inst.delete_s(TEMPLATE_DN2) - except ldap.LDAPError as e: - log.fatal('test_mep: Failed to delete template2: ' + e.message['desc']) - assert False - - try: - inst.delete_s(CONFIG_DN) - except ldap.LDAPError as e: - log.fatal('test_mep: Failed to delete config: ' + e.message['desc']) - assert False - - ############################################################################ - # Test passed - ############################################################################ - - log.info('test_mep: PASS\n') - return - - -################################################################################ -# -# Test Passthru Plugin (7) -# -################################################################################ -def test_passthru(inst, args=None): - # Passthru is a bit picky about the state of the entry - we can't just restart it - if args == "restart": - return - - # stop the plugin - inst.plugins.disable(name=PLUGIN_PASSTHRU) - - PLUGIN_DN = 'cn=' + PLUGIN_PASSTHRU + ',cn=plugins,cn=config' - PASSTHRU_DN = 'uid=admin,dc=pass,dc=thru' - PASSTHRU_DN2 = 'uid=admin2,dc=pass2,dc=thru' - PASS_SUFFIX1 = 'dc=pass,dc=thru' - PASS_SUFFIX2 = 'dc=pass2,dc=thru' - PASS_BE2 = 'PASS2' - - log.info('Testing ' + PLUGIN_PASSTHRU + '...') - - ############################################################################ - # Add a new "remote" instance, and a user for auth - ############################################################################ - - # Create second instance - passthru_inst = DirSrv(verbose=False) - - # Args for the instance - args_instance[SER_HOST] = LOCALHOST - args_instance[SER_PORT] = 33333 - args_instance[SER_SERVERID_PROP] = 'passthru' - args_instance[SER_CREATION_SUFFIX] = PASS_SUFFIX1 - args_passthru_inst = args_instance.copy() - passthru_inst.allocate(args_passthru_inst) - if passthru_inst.exists(): - passthru_inst.delete() - passthru_inst.create() - passthru_inst.open() - - # Create a second backend - passthru_inst.backends.create(None, properties={ - BACKEND_NAME: PASS_BE2, - 'suffix': PASS_SUFFIX2, - }) - - # Create the top of the tree - try: - passthru_inst.add_s(Entry((PASS_SUFFIX2, { - 'objectclass': 'top domain'.split(), - 'dc': 'pass2'}))) - except ldap.ALREADY_EXISTS: - pass - except ldap.LDAPError as e: - log.fatal('test_passthru: Failed to create suffix entry: error ' + e.message['desc']) - passthru_inst.delete() - assert False - - # Add user to suffix1 - try: - passthru_inst.add_s(Entry((PASSTHRU_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'uid': 'admin', - 'userpassword': 'password' - }))) - except ldap.LDAPError as e: - log.fatal('test_passthru: Failed to admin1: error ' + e.message['desc']) - passthru_inst.delete() - assert False - - # Add user to suffix 2 - try: - passthru_inst.add_s(Entry((PASSTHRU_DN2, { - 'objectclass': 'top extensibleObject'.split(), - 'uid': 'admin2', - 'userpassword': 'password' - }))) - except ldap.LDAPError as e: - log.fatal('test_passthru: Failed to admin2 : error ' + e.message['desc']) - passthru_inst.delete() - assert False - - ############################################################################ - # Configure and start plugin - ############################################################################ - - try: - inst.modify_s(PLUGIN_DN, [(ldap.MOD_REPLACE, 'nsslapd-pluginenabled', 'on'), - (ldap.MOD_REPLACE, 'nsslapd-pluginarg0', 'ldap://127.0.0.1:33333/dc=pass,dc=thru')]) - except ldap.LDAPError as e: - log.fatal('test_passthru: Failed to set mep plugin config: error ' + e.message['desc']) - passthru_inst.delete() - assert False - - ############################################################################ - # Test plugin - ############################################################################ - - # login as user - try: - inst.simple_bind_s(PASSTHRU_DN, "password") - except ldap.LDAPError as e: - log.fatal('test_passthru: pass through bind failed: ' + e.message['desc']) - passthru_inst.delete() - assert False - - ############################################################################ - # Change the config - ############################################################################ - - # login as root DN - try: - inst.simple_bind_s(DN_DM, PASSWORD) - except ldap.LDAPError as e: - log.fatal('test_passthru: pass through bind failed: ' + e.message['desc']) - passthru_inst.delete() - assert False - - try: - inst.modify_s(PLUGIN_DN, [(ldap.MOD_REPLACE, 'nsslapd-pluginarg0', 'ldap://127.0.0.1:33333/dc=pass2,dc=thru')]) - except ldap.LDAPError as e: - log.fatal('test_passthru: Failed to set mep plugin config: error ' + e.message['desc']) - passthru_inst.delete() - assert False - - ############################################################################ - # Test plugin - ############################################################################ - - # login as user - try: - inst.simple_bind_s(PASSTHRU_DN2, "password") - except ldap.LDAPError as e: - log.fatal('test_passthru: pass through bind failed: ' + e.message['desc']) - passthru_inst.delete() - assert False - - # login as root DN - try: - inst.simple_bind_s(DN_DM, PASSWORD) - except ldap.LDAPError as e: - log.fatal('test_passthru: pass through bind failed: ' + e.message['desc']) - passthru_inst.delete() - assert False - - ############################################################################ - # Test plugin dependency - ############################################################################ - - test_dependency(inst, PLUGIN_PASSTHRU) - - ############################################################################ - # Cleanup - ############################################################################ - - # remove the passthru instance - passthru_inst.delete() - - ############################################################################ - # Test passed - ############################################################################ - - log.info('test_passthru: PASS\n') - - return - - -################################################################################ -# -# Test Referential Integrity Plugin (8) -# -################################################################################ -def test_referint(inst, args=None): - # stop the plugin, and start it - inst.plugins.disable(name=PLUGIN_REFER_INTEGRITY) - inst.plugins.enable(name=PLUGIN_REFER_INTEGRITY) - - if args == "restart": - return - - log.info('Testing ' + PLUGIN_REFER_INTEGRITY + '...') - PLUGIN_DN = 'cn=' + PLUGIN_REFER_INTEGRITY + ',cn=plugins,cn=config' - SHARED_CONFIG_DN = 'cn=RI Config,' + DEFAULT_SUFFIX - - ############################################################################ - # Configure plugin - ############################################################################ - - try: - inst.modify_s(PLUGIN_DN, [(ldap.MOD_REPLACE, 'referint-membership-attr', 'member')]) - except ldap.LDAPError as e: - log.fatal('test_referint: Failed to configure RI plugin: error ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin - ############################################################################ - - # Add some users and a group - try: - inst.add_s(Entry((USER1_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'uid': 'user1' - }))) - except ldap.LDAPError as e: - log.fatal('test_referint: Failed to add user1: error ' + e.message['desc']) - assert False - - try: - inst.add_s(Entry((USER2_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'uid': 'user2' - }))) - except ldap.LDAPError as e: - log.fatal('test_referint: Failed to add user2: error ' + e.message['desc']) - assert False - - try: - inst.add_s(Entry((GROUP_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'cn': 'group', - 'member': USER1_DN, - 'uniquemember': USER2_DN - }))) - except ldap.LDAPError as e: - log.fatal('test_referint: Failed to add group: error ' + e.message['desc']) - assert False - - # Grab the referint log file from the plugin - - try: - entries = inst.search_s(PLUGIN_DN, ldap.SCOPE_BASE, '(objectclass=top)') - REFERINT_LOGFILE = entries[0].getValue('referint-logfile') - except ldap.LDAPError as e: - log.fatal('test_referint: Unable to search plugin entry: ' + e.message['desc']) - assert False - - # Add shared config entry - try: - inst.add_s(Entry((SHARED_CONFIG_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'referint-membership-attr': 'member', - 'referint-update-delay': '0', - 'referint-logfile': REFERINT_LOGFILE, - }))) - except ldap.LDAPError as e: - log.fatal('test_referint: Failed to shared config entry: error ' + e.message['desc']) - assert False - - # Delete a user - try: - inst.delete_s(USER1_DN) - except ldap.LDAPError as e: - log.fatal('test_referint: Failed to delete user1: ' + e.message['desc']) - assert False - - # Check for integrity - try: - entry = inst.search_s(GROUP_DN, ldap.SCOPE_BASE, '(member=' + USER1_DN + ')') - if entry: - log.fatal('test_referint: user1 was not removed from group') - assert False - except ldap.LDAPError as e: - log.fatal('test_referint: Unable to search group: ' + e.message['desc']) - assert False - - ############################################################################ - # Change the config - ############################################################################ - - try: - inst.modify_s(PLUGIN_DN, [(ldap.MOD_REPLACE, 'referint-membership-attr', 'uniquemember')]) - except ldap.LDAPError as e: - log.fatal('test_referint: Failed to configure RI plugin: error ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin - ############################################################################ - - # Delete a user - try: - inst.delete_s(USER2_DN) - except ldap.LDAPError as e: - log.fatal('test_referint: Failed to delete user1: ' + e.message['desc']) - assert False - - # Check for integrity - try: - entry = inst.search_s(GROUP_DN, ldap.SCOPE_BASE, '(uniquemember=' + USER2_DN + ')') - if entry: - log.fatal('test_referint: user2 was not removed from group') - assert False - except ldap.LDAPError as e: - log.fatal('test_referint: Unable to search group: ' + e.message['desc']) - assert False - - ############################################################################ - # Set the shared config entry and test the plugin - ############################################################################ - - # The shared config entry uses "member" - the above test used "uniquemember" - try: - inst.modify_s(PLUGIN_DN, [(ldap.MOD_REPLACE, CONFIG_AREA, SHARED_CONFIG_DN)]) - except ldap.LDAPError as e: - log.fatal('test_referint: Failed to set plugin area: error ' + e.message['desc']) - assert False - - # Delete the group, and readd everything - try: - inst.delete_s(GROUP_DN) - except ldap.LDAPError as e: - log.fatal('test_referint: Failed to delete group: ' + e.message['desc']) - assert False - - try: - inst.add_s(Entry((USER1_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'uid': 'user1' - }))) - except ldap.LDAPError as e: - log.fatal('test_referint: Failed to add user1: error ' + e.message['desc']) - assert False - - try: - inst.add_s(Entry((USER2_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'uid': 'user2' - }))) - except ldap.LDAPError as e: - log.fatal('test_referint: Failed to add user2: error ' + e.message['desc']) - assert False - - try: - inst.add_s(Entry((GROUP_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'cn': 'group', - 'member': USER1_DN, - 'uniquemember': USER2_DN - }))) - except ldap.LDAPError as e: - log.fatal('test_referint: Failed to add group: error ' + e.message['desc']) - assert False - - # Delete a user - try: - inst.delete_s(USER1_DN) - except ldap.LDAPError as e: - log.fatal('test_referint: Failed to delete user1: ' + e.message['desc']) - assert False - - # Check for integrity - try: - entry = inst.search_s(GROUP_DN, ldap.SCOPE_BASE, '(member=' + USER1_DN + ')') - if entry: - log.fatal('test_referint: user1 was not removed from group') - assert False - except ldap.LDAPError as e: - log.fatal('test_referint: Unable to search group: ' + e.message['desc']) - assert False - - ############################################################################ - # Change the shared config entry to use 'uniquemember' and test the plugin - ############################################################################ - - try: - inst.modify_s(SHARED_CONFIG_DN, [(ldap.MOD_REPLACE, 'referint-membership-attr', 'uniquemember')]) - except ldap.LDAPError as e: - log.fatal('test_referint: Failed to set shared plugin entry(uniquemember): error ' - + e.message['desc']) - assert False - - # Delete a user - try: - inst.delete_s(USER2_DN) - except ldap.LDAPError as e: - log.fatal('test_referint: Failed to delete user1: ' + e.message['desc']) - assert False - - # Check for integrity - try: - entry = inst.search_s(GROUP_DN, ldap.SCOPE_BASE, '(uniquemember=' + USER2_DN + ')') - if entry: - log.fatal('test_referint: user2 was not removed from group') - assert False - except ldap.LDAPError as e: - log.fatal('test_referint: Unable to search group: ' + e.message['desc']) - assert False - - ############################################################################ - # Remove shared config from plugin, and retest - ############################################################################ - - # First change the plugin to use member before we move the shared config that uses uniquemember - try: - inst.modify_s(PLUGIN_DN, [(ldap.MOD_REPLACE, 'referint-membership-attr', 'member')]) - except ldap.LDAPError as e: - log.fatal('test_referint: Failed to update config(uniquemember): error ' + e.message['desc']) - assert False - - # Remove shared config from plugin - try: - inst.modify_s(PLUGIN_DN, [(ldap.MOD_DELETE, CONFIG_AREA, None)]) - except ldap.LDAPError as e: - log.fatal('test_referint: Failed to add uniquemember: error ' + e.message['desc']) - assert False - - # Add test user - try: - inst.add_s(Entry((USER1_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'uid': 'user1' - }))) - except ldap.LDAPError as e: - log.fatal('test_referint: Failed to add user1: error ' + e.message['desc']) - assert False - - # Add user to group - try: - inst.modify_s(GROUP_DN, [(ldap.MOD_REPLACE, 'member', USER1_DN)]) - except ldap.LDAPError as e: - log.fatal('test_referint: Failed to add uniquemember: error ' + e.message['desc']) - assert False - - # Delete a user - try: - inst.delete_s(USER1_DN) - except ldap.LDAPError as e: - log.fatal('test_referint: Failed to delete user1: ' + e.message['desc']) - assert False - - # Check for integrity - try: - entry = inst.search_s(GROUP_DN, ldap.SCOPE_BASE, '(member=' + USER1_DN + ')') - if entry: - log.fatal('test_referint: user1 was not removed from group') - assert False - except ldap.LDAPError as e: - log.fatal('test_referint: Unable to search group: ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin dependency - ############################################################################ - - test_dependency(inst, PLUGIN_REFER_INTEGRITY) - - ############################################################################ - # Cleanup - ############################################################################ - - try: - inst.delete_s(GROUP_DN) - except ldap.LDAPError as e: - log.fatal('test_referint: Failed to delete group: ' + e.message['desc']) - assert False - - try: - inst.delete_s(SHARED_CONFIG_DN) - except ldap.LDAPError as e: - log.fatal('test_referint: Failed to delete shared config entry: ' + e.message['desc']) - assert False - - ############################################################################ - # Test passed - ############################################################################ - - log.info('test_referint: PASS\n') - - return - - -################################################################################ -# -# Test Retro Changelog Plugin (9) -# -################################################################################ -def test_retrocl(inst, args=None): - # stop the plugin, and start it - inst.plugins.disable(name=PLUGIN_RETRO_CHANGELOG) - inst.plugins.enable(name=PLUGIN_RETRO_CHANGELOG) - - if args == "restart": - return - - log.info('Testing ' + PLUGIN_RETRO_CHANGELOG + '...') - - ############################################################################ - # Configure plugin - ############################################################################ - - # Gather the current change count (it's not 1 once we start the stabilty tests) - try: - entry = inst.search_s(RETROCL_SUFFIX, ldap.SCOPE_SUBTREE, '(changenumber=*)') - except ldap.LDAPError as e: - log.fatal('test_retrocl: Failed to get the count: error ' + e.message['desc']) - assert False - - entry_count = len(entry) - - ############################################################################ - # Test plugin - ############################################################################ - - # Add a user - try: - inst.add_s(Entry((USER1_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'uid': 'user1' - }))) - except ldap.LDAPError as e: - log.fatal('test_retrocl: Failed to add user1: error ' + e.message['desc']) - assert False - - # Check we logged this in the retro cl - try: - entry = inst.search_s(RETROCL_SUFFIX, ldap.SCOPE_SUBTREE, '(changenumber=*)') - if not entry or len(entry) == entry_count: - log.fatal('test_retrocl: changelog not updated') - assert False - except ldap.LDAPError as e: - log.fatal('test_retrocl: Unable to search group: ' + e.message['desc']) - assert False - - entry_count += 1 - - ############################################################################ - # Change the config - disable plugin - ############################################################################ - - inst.plugins.disable(name=PLUGIN_RETRO_CHANGELOG) - - ############################################################################ - # Test plugin - ############################################################################ - - try: - inst.delete_s(USER1_DN) - except ldap.LDAPError as e: - log.fatal('test_retrocl: Failed to delete user1: ' + e.message['desc']) - assert False - - # Check we didn't logged this in the retro cl - try: - entry = inst.search_s(RETROCL_SUFFIX, ldap.SCOPE_SUBTREE, '(changenumber=*)') - if len(entry) != entry_count: - log.fatal('test_retrocl: changelog incorrectly updated - change count: ' - + str(len(entry)) + ' - expected 1') - assert False - except ldap.LDAPError as e: - log.fatal('test_retrocl: Unable to search retro changelog: ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin dependency - ############################################################################ - - inst.plugins.enable(name=PLUGIN_RETRO_CHANGELOG) - test_dependency(inst, PLUGIN_RETRO_CHANGELOG) - - ############################################################################ - # Cleanup - ############################################################################ - - # None - - ############################################################################ - # Test passed - ############################################################################ - - log.info('test_retrocl: PASS\n') - - return - - -################################################################################ -# -# Test Root DN Access Control Plugin (10) -# -################################################################################ -def test_rootdn(inst, args=None): - # stop the plugin, and start it - inst.plugins.disable(name=PLUGIN_ROOTDN_ACCESS) - inst.plugins.enable(name=PLUGIN_ROOTDN_ACCESS) - - if args == "restart": - return - - PLUGIN_DN = 'cn=' + PLUGIN_ROOTDN_ACCESS + ',cn=plugins,cn=config' - - log.info('Testing ' + PLUGIN_ROOTDN_ACCESS + '...') - - ############################################################################ - # Configure plugin - ############################################################################ - - # Add an user and aci to open up cn=config - try: - inst.add_s(Entry((USER1_DN, { - 'objectclass': 'top extensibleObject'.split(), - 'uid': 'user1', - 'userpassword': 'password' - }))) - except ldap.LDAPError as e: - log.fatal('test_rootdn: Failed to add user1: error ' + e.message['desc']) - assert False - - # Set an aci so we can modify the plugin after ew deny the root dn - ACI = ('(target ="ldap:///cn=config")(targetattr = "*")(version 3.0;acl ' + - '"all access";allow (all)(userdn="ldap:///anyone");)') - try: - inst.modify_s(DN_CONFIG, [(ldap.MOD_ADD, 'aci', ACI)]) - except ldap.LDAPError as e: - log.fatal('test_rootdn: Failed to add aci to config: error ' + e.message['desc']) - assert False - - # Set allowed IP to an unknown host - blocks root dn - try: - inst.modify_s(PLUGIN_DN, [(ldap.MOD_REPLACE, 'rootdn-allow-ip', '10.10.10.10')]) - except ldap.LDAPError as e: - log.fatal('test_rootdn: Failed to set rootDN plugin config: error ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin - ############################################################################ - - # Bind as Root DN - failed = False - try: - inst.simple_bind_s(DN_DM, PASSWORD) - except ldap.LDAPError as e: - failed = True - - if not failed: - log.fatal('test_rootdn: Root DN was incorrectly able to bind') - assert False - - ############################################################################ - # Change the config - ############################################################################ - - # Bind as the user who can make updates to the config - try: - inst.simple_bind_s(USER1_DN, 'password') - except ldap.LDAPError as e: - log.fatal('test_rootdn: failed to bind as user1') - assert False - - # First, test that invalid plugin changes are rejected - try: - inst.modify_s(PLUGIN_DN, [(ldap.MOD_REPLACE, 'rootdn-deny-ip', '12.12.ZZZ.12')]) - log.fatal('test_rootdn: Incorrectly allowed to add invalid "rootdn-deny-ip: 12.12.ZZZ.12"') - assert False - except ldap.LDAPError: - pass - - try: - inst.modify_s(PLUGIN_DN, [(ldap.MOD_REPLACE, 'rootdn-allow-host', 'host._.com')]) - log.fatal('test_rootdn: Incorrectly allowed to add invalid "rootdn-allow-host: host._.com"') - assert False - except ldap.LDAPError: - pass - - # Remove the restriction - try: - inst.modify_s(PLUGIN_DN, [(ldap.MOD_DELETE, 'rootdn-allow-ip', None)]) - except ldap.LDAPError as e: - log.fatal('test_rootdn: Failed to set rootDN plugin config: error ' + e.message['desc']) - assert False - - ############################################################################ - # Test plugin - ############################################################################ - - # Bind as Root DN - failed = False - try: - inst.simple_bind_s(DN_DM, PASSWORD) - except ldap.LDAPError as e: - failed = True - - if failed: - log.fatal('test_rootdn: Root DN was not able to bind') - assert False - - ############################################################################ - # Test plugin dependency - ############################################################################ - - test_dependency(inst, PLUGIN_ROOTDN_ACCESS) - - ############################################################################ - # Cleanup - remove ACI from cn=config and test user - ############################################################################ - - try: - inst.modify_s(DN_CONFIG, [(ldap.MOD_DELETE, 'aci', ACI)]) - except ldap.LDAPError as e: - log.fatal('test_rootdn: Failed to add aci to config: error ' + e.message['desc']) - assert False - - try: - inst.delete_s(USER1_DN) - except ldap.LDAPError as e: - log.fatal('test_rootdn: Failed to delete user1: ' + e.message['desc']) - assert False - - ############################################################################ - # Test passed - ############################################################################ - - log.info('test_rootdn: PASS\n') - - return - - -# Array of test functions -func_tests = [test_acctpolicy, test_attruniq, test_automember, test_dna, - test_linkedattrs, test_memberof, test_mep, test_passthru, - test_referint, test_retrocl, test_rootdn] - - -def test_all_plugins(inst, args=None): - for func in func_tests: - func(inst, args) - - return diff --git a/dirsrvtests/tests/suites/dynamic_plugins/stress_tests.py b/dirsrvtests/tests/suites/dynamic_plugins/stress_tests.py index f913fc5..b4d3e34 100644 --- a/dirsrvtests/tests/suites/dynamic_plugins/stress_tests.py +++ b/dirsrvtests/tests/suites/dynamic_plugins/stress_tests.py @@ -18,6 +18,9 @@ import ldap from lib389 import DirSrv, Entry from lib389._constants import * from lib389.properties import * +from lib389.plugins import ReferentialIntegrityPlugin, MemberOfPlugin +from lib389.utils import * +from lib389.idm.directorymanager import * log = logging.getLogger(__name__) @@ -25,39 +28,18 @@ NUM_USERS = 250 GROUP_DN = 'cn=stress-group,' + DEFAULT_SUFFIX -def openConnection(inst): - # Open a new connection to our LDAP server - server = DirSrv(verbose=False) - args_instance[SER_HOST] = HOST_STANDALONE - args_instance[SER_PORT] = PORT_STANDALONE - args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE - args_standalone = args_instance.copy() - server.allocate(args_standalone) - server.open() - - return server - - # Configure Referential Integrity Plugin for stress test def configureRI(inst): - inst.plugins.enable(name=PLUGIN_REFER_INTEGRITY) - PLUGIN_DN = 'cn=' + PLUGIN_REFER_INTEGRITY + ',cn=plugins,cn=config' - try: - inst.modify_s(PLUGIN_DN, [(ldap.MOD_REPLACE, 'referint-membership-attr', 'uniquemember')]) - except ldap.LDAPError as e: - log.fatal('configureRI: Failed to configure RI plugin: error ' + e.message['desc']) - assert False + plugin = ReferentialIntegrityPlugin(inst) + plugin.enable() + plugin.replace('referint-membership-attr', 'uniquemember') # Configure MemberOf Plugin for stress test def configureMO(inst): - inst.plugins.enable(name=PLUGIN_MEMBER_OF) - PLUGIN_DN = 'cn=' + PLUGIN_MEMBER_OF + ',cn=plugins,cn=config' - try: - inst.modify_s(PLUGIN_DN, [(ldap.MOD_REPLACE, 'memberofgroupattr', 'uniquemember')]) - except ldap.LDAPError as e: - log.fatal('configureMO: Failed to update config(uniquemember): error ' + e.message['desc']) - assert False + plugin = MemberOfPlugin(inst) + plugin.enable() + plugin.replace('memberofgroupattr', 'uniquemember') def cleanup(conn): @@ -76,7 +58,8 @@ class DelUsers(threading.Thread): self.rdnval = rdnval def run(self): - conn = openConnection(self.inst) + dm = DirectoryManager(self.inst) + conn = dm.bind() idx = 0 log.info('DelUsers - Deleting ' + str(NUM_USERS) + ' entries (' + self.rdnval + ')...') while idx < NUM_USERS: @@ -104,17 +87,18 @@ class AddUsers(threading.Thread): def run(self): # Start adding users - conn = openConnection(self.inst) + dm = DirectoryManager(self.inst) + conn = dm.bind() idx = 0 if self.addToGroup: try: conn.add_s(Entry((GROUP_DN, - {'objectclass': 'top groupOfNames groupOfUniqueNames extensibleObject'.split(), - 'uid': 'user' + str(idx)}))) + {'objectclass': b'top groupOfNames groupOfUniqueNames'.split(), + 'cn': 'stress-group'}))) except ldap.LDAPError as e: if e == ldap.UNAVAILABLE or e == ldap.SERVER_DOWN: - log.fatal('AddUsers: failed to add group (' + USER_DN + ') error: ' + e.message['desc']) + log.fatal('AddUsers: failed to add group (' + GROUP_DN + ') error: ' + e.message['desc']) assert False log.info('AddUsers - Adding ' + str(NUM_USERS) + ' entries (' + self.rdnval + ')...') @@ -122,8 +106,8 @@ class AddUsers(threading.Thread): while idx < NUM_USERS: USER_DN = 'uid=' + self.rdnval + str(idx) + ',' + DEFAULT_SUFFIX try: - conn.add_s(Entry((USER_DN, {'objectclass': 'top extensibleObject'.split(), - 'uid': 'user' + str(idx)}))) + conn.add_s(Entry((USER_DN, {'objectclass': b'top nsOrgPerson'.split(), + 'uid': ensure_bytes('user' + str(idx))}))) except ldap.LDAPError as e: if e == ldap.UNAVAILABLE or e == ldap.SERVER_DOWN: log.fatal('AddUsers: failed to add (' + USER_DN + ') error: ' + e.message['desc']) @@ -132,7 +116,7 @@ class AddUsers(threading.Thread): if self.addToGroup: # Add the user to the group try: - conn.modify_s(GROUP_DN, [(ldap.MOD_ADD, 'uniquemember', USER_DN)]) + conn.modify_s(GROUP_DN, [(ldap.MOD_ADD, 'uniquemember', ensure_bytes(USER_DN))]) except ldap.LDAPError as e: if e == ldap.UNAVAILABLE or e == ldap.SERVER_DOWN: log.fatal('AddUsers: Failed to add user' + USER_DN + ' to group: error ' + e.message['desc']) diff --git a/dirsrvtests/tests/suites/plugins/acceptance_test.py b/dirsrvtests/tests/suites/plugins/acceptance_test.py new file mode 100644 index 0000000..2ab8952 --- /dev/null +++ b/dirsrvtests/tests/suites/plugins/acceptance_test.py @@ -0,0 +1,1415 @@ +# --- BEGIN COPYRIGHT BLOCK --- +# Copyright (C) 2016 Red Hat, Inc. +# All rights reserved. +# +# License: GPL (version 3 or any later version). +# See LICENSE for details. +# --- END COPYRIGHT BLOCK --- +# +''' +Created on Dec 09, 2014 + +@author: mreynolds +''' +import logging +import subprocess +import pytest +from lib389.utils import * +from lib389.plugins import * +from lib389._constants import * +from lib389.dseldif import DSEldif +from lib389.idm.user import UserAccounts, UserAccount +from lib389.idm.group import Groups +from lib389.idm.organisationalunit import OrganisationalUnits +from lib389.idm.domain import Domain +from lib389.topologies import create_topology, topology_i2 as topo + +log = logging.getLogger(__name__) + +USER_DN = 'uid=test_user_1001,ou=people,dc=example,dc=com' +USER_PW = 'password' +GROUP_DN = 'cn=group,' + DEFAULT_SUFFIX +CONFIG_AREA = 'nsslapd-pluginConfigArea' + +if ds_is_older('1.3.7'): + MEMBER_ATTR = 'member' +else: + MEMBER_ATTR = 'memberOf' + +''' + Functional tests for each plugin + + Test: + plugin restarts (test when on and off) + plugin config validation + plugin dependencies + plugin functionality (including plugin tasks) +''' + + +################################################################################ +# +# Test Plugin Dependency +# +################################################################################ +def check_dependency(inst, plugin, online=True): + """Set the "account usability" plugin to depend on this plugin. + This plugin is generic, always enabled, and perfect for our testing + """ + + acct_usability = AccountUsabilityPlugin(inst) + acct_usability.replace('nsslapd-plugin-depends-on-named', plugin.rdn) + + if online: + with pytest.raises(ldap.UNWILLING_TO_PERFORM): + plugin.disable() + # Now undo the change + acct_usability.remove('nsslapd-plugin-depends-on-named', plugin.rdn) + else: + plugin.disable() + with pytest.raises(subprocess.CalledProcessError): + inst.restart() + dse_ldif = DSEldif(inst) + dse_ldif.delete(acct_usability.dn, 'nsslapd-plugin-depends-on-named') + dse_ldif.replace(plugin.dn, 'nsslapd-pluginEnabled', 'on') + inst.start() + + +################################################################################ +# +# Test Account Policy Plugin (0) +# +################################################################################ +def test_acctpolicy(topo, args=None): + inst = topo[0] + + # stop the plugin, and start it + plugin = AccountPolicyPlugin(inst) + plugin.disable() + plugin.enable() + + if args == "restart": + return True + + # If args is None then we run the test suite as pytest standalone and it's not dynamic + if args is None: + inst.restart() + + log.info('Testing {}'.format(PLUGIN_ACCT_POLICY)) + + ############################################################################ + # Configure plugin + ############################################################################ + # Add the config entry + ap_configs = AccountPolicyConfigs(inst) + try: + ap_config = ap_configs.create(properties={'cn': 'config', + 'alwaysrecordlogin': 'yes', + 'stateattrname': 'lastLoginTime'}) + except ldap.ALREADY_EXISTS: + ap_config = ap_configs.get('config') + ap_config.replace_many(('alwaysrecordlogin', 'yes'), + ('stateattrname', 'lastLoginTime')) + + ############################################################################ + # Test plugin + ############################################################################ + # Add an entry + users = UserAccounts(inst, DEFAULT_SUFFIX) + user = users.create_test_user(1000, 2000) + user.add('objectclass', 'extensibleObject') + user.replace('userPassword', USER_PW) + + # Bind as user + user.bind(USER_PW) + time.sleep(1) + + # Check lastLoginTime of USER1 + entries = inst.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, 'lastLoginTime=*') + assert entries + + ############################################################################ + # Change config - change the stateAttrName to a new attribute + ############################################################################ + ap_config.replace('stateattrname', 'testLastLoginTime') + + ############################################################################ + # Test plugin + ############################################################################ + # login as user + user.bind(USER_PW) + time.sleep(1) + + # Check testLastLoginTime was added to USER1 + entries = inst.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, '(testLastLoginTime=*)') + assert entries + + ############################################################################ + # Test plugin dependency + ############################################################################ + check_dependency(inst, plugin, online=isinstance(args, str)) + + ############################################################################ + # Cleanup + ############################################################################ + user.delete() + + ############################################################################ + # Test passed + ############################################################################ + log.info('test_acctpolicy: PASS\n') + + return + + +################################################################################ +# +# Test Attribute Uniqueness Plugin (1) +# +################################################################################ +def test_attruniq(topo, args=None): + inst = topo[0] + + # stop the plugin, and start it + plugin = AttributeUniquenessPlugin(inst) + plugin.disable() + plugin.enable() + + if args == "restart": + return + + # If args is None then we run the test suite as pytest standalone and it's not dynamic + if args is None: + inst.restart() + + log.info('Testing {}'.format(PLUGIN_ATTR_UNIQUENESS)) + user1_dict = {'objectclass': 'extensibleObject', + 'uid': 'testuser1', + 'cn': 'testuser1', + 'sn': 'user1', + 'uidNumber': '1001', + 'gidNumber': '2001', + 'mail': 'user1@example.com', + 'mailAlternateAddress': 'user1@alt.example.com', + 'homeDirectory': '/home/testuser1', + 'userpassword': 'password'} + user2_dict = {'objectclass': 'extensibleObject', + 'uid': 'testuser2', + 'cn': 'testuser2', + 'sn': 'user2', + 'uidNumber': '1000', + 'gidNumber': '2000', + 'homeDirectory': '/home/testuser2', + 'userpassword': 'password'} + + ############################################################################ + # Configure plugin + ############################################################################ + plugin.replace('uniqueness-attribute-name', 'cn') + if args is None: + inst.restart() + + ############################################################################ + # Test plugin + ############################################################################ + # Add an entry + users = UserAccounts(inst, DEFAULT_SUFFIX) + user1 = users.create(properties=user1_dict) + + # Add an entry with a duplicate "cn" + with pytest.raises(ldap.CONSTRAINT_VIOLATION): + user2_dict['cn'] = 'testuser1' + users.create(properties=user2_dict) + + ############################################################################ + # Change config to use "mail" instead of "uid" + ############################################################################ + + plugin.replace('uniqueness-attribute-name', 'mail') + + ############################################################################ + # Test plugin - Add an entry, that has a duplicate "mail" value + ############################################################################ + with pytest.raises(ldap.CONSTRAINT_VIOLATION): + user2_dict['mail'] = 'user1@example.com' + users.create(properties=user2_dict) + + ############################################################################ + # Reconfigure plugin for mail and mailAlternateAddress + ############################################################################ + plugin.add('uniqueness-attribute-name', 'mailAlternateAddress') + + ############################################################################ + # Test plugin - Add an entry, that has a duplicate "mail" value + ############################################################################ + with pytest.raises(ldap.CONSTRAINT_VIOLATION): + user2_dict['mail'] = 'user1@example.com' + users.create(properties=user2_dict) + + ############################################################################ + # Test plugin - Add an entry, that has a duplicate "mailAlternateAddress" value + ############################################################################ + with pytest.raises(ldap.CONSTRAINT_VIOLATION): + user2_dict['mailAlternateAddress'] = 'user1@alt.example.com' + users.create(properties=user2_dict) + + ############################################################################ + # Test plugin - Add an entry, that has a duplicate "mail" value conflicting mailAlternateAddress + ############################################################################ + with pytest.raises(ldap.CONSTRAINT_VIOLATION): + user2_dict['mail'] = 'user1@alt.example.com' + users.create(properties=user2_dict) + + ############################################################################ + # Test plugin - Add an entry, that has a duplicate "mailAlternateAddress" conflicting mail + ############################################################################ + with pytest.raises(ldap.CONSTRAINT_VIOLATION): + user2_dict['mailAlternateAddress'] = 'user1@example.com' + users.create(properties=user2_dict) + + ############################################################################ + # Test plugin dependency + ############################################################################ + check_dependency(inst, plugin, online=isinstance(args, str)) + + ############################################################################ + # Cleanup + ############################################################################ + user1.delete() + + ############################################################################ + # Test passed + ############################################################################ + log.info('test_attruniq: PASS\n') + return + + +################################################################################ +# +# Test Auto Membership Plugin (2) +# +################################################################################ +def test_automember(topo, args=None): + inst = topo[0] + + # stop the plugin, and start it + plugin = AutoMembershipPlugin(inst) + plugin.disable() + plugin.enable() + + if args == "restart": + return + + # If args is None then we run the test suite as pytest standalone and it's not dynamic + if args is None: + inst.restart() + + CONFIG_DN = 'cn=config,cn=' + PLUGIN_AUTOMEMBER + ',cn=plugins,cn=config' + + log.info('Testing ' + PLUGIN_AUTOMEMBER + '...') + + ############################################################################ + # Configure plugin + ############################################################################ + + # Add the automember group + groups = Groups(inst, DEFAULT_SUFFIX) + group = groups.create(properties={'cn': 'group'}) + + ous = OrganisationalUnits(inst, DEFAULT_SUFFIX) + branch1 = ous.create(properties={'ou': 'branch1'}) + branch2 = ous.create(properties={'ou': 'branch2'}) + + # Add the automember config entry + am_configs = AutoMembershipDefinitions(inst) + am_config = am_configs.create(properties={'cn': 'config', + 'autoMemberScope': branch1.dn, + 'autoMemberFilter': 'objectclass=top', + 'autoMemberDefaultGroup': group.dn, + 'autoMemberGroupingAttr': '{}:dn'.format(MEMBER_ATTR)}) + + ############################################################################ + # Test the plugin + ############################################################################ + + users = UserAccounts(inst, DEFAULT_SUFFIX, rdn='ou={}'.format(branch1.rdn)) + # Add a user that should get added to the group + user1 = users.create_test_user(uid=1001) + + # Check the group + group_members = group.get_attr_vals_utf8(MEMBER_ATTR) + assert user1.dn in group_members + + ############################################################################ + # Change config + ############################################################################ + group.add('objectclass', 'groupOfUniqueNames') + am_config.set_groupattr('uniquemember:dn') + am_config.set_scope(branch2.dn) + + ############################################################################ + # Test plugin + ############################################################################ + # Add a user that should get added to the group + users = UserAccounts(inst, DEFAULT_SUFFIX, rdn='ou={}'.format(branch2.rdn)) + user2 = users.create_test_user(uid=1002) + + # Check the group + group_members = group.get_attr_vals_utf8('uniquemember') + assert user2.dn in group_members + + ############################################################################ + # Test Task + ############################################################################ + + # Disable plugin + plugin.disable() + + # If args is None then we run the test suite as pytest standalone and it's not dynamic + if args is None: + inst.restart() + + # Add an entry that should be picked up by automember - verify it is not(yet) + user3 = users.create_test_user(uid=1003) + + # Check the group - uniquemember should not exist + group_members = group.get_attr_vals_utf8('uniquemember') + assert user3.dn not in group_members + + # Enable plugin + plugin.enable() + + # If args is None then we run the test suite as pytest standalone and it's not dynamic + if args is None: + inst.restart() + + task = plugin.fixup(branch2.dn, 'objectclass=top') + task.wait() + + # Verify the fixup task worked + group_members = group.get_attr_vals_utf8('uniquemember') + assert user3.dn in group_members + + ############################################################################ + # Test plugin dependency + ############################################################################ + check_dependency(inst, plugin, online=isinstance(args, str)) + + ############################################################################ + # Cleanup + ############################################################################ + user1.delete() + user2.delete() + user3.delete() + branch1.delete() + branch2.delete() + group.delete() + am_config.delete() + + ############################################################################ + # Test passed + ############################################################################ + log.info('test_automember: PASS\n') + return + + +################################################################################ +# +# Test DNA Plugin (3) +# +################################################################################ +def test_dna(topo, args=None): + inst = topo[0] + + # stop the plugin, and start it + plugin = DNAPlugin(inst) + plugin.disable() + plugin.enable() + + if args == "restart": + return + + # If args is None then we run the test suite as pytest standalone and it's not dynamic + if args is None: + inst.restart() + + log.info('Testing ' + PLUGIN_DNA + '...') + + ############################################################################ + # Configure plugin + ############################################################################ + dna_configs = DNAPluginConfigs(inst, plugin.dn) + try: + dna_config = dna_configs.create(properties={'cn': 'config', + 'dnatype': 'uidNumber', + 'dnafilter': '(objectclass=top)', + 'dnascope': DEFAULT_SUFFIX, + 'dnaMagicRegen': '-1', + 'dnaMaxValue': '50000', + 'dnaNextValue': '1'}) + except ldap.ALREADY_EXISTS: + dna_config = dna_configs.get('config') + dna_config.replace_many(('dnaNextValue', '1'), ('dnaMagicRegen', '-1')) + + ############################################################################ + # Test plugin + ############################################################################ + users = UserAccounts(inst, DEFAULT_SUFFIX) + user1 = users.create_test_user(uid=1) + + # See if the entry now has the new uidNumber assignment - uidNumber=1 + entries = inst.search_s(user1.dn, ldap.SCOPE_BASE, '(uidNumber=1)') + assert entries + + # Test the magic regen value + user1.replace('uidNumber', '-1') + + # See if the entry now has the new uidNumber assignment - uidNumber=2 + entries = inst.search_s(user1.dn, ldap.SCOPE_BASE, '(uidNumber=2)') + assert entries + + ################################################################################ + # Change the config + ################################################################################ + dna_config.replace('dnaMagicRegen', '-2') + + ################################################################################ + # Test plugin + ################################################################################ + + # Test the magic regen value + user1.replace('uidNumber', '-2') + + # See if the entry now has the new uidNumber assignment - uidNumber=3 + entries = inst.search_s(user1.dn, ldap.SCOPE_BASE, '(uidNumber=3)') + assert entries + + ############################################################################ + # Test plugin dependency + ############################################################################ + check_dependency(inst, plugin, online=isinstance(args, str)) + + ############################################################################ + # Cleanup + ############################################################################ + user1.delete() + dna_config.delete() + plugin.disable() + + # If args is None then we run the test suite as pytest standalone and it's not dynamic + if args is None: + inst.restart() + + ############################################################################ + # Test passed + ############################################################################ + log.info('test_dna: PASS\n') + return + + +################################################################################ +# +# Test Linked Attrs Plugin (4) +# +################################################################################ +def test_linkedattrs(topo, args=None): + inst = topo[0] + + # stop the plugin, and start it + plugin = LinkedAttributesPlugin(inst) + plugin.disable() + plugin.enable() + + if args == "restart": + return + + # If args is None then we run the test suite as pytest standalone and it's not dynamic + if args is None: + inst.restart() + + log.info('Testing ' + PLUGIN_LINKED_ATTRS + '...') + + ############################################################################ + # Configure plugin + ############################################################################ + + # Add test entries + users = UserAccounts(inst, DEFAULT_SUFFIX) + user1 = users.create_test_user(uid=1001) + user1.add('objectclass', 'extensibleObject') + user2 = users.create_test_user(uid=1002) + user2.add('objectclass', 'extensibleObject') + + # Add the linked attrs config entry + la_configs = LinkedAttributesConfigs(inst) + la_config = la_configs.create(properties={'cn': 'config', + 'linkType': 'directReport', + 'managedType': 'manager'}) + + ############################################################################ + # Test plugin + ############################################################################ + # Set "directReport" should add "manager" to the other entry + user1.replace('directReport', user2.dn) + + # See if manager was added to the other entry + entries = inst.search_s(user2.dn, ldap.SCOPE_BASE, '(manager=*)') + assert entries + + # Remove "directReport" should remove "manager" to the other entry + user1.remove_all('directReport') + + # See if manager was removed + entries = inst.search_s(user2.dn, ldap.SCOPE_BASE, '(manager=*)') + assert not entries + + ############################################################################ + # Change the config - using linkType "indirectReport" now + ############################################################################ + la_config.replace('linkType', 'indirectReport') + + ############################################################################ + # Test plugin + ############################################################################ + # Make sure the old linkType(directManager) is not working + user1.replace('directReport', user2.dn) + + # See if manager was added to the other entry, better not be... + entries = inst.search_s(user2.dn, ldap.SCOPE_BASE, '(manager=*)') + assert not entries + + # Now, set the new linkType "indirectReport", which should add "manager" to the other entry + user1.replace('indirectReport', user2.dn) + + # See if manager was added to the other entry, better not be + entries = inst.search_s(user2.dn, ldap.SCOPE_BASE, '(manager=*)') + assert entries + + # Remove "indirectReport" should remove "manager" to the other entry + user1.remove_all('indirectReport') + + # See if manager was removed + entries = inst.search_s(user2.dn, ldap.SCOPE_BASE, '(manager=*)') + assert not entries + + ############################################################################ + # Test Fixup Task + ############################################################################ + # Disable plugin and make some updates that would of triggered the plugin + plugin.disable() + + # If args is None then we run the test suite as pytest standalone and it's not dynamic + if args is None: + inst.restart() + + user1.replace('indirectReport', user2.dn) + + # The entry should not have a manager attribute + entries = inst.search_s(user2.dn, ldap.SCOPE_BASE, '(manager=*)') + assert not entries + + # Enable the plugin and rerun the task entry + plugin.enable() + + # If args is None then we run the test suite as pytest standalone and it's not dynamic + if args is None: + inst.restart() + + # Add the task again + task = plugin.fixup(DEFAULT_SUFFIX, 'objectclass=top') + task.wait() + + # Check if user2 now has a manager attribute now + entries = inst.search_s(user2.dn, ldap.SCOPE_BASE, '(manager=*)') + assert entries + + ############################################################################ + # Test plugin dependency + ############################################################################ + check_dependency(inst, plugin, online=isinstance(args, str)) + + ############################################################################ + # Cleanup + ############################################################################ + user1.delete() + user2.delete() + la_config.delete() + + ############################################################################ + # Test passed + ############################################################################ + log.info('test_linkedattrs: PASS\n') + return + + +################################################################################ +# +# Test MemberOf Plugin (5) +# +################################################################################ +def test_memberof(topo, args=None): + inst = topo[0] + + # stop the plugin, and start it + plugin = MemberOfPlugin(inst) + plugin.disable() + plugin.enable() + + if args == "restart": + return + + # If args is None then we run the test suite as pytest standalone and it's not dynamic + if args is None: + inst.restart() + + log.info('Testing ' + PLUGIN_MEMBER_OF + '...') + + ############################################################################ + # Configure plugin + ############################################################################ + plugin.replace_groupattr('member') + + ############################################################################ + # Test plugin + ############################################################################ + # Add our test entries + users = UserAccounts(inst, DEFAULT_SUFFIX) + user1 = users.create_test_user(uid=1001) + + groups = Groups(inst, DEFAULT_SUFFIX) + group = groups.create(properties={'cn': 'group', + 'member': user1.dn}) + group.add('objectclass', 'groupOfUniqueNames') + + memberof_config = MemberOfSharedConfig(inst, 'cn=memberOf config,{}'.format(DEFAULT_SUFFIX)) + memberof_config.create(properties={'cn': 'memberOf config', + 'memberOfGroupAttr': 'member', + 'memberOfAttr': MEMBER_ATTR}) + + # Check if the user now has a "memberOf" attribute + entries = inst.search_s(user1.dn, ldap.SCOPE_BASE, '({}=*)'.format(MEMBER_ATTR)) + assert entries + + # Remove "member" should remove "memberOf" from the entry + group.remove_all('member') + + # Check that "memberOf" was removed + entries = inst.search_s(user1.dn, ldap.SCOPE_BASE, '({}=*)'.format(MEMBER_ATTR)) + assert not entries + + ############################################################################ + # Change the config + ############################################################################ + plugin.replace('memberofgroupattr', 'uniquemember') + + ############################################################################ + # Test plugin + ############################################################################ + group.replace('uniquemember', user1.dn) + + # Check if the user now has a "memberOf" attribute + entries = inst.search_s(user1.dn, ldap.SCOPE_BASE, '({}=*)'.format(MEMBER_ATTR)) + assert entries + + # Remove "uniquemember" should remove "memberOf" from the entry + group.remove_all('uniquemember') + + # Check that "memberOf" was removed + entries = inst.search_s(user1.dn, ldap.SCOPE_BASE, '({}=*)'.format(MEMBER_ATTR)) + assert not entries + + ############################################################################ + # Set the shared config entry and test the plugin + ############################################################################ + # The shared config entry uses "member" - the above test uses "uniquemember" + plugin.set_configarea(memberof_config.dn) + if args is None: + inst.restart() + + # Delete the test entries then readd them to start with a clean slate + user1.delete() + group.delete() + + user1 = users.create_test_user(uid=1001) + group = groups.create(properties={'cn': 'group', + 'member': user1.dn}) + group.add('objectclass', 'groupOfUniqueNames') + + # Test the shared config + # Check if the user now has a "memberOf" attribute + entries = inst.search_s(user1.dn, ldap.SCOPE_BASE, '({}=*)'.format(MEMBER_ATTR)) + assert entries + + group.remove_all('member') + + # Check that "memberOf" was removed + entries = inst.search_s(user1.dn, ldap.SCOPE_BASE, '({}=*)'.format(MEMBER_ATTR)) + assert not entries + + ############################################################################ + # Change the shared config entry to use 'uniquemember' and test the plugin + ############################################################################ + memberof_config.replace('memberofgroupattr', 'uniquemember') + + group.replace('uniquemember', user1.dn) + + # Check if the user now has a "memberOf" attribute + entries = inst.search_s(user1.dn, ldap.SCOPE_BASE, '({}=*)'.format(MEMBER_ATTR)) + assert entries + + # Remove "uniquemember" should remove "memberOf" from the entry + group.remove_all('uniquemember') + + # Check that "memberOf" was removed + entries = inst.search_s(user1.dn, ldap.SCOPE_BASE, '({}=*)'.format(MEMBER_ATTR)) + assert not entries + + ############################################################################ + # Remove shared config from plugin, and retest + ############################################################################ + # First change the plugin to use member before we move the shared config that uses uniquemember + plugin.replace('memberofgroupattr', 'member') + + # Remove shared config from plugin + plugin.remove_configarea() + + group.replace('member', user1.dn) + + # Check if the user now has a "memberOf" attribute + entries = inst.search_s(user1.dn, ldap.SCOPE_BASE, '({}=*)'.format(MEMBER_ATTR)) + assert entries + + # Remove "uniquemember" should remove "memberOf" from the entry + group.remove_all('member') + + # Check that "memberOf" was removed + entries = inst.search_s(user1.dn, ldap.SCOPE_BASE, '({}=*)'.format(MEMBER_ATTR)) + assert not entries + + ############################################################################ + # Test Fixup Task + ############################################################################ + plugin.disable() + + # If args is None then we run the test suite as pytest standalone and it's not dynamic + if args is None: + inst.restart() + + # First change the plugin to use uniquemember + plugin.replace('memberofgroupattr', 'uniquemember') + + # Add uniquemember, should not update USER1 + group.replace('uniquemember', user1.dn) + + # Check for "memberOf" + entries = inst.search_s(user1.dn, ldap.SCOPE_BASE, '({}=*)'.format(MEMBER_ATTR)) + assert not entries + + # Enable memberof plugin + plugin.enable() + + # If args is None then we run the test suite as pytest standalone and it's not dynamic + if args is None: + inst.restart() + + ############################################################# + # Test memberOf fixup arg validation: Test the DN and filter + ############################################################# + for basedn, filter in (('{}bad'.format(DEFAULT_SUFFIX), 'objectclass=top'), + ("bad", 'objectclass=top'), + (DEFAULT_SUFFIX, '(objectclass=top')): + task = plugin.fixup(basedn, filter) + task.wait() + exitcode = task.get_exit_code() + assert exitcode != "0", 'test_memberof: Task with invalid DN still reported success' + + #################################################### + # Test fixup works + #################################################### + # Run the task and validate that it worked + task = plugin.fixup(DEFAULT_SUFFIX, 'objectclass=top') + task.wait() + + # Check for "memberOf" + entries = inst.search_s(user1.dn, ldap.SCOPE_BASE, '({}=*)'.format(MEMBER_ATTR)) + assert entries + + ############################################################################ + # Test plugin dependency + ############################################################################ + check_dependency(inst, plugin, online=isinstance(args, str)) + + ############################################################################ + # Cleanup + ############################################################################ + user1.delete() + group.delete() + memberof_config.delete() + + ############################################################################ + # Test passed + ############################################################################ + log.info('test_memberof: PASS\n') + return + + +################################################################################ +# +# Test Managed Entry Plugin (6) +# +################################################################################ +def test_mep(topo, args=None): + inst = topo[0] + + # stop the plugin, and start it + plugin = ManagedEntriesPlugin(inst) + plugin.disable() + plugin.enable() + + if args == "restart": + return + + # If args is None then we run the test suite as pytest standalone and it's not dynamic + if args is None: + inst.restart() + + log.info('Testing ' + PLUGIN_MANAGED_ENTRY + '...') + + ############################################################################ + # Configure plugin + ############################################################################ + # Add our org units + ous = OrganisationalUnits(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: posixGroup|objectclass: extensibleObject'.split('|'), + 'mepMappedAttr': 'cn: $cn|uid: $cn|gidNumber: $uidNumber'.split('|') + }) + mep_configs = MEPConfigs(inst) + mep_config = mep_configs.create(properties={'cn': 'config', + 'originScope': ou_people.dn, + 'originFilter': 'objectclass=posixAccount', + 'managedBase': ou_groups.dn, + 'managedTemplate': mep_template1.dn}) + if args is None: + inst.restart() + + ############################################################################ + # Test plugin + ############################################################################ + # Add an entry that meets the MEP scope + test_users_m1 = UserAccounts(inst, DEFAULT_SUFFIX, rdn='ou={}'.format(ou_people.rdn)) + test_user1 = test_users_m1.create_test_user(1001) + + # Check if a managed group entry was created + entries = inst.search_s('cn={},{}'.format(test_user1.rdn, ou_groups.dn), ldap.SCOPE_BASE, '(objectclass=top)') + assert len(entries) == 1 + + ############################################################################ + # Change the config + ############################################################################ + # Add a new template entry + mep_template2 = mep_templates.create(properties={ + 'cn': 'MEP template2', + 'mepRDNAttr': 'uid', + 'mepStaticAttr': 'objectclass: posixGroup|objectclass: extensibleObject'.split('|'), + 'mepMappedAttr': 'cn: $cn|uid: $cn|gidNumber: $uidNumber'.split('|') + }) + mep_config.replace('managedTemplate', mep_template2.dn) + + ############################################################################ + # Test plugin + ############################################################################ + # Add an entry that meets the MEP scope + test_user2 = test_users_m1.create_test_user(1002) + + # Check if a managed group entry was created + entries = inst.search_s('uid={},{}'.format(test_user2.rdn, ou_groups.dn), ldap.SCOPE_BASE, '(objectclass=top)') + assert len(entries) == 1 + + ############################################################################ + # Test plugin dependency + ############################################################################ + check_dependency(inst, plugin, online=isinstance(args, str)) + + ############################################################################ + # Cleanup + ############################################################################ + test_user1.delete() + test_user2.delete() + ou_people.delete() + ou_groups.delete() + mep_config.delete() + mep_template1.delete() + mep_template2.delete() + + ############################################################################ + # Test passed + ############################################################################ + log.info('test_mep: PASS\n') + return + + +################################################################################ +# +# Test Passthru Plugin (7) +# +################################################################################ +def test_passthru(topo, args=None): + inst1 = topo[0] + inst2 = topo[1] + + # Passthru is a bit picky about the state of the entry - we can't just restart it + if args == "restart": + return + + # stop the plugin + plugin = PassThroughAuthenticationPlugin(inst1) + plugin.disable() + + # If args is None then we run the test suite as pytest standalone and it's not dynamic + if args is None: + inst1.restart() + + PASS_SUFFIX1 = 'dc=pass1,dc=thru' + PASS_SUFFIX2 = 'dc=pass2,dc=thru' + PASS_BE1 = 'PASS1' + PASS_BE2 = 'PASS2' + + log.info('Testing ' + PLUGIN_PASSTHRU + '...') + + ############################################################################ + # Use a new "remote" instance, and a user for auth + ############################################################################ + # Create a second backend + backend1 = inst2.backends.create(properties={'cn': PASS_BE1, + 'nsslapd-suffix': PASS_SUFFIX1}) + backend2 = inst2.backends.create(properties={'cn': PASS_BE2, + 'nsslapd-suffix': PASS_SUFFIX2}) + + # Create the top of the tree + suffix = Domain(inst2, PASS_SUFFIX1) + pass1 = suffix.create(properties={'dc': 'pass1'}) + suffix = Domain(inst2, PASS_SUFFIX2) + pass2 = suffix.create(properties={'dc': 'pass2'}) + + # Add user to suffix1 + users = UserAccounts(inst2, pass1.dn, None) + test_user1 = users.create_test_user(1001) + test_user1.replace('userpassword', 'password') + + users = UserAccounts(inst2, pass2.dn, None) + test_user2 = users.create_test_user(1002) + test_user2.replace('userpassword', 'password') + + ############################################################################ + # Configure and start plugin + ############################################################################ + plugin.replace('nsslapd-pluginarg0', + 'ldap://{}:{}/{}'.format(inst2.host, inst2.port, pass1.dn)) + plugin.enable() + + # If args is None then we run the test suite as pytest standalone and it's not dynamic + if args is None: + inst1.restart() + + ############################################################################ + # Test plugin + ############################################################################ + # login as user + inst1.simple_bind_s(test_user1.dn, "password") + + ############################################################################ + # Change the config + ############################################################################ + # login as root DN + inst1.simple_bind_s(DN_DM, PASSWORD) + + plugin.replace('nsslapd-pluginarg0', + 'ldap://{}:{}/{}'.format(inst2.host, inst2.port, pass2.dn)) + if args is None: + inst1.restart() + + ############################################################################ + # Test plugin + ############################################################################ + + # login as user + inst1.simple_bind_s(test_user2.dn, "password") + + # login as root DN + inst1.simple_bind_s(DN_DM, PASSWORD) + + # Clean up + backend1.delete() + backend2.delete() + + ############################################################################ + # Test plugin dependency + ############################################################################ + check_dependency(inst1, plugin, online=isinstance(args, str)) + + ############################################################################ + # Test passed + ############################################################################ + log.info('test_passthru: PASS\n') + return + + +################################################################################ +# +# Test Referential Integrity Plugin (8) +# +################################################################################ +def test_referint(topo, args=None): + inst = topo[0] + + # stop the plugin, and start it + plugin = ReferentialIntegrityPlugin(inst) + plugin.disable() + plugin.enable() + + if args == "restart": + return + + # If args is None then we run the test suite as pytest standalone and it's not dynamic + if args is None: + inst.restart() + + log.info('Testing ' + PLUGIN_REFER_INTEGRITY + '...') + + ############################################################################ + # Configure plugin + ############################################################################ + plugin.replace('referint-membership-attr', 'member') + + ############################################################################ + # Test plugin + ############################################################################ + # Add some users and a group + users = UserAccounts(inst, DEFAULT_SUFFIX, None) + user1 = users.create_test_user(uid=1001) + user2 = users.create_test_user(uid=1002) + + groups = Groups(inst, DEFAULT_SUFFIX, None) + group = groups.create(properties={'cn': 'group', + MEMBER_ATTR: user1.dn}) + group.add('objectclass', 'groupOfUniqueNames') + group.add('uniquemember', user2.dn) + + # Grab the referint log file from the plugin + referin_logfile = plugin.get_attr_val_utf8('referint-logfile') + + # Add shared config entry + referin_config = ReferentialIntegrityConfig(inst, 'cn=RI config,{}'.format(DEFAULT_SUFFIX)) + referin_config.create(properties={'cn': 'RI config', + 'referint-membership-attr': 'member', + 'referint-update-delay': '0', + 'referint-logfile': referin_logfile}) + + user1.delete() + + # Check for integrity + entry = inst.search_s(group.dn, ldap.SCOPE_BASE, '(member={})'.format(user1.dn)) + assert not entry + + ############################################################################ + # Change the config + ############################################################################ + plugin.replace('referint-membership-attr', 'uniquemember') + + ############################################################################ + # Test plugin + ############################################################################ + + user2.delete() + + # Check for integrity + entry = inst.search_s(group.dn, ldap.SCOPE_BASE, '(uniquemember={})'.format(user2.dn)) + assert not entry + + ############################################################################ + # Set the shared config entry and test the plugin + ############################################################################ + # The shared config entry uses "member" - the above test used "uniquemember" + plugin.set_configarea(referin_config.dn) + group.delete() + + user1 = users.create_test_user(uid=1001) + user2 = users.create_test_user(uid=1002) + group = groups.create(properties={'cn': 'group', + MEMBER_ATTR: user1.dn}) + group.add('objectclass', 'groupOfUniqueNames') + group.add('uniquemember', user2.dn) + + # Delete a user + user1.delete() + + # Check for integrity + entry = inst.search_s(group.dn, ldap.SCOPE_BASE, '(member={})'.format(user1.dn)) + assert not entry + + ############################################################################ + # Change the shared config entry to use 'uniquemember' and test the plugin + ############################################################################ + + referin_config.replace('referint-membership-attr', 'uniquemember') + + # Delete a user + user2.delete() + + # Check for integrity + entry = inst.search_s(group.dn, ldap.SCOPE_BASE, '(uniquemember={})'.format(user2.dn)) + assert not entry + + ############################################################################ + # Remove shared config from plugin, and retest + ############################################################################ + # First change the plugin to use member before we move the shared config that uses uniquemember + plugin.replace('referint-membership-attr', 'member') + + # Remove shared config from plugin + plugin.remove_configarea() + + # Add test user + user1 = users.create_test_user(uid=1001) + + # Add user to group + group.replace('member', user1.dn) + + # Delete a user + user1.delete() + + # Check for integrity + entry = inst.search_s(group.dn, ldap.SCOPE_BASE, '(member={})'.format(user1.dn)) + assert not entry + + ############################################################################ + # Test plugin dependency + ############################################################################ + check_dependency(inst, plugin, online=isinstance(args, str)) + + ############################################################################ + # Cleanup + ############################################################################ + group.delete() + referin_config.delete() + + ############################################################################ + # Test passed + ############################################################################ + log.info('test_referint: PASS\n') + return + + +################################################################################ +# +# Test Retro Changelog Plugin (9) +# +################################################################################ +def test_retrocl(topo, args=None): + inst = topo[0] + + # stop the plugin, and start it + plugin = RetroChangelogPlugin(inst) + plugin.disable() + plugin.enable() + + if args == "restart": + return + + # If args is None then we run the test suite as pytest standalone and it's not dynamic + if args is None: + inst.restart() + + log.info('Testing ' + PLUGIN_RETRO_CHANGELOG + '...') + + ############################################################################ + # Configure plugin + ############################################################################ + + # Gather the current change count (it's not 1 once we start the stabilty tests) + entry = inst.search_s(RETROCL_SUFFIX, ldap.SCOPE_SUBTREE, '(changenumber=*)') + entry_count = len(entry) + + ############################################################################ + # Test plugin + ############################################################################ + + # Add a user + users = UserAccounts(inst, DEFAULT_SUFFIX) + user1 = users.create_test_user(uid=1001) + + # Check we logged this in the retro cl + entry = inst.search_s(RETROCL_SUFFIX, ldap.SCOPE_SUBTREE, '(changenumber=*)') + assert entry + assert len(entry) != entry_count + + entry_count += 1 + + ############################################################################ + # Change the config - disable plugin + ############################################################################ + plugin.disable() + + # If args is None then we run the test suite as pytest standalone and it's not dynamic + if args is None: + inst.restart() + + ############################################################################ + # Test plugin + ############################################################################ + user1.delete() + + # Check we didn't logged this in the retro cl + entry = inst.search_s(RETROCL_SUFFIX, ldap.SCOPE_SUBTREE, '(changenumber=*)') + assert len(entry) == entry_count + + plugin.enable() + if args is None: + inst.restart() + + ############################################################################ + # Test plugin dependency + ############################################################################ + check_dependency(inst, plugin, online=isinstance(args, str)) + + ############################################################################ + # Test passed + ############################################################################ + log.info('test_retrocl: PASS\n') + return + + +def _rootdn_restart(inst): + """Special restart wrapper function for rootDN plugin""" + + with pytest.raises(ldap.LDAPError): + inst.restart() + # Bind as the user who can make updates to the config + inst.simple_bind_s(USER_DN, USER_PW) + # We need it online for other operations to work + inst.state = DIRSRV_STATE_ONLINE + + +################################################################################ +# +# Test Root DN Access Control Plugin (10) +# +################################################################################ +def test_rootdn(topo, args=None): + inst = topo[0] + + # stop the plugin, and start it + plugin = RootDNAccessControlPlugin(inst) + plugin.disable() + plugin.enable() + + if args == "restart": + return + + # If args is None then we run the test suite as pytest standalone and it's not dynamic + if args is None: + inst.restart() + + log.info('Testing ' + PLUGIN_ROOTDN_ACCESS + '...') + + ############################################################################ + # Configure plugin + ############################################################################ + + # Add an user and aci to open up cn=config + users = UserAccounts(inst, DEFAULT_SUFFIX) + user1 = users.create_test_user(uid=1001) + user1.replace('userpassword', USER_PW) + + # Set an aci so we can modify the plugin after ew deny the root dn + ACI = ('(target ="ldap:///cn=config")(targetattr = "*")(version 3.0;acl ' + + '"all access";allow (all)(userdn="ldap:///anyone");)') + inst.config.add('aci', ACI) + + # Set allowed IP to an unknown host - blocks root dn + plugin.replace('rootdn-allow-ip', '10.10.10.10') + + ############################################################################ + # Test plugin + ############################################################################ + # Bind as Root DN + if args is None: + _rootdn_restart(inst) + else: + with pytest.raises(ldap.LDAPError): + inst.simple_bind_s(DN_DM, PASSWORD) + # Bind as the user who can make updates to the config + inst.simple_bind_s(USER_DN, USER_PW) + + ############################################################################ + # Change the config + ############################################################################ + # First, test that invalid plugin changes are rejected + if args is None: + plugin.replace('rootdn-deny-ip', '12.12.ZZZ.12') + with pytest.raises(subprocess.CalledProcessError): + inst.restart() + dse_ldif = DSEldif(inst) + dse_ldif.delete(plugin.dn, 'rootdn-deny-ip') + _rootdn_restart(inst) + + plugin.replace('rootdn-allow-host', 'host._.com') + with pytest.raises(subprocess.CalledProcessError): + inst.restart() + dse_ldif = DSEldif(inst) + dse_ldif.delete(plugin.dn, 'rootdn-allow-host') + _rootdn_restart(inst) + else: + with pytest.raises(ldap.LDAPError): + plugin.replace('rootdn-deny-ip', '12.12.ZZZ.12') + + with pytest.raises(ldap.LDAPError): + plugin.replace('rootdn-allow-host', 'host._.com') + + # Remove the restriction + plugin.remove_all('rootdn-allow-ip') + if args is None: + inst.restart() + + ############################################################################ + # Test plugin + ############################################################################ + # Bind as Root DN + inst.simple_bind_s(DN_DM, PASSWORD) + + ############################################################################ + # Test plugin dependency + ############################################################################ + check_dependency(inst, plugin, online=isinstance(args, str)) + + ############################################################################ + # Cleanup - remove ACI from cn=config and test user + ############################################################################ + inst.config.remove('aci', ACI) + user1.delete() + + ############################################################################ + # Test passed + ############################################################################ + log.info('test_rootdn: PASS\n') + return + + +# Array of test functions +func_tests = [test_acctpolicy, test_attruniq, test_automember, test_dna, + test_linkedattrs, test_memberof, test_mep, test_passthru, + test_referint, test_retrocl, test_rootdn] + + +def check_all_plugins(topo, args="online"): + for func in func_tests: + func(topo, args) + + return diff --git a/src/lib389/lib389/_constants.py b/src/lib389/lib389/_constants.py index f416c3e..f63ea7b 100644 --- a/src/lib389/lib389/_constants.py +++ b/src/lib389/lib389/_constants.py @@ -137,6 +137,8 @@ DN_BACKUP_TASK = "cn=backup,%s" % DN_TASKS DN_RESTORE_TASK = "cn=restore,%s" % DN_TASKS DN_MBO_TASK = "cn=memberOf task,%s" % DN_TASKS DN_TOMB_FIXUP_TASK = "cn=fixup tombstones,%s" % DN_TASKS +DN_FIXUP_LINKED_ATTIBUTES = "cn=fixup linked attributes,%s" % DN_TASKS +DN_AUTOMEMBER_REBUILD_TASK = "cn=automember rebuild membership,%s" % DN_TASKS # Script Constants LDIF2DB = 'ldif2db' diff --git a/src/lib389/lib389/dseldif.py b/src/lib389/lib389/dseldif.py index e6bc57e..69ead6a 100644 --- a/src/lib389/lib389/dseldif.py +++ b/src/lib389/lib389/dseldif.py @@ -19,12 +19,17 @@ class DSEldif(object): def __init__(self, instance): self._instance = instance + self._contents = [] ds_paths = Paths(self._instance.serverid, self._instance) self.path = os.path.join(ds_paths.config_dir, 'dse.ldif') with open(self.path, 'r') as file_dse: - self._contents = file_dse.readlines() + for line in file_dse.readlines(): + if line.startswith('dn'): + self._contents.append(line.lower()) + else: + self._contents.append(line) def _update(self): """Update the dse.ldif with a new contents""" @@ -39,7 +44,7 @@ class DSEldif(object): relative attribute indexes and the attribute value """ - entry_dn_i = self._contents.index("dn: {}\n".format(entry_dn)) + entry_dn_i = self._contents.index("dn: {}\n".format(entry_dn.lower())) attr_data = {} # Find where the entry ends @@ -58,7 +63,7 @@ class DSEldif(object): attr_data.update({entry_slice.index(line): attr_value}) if not attr_data: - raise ValueError("Attribute {} wasn't found under dn: {}".format(attr, entry_dn)) + raise ValueError("Attribute {} wasn't found under dn: {}".format(attr, entry_dn.lower())) return entry_dn_i, attr_data @@ -89,7 +94,7 @@ class DSEldif(object): :type value: str """ - entry_dn_i = self._contents.index("dn: {}\n".format(entry_dn)) + entry_dn_i = self._contents.index("dn: {}\n".format(entry_dn.lower())) self._contents.insert(entry_dn_i+1, "{}: {}\n".format(attr, value)) self._update() diff --git a/src/lib389/lib389/plugins.py b/src/lib389/lib389/plugins.py index 830ac7b..d06f299 100644 --- a/src/lib389/lib389/plugins.py +++ b/src/lib389/lib389/plugins.py @@ -12,7 +12,6 @@ import os.path from lib389 import tasks from lib389._mapped_object import DSLdapObjects, DSLdapObject -from lib389.exceptions import Error from lib389.lint import DSRILE0001 from lib389._constants import DN_PLUGIN from lib389.properties import ( @@ -20,7 +19,16 @@ from lib389.properties import ( PLUGINS_ENABLE_ON_VALUE, PLUGINS_ENABLE_OFF_VALUE, PLUGIN_ENABLE ) + class Plugin(DSLdapObject): + """A single instance of a plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + _plugin_properties = { 'nsslapd-pluginEnabled' : 'off' } @@ -43,20 +51,44 @@ class Plugin(DSLdapObject): self._protected = True def enable(self): + """Set nsslapd-pluginEnabled to on""" + self.set('nsslapd-pluginEnabled', 'on') def disable(self): + """Set nsslapd-pluginEnabled to off""" + self.set('nsslapd-pluginEnabled', 'off') + def restart(self): + """Disable and then enable the plugin""" + + self.disable() + self.enable() + def status(self): + """Check if the plugin is enabled""" + return self.get_attr_val_utf8('nsslapd-pluginEnabled') == 'on' def create(self, rdn=None, properties=None, basedn=None): - # When we create plugins, we don't want people to have to consider all - # the little details. Plus, the server during creation needs to be able - # to create these from nothing. - # As a result, all the named plugins carry a default properties - # dictionary that can be used. + """Create a plugin entry + + When we create plugins, we don't want people to have to consider all + the little details. Plus, the server during creation needs to be able + to create these from nothing. + As a result, all the named plugins carry a default properties + dictionary that can be used. + + :param rdn: RDN of the new entry + :type rdn: str + :param properties: Attributes for the new entry + :type properties: dict + :param basedn: Base DN of the new entry + :type rdn: str + + :returns: Plugin class instance of the created entry + """ # Copy the plugin internal properties. internal_properties = copy.deepcopy(self._plugin_properties) @@ -64,12 +96,30 @@ class Plugin(DSLdapObject): internal_properties.update(properties) return super(Plugin, self).create(rdn, internal_properties, basedn) + class AddnPlugin(Plugin): + """An instance of addn plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + def __init__(self, instance, dn="cn=addn,cn=plugins,cn=config"): super(AddnPlugin, self).__init__(instance, dn) # Need to add wrappers to add domains to this. + class AttributeUniquenessPlugin(Plugin): + """An instance of attribute uniqueness plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + def __init__(self, instance, dn="cn=attribute uniqueness,cn=plugins,cn=config"): super(AttributeUniquenessPlugin, self).__init__(instance, dn) @@ -78,24 +128,45 @@ class AttributeUniquenessPlugin(Plugin): # of the plugin, rather than many configs. def add_unique_attribute(self, attr): + """Add a uniqueness-attribute-name attribute""" + self.add('uniqueness-attribute-name', attr) def remove_unique_attribute(self, attr): + """Remove a uniqueness-attribute-name attribute""" + self.remove('uniqueness-attribute-name', attr) def add_unique_subtree(self, basedn): + """Add a uniqueness-subtree attribute""" + self.add('uniqueness-subtrees', basedn) def remove_unique_subtree(self, basedn): + """Remove a uniqueness-subtree attribute""" + self.remove('uniqueness-subtrees', basedn) def enable_all_subtrees(self): + """Set uniqueness-across-all-subtrees to on""" + self.set('uniqueness-across-all-subtrees', 'on') def disable_all_subtrees(self): + """Set uniqueness-across-all-subtrees to off""" + self.set('uniqueness-across-all-subtrees', 'off') + class LdapSSOTokenPlugin(Plugin): + """An instance of ldapssotoken plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + _plugin_properties = { 'cn' : 'ldapssotoken', 'nsslapd-pluginEnabled' : 'off', @@ -111,14 +182,97 @@ class LdapSSOTokenPlugin(Plugin): def __init__(self, instance, dn="cn=ldapssotoken,cn=plugins,cn=config"): super(LdapSSOTokenPlugin, self).__init__(instance, dn) + class ManagedEntriesPlugin(Plugin): + """An instance of managed entries plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + def __init__(self, instance, dn="cn=managed entries,cn=plugins,cn=config"): super(ManagedEntriesPlugin, self).__init__(instance, dn) - # This will likely need to be a bit like both the DSLdapObjects AND the object. - # Because there are potentially many MEP configs. + +class MEPConfig(DSLdapObject): + """A single instance of MEP config entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + + def __init__(self, instance, dn): + super(MEPConfig, self).__init__(instance, dn) + self._rdn_attribute = 'cn' + self._must_attributes = ['cn'] + self._create_objectclasses = ['top', 'extensibleObject'] + self._protected = False + + +class MEPConfigs(DSLdapObjects): + """A DSLdapObjects entity which represents MEP config entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param basedn: Base DN for all account entries below + :type basedn: str + """ + + def __init__(self, instance, basedn="cn=managed entries,cn=plugins,cn=config"): + super(MEPConfigs, self).__init__(instance) + self._objectclasses = ['top', 'extensibleObject'] + self._filterattrs = ['cn'] + self._childobject = MEPConfig + self._basedn = basedn + + +class MEPTemplate(DSLdapObject): + """A single instance of MEP template entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + + def __init__(self, instance, dn=None): + super(MEPTemplate, self).__init__(instance, dn) + self._rdn_attribute = 'cn' + self._must_attributes = ['cn'] + self._create_objectclasses = ['top', 'extensibleObject', 'mepTemplateEntry'] + self._protected = False + + +class MEPTemplates(DSLdapObjects): + """A DSLdapObjects entity which represents MEP template entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param basedn: Base DN for all account entries below + :type basedn: str + """ + + def __init__(self, instance, basedn): + super(MEPTemplates, self).__init__(instance) + self._objectclasses = ['top', 'extensibleObject'] + self._filterattrs = ['cn'] + self._childobject = MEPTemplate + self._basedn = basedn + class ReferentialIntegrityPlugin(Plugin): + """An instance of referential integrity postoperation plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + _plugin_properties = { 'cn' : 'referential integrity postoperation', 'nsslapd-pluginEnabled': 'off', @@ -146,6 +300,8 @@ class ReferentialIntegrityPlugin(Plugin): self._lint_functions = [self._lint_update_delay] def create(self, rdn=None, properties=None, basedn=None): + """Create an instance of the plugin""" + referint_log = os.path.join(self._instance.ds_paths.log_dir, "referint") if properties is None: properties = {'referint-logfile': referint_log} @@ -160,96 +316,240 @@ class ReferentialIntegrityPlugin(Plugin): return DSRILE0001 def get_update_delay(self): + """Get referint-update-delay attribute""" + return self.get_attr_val_int('referint-update-delay') def get_update_delay_formatted(self): + """Display referint-update-delay attribute""" + return self.display_attr('referint-update-delay') def set_update_delay(self, value): + """Set referint-update-delay attribute""" + self.set('referint-update-delay', str(value)) def get_membership_attr(self, formatted=False): + """Get referint-membership-attr attribute""" + return self.get_attr_vals_utf8('referint-membership-attr') def get_membership_attr_formatted(self): + """Display referint-membership-attr attribute""" + return self.display_attr('referint-membership-attr') def add_membership_attr(self, attr): + """Add referint-membership-attr attribute""" + self.add('referint-membership-attr', attr) def remove_membership_attr(self, attr): + """Remove referint-membership-attr attribute""" + self.remove('referint-membership-attr', attr) def get_entryscope(self, formatted=False): + """Get nsslapd-pluginentryscope attribute""" + return self.get_attr_vals_utf8('nsslapd-pluginentryscope') def get_entryscope_formatted(self): + """Display nsslapd-pluginentryscope attribute""" + return self.display_attr('nsslapd-pluginentryscope') def add_entryscope(self, attr): + """Add nsslapd-pluginentryscope attribute""" + self.add('nsslapd-pluginentryscope', attr) def remove_entryscope(self, attr): + """Remove nsslapd-pluginentryscope attribute""" + self.remove('nsslapd-pluginentryscope', attr) def remove_all_entryscope(self): + """Remove all nsslapd-pluginentryscope attributes""" + self.remove_all('nsslapd-pluginentryscope') def get_excludescope(self): + """Get nsslapd-pluginexcludeentryscope attribute""" + return self.get_attr_vals_ut8('nsslapd-pluginexcludeentryscope') def get_excludescope_formatted(self): + """Display nsslapd-pluginexcludeentryscope attribute""" + return self.display_attr('nsslapd-pluginexcludeentryscope') def add_excludescope(self, attr): + """Add nsslapd-pluginexcludeentryscope attribute""" + self.add('nsslapd-pluginexcludeentryscope', attr) def remove_excludescope(self, attr): + """Remove nsslapd-pluginexcludeentryscope attribute""" + self.remove('nsslapd-pluginexcludeentryscope', attr) def remove_all_excludescope(self): + """Remove all nsslapd-pluginexcludeentryscope attributes""" + self.remove_all('nsslapd-pluginexcludeentryscope') def get_container_scope(self): + """Get nsslapd-plugincontainerscope attribute""" + return self.get_attr_vals_ut8('nsslapd-plugincontainerscope') def get_container_scope_formatted(self): + """Display nsslapd-plugincontainerscope attribute""" + return self.display_attr('nsslapd-plugincontainerscope') def add_container_scope(self, attr): + """Add nsslapd-plugincontainerscope attribute""" + self.add('nsslapd-plugincontainerscope', attr) def remove_container_scope(self, attr): + """Remove nsslapd-plugincontainerscope attribute""" + self.remove('nsslapd-plugincontainerscope', attr) def remove_all_container_scope(self): + """Remove all nsslapd-plugincontainerscope attributes""" + self.remove_all('nsslapd-plugincontainerscope') + def get_configarea(self): + """Get nsslapd-pluginConfigArea attribute""" + + return self.get_attr_val_utf8_l('nsslapd-pluginConfigArea') + + def set_configarea(self, attr): + """Set nsslapd-pluginConfigArea attribute""" + + return self.set('nsslapd-pluginConfigArea', attr) + + def remove_configarea(self): + """Remove all nsslapd-pluginConfigArea attributes""" + + return self.remove_all('nsslapd-pluginConfigArea') + + +class ReferentialIntegrityConfig(DSLdapObject): + """An instance of Referential Integrity config entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + + def __init__(self, instance, dn): + super(ReferentialIntegrityConfig, self).__init__(instance, dn) + self._dn = dn + self._rdn_attribute = 'cn' + self._must_attributes = ['cn', + 'referint-update-delay', + 'referint-logfile', + 'referint-membership-attr'] + self._create_objectclasses = ['top', 'extensibleObject'] + self._protected = False + self._exit_code = None + + class SyntaxValidationPlugin(Plugin): + """An instance of Syntax Validation Task plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + def __init__(self, instance, dn="cn=Syntax Validation Task,cn=plugins,cn=config"): super(SyntaxValidationPlugin, self).__init__(instance, dn) + class SchemaReloadPlugin(Plugin): + """An instance of Schema Reload plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + def __init__(self, instance, dn="cn=Schema Reload,cn=plugins,cn=config"): super(SchemaReloadPlugin, self).__init__(instance, dn) + class StateChangePlugin(Plugin): + """An instance of State Change plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + def __init__(self, instance, dn="cn=State Change Plugin,cn=plugins,cn=config"): super(StateChangePlugin, self).__init__(instance, dn) + class ACLPlugin(Plugin): + """An instance of addn plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + def __init__(self, instance, dn="cn=ACL Plugin,cn=plugins,cn=config"): super(ACLPlugin, self).__init__(instance, dn) + class ACLPreoperationPlugin(Plugin): + """An instance of ACL preoperation plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + def __init__(self, instance, dn="cn=ACL preoperation,cn=plugins,cn=config"): super(ACLPreoperationPlugin, self).__init__(instance, dn) + class RolesPlugin(Plugin): + """An instance of Roles plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + def __init__(self, instance, dn="cn=Roles Plugin,cn=plugins,cn=config"): super(RolesPlugin, self).__init__(instance, dn) + class MemberOfPlugin(Plugin): + """An instance of MemberOf plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + _plugin_properties = { 'cn' : 'MemberOf Plugin', 'nsslapd-pluginEnabled' : 'off', @@ -271,93 +571,181 @@ class MemberOfPlugin(Plugin): self._must_attributes.extend(['memberOfGroupAttr', 'memberOfAttr']) def get_attr(self): + """Get memberofattr attribute""" + return self.get_attr_val_utf8_l('memberofattr') def get_attr_formatted(self): + """Display memberofattr attribute""" + return self.display_attr('memberofattr') def set_attr(self, attr): + """Set memberofattr attribute""" + self.set('memberofattr', attr) def get_groupattr(self): + """Get memberofgroupattr attribute""" + return self.get_attr_vals_utf8_l('memberofgroupattr') def get_groupattr_formatted(self): + """Display memberofgroupattr attribute""" + return self.display_attr('memberofgroupattr') def add_groupattr(self, attr): + """Add memberofgroupattr attribute""" + self.add('memberofgroupattr', attr) + def replace_groupattr(self, attr): + """Replace memberofgroupattr attribute""" + + self.replace('memberofgroupattr', attr) + def remove_groupattr(self, attr): + """Remove memberofgroupattr attribute""" + self.remove('memberofgroupattr', attr) def get_allbackends(self): + """Get memberofallbackends attribute""" + return self.get_attr_val_utf8_l('memberofallbackends') def get_allbackends_formatted(self): + """Display memberofallbackends attribute""" + return self.display_attr('memberofallbackends') def enable_allbackends(self): + """Set memberofallbackends to on""" + self.set('memberofallbackends', 'on') def disable_allbackends(self): + """Set memberofallbackends to off""" + self.set('memberofallbackends', 'off') def get_skipnested(self): + """Get memberofskipnested attribute""" + return self.get_attr_val_utf8_l('memberofskipnested') def get_skipnested_formatted(self): + """Display memberofskipnested attribute""" + return self.display_attr('memberofskipnested') def enable_skipnested(self): + """Set memberofskipnested to on""" + self.set('memberofskipnested', 'on') def disable_skipnested(self): + """Set memberofskipnested to off""" + self.set('memberofskipnested', 'off') def get_autoaddoc(self): + """Get memberofautoaddoc attribute""" + return self.get_attr_val_utf8_l('memberofautoaddoc') def get_autoaddoc_formatted(self): + """Display memberofautoaddoc attribute""" + return self.display_attr('memberofautoaddoc') def set_autoaddoc(self, object_class): + """Set memberofautoaddoc attribute""" + self.set('memberofautoaddoc', object_class) def remove_autoaddoc(self): + """Remove all memberofautoaddoc attributes""" + self.remove_all('memberofautoaddoc') def get_entryscope(self, formatted=False): + """Get memberofentryscope attributes""" + return self.get_attr_vals_utf8_l('memberofentryscope') def get_entryscope_formatted(self): + """Display memberofentryscope attributes""" + return self.display_attr('memberofentryscope') def add_entryscope(self, attr): + """Add memberofentryscope attribute""" + self.add('memberofentryscope', attr) def remove_entryscope(self, attr): + """Remove memberofentryscope attribute""" + self.remove('memberofentryscope', attr) def remove_all_entryscope(self): + """Remove all memberofentryscope attributes""" + self.remove_all('memberofentryscope') def get_excludescope(self): + """Get memberofentryscopeexcludesubtree attributes""" + return self.get_attr_vals_utf8_l('memberofentryscopeexcludesubtree') def get_excludescope_formatted(self): + """Display memberofentryscopeexcludesubtree attributes""" + return self.display_attr('memberofentryscopeexcludesubtree') def add_excludescope(self, attr): + """Add memberofentryscopeexcludesubtree attribute""" + self.add('memberofentryscopeexcludesubtree', attr) def remove_excludescope(self, attr): + """Remove memberofentryscopeexcludesubtree attribute""" + self.remove('memberofentryscopeexcludesubtree', attr) def remove_all_excludescope(self): + """Remove all memberofentryscopeexcludesubtree attributes""" + self.remove_all('memberofentryscopeexcludesubtree') + def get_configarea(self): + """Get nsslapd-pluginConfigArea attribute""" + + return self.get_attr_val_utf8_l('nsslapd-pluginConfigArea') + + def set_configarea(self, attr): + """Set nsslapd-pluginConfigArea attribute""" + + return self.set('nsslapd-pluginConfigArea', attr) + + def remove_configarea(self): + """Remove nsslapd-pluginConfigArea attribute""" + + return self.remove_all('nsslapd-pluginConfigArea') + def fixup(self, basedn, _filter=None): + """Create a memberOf task + + :param basedn: Basedn to fix up + :type basedn: str + :param _filter: a filter for entries to fix up + :type _filter: str + + :returns: an instance of Task(DSLdapObject) + """ + task = tasks.MemberOfFixupTask(self._instance) task_properties = {'basedn': basedn} if _filter is not None: @@ -366,27 +754,100 @@ class MemberOfPlugin(Plugin): return task + +class MemberOfSharedConfig(DSLdapObject): + """An instance of MemberOf config entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + + def __init__(self, instance, dn): + super(MemberOfSharedConfig, self).__init__(instance, dn) + self._dn = dn + self._rdn_attribute = 'cn' + self._must_attributes = ['cn', 'memberOfGroupAttr', 'memberOfAttr'] + self._create_objectclasses = ['top', 'extensibleObject'] + self._protected = False + self._exit_code = None + + class RetroChangelogPlugin(Plugin): + """An instance of Retro Changelog plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + def __init__(self, instance, dn="cn=Retro Changelog Plugin,cn=plugins,cn=config"): super(RetroChangelogPlugin, self).__init__(instance, dn) + class ClassOfServicePlugin(Plugin): + """An instance of Class of Service plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + def __init__(self, instance, dn="cn=Class of Service,cn=plugins,cn=config"): super(ClassOfServicePlugin, self).__init__(instance, dn) + class ViewsPlugin(Plugin): + """An instance of Views plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + def __init__(self, instance, dn="cn=Views,cn=plugins,cn=config"): super(ViewsPlugin, self).__init__(instance, dn) + class SevenBitCheckPlugin(Plugin): + """An instance of addn plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + def __init__(self, instance, dn="cn=7-bit check,cn=plugins,cn=config"): super(SevenBitCheckPlugin, self).__init__(instance, dn) + class AccountUsabilityPlugin(Plugin): + """An instance of Account Usability plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + def __init__(self, instance, dn="cn=Account Usability Plugin,cn=plugins,cn=config"): super(AccountUsabilityPlugin, self).__init__(instance, dn) + class AutoMembershipPlugin(Plugin): + """An instance of Auto Membership plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + _plugin_properties = { 'cn' : 'Auto Membership Plugin', 'nsslapd-pluginEnabled' : 'off', @@ -403,119 +864,227 @@ class AutoMembershipPlugin(Plugin): def __init__(self, instance, dn="cn=Auto Membership Plugin,cn=plugins,cn=config"): super(AutoMembershipPlugin, self).__init__(instance, dn) + def fixup(self, basedn, _filter=None): + """Create an automember rebuild membership task + + :param basedn: Basedn to fix up + :type basedn: str + :param _filter: a filter for entries to fix up + :type _filter: str + + :returns: an instance of Task(DSLdapObject) + """ + + task = tasks.AutomemberRebuildMembershipTask(self._instance) + task_properties = {'basedn': basedn} + if _filter is not None: + task_properties['filter'] = _filter + task.create(properties=task_properties) + + return task + + class AutoMembershipDefinition(DSLdapObject): - """A single instance of Auto Membership Plugin entry + """A single instance of Auto Membership Plugin config entry - :param instance: An instance - :type instance: lib389.DirSrv - :param dn: Entry DN - :type dn: str + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str """ def __init__(self, instance, dn=None): super(AutoMembershipDefinition, self).__init__(instance, dn) self._rdn_attribute = 'cn' - self._must_attributes = ['cn'] + self._must_attributes = ['cn', 'autoMemberScope', 'autoMemberFilter', 'autoMemberGroupingAttr'] self._create_objectclasses = ['top', 'AutoMemberDefinition'] self._protected = False def get_groupattr(self): - """Get grouping attributes + """Get autoMemberGroupingAttr attributes""" - :returns: autoMemberGroupingAttr values - """ return self.get_attr_vals_utf8('autoMemberGroupingAttr') def set_groupattr(self, attr): - """Set grouping attributes + """Set autoMemberGroupingAttr attribute""" - :param attr: autoMemberGroupingAttr value - :type attr: str - """ self.set('autoMemberGroupingAttr', attr) def get_defaultgroup(self, attr): - """Get default group + """Get autoMemberDefaultGroup attributes""" - :returns: autoMemberDefaultGroup value - """ return self.get_attr_vals_utf8('autoMemberDefaultGroup') def set_defaultgroup(self, attr): - """Set default group + """Set autoMemberDefaultGroup attribute""" - :param attr: autoMemberDefaultGroup value - :type attr: str - """ self.set('autoMemberDefaultGroup', attr) def get_scope(self, attr): - """Get scope + """Get autoMemberScope attributes""" - :returns: autoMemberScope value - """ return self.get_attr_vals_utf8('autoMemberScope') def set_scope(self, attr): - """Set scope + """Set autoMemberScope attribute""" - :param attr: autoMemberScope value - :type attr: str - """ self.set('autoMemberScope', attr) def get_filter(self, attr): - """Get filter + """Get autoMemberFilter attributes""" - :returns: autoMemberFilter value - """ return self.get_attr_vals_utf8('autoMemberFilter') def set_filter(self, attr): - """Set filter + """Set autoMemberFilter attributes""" - :param attr: autoMemberFilter value - :type attr: str - """ self.set('autoMemberFilter', attr) + class AutoMembershipDefinitions(DSLdapObjects): - """DSLdapObjects that represents Auto Membership Plugin entry + """A DSLdapObjects entity which represents Auto Membership Plugin config entry - :param instance: An instance - :type instance: lib389.DirSrv - :param basedn: Base DN for all account entries below - :type basedn: str + :param instance: An instance + :type instance: lib389.DirSrv + :param basedn: Base DN for all account entries below + :type basedn: str """ def __init__(self, instance, basedn="cn=Auto Membership Plugin,cn=plugins,cn=config"): super(AutoMembershipDefinitions, self).__init__(instance) - self._objectclasses = ['top','autoMemberDefinition'] + self._objectclasses = ['top', 'autoMemberDefinition'] self._filterattrs = ['cn'] self._childobject = AutoMembershipDefinition self._basedn = basedn + class ContentSynchronizationPlugin(Plugin): + """A single instance of Content Synchronization plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + def __init__(self, instance, dn="cn=Content Synchronization,cn=plugins,cn=config"): super(ContentSynchronizationPlugin, self).__init__(instance, dn) + class DereferencePlugin(Plugin): + """A single instance of deref plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + def __init__(self, instance, dn="cn=deref,cn=plugins,cn=config"): super(DereferencePlugin, self).__init__(instance, dn) + class HTTPClientPlugin(Plugin): + """A single instance of HTTP Client plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + def __init__(self, instance, dn="cn=HTTP Client,cn=plugins,cn=config"): super(HTTPClientPlugin, self).__init__(instance, dn) + class LinkedAttributesPlugin(Plugin): + """A single instance of Linked Attributes plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + def __init__(self, instance, dn="cn=Linked Attributes,cn=plugins,cn=config"): super(LinkedAttributesPlugin, self).__init__(instance, dn) + def fixup(self, basedn, _filter=None): + """Create a fixup linked attributes task + + :param basedn: Basedn to fix up + :type basedn: str + :param _filter: a filter for entries to fix up + :type _filter: str + + :returns: an instance of Task(DSLdapObject) + """ + + task = tasks.FixupLinkedAttributesTask(self._instance) + task_properties = {'basedn': basedn} + if _filter is not None: + task_properties['filter'] = _filter + task.create(properties=task_properties) + + return task + + +class LinkedAttributesConfig(DSLdapObject): + """A single instance of Linked Attributes Plugin config entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + + def __init__(self, instance, dn=None): + super(LinkedAttributesConfig, self).__init__(instance, dn) + self._rdn_attribute = 'cn' + self._must_attributes = ['cn'] + self._create_objectclasses = ['top', 'extensibleObject'] + self._protected = False + + +class LinkedAttributesConfigs(DSLdapObjects): + """A DSLdapObjects entity which represents Linked Attributes Plugin config entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param basedn: Base DN for all account entries below + :type basedn: str + """ + + def __init__(self, instance, basedn="cn=Linked Attributes,cn=plugins,cn=config"): + super(LinkedAttributesConfigs, self).__init__(instance) + self._objectclasses = ['top', 'extensibleObject'] + self._filterattrs = ['cn'] + self._childobject = LinkedAttributesConfig + self._basedn = basedn + + class PassThroughAuthenticationPlugin(Plugin): + """A single instance of Pass Through Authentication plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + def __init__(self, instance, dn="cn=Pass Through Authentication,cn=plugins,cn=config"): super(PassThroughAuthenticationPlugin, self).__init__(instance, dn) + class USNPlugin(Plugin): + """A single instance of USN (Update Sequence Number) plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + _plugin_properties = { 'cn' : 'USN', 'nsslapd-pluginEnabled': 'off', @@ -535,16 +1104,31 @@ class USNPlugin(Plugin): self._create_objectclasses.extend(['extensibleObject']) def is_global_mode_set(self): - """Return True if global mode is enabled, else False.""" + """Return True if nsslapd-entryusn-global is set to on, else False""" + return self._instance.config.get_attr_val_utf8('nsslapd-entryusn-global') == 'on' def enable_global_mode(self): + """Set nsslapd-entryusn-global to on""" + self._instance.config.set('nsslapd-entryusn-global', 'on') def disable_global_mode(self): + """Set nsslapd-entryusn-global to off""" + self._instance.config.set('nsslapd-entryusn-global', 'off') def cleanup(self, suffix=None, backend=None, max_usn=None): + """Create a USN tombstone cleanup task + + :param basedn: Basedn to fix up + :type basedn: str + :param _filter: a filter for entries to fix up + :type _filter: str + + :returns: an instance of Task(DSLdapObject) + """ + task = tasks.USNTombstoneCleanupTask(self._instance) task_properties = {} @@ -559,7 +1143,16 @@ class USNPlugin(Plugin): return task + class WhoamiPlugin(Plugin): + """A single instance of whoami plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + _plugin_properties = { 'cn' : 'whoami', 'nsslapd-pluginEnabled' : 'on', @@ -576,7 +1169,16 @@ class WhoamiPlugin(Plugin): def __init__(self, instance, dn="cn=whoami,cn=plugins,cn=config"): super(WhoamiPlugin, self).__init__(instance, dn) + class RootDNAccessControlPlugin(Plugin): + """A single instance of RootDN Access Control plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + _plugin_properties = { 'cn' : 'RootDN Access Control', 'nsslapd-pluginEnabled' : 'off', @@ -595,42 +1197,68 @@ class RootDNAccessControlPlugin(Plugin): self._create_objectclasses.extend(['rootDNPluginConfig']) def get_open_time(self): + """Get rootdn-open-time attribute""" + return self.get_attr_val_utf8('rootdn-open-time') def get_open_time_formatted(self): + """Display rootdn-open-time attribute""" + return self.display_attr('rootdn-open-time') def set_open_time(self, attr): + """Set rootdn-open-time attribute""" + self.set('rootdn-open-time', attr) def remove_open_time(self): + """Remove all rootdn-open-time attributes""" + self.remove_all('rootdn-open-time') def get_close_time(self): + """Get rootdn-close-time attribute""" + return self.get_attr_val_utf8('rootdn-close-time') def get_close_time_formatted(self): + """Display rootdn-close-time attribute""" + return self.display_attr('rootdn-close-time') def set_close_time(self, attr): + """Set rootdn-close-time attribute""" + self.set('rootdn-close-time', attr) def remove_close_time(self): + """Remove all rootdn-close-time attributes""" + self.remove_all('rootdn-close-time') def get_days_allowed(self): + """Get rootdn-days-allowed attribute""" + return self.get_attr_val_utf8('rootdn-days-allowed') def get_days_allowed_formatted(self): + """Display rootdn-days-allowed attribute""" + return self.display_attr('rootdn-days-allowed') def set_days_allowed(self, attr): + """Set rootdn-days-allowed attribute""" + self.set('rootdn-days-allowed', attr) def remove_days_allowed(self): + """Remove all rootdn-days-allowed attributes""" + self.remove_all('rootdn-days-allowed') def add_allow_day(self, day): + """Add a value to rootdn-days-allowed attribute""" + days = self.get_days_allowed() if days is None: days = "" @@ -641,6 +1269,8 @@ class RootDNAccessControlPlugin(Plugin): self.remove_days_allowed() def remove_allow_day(self, day): + """Remove a value from rootdn-days-allowed attribute""" + days = self.get_days_allowed() if days is None: days = "" @@ -651,69 +1281,108 @@ class RootDNAccessControlPlugin(Plugin): self.remove_days_allowed() def get_allow_host(self): + """Get rootdn-allow-host attribute""" + return self.get_attr_val_utf8('rootdn-allow-host') def get_allow_host_formatted(self): + """Display rootdn-allow-host attribute""" + return self.display_attr('rootdn-allow-host') def add_allow_host(self, attr): + """Add rootdn-allow-host attribute""" + self.add('rootdn-allow-host', attr) def remove_allow_host(self, attr): + """Remove rootdn-allow-host attribute""" + self.remove('rootdn-allow-host', attr) def remove_all_allow_host(self): + """Remove all rootdn-allow-host attributes""" + self.remove_all('rootdn-allow-host') def get_deny_host(self): + """Get rootdn-deny-host attribute""" + return self.get_attr_val_utf8('rootdn-deny-host') def get_deny_host_formatted(self): + """Display rootdn-deny-host attribute""" + return self.display_attr('rootdn-deny-host') def add_deny_host(self, attr): + """Add rootdn-deny-host attribute""" + self.add('rootdn-deny-host', attr) def remove_deny_host(self, attr): + """Remove rootdn-deny-host attribute""" + self.remove('rootdn-deny-host', attr) def remove_all_deny_host(self): + """Remove all rootdn-deny-host attribute""" + self.remove_all('rootdn-deny-host') def get_allow_ip(self): + """Get rootdn-allow-ip attribute""" + return self.get_attr_val_utf8('rootdn-allow-ip') def get_allow_ip_formatted(self): + """Display rootdn-allow-ip attribute""" + return self.display_attr('rootdn-allow-ip') def add_allow_ip(self, attr): + """Add rootdn-allow-ip attribute""" + self.add('rootdn-allow-ip', attr) def remove_allow_ip(self, attr): + """Remove rootdn-allow-ip attribute""" + self.remove('rootdn-allow-ip', attr) def remove_all_allow_ip(self): + """Remove all rootdn-allow-ip attribute""" + self.remove_all('rootdn-allow-ip') def get_deny_ip(self): + """Remove all rootdn-deny-ip attribute""" + return self.get_attr_val_utf8('rootdn-deny-ip') def get_deny_ip_formatted(self): + """Display rootdn-deny-ip attribute""" + return self.display_attr('rootdn-deny-ip') def add_deny_ip(self, attr): + """Add rootdn-deny-ip attribute""" + self.add('rootdn-deny-ip', attr) def remove_deny_ip(self, attr): + """Remove rootdn-deny-ip attribute""" + self.remove('rootdn-deny-ip', attr) def remove_all_deny_ip(self): + """Remove all rootdn-deny-ip attribute""" + self.remove_all('rootdn-deny-ip') @staticmethod def add_day_to_days(string_of_days, day): - """ - Append a day in a string of comma seperated days and return the string. + """Append a day in a string of comma separated days and return the string. If day already exists in the string, return processed string. Keyword arguments: @@ -723,6 +1392,7 @@ class RootDNAccessControlPlugin(Plugin): Tue, Wed, Thu day -- a day, e.g. Mon, Tue, etc. """ + days = [i.strip() for i in string_of_days.split(',') if i] if not day in days: @@ -732,8 +1402,7 @@ class RootDNAccessControlPlugin(Plugin): @staticmethod def remove_day_from_days(string_of_days, day): - """ - Remove a day from a string of comma seperated days and return the string. + """Remove a day from a string of comma separated days and return the string. If day does not exists in the string, return processed string. Keyword arguments: @@ -743,6 +1412,7 @@ class RootDNAccessControlPlugin(Plugin): Tue, Wed, Thu day -- a day, e.g. Mon, Tue, etc. """ + days = [i.strip() for i in string_of_days.split(',') if i] if day in days: @@ -752,18 +1422,133 @@ class RootDNAccessControlPlugin(Plugin): class LDBMBackendPlugin(Plugin): + """A single instance of ldbm database plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + def __init__(self, instance, dn="cn=ldbm database,cn=plugins,cn=config"): super(LDBMBackendPlugin, self).__init__(instance, dn) + class ChainingBackendPlugin(Plugin): + """A single instance of chaining database plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + def __init__(self, instance, dn="cn=chaining database,cn=plugins,cn=config"): super(ChainingBackendPlugin, self).__init__(instance, dn) + class AccountPolicyPlugin(Plugin): + """A single instance of Account Policy plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + def __init__(self, instance, dn="cn=Account Policy Plugin,cn=plugins,cn=config"): super(AccountPolicyPlugin, self).__init__(instance, dn) + +class AccountPolicyConfig(DSLdapObject): + """A single instance of Account Policy Plugin config entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + + def __init__(self, instance, dn=None): + super(AccountPolicyConfig, self).__init__(instance, dn) + self._rdn_attribute = 'cn' + self._must_attributes = ['cn'] + self._create_objectclasses = ['top', 'extensibleObject'] + self._protected = False + + +class AccountPolicyConfigs(DSLdapObjects): + """A DSLdapObjects entity which represents Account Policy Plugin config entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param basedn: Base DN for all account entries below + :type basedn: str + """ + + def __init__(self, instance, basedn="cn=Account Policy Plugin,cn=plugins,cn=config"): + super(AccountPolicyConfigs, self).__init__(instance) + self._objectclasses = ['top', 'extensibleObject'] + self._filterattrs = ['cn'] + self._childobject = AccountPolicyConfig + self._basedn = basedn + + +class DNAPlugin(Plugin): + """A single instance of Distributed Numeric Assignment plugin entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + + def __init__(self, instance, dn="cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config"): + super(DNAPlugin, self).__init__(instance, dn) + + +class DNAPluginConfig(DSLdapObject): + """A single instance of DNA Plugin config entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + + def __init__(self, instance, dn=None): + super(DNAPluginConfig, self).__init__(instance, dn) + self._rdn_attribute = 'cn' + self._must_attributes = ['cn'] + self._create_objectclasses = ['top', 'dnaPluginConfig'] + self._protected = False + + +class DNAPluginConfigs(DSLdapObjects): + """A DSLdapObjects entity which represents DNA Plugin config entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param basedn: Base DN for all account entries below + :type basedn: str + """ + + def __init__(self, instance, basedn="cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config"): + super(DNAPluginConfigs, self).__init__(instance) + self._objectclasses = ['top', 'dnaPluginConfig'] + self._filterattrs = ['cn'] + self._childobject = DNAPluginConfig + self._basedn = basedn + + class Plugins(DSLdapObjects): + """A DSLdapObjects entity which represents MEP config entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param basedn: Base DN for all account entries below + :type basedn: str + """ # This is a map of plugin to type, so when we # do a get / list / create etc, we can map to the correct @@ -793,10 +1578,10 @@ class Plugins(DSLdapObjects): else: return super(Plugins, self)._entry_to_instance(dn) - - # To maintain compat with pluginslegacy, here are some helpers. - + # To maintain compatibility with plugin's legacy, here are some helpers. def enable(self, name=None, plugin_dn=None): + """Set nsslapd-pluginEnabled to on""" + if plugin_dn is not None: raise ValueError('You should swap to the new Plugin API!') if name is None: @@ -805,6 +1590,8 @@ class Plugins(DSLdapObjects): plugin.enable() def disable(self, name=None, plugin_dn=None): + """Set nsslapd-pluginEnabled to off""" + if plugin_dn is not None: raise ValueError('You should swap to the new Plugin API!') if name is None: diff --git a/src/lib389/lib389/tasks.py b/src/lib389/lib389/tasks.py index 7fbf50a..66a2c57 100644 --- a/src/lib389/lib389/tasks.py +++ b/src/lib389/lib389/tasks.py @@ -15,11 +15,7 @@ from datetime import datetime from lib389 import Entry from lib389._mapped_object import DSLdapObject from lib389.exceptions import Error -from lib389._constants import ( - DEFAULT_SUFFIX, DEFAULT_BENAME, DN_EXPORT_TASK, DN_BACKUP_TASK, - DN_IMPORT_TASK, DN_RESTORE_TASK, DN_INDEX_TASK, DN_MBO_TASK, - DN_TOMB_FIXUP_TASK, DN_TASKS, DIRSRV_STATE_ONLINE - ) +from lib389._constants import * from lib389.properties import ( TASK_WAIT, EXPORT_REPL_INFO, MT_PROPNAME_TO_ATTRNAME, MT_SUFFIX, TASK_TOMB_STRIP @@ -27,6 +23,14 @@ from lib389.properties import ( class Task(DSLdapObject): + """A single instance of a task entry + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + def __init__(self, instance, dn=None): super(Task, self).__init__(instance, dn) self._rdn_attribute = 'cn' @@ -37,6 +41,7 @@ class Task(DSLdapObject): def is_complete(self): """Return True if task is complete, else False.""" + self._exit_code = self.get_attr_val("nsTaskExitCode") if not self.exists() or self._exit_code is not None: return True @@ -44,6 +49,7 @@ class Task(DSLdapObject): def get_exit_code(self): """Return task's exit code if task is complete, else None.""" + if self.is_complete(): try: return int(self._exit_code) @@ -53,6 +59,7 @@ class Task(DSLdapObject): def wait(self, timeout=120): """Wait until task is complete.""" + count = 0 while count < timeout: if self.is_complete(): @@ -61,16 +68,65 @@ class Task(DSLdapObject): time.sleep(2) def create(self, rdn=None, properties={}, basedn=None): + """Create a Task entry + + :param rdn: RDN of the new entry + :type rdn: str + :param properties: Attributes for the new entry + :type properties: dict + :param basedn: Base DN of the new entry + :type rdn: str + + :returns: DSLdapObject of the created entry + """ + properties['cn'] = self.cn return super(Task, self).create(rdn, properties, basedn) @staticmethod def _get_task_date(): """Return a timestamp to use in naming new task entries.""" + return datetime.now().isoformat() +class AutomemberRebuildMembershipTask(Task): + """A single instance of automember rebuild membership task entry + + :param instance: An instance + :type instance: lib389.DirSrv + """ + + def __init__(self, instance, dn=None): + self.cn = 'automember_rebuild_' + Task._get_task_date() + dn = "cn=" + self.cn + "," + DN_AUTOMEMBER_REBUILD_TASK + + super(AutomemberRebuildMembershipTask, self).__init__(instance, dn) + self._must_attributes.extend(['basedn', 'filter']) + + +class FixupLinkedAttributesTask(Task): + """A single instance of fixup linked attributes task entry + + :param instance: An instance + :type instance: lib389.DirSrv + """ + + def __init__(self, instance, dn=None): + self.cn = 'fixup_linked_attrs_' + Task._get_task_date() + dn = "cn=" + self.cn + "," + DN_FIXUP_LINKED_ATTIBUTES + + super(FixupLinkedAttributesTask, self).__init__(instance, dn) + self._must_attributes.extend(['basedn']) + + class MemberOfFixupTask(Task): + """A single instance of memberOf task entry + + :param instance: An instance + :type instance: lib389.DirSrv + """ + def __init__(self, instance, dn=None): self.cn = 'memberOf_fixup_' + Task._get_task_date() dn = "cn=" + self.cn + "," + DN_MBO_TASK @@ -80,6 +136,12 @@ class MemberOfFixupTask(Task): class USNTombstoneCleanupTask(Task): + """A single instance of USN tombstone cleanup task entry + + :param instance: An instance + :type instance: lib389.DirSrv + """ + def __init__(self, instance, dn=None): self.cn = 'usn_cleanup_' + Task._get_task_date() dn = "cn=" + self.cn + ",cn=USN tombstone cleanup task," + DN_TASKS @@ -92,13 +154,21 @@ class USNTombstoneCleanupTask(Task): return super(USNTombstoneCleanupTask, self)._validate(rdn, properties, basedn) + class SchemaReloadTask(Task): + """A single instance of schema reload task entry + + :param instance: An instance + :type instance: lib389.DirSrv + """ + def __init__(self, instance, dn=None): self.cn = 'schema_reload_' + Task._get_task_date() dn = "cn=" + self.cn + ",cn=schema reload task," + DN_TASKS super(SchemaReloadTask, self).__init__(instance, dn) + class AbortCleanAllRUVTask(Task): """Abort the Clean All Ruv task on all masters. You should call this from "CleanAllRUVTask.abort()" instead to provide @@ -107,12 +177,14 @@ class AbortCleanAllRUVTask(Task): :param instance: The instance :type instance: lib389.DirSrv """ + def __init__(self, instance, dn=None): self.cn = 'abortcleanallruv_' + Task._get_task_date() dn = "cn=" + self.cn + ",cn=abort cleanallruv," + DN_TASKS super(AbortCleanAllRUVTask, self).__init__(instance, dn) + class CleanAllRUVTask(Task): """Create the clean all ruv task. This will be replicated through a topology to remove non-present ruvs. Note that if a ruv is NOT @@ -122,6 +194,7 @@ class CleanAllRUVTask(Task): :param instance: The instance :type instance: lib389.DirSrv """ + def __init__(self, instance, dn=None): self.cn = 'cleanallruv_' + Task._get_task_date() dn = "cn=" + self.cn + ",cn=cleanallruv," + DN_TASKS @@ -139,6 +212,7 @@ class CleanAllRUVTask(Task): :param basedn: Basedn to create the entry. Do not change this. :type basedn: str """ + # Stash the props for abort self._properties = properties return super(CleanAllRUVTask, self).create(rdn, properties, basedn) @@ -150,6 +224,7 @@ class CleanAllRUVTask(Task): :type certify: bool :returns: AbortCleanAllRUVTask """ + if certify is True: self._properties['replica-certify-all'] = 'yes' else: @@ -159,6 +234,7 @@ class CleanAllRUVTask(Task): abort_task.create(properties=self._properties) return abort_task + class Tasks(object): proxied_methods = 'search_s getEntry'.split() diff --git a/src/lib389/lib389/topologies.py b/src/lib389/lib389/topologies.py index a41df3f..372d26e 100644 --- a/src/lib389/lib389/topologies.py +++ b/src/lib389/lib389/topologies.py @@ -196,6 +196,9 @@ class TopologyMain(object): def __iter__(self): return self.all_insts.values().__iter__() + def __getitem__(self, index): + return list(self.all_insts.values())[index] + def pause_all_replicas(self): """Pause all agreements in the class instance"""