ticket47721_test.py 16 KB

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