b10-stats_test.py 42 KB

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