b10-stats_test.py 67 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429
  1. # Copyright (C) 2010, 2011, 2012 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. In each of these tests we start several virtual components. They are
  17. not the real components, no external processes are started. They are
  18. just simple mock objects running each in its own thread and pretending
  19. to be bind10 modules. This helps testing the stats module in a close
  20. to real environment.
  21. """
  22. import unittest
  23. import os
  24. import io
  25. import time
  26. import imp
  27. import sys
  28. import stats
  29. import isc.log
  30. from test_utils import MyStats
  31. class TestUtilties(unittest.TestCase):
  32. items = [
  33. { 'item_name': 'test_int1', 'item_type': 'integer', 'item_default': 12345 },
  34. { 'item_name': 'test_real1', 'item_type': 'real', 'item_default': 12345.6789 },
  35. { 'item_name': 'test_bool1', 'item_type': 'boolean', 'item_default': True },
  36. { 'item_name': 'test_str1', 'item_type': 'string', 'item_default': 'ABCD' },
  37. { 'item_name': 'test_list1', 'item_type': 'list', 'item_default': [1,2,3],
  38. 'list_item_spec' : { 'item_name': 'number', 'item_type': 'integer' } },
  39. { 'item_name': 'test_map1', 'item_type': 'map', 'item_default': {'a':1,'b':2,'c':3},
  40. 'map_item_spec' : [ { 'item_name': 'a', 'item_type': 'integer'},
  41. { 'item_name': 'b', 'item_type': 'integer'},
  42. { 'item_name': 'c', 'item_type': 'integer'} ] },
  43. { 'item_name': 'test_int2', 'item_type': 'integer' },
  44. { 'item_name': 'test_real2', 'item_type': 'real' },
  45. { 'item_name': 'test_bool2', 'item_type': 'boolean' },
  46. { 'item_name': 'test_str2', 'item_type': 'string' },
  47. { 'item_name': 'test_list2', 'item_type': 'list',
  48. 'list_item_spec' : { 'item_name': 'number', 'item_type': 'integer' } },
  49. { 'item_name': 'test_map2', 'item_type': 'map',
  50. 'map_item_spec' : [ { 'item_name': 'A', 'item_type': 'integer'},
  51. { 'item_name': 'B', 'item_type': 'integer'},
  52. { 'item_name': 'C', 'item_type': 'integer'} ] },
  53. { 'item_name': 'test_none', 'item_type': 'none' },
  54. { 'item_name': 'test_list3', 'item_type': 'list', 'item_default': ["one","two","three"],
  55. 'list_item_spec' : { 'item_name': 'number', 'item_type': 'string' } },
  56. { 'item_name': 'test_map3', 'item_type': 'map', 'item_default': {'a':'one','b':'two','c':'three'},
  57. 'map_item_spec' : [ { 'item_name': 'a', 'item_type': 'string'},
  58. { 'item_name': 'b', 'item_type': 'string'},
  59. { 'item_name': 'c', 'item_type': 'string'} ] },
  60. {
  61. 'item_name': 'test_named_set',
  62. 'item_type': 'named_set',
  63. 'item_default': { },
  64. 'named_set_item_spec': {
  65. 'item_name': 'name',
  66. 'item_type': 'map',
  67. 'item_default': { },
  68. 'map_item_spec': [
  69. {
  70. 'item_name': 'number1',
  71. 'item_type': 'integer'
  72. },
  73. {
  74. 'item_name': 'number2',
  75. 'item_type': 'integer'
  76. }
  77. ]
  78. }
  79. }
  80. ]
  81. def setUp(self):
  82. self.const_timestamp = 1308730448.965706
  83. self.const_timetuple = (2011, 6, 22, 8, 14, 8, 2, 173, 0)
  84. self.const_datetime = '2011-06-22T08:14:08Z'
  85. self.__orig_time = stats.time
  86. self.__orig_gmtime = stats.gmtime
  87. stats.time = lambda : self.const_timestamp
  88. stats.gmtime = lambda : self.const_timetuple
  89. def tearDown(self):
  90. stats.time = self.__orig_time
  91. stats.gmtime = self.__orig_gmtime
  92. def test_get_spec_defaults(self):
  93. self.assertEqual(
  94. stats.get_spec_defaults(self.items), {
  95. 'test_int1' : 12345 ,
  96. 'test_real1' : 12345.6789 ,
  97. 'test_bool1' : True ,
  98. 'test_str1' : 'ABCD' ,
  99. 'test_list1' : [1,2,3] ,
  100. 'test_map1' : {'a':1,'b':2,'c':3},
  101. 'test_int2' : 0 ,
  102. 'test_real2' : 0.0,
  103. 'test_bool2' : False,
  104. 'test_str2' : "",
  105. 'test_list2' : [0],
  106. 'test_map2' : { 'A' : 0, 'B' : 0, 'C' : 0 },
  107. 'test_none' : None,
  108. 'test_list3' : [ "one", "two", "three" ],
  109. 'test_map3' : { 'a' : 'one', 'b' : 'two', 'c' : 'three' },
  110. 'test_named_set' : {} })
  111. self.assertEqual(stats.get_spec_defaults(None), {})
  112. self.assertRaises(KeyError, stats.get_spec_defaults, [{'item_name':'Foo'}])
  113. def test_get_timestamp(self):
  114. self.assertEqual(stats.get_timestamp(), self.const_timestamp)
  115. def test_get_datetime(self):
  116. self.assertEqual(stats.get_datetime(), self.const_datetime)
  117. self.assertNotEqual(stats.get_datetime(
  118. (2011, 6, 22, 8, 23, 40, 2, 173, 0)), self.const_datetime)
  119. def test__accum(self):
  120. self.assertEqual(stats._accum(None, None), None)
  121. self.assertEqual(stats._accum(None, "b"), "b")
  122. self.assertEqual(stats._accum("a", None), "a")
  123. self.assertEqual(stats._accum(1, 2), 3)
  124. self.assertEqual(stats._accum(0.5, 0.3), 0.8)
  125. self.assertEqual(stats._accum('aa','bb'), 'bb')
  126. self.assertEqual(stats._accum('1970-01-01T09:00:00Z','2012-08-09T09:33:31Z'),
  127. '2012-08-09T09:33:31Z')
  128. self.assertEqual(stats._accum(
  129. [1, 2, 3], [4, 5]), [5, 7, 3])
  130. self.assertEqual(stats._accum(
  131. [4, 5], [1, 2, 3]), [5, 7, 3])
  132. self.assertEqual(stats._accum(
  133. [1, 2, 3], [None, 5, 6]), [1, 7, 9])
  134. self.assertEqual(stats._accum(
  135. [None, 5, 6], [1, 2, 3]), [1, 7, 9])
  136. self.assertEqual(stats._accum(
  137. [1, 2, 3], [None, None, None, None]), [1,2,3,None])
  138. self.assertEqual(stats._accum(
  139. [[1,2],3],[[],5,6]), [[1,2],8,6])
  140. self.assertEqual(stats._accum(
  141. {'one': 1, 'two': 2, 'three': 3},
  142. {'one': 4, 'two': 5}),
  143. {'one': 5, 'two': 7, 'three': 3})
  144. self.assertEqual(stats._accum(
  145. {'one': 1, 'two': 2, 'three': 3},
  146. {'four': 4, 'five': 5}),
  147. {'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5})
  148. self.assertEqual(stats._accum(
  149. {'one': [1, 2], 'two': [3, None, 5], 'three': [None, 3, None]},
  150. {'one': [2], 'two': [4, 5], 'three': [None, None, None], 'four': 'FOUR'}),
  151. {'one':[3,2], 'two':[7,5,5], 'three':[None,3,None], 'four': 'FOUR'})
  152. self.assertEqual(stats._accum(
  153. [ {'one': 1, 'two': 2, 'three': 3}, {'four': 4, 'five': 5, 'six': 6} ],
  154. [ {}, {'four': 1, 'five': 2, 'six': 3} ]),
  155. [ {'one': 1, 'two': 2, 'three': 3}, {'four': 5, 'five': 7, 'six': 9} ])
  156. def test_merge_oldnre(self):
  157. self.assertEqual(stats.merge_oldnew(1, 2), 2)
  158. self.assertEqual(stats.merge_oldnew(0.5, 0.3), 0.3)
  159. self.assertEqual(stats.merge_oldnew('aa','bb'), 'bb')
  160. self.assertEqual(stats.merge_oldnew(
  161. [1, 2, 3], [4, 5]), [4, 5, 3])
  162. self.assertEqual(stats.merge_oldnew(
  163. [4, 5], [1, 2, 3]), [1, 2, 3])
  164. self.assertEqual(stats.merge_oldnew(
  165. [1, 2, 3], [None, 5, 6]), [None, 5, 6])
  166. self.assertEqual(stats.merge_oldnew(
  167. [None, 5, 6], [1, 2, 3]), [1, 2, 3])
  168. self.assertEqual(stats.merge_oldnew(
  169. [1, 2, 3], [None, None, None, None]), [None, None, None, None])
  170. self.assertEqual(stats.merge_oldnew(
  171. [[1,2],3],[[],5,6]), [[1,2],5,6])
  172. self.assertEqual(stats.merge_oldnew(
  173. {'one': 1, 'two': 2, 'three': 3},
  174. {'one': 4, 'two': 5}),
  175. {'one': 4, 'two': 5, 'three': 3})
  176. self.assertEqual(stats.merge_oldnew(
  177. {'one': 1, 'two': 2, 'three': 3},
  178. {'four': 4, 'five': 5}),
  179. {'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5})
  180. self.assertEqual(stats.merge_oldnew(
  181. {'one': [1, 2], 'two': [3, None, 5], 'three': [None, 3, None]},
  182. {'one': [2], 'two': [4, 5], 'three': [None, None, None], 'four': 'FOUR'}),
  183. {'one':[2,2], 'two':[4,5,5], 'three':[None,None,None], 'four': 'FOUR'})
  184. self.assertEqual(stats.merge_oldnew(
  185. [ {'one': 1, 'two': 2, 'three': 3}, {'four': 4, 'five': 5, 'six': 6} ],
  186. [ {}, {'four': 1, 'five': 2, 'six': 3} ]),
  187. [ {'one': 1, 'two': 2, 'three': 3}, {'four': 1, 'five': 2, 'six': 3} ])
  188. class TestCallback(unittest.TestCase):
  189. def setUp(self):
  190. self.dummy_func = lambda *x, **y : (x, y)
  191. self.dummy_args = (1,2,3)
  192. self.dummy_kwargs = {'a':1,'b':2,'c':3}
  193. self.cback1 = stats.Callback(
  194. command=self.dummy_func,
  195. args=self.dummy_args,
  196. kwargs=self.dummy_kwargs
  197. )
  198. self.cback2 = stats.Callback(
  199. args=self.dummy_args,
  200. kwargs=self.dummy_kwargs
  201. )
  202. self.cback3 = stats.Callback(
  203. command=self.dummy_func,
  204. kwargs=self.dummy_kwargs
  205. )
  206. self.cback4 = stats.Callback(
  207. command=self.dummy_func,
  208. args=self.dummy_args
  209. )
  210. def test_init(self):
  211. self.assertEqual((self.cback1.command, self.cback1.args, self.cback1.kwargs),
  212. (self.dummy_func, self.dummy_args, self.dummy_kwargs))
  213. self.assertEqual((self.cback2.command, self.cback2.args, self.cback2.kwargs),
  214. (None, self.dummy_args, self.dummy_kwargs))
  215. self.assertEqual((self.cback3.command, self.cback3.args, self.cback3.kwargs),
  216. (self.dummy_func, (), self.dummy_kwargs))
  217. self.assertEqual((self.cback4.command, self.cback4.args, self.cback4.kwargs),
  218. (self.dummy_func, self.dummy_args, {}))
  219. def test_call(self):
  220. self.assertEqual(self.cback1(), (self.dummy_args, self.dummy_kwargs))
  221. self.assertEqual(self.cback1(100, 200), ((100, 200), self.dummy_kwargs))
  222. self.assertEqual(self.cback1(a=100, b=200), (self.dummy_args, {'a':100, 'b':200}))
  223. self.assertEqual(self.cback2(), None)
  224. self.assertEqual(self.cback3(), ((), self.dummy_kwargs))
  225. self.assertEqual(self.cback3(100, 200), ((100, 200), self.dummy_kwargs))
  226. self.assertEqual(self.cback3(a=100, b=200), ((), {'a':100, 'b':200}))
  227. self.assertEqual(self.cback4(), (self.dummy_args, {}))
  228. self.assertEqual(self.cback4(100, 200), ((100, 200), {}))
  229. self.assertEqual(self.cback4(a=100, b=200), (self.dummy_args, {'a':100, 'b':200}))
  230. class TestStats(unittest.TestCase):
  231. def setUp(self):
  232. # set the signal handler for deadlock
  233. self.const_timestamp = 1308730448.965706
  234. self.const_datetime = '2011-06-22T08:14:08Z'
  235. self.const_default_datetime = '1970-01-01T00:00:00Z'
  236. # Record original module-defined functions in case we replace them
  237. self.__orig_timestamp = stats.get_timestamp
  238. self.__orig_get_datetime = stats.get_datetime
  239. def tearDown(self):
  240. # restore the stored original function in case we replaced them
  241. stats.get_timestamp = self.__orig_timestamp
  242. stats.get_datetime = self.__orig_get_datetime
  243. def test_init(self):
  244. self.stats = MyStats()
  245. self.assertEqual(self.stats.module_name, 'Stats')
  246. self.assertFalse(self.stats.running)
  247. self.assertTrue('command_show' in self.stats.callbacks)
  248. self.assertTrue('command_status' in self.stats.callbacks)
  249. self.assertTrue('command_shutdown' in self.stats.callbacks)
  250. self.assertTrue('command_show' in self.stats.callbacks)
  251. self.assertTrue('command_showschema' in self.stats.callbacks)
  252. self.assertEqual(self.stats.config['poll-interval'], 60)
  253. def test_init_undefcmd(self):
  254. spec_str = """\
  255. {
  256. "module_spec": {
  257. "module_name": "Stats",
  258. "module_description": "Stats daemon",
  259. "config_data": [],
  260. "commands": [
  261. {
  262. "command_name": "_undef_command_",
  263. "command_description": "a undefined command in stats",
  264. "command_args": []
  265. }
  266. ],
  267. "statistics": []
  268. }
  269. }
  270. """
  271. orig_spec_location = stats.SPECFILE_LOCATION
  272. stats.SPECFILE_LOCATION = io.StringIO(spec_str)
  273. self.assertRaises(stats.StatsError, MyStats)
  274. stats.SPECFILE_LOCATION = orig_spec_location
  275. def __send_command(self, stats, command_name, params=None):
  276. '''Emulate a command arriving to stats by directly calling callback'''
  277. return isc.config.ccsession.parse_answer(
  278. stats.command_handler(command_name, params))
  279. def test_start(self):
  280. # Define a separate exception class so we can be sure that's actually
  281. # the one raised in __check_start() below
  282. class CheckException(Exception):
  283. pass
  284. def __check_start(tested_stats):
  285. self.assertTrue(tested_stats.running)
  286. raise CheckException # terminate the loop
  287. # start without err
  288. self.stats = MyStats()
  289. self.assertFalse(self.stats.running)
  290. self.stats._check_command = lambda: __check_start(self.stats)
  291. # We are going to confirm start() will set running to True, avoiding
  292. # to fall into a loop with the exception trick.
  293. self.assertRaises(CheckException, self.stats.start)
  294. self.assertEqual(self.__send_command(self.stats, "status"),
  295. (0, "Stats is up. (PID " + str(os.getpid()) + ")"))
  296. def test_shutdown(self):
  297. def __check_shutdown(tested_stats):
  298. self.assertTrue(tested_stats.running)
  299. self.assertEqual(self.__send_command(tested_stats, "shutdown"),
  300. (0, None))
  301. self.assertFalse(tested_stats.running)
  302. # override get_interval() so it won't go poll statistics
  303. tested_stats.get_interval = lambda : 0
  304. self.stats = MyStats()
  305. self.stats._check_command = lambda: __check_shutdown(self.stats)
  306. self.stats.start()
  307. self.assertTrue(self.stats.mccs.stopped)
  308. def test_handlers(self):
  309. """Test command_handler"""
  310. __stats = MyStats()
  311. # 'show' command. We're going to check the expected methods are
  312. # called in the expected order, and check the resulting response.
  313. # Details of each method are tested separately.
  314. call_log = []
  315. def __steal_method(fn_name, *arg):
  316. call_log.append((fn_name, arg))
  317. if fn_name == 'update_stat':
  318. return False # "no error"
  319. if fn_name == 'showschema':
  320. return isc.config.create_answer(0, 'no error')
  321. # Fake some methods and attributes for inspection
  322. __stats.do_polling = lambda: __steal_method('polling')
  323. __stats.update_statistics_data = \
  324. lambda x, y, z: __steal_method('update_stat', x, y, z)
  325. __stats.update_modules = lambda: __steal_method('update_module')
  326. __stats.mccs.lname = 'test lname'
  327. __stats.statistics_data = {'Init': {'boot_time': self.const_datetime}}
  328. # skip initial polling
  329. stats.get_timestamp = lambda: 0
  330. __stats._lasttime_poll = 0
  331. stats.get_datetime = lambda: 42 # make the result predictable
  332. # now send the command
  333. self.assertEqual(
  334. self.__send_command(
  335. __stats, 'show',
  336. params={ 'owner' : 'Init', 'name' : 'boot_time' }),
  337. (0, {'Init': {'boot_time': self.const_datetime}}))
  338. # Check if expected methods are called
  339. self.assertEqual([('update_stat',
  340. ('Stats', 'test lname',
  341. {'timestamp': 0,
  342. 'report_time': 42})),
  343. ('update_module', ())], call_log)
  344. # Then update faked timestamp so the initial polling will happen, and
  345. # confirm that.
  346. call_log = []
  347. stats.get_timestamp = lambda: 10
  348. self.assertEqual(
  349. self.__send_command(
  350. __stats, 'show',
  351. params={ 'owner' : 'Init', 'name' : 'boot_time' }),
  352. (0, {'Init': {'boot_time': self.const_datetime}}))
  353. self.assertEqual([('polling', ()),
  354. ('update_stat',
  355. ('Stats', 'test lname',
  356. {'timestamp': 10,
  357. 'report_time': 42})),
  358. ('update_module', ())], call_log)
  359. # 'status' command. We can confirm the behavior without any fake
  360. self.assertEqual(
  361. self.__send_command(__stats, 'status'),
  362. (0, "Stats is up. (PID " + str(os.getpid()) + ")"))
  363. # 'showschema' command. update_modules() will be called, which
  364. # (implicitly) confirms the correct method is called; further details
  365. # are tested separately.
  366. call_log = []
  367. (rcode, value) = self.__send_command(__stats, 'showschema')
  368. self.assertEqual([('update_module', ())], call_log)
  369. # Unknown command. Error should be returned
  370. self.assertEqual(
  371. self.__send_command(__stats, '__UNKNOWN__'),
  372. (1, "Unknown command: '__UNKNOWN__'"))
  373. def test_update_modules(self):
  374. """Confirm the behavior of Stats.update_modules().
  375. It checks whether the expected command is sent to ConfigManager,
  376. and whether the answer from ConfigManager is handled as expected.
  377. """
  378. def __check_rpc_call(command, group):
  379. self.assertEqual('ConfigManager', group)
  380. self.assertEqual(command,
  381. isc.config.ccsession.COMMAND_GET_STATISTICS_SPEC)
  382. answer_value = {'Init': [{
  383. "item_name": "boot_time",
  384. "item_type": "string",
  385. "item_optional": False,
  386. # Use a different default so we can check it below
  387. "item_default": "2013-01-01T00:00:01Z",
  388. "item_title": "Boot time",
  389. "item_description": "dummy desc",
  390. "item_format": "date-time"
  391. }]}
  392. return answer_value
  393. self.stats = MyStats()
  394. self.stats.cc_session.rpc_call = __check_rpc_call
  395. self.stats.update_modules()
  396. # Stats is always incorporated. For others, only the ones returned
  397. # by group_recvmsg() above is available.
  398. self.assertTrue('Stats' in self.stats.modules)
  399. self.assertTrue('Init' in self.stats.modules)
  400. self.assertFalse('Dummy' in self.stats.modules)
  401. my_statistics_data = stats.get_spec_defaults(
  402. self.stats.modules['Stats'].get_statistics_spec())
  403. self.assertTrue('report_time' in my_statistics_data)
  404. self.assertTrue('boot_time' in my_statistics_data)
  405. self.assertTrue('last_update_time' in my_statistics_data)
  406. self.assertTrue('timestamp' in my_statistics_data)
  407. self.assertTrue('lname' in my_statistics_data)
  408. self.assertEqual(my_statistics_data['report_time'],
  409. self.const_default_datetime)
  410. self.assertEqual(my_statistics_data['boot_time'],
  411. self.const_default_datetime)
  412. self.assertEqual(my_statistics_data['last_update_time'],
  413. self.const_default_datetime)
  414. self.assertEqual(my_statistics_data['timestamp'], 0.0)
  415. self.assertEqual(my_statistics_data['lname'], "")
  416. my_statistics_data = stats.get_spec_defaults(
  417. self.stats.modules['Init'].get_statistics_spec())
  418. self.assertTrue('boot_time' in my_statistics_data)
  419. self.assertEqual(my_statistics_data['boot_time'],
  420. "2013-01-01T00:00:01Z")
  421. # Error case
  422. def __raise_on_rpc_call(x, y):
  423. raise isc.config.RPCError(99, 'error')
  424. orig_parse_answer = stats.isc.config.ccsession.parse_answer
  425. self.stats.cc_session.rpc_call = __raise_on_rpc_call
  426. self.assertRaises(stats.StatsError, self.stats.update_modules)
  427. def test_get_statistics_data(self):
  428. """Confirm the behavior of Stats.get_statistics_data().
  429. It should first call update_modules(), and then retrieve the requested
  430. data from statistics_data. We confirm this by fake update_modules()
  431. where we set the expected data in statistics_data.
  432. """
  433. self.stats = MyStats()
  434. def __faked_update_modules():
  435. self.stats.statistics_data = { \
  436. 'Stats': {
  437. 'report_time': self.const_default_datetime,
  438. 'boot_time': None,
  439. 'last_update_time': None,
  440. 'timestamp': 0.0,
  441. 'lname': 'dummy name'
  442. },
  443. 'Init': { 'boot_time': None }
  444. }
  445. self.stats.update_modules = __faked_update_modules
  446. my_statistics_data = self.stats.get_statistics_data()
  447. self.assertTrue('Stats' in my_statistics_data)
  448. self.assertTrue('Init' in my_statistics_data)
  449. self.assertTrue('boot_time' in my_statistics_data['Init'])
  450. my_statistics_data = self.stats.get_statistics_data(owner='Stats')
  451. self.assertTrue('Stats' in my_statistics_data)
  452. self.assertTrue('report_time' in my_statistics_data['Stats'])
  453. self.assertTrue('boot_time' in my_statistics_data['Stats'])
  454. self.assertTrue('last_update_time' in my_statistics_data['Stats'])
  455. self.assertTrue('timestamp' in my_statistics_data['Stats'])
  456. self.assertTrue('lname' in my_statistics_data['Stats'])
  457. self.assertRaises(stats.StatsError, self.stats.get_statistics_data,
  458. owner='Foo')
  459. my_statistics_data = self.stats.get_statistics_data(
  460. owner='Stats', name='report_time')
  461. self.assertEqual(my_statistics_data['Stats']['report_time'],
  462. self.const_default_datetime)
  463. my_statistics_data = self.stats.get_statistics_data(
  464. owner='Stats', name='boot_time')
  465. self.assertTrue('boot_time' in my_statistics_data['Stats'])
  466. my_statistics_data = self.stats.get_statistics_data(
  467. owner='Stats', name='last_update_time')
  468. self.assertTrue('last_update_time' in my_statistics_data['Stats'])
  469. my_statistics_data = self.stats.get_statistics_data(
  470. owner='Stats', name='timestamp')
  471. self.assertEqual(my_statistics_data['Stats']['timestamp'], 0.0)
  472. my_statistics_data = self.stats.get_statistics_data(
  473. owner='Stats', name='lname')
  474. self.assertTrue(len(my_statistics_data['Stats']['lname']) >0)
  475. self.assertRaises(stats.StatsError, self.stats.get_statistics_data,
  476. owner='Stats', name='Bar')
  477. self.assertRaises(stats.StatsError, self.stats.get_statistics_data,
  478. owner='Foo', name='Bar')
  479. self.assertRaises(stats.StatsError, self.stats.get_statistics_data,
  480. name='Bar')
  481. def test_update_statistics_data(self):
  482. """test for list-type statistics"""
  483. self.stats = MyStats()
  484. _test_exp1 = {
  485. 'zonename': 'test1.example',
  486. 'queries.tcp': 5,
  487. 'queries.udp': 4
  488. }
  489. _test_exp2 = {
  490. 'zonename': 'test2.example',
  491. 'queries.tcp': 3,
  492. 'queries.udp': 2
  493. }
  494. _test_exp3 = {}
  495. _test_exp4 = {
  496. 'queries.udp': 4
  497. }
  498. _test_exp5_1 = {
  499. 'queries.perzone': [
  500. { },
  501. {
  502. 'queries.udp': 9876
  503. }
  504. ]
  505. }
  506. _test_exp5_2 = {
  507. 'queries.perzone[1]/queries.udp':
  508. isc.cc.data.find(_test_exp5_1,
  509. 'queries.perzone[1]/queries.udp')
  510. }
  511. # Success cases
  512. self.assertEqual(self.stats.statistics_data['Stats']['lname'],
  513. self.stats.cc_session.lname)
  514. self.stats.update_statistics_data(
  515. 'Stats', self.stats.cc_session.lname,
  516. {'lname': 'foo@bar'})
  517. self.assertEqual(self.stats.statistics_data['Stats']['lname'],
  518. 'foo@bar')
  519. self.assertIsNone(self.stats.update_statistics_data(
  520. 'Auth', 'foo1', {'queries.perzone': [_test_exp1]}))
  521. self.assertEqual(self.stats.statistics_data_bymid['Auth']\
  522. ['foo1']['queries.perzone'],\
  523. [_test_exp1])
  524. self.assertIsNone(self.stats.update_statistics_data(
  525. 'Auth', 'foo1', {'queries.perzone': [_test_exp2]}))
  526. self.assertEqual(self.stats.statistics_data_bymid['Auth']\
  527. ['foo1']['queries.perzone'],\
  528. [_test_exp2])
  529. self.assertIsNone(self.stats.update_statistics_data(
  530. 'Auth', 'foo1', {'queries.perzone': [_test_exp1,_test_exp2]}))
  531. self.assertEqual(self.stats.statistics_data_bymid['Auth']\
  532. ['foo1']['queries.perzone'],
  533. [_test_exp1,_test_exp2])
  534. # differential update
  535. self.assertIsNone(self.stats.update_statistics_data(
  536. 'Auth', 'foo1', {'queries.perzone': [_test_exp3,_test_exp4]}))
  537. _new_data = stats.merge_oldnew(_test_exp2,_test_exp4)
  538. self.assertEqual(self.stats.statistics_data_bymid['Auth']\
  539. ['foo1']['queries.perzone'], \
  540. [_test_exp1,_new_data])
  541. self.assertIsNone(self.stats.update_statistics_data(
  542. 'Auth', 'foo1', _test_exp5_2))
  543. _new_data = stats.merge_oldnew(_new_data,
  544. _test_exp5_1['queries.perzone'][1])
  545. self.assertEqual(self.stats.statistics_data_bymid['Auth']\
  546. ['foo1']['queries.perzone'], \
  547. [_test_exp1,_new_data])
  548. # Error cases
  549. self.assertEqual(self.stats.update_statistics_data('Stats', None,
  550. {'lname': 0.0}),
  551. ['0.0 should be a string'])
  552. self.assertEqual(self.stats.update_statistics_data('Dummy', None,
  553. {'foo': 'bar'}),
  554. ['unknown module name: Dummy'])
  555. self.assertEqual(self.stats.update_statistics_data(
  556. 'Auth', 'foo1', {'queries.perzone': [None]}), ['None should be a map'])
  557. def test_update_statistics_data_pt2(self):
  558. """test for named_set-type statistics"""
  559. self.stats = MyStats()
  560. _test_exp1 = \
  561. { 'test10.example': { 'queries.tcp': 5, 'queries.udp': 4 } }
  562. _test_exp2 = \
  563. { 'test20.example': { 'queries.tcp': 3, 'queries.udp': 2 } }
  564. _test_exp3 = {}
  565. _test_exp4 = { 'test20.example': { 'queries.udp': 4 } }
  566. _test_exp5_1 = { 'test10.example': { 'queries.udp': 5432 } }
  567. _test_exp5_2 ={
  568. 'nds_queries.perzone/test10.example/queries.udp':
  569. isc.cc.data.find(_test_exp5_1, 'test10.example/queries.udp')
  570. }
  571. _test_exp6 = { 'foo/bar': 'brabra' }
  572. _test_exp7 = { 'foo[100]': 'bar' }
  573. # Success cases
  574. self.assertIsNone(self.stats.update_statistics_data(
  575. 'Auth', 'foo1', {'nds_queries.perzone': _test_exp1}))
  576. self.assertEqual(self.stats.statistics_data_bymid['Auth']\
  577. ['foo1']['nds_queries.perzone'],\
  578. _test_exp1)
  579. self.assertIsNone(self.stats.update_statistics_data(
  580. 'Auth', 'foo1', {'nds_queries.perzone': _test_exp2}))
  581. self.assertEqual(self.stats.statistics_data_bymid['Auth']\
  582. ['foo1']['nds_queries.perzone'],\
  583. dict(_test_exp1,**_test_exp2))
  584. self.assertIsNone(self.stats.update_statistics_data(
  585. 'Auth', 'foo1', {'nds_queries.perzone':
  586. dict(_test_exp1, **_test_exp2)}))
  587. self.assertEqual(self.stats.statistics_data_bymid['Auth']\
  588. ['foo1']['nds_queries.perzone'],
  589. dict(_test_exp1, **_test_exp2))
  590. # differential update
  591. self.assertIsNone(self.stats.update_statistics_data(
  592. 'Auth', 'foo1', {'nds_queries.perzone':
  593. dict(_test_exp3, **_test_exp4)}))
  594. _new_val = dict(_test_exp1,
  595. **stats.merge_oldnew(_test_exp2,_test_exp4))
  596. self.assertEqual(self.stats.statistics_data_bymid['Auth']\
  597. ['foo1']['nds_queries.perzone'],\
  598. _new_val)
  599. self.assertIsNone(self.stats.update_statistics_data(
  600. 'Auth', 'foo1', _test_exp5_2))
  601. _new_val = stats.merge_oldnew(_new_val, _test_exp5_1)
  602. self.assertEqual(self.stats.statistics_data_bymid['Auth']\
  603. ['foo1']['nds_queries.perzone'],\
  604. _new_val)
  605. self.assertIsNone(self.stats.update_statistics_data(
  606. 'Auth', 'foo2', _test_exp5_2))
  607. _new_val = stats.merge_oldnew(_new_val, _test_exp5_1)
  608. self.assertEqual(self.stats.statistics_data_bymid['Auth']\
  609. ['foo2']['nds_queries.perzone'],\
  610. _test_exp5_1)
  611. # Error cases
  612. self.assertEqual(self.stats.update_statistics_data(
  613. 'Auth', 'foo1', {'nds_queries.perzone': None}),
  614. ['None should be a map'])
  615. self.assertEqual(self.stats.statistics_data_bymid['Auth']\
  616. ['foo1']['nds_queries.perzone'],\
  617. _new_val)
  618. self.assertEqual(self.stats.update_statistics_data(
  619. 'Auth', 'foo1', _test_exp6), ['unknown item foo'])
  620. self.assertEqual(self.stats.statistics_data_bymid['Auth']\
  621. ['foo1']['nds_queries.perzone'],\
  622. _new_val)
  623. self.assertEqual(self.stats.update_statistics_data(
  624. 'Init', 'bar1', _test_exp7), ["KeyError: 'foo'"])
  625. self.assertEqual(self.stats.update_statistics_data(
  626. 'Foo', 'foo1', _test_exp6), ['unknown module name: Foo'])
  627. def test_update_statistics_data_withmid(self):
  628. self.stats = MyStats()
  629. # This test relies on existing statistics data at the Stats object.
  630. # This version of test prepares the data using the do_polling() method;
  631. # that's a bad practice because a unittest for a method
  632. # (update_statistics_data) would heavily depend on details of another
  633. # method (do_polling). However, there's currently no direct test
  634. # for do_polling (which is also bad), so we still keep that approach,
  635. # partly for testing do_polling indirectly. #2781 should provide
  636. # direct test for do_polling, with which this test scenario should
  637. # also be changed to be more stand-alone.
  638. # We use the knowledge of what kind of messages are sent via
  639. # do_polling, and return the following faked answer directly.
  640. create_answer = isc.config.ccsession.create_answer # shortcut
  641. self.stats._answers = [
  642. # Answer for "show_processes"
  643. (create_answer(0, [[1034, 'b10-auth-1', 'Auth'],
  644. [1035, 'b10-auth-2', 'Auth']]), None),
  645. # Answers for "getstats". 2 for Auth instances and 1 for Init.
  646. # we return some bogus values for Init, but the rest of the test
  647. # doesn't need it, so it's okay.
  648. (create_answer(0, self.stats._auth_sdata), {'from': 'auth1'}),
  649. (create_answer(0, self.stats._auth_sdata), {'from': 'auth2'}),
  650. (create_answer(0, self.stats._auth_sdata), {'from': 'auth3'})
  651. ]
  652. # do_polling calls update_modules internally; in our scenario there's
  653. # no change in modules, so we make it no-op.
  654. self.stats.update_modules = lambda: None
  655. # Now call do_polling.
  656. self.stats.do_polling()
  657. # samples of query number
  658. bar1_tcp = 1001
  659. bar2_tcp = 2001
  660. bar3_tcp = 1002
  661. bar3_udp = 1003
  662. # two auth instances invoked, so we double the pre-set stat values
  663. sum_qtcp = self.stats._queries_tcp * 2
  664. sum_qudp = self.stats._queries_udp * 2
  665. self.stats.update_statistics_data('Auth', "bar1@foo",
  666. {'queries.tcp': bar1_tcp})
  667. self.assertTrue('Auth' in self.stats.statistics_data)
  668. self.assertTrue('queries.tcp' in self.stats.statistics_data['Auth'])
  669. self.assertEqual(self.stats.statistics_data['Auth']['queries.tcp'],
  670. bar1_tcp + sum_qtcp)
  671. self.assertTrue('Auth' in self.stats.statistics_data_bymid)
  672. self.assertTrue('bar1@foo' in self.stats.statistics_data_bymid['Auth'])
  673. self.assertTrue('queries.tcp' in self.stats.statistics_data_bymid
  674. ['Auth']['bar1@foo'])
  675. self.assertEqual(self.stats.statistics_data_bymid['Auth']['bar1@foo'],
  676. {'queries.tcp': bar1_tcp})
  677. # check consolidation of statistics data even if there is
  678. # non-existent mid of Auth
  679. self.stats.update_statistics_data('Auth', "bar2@foo",
  680. {'queries.tcp': bar2_tcp})
  681. self.assertTrue('Auth' in self.stats.statistics_data)
  682. self.assertTrue('queries.tcp' in self.stats.statistics_data['Auth'])
  683. self.assertEqual(self.stats.statistics_data['Auth']['queries.tcp'],
  684. bar1_tcp + bar2_tcp + sum_qtcp)
  685. self.assertTrue('Auth' in self.stats.statistics_data_bymid)
  686. self.assertTrue('bar1@foo' in self.stats.statistics_data_bymid['Auth'])
  687. self.assertTrue('queries.tcp' in self.stats.statistics_data_bymid['Auth']['bar1@foo'])
  688. self.assertEqual(self.stats.statistics_data_bymid['Auth']['bar1@foo'],
  689. {'queries.tcp': bar1_tcp})
  690. self.assertEqual(self.stats.statistics_data_bymid['Auth']['bar2@foo'],
  691. {'queries.tcp': bar2_tcp})
  692. # kill running Auth but the statistics data doesn't change
  693. self.stats.update_statistics_data()
  694. self.assertTrue('Auth' in self.stats.statistics_data)
  695. self.assertTrue('queries.tcp' in self.stats.statistics_data['Auth'])
  696. self.assertTrue('queries.udp' in self.stats.statistics_data['Auth'])
  697. self.assertEqual(self.stats.statistics_data['Auth']['queries.tcp'],
  698. bar1_tcp + bar2_tcp + sum_qtcp)
  699. self.assertEqual(self.stats.statistics_data['Auth']['queries.udp'],
  700. sum_qudp)
  701. self.assertTrue('Auth' in self.stats.statistics_data_bymid)
  702. # restore statistics data of killed auth
  703. self.stats.update_statistics_data('Auth',
  704. "bar1@foo",
  705. {'queries.tcp': bar1_tcp})
  706. # set another mid of Auth
  707. self.stats.update_statistics_data('Auth',
  708. "bar3@foo",
  709. {'queries.tcp':bar3_tcp,
  710. 'queries.udp':bar3_udp})
  711. self.assertTrue('Auth' in self.stats.statistics_data)
  712. self.assertTrue('queries.tcp' in self.stats.statistics_data['Auth'])
  713. self.assertTrue('queries.udp' in self.stats.statistics_data['Auth'])
  714. self.assertEqual(self.stats.statistics_data['Auth']['queries.tcp'],
  715. bar1_tcp + bar2_tcp + bar3_tcp + sum_qtcp)
  716. self.assertEqual(self.stats.statistics_data['Auth']['queries.udp'],
  717. bar3_udp + sum_qudp)
  718. self.assertTrue('Auth' in self.stats.statistics_data_bymid)
  719. self.assertTrue('bar1@foo' in self.stats.statistics_data_bymid['Auth'])
  720. self.assertTrue('bar3@foo' in self.stats.statistics_data_bymid['Auth'])
  721. self.assertTrue('queries.tcp' in self.stats.statistics_data_bymid['Auth']['bar1@foo'])
  722. self.assertTrue('queries.udp' in self.stats.statistics_data_bymid['Auth']['bar3@foo'])
  723. self.assertTrue('queries.udp' in self.stats.statistics_data_bymid['Auth']['bar3@foo'])
  724. self.assertEqual(self.stats.statistics_data_bymid['Auth']['bar1@foo']['queries.tcp'], bar1_tcp)
  725. self.assertEqual(self.stats.statistics_data_bymid['Auth']['bar3@foo']['queries.tcp'], bar3_tcp)
  726. self.assertEqual(self.stats.statistics_data_bymid['Auth']['bar3@foo']['queries.udp'], bar3_udp)
  727. def test_config(self):
  728. orig_get_timestamp = stats.get_timestamp
  729. stats.get_timestamp = lambda : self.const_timestamp
  730. stat = MyStats()
  731. # test updating poll-interval
  732. self.assertEqual(stat.config['poll-interval'], 60)
  733. self.assertEqual(stat.get_interval(), 60)
  734. self.assertEqual(stat.next_polltime, self.const_timestamp + 60)
  735. self.assertEqual(stat.config_handler({'poll-interval': 120}),
  736. isc.config.create_answer(0))
  737. self.assertEqual(stat.config['poll-interval'], 120)
  738. self.assertEqual(stat.get_interval(), 120)
  739. self.assertEqual(stat.next_polltime, self.const_timestamp + 120)
  740. stats.get_timestamp = orig_get_timestamp
  741. self.assertEqual(stat.config_handler({'poll-interval': "foo"}),
  742. isc.config.create_answer(1, 'foo should be an integer'))
  743. self.assertEqual(stat.config_handler({'poll-interval': -1}),
  744. isc.config.create_answer(1, 'Negative integer ignored'))
  745. # unknown item
  746. self.assertEqual(
  747. stat.config_handler({'_UNKNOWN_KEY_': None}),
  748. isc.config.ccsession.create_answer(
  749. 1, "unknown item _UNKNOWN_KEY_"))
  750. # test no change if zero interval time
  751. self.assertEqual(stat.config_handler({'poll-interval': 0}),
  752. isc.config.create_answer(0))
  753. self.assertEqual(stat.config['poll-interval'], 0)
  754. # see the comment for test_update_statistics_data_withmid. We abuse
  755. # do_polling here, too. With #2781 we should make it more direct.
  756. create_answer = isc.config.ccsession.create_answer # shortcut
  757. stat._answers = [\
  758. # Answer for "show_processes"
  759. (create_answer(0, []), None),
  760. # Answers for "getstats" for Init (the other one for Auth, but
  761. # that doesn't matter for this test)
  762. (create_answer(0, stat._init_sdata), {'from': 'init'}),
  763. (create_answer(0, stat._init_sdata), {'from': 'init'})
  764. ]
  765. stat.update_modules = lambda: None
  766. self.assertEqual(
  767. self.__send_command(
  768. stat, 'show',
  769. params={ 'owner' : 'Init', 'name' : 'boot_time' }),
  770. (0, {'Init': {'boot_time': self.const_datetime}}))
  771. def test_commands(self):
  772. self.stats = MyStats()
  773. # status
  774. self.assertEqual(self.stats.command_status(),
  775. isc.config.create_answer(
  776. 0, "Stats is up. (PID " + str(os.getpid()) + ")"))
  777. # shutdown
  778. self.stats.running = True
  779. self.assertEqual(self.stats.command_shutdown(),
  780. isc.config.create_answer(0))
  781. self.assertFalse(self.stats.running)
  782. def test_command_show_error(self):
  783. self.stats = MyStats()
  784. self.assertEqual(self.stats.command_show(owner='Foo', name=None),
  785. isc.config.create_answer(
  786. 1,
  787. "specified arguments are incorrect: owner: Foo, name: None"))
  788. self.assertEqual(self.stats.command_show(owner='Foo', name='_bar_'),
  789. isc.config.create_answer(
  790. 1,
  791. "specified arguments are incorrect: owner: Foo, name: _bar_"))
  792. self.assertEqual(self.stats.command_show(owner='Foo', name='bar'),
  793. isc.config.create_answer(
  794. 1,
  795. "specified arguments are incorrect: owner: Foo, name: bar"))
  796. def test_command_show_auth(self):
  797. self.stats = MyStats()
  798. self.stats.update_modules = lambda: None
  799. # Test data borrowed from test_update_statistics_data_withmid
  800. create_answer = isc.config.ccsession.create_answer # shortcut
  801. self.stats._answers = [
  802. (create_answer(0, [[1034, 'b10-auth-1', 'Auth'],
  803. [1035, 'b10-auth-2', 'Auth']]), None),
  804. (create_answer(0, self.stats._auth_sdata), {'from': 'auth1'}),
  805. (create_answer(0, self.stats._auth_sdata), {'from': 'auth2'}),
  806. (create_answer(0, self.stats._auth_sdata), {'from': 'auth3'})
  807. ]
  808. num_instances = 2
  809. sum_qtcp = 0
  810. sum_qudp = 0
  811. sum_qtcp_perzone1 = 0
  812. sum_qudp_perzone1 = 0
  813. sum_qtcp_perzone2 = 4 * num_instances
  814. sum_qudp_perzone2 = 3 * num_instances
  815. sum_qtcp_nds_perzone10 = 0
  816. sum_qudp_nds_perzone10 = 0
  817. sum_qtcp_nds_perzone20 = 4 * num_instances
  818. sum_qudp_nds_perzone20 = 3 * num_instances
  819. self.maxDiff = None
  820. for a in (0, num_instances):
  821. sum_qtcp += self.stats._queries_tcp
  822. sum_qudp += self.stats._queries_udp
  823. sum_qtcp_perzone1 += self.stats._queries_per_zone[0]['queries.tcp']
  824. sum_qudp_perzone1 += self.stats._queries_per_zone[0]['queries.udp']
  825. sum_qtcp_nds_perzone10 += \
  826. self.stats._nds_queries_per_zone['test10.example']['queries.tcp']
  827. sum_qudp_nds_perzone10 += \
  828. self.stats._nds_queries_per_zone['test10.example']['queries.udp']
  829. self.assertEqual(self.stats.command_show(owner='Auth'),
  830. isc.config.create_answer(
  831. 0, {'Auth':{ 'queries.udp': sum_qudp,
  832. 'queries.tcp': sum_qtcp,
  833. 'queries.perzone': [{ 'zonename': 'test1.example',
  834. 'queries.udp': sum_qudp_perzone1,
  835. 'queries.tcp': sum_qtcp_perzone1 },
  836. { 'zonename': 'test2.example',
  837. 'queries.udp': sum_qudp_perzone2,
  838. 'queries.tcp': sum_qtcp_perzone2 }
  839. ],
  840. 'nds_queries.perzone': { 'test10.example' : {
  841. 'queries.udp': sum_qudp_nds_perzone10,
  842. 'queries.tcp': sum_qtcp_nds_perzone10 },
  843. 'test20.example' : {
  844. 'queries.udp': sum_qudp_nds_perzone20,
  845. 'queries.tcp': sum_qtcp_nds_perzone20 }
  846. }}}))
  847. self.assertEqual(self.stats.command_show(owner='Auth', name='queries.udp'),
  848. isc.config.create_answer(
  849. 0, {'Auth': {'queries.udp': sum_qudp}}))
  850. self.assertEqual(self.stats.command_show(owner='Auth', name='queries.perzone'),
  851. isc.config.create_answer(
  852. 0, {'Auth': {'queries.perzone': [
  853. { 'zonename': 'test1.example',
  854. 'queries.udp': sum_qudp_perzone1,
  855. 'queries.tcp': sum_qtcp_perzone1 },
  856. { 'zonename': 'test2.example',
  857. 'queries.udp': sum_qudp_perzone2,
  858. 'queries.tcp': sum_qtcp_perzone2 }]}}))
  859. self.assertEqual(self.stats.command_show(owner='Auth', name='nds_queries.perzone'),
  860. isc.config.create_answer(
  861. 0, {'Auth': {'nds_queries.perzone': {
  862. 'test10.example': {
  863. 'queries.udp': sum_qudp_nds_perzone10,
  864. 'queries.tcp': sum_qtcp_nds_perzone10 },
  865. 'test20.example': {
  866. 'queries.udp': sum_qudp_nds_perzone20,
  867. 'queries.tcp': sum_qtcp_nds_perzone20 }}}}))
  868. def test_command_show_stats(self):
  869. self.stats = MyStats()
  870. orig_get_datetime = stats.get_datetime
  871. orig_get_timestamp = stats.get_timestamp
  872. stats.get_datetime = lambda x=None: self.const_datetime
  873. stats.get_timestamp = lambda : self.const_timestamp
  874. self.assertEqual(self.stats.command_show(owner='Stats',
  875. name='report_time'),
  876. isc.config.create_answer(
  877. 0, {'Stats': {'report_time':self.const_datetime}}))
  878. self.assertEqual(self.stats.command_show(owner='Stats',
  879. name='timestamp'),
  880. isc.config.create_answer(
  881. 0, {'Stats': {'timestamp':self.const_timestamp}}))
  882. stats.get_datetime = orig_get_datetime
  883. stats.get_timestamp = orig_get_timestamp
  884. self.stats.do_polling = lambda : None
  885. self.stats.modules[self.stats.module_name] = \
  886. isc.config.module_spec.ModuleSpec(
  887. { "module_name": self.stats.module_name, "statistics": [] } )
  888. self.assertRaises(
  889. stats.StatsError, self.stats.command_show,
  890. owner=self.stats.module_name, name='bar')
  891. def test_command_showchema(self):
  892. self.stats = MyStats()
  893. (rcode, value) = isc.config.ccsession.parse_answer(
  894. self.stats.command_showschema())
  895. self.assertEqual(rcode, 0)
  896. self.assertEqual(len(value), 3)
  897. self.assertTrue('Stats' in value)
  898. self.assertTrue('Init' in value)
  899. self.assertTrue('Auth' in value)
  900. self.assertFalse('__Dummy__' in value)
  901. schema = value['Stats']
  902. self.assertEqual(len(schema), 5)
  903. for item in schema:
  904. self.assertTrue(len(item) == 6 or len(item) == 7)
  905. self.assertTrue('item_name' in item)
  906. self.assertTrue('item_type' in item)
  907. self.assertTrue('item_optional' in item)
  908. self.assertTrue('item_default' in item)
  909. self.assertTrue('item_title' in item)
  910. self.assertTrue('item_description' in item)
  911. if len(item) == 7:
  912. self.assertTrue('item_format' in item)
  913. schema = value['Init']
  914. self.assertEqual(len(schema), 1)
  915. for item in schema:
  916. self.assertTrue(len(item) == 7)
  917. self.assertTrue('item_name' in item)
  918. self.assertTrue('item_type' in item)
  919. self.assertTrue('item_optional' in item)
  920. self.assertTrue('item_default' in item)
  921. self.assertTrue('item_title' in item)
  922. self.assertTrue('item_description' in item)
  923. self.assertTrue('item_format' in item)
  924. schema = value['Auth']
  925. self.assertEqual(len(schema), 4)
  926. for item in schema:
  927. if item['item_type'] == 'list' or item['item_type'] == 'named_set':
  928. self.assertEqual(len(item), 7)
  929. else:
  930. self.assertEqual(len(item), 6)
  931. self.assertTrue('item_name' in item)
  932. self.assertTrue('item_type' in item)
  933. self.assertTrue('item_optional' in item)
  934. self.assertTrue('item_default' in item)
  935. self.assertTrue('item_title' in item)
  936. self.assertTrue('item_description' in item)
  937. (rcode, value) = isc.config.ccsession.parse_answer(
  938. self.stats.command_showschema(owner='Stats'))
  939. self.assertEqual(rcode, 0)
  940. self.assertTrue('Stats' in value)
  941. self.assertFalse('Init' in value)
  942. self.assertFalse('Auth' in value)
  943. for item in value['Stats']:
  944. self.assertTrue(len(item) == 6 or len(item) == 7)
  945. self.assertTrue('item_name' in item)
  946. self.assertTrue('item_type' in item)
  947. self.assertTrue('item_optional' in item)
  948. self.assertTrue('item_default' in item)
  949. self.assertTrue('item_title' in item)
  950. self.assertTrue('item_description' in item)
  951. if len(item) == 7:
  952. self.assertTrue('item_format' in item)
  953. (rcode, value) = isc.config.ccsession.parse_answer(
  954. self.stats.command_showschema(owner='Stats', name='report_time'))
  955. self.assertEqual(rcode, 0)
  956. self.assertTrue('Stats' in value)
  957. self.assertFalse('Init' in value)
  958. self.assertFalse('Auth' in value)
  959. self.assertEqual(len(value['Stats'][0]), 7)
  960. self.assertTrue('item_name' in value['Stats'][0])
  961. self.assertTrue('item_type' in value['Stats'][0])
  962. self.assertTrue('item_optional' in value['Stats'][0])
  963. self.assertTrue('item_default' in value['Stats'][0])
  964. self.assertTrue('item_title' in value['Stats'][0])
  965. self.assertTrue('item_description' in value['Stats'][0])
  966. self.assertTrue('item_format' in value['Stats'][0])
  967. self.assertEqual(value['Stats'][0]['item_name'], 'report_time')
  968. self.assertEqual(value['Stats'][0]['item_format'], 'date-time')
  969. self.assertEqual(self.stats.command_showschema(owner='Foo'),
  970. isc.config.create_answer(
  971. 1, "specified arguments are incorrect: owner: Foo, name: None"))
  972. self.assertEqual(self.stats.command_showschema(owner='Foo', name='bar'),
  973. isc.config.create_answer(
  974. 1, "specified arguments are incorrect: owner: Foo, name: bar"))
  975. self.assertEqual(self.stats.command_showschema(owner='Auth'),
  976. isc.config.create_answer(
  977. 0, {'Auth': [{
  978. "item_default": 0,
  979. "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially",
  980. "item_name": "queries.tcp",
  981. "item_optional": False,
  982. "item_title": "Queries TCP",
  983. "item_type": "integer"
  984. },
  985. {
  986. "item_default": 0,
  987. "item_description": "A number of total query counts which all auth servers receive over UDP since they started initially",
  988. "item_name": "queries.udp",
  989. "item_optional": False,
  990. "item_title": "Queries UDP",
  991. "item_type": "integer"
  992. },
  993. {
  994. "item_name": "queries.perzone",
  995. "item_type": "list",
  996. "item_optional": False,
  997. "item_default": [
  998. {
  999. "zonename" : "test1.example",
  1000. "queries.udp" : 1,
  1001. "queries.tcp" : 2
  1002. },
  1003. {
  1004. "zonename" : "test2.example",
  1005. "queries.udp" : 3,
  1006. "queries.tcp" : 4
  1007. }
  1008. ],
  1009. "item_title": "Queries per zone",
  1010. "item_description": "Queries per zone",
  1011. "list_item_spec": {
  1012. "item_name": "zones",
  1013. "item_type": "map",
  1014. "item_optional": False,
  1015. "item_default": {},
  1016. "map_item_spec": [
  1017. {
  1018. "item_name": "zonename",
  1019. "item_type": "string",
  1020. "item_optional": False,
  1021. "item_default": "",
  1022. "item_title": "Zonename",
  1023. "item_description": "Zonename"
  1024. },
  1025. {
  1026. "item_name": "queries.udp",
  1027. "item_type": "integer",
  1028. "item_optional": False,
  1029. "item_default": 0,
  1030. "item_title": "Queries UDP per zone",
  1031. "item_description": "A number of UDP query counts per zone"
  1032. },
  1033. {
  1034. "item_name": "queries.tcp",
  1035. "item_type": "integer",
  1036. "item_optional": False,
  1037. "item_default": 0,
  1038. "item_title": "Queries TCP per zone",
  1039. "item_description": "A number of TCP query counts per zone"
  1040. }
  1041. ]
  1042. }
  1043. },
  1044. {
  1045. "item_name": "nds_queries.perzone",
  1046. "item_type": "named_set",
  1047. "item_optional": False,
  1048. "item_default": {
  1049. "test10.example" : {
  1050. "queries.udp" : 1,
  1051. "queries.tcp" : 2
  1052. },
  1053. "test20.example" : {
  1054. "queries.udp" : 3,
  1055. "queries.tcp" : 4
  1056. }
  1057. },
  1058. "item_title": "Queries per zone",
  1059. "item_description": "Queries per zone",
  1060. "named_set_item_spec": {
  1061. "item_name": "zonename",
  1062. "item_type": "map",
  1063. "item_optional": False,
  1064. "item_default": {},
  1065. "item_title": "Zonename",
  1066. "item_description": "Zonename",
  1067. "map_item_spec": [
  1068. {
  1069. "item_name": "queries.udp",
  1070. "item_type": "integer",
  1071. "item_optional": False,
  1072. "item_default": 0,
  1073. "item_title": "Queries UDP per zone",
  1074. "item_description": "A number of UDP query counts per zone"
  1075. },
  1076. {
  1077. "item_name": "queries.tcp",
  1078. "item_type": "integer",
  1079. "item_optional": False,
  1080. "item_default": 0,
  1081. "item_title": "Queries TCP per zone",
  1082. "item_description": "A number of TCP query counts per zone"
  1083. }
  1084. ]
  1085. }
  1086. }]}))
  1087. self.assertEqual(self.stats.command_showschema(owner='Auth', name='queries.tcp'),
  1088. isc.config.create_answer(
  1089. 0, {'Auth': [{
  1090. "item_default": 0,
  1091. "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially",
  1092. "item_name": "queries.tcp",
  1093. "item_optional": False,
  1094. "item_title": "Queries TCP",
  1095. "item_type": "integer"
  1096. }]}))
  1097. self.assertEqual(self.stats.command_showschema(owner='Auth', name='queries.perzone'),
  1098. isc.config.create_answer(
  1099. 0, {'Auth':[{
  1100. "item_name": "queries.perzone",
  1101. "item_type": "list",
  1102. "item_optional": False,
  1103. "item_default": [
  1104. {
  1105. "zonename" : "test1.example",
  1106. "queries.udp" : 1,
  1107. "queries.tcp" : 2
  1108. },
  1109. {
  1110. "zonename" : "test2.example",
  1111. "queries.udp" : 3,
  1112. "queries.tcp" : 4
  1113. }
  1114. ],
  1115. "item_title": "Queries per zone",
  1116. "item_description": "Queries per zone",
  1117. "list_item_spec": {
  1118. "item_name": "zones",
  1119. "item_type": "map",
  1120. "item_optional": False,
  1121. "item_default": {},
  1122. "map_item_spec": [
  1123. {
  1124. "item_name": "zonename",
  1125. "item_type": "string",
  1126. "item_optional": False,
  1127. "item_default": "",
  1128. "item_title": "Zonename",
  1129. "item_description": "Zonename"
  1130. },
  1131. {
  1132. "item_name": "queries.udp",
  1133. "item_type": "integer",
  1134. "item_optional": False,
  1135. "item_default": 0,
  1136. "item_title": "Queries UDP per zone",
  1137. "item_description": "A number of UDP query counts per zone"
  1138. },
  1139. {
  1140. "item_name": "queries.tcp",
  1141. "item_type": "integer",
  1142. "item_optional": False,
  1143. "item_default": 0,
  1144. "item_title": "Queries TCP per zone",
  1145. "item_description": "A number of TCP query counts per zone"
  1146. }
  1147. ]
  1148. }
  1149. }]}))
  1150. self.assertEqual(self.stats.command_showschema(owner='Auth', name='nds_queries.perzone'),
  1151. isc.config.create_answer(
  1152. 0, {'Auth':[{
  1153. "item_name": "nds_queries.perzone",
  1154. "item_type": "named_set",
  1155. "item_optional": False,
  1156. "item_default": {
  1157. "test10.example" : {
  1158. "queries.udp" : 1,
  1159. "queries.tcp" : 2
  1160. },
  1161. "test20.example" : {
  1162. "queries.udp" : 3,
  1163. "queries.tcp" : 4
  1164. }
  1165. },
  1166. "item_title": "Queries per zone",
  1167. "item_description": "Queries per zone",
  1168. "named_set_item_spec": {
  1169. "item_name": "zonename",
  1170. "item_type": "map",
  1171. "item_optional": False,
  1172. "item_default": {},
  1173. "item_title": "Zonename",
  1174. "item_description": "Zonename",
  1175. "map_item_spec": [
  1176. {
  1177. "item_name": "queries.udp",
  1178. "item_type": "integer",
  1179. "item_optional": False,
  1180. "item_default": 0,
  1181. "item_title": "Queries UDP per zone",
  1182. "item_description": "A number of UDP query counts per zone"
  1183. },
  1184. {
  1185. "item_name": "queries.tcp",
  1186. "item_type": "integer",
  1187. "item_optional": False,
  1188. "item_default": 0,
  1189. "item_title": "Queries TCP per zone",
  1190. "item_description": "A number of TCP query counts per zone"
  1191. }
  1192. ]
  1193. }
  1194. }]}))
  1195. self.assertEqual(self.stats.command_showschema(owner='Stats', name='bar'),
  1196. isc.config.create_answer(
  1197. 1, "specified arguments are incorrect: owner: Stats, name: bar"))
  1198. self.assertEqual(self.stats.command_showschema(name='bar'),
  1199. isc.config.create_answer(
  1200. 1, "module name is not specified"))
  1201. def test_polling_init(self):
  1202. """check statistics data of 'Init'."""
  1203. stat = MyStats()
  1204. stat.update_modules = lambda: None
  1205. create_answer = isc.config.ccsession.create_answer # shortcut
  1206. stat._answers = [
  1207. # Answer for "show_processes"
  1208. (create_answer(0, []), None),
  1209. # Answers for "getstats" for Init (type of boot_time is invalid)
  1210. (create_answer(0, {'boot_time': self.const_datetime}),
  1211. {'from': 'init'}),
  1212. ]
  1213. stat.do_polling()
  1214. self.assertEqual(
  1215. stat.statistics_data_bymid['Init']['init'],
  1216. {'boot_time': self.const_datetime})
  1217. def test_polling_consolidate(self):
  1218. """check statistics data of multiple instances of same module."""
  1219. stat = MyStats()
  1220. stat.update_modules = lambda: None
  1221. create_answer = isc.config.ccsession.create_answer # shortcut
  1222. # Test data borrowed from test_update_statistics_data_withmid
  1223. stat._answers = [
  1224. (create_answer(0, [[1034, 'b10-auth-1', 'Auth'],
  1225. [1035, 'b10-auth-2', 'Auth']]), None),
  1226. (create_answer(0, stat._auth_sdata), {'from': 'auth1'}),
  1227. (create_answer(0, stat._auth_sdata), {'from': 'auth2'}),
  1228. (create_answer(0, stat._auth_sdata), {'from': 'auth3'})
  1229. ]
  1230. stat.do_polling()
  1231. # check statistics data of each 'Auth' instances. expected data
  1232. # for 'nds_queries.perzone' is special as it needs data merge.
  1233. self.assertEqual(2, len(stat.statistics_data_bymid['Auth'].values()))
  1234. for s in stat.statistics_data_bymid['Auth'].values():
  1235. self.assertEqual(
  1236. s, {'queries.perzone': stat._auth_sdata['queries.perzone'],
  1237. 'nds_queries.perzone': stat._nds_queries_per_zone,
  1238. 'queries.tcp': stat._auth_sdata['queries.tcp'],
  1239. 'queries.udp': stat._auth_sdata['queries.udp']})
  1240. # check consolidation of statistics data of the auth instances.
  1241. # it's union of the reported data and the spec default.
  1242. n = len(stat.statistics_data_bymid['Auth'].values())
  1243. self.maxDiff = None
  1244. self.assertEqual(
  1245. stat.statistics_data['Auth'],
  1246. {'queries.perzone': [
  1247. {'zonename': 'test1.example',
  1248. 'queries.tcp': 5 * n,
  1249. 'queries.udp': 4 * n},
  1250. {'zonename': 'test2.example',
  1251. 'queries.tcp': 4 * n,
  1252. 'queries.udp': 3 * n},
  1253. ],
  1254. 'nds_queries.perzone': {
  1255. 'test10.example': {
  1256. 'queries.tcp': 5 * n,
  1257. 'queries.udp': 4 * n
  1258. },
  1259. 'test20.example': {
  1260. 'queries.tcp': 4 * n,
  1261. 'queries.udp': 3 * n
  1262. },
  1263. },
  1264. 'queries.tcp': 3 * n,
  1265. 'queries.udp': 2 * n})
  1266. def test_polling_stats(self):
  1267. """Check statistics data of 'Stats'
  1268. This is actually irrelevant to do_polling(), but provided to
  1269. compatibility of older tests.
  1270. """
  1271. stat = MyStats()
  1272. self.assertEqual(len(stat.statistics_data['Stats']), 5)
  1273. self.assertTrue('boot_time' in stat.statistics_data['Stats'])
  1274. self.assertTrue('last_update_time' in stat.statistics_data['Stats'])
  1275. self.assertTrue('report_time' in stat.statistics_data['Stats'])
  1276. self.assertTrue('timestamp' in stat.statistics_data['Stats'])
  1277. self.assertEqual(stat.statistics_data['Stats']['lname'],
  1278. stat.mccs._session.lname)
  1279. def test_polling2(self):
  1280. """Test do_polling() doesn't incorporate broken statistics data.
  1281. Actually, this is not a test for do_polling() itself. It's bad, but
  1282. fixing that is a subject of different ticket.
  1283. """
  1284. stat = MyStats()
  1285. # check default statistics data of 'Init'
  1286. self.assertEqual(
  1287. stat.statistics_data['Init'],
  1288. {'boot_time': self.const_default_datetime})
  1289. # set invalid statistics
  1290. create_answer = isc.config.ccsession.create_answer # shortcut
  1291. stat._answers = [
  1292. # Answer for "show_processes"
  1293. (create_answer(0, []), None),
  1294. # Answers for "getstats" for Init (type of boot_time is invalid)
  1295. (create_answer(0, {'boot_time': 1}), {'from': 'init'}),
  1296. ]
  1297. stat.update_modules = lambda: None
  1298. # do_polling() should ignore the invalid answer;
  1299. # default data shouldn't be replaced.
  1300. stat.do_polling()
  1301. self.assertEqual(
  1302. stat.statistics_data['Init'],
  1303. {'boot_time': self.const_default_datetime})
  1304. class TestOSEnv(unittest.TestCase):
  1305. def test_osenv(self):
  1306. """
  1307. test for the environ variable "B10_FROM_SOURCE"
  1308. "B10_FROM_SOURCE" is set in Makefile
  1309. """
  1310. return
  1311. # test case having B10_FROM_SOURCE
  1312. self.assertTrue("B10_FROM_SOURCE" in os.environ)
  1313. self.assertEqual(stats.SPECFILE_LOCATION, \
  1314. os.environ["B10_FROM_SOURCE"] + os.sep + \
  1315. "src" + os.sep + "bin" + os.sep + "stats" + \
  1316. os.sep + "stats.spec")
  1317. # test case not having B10_FROM_SOURCE
  1318. path = os.environ["B10_FROM_SOURCE"]
  1319. os.environ.pop("B10_FROM_SOURCE")
  1320. self.assertFalse("B10_FROM_SOURCE" in os.environ)
  1321. # import stats again
  1322. imp.reload(stats)
  1323. # revert the changes
  1324. os.environ["B10_FROM_SOURCE"] = path
  1325. imp.reload(stats)
  1326. if __name__ == "__main__":
  1327. isc.log.resetUnitTestRootLogger()
  1328. unittest.main()