ticket47462_test.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. # --- BEGIN COPYRIGHT BLOCK ---
  2. # Copyright (C) 2015 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 sys
  10. import time
  11. import ldap
  12. import logging
  13. import pytest
  14. from lib389 import DirSrv, Entry, tools
  15. from lib389.tools import DirSrvTools
  16. from lib389._constants import *
  17. from lib389.properties import *
  18. logging.getLogger(__name__).setLevel(logging.DEBUG)
  19. log = logging.getLogger(__name__)
  20. #
  21. # important part. We can deploy Master1 and Master2 on different versions
  22. #
  23. installation1_prefix = None
  24. installation2_prefix = None
  25. DES_PLUGIN = 'cn=DES,cn=Password Storage Schemes,cn=plugins,cn=config'
  26. AES_PLUGIN = 'cn=AES,cn=Password Storage Schemes,cn=plugins,cn=config'
  27. MMR_PLUGIN = 'cn=Multimaster Replication Plugin,cn=plugins,cn=config'
  28. AGMT_DN = ''
  29. USER_DN = 'cn=test_user,' + DEFAULT_SUFFIX
  30. USER1_DN = 'cn=test_user1,' + DEFAULT_SUFFIX
  31. TEST_REPL_DN = 'cn=test repl,' + DEFAULT_SUFFIX
  32. class TopologyMaster1Master2(object):
  33. def __init__(self, master1, master2):
  34. master1.open()
  35. self.master1 = master1
  36. master2.open()
  37. self.master2 = master2
  38. @pytest.fixture(scope="module")
  39. def topology(request):
  40. '''
  41. This fixture is used to create a replicated topology for the 'module'.
  42. The replicated topology is MASTER1 <-> Master2.
  43. '''
  44. global installation1_prefix
  45. global installation2_prefix
  46. # allocate master1 on a given deployement
  47. master1 = DirSrv(verbose=False)
  48. if installation1_prefix:
  49. args_instance[SER_DEPLOYED_DIR] = installation1_prefix
  50. # Args for the master1 instance
  51. args_instance[SER_HOST] = HOST_MASTER_1
  52. args_instance[SER_PORT] = PORT_MASTER_1
  53. args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_1
  54. args_master = args_instance.copy()
  55. master1.allocate(args_master)
  56. # allocate master1 on a given deployement
  57. master2 = DirSrv(verbose=False)
  58. if installation2_prefix:
  59. args_instance[SER_DEPLOYED_DIR] = installation2_prefix
  60. # Args for the consumer instance
  61. args_instance[SER_HOST] = HOST_MASTER_2
  62. args_instance[SER_PORT] = PORT_MASTER_2
  63. args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_2
  64. args_master = args_instance.copy()
  65. master2.allocate(args_master)
  66. # Get the status of the instance and restart it if it exists
  67. instance_master1 = master1.exists()
  68. instance_master2 = master2.exists()
  69. # Remove all the instances
  70. if instance_master1:
  71. master1.delete()
  72. if instance_master2:
  73. master2.delete()
  74. # Create the instances
  75. master1.create()
  76. master1.open()
  77. master2.create()
  78. master2.open()
  79. #
  80. # Now prepare the Master-Consumer topology
  81. #
  82. # First Enable replication
  83. master1.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_1)
  84. master2.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_2)
  85. # Initialize the supplier->consumer
  86. properties = {RA_NAME: r'meTo_$host:$port',
  87. RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
  88. RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
  89. RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
  90. RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
  91. AGMT_DN = master1.agreement.create(suffix=SUFFIX, host=master2.host, port=master2.port, properties=properties)
  92. master1.agreement
  93. if not AGMT_DN:
  94. log.fatal("Fail to create a replica agreement")
  95. sys.exit(1)
  96. log.debug("%s created" % AGMT_DN)
  97. properties = {RA_NAME: r'meTo_$host:$port',
  98. RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
  99. RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
  100. RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
  101. RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
  102. master2.agreement.create(suffix=DEFAULT_SUFFIX, host=master1.host, port=master1.port, properties=properties)
  103. master1.agreement.init(SUFFIX, HOST_MASTER_2, PORT_MASTER_2)
  104. master1.waitForReplInit(AGMT_DN)
  105. # Check replication is working fine
  106. if master1.testReplication(DEFAULT_SUFFIX, master2):
  107. log.info('Replication is working.')
  108. else:
  109. log.fatal('Replication is not working.')
  110. assert False
  111. # clear the tmp directory
  112. master1.clearTmpDir(__file__)
  113. return TopologyMaster1Master2(master1, master2)
  114. def test_ticket47462(topology):
  115. """
  116. Test that AES properly replaces DES during an update/restart, and that
  117. replication also works correctly.
  118. """
  119. #
  120. # First set config as if it's an older version. Set DES to use libdes-plugin,
  121. # MMR to depend on DES, delete the existing AES plugin, and set a DES password
  122. # for the replication agreement.
  123. #
  124. #
  125. # Add an extra attribute to the DES plugin args
  126. #
  127. try:
  128. topology.master1.modify_s(DES_PLUGIN,
  129. [(ldap.MOD_REPLACE, 'nsslapd-pluginEnabled', 'on')])
  130. except ldap.LDAPError as e:
  131. log.fatal('Failed to enable DES plugin, error: ' + e.message['desc'])
  132. assert False
  133. try:
  134. topology.master1.modify_s(DES_PLUGIN,
  135. [(ldap.MOD_ADD, 'nsslapd-pluginarg2', 'description')])
  136. except ldap.LDAPError as e:
  137. log.fatal('Failed to reset DES plugin, error: ' + e.message['desc'])
  138. assert False
  139. try:
  140. topology.master1.modify_s(MMR_PLUGIN,
  141. [(ldap.MOD_DELETE, 'nsslapd-plugin-depends-on-named', 'AES')])
  142. except ldap.NO_SUCH_ATTRIBUTE:
  143. pass
  144. except ldap.LDAPError as e:
  145. log.fatal('Failed to reset MMR plugin, error: ' + e.message['desc'])
  146. assert False
  147. #
  148. # Delete the AES plugin
  149. #
  150. try:
  151. topology.master1.delete_s(AES_PLUGIN)
  152. except ldap.NO_SUCH_OBJECT:
  153. pass
  154. except ldap.LDAPError as e:
  155. log.fatal('Failed to delete AES plugin, error: ' + e.message['desc'])
  156. assert False
  157. # restart the server so we must use DES plugin
  158. topology.master1.restart(timeout=10)
  159. #
  160. # Get the agmt dn, and set the password
  161. #
  162. try:
  163. entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE, 'objectclass=nsDS5ReplicationAgreement')
  164. if entry:
  165. agmt_dn = entry[0].dn
  166. log.info('Found agmt dn (%s)' % agmt_dn)
  167. else:
  168. log.fatal('No replication agreements!')
  169. assert False
  170. except ldap.LDAPError as e:
  171. log.fatal('Failed to search for replica credentials: ' + e.message['desc'])
  172. assert False
  173. try:
  174. properties = {RA_BINDPW: "password"}
  175. topology.master1.agreement.setProperties(None, agmt_dn, None, properties)
  176. log.info('Successfully modified replication agreement')
  177. except ValueError:
  178. log.error('Failed to update replica agreement: ' + AGMT_DN)
  179. assert False
  180. #
  181. # Check replication works with the new DES password
  182. #
  183. try:
  184. topology.master1.add_s(Entry((USER1_DN,
  185. {'objectclass': "top person".split(),
  186. 'sn': 'sn',
  187. 'cn': 'test_user'})))
  188. loop = 0
  189. ent = None
  190. while loop <= 10:
  191. try:
  192. ent = topology.master2.getEntry(USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)")
  193. break
  194. except ldap.NO_SUCH_OBJECT:
  195. time.sleep(1)
  196. loop += 1
  197. if not ent:
  198. log.fatal('Replication test failed fo user1!')
  199. assert False
  200. else:
  201. log.info('Replication test passed')
  202. except ldap.LDAPError as e:
  203. log.fatal('Failed to add test user: ' + e.message['desc'])
  204. assert False
  205. #
  206. # Run the upgrade...
  207. #
  208. topology.master1.upgrade('online')
  209. topology.master1.restart(timeout=10)
  210. topology.master2.restart(timeout=10)
  211. #
  212. # Check that the restart converted existing DES credentials
  213. #
  214. try:
  215. entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE, 'nsDS5ReplicaCredentials=*')
  216. if entry:
  217. val = entry[0].getValue('nsDS5ReplicaCredentials')
  218. if val.startswith('{AES-'):
  219. log.info('The DES credentials have been converted to AES')
  220. else:
  221. log.fatal('Failed to convert credentials from DES to AES!')
  222. assert False
  223. else:
  224. log.fatal('Failed to find any entries with nsDS5ReplicaCredentials ')
  225. assert False
  226. except ldap.LDAPError as e:
  227. log.fatal('Failed to search for replica credentials: ' + e.message['desc'])
  228. assert False
  229. #
  230. # Check that the AES plugin exists, and has all the attributes listed in DES plugin.
  231. # The attributes might not be in the expected order so check all the attributes.
  232. #
  233. try:
  234. entry = topology.master1.search_s(AES_PLUGIN, ldap.SCOPE_BASE, 'objectclass=*')
  235. if not entry[0].hasValue('nsslapd-pluginarg0', 'description') and \
  236. not entry[0].hasValue('nsslapd-pluginarg1', 'description') and \
  237. not entry[0].hasValue('nsslapd-pluginarg2', 'description'):
  238. log.fatal('The AES plugin did not have the DES attribute copied over correctly')
  239. assert False
  240. else:
  241. log.info('The AES plugin was correctly setup')
  242. except ldap.LDAPError as e:
  243. log.fatal('Failed to find AES plugin: ' + e.message['desc'])
  244. assert False
  245. #
  246. # Check that the MMR plugin was updated
  247. #
  248. try:
  249. entry = topology.master1.search_s(MMR_PLUGIN, ldap.SCOPE_BASE, 'objectclass=*')
  250. if not entry[0].hasValue('nsslapd-plugin-depends-on-named', 'AES'):
  251. log.fatal('The MMR Plugin was not correctly updated')
  252. assert False
  253. else:
  254. log.info('The MMR plugin was correctly updated')
  255. except ldap.LDAPError as e:
  256. log.fatal('Failed to find AES plugin: ' + e.message['desc'])
  257. assert False
  258. #
  259. # Check that the DES plugin was correctly updated
  260. #
  261. try:
  262. entry = topology.master1.search_s(DES_PLUGIN, ldap.SCOPE_BASE, 'objectclass=*')
  263. if not entry[0].hasValue('nsslapd-pluginPath', 'libpbe-plugin'):
  264. log.fatal('The DES Plugin was not correctly updated')
  265. assert False
  266. else:
  267. log.info('The DES plugin was correctly updated')
  268. except ldap.LDAPError as e:
  269. log.fatal('Failed to find AES plugin: ' + e.message['desc'])
  270. assert False
  271. #
  272. # Check replication one last time
  273. #
  274. try:
  275. topology.master1.add_s(Entry((USER_DN,
  276. {'objectclass': "top person".split(),
  277. 'sn': 'sn',
  278. 'cn': 'test_user'})))
  279. loop = 0
  280. ent = None
  281. while loop <= 10:
  282. try:
  283. ent = topology.master2.getEntry(USER_DN, ldap.SCOPE_BASE, "(objectclass=*)")
  284. break
  285. except ldap.NO_SUCH_OBJECT:
  286. time.sleep(1)
  287. loop += 1
  288. if not ent:
  289. log.fatal('Replication test failed!')
  290. assert False
  291. else:
  292. log.info('Replication test passed')
  293. except ldap.LDAPError as e:
  294. log.fatal('Failed to add test user: ' + e.message['desc'])
  295. assert False
  296. def test_ticket47462_final(topology):
  297. topology.master1.delete()
  298. topology.master2.delete()
  299. log.info('Testcase PASSED')
  300. def run_isolated():
  301. '''
  302. run_isolated is used to run these test cases independently of a test scheduler (xunit, py.test..)
  303. To run isolated without py.test, you need to
  304. - edit this file and comment '@pytest.fixture' line before 'topology' function.
  305. - set the installation prefix
  306. - run this program
  307. '''
  308. global installation1_prefix
  309. global installation2_prefix
  310. installation1_prefix = None
  311. installation2_prefix = None
  312. topo = topology(True)
  313. test_ticket47462(topo)
  314. test_ticket47462_final(topo)
  315. if __name__ == '__main__':
  316. run_isolated()