counters_test.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. # Copyright (C) 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. '''Tests for isc.statistics.counter'''
  16. import unittest
  17. import threading
  18. from datetime import timedelta
  19. import os
  20. import imp
  21. import isc.config
  22. TEST_ZONE_NAME_STR = "example.com."
  23. TESTDATA_SRCDIR = os.getenv("TESTDATASRCDIR")
  24. from isc.statistics import counters
  25. def setup_functor(event, cycle, functor, *args):
  26. """Waits until the event is started, and then invokes the functor
  27. by times of the cycle with args."""
  28. event.wait()
  29. for i in range(cycle): functor(*args)
  30. def start_functor(concurrency, number, functor, *args):
  31. """Creates the threads of the number and makes them start. Sets
  32. the event and waits until these threads are finished."""
  33. threads = []
  34. event = threading.Event()
  35. for i in range(concurrency):
  36. threads.append(threading.Thread(\
  37. target=setup_functor, \
  38. args=(event, number, functor,) + args))
  39. for th in threads: th.start()
  40. event.set()
  41. for th in threads: th.join()
  42. class TestBasicMethods(unittest.TestCase):
  43. TEST_SPECFILE_LOCATION = TESTDATA_SRCDIR + os.sep + 'test_spec1.spec'
  44. def setUp(self):
  45. imp.reload(counters)
  46. self.counters = counters.Counters(self.TEST_SPECFILE_LOCATION)
  47. def tearDown(self):
  48. self.counters.clear_all()
  49. def test_clear_counters(self):
  50. self.assertRaises(isc.cc.data.DataNotFoundError,
  51. self.counters.get, 'counter')
  52. self.counters.inc('counter')
  53. self.assertEqual(self.counters.get('counter'), 1)
  54. self.counters.clear_all()
  55. self.assertRaises(isc.cc.data.DataNotFoundError,
  56. self.counters.get, 'counter')
  57. def test_enablediable(self):
  58. self.assertFalse(self.counters._disabled)
  59. self.counters.disable()
  60. self.assertTrue(self.counters._disabled)
  61. self.counters.enable()
  62. self.assertFalse(self.counters._disabled)
  63. def test_add_counter_normal(self):
  64. element = {'counter' : 1}
  65. self.assertEqual(\
  66. counters._add_counter(element, [], 'counter'), 1)
  67. def test_add_counter_wrongspec(self):
  68. self.assertRaises(isc.cc.data.DataNotFoundError,
  69. counters._add_counter,
  70. {}, [], 'counter')
  71. def test_add_counter_empty(self):
  72. self.assertEqual(\
  73. counters._add_counter(
  74. {},
  75. [ { 'item_name' : 'counter',
  76. 'item_type' : 'integer',
  77. 'item_default' : 0 } ],
  78. 'counter'), 0)
  79. def test_add_counter_empty_namedset(self):
  80. elem = {}
  81. spec = [ { 'item_name': 'dirs',
  82. 'item_type': 'named_set',
  83. 'named_set_item_spec': {
  84. 'item_name': 'dir',
  85. 'item_type': 'map',
  86. 'map_item_spec': [
  87. { 'item_name': 'counter1',
  88. 'item_type': 'integer',
  89. 'item_default': 0 },
  90. { 'item_name': 'counter2',
  91. 'item_type': 'integer',
  92. 'item_default': 0 } ]}
  93. }]
  94. self.assertEqual(\
  95. counters._add_counter(elem, spec, 'dirs/foo/counter1'), 0)
  96. self.assertEqual(\
  97. counters._add_counter(elem, spec, 'dirs/bar/counter2'), 0)
  98. def test_timer(self):
  99. t1 = counters._start_timer()
  100. t2 = t1 - timedelta(seconds=1)
  101. self.assertEqual((t1 - t2).seconds, 1)
  102. elem = {}
  103. spec = [{ 'item_name': 'time',
  104. 'item_type': 'real',
  105. 'item_default': 0.0 }]
  106. counters._stop_timer(t2, elem, spec, 'time')
  107. self.assertGreaterEqual(counters._get_counter(elem,'time'), 1.0)
  108. def test_rasing_incrementers(self):
  109. """ use Thread"""
  110. concurrency = 3 # number of the threads
  111. number = 10000 # number of counting per thread
  112. counter_name = "counter"
  113. timer_name = "seconds"
  114. start_time = counters._start_timer()
  115. start_functor(concurrency, number, self.counters.inc,
  116. counter_name)
  117. counters._stop_timer(start_time,
  118. self.counters._statistics._data,
  119. self.counters._statistics._spec,
  120. timer_name)
  121. self.assertEqual(
  122. counters._get_counter(self.counters._statistics._data,
  123. counter_name),
  124. concurrency * number)
  125. self.assertGreaterEqual(
  126. counters._get_counter(self.counters._statistics._data,
  127. timer_name), 0.0)
  128. def test_concat(self):
  129. # only strings
  130. a = ( 'a','b','c','d' )
  131. self.assertEqual('a/b/c/d', counters._concat(*a))
  132. self.assertEqual('aTbTcTd', counters._concat(*a, sep='T'))
  133. self.assertEqual('a\\b\\c\\d', counters._concat(*a, sep='\\'))
  134. # mixed with other types
  135. b = a + (1,)
  136. self.assertRaises(TypeError, counters._concat, *b)
  137. b = a + (1.1,)
  138. self.assertRaises(TypeError, counters._concat, *b)
  139. b = a + ([],)
  140. self.assertRaises(TypeError, counters._concat, *b)
  141. b = a + ({},)
  142. self.assertRaises(TypeError, counters._concat, *b)
  143. class BaseTestCounters():
  144. def setUp(self):
  145. imp.reload(counters)
  146. self._statistics_data = {}
  147. self.counters = counters.Counters(self.TEST_SPECFILE_LOCATION)
  148. self._entire_server = self.counters._entire_server
  149. self._perzone_prefix = self.counters._perzone_prefix
  150. def tearDown(self):
  151. self.counters.clear_all()
  152. def check_get_statistics(self):
  153. """Checks no differences between the value returned from
  154. get_statistics() and locally collected statistics data. Also
  155. checks the result isn't changed even after the method is
  156. invoked twice. Finally checks it is valid for the the
  157. statistics spec."""
  158. self.assertEqual(self.counters.get_statistics(),
  159. self._statistics_data)
  160. # Idempotency check
  161. self.assertEqual(self.counters.get_statistics(),
  162. self._statistics_data)
  163. if self.TEST_SPECFILE_LOCATION:
  164. self.assertTrue(isc.config.module_spec_from_file(
  165. self.TEST_SPECFILE_LOCATION).validate_statistics(
  166. False, self._statistics_data))
  167. else:
  168. self.assertTrue(isc.config.ModuleSpec(
  169. {'module_name': 'Foo',
  170. 'statistics': self.counters._statistics._spec}
  171. ).validate_statistics(
  172. False, self._statistics_data))
  173. def test_perzone_counters(self):
  174. # for per-zone counters
  175. for name in self.counters._zones_item_list:
  176. args = (self._perzone_prefix, TEST_ZONE_NAME_STR, name)
  177. if name.find('time_to_') == 0:
  178. self.counters.start_timer(*args)
  179. self.counters.stop_timer(*args)
  180. self.assertGreaterEqual(self.counters.get(*args), 0.0)
  181. sec = self.counters.get(*args)
  182. for zone_str in (self._entire_server, TEST_ZONE_NAME_STR):
  183. isc.cc.data.set(self._statistics_data,
  184. '%s/%s/%s' % (args[0], zone_str, name), sec)
  185. # twice exec stopper, then second is not changed
  186. self.counters.stop_timer(*args)
  187. self.assertEqual(self.counters.get(*args), sec)
  188. else:
  189. self.counters.inc(*args)
  190. self.assertEqual(self.counters.get(*args), 1)
  191. # checks disable/enable
  192. self.counters.disable()
  193. self.counters.inc(*args)
  194. self.assertEqual(self.counters.get(*args), 1)
  195. self.counters.enable()
  196. self.counters.inc(*args)
  197. self.assertEqual(self.counters.get(*args), 2)
  198. for zone_str in (self._entire_server, TEST_ZONE_NAME_STR):
  199. isc.cc.data.set(self._statistics_data,
  200. '%s/%s/%s' % (args[0], zone_str, name), 2)
  201. self.check_get_statistics()
  202. def test_xfrrunning_counters(self):
  203. # for counters of xfer running
  204. _suffix = 'xfr_running'
  205. _xfrrunning_names = \
  206. isc.config.spec_name_list(self.counters._statistics._spec,
  207. "", True)
  208. for name in _xfrrunning_names:
  209. if name.find(_suffix) != 1: continue
  210. args = name.split('/')
  211. self.counters.inc(*args)
  212. self.assertEqual(self.counters.get(*args), 1)
  213. self.counters.dec(*args)
  214. self.assertEqual(self.counters.get(*args), 0)
  215. # checks disable/enable
  216. self.counters.disable()
  217. self.counters.inc(*args)
  218. self.assertEqual(self.counters.get(*args), 0)
  219. self.counters.enable()
  220. self.counters.inc(*args)
  221. self.assertEqual(self.counters.get(*args), 1)
  222. self.counters.disable()
  223. self.counters.dec(*args)
  224. self.assertEqual(self.counters.get(*args), 1)
  225. self.counters.enable()
  226. self.counters.dec(*args)
  227. self.assertEqual(self.counters.get(*args), 0)
  228. self._statistics_data[name] = 0
  229. self.check_get_statistics()
  230. def test_socket_counters(self):
  231. # for ipsocket/unixsocket counters
  232. _prefix = 'socket/'
  233. _socket_names = \
  234. isc.config.spec_name_list(self.counters._statistics._spec,
  235. "", True)
  236. for name in _socket_names:
  237. if name.find(_prefix) != 0: continue
  238. args = name.split('/')
  239. self.counters.inc(*args)
  240. self.assertEqual(self.counters.get(*args), 1)
  241. # checks disable/enable
  242. self.counters.disable()
  243. self.counters.inc(*args)
  244. self.assertEqual(self.counters.get(*args), 1)
  245. self.counters.enable()
  246. self.counters.inc(*args)
  247. self.assertEqual(self.counters.get(*args), 2)
  248. isc.cc.data.set(
  249. self._statistics_data, '/'.join(args), 2)
  250. self.check_get_statistics()
  251. def test_undefined_item(self):
  252. # test DataNotFoundError raising when specifying item defined
  253. # in the specfile
  254. self.assertRaises(isc.cc.data.DataNotFoundError,
  255. self.counters.inc, '__undefined__')
  256. self.assertRaises(isc.cc.data.DataNotFoundError,
  257. self.counters.dec, '__undefined__')
  258. self.counters.start_timer('__undefined__')
  259. self.assertRaises(isc.cc.data.DataNotFoundError,
  260. self.counters.stop_timer, '__undefined__')
  261. self.assertRaises(isc.cc.data.DataNotFoundError,
  262. self.counters.get, '__undefined__')
  263. class TestCounters0(unittest.TestCase, BaseTestCounters):
  264. TEST_SPECFILE_LOCATION = None
  265. def setUp(self):
  266. BaseTestCounters.setUp(self)
  267. def tearDown(self):
  268. BaseTestCounters.tearDown(self)
  269. class TestCounters1(unittest.TestCase, BaseTestCounters):
  270. TEST_SPECFILE_LOCATION = TESTDATA_SRCDIR + os.sep + 'test_spec1.spec'
  271. def setUp(self):
  272. BaseTestCounters.setUp(self)
  273. def tearDown(self):
  274. BaseTestCounters.tearDown(self)
  275. class TestCounters2(unittest.TestCase, BaseTestCounters):
  276. TEST_SPECFILE_LOCATION = TESTDATA_SRCDIR + os.sep + 'test_spec2.spec'
  277. def setUp(self):
  278. BaseTestCounters.setUp(self)
  279. def tearDown(self):
  280. BaseTestCounters.tearDown(self)
  281. class TestCounters3(unittest.TestCase, BaseTestCounters):
  282. TEST_SPECFILE_LOCATION = TESTDATA_SRCDIR + os.sep + 'test_spec3.spec'
  283. @classmethod
  284. def setUpClass(cls):
  285. imp.reload(counters)
  286. def setUp(self):
  287. BaseTestCounters.setUp(self)
  288. def tearDown(self):
  289. BaseTestCounters.tearDown(self)
  290. class BaseDummyModule():
  291. """A base dummy class"""
  292. TEST_SPECFILE_LOCATION = TESTDATA_SRCDIR + os.sep + 'test_spec2.spec'
  293. def __init__(self):
  294. self.counters = counters.Counters(self.TEST_SPECFILE_LOCATION)
  295. def get_counters(self):
  296. return self.counters.get_statistics()
  297. def clear_counters(self):
  298. self.counters.clear_all()
  299. class DummyNotifyOut(BaseDummyModule):
  300. """A dummy class equivalent to notify.notify_out.NotifyOut"""
  301. def __init__(self):
  302. self.counters = counters.Counters()
  303. def inc_counters(self):
  304. """increments counters"""
  305. self.counters.inc('zones', TEST_ZONE_NAME_STR, 'notifyoutv4')
  306. self.counters.inc('zones', TEST_ZONE_NAME_STR, 'notifyoutv6')
  307. class DummyXfroutSession(BaseDummyModule):
  308. """A dummy class equivalent to XfroutSession in b10-xfrout"""
  309. def inc_counters(self):
  310. """increments counters"""
  311. self.counters.inc('zones', TEST_ZONE_NAME_STR, 'xfrreqdone')
  312. self.counters.inc('zones', TEST_ZONE_NAME_STR, 'xfrrej')
  313. self.counters.inc('axfr_running')
  314. self.counters.inc('ixfr_running')
  315. self.counters.dec('axfr_running')
  316. self.counters.dec('ixfr_running')
  317. class DummyUnixSockServer(BaseDummyModule):
  318. """A dummy class equivalent to UnixSockServer in b10-xfrout"""
  319. def inc_counters(self):
  320. """increments counters"""
  321. self.counters.inc('socket', 'unixdomain', 'open')
  322. self.counters.inc('socket', 'unixdomain', 'close')
  323. class DummyXfroutServer(BaseDummyModule):
  324. """A dummy class equivalent to XfroutServer in b10-xfrout"""
  325. def __init__(self):
  326. super().__init__()
  327. self.xfrout_sess = DummyXfroutSession()
  328. self.unix_socket_server = DummyUnixSockServer()
  329. self.notifier = DummyNotifyOut()
  330. def inc_counters(self):
  331. self.xfrout_sess.inc_counters()
  332. self.unix_socket_server.inc_counters()
  333. self.notifier.inc_counters()
  334. class TestDummyNotifyOut(unittest.TestCase):
  335. """Tests counters are incremented in which the spec file is not
  336. loaded"""
  337. def setUp(self):
  338. imp.reload(counters)
  339. self.notifier = DummyNotifyOut()
  340. self.notifier.inc_counters()
  341. def tearDown(self):
  342. self.notifier.clear_counters()
  343. def test_counters(self):
  344. self.assertEqual(
  345. {'zones': {'_SERVER_': {'notifyoutv4': 1, 'notifyoutv6': 1},
  346. TEST_ZONE_NAME_STR: {'notifyoutv4': 1, 'notifyoutv6': 1}}},
  347. self.notifier.get_counters())
  348. class TestDummyXfroutServer(unittest.TestCase):
  349. """Tests counters are incremented or decremented in which the same
  350. spec file is multiply loaded in each child class"""
  351. def setUp(self):
  352. imp.reload(counters)
  353. self.xfrout_server = DummyXfroutServer()
  354. self.xfrout_server.inc_counters()
  355. def tearDown(self):
  356. self.xfrout_server.clear_counters()
  357. def test_counters(self):
  358. self.assertEqual(
  359. {'axfr_running': 0, 'ixfr_running': 0,
  360. 'socket': {'unixdomain': {'open': 1, 'close': 1}},
  361. 'zones': {'_SERVER_': {'notifyoutv4': 1,
  362. 'notifyoutv6': 1,
  363. 'xfrrej': 1, 'xfrreqdone': 1},
  364. TEST_ZONE_NAME_STR: {'notifyoutv4': 1,
  365. 'notifyoutv6': 1,
  366. 'xfrrej': 1,
  367. 'xfrreqdone': 1}}},
  368. self.xfrout_server.get_counters())
  369. if __name__== "__main__":
  370. unittest.main()