ticket47871_test.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. '''
  2. Created on Nov 7, 2013
  3. @author: tbordaz
  4. '''
  5. import os
  6. import sys
  7. import time
  8. import ldap
  9. import logging
  10. import pytest
  11. from lib389 import DirSrv, Entry, tools
  12. from lib389.tools import DirSrvTools
  13. from lib389._constants import *
  14. from lib389.properties import *
  15. logging.getLogger(__name__).setLevel(logging.DEBUG)
  16. log = logging.getLogger(__name__)
  17. installation_prefix = None
  18. TEST_REPL_DN = "cn=test_repl, %s" % SUFFIX
  19. ENTRY_DN = "cn=test_entry, %s" % SUFFIX
  20. OTHER_NAME = 'other_entry'
  21. MAX_OTHERS = 10
  22. ATTRIBUTES = ['street', 'countryName', 'description', 'postalAddress', 'postalCode', 'title', 'l', 'roomNumber']
  23. class TopologyMasterConsumer(object):
  24. def __init__(self, master, consumer):
  25. master.open()
  26. self.master = master
  27. consumer.open()
  28. self.consumer = consumer
  29. def __repr__(self):
  30. return "Master[%s] -> Consumer[%s" % (self.master, self.consumer)
  31. @pytest.fixture(scope="module")
  32. def topology(request):
  33. '''
  34. This fixture is used to create a replicated topology for the 'module'.
  35. The replicated topology is MASTER -> Consumer.
  36. '''
  37. global installation_prefix
  38. if installation_prefix:
  39. args_instance[SER_DEPLOYED_DIR] = installation_prefix
  40. master = DirSrv(verbose=False)
  41. consumer = DirSrv(verbose=False)
  42. # Args for the master instance
  43. args_instance[SER_HOST] = HOST_MASTER_1
  44. args_instance[SER_PORT] = PORT_MASTER_1
  45. args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_1
  46. args_master = args_instance.copy()
  47. master.allocate(args_master)
  48. # Args for the consumer instance
  49. args_instance[SER_HOST] = HOST_CONSUMER_1
  50. args_instance[SER_PORT] = PORT_CONSUMER_1
  51. args_instance[SER_SERVERID_PROP] = SERVERID_CONSUMER_1
  52. args_consumer = args_instance.copy()
  53. consumer.allocate(args_consumer)
  54. # Get the status of the instance and restart it if it exists
  55. instance_master = master.exists()
  56. instance_consumer = consumer.exists()
  57. # Remove all the instances
  58. if instance_master:
  59. master.delete()
  60. if instance_consumer:
  61. consumer.delete()
  62. # Create the instances
  63. master.create()
  64. master.open()
  65. consumer.create()
  66. consumer.open()
  67. #
  68. # Now prepare the Master-Consumer topology
  69. #
  70. # First Enable replication
  71. master.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_1)
  72. consumer.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_CONSUMER)
  73. # Initialize the supplier->consumer
  74. properties = {RA_NAME: r'meTo_$host:$port',
  75. RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
  76. RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
  77. RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
  78. RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
  79. repl_agreement = master.agreement.create(suffix=SUFFIX, host=consumer.host, port=consumer.port, properties=properties)
  80. if not repl_agreement:
  81. log.fatal("Fail to create a replica agreement")
  82. sys.exit(1)
  83. log.debug("%s created" % repl_agreement)
  84. master.agreement.init(SUFFIX, HOST_CONSUMER_1, PORT_CONSUMER_1)
  85. master.waitForReplInit(repl_agreement)
  86. # Check replication is working fine
  87. if master.testReplication(DEFAULT_SUFFIX, consumer):
  88. log.info('Replication is working.')
  89. else:
  90. log.fatal('Replication is not working.')
  91. assert False
  92. # clear the tmp directory
  93. master.clearTmpDir(__file__)
  94. #
  95. # Here we have two instances master and consumer
  96. # with replication working. Either coming from a backup recovery
  97. # or from a fresh (re)init
  98. # Time to return the topology
  99. return TopologyMasterConsumer(master, consumer)
  100. def test_ticket47871_init(topology):
  101. """
  102. Initialize the test environment
  103. """
  104. topology.master.plugins.enable(name=PLUGIN_RETRO_CHANGELOG)
  105. mod = [(ldap.MOD_REPLACE, 'nsslapd-changelogmaxage', "10s"), # 10 second triming
  106. (ldap.MOD_REPLACE, 'nsslapd-changelog-trim-interval', "5s")]
  107. topology.master.modify_s("cn=%s,%s" % (PLUGIN_RETRO_CHANGELOG, DN_PLUGIN), mod)
  108. #topology.master.plugins.enable(name=PLUGIN_MEMBER_OF)
  109. #topology.master.plugins.enable(name=PLUGIN_REFER_INTEGRITY)
  110. topology.master.stop(timeout=10)
  111. topology.master.start(timeout=10)
  112. topology.master.log.info("test_ticket47871_init topology %r" % (topology))
  113. # the test case will check if a warning message is logged in the
  114. # error log of the supplier
  115. topology.master.errorlog_file = open(topology.master.errlog, "r")
  116. def test_ticket47871_1(topology):
  117. '''
  118. ADD entries and check they are all in the retrocl
  119. '''
  120. # add dummy entries
  121. for cpt in range(MAX_OTHERS):
  122. name = "%s%d" % (OTHER_NAME, cpt)
  123. topology.master.add_s(Entry(("cn=%s,%s" % (name, SUFFIX), {
  124. 'objectclass': "top person".split(),
  125. 'sn': name,
  126. 'cn': name})))
  127. topology.master.log.info("test_ticket47871_init: %d entries ADDed %s[0..%d]" % (MAX_OTHERS, OTHER_NAME, MAX_OTHERS - 1))
  128. # Check the number of entries in the retro changelog
  129. time.sleep(1)
  130. ents = topology.master.search_s(RETROCL_SUFFIX, ldap.SCOPE_ONELEVEL, "(objectclass=*)")
  131. assert len(ents) == MAX_OTHERS
  132. topology.master.log.info("Added entries are")
  133. for ent in ents:
  134. topology.master.log.info("%s" % ent.dn)
  135. def test_ticket47871_2(topology):
  136. '''
  137. Wait until there is just a last entries
  138. '''
  139. MAX_TRIES = 10
  140. TRY_NO = 1
  141. while TRY_NO <= MAX_TRIES:
  142. time.sleep(6) # at least 1 trimming occurred
  143. ents = topology.master.search_s(RETROCL_SUFFIX, ldap.SCOPE_ONELEVEL, "(objectclass=*)")
  144. assert len(ents) <= MAX_OTHERS
  145. topology.master.log.info("\nTry no %d it remains %d entries" % (TRY_NO, len(ents)))
  146. for ent in ents:
  147. topology.master.log.info("%s" % ent.dn)
  148. if len(ents) > 1:
  149. TRY_NO += 1
  150. else:
  151. break
  152. assert TRY_NO <= MAX_TRIES
  153. assert len(ents) <= 1
  154. def test_ticket47871_final(topology):
  155. topology.master.delete()
  156. topology.consumer.delete()
  157. log.info('Testcase PASSED')
  158. def run_isolated():
  159. '''
  160. run_isolated is used to run these test cases independently of a test scheduler (xunit, py.test..)
  161. To run isolated without py.test, you need to
  162. - edit this file and comment '@pytest.fixture' line before 'topology' function.
  163. - set the installation prefix
  164. - run this program
  165. '''
  166. global installation_prefix
  167. installation_prefix = None
  168. topo = topology(True)
  169. test_ticket47871_init(topo)
  170. test_ticket47871_1(topo)
  171. test_ticket47871_2(topo)
  172. test_ticket47871_final(topo)
  173. if __name__ == '__main__':
  174. run_isolated()