Code

Rework http2nagios (and xml2nagios) and add SSL
authorSven Velt <sven@velt.de>
Tue, 9 Feb 2010 22:13:03 +0000 (23:13 +0100)
committerSven Velt <sven@velt.de>
Tue, 9 Feb 2010 22:13:03 +0000 (23:13 +0100)
"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
nagixsc_http2nagios.py
nagixsc_xml2nagios.py
sample-configs/http2nagios.cfg

index 21d2b0c1b9e5b5f46e400674e3e6ac65489b2891..191d26e2f0a1e90b9f6197f450a8c2f53cc61ac4 100644 (file)
@@ -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):
index 085609d8d7fb8e12d9e83ac9c23d6fa83ff0af84..4c23fe50d908cf549b6df4e25cfaab5a5bf82956 100755 (executable)
@@ -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()
index 58d5c0707e9bdb86a79bdc27ea52cdc7bbec7cbf..96b6a522b6090c3e9055c2ef7fca84b92b82cf54 100755 (executable)
@@ -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':
 
index 2d7be30108b773868f23686604c7100da7d357d0..e103b68b900b1990185f2d5e5ec5f21538c3454c 100644 (file)
@@ -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 -