b10-stats_test.py 66 KB

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