ticket47819_test.py 9.1 KB

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