b10-stats_test.py 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923
  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 stats
  29. import isc.cc.session
  30. from test_utils import BaseModules, ThreadingServerManager, MyStats, SignalHandler, send_command, send_shutdown
  31. from isc.testutils.ccsession_mock import MockModuleCCSession
  32. class TestUtilties(unittest.TestCase):
  33. items = [
  34. { 'item_name': 'test_int1', 'item_type': 'integer', 'item_default': 12345 },
  35. { 'item_name': 'test_real1', 'item_type': 'real', 'item_default': 12345.6789 },
  36. { 'item_name': 'test_bool1', 'item_type': 'boolean', 'item_default': True },
  37. { 'item_name': 'test_str1', 'item_type': 'string', 'item_default': 'ABCD' },
  38. { 'item_name': 'test_list1', 'item_type': 'list', 'item_default': [1,2,3],
  39. 'list_item_spec' : { 'item_name': 'number', 'item_type': 'integer' } },
  40. { 'item_name': 'test_map1', 'item_type': 'map', 'item_default': {'a':1,'b':2,'c':3},
  41. 'map_item_spec' : [ { 'item_name': 'a', 'item_type': 'integer'},
  42. { 'item_name': 'b', 'item_type': 'integer'},
  43. { 'item_name': 'c', 'item_type': 'integer'} ] },
  44. { 'item_name': 'test_int2', 'item_type': 'integer' },
  45. { 'item_name': 'test_real2', 'item_type': 'real' },
  46. { 'item_name': 'test_bool2', 'item_type': 'boolean' },
  47. { 'item_name': 'test_str2', 'item_type': 'string' },
  48. { 'item_name': 'test_list2', 'item_type': 'list',
  49. 'list_item_spec' : { 'item_name': 'number', 'item_type': 'integer' } },
  50. { 'item_name': 'test_map2', 'item_type': 'map',
  51. 'map_item_spec' : [ { 'item_name': 'A', 'item_type': 'integer'},
  52. { 'item_name': 'B', 'item_type': 'integer'},
  53. { 'item_name': 'C', 'item_type': 'integer'} ] },
  54. { 'item_name': 'test_none', 'item_type': 'none' },
  55. { 'item_name': 'test_list3', 'item_type': 'list', 'item_default': ["one","two","three"],
  56. 'list_item_spec' : { 'item_name': 'number', 'item_type': 'string' } },
  57. { 'item_name': 'test_map3', 'item_type': 'map', 'item_default': {'a':'one','b':'two','c':'three'},
  58. 'map_item_spec' : [ { 'item_name': 'a', 'item_type': 'string'},
  59. { 'item_name': 'b', 'item_type': 'string'},
  60. { 'item_name': 'c', 'item_type': 'string'} ] }
  61. ]
  62. def setUp(self):
  63. self.const_timestamp = 1308730448.965706
  64. self.const_timetuple = (2011, 6, 22, 8, 14, 8, 2, 173, 0)
  65. self.const_datetime = '2011-06-22T08:14:08Z'
  66. stats.time = lambda : self.const_timestamp
  67. stats.gmtime = lambda : self.const_timetuple
  68. def test_get_spec_defaults(self):
  69. self.assertEqual(
  70. stats.get_spec_defaults(self.items), {
  71. 'test_int1' : 12345 ,
  72. 'test_real1' : 12345.6789 ,
  73. 'test_bool1' : True ,
  74. 'test_str1' : 'ABCD' ,
  75. 'test_list1' : [1,2,3] ,
  76. 'test_map1' : {'a':1,'b':2,'c':3},
  77. 'test_int2' : 0 ,
  78. 'test_real2' : 0.0,
  79. 'test_bool2' : False,
  80. 'test_str2' : "",
  81. 'test_list2' : [0],
  82. 'test_map2' : { 'A' : 0, 'B' : 0, 'C' : 0 },
  83. 'test_none' : None,
  84. 'test_list3' : [ "one", "two", "three" ],
  85. 'test_map3' : { 'a' : 'one', 'b' : 'two', 'c' : 'three' } })
  86. self.assertEqual(stats.get_spec_defaults(None), {})
  87. self.assertRaises(KeyError, stats.get_spec_defaults, [{'item_name':'Foo'}])
  88. def test_get_timestamp(self):
  89. self.assertEqual(stats.get_timestamp(), self.const_timestamp)
  90. def test_get_datetime(self):
  91. self.assertEqual(stats.get_datetime(), self.const_datetime)
  92. self.assertNotEqual(stats.get_datetime(
  93. (2011, 6, 22, 8, 23, 40, 2, 173, 0)), self.const_datetime)
  94. def test__accum(self):
  95. self.assertEqual(stats._accum(None, None), None)
  96. self.assertEqual(stats._accum(None, "b"), "b")
  97. self.assertEqual(stats._accum("a", None), "a")
  98. self.assertEqual(stats._accum(1, 2), 3)
  99. self.assertEqual(stats._accum(0.5, 0.3), 0.8)
  100. self.assertEqual(stats._accum('aa','bb'), 'bb')
  101. self.assertEqual(stats._accum('1970-01-01T09:00:00Z','2012-08-09T09:33:31Z'),
  102. '2012-08-09T09:33:31Z')
  103. self.assertEqual(stats._accum(
  104. [1, 2, 3], [4, 5]), [5, 7, 3])
  105. self.assertEqual(stats._accum(
  106. [4, 5], [1, 2, 3]), [5, 7, 3])
  107. self.assertEqual(stats._accum(
  108. [1, 2, 3], [None, 5, 6]), [1, 7, 9])
  109. self.assertEqual(stats._accum(
  110. [None, 5, 6], [1, 2, 3]), [1, 7, 9])
  111. self.assertEqual(stats._accum(
  112. [1, 2, 3], [None, None, None, None]), [1,2,3,None])
  113. self.assertEqual(stats._accum(
  114. [[1,2],3],[[],5,6]), [[1,2],8,6])
  115. self.assertEqual(stats._accum(
  116. {'one': 1, 'two': 2, 'three': 3},
  117. {'one': 4, 'two': 5}),
  118. {'one': 5, 'two': 7, 'three': 3})
  119. self.assertEqual(stats._accum(
  120. {'one': 1, 'two': 2, 'three': 3},
  121. {'four': 4, 'five': 5}),
  122. {'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5})
  123. self.assertEqual(stats._accum(
  124. {'one': [1, 2], 'two': [3, None, 5], 'three': [None, 3, None]},
  125. {'one': [2], 'two': [4, 5], 'three': [None, None, None], 'four': 'FOUR'}),
  126. {'one':[3,2], 'two':[7,5,5], 'three':[None,3,None], 'four': 'FOUR'})
  127. self.assertEqual(stats._accum(
  128. [ {'one': 1, 'two': 2, 'three': 3}, {'four': 4, 'five': 5, 'six': 6} ],
  129. [ {}, {'four': 1, 'five': 2, 'six': 3} ]),
  130. [ {'one': 1, 'two': 2, 'three': 3}, {'four': 5, 'five': 7, 'six': 9} ])
  131. class TestCallback(unittest.TestCase):
  132. def setUp(self):
  133. self.dummy_func = lambda *x, **y : (x, y)
  134. self.dummy_args = (1,2,3)
  135. self.dummy_kwargs = {'a':1,'b':2,'c':3}
  136. self.cback1 = stats.Callback(
  137. command=self.dummy_func,
  138. args=self.dummy_args,
  139. kwargs=self.dummy_kwargs
  140. )
  141. self.cback2 = stats.Callback(
  142. args=self.dummy_args,
  143. kwargs=self.dummy_kwargs
  144. )
  145. self.cback3 = stats.Callback(
  146. command=self.dummy_func,
  147. kwargs=self.dummy_kwargs
  148. )
  149. self.cback4 = stats.Callback(
  150. command=self.dummy_func,
  151. args=self.dummy_args
  152. )
  153. def test_init(self):
  154. self.assertEqual((self.cback1.command, self.cback1.args, self.cback1.kwargs),
  155. (self.dummy_func, self.dummy_args, self.dummy_kwargs))
  156. self.assertEqual((self.cback2.command, self.cback2.args, self.cback2.kwargs),
  157. (None, self.dummy_args, self.dummy_kwargs))
  158. self.assertEqual((self.cback3.command, self.cback3.args, self.cback3.kwargs),
  159. (self.dummy_func, (), self.dummy_kwargs))
  160. self.assertEqual((self.cback4.command, self.cback4.args, self.cback4.kwargs),
  161. (self.dummy_func, self.dummy_args, {}))
  162. def test_call(self):
  163. self.assertEqual(self.cback1(), (self.dummy_args, self.dummy_kwargs))
  164. self.assertEqual(self.cback1(100, 200), ((100, 200), self.dummy_kwargs))
  165. self.assertEqual(self.cback1(a=100, b=200), (self.dummy_args, {'a':100, 'b':200}))
  166. self.assertEqual(self.cback2(), None)
  167. self.assertEqual(self.cback3(), ((), self.dummy_kwargs))
  168. self.assertEqual(self.cback3(100, 200), ((100, 200), self.dummy_kwargs))
  169. self.assertEqual(self.cback3(a=100, b=200), ((), {'a':100, 'b':200}))
  170. self.assertEqual(self.cback4(), (self.dummy_args, {}))
  171. self.assertEqual(self.cback4(100, 200), ((100, 200), {}))
  172. self.assertEqual(self.cback4(a=100, b=200), (self.dummy_args, {'a':100, 'b':200}))
  173. class TestStats(unittest.TestCase):
  174. def setUp(self):
  175. # set the signal handler for deadlock
  176. self.sig_handler = SignalHandler(self.fail)
  177. self.base = BaseModules()
  178. self.const_timestamp = 1308730448.965706
  179. self.const_datetime = '2011-06-22T08:14:08Z'
  180. self.const_default_datetime = '1970-01-01T00:00:00Z'
  181. def tearDown(self):
  182. self.base.shutdown()
  183. # reset the signal handler
  184. self.sig_handler.reset()
  185. def test_init(self):
  186. self.stats = stats.Stats()
  187. self.assertEqual(self.stats.module_name, 'Stats')
  188. self.assertFalse(self.stats.running)
  189. self.assertTrue('command_show' in self.stats.callbacks)
  190. self.assertTrue('command_status' in self.stats.callbacks)
  191. self.assertTrue('command_shutdown' in self.stats.callbacks)
  192. self.assertTrue('command_show' in self.stats.callbacks)
  193. self.assertTrue('command_showschema' in self.stats.callbacks)
  194. self.assertEqual(self.stats.config['poll-interval'], 60)
  195. def test_init_undefcmd(self):
  196. spec_str = """\
  197. {
  198. "module_spec": {
  199. "module_name": "Stats",
  200. "module_description": "Stats daemon",
  201. "config_data": [],
  202. "commands": [
  203. {
  204. "command_name": "_undef_command_",
  205. "command_description": "a undefined command in stats",
  206. "command_args": []
  207. }
  208. ],
  209. "statistics": []
  210. }
  211. }
  212. """
  213. orig_spec_location = stats.SPECFILE_LOCATION
  214. stats.SPECFILE_LOCATION = io.StringIO(spec_str)
  215. self.assertRaises(stats.StatsError, stats.Stats)
  216. stats.SPECFILE_LOCATION = orig_spec_location
  217. def test_start(self):
  218. # start without err
  219. self.stats_server = ThreadingServerManager(MyStats)
  220. self.stats = self.stats_server.server
  221. self.assertFalse(self.stats.running)
  222. self.stats_server.run()
  223. self.assertEqual(send_command("status", "Stats"),
  224. (0, "Stats is up. (PID " + str(os.getpid()) + ")"))
  225. self.assertTrue(self.stats.running)
  226. # Due to a race-condition related to the threads used in these
  227. # tests, use of the mock session and the stopped check (see
  228. # below), are temporarily disabled
  229. # See ticket #1668
  230. # Override moduleCCSession so we can check if send_stopping is called
  231. #self.stats.mccs = MockModuleCCSession()
  232. self.assertEqual(send_shutdown("Stats"), (0, None))
  233. self.assertFalse(self.stats.running)
  234. # Call server.shutdown with argument True so the thread.join() call
  235. # blocks and we are sure the main loop has finished (and set
  236. # mccs.stopped)
  237. self.stats_server.shutdown(True)
  238. # Also temporarily disabled for #1668, see above
  239. #self.assertTrue(self.stats.mccs.stopped)
  240. def test_handlers(self):
  241. self.stats_server = ThreadingServerManager(MyStats)
  242. self.stats = self.stats_server.server
  243. self.stats_server.run()
  244. # command_handler
  245. self.assertEqual(
  246. send_command(
  247. 'show', 'Stats',
  248. params={ 'owner' : 'Boss',
  249. 'name' : 'boot_time' }),
  250. (0, {'Boss': {'boot_time': self.const_datetime}}))
  251. self.assertEqual(
  252. send_command(
  253. 'show', 'Stats',
  254. params={ 'owner' : 'Boss',
  255. 'name' : 'boot_time' }),
  256. (0, {'Boss': {'boot_time': self.const_datetime}}))
  257. self.assertEqual(
  258. send_command('status', 'Stats'),
  259. (0, "Stats is up. (PID " + str(os.getpid()) + ")"))
  260. (rcode, value) = send_command('show', 'Stats')
  261. self.assertEqual(rcode, 0)
  262. self.assertEqual(len(value), 3)
  263. self.assertTrue('Boss' in value)
  264. self.assertTrue('Stats' in value)
  265. self.assertTrue('Auth' in value)
  266. self.assertEqual(len(value['Stats']), 5)
  267. self.assertEqual(len(value['Boss']), 1)
  268. self.assertTrue('boot_time' in value['Boss'])
  269. self.assertEqual(value['Boss']['boot_time'], self.const_datetime)
  270. self.assertTrue('report_time' in value['Stats'])
  271. self.assertTrue('boot_time' in value['Stats'])
  272. self.assertTrue('last_update_time' in value['Stats'])
  273. self.assertTrue('timestamp' in value['Stats'])
  274. self.assertTrue('lname' in value['Stats'])
  275. (rcode, value) = send_command('showschema', 'Stats')
  276. self.assertEqual(rcode, 0)
  277. self.assertEqual(len(value), 3)
  278. self.assertTrue('Boss' in value)
  279. self.assertTrue('Stats' in value)
  280. self.assertTrue('Auth' in value)
  281. self.assertEqual(len(value['Stats']), 5)
  282. self.assertEqual(len(value['Boss']), 1)
  283. for item in value['Boss']:
  284. self.assertTrue(len(item) == 7)
  285. self.assertTrue('item_name' in item)
  286. self.assertTrue('item_type' in item)
  287. self.assertTrue('item_optional' in item)
  288. self.assertTrue('item_default' in item)
  289. self.assertTrue('item_title' in item)
  290. self.assertTrue('item_description' in item)
  291. self.assertTrue('item_format' in item)
  292. for item in value['Stats']:
  293. self.assertTrue(len(item) == 6 or len(item) == 7)
  294. self.assertTrue('item_name' in item)
  295. self.assertTrue('item_type' in item)
  296. self.assertTrue('item_optional' in item)
  297. self.assertTrue('item_default' in item)
  298. self.assertTrue('item_title' in item)
  299. self.assertTrue('item_description' in item)
  300. if len(item) == 7:
  301. self.assertTrue('item_format' in item)
  302. self.assertEqual(
  303. send_command('__UNKNOWN__', 'Stats'),
  304. (1, "Unknown command: '__UNKNOWN__'"))
  305. self.stats_server.shutdown()
  306. def test_update_modules(self):
  307. self.stats = stats.Stats()
  308. self.assertEqual(len(self.stats.modules), 3) # Auth, Boss, Stats
  309. self.stats.update_modules()
  310. self.assertTrue('Stats' in self.stats.modules)
  311. self.assertTrue('Boss' in self.stats.modules)
  312. self.assertFalse('Dummy' in self.stats.modules)
  313. my_statistics_data = stats.get_spec_defaults(self.stats.modules['Stats'].get_statistics_spec())
  314. self.assertTrue('report_time' in my_statistics_data)
  315. self.assertTrue('boot_time' in my_statistics_data)
  316. self.assertTrue('last_update_time' in my_statistics_data)
  317. self.assertTrue('timestamp' in my_statistics_data)
  318. self.assertTrue('lname' in my_statistics_data)
  319. self.assertEqual(my_statistics_data['report_time'], self.const_default_datetime)
  320. self.assertEqual(my_statistics_data['boot_time'], self.const_default_datetime)
  321. self.assertEqual(my_statistics_data['last_update_time'], self.const_default_datetime)
  322. self.assertEqual(my_statistics_data['timestamp'], 0.0)
  323. self.assertEqual(my_statistics_data['lname'], "")
  324. my_statistics_data = stats.get_spec_defaults(self.stats.modules['Boss'].get_statistics_spec())
  325. self.assertTrue('boot_time' in my_statistics_data)
  326. self.assertEqual(my_statistics_data['boot_time'], self.const_default_datetime)
  327. orig_parse_answer = stats.isc.config.ccsession.parse_answer
  328. stats.isc.config.ccsession.parse_answer = lambda x: (99, 'error')
  329. self.assertRaises(stats.StatsError, self.stats.update_modules)
  330. stats.isc.config.ccsession.parse_answer = orig_parse_answer
  331. def test_get_statistics_data(self):
  332. self.stats = stats.Stats()
  333. my_statistics_data = self.stats.get_statistics_data()
  334. self.assertTrue('Stats' in my_statistics_data)
  335. self.assertTrue('Boss' in my_statistics_data)
  336. self.assertTrue('boot_time' in my_statistics_data['Boss'])
  337. my_statistics_data = self.stats.get_statistics_data(owner='Stats')
  338. self.assertTrue('Stats' in my_statistics_data)
  339. self.assertTrue('report_time' in my_statistics_data['Stats'])
  340. self.assertTrue('boot_time' in my_statistics_data['Stats'])
  341. self.assertTrue('last_update_time' in my_statistics_data['Stats'])
  342. self.assertTrue('timestamp' in my_statistics_data['Stats'])
  343. self.assertTrue('lname' in my_statistics_data['Stats'])
  344. self.assertRaises(stats.StatsError, self.stats.get_statistics_data, owner='Foo')
  345. my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='report_time')
  346. self.assertEqual(my_statistics_data['Stats']['report_time'], self.const_default_datetime)
  347. my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='boot_time')
  348. self.assertTrue('boot_time' in my_statistics_data['Stats'])
  349. my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='last_update_time')
  350. self.assertTrue('last_update_time' in my_statistics_data['Stats'])
  351. my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='timestamp')
  352. self.assertEqual(my_statistics_data['Stats']['timestamp'], 0.0)
  353. my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='lname')
  354. self.assertTrue(len(my_statistics_data['Stats']['lname']) >0)
  355. self.assertRaises(stats.StatsError, self.stats.get_statistics_data,
  356. owner='Stats', name='Bar')
  357. self.assertRaises(stats.StatsError, self.stats.get_statistics_data,
  358. owner='Foo', name='Bar')
  359. self.assertRaises(stats.StatsError, self.stats.get_statistics_data,
  360. name='Bar')
  361. def test_update_statistics_data(self):
  362. self.stats = stats.Stats()
  363. # success
  364. self.assertEqual(self.stats.statistics_data['Stats']['lname'],
  365. self.stats.cc_session.lname)
  366. self.stats.update_statistics_data(
  367. 'Stats', self.stats.cc_session.lname,
  368. {'lname': 'foo@bar'})
  369. self.assertEqual(self.stats.statistics_data['Stats']['lname'],
  370. 'foo@bar')
  371. # error case
  372. self.assertEqual(self.stats.update_statistics_data('Stats', None,
  373. {'lname': 0.0}),
  374. ['0.0 should be a string'])
  375. self.assertEqual(self.stats.update_statistics_data('Dummy', None,
  376. {'foo': 'bar'}),
  377. ['unknown module name: Dummy'])
  378. def test_update_statistics_data_withmid(self):
  379. self.stats = stats.Stats()
  380. # samples of query number
  381. bar1_tcp = 1001
  382. bar2_tcp = 2001
  383. bar3_tcp = 1002
  384. bar3_udp = 1003
  385. # two auth instances invoked
  386. list_auth = [ self.base.auth.server,
  387. self.base.auth2.server ]
  388. sum_qtcp = 0
  389. for a in list_auth:
  390. sum_qtcp += a.queries_tcp
  391. sum_qudp = 0
  392. for a in list_auth:
  393. sum_qudp += a.queries_udp
  394. self.stats.update_statistics_data('Auth', "bar1@foo",
  395. {'queries.tcp':bar1_tcp})
  396. self.assertTrue('Auth' in self.stats.statistics_data)
  397. self.assertTrue('queries.tcp' in self.stats.statistics_data['Auth'])
  398. self.assertEqual(self.stats.statistics_data['Auth']['queries.tcp'],
  399. bar1_tcp + sum_qtcp)
  400. self.assertTrue('Auth' in self.stats.statistics_data_bymid)
  401. self.assertTrue('bar1@foo' in self.stats.statistics_data_bymid['Auth'])
  402. self.assertTrue('queries.tcp' in self.stats.statistics_data_bymid['Auth']['bar1@foo'])
  403. self.assertEqual(self.stats.statistics_data_bymid['Auth']['bar1@foo'],
  404. {'queries.tcp': bar1_tcp})
  405. # check consolidation of statistics data even if there is
  406. # non-existent mid of Auth
  407. self.stats.update_statistics_data('Auth', "bar2@foo",
  408. {'queries.tcp': bar2_tcp})
  409. self.assertTrue('Auth' in self.stats.statistics_data)
  410. self.assertTrue('queries.tcp' in self.stats.statistics_data['Auth'])
  411. self.assertEqual(self.stats.statistics_data['Auth']['queries.tcp'],
  412. bar1_tcp + bar2_tcp + sum_qtcp)
  413. self.assertTrue('Auth' in self.stats.statistics_data_bymid)
  414. self.assertTrue('bar1@foo' in self.stats.statistics_data_bymid['Auth'])
  415. self.assertTrue('queries.tcp' in self.stats.statistics_data_bymid['Auth']['bar1@foo'])
  416. self.assertEqual(self.stats.statistics_data_bymid['Auth']['bar1@foo'],
  417. {'queries.tcp': bar1_tcp})
  418. self.assertEqual(self.stats.statistics_data_bymid['Auth']['bar2@foo'],
  419. {'queries.tcp': bar2_tcp})
  420. # kill running Auth but the statistics data doesn't change
  421. self.base.auth2.server.shutdown()
  422. self.stats.update_statistics_data()
  423. self.assertTrue('Auth' in self.stats.statistics_data)
  424. self.assertTrue('queries.tcp' in self.stats.statistics_data['Auth'])
  425. self.assertTrue('queries.udp' in self.stats.statistics_data['Auth'])
  426. self.assertEqual(self.stats.statistics_data['Auth']['queries.tcp'],
  427. bar1_tcp + bar2_tcp + sum_qtcp)
  428. self.assertEqual(self.stats.statistics_data['Auth']['queries.udp'], sum_qudp)
  429. self.assertTrue('Auth' in self.stats.statistics_data_bymid)
  430. # restore statistics data of killed auth
  431. # self.base.boss.server.pid_list = [ killed ] + self.base.boss.server.pid_list[:]
  432. self.stats.update_statistics_data('Auth',
  433. "bar1@foo",
  434. {'queries.tcp': bar1_tcp})
  435. # set another mid of Auth
  436. self.stats.update_statistics_data('Auth',
  437. "bar3@foo",
  438. {'queries.tcp':bar3_tcp,
  439. 'queries.udp':bar3_udp})
  440. self.assertTrue('Auth' in self.stats.statistics_data)
  441. self.assertTrue('queries.tcp' in self.stats.statistics_data['Auth'])
  442. self.assertTrue('queries.udp' in self.stats.statistics_data['Auth'])
  443. self.assertEqual(self.stats.statistics_data['Auth']['queries.tcp'],
  444. bar1_tcp + bar2_tcp + bar3_tcp + sum_qtcp)
  445. self.assertEqual(self.stats.statistics_data['Auth']['queries.udp'],
  446. bar3_udp + sum_qudp)
  447. self.assertTrue('Auth' in self.stats.statistics_data_bymid)
  448. self.assertTrue('bar1@foo' in self.stats.statistics_data_bymid['Auth'])
  449. self.assertTrue('bar3@foo' in self.stats.statistics_data_bymid['Auth'])
  450. self.assertTrue('queries.tcp' in self.stats.statistics_data_bymid['Auth']['bar1@foo'])
  451. self.assertTrue('queries.udp' in self.stats.statistics_data_bymid['Auth']['bar3@foo'])
  452. self.assertTrue('queries.udp' in self.stats.statistics_data_bymid['Auth']['bar3@foo'])
  453. self.assertEqual(self.stats.statistics_data_bymid['Auth']['bar1@foo']['queries.tcp'], bar1_tcp)
  454. self.assertEqual(self.stats.statistics_data_bymid['Auth']['bar3@foo']['queries.tcp'], bar3_tcp)
  455. self.assertEqual(self.stats.statistics_data_bymid['Auth']['bar3@foo']['queries.udp'], bar3_udp)
  456. def test_config(self):
  457. orig_get_timestamp = stats.get_timestamp
  458. stats.get_timestamp = lambda : self.const_timestamp
  459. stats_server = ThreadingServerManager(MyStats)
  460. stat = stats_server.server
  461. # test updating poll-interval
  462. self.assertEqual(stat.config['poll-interval'], 60)
  463. self.assertEqual(stat.get_interval(), 60)
  464. self.assertEqual(stat.next_polltime, self.const_timestamp + 60)
  465. self.assertEqual(stat.config_handler({'poll-interval': 120}),
  466. isc.config.create_answer(0))
  467. self.assertEqual(stat.config['poll-interval'], 120)
  468. self.assertEqual(stat.get_interval(), 120)
  469. self.assertEqual(stat.next_polltime, self.const_timestamp + 120)
  470. stats.get_timestamp = orig_get_timestamp
  471. self.assertEqual(stat.config_handler({'poll-interval': "foo"}),
  472. isc.config.create_answer(1, 'foo should be an integer'))
  473. self.assertEqual(stat.config_handler({'poll-interval': -1}),
  474. isc.config.create_answer(1, 'Negative integer ignored'))
  475. # unknown item
  476. self.assertEqual(
  477. stat.config_handler({'_UNKNOWN_KEY_': None}),
  478. isc.config.ccsession.create_answer(
  479. 1, "unknown item _UNKNOWN_KEY_"))
  480. # test no change if zero interval time
  481. self.assertEqual(stat.config_handler({'poll-interval': 0}),
  482. isc.config.create_answer(0))
  483. self.assertEqual(stat.config['poll-interval'], 0)
  484. stats_server.run()
  485. self.assertEqual(
  486. send_command(
  487. 'show', 'Stats',
  488. params={ 'owner' : 'Boss',
  489. 'name' : 'boot_time' }),
  490. (0, {'Boss': {'boot_time': self.const_datetime}}))
  491. stats_server.shutdown()
  492. def test_commands(self):
  493. self.stats = stats.Stats()
  494. # status
  495. self.assertEqual(self.stats.command_status(),
  496. isc.config.create_answer(
  497. 0, "Stats is up. (PID " + str(os.getpid()) + ")"))
  498. # shutdown
  499. self.stats.running = True
  500. self.assertEqual(self.stats.command_shutdown(),
  501. isc.config.create_answer(0))
  502. self.assertFalse(self.stats.running)
  503. def test_command_show(self):
  504. # two auth instances invoked
  505. list_auth = [ self.base.auth.server,
  506. self.base.auth2.server ]
  507. sum_qtcp = 0
  508. sum_qudp = 0
  509. sum_qtcp_perzone = 0
  510. sum_qudp_perzone = 0
  511. self.stats = stats.Stats()
  512. self.assertEqual(self.stats.command_show(owner='Foo', name=None),
  513. isc.config.create_answer(
  514. 1, "specified arguments are incorrect: owner: Foo, name: None"))
  515. self.assertEqual(self.stats.command_show(owner='Foo', name='_bar_'),
  516. isc.config.create_answer(
  517. 1, "specified arguments are incorrect: owner: Foo, name: _bar_"))
  518. self.assertEqual(self.stats.command_show(owner='Foo', name='bar'),
  519. isc.config.create_answer(
  520. 1, "specified arguments are incorrect: owner: Foo, name: bar"))
  521. for a in list_auth:
  522. sum_qtcp += a.queries_tcp
  523. sum_qudp += a.queries_udp
  524. zonename = a.queries_per_zone[0]['zonename']
  525. sum_qtcp_perzone += a.queries_per_zone[0]['queries.tcp']
  526. sum_qudp_perzone += a.queries_per_zone[0]['queries.udp']
  527. self.assertEqual(self.stats.command_show(owner='Auth'),
  528. isc.config.create_answer(
  529. 0, {'Auth':{ 'queries.udp': sum_qudp,
  530. 'queries.tcp': sum_qtcp,
  531. 'queries.perzone': [{ 'zonename': 'test1.example',
  532. 'queries.udp': sum_qudp_perzone,
  533. 'queries.tcp': sum_qtcp_perzone }
  534. ]}}))
  535. self.assertEqual(self.stats.command_show(owner='Auth', name='queries.udp'),
  536. isc.config.create_answer(
  537. 0, {'Auth': {'queries.udp': sum_qudp}}))
  538. self.assertEqual(self.stats.command_show(owner='Auth', name='queries.perzone'),
  539. isc.config.create_answer(
  540. 0, {'Auth': {'queries.perzone': [{ 'zonename': 'test1.example',
  541. 'queries.udp': sum_qudp_perzone,
  542. 'queries.tcp': sum_qtcp_perzone }]}}))
  543. orig_get_datetime = stats.get_datetime
  544. orig_get_timestamp = stats.get_timestamp
  545. stats.get_datetime = lambda x=None: self.const_datetime
  546. stats.get_timestamp = lambda : self.const_timestamp
  547. self.assertEqual(self.stats.command_show(owner='Stats', name='report_time'),
  548. isc.config.create_answer(
  549. 0, {'Stats': {'report_time':self.const_datetime}}))
  550. self.assertEqual(self.stats.command_show(owner='Stats', name='timestamp'),
  551. isc.config.create_answer(
  552. 0, {'Stats': {'timestamp':self.const_timestamp}}))
  553. stats.get_datetime = orig_get_datetime
  554. stats.get_timestamp = orig_get_timestamp
  555. self.stats.mccs.specification = isc.config.module_spec.ModuleSpec(
  556. { "module_name": self.stats.module_name,
  557. "statistics": [] } )
  558. self.assertRaises(
  559. stats.StatsError, self.stats.command_show, owner='Foo', name='bar')
  560. def test_command_showchema(self):
  561. self.stats = stats.Stats()
  562. (rcode, value) = isc.config.ccsession.parse_answer(
  563. self.stats.command_showschema())
  564. self.assertEqual(rcode, 0)
  565. self.assertEqual(len(value), 3)
  566. self.assertTrue('Stats' in value)
  567. self.assertTrue('Boss' in value)
  568. self.assertTrue('Auth' in value)
  569. self.assertFalse('__Dummy__' in value)
  570. schema = value['Stats']
  571. self.assertEqual(len(schema), 5)
  572. for item in schema:
  573. self.assertTrue(len(item) == 6 or len(item) == 7)
  574. self.assertTrue('item_name' in item)
  575. self.assertTrue('item_type' in item)
  576. self.assertTrue('item_optional' in item)
  577. self.assertTrue('item_default' in item)
  578. self.assertTrue('item_title' in item)
  579. self.assertTrue('item_description' in item)
  580. if len(item) == 7:
  581. self.assertTrue('item_format' in item)
  582. schema = value['Boss']
  583. self.assertEqual(len(schema), 1)
  584. for item in schema:
  585. self.assertTrue(len(item) == 7)
  586. self.assertTrue('item_name' in item)
  587. self.assertTrue('item_type' in item)
  588. self.assertTrue('item_optional' in item)
  589. self.assertTrue('item_default' in item)
  590. self.assertTrue('item_title' in item)
  591. self.assertTrue('item_description' in item)
  592. self.assertTrue('item_format' in item)
  593. schema = value['Auth']
  594. self.assertEqual(len(schema), 3)
  595. for item in schema:
  596. if item['item_type'] == 'list':
  597. self.assertEqual(len(item), 7)
  598. else:
  599. self.assertEqual(len(item), 6)
  600. self.assertTrue('item_name' in item)
  601. self.assertTrue('item_type' in item)
  602. self.assertTrue('item_optional' in item)
  603. self.assertTrue('item_default' in item)
  604. self.assertTrue('item_title' in item)
  605. self.assertTrue('item_description' in item)
  606. (rcode, value) = isc.config.ccsession.parse_answer(
  607. self.stats.command_showschema(owner='Stats'))
  608. self.assertEqual(rcode, 0)
  609. self.assertTrue('Stats' in value)
  610. self.assertFalse('Boss' in value)
  611. self.assertFalse('Auth' in value)
  612. for item in value['Stats']:
  613. self.assertTrue(len(item) == 6 or len(item) == 7)
  614. self.assertTrue('item_name' in item)
  615. self.assertTrue('item_type' in item)
  616. self.assertTrue('item_optional' in item)
  617. self.assertTrue('item_default' in item)
  618. self.assertTrue('item_title' in item)
  619. self.assertTrue('item_description' in item)
  620. if len(item) == 7:
  621. self.assertTrue('item_format' in item)
  622. (rcode, value) = isc.config.ccsession.parse_answer(
  623. self.stats.command_showschema(owner='Stats', name='report_time'))
  624. self.assertEqual(rcode, 0)
  625. self.assertTrue('Stats' in value)
  626. self.assertFalse('Boss' in value)
  627. self.assertFalse('Auth' in value)
  628. self.assertEqual(len(value['Stats'][0]), 7)
  629. self.assertTrue('item_name' in value['Stats'][0])
  630. self.assertTrue('item_type' in value['Stats'][0])
  631. self.assertTrue('item_optional' in value['Stats'][0])
  632. self.assertTrue('item_default' in value['Stats'][0])
  633. self.assertTrue('item_title' in value['Stats'][0])
  634. self.assertTrue('item_description' in value['Stats'][0])
  635. self.assertTrue('item_format' in value['Stats'][0])
  636. self.assertEqual(value['Stats'][0]['item_name'], 'report_time')
  637. self.assertEqual(value['Stats'][0]['item_format'], 'date-time')
  638. self.assertEqual(self.stats.command_showschema(owner='Foo'),
  639. isc.config.create_answer(
  640. 1, "specified arguments are incorrect: owner: Foo, name: None"))
  641. self.assertEqual(self.stats.command_showschema(owner='Foo', name='bar'),
  642. isc.config.create_answer(
  643. 1, "specified arguments are incorrect: owner: Foo, name: bar"))
  644. self.assertEqual(self.stats.command_showschema(owner='Auth'),
  645. isc.config.create_answer(
  646. 0, {'Auth': [{
  647. "item_default": 0,
  648. "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially",
  649. "item_name": "queries.tcp",
  650. "item_optional": False,
  651. "item_title": "Queries TCP",
  652. "item_type": "integer"
  653. },
  654. {
  655. "item_default": 0,
  656. "item_description": "A number of total query counts which all auth servers receive over UDP since they started initially",
  657. "item_name": "queries.udp",
  658. "item_optional": False,
  659. "item_title": "Queries UDP",
  660. "item_type": "integer"
  661. },
  662. {
  663. "item_name": "queries.perzone",
  664. "item_type": "list",
  665. "item_optional": False,
  666. "item_default": [
  667. {
  668. "zonename" : "test1.example",
  669. "queries.udp" : 1,
  670. "queries.tcp" : 2
  671. },
  672. {
  673. "zonename" : "test2.example",
  674. "queries.udp" : 3,
  675. "queries.tcp" : 4
  676. }
  677. ],
  678. "item_title": "Queries per zone",
  679. "item_description": "Queries per zone",
  680. "list_item_spec": {
  681. "item_name": "zones",
  682. "item_type": "map",
  683. "item_optional": False,
  684. "item_default": {},
  685. "map_item_spec": [
  686. {
  687. "item_name": "zonename",
  688. "item_type": "string",
  689. "item_optional": False,
  690. "item_default": "",
  691. "item_title": "Zonename",
  692. "item_description": "Zonename"
  693. },
  694. {
  695. "item_name": "queries.udp",
  696. "item_type": "integer",
  697. "item_optional": False,
  698. "item_default": 0,
  699. "item_title": "Queries UDP per zone",
  700. "item_description": "A number of UDP query counts per zone"
  701. },
  702. {
  703. "item_name": "queries.tcp",
  704. "item_type": "integer",
  705. "item_optional": False,
  706. "item_default": 0,
  707. "item_title": "Queries TCP per zone",
  708. "item_description": "A number of TCP query counts per zone"
  709. }
  710. ]
  711. }
  712. }]}))
  713. self.assertEqual(self.stats.command_showschema(owner='Auth', name='queries.tcp'),
  714. isc.config.create_answer(
  715. 0, {'Auth': [{
  716. "item_default": 0,
  717. "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially",
  718. "item_name": "queries.tcp",
  719. "item_optional": False,
  720. "item_title": "Queries TCP",
  721. "item_type": "integer"
  722. }]}))
  723. self.assertEqual(self.stats.command_showschema(owner='Auth', name='queries.perzone'),
  724. isc.config.create_answer(
  725. 0, {'Auth':[{
  726. "item_name": "queries.perzone",
  727. "item_type": "list",
  728. "item_optional": False,
  729. "item_default": [
  730. {
  731. "zonename" : "test1.example",
  732. "queries.udp" : 1,
  733. "queries.tcp" : 2
  734. },
  735. {
  736. "zonename" : "test2.example",
  737. "queries.udp" : 3,
  738. "queries.tcp" : 4
  739. }
  740. ],
  741. "item_title": "Queries per zone",
  742. "item_description": "Queries per zone",
  743. "list_item_spec": {
  744. "item_name": "zones",
  745. "item_type": "map",
  746. "item_optional": False,
  747. "item_default": {},
  748. "map_item_spec": [
  749. {
  750. "item_name": "zonename",
  751. "item_type": "string",
  752. "item_optional": False,
  753. "item_default": "",
  754. "item_title": "Zonename",
  755. "item_description": "Zonename"
  756. },
  757. {
  758. "item_name": "queries.udp",
  759. "item_type": "integer",
  760. "item_optional": False,
  761. "item_default": 0,
  762. "item_title": "Queries UDP per zone",
  763. "item_description": "A number of UDP query counts per zone"
  764. },
  765. {
  766. "item_name": "queries.tcp",
  767. "item_type": "integer",
  768. "item_optional": False,
  769. "item_default": 0,
  770. "item_title": "Queries TCP per zone",
  771. "item_description": "A number of TCP query counts per zone"
  772. }
  773. ]
  774. }
  775. }]}))
  776. self.assertEqual(self.stats.command_showschema(owner='Stats', name='bar'),
  777. isc.config.create_answer(
  778. 1, "specified arguments are incorrect: owner: Stats, name: bar"))
  779. self.assertEqual(self.stats.command_showschema(name='bar'),
  780. isc.config.create_answer(
  781. 1, "module name is not specified"))
  782. def test_polling(self):
  783. stats_server = ThreadingServerManager(MyStats)
  784. stat = stats_server.server
  785. stats_server.run()
  786. self.assertEqual(
  787. send_command('status', 'Stats'),
  788. (0, "Stats is up. (PID " + str(os.getpid()) + ")"))
  789. # check statistics data of 'Boss'
  790. boss = self.base.boss.server
  791. self.assertEqual(
  792. stat.statistics_data_bymid['Boss'][boss.cc_session.lname],
  793. {'boot_time': self.const_datetime})
  794. self.assertEqual(
  795. len(stat.statistics_data_bymid['Boss']), 1)
  796. self.assertEqual(
  797. stat.statistics_data['Boss'],
  798. {'boot_time': self.const_datetime})
  799. # check statistics data of each 'Auth' instances
  800. list_auth = ['', '2']
  801. for i in list_auth:
  802. auth = getattr(self.base,"auth"+i).server
  803. for s in stat.statistics_data_bymid['Auth'].values():
  804. self.assertEqual(
  805. s, {'queries.perzone': auth.queries_per_zone,
  806. 'queries.tcp': auth.queries_tcp,
  807. 'queries.udp': auth.queries_udp})
  808. n = len(stat.statistics_data_bymid['Auth'])
  809. self.assertEqual(n, len(list_auth))
  810. # check consolidation of statistics data of the auth
  811. # instances
  812. self.assertEqual(
  813. stat.statistics_data['Auth'],
  814. {'queries.perzone': [
  815. {'zonename':
  816. auth.queries_per_zone[0]['zonename'],
  817. 'queries.tcp':
  818. auth.queries_per_zone[0]['queries.tcp']*n,
  819. 'queries.udp':
  820. auth.queries_per_zone[0]['queries.udp']*n}],
  821. 'queries.tcp': auth.queries_tcp*n,
  822. 'queries.udp': auth.queries_udp*n})
  823. # check statistics data of 'Stats'
  824. self.assertEqual(
  825. len(stat.statistics_data['Stats']), 5)
  826. self.assertTrue('boot_time' in
  827. stat.statistics_data['Stats'])
  828. self.assertTrue('last_update_time' in
  829. stat.statistics_data['Stats'])
  830. self.assertTrue('report_time' in
  831. stat.statistics_data['Stats'])
  832. self.assertTrue('timestamp' in
  833. stat.statistics_data['Stats'])
  834. self.assertEqual(
  835. stat.statistics_data['Stats']['lname'],
  836. stat.mccs._session.lname)
  837. stats_server.shutdown()
  838. def test_polling2(self):
  839. # set invalid statistics
  840. boss = self.base.boss.server
  841. boss.statistics_data = {'boot_time':1}
  842. stats_server = ThreadingServerManager(MyStats)
  843. stat = stats_server.server
  844. stats_server.run()
  845. self.assertEqual(
  846. send_command('status', 'Stats'),
  847. (0, "Stats is up. (PID " + str(os.getpid()) + ")"))
  848. # check default statistics data of 'Boss'
  849. self.assertEqual(
  850. stat.statistics_data['Boss'],
  851. {'boot_time': self.const_default_datetime})
  852. stats_server.shutdown()
  853. class TestOSEnv(unittest.TestCase):
  854. def test_osenv(self):
  855. """
  856. test for the environ variable "B10_FROM_SOURCE"
  857. "B10_FROM_SOURCE" is set in Makefile
  858. """
  859. # test case having B10_FROM_SOURCE
  860. self.assertTrue("B10_FROM_SOURCE" in os.environ)
  861. self.assertEqual(stats.SPECFILE_LOCATION, \
  862. os.environ["B10_FROM_SOURCE"] + os.sep + \
  863. "src" + os.sep + "bin" + os.sep + "stats" + \
  864. os.sep + "stats.spec")
  865. # test case not having B10_FROM_SOURCE
  866. path = os.environ["B10_FROM_SOURCE"]
  867. os.environ.pop("B10_FROM_SOURCE")
  868. self.assertFalse("B10_FROM_SOURCE" in os.environ)
  869. # import stats again
  870. imp.reload(stats)
  871. # revert the changes
  872. os.environ["B10_FROM_SOURCE"] = path
  873. imp.reload(stats)
  874. def test_main():
  875. unittest.main()
  876. if __name__ == "__main__":
  877. test_main()