From 0ced934858e4ff861feebf37c5301752a0986e75 Mon Sep 17 00:00:00 2001 From: Sven Velt Date: Tue, 9 Feb 2010 23:13:03 +0100 Subject: [PATCH] Rework http2nagios (and xml2nagios) and add SSL "nagixsc_http2nagios.py" does not call "nagixsc_xml2nagios.py" as a subprocess anymore. Instead there are 2 function "dict2out_passive" for passive checks and "dict2out_checkresult" for check result creation. And... nagixsc_http2nagios now also supports SSL so noone has to send his authentication data unencrypted over the network :) --- nagixsc.py | 84 ++++++++++++++++++++++++++++++++++ nagixsc_http2nagios.py | 50 ++++++++++---------- nagixsc_xml2nagios.py | 83 ++++++--------------------------- sample-configs/http2nagios.cfg | 12 ++++- 4 files changed, 135 insertions(+), 94 deletions(-) diff --git a/nagixsc.py b/nagixsc.py index 21d2b0c..191d26e 100644 --- a/nagixsc.py +++ b/nagixsc.py @@ -4,8 +4,11 @@ import SocketServer import base64 import datetime import libxml2 +import os +import random import shlex import socket +import string import subprocess import sys @@ -119,6 +122,83 @@ def conf2dict(config, opt_host=None, opt_service=None): return checks +############################################################################## + +def dict2out_passive(checks, xmltimestamp, opt_pipe, opt_verb=0): + FORMAT_HOST = '[%s] PROCESS_HOST_CHECK_RESULT;%s;%s;%s' + FORMAT_SERVICE = '[%s] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%s;%s' + count_services = 0 + now = datetime.datetime.now().strftime('%s') + + # Prepare + if opt_verb <= 2: + pipe = open(opt_pipe, "w") + else: + pipe = None + + # Output + for check in checks: + count_services += 1 + if check.has_key('timestamp'): + timestamp = check['timestamp'] + else: + timestamp = xmltimestamp + count_services += 1 + + if check['service_description'] == None or check['service_description'] == '': + # Host check + line = FORMAT_HOST % (now, check['host_name'], check['returncode'], check['output'].replace('\n', '\\n')) + else: + # Service check + line = FORMAT_SERVICE % (now, check['host_name'], check['service_description'], check['returncode'], check['output'].replace('\n', '\\n')) + + if pipe: + pipe.write(line + '\n') + debug(2, opt_verb, line) + + # Close + if pipe: + pipe.close() + else: + print "Passive check results NOT written to Nagios pipe due to -vvv!" + + return count_services + + +def dict2out_checkresult(checks, xmltimestamp, opt_checkresultdir, opt_verb): + count_services = 0 + count_failed = 0 + list_failed = [] + chars = string.letters + string.digits + ctimestamp = datetime.datetime.now().ctime() + + for check in checks: + count_services += 1 + if check.has_key('timestamp'): + timestamp = check['timestamp'] + else: + timestamp = xmltimestamp + + filename = os.path.join(opt_checkresultdir, 'c' + ''.join([random.choice(chars) for i in range(6)])) + try: + crfile = open(filename, "w") + if check['service_description'] == None or check['service_description'] == '': + # Host check + crfile.write('### Active Check Result File ###\nfile_time=%s\n\n### Nagios Service Check Result ###\n# Time: %s\nhost_name=%s\ncheck_type=0\ncheck_options=0\nscheduled_check=1\nreschedule_check=1\nlatency=0.0\nstart_time=%s.00\nfinish_time=%s.05\nearly_timeout=0\nexited_ok=1\nreturn_code=%s\noutput=%s\n' % (timestamp, ctimestamp, check['host_name'], timestamp, timestamp, check['returncode'], check['output'].replace('\n', '\\n') ) ) + else: + # Service check + crfile.write('### Active Check Result File ###\nfile_time=%s\n\n### Nagios Service Check Result ###\n# Time: %s\nhost_name=%s\nservice_description=%s\ncheck_type=0\ncheck_options=0\nscheduled_check=1\nreschedule_check=1\nlatency=0.0\nstart_time=%s.00\nfinish_time=%s.05\nearly_timeout=0\nexited_ok=1\nreturn_code=%s\noutput=%s\n' % (timestamp, ctimestamp, check['host_name'], check['service_description'], timestamp, timestamp, check['returncode'], check['output'].replace('\n', '\\n') ) ) + crfile.close() + + # Create OK file + open(filename + '.ok', 'w').close() + except: + count_failed += 1 + list_failed.append([filename, check['host_name'], check['service_description']]) + + return (count_services, count_failed, list_failed) + + ############################################################################## def read_xml(options): @@ -150,6 +230,10 @@ def read_xml(options): return doc +def read_xml_from_string(content): + return libxml2.parseDoc(content) + + ############################################################################## def xml_check_version(xmldoc): diff --git a/nagixsc_http2nagios.py b/nagixsc_http2nagios.py index 085609d..4c23fe5 100755 --- a/nagixsc_http2nagios.py +++ b/nagixsc_http2nagios.py @@ -1,13 +1,11 @@ #!/usr/bin/python -import BaseHTTPServer import ConfigParser import base64 import cgi import optparse import os import re -import subprocess import sys try: @@ -17,6 +15,10 @@ except ImportError: ############################################################################## +from nagixsc import * + +############################################################################## + parser = optparse.OptionParser() parser.add_option('-c', '', dest='cfgfile', help='Config file') @@ -37,9 +39,11 @@ config = {} try: config['ip'] = cfgread.get('server', 'ip') config['port'] = cfgread.getint('server', 'port') + config['ssl'] = cfgread.getboolean('server', 'ssl') + config['cert'] = cfgread.get('server', 'sslcert') config['max_xml_file_size'] = cfgread.get('server', 'max_xml_file_size') - config['xml2nagios_cmdline'] = cfgread.get('server', 'xml2nagios_cmdline') + config['checkresultdir'] = cfgread.get('mode_checkresult', 'dir') except ConfigParser.NoOptionError, e: print 'Config file error: %s ' % e @@ -51,7 +55,7 @@ for u in cfgread.options('users'): ############################################################################## -class HTTP2NagiosHandler(BaseHTTPServer.BaseHTTPRequestHandler): +class HTTP2NagiosHandler(MyHTTPRequestHandler): def http_error(self, code, output): self.send_response(code) @@ -76,8 +80,6 @@ class HTTP2NagiosHandler(BaseHTTPServer.BaseHTTPRequestHandler): def do_POST(self): - cmdline = config['xml2nagios_cmdline'] - # Check Basic Auth try: authdata = base64.b64decode(self.headers['Authorization'].split(' ')[1]).split(':') @@ -97,23 +99,19 @@ class HTTP2NagiosHandler(BaseHTTPServer.BaseHTTPRequestHandler): xmltext = query.get('xmlfile')[0] if len(xmltext) > 0: - try: - cmd = subprocess.Popen(cmdline.split(' '), stdin=subprocess.PIPE, stdout=subprocess.PIPE) - output = cmd.communicate(xmltext)[0].rstrip() - retcode = cmd.returncode - - if retcode == 0: - self.send_response(200) - self.send_header('Content-Type', 'text/plain') - self.end_headers() - self.wfile.write(output) - return - else: - http_error(500, output) - return - - except OSError: - http_error(500, 'Nag(IX)SC - Could not execute "%s"' % cmdline) + doc = read_xml_from_string(xmltext) + checks = xml_to_dict(doc) + + (count_services, count_failed, list_failed) = dict2out_checkresult(checks, xml_get_timestamp(doc), config['checkresultdir'], 0) + + if count_failed < count_services: + self.send_response(200) + self.send_header('Content-Type', 'text/plain') + self.end_headers() + self.wfile.write('Wrote %s check results, %s failed' % (count_services, count_failed)) + return + else: + http_error(500, 'Could not write all %s check results' % count_services) return else: @@ -123,8 +121,12 @@ class HTTP2NagiosHandler(BaseHTTPServer.BaseHTTPRequestHandler): def main(): + if config['ssl'] and not os.path.isfile(config['cert']): + print 'SSL certificate "%s" not found!' % config['cert'] + sys.exit(127) + + server = MyHTTPServer((config['ip'], config['port']), HTTP2NagiosHandler, ssl=config['ssl'], sslpemfile=config['cert']) try: - server = BaseHTTPServer.HTTPServer((config['ip'], config['port']), HTTP2NagiosHandler) server.serve_forever() except: server.socket.close() diff --git a/nagixsc_xml2nagios.py b/nagixsc_xml2nagios.py index 58d5c07..96b6a52 100755 --- a/nagixsc_xml2nagios.py +++ b/nagixsc_xml2nagios.py @@ -5,8 +5,6 @@ import datetime import libxml2 import optparse import os -import random -import string import sys NAGIOSCMDs = [ '/usr/local/nagios/var/rw/nagios.cmd', '/var/lib/nagios3/rw/nagios.cmd', ] @@ -52,7 +50,7 @@ from nagixsc import * ############################################################################## if options.mode not in MODEs: - print 'Not an allowed mode "%s" - allowed are: %s' % (options.mode, ", ".join(MODEs)) + print 'Not an allowed mode "%s" - allowed are: "%s"' % (options.mode, '", "'.join(MODEs)) sys.exit(127) # Check command line options wrt mode @@ -141,39 +139,13 @@ for check in checks: # Next steps depend on mode, output results # MODE: passive if options.mode == 'passive' or options.mode == 'passive_check': - count_services = 0 - # Prepare - if options.verb <= 2: - pipe = open(options.pipe, "w") - else: - pipe = None - - # Output - for check in checks: - count_services += 1 - if check['service_description'] == None or check['service_description'] == '': - # Host check - line = '[%s] PROCESS_HOST_CHECK_RESULT;%s;%s;%s' % (now, check['host_name'], check['returncode'], check['output'].replace('\n', '\\n')) - else: - # Service check - line = '[%s] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%s;%s' % (now, check['host_name'], check['service_description'], check['returncode'], check['output'].replace('\n', '\\n')) - - if pipe: - pipe.write(line + '\n') - debug(2, options.verb, '%s / %s: %s - "%s"' % (check['host_name'], check['service_description'], check['returncode'], check['output'].replace('\n', '\\n'))) - debug(3, options.verb, line) - - # Close - if pipe: - pipe.close() - else: - print "Passive check results NOT written to Nagios pipe due to -vvv!" + count_services = dict2out_passive(checks, xml_get_timestamp(doc), options.pipe, options.verb) # Return/Exit as a Nagios Plugin if called with mode 'passive_check' if options.mode == 'passive_check': returncode = 0 returnstring = 'OK' - output = '' + output = '%s check results written which are %s seconds old' % (count_services, (now-filetimestamp)) if options.markold: if (now - filetimestamp) > options.seconds: @@ -181,45 +153,23 @@ if options.mode == 'passive' or options.mode == 'passive_check': output = '%s check results written, which are %s(>%s) seconds old' % (count_services, (now-filetimestamp), options.seconds) returncode = 1 - if not output: - output = '%s check results written which are %s seconds old' % (count_services, (now-filetimestamp)) - print 'Nag(ix)SC %s - %s' % (returnstring, output) sys.exit(returncode) -# MODE: checkresult -elif options.mode == 'checkresult' or options.mode == 'checkresult_check': - count_services = 0 - count_failed = 0 +# MODE: checkresult: "checkresult", "checkresult_check" +elif options.mode.startswith('checkresult'): + (count_services, count_failed, list_failed) = dict2out_checkresult(checks, xml_get_timestamp(doc), options.checkresultdir, options.verb) - chars = string.letters + string.digits + if list_failed and options.mode == 'checkresult': + for entry in list_failed: + print 'Could not write checkresult files "%s(.ok)" for "%s"/"%s"!' % (entry[0], entry[1], entry[2]) - for check in checks: - count_services += 1 - if check.has_key('timestamp'): - timestamp = check['timestamp'] + if count_failed == 0: + sys.exit(0) else: - timestamp = xml_get_timestamp(xmldoc) - - filename = os.path.join(options.checkresultdir, 'c' + ''.join([random.choice(chars) for i in range(6)])) - try: - crfile = open(filename, "w") - if check['service_description'] == None or check['service_description'] == '': - # Host check - crfile.write('### Active Check Result File ###\nfile_time=%s\n\n### Nagios Service Check Result ###\n# Time: %s\nhost_name=%s\ncheck_type=0\ncheck_options=0\nscheduled_check=1\nreschedule_check=1\nlatency=0.0\nstart_time=%s.00\nfinish_time=%s.05\nearly_timeout=0\nexited_ok=1\nreturn_code=%s\noutput=%s\n' % (timestamp, datetime.datetime.now().ctime(), check['host_name'], timestamp, timestamp, check['returncode'], check['output'].replace('\n', '\\n') ) ) - else: - # Service check - crfile.write('### Active Check Result File ###\nfile_time=%s\n\n### Nagios Service Check Result ###\n# Time: %s\nhost_name=%s\nservice_description=%s\ncheck_type=0\ncheck_options=0\nscheduled_check=1\nreschedule_check=1\nlatency=0.0\nstart_time=%s.00\nfinish_time=%s.05\nearly_timeout=0\nexited_ok=1\nreturn_code=%s\noutput=%s\n' % (timestamp, datetime.datetime.now().ctime(), check['host_name'], check['service_description'], timestamp, timestamp, check['returncode'], check['output'].replace('\n', '\\n') ) ) - crfile.close() - - # Create OK file - open(filename + '.ok', 'w').close() - except: - count_failed += 1 - if options.mode == 'checkresult': - print 'Could not write checkresult files "%s(.ok)" for "%s"/"%s"!' % (filename, check['host_name'], check['service_description']) - - if options.mode == 'checkresult_check': + sys.exit(1) + + elif options.mode == 'checkresult_check': returnstring = '' output = '' if count_failed == 0: @@ -238,11 +188,6 @@ elif options.mode == 'checkresult' or options.mode == 'checkresult_check': print 'Nag(ix)SC %s - %s' % (returnstring, output) sys.exit(returncode) - if count_failed == 0: - sys.exit(0) - else: - sys.exit(1) - # MODE: active elif options.mode == 'active': diff --git a/sample-configs/http2nagios.cfg b/sample-configs/http2nagios.cfg index 2d7be30..e103b68 100644 --- a/sample-configs/http2nagios.cfg +++ b/sample-configs/http2nagios.cfg @@ -1,9 +1,19 @@ [server] ip: 0.0.0.0 port: 15667 +ssl: true +sslcert: server.pem max_xml_file_size: 102400 -xml2nagios_cmdline: ./nagixsc_xml2nagios.py -O passive -vvv -f - + +mode: checkresult + +[mode_passive] +pipe: /var/lib/nagios3/rw/nagios.cmd + +[mode_checkresult] +#dir: /var/lib/nagios3/spool/checkresults +dir: /tmp/cr [users] ; echo -n "Password" | md5sum - -- 2.30.2