cfgmgr_test.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  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. #
  16. # Tests for the configuration manager module
  17. #
  18. import unittest
  19. import os
  20. from isc.config.cfgmgr import *
  21. from isc.config import config_data
  22. from unittest_fakesession import FakeModuleCCSession
  23. class TestConfigManagerData(unittest.TestCase):
  24. def setUp(self):
  25. self.data_path = os.environ['CONFIG_TESTDATA_PATH']
  26. self.writable_data_path = os.environ['CONFIG_WR_TESTDATA_PATH']
  27. self.config_manager_data = ConfigManagerData(self.writable_data_path,
  28. file_name="b10-config.db")
  29. self.assertTrue(self.config_manager_data)
  30. def test_abs_file(self):
  31. """
  32. Test what happens if we give the config manager an absolute path.
  33. It shouldn't append the data path to it.
  34. """
  35. abs_path = self.data_path + os.sep + "b10-config-imaginary.db"
  36. data = ConfigManagerData(self.data_path, abs_path)
  37. self.assertEqual(abs_path, data.db_filename)
  38. self.assertEqual(self.data_path, data.data_path)
  39. def test_init(self):
  40. self.assertEqual(self.config_manager_data.data['version'],
  41. config_data.BIND10_CONFIG_DATA_VERSION)
  42. self.assertEqual(self.config_manager_data.data_path,
  43. self.writable_data_path)
  44. self.assertEqual(self.config_manager_data.db_filename,
  45. self.writable_data_path + os.sep + "b10-config.db")
  46. def test_read_from_file(self):
  47. ConfigManagerData.read_from_file(self.writable_data_path, "b10-config.db")
  48. self.assertRaises(ConfigManagerDataEmpty,
  49. ConfigManagerData.read_from_file,
  50. "doesnotexist", "b10-config.db")
  51. self.assertRaises(ConfigManagerDataReadError,
  52. ConfigManagerData.read_from_file,
  53. self.data_path, "b10-config-bad1.db")
  54. self.assertRaises(ConfigManagerDataReadError,
  55. ConfigManagerData.read_from_file,
  56. self.data_path, "b10-config-bad2.db")
  57. self.assertRaises(ConfigManagerDataReadError,
  58. ConfigManagerData.read_from_file,
  59. self.data_path, "b10-config-bad3.db")
  60. self.assertRaises(ConfigManagerDataReadError,
  61. ConfigManagerData.read_from_file,
  62. self.data_path, "b10-config-bad4.db")
  63. def test_write_to_file(self):
  64. output_file_name = "b10-config-write-test"
  65. self.config_manager_data.write_to_file(output_file_name)
  66. new_config = ConfigManagerData(self.data_path, output_file_name)
  67. self.assertEqual(self.config_manager_data, new_config)
  68. os.remove(output_file_name)
  69. def check_existence(self, files, should_exist=[], should_not_exist=[]):
  70. """Helper function for test_rename_config_file.
  71. Arguments:
  72. files: array of file names to check.
  73. should_exist: array of indices, the files in 'files' with these
  74. indices should exist.
  75. should_not_exist: array of indices, the files in 'files' with
  76. these indices should not exist."""
  77. for n in should_exist:
  78. self.assertTrue(os.path.exists(files[n]))
  79. for n in should_not_exist:
  80. self.assertFalse(os.path.exists(files[n]))
  81. def test_rename_config_file(self):
  82. # test file names, put in array for easy cleanup
  83. filenames = [ "b10-config-rename-test",
  84. "b10-config-rename-test.bak",
  85. "b10-config-rename-test.bak.1",
  86. "b10-config-rename-test.bak.2" ]
  87. for filename in filenames:
  88. if os.path.exists(filename):
  89. os.remove(filename)
  90. # The original does not exist, so the new one should not be created
  91. self.config_manager_data.rename_config_file(filenames[0])
  92. self.check_existence(filenames, [], [0, 1, 2, 3])
  93. # now create a file to rename, and call rename again
  94. self.config_manager_data.write_to_file(filenames[0])
  95. self.config_manager_data.rename_config_file(filenames[0])
  96. self.check_existence(filenames, [1], [0, 2, 3])
  97. # If backup already exists, give it a new name automatically
  98. self.config_manager_data.write_to_file(filenames[0])
  99. self.config_manager_data.rename_config_file(filenames[0])
  100. self.check_existence(filenames, [1, 2], [0, 3])
  101. # If backup already exists, give it a new name automatically with
  102. # increasing postfix
  103. self.config_manager_data.write_to_file(filenames[0])
  104. self.config_manager_data.rename_config_file(filenames[0])
  105. self.check_existence(filenames, [1, 2, 3], [0])
  106. # Test with explicit renamed file argument
  107. self.config_manager_data.rename_config_file(filenames[1],
  108. filenames[0])
  109. self.check_existence(filenames, [0, 2, 3], [1])
  110. # clean up again to be nice
  111. for filename in filenames:
  112. if os.path.exists(filename):
  113. os.remove(filename)
  114. def test_equality(self):
  115. # tests the __eq__ function. Equality is only defined
  116. # by equality of the .data element. If data_path or db_filename
  117. # are different, but the contents are the same, it's still
  118. # considered equal
  119. cfd1 = ConfigManagerData(self.data_path, file_name="b10-config.db")
  120. cfd2 = ConfigManagerData(self.data_path, file_name="b10-config.db")
  121. self.assertEqual(cfd1, cfd2)
  122. cfd2.data_path = "some/unknown/path"
  123. self.assertEqual(cfd1, cfd2)
  124. cfd2.db_filename = "bad_file.name"
  125. self.assertEqual(cfd1, cfd2)
  126. cfd2.data['test'] = { 'a': [ 1, 2, 3]}
  127. self.assertNotEqual(cfd1, cfd2)
  128. class TestConfigManager(unittest.TestCase):
  129. def setUp(self):
  130. self.data_path = os.environ['CONFIG_TESTDATA_PATH']
  131. self.writable_data_path = os.environ['CONFIG_WR_TESTDATA_PATH']
  132. self.fake_session = FakeModuleCCSession()
  133. self.cm = ConfigManager(self.writable_data_path,
  134. database_filename="b10-config.db",
  135. session=self.fake_session)
  136. self.name = "TestModule"
  137. self.spec = isc.config.module_spec_from_file(self.data_path + os.sep + "/spec2.spec")
  138. def test_paths(self):
  139. """
  140. Test data_path and database filename is passed trough to
  141. underlying ConfigManagerData.
  142. """
  143. cm = ConfigManager("datapath", "filename", self.fake_session)
  144. self.assertEqual("datapath" + os.sep + "filename",
  145. cm.config.db_filename)
  146. # It should preserve it while reading
  147. cm.read_config()
  148. self.assertEqual("datapath" + os.sep + "filename",
  149. cm.config.db_filename)
  150. def test_init(self):
  151. self.assertEqual(self.cm.module_specs, {})
  152. self.assertEqual(self.cm.data_path, self.writable_data_path)
  153. self.assertIsNotNone(self.cm.config)
  154. self.assertTrue(self.fake_session.has_subscription("ConfigManager"))
  155. self.assertTrue(self.fake_session.has_subscription("Boss", "ConfigManager"))
  156. self.assertFalse(self.cm.running)
  157. def test_notify_boss(self):
  158. self.cm.notify_boss()
  159. msg = self.fake_session.get_message("Boss", None)
  160. self.assertTrue(msg)
  161. # this one is actually wrong, but 'current status quo'
  162. self.assertEqual(msg, {"running": "ConfigManager"})
  163. def test_set_module_spec(self):
  164. module_spec = isc.config.module_spec.module_spec_from_file(self.data_path + os.sep + "spec1.spec")
  165. self.assertNotIn(module_spec.get_module_name(), self.cm.module_specs)
  166. self.cm.set_module_spec(module_spec)
  167. self.assertIn(module_spec.get_module_name(), self.cm.module_specs)
  168. self.assertNotIn(module_spec.get_module_name(), self.cm.virtual_modules)
  169. def test_remove_module_spec(self):
  170. module_spec = isc.config.module_spec.module_spec_from_file(self.data_path + os.sep + "spec1.spec")
  171. self.assertNotIn(module_spec.get_module_name(), self.cm.module_specs)
  172. self.cm.set_module_spec(module_spec)
  173. self.assertIn(module_spec.get_module_name(), self.cm.module_specs)
  174. self.cm.remove_module_spec(module_spec.get_module_name())
  175. self.assertNotIn(module_spec.get_module_name(), self.cm.module_specs)
  176. self.assertNotIn(module_spec.get_module_name(), self.cm.virtual_modules)
  177. def test_add_remove_virtual_module(self):
  178. module_spec = isc.config.module_spec.module_spec_from_file(
  179. self.data_path + os.sep + "spec1.spec")
  180. check_func = lambda: True
  181. # Make sure it's not in there before
  182. self.assertNotIn(module_spec.get_module_name(), self.cm.module_specs)
  183. self.assertNotIn(module_spec.get_module_name(), self.cm.virtual_modules)
  184. # Add it there
  185. self.cm.set_virtual_module(module_spec, check_func)
  186. # Check it's in there
  187. self.assertIn(module_spec.get_module_name(), self.cm.module_specs)
  188. self.assertEqual(self.cm.module_specs[module_spec.get_module_name()],
  189. module_spec)
  190. self.assertEqual(self.cm.virtual_modules[module_spec.get_module_name()],
  191. check_func)
  192. # Remove it again
  193. self.cm.remove_module_spec(module_spec.get_module_name())
  194. self.assertNotIn(module_spec.get_module_name(), self.cm.module_specs)
  195. self.assertNotIn(module_spec.get_module_name(), self.cm.virtual_modules)
  196. def test_get_module_spec(self):
  197. module_spec = isc.config.module_spec.module_spec_from_file(self.data_path + os.sep + "spec1.spec")
  198. self.assertNotIn(module_spec.get_module_name(), self.cm.module_specs)
  199. self.cm.set_module_spec(module_spec)
  200. self.assertIn(module_spec.get_module_name(), self.cm.module_specs)
  201. module_spec2 = self.cm.get_module_spec(module_spec.get_module_name())
  202. self.assertEqual(module_spec.get_full_spec(), module_spec2)
  203. self.assertEqual({}, self.cm.get_module_spec("nosuchmodule"))
  204. def test_get_config_spec(self):
  205. config_spec = self.cm.get_config_spec()
  206. self.assertEqual(config_spec, {})
  207. module_spec = isc.config.module_spec.module_spec_from_file(self.data_path + os.sep + "spec1.spec")
  208. self.assertNotIn(module_spec.get_module_name(), self.cm.module_specs)
  209. self.cm.set_module_spec(module_spec)
  210. self.assertIn(module_spec.get_module_name(), self.cm.module_specs)
  211. config_spec = self.cm.get_config_spec()
  212. self.assertEqual(config_spec, { 'Spec1': None })
  213. self.cm.remove_module_spec('Spec1')
  214. module_spec = isc.config.module_spec.module_spec_from_file(self.data_path + os.sep + "spec2.spec")
  215. self.assertNotIn(module_spec.get_module_name(), self.cm.module_specs)
  216. self.cm.set_module_spec(module_spec)
  217. self.assertIn(module_spec.get_module_name(), self.cm.module_specs)
  218. config_spec = self.cm.get_config_spec()
  219. self.assertEqual(config_spec['Spec2'], module_spec.get_config_spec())
  220. config_spec = self.cm.get_config_spec('Spec2')
  221. self.assertEqual(config_spec['Spec2'], module_spec.get_config_spec())
  222. def test_get_commands_spec(self):
  223. commands_spec = self.cm.get_commands_spec()
  224. self.assertEqual(commands_spec, {})
  225. module_spec = isc.config.module_spec.module_spec_from_file(self.data_path + os.sep + "spec1.spec")
  226. self.assertNotIn(module_spec.get_module_name(), self.cm.module_specs)
  227. self.cm.set_module_spec(module_spec)
  228. self.assertIn(module_spec.get_module_name(), self.cm.module_specs)
  229. commands_spec = self.cm.get_commands_spec()
  230. self.assertEqual(commands_spec, { 'Spec1': None })
  231. self.cm.remove_module_spec('Spec1')
  232. module_spec = isc.config.module_spec.module_spec_from_file(self.data_path + os.sep + "spec2.spec")
  233. self.assertNotIn(module_spec.get_module_name(), self.cm.module_specs)
  234. self.cm.set_module_spec(module_spec)
  235. self.assertIn(module_spec.get_module_name(), self.cm.module_specs)
  236. commands_spec = self.cm.get_commands_spec()
  237. self.assertEqual(commands_spec['Spec2'], module_spec.get_commands_spec())
  238. commands_spec = self.cm.get_commands_spec('Spec2')
  239. self.assertEqual(commands_spec['Spec2'], module_spec.get_commands_spec())
  240. def test_get_statistics_spec(self):
  241. statistics_spec = self.cm.get_statistics_spec()
  242. self.assertEqual(statistics_spec, {})
  243. module_spec = isc.config.module_spec.module_spec_from_file(self.data_path + os.sep + "spec1.spec")
  244. self.assertNotIn(module_spec.get_module_name(), self.cm.module_specs)
  245. self.cm.set_module_spec(module_spec)
  246. self.assertIn(module_spec.get_module_name(), self.cm.module_specs)
  247. statistics_spec = self.cm.get_statistics_spec()
  248. self.assertEqual(statistics_spec, { 'Spec1': None })
  249. self.cm.remove_module_spec('Spec1')
  250. module_spec = isc.config.module_spec.module_spec_from_file(self.data_path + os.sep + "spec2.spec")
  251. self.assertNotIn(module_spec.get_module_name(), self.cm.module_specs)
  252. self.cm.set_module_spec(module_spec)
  253. self.assertIn(module_spec.get_module_name(), self.cm.module_specs)
  254. statistics_spec = self.cm.get_statistics_spec()
  255. self.assertEqual(statistics_spec['Spec2'], module_spec.get_statistics_spec())
  256. statistics_spec = self.cm.get_statistics_spec('Spec2')
  257. self.assertEqual(statistics_spec['Spec2'], module_spec.get_statistics_spec())
  258. def test_read_config(self):
  259. self.assertEqual(self.cm.config.data, {'version': config_data.BIND10_CONFIG_DATA_VERSION})
  260. self.cm.data_path = "/no_such_path"
  261. self.cm.read_config()
  262. self.assertEqual(self.cm.config.data, {'version': config_data.BIND10_CONFIG_DATA_VERSION})
  263. def test_write_config(self):
  264. # tested in ConfigManagerData tests
  265. pass
  266. def _handle_msg_helper(self, msg, expected_answer):
  267. answer = self.cm.handle_msg(msg)
  268. self.assertEqual(expected_answer, answer)
  269. def test_handle_msg_basic_commands(self):
  270. # Some basic commands, where not much interaction happens, just
  271. # check the result
  272. self._handle_msg_helper({},
  273. { 'result': [ 1, 'Unknown message format: {}']})
  274. self._handle_msg_helper("",
  275. { 'result': [ 1, 'Unknown message format: ']})
  276. self._handle_msg_helper({ "command": [ "badcommand" ] },
  277. { 'result': [ 1, "Unknown command: badcommand"]})
  278. self._handle_msg_helper({ "command": [ "get_commands_spec" ] },
  279. { 'result': [ 0, {} ]})
  280. self._handle_msg_helper({ "command": [ "get_statistics_spec" ] },
  281. { 'result': [ 0, {} ]})
  282. self._handle_msg_helper({ "command": [ "get_module_spec" ] },
  283. { 'result': [ 0, {} ]})
  284. self._handle_msg_helper({ "command": [ "get_module_spec",
  285. { "module_name": "Spec2" } ] },
  286. { 'result': [ 0, {} ]})
  287. self._handle_msg_helper({ "command": [ "get_module_spec", 1 ] },
  288. {'result': [1, 'Bad get_module_spec command, '+
  289. 'argument not a dict']})
  290. self._handle_msg_helper({ "command": [ "get_module_spec", { } ] },
  291. {'result': [1, 'Bad module_name in '+
  292. 'get_module_spec command']})
  293. self._handle_msg_helper({ "command": [ "get_config" ] },
  294. { 'result': [ 0, { 'version':
  295. config_data.BIND10_CONFIG_DATA_VERSION }]})
  296. self._handle_msg_helper({ "command": [ "get_config",
  297. { "module_name": "nosuchmodule" } ] },
  298. {'result': [0, { 'version':
  299. config_data.BIND10_CONFIG_DATA_VERSION }]})
  300. self._handle_msg_helper({ "command": [ "get_config", 1 ] },
  301. {'result': [1, 'Bad get_config command, '+
  302. 'argument not a dict']})
  303. self._handle_msg_helper({ "command": [ "get_config", { } ] },
  304. {'result': [1, 'Bad module_name in '+
  305. 'get_config command']})
  306. self._handle_msg_helper({ "command": [ "set_config" ] },
  307. {'result': [1, 'Wrong number of arguments']})
  308. self._handle_msg_helper({ "command": [ "set_config", [{}]] },
  309. {'result': [0]})
  310. self.assertEqual(len(self.fake_session.message_queue), 0)
  311. def test_handle_msg_module_and_stats_commands(self):
  312. self._handle_msg_helper({ "command":
  313. ["module_spec", self.spec.get_full_spec()]
  314. },
  315. {'result': [0]})
  316. # There should be a message on the queue about the 'new' Spec2 module
  317. # from ConfigManager to Cmdctl, containing its name and full
  318. # specification
  319. self.assertEqual(ccsession.create_command(
  320. ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE,
  321. [ self.spec.get_module_name(),
  322. self.spec.get_full_spec()]),
  323. self.fake_session.get_message("Cmdctl", None))
  324. self._handle_msg_helper({ "command": [ "module_spec", { 'foo': 1 } ] },
  325. {'result': [1, 'Error in data definition: no '+
  326. 'module_name in module_spec']})
  327. self._handle_msg_helper({ "command": [ "get_module_spec" ] },
  328. { 'result': [ 0, { self.spec.get_module_name():
  329. self.spec.get_full_spec() } ]})
  330. self._handle_msg_helper({ "command": [ "get_module_spec",
  331. { "module_name" : "Spec2" } ] },
  332. { 'result': [ 0, self.spec.get_full_spec() ] })
  333. self._handle_msg_helper({ "command": [ "get_commands_spec" ] },
  334. { 'result': [ 0, { self.spec.get_module_name():
  335. self.spec.get_commands_spec()}]})
  336. self._handle_msg_helper({ "command": [ "get_statistics_spec" ] },
  337. { 'result': [ 0, { self.spec.get_module_name():
  338. self.spec.get_statistics_spec()}]})
  339. def __test_handle_msg_update_config_helper(self, new_config):
  340. # Helper function for the common pattern in
  341. # test_handle_msg_update_config; send 'set config', check for
  342. # update message, check if config has indeed been updated
  343. my_ok_answer = { 'result': [ 0 ] }
  344. # Send the 'ok' that cfgmgr expects back to the fake queue first
  345. self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
  346. config_version = config_data.BIND10_CONFIG_DATA_VERSION
  347. self._handle_msg_helper({ "command": [ "set_config",
  348. [ { "version": config_version,
  349. self.name: new_config } ] ] },
  350. my_ok_answer)
  351. # The cfgmgr should have eaten the ok message, and sent out an update
  352. # message
  353. self.assertEqual(len(self.fake_session.message_queue), 1)
  354. self.assertEqual({'command': [ 'config_update', new_config]},
  355. self.fake_session.get_message(self.name, None))
  356. # Config should have been updated
  357. self.assertEqual(self.cm.config.data, {self.name: new_config,
  358. 'version': config_version})
  359. # and the queue should now be empty again
  360. self.assertEqual(len(self.fake_session.message_queue), 0)
  361. def test_handle_msg_update_config(self):
  362. # Update the configuration and check results a few times
  363. # only work the first time
  364. self.__test_handle_msg_update_config_helper({ "test": 123 })
  365. self.__test_handle_msg_update_config_helper({ "test": 124 })
  366. self.__test_handle_msg_update_config_helper({ "test": 125 })
  367. self.__test_handle_msg_update_config_helper({ "test": 126 })
  368. # Now send an error result (i.e. config not accepted)
  369. my_bad_answer = { 'result': [1, "bad config"] }
  370. self.fake_session.group_sendmsg(my_bad_answer, "ConfigManager")
  371. self._handle_msg_helper({ "command": [ "set_config",
  372. [self.name, { "test": 127 }] ] },
  373. my_bad_answer )
  374. self.assertEqual(len(self.fake_session.message_queue), 1)
  375. self.assertEqual({'command': [ 'config_update', {'test': 127}]},
  376. self.fake_session.get_message(self.name, None))
  377. # Config should not be updated due to the error
  378. self.cm.read_config()
  379. self.assertEqual(self.cm.config.data, { self.name: {'test': 126},
  380. 'version': config_data.BIND10_CONFIG_DATA_VERSION})
  381. self.assertEqual(len(self.fake_session.message_queue), 0)
  382. self.fake_session.group_sendmsg(None, 'ConfigManager')
  383. self._handle_msg_helper({ "command": [ "set_config", [ ] ] },
  384. {'result': [1, 'Wrong number of arguments']} )
  385. self._handle_msg_helper({ "command": [ "set_config",
  386. [ self.name, { "test": 128 }]]},
  387. { 'result': [1, 'No answer message '+
  388. 'from TestModule']} )
  389. # This command should leave a message to the TestModule to update its
  390. # configuration (since the TestModule did not eat it)
  391. self.assertEqual(len(self.fake_session.message_queue), 1)
  392. self.assertEqual(
  393. ccsession.create_command(ccsession.COMMAND_CONFIG_UPDATE,
  394. { "test": 128 }),
  395. self.fake_session.get_message("TestModule", None))
  396. # Make sure queue is empty now
  397. self.assertEqual(len(self.fake_session.message_queue), 0)
  398. # Shutdown should result in 'ok' answer
  399. self._handle_msg_helper({ "command":
  400. ["shutdown"]
  401. },
  402. {'result': [0]})
  403. def test_stopping_message(self):
  404. # Update the system by announcing this module
  405. self._handle_msg_helper({ "command":
  406. ["module_spec", self.spec.get_full_spec()]
  407. },
  408. {'result': [0]})
  409. # This causes a update to be sent from the ConfigManager to the CmdCtl
  410. # channel, containing the new module's name and full specification
  411. self.assertEqual(ccsession.create_command(
  412. ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE,
  413. [ self.spec.get_module_name(),
  414. self.spec.get_full_spec()]),
  415. self.fake_session.get_message("Cmdctl", None))
  416. # A stopping message should get no response, but should cause another
  417. # message to be sent, if it is a known module
  418. self._handle_msg_helper({ "command": [ "stopping",
  419. { "module_name": "Spec2"}] },
  420. None)
  421. self.assertEqual(len(self.fake_session.message_queue), 1)
  422. self.assertEqual({'command': [ 'module_specification_update',
  423. ['Spec2', None] ] },
  424. self.fake_session.get_message("Cmdctl", None))
  425. # but if the 'stopping' module is either unknown or not running,
  426. # no followup message should be sent
  427. self._handle_msg_helper({ "command":
  428. [ "stopping",
  429. { "module_name": "NoSuchModule" } ] },
  430. None)
  431. self.assertEqual(len(self.fake_session.message_queue), 0)
  432. def test_set_config_virtual(self):
  433. """Test that if the module is virtual, we don't send it over the
  434. message bus, but call the checking function.
  435. """
  436. # We run the same three times, with different return values
  437. def single_test(value, returnFunc, expectedResult):
  438. # Because closures can't assign to closed-in variables, we pass
  439. # it trough self
  440. self.called_with = None
  441. def check_test(new_data):
  442. self.called_with = new_data
  443. return returnFunc()
  444. # Register our virtual module
  445. self.cm.set_virtual_module(self.spec, check_test)
  446. # The fake session will throw now if it tries to read a response.
  447. # Handy, we don't need to find a complicated way to check for it.
  448. result = self.cm.handle_msg(ccsession.create_command(
  449. ccsession.COMMAND_SET_CONFIG,
  450. [self.spec.get_module_name(), { "item1": value }]))
  451. # Check the correct result is passed and our function was called
  452. # With correct data
  453. self.assertEqual(self.called_with['item1'], value)
  454. self.assertEqual(result, {'result': expectedResult})
  455. if expectedResult[0] == 0:
  456. # Check it provided the correct notification
  457. self.assertEqual(len(self.fake_session.message_queue), 1)
  458. self.assertEqual({'command': [ 'config_update',
  459. {'item1': value}]},
  460. self.fake_session.get_message('Spec2', None))
  461. # and the queue should now be empty again
  462. self.assertEqual(len(self.fake_session.message_queue), 0)
  463. else:
  464. # It shouldn't send anything on error
  465. self.assertEqual(len(self.fake_session.message_queue), 0)
  466. # Success
  467. single_test(5, lambda: None, [0])
  468. # Graceful error
  469. single_test(6, lambda: "Just error", [1, "Just error"])
  470. # Exception from the checker
  471. def raiser():
  472. raise Exception("Just exception")
  473. single_test(7, raiser, [1, "Exception: Just exception"])
  474. def test_set_config_all(self):
  475. my_ok_answer = { 'result': [ 0 ] }
  476. self.assertEqual({"version": 2}, self.cm.config.data)
  477. self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
  478. self.cm.handle_msg(ccsession.create_command(
  479. ccsession.COMMAND_SET_CONFIG, ["test", { "value1": 123 }]))
  480. self.assertEqual({"version": config_data.BIND10_CONFIG_DATA_VERSION,
  481. "test": { "value1": 123 }
  482. }, self.cm.config.data)
  483. self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
  484. self.cm.handle_msg(ccsession.create_command(
  485. ccsession.COMMAND_SET_CONFIG, ["test", { "value1": 124 }]))
  486. self.assertEqual({"version": config_data.BIND10_CONFIG_DATA_VERSION,
  487. "test": { "value1": 124 }
  488. }, self.cm.config.data)
  489. self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
  490. self.cm.handle_msg(ccsession.create_command(
  491. ccsession.COMMAND_SET_CONFIG, ["test", { "value2": True }]))
  492. self.assertEqual({"version": config_data.BIND10_CONFIG_DATA_VERSION,
  493. "test": { "value1": 124,
  494. "value2": True
  495. }
  496. }, self.cm.config.data)
  497. self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
  498. self.cm.handle_msg(ccsession.create_command(
  499. ccsession.COMMAND_SET_CONFIG, ["test", { "value3": [ 1, 2, 3 ] }]))
  500. self.assertEqual({"version": config_data.BIND10_CONFIG_DATA_VERSION,
  501. "test": { "value1": 124,
  502. "value2": True,
  503. "value3": [ 1, 2, 3 ]
  504. }
  505. }, self.cm.config.data)
  506. self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
  507. self.cm.handle_msg(ccsession.create_command(
  508. ccsession.COMMAND_SET_CONFIG, ["test", { "value2": False }]))
  509. self.assertEqual({"version": config_data.BIND10_CONFIG_DATA_VERSION,
  510. "test": { "value1": 124,
  511. "value2": False,
  512. "value3": [ 1, 2, 3 ]
  513. }
  514. }, self.cm.config.data)
  515. self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
  516. self.cm.handle_msg(ccsession.create_command(
  517. ccsession.COMMAND_SET_CONFIG, ["test", { "value1": None }]))
  518. self.assertEqual({"version": config_data.BIND10_CONFIG_DATA_VERSION,
  519. "test": { "value2": False,
  520. "value3": [ 1, 2, 3 ]
  521. }
  522. }, self.cm.config.data)
  523. self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
  524. self.cm.handle_msg(ccsession.create_command(
  525. ccsession.COMMAND_SET_CONFIG, ["test", { "value3": [ 1 ] }]))
  526. self.assertEqual({"version": config_data.BIND10_CONFIG_DATA_VERSION,
  527. "test": { "value2": False,
  528. "value3": [ 1 ]
  529. }
  530. }, self.cm.config.data)
  531. def test_run(self):
  532. self.fake_session.group_sendmsg({ "command": [ "get_commands_spec" ] }, "ConfigManager")
  533. self.fake_session.group_sendmsg({ "command": [ "get_statistics_spec" ] }, "ConfigManager")
  534. self.fake_session.group_sendmsg({ "command": [ "stopping", { "module_name": "FooModule" } ] }, "ConfigManager")
  535. self.fake_session.group_sendmsg({ "command": [ "shutdown" ] }, "ConfigManager")
  536. self.assertEqual(len(self.fake_session.message_queue), 4)
  537. self.cm.run()
  538. # All commands should have been read out by run()
  539. # Three of the commands should have been responded to, so the queue
  540. # should now contain three answers
  541. self.assertEqual(len(self.fake_session.message_queue), 3)
  542. if __name__ == '__main__':
  543. if not 'CONFIG_TESTDATA_PATH' in os.environ or not 'CONFIG_WR_TESTDATA_PATH' in os.environ:
  544. print("You need to set the environment variable CONFIG_TESTDATA_PATH and CONFIG_WR_TESTDATA_PATH to point to the directory containing the test data files")
  545. exit(1)
  546. isc.log.init("unittests")
  547. isc.log.resetUnitTestRootLogger()
  548. unittest.main()