notify_out_test.py 22.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Copyright (C) 2010  Internet Systems Consortium.
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

16
17
18
19
20
21
import unittest
import sys
import os
import tempfile
import time
import socket
Michal Vaner's avatar
Michal Vaner committed
22
from isc.notify import notify_out, SOCK_DATA
23
import isc.log
24
from isc.dns import *
25

26
27
TESTDATA_SRCDIR = os.getenv("TESTDATASRCDIR")

28
29
# our fake socket, where we can read and insert messages
class MockSocket():
30
    def __init__(self):
31
        self._local_sock, self._remote_sock = socket.socketpair()
32
33
34
35
36

    def connect(self, to):
        pass

    def fileno(self):
37
        return self._local_sock.fileno()
38
39

    def close(self):
40
41
        self._local_sock.close()
        self._remote_sock.close()
42
43

    def sendto(self, data, flag, dst):
44
        return self._local_sock.send(data)
45
46

    def recvfrom(self, length):
47
        data = self._local_sock.recv(length)
48
49
        return (data, None)

50
51
52
53
    # provide a remote end which can write data to MockSocket for testing.
    def remote_end(self):
        return self._remote_sock

54
# We subclass the ZoneNotifyInfo class we're testing here, only
55
# to override the create_socket() method.
56
class MockZoneNotifyInfo(notify_out.ZoneNotifyInfo):
57
58
    def create_socket(self, addrinfo):
        super().create_socket(addrinfo)
59
60
61
        # before replacing the underlying socket, remember the address family
        # of the original socket so that tests can check that.
        self.sock_family = self._sock.family
62
        self._sock.close()
63
        self._sock = MockSocket()
64
        self._sock.family = self.sock_family
65
        return self._sock
66

67
68
class TestZoneNotifyInfo(unittest.TestCase):
    def setUp(self):
69
        self.info = notify_out.ZoneNotifyInfo('example.net.', 'IN')
70
71
72

    def test_prepare_finish_notify_out(self):
        self.info.prepare_notify_out()
73
        self.assertNotEqual(self.info.notify_timeout, None)
74
75
76
77
        self.assertIsNone(self.info._notify_current)

        self.info.finish_notify_out()
        self.assertEqual(self.info._sock, None)
78
        self.assertEqual(self.info.notify_timeout, None)
79
80

    def test_set_next_notify_target(self):
81
82
        self.info.notify_slaves.append(('127.0.0.1', 53))
        self.info.notify_slaves.append(('1.1.1.1', 5353))
83
84
85
86
87
88
89
90
        self.info.prepare_notify_out()
        self.assertEqual(self.info.get_current_notify_target(), ('127.0.0.1', 53))

        self.info.set_next_notify_target()
        self.assertEqual(self.info.get_current_notify_target(), ('1.1.1.1', 5353))
        self.info.set_next_notify_target()
        self.assertIsNone(self.info.get_current_notify_target())

91
        temp_info = notify_out.ZoneNotifyInfo('example.com.', 'IN')
92
93
94
95
96
97
        temp_info.prepare_notify_out()
        self.assertIsNone(temp_info.get_current_notify_target())


class TestNotifyOut(unittest.TestCase):
    def setUp(self):
98
        self._db_file = TESTDATA_SRCDIR + '/test.sqlite3'
99
100
101
102
103
104
105
        self._notifiedv4_zone_name = None
        def _dummy_counter_notifyoutv4(z): self._notifiedv4_zone_name = z
        self._notifiedv6_zone_name = None
        def _dummy_counter_notifyoutv6(z): self._notifiedv6_zone_name = z
        self._notify = notify_out.NotifyOut(self._db_file,
                                            counter_notifyoutv4=_dummy_counter_notifyoutv4,
                                            counter_notifyoutv6=_dummy_counter_notifyoutv6)
106
107
108
109
110
111
        self._notify._notify_infos[('example.com.', 'IN')] = MockZoneNotifyInfo('example.com.', 'IN')
        self._notify._notify_infos[('example.com.', 'CH')] = MockZoneNotifyInfo('example.com.', 'CH')
        self._notify._notify_infos[('example.net.', 'IN')] = MockZoneNotifyInfo('example.net.', 'IN')
        self._notify._notify_infos[('example.org.', 'IN')] = MockZoneNotifyInfo('example.org.', 'IN')
        self._notify._notify_infos[('example.org.', 'CH')] = MockZoneNotifyInfo('example.org.', 'CH')

