ticket47819_test.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. # --- BEGIN COPYRIGHT BLOCK ---
  2. # Copyright (C) 2015 Red Hat, Inc.
  3. # All rights reserved.
  4. #
  5. # License: GPL (version 3 or any later version).
  6. # See LICENSE for details.
  7. # --- END COPYRIGHT BLOCK ---
  8. #
  9. import os
  10. import sys
  11. import time
  12. import ldap
  13. import logging
  14. import pytest
  15. from lib389 import DirSrv, Entry, tools, tasks
  16. from lib389.tools import DirSrvTools
  17. from lib389._constants import *
  18. from lib389.properties import *
  19. from lib389.tasks import *
  20. log = logging.getLogger(__name__)
  21. installation_prefix = None
  22. class TopologyStandalone(object):
  23. def __init__(self, standalone):
  24. standalone.open()
  25. self.standalone = standalone
  26. @pytest.fixture(scope="module")
  27. def topology(request):
  28. '''
  29. This fixture is used to standalone topology for the 'module'.
  30. '''
  31. global installation_prefix
  32. if installation_prefix:
  33. args_instance[SER_DEPLOYED_DIR] = installation_prefix
  34. standalone = DirSrv(verbose=False)
  35. # Args for the standalone instance
  36. args_instance[SER_HOST] = HOST_STANDALONE
  37. args_instance[SER_PORT] = PORT_STANDALONE
  38. args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE
  39. args_standalone = args_instance.copy()
  40. standalone.allocate(args_standalone)
  41. # Get the status of the instance and restart it if it exists
  42. instance_standalone = standalone.exists()
  43. # Remove the instance
  44. if instance_standalone:
  45. standalone.delete()
  46. # Create the instance
  47. standalone.create()
  48. # Used to retrieve configuration information (dbdir, confdir...)
  49. standalone.open()
  50. # clear the tmp directory
  51. standalone.clearTmpDir(__file__)
  52. # Here we have standalone instance up and running
  53. return TopologyStandalone(standalone)
  54. def test_ticket47819(topology):
  55. """
  56. Testing precise tombstone purging:
  57. [1] Make sure "nsTombstoneCSN" is added to new tombstones
  58. [2] Make sure an import of a replication ldif adds "nsTombstoneCSN"
  59. to old tombstones
  60. [4] Test fixup task
  61. [3] Make sure tombstone purging works
  62. """
  63. log.info('Testing Ticket 47819 - Test precise tombstone purging')
  64. #
  65. # Setup Replication
  66. #
  67. log.info('Setting up replication...')
  68. topology.standalone.replica.enableReplication(suffix=DEFAULT_SUFFIX, role=REPLICAROLE_MASTER,
  69. replicaId=REPLICAID_MASTER_1)
  70. #
  71. # Part 1 create a tombstone entry and make sure nsTombstoneCSN is added
  72. #
  73. log.info('Part 1: Add and then delete an entry to create a tombstone...')
  74. try:
  75. topology.standalone.add_s(Entry(('cn=entry1,dc=example,dc=com', {
  76. 'objectclass': 'top person'.split(),
  77. 'sn': 'user',
  78. 'cn': 'entry1'})))
  79. except ldap.LDAPError as e:
  80. log.error('Failed to add entry: ' + e.message['desc'])
  81. assert False
  82. try:
  83. topology.standalone.delete_s('cn=entry1,dc=example,dc=com')
  84. except ldap.LDAPError as e:
  85. log.error('Failed to delete entry: ' + e.message['desc'])
  86. assert False
  87. log.info('Search for tombstone entries...')
  88. try:
  89. entries = topology.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE,
  90. '(&(nsTombstoneCSN=*)(objectclass=nsTombstone))')
  91. if not entries:
  92. log.fatal('Search failed to the new tombstone(nsTombstoneCSN is probably missing).')
  93. assert False
  94. except ldap.LDAPError as e:
  95. log.fatal('Search failed: ' + e.message['desc'])
  96. assert False
  97. log.info('Part 1 - passed')
  98. #
  99. # Part 2 - import ldif with tombstones missing 'nsTombstoneCSN'
  100. #
  101. # First, export the replication ldif, edit the file(remove nstombstonecsn),
  102. # and reimport it.
  103. #
  104. log.info('Part 2: Exporting replication ldif...')
  105. # Get the the full path and name for our LDIF we will be exporting
  106. ldif_file = topology.standalone.getDir(__file__, TMP_DIR) + "export.ldif"
  107. args = {EXPORT_REPL_INFO: True,
  108. TASK_WAIT: True}
  109. exportTask = Tasks(topology.standalone)
  110. try:
  111. exportTask.exportLDIF(DEFAULT_SUFFIX, None, ldif_file, args)
  112. except ValueError:
  113. assert False
  114. # open the ldif file, get the lines, then rewrite the file
  115. ldif = open(ldif_file, "r")
  116. lines = ldif.readlines()
  117. ldif.close()
  118. ldif = open(ldif_file, "w")
  119. for line in lines:
  120. if not line.lower().startswith('nstombstonecsn'):
  121. ldif.write(line)
  122. ldif.close()
  123. # import the new ldif file
  124. log.info('Import replication LDIF file...')
  125. importTask = Tasks(topology.standalone)
  126. args = {TASK_WAIT: True}
  127. try:
  128. importTask.importLDIF(DEFAULT_SUFFIX, None, ldif_file, args)
  129. os.remove(ldif_file)
  130. except ValueError:
  131. os.remove(ldif_file)
  132. assert False
  133. # Search for the tombstone again
  134. log.info('Search for tombstone entries...')
  135. try:
  136. entries = topology.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE,
  137. '(&(nsTombstoneCSN=*)(objectclass=nsTombstone))')
  138. if not entries:
  139. log.fatal('Search failed to fine the new tombstone(nsTombstoneCSN is probably missing).')
  140. assert False
  141. except ldap.LDAPError as e:
  142. log.fatal('Search failed: ' + e.message['desc'])
  143. assert False
  144. log.info('Part 2 - passed')
  145. #
  146. # Part 3 - test fixup task
  147. #
  148. log.info('Part 4: test the fixup task')
  149. # Run fixup task using the strip option. This removes nsTombstoneCSN
  150. # so we can test if the fixup task works.
  151. args = {TASK_WAIT: True,
  152. TASK_TOMB_STRIP: True}
  153. fixupTombTask = Tasks(topology.standalone)
  154. try:
  155. fixupTombTask.fixupTombstones(DEFAULT_BENAME, args)
  156. except:
  157. assert False
  158. # Search for tombstones with nsTombstoneCSN - better not find any
  159. log.info('Search for tombstone entries...')
  160. try:
  161. entries = topology.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE,
  162. '(&(nsTombstoneCSN=*)(objectclass=nsTombstone))')
  163. if entries:
  164. log.fatal('Search found tombstones with nsTombstoneCSN')
  165. assert False
  166. except ldap.LDAPError as e:
  167. log.fatal('Search failed: ' + e.message['desc'])
  168. assert False
  169. # Now run the fixup task
  170. args = {TASK_WAIT: True}
  171. fixupTombTask = Tasks(topology.standalone)
  172. try:
  173. fixupTombTask.fixupTombstones(DEFAULT_BENAME, args)
  174. except:
  175. assert False
  176. # Search for tombstones with nsTombstoneCSN - better find some
  177. log.info('Search for tombstone entries...')
  178. try:
  179. entries = topology.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE,
  180. '(&(nsTombstoneCSN=*)(objectclass=nsTombstone))')
  181. if not entries:
  182. log.fatal('Search did not find any fixed-up tombstones')
  183. assert False
  184. except ldap.LDAPError as e:
  185. log.fatal('Search failed: ' + e.message['desc'])
  186. assert False
  187. log.info('Part 3 - passed')
  188. #
  189. # Part 4 - Test tombstone purging
  190. #
  191. log.info('Part 4: test tombstone purging...')
  192. args = {REPLICA_PRECISE_PURGING: 'on',
  193. REPLICA_PURGE_DELAY: '5',
  194. REPLICA_PURGE_INTERVAL: '5'}
  195. try:
  196. topology.standalone.replica.setProperties(DEFAULT_SUFFIX, None, None, args)
  197. except:
  198. log.fatal('Failed to configure replica')
  199. assert False
  200. # Wait for the interval to pass
  201. log.info('Wait for tombstone purge interval to pass...')
  202. time.sleep(6)
  203. # Add an entry to trigger replication
  204. log.info('Perform an update to help trigger tombstone purging...')
  205. try:
  206. topology.standalone.add_s(Entry(('cn=test_entry,dc=example,dc=com', {
  207. 'objectclass': 'top person'.split(),
  208. 'sn': 'user',
  209. 'cn': 'entry1'})))
  210. except ldap.LDAPError as e:
  211. log.error('Failed to add entry: ' + e.message['desc'])
  212. assert False
  213. # Wait for the interval to pass again
  214. log.info('Wait for tombstone purge interval to pass again...')
  215. time.sleep(10)
  216. # search for tombstones, there should be none
  217. log.info('Search for tombstone entries...')
  218. try:
  219. entries = topology.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE,
  220. '(&(nsTombstoneCSN=*)(objectclass=nsTombstone))')
  221. if entries:
  222. log.fatal('Search unexpectedly found tombstones')
  223. assert False
  224. except ldap.LDAPError as e:
  225. log.fatal('Search failed: ' + e.message['desc'])
  226. assert False
  227. log.info('Part 4 - passed')
  228. def test_ticket47819_final(topology):
  229. topology.standalone.delete()
  230. log.info('Testcase PASSED')
  231. def run_isolated():
  232. '''
  233. run_isolated is used to run these test cases independently of a test scheduler (xunit, py.test..)
  234. To run isolated without py.test, you need to
  235. - edit this file and comment '@pytest.fixture' line before 'topology' function.
  236. - set the installation prefix
  237. - run this program
  238. '''
  239. global installation_prefix
  240. installation_prefix = None
  241. topo = topology(True)
  242. test_ticket47819(topo)
  243. test_ticket47819_final(topo)
  244. if __name__ == '__main__':
  245. run_isolated()