bind10_test.py 15.7 KB
Newer Older
1
from bind10 import ProcessInfo, BoB
Shane Kerr's avatar
Shane Kerr committed
2

3
4
5
# XXX: environment tests are currently disabled, due to the preprocessor
#      setup that we have now complicating the environment

Shane Kerr's avatar
Shane Kerr committed
6
7
8
9
import unittest
import sys
import os
import signal
Evan Hunt's avatar
Evan Hunt committed
10
import socket
11
from isc.net.addr import IPAddr
Shane Kerr's avatar
Shane Kerr committed
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

class TestProcessInfo(unittest.TestCase):
    def setUp(self):
        # redirect stdout to a pipe so we can check that our
        # process spawning is doing the right thing with stdout
        self.old_stdout = os.dup(sys.stdout.fileno())
        self.pipes = os.pipe()
        os.dup2(self.pipes[1], sys.stdout.fileno())
        os.close(self.pipes[1])
        # note that we use dup2() to restore the original stdout
        # to the main program ASAP in each test... this prevents
        # hangs reading from the child process (as the pipe is only
        # open in the child), and also insures nice pretty output

    def tearDown(self):
        # clean up our stdout munging
        os.dup2(self.old_stdout, sys.stdout.fileno())
        os.close(self.pipes[0])

    def test_init(self):
        pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ])
        os.dup2(self.old_stdout, sys.stdout.fileno())
        self.assertEqual(pi.name, 'Test Process')
        self.assertEqual(pi.args, [ '/bin/echo', 'foo' ])
36
37
#        self.assertEqual(pi.env, { 'PATH': os.environ['PATH'],
#                                   'PYTHON_EXEC': os.environ['PYTHON_EXEC'] })
Shane Kerr's avatar
Shane Kerr committed
38
39
40
41
42
        self.assertEqual(pi.dev_null_stdout, False)
        self.assertEqual(os.read(self.pipes[0], 100), b"foo\n")
        self.assertNotEqual(pi.process, None)
        self.assertTrue(type(pi.pid) is int)

43
44
45
46
47
48
#    def test_setting_env(self):
#        pi = ProcessInfo('Test Process', [ '/bin/true' ], env={'FOO': 'BAR'})
#        os.dup2(self.old_stdout, sys.stdout.fileno())
#        self.assertEqual(pi.env, { 'PATH': os.environ['PATH'],
#                                   'PYTHON_EXEC': os.environ['PYTHON_EXEC'],
#                                   'FOO': 'BAR' })
Shane Kerr's avatar
Shane Kerr committed
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

    def test_setting_null_stdout(self):
        pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ], 
                         dev_null_stdout=True)
        os.dup2(self.old_stdout, sys.stdout.fileno())
        self.assertEqual(pi.dev_null_stdout, True)
        self.assertEqual(os.read(self.pipes[0], 100), b"")

    def test_respawn(self):
        pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ])
        # wait for old process to work...
        self.assertEqual(os.read(self.pipes[0], 100), b"foo\n")
        # respawn it
        old_pid = pi.pid
        pi.respawn()
        os.dup2(self.old_stdout, sys.stdout.fileno())
        # make sure the new one started properly
        self.assertEqual(pi.name, 'Test Process')
        self.assertEqual(pi.args, [ '/bin/echo', 'foo' ])
68
69
#        self.assertEqual(pi.env, { 'PATH': os.environ['PATH'],
#                                   'PYTHON_EXEC': os.environ['PYTHON_EXEC'] })
Shane Kerr's avatar
Shane Kerr committed
70
71
72
73
74
75
        self.assertEqual(pi.dev_null_stdout, False)
        self.assertEqual(os.read(self.pipes[0], 100), b"foo\n")
        self.assertNotEqual(pi.process, None)
        self.assertTrue(type(pi.pid) is int)
        self.assertNotEqual(pi.pid, old_pid)

Shane Kerr's avatar
Shane Kerr committed
76
77
78
79
class TestBoB(unittest.TestCase):
    def test_init(self):
        bob = BoB()
        self.assertEqual(bob.verbose, False)
Jelte Jansen's avatar
Jelte Jansen committed
80
        self.assertEqual(bob.msgq_socket_file, None)
81
        self.assertEqual(bob.dns_port, 5300)
Evan Hunt's avatar
Evan Hunt committed
82
        self.assertEqual(bob.address, None)
83
84
        self.assertEqual(bob.cc_session, None)
        self.assertEqual(bob.ccs, None)
Shane Kerr's avatar
Shane Kerr committed
85
86
87
        self.assertEqual(bob.processes, {})
        self.assertEqual(bob.dead_processes, {})
        self.assertEqual(bob.runnable, False)
