From 9c4860049d692d85a6d3862c685f78f6287803cf Mon Sep 17 00:00:00 2001 From: ranl Date: Sun, 29 Dec 2013 12:18:59 +0200 Subject: [PATCH] activemq nagios plugin --- nagios/check_activemq.py | 268 +++++++++++++++++++++++++++++ zabbix/scripts/activemq-monitor.py | 44 ++--- 2 files changed, 290 insertions(+), 22 deletions(-) create mode 100755 nagios/check_activemq.py diff --git a/nagios/check_activemq.py b/nagios/check_activemq.py new file mode 100755 index 0000000..904e39c --- /dev/null +++ b/nagios/check_activemq.py @@ -0,0 +1,268 @@ +#!/usr/bin/env python + +''' +Monitor ActiveMQ server via its http web interface +''' + +from HTMLParser import HTMLParser +from optparse import OptionParser +import xml.etree.ElementTree as ET +import json +import urllib2 +import urllib + +EXIT_CODE = { + 'OK': 0, + 'WARN': 1, + 'CRIT': 2, + 'UNKNOWN': 3, +} + +def prepareOpts(): + ''' + Parse options from the shell + ''' + + cmds = { + 'queues' : 'notify if there is a least one queue with no consumers (--exlcude)', + 'consumer': 'notify if the specific consumers does not consume the queues (--queues, --client)', + } + def err( string ): + print 'Error: {0}'.format( string ) + parser.print_help() + print __doc__ + exit(1) + + parser = OptionParser() + parser.add_option('-H', '--server', dest='server', type='string', help='ActiveMQ fqdn or ip', default='localhost') + parser.add_option('-U', '--user', dest='user', type='string', help='http username', default=None) + parser.add_option('-P', '--password', dest='password', type='string', help='http password', default=None) + parser.add_option('-p', '--port', dest='port', type='int', help='ActiveMQ web interface port', default=8161) + parser.add_option('-t', '--timeout', dest='timeout', type='float', help='how many seconds to wait for each http request', default=5) + parser.add_option('-T', '--type', dest='type', type='choice', choices=cmds.keys(), help='what to check: {0}'.format(cmds.keys()) ) + parser.add_option('-e', '--exclude', dest='exclude', type='string', help='csv list of queues to exclude (implies -T queues)', default=None ) + parser.add_option('-q', '--queues', dest='queues', type='string', help='csv list of queues (implies -T consumer)', default=None) + parser.add_option('-c', '--client', dest='client', type='string', help='the client prefix to search (implies -T consumer)', default=None ) + (opts, args) = parser.parse_args() + + kargs = {} + + if opts.user is None and opts.password is not None: + err('missing -P') + elif opts.password is None and opts.user is not None: + err('missing -U') + + if not opts.type: + err('missing -T') + elif opts.type == 'consumer': + if opts.client is None: + err('missing -c') + if opts.queues is None: + err('missing -q') + else: + kargs.update({'queues': opts.queues.split(',')}) + kargs.update({'client': opts.client}) + elif opts.exclude is not None: + kargs.update({'exclude': opts.exclude.split(',')}) + + + return (opts, kargs) + +class AmqException(Exception): + def __init__(self, msg, xcode): + self.msg = msg + self.xcode = xcode + def __str__(self): + return self.msg + def getXcode(self): + return self.xcode + def getMsg(self): + return self.msg + +class ConsumerHTMLParser(HTMLParser): + ''' + Parse the consumers id from http://url/admin/queueConsumers.jsp?JMSDestination=QUEUENAME + ''' + + consumers = [] + table = False + body = False + tr = False + td = False + a = False + + def reset_vars(self): + self.consumers = [] + self.table = False + self.body = False + self.tr = False + self.td = False + self.a = False + + def handle_starttag(self, tag, attrs): + if self.td and tag == 'a': + self.a = True + elif self.tr and tag == 'td': + self.td = True + elif self.body and tag == 'tr': + self.tr = True + elif self.table and tag == 'tbody': + self.body = True + elif tag == 'table': + self.table = ('id', 'messages') in attrs + + def handle_data(self, data): + if self.a: + if not data in self.consumers: + self.consumers.append( data ) + self.a = False + self.td = False + self.tr = False + + def get_consumers(self): + return self.consumers + +class ActivemqMonitor(): + ''' + Monitor ActiveMQ via http web interface + ''' + + def __init__(self, server, port, timeout, user=None, password=None, realm='ActiveMQRealm'): + self.url = 'http://{0}:{1}'.format(server, port) + self.server = server + self.port = port + self.timeout = timeout + self.user = user + self.password = password + self.realm = realm + if user is not None and password is not None: + urllib2.install_opener( + self._auth( + self.url, self.user, self.password, self.realm + ) + ) + + def _auth(self, uri, user, password, realm): + ''' + returns a authentication handler. + ''' + + basic = urllib2.HTTPBasicAuthHandler() + basic.add_password( + realm=realm, uri=uri, user=user, passwd=password + ) + digest = urllib2.HTTPDigestAuthHandler() + digest.add_password( + realm=realm, uri=uri, user=user, passwd=password + ) + + return urllib2.build_opener(basic, digest) + + def _wget(self, url): + ''' + create the http request to AMQ web UI + ''' + + try: + ret = urllib2.urlopen(url, timeout=self.timeout).read() + except urllib2.URLError: + raise AmqException( + 'UNKNOWN: Could not create http request to ActiveMQ', + EXIT_CODE['UNKNOWN'] + ) + + return ret + + def _getQueueConsumers(self, queue, parser): + ''' + Get the parsed data of the queue + ''' + + url = '{0}/admin/queueConsumers.jsp?{1}'.format( + self.url, + urllib.urlencode( { 'JMSDestination': queue } ), + ) + parser.reset_vars() + parser.feed( self._wget(url) ) + return parser.get_consumers() + + def _eval_queues(self, res, opts): + if res: + return { + 'msg': 'CRIT: the following queues are not consumed {0}'.format(','.join(res)), + 'exit': EXIT_CODE['CRIT'] + } + else: + return { + 'msg': 'OK: All the queues are consumed', + 'exit': EXIT_CODE['OK'] + } + + def _eval_consumer(self, res, opts): + if res: + return { + 'msg': 'CRIT: the following queues are not consumed by {0} {1}'.format( + opts.client, ','.join(res) + ), + 'exit': EXIT_CODE['CRIT'] + } + else: + return { + 'msg': 'OK: {0} is consuming all of its queues'.format(opts.client), + 'exit': EXIT_CODE['OK'] + } + + def queues(self, exclude=[]): + ''' + return all the queues with zero consumers + ''' + errors = [] + url = '{0}/admin/xml/queues.jsp'.format(self.url, ) + html = self._wget(url) + for q in ET.fromstring( html ).findall('queue'): + if q.get('name') not in exclude and int(q.find('stats').get('consumerCount')) <= 0: + errors.append(q.get('name')) + + return errors + + def consumer(self, client, queues): + ''' + check if the clientid is configured as a subscriber on the queue + ''' + + missing = list(queues) + parser = ConsumerHTMLParser() + for queue in queues: + for consumer in self._getQueueConsumers(queue, parser): + if client in consumer: + missing.remove(queue) + break + + return missing + + @staticmethod + def main(): + ''' + Main function + ''' + (opts, kargs) = prepareOpts() + amq = ActivemqMonitor( + opts.server, opts.port, opts.timeout, opts.user, opts.password + ) + + method = getattr(amq, opts.type) + try: + res = method(**kargs) + except AmqException as e: + print e.getMsg() + exit(e.getXcode()) + + eval_method = getattr( + amq, '_eval_{0}'.format(opts.type) + ) + ret = eval_method(res, opts) + print ret['msg'] + exit(ret['exit']) + +if __name__ == '__main__': + ActivemqMonitor.main() diff --git a/zabbix/scripts/activemq-monitor.py b/zabbix/scripts/activemq-monitor.py index 6762e14..9f25cc2 100755 --- a/zabbix/scripts/activemq-monitor.py +++ b/zabbix/scripts/activemq-monitor.py @@ -60,36 +60,36 @@ class ConsumerHTMLParser(HTMLParser): a = False def reset_vars(self): - ConsumerHTMLParser.consumers = [] - ConsumerHTMLParser.table = False - ConsumerHTMLParser.body = False - ConsumerHTMLParser.tr = False - ConsumerHTMLParser.td = False - ConsumerHTMLParser.a = False + self.consumers = [] + self.table = False + self.body = False + self.tr = False + self.td = False + self.a = False def handle_starttag(self, tag, attrs): - if ConsumerHTMLParser.td and tag == 'a': - ConsumerHTMLParser.a = True - elif ConsumerHTMLParser.tr and tag == 'td': - ConsumerHTMLParser.td = True - elif ConsumerHTMLParser.body and tag == 'tr': - ConsumerHTMLParser.tr = True - elif ConsumerHTMLParser.table and tag == 'tbody': - ConsumerHTMLParser.body = True + if self.td and tag == 'a': + self.a = True + elif self.tr and tag == 'td': + self.td = True + elif self.body and tag == 'tr': + self.tr = True + elif self.table and tag == 'tbody': + self.body = True elif tag == 'table': - ConsumerHTMLParser.table = ('id', 'messages') in attrs + self.table = ('id', 'messages') in attrs def handle_data(self, data): - if ConsumerHTMLParser.a: + if self.a: tmp = data.split('-')[0] - if not tmp in ConsumerHTMLParser.consumers: - ConsumerHTMLParser.consumers.append( tmp ) - ConsumerHTMLParser.a = False - ConsumerHTMLParser.td = False - ConsumerHTMLParser.tr = False + if not tmp in self.consumers: + self.consumers.append( tmp ) + self.a = False + self.td = False + self.tr = False def get_consumers(self): - return ConsumerHTMLParser.consumers + return self.consumers class ActivemqMonitor():