Browse Source

add testcase for ticket 48759

Fixes for ticket 47859 reviewed by Noriko, thanks
Ludwig Krispenz 9 years ago
parent
commit
5d975ea1a1
1 changed files with 285 additions and 0 deletions
  1. 285 0
      dirsrvtests/tests/tickets/ticket48759_test.py

+ 285 - 0
dirsrvtests/tests/tickets/ticket48759_test.py

@@ -0,0 +1,285 @@
+# --- BEGIN COPYRIGHT BLOCK ---
+# Copyright (C) 2015 Red Hat, Inc.
+# All rights reserved.
+#
+# License: GPL (version 3 or any later version).
+# See LICENSE for details.
+# --- END COPYRIGHT BLOCK ---
+#
+import os
+import sys
+import time
+import ldap
+import logging
+import pytest
+from lib389 import DirSrv, Entry, tools, tasks
+from lib389.tools import DirSrvTools
+from lib389._constants import *
+from lib389.properties import *
+from lib389.tasks import *
+
+log = logging.getLogger(__name__)
+
+installation_prefix = None
+
+MEMBEROF_PLUGIN_DN = ('cn=' + PLUGIN_MEMBER_OF + ',cn=plugins,cn=config')
+GROUP_DN = ("cn=group," + DEFAULT_SUFFIX)
+MEMBER_DN_COMP = "uid=member"
+
+class TopologyStandalone(object):
+    def __init__(self, standalone):
+        standalone.open()
+        self.standalone = standalone
+
+
[email protected](scope="module")
+def topology(request):
+    '''
+        This fixture is used to standalone topology for the 'module'.
+    '''
+    global installation_prefix
+
+    if installation_prefix:
+        args_instance[SER_DEPLOYED_DIR] = installation_prefix
+
+    standalone = DirSrv(verbose=False)
+
+    # Args for the standalone instance
+    args_instance[SER_HOST] = HOST_STANDALONE
+    args_instance[SER_PORT] = PORT_STANDALONE
+    args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE
+    args_standalone = args_instance.copy()
+    standalone.allocate(args_standalone)
+
+    # Get the status of the instance and restart it if it exists
+    instance_standalone = standalone.exists()
+
+    # Remove the instance
+    if instance_standalone:
+        standalone.delete()
+
+    # Create the instance
+    standalone.create()
+
+    # Used to retrieve configuration information (dbdir, confdir...)
+    standalone.open()
+
+    # clear the tmp directory
+    standalone.clearTmpDir(__file__)
+
+    # Here we have standalone instance up and running
+    return TopologyStandalone(standalone)
+
+def _add_group_with_members(topology):
+    # Create group
+    try:
+        topology.standalone.add_s(Entry((GROUP_DN,
+                                         {'objectclass': 'top groupofnames'.split(),
+                                          'cn': 'group'})))
+    except ldap.LDAPError as e:
+        log.fatal('Failed to add group: error ' + e.message['desc'])
+        assert False
+
+    # Add members to the group - set timeout
+    log.info('Adding members to the group...')
+    for idx in range(1, 5):
+        try:
+            MEMBER_VAL = ("uid=member%d,%s" % (idx, DEFAULT_SUFFIX))
+            topology.standalone.modify_s(GROUP_DN,
+                                         [(ldap.MOD_ADD,
+                                           'member',
+                                           MEMBER_VAL)])
+        except ldap.LDAPError as e:
+            log.fatal('Failed to update group: member (%s) - error: %s' %
+                      (MEMBER_VAL, e.message['desc']))
+            assert False
+
+def _find_retrocl_changes(topology, user_dn=None):
+    ents = topology.standalone.search_s('cn=changelog', ldap.SCOPE_SUBTREE, '(targetDn=%s)' %user_dn)
+    return len(ents)
+
+def _find_memberof(topology, user_dn=None, group_dn=None, find_result=True):
+    ent = topology.standalone.getEntry(user_dn, ldap.SCOPE_BASE, "(objectclass=*)", ['memberof'])
+    found = False
+    if ent.hasAttr('memberof'):
+
+        for val in ent.getValues('memberof'):
+            topology.standalone.log.info("!!!!!!! %s: memberof->%s" % (user_dn, val))
+            if val == group_dn:
+                found = True
+                break
+            
+    if find_result:
+        assert(found) 
+    else:
+        assert(not found)
+
+def test_ticket48759(topology):
+    """
+    The fix for ticket 48759 has to prevent plugin calls for tombstone purging
+
+    The test uses the memberof and retrocl plugins to verify this.
+    In tombstone purging without the fix the mmeberof plugin is called, 
+        if the tombstone entry is a group, 
+        it  modifies the user entries for the group 
+        and if retrocl is enabled this mod is written to the retrocl
+
+    The test sequence is:
+    - enable replication
+    - enable memberof and retro cl plugin
+    - add user entries
+    - add a group and add the users as members
+    - verify memberof is set to users
+    - delete the group
+    - verify memberof is removed from users
+    - add group again
+    - verify memberof is set to users
+    - get number of changes in retro cl for one user
+    - configure tombstone purging
+    - wait for purge interval to pass
+    - add a dummy entry to increase maxcsn
+    - wait for purge interval to pass two times
+    - get number of changes in retro cl for user again
+    - assert there was no additional change
+    """
+
+    log.info('Testing Ticket 48759 - no plugin calls for tombstone purging')
+
+    #
+    # Setup Replication
+    #
+    log.info('Setting up replication...')
+    topology.standalone.replica.enableReplication(suffix=DEFAULT_SUFFIX, role=REPLICAROLE_MASTER,
+                                                  replicaId=REPLICAID_MASTER_1)
+
+    #
+    # enable dynamic plugins, memberof and retro cl plugin
+    #
+    log.info('Enable plugins...')
+    try:
+        topology.standalone.modify_s(DN_CONFIG,
+                                     [(ldap.MOD_REPLACE,
+                                       'nsslapd-dynamic-plugins',
+                                       'on')])
+    except ldap.LDAPError as e:
+        ldap.error('Failed to enable dynamic plugins! ' + e.message['desc'])
+        assert False
+
+    topology.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
+    topology.standalone.plugins.enable(name=PLUGIN_RETRO_CHANGELOG)
+    # Configure memberOf group attribute
+    try:
+        topology.standalone.modify_s(MEMBEROF_PLUGIN_DN,
+                                     [(ldap.MOD_REPLACE,
+                                       'memberofgroupattr',
+                                       'member')])
+    except ldap.LDAPError as e:
+        log.fatal('Failed to configure memberOf plugin: error ' + e.message['desc'])
+        assert False
+
+
+    #
+    #  create some users and a group
+    #
+    log.info('create users and group...')
+    for idx in range(1, 5):
+        try:
+            USER_DN = ("uid=member%d,%s" % (idx, DEFAULT_SUFFIX))
+            topology.standalone.add_s(Entry((USER_DN,
+                                             {'objectclass': 'top extensibleObject'.split(),
+                                              'uid': 'member%d' % (idx)})))
+        except ldap.LDAPError as e:
+            log.fatal('Failed to add user (%s): error %s' % (USER_DN, e.message['desc']))
+            assert False
+
+    _add_group_with_members(topology)
+
+    MEMBER_VAL = ("uid=member2,%s" % DEFAULT_SUFFIX)
+    time.sleep(1)
+    _find_memberof(topology, MEMBER_VAL, GROUP_DN, True)
+
+    # delete group
+    log.info('delete group...')
+    try:
+        topology.standalone.delete_s(GROUP_DN)
+    except ldap.LDAPError as e:
+        log.error('Failed to delete entry: ' + e.message['desc'])
+        assert False
+
+    time.sleep(1)
+    _find_memberof(topology, MEMBER_VAL, GROUP_DN, False)
+
+    # add group again
+    log.info('add group again')
+    _add_group_with_members(topology)
+    time.sleep(1)
+    _find_memberof(topology, MEMBER_VAL, GROUP_DN, True)
+
+    #
+    # get number of changelog records for one user entry
+    log.info('get number of changes for %s before tombstone purging' % MEMBER_VAL)
+    changes_pre = _find_retrocl_changes(topology, MEMBER_VAL)
+
+    # configure tombstone purging
+    args = {REPLICA_PRECISE_PURGING: 'on',
+            REPLICA_PURGE_DELAY: '5',
+            REPLICA_PURGE_INTERVAL: '5'}
+    try:
+        topology.standalone.replica.setProperties(DEFAULT_SUFFIX, None, None, args)
+    except:
+        log.fatal('Failed to configure replica')
+        assert False
+
+    # Wait for the interval to pass
+    log.info('Wait for tombstone purge interval to pass ...')
+    time.sleep(6)
+
+    # Add an entry to trigger replication
+    log.info('add dummy entry')
+    try:
+        topology.standalone.add_s(Entry(('cn=test_entry,dc=example,dc=com', {
+                                  'objectclass': 'top person'.split(),
+                                  'sn': 'user',
+                                  'cn': 'entry1'})))
+    except ldap.LDAPError as e:
+        log.error('Failed to add entry: ' + e.message['desc'])
+        assert False
+
+    # check memberof is still correct
+    time.sleep(1)
+    _find_memberof(topology, MEMBER_VAL, GROUP_DN, True)
+
+    # Wait for the interval to pass again
+    log.info('Wait for tombstone purge interval to pass again...')
+    time.sleep(10)
+
+    #
+    # get number of changelog records for one user entry
+    log.info('get number of changes for %s before tombstone purging' % MEMBER_VAL)
+    changes_post = _find_retrocl_changes(topology, MEMBER_VAL)
+
+    assert (changes_pre == changes_post)
+
+
+def test_ticket48759_final(topology):
+    topology.standalone.delete()
+    log.info('Testcase PASSED')
+
+
+def run_isolated():
+    '''
+        run_isolated is used to run these test cases independently of a test scheduler (xunit, py.test..)
+        To run isolated without py.test, you need to
+            - edit this file and comment '@pytest.fixture' line before 'topology' function.
+            - set the installation prefix
+            - run this program
+    '''
+    global installation_prefix
+    installation_prefix = None
+
+    topo = topology(True)
+    test_ticket48759(topo)
+    test_ticket48759_final(topo)
+
+if __name__ == '__main__':
+    run_isolated()