ticket47462_test.py 12 KB

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