datasrc_info_tests.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. # Copyright (C) 2013 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. import os
  16. import unittest
  17. from isc.dns import *
  18. import isc.config
  19. import isc.datasrc
  20. import isc.log
  21. from isc.server_common.datasrc_clients_mgr import DataSrcClientsMgr
  22. from isc.memmgr.datasrc_info import *
  23. # Defined for easier tests with DataSrcClientsMgr.reconfigure(), which
  24. # only needs get_value() method
  25. class MockConfigData:
  26. def __init__(self, data):
  27. self.__data = data
  28. def get_value(self, identifier):
  29. return self.__data[identifier], False
  30. class TestSegmentInfo(unittest.TestCase):
  31. def setUp(self):
  32. self.__mapped_file_dir = os.environ['TESTDATA_PATH']
  33. self.__sgmt_info = SegmentInfo.create('mapped', 0, RRClass.IN,
  34. 'sqlite3',
  35. {'mapped_file_dir':
  36. self.__mapped_file_dir})
  37. def __check_sgmt_reset_param(self, user_type, expected_ver):
  38. """Common check on the return value of get_reset_param() for
  39. MappedSegmentInfo.
  40. Unless it's expected to return None, it should be a map that
  41. maps "mapped-file" to the expected version of mapped-file.
  42. """
  43. if expected_ver is None:
  44. self.assertIsNone(self.__sgmt_info.get_reset_param(user_type))
  45. return
  46. param = self.__sgmt_info.get_reset_param(user_type)
  47. self.assertEqual(self.__mapped_file_dir +
  48. '/zone-IN-0-sqlite3-mapped.' + str(expected_ver),
  49. param['mapped-file'])
  50. def test_initial_params(self):
  51. self.__check_sgmt_reset_param(SegmentInfo.WRITER, 0)
  52. self.__check_sgmt_reset_param(SegmentInfo.READER, None)
  53. self.assertEqual(self.__sgmt_info.get_state(), SegmentInfo.READY)
  54. self.assertEqual(len(self.__sgmt_info.get_readers()), 0)
  55. self.assertEqual(len(self.__sgmt_info.get_old_readers()), 0)
  56. self.assertEqual(len(self.__sgmt_info.get_events()), 0)
  57. def __si_to_ready_state(self):
  58. # Go to a default starting state
  59. self.__sgmt_info = SegmentInfo.create('mapped', 0, RRClass.IN,
  60. 'sqlite3',
  61. {'mapped_file_dir':
  62. self.__mapped_file_dir})
  63. self.assertEqual(self.__sgmt_info.get_state(), SegmentInfo.READY)
  64. def __si_to_updating_state(self):
  65. self.__si_to_ready_state()
  66. self.__sgmt_info.add_reader(3)
  67. self.__sgmt_info.add_event((42,))
  68. e = self.__sgmt_info.start_update()
  69. self.assertTupleEqual(e, (42,))
  70. self.assertSetEqual(self.__sgmt_info.get_readers(), {3})
  71. self.assertEqual(self.__sgmt_info.get_state(), SegmentInfo.UPDATING)
  72. def __si_to_synchronizing_state(self):
  73. self.__si_to_updating_state()
  74. self.__sgmt_info.complete_update()
  75. self.assertEqual(self.__sgmt_info.get_state(), SegmentInfo.SYNCHRONIZING)
  76. def __si_to_copying_state(self):
  77. self.__si_to_synchronizing_state()
  78. self.__sgmt_info.sync_reader(3)
  79. self.assertEqual(self.__sgmt_info.get_state(), SegmentInfo.COPYING)
  80. def test_add_event(self):
  81. self.assertEqual(len(self.__sgmt_info.get_events()), 0)
  82. self.__sgmt_info.add_event(None)
  83. self.assertNotEqual(len(self.__sgmt_info.get_events()), 0)
  84. def test_add_reader(self):
  85. self.assertSetEqual(self.__sgmt_info.get_readers(), set())
  86. self.assertSetEqual(self.__sgmt_info.get_old_readers(), set())
  87. self.__sgmt_info.add_reader(1)
  88. self.assertSetEqual(self.__sgmt_info.get_readers(), {1})
  89. self.__sgmt_info.add_reader(3)
  90. self.assertSetEqual(self.__sgmt_info.get_readers(), {1, 3})
  91. # ordering doesn't matter in sets
  92. self.__sgmt_info.add_reader(2)
  93. self.assertSetEqual(self.__sgmt_info.get_readers(), {1, 2, 3})
  94. self.assertSetEqual(self.__sgmt_info.get_readers(), {1, 3, 2})
  95. # adding the same existing reader must throw
  96. self.assertRaises(SegmentInfoError, self.__sgmt_info.add_reader, (1))
  97. # but the existing readers must be untouched
  98. self.assertSetEqual(self.__sgmt_info.get_readers(), {1, 3, 2})
  99. # none of this touches the old readers
  100. self.assertSetEqual(self.__sgmt_info.get_old_readers(), set())
  101. def test_start_update(self):
  102. # in READY state
  103. # a) there are no events
  104. self.__si_to_ready_state()
  105. e = self.__sgmt_info.start_update()
  106. self.assertIsNone(e)
  107. # if there are no events, there is nothing to update
  108. self.assertEqual(self.__sgmt_info.get_state(), SegmentInfo.READY)
  109. # b) there are events. this is the same as calling
  110. # self.__si_to_updating_state(), but let's try to be
  111. # descriptive.
  112. self.__si_to_ready_state()
  113. self.__sgmt_info.add_event((42,))
  114. e = self.__sgmt_info.start_update()
  115. self.assertTupleEqual(e, (42,))
  116. self.assertEqual(self.__sgmt_info.get_state(), SegmentInfo.UPDATING)
  117. # in UPDATING state, it should always return None and not change
  118. # state.
  119. self.__si_to_updating_state()
  120. e = self.__sgmt_info.start_update()
  121. self.assertIsNone(e)
  122. self.assertEqual(self.__sgmt_info.get_state(), SegmentInfo.UPDATING)
  123. # in SYNCHRONIZING state, it should always return None and not
  124. # change state.
  125. self.__si_to_synchronizing_state()
  126. e = self.__sgmt_info.start_update()
  127. self.assertIsNone(e)
  128. self.assertEqual(self.__sgmt_info.get_state(), SegmentInfo.SYNCHRONIZING)
  129. # in COPYING state, it should always return None and not
  130. # change state.
  131. self.__si_to_copying_state()
  132. e = self.__sgmt_info.start_update()
  133. self.assertIsNone(e)
  134. self.assertEqual(self.__sgmt_info.get_state(), SegmentInfo.COPYING)
  135. def test_complete_update(self):
  136. # in READY state
  137. self.__si_to_ready_state()
  138. self.assertRaises(SegmentInfoError, self.__sgmt_info.complete_update)
  139. self.assertEqual(self.__sgmt_info.get_state(), SegmentInfo.READY)
  140. # in UPDATING state this is the same as calling
  141. # self.__si_to_synchronizing_state(), but let's try to be
  142. # descriptive.
  143. #
  144. # a) with no events
  145. self.__si_to_updating_state()
  146. e = self.__sgmt_info.complete_update()
  147. self.assertIsNone(e)
  148. self.assertEqual(self.__sgmt_info.get_state(), SegmentInfo.SYNCHRONIZING)
  149. # b) with events
  150. self.__si_to_updating_state()
  151. self.__sgmt_info.add_event((81,))
  152. e = self.__sgmt_info.complete_update()
  153. self.assertIsNone(e) # old_readers is not empty
  154. self.assertEqual(self.__sgmt_info.get_state(), SegmentInfo.SYNCHRONIZING)
  155. # in SYNCHRONIZING state
  156. self.__si_to_synchronizing_state()
  157. self.assertRaises(SegmentInfoError, self.__sgmt_info.complete_update)
  158. self.assertEqual(self.__sgmt_info.get_state(), SegmentInfo.SYNCHRONIZING)
  159. # in COPYING state
  160. self.__si_to_copying_state()
  161. e = self.__sgmt_info.complete_update()
  162. self.assertIsNone(e)
  163. self.assertEqual(self.__sgmt_info.get_state(), SegmentInfo.READY)
  164. def test_sync_reader(self):
  165. # in READY state, it must raise an exception
  166. self.__si_to_ready_state()
  167. self.assertRaises(SegmentInfoError, self.__sgmt_info.sync_reader, (0))
  168. self.assertEqual(self.__sgmt_info.get_state(), SegmentInfo.READY)
  169. # in UPDATING state, it must raise an exception
  170. self.__si_to_updating_state()
  171. self.assertRaises(SegmentInfoError, self.__sgmt_info.sync_reader, (0))
  172. self.assertEqual(self.__sgmt_info.get_state(), SegmentInfo.UPDATING)
  173. # in COPYING state, it must raise an exception
  174. self.__si_to_copying_state()
  175. self.assertRaises(SegmentInfoError, self.__sgmt_info.sync_reader, (0))
  176. self.assertEqual(self.__sgmt_info.get_state(), SegmentInfo.COPYING)
  177. def test_remove_reader(self):
  178. # in READY state, it must raise an exception
  179. self.__si_to_ready_state()
  180. self.assertRaises(SegmentInfoError, self.__sgmt_info.remove_reader, (0))
  181. self.assertEqual(self.__sgmt_info.get_state(), SegmentInfo.READY)
  182. # in UPDATING state, it must raise an exception
  183. self.__si_to_updating_state()
  184. self.assertRaises(SegmentInfoError, self.__sgmt_info.remove_reader, (0))
  185. self.assertEqual(self.__sgmt_info.get_state(), SegmentInfo.UPDATING)
  186. # in COPYING state, it must raise an exception
  187. self.__si_to_copying_state()
  188. self.assertRaises(SegmentInfoError, self.__sgmt_info.remove_reader, (0))
  189. self.assertEqual(self.__sgmt_info.get_state(), SegmentInfo.COPYING)
  190. def test_switch_versions(self):
  191. self.__sgmt_info.switch_versions()
  192. self.__check_sgmt_reset_param(SegmentInfo.WRITER, 1)
  193. self.__check_sgmt_reset_param(SegmentInfo.READER, 0)
  194. self.__sgmt_info.switch_versions()
  195. self.__check_sgmt_reset_param(SegmentInfo.WRITER, 0)
  196. self.__check_sgmt_reset_param(SegmentInfo.READER, 1)
  197. def test_init_others(self):
  198. # For local type of segment, information isn't needed and won't be
  199. # created.
  200. self.assertIsNone(SegmentInfo.create('local', 0, RRClass.IN,
  201. 'sqlite3', {}))
  202. # Unknown type of segment will result in an exception.
  203. self.assertRaises(SegmentInfoError, SegmentInfo.create, 'unknown', 0,
  204. RRClass.IN, 'sqlite3', {})
  205. def test_missing_methods(self):
  206. # Bad subclass of SegmentInfo that doesn't implement mandatory methods.
  207. class TestSegmentInfo(SegmentInfo):
  208. pass
  209. self.assertRaises(SegmentInfoError,
  210. TestSegmentInfo().get_reset_param,
  211. SegmentInfo.WRITER)
  212. self.assertRaises(SegmentInfoError, TestSegmentInfo().switch_versions)
  213. class MockClientList:
  214. """A mock ConfigurableClientList class.
  215. Just providing minimal shortcut interfaces needed for DataSrcInfo class.
  216. """
  217. def __init__(self, status_list):
  218. self.__status_list = status_list
  219. def get_status(self):
  220. return self.__status_list
  221. class TestDataSrcInfo(unittest.TestCase):
  222. def setUp(self):
  223. self.__mapped_file_dir = os.environ['TESTDATA_PATH']
  224. self.__mgr_config = {'mapped_file_dir': self.__mapped_file_dir}
  225. self.__sqlite3_dbfile = os.environ['TESTDATA_PATH'] + '/' + 'zone.db'
  226. self.__clients_map = {
  227. # mixture of 'local' and 'mapped' and 'unused' (type =None)
  228. # segments
  229. RRClass.IN: MockClientList([('datasrc1', 'local', None),
  230. ('datasrc2', 'mapped', None),
  231. ('datasrc3', None, None)]),
  232. RRClass.CH: MockClientList([('datasrc2', 'mapped', None),
  233. ('datasrc1', 'local', None)]) }
  234. def tearDown(self):
  235. if os.path.exists(self.__sqlite3_dbfile):
  236. os.unlink(self.__sqlite3_dbfile)
  237. def __check_sgmt_reset_param(self, sgmt_info, writer_file):
  238. # Check if the initial state of (mapped) segment info object has
  239. # expected values.
  240. self.assertIsNone(sgmt_info.get_reset_param(SegmentInfo.READER))
  241. param = sgmt_info.get_reset_param(SegmentInfo.WRITER)
  242. self.assertEqual(writer_file, param['mapped-file'])
  243. def test_init(self):
  244. """Check basic scenarios of constructing DataSrcInfo."""
  245. # This checks that all data sources of all RR classes are covered,
  246. # "local" segments are ignored, info objects for "mapped" segments
  247. # are created and stored in segment_info_map.
  248. datasrc_info = DataSrcInfo(42, self.__clients_map, self.__mgr_config)
  249. self.assertEqual(42, datasrc_info.gen_id)
  250. self.assertEqual(self.__clients_map, datasrc_info.clients_map)
  251. self.assertEqual(2, len(datasrc_info.segment_info_map))
  252. sgmt_info = datasrc_info.segment_info_map[(RRClass.IN, 'datasrc2')]
  253. self.__check_sgmt_reset_param(sgmt_info, self.__mapped_file_dir +
  254. '/zone-IN-42-datasrc2-mapped.0')
  255. sgmt_info = datasrc_info.segment_info_map[(RRClass.CH, 'datasrc2')]
  256. self.__check_sgmt_reset_param(sgmt_info, self.__mapped_file_dir +
  257. '/zone-CH-42-datasrc2-mapped.0')
  258. # A case where clist.get_status() returns an empty list; shouldn't
  259. # cause disruption
  260. self.__clients_map = { RRClass.IN: MockClientList([])}
  261. datasrc_info = DataSrcInfo(42, self.__clients_map, self.__mgr_config)
  262. self.assertEqual(42, datasrc_info.gen_id)
  263. self.assertEqual(0, len(datasrc_info.segment_info_map))
  264. # A case where clients_map is empty; shouldn't cause disruption
  265. self.__clients_map = {}
  266. datasrc_info = DataSrcInfo(42, self.__clients_map, self.__mgr_config)
  267. self.assertEqual(42, datasrc_info.gen_id)
  268. self.assertEqual(0, len(datasrc_info.segment_info_map))
  269. # This test uses real "mmaped" segment and doesn't work without shared
  270. # memory support.
  271. @unittest.skipIf(os.environ['HAVE_SHARED_MEMORY'] != 'yes',
  272. 'shared memory support is not available')
  273. def test_production(self):
  274. """Check the behavior closer to a production environment.
  275. Instead of using a mock classes, just for confirming we didn't miss
  276. something.
  277. """
  278. cfg_data = MockConfigData(
  279. {"classes":
  280. {"IN": [{"type": "sqlite3", "cache-enable": True,
  281. "cache-type": "mapped", "cache-zones": [],
  282. "params": {"database_file": self.__sqlite3_dbfile}}]
  283. }
  284. })
  285. cmgr = DataSrcClientsMgr(use_cache=True)
  286. cmgr.reconfigure({}, cfg_data)
  287. genid, clients_map = cmgr.get_clients_map()
  288. datasrc_info = DataSrcInfo(genid, clients_map, self.__mgr_config)
  289. self.assertEqual(1, datasrc_info.gen_id)
  290. self.assertEqual(clients_map, datasrc_info.clients_map)
  291. self.assertEqual(1, len(datasrc_info.segment_info_map))
  292. sgmt_info = datasrc_info.segment_info_map[(RRClass.IN, 'sqlite3')]
  293. self.assertIsNone(sgmt_info.get_reset_param(SegmentInfo.READER))
  294. if __name__ == "__main__":
  295. isc.log.init("bind10-test")
  296. isc.log.resetUnitTestRootLogger()
  297. unittest.main()