ticket47573_test.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. # --- BEGIN COPYRIGHT BLOCK ---
  2. # Copyright (C) 2016 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 logging
  14. import re
  15. import time
  16. import ldap
  17. import pytest
  18. from lib389 import Entry
  19. from lib389._constants import *
  20. from lib389.topologies import topology_m1c1
  21. logging.getLogger(__name__).setLevel(logging.DEBUG)
  22. log = logging.getLogger(__name__)
  23. TEST_REPL_DN = "cn=test_repl, %s" % SUFFIX
  24. ENTRY_DN = "cn=test_entry, %s" % SUFFIX
  25. MUST_OLD = "(postalAddress $ preferredLocale $ telexNumber)"
  26. MAY_OLD = "(postalCode $ street)"
  27. MUST_NEW = "(postalAddress $ preferredLocale)"
  28. MAY_NEW = "(telexNumber $ postalCode $ street)"
  29. def pattern_errorlog(file, log_pattern):
  30. try:
  31. pattern_errorlog.last_pos += 1
  32. except AttributeError:
  33. pattern_errorlog.last_pos = 0
  34. found = None
  35. log.debug("_pattern_errorlog: start at offset %d" % pattern_errorlog.last_pos)
  36. file.seek(pattern_errorlog.last_pos)
  37. # Use a while true iteration because 'for line in file: hit a
  38. # python bug that break file.tell()
  39. while True:
  40. line = file.readline()
  41. log.debug("_pattern_errorlog: [%d] %s" % (file.tell(), line))
  42. found = log_pattern.search(line)
  43. if ((line == '') or (found)):
  44. break
  45. log.debug("_pattern_errorlog: end at offset %d" % file.tell())
  46. pattern_errorlog.last_pos = file.tell()
  47. return found
  48. def _oc_definition(oid_ext, name, must=None, may=None):
  49. oid = "1.2.3.4.5.6.7.8.9.10.%d" % oid_ext
  50. desc = 'To test ticket 47573'
  51. sup = 'person'
  52. if not must:
  53. must = MUST_OLD
  54. if not may:
  55. may = MAY_OLD
  56. new_oc = "( %s NAME '%s' DESC '%s' SUP %s AUXILIARY MUST %s MAY %s )" % (oid, name, desc, sup, must, may)
  57. return new_oc
  58. def add_OC(instance, oid_ext, name):
  59. new_oc = _oc_definition(oid_ext, name)
  60. instance.schema.add_schema('objectClasses', new_oc)
  61. def mod_OC(instance, oid_ext, name, old_must=None, old_may=None, new_must=None, new_may=None):
  62. old_oc = _oc_definition(oid_ext, name, old_must, old_may)
  63. new_oc = _oc_definition(oid_ext, name, new_must, new_may)
  64. instance.schema.del_schema('objectClasses', old_oc)
  65. instance.schema.add_schema('objectClasses', new_oc)
  66. def trigger_schema_push(topology_m1c1):
  67. """
  68. It triggers an update on the supplier. This will start a replication
  69. session and a schema push
  70. """
  71. try:
  72. trigger_schema_push.value += 1
  73. except AttributeError:
  74. trigger_schema_push.value = 1
  75. replace = [(ldap.MOD_REPLACE, 'telephonenumber', str(trigger_schema_push.value))]
  76. topology_m1c1.ms["master1"].modify_s(ENTRY_DN, replace)
  77. # wait 10 seconds that the update is replicated
  78. loop = 0
  79. while loop <= 10:
  80. try:
  81. ent = topology_m1c1.cs["consumer1"].getEntry(ENTRY_DN, ldap.SCOPE_BASE, "(objectclass=*)",
  82. ['telephonenumber'])
  83. val = ent.telephonenumber or "0"
  84. if int(val) == trigger_schema_push.value:
  85. return
  86. # the expected value is not yet replicated. try again
  87. time.sleep(1)
  88. loop += 1
  89. log.debug("trigger_schema_push: receive %s (expected %d)" % (val, trigger_schema_push.value))
  90. except ldap.NO_SUCH_OBJECT:
  91. time.sleep(1)
  92. loop += 1
  93. def test_ticket47573_init(topology_m1c1):
  94. """
  95. Initialize the test environment
  96. """
  97. log.debug("test_ticket47573_init topology_m1c1 %r (master %r, consumer %r" %
  98. (topology_m1c1, topology_m1c1.ms["master1"], topology_m1c1.cs["consumer1"]))
  99. # the test case will check if a warning message is logged in the
  100. # error log of the supplier
  101. topology_m1c1.ms["master1"].errorlog_file = open(topology_m1c1.ms["master1"].errlog, "r")
  102. # This entry will be used to trigger attempt of schema push
  103. topology_m1c1.ms["master1"].add_s(Entry((ENTRY_DN, {
  104. 'objectclass': "top person".split(),
  105. 'sn': 'test_entry',
  106. 'cn': 'test_entry'})))
  107. def test_ticket47573_one(topology_m1c1):
  108. """
  109. Summary: Add a custom OC with MUST and MAY
  110. MUST = postalAddress $ preferredLocale
  111. MAY = telexNumber $ postalCode $ street
  112. Final state
  113. - supplier +OCwithMayAttr
  114. - consumer +OCwithMayAttr
  115. """
  116. log.debug("test_ticket47573_one topology_m1c1 %r (master %r, consumer %r" % (
  117. topology_m1c1, topology_m1c1.ms["master1"], topology_m1c1.cs["consumer1"]))
  118. # update the schema of the supplier so that it is a superset of
  119. # consumer. Schema should be pushed
  120. new_oc = _oc_definition(2, 'OCwithMayAttr',
  121. must=MUST_OLD,
  122. may=MAY_OLD)
  123. topology_m1c1.ms["master1"].schema.add_schema('objectClasses', new_oc)
  124. trigger_schema_push(topology_m1c1)
  125. master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn()
  126. consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn()
  127. # Check the schemaCSN was updated on the consumer
  128. log.debug("test_ticket47573_one master_schema_csn=%s", master_schema_csn)
  129. log.debug("ctest_ticket47573_one onsumer_schema_csn=%s", consumer_schema_csn)
  130. assert master_schema_csn == consumer_schema_csn
  131. # Check the error log of the supplier does not contain an error
  132. regex = re.compile("must not be overwritten \(set replication log for additional info\)")
  133. res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex)
  134. assert res is None
  135. def test_ticket47573_two(topology_m1c1):
  136. """
  137. Summary: Change OCwithMayAttr to move a MAY attribute to a MUST attribute
  138. Final state
  139. - supplier OCwithMayAttr updated
  140. - consumer OCwithMayAttr updated
  141. """
  142. # Update the objectclass so that a MAY attribute is moved to MUST attribute
  143. mod_OC(topology_m1c1.ms["master1"], 2, 'OCwithMayAttr', old_must=MUST_OLD, new_must=MUST_NEW, old_may=MAY_OLD,
  144. new_may=MAY_NEW)
  145. # now push the scheam
  146. trigger_schema_push(topology_m1c1)
  147. master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn()
  148. consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn()
  149. # Check the schemaCSN was NOT updated on the consumer
  150. log.debug("test_ticket47573_two master_schema_csn=%s", master_schema_csn)
  151. log.debug("test_ticket47573_two consumer_schema_csn=%s", consumer_schema_csn)
  152. assert master_schema_csn == consumer_schema_csn
  153. # Check the error log of the supplier does not contain an error
  154. regex = re.compile("must not be overwritten \(set replication log for additional info\)")
  155. res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex)
  156. assert res is None
  157. def test_ticket47573_three(topology_m1c1):
  158. '''
  159. Create a entry with OCwithMayAttr OC
  160. '''
  161. # Check replication is working fine
  162. dn = "cn=ticket47573, %s" % SUFFIX
  163. topology_m1c1.ms["master1"].add_s(Entry((dn,
  164. {'objectclass': "top person OCwithMayAttr".split(),
  165. 'sn': 'test_repl',
  166. 'cn': 'test_repl',
  167. 'postalAddress': 'here',
  168. 'preferredLocale': 'en',
  169. 'telexNumber': '12$us$21',
  170. 'postalCode': '54321'})))
  171. loop = 0
  172. ent = None
  173. while loop <= 10:
  174. try:
  175. ent = topology_m1c1.cs["consumer1"].getEntry(dn, ldap.SCOPE_BASE, "(objectclass=*)")
  176. break
  177. except ldap.NO_SUCH_OBJECT:
  178. time.sleep(1)
  179. loop += 1
  180. if ent is None:
  181. assert False
  182. log.info('Testcase PASSED')
  183. if __name__ == '__main__':
  184. # Run isolated
  185. # -s for DEBUG mode
  186. CURRENT_FILE = os.path.realpath(__file__)
  187. pytest.main("-s %s" % CURRENT_FILE)