zonemgr_test.py 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  1. # Copyright (C) 2010 Internet Systems Consortium.
  2. #
  3. # Permission to use, copy, modify, and distribute this software for any
  4. # purpose with or without fee is hereby granted, provided that the above
  5. # copyright notice and this permission notice appear in all copies.
  6. #
  7. # THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
  8. # DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
  9. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
  10. # INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
  11. # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
  12. # FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  13. # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
  14. # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. '''Tests for the ZonemgrRefresh and Zonemgr classes '''
  16. import unittest
  17. import os
  18. import tempfile
  19. from zonemgr import *
  20. from isc.testutils.ccsession_mock import MockModuleCCSession
  21. from isc.notify import notify_out
  22. ZONE_NAME_CLASS1_IN = ("example.net.", "IN")
  23. ZONE_NAME_CLASS1_CH = ("example.net.", "CH")
  24. ZONE_NAME_CLASS2_IN = ("example.org.", "IN")
  25. ZONE_NAME_CLASS2_CH = ("example.org.", "CH")
  26. ZONE_NAME_CLASS3_IN = ("example.com.", "IN")
  27. ZONE_NAME_CLASS3_CH = ("example.com.", "CH")
  28. MAX_TRANSFER_TIMEOUT = 14400
  29. LOWERBOUND_REFRESH = 10
  30. LOWERBOUND_RETRY = 5
  31. REFRESH_JITTER = 0.10
  32. RELOAD_JITTER = 0.75
  33. TEST_SQLITE3_DBFILE = os.getenv("TESTDATAOBJDIR") + '/initdb.file'
  34. class ZonemgrTestException(Exception):
  35. pass
  36. class MySession():
  37. def __init__(self):
  38. pass
  39. def group_sendmsg(self, msg, module_name):
  40. if module_name not in ("Auth", "Xfrin"):
  41. raise ZonemgrTestException("module name not exist")
  42. def group_recvmsg(self, nonblock, seq):
  43. return None, None
  44. class FakeCCSession(isc.config.ConfigData, MockModuleCCSession):
  45. def __init__(self):
  46. module_spec = isc.config.module_spec_from_file(SPECFILE_LOCATION)
  47. ConfigData.__init__(self, module_spec)
  48. MockModuleCCSession.__init__(self)
  49. def get_remote_config_value(self, module_name, identifier):
  50. if module_name == "Auth" and identifier == "database_file":
  51. return TEST_SQLITE3_DBFILE, False
  52. else:
  53. return "unknown", False
  54. class MyZonemgrRefresh(ZonemgrRefresh):
  55. def __init__(self):
  56. self._master_socket, self._slave_socket = socket.socketpair()
  57. self._zonemgr_refresh_info = {}
  58. self._lowerbound_refresh = 10
  59. self._lowerbound_retry = 5
  60. self._reload_jitter = 0.75
  61. self._refresh_jitter = 0.25
  62. def get_zone_soa(zone_name, db_file):
  63. if zone_name == 'example.net.':
  64. return (1, 2, 'example.net.', 'example.net.sd.', 21600, 'SOA', None,
  65. 'a.example.net. root.example.net. 2009073106 7200 3600 2419200 21600')
  66. elif zone_name == 'example.org.':
  67. return (1, 2, 'example.org.', 'example.org.sd.', 21600, 'SOA', None,
  68. 'a.example.org. root.example.org. 2009073112 7200 3600 2419200 21600')
  69. else:
  70. return None
  71. sqlite3_ds.get_zone_soa = get_zone_soa
  72. ZonemgrRefresh.__init__(self, MySession(), TEST_SQLITE3_DBFILE,
  73. self._slave_socket, FakeCCSession())
  74. current_time = time.time()
  75. self._zonemgr_refresh_info = {
  76. ('example.net.', 'IN'): {
  77. 'last_refresh_time': current_time,
  78. 'next_refresh_time': current_time + 6500,
  79. 'zone_soa_rdata': 'a.example.net. root.example.net. 2009073105 7200 3600 2419200 21600',
  80. 'zone_state': 0},
  81. ('example.org.', 'CH'): {
  82. 'last_refresh_time': current_time,
  83. 'next_refresh_time': current_time + 6900,
  84. 'zone_soa_rdata': 'a.example.org. root.example.org. 2009073112 7200 3600 2419200 21600',
  85. 'zone_state': 0}
  86. }
  87. class TestZonemgrRefresh(unittest.TestCase):
  88. def setUp(self):
  89. if os.path.exists(TEST_SQLITE3_DBFILE):
  90. os.unlink(TEST_SQLITE3_DBFILE)
  91. self.stderr_backup = sys.stderr
  92. sys.stderr = open(os.devnull, 'w')
  93. self.zone_refresh = MyZonemgrRefresh()
  94. self.cc_session = FakeCCSession()
  95. def tearDown(self):
  96. if os.path.exists(TEST_SQLITE3_DBFILE):
  97. os.unlink(TEST_SQLITE3_DBFILE)
  98. sys.stderr.close()
  99. sys.stderr = self.stderr_backup
  100. def test_random_jitter(self):
  101. max = 100025.120
  102. jitter = 0
  103. self.assertEqual(max, self.zone_refresh._random_jitter(max, jitter))
  104. jitter = 0.3 * max
  105. for i in range (0, 150):
  106. self.assertTrue((max - jitter) <= self.zone_refresh._random_jitter(max, jitter))
  107. self.assertTrue(self.zone_refresh._random_jitter(max, jitter) <= max)
  108. i += 1;
  109. def test_get_current_time(self):
  110. pass
  111. def test_set_zone_timer(self):
  112. max = 3600
  113. jitter = 900
  114. time1 = time.time()
  115. self.zone_refresh._set_zone_timer(ZONE_NAME_CLASS1_IN, 3600, 900)
  116. time2 = time.time()
  117. zone_timeout = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["next_refresh_time"]
  118. self.assertTrue((3600 - 900) <= (zone_timeout - time1))
  119. self.assertTrue((zone_timeout - time2) <= 3600)
  120. def test_set_zone_refresh_timer(self):
  121. time1 = time.time()
  122. self.zone_refresh._set_zone_refresh_timer(ZONE_NAME_CLASS1_IN)
  123. zone_timeout = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["next_refresh_time"]
  124. time2 = time.time()
  125. self.assertTrue((time1 + 7200 * (1 - self.zone_refresh._refresh_jitter)) <= zone_timeout)
  126. self.assertTrue(zone_timeout <= time2 + 7200)
  127. def test_set_zone_retry_timer(self):
  128. time1 = time.time()
  129. self.zone_refresh._set_zone_retry_timer(ZONE_NAME_CLASS1_IN)
  130. zone_timeout = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["next_refresh_time"]
  131. time2 = time.time()
  132. self.assertTrue((time1 + 3600 * (1 - self.zone_refresh._refresh_jitter)) <= zone_timeout)
  133. self.assertTrue(zone_timeout <= time2 + 3600)
  134. # No soa rdata
  135. self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_soa_rdata"] = None
  136. time3 = time.time()
  137. self.zone_refresh._set_zone_retry_timer(ZONE_NAME_CLASS1_IN)
  138. zone_timeout = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["next_refresh_time"]
  139. time4 = time.time()
  140. self.assertTrue((time3 + self.zone_refresh._lowerbound_retry * (1 - self.zone_refresh._refresh_jitter))
  141. <= zone_timeout)
  142. self.assertTrue(zone_timeout <= time4 + self.zone_refresh._lowerbound_retry)
  143. def test_zone_not_exist(self):
  144. self.assertFalse(self.zone_refresh._zone_not_exist(ZONE_NAME_CLASS1_IN))
  145. self.assertTrue(self.zone_refresh._zone_not_exist(ZONE_NAME_CLASS1_CH))
  146. self.assertFalse(self.zone_refresh._zone_not_exist(ZONE_NAME_CLASS2_CH))
  147. self.assertTrue(self.zone_refresh._zone_not_exist(ZONE_NAME_CLASS2_IN))
  148. self.assertTrue(self.zone_refresh._zone_not_exist(ZONE_NAME_CLASS3_IN))
  149. self.assertTrue(self.zone_refresh._zone_not_exist(ZONE_NAME_CLASS3_CH))
  150. def test_set_zone_notify_timer(self):
  151. time1 = time.time()
  152. self.zone_refresh._set_zone_notify_timer(ZONE_NAME_CLASS1_IN)
  153. zone_timeout = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["next_refresh_time"]
  154. time2 = time.time()
  155. self.assertTrue(time1 <= zone_timeout)
  156. self.assertTrue(zone_timeout <= time2)
  157. def test_zone_is_expired(self):
  158. current_time = time.time()
  159. zone_expired_time = 2419200
  160. self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["last_refresh_time"] = \
  161. current_time - zone_expired_time - 1
  162. self.assertTrue(self.zone_refresh._zone_is_expired(ZONE_NAME_CLASS1_IN))
  163. self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["last_refresh_time"] = \
  164. current_time - zone_expired_time + 1
  165. self.assertFalse(self.zone_refresh._zone_is_expired(ZONE_NAME_CLASS1_IN))
  166. self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"] = ZONE_EXPIRED
  167. self.assertTrue(self.zone_refresh._zone_is_expired(ZONE_NAME_CLASS1_IN))
  168. def test_get_zone_soa_rdata(self):
  169. soa_rdata1 = 'a.example.net. root.example.net. 2009073105 7200 3600 2419200 21600'
  170. soa_rdata2 = 'a.example.org. root.example.org. 2009073112 7200 3600 2419200 21600'
  171. self.assertEqual(soa_rdata1, self.zone_refresh._get_zone_soa_rdata(ZONE_NAME_CLASS1_IN))
  172. self.assertRaises(KeyError, self.zone_refresh._get_zone_soa_rdata, ZONE_NAME_CLASS1_CH)
  173. self.assertEqual(soa_rdata2, self.zone_refresh._get_zone_soa_rdata(ZONE_NAME_CLASS2_CH))
  174. self.assertRaises(KeyError, self.zone_refresh._get_zone_soa_rdata, ZONE_NAME_CLASS2_IN)
  175. def test_zonemgr_reload_zone(self):
  176. soa_rdata = 'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600'
  177. # We need to restore this not to harm other tests
  178. old_get_zone_soa = sqlite3_ds.get_zone_soa
  179. def get_zone_soa(zone_name, db_file):
  180. return (1, 2, 'example.net.', 'example.net.sd.', 21600, 'SOA', None,
  181. 'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600')
  182. sqlite3_ds.get_zone_soa = get_zone_soa
  183. self.zone_refresh.zonemgr_reload_zone(ZONE_NAME_CLASS1_IN)
  184. self.assertEqual(soa_rdata, self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_soa_rdata"])
  185. sqlite3_ds.get_zone_soa = old_get_zone_soa
  186. def test_get_zone_notifier_master(self):
  187. notify_master = "192.168.1.1"
  188. self.assertEqual(None, self.zone_refresh._get_zone_notifier_master(ZONE_NAME_CLASS1_IN))
  189. self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["notify_master"] = notify_master
  190. self.assertEqual(notify_master, self.zone_refresh._get_zone_notifier_master(ZONE_NAME_CLASS1_IN))
  191. def test_set_zone_notifier_master(self):
  192. notify_master = "192.168.1.1"
  193. self.zone_refresh._set_zone_notifier_master(ZONE_NAME_CLASS1_IN, notify_master)
  194. self.assertEqual(self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]\
  195. ["notify_master"], notify_master)
  196. def test_clear_zone_notifier_master(self):
  197. notify_master = "192.168.1.1"
  198. self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["notify_master"] = notify_master
  199. self.zone_refresh._clear_zone_notifier_master(ZONE_NAME_CLASS1_IN)
  200. self.assertFalse("notify_master" in self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN].keys())
  201. self.zone_refresh._clear_zone_notifier_master(ZONE_NAME_CLASS2_CH)
  202. self.assertFalse("notify_master" in self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS2_CH].keys())
  203. def test_get_zone_state(self):
  204. self.assertEqual(ZONE_OK, self.zone_refresh._get_zone_state(ZONE_NAME_CLASS1_IN))
  205. self.assertEqual(ZONE_OK, self.zone_refresh._get_zone_state(ZONE_NAME_CLASS2_CH))
  206. def test_set_zone_state(self):
  207. self.zone_refresh._set_zone_state(ZONE_NAME_CLASS1_IN, ZONE_REFRESHING)
  208. self.zone_refresh._set_zone_state(ZONE_NAME_CLASS2_CH, ZONE_EXPIRED)
  209. self.assertEqual(ZONE_REFRESHING, \
  210. self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"])
  211. self.assertEqual(ZONE_EXPIRED, \
  212. self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS2_CH]["zone_state"])
  213. def test_get_zone_refresh_timeout(self):
  214. current_time = time.time()
  215. self.assertFalse("refresh_timeout" in self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN].keys())
  216. self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["refresh_timeout"] = current_time
  217. self.assertEqual(current_time, self.zone_refresh._get_zone_refresh_timeout(ZONE_NAME_CLASS1_IN))
  218. def test_set_zone_refresh_timeout(self):
  219. current_time = time.time()
  220. self.zone_refresh._set_zone_refresh_timeout(ZONE_NAME_CLASS1_IN, current_time)
  221. refresh_time = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["refresh_timeout"]
  222. self.assertEqual(current_time, refresh_time)
  223. def test_get_zone_next_refresh_time(self):
  224. current_time = time.time()
  225. self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["next_refresh_time"] = current_time
  226. self.assertEqual(current_time, self.zone_refresh._get_zone_next_refresh_time(ZONE_NAME_CLASS1_IN))
  227. def test_set_zone_next_refresh_time(self):
  228. current_time = time.time()
  229. self.zone_refresh._set_zone_next_refresh_time(ZONE_NAME_CLASS1_IN, current_time)
  230. next_refresh_time = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["next_refresh_time"]
  231. self.assertEqual(current_time, next_refresh_time)
  232. def test_get_zone_last_refresh_time(self):
  233. current_time = time.time()
  234. self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["last_refresh_time"] = current_time
  235. self.assertEqual(current_time, self.zone_refresh._get_zone_last_refresh_time(ZONE_NAME_CLASS1_IN))
  236. def test_set_zone_last_refresh_time(self):
  237. current_time = time.time()
  238. self.zone_refresh._set_zone_last_refresh_time(ZONE_NAME_CLASS1_IN, current_time)
  239. last_refresh_time = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["last_refresh_time"]
  240. self.assertEqual(current_time, last_refresh_time)
  241. def test_send_command(self):
  242. self.assertRaises(ZonemgrTestException, self.zone_refresh._send_command, "Unknown", "Notify", None)
  243. def test_zone_mgr_is_empty(self):
  244. self.assertFalse(self.zone_refresh._zone_mgr_is_empty())
  245. self.zone_refresh._zonemgr_refresh_info = {}
  246. self.assertTrue(self.zone_refresh._zone_mgr_is_empty())
  247. def test_zonemgr_add_zone(self):
  248. soa_rdata = 'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600'
  249. # This needs to be restored. The following test actually failed if we left
  250. # this unclean
  251. old_get_zone_soa = sqlite3_ds.get_zone_soa
  252. time1 = time.time()
  253. def get_zone_soa(zone_name, db_file):
  254. return (1, 2, 'example.net.', 'example.net.sd.', 21600, 'SOA', None,
  255. 'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600')
  256. sqlite3_ds.get_zone_soa = get_zone_soa
  257. self.zone_refresh._zonemgr_refresh_info = {}
  258. self.zone_refresh.zonemgr_add_zone(ZONE_NAME_CLASS1_IN)
  259. self.assertEqual(1, len(self.zone_refresh._zonemgr_refresh_info))
  260. zone_soa_rdata = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_soa_rdata"]
  261. self.assertEqual(soa_rdata, zone_soa_rdata)
  262. self.assertEqual(ZONE_OK, self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"])
  263. self.assertTrue("last_refresh_time" in self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN].keys())
  264. self.assertTrue("next_refresh_time" in self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN].keys())
  265. time2 = time.time()
  266. zone_timeout = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["next_refresh_time"]
  267. self.assertTrue((time1 + 900 * (1 - self.zone_refresh._reload_jitter)) <= zone_timeout)
  268. self.assertTrue(zone_timeout <= time2 + 900)
  269. def get_zone_soa2(zone_name, db_file):
  270. return None
  271. sqlite3_ds.get_zone_soa = get_zone_soa2
  272. self.zone_refresh.zonemgr_add_zone(ZONE_NAME_CLASS2_IN)
  273. self.assertTrue(self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS2_IN]["zone_soa_rdata"] is None)
  274. sqlite3_ds.get_zone_soa = old_get_zone_soa
  275. def test_zone_handle_notify(self):
  276. self.zone_refresh.zone_handle_notify(ZONE_NAME_CLASS1_IN,"127.0.0.1")
  277. notify_master = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["notify_master"]
  278. self.assertEqual("127.0.0.1", notify_master)
  279. zone_timeout = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["next_refresh_time"]
  280. current_time = time.time()
  281. self.assertTrue(zone_timeout <= current_time)
  282. self.assertRaises(ZonemgrException, self.zone_refresh.zone_handle_notify,\
  283. ZONE_NAME_CLASS3_CH, "127.0.0.1")
  284. self.assertRaises(ZonemgrException, self.zone_refresh.zone_handle_notify,\
  285. ZONE_NAME_CLASS3_IN, "127.0.0.1")
  286. def test_zone_refresh_success(self):
  287. soa_rdata = 'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600'
  288. def get_zone_soa(zone_name, db_file):
  289. return (1, 2, 'example.net.', 'example.net.sd.', 21600, 'SOA', None,
  290. 'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600')
  291. sqlite3_ds.get_zone_soa = get_zone_soa
  292. time1 = time.time()
  293. self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"] = ZONE_REFRESHING
  294. self.zone_refresh.zone_refresh_success(ZONE_NAME_CLASS1_IN)
  295. time2 = time.time()
  296. zone_soa_rdata = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_soa_rdata"]
  297. self.assertEqual(soa_rdata, zone_soa_rdata)
  298. next_refresh_time = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["next_refresh_time"]
  299. self.assertTrue((time1 + 1800 * (1 - self.zone_refresh._refresh_jitter)) <= next_refresh_time)
  300. self.assertTrue(next_refresh_time <= time2 + 1800)
  301. self.assertEqual(ZONE_OK, self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"])
  302. last_refresh_time = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["last_refresh_time"]
  303. self.assertTrue(time1 <= last_refresh_time)
  304. self.assertTrue(last_refresh_time <= time2)
  305. self.assertRaises(ZonemgrException, self.zone_refresh.zone_refresh_success, ("example.test.", "CH"))
  306. self.assertRaises(ZonemgrException, self.zone_refresh.zone_refresh_success, ZONE_NAME_CLASS3_IN)
  307. def test_zone_refresh_fail(self):
  308. soa_rdata = 'a.example.net. root.example.net. 2009073105 7200 3600 2419200 21600'
  309. time1 = time.time()
  310. self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"] = ZONE_REFRESHING
  311. self.zone_refresh.zone_refresh_fail(ZONE_NAME_CLASS1_IN)
  312. time2 = time.time()
  313. zone_soa_rdata = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_soa_rdata"]
  314. self.assertEqual(soa_rdata, zone_soa_rdata)
  315. next_refresh_time = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["next_refresh_time"]
  316. self.assertTrue(((time1 + 3600 * (1 - self.zone_refresh._refresh_jitter))) <= next_refresh_time)
  317. self.assertTrue(next_refresh_time <= time2 + 3600)
  318. self.assertEqual(ZONE_OK, self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"])
  319. self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["last_refresh_time"] = time1 - 2419200
  320. self.zone_refresh.zone_refresh_fail(ZONE_NAME_CLASS1_IN)
  321. self.assertEqual(ZONE_EXPIRED, self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"])
  322. self.assertRaises(ZonemgrException, self.zone_refresh.zone_refresh_fail, ZONE_NAME_CLASS3_CH)
  323. self.assertRaises(ZonemgrException, self.zone_refresh.zone_refresh_fail, ZONE_NAME_CLASS3_IN)
  324. old_get_zone_soa = sqlite3_ds.get_zone_soa
  325. def get_zone_soa(zone_name, db_file):
  326. return None
  327. sqlite3_ds.get_zone_soa = get_zone_soa
  328. self.zone_refresh.zone_refresh_fail(ZONE_NAME_CLASS1_IN)
  329. self.assertEqual(self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"],
  330. ZONE_EXPIRED)
  331. sqlite3_ds.get_zone_soa = old_get_zone_soa
  332. def test_find_need_do_refresh_zone(self):
  333. time1 = time.time()
  334. self.zone_refresh._zonemgr_refresh_info = {
  335. ("example.net.","IN"):{
  336. 'last_refresh_time': time1,
  337. 'next_refresh_time': time1 + 7200,
  338. 'zone_soa_rdata': 'a.example.net. root.example.net. 2009073105 7200 3600 2419200 21600',
  339. 'zone_state': ZONE_OK},
  340. ("example.org.","CH"):{
  341. 'last_refresh_time': time1 - 7200,
  342. 'next_refresh_time': time1,
  343. 'refresh_timeout': time1 + MAX_TRANSFER_TIMEOUT,
  344. 'zone_soa_rdata': 'a.example.org. root.example.org. 2009073112 7200 3600 2419200 21600',
  345. 'zone_state': ZONE_REFRESHING}
  346. }
  347. zone_need_refresh = self.zone_refresh._find_need_do_refresh_zone()
  348. self.assertEqual(ZONE_NAME_CLASS1_IN, zone_need_refresh)
  349. self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS2_CH]["refresh_timeout"] = time1
  350. zone_need_refresh = self.zone_refresh._find_need_do_refresh_zone()
  351. self.assertEqual(ZONE_NAME_CLASS2_CH, zone_need_refresh)
  352. def test_do_refresh(self):
  353. time1 = time.time()
  354. self.zone_refresh._zonemgr_refresh_info = {
  355. ("example.net.", "IN"):{
  356. 'last_refresh_time': time1 - 7200,
  357. 'next_refresh_time': time1 - 1,
  358. 'zone_soa_rdata': 'a.example.net. root.example.net. 2009073105 7200 3600 2419200 21600',
  359. 'zone_state': ZONE_OK}
  360. }
  361. self.zone_refresh._do_refresh(ZONE_NAME_CLASS1_IN)
  362. time2 = time.time()
  363. zone_state = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"]
  364. self.assertEqual(ZONE_REFRESHING, zone_state)
  365. refresh_timeout = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["refresh_timeout"]
  366. self.assertTrue(time1 + MAX_TRANSFER_TIMEOUT <= refresh_timeout)
  367. self.assertTrue(time2 + MAX_TRANSFER_TIMEOUT >= refresh_timeout)
  368. self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["notify_master"] = "127.0.0.1"
  369. self.zone_refresh._do_refresh(ZONE_NAME_CLASS1_IN)
  370. time2 = time.time()
  371. zone_state = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"]
  372. self.assertEqual(ZONE_REFRESHING, zone_state)
  373. refresh_timeout = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["refresh_timeout"]
  374. self.assertTrue(time1 + MAX_TRANSFER_TIMEOUT <= refresh_timeout)
  375. self.assertTrue(time2 + MAX_TRANSFER_TIMEOUT >= refresh_timeout)
  376. self.assertFalse("notify_master" in self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN].keys())
  377. def test_run_timer(self):
  378. """This case will run timer in daemon thread.
  379. The zone's next_refresh_time is less than now, so zonemgr will do zone refresh
  380. immediately. The zone's state will become "refreshing".
  381. """
  382. time1 = time.time()
  383. self.zone_refresh._zonemgr_refresh_info = {
  384. ("example.net.", "IN"):{
  385. 'last_refresh_time': time1 - 7200,
  386. 'next_refresh_time': time1 - 1,
  387. 'zone_soa_rdata': 'a.example.net. root.example.net. 2009073105 7200 3600 2419200 21600',
  388. 'zone_state': ZONE_OK}
  389. }
  390. self.zone_refresh._check_sock = self.zone_refresh._master_socket
  391. listener = self.zone_refresh.run_timer(daemon=True)
  392. # Shut down the timer thread
  393. self.zone_refresh.shutdown()
  394. # After running timer, the zone's state should become "refreshing".
  395. zone_state = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"]
  396. self.assertTrue("refresh_timeout" in self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN].keys())
  397. self.assertTrue(zone_state == ZONE_REFRESHING)
  398. def test_update_config_data(self):
  399. # make sure it doesn't fail if we only provide secondary zones
  400. config_data = {
  401. "secondary_zones": [ { "name": "example.net.",
  402. "class": "IN" } ]
  403. }
  404. self.zone_refresh.update_config_data(config_data, self.cc_session)
  405. self.assertTrue(("example.net.", "IN") in
  406. self.zone_refresh._zonemgr_refresh_info)
  407. # make sure it does fail if we don't provide a name
  408. config_data = {
  409. "secondary_zones": [ { "class": "IN" } ]
  410. }
  411. self.assertRaises(ZonemgrException,
  412. self.zone_refresh.update_config_data,
  413. config_data, self.cc_session)
  414. # But not if we don't provide a class
  415. config_data = {
  416. "secondary_zones": [ { "name": "example.net." } ]
  417. }
  418. self.zone_refresh.update_config_data(config_data, self.cc_session)
  419. self.assertTrue(("example.net.", "IN") in
  420. self.zone_refresh._zonemgr_refresh_info)
  421. # update all values
  422. config_data = {
  423. "lowerbound_refresh" : 60,
  424. "lowerbound_retry" : 30,
  425. "max_transfer_timeout" : 19800,
  426. "refresh_jitter" : 0.25,
  427. "reload_jitter" : 0.75,
  428. "secondary_zones": []
  429. }
  430. self.zone_refresh.update_config_data(config_data, self.cc_session)
  431. self.assertEqual(60, self.zone_refresh._lowerbound_refresh)
  432. self.assertEqual(30, self.zone_refresh._lowerbound_retry)
  433. self.assertEqual(19800, self.zone_refresh._max_transfer_timeout)
  434. self.assertEqual(0.25, self.zone_refresh._refresh_jitter)
  435. self.assertEqual(0.75, self.zone_refresh._reload_jitter)
  436. # make sure they are not reset when we only update one
  437. config_data = {
  438. "reload_jitter" : 0.35,
  439. }
  440. self.zone_refresh.update_config_data(config_data, self.cc_session)
  441. self.assertEqual(60, self.zone_refresh._lowerbound_refresh)
  442. self.assertEqual(30, self.zone_refresh._lowerbound_retry)
  443. self.assertEqual(19800, self.zone_refresh._max_transfer_timeout)
  444. self.assertEqual(0.25, self.zone_refresh._refresh_jitter)
  445. self.assertEqual(0.35, self.zone_refresh._reload_jitter)
  446. # and make sure we restore the previous config if something
  447. # goes wrong
  448. config_data = {
  449. "lowerbound_refresh" : 61,
  450. "lowerbound_retry" : 31,
  451. "max_transfer_timeout" : 19801,
  452. "refresh_jitter" : 0.21,
  453. "reload_jitter" : 0.71,
  454. "secondary_zones": [ { "name": "doesnotexist",
  455. "class": "IN" } ]
  456. }
  457. self.zone_refresh.update_config_data(config_data, self.cc_session)
  458. name_class = ("doesnotexist.", "IN")
  459. self.assertTrue(self.zone_refresh._zonemgr_refresh_info[name_class]["zone_soa_rdata"]
  460. is None)
  461. # The other configs should be updated successfully
  462. self.assertEqual(61, self.zone_refresh._lowerbound_refresh)
  463. self.assertEqual(31, self.zone_refresh._lowerbound_retry)
  464. self.assertEqual(19801, self.zone_refresh._max_transfer_timeout)
  465. self.assertEqual(0.21, self.zone_refresh._refresh_jitter)
  466. self.assertEqual(0.71, self.zone_refresh._reload_jitter)
  467. # Make sure we accept 0 as a value
  468. config_data = {
  469. "lowerbound_refresh" : 60,
  470. "lowerbound_retry" : 30,
  471. "max_transfer_timeout" : 19800,
  472. "refresh_jitter" : 0,
  473. "reload_jitter" : 0.75,
  474. "secondary_zones": []
  475. }
  476. self.zone_refresh.update_config_data(config_data, self.cc_session)
  477. self.assertEqual(60, self.zone_refresh._lowerbound_refresh)
  478. self.assertEqual(30, self.zone_refresh._lowerbound_retry)
  479. self.assertEqual(19800, self.zone_refresh._max_transfer_timeout)
  480. self.assertEqual(0, self.zone_refresh._refresh_jitter)
  481. self.assertEqual(0.75, self.zone_refresh._reload_jitter)
  482. def test_shutdown(self):
  483. self.zone_refresh._check_sock = self.zone_refresh._master_socket
  484. listener = self.zone_refresh.run_timer()
  485. self.assertTrue(listener.is_alive())
  486. # Shut down the timer thread
  487. self.zone_refresh.shutdown()
  488. self.assertFalse(listener.is_alive())
  489. def test_secondary_zones(self):
  490. def zone_list_from_name_classes(zones):
  491. return map(lambda nc: {"name": nc[0], "class": nc[1]}, zones)
  492. """Test that we can modify the list of secondary zones"""
  493. config = self.cc_session.get_full_config()
  494. config['secondary_zones'] = []
  495. # First, remove everything
  496. self.zone_refresh.update_config_data(config, self.cc_session)
  497. self.assertEqual(self.zone_refresh._zonemgr_refresh_info, {})
  498. # Put something in
  499. config['secondary_zones'] = \
  500. zone_list_from_name_classes([ZONE_NAME_CLASS1_IN])
  501. self.zone_refresh.update_config_data(config, self.cc_session)
  502. self.assertTrue(("example.net.", "IN") in
  503. self.zone_refresh._zonemgr_refresh_info)
  504. # Reset the data, set to use a different class, and make sure
  505. # it does not get set to IN
  506. config['secondary_zones'] = \
  507. zone_list_from_name_classes([ZONE_NAME_CLASS1_CH])
  508. self.zone_refresh.update_config_data(config, self.cc_session)
  509. self.assertFalse(("example.net.", "IN") in
  510. self.zone_refresh._zonemgr_refresh_info)
  511. # Make sure it works even when we "accidentally" forget the final dot
  512. config['secondary_zones'] = \
  513. zone_list_from_name_classes([("example.net", "IN")])
  514. self.zone_refresh.update_config_data(config, self.cc_session)
  515. self.assertTrue(("example.net.", "IN") in
  516. self.zone_refresh._zonemgr_refresh_info)
  517. # and with case-insensitive checking
  518. config['secondary_zones'] = \
  519. zone_list_from_name_classes([("Example.NeT.", "in")])
  520. self.zone_refresh.update_config_data(config, self.cc_session)
  521. self.assertTrue(("example.net.", "IN") in
  522. self.zone_refresh._zonemgr_refresh_info)
  523. # Try some bad names
  524. config['secondary_zones'] = \
  525. zone_list_from_name_classes([("example..net", "IN")])
  526. self.assertRaises(ZonemgrException,
  527. self.zone_refresh.update_config_data,
  528. config, self.cc_session)
  529. config['secondary_zones'] = \
  530. zone_list_from_name_classes([("", "IN")])
  531. self.assertRaises(ZonemgrException,
  532. self.zone_refresh.update_config_data,
  533. config, self.cc_session)
  534. # Try a bad class
  535. config['secondary_zones'] = \
  536. zone_list_from_name_classes([("example.net", "BADCLASS")])
  537. self.assertRaises(ZonemgrException,
  538. self.zone_refresh.update_config_data,
  539. config, self.cc_session)
  540. config['secondary_zones'] = \
  541. zone_list_from_name_classes([("example.net", "")])
  542. self.assertRaises(ZonemgrException,
  543. self.zone_refresh.update_config_data,
  544. config, self.cc_session)
  545. class MyZonemgr(Zonemgr):
  546. def __init__(self):
  547. self._db_file = TEST_SQLITE3_DBFILE
  548. self._zone_refresh = None
  549. self._shutdown_event = threading.Event()
  550. self._cc = MySession()
  551. self._module_cc = FakeCCSession()
  552. self._config_data = {
  553. "lowerbound_refresh" : 10,
  554. "lowerbound_retry" : 5,
  555. "max_transfer_timeout" : 14400,
  556. "refresh_jitter" : 0.1,
  557. "reload_jitter" : 0.75,
  558. "secondary_zones": []
  559. }
  560. def _start_zone_refresh_timer(self):
  561. pass
  562. class TestZonemgr(unittest.TestCase):
  563. def setUp(self):
  564. if os.path.exists(TEST_SQLITE3_DBFILE):
  565. os.unlink(TEST_SQLITE3_DBFILE)
  566. self.zonemgr = MyZonemgr()
  567. def tearDown(self):
  568. if os.path.exists(TEST_SQLITE3_DBFILE):
  569. os.unlink(TEST_SQLITE3_DBFILE)
  570. def test_config_handler(self):
  571. config_data1 = {
  572. "lowerbound_refresh" : 60,
  573. "lowerbound_retry" : 30,
  574. "max_transfer_timeout" : 14400,
  575. "refresh_jitter" : 0.1,
  576. "reload_jitter" : 0.75,
  577. "secondary_zones": []
  578. }
  579. self.assertEqual(self.zonemgr.config_handler(config_data1),
  580. {"result": [0]})
  581. self.assertEqual(config_data1, self.zonemgr._config_data)
  582. config_data2 = {"zone_name" : "example.net.", "port" : "53", "master" : "192.168.1.1"}
  583. self.zonemgr.config_handler(config_data2)
  584. self.assertEqual(config_data1, self.zonemgr._config_data)
  585. # jitter should not be bigger than half of the original value
  586. config_data3 = {"refresh_jitter" : 0.7}
  587. self.zonemgr.config_handler(config_data3)
  588. self.assertEqual(0.5, self.zonemgr._config_data.get("refresh_jitter"))
  589. # The zone doesn't exist in database, simply skip loading soa for it and log an warning
  590. self.zonemgr._zone_refresh = ZonemgrRefresh(None, TEST_SQLITE3_DBFILE,
  591. None, FakeCCSession())
  592. config_data1["secondary_zones"] = [{"name": "nonexistent.example",
  593. "class": "IN"}]
  594. self.assertEqual(self.zonemgr.config_handler(config_data1),
  595. {"result": [0]})
  596. # other configs should be updated successfully
  597. name_class = ("nonexistent.example.", "IN")
  598. self.assertTrue(self.zonemgr._zone_refresh._zonemgr_refresh_info[name_class]["zone_soa_rdata"]
  599. is None)
  600. self.assertEqual(0.1, self.zonemgr._config_data.get("refresh_jitter"))
  601. def test_get_db_file(self):
  602. self.assertEqual(TEST_SQLITE3_DBFILE, self.zonemgr.get_db_file())
  603. def test_parse_cmd_params(self):
  604. params1 = {"zone_name" : "example.com.", "zone_class" : "CH", "master" : "127.0.0.1"}
  605. answer1 = (ZONE_NAME_CLASS3_CH, "127.0.0.1")
  606. self.assertEqual(answer1, self.zonemgr._parse_cmd_params(params1, ZONE_NOTIFY_COMMAND))
  607. params2 = {"zone_name" : "example.com.", "zone_class" : "IN"}
  608. answer2 = ZONE_NAME_CLASS3_IN
  609. self.assertEqual(answer2, self.zonemgr._parse_cmd_params(params2, notify_out.ZONE_NEW_DATA_READY_CMD))
  610. self.assertRaises(ZonemgrException, self.zonemgr._parse_cmd_params, params2, ZONE_NOTIFY_COMMAND)
  611. params1 = {"zone_class" : "CH"}
  612. self.assertRaises(ZonemgrException, self.zonemgr._parse_cmd_params, params2, ZONE_NOTIFY_COMMAND)
  613. def test_config_data_check(self):
  614. # jitter should not be bigger than half of the original value
  615. config_data2 = {"refresh_jitter" : 0.2}
  616. config_data3 = {"refresh_jitter" : 0.6}
  617. self.zonemgr._config_data_check(config_data2)
  618. self.assertEqual(0.2, config_data2.get("refresh_jitter"))
  619. self.zonemgr._config_data_check(config_data3)
  620. self.assertEqual(0.5, config_data3.get("refresh_jitter"))
  621. def test_shutdown(self):
  622. self.assertFalse(self.zonemgr._module_cc.stopped)
  623. self.zonemgr._shutdown_event.set()
  624. self.zonemgr.run()
  625. self.assertTrue(self.zonemgr._module_cc.stopped)
  626. if __name__== "__main__":
  627. isc.log.resetUnitTestRootLogger()
  628. unittest.main()