88
89
90
91
        self.assertEqual(bob.uid, None)
        self.assertEqual(bob.username, None)
        self.assertEqual(bob.nocache, False)
        self.assertEqual(bob.cfg_start_auth, True)
92
        self.assertEqual(bob.cfg_start_resolver, False)
Shane Kerr's avatar
Shane Kerr committed
93

Jelte Jansen's avatar
Jelte Jansen committed
94
95
    def test_init_alternate_socket(self):
        bob = BoB("alt_socket_file")
Shane Kerr's avatar
Shane Kerr committed
96
        self.assertEqual(bob.verbose, False)
Jelte Jansen's avatar
Jelte Jansen committed
97
        self.assertEqual(bob.msgq_socket_file, "alt_socket_file")
98
        self.assertEqual(bob.address, None)
Jelte Jansen's avatar
Jelte Jansen committed
99
        self.assertEqual(bob.dns_port, 5300)
Shane Kerr's avatar
Shane Kerr committed
100
        self.assertEqual(bob.cc_session, None)
101
        self.assertEqual(bob.ccs, None)
Shane Kerr's avatar
Shane Kerr committed
102
103
104
        self.assertEqual(bob.processes, {})
        self.assertEqual(bob.dead_processes, {})
        self.assertEqual(bob.runnable, False)
105
106
107
108
        self.assertEqual(bob.uid, None)
        self.assertEqual(bob.username, None)
        self.assertEqual(bob.nocache, False)
        self.assertEqual(bob.cfg_start_auth, True)
109
        self.assertEqual(bob.cfg_start_resolver, False)
Shane Kerr's avatar
Shane Kerr committed
110

111
    def test_init_alternate_dns_port(self):
Evan Hunt's avatar
Evan Hunt committed
112
113
114
        bob = BoB(None, 9999)
        self.assertEqual(bob.verbose, False)
        self.assertEqual(bob.msgq_socket_file, None)
115
        self.assertEqual(bob.dns_port, 9999)
Evan Hunt's avatar
Evan Hunt committed
116
117
        self.assertEqual(bob.address, None)
        self.assertEqual(bob.cc_session, None)
118
        self.assertEqual(bob.ccs, None)
Evan Hunt's avatar
Evan Hunt committed
119
120
121
        self.assertEqual(bob.processes, {})
        self.assertEqual(bob.dead_processes, {})
        self.assertEqual(bob.runnable, False)
122
123
124
125
        self.assertEqual(bob.uid, None)
        self.assertEqual(bob.username, None)
        self.assertEqual(bob.nocache, False)
        self.assertEqual(bob.cfg_start_auth, True)
126
        self.assertEqual(bob.cfg_start_resolver, False)
Evan Hunt's avatar
Evan Hunt committed
127
128

    def test_init_alternate_address(self):
129
        bob = BoB(None, 1234, IPAddr('127.127.127.127'))
Evan Hunt's avatar
Evan Hunt committed
130
131
        self.assertEqual(bob.verbose, False)
        self.assertEqual(bob.msgq_socket_file, None)
Jelte Jansen's avatar
Jelte Jansen committed
132
        self.assertEqual(bob.dns_port, 1234)
Evan Hunt's avatar
Evan Hunt committed
133
        self.assertEqual(bob.address.addr, socket.inet_aton('127.127.127.127'))
134
135
        self.assertEqual(bob.cc_session, None)
        self.assertEqual(bob.ccs, None)
Evan Hunt's avatar
Evan Hunt committed
136
137
138
        self.assertEqual(bob.processes, {})
        self.assertEqual(bob.dead_processes, {})
        self.assertEqual(bob.runnable, False)
139
140
141
142
        self.assertEqual(bob.uid, None)
        self.assertEqual(bob.username, None)
        self.assertEqual(bob.nocache, False)
        self.assertEqual(bob.cfg_start_auth, True)
143
        self.assertEqual(bob.cfg_start_resolver, False)
144

145
# Class for testing the BoB start/stop components routines.
146
147
148
149
150
#
# Although testing that external processes start is outside the scope
# of the unit test, by overriding the process start methods we can check
# that the right processes are started depending on the configuration
# options.
151
class StartStopCheckBob(BoB):
152
153
154
155
156
157
158
159
    def __init__(self):
        BoB.__init__(self)

# Set flags as to which of the overridden methods has been run.
        self.msgq = False
        self.cfgmgr = False
        self.ccsession = False
        self.auth = False
160
        self.resolver = False
161
162
163
164
165
        self.xfrout = False
        self.xfrin = False
        self.zonemgr = False
        self.stats = False
        self.cmdctl = False