112
113
114
115
116
117
118
        net_info = self._notify._notify_infos[('example.net.', 'IN')]
        net_info.notify_slaves.append(('127.0.0.1', 53))
        net_info.notify_slaves.append(('1.1.1.1', 5353))
        com_info = self._notify._notify_infos[('example.com.', 'IN')]
        com_info.notify_slaves.append(('1.1.1.1', 5353))
        com_ch_info = self._notify._notify_infos[('example.com.', 'CH')]
        com_ch_info.notify_slaves.append(('1.1.1.1', 5353))
119
120

    def test_send_notify(self):
121
122
        notify_out._MAX_NOTIFY_NUM = 2

123
        self._notify._nonblock_event.clear()
124
        self.assertTrue(self._notify.send_notify('example.net'))
125
        self.assertTrue(self._notify._nonblock_event.isSet())
126
        self.assertEqual(self._notify.notify_num, 1)
127
        self.assertEqual(self._notify._notifying_zones[0], ('example.net.', 'IN'))
128

129
        self.assertTrue(self._notify.send_notify('example.com'))
130
        self.assertEqual(self._notify.notify_num, 2)
131
        self.assertEqual(self._notify._notifying_zones[1], ('example.com.', 'IN'))
132

133
        # notify_num is equal to MAX_NOTIFY_NUM, append it to waiting_zones list.
134
        self._notify._nonblock_event.clear()
135
        self.assertTrue(self._notify.send_notify('example.com', 'CH'))
136
137
        # add waiting zones won't set nonblock_event.
        self.assertFalse(self._notify._nonblock_event.isSet())
138
        self.assertEqual(self._notify.notify_num, 2)
139
140
        self.assertEqual(1, len(self._notify._waiting_zones))

141
        # zone_id is already in notifying_zones list, append it to waiting_zones list.
142
        self.assertTrue(self._notify.send_notify('example.net'))
143
144
145
146
        self.assertEqual(2, len(self._notify._waiting_zones))
        self.assertEqual(self._notify._waiting_zones[1], ('example.net.', 'IN'))

        # zone_id is already in waiting_zones list, skip it.
147
        self.assertTrue(self._notify.send_notify('example.net'))
148
149
150
        self.assertEqual(2, len(self._notify._waiting_zones))

        # has no slave masters, skip it.
151
        self.assertTrue(self._notify.send_notify('example.org.', 'CH'))
152
153
154
        self.assertEqual(self._notify.notify_num, 2)
        self.assertEqual(2, len(self._notify._waiting_zones))

155
156
157
158
159
160
161
162
163
164
165
        self.assertTrue(self._notify.send_notify('example.org.'))
        self.assertEqual(self._notify.notify_num, 2)
        self.assertEqual(2, len(self._notify._waiting_zones))

        # zone does not exist, should return False, and no change in other
        # values
        self.assertFalse(self._notify.send_notify('does.not.exist.'))
        self.assertEqual(self._notify.notify_num, 2)
        self.assertEqual(2, len(self._notify._waiting_zones))

        self.assertFalse(self._notify.send_notify('example.net.', 'CH'))
166
        self.assertEqual(self._notify.notify_num, 2)
167
168
        self.assertEqual(2, len(self._notify._waiting_zones))

169
    def test_wait_for_notify_reply(self):
170
171
172
        self._notify.send_notify('example.net.')
        self._notify.send_notify('example.com.')

173
        notify_out._MAX_NOTIFY_NUM = 2
174
        self._notify.send_notify('example.org.')
175
176
177
178
        replied_zones, timeout_zones = self._notify._wait_for_notify_reply()
        self.assertEqual(len(replied_zones), 0)
        self.assertEqual(len(timeout_zones), 2)

179
180
181
182
183
        # Trigger timeout events to "send" notifies via a mock socket
        for zone in timeout_zones:
            self._notify._zone_notify_handler(timeout_zones[zone],
                                              notify_out._EVENT_TIMEOUT)

184
        # Now make one socket be readable
