memberof_test.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. # --- BEGIN COPYRIGHT BLOCK ---
  2. # Copyright (C) 2017 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 import Entry
  12. from lib389.tasks import Tasks
  13. from lib389.dseldif import DSEldif
  14. from create_data import RHDSDataLDIF
  15. from lib389.properties import TASK_WAIT
  16. from lib389.utils import ldap, os, time, logging, ds_is_older
  17. from lib389._constants import SUFFIX, DN_SCHEMA, DN_DM, DEFAULT_SUFFIX, PASSWORD, PLUGIN_MEMBER_OF, \
  18. PLUGIN_MANAGED_ENTRY, PLUGIN_AUTOMEMBER, DN_CONFIG_LDBM, HOST_STANDALONE, PORT_STANDALONE
  19. from lib389.topologies import topology_st as topo
  20. pytestmark = pytest.mark.tier3
  21. MEMOF_PLUGIN = ('cn=' + PLUGIN_MEMBER_OF + ',cn=plugins,cn=config')
  22. MAN_ENTRY_PLUGIN = ('cn=' + PLUGIN_MANAGED_ENTRY + ',cn=plugins,cn=config')
  23. AUTO_MEM_PLUGIN = ('cn=' + PLUGIN_AUTOMEMBER + ',cn=plugins,cn=config')
  24. DOMAIN = 'redhat.com'
  25. LDAP_MOD = '/usr/bin/ldapmodify'
  26. FILTER = 'objectClass=*'
  27. USER_FILTER = '(|(uid=user*)(cn=group*))'
  28. MEMBEROF_ATTR = 'memberOf'
  29. DN_ATTR = 'dn:'
  30. logging.basicConfig(level=logging.DEBUG)
  31. log = logging.getLogger(__name__)
  32. @pytest.fixture(scope="module")
  33. def memberof_setup(topo, request):
  34. """Configure required plugins and restart the server"""
  35. log.info('Configuring memberOf, managedEntry and autoMembers plugins and restarting the server')
  36. topo.standalone.simple_bind_s(DN_DM, PASSWORD)
  37. try:
  38. topo.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
  39. except ldap.LDAPError as e:
  40. log.error('Failed to enable {} plugin'.format(PLUGIN_MEMBER_OF))
  41. raise e
  42. try:
  43. topo.standalone.plugins.enable(name=PLUGIN_MANAGED_ENTRY)
  44. topo.standalone.plugins.enable(name=PLUGIN_AUTOMEMBER)
  45. except ldap.LDAPError as e:
  46. log.error('Failed to enable {}, {} plugins'.format(PLUGIN_MANAGED_ENTRY, PLUGIN_AUTOMEMBER))
  47. raise e
  48. log.info('Change config values for db-locks and dbcachesize to import large ldif files')
  49. if ds_is_older('1.3.6'):
  50. topo.standalone.stop(timeout=10)
  51. dse_ldif = DSEldif(topo.standalone)
  52. try:
  53. dse_ldif.replace(DN_CONFIG_LDBM, 'nsslapd-db-locks', '100000')
  54. dse_ldif.replace(DN_CONFIG_LDBM, 'nsslapd-dbcachesize', '10000000')
  55. except:
  56. log.error('Failed to replace cn=config values of db-locks and dbcachesize')
  57. raise
  58. topo.standalone.start(timeout=10)
  59. else:
  60. try:
  61. topo.standalone.modify_s(DN_CONFIG_LDBM, [(ldap.MOD_REPLACE, 'nsslapd-db-locks', '100000')])
  62. topo.standalone.modify_s(DN_CONFIG_LDBM, [(ldap.MOD_REPLACE, 'nsslapd-cache-autosize', '0')])
  63. topo.standalone.modify_s(DN_CONFIG_LDBM, [(ldap.MOD_REPLACE, 'nsslapd-dbcachesize', '10000000')])
  64. except ldap.LDAPError as e:
  65. log.error(
  66. 'Failed to replace values of nsslapd-db-locks and nsslapd-dbcachesize {}'.format(e.message['desc']))
  67. raise e
  68. topo.standalone.restart(timeout=10)
  69. def fin():
  70. log.info('Disabling plugins {}, {}, {}'.format(PLUGIN_MEMBER_OF, PLUGIN_MANAGED_ENTRY, PLUGIN_AUTOMEMBER))
  71. topo.standalone.simple_bind_s(DN_DM, PASSWORD)
  72. try:
  73. topo.standalone.plugins.disable(name=PLUGIN_MEMBER_OF)
  74. topo.standalone.plugins.disable(name=PLUGIN_MANAGED_ENTRY)
  75. topo.standalone.plugins.disable(name=PLUGIN_AUTOMEMBER)
  76. except ldap.LDAPError as e:
  77. log.error('Failed to disable plugins, {}'.format(e.message['desc']))
  78. assert False
  79. topo.standalone.restart(timeout=10)
  80. request.addfinalizer(fin)
  81. def _create_base_ldif(topo, import_base=False):
  82. """Create base ldif file to clean entries from suffix"""
  83. log.info('Add base entry for online import')
  84. ldif_dir = topo.standalone.get_ldif_dir()
  85. ldif_file = os.path.join(ldif_dir, '/perf.ldif')
  86. log.info('LDIF FILE is this: {}'.format(ldif_file))
  87. base_ldif = """dn: dc=example,dc=com
  88. objectclass: top
  89. objectclass: domain
  90. dc: example
  91. dn: ou=people,dc=example,dc=com
  92. objectclass: top
  93. objectclass: organizationalUnit
  94. ou: people
  95. dn: ou=groups,dc=example,dc=com
  96. objectclass: top
  97. objectclass: organizationalUnit
  98. ou: groups
  99. """
  100. with open(ldif_file, "w") as fd:
  101. fd.write(base_ldif)
  102. if import_base:
  103. log.info('Adding base entry to suffix to remove users/groups and leave only the OUs')
  104. try:
  105. topo.standalone.tasks.importLDIF(suffix=SUFFIX, input_file=ldif_file, args={TASK_WAIT: True})
  106. except ValueError as e:
  107. log.error('Online import failed' + e.message('desc'))
  108. assert False
  109. else:
  110. log.info('Return LDIF file')
  111. return ldif_file
  112. def _run_fixup_memberof(topo):
  113. """Run fixup memberOf task and measure the time taken"""
  114. log.info('Running fixup memberOf task and measuring the time taken')
  115. start = time.time()
  116. try:
  117. topo.standalone.tasks.fixupMemberOf(suffix=SUFFIX, args={TASK_WAIT: True})
  118. except ValueError as e:
  119. log.error('Running fixup MemberOf task failed' + e.message('desc'))
  120. assert False
  121. end = time.time()
  122. cmd_time = int(end - start)
  123. return cmd_time
  124. def _nested_import_add_ldif(topo, nof_users, nof_groups, grps_user, ngrps_user, nof_depth, is_import=False):
  125. """Create LDIF files for given nof users, groups and nested group levels"""
  126. log.info('Checking if the operation is Import or Ldapadd')
  127. if is_import:
  128. log.info('Import: Create base entry before adding users and groups')
  129. exp_entries = nof_users + nof_groups
  130. data_ldif = _create_base_ldif(topo, False)
  131. log.info('Create data LDIF file by appending users, groups and nested groups')
  132. with open(data_ldif, 'a') as file1:
  133. data = RHDSDataLDIF(stream=file1, users=nof_users, groups=nof_groups, grps_puser=grps_user,
  134. nest_level=nof_depth, ngrps_puser=ngrps_user, basedn=SUFFIX)
  135. data.do_magic()
  136. start = time.time()
  137. log.info('Run importLDIF task to add entries to Server')
  138. try:
  139. topo.standalone.tasks.importLDIF(suffix=SUFFIX, input_file=data_ldif, args={TASK_WAIT: True})
  140. except ValueError as e:
  141. log.error('Online import failed' + e.message('desc'))
  142. assert False
  143. end = time.time()
  144. time_import = int(end - start)
  145. log.info('Check if number of entries created matches the expected entries')
  146. users_groups = topo.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, USER_FILTER, [DN_ATTR])
  147. act_entries = str(users_groups).count(DN_ATTR)
  148. log.info('Expected entries: {}, Actual entries: {}'.format(exp_entries, act_entries))
  149. assert act_entries == exp_entries
  150. return time_import
  151. else:
  152. log.info('Ldapadd: Create data LDIF file with users, groups and nested groups')
  153. ldif_dir = topo.standalone.get_ldif_dir()
  154. data_ldif = os.path.join(ldif_dir, '/perf_add.ldif')
  155. with open(data_ldif, 'w') as file1:
  156. data = RHDSDataLDIF(stream=file1, users=nof_users, groups=nof_groups, grps_puser=grps_user,
  157. nest_level=nof_depth, ngrps_puser=ngrps_user, basedn=SUFFIX)
  158. data.do_magic()
  159. start = time.time()
  160. log.info('Run LDAPMODIFY to add entries to Server')
  161. try:
  162. subprocess.check_output(
  163. [LDAP_MOD, '-cx', '-D', DN_DM, '-w', PASSWORD, '-h', HOST_STANDALONE, '-p', str(PORT_STANDALONE), '-af',
  164. data_ldif])
  165. except subprocess.CalledProcessError as e:
  166. log.error('LDAPMODIFY failed to add entries, error:{:s}'.format(str(e)))
  167. raise e
  168. end = time.time()
  169. cmd_time = int(end - start)
  170. log.info('Time taken to complete LDAPADD: {} secs'.format(cmd_time))
  171. return cmd_time
  172. def _sync_memberof_attrs(topo, exp_memberof):
  173. """Check if expected entries are created or attributes are synced"""
  174. log.info('_sync_memberof_attrs: Check if expected memberOf attributes are synced/created')
  175. loop = 0
  176. start = time.time()
  177. entries = topo.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, FILTER, [MEMBEROF_ATTR])
  178. act_memberof = str(entries).count(MEMBEROF_ATTR)
  179. end = time.time()
  180. cmd_time = int(end - start)
  181. log.info('Loop-{}, expected memberOf attrs: {}, synced: {}, time for search-{} secs'.format(loop, exp_memberof,
  182. act_memberof, cmd_time))
  183. while act_memberof != exp_memberof:
  184. loop = loop + 1
  185. time.sleep(30)
  186. start = time.time()
  187. entries = topo.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, FILTER, [MEMBEROF_ATTR])
  188. act_memberof = str(entries).count(MEMBEROF_ATTR)
  189. end = time.time()
  190. cmd_time = cmd_time + int(end - start)
  191. log.info('Loop-{}, expected memberOf attrs: {}, synced: {}, time for search-{} secs'.format(loop, exp_memberof,
  192. act_memberof,
  193. cmd_time))
  194. # Worst case scenario, exit the test after 10hrs of wait
  195. if loop > 1200:
  196. log.error('Either syncing memberOf attrs takes too long or some issue with the test itself')
  197. assert False
  198. sync_time = 1 + loop * 30
  199. log.info('Expected memberOf attrs: {}, Actual memberOf attrs: {}'.format(exp_memberof, act_memberof))
  200. assert act_memberof == exp_memberof
  201. return sync_time
  202. @pytest.mark.parametrize("nof_users, nof_groups, grps_user, ngrps_user, nof_depth",
  203. [(20000, 200, 20, 10, 5), (50000, 500, 50, 10, 10), (100000, 1000, 100, 20, 20)])
  204. def test_nestgrps_import(topo, memberof_setup, nof_users, nof_groups, grps_user, ngrps_user, nof_depth):
  205. """Import large users and nested groups with N depth and measure the time taken
  206. :ID: 169a09f2-2c2d-4e42-8b90-a0bd1034f278
  207. :feature: MemberOf Plugin
  208. :setup: Standalone instance, memberOf plugin enabled
  209. :steps: 1. Create LDIF file for given nof_users and nof_groups
  210. 2. Import entries to server
  211. 3. Check if entries are created
  212. 4. Run fixupMemberOf task to create memberOf attributes
  213. 5. Check if memberOf attributes are synced for all users and groups
  214. 6. Compare the actual no of memberOf attributes to the expected
  215. 7. Measure the time taken to sync memberOf attributes
  216. :expectedresults: MemberOf attributes should be synced
  217. """
  218. exp_memberof = (nof_users * grps_user) + (
  219. (nof_groups // grps_user) * (ngrps_user // nof_depth) * (nof_depth * (nof_depth + 1)) // 2)
  220. log.info('Create nested ldif file with users-{}, groups-{}, nested-{}'.format(nof_users, nof_groups, nof_depth))
  221. log.info('Import LDIF file and measure the time taken')
  222. import_time = _nested_import_add_ldif(topo, nof_users, nof_groups, grps_user, ngrps_user, nof_depth, True)
  223. log.info('Run fixup memberOf task and measure the time taken to complete the task')
  224. fixup_time = _run_fixup_memberof(topo)
  225. log.info('Check the total number of memberOf entries created for users and groups')
  226. sync_memberof = _sync_memberof_attrs(topo, exp_memberof)
  227. total_time = import_time + fixup_time + sync_memberof
  228. log.info('Time for import-{}secs, fixup task-{}secs, total time for memberOf sync: {}secs'.format(import_time,
  229. fixup_time,
  230. total_time))
  231. @pytest.mark.parametrize("nof_users, nof_groups, grps_user, ngrps_user, nof_depth",
  232. [(20000, 100, 20, 10, 5), (50000, 200, 50, 10, 10), (100000, 100, 20, 10, 10)])
  233. def test_nestgrps_add(topo, memberof_setup, nof_users, nof_groups, grps_user, ngrps_user, nof_depth):
  234. """Import large users and nested groups with n depth and measure the time taken
  235. :ID: 6eda75c6-5ae0-4b17-b610-d217d7ec7542
  236. :feature: MemberOf Plugin
  237. :setup: Standalone instance, memberOf plugin enabled
  238. :steps: 1. Create LDIF file for given nof_users and nof_groups
  239. 2. Add entries using LDAPADD
  240. 3. Check if entries are created
  241. 4. Check if memberOf attributes are synced for all users and groups
  242. 5. Compare the actual no of memberOf attributes to the expected
  243. 6. Measure the time taken to sync memberOf attributes
  244. :expectedresults: MemberOf attributes should be created and synced
  245. """
  246. exp_memberof = (nof_users * grps_user) + (
  247. (nof_groups // grps_user) * (ngrps_user // nof_depth) * (nof_depth * (nof_depth + 1)) // 2)
  248. log.info('Creating base_ldif file and importing it to wipe out all users and groups')
  249. _create_base_ldif(topo, True)
  250. log.info('Create nested ldif file with users-{}, groups-{}, nested-{}'.format(nof_users, nof_groups, nof_depth))
  251. log.info('Run LDAPADD to add entries to Server')
  252. add_time = _nested_import_add_ldif(topo, nof_users, nof_groups, grps_user, ngrps_user, nof_depth, False)
  253. log.info('Check the total number of memberOf entries created for users and groups')
  254. sync_memberof = _sync_memberof_attrs(topo, exp_memberof)
  255. total_time = add_time + sync_memberof
  256. log.info('Time for ldapadd-{}secs, total time for memberOf sync: {}secs'.format(add_time, total_time))
  257. @pytest.mark.parametrize("nof_users, nof_groups, grps_user, ngrps_user, nof_depth",
  258. [(20000, 200, 20, 10, 5), (50000, 500, 50, 10, 10), (100000, 1000, 100, 20, 20)])
  259. def test_mod_nestgrp(topo, memberof_setup, nof_users, nof_groups, grps_user, ngrps_user, nof_depth):
  260. """Import bulk entries, modify nested groups at N depth and measure the time taken
  261. :ID: 4bf8e753-6ded-4177-8225-aaf6aef4d131
  262. :feature: MemberOf Plugin
  263. :setup: Standalone instance, memberOf plugin enabled
  264. :steps: 1. Import bulk entries with nested group and create memberOf attributes
  265. 2. Modify nested groups by adding new members at each nested level
  266. 3. Check new memberOf attributes created for users and groups
  267. 4. Compare the actual memberOf attributes with the expected
  268. 5. Measure the time taken to sync memberOf attributes
  269. :expectedresults: MemberOf attributes should be modified and synced
  270. """
  271. exp_memberof = (nof_users * grps_user) + (
  272. (nof_groups // grps_user) * (ngrps_user // nof_depth) * (nof_depth * (nof_depth + 1)) // 2)
  273. log.info('Create nested ldif file, import it and measure the time taken')
  274. import_time = _nested_import_add_ldif(topo, nof_users, nof_groups, grps_user, ngrps_user, nof_depth, True)
  275. log.info('Run fixup memberOf task and measure the time to complete the task')
  276. fixup_time = _run_fixup_memberof(topo)
  277. sync_memberof = _sync_memberof_attrs(topo, exp_memberof)
  278. total_time = import_time + fixup_time + sync_memberof
  279. log.info('Time for import-{}secs, fixup task-{}secs, total time for memberOf sync: {}secs'.format(import_time,
  280. fixup_time,
  281. total_time))
  282. log.info('Add {} users to existing nested groups at all depth level'.format(nof_groups))
  283. log.info('Add one user to each groups at different nest levels')
  284. start = time.time()
  285. for usr in range(nof_groups):
  286. usrrdn = 'newcliusr{}'.format(usr)
  287. userdn = 'uid={},ou=people,{}'.format(usrrdn, SUFFIX)
  288. groupdn = 'cn=group{},ou=groups,{}'.format(usr, SUFFIX)
  289. try:
  290. topo.standalone.add_s(Entry((userdn, {
  291. 'objectclass': 'top person inetUser inetOrgperson'.split(),
  292. 'cn': usrrdn,
  293. 'sn': usrrdn,
  294. 'userpassword': 'Secret123'})))
  295. except ldap.LDAPError as e:
  296. log.error('Failed to add {} user: error {}'.format(userdn, e.message['desc']))
  297. raise
  298. try:
  299. topo.standalone.modify_s(groupdn, [(ldap.MOD_ADD, 'member', userdn)])
  300. except ldap.LDAPError as e:
  301. log.error('Error-{}: Failed to add user to group'.format(e.message['desc']))
  302. assert False
  303. end = time.time()
  304. cmd_time = int(end - start)
  305. exp_memberof = (nof_users * grps_user) + nof_groups + (
  306. (nof_groups // grps_user) * (ngrps_user // nof_depth) * (nof_depth * (nof_depth + 1)))
  307. log.info('Check the total number of memberOf entries created for users and groups')
  308. sync_memberof = _sync_memberof_attrs(topo, exp_memberof)
  309. total_time = cmd_time + sync_memberof
  310. log.info('Time taken add new members to existing nested groups + memberOf sync: {} secs'.format(total_time))
  311. @pytest.mark.parametrize("nof_users, nof_groups, grps_user, ngrps_user, nof_depth",
  312. [(20000, 200, 20, 10, 5), (50000, 500, 50, 10, 10), (100000, 1000, 100, 20, 20)])
  313. def test_del_nestgrp(topo, memberof_setup, nof_users, nof_groups, grps_user, ngrps_user, nof_depth):
  314. """Import bulk entries, delete nested groups at N depth and measure the time taken
  315. :ID: d3d82ac5-d968-4cd6-a268-d380fc9fd51b
  316. :feature: MemberOf Plugin
  317. :setup: Standalone instance, memberOf plugin enabled
  318. :steps: 1. Import bulk users and groups with nested level N.
  319. 2. Run fixup memberOf task to create memberOf attributes
  320. 3. Delete nested groups at nested level N
  321. 4. Check memberOf attributes deleted for users and groups
  322. 5. Compare the actual memberOf attributes with the expected
  323. 6. Measure the time taken to sync memberOf attributes
  324. :expectedresults: MemberOf attributes should be deleted and synced
  325. """
  326. exp_memberof = (nof_users * grps_user) + (
  327. (nof_groups // grps_user) * (ngrps_user // nof_depth) * (nof_depth * (nof_depth + 1)) // 2)
  328. log.info('Create nested ldif file, import it and measure the time taken')
  329. import_time = _nested_import_add_ldif(topo, nof_users, nof_groups, grps_user, ngrps_user, nof_depth, True)
  330. log.info('Run fixup memberOf task and measure the time to complete the task')
  331. fixup_time = _run_fixup_memberof(topo)
  332. sync_memberof = _sync_memberof_attrs(topo, exp_memberof)
  333. total_time = import_time + fixup_time + sync_memberof
  334. log.info('Time taken to complete add users + memberOf sync: {} secs'.format(total_time))
  335. log.info('Delete {} groups from nested groups at depth level-{}'.format(nof_depth, nof_depth))
  336. start = time.time()
  337. for nos in range(nof_depth, nof_groups, grps_user):
  338. groupdn = 'cn=group{},ou=groups,{}'.format(nos, SUFFIX)
  339. try:
  340. topo.standalone.delete_s(groupdn)
  341. except ldap.LDAPError as e:
  342. log.error('Error-{}: Failed to delete group'.format(e.message['desc']))
  343. assert False
  344. end = time.time()
  345. cmd_time = int(end - start)
  346. exp_memberof = exp_memberof - (nof_users + (nof_depth * (nof_groups // grps_user)))
  347. log.info('Check memberOf attributes after deleting groups at depth-{}'.format(nof_depth))
  348. sync_memberof = _sync_memberof_attrs(topo, exp_memberof)
  349. total_time = cmd_time + sync_memberof
  350. log.info('Time taken to delete and sync memberOf attributes: {}secs'.format(total_time))
  351. if __name__ == '__main__':
  352. # Run isolated
  353. # -s for DEBUG mode
  354. CURRENT_FILE = os.path.realpath(__file__)
  355. pytest.main("-s {}".format(CURRENT_FILE))