blob: 3941291fcabe78c9c10d9c980de5e456ccd3e600 [file] [log] [blame]
#! /usr/bin/env python
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import unittest
import socket
import sys
from StringIO import StringIO
from check_zookeeper import ZooKeeperServer, NagiosHandler, CactiHandler, GangliaHandler
ZK_MNTR_OUTPUT = """zk_version\t3.4.0--1, built on 06/19/2010 15:07 GMT
zk_avg_latency\t1
zk_max_latency\t132
zk_min_latency\t0
zk_packets_received\t640
zk_packets_sent\t639
zk_outstanding_requests\t0
zk_server_state\tfollower
zk_znode_count\t4
zk_watch_count\t0
zk_ephemerals_count\t0
zk_approximate_data_size\t27
zk_open_file_descriptor_count\t22
zk_max_file_descriptor_count\t1024
"""
ZK_MNTR_OUTPUT_WITH_BROKEN_LINES = """zk_version\t3.4.0
zk_avg_latency\t23
broken-line
"""
ZK_STAT_OUTPUT = """Zookeeper version: 3.3.0-943314, built on 05/11/2010 22:20 GMT
Clients:
/0:0:0:0:0:0:0:1:34564[0](queued=0,recved=1,sent=0)
Latency min/avg/max: 0/40/121
Received: 11
Sent: 10
Outstanding: 0
Zxid: 0x700000003
Mode: follower
Node count: 4
"""
class SocketMock(object):
def __init__(self):
self.sent = []
def settimeout(self, timeout):
self.timeout = timeout
def connect(self, address):
self.address = address
def send(self, data):
self.sent.append(data)
return len(data)
def recv(self, size):
return ZK_MNTR_OUTPUT[:size]
def close(self): pass
class ZK33xSocketMock(SocketMock):
def __init__(self):
SocketMock.__init__(self)
self.got_stat_cmd = False
def recv(self, size):
if 'stat' in self.sent:
return ZK_STAT_OUTPUT[:size]
else:
return ''
class UnableToConnectSocketMock(SocketMock):
def connect(self, _):
raise socket.error('[Errno 111] Connection refused')
def create_server_mock(socket_class):
class ZooKeeperServerMock(ZooKeeperServer):
def _create_socket(self):
return socket_class()
return ZooKeeperServerMock()
class TestCheckZookeeper(unittest.TestCase):
def setUp(self):
self.zk = ZooKeeperServer()
def test_parse_valid_line(self):
key, value = self.zk._parse_line('something\t5')
self.assertEqual(key, 'something')
self.assertEqual(value, 5)
def test_parse_line_raises_exception_on_invalid_output(self):
invalid_lines = ['something', '', 'a\tb\tc', '\t1']
for line in invalid_lines:
self.assertRaises(ValueError, self.zk._parse_line, line)
def test_parser_on_valid_output(self):
data = self.zk._parse(ZK_MNTR_OUTPUT)
self.assertEqual(len(data), 14)
self.assertEqual(data['zk_znode_count'], 4)
def test_parse_should_ignore_invalid_lines(self):
data = self.zk._parse(ZK_MNTR_OUTPUT_WITH_BROKEN_LINES)
self.assertEqual(len(data), 2)
def test_parse_stat_valid_output(self):
data = self.zk._parse_stat(ZK_STAT_OUTPUT)
result = {
'zk_version' : '3.3.0-943314, built on 05/11/2010 22:20 GMT',
'zk_min_latency' : 0,
'zk_avg_latency' : 40,
'zk_max_latency' : 121,
'zk_packets_received': 11,
'zk_packets_sent': 10,
'zk_server_state': 'follower',
'zk_znode_count': 4
}
for k, v in result.iteritems():
self.assertEqual(v, data[k])
def test_recv_valid_output(self):
zk = create_server_mock(SocketMock)
data = zk.get_stats()
self.assertEqual(len(data), 14)
self.assertEqual(data['zk_znode_count'], 4)
def test_socket_unable_to_connect(self):
zk = create_server_mock(UnableToConnectSocketMock)
self.assertRaises(socket.error, zk.get_stats)
def test_use_stat_cmd_if_mntr_is_not_available(self):
zk = create_server_mock(ZK33xSocketMock)
data = zk.get_stats()
self.assertEqual(data['zk_version'], '3.3.0-943314, built on 05/11/2010 22:20 GMT')
class HandlerTestCase(unittest.TestCase):
def setUp(self):
try:
sys._stdout
except:
sys._stdout = sys.stdout
sys.stdout = StringIO()
def tearDown(self):
sys.stdout = sys._stdout
def output(self):
sys.stdout.seek(0)
return sys.stdout.read()
class TestNagiosHandler(HandlerTestCase):
def _analyze(self, w, c, k, stats):
class Opts(object):
warning = w
critical = c
key = k
return NagiosHandler().analyze(Opts(), {'localhost:2181':stats})
def test_ok_status(self):
r = self._analyze(10, 20, 'a', {'a': 5})
self.assertEqual(r, 0)
self.assertEqual(self.output(), 'Ok "a"!|localhost:2181=5;10;20\n')
r = self._analyze(20, 10, 'a', {'a': 30})
self.assertEqual(r, 0)
def test_warning_status(self):
r = self._analyze(10, 20, 'a', {'a': 15})
self.assertEqual(r, 1)
self.assertEqual(self.output(),
'Warning "a" localhost:2181!|localhost:2181=15;10;20\n')
r = self._analyze(20, 10, 'a', {'a': 15})
self.assertEqual(r, 1)
def test_critical_status(self):
r = self._analyze(10, 20, 'a', {'a': 30})
self.assertEqual(r, 2)
self.assertEqual(self.output(),
'Critical "a" localhost:2181!|localhost:2181=30;10;20\n')
r = self._analyze(20, 10, 'a', {'a': 5})
self.assertEqual(r, 2)
def test_check_a_specific_key_on_all_hosts(self):
class Opts(object):
warning = 10
critical = 20
key = 'latency'
r = NagiosHandler().analyze(Opts(), {
's1:2181': {'latency': 5},
's2:2181': {'latency': 15},
's3:2181': {'latency': 35},
})
self.assertEqual(r, 2)
self.assertEqual(self.output(),
'Critical "latency" s3:2181!|s1:2181=5;10;20 '\
's3:2181=35;10;20 s2:2181=15;10;20\n')
class TestCactiHandler(HandlerTestCase):
class Opts(object):
key = 'a'
leader = False
def __init__(self, leader=False):
self.leader = leader
def test_output_values_for_all_hosts(self):
r = CactiHandler().analyze(TestCactiHandler.Opts(), {
's1:2181':{'a':1},
's2:2181':{'a':2, 'b':3}
})
self.assertEqual(r, None)
self.assertEqual(self.output(), 's1_2181:1 s2_2181:2')
def test_output_single_value_for_leader(self):
r = CactiHandler().analyze(TestCactiHandler.Opts(leader=True), {
's1:2181': {'a':1, 'zk_server_state': 'leader'},
's2:2181': {'a':2}
})
self.assertEqual(r, 0)
self.assertEqual(self.output(), '1\n')
class TestGangliaHandler(unittest.TestCase):
class TestableGangliaHandler(GangliaHandler):
def __init__(self):
GangliaHandler.__init__(self)
self.cli_calls = []
def call(self, cli):
self.cli_calls.append(' '.join(cli))
def test_send_single_metric(self):
class Opts(object):
@property
def gmetric(self): return '/usr/bin/gmetric'
opts = Opts()
h = TestGangliaHandler.TestableGangliaHandler()
h.analyze(opts, {'localhost:2181':{'latency':10}})
cmd = "%s -n latency -v 10 -t uint32" % opts.gmetric
assert cmd in h.cli_calls
if __name__ == '__main__':
unittest.main()