185
186
187
        self._notify._notify_infos[('example.net.', 'IN')].notify_timeout = time.time() + 10
        self._notify._notify_infos[('example.com.', 'IN')].notify_timeout = time.time() + 10

188
        #Send some data to socket 12340, to make the target socket be readable
189
        self._notify._notify_infos[('example.net.', 'IN')]._sock.remote_end().send(b'data')
190
191
192
        replied_zones, timeout_zones = self._notify._wait_for_notify_reply()
        self.assertEqual(len(replied_zones), 1)
        self.assertEqual(len(timeout_zones), 1)
193
194
195
196
        self.assertTrue(('example.net.', 'IN') in replied_zones.keys())
        self.assertTrue(('example.com.', 'IN') in timeout_zones.keys())
        self.assertLess(time.time(), self._notify._notify_infos[('example.com.', 'IN')].notify_timeout)

197
198
    def test_wait_for_notify_reply_2(self):
        # Test the returned value when the read_side socket is readable.
199
200
        self._notify.send_notify('example.net.')
        self._notify.send_notify('example.com.')
201
202

        # Now make one socket be readable
203
204
        self._notify._notify_infos[('example.net.', 'IN')].notify_timeout = time.time() + 10
        self._notify._notify_infos[('example.com.', 'IN')].notify_timeout = time.time() + 10
205
206
207
208
209

        if self._notify._read_sock is not None:
            self._notify._read_sock.close()
        if self._notify._write_sock is not None:
            self._notify._write_sock.close()
Michal Vaner's avatar
Michal Vaner committed
210
        self._notify._read_sock, self._notify._write_sock = socket.socketpair()
Michal Vaner's avatar
Michal Vaner committed
211
        self._notify._write_sock.send(SOCK_DATA)
212
        replied_zones, timeout_zones = self._notify._wait_for_notify_reply()
Michal Vaner's avatar
Michal Vaner committed
213
214
        self.assertEqual(0, len(replied_zones))
        self.assertEqual(0, len(timeout_zones))
215

216
    def test_notify_next_target(self):
217
218
        self._notify.send_notify('example.net.')
        self._notify.send_notify('example.com.')
219
        notify_out._MAX_NOTIFY_NUM = 2
220
        # zone example.org. has no slave servers.
221
222
        self._notify.send_notify('example.org.')
        self._notify.send_notify('example.com.', 'CH')
223

224
        info = self._notify._notify_infos[('example.net.', 'IN')]
225
226
227
228
        self._notify._notify_next_target(info)
        self.assertEqual(0, info.notify_try_num)
        self.assertEqual(info.get_current_notify_target(), ('1.1.1.1', 5353))
        self.assertEqual(2, self._notify.notify_num)
229
        self.assertEqual(1, len(self._notify._waiting_zones))
230
231
232
233
234

        self._notify._notify_next_target(info)
        self.assertEqual(0, info.notify_try_num)
        self.assertIsNone(info.get_current_notify_target())
        self.assertEqual(2, self._notify.notify_num)
235
        self.assertEqual(0, len(self._notify._waiting_zones))
236

237
238
        example_com_info = self._notify._notify_infos[('example.com.', 'IN')]
        self._notify._notify_next_target(example_com_info)
239
240
241
        self.assertEqual(1, self._notify.notify_num)
        self.assertEqual(1, len(self._notify._notifying_zones))
        self.assertEqual(0, len(self._notify._waiting_zones))
242

243
    def test_handle_notify_reply(self):
244
245
        fake_address = ('192.0.2.1', 53)
        self.assertEqual(notify_out._BAD_REPLY_PACKET, self._notify._handle_notify_reply(None, b'badmsg', fake_address))
246
247
        example_com_info = self._notify._notify_infos[('example.com.', 'IN')]
        example_com_info.notify_msg_id = 0X2f18
248
249

        # test with right notify reply message
250
        data = b'\x2f\x18\xa0\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\03com\x00\x00\x06\x00\x01'
251
        self.assertEqual(notify_out._REPLY_OK, self._notify._handle_notify_reply(example_com_info, data, fake_address))
252
253

        # test with unright query id
254
        data = b'\x2e\x18\xa0\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\03com\x00\x00\x06\x00\x01'
