ticket47721_test.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  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. master1.add_s(Entry((TEST_REPL_DN, {
  131. 'objectclass': "top person".split(),
  132. 'sn': 'test_repl',
  133. 'cn': 'test_repl'})))
  134. loop = 0
  135. ent = None
  136. while loop <= 10:
  137. try:
  138. ent = master2.getEntry(TEST_REPL_DN, ldap.SCOPE_BASE, "(objectclass=*)")
  139. break
  140. except ldap.NO_SUCH_OBJECT:
  141. time.sleep(1)
  142. loop += 1
  143. if ent is None:
  144. assert False
  145. # clear the tmp directory
  146. master1.clearTmpDir(__file__)
  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. topology.master1.delete()
  355. topology.master2.delete()
  356. log.info('Testcase PASSED')
  357. def run_isolated():
  358. '''
  359. run_isolated is used to run these test cases independently of a test scheduler (xunit, py.test..)
  360. To run isolated without py.test, you need to
  361. - edit this file and comment '@pytest.fixture' line before 'topology' function.
  362. - set the installation prefix
  363. - run this program
  364. '''
  365. global installation1_prefix
  366. global installation2_prefix
  367. installation1_prefix = None
  368. installation2_prefix = None
  369. topo = topology(True)
  370. topo.master1.log.info("\n\n######################### Ticket 47721 ######################\n")
  371. test_ticket47721_init(topo)
  372. test_ticket47721_0(topo)
  373. test_ticket47721_1(topo)
  374. test_ticket47721_2(topo)
  375. test_ticket47721_3(topo)
  376. test_ticket47721_4(topo)
  377. test_ticket47721_final(topo)
  378. if __name__ == '__main__':
  379. run_isolated()