PR#49626 Merged Issue 49581 - Fix dynamic plugins test suite

Proposed 2 months ago by spichugi
Modified 9 days ago
From forks/spichugi/389-ds-base dynamic_plugins  into 389-ds-base master

Description: Refactor plugins module and fix tests accordingly.
Add absent Task and Plugin objects. Add docstrings for every
fixed object.

https://pagure.io/389-ds-base/issue/49581

Reviewed by: ?

@@ -16,78 +16,83 @@ 

  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 @@

          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 @@

          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 @@

          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 @@

              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 @@

              # 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__':
The removed file is too large to be shown here, see it at: dirsrvtests/tests/suites/dynamic_plugins/plugin_tests.py

@@ -18,6 +18,9 @@ 

  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 @@

  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 @@

          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 @@

  

      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 @@

          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 @@

              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'])
The added file is too large to be shown here, see it at: dirsrvtests/tests/suites/plugins/acceptance_test.py
file changed

@@ -137,6 +137,8 @@ 

  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'
file changed

@@ -19,12 +19,17 @@ 

  

      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 @@

          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 @@

                  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 @@

          :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()

  
file changed

@@ -12,7 +12,6 @@ 

  

  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 @@

          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 @@

          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 @@

              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 @@

      # 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 @@

      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 @@

          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 @@

                  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 @@

          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 @@

  

          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 @@

      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

+     """