255
        self.assertEqual(notify_out._BAD_QUERY_ID, self._notify._handle_notify_reply(example_com_info, data, fake_address))
256
257

        # test with unright query name
258
        data = b'\x2f\x18\xa0\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\03net\x00\x00\x06\x00\x01'
259
        self.assertEqual(notify_out._BAD_QUERY_NAME, self._notify._handle_notify_reply(example_com_info, data, fake_address))
260
261

        # test with unright opcode
262
        data = b'\x2f\x18\x80\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\03com\x00\x00\x06\x00\x01'
263
        self.assertEqual(notify_out._BAD_OPCODE, self._notify._handle_notify_reply(example_com_info, data, fake_address))
264
265

        # test with unright qr
266
        data = b'\x2f\x18\x10\x10\x00\x01\x00\x00\x00\x00\x00\x00\x07example\03com\x00\x00\x06\x00\x01'
267
        self.assertEqual(notify_out._BAD_QR, self._notify._handle_notify_reply(example_com_info, data, fake_address))
268

269
    def test_send_notify_message_udp_ipv4(self):
270
271
        example_com_info = self._notify._notify_infos[('example.net.', 'IN')]
        example_com_info.prepare_notify_out()
272
273
        self.assertIsNone(self._notifiedv4_zone_name)
        self.assertIsNone(self._notifiedv6_zone_name)
274
275
        ret = self._notify._send_notify_message_udp(example_com_info,
                                                    ('192.0.2.1', 53))
276
        self.assertTrue(ret)
277
        self.assertEqual(socket.AF_INET, example_com_info.sock_family)
278
279
        self.assertEqual(self._notifiedv4_zone_name, 'example.net.')
        self.assertIsNone(self._notifiedv6_zone_name)
280
281
282

    def test_send_notify_message_udp_ipv6(self):
        example_com_info = self._notify._notify_infos[('example.net.', 'IN')]
283
284
        self.assertIsNone(self._notifiedv4_zone_name)
        self.assertIsNone(self._notifiedv6_zone_name)
285
286
287
288
        ret = self._notify._send_notify_message_udp(example_com_info,
                                                    ('2001:db8::53', 53))
        self.assertTrue(ret)
        self.assertEqual(socket.AF_INET6, example_com_info.sock_family)
289
290
        self.assertIsNone(self._notifiedv4_zone_name)
        self.assertEqual(self._notifiedv6_zone_name, 'example.net.')
291

292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
    def test_send_notify_message_udp_ipv4_with_nonetype_notifyoutv4(self):
        example_com_info = self._notify._notify_infos[('example.net.', 'IN')]
        example_com_info.prepare_notify_out()
        self.assertIsNone(self._notifiedv4_zone_name)
        self.assertIsNone(self._notifiedv6_zone_name)
        self._notify._counter_notifyoutv4 = None
        self._notify._send_notify_message_udp(example_com_info,
                                              ('192.0.2.1', 53))
        self.assertIsNone(self._notifiedv4_zone_name)
        self.assertIsNone(self._notifiedv6_zone_name)

    def test_send_notify_message_udp_ipv4_with_notcallable_notifyoutv4(self):
        example_com_info = self._notify._notify_infos[('example.net.', 'IN')]
        example_com_info.prepare_notify_out()
        self._notify._counter_notifyoutv4 = 'NOT CALLABLE'
        self.assertRaises(TypeError,
                          self._notify._send_notify_message_udp,
                          example_com_info, ('192.0.2.1', 53))

    def test_send_notify_message_udp_ipv6_with_nonetype_notifyoutv6(self):
        example_com_info = self._notify._notify_infos[('example.net.', 'IN')]
        self.assertIsNone(self._notifiedv4_zone_name)
        self.assertIsNone(self._notifiedv6_zone_name)
        self._notify._counter_notifyoutv6 = None
        self._notify._send_notify_message_udp(example_com_info,
                                              ('2001:db8::53', 53))
        self.assertIsNone(self._notifiedv4_zone_name)
        self.assertIsNone(self._notifiedv6_zone_name)

    def test_send_notify_message_udp_ipv6_with_notcallable_notifyoutv6(self):
        example_com_info = self._notify._notify_infos[('example.net.', 'IN')]
        self._notify._counter_notifyoutv6 = 'NOT CALLABLE'
        self.assertRaises(TypeError,
                          self._notify._send_notify_message_udp,
                          example_com_info, ('2001:db8::53', 53))

