bind10_test.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. from bind10 import ProcessInfo, BoB
  2. # XXX: environment tests are currently disabled, due to the preprocessor
  3. # setup that we have now complicating the environment
  4. import unittest
  5. import sys
  6. import os
  7. import signal
  8. import socket
  9. from isc.net.addr import IPAddr
  10. class TestProcessInfo(unittest.TestCase):
  11. def setUp(self):
  12. # redirect stdout to a pipe so we can check that our
  13. # process spawning is doing the right thing with stdout
  14. self.old_stdout = os.dup(sys.stdout.fileno())
  15. self.pipes = os.pipe()
  16. os.dup2(self.pipes[1], sys.stdout.fileno())
  17. os.close(self.pipes[1])
  18. # note that we use dup2() to restore the original stdout
  19. # to the main program ASAP in each test... this prevents
  20. # hangs reading from the child process (as the pipe is only
  21. # open in the child), and also insures nice pretty output
  22. def tearDown(self):
  23. # clean up our stdout munging
  24. os.dup2(self.old_stdout, sys.stdout.fileno())
  25. os.close(self.pipes[0])
  26. def test_init(self):
  27. pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ])
  28. os.dup2(self.old_stdout, sys.stdout.fileno())
  29. self.assertEqual(pi.name, 'Test Process')
  30. self.assertEqual(pi.args, [ '/bin/echo', 'foo' ])
  31. # self.assertEqual(pi.env, { 'PATH': os.environ['PATH'],
  32. # 'PYTHON_EXEC': os.environ['PYTHON_EXEC'] })
  33. self.assertEqual(pi.dev_null_stdout, False)
  34. self.assertEqual(os.read(self.pipes[0], 100), b"foo\n")
  35. self.assertNotEqual(pi.process, None)
  36. self.assertTrue(type(pi.pid) is int)
  37. # def test_setting_env(self):
  38. # pi = ProcessInfo('Test Process', [ '/bin/true' ], env={'FOO': 'BAR'})
  39. # os.dup2(self.old_stdout, sys.stdout.fileno())
  40. # self.assertEqual(pi.env, { 'PATH': os.environ['PATH'],
  41. # 'PYTHON_EXEC': os.environ['PYTHON_EXEC'],
  42. # 'FOO': 'BAR' })
  43. def test_setting_null_stdout(self):
  44. pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ],
  45. dev_null_stdout=True)
  46. os.dup2(self.old_stdout, sys.stdout.fileno())
  47. self.assertEqual(pi.dev_null_stdout, True)
  48. self.assertEqual(os.read(self.pipes[0], 100), b"")
  49. def test_respawn(self):
  50. pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ])
  51. # wait for old process to work...
  52. self.assertEqual(os.read(self.pipes[0], 100), b"foo\n")
  53. # respawn it
  54. old_pid = pi.pid
  55. pi.respawn()
  56. os.dup2(self.old_stdout, sys.stdout.fileno())
  57. # make sure the new one started properly
  58. self.assertEqual(pi.name, 'Test Process')
  59. self.assertEqual(pi.args, [ '/bin/echo', 'foo' ])
  60. # self.assertEqual(pi.env, { 'PATH': os.environ['PATH'],
  61. # 'PYTHON_EXEC': os.environ['PYTHON_EXEC'] })
  62. self.assertEqual(pi.dev_null_stdout, False)
  63. self.assertEqual(os.read(self.pipes[0], 100), b"foo\n")
  64. self.assertNotEqual(pi.process, None)
  65. self.assertTrue(type(pi.pid) is int)
  66. self.assertNotEqual(pi.pid, old_pid)
  67. class TestBoB(unittest.TestCase):
  68. def test_init(self):
  69. bob = BoB()
  70. self.assertEqual(bob.verbose, False)
  71. self.assertEqual(bob.msgq_socket_file, None)
  72. self.assertEqual(bob.dns_port, 5300)
  73. self.assertEqual(bob.address, None)
  74. self.assertEqual(bob.cc_session, None)
  75. self.assertEqual(bob.ccs, None)
  76. self.assertEqual(bob.processes, {})
  77. self.assertEqual(bob.dead_processes, {})
  78. self.assertEqual(bob.runnable, False)
  79. self.assertEqual(bob.uid, None)
  80. self.assertEqual(bob.username, None)
  81. self.assertEqual(bob.nocache, False)
  82. self.assertEqual(bob.cfg_start_auth, True)
  83. self.assertEqual(bob.cfg_start_resolver, False)
  84. def test_init_alternate_socket(self):
  85. bob = BoB("alt_socket_file")
  86. self.assertEqual(bob.verbose, False)
  87. self.assertEqual(bob.msgq_socket_file, "alt_socket_file")
  88. self.assertEqual(bob.address, None)
  89. self.assertEqual(bob.dns_port, 5300)
  90. self.assertEqual(bob.cc_session, None)
  91. self.assertEqual(bob.ccs, None)
  92. self.assertEqual(bob.processes, {})
  93. self.assertEqual(bob.dead_processes, {})
  94. self.assertEqual(bob.runnable, False)
  95. self.assertEqual(bob.uid, None)
  96. self.assertEqual(bob.username, None)
  97. self.assertEqual(bob.nocache, False)
  98. self.assertEqual(bob.cfg_start_auth, True)
  99. self.assertEqual(bob.cfg_start_resolver, False)
  100. def test_init_alternate_dns_port(self):
  101. bob = BoB(None, 9999)
  102. self.assertEqual(bob.verbose, False)
  103. self.assertEqual(bob.msgq_socket_file, None)
  104. self.assertEqual(bob.dns_port, 9999)
  105. self.assertEqual(bob.address, None)
  106. self.assertEqual(bob.cc_session, None)
  107. self.assertEqual(bob.ccs, None)
  108. self.assertEqual(bob.processes, {})
  109. self.assertEqual(bob.dead_processes, {})
  110. self.assertEqual(bob.runnable, False)
  111. self.assertEqual(bob.uid, None)
  112. self.assertEqual(bob.username, None)
  113. self.assertEqual(bob.nocache, False)
  114. self.assertEqual(bob.cfg_start_auth, True)
  115. self.assertEqual(bob.cfg_start_resolver, False)
  116. def test_init_alternate_address(self):
  117. bob = BoB(None, 1234, IPAddr('127.127.127.127'))
  118. self.assertEqual(bob.verbose, False)
  119. self.assertEqual(bob.msgq_socket_file, None)
  120. self.assertEqual(bob.dns_port, 1234)
  121. self.assertEqual(bob.address.addr, socket.inet_aton('127.127.127.127'))
  122. self.assertEqual(bob.cc_session, None)
  123. self.assertEqual(bob.ccs, None)
  124. self.assertEqual(bob.processes, {})
  125. self.assertEqual(bob.dead_processes, {})
  126. self.assertEqual(bob.runnable, False)
  127. self.assertEqual(bob.uid, None)
  128. self.assertEqual(bob.username, None)
  129. self.assertEqual(bob.nocache, False)
  130. self.assertEqual(bob.cfg_start_auth, True)
  131. self.assertEqual(bob.cfg_start_resolver, False)
  132. # Class for testing the BoB start/stop components routines.
  133. #
  134. # Although testing that external processes start is outside the scope
  135. # of the unit test, by overriding the process start methods we can check
  136. # that the right processes are started depending on the configuration
  137. # options.
  138. class StartStopCheckBob(BoB):
  139. def __init__(self):
  140. BoB.__init__(self)
  141. # Set flags as to which of the overridden methods has been run.
  142. self.msgq = False
  143. self.cfgmgr = False
  144. self.ccsession = False
  145. self.auth = False
  146. self.resolver = False
  147. self.xfrout = False
  148. self.xfrin = False
  149. self.zonemgr = False
  150. self.stats = False
  151. self.cmdctl = False
  152. self.c_channel_env = {}
  153. def read_bind10_config(self):
  154. # Configuration options are set directly
  155. pass
  156. def start_msgq(self, c_channel_env):
  157. self.msgq = True
  158. def start_cfgmgr(self, c_channel_env):
  159. self.cfgmgr = True
  160. def start_ccsession(self, c_channel_env):
  161. self.ccsession = True
  162. def start_auth(self, c_channel_env):
  163. self.auth = True
  164. def start_resolver(self, c_channel_env):
  165. self.resolver = True
  166. def start_xfrout(self, c_channel_env):
  167. self.xfrout = True
  168. def start_xfrin(self, c_channel_env):
  169. self.xfrin = True
  170. def start_zonemgr(self, c_channel_env):
  171. self.zonemgr = True
  172. def start_stats(self, c_channel_env):
  173. self.stats = True
  174. def start_cmdctl(self, c_channel_env):
  175. self.cmdctl = True
  176. # We don't really use all of these stop_ methods. But it might turn out
  177. # someone would add some stop_ method to BoB and we want that one overriden
  178. # in case he forgets to update the tests.
  179. def stop_msgq(self):
  180. self.msgq = False
  181. def stop_cfgmgr(self):
  182. self.cfgmgr = False
  183. def stop_ccsession(self):
  184. self.ccsession = False
  185. def stop_auth(self):
  186. self.auth = False
  187. def stop_resolver(self):
  188. self.resolver = False
  189. def stop_xfrout(self):
  190. self.xfrout = False
  191. def stop_xfrin(self):
  192. self.xfrin = False
  193. def stop_zonemgr(self):
  194. self.zonemgr = False
  195. def stop_stats(self):
  196. self.stats = False
  197. def stop_cmdctl(self):
  198. self.cmdctl = False
  199. class TestStartStopProcessesBob(unittest.TestCase):
  200. """
  201. Check that the start_all_processes method starts the right combination
  202. of processes and that the right processes are started and stopped
  203. according to changes in configuration.
  204. """
  205. def check_started(self, bob, core, auth, resolver):
  206. """
  207. Check that the right sets of services are started. The ones that
  208. should be running are specified by the core, auth and resolver parameters
  209. (they are groups of processes, eg. auth means b10-auth, -xfrout, -xfrin
  210. and -zonemgr).
  211. """
  212. self.assertEqual(bob.msgq, core)
  213. self.assertEqual(bob.cfgmgr, core)
  214. self.assertEqual(bob.ccsession, core)
  215. self.assertEqual(bob.auth, auth)
  216. self.assertEqual(bob.resolver, resolver)
  217. self.assertEqual(bob.xfrout, auth)
  218. self.assertEqual(bob.xfrin, auth)
  219. self.assertEqual(bob.zonemgr, auth)
  220. self.assertEqual(bob.stats, core)
  221. self.assertEqual(bob.cmdctl, core)
  222. def check_preconditions(self, bob):
  223. self.check_started(bob, False, False, False)
  224. def check_started_none(self, bob):
  225. """
  226. Check that the situation is according to configuration where no servers
  227. should be started. Some processes still need to be running.
  228. """
  229. self.check_started(bob, True, False, False)
  230. def check_started_both(self, bob):
  231. """
  232. Check the situation is according to configuration where both servers
  233. (auth and resolver) are enabled.
  234. """
  235. self.check_started(bob, True, True, True)
  236. def check_started_auth(self, bob):
  237. """
  238. Check the set of processes needed to run auth only is started.
  239. """
  240. self.check_started(bob, True, True, False)
  241. def check_started_resolver(self, bob):
  242. """
  243. Check the set of processes needed to run resolver only is started.
  244. """
  245. self.check_started(bob, True, False, True)
  246. # Checks the processes started when starting neither auth nor resolver
  247. # is specified.
  248. def test_start_none(self):
  249. # Create BoB and ensure correct initialization
  250. bob = StartStopCheckBob()
  251. self.check_preconditions(bob)
  252. # Start processes and check what was started
  253. bob.cfg_start_auth = False
  254. bob.cfg_start_resolver = False
  255. bob.start_all_processes()
  256. self.check_started_none(bob)
  257. # Checks the processes started when starting only the auth process
  258. def test_start_auth(self):
  259. # Create BoB and ensure correct initialization
  260. bob = StartStopCheckBob()
  261. self.check_preconditions(bob)
  262. # Start processes and check what was started
  263. bob.cfg_start_auth = True
  264. bob.cfg_start_resolver = False
  265. bob.start_all_processes()
  266. self.check_started_auth(bob)
  267. # Checks the processes started when starting only the resolver process
  268. def test_start_resolver(self):
  269. # Create BoB and ensure correct initialization
  270. bob = StartStopCheckBob()
  271. self.check_preconditions(bob)
  272. # Start processes and check what was started
  273. bob.cfg_start_auth = False
  274. bob.cfg_start_resolver = True
  275. bob.start_all_processes()
  276. self.check_started_resolver(bob)
  277. # Checks the processes started when starting both auth and resolver process
  278. def test_start_both(self):
  279. # Create BoB and ensure correct initialization
  280. bob = StartStopCheckBob()
  281. self.check_preconditions(bob)
  282. # Start processes and check what was started
  283. bob.cfg_start_auth = True
  284. bob.cfg_start_resolver = True
  285. bob.start_all_processes()
  286. self.check_started_both(bob)
  287. def test_config_start(self):
  288. """
  289. Test that the configuration starts and stops processes according
  290. to configuration changes.
  291. """
  292. # Create BoB and ensure correct initialization
  293. bob = StartStopCheckBob()
  294. self.check_preconditions(bob)
  295. # Start processes (nothing much should be started, as in
  296. # test_start_none)
  297. bob.cfg_start_auth = False
  298. bob.cfg_start_resolver = False
  299. bob.start_all_processes()
  300. bob.runnable = True
  301. self.check_started_none(bob)
  302. # Enable both at once
  303. bob.config_handler({'start_auth': True, 'start_resolver': True})
  304. self.check_started_both(bob)
  305. # Not touched by empty change
  306. bob.config_handler({})
  307. self.check_started_both(bob)
  308. # Not touched by change to the same configuration
  309. bob.config_handler({'start_auth': True, 'start_resolver': True})
  310. self.check_started_both(bob)
  311. # Turn them both off again
  312. bob.config_handler({'start_auth': False, 'start_resolver': False})
  313. self.check_started_none(bob)
  314. # Not touched by empty change
  315. bob.config_handler({})
  316. self.check_started_none(bob)
  317. # Not touched by change to the same configuration
  318. bob.config_handler({'start_auth': False, 'start_resolver': False})
  319. self.check_started_none(bob)
  320. # Start and stop auth separately
  321. bob.config_handler({'start_auth': True})
  322. self.check_started_auth(bob)
  323. bob.config_handler({'start_auth': False})
  324. self.check_started_none(bob)
  325. # Start and stop resolver separately
  326. bob.config_handler({'start_resolver': True})
  327. self.check_started_resolver(bob)
  328. bob.config_handler({'start_resolver': False})
  329. self.check_started_none(bob)
  330. # Alternate
  331. bob.config_handler({'start_auth': True})
  332. self.check_started_auth(bob)
  333. bob.config_handler({'start_auth': False, 'start_resolver': True})
  334. self.check_started_resolver(bob)
  335. bob.config_handler({'start_auth': True, 'start_resolver': False})
  336. self.check_started_auth(bob)
  337. def test_config_start_once(self):
  338. """
  339. Tests that a process is started only once.
  340. """
  341. # Create BoB and ensure correct initialization
  342. bob = StartStopCheckBob()
  343. self.check_preconditions(bob)
  344. # Start processes (both)
  345. bob.cfg_start_auth = True
  346. bob.cfg_start_resolver = True
  347. bob.start_all_processes()
  348. bob.runnable = True
  349. self.check_started_both(bob)
  350. bob.start_auth = lambda: self.fail("Started auth again")
  351. bob.start_xfrout = lambda: self.fail("Started xfrout again")
  352. bob.start_xfrin = lambda: self.fail("Started xfrin again")
  353. bob.start_zonemgr = lambda: self.fail("Started zonemgr again")
  354. bob.start_resolver = lambda: self.fail("Started resolver again")
  355. # Send again we want to start them. Should not do it, as they are.
  356. bob.config_handler({'start_auth': True})
  357. bob.config_handler({'start_resolver': True})
  358. def test_config_not_started_early(self):
  359. """
  360. Test that processes are not started by the config handler before
  361. startup.
  362. """
  363. bob = StartStopCheckBob()
  364. self.check_preconditions(bob)
  365. bob.start_auth = lambda: self.fail("Started auth again")
  366. bob.start_xfrout = lambda: self.fail("Started xfrout again")
  367. bob.start_xfrin = lambda: self.fail("Started xfrin again")
  368. bob.start_zonemgr = lambda: self.fail("Started zonemgr again")
  369. bob.start_resolver = lambda: self.fail("Started resolver again")
  370. bob.config_handler({'start_auth': True, 'start_resolver': True})
  371. if __name__ == '__main__':
  372. unittest.main()