test_dynamic_plugins.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. # --- BEGIN COPYRIGHT BLOCK ---
  2. # Copyright (C) 2016 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. '''
  10. Created on Dec 09, 2014
  11. @author: mreynolds
  12. '''
  13. import logging
  14. import ldap.sasl
  15. import pytest
  16. from lib389.tasks import *
  17. import plugin_tests
  18. import stress_tests
  19. from lib389.topologies import topology_st
  20. log = logging.getLogger(__name__)
  21. def repl_fail(replica):
  22. """Remove replica instance, and assert failure"""
  23. replica.delete()
  24. assert False
  25. def test_dynamic_plugins(topology_st):
  26. """
  27. Test Dynamic Plugins - exercise each plugin and its main features, while
  28. changing the configuration without restarting the server.
  29. Need to test: functionality, stability, and stress. These tests need to run
  30. with replication disabled, and with replication setup with a
  31. second instance. Then test if replication is working, and we have
  32. same entries on each side.
  33. Functionality - Make sure that as configuration changes are made they take
  34. effect immediately. Cross plugin interaction (e.g. automember/memberOf)
  35. needs to tested, as well as plugin tasks. Need to test plugin
  36. config validation(dependencies, etc).
  37. Memory Corruption - Restart the plugins many times, and in different orders and test
  38. functionality, and stability. This will excerise the internal
  39. plugin linked lists, dse callbacks, and task handlers.
  40. Stress - Put the server under load that will trigger multiple plugins(MO, RI, DNA, etc)
  41. Restart various plugins while these operations are going on. Perform this test
  42. 5 times(stress_max_run).
  43. """
  44. REPLICA_PORT = 33334
  45. RUV_FILTER = '(&(nsuniqueid=ffffffff-ffffffff-ffffffff-ffffffff)(objectclass=nstombstone))'
  46. master_maxcsn = 0
  47. replica_maxcsn = 0
  48. msg = ' (no replication)'
  49. replication_run = False
  50. stress_max_runs = 5
  51. # First enable dynamic plugins
  52. try:
  53. topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-dynamic-plugins', 'on')])
  54. except ldap.LDAPError as e:
  55. ldap.fatal('Failed to enable dynamic plugin!' + e.message['desc'])
  56. assert False
  57. # Test that critical plugins can be updated even though the change might not be applied
  58. try:
  59. topology_st.standalone.modify_s(DN_LDBM, [(ldap.MOD_REPLACE, 'description', 'test')])
  60. except ldap.LDAPError as e:
  61. ldap.fatal('Failed to apply change to critical plugin' + e.message['desc'])
  62. assert False
  63. while 1:
  64. #
  65. # First run the tests with replication disabled, then rerun them with replication set up
  66. #
  67. ############################################################################
  68. # Test plugin functionality
  69. ############################################################################
  70. log.info('####################################################################')
  71. log.info('Testing Dynamic Plugins Functionality' + msg + '...')
  72. log.info('####################################################################\n')
  73. plugin_tests.test_all_plugins(topology_st.standalone)
  74. log.info('####################################################################')
  75. log.info('Successfully Tested Dynamic Plugins Functionality' + msg + '.')
  76. log.info('####################################################################\n')
  77. ############################################################################
  78. # Test the stability by exercising the internal lists, callabcks, and task handlers
  79. ############################################################################
  80. log.info('####################################################################')
  81. log.info('Testing Dynamic Plugins for Memory Corruption' + msg + '...')
  82. log.info('####################################################################\n')
  83. prev_plugin_test = None
  84. prev_prev_plugin_test = None
  85. for plugin_test in plugin_tests.func_tests:
  86. #
  87. # Restart the plugin several times (and prev plugins) - work that linked list
  88. #
  89. plugin_test(topology_st.standalone, "restart")
  90. if prev_prev_plugin_test:
  91. prev_prev_plugin_test(topology_st.standalone, "restart")
  92. plugin_test(topology_st.standalone, "restart")
  93. if prev_plugin_test:
  94. prev_plugin_test(topology_st.standalone, "restart")
  95. plugin_test(topology_st.standalone, "restart")
  96. # Now run the functional test
  97. plugin_test(topology_st.standalone)
  98. # Set the previous tests
  99. if prev_plugin_test:
  100. prev_prev_plugin_test = prev_plugin_test
  101. prev_plugin_test = plugin_test
  102. log.info('####################################################################')
  103. log.info('Successfully Tested Dynamic Plugins for Memory Corruption' + msg + '.')
  104. log.info('####################################################################\n')
  105. ############################################################################
  106. # Stress two plugins while restarting it, and while restarting other plugins.
  107. # The goal is to not crash, and have the plugins work after stressing them.
  108. ############################################################################
  109. log.info('####################################################################')
  110. log.info('Stressing Dynamic Plugins' + msg + '...')
  111. log.info('####################################################################\n')
  112. stress_tests.configureMO(topology_st.standalone)
  113. stress_tests.configureRI(topology_st.standalone)
  114. stress_count = 0
  115. while stress_count < stress_max_runs:
  116. log.info('####################################################################')
  117. log.info('Running stress test' + msg + '. Run (%d/%d)...' % (stress_count + 1, stress_max_runs))
  118. log.info('####################################################################\n')
  119. try:
  120. # Launch three new threads to add a bunch of users
  121. add_users = stress_tests.AddUsers(topology_st.standalone, 'employee', True)
  122. add_users.start()
  123. add_users2 = stress_tests.AddUsers(topology_st.standalone, 'entry', True)
  124. add_users2.start()
  125. add_users3 = stress_tests.AddUsers(topology_st.standalone, 'person', True)
  126. add_users3.start()
  127. time.sleep(1)
  128. # While we are adding users restart the MO plugin and an idle plugin
  129. topology_st.standalone.plugins.disable(name=PLUGIN_MEMBER_OF)
  130. topology_st.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
  131. time.sleep(1)
  132. topology_st.standalone.plugins.disable(name=PLUGIN_MEMBER_OF)
  133. time.sleep(1)
  134. topology_st.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
  135. topology_st.standalone.plugins.disable(name=PLUGIN_LINKED_ATTRS)
  136. topology_st.standalone.plugins.enable(name=PLUGIN_LINKED_ATTRS)
  137. time.sleep(1)
  138. topology_st.standalone.plugins.disable(name=PLUGIN_MEMBER_OF)
  139. topology_st.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
  140. time.sleep(2)
  141. topology_st.standalone.plugins.disable(name=PLUGIN_MEMBER_OF)
  142. time.sleep(1)
  143. topology_st.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
  144. topology_st.standalone.plugins.disable(name=PLUGIN_LINKED_ATTRS)
  145. topology_st.standalone.plugins.enable(name=PLUGIN_LINKED_ATTRS)
  146. topology_st.standalone.plugins.disable(name=PLUGIN_MEMBER_OF)
  147. time.sleep(1)
  148. topology_st.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
  149. topology_st.standalone.plugins.disable(name=PLUGIN_MEMBER_OF)
  150. topology_st.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
  151. # Wait for the 'adding' threads to complete
  152. add_users.join()
  153. add_users2.join()
  154. add_users3.join()
  155. # Now launch three threads to delete the users
  156. del_users = stress_tests.DelUsers(topology_st.standalone, 'employee')
  157. del_users.start()
  158. del_users2 = stress_tests.DelUsers(topology_st.standalone, 'entry')
  159. del_users2.start()
  160. del_users3 = stress_tests.DelUsers(topology_st.standalone, 'person')
  161. del_users3.start()
  162. time.sleep(1)
  163. # Restart both the MO, RI plugins during these deletes, and an idle plugin
  164. topology_st.standalone.plugins.disable(name=PLUGIN_REFER_INTEGRITY)
  165. topology_st.standalone.plugins.disable(name=PLUGIN_MEMBER_OF)
  166. topology_st.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
  167. topology_st.standalone.plugins.enable(name=PLUGIN_REFER_INTEGRITY)
  168. time.sleep(1)
  169. topology_st.standalone.plugins.disable(name=PLUGIN_REFER_INTEGRITY)
  170. time.sleep(1)
  171. topology_st.standalone.plugins.disable(name=PLUGIN_MEMBER_OF)
  172. time.sleep(1)
  173. topology_st.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
  174. time.sleep(1)
  175. topology_st.standalone.plugins.enable(name=PLUGIN_REFER_INTEGRITY)
  176. topology_st.standalone.plugins.disable(name=PLUGIN_LINKED_ATTRS)
  177. topology_st.standalone.plugins.enable(name=PLUGIN_LINKED_ATTRS)
  178. topology_st.standalone.plugins.disable(name=PLUGIN_REFER_INTEGRITY)
  179. topology_st.standalone.plugins.disable(name=PLUGIN_MEMBER_OF)
  180. topology_st.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
  181. topology_st.standalone.plugins.enable(name=PLUGIN_REFER_INTEGRITY)
  182. time.sleep(2)
  183. topology_st.standalone.plugins.disable(name=PLUGIN_REFER_INTEGRITY)
  184. time.sleep(1)
  185. topology_st.standalone.plugins.disable(name=PLUGIN_MEMBER_OF)
  186. time.sleep(1)
  187. topology_st.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
  188. time.sleep(1)
  189. topology_st.standalone.plugins.enable(name=PLUGIN_REFER_INTEGRITY)
  190. topology_st.standalone.plugins.disable(name=PLUGIN_LINKED_ATTRS)
  191. topology_st.standalone.plugins.enable(name=PLUGIN_LINKED_ATTRS)
  192. # Wait for the 'deleting' threads to complete
  193. del_users.join()
  194. del_users2.join()
  195. del_users3.join()
  196. # Now make sure both the MO and RI plugins still work correctly
  197. plugin_tests.func_tests[8](topology_st.standalone) # RI plugin
  198. plugin_tests.func_tests[5](topology_st.standalone) # MO plugin
  199. # Cleanup the stress tests
  200. stress_tests.cleanup(topology_st.standalone)
  201. except:
  202. log.info('Stress test failed!')
  203. if replication_run:
  204. repl_fail(replica_inst)
  205. stress_count += 1
  206. log.info('####################################################################')
  207. log.info('Successfully Stressed Dynamic Plugins' + msg +
  208. '. Completed (%d/%d)' % (stress_count, stress_max_runs))
  209. log.info('####################################################################\n')
  210. if replication_run:
  211. # We're done.
  212. break
  213. else:
  214. #
  215. # Enable replication and run everything one more time
  216. #
  217. log.info('Setting up replication, and rerunning the tests...\n')
  218. # Create replica instance
  219. replica_inst = DirSrv(verbose=False)
  220. args_instance[SER_HOST] = LOCALHOST
  221. args_instance[SER_PORT] = REPLICA_PORT
  222. args_instance[SER_SERVERID_PROP] = 'replica'
  223. args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
  224. args_replica_inst = args_instance.copy()
  225. replica_inst.allocate(args_replica_inst)
  226. replica_inst.create()
  227. replica_inst.open()
  228. try:
  229. topology_st.standalone.replica.enableReplication(suffix=DEFAULT_SUFFIX,
  230. role=REPLICAROLE_MASTER,
  231. replicaId=1)
  232. replica_inst.replica.enableReplication(suffix=DEFAULT_SUFFIX,
  233. role=REPLICAROLE_CONSUMER,
  234. replicaId=65535)
  235. properties = {RA_NAME: r'to_replica',
  236. RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
  237. RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
  238. RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
  239. RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
  240. repl_agreement = topology_st.standalone.agreement.create(suffix=DEFAULT_SUFFIX,
  241. host=LOCALHOST,
  242. port=REPLICA_PORT,
  243. properties=properties)
  244. if not repl_agreement:
  245. log.fatal("Fail to create a replica agreement")
  246. repl_fail(replica_inst)
  247. topology_st.standalone.agreement.init(DEFAULT_SUFFIX, LOCALHOST, REPLICA_PORT)
  248. topology_st.standalone.waitForReplInit(repl_agreement)
  249. except:
  250. log.info('Failed to setup replication!')
  251. repl_fail(replica_inst)
  252. replication_run = True
  253. msg = ' (replication enabled)'
  254. time.sleep(1)
  255. ############################################################################
  256. # Check replication, and data are in sync, and remove the instance
  257. ############################################################################
  258. log.info('Checking if replication is in sync...')
  259. try:
  260. # Grab master's max CSN
  261. entry = topology_st.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, RUV_FILTER)
  262. if not entry:
  263. log.error('Failed to find db tombstone entry from master')
  264. repl_fail(replica_inst)
  265. elements = entry[0].getValues('nsds50ruv')
  266. for ruv in elements:
  267. if 'replica 1' in ruv:
  268. parts = ruv.split()
  269. if len(parts) == 5:
  270. master_maxcsn = parts[4]
  271. break
  272. else:
  273. log.error('RUV is incomplete')
  274. repl_fail(replica_inst)
  275. if master_maxcsn == 0:
  276. log.error('Failed to find maxcsn on master')
  277. repl_fail(replica_inst)
  278. except ldap.LDAPError as e:
  279. log.fatal('Unable to search masterfor db tombstone: ' + e.message['desc'])
  280. repl_fail(replica_inst)
  281. # Loop on the consumer - waiting for it to catch up
  282. count = 0
  283. insync = False
  284. while count < 60:
  285. try:
  286. # Grab master's max CSN
  287. entry = replica_inst.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, RUV_FILTER)
  288. if not entry:
  289. log.error('Failed to find db tombstone entry on consumer')
  290. repl_fail(replica_inst)
  291. elements = entry[0].getValues('nsds50ruv')
  292. for ruv in elements:
  293. if 'replica 1' in ruv:
  294. parts = ruv.split()
  295. if len(parts) == 5:
  296. replica_maxcsn = parts[4]
  297. break
  298. if replica_maxcsn == 0:
  299. log.error('Failed to find maxcsn on consumer')
  300. repl_fail(replica_inst)
  301. except ldap.LDAPError as e:
  302. log.fatal('Unable to search for db tombstone on consumer: ' + e.message['desc'])
  303. repl_fail(replica_inst)
  304. if master_maxcsn == replica_maxcsn:
  305. insync = True
  306. log.info('Replication is in sync.\n')
  307. break
  308. count += 1
  309. time.sleep(1)
  310. # Report on replication status
  311. if not insync:
  312. log.error('Consumer not in sync with master!')
  313. repl_fail(replica_inst)
  314. #
  315. # Verify the databases are identical. There should not be any "user, entry, employee" entries
  316. #
  317. log.info('Checking if the data is the same between the replicas...')
  318. # Check the master
  319. try:
  320. entries = topology_st.standalone.search_s(DEFAULT_SUFFIX,
  321. ldap.SCOPE_SUBTREE,
  322. "(|(uid=person*)(uid=entry*)(uid=employee*))")
  323. if len(entries) > 0:
  324. log.error('Master database has incorrect data set!\n')
  325. repl_fail(replica_inst)
  326. except ldap.LDAPError as e:
  327. log.fatal('Unable to search db on master: ' + e.message['desc'])
  328. repl_fail(replica_inst)
  329. # Check the consumer
  330. try:
  331. entries = replica_inst.search_s(DEFAULT_SUFFIX,
  332. ldap.SCOPE_SUBTREE,
  333. "(|(uid=person*)(uid=entry*)(uid=employee*))")
  334. if len(entries) > 0:
  335. log.error('Consumer database in not consistent with master database')
  336. repl_fail(replica_inst)
  337. except ldap.LDAPError as e:
  338. log.fatal('Unable to search db on consumer: ' + e.message['desc'])
  339. repl_fail(replica_inst)
  340. log.info('Data is consistent across the replicas.\n')
  341. log.info('####################################################################')
  342. log.info('Replication consistency test passed')
  343. log.info('####################################################################\n')
  344. # Remove the replica instance
  345. replica_inst.delete()
  346. ############################################################################
  347. # We made it to the end!
  348. ############################################################################
  349. log.info('#####################################################')
  350. log.info('#####################################################')
  351. log.info("Dynamic Plugins Testsuite: Completed Successfully!")
  352. log.info('#####################################################')
  353. log.info('#####################################################\n')
  354. if __name__ == '__main__':
  355. # Run isolated
  356. # -s for DEBUG mode
  357. CURRENT_FILE = os.path.realpath(__file__)
  358. pytest.main("-s %s" % CURRENT_FILE)