ticket47462_test.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  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. if master1.testReplication(DEFAULT_SUFFIX, master2):
  99. log.info('Replication is working.')
  100. else:
  101. log.fatal('Replication is not working.')
  102. assert False
  103. # clear the tmp directory
  104. master1.clearTmpDir(__file__)
  105. return TopologyMaster1Master2(master1, master2)
  106. def test_ticket47462(topology):
  107. """
  108. Test that AES properly replaces DES during an update/restart, and that
  109. replication also works correctly.
  110. """
  111. #
  112. # First set config as if it's an older version. Set DES to use libdes-plugin,
  113. # MMR to depend on DES, delete the existing AES plugin, and set a DES password
  114. # for the replication agreement.
  115. #
  116. #
  117. # Add an extra attribute to the DES plugin args
  118. #
  119. try:
  120. topology.master1.modify_s(DES_PLUGIN,
  121. [(ldap.MOD_REPLACE, 'nsslapd-pluginEnabled', 'on')])
  122. except ldap.LDAPError, e:
  123. log.fatal('Failed to enable DES plugin, error: ' + e.message['desc'])
  124. assert False
  125. try:
  126. topology.master1.modify_s(DES_PLUGIN,
  127. [(ldap.MOD_ADD, 'nsslapd-pluginarg2', 'description')])
  128. except ldap.LDAPError, e:
  129. log.fatal('Failed to reset DES plugin, error: ' + e.message['desc'])
  130. assert False
  131. try:
  132. topology.master1.modify_s(MMR_PLUGIN,
  133. [(ldap.MOD_DELETE, 'nsslapd-plugin-depends-on-named', 'AES')])
  134. except ldap.NO_SUCH_ATTRIBUTE:
  135. pass
  136. except ldap.LDAPError, e:
  137. log.fatal('Failed to reset MMR plugin, error: ' + e.message['desc'])
  138. assert False
  139. #
  140. # Delete the AES plugin
  141. #
  142. try:
  143. topology.master1.delete_s(AES_PLUGIN)
  144. except ldap.NO_SUCH_OBJECT:
  145. pass
  146. except ldap.LDAPError, e:
  147. log.fatal('Failed to delete AES plugin, error: ' + e.message['desc'])
  148. assert False
  149. # restart the server so we must use DES plugin
  150. topology.master1.restart(timeout=10)
  151. #
  152. # Get the agmt dn, and set the password
  153. #
  154. try:
  155. entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE, 'objectclass=nsDS5ReplicationAgreement')
  156. if entry:
  157. agmt_dn = entry[0].dn
  158. log.info('Found agmt dn (%s)' % agmt_dn)
  159. else:
  160. log.fatal('No replication agreements!')
  161. assert False
  162. except ldap.LDAPError, e:
  163. log.fatal('Failed to search for replica credentials: ' + e.message['desc'])
  164. assert False
  165. try:
  166. properties = {RA_BINDPW: "password"}
  167. topology.master1.agreement.setProperties(None, agmt_dn, None, properties)
  168. log.info('Successfully modified replication agreement')
  169. except ValueError:
  170. log.error('Failed to update replica agreement: ' + AGMT_DN)
  171. assert False
  172. #
  173. # Check replication works with the new DES password
  174. #
  175. try:
  176. topology.master1.add_s(Entry((USER1_DN,
  177. {'objectclass': "top person".split(),
  178. 'sn': 'sn',
  179. 'cn': 'test_user'})))
  180. loop = 0
  181. ent = None
  182. while loop <= 10:
  183. try:
  184. ent = topology.master2.getEntry(USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)")
  185. break
  186. except ldap.NO_SUCH_OBJECT:
  187. time.sleep(1)
  188. loop += 1
  189. if not ent:
  190. log.fatal('Replication test failed fo user1!')
  191. assert False
  192. else:
  193. log.info('Replication test passed')
  194. except ldap.LDAPError, e:
  195. log.fatal('Failed to add test user: ' + e.message['desc'])
  196. assert False
  197. #
  198. # Run the upgrade...
  199. #
  200. topology.master1.upgrade('online')
  201. topology.master1.restart(timeout=10)
  202. topology.master2.restart(timeout=10)
  203. #
  204. # Check that the restart converted existing DES credentials
  205. #
  206. try:
  207. entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE, 'nsDS5ReplicaCredentials=*')
  208. if entry:
  209. val = entry[0].getValue('nsDS5ReplicaCredentials')
  210. if val.startswith('{AES-'):
  211. log.info('The DES credentials have been converted to AES')
  212. else:
  213. log.fatal('Failed to convert credentials from DES to AES!')
  214. assert False
  215. else:
  216. log.fatal('Failed to find any entries with nsDS5ReplicaCredentials ')
  217. assert False
  218. except ldap.LDAPError, e:
  219. log.fatal('Failed to search for replica credentials: ' + e.message['desc'])
  220. assert False
  221. #
  222. # Check that the AES plugin exists, and has all the attributes listed in DES plugin.
  223. # The attributes might not be in the expected order so check all the attributes.
  224. #
  225. try:
  226. entry = topology.master1.search_s(AES_PLUGIN, ldap.SCOPE_BASE, 'objectclass=*')
  227. if not entry[0].hasValue('nsslapd-pluginarg0', 'description') and \
  228. not entry[0].hasValue('nsslapd-pluginarg1', 'description') and \
  229. not entry[0].hasValue('nsslapd-pluginarg2', 'description'):
  230. log.fatal('The AES plugin did not have the DES attribute copied over correctly')
  231. assert False
  232. else:
  233. log.info('The AES plugin was correctly setup')
  234. except ldap.LDAPError, e:
  235. log.fatal('Failed to find AES plugin: ' + e.message['desc'])
  236. assert False
  237. #
  238. # Check that the MMR plugin was updated
  239. #
  240. try:
  241. entry = topology.master1.search_s(MMR_PLUGIN, ldap.SCOPE_BASE, 'objectclass=*')
  242. if not entry[0].hasValue('nsslapd-plugin-depends-on-named', 'AES'):
  243. log.fatal('The MMR Plugin was not correctly updated')
  244. assert False
  245. else:
  246. log.info('The MMR plugin was correctly updated')
  247. except ldap.LDAPError, e:
  248. log.fatal('Failed to find AES plugin: ' + e.message['desc'])
  249. assert False
  250. #
  251. # Check that the DES plugin was correctly updated
  252. #
  253. try:
  254. entry = topology.master1.search_s(DES_PLUGIN, ldap.SCOPE_BASE, 'objectclass=*')
  255. if not entry[0].hasValue('nsslapd-pluginPath', 'libpbe-plugin'):
  256. log.fatal('The DES Plugin was not correctly updated')
  257. assert False
  258. else:
  259. log.info('The DES plugin was correctly updated')
  260. except ldap.LDAPError, e:
  261. log.fatal('Failed to find AES plugin: ' + e.message['desc'])
  262. assert False
  263. #
  264. # Check replication one last time
  265. #
  266. try:
  267. topology.master1.add_s(Entry((USER_DN,
  268. {'objectclass': "top person".split(),
  269. 'sn': 'sn',
  270. 'cn': 'test_user'})))
  271. loop = 0
  272. ent = None
  273. while loop <= 10:
  274. try:
  275. ent = topology.master2.getEntry(USER_DN, ldap.SCOPE_BASE, "(objectclass=*)")
  276. break
  277. except ldap.NO_SUCH_OBJECT:
  278. time.sleep(1)
  279. loop += 1
  280. if not ent:
  281. log.fatal('Replication test failed!')
  282. assert False
  283. else:
  284. log.info('Replication test passed')
  285. except ldap.LDAPError, e:
  286. log.fatal('Failed to add test user: ' + e.message['desc'])
  287. assert False
  288. def test_ticket47462_final(topology):
  289. topology.master1.delete()
  290. topology.master2.delete()
  291. log.info('Testcase PASSED')
  292. def run_isolated():
  293. '''
  294. run_isolated is used to run these test cases independently of a test scheduler (xunit, py.test..)
  295. To run isolated without py.test, you need to
  296. - edit this file and comment '@pytest.fixture' line before 'topology' function.
  297. - set the installation prefix
  298. - run this program
  299. '''
  300. global installation1_prefix
  301. global installation2_prefix
  302. installation1_prefix = None
  303. installation2_prefix = None
  304. topo = topology(True)
  305. test_ticket47462(topo)
  306. test_ticket47462_final(topo)
  307. if __name__ == '__main__':
  308. run_isolated()