|
|
@@ -0,0 +1,258 @@
|
|
|
+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 *
|
|
|
+
|
|
|
+DEBUGGING = False
|
|
|
+GROUP_DN = ("cn=group," + DEFAULT_SUFFIX)
|
|
|
+
|
|
|
+if DEBUGGING:
|
|
|
+ logging.getLogger(__name__).setLevel(logging.DEBUG)
|
|
|
+else:
|
|
|
+ logging.getLogger(__name__).setLevel(logging.INFO)
|
|
|
+log = logging.getLogger(__name__)
|
|
|
+
|
|
|
+
|
|
|
+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):
|
|
|
+ """Create Replication Deployment"""
|
|
|
+
|
|
|
+ # Creating master 1...
|
|
|
+ if DEBUGGING:
|
|
|
+ master1 = DirSrv(verbose=True)
|
|
|
+ else:
|
|
|
+ master1 = DirSrv(verbose=False)
|
|
|
+ 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...
|
|
|
+ if DEBUGGING:
|
|
|
+ master2 = DirSrv(verbose=True)
|
|
|
+ else:
|
|
|
+ master2 = DirSrv(verbose=False)
|
|
|
+ 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)
|
|
|
+
|
|
|
+ def fin():
|
|
|
+ """If we are debugging just stop the instances,
|
|
|
+ otherwise remove them
|
|
|
+ """
|
|
|
+
|
|
|
+ if DEBUGGING:
|
|
|
+ master1.stop()
|
|
|
+ master2.stop()
|
|
|
+ else:
|
|
|
+ #master1.delete()
|
|
|
+ #master2.delete()
|
|
|
+ pass
|
|
|
+
|
|
|
+ request.addfinalizer(fin)
|
|
|
+
|
|
|
+ # Create all the agreements
|
|
|
+
|
|
|
+ # Creating agreement from master 1 to master 2
|
|
|
+ properties = {RA_NAME: 'meTo_' + master2.host + ':' + str(master2.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: 'meTo_' + master1.host + ':' + str(master1.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 _add_group_with_members(topology):
|
|
|
+ # Create group
|
|
|
+ try:
|
|
|
+ topology.master1.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.master1.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 _check_memberof(master, presence_flag):
|
|
|
+ # Check that members have memberof attribute on M1
|
|
|
+ for idx in range(1, 5):
|
|
|
+ try:
|
|
|
+ USER_DN = ("uid=member%d,%s" % (idx, DEFAULT_SUFFIX))
|
|
|
+ ent = master.getEntry(USER_DN, ldap.SCOPE_BASE, "(objectclass=*)")
|
|
|
+ if presence_flag:
|
|
|
+ assert ent.hasAttr('memberof') and ent.getValue('memberof') == GROUP_DN
|
|
|
+ else:
|
|
|
+ assert not ent.hasAttr('memberof')
|
|
|
+ except ldap.LDAPError as e:
|
|
|
+ log.fatal('Failed to retrieve user (%s): error %s' % (USER_DN, e.message['desc']))
|
|
|
+ assert False
|
|
|
+
|
|
|
+def _check_entry_exist(master, dn):
|
|
|
+ attempt = 0
|
|
|
+ while attempt <= 10:
|
|
|
+ try:
|
|
|
+ dn
|
|
|
+ ent = master.getEntry(dn, ldap.SCOPE_BASE, "(objectclass=*)")
|
|
|
+ break
|
|
|
+ except ldap.NO_SUCH_OBJECT:
|
|
|
+ attempt = attempt + 1
|
|
|
+ time.sleep(1)
|
|
|
+ except ldap.LDAPError as e:
|
|
|
+ log.fatal('Failed to retrieve user (%s): error %s' % (dn, e.message['desc']))
|
|
|
+ assert False
|
|
|
+ assert attempt != 10
|
|
|
+
|
|
|
+def test_ticket49073(topology):
|
|
|
+ """Write your replication test here.
|
|
|
+
|
|
|
+ To access each DirSrv instance use: topology.master1, topology.master2,
|
|
|
+ ..., topology.hub1, ..., topology.consumer1,...
|
|
|
+
|
|
|
+ Also, if you need any testcase initialization,
|
|
|
+ please, write additional fixture for that(include finalizer).
|
|
|
+ """
|
|
|
+ topology.master1.plugins.enable(name=PLUGIN_MEMBER_OF)
|
|
|
+ topology.master1.restart(timeout=10)
|
|
|
+ topology.master2.plugins.enable(name=PLUGIN_MEMBER_OF)
|
|
|
+ topology.master2.restart(timeout=10)
|
|
|
+
|
|
|
+ # Configure fractional to prevent total init to send memberof
|
|
|
+ ents = topology.master1.agreement.list(suffix=SUFFIX)
|
|
|
+ assert len(ents) == 1
|
|
|
+ log.info('update %s to add nsDS5ReplicatedAttributeListTotal' % ents[0].dn)
|
|
|
+ topology.master1.modify_s(ents[0].dn,
|
|
|
+ [(ldap.MOD_REPLACE,
|
|
|
+ 'nsDS5ReplicatedAttributeListTotal',
|
|
|
+ '(objectclass=*) $ EXCLUDE '),
|
|
|
+ (ldap.MOD_REPLACE,
|
|
|
+ 'nsDS5ReplicatedAttributeList',
|
|
|
+ '(objectclass=*) $ EXCLUDE memberOf')])
|
|
|
+ topology.master1.restart(timeout=10)
|
|
|
+
|
|
|
+ #
|
|
|
+ # 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.master1.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
|
|
|
+
|
|
|
+ _check_entry_exist(topology.master2, "uid=member4,%s" % (DEFAULT_SUFFIX))
|
|
|
+ _add_group_with_members(topology)
|
|
|
+ _check_entry_exist(topology.master2, GROUP_DN)
|
|
|
+
|
|
|
+ # Check that for regular update memberof was on both side (because plugin is enabled both)
|
|
|
+ time.sleep(5)
|
|
|
+ _check_memberof(topology.master1, True)
|
|
|
+ _check_memberof(topology.master2, True)
|
|
|
+
|
|
|
+
|
|
|
+ # reinit with fractional definition
|
|
|
+ ents = topology.master1.agreement.list(suffix=SUFFIX)
|
|
|
+ assert len(ents) == 1
|
|
|
+ topology.master1.agreement.init(SUFFIX, HOST_MASTER_2, PORT_MASTER_2)
|
|
|
+ topology.master1.waitForReplInit(ents[0].dn)
|
|
|
+
|
|
|
+ # Check that for total update memberof was on both side
|
|
|
+ # because memberof is NOT excluded from total init
|
|
|
+ time.sleep(5)
|
|
|
+ _check_memberof(topology.master1, True)
|
|
|
+ _check_memberof(topology.master2, True)
|
|
|
+
|
|
|
+ if DEBUGGING:
|
|
|
+ # Add debugging steps(if any)...
|
|
|
+ pass
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == '__main__':
|
|
|
+ # Run isolated
|
|
|
+ # -s for DEBUG mode
|
|
|
+ CURRENT_FILE = os.path.realpath(__file__)
|
|
|
+ pytest.main("-s %s" % CURRENT_FILE)
|
|
|
+
|