test_dynamic_plugins.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. '''
  2. Created on Dec 09, 2014
  3. @author: mreynolds
  4. '''
  5. import os
  6. import sys
  7. import time
  8. import ldap
  9. import ldap.sasl
  10. import logging
  11. import socket
  12. import pytest
  13. import plugin_tests
  14. import stress_tests
  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. from constants import *
  21. log = logging.getLogger(__name__)
  22. installation_prefix = None
  23. class TopologyStandalone(object):
  24. def __init__(self, standalone):
  25. standalone.open()
  26. self.standalone = standalone
  27. def repl_fail(replica):
  28. # remove replica instance, and assert failure
  29. replica.delete()
  30. assert False
  31. @pytest.fixture(scope="module")
  32. def topology(request):
  33. '''
  34. This fixture is used to standalone topology for the 'module'.
  35. At the beginning, It may exists a standalone instance.
  36. It may also exists a backup for the standalone instance.
  37. Principle:
  38. If standalone instance exists:
  39. restart it
  40. If backup of standalone exists:
  41. create/rebind to standalone
  42. restore standalone instance from backup
  43. else:
  44. Cleanup everything
  45. remove instance
  46. remove backup
  47. Create instance
  48. Create backup
  49. '''
  50. global installation_prefix
  51. if installation_prefix:
  52. args_instance[SER_DEPLOYED_DIR] = installation_prefix
  53. standalone = DirSrv(verbose=False)
  54. # Args for the standalone instance
  55. args_instance[SER_HOST] = HOST_STANDALONE
  56. args_instance[SER_PORT] = PORT_STANDALONE
  57. args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE
  58. args_standalone = args_instance.copy()
  59. standalone.allocate(args_standalone)
  60. # Get the status of the backups
  61. backup_standalone = standalone.checkBackupFS()
  62. # Get the status of the instance and restart it if it exists
  63. instance_standalone = standalone.exists()
  64. if instance_standalone:
  65. # assuming the instance is already stopped, just wait 5 sec max
  66. standalone.stop(timeout=5)
  67. standalone.start(timeout=10)
  68. if backup_standalone:
  69. # The backup exist, assuming it is correct
  70. # we just re-init the instance with it
  71. if not instance_standalone:
  72. standalone.create()
  73. # Used to retrieve configuration information (dbdir, confdir...)
  74. standalone.open()
  75. # restore standalone instance from backup
  76. standalone.stop(timeout=10)
  77. standalone.restoreFS(backup_standalone)
  78. standalone.start(timeout=10)
  79. else:
  80. # We should be here only in two conditions
  81. # - This is the first time a test involve standalone instance
  82. # - Something weird happened (instance/backup destroyed)
  83. # so we discard everything and recreate all
  84. # Remove the backup. So even if we have a specific backup file
  85. # (e.g backup_standalone) we clear backup that an instance may have created
  86. if backup_standalone:
  87. standalone.clearBackupFS()
  88. # Remove the instance
  89. if instance_standalone:
  90. standalone.delete()
  91. # Create the instance
  92. standalone.create()
  93. # Used to retrieve configuration information (dbdir, confdir...)
  94. standalone.open()
  95. # Time to create the backups
  96. standalone.stop(timeout=10)
  97. standalone.backupfile = standalone.backupFS()
  98. standalone.start(timeout=10)
  99. #
  100. # Here we have standalone instance up and running
  101. # Either coming from a backup recovery
  102. # or from a fresh (re)init
  103. # Time to return the topology
  104. return TopologyStandalone(standalone)
  105. def test_dynamic_plugins(topology):
  106. """
  107. Test Dynamic Plugins - exercise each plugin and its main features, while
  108. changing the configuration without restarting the server.
  109. Need to test: functionality, stability, and stress. These tests need to run
  110. with replication disabled, and with replication setup with a
  111. second instance. Then test if replication is working, and we have
  112. same entries on each side.
  113. Functionality - Make sure that as configuration changes are made they take
  114. effect immediately. Cross plugin interaction (e.g. automember/memberOf)
  115. needs to tested, as well as plugin tasks. Need to test plugin
  116. config validation(dependencies, etc).
  117. Memory Corruption - Restart the plugins many times, and in different orders and test
  118. functionality, and stability. This will excerise the internal
  119. plugin linked lists, dse callbacks, and task handlers.
  120. Stress - Put the server under load that will trigger multiple plugins(MO, RI, DNA, etc)
  121. Restart various plugins while these operations are going on. Perform this test
  122. 5 times(stress_max_run).
  123. """
  124. REPLICA_PORT = 33334
  125. RUV_FILTER = '(&(nsuniqueid=ffffffff-ffffffff-ffffffff-ffffffff)(objectclass=nstombstone))'
  126. master_maxcsn = 0
  127. replica_maxcsn = 0
  128. msg = ' (no replication)'
  129. replication_run = False
  130. stress_max_runs = 5
  131. # First enable dynamic plugins
  132. try:
  133. topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-dynamic-plugins', 'on')])
  134. except ldap.LDAPError, e:
  135. ldap.error('Failed to enable dynamic plugin!' + e.message['desc'])
  136. assert False
  137. while 1:
  138. #
  139. # First run the tests with replication disabled, then rerun them with replication set up
  140. #
  141. ############################################################################
  142. # Test plugin functionality
  143. ############################################################################
  144. log.info('####################################################################')
  145. log.info('Testing Dynamic Plugins Functionality' + msg + '...')
  146. log.info('####################################################################\n')
  147. plugin_tests.test_all_plugins(topology.standalone)
  148. log.info('####################################################################')
  149. log.info('Successfully Tested Dynamic Plugins Functionality' + msg + '.')
  150. log.info('####################################################################\n')
  151. ############################################################################
  152. # Test the stability by exercising the internal lists, callabcks, and task handlers
  153. ############################################################################
  154. log.info('####################################################################')
  155. log.info('Testing Dynamic Plugins for Memory Corruption' + msg + '...')
  156. log.info('####################################################################\n')
  157. prev_plugin_test = None
  158. prev_prev_plugin_test = None
  159. for plugin_test in plugin_tests.func_tests:
  160. #
  161. # Restart the plugin several times (and prev plugins) - work that linked list
  162. #
  163. plugin_test(topology.standalone, "restart")
  164. if prev_prev_plugin_test:
  165. prev_prev_plugin_test(topology.standalone, "restart")
  166. plugin_test(topology.standalone, "restart")
  167. if prev_plugin_test:
  168. prev_plugin_test(topology.standalone, "restart")
  169. plugin_test(topology.standalone, "restart")
  170. # Now run the functional test
  171. plugin_test(topology.standalone)
  172. # Set the previous tests
  173. if prev_plugin_test:
  174. prev_prev_plugin_test = prev_plugin_test
  175. prev_plugin_test = plugin_test
  176. log.info('####################################################################')
  177. log.info('Successfully Tested Dynamic Plugins for Memory Corruption' + msg + '.')
  178. log.info('####################################################################\n')
  179. ############################################################################
  180. # Stress two plugins while restarting it, and while restarting other plugins.
  181. # The goal is to not crash, and have the plugins work after stressing them.
  182. ############################################################################
  183. log.info('####################################################################')
  184. log.info('Stressing Dynamic Plugins' + msg + '...')
  185. log.info('####################################################################\n')
  186. stress_tests.configureMO(topology.standalone)
  187. stress_tests.configureRI(topology.standalone)
  188. stress_count = 0
  189. while stress_count < stress_max_runs:
  190. log.info('####################################################################')
  191. log.info('Running stress test' + msg + '. Run (%d/%d)...' % (stress_count + 1, stress_max_runs))
  192. log.info('####################################################################\n')
  193. try:
  194. # Launch three new threads to add a bunch of users
  195. add_users = stress_tests.AddUsers(topology.standalone, 'employee', True)
  196. add_users.start()
  197. add_users2 = stress_tests.AddUsers(topology.standalone, 'entry', True)
  198. add_users2.start()
  199. add_users3 = stress_tests.AddUsers(topology.standalone, 'person', True)
  200. add_users3.start()
  201. time.sleep(1)
  202. # While we are adding users restart the MO plugin and an idle plugin
  203. topology.standalone.plugins.disable(name=PLUGIN_MEMBER_OF)
  204. topology.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
  205. time.sleep(1)
  206. topology.standalone.plugins.disable(name=PLUGIN_MEMBER_OF)
  207. time.sleep(1)
  208. topology.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
  209. topology.standalone.plugins.disable(name=PLUGIN_LINKED_ATTRS)
  210. topology.standalone.plugins.enable(name=PLUGIN_LINKED_ATTRS)
  211. time.sleep(1)
  212. topology.standalone.plugins.disable(name=PLUGIN_MEMBER_OF)
  213. topology.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
  214. time.sleep(2)
  215. topology.standalone.plugins.disable(name=PLUGIN_MEMBER_OF)
  216. time.sleep(1)
  217. topology.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
  218. topology.standalone.plugins.disable(name=PLUGIN_LINKED_ATTRS)
  219. topology.standalone.plugins.enable(name=PLUGIN_LINKED_ATTRS)
  220. topology.standalone.plugins.disable(name=PLUGIN_MEMBER_OF)
  221. time.sleep(1)
  222. topology.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
  223. topology.standalone.plugins.disable(name=PLUGIN_MEMBER_OF)
  224. topology.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
  225. # Wait for the 'adding' threads to complete
  226. add_users.join()
  227. add_users2.join()
  228. add_users3.join()
  229. # Now launch three threads to delete the users
  230. del_users = stress_tests.DelUsers(topology.standalone, 'employee')
  231. del_users.start()
  232. del_users2 = stress_tests.DelUsers(topology.standalone, 'entry')
  233. del_users2.start()
  234. del_users3 = stress_tests.DelUsers(topology.standalone, 'person')
  235. del_users3.start()
  236. time.sleep(1)
  237. # Restart both the MO, RI plugins during these deletes, and an idle plugin
  238. topology.standalone.plugins.disable(name=PLUGIN_REFER_INTEGRITY)
  239. topology.standalone.plugins.disable(name=PLUGIN_MEMBER_OF)
  240. topology.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
  241. topology.standalone.plugins.enable(name=PLUGIN_REFER_INTEGRITY)
  242. time.sleep(1)
  243. topology.standalone.plugins.disable(name=PLUGIN_REFER_INTEGRITY)
  244. time.sleep(1)
  245. topology.standalone.plugins.disable(name=PLUGIN_MEMBER_OF)
  246. time.sleep(1)
  247. topology.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
  248. time.sleep(1)
  249. topology.standalone.plugins.enable(name=PLUGIN_REFER_INTEGRITY)
  250. topology.standalone.plugins.disable(name=PLUGIN_LINKED_ATTRS)
  251. topology.standalone.plugins.enable(name=PLUGIN_LINKED_ATTRS)
  252. topology.standalone.plugins.disable(name=PLUGIN_REFER_INTEGRITY)
  253. topology.standalone.plugins.disable(name=PLUGIN_MEMBER_OF)
  254. topology.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
  255. topology.standalone.plugins.enable(name=PLUGIN_REFER_INTEGRITY)
  256. time.sleep(2)
  257. topology.standalone.plugins.disable(name=PLUGIN_REFER_INTEGRITY)
  258. time.sleep(1)
  259. topology.standalone.plugins.disable(name=PLUGIN_MEMBER_OF)
  260. time.sleep(1)
  261. topology.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
  262. time.sleep(1)
  263. topology.standalone.plugins.enable(name=PLUGIN_REFER_INTEGRITY)
  264. topology.standalone.plugins.disable(name=PLUGIN_LINKED_ATTRS)
  265. topology.standalone.plugins.enable(name=PLUGIN_LINKED_ATTRS)
  266. # Wait for the 'deleting' threads to complete
  267. del_users.join()
  268. del_users2.join()
  269. del_users3.join()
  270. # Now make sure both the MO and RI plugins still work correctly
  271. plugin_tests.func_tests[8](topology.standalone) # RI plugin
  272. plugin_tests.func_tests[5](topology.standalone) # MO plugin
  273. # Cleanup the stress tests
  274. stress_tests.cleanup(topology.standalone)
  275. except:
  276. log.info('Stress test failed!')
  277. repl_fail(replica_inst)
  278. stress_count += 1
  279. log.info('####################################################################')
  280. log.info('Successfully Stressed Dynamic Plugins' + msg +
  281. '. Completed (%d/%d)' % (stress_count, stress_max_runs))
  282. log.info('####################################################################\n')
  283. if replication_run:
  284. # We're done.
  285. break
  286. else:
  287. #
  288. # Enable replication and run everything one more time
  289. #
  290. log.info('Setting up replication, and rerunning the tests...\n')
  291. # Create replica instance
  292. replica_inst = DirSrv(verbose=False)
  293. args_instance[SER_HOST] = LOCALHOST
  294. args_instance[SER_PORT] = REPLICA_PORT
  295. args_instance[SER_SERVERID_PROP] = 'replica'
  296. args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
  297. args_replica_inst = args_instance.copy()
  298. replica_inst.allocate(args_replica_inst)
  299. replica_inst.create()
  300. replica_inst.open()
  301. try:
  302. topology.standalone.replica.enableReplication(suffix=DEFAULT_SUFFIX,
  303. role=REPLICAROLE_MASTER,
  304. replicaId=1)
  305. replica_inst.replica.enableReplication(suffix=DEFAULT_SUFFIX,
  306. role=REPLICAROLE_CONSUMER,
  307. replicaId=65535)
  308. properties = {RA_NAME: r'to_replica',
  309. RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
  310. RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
  311. RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
  312. RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
  313. repl_agreement = topology.standalone.agreement.create(suffix=DEFAULT_SUFFIX,
  314. host=LOCALHOST,
  315. port=REPLICA_PORT,
  316. properties=properties)
  317. if not repl_agreement:
  318. log.fatal("Fail to create a replica agreement")
  319. repl_fail(replica_inst)
  320. topology.standalone.agreement.init(DEFAULT_SUFFIX, LOCALHOST, REPLICA_PORT)
  321. topology.standalone.waitForReplInit(repl_agreement)
  322. except:
  323. log.info('Failed to setup replication!')
  324. repl_fail(replica_inst)
  325. replication_run = True
  326. msg = ' (replication enabled)'
  327. time.sleep(1)
  328. ############################################################################
  329. # Check replication, and data are in sync, and remove the instance
  330. ############################################################################
  331. log.info('Checking if replication is in sync...')
  332. try:
  333. # Grab master's max CSN
  334. entry = topology.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, RUV_FILTER)
  335. if not entry:
  336. log.error('Failed to find db tombstone entry from master')
  337. repl_fail(replica_inst)
  338. elements = entry[0].getValues('nsds50ruv')
  339. for ruv in elements:
  340. if 'replica 1' in ruv:
  341. parts = ruv.split()
  342. if len(parts) == 5:
  343. master_maxcsn = parts[4]
  344. break
  345. else:
  346. log.error('RUV is incomplete')
  347. repl_fail(replica_inst)
  348. if master_maxcsn == 0:
  349. log.error('Failed to find maxcsn on master')
  350. repl_fail(replica_inst)
  351. except ldap.LDAPError, e:
  352. log.fatal('Unable to search masterfor db tombstone: ' + e.message['desc'])
  353. repl_fail(replica_inst)
  354. # Loop on the consumer - waiting for it to catch up
  355. count = 0
  356. insync = False
  357. while count < 10:
  358. try:
  359. # Grab master's max CSN
  360. entry = replica_inst.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, RUV_FILTER)
  361. if not entry:
  362. log.error('Failed to find db tombstone entry on consumer')
  363. repl_fail(replica_inst)
  364. elements = entry[0].getValues('nsds50ruv')
  365. for ruv in elements:
  366. if 'replica 1' in ruv:
  367. parts = ruv.split()
  368. if len(parts) == 5:
  369. replica_maxcsn = parts[4]
  370. break
  371. if replica_maxcsn == 0:
  372. log.error('Failed to find maxcsn on consumer')
  373. repl_fail(replica_inst)
  374. except ldap.LDAPError, e:
  375. log.fatal('Unable to search for db tombstone on consumer: ' + e.message['desc'])
  376. repl_fail(replica_inst)
  377. if master_maxcsn == replica_maxcsn:
  378. insync = True
  379. log.info('Replication is in sync.\n')
  380. break
  381. count += 1
  382. time.sleep(1)
  383. # Report on replication status
  384. if not insync:
  385. log.error('Consumer not in sync with master!')
  386. repl_fail(replica_inst)
  387. #
  388. # Verify the databases are identical. There should not be any "user, entry, employee" entries
  389. #
  390. log.info('Checking if the data is the same between the replicas...')
  391. # Check the master
  392. try:
  393. entries = topology.standalone.search_s(DEFAULT_SUFFIX,
  394. ldap.SCOPE_SUBTREE,
  395. "(|(uid=person*)(uid=entry*)(uid=employee*))")
  396. if len(entries) > 0:
  397. log.error('Master database has incorrect data set!\n')
  398. repl_fail(replica_inst)
  399. except ldap.LDAPError, e:
  400. log.fatal('Unable to search db on master: ' + e.message['desc'])
  401. repl_fail(replica_inst)
  402. # Check the consumer
  403. try:
  404. entries = replica_inst.search_s(DEFAULT_SUFFIX,
  405. ldap.SCOPE_SUBTREE,
  406. "(|(uid=person*)(uid=entry*)(uid=employee*))")
  407. if len(entries) > 0:
  408. log.error('Consumer database in not consistent with master database')
  409. repl_fail(replica_inst)
  410. except ldap.LDAPError, e:
  411. log.fatal('Unable to search db on consumer: ' + e.message['desc'])
  412. repl_fail(replica_inst)
  413. log.info('Data is consistent across the replicas.\n')
  414. log.info('####################################################################')
  415. log.info('Replication consistency test passed')
  416. log.info('####################################################################\n')
  417. # Remove the replica instance
  418. replica_inst.delete()
  419. ############################################################################
  420. # We made it to the end!
  421. ############################################################################
  422. log.info('#####################################################')
  423. log.info('#####################################################')
  424. log.info("Dynamic Plugins Testsuite: Completed Successfully!")
  425. log.info('#####################################################')
  426. log.info('#####################################################\n')
  427. def test_dynamic_plugins_final(topology):
  428. topology.standalone.delete()
  429. def run_isolated():
  430. '''
  431. run_isolated is used to run these test cases independently of a test scheduler (xunit, py.test..)
  432. To run isolated without py.test, you need to
  433. - edit this file and comment '@pytest.fixture' line before 'topology' function.
  434. - set the installation prefix
  435. - run this program
  436. '''
  437. global installation_prefix
  438. installation_prefix = None
  439. topo = topology(True)
  440. test_dynamic_plugins(topo)
  441. test_dynamic_plugins_final(topo)
  442. if __name__ == '__main__':
  443. run_isolated()