bind10_test.py.in 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769
  1. # Copyright (C) 2011 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. from bind10_src import ProcessInfo, BoB, parse_args, dump_pid, unlink_pid_file, _BASETIME
  16. # XXX: environment tests are currently disabled, due to the preprocessor
  17. # setup that we have now complicating the environment
  18. import unittest
  19. import sys
  20. import os
  21. import signal
  22. import socket
  23. from isc.net.addr import IPAddr
  24. import time
  25. import isc
  26. import isc.log
  27. from isc.testutils.parse_args import TestOptParser, OptsError
  28. class TestProcessInfo(unittest.TestCase):
  29. def setUp(self):
  30. # redirect stdout to a pipe so we can check that our
  31. # process spawning is doing the right thing with stdout
  32. self.old_stdout = os.dup(sys.stdout.fileno())
  33. self.pipes = os.pipe()
  34. os.dup2(self.pipes[1], sys.stdout.fileno())
  35. os.close(self.pipes[1])
  36. # note that we use dup2() to restore the original stdout
  37. # to the main program ASAP in each test... this prevents
  38. # hangs reading from the child process (as the pipe is only
  39. # open in the child), and also insures nice pretty output
  40. def tearDown(self):
  41. # clean up our stdout munging
  42. os.dup2(self.old_stdout, sys.stdout.fileno())
  43. os.close(self.pipes[0])
  44. def test_init(self):
  45. pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ])
  46. pi.spawn()
  47. os.dup2(self.old_stdout, sys.stdout.fileno())
  48. self.assertEqual(pi.name, 'Test Process')
  49. self.assertEqual(pi.args, [ '/bin/echo', 'foo' ])
  50. # self.assertEqual(pi.env, { 'PATH': os.environ['PATH'],
  51. # 'PYTHON_EXEC': os.environ['PYTHON_EXEC'] })
  52. self.assertEqual(pi.dev_null_stdout, False)
  53. self.assertEqual(os.read(self.pipes[0], 100), b"foo\n")
  54. self.assertNotEqual(pi.process, None)
  55. self.assertTrue(type(pi.pid) is int)
  56. # def test_setting_env(self):
  57. # pi = ProcessInfo('Test Process', [ '/bin/true' ], env={'FOO': 'BAR'})
  58. # os.dup2(self.old_stdout, sys.stdout.fileno())
  59. # self.assertEqual(pi.env, { 'PATH': os.environ['PATH'],
  60. # 'PYTHON_EXEC': os.environ['PYTHON_EXEC'],
  61. # 'FOO': 'BAR' })
  62. def test_setting_null_stdout(self):
  63. pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ],
  64. dev_null_stdout=True)
  65. pi.spawn()
  66. os.dup2(self.old_stdout, sys.stdout.fileno())
  67. self.assertEqual(pi.dev_null_stdout, True)
  68. self.assertEqual(os.read(self.pipes[0], 100), b"")
  69. def test_respawn(self):
  70. pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ])
  71. pi.spawn()
  72. # wait for old process to work...
  73. self.assertEqual(os.read(self.pipes[0], 100), b"foo\n")
  74. # respawn it
  75. old_pid = pi.pid
  76. pi.respawn()
  77. os.dup2(self.old_stdout, sys.stdout.fileno())
  78. # make sure the new one started properly
  79. self.assertEqual(pi.name, 'Test Process')
  80. self.assertEqual(pi.args, [ '/bin/echo', 'foo' ])
  81. # self.assertEqual(pi.env, { 'PATH': os.environ['PATH'],
  82. # 'PYTHON_EXEC': os.environ['PYTHON_EXEC'] })
  83. self.assertEqual(pi.dev_null_stdout, False)
  84. self.assertEqual(os.read(self.pipes[0], 100), b"foo\n")
  85. self.assertNotEqual(pi.process, None)
  86. self.assertTrue(type(pi.pid) is int)
  87. self.assertNotEqual(pi.pid, old_pid)
  88. class TestBoB(unittest.TestCase):
  89. def test_init(self):
  90. bob = BoB()
  91. self.assertEqual(bob.verbose, False)
  92. self.assertEqual(bob.msgq_socket_file, None)
  93. self.assertEqual(bob.cc_session, None)
  94. self.assertEqual(bob.ccs, None)
  95. self.assertEqual(bob.processes, {})
  96. self.assertEqual(bob.dead_processes, {})
  97. self.assertEqual(bob.runnable, False)
  98. self.assertEqual(bob.uid, None)
  99. self.assertEqual(bob.username, None)
  100. self.assertEqual(bob.nocache, False)
  101. self.assertEqual(bob.cfg_start_auth, True)
  102. self.assertEqual(bob.cfg_start_resolver, False)
  103. self.assertEqual(bob.cfg_start_dhcp4, False)
  104. self.assertEqual(bob.cfg_start_dhcp6, False)
  105. def test_init_alternate_socket(self):
  106. bob = BoB("alt_socket_file")
  107. self.assertEqual(bob.verbose, False)
  108. self.assertEqual(bob.msgq_socket_file, "alt_socket_file")
  109. self.assertEqual(bob.cc_session, None)
  110. self.assertEqual(bob.ccs, None)
  111. self.assertEqual(bob.processes, {})
  112. self.assertEqual(bob.dead_processes, {})
  113. self.assertEqual(bob.runnable, False)
  114. self.assertEqual(bob.uid, None)
  115. self.assertEqual(bob.username, None)
  116. self.assertEqual(bob.nocache, False)
  117. self.assertEqual(bob.cfg_start_auth, True)
  118. self.assertEqual(bob.cfg_start_resolver, False)
  119. self.assertEqual(bob.cfg_start_dhcp4, False)
  120. self.assertEqual(bob.cfg_start_dhcp6, False)
  121. def test_command_handler(self):
  122. class DummySession():
  123. def group_sendmsg(self, msg, group):
  124. (self.msg, self.group) = (msg, group)
  125. def group_recvmsg(self, nonblock, seq): pass
  126. bob = BoB()
  127. bob.verbose = True
  128. bob.cc_session = DummySession()
  129. # a bad command
  130. self.assertEqual(bob.command_handler(-1, None),
  131. isc.config.ccsession.create_answer(1, "bad command"))
  132. # "shutdown" command
  133. self.assertEqual(bob.command_handler("shutdown", None),
  134. isc.config.ccsession.create_answer(0))
  135. self.assertFalse(bob.runnable)
  136. # "sendstats" command
  137. self.assertEqual(bob.command_handler("sendstats", None),
  138. isc.config.ccsession.create_answer(0))
  139. self.assertEqual(bob.cc_session.group, "Stats")
  140. self.assertEqual(bob.cc_session.msg,
  141. isc.config.ccsession.create_command(
  142. 'set', { "stats_data": {
  143. 'bind10.boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME)
  144. }}))
  145. # "ping" command
  146. self.assertEqual(bob.command_handler("ping", None),
  147. isc.config.ccsession.create_answer(0, "pong"))
  148. # "show_processes" command
  149. self.assertEqual(bob.command_handler("show_processes", None),
  150. isc.config.ccsession.create_answer(0,
  151. bob.get_processes()))
  152. # an unknown command
  153. self.assertEqual(bob.command_handler("__UNKNOWN__", None),
  154. isc.config.ccsession.create_answer(1, "Unknown command"))
  155. # Class for testing the BoB without actually starting processes.
  156. # This is used for testing the start/stop components routines and
  157. # the BoB commands.
  158. #
  159. # Testing that external processes start is outside the scope
  160. # of the unit test, by overriding the process start methods we can check
  161. # that the right processes are started depending on the configuration
  162. # options.
  163. class MockBob(BoB):
  164. def __init__(self):
  165. BoB.__init__(self)
  166. # Set flags as to which of the overridden methods has been run.
  167. self.msgq = False
  168. self.cfgmgr = False
  169. self.ccsession = False
  170. self.auth = False
  171. self.resolver = False
  172. self.xfrout = False
  173. self.xfrin = False
  174. self.zonemgr = False
  175. self.stats = False
  176. self.stats_httpd = False
  177. self.cmdctl = False
  178. self.c_channel_env = {}
  179. self.processes = { }
  180. def read_bind10_config(self):
  181. # Configuration options are set directly
  182. pass
  183. def start_msgq(self, c_channel_env):
  184. self.msgq = True
  185. self.processes[2] = ProcessInfo('b10-msgq', ['/bin/false'])
  186. self.processes[2].pid = 2
  187. def start_cfgmgr(self, c_channel_env):
  188. self.cfgmgr = True
  189. self.processes[3] = ProcessInfo('b10-cfgmgr', ['/bin/false'])
  190. self.processes[3].pid = 3
  191. def start_ccsession(self, c_channel_env):
  192. self.ccsession = True
  193. self.processes[4] = ProcessInfo('b10-ccsession', ['/bin/false'])
  194. self.processes[4].pid = 4
  195. def start_auth(self, c_channel_env):
  196. self.auth = True
  197. self.processes[5] = ProcessInfo('b10-auth', ['/bin/false'])
  198. self.processes[5].pid = 5
  199. def start_resolver(self, c_channel_env):
  200. self.resolver = True
  201. self.processes[6] = ProcessInfo('b10-resolver', ['/bin/false'])
  202. self.processes[6].pid = 6
  203. def start_xfrout(self, c_channel_env):
  204. self.xfrout = True
  205. self.processes[7] = ProcessInfo('b10-xfrout', ['/bin/false'])
  206. self.processes[7].pid = 7
  207. def start_xfrin(self, c_channel_env):
  208. self.xfrin = True
  209. self.processes[8] = ProcessInfo('b10-xfrin', ['/bin/false'])
  210. self.processes[8].pid = 8
  211. def start_zonemgr(self, c_channel_env):
  212. self.zonemgr = True
  213. self.processes[9] = ProcessInfo('b10-zonemgr', ['/bin/false'])
  214. self.processes[9].pid = 9
  215. def start_stats(self, c_channel_env):
  216. self.stats = True
  217. self.processes[10] = ProcessInfo('b10-stats', ['/bin/false'])
  218. self.processes[10].pid = 10
  219. def start_stats_httpd(self, c_channel_env):
  220. self.stats_httpd = True
  221. self.processes[11] = ProcessInfo('b10-stats-httpd', ['/bin/false'])
  222. self.processes[11].pid = 11
  223. def start_cmdctl(self, c_channel_env):
  224. self.cmdctl = True
  225. self.processes[12] = ProcessInfo('b10-cmdctl', ['/bin/false'])
  226. self.processes[12].pid = 12
  227. def start_dhcp6(self, c_channel_env):
  228. self.dhcp6 = True
  229. self.processes[13] = ProcessInfo('b10-dhcp6', ['/bin/false'])
  230. self.processes[13]
  231. def start_dhcp4(self, c_channel_env):
  232. self.dhcp4 = True
  233. self.processes[14] = ProcessInfo('b10-dhcp4', ['/bin/false'])
  234. self.processes[14]
  235. # We don't really use all of these stop_ methods. But it might turn out
  236. # someone would add some stop_ method to BoB and we want that one overriden
  237. # in case he forgets to update the tests.
  238. def stop_msgq(self):
  239. if self.msgq:
  240. del self.processes[2]
  241. self.msgq = False
  242. def stop_cfgmgr(self):
  243. if self.cfgmgr:
  244. del self.processes[3]
  245. self.cfgmgr = False
  246. def stop_ccsession(self):
  247. if self.ccssession:
  248. del self.processes[4]
  249. self.ccsession = False
  250. def stop_auth(self):
  251. if self.auth:
  252. del self.processes[5]
  253. self.auth = False
  254. def stop_resolver(self):
  255. if self.resolver:
  256. del self.processes[6]
  257. self.resolver = False
  258. def stop_xfrout(self):
  259. if self.xfrout:
  260. del self.processes[7]
  261. self.xfrout = False
  262. def stop_xfrin(self):
  263. if self.xfrin:
  264. del self.processes[8]
  265. self.xfrin = False
  266. def stop_zonemgr(self):
  267. if self.zonemgr:
  268. del self.processes[9]
  269. self.zonemgr = False
  270. def stop_stats(self):
  271. if self.stats:
  272. del self.processes[10]
  273. self.stats = False
  274. def stop_stats_httpd(self):
  275. if self.stats_httpd:
  276. del self.processes[11]
  277. self.stats_httpd = False
  278. def stop_cmdctl(self):
  279. if self.cmdctl:
  280. del self.processes[12]
  281. self.cmdctl = False
  282. class TestStartStopProcessesBob(unittest.TestCase):
  283. """
  284. Check that the start_all_processes method starts the right combination
  285. of processes and that the right processes are started and stopped
  286. according to changes in configuration.
  287. """
  288. def check_started(self, bob, core, auth, resolver):
  289. """
  290. Check that the right sets of services are started. The ones that
  291. should be running are specified by the core, auth and resolver parameters
  292. (they are groups of processes, eg. auth means b10-auth, -xfrout, -xfrin
  293. and -zonemgr).
  294. """
  295. self.assertEqual(bob.msgq, core)
  296. self.assertEqual(bob.cfgmgr, core)
  297. self.assertEqual(bob.ccsession, core)
  298. self.assertEqual(bob.auth, auth)
  299. self.assertEqual(bob.resolver, resolver)
  300. self.assertEqual(bob.xfrout, auth)
  301. self.assertEqual(bob.xfrin, auth)
  302. self.assertEqual(bob.zonemgr, auth)
  303. self.assertEqual(bob.stats, core)
  304. self.assertEqual(bob.stats_httpd, core)
  305. self.assertEqual(bob.cmdctl, core)
  306. def check_preconditions(self, bob):
  307. self.check_started(bob, False, False, False)
  308. def check_started_none(self, bob):
  309. """
  310. Check that the situation is according to configuration where no servers
  311. should be started. Some processes still need to be running.
  312. """
  313. self.check_started(bob, True, False, False)
  314. def check_started_both(self, bob):
  315. """
  316. Check the situation is according to configuration where both servers
  317. (auth and resolver) are enabled.
  318. """
  319. self.check_started(bob, True, True, True)
  320. def check_started_auth(self, bob):
  321. """
  322. Check the set of processes needed to run auth only is started.
  323. """
  324. self.check_started(bob, True, True, False)
  325. def check_started_resolver(self, bob):
  326. """
  327. Check the set of processes needed to run resolver only is started.
  328. """
  329. self.check_started(bob, True, False, True)
  330. def check_started_dhcp(self, bob, v4, v6):
  331. """
  332. Check if proper combinations of DHCPv4 and DHCpv6 can be started
  333. """
  334. v4found = 0
  335. v6found = 0
  336. for pid in bob.processes:
  337. if (bob.processes[pid].name == "b10-dhcp4"):
  338. v4found += 1
  339. if (bob.processes[pid].name == "b10-dhcp6"):
  340. v6found += 1
  341. # there should be exactly one DHCPv4 daemon (if v4==True)
  342. # there should be exactly one DHCPv6 daemon (if v6==True)
  343. self.assertEqual(v4==True, v4found==1)
  344. self.assertEqual(v6==True, v6found==1)
  345. # Checks the processes started when starting neither auth nor resolver
  346. # is specified.
  347. def test_start_none(self):
  348. # Create BoB and ensure correct initialization
  349. bob = MockBob()
  350. self.check_preconditions(bob)
  351. # Start processes and check what was started
  352. bob.cfg_start_auth = False
  353. bob.cfg_start_resolver = False
  354. bob.start_all_processes()
  355. self.check_started_none(bob)
  356. # Checks the processes started when starting only the auth process
  357. def test_start_auth(self):
  358. # Create BoB and ensure correct initialization
  359. bob = MockBob()
  360. self.check_preconditions(bob)
  361. # Start processes and check what was started
  362. bob.cfg_start_auth = True
  363. bob.cfg_start_resolver = False
  364. bob.start_all_processes()
  365. self.check_started_auth(bob)
  366. # Checks the processes started when starting only the resolver process
  367. def test_start_resolver(self):
  368. # Create BoB and ensure correct initialization
  369. bob = MockBob()
  370. self.check_preconditions(bob)
  371. # Start processes and check what was started
  372. bob.cfg_start_auth = False
  373. bob.cfg_start_resolver = True
  374. bob.start_all_processes()
  375. self.check_started_resolver(bob)
  376. # Checks the processes started when starting both auth and resolver process
  377. def test_start_both(self):
  378. # Create BoB and ensure correct initialization
  379. bob = MockBob()
  380. self.check_preconditions(bob)
  381. # Start processes and check what was started
  382. bob.cfg_start_auth = True
  383. bob.cfg_start_resolver = True
  384. bob.start_all_processes()
  385. self.check_started_both(bob)
  386. def test_config_start(self):
  387. """
  388. Test that the configuration starts and stops processes according
  389. to configuration changes.
  390. """
  391. # Create BoB and ensure correct initialization
  392. bob = MockBob()
  393. self.check_preconditions(bob)
  394. # Start processes (nothing much should be started, as in
  395. # test_start_none)
  396. bob.cfg_start_auth = False
  397. bob.cfg_start_resolver = False
  398. bob.start_all_processes()
  399. bob.runnable = True
  400. self.check_started_none(bob)
  401. # Enable both at once
  402. bob.config_handler({'start_auth': True, 'start_resolver': True})
  403. self.check_started_both(bob)
  404. # Not touched by empty change
  405. bob.config_handler({})
  406. self.check_started_both(bob)
  407. # Not touched by change to the same configuration
  408. bob.config_handler({'start_auth': True, 'start_resolver': True})
  409. self.check_started_both(bob)
  410. # Turn them both off again
  411. bob.config_handler({'start_auth': False, 'start_resolver': False})
  412. self.check_started_none(bob)
  413. # Not touched by empty change
  414. bob.config_handler({})
  415. self.check_started_none(bob)
  416. # Not touched by change to the same configuration
  417. bob.config_handler({'start_auth': False, 'start_resolver': False})
  418. self.check_started_none(bob)
  419. # Start and stop auth separately
  420. bob.config_handler({'start_auth': True})
  421. self.check_started_auth(bob)
  422. bob.config_handler({'start_auth': False})
  423. self.check_started_none(bob)
  424. # Start and stop resolver separately
  425. bob.config_handler({'start_resolver': True})
  426. self.check_started_resolver(bob)
  427. bob.config_handler({'start_resolver': False})
  428. self.check_started_none(bob)
  429. # Alternate
  430. bob.config_handler({'start_auth': True})
  431. self.check_started_auth(bob)
  432. bob.config_handler({'start_auth': False, 'start_resolver': True})
  433. self.check_started_resolver(bob)
  434. bob.config_handler({'start_auth': True, 'start_resolver': False})
  435. self.check_started_auth(bob)
  436. def test_config_start_once(self):
  437. """
  438. Tests that a process is started only once.
  439. """
  440. # Create BoB and ensure correct initialization
  441. bob = MockBob()
  442. self.check_preconditions(bob)
  443. # Start processes (both)
  444. bob.cfg_start_auth = True
  445. bob.cfg_start_resolver = True
  446. bob.start_all_processes()
  447. bob.runnable = True
  448. self.check_started_both(bob)
  449. bob.start_auth = lambda: self.fail("Started auth again")
  450. bob.start_xfrout = lambda: self.fail("Started xfrout again")
  451. bob.start_xfrin = lambda: self.fail("Started xfrin again")
  452. bob.start_zonemgr = lambda: self.fail("Started zonemgr again")
  453. bob.start_resolver = lambda: self.fail("Started resolver again")
  454. # Send again we want to start them. Should not do it, as they are.
  455. bob.config_handler({'start_auth': True})
  456. bob.config_handler({'start_resolver': True})
  457. def test_config_not_started_early(self):
  458. """
  459. Test that processes are not started by the config handler before
  460. startup.
  461. """
  462. bob = MockBob()
  463. self.check_preconditions(bob)
  464. bob.start_auth = lambda: self.fail("Started auth again")
  465. bob.start_xfrout = lambda: self.fail("Started xfrout again")
  466. bob.start_xfrin = lambda: self.fail("Started xfrin again")
  467. bob.start_zonemgr = lambda: self.fail("Started zonemgr again")
  468. bob.start_resolver = lambda: self.fail("Started resolver again")
  469. bob.config_handler({'start_auth': True, 'start_resolver': True})
  470. # Checks that DHCP (v4 and v6) processes are started when expected
  471. def test_start_dhcp(self):
  472. # Create BoB and ensure correct initialization
  473. bob = MockBob()
  474. self.check_preconditions(bob)
  475. # don't care about DNS stuff
  476. bob.cfg_start_auth = False
  477. bob.cfg_start_resolver = False
  478. # v4 and v6 disabled
  479. bob.cfg_start_dhcp6 = False
  480. bob.cfg_start_dhcp4 = False
  481. bob.start_all_processes()
  482. self.check_started_dhcp(bob, False, False)
  483. # v6 only enabled
  484. bob.cfg_start_dhcp6 = True
  485. bob.cfg_start_dhcp4 = False
  486. bob.start_all_processes()
  487. self.check_started_dhcp(bob, False, True)
  488. # uncomment when dhcpv4 becomes implemented
  489. # v4 only enabled
  490. #bob.cfg_start_dhcp6 = False
  491. #bob.cfg_start_dhcp4 = True
  492. #self.check_started_dhcp(bob, True, False)
  493. # both v4 and v6 enabled
  494. #bob.cfg_start_dhcp6 = True
  495. #bob.cfg_start_dhcp4 = True
  496. #self.check_started_dhcp(bob, True, True)
  497. class TestBossCmd(unittest.TestCase):
  498. def test_ping(self):
  499. """
  500. Confirm simple ping command works.
  501. """
  502. bob = MockBob()
  503. answer = bob.command_handler("ping", None)
  504. self.assertEqual(answer, {'result': [0, 'pong']})
  505. def test_show_processes(self):
  506. """
  507. Confirm getting a list of processes works.
  508. """
  509. bob = MockBob()
  510. answer = bob.command_handler("show_processes", None)
  511. self.assertEqual(answer, {'result': [0, []]})
  512. def test_show_processes_started(self):
  513. """
  514. Confirm getting a list of processes works.
  515. """
  516. bob = MockBob()
  517. bob.start_all_processes()
  518. answer = bob.command_handler("show_processes", None)
  519. processes = [[2, 'b10-msgq'],
  520. [3, 'b10-cfgmgr'],
  521. [4, 'b10-ccsession'],
  522. [5, 'b10-auth'],
  523. [7, 'b10-xfrout'],
  524. [8, 'b10-xfrin'],
  525. [9, 'b10-zonemgr'],
  526. [10, 'b10-stats'],
  527. [11, 'b10-stats-httpd'],
  528. [12, 'b10-cmdctl']]
  529. self.assertEqual(answer, {'result': [0, processes]})
  530. class TestParseArgs(unittest.TestCase):
  531. """
  532. This tests parsing of arguments of the bind10 master process.
  533. """
  534. #TODO: Write tests for the original parsing, bad options, etc.
  535. def test_no_opts(self):
  536. """
  537. Test correct default values when no options are passed.
  538. """
  539. options = parse_args([], TestOptParser)
  540. self.assertEqual(None, options.data_path)
  541. self.assertEqual(None, options.config_file)
  542. self.assertEqual(None, options.cmdctl_port)
  543. def test_data_path(self):
  544. """
  545. Test it can parse the data path.
  546. """
  547. self.assertRaises(OptsError, parse_args, ['-p'], TestOptParser)
  548. self.assertRaises(OptsError, parse_args, ['--data-path'],
  549. TestOptParser)
  550. options = parse_args(['-p', '/data/path'], TestOptParser)
  551. self.assertEqual('/data/path', options.data_path)
  552. options = parse_args(['--data-path=/data/path'], TestOptParser)
  553. self.assertEqual('/data/path', options.data_path)
  554. def test_config_filename(self):
  555. """
  556. Test it can parse the config switch.
  557. """
  558. self.assertRaises(OptsError, parse_args, ['-c'], TestOptParser)
  559. self.assertRaises(OptsError, parse_args, ['--config-file'],
  560. TestOptParser)
  561. options = parse_args(['-c', 'config-file'], TestOptParser)
  562. self.assertEqual('config-file', options.config_file)
  563. options = parse_args(['--config-file=config-file'], TestOptParser)
  564. self.assertEqual('config-file', options.config_file)
  565. def test_cmdctl_port(self):
  566. """
  567. Test it can parse the command control port.
  568. """
  569. self.assertRaises(OptsError, parse_args, ['--cmdctl-port=abc'],
  570. TestOptParser)
  571. self.assertRaises(OptsError, parse_args, ['--cmdctl-port=100000000'],
  572. TestOptParser)
  573. self.assertRaises(OptsError, parse_args, ['--cmdctl-port'],
  574. TestOptParser)
  575. options = parse_args(['--cmdctl-port=1234'], TestOptParser)
  576. self.assertEqual(1234, options.cmdctl_port)
  577. def test_brittle(self):
  578. """
  579. Test we can use the "brittle" flag.
  580. """
  581. options = parse_args([], TestOptParser)
  582. self.assertFalse(options.brittle)
  583. options = parse_args(['--brittle'], TestOptParser)
  584. self.assertTrue(options.brittle)
  585. class TestPIDFile(unittest.TestCase):
  586. def setUp(self):
  587. self.pid_file = '@builddir@' + os.sep + 'bind10.pid'
  588. if os.path.exists(self.pid_file):
  589. os.unlink(self.pid_file)
  590. def tearDown(self):
  591. if os.path.exists(self.pid_file):
  592. os.unlink(self.pid_file)
  593. def check_pid_file(self):
  594. # dump PID to the file, and confirm the content is correct
  595. dump_pid(self.pid_file)
  596. my_pid = os.getpid()
  597. self.assertEqual(my_pid, int(open(self.pid_file, "r").read()))
  598. def test_dump_pid(self):
  599. self.check_pid_file()
  600. # make sure any existing content will be removed
  601. open(self.pid_file, "w").write('dummy data\n')
  602. self.check_pid_file()
  603. def test_unlink_pid_file_notexist(self):
  604. dummy_data = 'dummy_data\n'
  605. open(self.pid_file, "w").write(dummy_data)
  606. unlink_pid_file("no_such_pid_file")
  607. # the file specified for unlink_pid_file doesn't exist,
  608. # and the original content of the file should be intact.
  609. self.assertEqual(dummy_data, open(self.pid_file, "r").read())
  610. def test_dump_pid_with_none(self):
  611. # Check the behavior of dump_pid() and unlink_pid_file() with None.
  612. # This should be no-op.
  613. dump_pid(None)
  614. self.assertFalse(os.path.exists(self.pid_file))
  615. dummy_data = 'dummy_data\n'
  616. open(self.pid_file, "w").write(dummy_data)
  617. unlink_pid_file(None)
  618. self.assertEqual(dummy_data, open(self.pid_file, "r").read())
  619. def test_dump_pid_failure(self):
  620. # the attempt to open file will fail, which should result in exception.
  621. self.assertRaises(IOError, dump_pid,
  622. 'nonexistent_dir' + os.sep + 'bind10.pid')
  623. class TestBrittle(unittest.TestCase):
  624. def test_brittle_disabled(self):
  625. bob = MockBob()
  626. bob.start_all_processes()
  627. bob.runnable = True
  628. bob.reap_children()
  629. self.assertTrue(bob.runnable)
  630. def simulated_exit(self):
  631. ret_val = self.exit_info
  632. self.exit_info = (0, 0)
  633. return ret_val
  634. def test_brittle_enabled(self):
  635. bob = MockBob()
  636. bob.start_all_processes()
  637. bob.runnable = True
  638. bob.brittle = True
  639. self.exit_info = (5, 0)
  640. bob._get_process_exit_status = self.simulated_exit
  641. old_stdout = sys.stdout
  642. sys.stdout = open("/dev/null", "w")
  643. bob.reap_children()
  644. sys.stdout = old_stdout
  645. self.assertFalse(bob.runnable)
  646. if __name__ == '__main__':
  647. isc.log.resetUnitTestRootLogger()
  648. unittest.main()