datasrc_info_tests.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  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.__sgmt_info.add_reader(1)
  87. self.assertSetEqual(self.__sgmt_info.get_readers(), {1})
  88. self.__sgmt_info.add_reader(3)
  89. self.assertSetEqual(self.__sgmt_info.get_readers(), {1, 3})
  90. # ordering doesn't matter in sets
  91. self.__sgmt_info.add_reader(2)
  92. self.assertSetEqual(self.__sgmt_info.get_readers(), {1, 2, 3})
  93. self.assertSetEqual(self.__sgmt_info.get_readers(), {1, 3, 2})
  94. # adding the same existing reader must throw
  95. self.assertRaises(SegmentInfoError, self.__sgmt_info.add_reader, (1))
  96. # but the existing readers must be untouched
  97. self.assertSetEqual(self.__sgmt_info.get_readers(), {1, 3, 2})
  98. def test_complete_update(self):
  99. # in READY state
  100. self.__si_to_ready_state()
  101. self.assertRaises(SegmentInfoError, self.__sgmt_info.complete_update)
  102. # in UPDATING state this is the same as calling
  103. # self.__si_to_synchronizing_state(), but let's try to be
  104. # descriptive
  105. #
  106. # a) with no events
  107. self.__si_to_updating_state()
  108. e = self.__sgmt_info.complete_update()
  109. self.assertIsNone(e)
  110. self.assertEqual(self.__sgmt_info.get_state(), SegmentInfo.SYNCHRONIZING)
  111. # b) with events
  112. self.__si_to_updating_state()
  113. self.__sgmt_info.add_event((81,))
  114. e = self.__sgmt_info.complete_update()
  115. self.assertIsNone(e) # old_readers is not empty
  116. self.assertEqual(self.__sgmt_info.get_state(), SegmentInfo.SYNCHRONIZING)
  117. # in SYNCHRONIZING state
  118. self.__si_to_synchronizing_state()
  119. self.assertRaises(SegmentInfoError, self.__sgmt_info.complete_update)
  120. # in COPYING state with no events
  121. self.__si_to_copying_state()
  122. self.__sgmt_info.complete_update()
  123. self.assertEqual(self.__sgmt_info.get_state(), SegmentInfo.READY)
  124. def test_sync_reader_when_ready(self):
  125. self.assertRaises(SegmentInfoError, self.__sgmt_info.sync_reader, (None))
  126. def test_remove_reader_when_ready(self):
  127. self.assertRaises(SegmentInfoError, self.__sgmt_info.remove_reader, (None))
  128. def test_switch_versions(self):
  129. self.__sgmt_info.switch_versions()
  130. self.__check_sgmt_reset_param(SegmentInfo.WRITER, 1)
  131. self.__check_sgmt_reset_param(SegmentInfo.READER, 0)
  132. self.__sgmt_info.switch_versions()
  133. self.__check_sgmt_reset_param(SegmentInfo.WRITER, 0)
  134. self.__check_sgmt_reset_param(SegmentInfo.READER, 1)
  135. def test_init_others(self):
  136. # For local type of segment, information isn't needed and won't be
  137. # created.
  138. self.assertIsNone(SegmentInfo.create('local', 0, RRClass.IN,
  139. 'sqlite3', {}))
  140. # Unknown type of segment will result in an exception.
  141. self.assertRaises(SegmentInfoError, SegmentInfo.create, 'unknown', 0,
  142. RRClass.IN, 'sqlite3', {})
  143. def test_missing_methods(self):
  144. # Bad subclass of SegmentInfo that doesn't implement mandatory methods.
  145. class TestSegmentInfo(SegmentInfo):
  146. pass
  147. self.assertRaises(SegmentInfoError,
  148. TestSegmentInfo().get_reset_param,
  149. SegmentInfo.WRITER)
  150. self.assertRaises(SegmentInfoError, TestSegmentInfo().switch_versions)
  151. class MockClientList:
  152. """A mock ConfigurableClientList class.
  153. Just providing minimal shortcut interfaces needed for DataSrcInfo class.
  154. """
  155. def __init__(self, status_list):
  156. self.__status_list = status_list
  157. def get_status(self):
  158. return self.__status_list
  159. class TestDataSrcInfo(unittest.TestCase):
  160. def setUp(self):
  161. self.__mapped_file_dir = os.environ['TESTDATA_PATH']
  162. self.__mgr_config = {'mapped_file_dir': self.__mapped_file_dir}
  163. self.__sqlite3_dbfile = os.environ['TESTDATA_PATH'] + '/' + 'zone.db'
  164. self.__clients_map = {
  165. # mixture of 'local' and 'mapped' and 'unused' (type =None)
  166. # segments
  167. RRClass.IN: MockClientList([('datasrc1', 'local', None),
  168. ('datasrc2', 'mapped', None),
  169. ('datasrc3', None, None)]),
  170. RRClass.CH: MockClientList([('datasrc2', 'mapped', None),
  171. ('datasrc1', 'local', None)]) }
  172. def tearDown(self):
  173. if os.path.exists(self.__sqlite3_dbfile):
  174. os.unlink(self.__sqlite3_dbfile)
  175. def __check_sgmt_reset_param(self, sgmt_info, writer_file):
  176. # Check if the initial state of (mapped) segment info object has
  177. # expected values.
  178. self.assertIsNone(sgmt_info.get_reset_param(SegmentInfo.READER))
  179. param = sgmt_info.get_reset_param(SegmentInfo.WRITER)
  180. self.assertEqual(writer_file, param['mapped-file'])
  181. def test_init(self):
  182. """Check basic scenarios of constructing DataSrcInfo."""
  183. # This checks that all data sources of all RR classes are covered,
  184. # "local" segments are ignored, info objects for "mapped" segments
  185. # are created and stored in segment_info_map.
  186. datasrc_info = DataSrcInfo(42, self.__clients_map, self.__mgr_config)
  187. self.assertEqual(42, datasrc_info.gen_id)
  188. self.assertEqual(self.__clients_map, datasrc_info.clients_map)
  189. self.assertEqual(2, len(datasrc_info.segment_info_map))
  190. sgmt_info = datasrc_info.segment_info_map[(RRClass.IN, 'datasrc2')]
  191. self.__check_sgmt_reset_param(sgmt_info, self.__mapped_file_dir +
  192. '/zone-IN-42-datasrc2-mapped.0')
  193. sgmt_info = datasrc_info.segment_info_map[(RRClass.CH, 'datasrc2')]
  194. self.__check_sgmt_reset_param(sgmt_info, self.__mapped_file_dir +
  195. '/zone-CH-42-datasrc2-mapped.0')
  196. # A case where clist.get_status() returns an empty list; shouldn't
  197. # cause disruption
  198. self.__clients_map = { RRClass.IN: MockClientList([])}
  199. datasrc_info = DataSrcInfo(42, self.__clients_map, self.__mgr_config)
  200. self.assertEqual(42, datasrc_info.gen_id)
  201. self.assertEqual(0, len(datasrc_info.segment_info_map))
  202. # A case where clients_map is empty; shouldn't cause disruption
  203. self.__clients_map = {}
  204. datasrc_info = DataSrcInfo(42, self.__clients_map, self.__mgr_config)
  205. self.assertEqual(42, datasrc_info.gen_id)
  206. self.assertEqual(0, len(datasrc_info.segment_info_map))
  207. # This test uses real "mmaped" segment and doesn't work without shared
  208. # memory support.
  209. @unittest.skipIf(os.environ['HAVE_SHARED_MEMORY'] != 'yes',
  210. 'shared memory support is not available')
  211. def test_production(self):
  212. """Check the behavior closer to a production environment.
  213. Instead of using a mock classes, just for confirming we didn't miss
  214. something.
  215. """
  216. cfg_data = MockConfigData(
  217. {"classes":
  218. {"IN": [{"type": "sqlite3", "cache-enable": True,
  219. "cache-type": "mapped", "cache-zones": [],
  220. "params": {"database_file": self.__sqlite3_dbfile}}]
  221. }
  222. })
  223. cmgr = DataSrcClientsMgr(use_cache=True)
  224. cmgr.reconfigure({}, cfg_data)
  225. genid, clients_map = cmgr.get_clients_map()
  226. datasrc_info = DataSrcInfo(genid, clients_map, self.__mgr_config)
  227. self.assertEqual(1, datasrc_info.gen_id)
  228. self.assertEqual(clients_map, datasrc_info.clients_map)
  229. self.assertEqual(1, len(datasrc_info.segment_info_map))
  230. sgmt_info = datasrc_info.segment_info_map[(RRClass.IN, 'sqlite3')]
  231. self.assertIsNone(sgmt_info.get_reset_param(SegmentInfo.READER))
  232. if __name__ == "__main__":
  233. isc.log.init("bind10-test")
  234. isc.log.resetUnitTestRootLogger()
  235. unittest.main()