166
        self.c_channel_env = {}
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183

    def read_bind10_config(self):
        # Configuration options are set directly
        pass

    def start_msgq(self, c_channel_env):
        self.msgq = True

    def start_cfgmgr(self, c_channel_env):
        self.cfgmgr = True

    def start_ccsession(self, c_channel_env):
        self.ccsession = True

    def start_auth(self, c_channel_env):
        self.auth = True

184
185
    def start_resolver(self, c_channel_env):
        self.resolver = True
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201

    def start_xfrout(self, c_channel_env):
        self.xfrout = True

    def start_xfrin(self, c_channel_env):
        self.xfrin = True

    def start_zonemgr(self, c_channel_env):
        self.zonemgr = True

    def start_stats(self, c_channel_env):
        self.stats = True

    def start_cmdctl(self, c_channel_env):
        self.cmdctl = True

202
203
204
    # We don't really use all of these stop_ methods. But it might turn out
    # someone would add some stop_ method to BoB and we want that one overriden
    # in case he forgets to update the tests.
205
    def stop_msgq(self):
206
207
        self.msgq = False

208
    def stop_cfgmgr(self):
209
210
        self.cfgmgr = False

211
    def stop_ccsession(self):
212
213
        self.ccsession = False

214
    def stop_auth(self):
215
216
        self.auth = False

217
    def stop_resolver(self):
218
219
        self.resolver = False

220
    def stop_xfrout(self):
221
222
        self.xfrout = False

223
    def stop_xfrin(self):
224
225
        self.xfrin = False

226
    def stop_zonemgr(self):
227
228
        self.zonemgr = False

229
    def stop_stats(self):
230
231
        self.stats = False

232
    def stop_cmdctl(self):
233
234
235
236
237
238
239
240
        self.cmdctl = False

class TestStartStopProcessesBob(unittest.TestCase):
    """
    Check that the start_all_processes method starts the right combination
    of processes and that the right processes are started and stopped
    according to changes in configuration.
    """
241
242
    def check_started(self, bob, core, auth, resolver):
        """
243
244
        Check that the right sets of services are started. The ones that
        should be running are specified by the core, auth and resolver parameters
245
246
247
248
249
250
251
252
253
254
255
256
257
258
        (they are groups of processes, eg. auth means b10-auth, -xfrout, -xfrin
        and -zonemgr).
        """
        self.assertEqual(bob.msgq, core)
        self.assertEqual(bob.cfgmgr, core)
        self.assertEqual(bob.ccsession, core)
        self.assertEqual(bob.auth, auth)
        self.assertEqual(bob.resolver, resolver)
        self.assertEqual(bob.xfrout, auth)
        self.assertEqual(bob.xfrin, auth)
        self.assertEqual(bob.zonemgr, auth)
        self.assertEqual(bob.stats, core)
        self.assertEqual(bob.cmdctl, core)

259
    def check_preconditions(self, bob):
260
        self.check_started(bob, False, False, False)
261

262
263
264
265
266
    def check_started_none(self, bob):
        """
        Check that the situation is according to configuration where no servers
        should be started. Some processes still need to be running.
        """
267
        self.check_started(bob, True, False, False)
268
269
270
271
272
273

    def check_started_both(self, bob):
        """
        Check the situation is according to configuration where both servers
        (auth and resolver) are enabled.
        """
274
        self.check_started(bob, True, True, True)
275
276
277
278
279

    def check_started_auth(self, bob):
        """
        Check the set of processes needed to run auth only is started.
        """
280
        self.check_started(bob, True, True, False)
281
282
283
284
285

    def check_started_resolver(self, bob):
        """
        Check the set of processes needed to run resolver only is started.
        """
286
        self.check_started(bob, True, False, True)
287

288
    # Checks the processes started when starting neither auth nor resolver
289
290
    # is specified.
    def test_start_none(self):
291
        # Create BoB and ensure correct initialization
292
        bob = StartStopCheckBob()
293
294
295
296
        self.check_preconditions(bob)

        # Start processes and check what was started
        bob.cfg_start_auth = False
297
        bob.cfg_start_resolver = False
298

299
        bob.start_all_processes()
300
        self.check_started_none(bob)
301
302
303

    # Checks the processes started when starting only the auth process
    def test_start_auth(self):
304
        # Create BoB and ensure correct initialization
305
        bob = StartStopCheckBob()
306
307
308
309
        self.check_preconditions(bob)

        # Start processes and check what was started
        bob.cfg_start_auth = True
310
        bob.cfg_start_resolver = False
311

312
        bob.start_all_processes()
313

314
        self.check_started_auth(bob)
315

316
317
    # Checks the processes started when starting only the resolver process
    def test_start_resolver(self):
