1
0

ticket47819_test.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  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. def fin():
  51. standalone.delete()
  52. request.addfinalizer(fin)
  53. # Here we have standalone instance up and running
  54. return TopologyStandalone(standalone)
  55. def test_ticket47819(topology):
  56. """
  57. Testing precise tombstone purging:
  58. [1] Make sure "nsTombstoneCSN" is added to new tombstones
  59. [2] Make sure an import of a replication ldif adds "nsTombstoneCSN"
  60. to old tombstones
  61. [4] Test fixup task
  62. [3] Make sure tombstone purging works
  63. """
  64. log.info('Testing Ticket 47819 - Test precise tombstone purging')
  65. #
  66. # Setup Replication
  67. #
  68. log.info('Setting up replication...')
  69. topology.standalone.replica.enableReplication(suffix=DEFAULT_SUFFIX, role=REPLICAROLE_MASTER,
  70. replicaId=REPLICAID_MASTER_1)
  71. #
  72. # Part 1 create a tombstone entry and make sure nsTombstoneCSN is added
  73. #
  74. log.info('Part 1: Add and then delete an entry to create a tombstone...')
  75. try:
  76. topology.standalone.add_s(Entry(('cn=entry1,dc=example,dc=com', {
  77. 'objectclass': 'top person'.split(),
  78. 'sn': 'user',
  79. 'cn': 'entry1'})))
  80. except ldap.LDAPError as e:
  81. log.error('Failed to add entry: ' + e.message['desc'])
  82. assert False
  83. try:
  84. topology.standalone.delete_s('cn=entry1,dc=example,dc=com')
  85. except ldap.LDAPError as e:
  86. log.error('Failed to delete entry: ' + e.message['desc'])
  87. assert False
  88. log.info('Search for tombstone entries...')
  89. try:
  90. entries = topology.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE,
  91. '(&(nsTombstoneCSN=*)(objectclass=nsTombstone))')
  92. if not entries:
  93. log.fatal('Search failed to the new tombstone(nsTombstoneCSN is probably missing).')
  94. assert False
  95. except ldap.LDAPError as e:
  96. log.fatal('Search failed: ' + e.message['desc'])
  97. assert False
  98. log.info('Part 1 - passed')
  99. #
  100. # Part 2 - import ldif with tombstones missing 'nsTombstoneCSN'
  101. #
  102. # First, export the replication ldif, edit the file(remove nstombstonecsn),
  103. # and reimport it.
  104. #
  105. log.info('Part 2: Exporting replication ldif...')
  106. # Get the the full path and name for our LDIF we will be exporting
  107. ldif_file = "/tmp/export.ldif"
  108. args = {EXPORT_REPL_INFO: True,
  109. TASK_WAIT: True}
  110. exportTask = Tasks(topology.standalone)
  111. try:
  112. exportTask.exportLDIF(DEFAULT_SUFFIX, None, ldif_file, args)
  113. except ValueError:
  114. assert False
  115. # open the ldif file, get the lines, then rewrite the file
  116. ldif = open(ldif_file, "r")
  117. lines = ldif.readlines()
  118. ldif.close()
  119. ldif = open(ldif_file, "w")
  120. for line in lines:
  121. if not line.lower().startswith('nstombstonecsn'):
  122. ldif.write(line)
  123. ldif.close()
  124. # import the new ldif file
  125. log.info('Import replication LDIF file...')
  126. importTask = Tasks(topology.standalone)
  127. args = {TASK_WAIT: True}
  128. try:
  129. importTask.importLDIF(DEFAULT_SUFFIX, None, ldif_file, args)
  130. os.remove(ldif_file)
  131. except ValueError:
  132. os.remove(ldif_file)
  133. assert False
  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. # Search for tombstones with nsTombstoneCSN - better not find any
  160. log.info('Search for tombstone entries...')
  161. try:
  162. entries = topology.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE,
  163. '(&(nsTombstoneCSN=*)(objectclass=nsTombstone))')
  164. if entries:
  165. log.fatal('Search found tombstones with nsTombstoneCSN')
  166. assert False
  167. except ldap.LDAPError as e:
  168. log.fatal('Search failed: ' + e.message['desc'])
  169. assert False
  170. # Now run the fixup task
  171. args = {TASK_WAIT: True}
  172. fixupTombTask = Tasks(topology.standalone)
  173. try:
  174. fixupTombTask.fixupTombstones(DEFAULT_BENAME, args)
  175. except:
  176. assert False
  177. time.sleep(1)
  178. # Search for tombstones with nsTombstoneCSN - better find some
  179. log.info('Search for tombstone entries...')
  180. try:
  181. entries = topology.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE,
  182. '(&(nsTombstoneCSN=*)(objectclass=nsTombstone))')
  183. if not entries:
  184. log.fatal('Search did not find any fixed-up tombstones')
  185. assert False
  186. except ldap.LDAPError as e:
  187. log.fatal('Search failed: ' + e.message['desc'])
  188. assert False
  189. log.info('Part 3 - passed')
  190. #
  191. # Part 4 - Test tombstone purging
  192. #
  193. log.info('Part 4: test tombstone purging...')
  194. args = {REPLICA_PRECISE_PURGING: 'on',
  195. REPLICA_PURGE_DELAY: '5',
  196. REPLICA_PURGE_INTERVAL: '5'}
  197. try:
  198. topology.standalone.replica.setProperties(DEFAULT_SUFFIX, None, None, args)
  199. except:
  200. log.fatal('Failed to configure replica')
  201. assert False
  202. # Wait for the interval to pass
  203. log.info('Wait for tombstone purge interval to pass...')
  204. time.sleep(6)
  205. # Add an entry to trigger replication
  206. log.info('Perform an update to help trigger tombstone purging...')
  207. try:
  208. topology.standalone.add_s(Entry(('cn=test_entry,dc=example,dc=com', {
  209. 'objectclass': 'top person'.split(),
  210. 'sn': 'user',
  211. 'cn': 'entry1'})))
  212. except ldap.LDAPError as e:
  213. log.error('Failed to add entry: ' + e.message['desc'])
  214. assert False
  215. # Wait for the interval to pass again
  216. log.info('Wait for tombstone purge interval to pass again...')
  217. time.sleep(10)
  218. # search for tombstones, there should be none
  219. log.info('Search for tombstone entries...')
  220. try:
  221. entries = topology.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE,
  222. '(&(nsTombstoneCSN=*)(objectclass=nsTombstone))')
  223. if entries:
  224. log.fatal('Search unexpectedly found tombstones')
  225. assert False
  226. except ldap.LDAPError as e:
  227. log.fatal('Search failed: ' + e.message['desc'])
  228. assert False
  229. log.info('Part 4 - passed')
  230. def test_ticket47819_final(topology):
  231. log.info('Testcase PASSED')
  232. def run_isolated():
  233. '''
  234. run_isolated is used to run these test cases independently of a test scheduler (xunit, py.test..)
  235. To run isolated without py.test, you need to
  236. - edit this file and comment '@pytest.fixture' line before 'topology' function.
  237. - set the installation prefix
  238. - run this program
  239. '''
  240. global installation_prefix
  241. installation_prefix = None
  242. topo = topology(True)
  243. test_ticket47819(topo)
  244. test_ticket47819_final(topo)
  245. if __name__ == '__main__':
  246. run_isolated()