notify_out_test.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  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. import unittest
  16. import sys
  17. import os
  18. import tempfile
  19. import time
  20. import socket
  21. from isc.notify import notify_out, SOCK_DATA
  22. import isc.log
  23. from isc.dns import *
  24. TESTDATA_SRCDIR = os.getenv("TESTDATASRCDIR")
  25. # our fake socket, where we can read and insert messages
  26. class MockSocket():
  27. def __init__(self):
  28. self._local_sock, self._remote_sock = socket.socketpair()
  29. def connect(self, to):
  30. pass
  31. def fileno(self):
  32. return self._local_sock.fileno()
  33. def close(self):
  34. self._local_sock.close()
  35. self._remote_sock.close()
  36. def sendto(self, data, flag, dst):
  37. return self._local_sock.send(data)
  38. def recvfrom(self, length):
  39. data = self._local_sock.recv(length)
  40. return (data, None)
  41. # provide a remote end which can write data to MockSocket for testing.
  42. def remote_end(self):
  43. return self._remote_sock
  44. # We subclass the ZoneNotifyInfo class we're testing here, only
  45. # to override the create_socket() method.
  46. class MockZoneNotifyInfo(notify_out.ZoneNotifyInfo):
  47. def create_socket(self, addrinfo):
  48. super().create_socket(addrinfo)
  49. # before replacing the underlying socket, remember the address family
  50. # of the original socket so that tests can check that.
  51. self.sock_family = self._sock.family
  52. self._sock.close()
  53. self._sock = MockSocket()
  54. self._sock.family = self.sock_family
  55. return self._sock
  56. class TestZoneNotifyInfo(unittest.TestCase):
  57. def setUp(self):
  58. self.info = notify_out.ZoneNotifyInfo('example.net.', 'IN')
  59. def test_prepare_finish_notify_out(self):
  60. self.info.prepare_notify_out()
  61. self.assertNotEqual(self.info.notify_timeout, None)
  62. self.assertIsNone(self.info._notify_current)
  63. self.info.finish_notify_out()
  64. self.assertEqual(self.info._sock, None)
  65. self.assertEqual(self.info.notify_timeout, None)
  66. def test_set_next_notify_target(self):
  67. self.info.notify_slaves.append(('127.0.0.1', 53))
  68. self.info.notify_slaves.append(('1.1.1.1', 5353))
  69. self.info.prepare_notify_out()
  70. self.assertEqual(self.info.get_current_notify_target(), ('127.0.0.1', 53))
  71. self.info.set_next_notify_target()
  72. self.assertEqual(self.info.get_current_notify_target(), ('1.1.1.1', 5353))
  73. self.info.set_next_notify_target()
  74. self.assertIsNone(self.info.get_current_notify_target())
  75. temp_info = notify_out.ZoneNotifyInfo('example.com.', 'IN')
  76. temp_info.prepare_notify_out()
  77. self.assertIsNone(temp_info.get_current_notify_target())
  78. class TestNotifyOut(unittest.TestCase):
  79. def setUp(self):
  80. self._db_file = TESTDATA_SRCDIR + '/test.sqlite3'
  81. self._notifiedv4_zone_name = None
  82. def _dummy_counter_notifyoutv4(z): self._notifiedv4_zone_name = z
  83. self._notifiedv6_zone_name = None
  84. def _dummy_counter_notifyoutv6(z): self._notifiedv6_zone_name = z
  85. self._notify = notify_out.NotifyOut(self._db_file,
  86. counter_notifyoutv4=_dummy_counter_notifyoutv4,
  87. counter_notifyoutv6=_dummy_counter_notifyoutv6)
  88. self._notify._notify_infos[('example.com.', 'IN')] = MockZoneNotifyInfo('example.com.', 'IN')
  89. self._notify._notify_infos[('example.com.', 'CH')] = MockZoneNotifyInfo('example.com.', 'CH')
  90. self._notify._notify_infos[('example.net.', 'IN')] = MockZoneNotifyInfo('example.net.', 'IN')
  91. self._notify._notify_infos[('example.org.', 'IN')] = MockZoneNotifyInfo('example.org.', 'IN')
  92. self._notify._notify_infos[('example.org.', 'CH')] = MockZoneNotifyInfo('example.org.', 'CH')
  93. net_info = self._notify._notify_infos[('example.net.', 'IN')]
  94. net_info.notify_slaves.append(('127.0.0.1', 53))
  95. net_info.notify_slaves.append(('1.1.1.1', 5353))
  96. com_info = self._notify._notify_infos[('example.com.', 'IN')]
  97. com_info.notify_slaves.append(('1.1.1.1', 5353))
  98. com_ch_info = self._notify._notify_infos[('example.com.', 'CH')]
  99. com_ch_info.notify_slaves.append(('1.1.1.1', 5353))
  100. def test_send_notify(self):
  101. notify_out._MAX_NOTIFY_NUM = 2
  102. self._notify._nonblock_event.clear()
  103. self.assertTrue(self._notify.send_notify('example.net'))
  104. self.assertTrue(self._notify._nonblock_event.isSet())
  105. self.assertEqual(self._notify.notify_num, 1)
  106. self.assertEqual(self._notify._notifying_zones[0], ('example.net.', 'IN'))
  107. self.assertTrue(self._notify.send_notify('example.com'))
  108. self.assertEqual(self._notify.notify_num, 2)
  109. self.assertEqual(self._notify._notifying_zones[1], ('example.com.', 'IN'))
  110. # notify_num is equal to MAX_NOTIFY_NUM, append it to waiting_zones list.
  111. self._notify._nonblock_event.clear()
  112. self.assertTrue(self._notify.send_notify('example.com', 'CH'))
  113. # add waiting zones won't set nonblock_event.
  114. self.assertFalse(self._notify._nonblock_event.isSet())
  115. self.assertEqual(self._notify.notify_num, 2)
  116. self.assertEqual(1, len(self._notify._waiting_zones))
  117. # zone_id is already in notifying_zones list, append it to waiting_zones list.
  118. self.assertTrue(self._notify.send_notify('example.net'))
  119. self.assertEqual(2, len(self._notify._waiting_zones))
  120. self.assertEqual(self._notify._waiting_zones[1], ('example.net.', 'IN'))
  121. # zone_id is already in waiting_zones list, skip it.
  122. self.assertTrue(self._notify.send_notify('example.net'))
  123. self.assertEqual(2, len(self._notify._waiting_zones))
  124. # has no slave masters, skip it.
  125. self.assertTrue(self._notify.send_notify('example.org.', 'CH'))
  126. self.assertEqual(self._notify.notify_num, 2)
  127. self.assertEqual(2, len(self._notify._waiting_zones))
  128. self.assertTrue(self._notify.send_notify('example.org.'))
  129. self.assertEqual(self._notify.notify_num, 2)
  130. self.assertEqual(2, len(self._notify._waiting_zones))
  131. # zone does not exist, should return False, and no change in other
  132. # values
  133. self.assertFalse(self._notify.send_notify('does.not.exist.'))
  134. self.assertEqual(self._notify.notify_num, 2)
  135. self.assertEqual(2, len(self._notify._waiting_zones))
  136. self.assertFalse(self._notify.send_notify('example.net.', 'CH'))
  137. self.assertEqual(self._notify.notify_num, 2)
  138. self.assertEqual(2, len(self._notify._waiting_zones))
  139. def test_wait_for_notify_reply(self):
  140. self._notify.send_notify('example.net.')
  141. self._notify.send_notify('example.com.')
  142. notify_out._MAX_NOTIFY_NUM = 2
  143. self._notify.send_notify('example.org.')
  144. replied_zones, timeout_zones = self._notify._wait_for_notify_reply()
  145. self.assertEqual(len(replied_zones), 0)
  146. self.assertEqual(len(timeout_zones), 2)
  147. # Trigger timeout events to "send" notifies via a mock socket
  148. for zone in timeout_zones:
  149. self._notify._zone_notify_handler(timeout_zones[zone],
  150. notify_out._EVENT_TIMEOUT)
  151. # Now make one socket be readable
  152. self._notify._notify_infos[('example.net.', 'IN')].notify_timeout = time.time() + 10
  153. self._notify._notify_infos[('example.com.', 'IN')].notify_timeout = time.time() + 10
  154. #Send some data to socket 12340, to make the target socket be readable
  155. self._notify._notify_infos[('example.net.', 'IN')]._sock.remote_end().send(b'data')
  156. replied_zones, timeout_zones = self._notify._wait_for_notify_reply()
  157. self.assertEqual(len(replied_zones), 1)
  158. self.assertEqual(len(timeout_zones), 1)
  159. self.assertTrue(('example.net.', 'IN') in replied_zones.keys())
  160. self.assertTrue(('example.com.', 'IN') in timeout_zones.keys())
  161. self.assertLess(time.time(), self._notify._notify_infos[('example.com.', 'IN')].notify_timeout)
  162. def test_wait_for_notify_reply_2(self):
  163. # Test the returned value when the read_side socket is readable.
  164. self._notify.send_notify('example.net.')
  165. self._notify.send_notify('example.com.')
  166. # Now make one socket be readable
  167. self._notify._notify_infos[('example.net.', 'IN')].notify_timeout = time.time() + 10
  168. self._notify._notify_infos[('example.com.', 'IN')].notify_timeout = time.time() + 10
  169. if self._notify._read_sock is not None:
  170. self._notify._read_sock.close()
  171. if self._notify._write_sock is not None:
  172. self._notify._write_sock.close()
  173. self._notify._read_sock, self._notify._write_sock = socket.socketpair()
  174. self._notify._write_sock.send(SOCK_DATA)
  175. replied_zones, timeout_zones = self._notify._wait_for_notify_reply()
  176. self.assertEqual(0, len(replied_zones))
  177. self.assertEqual(0, len(timeout_zones))
  178. def test_notify_next_target(self):
  179. self._notify.send_notify('example.net.')
  180. self._notify.send_notify('example.com.')
  181. notify_out._MAX_NOTIFY_NUM = 2
  182. # zone example.org. has no slave servers.
  183. self._notify.send_notify('example.org.')
  184. self._notify.send_notify('example.com.', 'CH')
  185. info = self._notify._notify_infos[('example.net.', 'IN')]
  186. self._notify._notify_next_target(info)
  187. self.assertEqual(0, info.notify_try_num)
  188. self.assertEqual(info.get_current_notify_target(), ('1.1.1.1', 5353))
  189. self.assertEqual(2, self._notify.notify_num)
  190. self.assertEqual(1, len(self._notify._waiting_zones))
  191. self._notify._notify_next_target(info)
  192. self.assertEqual(0, info.notify_try_num)
  193. self.assertIsNone(info.get_current_notify_target())
  194. self.assertEqual(2, self._notify.notify_num)
  195. self.assertEqual(0, len(self._notify._waiting_zones))
  196. example_com_info = self._notify._notify_infos[('example.com.', 'IN')]
  197. self._notify._notify_next_target(example_com_info)
  198. self.assertEqual(1, self._notify.notify_num)
  199. self.assertEqual(1, len(self._notify._notifying_zones))
  200. self.assertEqual(0, len(self._notify._waiting_zones))
  201. def test_handle_notify_reply(self):
  202. fake_address = ('192.0.2.1', 53)
  203. self.assertEqual(notify_out._BAD_REPLY_PACKET, self._notify._handle_notify_reply(None, b'badmsg', fake_address))
  204. example_com_info = self._notify._notify_infos[('example.com.', 'IN')]
  205. example_com_info.notify_msg_id = 0X2f18
  206. # test with right notify reply message
  207. data = b'\x2f\x18\xa0\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\03com\x00\x00\x06\x00\x01'
  208. self.assertEqual(notify_out._REPLY_OK, self._notify._handle_notify_reply(example_com_info, data, fake_address))
  209. # test with unright query id
  210. data = b'\x2e\x18\xa0\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\03com\x00\x00\x06\x00\x01'
  211. self.assertEqual(notify_out._BAD_QUERY_ID, self._notify._handle_notify_reply(example_com_info, data, fake_address))
  212. # test with unright query name
  213. data = b'\x2f\x18\xa0\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\03net\x00\x00\x06\x00\x01'
  214. self.assertEqual(notify_out._BAD_QUERY_NAME, self._notify._handle_notify_reply(example_com_info, data, fake_address))
  215. # test with unright opcode
  216. data = b'\x2f\x18\x80\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\03com\x00\x00\x06\x00\x01'
  217. self.assertEqual(notify_out._BAD_OPCODE, self._notify._handle_notify_reply(example_com_info, data, fake_address))
  218. # test with unright qr
  219. data = b'\x2f\x18\x10\x10\x00\x01\x00\x00\x00\x00\x00\x00\x07example\03com\x00\x00\x06\x00\x01'
  220. self.assertEqual(notify_out._BAD_QR, self._notify._handle_notify_reply(example_com_info, data, fake_address))
  221. def test_send_notify_message_udp_ipv4(self):
  222. example_com_info = self._notify._notify_infos[('example.net.', 'IN')]
  223. example_com_info.prepare_notify_out()
  224. self.assertIsNone(self._notifiedv4_zone_name)
  225. self.assertIsNone(self._notifiedv6_zone_name)
  226. ret = self._notify._send_notify_message_udp(example_com_info,
  227. ('192.0.2.1', 53))
  228. self.assertTrue(ret)
  229. self.assertEqual(socket.AF_INET, example_com_info.sock_family)
  230. self.assertEqual(self._notifiedv4_zone_name, 'example.net.')
  231. self.assertIsNone(self._notifiedv6_zone_name)
  232. def test_send_notify_message_udp_ipv6(self):
  233. example_com_info = self._notify._notify_infos[('example.net.', 'IN')]
  234. self.assertIsNone(self._notifiedv4_zone_name)
  235. self.assertIsNone(self._notifiedv6_zone_name)
  236. ret = self._notify._send_notify_message_udp(example_com_info,
  237. ('2001:db8::53', 53))
  238. self.assertTrue(ret)
  239. self.assertEqual(socket.AF_INET6, example_com_info.sock_family)
  240. self.assertIsNone(self._notifiedv4_zone_name)
  241. self.assertEqual(self._notifiedv6_zone_name, 'example.net.')
  242. def test_send_notify_message_udp_ipv4_with_nonetype_notifyoutv4(self):
  243. example_com_info = self._notify._notify_infos[('example.net.', 'IN')]
  244. example_com_info.prepare_notify_out()
  245. self.assertIsNone(self._notifiedv4_zone_name)
  246. self.assertIsNone(self._notifiedv6_zone_name)
  247. self._notify._counter_notifyoutv4 = None
  248. self._notify._send_notify_message_udp(example_com_info,
  249. ('192.0.2.1', 53))
  250. self.assertIsNone(self._notifiedv4_zone_name)
  251. self.assertIsNone(self._notifiedv6_zone_name)
  252. def test_send_notify_message_udp_ipv4_with_notcallable_notifyoutv4(self):
  253. example_com_info = self._notify._notify_infos[('example.net.', 'IN')]
  254. example_com_info.prepare_notify_out()
  255. self._notify._counter_notifyoutv4 = 'NOT CALLABLE'
  256. self.assertRaises(TypeError,
  257. self._notify._send_notify_message_udp,
  258. example_com_info, ('192.0.2.1', 53))
  259. def test_send_notify_message_udp_ipv6_with_nonetype_notifyoutv6(self):
  260. example_com_info = self._notify._notify_infos[('example.net.', 'IN')]
  261. self.assertIsNone(self._notifiedv4_zone_name)
  262. self.assertIsNone(self._notifiedv6_zone_name)
  263. self._notify._counter_notifyoutv6 = None
  264. self._notify._send_notify_message_udp(example_com_info,
  265. ('2001:db8::53', 53))
  266. self.assertIsNone(self._notifiedv4_zone_name)
  267. self.assertIsNone(self._notifiedv6_zone_name)
  268. def test_send_notify_message_udp_ipv6_with_notcallable_notifyoutv6(self):
  269. example_com_info = self._notify._notify_infos[('example.net.', 'IN')]
  270. self._notify._counter_notifyoutv6 = 'NOT CALLABLE'
  271. self.assertRaises(TypeError,
  272. self._notify._send_notify_message_udp,
  273. example_com_info, ('2001:db8::53', 53))
  274. def test_send_notify_message_with_bogus_address(self):
  275. example_com_info = self._notify._notify_infos[('example.net.', 'IN')]
  276. # As long as the underlying data source validates RDATA this shouldn't
  277. # happen, but right now it's not actually the case. Even if the
  278. # data source does its job, it's prudent to confirm the behavior for
  279. # an unexpected case.
  280. self.assertIsNone(self._notifiedv4_zone_name)
  281. self.assertIsNone(self._notifiedv6_zone_name)
  282. ret = self._notify._send_notify_message_udp(example_com_info,
  283. ('invalid', 53))
  284. self.assertFalse(ret)
  285. self.assertIsNone(self._notifiedv4_zone_name)
  286. self.assertIsNone(self._notifiedv6_zone_name)
  287. def test_zone_notify_handler(self):
  288. old_send_msg = self._notify._send_notify_message_udp
  289. def _fake_send_notify_message_udp(va1, va2):
  290. pass
  291. self._notify._send_notify_message_udp = _fake_send_notify_message_udp
  292. self._notify.send_notify('example.net.')
  293. self._notify.send_notify('example.com.')
  294. notify_out._MAX_NOTIFY_NUM = 2
  295. self._notify.send_notify('example.org.')
  296. example_net_info = self._notify._notify_infos[('example.net.', 'IN')]
  297. example_net_info.prepare_notify_out()
  298. example_net_info.notify_try_num = 2
  299. self._notify._zone_notify_handler(example_net_info, notify_out._EVENT_TIMEOUT)
  300. self.assertEqual(3, example_net_info.notify_try_num)
  301. time1 = example_net_info.notify_timeout
  302. self._notify._zone_notify_handler(example_net_info, notify_out._EVENT_TIMEOUT)
  303. self.assertEqual(4, example_net_info.notify_try_num)
  304. self.assertGreater(example_net_info.notify_timeout, time1 + 2) # bigger than 2 seconds
  305. cur_tgt = example_net_info._notify_current
  306. example_net_info.notify_try_num = notify_out._MAX_NOTIFY_TRY_NUM
  307. self._notify._zone_notify_handler(example_net_info, notify_out._EVENT_NONE)
  308. self.assertNotEqual(cur_tgt, example_net_info._notify_current)
  309. cur_tgt = example_net_info._notify_current
  310. example_net_info.create_socket('127.0.0.1')
  311. # dns message, will result in bad_qid, but what we are testing
  312. # here is whether handle_notify_reply is called correctly
  313. example_net_info._sock.remote_end().send(b'\x2f\x18\xa0\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\03com\x00\x00\x06\x00\x01')
  314. self._notify._zone_notify_handler(example_net_info, notify_out._EVENT_READ)
  315. self.assertNotEqual(cur_tgt, example_net_info._notify_current)
  316. def test_get_notify_slaves_from_ns(self):
  317. records = self._notify._get_notify_slaves_from_ns(Name('example.net.'),
  318. RRClass.IN)
  319. self.assertEqual(6, len(records))
  320. self.assertEqual('8:8::8:8', records[5])
  321. self.assertEqual('7.7.7.7', records[4])
  322. self.assertEqual('6.6.6.6', records[3])
  323. self.assertEqual('5:5::5:5', records[2])
  324. self.assertEqual('4:4::4:4', records[1])
  325. self.assertEqual('3.3.3.3', records[0])
  326. records = self._notify._get_notify_slaves_from_ns(Name('example.com.'),
  327. RRClass.IN)
  328. self.assertEqual(3, len(records))
  329. self.assertEqual('5:5::5:5', records[2])
  330. self.assertEqual('4:4::4:4', records[1])
  331. self.assertEqual('3.3.3.3', records[0])
  332. def test_get_notify_slaves_from_ns_unusual(self):
  333. self._notify._db_file = TESTDATA_SRCDIR + '/brokentest.sqlite3'
  334. self.assertEqual([], self._notify._get_notify_slaves_from_ns(
  335. Name('nons.example'), RRClass.IN))
  336. self.assertEqual([], self._notify._get_notify_slaves_from_ns(
  337. Name('nosoa.example'), RRClass.IN))
  338. self.assertEqual([], self._notify._get_notify_slaves_from_ns(
  339. Name('multisoa.example'), RRClass.IN))
  340. self.assertEqual([], self._notify._get_notify_slaves_from_ns(
  341. Name('nosuchzone.example'), RRClass.IN))
  342. # This will cause failure in getting access to the data source.
  343. self._notify._db_file = TESTDATA_SRCDIR + '/nodir/error.sqlite3'
  344. self.assertEqual([], self._notify._get_notify_slaves_from_ns(
  345. Name('example.com'), RRClass.IN))
  346. def test_init_notify_out(self):
  347. self._notify._init_notify_out(self._db_file)
  348. self.assertListEqual([('3.3.3.3', 53), ('4:4::4:4', 53), ('5:5::5:5', 53)],
  349. self._notify._notify_infos[('example.com.', 'IN')].notify_slaves)
  350. def test_prepare_select_info(self):
  351. timeout, valid_fds, notifying_zones = self._notify._prepare_select_info()
  352. self.assertEqual(None, timeout)
  353. self.assertListEqual([], valid_fds)
  354. self._notify._notify_infos[('example.net.', 'IN')]._sock = 1
  355. self._notify._notify_infos[('example.net.', 'IN')].notify_timeout = time.time() + 5
  356. timeout, valid_fds, notifying_zones = self._notify._prepare_select_info()
  357. self.assertGreater(timeout, 0)
  358. self.assertListEqual([1], valid_fds)
  359. self._notify._notify_infos[('example.net.', 'IN')]._sock = 1
  360. self._notify._notify_infos[('example.net.', 'IN')].notify_timeout = time.time() - 5
  361. timeout, valid_fds, notifying_zones = self._notify._prepare_select_info()
  362. self.assertEqual(timeout, 0)
  363. self.assertListEqual([1], valid_fds)
  364. self._notify._notify_infos[('example.com.', 'IN')]._sock = 2
  365. self._notify._notify_infos[('example.com.', 'IN')].notify_timeout = time.time() + 5
  366. timeout, valid_fds, notifying_zones = self._notify._prepare_select_info()
  367. self.assertEqual(timeout, 0)
  368. self.assertEqual(len(valid_fds), 2)
  369. self.assertIn(1, valid_fds)
  370. self.assertIn(2, valid_fds)
  371. def test_shutdown(self):
  372. thread = self._notify.dispatcher()
  373. self.assertTrue(thread.is_alive())
  374. # nonblock_event won't be setted since there are no notifying zones.
  375. self.assertFalse(self._notify._nonblock_event.isSet())
  376. # set nonblock_event manually
  377. self._notify._nonblock_event.set()
  378. # nonblock_event will be cleared soon since there are no notifying zones.
  379. while (self._notify._nonblock_event.isSet()):
  380. pass
  381. # send notify
  382. example_net_info = self._notify._notify_infos[('example.net.', 'IN')]
  383. example_net_info.notify_slaves = [('127.0.0.1', 53)]
  384. example_net_info.create_socket('127.0.0.1')
  385. self._notify.send_notify('example.net')
  386. self.assertTrue(self._notify._nonblock_event.isSet())
  387. # set notify_try_num to _MAX_NOTIFY_TRY_NUM, zone 'example.net' will be removed
  388. # from notifying zones soon and nonblock_event will be cleared since there is no
  389. # notifying zone left.
  390. example_net_info.notify_try_num = notify_out._MAX_NOTIFY_TRY_NUM
  391. while (self._notify._nonblock_event.isSet()):
  392. pass
  393. self.assertFalse(self._notify._nonblock_event.isSet())
  394. self._notify.shutdown()
  395. # nonblock_event should have been setted to stop waiting.
  396. self.assertTrue(self._notify._nonblock_event.isSet())
  397. self.assertFalse(thread.is_alive())
  398. if __name__== "__main__":
  399. isc.log.init("bind10")
  400. isc.log.resetUnitTestRootLogger()
  401. unittest.main()