ticket47819_test.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  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. class TopologyStandalone(object):
  22. def __init__(self, standalone):
  23. standalone.open()
  24. self.standalone = standalone
  25. @pytest.fixture(scope="module")
  26. def topology(request):
  27. '''
  28. This fixture is used to standalone topology for the 'module'.
  29. '''
  30. standalone = DirSrv(verbose=False)
  31. # Args for the standalone instance
  32. args_instance[SER_HOST] = HOST_STANDALONE
  33. args_instance[SER_PORT] = PORT_STANDALONE
  34. args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE
  35. args_standalone = args_instance.copy()
  36. standalone.allocate(args_standalone)
  37. # Get the status of the instance and restart it if it exists
  38. instance_standalone = standalone.exists()
  39. # Remove the instance
  40. if instance_standalone:
  41. standalone.delete()
  42. # Create the instance
  43. standalone.create()
  44. # Used to retrieve configuration information (dbdir, confdir...)
  45. standalone.open()
  46. def fin():
  47. standalone.delete()
  48. request.addfinalizer(fin)
  49. # Here we have standalone instance up and running
  50. return TopologyStandalone(standalone)
  51. def test_ticket47819(topology):
  52. """
  53. Testing precise tombstone purging:
  54. [1] Make sure "nsTombstoneCSN" is added to new tombstones
  55. [2] Make sure an import of a replication ldif adds "nsTombstoneCSN"
  56. to old tombstones
  57. [4] Test fixup task
  58. [3] Make sure tombstone purging works
  59. """
  60. log.info('Testing Ticket 47819 - Test precise tombstone purging')
  61. #
  62. # Setup Replication
  63. #
  64. log.info('Setting up replication...')
  65. topology.standalone.replica.enableReplication(suffix=DEFAULT_SUFFIX, role=REPLICAROLE_MASTER,
  66. replicaId=REPLICAID_MASTER_1)
  67. #
  68. # Part 1 create a tombstone entry and make sure nsTombstoneCSN is added
  69. #
  70. log.info('Part 1: Add and then delete an entry to create a tombstone...')
  71. try:
  72. topology.standalone.add_s(Entry(('cn=entry1,dc=example,dc=com', {
  73. 'objectclass': 'top person'.split(),
  74. 'sn': 'user',
  75. 'cn': 'entry1'})))
  76. except ldap.LDAPError as e:
  77. log.error('Failed to add entry: ' + e.message['desc'])
  78. assert False
  79. try:
  80. topology.standalone.delete_s('cn=entry1,dc=example,dc=com')
  81. except ldap.LDAPError as e:
  82. log.error('Failed to delete entry: ' + e.message['desc'])
  83. assert False
  84. log.info('Search for tombstone entries...')
  85. try:
  86. entries = topology.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE,
  87. '(&(nsTombstoneCSN=*)(objectclass=nsTombstone))')
  88. if not entries:
  89. log.fatal('Search failed to the new tombstone(nsTombstoneCSN is probably missing).')
  90. assert False
  91. except ldap.LDAPError as e:
  92. log.fatal('Search failed: ' + e.message['desc'])
  93. assert False
  94. log.info('Part 1 - passed')
  95. #
  96. # Part 2 - import ldif with tombstones missing 'nsTombstoneCSN'
  97. #
  98. # First, export the replication ldif, edit the file(remove nstombstonecsn),
  99. # and reimport it.
  100. #
  101. log.info('Part 2: Exporting replication ldif...')
  102. # Get the the full path and name for our LDIF we will be exporting
  103. ldif_file = "/tmp/export.ldif"
  104. args = {EXPORT_REPL_INFO: True,
  105. TASK_WAIT: True}
  106. exportTask = Tasks(topology.standalone)
  107. try:
  108. exportTask.exportLDIF(DEFAULT_SUFFIX, None, ldif_file, args)
  109. except ValueError:
  110. assert False
  111. time.sleep(1)
  112. # open the ldif file, get the lines, then rewrite the file
  113. ldif = open(ldif_file, "r")
  114. lines = ldif.readlines()
  115. ldif.close()
  116. time.sleep(1)
  117. ldif = open(ldif_file, "w")
  118. for line in lines:
  119. if not line.lower().startswith('nstombstonecsn'):
  120. ldif.write(line)
  121. ldif.close()
  122. time.sleep(1)
  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. time.sleep(1)
  134. # Search for the tombstone again
  135. log.info('Search for tombstone entries...')
  136. try:
  137. entries = topology.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE,
  138. '(&(nsTombstoneCSN=*)(objectclass=nsTombstone))')
  139. if not entries:
  140. log.fatal('Search failed to fine the new tombstone(nsTombstoneCSN is probably missing).')
  141. assert False
  142. except ldap.LDAPError as e:
  143. log.fatal('Search failed: ' + e.message['desc'])
  144. assert False
  145. log.info('Part 2 - passed')
  146. #
  147. # Part 3 - test fixup task
  148. #
  149. log.info('Part 3: test the fixup task')
  150. # Run fixup task using the strip option. This removes nsTombstoneCSN
  151. # so we can test if the fixup task works.
  152. args = {TASK_WAIT: True,
  153. TASK_TOMB_STRIP: True}
  154. fixupTombTask = Tasks(topology.standalone)
  155. try:
  156. fixupTombTask.fixupTombstones(DEFAULT_BENAME, args)
  157. except:
  158. assert False
  159. time.sleep(1)
  160. # Search for tombstones with nsTombstoneCSN - better not find any
  161. log.info('Search for tombstone entries...')
  162. try:
  163. entries = topology.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE,
  164. '(&(nsTombstoneCSN=*)(objectclass=nsTombstone))')
  165. if entries:
  166. log.fatal('Search found tombstones with nsTombstoneCSN')
  167. assert False
  168. except ldap.LDAPError as e:
  169. log.fatal('Search failed: ' + e.message['desc'])
  170. assert False
  171. # Now run the fixup task
  172. args = {TASK_WAIT: True}
  173. fixupTombTask = Tasks(topology.standalone)
  174. try:
  175. fixupTombTask.fixupTombstones(DEFAULT_BENAME, args)
  176. except:
  177. assert False
  178. time.sleep(1)
  179. # Search for tombstones with nsTombstoneCSN - better find some
  180. log.info('Search for tombstone entries...')
  181. try:
  182. entries = topology.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE,
  183. '(&(nsTombstoneCSN=*)(objectclass=nsTombstone))')
  184. if not entries:
  185. log.fatal('Search did not find any fixed-up tombstones')
  186. assert False
  187. except ldap.LDAPError as e:
  188. log.fatal('Search failed: ' + e.message['desc'])
  189. assert False
  190. log.info('Part 3 - passed')
  191. #
  192. # Part 4 - Test tombstone purging
  193. #
  194. log.info('Part 4: test tombstone purging...')
  195. args = {REPLICA_PRECISE_PURGING: 'on',
  196. REPLICA_PURGE_DELAY: '5',
  197. REPLICA_PURGE_INTERVAL: '5'}
  198. try:
  199. topology.standalone.replica.setProperties(DEFAULT_SUFFIX, None, None, args)
  200. except:
  201. log.fatal('Failed to configure replica')
  202. assert False
  203. # Wait for the interval to pass
  204. log.info('Wait for tombstone purge interval to pass...')
  205. time.sleep(10)
  206. # Add an entry to trigger replication
  207. log.info('Perform an update to help trigger tombstone purging...')
  208. try:
  209. topology.standalone.add_s(Entry(('cn=test_entry,dc=example,dc=com', {
  210. 'objectclass': 'top person'.split(),
  211. 'sn': 'user',
  212. 'cn': 'entry1'})))
  213. except ldap.LDAPError as e:
  214. log.error('Failed to add entry: ' + e.message['desc'])
  215. assert False
  216. # Wait for the interval to pass again
  217. log.info('Wait for tombstone purge interval to pass again...')
  218. time.sleep(10)
  219. # search for tombstones, there should be none
  220. log.info('Search for tombstone entries...')
  221. try:
  222. entries = topology.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE,
  223. '(&(nsTombstoneCSN=*)(objectclass=nsTombstone))')
  224. if entries:
  225. log.fatal('Search unexpectedly found tombstones')
  226. assert False
  227. except ldap.LDAPError as e:
  228. log.fatal('Search failed: ' + e.message['desc'])
  229. assert False
  230. log.info('Part 4 - passed')
  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)