ticket47721_test.py 19 KB

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