replcheck_test.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. # --- BEGIN COPYRIGHT BLOCK ---
  2. # Copyright (C) 2018 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 pytest
  10. import subprocess
  11. from lib389.utils import *
  12. from lib389.replica import Replicas, Replica, ReplicationManager
  13. from lib389._constants import *
  14. from lib389.config import CertmapLegacy
  15. from lib389.idm.nscontainer import nsContainers
  16. from lib389.idm.user import UserAccounts, TEST_USER_PROPERTIES
  17. from lib389.idm.services import ServiceAccounts
  18. from lib389.topologies import topology_m2 as topo
  19. pytestmark = pytest.mark.tier1
  20. DEBUGGING = os.getenv("DEBUGGING", default=False)
  21. if DEBUGGING:
  22. logging.getLogger(__name__).setLevel(logging.DEBUG)
  23. else:
  24. logging.getLogger(__name__).setLevel(logging.INFO)
  25. log = logging.getLogger(__name__)
  26. def _create_container(inst, dn, name):
  27. """Creates container entry"""
  28. conts = nsContainers(inst, dn)
  29. cont = conts.create(properties={'cn': name})
  30. time.sleep(1)
  31. return cont
  32. def _delete_container(cont):
  33. """Deletes container entry"""
  34. cont.delete()
  35. time.sleep(1)
  36. @pytest.fixture(scope="module")
  37. def topo_tls_ldapi(topo):
  38. """Enable TLS on both masters and reconfigure both agreements
  39. to use TLS Client auth. Also, setup ldapi and export DB
  40. """
  41. m1 = topo.ms["master1"]
  42. m2 = topo.ms["master2"]
  43. # Create the certmap before we restart for enable_tls
  44. cm_m1 = CertmapLegacy(m1)
  45. cm_m2 = CertmapLegacy(m2)
  46. # We need to configure the same maps for both ....
  47. certmaps = cm_m1.list()
  48. certmaps['default']['DNComps'] = None
  49. certmaps['default']['CmapLdapAttr'] = 'nsCertSubjectDN'
  50. cm_m1.set(certmaps)
  51. cm_m2.set(certmaps)
  52. [i.enable_tls() for i in topo]
  53. # Create the replication dns
  54. services = ServiceAccounts(m1, DEFAULT_SUFFIX)
  55. repl_m1 = services.get('%s:%s' % (m1.host, m1.sslport))
  56. repl_m1.set('nsCertSubjectDN', m1.get_server_tls_subject())
  57. repl_m2 = services.get('%s:%s' % (m2.host, m2.sslport))
  58. repl_m2.set('nsCertSubjectDN', m2.get_server_tls_subject())
  59. # Check the replication is "done".
  60. repl = ReplicationManager(DEFAULT_SUFFIX)
  61. repl.wait_for_replication(m1, m2)
  62. # Now change the auth type
  63. replica_m1 = Replicas(m1).get(DEFAULT_SUFFIX)
  64. agmt_m1 = replica_m1.get_agreements().list()[0]
  65. agmt_m1.replace_many(
  66. ('nsDS5ReplicaBindMethod', 'SSLCLIENTAUTH'),
  67. ('nsDS5ReplicaTransportInfo', 'SSL'),
  68. ('nsDS5ReplicaPort', '%s' % m2.sslport),
  69. )
  70. agmt_m1.remove_all('nsDS5ReplicaBindDN')
  71. replica_m2 = Replicas(m2).get(DEFAULT_SUFFIX)
  72. agmt_m2 = replica_m2.get_agreements().list()[0]
  73. agmt_m2.replace_many(
  74. ('nsDS5ReplicaBindMethod', 'SSLCLIENTAUTH'),
  75. ('nsDS5ReplicaTransportInfo', 'SSL'),
  76. ('nsDS5ReplicaPort', '%s' % m1.sslport),
  77. )
  78. agmt_m2.remove_all('nsDS5ReplicaBindDN')
  79. log.info("Export LDAPTLS_CACERTDIR env variable for ds-replcheck")
  80. os.environ["LDAPTLS_CACERTDIR"] = m1.get_ssca_dir()
  81. for inst in topo:
  82. inst.config.set('nsslapd-ldapilisten', 'on')
  83. inst.config.set('nsslapd-ldapifilepath', '/var/run/slapd-{}.socket'.format(inst.serverid))
  84. inst.restart()
  85. repl.test_replication(m1, m2)
  86. repl.test_replication(m2, m1)
  87. return topo
  88. def replcheck_cmd_list(topo_tls_ldapi):
  89. """Check ds-replcheck tool through ldap, ldaps, ldap with StartTLS, ldapi
  90. and compare exported ldif files
  91. """
  92. m1 = topo_tls_ldapi.ms["master1"]
  93. m2 = topo_tls_ldapi.ms["master2"]
  94. for inst in topo_tls_ldapi:
  95. inst.stop()
  96. inst.db2ldif(bename=DEFAULT_BENAME, suffixes=[DEFAULT_SUFFIX], excludeSuffixes=[],
  97. encrypt=False, repl_data=True, outputfile='/tmp/export_{}.ldif'.format(inst.serverid))
  98. inst.start()
  99. ds_replcheck_path = os.path.join(m1.ds_paths.bin_dir, 'ds-replcheck')
  100. if ds_is_newer("1.4.1.2"):
  101. replcheck_cmd = [[ds_replcheck_path, 'online', '-b', DEFAULT_SUFFIX, '-D', DN_DM, '-w', PW_DM, '-l', '1',
  102. '-m', 'ldap://{}:{}'.format(m1.host, m1.port), '--conflicts',
  103. '-r', 'ldap://{}:{}'.format(m2.host, m2.port)],
  104. [ds_replcheck_path, 'online', '-b', DEFAULT_SUFFIX, '-D', DN_DM, '-w', PW_DM, '-l', '1',
  105. '-m', 'ldaps://{}:{}'.format(m1.host, m1.sslport), '--conflicts',
  106. '-r', 'ldaps://{}:{}'.format(m2.host, m2.sslport)],
  107. [ds_replcheck_path, 'online', '-b', DEFAULT_SUFFIX, '-D', DN_DM, '-w', PW_DM, '-l', '1',
  108. '-m', 'ldap://{}:{}'.format(m1.host, m1.port), '-Z', m1.get_ssca_dir(),
  109. '-r', 'ldap://{}:{}'.format(m2.host, m2.port), '--conflicts'],
  110. [ds_replcheck_path, 'online', '-b', DEFAULT_SUFFIX, '-D', DN_DM, '-w', PW_DM, '-l', '1',
  111. '-m', 'ldapi://%2fvar%2frun%2fslapd-{}.socket'.format(m1.serverid), '--conflict',
  112. '-r', 'ldapi://%2fvar%2frun%2fslapd-{}.socket'.format(m2.serverid)],
  113. [ds_replcheck_path, 'offline', '-b', DEFAULT_SUFFIX, '--conflicts', '--rid', '1',
  114. '-m', '/tmp/export_{}.ldif'.format(m1.serverid),
  115. '-r', '/tmp/export_{}.ldif'.format(m2.serverid)]]
  116. else:
  117. replcheck_cmd = [[ds_replcheck_path, '-b', DEFAULT_SUFFIX, '-D', DN_DM, '-w', PW_DM, '-l', '1',
  118. '-m', 'ldap://{}:{}'.format(m1.host, m1.port), '--conflicts',
  119. '-r', 'ldap://{}:{}'.format(m2.host, m2.port)],
  120. [ds_replcheck_path, '-b', DEFAULT_SUFFIX, '-D', DN_DM, '-w', PW_DM, '-l', '1',
  121. '-m', 'ldaps://{}:{}'.format(m1.host, m1.sslport), '--conflicts',
  122. '-r', 'ldaps://{}:{}'.format(m2.host, m2.sslport)],
  123. [ds_replcheck_path, '-b', DEFAULT_SUFFIX, '-D', DN_DM, '-w', PW_DM, '-l', '1',
  124. '-m', 'ldap://{}:{}'.format(m1.host, m1.port), '-Z', m1.get_ssca_dir(),
  125. '-r', 'ldap://{}:{}'.format(m2.host, m2.port), '--conflicts'],
  126. [ds_replcheck_path, '-b', DEFAULT_SUFFIX, '-D', DN_DM, '-w', PW_DM, '-l', '1',
  127. '-m', 'ldapi://%2fvar%2frun%2fslapd-{}.socket'.format(m1.serverid), '--conflict',
  128. '-r', 'ldapi://%2fvar%2frun%2fslapd-{}.socket'.format(m2.serverid)],
  129. [ds_replcheck_path, '-b', DEFAULT_SUFFIX, '-D', DN_DM, '-w', PW_DM, '--conflicts',
  130. '-M', '/tmp/export_{}.ldif'.format(m1.serverid),
  131. '-R', '/tmp/export_{}.ldif'.format(m2.serverid)]]
  132. return replcheck_cmd
  133. @pytest.mark.skipif(ds_is_older("1.4.1.2"), reason="Not implemented")
  134. def test_state(topo_tls_ldapi):
  135. """Check "state" report
  136. :id: 1cc6b28b-8a42-45fb-ab50-9552db0ac178
  137. :setup: Two master replication
  138. :steps:
  139. 1. Get the replication state value
  140. 2. The state value is as expected
  141. :expectedresults:
  142. 1. It should be successful
  143. 2. It should be successful
  144. """
  145. m1 = topo_tls_ldapi.ms["master1"]
  146. m2 = topo_tls_ldapi.ms["master2"]
  147. ds_replcheck_path = os.path.join(m1.ds_paths.bin_dir, 'ds-replcheck')
  148. tool_cmd = [ds_replcheck_path, 'state', '-b', DEFAULT_SUFFIX, '-D', DN_DM, '-w', PW_DM,
  149. '-m', 'ldaps://{}:{}'.format(m1.host, m1.sslport),
  150. '-r', 'ldaps://{}:{}'.format(m2.host, m2.sslport)]
  151. result = subprocess.check_output(tool_cmd, encoding='utf-8')
  152. assert (result.rstrip() == "Replication State: Master and Replica are in perfect synchronization")
  153. def test_check_ruv(topo_tls_ldapi):
  154. """Check that the report has RUV
  155. :id: 1cc6b28b-8a42-45fb-ab50-9552db0ac179
  156. :setup: Two master replication
  157. :steps:
  158. 1. Get RUV from master and replica
  159. 2. Generate the report
  160. 3. Check that the RUV is mentioned in the report
  161. :expectedresults:
  162. 1. It should be successful
  163. 2. It should be successful
  164. 3. The RUV should be mentioned in the report
  165. """
  166. m1 = topo_tls_ldapi.ms["master1"]
  167. replicas_m1 = Replica(m1, DEFAULT_SUFFIX)
  168. ruv_entries = replicas_m1.get_attr_vals_utf8('nsds50ruv')
  169. for tool_cmd in replcheck_cmd_list(topo_tls_ldapi):
  170. result = subprocess.check_output(tool_cmd, encoding='utf-8')
  171. assert all([ruv_entry in result for ruv_entry in ruv_entries])
  172. def test_missing_entries(topo_tls_ldapi):
  173. """Check that the report has missing entries
  174. :id: f91b6798-6e6e-420a-ad2f-3222bb908b7d
  175. :setup: Two master replication
  176. :steps:
  177. 1. Pause replication between master and replica
  178. 2. Add two entries to master and two entries to replica
  179. 3. Generate the report
  180. 4. Check that the entries DN are mentioned in the report
  181. :expectedresults:
  182. 1. It should be successful
  183. 2. It should be successful
  184. 3. It should be successful
  185. 4. The entries DN should be mentioned in the report
  186. """
  187. m1 = topo_tls_ldapi.ms["master1"]
  188. m2 = topo_tls_ldapi.ms["master2"]
  189. try:
  190. topo_tls_ldapi.pause_all_replicas()
  191. users_m1 = UserAccounts(m1, DEFAULT_SUFFIX)
  192. user0 = users_m1.create_test_user(1000)
  193. user1 = users_m1.create_test_user(1001)
  194. users_m2 = UserAccounts(m2, DEFAULT_SUFFIX)
  195. user2 = users_m2.create_test_user(1002)
  196. user3 = users_m2.create_test_user(1003)
  197. for tool_cmd in replcheck_cmd_list(topo_tls_ldapi):
  198. result = subprocess.check_output(tool_cmd, encoding='utf-8').lower()
  199. assert user0.dn.lower() in result
  200. assert user1.dn.lower() in result
  201. finally:
  202. user0.delete()
  203. user1.delete()
  204. user2.delete()
  205. user3.delete()
  206. topo_tls_ldapi.resume_all_replicas()
  207. def test_tombstones(topo_tls_ldapi):
  208. """Check that the report mentions right number of tombstones
  209. :id: bd27de78-0046-431c-8240-a93052df1cdc
  210. :setup: Two master replication
  211. :steps:
  212. 1. Add an entry to master and wait for replication
  213. 2. Pause replication between master and replica
  214. 3. Delete the entry from master
  215. 4. Generate the report
  216. 5. Check that we have different number of tombstones in the report
  217. :expectedresults:
  218. 1. It should be successful
  219. 2. It should be successful
  220. 3. It should be successful
  221. 4. It should be successful
  222. 5. It should be successful
  223. """
  224. m1 = topo_tls_ldapi.ms["master1"]
  225. try:
  226. users_m1 = UserAccounts(m1, DEFAULT_SUFFIX)
  227. user_m1 = users_m1.create(properties=TEST_USER_PROPERTIES)
  228. time.sleep(1)
  229. topo_tls_ldapi.pause_all_replicas()
  230. user_m1.delete()
  231. time.sleep(2)
  232. for tool_cmd in replcheck_cmd_list(topo_tls_ldapi):
  233. result = subprocess.check_output(tool_cmd, encoding='utf-8').lower()
  234. log.debug(result)
  235. finally:
  236. topo_tls_ldapi.resume_all_replicas()
  237. def test_conflict_entries(topo_tls_ldapi):
  238. """Check that the report has conflict entries
  239. :id: 4eda0c5d-0824-4cfd-896e-845faf49ddaf
  240. :setup: Two master replication
  241. :steps:
  242. 1. Pause replication between master and replica
  243. 2. Add two entries to master and two entries to replica
  244. 3. Delete first entry from master
  245. 4. Add a child to the first entry
  246. 5. Resume replication between master and replica
  247. 6. Generate the report
  248. 7. Check that the entries DN are mentioned in the report
  249. :expectedresults:
  250. 1. It should be successful
  251. 2. It should be successful
  252. 3. It should be successful
  253. 4. It should be successful
  254. 5. It should be successful
  255. 6. It should be successful
  256. 7. The entries DN should be mentioned in the report
  257. """
  258. m1 = topo_tls_ldapi.ms["master1"]
  259. m2 = topo_tls_ldapi.ms["master2"]
  260. topo_tls_ldapi.pause_all_replicas()
  261. _create_container(m1, DEFAULT_SUFFIX, 'conflict_parent0')
  262. _create_container(m2, DEFAULT_SUFFIX, 'conflict_parent0')
  263. cont_p_m1 = _create_container(m1, DEFAULT_SUFFIX, 'conflict_parent1')
  264. cont_p_m2 = _create_container(m2, DEFAULT_SUFFIX, 'conflict_parent1')
  265. _delete_container(cont_p_m1)
  266. _create_container(m2, cont_p_m2.dn, 'conflict_child0')
  267. topo_tls_ldapi.resume_all_replicas()
  268. time.sleep(5)
  269. for tool_cmd in replcheck_cmd_list(topo_tls_ldapi):
  270. result = subprocess.check_output(tool_cmd, encoding='utf-8')
  271. assert 'conflict_parent1' in result
  272. def test_inconsistencies(topo_tls_ldapi):
  273. """Check that the report mentions inconsistencies with attributes
  274. :id: c8fe3e84-b346-4969-8f5d-3462b643a1d2
  275. :setup: Two master replication
  276. :steps:
  277. 1. Add an entry to master and wait for replication
  278. 2. Pause replication between master and replica
  279. 3. Set different description attr values to master and replica
  280. 4. Add telephoneNumber attribute to master and not to replica
  281. 5. Generate the report
  282. 6. Check that attribute values are mentioned in the report
  283. 7. Generate the report with -i option to ignore some attributes
  284. 8. Check that attribute values are mentioned in the report
  285. :expectedresults:
  286. 1. It should be successful
  287. 2. It should be successful
  288. 3. It should be successful
  289. 4. It should be successful
  290. 5. It should be successful
  291. 6. The attribute values should be mentioned in the report
  292. 7. It should be successful
  293. 8. The attribute values should not be mentioned in the report
  294. """
  295. m1 = topo_tls_ldapi.ms["master1"]
  296. m2 = topo_tls_ldapi.ms["master2"]
  297. attr_m1 = "m1_inconsistency"
  298. attr_m2 = "m2_inconsistency"
  299. attr_first = "first ordered valued"
  300. attr_second = "second ordered valued"
  301. attr_m1_only = "123123123"
  302. try:
  303. users_m1 = UserAccounts(m1, DEFAULT_SUFFIX)
  304. users_m2 = UserAccounts(m2, DEFAULT_SUFFIX)
  305. user_m1 = users_m1.create(properties=TEST_USER_PROPERTIES)
  306. time.sleep(1)
  307. user_m2 = users_m2.get(user_m1.rdn)
  308. topo_tls_ldapi.pause_all_replicas()
  309. user_m1.set("description", attr_m1)
  310. user_m2.set("description", attr_m2)
  311. user_m1.set("telephonenumber", attr_m1_only)
  312. # Add the same multi-valued attrs, but out of order
  313. user_m1.set("cn", [attr_first, attr_second])
  314. user_m2.set("cn", [attr_second, attr_first])
  315. time.sleep(2)
  316. for tool_cmd in replcheck_cmd_list(topo_tls_ldapi):
  317. result = subprocess.check_output(tool_cmd, encoding='utf-8').lower()
  318. assert attr_m1 in result
  319. assert attr_m2 in result
  320. assert attr_m1_only in result
  321. if ds_is_newer("1.3.9.1", "1.4.1.2"):
  322. assert attr_first not in result
  323. assert attr_second not in result
  324. # Ignore some attributes and check the output
  325. tool_cmd.extend(['-i', '{},{}'.format('description', 'telephonenumber')])
  326. result = subprocess.check_output(tool_cmd, encoding='utf-8').lower()
  327. assert attr_m1 not in result
  328. assert attr_m2 not in result
  329. assert attr_m1_only not in result
  330. if ds_is_newer("1.3.9.1", "1.4.1.2"):
  331. assert attr_first not in result
  332. assert attr_second not in result
  333. finally:
  334. topo_tls_ldapi.resume_all_replicas()
  335. user_m1.delete()
  336. def test_suffix_exists(topo_tls_ldapi):
  337. """Check if wrong suffix is provided, server is giving Error: Failed
  338. to validate suffix.
  339. :id: ce75debc-c07f-4e72-8787-8f99cbfaf1e2
  340. :setup: Two master replication
  341. :steps:
  342. 1. Run ds-replcheck with wrong suffix (Non Existing)
  343. :expectedresults:
  344. 1. It should be unsuccessful
  345. """
  346. m1 = topo_tls_ldapi.ms["master1"]
  347. m2 = topo_tls_ldapi.ms["master2"]
  348. ds_replcheck_path = os.path.join(m1.ds_paths.bin_dir, 'ds-replcheck')
  349. if ds_is_newer("1.4.1.2"):
  350. tool_cmd = [ds_replcheck_path, 'online', '-b', 'dc=test,dc=com', '-D', DN_DM, '-w', PW_DM,
  351. '-m', 'ldaps://{}:{}'.format(m1.host, m1.sslport),
  352. '-r', 'ldaps://{}:{}'.format(m2.host, m2.sslport)]
  353. else:
  354. tool_cmd = [ds_replcheck_path, '-b', 'dc=test,dc=com', '-D', DN_DM, '-w', PW_DM,
  355. '-m', 'ldaps://{}:{}'.format(m1.host, m1.sslport),
  356. '-r', 'ldaps://{}:{}'.format(m2.host, m2.sslport)]
  357. result1 = subprocess.Popen(tool_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf-8')
  358. result = result1.communicate()
  359. assert "Failed to validate suffix" in result[0]
  360. def test_check_missing_tombstones(topo_tls_ldapi):
  361. """Check missing tombstone entries is not reported.
  362. :id: 93067a5a-416e-4243-9418-c4dfcf42e093
  363. :setup: Two master replication
  364. :steps:
  365. 1. Pause replication between master and replica
  366. 2. Add and delete an entry on the master
  367. 3. Run ds-replcheck
  368. 4. Verify there are NO complaints about missing entries/tombstones
  369. :expectedresults:
  370. 1. It should be successful
  371. 2. It should be successful
  372. 3. It should be successful
  373. 4. It should be successful
  374. """
  375. m1 = topo_tls_ldapi.ms["master1"]
  376. m2 = topo_tls_ldapi.ms["master2"]
  377. try:
  378. topo_tls_ldapi.pause_all_replicas()
  379. users_m1 = UserAccounts(m1, DEFAULT_SUFFIX)
  380. user0 = users_m1.create_test_user(1000)
  381. user0.delete()
  382. for tool_cmd in replcheck_cmd_list(topo_tls_ldapi):
  383. result = subprocess.check_output(tool_cmd, encoding='utf-8').lower()
  384. assert "entries missing on replica" not in result
  385. finally:
  386. topo_tls_ldapi.resume_all_replicas()
  387. if __name__ == '__main__':
  388. # Run isolated
  389. # -s for DEBUG mode
  390. CURRENT_FILE = os.path.realpath(__file__)