ticket47721_test.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  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. '''
  10. Created on Nov 7, 2013
  11. @author: tbordaz
  12. '''
  13. import os
  14. import sys
  15. import time
  16. import ldap
  17. import logging
  18. import pytest
  19. from lib389 import DirSrv, Entry, tools
  20. from lib389.tools import DirSrvTools
  21. from lib389._constants import *
  22. from lib389.properties import *
  23. from lib389._constants import REPLICAROLE_MASTER
  24. logging.getLogger(__name__).setLevel(logging.DEBUG)
  25. log = logging.getLogger(__name__)
  26. #
  27. # important part. We can deploy Master1 and Master2 on different versions
  28. #
  29. installation1_prefix = None
  30. installation2_prefix = None
  31. SCHEMA_DN = "cn=schema"
  32. TEST_REPL_DN = "cn=test_repl, %s" % SUFFIX
  33. OC_NAME = 'OCticket47721'
  34. OC_OID_EXT = 2
  35. MUST = "(postalAddress $ postalCode)"
  36. MAY = "(member $ street)"
  37. OC2_NAME = 'OC2ticket47721'
  38. OC2_OID_EXT = 3
  39. MUST_2 = "(postalAddress $ postalCode)"
  40. MAY_2 = "(member $ street)"
  41. REPL_SCHEMA_POLICY_CONSUMER = "cn=consumerUpdatePolicy,cn=replSchema,cn=config"
  42. REPL_SCHEMA_POLICY_SUPPLIER = "cn=supplierUpdatePolicy,cn=replSchema,cn=config"
  43. OTHER_NAME = 'other_entry'
  44. MAX_OTHERS = 10
  45. BIND_NAME = 'bind_entry'
  46. BIND_DN = 'cn=%s, %s' % (BIND_NAME, SUFFIX)
  47. BIND_PW = 'password'
  48. ENTRY_NAME = 'test_entry'
  49. ENTRY_DN = 'cn=%s, %s' % (ENTRY_NAME, SUFFIX)
  50. ENTRY_OC = "top person %s" % OC_NAME
  51. BASE_OID = "1.2.3.4.5.6.7.8.9.10"
  52. SLEEP_INTERVAL = 60
  53. def _add_custom_at_definition(name='ATticket47721'):
  54. new_at = "( %s-oid NAME '%s' DESC 'test AT ticket 47721' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN ( 'Test 47721' 'user defined' ) )" % (name, name)
  55. return new_at
  56. def _chg_std_at_defintion():
  57. new_at = "( 2.16.840.1.113730.3.1.569 NAME 'cosPriority' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 X-ORIGIN 'Netscape Directory Server' )"
  58. return new_at
  59. def _add_custom_oc_defintion(name='OCticket47721'):
  60. new_oc = "( %s-oid NAME '%s' DESC 'An group of related automount objects' SUP top STRUCTURAL MUST ou X-ORIGIN 'draft-howard-rfc2307bis' )" % (name, name)
  61. return new_oc
  62. def _chg_std_oc_defintion():
  63. new_oc = "( 5.3.6.1.1.1.2.0 NAME 'trustAccount' DESC 'Sets trust accounts information' SUP top AUXILIARY MUST trustModel MAY ( accessTo $ ou ) X-ORIGIN 'nss_ldap/pam_ldap' )"
  64. return new_oc
  65. class TopologyMaster1Master2(object):
  66. def __init__(self, master1, master2):
  67. master1.open()
  68. self.master1 = master1
  69. master2.open()
  70. self.master2 = master2
  71. @pytest.fixture(scope="module")
  72. def topology(request):
  73. '''
  74. This fixture is used to create a replicated topology for the 'module'.
  75. The replicated topology is MASTER1 <-> Master2.
  76. '''
  77. global installation1_prefix
  78. global installation2_prefix
  79. # allocate master1 on a given deployement
  80. master1 = DirSrv(verbose=False)
  81. if installation1_prefix:
  82. args_instance[SER_DEPLOYED_DIR] = installation1_prefix
  83. # Args for the master1 instance
  84. args_instance[SER_HOST] = HOST_MASTER_1
  85. args_instance[SER_PORT] = PORT_MASTER_1
  86. args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_1
  87. args_master = args_instance.copy()
  88. master1.allocate(args_master)
  89. # allocate master1 on a given deployement
  90. master2 = DirSrv(verbose=False)
  91. if installation2_prefix:
  92. args_instance[SER_DEPLOYED_DIR] = installation2_prefix
  93. # Args for the consumer instance
  94. args_instance[SER_HOST] = HOST_MASTER_2
  95. args_instance[SER_PORT] = PORT_MASTER_2
  96. args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_2
  97. args_master = args_instance.copy()
  98. master2.allocate(args_master)
  99. # Get the status of the instance and restart it if it exists
  100. instance_master1 = master1.exists()
  101. instance_master2 = master2.exists()
  102. # Remove all the instances
  103. if instance_master1:
  104. master1.delete()
  105. if instance_master2:
  106. master2.delete()
  107. # Create the instances
  108. master1.create()
  109. master1.open()
  110. master2.create()
  111. master2.open()
  112. #
  113. # Now prepare the Master-Consumer topology
  114. #
  115. # First Enable replication
  116. master1.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_1)
  117. master2.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_2)
  118. # Initialize the supplier->consumer
  119. properties = {RA_NAME: r'meTo_$host:$port',
  120. RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
  121. RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
  122. RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
  123. RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
  124. repl_agreement = master1.agreement.create(suffix=SUFFIX, host=master2.host, port=master2.port, properties=properties)
  125. if not repl_agreement:
  126. log.fatal("Fail to create a replica agreement")
  127. sys.exit(1)
  128. log.debug("%s created" % repl_agreement)
  129. properties = {RA_NAME: r'meTo_$host:$port',
  130. RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
  131. RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
  132. RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
  133. RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
  134. master2.agreement.create(suffix=SUFFIX, host=master1.host, port=master1.port, properties=properties)
  135. master1.agreement.init(SUFFIX, HOST_MASTER_2, PORT_MASTER_2)
  136. master1.waitForReplInit(repl_agreement)
  137. # Check replication is working fine
  138. if master1.testReplication(DEFAULT_SUFFIX, master2):
  139. log.info('Replication is working.')
  140. else:
  141. log.fatal('Replication is not working.')
  142. assert False
  143. def fin():
  144. master1.delete()
  145. master2.delete()
  146. request.addfinalizer(fin)
  147. #
  148. # Here we have two instances master and consumer
  149. # with replication working. Either coming from a backup recovery
  150. # or from a fresh (re)init
  151. # Time to return the topology
  152. return TopologyMaster1Master2(master1, master2)
  153. def test_ticket47721_init(topology):
  154. """
  155. It adds
  156. - Objectclass with MAY 'member'
  157. - an entry ('bind_entry') with which we bind to test the 'SELFDN' operation
  158. It deletes the anonymous aci
  159. """
  160. # entry used to bind with
  161. topology.master1.log.info("Add %s" % BIND_DN)
  162. topology.master1.add_s(Entry((BIND_DN, {
  163. 'objectclass': "top person".split(),
  164. 'sn': BIND_NAME,
  165. 'cn': BIND_NAME,
  166. 'userpassword': BIND_PW})))
  167. # enable repl error logging
  168. mod = [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', str(8192))] # REPL logging
  169. topology.master1.modify_s(DN_CONFIG, mod)
  170. topology.master2.modify_s(DN_CONFIG, mod)
  171. # add dummy entries
  172. for cpt in range(MAX_OTHERS):
  173. name = "%s%d" % (OTHER_NAME, cpt)
  174. topology.master1.add_s(Entry(("cn=%s,%s" % (name, SUFFIX), {
  175. 'objectclass': "top person".split(),
  176. 'sn': name,
  177. 'cn': name})))
  178. def test_ticket47721_0(topology):
  179. dn = "cn=%s0,%s" % (OTHER_NAME, SUFFIX)
  180. loop = 0
  181. ent = None
  182. while loop <= 10:
  183. try:
  184. ent = topology.master2.getEntry(dn, ldap.SCOPE_BASE, "(objectclass=*)")
  185. break
  186. except ldap.NO_SUCH_OBJECT:
  187. time.sleep(1)
  188. loop += 1
  189. if ent is None:
  190. assert False
  191. def test_ticket47721_1(topology):
  192. log.info('Running test 1...')
  193. #topology.master1.log.info("Attach debugger\n\n")
  194. #time.sleep(30)
  195. new = _add_custom_at_definition()
  196. topology.master1.log.info("Add (M2) %s " % new)
  197. topology.master2.schema.add_schema('attributetypes', new)
  198. new = _chg_std_at_defintion()
  199. topology.master1.log.info("Chg (M2) %s " % new)
  200. topology.master2.schema.add_schema('attributetypes', new)
  201. new = _add_custom_oc_defintion()
  202. topology.master1.log.info("Add (M2) %s " % new)
  203. topology.master2.schema.add_schema('objectClasses', new)
  204. new = _chg_std_oc_defintion()
  205. topology.master1.log.info("Chg (M2) %s " % new)
  206. topology.master2.schema.add_schema('objectClasses', new)
  207. mod = [(ldap.MOD_REPLACE, 'description', 'Hello world 1')]
  208. dn = "cn=%s0,%s" % (OTHER_NAME, SUFFIX)
  209. topology.master2.modify_s(dn, mod)
  210. loop = 0
  211. while loop <= 10:
  212. try:
  213. ent = topology.master1.getEntry(dn, ldap.SCOPE_BASE, "(objectclass=*)")
  214. if ent.hasAttr('description') and (ent.getValue('description') == 'Hello world 1'):
  215. break
  216. except ldap.NO_SUCH_OBJECT:
  217. loop += 1
  218. time.sleep(1)
  219. assert loop <= 10
  220. time.sleep(2)
  221. schema_csn_master1 = topology.master1.schema.get_schema_csn()
  222. schema_csn_master2 = topology.master2.schema.get_schema_csn()
  223. log.debug('Master 1 schemaCSN: %s' % schema_csn_master1)
  224. log.debug('Master 2 schemaCSN: %s' % schema_csn_master2)
  225. def test_ticket47721_2(topology):
  226. log.info('Running test 2...')
  227. mod = [(ldap.MOD_REPLACE, 'description', 'Hello world 2')]
  228. dn = "cn=%s0,%s" % (OTHER_NAME, SUFFIX)
  229. topology.master1.modify_s(dn, mod)
  230. loop = 0
  231. while loop <= 10:
  232. try:
  233. ent = topology.master2.getEntry(dn, ldap.SCOPE_BASE, "(objectclass=*)")
  234. if ent.hasAttr('description') and (ent.getValue('description') == 'Hello world 2'):
  235. break
  236. except ldap.NO_SUCH_OBJECT:
  237. loop += 1
  238. time.sleep(1)
  239. assert loop <= 10
  240. time.sleep(2)
  241. schema_csn_master1 = topology.master1.schema.get_schema_csn()
  242. schema_csn_master2 = topology.master2.schema.get_schema_csn()
  243. log.debug('Master 1 schemaCSN: %s' % schema_csn_master1)
  244. log.debug('Master 2 schemaCSN: %s' % schema_csn_master2)
  245. if schema_csn_master1 != schema_csn_master2:
  246. # We need to give the server a little more time, then check it again
  247. log.info('Schema CSNs are not in sync yet: m1 (%s) vs m2 (%s), wait a little...'
  248. % (schema_csn_master1, schema_csn_master2))
  249. time.sleep(SLEEP_INTERVAL)
  250. schema_csn_master1 = topology.master1.schema.get_schema_csn()
  251. schema_csn_master2 = topology.master2.schema.get_schema_csn()
  252. assert schema_csn_master1 is not None
  253. assert schema_csn_master1 == schema_csn_master2
  254. def test_ticket47721_3(topology):
  255. '''
  256. Check that the supplier can update its schema from consumer schema
  257. Update M2 schema, then trigger a replication M1->M2
  258. '''
  259. log.info('Running test 3...')
  260. # stop RA M2->M1, so that M1 can only learn being a supplier
  261. ents = topology.master2.agreement.list(suffix=SUFFIX)
  262. assert len(ents) == 1
  263. topology.master2.agreement.pause(ents[0].dn)
  264. new = _add_custom_at_definition('ATtest3')
  265. topology.master1.log.info("Update schema (M2) %s " % new)
  266. topology.master2.schema.add_schema('attributetypes', new)
  267. new = _add_custom_oc_defintion('OCtest3')
  268. topology.master1.log.info("Update schema (M2) %s " % new)
  269. topology.master2.schema.add_schema('objectClasses', new)
  270. mod = [(ldap.MOD_REPLACE, 'description', 'Hello world 3')]
  271. dn = "cn=%s0,%s" % (OTHER_NAME, SUFFIX)
  272. topology.master1.modify_s(dn, mod)
  273. loop = 0
  274. while loop <= 10:
  275. try:
  276. ent = topology.master2.getEntry(dn, ldap.SCOPE_BASE, "(objectclass=*)")
  277. if ent.hasAttr('description') and (ent.getValue('description') == 'Hello world 3'):
  278. break
  279. except ldap.NO_SUCH_OBJECT:
  280. loop += 1
  281. time.sleep(1)
  282. assert loop <= 10
  283. time.sleep(2)
  284. schema_csn_master1 = topology.master1.schema.get_schema_csn()
  285. schema_csn_master2 = topology.master2.schema.get_schema_csn()
  286. log.debug('Master 1 schemaCSN: %s' % schema_csn_master1)
  287. log.debug('Master 2 schemaCSN: %s' % schema_csn_master2)
  288. if schema_csn_master1 == schema_csn_master2:
  289. # We need to give the server a little more time, then check it again
  290. log.info('Schema CSNs are not in sync yet: m1 (%s) vs m2 (%s), wait a little...'
  291. % (schema_csn_master1, schema_csn_master2))
  292. time.sleep(SLEEP_INTERVAL)
  293. schema_csn_master1 = topology.master1.schema.get_schema_csn()
  294. schema_csn_master2 = topology.master2.schema.get_schema_csn()
  295. assert schema_csn_master1 is not None
  296. # schema csn on M2 is larger that on M1. M1 only took the new definitions
  297. assert schema_csn_master1 != schema_csn_master2
  298. def test_ticket47721_4(topology):
  299. '''
  300. Here M2->M1 agreement is disabled.
  301. with test_ticket47721_3, M1 schema and M2 should be identical BUT
  302. the nsschemacsn is M2>M1. But as the RA M2->M1 is disabled, M1 keeps its schemacsn.
  303. Update schema on M2 (nsschemaCSN update), update M2. Check they have the same schemacsn
  304. '''
  305. log.info('Running test 4...')
  306. new = _add_custom_at_definition('ATtest4')
  307. topology.master1.log.info("Update schema (M1) %s " % new)
  308. topology.master1.schema.add_schema('attributetypes', new)
  309. new = _add_custom_oc_defintion('OCtest4')
  310. topology.master1.log.info("Update schema (M1) %s " % new)
  311. topology.master1.schema.add_schema('objectClasses', new)
  312. topology.master1.log.info("trigger replication M1->M2: to update the schema")
  313. mod = [(ldap.MOD_REPLACE, 'description', 'Hello world 4')]
  314. dn = "cn=%s0,%s" % (OTHER_NAME, SUFFIX)
  315. topology.master1.modify_s(dn, mod)
  316. loop = 0
  317. while loop <= 10:
  318. try:
  319. ent = topology.master2.getEntry(dn, ldap.SCOPE_BASE, "(objectclass=*)")
  320. if ent.hasAttr('description') and (ent.getValue('description') == 'Hello world 4'):
  321. break
  322. except ldap.NO_SUCH_OBJECT:
  323. loop += 1
  324. time.sleep(1)
  325. assert loop <= 10
  326. topology.master1.log.info("trigger replication M1->M2: to push the schema")
  327. mod = [(ldap.MOD_REPLACE, 'description', 'Hello world 5')]
  328. dn = "cn=%s0,%s" % (OTHER_NAME, SUFFIX)
  329. topology.master1.modify_s(dn, mod)
  330. loop = 0
  331. while loop <= 10:
  332. try:
  333. ent = topology.master2.getEntry(dn, ldap.SCOPE_BASE, "(objectclass=*)")
  334. if ent.hasAttr('description') and (ent.getValue('description') == 'Hello world 5'):
  335. break
  336. except ldap.NO_SUCH_OBJECT:
  337. loop += 1
  338. time.sleep(1)
  339. assert loop <= 10
  340. time.sleep(2)
  341. schema_csn_master1 = topology.master1.schema.get_schema_csn()
  342. schema_csn_master2 = topology.master2.schema.get_schema_csn()
  343. log.debug('Master 1 schemaCSN: %s' % schema_csn_master1)
  344. log.debug('Master 2 schemaCSN: %s' % schema_csn_master2)
  345. if schema_csn_master1 != schema_csn_master2:
  346. # We need to give the server a little more time, then check it again
  347. log.info('Schema CSNs are incorrectly in sync, wait a little...')
  348. time.sleep(SLEEP_INTERVAL)
  349. schema_csn_master1 = topology.master1.schema.get_schema_csn()
  350. schema_csn_master2 = topology.master2.schema.get_schema_csn()
  351. assert schema_csn_master1 is not None
  352. assert schema_csn_master1 == schema_csn_master2
  353. def test_ticket47721_final(topology):
  354. log.info('Testcase PASSED')
  355. def run_isolated():
  356. '''
  357. run_isolated is used to run these test cases independently of a test scheduler (xunit, py.test..)
  358. To run isolated without py.test, you need to
  359. - edit this file and comment '@pytest.fixture' line before 'topology' function.
  360. - set the installation prefix
  361. - run this program
  362. '''
  363. global installation1_prefix
  364. global installation2_prefix
  365. installation1_prefix = None
  366. installation2_prefix = None
  367. topo = topology(True)
  368. topo.master1.log.info("\n\n######################### Ticket 47721 ######################\n")
  369. test_ticket47721_init(topo)
  370. test_ticket47721_0(topo)
  371. test_ticket47721_1(topo)
  372. test_ticket47721_2(topo)
  373. test_ticket47721_3(topo)
  374. test_ticket47721_4(topo)
  375. test_ticket47721_final(topo)
  376. if __name__ == '__main__':
  377. run_isolated()