wait_for_async_feature_test.py 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. import os
  2. import sys
  3. import time
  4. import ldap
  5. import logging
  6. import pytest
  7. from lib389 import DirSrv, Entry, tools, tasks
  8. from lib389.tools import DirSrvTools
  9. from lib389._constants import *
  10. from lib389.properties import *
  11. from lib389.tasks import *
  12. from lib389.utils import *
  13. from collections import Counter
  14. logging.getLogger(__name__).setLevel(logging.DEBUG)
  15. log = logging.getLogger(__name__)
  16. installation1_prefix = None
  17. WAITFOR_ASYNC_ATTR = "nsDS5ReplicaWaitForAsyncResults"
  18. class TopologyReplication(object):
  19. def __init__(self, master1, master2, m1_m2_agmt, m2_m1_agmt):
  20. master1.open()
  21. master2.open()
  22. self.masters = ((master1, m1_m2_agmt),
  23. (master2, m2_m1_agmt))
  24. @pytest.fixture(scope="module")
  25. def topology(request):
  26. global installation1_prefix
  27. if installation1_prefix:
  28. args_instance[SER_DEPLOYED_DIR] = installation1_prefix
  29. # Creating master 1...
  30. master1 = DirSrv(verbose=False)
  31. args_instance[SER_HOST] = HOST_MASTER_1
  32. args_instance[SER_PORT] = PORT_MASTER_1
  33. args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_1
  34. args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
  35. args_master = args_instance.copy()
  36. master1.allocate(args_master)
  37. instance_master1 = master1.exists()
  38. if instance_master1:
  39. master1.delete()
  40. master1.create()
  41. master1.open()
  42. master1.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_1)
  43. # Creating master 2...
  44. master2 = DirSrv(verbose=False)
  45. args_instance[SER_HOST] = HOST_MASTER_2
  46. args_instance[SER_PORT] = PORT_MASTER_2
  47. args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_2
  48. args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
  49. args_master = args_instance.copy()
  50. master2.allocate(args_master)
  51. instance_master2 = master2.exists()
  52. if instance_master2:
  53. master2.delete()
  54. master2.create()
  55. master2.open()
  56. master2.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_2)
  57. #
  58. # Create all the agreements
  59. #
  60. # Creating agreement from master 1 to master 2
  61. properties = {RA_NAME: 'meTo_%s:%s' %(master2.host, master2.port),
  62. RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
  63. RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
  64. RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
  65. RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
  66. m1_m2_agmt = master1.agreement.create(suffix=SUFFIX, host=master2.host, port=master2.port, properties=properties)
  67. if not m1_m2_agmt:
  68. log.fatal("Fail to create a master -> master replica agreement")
  69. sys.exit(1)
  70. log.debug("%s created" % m1_m2_agmt)
  71. # Creating agreement from master 2 to master 1
  72. properties = {RA_NAME: 'meTo_%s:%s' %(master1.host, master1.port),
  73. RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
  74. RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
  75. RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
  76. RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
  77. m2_m1_agmt = master2.agreement.create(suffix=SUFFIX, host=master1.host, port=master1.port, properties=properties)
  78. if not m2_m1_agmt:
  79. log.fatal("Fail to create a master -> master replica agreement")
  80. sys.exit(1)
  81. log.debug("%s created" % m2_m1_agmt)
  82. # Allow the replicas to get situated with the new agreements...
  83. time.sleep(5)
  84. #
  85. # Initialize all the agreements
  86. #
  87. master1.agreement.init(SUFFIX, HOST_MASTER_2, PORT_MASTER_2)
  88. master1.waitForReplInit(m1_m2_agmt)
  89. master2.agreement.init(SUFFIX, HOST_MASTER_1, PORT_MASTER_1)
  90. master2.waitForReplInit(m2_m1_agmt)
  91. # Check replication is working...
  92. if master1.testReplication(DEFAULT_SUFFIX, master2):
  93. log.info('Replication is working.')
  94. else:
  95. log.fatal('Replication is not working.')
  96. assert False
  97. log.info("Set Replication Debugging loglevel for the errorlog")
  98. master1.setLogLevel(LOG_REPLICA)
  99. master2.setLogLevel(LOG_REPLICA)
  100. logging_attr = 'nsslapd-logging-hr-timestamps-enabled'
  101. master1.modify_s("cn=config", [(ldap.MOD_REPLACE, logging_attr, "off")])
  102. master2.modify_s("cn=config", [(ldap.MOD_REPLACE, logging_attr, "off")])
  103. # Delete each instance in the end
  104. def fin():
  105. master1.delete()
  106. master2.delete()
  107. request.addfinalizer(fin)
  108. # Clear out the tmp dir
  109. master1.clearTmpDir(__file__)
  110. return TopologyReplication(master1, master2, m1_m2_agmt, m2_m1_agmt)
  111. @pytest.fixture(params=[(None, (4, 10)),
  112. ('2000', (0, 1)),
  113. ('0', (4, 10)),
  114. ('-5', (4, 10))])
  115. def waitfor_async_attr(topology, request):
  116. """Sets attribute on all replicas"""
  117. attr_value = request.param[0]
  118. expected_result = request.param[1]
  119. # Run through all masters
  120. for master in topology.masters:
  121. agmt = master[1]
  122. try:
  123. if attr_value:
  124. log.info("Set %s: %s on %s" % (
  125. WAITFOR_ASYNC_ATTR, attr_value, master[0].serverid))
  126. mod = [(ldap.MOD_REPLACE, WAITFOR_ASYNC_ATTR, attr_value)]
  127. else:
  128. log.info("Delete %s from %s" % (
  129. WAITFOR_ASYNC_ATTR, master[0].serverid))
  130. mod = [(ldap.MOD_DELETE, WAITFOR_ASYNC_ATTR, None)]
  131. master[0].modify_s(agmt, mod)
  132. except ldap.LDAPError as e:
  133. log.error('Failed to set or delete %s attribute: (%s)' % (
  134. WAITFOR_ASYNC_ATTR, e.message['desc']))
  135. return (attr_value, expected_result)
  136. @pytest.fixture
  137. def entries(topology, request):
  138. """Adds entries to the master1"""
  139. master1 = topology.masters[0][0]
  140. TEST_OU = "test"
  141. test_dn = SUFFIX
  142. test_list = []
  143. log.info("Add 100 nested entries under replicated suffix on %s" % master1.serverid)
  144. for i in xrange(100):
  145. test_dn = 'ou=%s%s,%s' % (TEST_OU, i, test_dn)
  146. test_list.insert(0, test_dn)
  147. try:
  148. master1.add_s(Entry((test_dn,
  149. {'objectclass': 'top',
  150. 'objectclass': 'organizationalUnit',
  151. 'ou': TEST_OU})))
  152. except ldap.LDAPError as e:
  153. log.error('Failed to add entry (%s): error (%s)' % (test_dn,
  154. e.message['desc']))
  155. assert False
  156. log.info("Delete created entries")
  157. for test_dn in test_list:
  158. try:
  159. master1.delete_s(test_dn)
  160. except ldap.LDAPError, e:
  161. log.error('Failed to delete entry (%s): error (%s)' % (test_dn,
  162. e.message['desc']))
  163. assert False
  164. def fin():
  165. log.info("Clear the errors log in the end of the test case")
  166. with open(master1.errlog, 'w') as errlog:
  167. errlog.writelines("")
  168. request.addfinalizer(fin)
  169. def test_not_int_value(topology):
  170. """Tests not integer value"""
  171. master1 = topology.masters[0][0]
  172. agmt = topology.masters[0][1]
  173. log.info("Try to set %s: wv1" % WAITFOR_ASYNC_ATTR)
  174. try:
  175. mod = [(ldap.MOD_REPLACE, WAITFOR_ASYNC_ATTR, "wv1")]
  176. master1.modify_s(agmt, mod)
  177. except ldap.LDAPError as e:
  178. assert e.message['desc'] == 'Invalid syntax'
  179. def test_multi_value(topology):
  180. """Tests multi value"""
  181. master1 = topology.masters[0][0]
  182. agmt = topology.masters[0][1]
  183. log.info("agmt: %s" % agmt)
  184. log.info("Try to set %s: 100 and 101 in the same time (multi value test)" % (
  185. WAITFOR_ASYNC_ATTR))
  186. try:
  187. mod = [(ldap.MOD_ADD, WAITFOR_ASYNC_ATTR, "100")]
  188. master1.modify_s(agmt, mod)
  189. mod = [(ldap.MOD_ADD, WAITFOR_ASYNC_ATTR, "101")]
  190. master1.modify_s(agmt, mod)
  191. except ldap.LDAPError as e:
  192. assert e.message['desc'] == 'Object class violation'
  193. def test_value_check(topology, waitfor_async_attr):
  194. """Checks that value has been set correctly"""
  195. attr_value = waitfor_async_attr[0]
  196. for master in topology.masters:
  197. agmt = master[1]
  198. log.info("Check attr %s on %s" % (WAITFOR_ASYNC_ATTR, master[0].serverid))
  199. try:
  200. if attr_value:
  201. entry = master[0].search_s(agmt, ldap.SCOPE_BASE, "%s=%s" % (
  202. WAITFOR_ASYNC_ATTR, attr_value))
  203. assert entry
  204. else:
  205. entry = master[0].search_s(agmt, ldap.SCOPE_BASE, "%s=*" % WAITFOR_ASYNC_ATTR)
  206. assert not entry
  207. except ldap.LDAPError as e:
  208. log.fatal('Search failed, error: ' + e.message['desc'])
  209. assert False
  210. def test_behavior_with_value(topology, waitfor_async_attr, entries):
  211. """Tests replication behavior with valid
  212. nsDS5ReplicaWaitForAsyncResults attribute values
  213. """
  214. master1 = topology.masters[0][0]
  215. sync_dict = Counter()
  216. min_ap = waitfor_async_attr[1][0]
  217. max_ap = waitfor_async_attr[1][1]
  218. time.sleep(20)
  219. log.info("Gather all sync attempts within Counter dict, group by timestamp")
  220. with open(master1.errlog, 'r') as errlog:
  221. errlog_filtered = filter(lambda x: "waitfor_async_results" in x, errlog)
  222. for line in errlog_filtered:
  223. # Watch only over unsuccessful sync attempts
  224. if line.split()[3] != line.split()[4]:
  225. timestamp = line.split(']')[0]
  226. sync_dict[timestamp] += 1
  227. log.info("Take the most common timestamp and assert it has appeared " \
  228. "in the range from %s to %s times" % (min_ap, max_ap))
  229. most_common_val = sync_dict.most_common(1)[0][1]
  230. assert min_ap <= most_common_val <= max_ap
  231. if __name__ == '__main__':
  232. # Run isolated
  233. # -s for DEBUG mode
  234. CURRENT_FILE = os.path.realpath(__file__)
  235. pytest.main("-s %s" % CURRENT_FILE)