|
|
@@ -0,0 +1,239 @@
|
|
|
+# --- 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 *
|
|
|
+from lib389.utils import *
|
|
|
+
|
|
|
+logging.getLogger(__name__).setLevel(logging.DEBUG)
|
|
|
+log = logging.getLogger(__name__)
|
|
|
+
|
|
|
+installation1_prefix = None
|
|
|
+
|
|
|
+
|
|
|
+class TopologyReplication(object):
|
|
|
+ def __init__(self, master1, master2):
|
|
|
+ master1.open()
|
|
|
+ self.master1 = master1
|
|
|
+ master2.open()
|
|
|
+ self.master2 = master2
|
|
|
+
|
|
|
+
|
|
|
[email protected](scope="module")
|
|
|
+def topology(request):
|
|
|
+ global installation1_prefix
|
|
|
+ os.environ['USE_VALGRIND'] = '1'
|
|
|
+ if installation1_prefix:
|
|
|
+ args_instance[SER_DEPLOYED_DIR] = installation1_prefix
|
|
|
+
|
|
|
+ # Creating master 1...
|
|
|
+ master1 = DirSrv(verbose=False)
|
|
|
+ if installation1_prefix:
|
|
|
+ args_instance[SER_DEPLOYED_DIR] = installation1_prefix
|
|
|
+ args_instance[SER_HOST] = HOST_MASTER_1
|
|
|
+ args_instance[SER_PORT] = PORT_MASTER_1
|
|
|
+ args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_1
|
|
|
+ args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
|
|
|
+ args_master = args_instance.copy()
|
|
|
+ master1.allocate(args_master)
|
|
|
+ instance_master1 = master1.exists()
|
|
|
+ if instance_master1:
|
|
|
+ master1.delete()
|
|
|
+ master1.create()
|
|
|
+ master1.open()
|
|
|
+ master1.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_1)
|
|
|
+
|
|
|
+ # Creating master 2...
|
|
|
+ master2 = DirSrv(verbose=False)
|
|
|
+ if installation1_prefix:
|
|
|
+ args_instance[SER_DEPLOYED_DIR] = installation1_prefix
|
|
|
+ args_instance[SER_HOST] = HOST_MASTER_2
|
|
|
+ args_instance[SER_PORT] = PORT_MASTER_2
|
|
|
+ args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_2
|
|
|
+ args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
|
|
|
+ args_master = args_instance.copy()
|
|
|
+ master2.allocate(args_master)
|
|
|
+ instance_master2 = master2.exists()
|
|
|
+ if instance_master2:
|
|
|
+ master2.delete()
|
|
|
+ master2.create()
|
|
|
+ master2.open()
|
|
|
+ master2.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_2)
|
|
|
+
|
|
|
+ #
|
|
|
+ # Create all the agreements
|
|
|
+ #
|
|
|
+ # Creating agreement from master 1 to master 2
|
|
|
+ properties = {RA_NAME: r'meTo_$host:$port',
|
|
|
+ RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
|
|
|
+ RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
|
|
|
+ RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
|
|
|
+ RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
|
|
|
+ m1_m2_agmt = master1.agreement.create(suffix=SUFFIX, host=master2.host, port=master2.port, properties=properties)
|
|
|
+ if not m1_m2_agmt:
|
|
|
+ log.fatal("Fail to create a master -> master replica agreement")
|
|
|
+ sys.exit(1)
|
|
|
+ log.debug("%s created" % m1_m2_agmt)
|
|
|
+
|
|
|
+ # Creating agreement from master 2 to master 1
|
|
|
+ properties = {RA_NAME: r'meTo_$host:$port',
|
|
|
+ RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
|
|
|
+ RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
|
|
|
+ RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
|
|
|
+ RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
|
|
|
+ m2_m1_agmt = master2.agreement.create(suffix=SUFFIX, host=master1.host, port=master1.port, properties=properties)
|
|
|
+ if not m2_m1_agmt:
|
|
|
+ log.fatal("Fail to create a master -> master replica agreement")
|
|
|
+ sys.exit(1)
|
|
|
+ log.debug("%s created" % m2_m1_agmt)
|
|
|
+
|
|
|
+ # Allow the replicas to get situated with the new agreements...
|
|
|
+ time.sleep(5)
|
|
|
+
|
|
|
+ #
|
|
|
+ # Initialize all the agreements
|
|
|
+ #
|
|
|
+ master1.agreement.init(SUFFIX, HOST_MASTER_2, PORT_MASTER_2)
|
|
|
+ master1.waitForReplInit(m1_m2_agmt)
|
|
|
+
|
|
|
+ # Check replication is working...
|
|
|
+ if master1.testReplication(DEFAULT_SUFFIX, master2):
|
|
|
+ log.info('Replication is working.')
|
|
|
+ else:
|
|
|
+ log.fatal('Replication is not working.')
|
|
|
+ assert False
|
|
|
+
|
|
|
+ # Clear out the tmp dir
|
|
|
+ master1.clearTmpDir(__file__)
|
|
|
+
|
|
|
+ return TopologyReplication(master1, master2)
|
|
|
+
|
|
|
+def test_ticket11111_set_purgedelay(topology):
|
|
|
+ args = {REPLICA_PURGE_DELAY: '5',
|
|
|
+ REPLICA_PURGE_INTERVAL: '5'}
|
|
|
+ try:
|
|
|
+ topology.master1.replica.setProperties(DEFAULT_SUFFIX, None, None, args)
|
|
|
+ except:
|
|
|
+ log.fatal('Failed to configure replica')
|
|
|
+ assert False
|
|
|
+ try:
|
|
|
+ topology.master2.replica.setProperties(DEFAULT_SUFFIX, None, None, args)
|
|
|
+ except:
|
|
|
+ log.fatal('Failed to configure replica')
|
|
|
+ assert False
|
|
|
+ topology.master1.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-auditlog-logging-enabled', 'on')])
|
|
|
+ topology.master2.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-auditlog-logging-enabled', 'on')])
|
|
|
+ topology.master1.restart(10)
|
|
|
+ topology.master2.restart(10)
|
|
|
+
|
|
|
+
|
|
|
+def test_ticket11111_1(topology):
|
|
|
+ name = 'test_entry'
|
|
|
+ dn = "cn=%s,%s" % (name, SUFFIX)
|
|
|
+
|
|
|
+ topology.master1.add_s(Entry((dn , {
|
|
|
+ 'objectclass': "top person".split(),
|
|
|
+ 'sn': name,
|
|
|
+ 'cn': name})))
|
|
|
+
|
|
|
+ # First do an update that is replicated
|
|
|
+ mods = [(ldap.MOD_ADD, 'description', '5')]
|
|
|
+ topology.master1.modify_s(dn, mods)
|
|
|
+
|
|
|
+ nbtry = 0
|
|
|
+ while (nbtry <= 10):
|
|
|
+ try:
|
|
|
+ ent = topology.master2.getEntry(dn, ldap.SCOPE_BASE, "(objectclass=*)", ['description'])
|
|
|
+ if ent.hasAttr('description') and ent.getValue('description') == '5':
|
|
|
+ break
|
|
|
+ except ldap.NO_SUCH_OBJECT:
|
|
|
+ pass
|
|
|
+ nbtry = nbtry + 1
|
|
|
+ time.sleep(1)
|
|
|
+ assert nbtry <= 10
|
|
|
+
|
|
|
+ # Stop M2 so that it will not receive the next update
|
|
|
+ topology.master2.stop(10)
|
|
|
+
|
|
|
+ # ADD a new value that is not replicated
|
|
|
+ mods = [(ldap.MOD_DELETE, 'description', '5')]
|
|
|
+ topology.master1.modify_s(dn, mods)
|
|
|
+
|
|
|
+ # Stop M1 so that it will keep del '5' that is unknown from master2
|
|
|
+ topology.master1.stop(10)
|
|
|
+
|
|
|
+ # Get the sbin directory so we know where to replace 'ns-slapd'
|
|
|
+ sbin_dir = get_sbin_dir(prefix=topology.master2.prefix)
|
|
|
+
|
|
|
+ # Enable valgrind
|
|
|
+ valgrind_enable(sbin_dir)
|
|
|
+
|
|
|
+ # start M2 to do the next updates
|
|
|
+ topology.master2.start(10)
|
|
|
+
|
|
|
+ # ADD 'description' by '5'
|
|
|
+ mods = [(ldap.MOD_DELETE, 'description', '5')]
|
|
|
+ topology.master2.modify_s(dn, mods)
|
|
|
+
|
|
|
+ # DEL 'description' by '5'
|
|
|
+ mods = [(ldap.MOD_ADD, 'description', '5')]
|
|
|
+ topology.master2.modify_s(dn, mods)
|
|
|
+
|
|
|
+ # sleep of purgedelay so that the next update will purge the CSN_7
|
|
|
+ time.sleep(6)
|
|
|
+
|
|
|
+ # ADD 'description' by '8' that purge the state info
|
|
|
+ mods = [(ldap.MOD_ADD, 'description', '6')]
|
|
|
+ topology.master2.modify_s(dn, mods)
|
|
|
+
|
|
|
+ if valgrind_check_leak(topology.master2, 'csnset_dup'):
|
|
|
+ log.error('test_csnset_dup: Memory leak is present!')
|
|
|
+ else:
|
|
|
+ log.info('test_csnset_dup: No leak is present!')
|
|
|
+
|
|
|
+ if valgrind_check_leak(topology.master2, 'Invalid'):
|
|
|
+ log.info('Valgrind reported invalid!')
|
|
|
+ else:
|
|
|
+ log.info('Valgrind is happy!')
|
|
|
+
|
|
|
+ #log.info("You can attach yourself")
|
|
|
+ #time.sleep(60)
|
|
|
+
|
|
|
+ # Enable valgrind
|
|
|
+ valgrind_disable(sbin_dir)
|
|
|
+
|
|
|
+ topology.master1.start(10)
|
|
|
+
|
|
|
+
|
|
|
+def test_ticket11111_final(topology):
|
|
|
+ topology.master1.delete()
|
|
|
+ topology.master2.delete()
|
|
|
+ log.info('Testcase PASSED')
|
|
|
+
|
|
|
+
|
|
|
+def run_isolated():
|
|
|
+ global installation1_prefix
|
|
|
+ installation1_prefix = None
|
|
|
+
|
|
|
+ topo = topology(True)
|
|
|
+ test_ticket11111_set_purgedelay(topo)
|
|
|
+ test_ticket11111_1(topo)
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == '__main__':
|
|
|
+ run_isolated()
|
|
|
+
|