test.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. #! /usr/bin/env python
  2. # Licensed to the Apache Software Foundation (ASF) under one
  3. # or more contributor license agreements. See the NOTICE file
  4. # distributed with this work for additional information
  5. # regarding copyright ownership. The ASF licenses this file
  6. # to you under the Apache License, Version 2.0 (the
  7. # "License"); you may not use this file except in compliance
  8. # with the License. You may obtain a copy of the License at
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import unittest
  16. import socket
  17. import sys
  18. from StringIO import StringIO
  19. from check_zookeeper import ZooKeeperServer, NagiosHandler, CactiHandler, GangliaHandler
  20. ZK_MNTR_OUTPUT = """zk_version\t3.4.0--1, built on 06/19/2010 15:07 GMT
  21. zk_avg_latency\t1
  22. zk_max_latency\t132
  23. zk_min_latency\t0
  24. zk_packets_received\t640
  25. zk_packets_sent\t639
  26. zk_outstanding_requests\t0
  27. zk_server_state\tfollower
  28. zk_znode_count\t4
  29. zk_watch_count\t0
  30. zk_ephemerals_count\t0
  31. zk_approximate_data_size\t27
  32. zk_open_file_descriptor_count\t22
  33. zk_max_file_descriptor_count\t1024
  34. """
  35. ZK_MNTR_OUTPUT_WITH_BROKEN_LINES = """zk_version\t3.4.0
  36. zk_avg_latency\t23
  37. broken-line
  38. """
  39. ZK_STAT_OUTPUT = """Zookeeper version: 3.3.0-943314, built on 05/11/2010 22:20 GMT
  40. Clients:
  41. /0:0:0:0:0:0:0:1:34564[0](queued=0,recved=1,sent=0)
  42. Latency min/avg/max: 0/40/121
  43. Received: 11
  44. Sent: 10
  45. Outstanding: 0
  46. Zxid: 0x700000003
  47. Mode: follower
  48. Node count: 4
  49. """
  50. class SocketMock(object):
  51. def __init__(self):
  52. self.sent = []
  53. def settimeout(self, timeout):
  54. self.timeout = timeout
  55. def connect(self, address):
  56. self.address = address
  57. def send(self, data):
  58. self.sent.append(data)
  59. return len(data)
  60. def recv(self, size):
  61. return ZK_MNTR_OUTPUT[:size]
  62. def close(self): pass
  63. class ZK33xSocketMock(SocketMock):
  64. def __init__(self):
  65. SocketMock.__init__(self)
  66. self.got_stat_cmd = False
  67. def recv(self, size):
  68. if 'stat' in self.sent:
  69. return ZK_STAT_OUTPUT[:size]
  70. else:
  71. return ''
  72. class UnableToConnectSocketMock(SocketMock):
  73. def connect(self, _):
  74. raise socket.error('[Errno 111] Connection refused')
  75. def create_server_mock(socket_class):
  76. class ZooKeeperServerMock(ZooKeeperServer):
  77. def _create_socket(self):
  78. return socket_class()
  79. return ZooKeeperServerMock()
  80. class TestCheckZookeeper(unittest.TestCase):
  81. def setUp(self):
  82. self.zk = ZooKeeperServer()
  83. def test_parse_valid_line(self):
  84. key, value = self.zk._parse_line('something\t5')
  85. self.assertEqual(key, 'something')
  86. self.assertEqual(value, 5)
  87. def test_parse_line_raises_exception_on_invalid_output(self):
  88. invalid_lines = ['something', '', 'a\tb\tc', '\t1']
  89. for line in invalid_lines:
  90. self.assertRaises(ValueError, self.zk._parse_line, line)
  91. def test_parser_on_valid_output(self):
  92. data = self.zk._parse(ZK_MNTR_OUTPUT)
  93. self.assertEqual(len(data), 14)
  94. self.assertEqual(data['zk_znode_count'], 4)
  95. def test_parse_should_ignore_invalid_lines(self):
  96. data = self.zk._parse(ZK_MNTR_OUTPUT_WITH_BROKEN_LINES)
  97. self.assertEqual(len(data), 2)
  98. def test_parse_stat_valid_output(self):
  99. data = self.zk._parse_stat(ZK_STAT_OUTPUT)
  100. result = {
  101. 'zk_version' : '3.3.0-943314, built on 05/11/2010 22:20 GMT',
  102. 'zk_min_latency' : 0,
  103. 'zk_avg_latency' : 40,
  104. 'zk_max_latency' : 121,
  105. 'zk_packets_received': 11,
  106. 'zk_packets_sent': 10,
  107. 'zk_server_state': 'follower',
  108. 'zk_znode_count': 4
  109. }
  110. for k, v in result.iteritems():
  111. self.assertEqual(v, data[k])
  112. def test_recv_valid_output(self):
  113. zk = create_server_mock(SocketMock)
  114. data = zk.get_stats()
  115. self.assertEqual(len(data), 14)
  116. self.assertEqual(data['zk_znode_count'], 4)
  117. def test_socket_unable_to_connect(self):
  118. zk = create_server_mock(UnableToConnectSocketMock)
  119. self.assertRaises(socket.error, zk.get_stats)
  120. def test_use_stat_cmd_if_mntr_is_not_available(self):
  121. zk = create_server_mock(ZK33xSocketMock)
  122. data = zk.get_stats()
  123. self.assertEqual(data['zk_version'], '3.3.0-943314, built on 05/11/2010 22:20 GMT')
  124. class HandlerTestCase(unittest.TestCase):
  125. def setUp(self):
  126. try:
  127. sys._stdout
  128. except:
  129. sys._stdout = sys.stdout
  130. sys.stdout = StringIO()
  131. def tearDown(self):
  132. sys.stdout = sys._stdout
  133. def output(self):
  134. sys.stdout.seek(0)
  135. return sys.stdout.read()
  136. class TestNagiosHandler(HandlerTestCase):
  137. def _analyze(self, w, c, k, stats):
  138. class Opts(object):
  139. warning = w
  140. critical = c
  141. key = k
  142. return NagiosHandler().analyze(Opts(), {'localhost:2181':stats})
  143. def test_ok_status(self):
  144. r = self._analyze(10, 20, 'a', {'a': 5})
  145. self.assertEqual(r, 0)
  146. self.assertEqual(self.output(), 'Ok "a"!|localhost:2181=5;10;20\n')
  147. r = self._analyze(20, 10, 'a', {'a': 30})
  148. self.assertEqual(r, 0)
  149. def test_warning_status(self):
  150. r = self._analyze(10, 20, 'a', {'a': 15})
  151. self.assertEqual(r, 1)
  152. self.assertEqual(self.output(),
  153. 'Warning "a" localhost:2181!|localhost:2181=15;10;20\n')
  154. r = self._analyze(20, 10, 'a', {'a': 15})
  155. self.assertEqual(r, 1)
  156. def test_critical_status(self):
  157. r = self._analyze(10, 20, 'a', {'a': 30})
  158. self.assertEqual(r, 2)
  159. self.assertEqual(self.output(),
  160. 'Critical "a" localhost:2181!|localhost:2181=30;10;20\n')
  161. r = self._analyze(20, 10, 'a', {'a': 5})
  162. self.assertEqual(r, 2)
  163. def test_check_a_specific_key_on_all_hosts(self):
  164. class Opts(object):
  165. warning = 10
  166. critical = 20
  167. key = 'latency'
  168. r = NagiosHandler().analyze(Opts(), {
  169. 's1:2181': {'latency': 5},
  170. 's2:2181': {'latency': 15},
  171. 's3:2181': {'latency': 35},
  172. })
  173. self.assertEqual(r, 2)
  174. self.assertEqual(self.output(),
  175. 'Critical "latency" s3:2181!|s1:2181=5;10;20 '\
  176. 's3:2181=35;10;20 s2:2181=15;10;20\n')
  177. class TestCactiHandler(HandlerTestCase):
  178. class Opts(object):
  179. key = 'a'
  180. leader = False
  181. def __init__(self, leader=False):
  182. self.leader = leader
  183. def test_output_values_for_all_hosts(self):
  184. r = CactiHandler().analyze(TestCactiHandler.Opts(), {
  185. 's1:2181':{'a':1},
  186. 's2:2181':{'a':2, 'b':3}
  187. })
  188. self.assertEqual(r, None)
  189. self.assertEqual(self.output(), 's1_2181:1 s2_2181:2')
  190. def test_output_single_value_for_leader(self):
  191. r = CactiHandler().analyze(TestCactiHandler.Opts(leader=True), {
  192. 's1:2181': {'a':1, 'zk_server_state': 'leader'},
  193. 's2:2181': {'a':2}
  194. })
  195. self.assertEqual(r, 0)
  196. self.assertEqual(self.output(), '1\n')
  197. class TestGangliaHandler(unittest.TestCase):
  198. class TestableGangliaHandler(GangliaHandler):
  199. def __init__(self):
  200. GangliaHandler.__init__(self)
  201. self.cli_calls = []
  202. def call(self, cli):
  203. self.cli_calls.append(' '.join(cli))
  204. def test_send_single_metric(self):
  205. class Opts(object):
  206. @property
  207. def gmetric(self): return '/usr/bin/gmetric'
  208. opts = Opts()
  209. h = TestGangliaHandler.TestableGangliaHandler()
  210. h.analyze(opts, {'localhost:2181':{'latency':10}})
  211. cmd = "%s -n latency -v 10 -t uint32" % opts.gmetric
  212. assert cmd in h.cli_calls
  213. if __name__ == '__main__':
  214. unittest.main()