328
329
330
331
332
333
334
    def test_send_notify_message_with_bogus_address(self):
        example_com_info = self._notify._notify_infos[('example.net.', 'IN')]

        # As long as the underlying data source validates RDATA this shouldn't
        # happen, but right now it's not actually the case.  Even if the
        # data source does its job, it's prudent to confirm the behavior for
        # an unexpected case.
335
336
        self.assertIsNone(self._notifiedv4_zone_name)
        self.assertIsNone(self._notifiedv6_zone_name)
337
338
339
        ret = self._notify._send_notify_message_udp(example_com_info,
                                                    ('invalid', 53))
        self.assertFalse(ret)
340
341
        self.assertIsNone(self._notifiedv4_zone_name)
        self.assertIsNone(self._notifiedv6_zone_name)
342

343
344
    def test_zone_notify_handler(self):
        old_send_msg = self._notify._send_notify_message_udp
345
        def _fake_send_notify_message_udp(va1, va2):
346
347
            pass
        self._notify._send_notify_message_udp = _fake_send_notify_message_udp
348
349
        self._notify.send_notify('example.net.')
        self._notify.send_notify('example.com.')
350
        notify_out._MAX_NOTIFY_NUM = 2
351
        self._notify.send_notify('example.org.')
352

353
354
        example_net_info = self._notify._notify_infos[('example.net.', 'IN')]
        example_net_info.prepare_notify_out()
355

356
357
358
        example_net_info.notify_try_num = 2
        self._notify._zone_notify_handler(example_net_info, notify_out._EVENT_TIMEOUT)
        self.assertEqual(3, example_net_info.notify_try_num)
359

360
361
362
363
        time1 = example_net_info.notify_timeout
        self._notify._zone_notify_handler(example_net_info, notify_out._EVENT_TIMEOUT)
        self.assertEqual(4, example_net_info.notify_try_num)
        self.assertGreater(example_net_info.notify_timeout, time1 + 2) # bigger than 2 seconds
364

365
366
367
368
        cur_tgt = example_net_info._notify_current
        example_net_info.notify_try_num = notify_out._MAX_NOTIFY_TRY_NUM
        self._notify._zone_notify_handler(example_net_info, notify_out._EVENT_NONE)
        self.assertNotEqual(cur_tgt, example_net_info._notify_current)
369

Jelte Jansen's avatar
Jelte Jansen committed
370
371
372
373
374
375
376
377
        cur_tgt = example_net_info._notify_current
        example_net_info.create_socket('127.0.0.1')
        # dns message, will result in bad_qid, but what we are testing
        # here is whether handle_notify_reply is called correctly
        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')
        self._notify._zone_notify_handler(example_net_info, notify_out._EVENT_READ)
        self.assertNotEqual(cur_tgt, example_net_info._notify_current)

378
    def test_get_notify_slaves_from_ns(self):
379
380
        records = self._notify._get_notify_slaves_from_ns(Name('example.net.'),
                                                          RRClass.IN())
381
        self.assertEqual(6, len(records))
382
        self.assertEqual('8:8::8:8', records[5])
383
384
        self.assertEqual('7.7.7.7', records[4])
        self.assertEqual('6.6.6.6', records[3])
385
386
        self.assertEqual('5:5::5:5', records[2])
        self.assertEqual('4:4::4:4', records[1])
387
388
        self.assertEqual('3.3.3.3', records[0])

389
390
        records = self._notify._get_notify_slaves_from_ns(Name('example.com.'),
                                                          RRClass.IN())
391
        self.assertEqual(3, len(records))
392
393
        self.assertEqual('5:5::5:5', records[2])
        self.assertEqual('4:4::4:4', records[1])
394
        self.assertEqual('3.3.3.3', records[0])
395