318
        # Create BoB and ensure correct initialization
319
        bob = StartStopCheckBob()
320
321
322
323
        self.check_preconditions(bob)

        # Start processes and check what was started
        bob.cfg_start_auth = False
324
        bob.cfg_start_resolver = True
325

326
        bob.start_all_processes()
327

328
        self.check_started_resolver(bob)
329

330
    # Checks the processes started when starting both auth and resolver process
331
    def test_start_both(self):
332
        # Create BoB and ensure correct initialization
333
        bob = StartStopCheckBob()
334
335
336
337
        self.check_preconditions(bob)

        # Start processes and check what was started
        bob.cfg_start_auth = True
338
        bob.cfg_start_resolver = True
339

340
        bob.start_all_processes()
341

342
343
344
345
346
347
348
349
        self.check_started_both(bob)

    def test_config_start(self):
        """
        Test that the configuration starts and stops processes according
        to configuration changes.
        """

350
        # Create BoB and ensure correct initialization
351
        bob = StartStopCheckBob()
352
353
354
355
356
357
358
        self.check_preconditions(bob)

        # Start processes (nothing much should be started, as in
        # test_start_none)
        bob.cfg_start_auth = False
        bob.cfg_start_resolver = False

359
        bob.start_all_processes()
360
        bob.runnable = True
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
        self.check_started_none(bob)

        # Enable both at once
        bob.config_handler({'start_auth': True, 'start_resolver': True})
        self.check_started_both(bob)

        # Not touched by empty change
        bob.config_handler({})
        self.check_started_both(bob)

        # Not touched by change to the same configuration
        bob.config_handler({'start_auth': True, 'start_resolver': True})
        self.check_started_both(bob)

        # Turn them both off again
        bob.config_handler({'start_auth': False, 'start_resolver': False})
        self.check_started_none(bob)

        # Not touched by empty change
        bob.config_handler({})
        self.check_started_none(bob)

        # Not touched by change to the same configuration
        bob.config_handler({'start_auth': False, 'start_resolver': False})
        self.check_started_none(bob)

        # Start and stop auth separately
        bob.config_handler({'start_auth': True})
        self.check_started_auth(bob)

        bob.config_handler({'start_auth': False})
        self.check_started_none(bob)

        # Start and stop resolver separately
        bob.config_handler({'start_resolver': True})
        self.check_started_resolver(bob)

        bob.config_handler({'start_resolver': False})
        self.check_started_none(bob)

        # Alternate
        bob.config_handler({'start_auth': True})
        self.check_started_auth(bob)

        bob.config_handler({'start_auth': False, 'start_resolver': True})
        self.check_started_resolver(bob)
407

408
409
        bob.config_handler({'start_auth': True, 'start_resolver': False})
        self.check_started_auth(bob)
Shane Kerr's avatar
Shane Kerr committed
410

411
412
413
414
    def test_config_start_once(self):
        """
        Tests that a process is started only once.
        """
415
        # Create BoB and ensure correct initialization
416
        bob = StartStopCheckBob()
417
418
419
420
421
422
423
        self.check_preconditions(bob)

        # Start processes (both)
        bob.cfg_start_auth = True
        bob.cfg_start_resolver = True

        bob.start_all_processes()
424
        bob.runnable = True
425
426
427
428
429
430
431
432
433
434
435
436
        self.check_started_both(bob)

        bob.start_auth = lambda: self.fail("Started auth again")
        bob.start_xfrout = lambda: self.fail("Started xfrout again")
        bob.start_xfrin = lambda: self.fail("Started xfrin again")
        bob.start_zonemgr = lambda: self.fail("Started zonemgr again")
        bob.start_resolver = lambda: self.fail("Started resolver again")

        # Send again we want to start them. Should not do it, as they are.
        bob.config_handler({'start_auth': True})
        bob.config_handler({'start_resolver': True})

437
438
439
440
441
    def test_config_not_started_early(self):
        """
        Test that processes are not started by the config handler before
        startup.
        """
442
        bob = StartStopCheckBob()
443
444
445
446
447
448
449
450
451
452
        self.check_preconditions(bob)

        bob.start_auth = lambda: self.fail("Started auth again")
        bob.start_xfrout = lambda: self.fail("Started xfrout again")
        bob.start_xfrin = lambda: self.fail("Started xfrin again")
        bob.start_zonemgr = lambda: self.fail("Started zonemgr again")
        bob.start_resolver = lambda: self.fail("Started resolver again")

        bob.config_handler({'start_auth': True, 'start_resolver': True})

Shane Kerr's avatar
Shane Kerr committed
453
454
if __name__ == '__main__':
    unittest.main()