396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
    def test_get_notify_slaves_from_ns_unusual(self):
        self._notify._db_file = TESTDATA_SRCDIR + '/brokentest.sqlite3'
        self.assertEqual([], self._notify._get_notify_slaves_from_ns(
                Name('nons.example'), RRClass.IN()))
        self.assertEqual([], self._notify._get_notify_slaves_from_ns(
                Name('nosoa.example'), RRClass.IN()))
        self.assertEqual([], self._notify._get_notify_slaves_from_ns(
                Name('multisoa.example'), RRClass.IN()))

        self.assertEqual([], self._notify._get_notify_slaves_from_ns(
                Name('nosuchzone.example'), RRClass.IN()))

        # This will cause failure in getting access to the data source.
        self._notify._db_file = TESTDATA_SRCDIR + '/nodir/error.sqlite3'
        self.assertEqual([], self._notify._get_notify_slaves_from_ns(
                Name('example.com'), RRClass.IN()))

413
    def test_init_notify_out(self):
414
        self._notify._init_notify_out(self._db_file)
415
416
417
        self.assertListEqual([('3.3.3.3', 53), ('4:4::4:4', 53), ('5:5::5:5', 53)],
                             self._notify._notify_infos[('example.com.', 'IN')].notify_slaves)

418
419
    def test_prepare_select_info(self):
        timeout, valid_fds, notifying_zones = self._notify._prepare_select_info()
420
        self.assertEqual(None, timeout)
421
422
        self.assertListEqual([], valid_fds)

423
424
        self._notify._notify_infos[('example.net.', 'IN')]._sock = 1
        self._notify._notify_infos[('example.net.', 'IN')].notify_timeout = time.time() + 5
425
426
427
428
        timeout, valid_fds, notifying_zones = self._notify._prepare_select_info()
        self.assertGreater(timeout, 0)
        self.assertListEqual([1], valid_fds)

429
430
        self._notify._notify_infos[('example.net.', 'IN')]._sock = 1
        self._notify._notify_infos[('example.net.', 'IN')].notify_timeout = time.time() - 5
431
432
433
434
        timeout, valid_fds, notifying_zones = self._notify._prepare_select_info()
        self.assertEqual(timeout, 0)
        self.assertListEqual([1], valid_fds)

435
436
        self._notify._notify_infos[('example.com.', 'IN')]._sock = 2
        self._notify._notify_infos[('example.com.', 'IN')].notify_timeout = time.time() + 5
437
438
        timeout, valid_fds, notifying_zones = self._notify._prepare_select_info()
        self.assertEqual(timeout, 0)
439
440
441
        self.assertEqual(len(valid_fds), 2)
        self.assertIn(1, valid_fds)
        self.assertIn(2, valid_fds)
442

443
    def test_shutdown(self):
Michal Vaner's avatar
Michal Vaner committed
444
445
        thread = self._notify.dispatcher()
        self.assertTrue(thread.is_alive())
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
        # nonblock_event won't be setted since there are no notifying zones.
        self.assertFalse(self._notify._nonblock_event.isSet())

        # set nonblock_event manually
        self._notify._nonblock_event.set()
        # nonblock_event will be cleared soon since there are no notifying zones.
        while (self._notify._nonblock_event.isSet()):
            pass

        # send notify
        example_net_info = self._notify._notify_infos[('example.net.', 'IN')]
        example_net_info.notify_slaves = [('127.0.0.1', 53)]
        example_net_info.create_socket('127.0.0.1')
        self._notify.send_notify('example.net')
        self.assertTrue(self._notify._nonblock_event.isSet())
        # set notify_try_num to _MAX_NOTIFY_TRY_NUM, zone 'example.net' will be removed
        # from notifying zones soon and nonblock_event will be cleared since there is no 
        # notifying zone left.
        example_net_info.notify_try_num = notify_out._MAX_NOTIFY_TRY_NUM
        while (self._notify._nonblock_event.isSet()):
            pass

468
        self.assertFalse(self._notify._nonblock_event.isSet())
469
        self._notify.shutdown()
470
471
        # nonblock_event should have been setted to stop waiting.
        self.assertTrue(self._notify._nonblock_event.isSet())
Michal Vaner's avatar
Michal Vaner committed
472
        self.assertFalse(thread.is_alive())
473

474
if __name__== "__main__":
475
    isc.log.init("bind10")
476
    isc.log.resetUnitTestRootLogger()
477
    unittest.main()