author | Sven Velt <sven@velt.de> | |
Sun, 29 Nov 2009 19:26:08 +0000 (20:26 +0100) | ||
committer | Sven Velt <sven@velt.de> | |
Sun, 29 Nov 2009 19:26:08 +0000 (20:26 +0100) |
14 files changed:
.gitignore | [new file with mode: 0644] | patch | blob |
QUICKSTART.de.txt | [new file with mode: 0644] | patch | blob |
dummy_read_xml.py | [new file with mode: 0755] | patch | blob |
dummy_write_xml.py | [new file with mode: 0755] | patch | blob |
examples/nagixsc_debian.conf | [new file with mode: 0644] | patch | blob |
examples/nagixsc_usrlocal.conf | [new file with mode: 0644] | patch | blob |
nagixsc.py | [new file with mode: 0644] | patch | blob |
nagixsc_conf2xml.py | [new file with mode: 0755] | patch | blob |
nagixsc_xml2nagios.py | [new file with mode: 0755] | patch | blob |
xml/dtd_no_timestamp.xml | [new file with mode: 0644] | patch | blob |
xml/dtd_test.xml | [new file with mode: 0644] | patch | blob |
xml/nagixsc.dtd | [new file with mode: 0644] | patch | blob |
xml/nagixsc.xml | [new file with mode: 0644] | patch | blob |
xml/nagixsc.xsd | [new file with mode: 0644] | patch | blob |
diff --git a/.gitignore b/.gitignore
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+*.pyc
+.*.swp
+
diff --git a/QUICKSTART.de.txt b/QUICKSTART.de.txt
--- /dev/null
+++ b/QUICKSTART.de.txt
@@ -0,0 +1,178 @@
+==========
+00. Inhalt
+==========
+
+- 01. Auf dem zu überwachenden Rechner
+- 02. Auf dem Nagios
+
+
+
+
+
+====================================
+01. Auf dem zu überwachenden Rechner
+====================================
+
+-----------------------------------
+01.01. Erstellen einer "conf"-Datei
+-----------------------------------
+
+Vorbemerkung:
+Zur Zeit wird ein INI-Datei ähnliches Format verwendet. Dies kann sich aber in
+Zukunft noch ändern!
+
+Als Vorlage können die Dateien "examples/nagixsc_debian.conf" (Nagios mit
+Hilfe der Debian-Packages installiert) und "examples/nagixsc_usrlocal.conf"
+(selbst kompiliert nach /usr/local/nagios) dienen.
+
+Je Rechner, der überwacht werden soll, wird ein Abschnitt in eckigen Klammern
+angelegt. Der Abschnittsname entspricht dabei dem Nagios-Hostname. Innerhalb
+des Abschnitts können nun die Service Descriptions und die auszuführenden
+Commands definiert werden.
+
+Beginnt eine Option mit einem Unterstrich/Underscore/"_", so sind dies
+Nag(ix)SC-interne Definitionen und werden NICHT als Service angesehen. Bisher
+gibt es folgende Definitionen:
+
+- "_host_name": Überschreibt den Abschnittsnamen mit dem angegebenen
+ Hostnamen.
+
+Der Abschnitt "[nagixsc]" ist für zukünftige Erweiterungen reserviert!
+
+
+----------------------------------------------------
+01.02. Ausführen der Checks, Erstellen der XML-Datei
+----------------------------------------------------
+
+Zum ersten Test lässt man sich das XML am Besten an der Konsole ausgeben:
+
+% ./nagixsc_conf2xml.py -e plain -c examples/nagixsc.conf
+
+Zur Lesbarkeit wird das Encoding ausgeschaltet ("-e plain"). Dies ist nicht
+für den Produktivbetrieb gedacht!
+
+Man sollte hier bereits erkennen, wie Nag(ix)SC "intern tickt". Jetzt wäre der
+geeignete Zeitpunkt sich eine kleine, eigene Konfigurationsdatei zu schreiben.
+;-)
+
+Hat man diese (zur Not geht's natürlich auch mit den Beispielen weiter),
+erstellt man nun ein XML, welches zur Weiterverarbeitung geeignet ist:
+
+% ./nagixsc_conf2xml.py --c examples/nagixsc.conf -o /tmp/nagixsc.xml
+
+In der Praxis kann man diese XML-Datei auf einen Web-Server legen, damit sie
+direkt vom Nagios aus abrufbar ist.
+
+
+-----------------------------------------
+01.03. Kontrolle der erstellten XML-Datei
+-----------------------------------------
+
+Mit Hilfe des Skripts "dummy_read_xml.py" kann man sich den Inhalt einer
+Nag(ix)SC-XML-Datei ansehen:
+
+% ./dummy_read_xml.py -f /tmp/nagixsc.xml
+
+Der Inhalt wird zuerst blockweise, dann als formatiertes Python Dict
+ausgegeben.
+
+
+
+
+
+==================
+02. Auf dem Nagios
+==================
+
+--------------------------------
+02.01. Verschiedene Übergabewege
+--------------------------------
+
+Auf der Nagios-Seite gibt es im Moment 2 Wege an die Check-Ergebnisse
+heranzukommen: Entweder man sorgt manuell dafür, dass die XML-Datei irgendwo
+im Dateisystem lesbar ist ("-f"), oder man greift auf einen Web-Server zu
+("-u"). Die Beispiele gehen von einer lokalen Datei aus.
+
+Das Tool "nagixsc_xml2nagios.py" kennt 5 verschiedene Arbeitsweisen, angegeben
+durch den Schalter "-O":
+
+- "passive": Schreibt die Ergebnisse als passive Service Checks in die Nagios
+ Command Pipe. Es generiert keine Ausgaben und ist daher für einen
+ Aufruf durch Cron geeignet.
+
+- "passive_check": Schreibt ebenfalls passive Service Checks, die Rückgabe
+ erfolgt allerdings im Stil eines Nagios-Plugins (eine Zeile
+ Ausgabe, Exit-Code).
+
+- "checkresult": Schreibt die Ergebnisse aus dem XML in das Nagios
+ "checkresults"-Verzeichnis. Ebenfalls für Cron geeignet.
+
+- "checkresult_check": Siehe "checkresult", Verhalten wie ein Nagios-Plugin
+ (eine Zeile Ausgabe, Exit-Code)
+
+- "active": Durchsucht ein XML nach einer Host/Service-Kombination und beendet
+ sich wie ein Nagios-Plugin mit den gefundenen Daten.
+
+Beispielaufrufe:
+
+% ./nagixsc_xml2nagios.py -O passive -f /tmp/nagixsc.xml
+%
+
+% ./nagixsc_xml2nagios.py -O passive_check -f /tmp/nagixsc.xml
+Nag(ix)SC OK - Check results are 25077576 seconds old
+%
+
+% ./nagixsc_xml2nagios.py -O checkresult -f /tmp/nagixsc.xml
+%
+
+% ./nagixsc_xml2nagios.py -O checkresult_check -f /tmp/nagixsc.xml
+Nag(ix)SC OK - Wrote checkresult files for 6 services
+%
+
+./nagixsc_xml2nagios.py -O active -f /tmp/nagixsc.xml -D Disk_Root
+DISK OK - free space: / 2167 MB (22% inode=97%);| /=7353MB;8568;9044;0;9520
+%
+
+Für einen selbstkompilierten Nagios und die Debian-Packages können die
+"nagios.cmd" und das "checkresults"-Verzeichnis automatische gefunden werden.
+Sollte dies nicht klappen, kann man sie mit "-p" (Pipe) bzw. "-r" (Resultdir)
+übergeben werden.
+
+Will man "erstmal nur testen", so kann man "nagix_xm2nagios.py" mit "touch
+/tmp/nagixsc.cmd" und "-p /tmp/nagixsc.cmd" bzw. "mkdir /tmp/nagixsc.result"
+und "-r /tmp/nagixsc.result" aufrufen und die generierten External Commands
+bzw. Checkresult-Dateien ansehen.
+
+
+----------------------
+02.02. Veraltete Daten
+----------------------
+
+"nagixsc_xml2nagios.py" geht davon aus, dass die Daten im XML nicht älter als
+vier Stunden (14400 Sekunden) sind. Sollten sie es dennoch sein, werden sie
+automatisch mit dem Text "Nag(ix)SC: Check result is XX(>1440) seconds old"
+gekennzeichnet. Dieses Verhalten kann mit zwei Optionen beeinflusst werden:
+
+- "-s SEC" gibt an, nach wie vielen Sekunden Nag(ix)SC davon ausgegen soll,
+ dass die Daten im XML veraltet sind und mit dem oben genannten
+ Hinweis versehen werden sollen.
+
+- "-m" sorgt dafür, dass nicht nur der Text ergänzt, sondern dass der Check im
+ Nagios als UNKNOWN geführt wird.
+
+
+------------------------
+02.03. Weitere Parameter
+------------------------
+
+- "-S": Teste die XML-Datei vor der Verarbeitung gegen die angegebene
+ DTD-Datei. Eine DTD wird als "xml/nagixsc.dtd" mitgeliefert (ohne
+ Gewähr ;-)
+
+- "-H": Filtert die XML-Datei nach einem Hostnamen und gibt nur dessen Checks
+ zurück. Kann mit allen Varianten kombiniert werden.
+
+- "-D": Filtert die XML-Datei nach einer (Service) Description. In Kombination
+ mit "-H" wird nach einer Host/Service-Kombination gesucht. Ist kein
+ Host angegeben, wird der erste Host in der XML-Datei genommen.
+
diff --git a/dummy_read_xml.py b/dummy_read_xml.py
--- /dev/null
+++ b/dummy_read_xml.py
@@ -0,0 +1,72 @@
+#!/usr/bin/python
+
+#import base64
+import datetime
+import libxml2
+import optparse
+import sys
+
+parser = optparse.OptionParser()
+
+parser.add_option('-u', '', dest='url', help='URL of status file (xml)')
+parser.add_option('-f', '', dest='file', help='(Path and) file name of status file')
+parser.add_option('-s', '', dest='seconds', type='int', help='Maximum age in seconds of xml timestamp')
+parser.add_option('-m', '', action='store_true', dest='markold', help='Mark (Set state) of too old checks as UNKNOWN')
+parser.add_option('-v', '', action='count', dest='verb', help='Verbose output')
+
+parser.set_defaults(url=None)
+parser.set_defaults(file='nagixsc.xml')
+parser.set_defaults(seconds=14400)
+parser.set_defaults(markold=False)
+parser.set_defaults(verb=0)
+
+(options, args) = parser.parse_args()
+
+##############################################################################
+
+from nagixsc import *
+
+##############################################################################
+
+now = int(datetime.datetime.now().strftime('%s'))
+
+# Get URL or file
+if options.url != None:
+ import urllib2
+
+ response = urllib2.urlopen(options.url)
+ doc = libxml2.parseDoc(response.read())
+ response.close()
+else:
+ doc = libxml2.parseFile(options.file)
+
+
+# Check XML file basics
+(status, string) = xml_check_version(doc)
+debug(1, options.verb, string)
+if not status:
+ print string
+ sys.exit(127)
+
+
+# Get timestamp and check it
+filetimestamp = xml_get_timestamp(doc)
+if not filetimestamp:
+ print 'No timestamp found in XML file, exiting because of invalid XML data...'
+ sys.exit(127)
+
+timedelta = int(now) - int(filetimestamp)
+debug(1, options.verb, 'Age of XML file: %s seconds, max allowed: %s seconds' % (timedelta, options.seconds))
+
+
+# Put XML to Python dict
+checks = xml_to_dict(doc)
+
+
+# Loop over check results and output them
+for check in checks:
+ check = check_mark_outdated(check, now, options.seconds, options.markold)
+ print 'Host: %s\nService: %s\nRetCode: %s\nOutput: %r\nTimestamp: %s\n' % (check['host_name'], check['service_description'], check['returncode'], check['output'], check['timestamp'])
+
+import pprint
+pprint.pprint(checks)
diff --git a/dummy_write_xml.py b/dummy_write_xml.py
--- /dev/null
+++ b/dummy_write_xml.py
@@ -0,0 +1,48 @@
+#!/usr/bin/python
+
+import nagixsc
+import optparse
+
+parser = optparse.OptionParser()
+
+parser.add_option('-o', '', dest='outfile', help='(Path and) file name of status file, default STDOUT')
+parser.add_option('-v', '', action='count', dest='verb', help='Verbose output')
+
+parser.set_defaults(outfile='-')
+parser.set_defaults(verb=0)
+
+(options, args) = parser.parse_args()
+
+checks = [{'host_name': 'host1',
+ 'output': 'Nag(ix)SC: Check result is 24535725(>14400) seconds old - DISK CRITICAL - free space: /home 775 MB (1% inode=91%);| /home=67584MB;61523;64941;0;68359',
+ 'returncode': '2',
+ 'service_description': 'Disk_Home',
+ 'timestamp': 1234443420},
+ {'host_name': 'host1',
+ 'output': 'Nag(ix)SC: Check result is 24535725(>14400) seconds old - OK - load average: 0.00, 0.00, 0.00|load1=0.000;5.000;10.000;0; load5=0.000;5.000;10.000;0; load15=0.000;5.000;10.000;0;',
+ 'returncode': '0',
+ 'service_description': 'Load',
+ 'timestamp': 1234443420},
+ {'host_name': 'host2',
+ 'output': 'Nag(ix)SC: Check result is 24535735(>14400) seconds old - PROCS OK: 163 processes',
+ 'returncode': '0',
+ 'service_description': 'Procs_Total',
+ 'timestamp': 1234443410},
+ {'host_name': 'host2',
+ 'output': 'Nag(ix)SC: Check result is 24535715(>14400) seconds old - SWAP OK - 79% free (1492 MB out of 1906 MB) |swap=1492MB;953;476;0;1906',
+ 'returncode': '0',
+ 'service_description': 'Swap', },
+ {'host_name': 'host1',
+ 'output': 'Nag(ix)SC: Check result is 24535725(>14400) seconds old - DISK OK - free space: / 2167 MB (22% inode=97%);| /=7353MB;8568;9044;0;9520',
+ 'returncode': '0',
+ 'service_description': 'Disk_Root',
+ 'timestamp': 1234443420},
+ {'host_name': 'host2',
+ 'output': 'Nag(ix)SC: Check result is 24535735(>14400) seconds old - USERS WARNING - 11 users currently logged in |users=11;10;15;0\n3 root sessions\n8 non-root sessions',
+ 'returncode': '1',
+ 'service_description': 'Users',
+ 'timestamp': 1234443410}]
+
+xmldoc = nagixsc.xml_from_dict(checks)
+xmldoc.saveFile(options.outfile)
+
diff --git a/examples/nagixsc_debian.conf b/examples/nagixsc_debian.conf
--- /dev/null
@@ -0,0 +1,16 @@
+[nagixsc]
+Reserved: For future use
+
+[host1]
+_underscores_at_start: reserved
+_host_name: host1
+Disk_Home: /usr/lib/nagios/plugins/check_disk -w 10% -c 5% -m -p /home
+Disk_Root: /usr/lib/nagios/plugins/check_disk -w 10% -c 5% -m -p /
+Load: /usr/lib/nagios/plugins/check_load -w 5,5,5 -c 10,10,10
+
+[host2]
+_host_name: host2.foo.bar
+Procs_Total: /usr/lib/nagios/plugins/check_procs -w 200 -c 250
+Swap: /usr/lib/nagios/plugins/check_swap -w 50% -c 25%
+Users: /usr/lib/nagios/plugins/check_users -w 10 -c 15
+
diff --git a/examples/nagixsc_usrlocal.conf b/examples/nagixsc_usrlocal.conf
--- /dev/null
@@ -0,0 +1,16 @@
+[nagixsc]
+Reserved: For future use
+
+[host1]
+_underscores_at_start: reserved
+_host_name: host1
+Disk_Home: /usr/local/nagios/libexec/check_disk -w 10% -c 5% -m -p /home
+Disk_Root: /usr/local/nagios/libexec/check_disk -w 10% -c 5% -m -p /
+Load: /usr/local/nagios/libexec/check_load -w 5,5,5 -c 10,10,10
+
+[host2]
+_host_name: host2.foo.bar
+Procs_Total: /usr/local/nagios/libexec/check_procs -w 200 -c 250
+Swap: /usr/local/nagios/libexec/check_swap -w 50% -c 25%
+Users: /usr/local/nagios/libexec/check_users -w 10 -c 15
+
diff --git a/nagixsc.py b/nagixsc.py
--- /dev/null
+++ b/nagixsc.py
@@ -0,0 +1,139 @@
+import base64
+import datetime
+import libxml2
+
+def debug(level, verb, string):
+ if level <= verb:
+ print "%s: %s" % (level, string)
+
+
+##############################################################################
+
+def available_encodings():
+ return ['base64', 'plain',]
+
+
+def decode(data, encoding):
+ if encoding == 'plain':
+ return data
+ else:
+ return base64.b64decode(data)
+
+
+def encode(data, encoding=None):
+ if encoding == 'plain':
+ return data
+ else:
+ return base64.b64encode(data)
+
+
+##############################################################################
+
+def xml_check_version(xmldoc):
+ # FIXME: Check XML structure
+ try:
+ xmlnagixsc = xmldoc.xpathNewContext().xpathEval('/nagixsc')[0]
+ except:
+ return (False, 'Not a Nag(IX)SC XML file!')
+
+ try:
+ if xmlnagixsc.prop('version') != "1.0":
+ return (False, 'Wrong version (found "%s", need "1.0") of XML file!' % xmlnagixsc.prop('version'))
+ except:
+ return (False, 'No version information found in XML file!')
+
+ return (True, 'XML seems to be ok')
+
+
+def xml_get_timestamp(xmldoc):
+ try:
+ timestamp = int(xmldoc.xpathNewContext().xpathEval('/nagixsc/timestamp')[0].get_content())
+ except:
+ return False
+
+ return timestamp
+
+
+def xml_to_dict(xmldoc, verb=0, hostfilter=None, servicefilter=None):
+ checks = []
+ filetimestamp = xml_get_timestamp(xmldoc)
+
+ if hostfilter:
+ hosts = xmldoc.xpathNewContext().xpathEval('/nagixsc/host[name="%s"] | /nagixsc/host[name="%s"]' % (hostfilter, encode(hostfilter)))
+ else:
+ hosts = xmldoc.xpathNewContext().xpathEval('/nagixsc/host')
+
+ for host in hosts:
+ xmlhostname = host.xpathEval('name')[0]
+ hostname = decode(xmlhostname.get_content(), xmlhostname.prop('encoding'))
+ debug(2, verb, 'Found host "%s"' % hostname)
+
+ if servicefilter:
+ services = host.xpathEval('service[description="%s"] | service[description="%s"]' % (servicefilter, encode(servicefilter)))
+ else:
+ services = host.xpathEval('service')
+
+ for service in services:
+ service_dict = {}
+
+ xmldescr = service.xpathEval('description')[0]
+ xmloutput = service.xpathEval('output')[0]
+
+ srvdescr = decode(xmldescr.get_content(), xmldescr.prop('encoding'))
+ retcode = service.xpathEval('returncode')[0].get_content()
+ output = decode(xmloutput.get_content(), xmldescr.prop('encoding')).rstrip()
+
+ try:
+ timestamp = int(service.xpathEval('timestamp')[0].get_content())
+ except:
+ timestamp = filetimestamp
+
+ debug(2, verb, 'Found service "%s"' % srvdescr)
+
+ service_dict = {'host_name':hostname, 'service_description':srvdescr, 'returncode':retcode, 'output':output, 'timestamp':timestamp}
+ checks.append(service_dict)
+
+ debug(1, verb, 'Host: "%s" - Service: "%s" - RetCode: "%s" - Output: "%s"' % (hostname, srvdescr, retcode, output) )
+
+ return checks
+
+
+def xml_from_dict(checks, encoding='base64'):
+ lasthost = None
+
+ db = [(check['host_name'], check) for check in checks]
+ db.sort()
+
+ xmldoc = libxml2.newDoc('1.0')
+ xmlroot = xmldoc.newChild(None, 'nagixsc', None)
+ xmlroot.setProp('version', '1.0')
+ xmltimestamp = xmlroot.newChild(None, 'timestamp', datetime.datetime.now().strftime('%s'))
+
+ for entry in db:
+ check = entry[1]
+
+ if check['host_name'] != lasthost:
+ xmlhost = xmlroot.newChild(None, 'host', None)
+ xmlhostname = xmlhost.newChild(None, 'name', encode(check['host_name'], encoding))
+ lasthost = check['host_name']
+
+ xmlservice = xmlhost.newChild(None, 'service', None)
+ xmlname = xmlservice.newChild(None, 'description', encode(check['service_description'], encoding))
+ xmlname.setProp('encoding', encoding)
+ xmlreturncode = xmlservice.newChild(None, 'returncode', str(check['returncode']))
+ xmloutput = xmlservice.newChild(None, 'output', encode(check['output'], encoding))
+ xmloutput.setProp('encoding', encoding)
+ if check.has_key('timestamp'):
+ xmltimestamp = xmlservice.newChild(None, 'timestamp', str(check['timestamp']))
+
+ return xmldoc
+
+
+def check_mark_outdated(check, now, maxtimediff, markold):
+ timedelta = now - check['timestamp']
+ if timedelta > maxtimediff:
+ check['output'] = 'Nag(ix)SC: Check result is %s(>%s) seconds old - %s' % (timedelta, maxtimediff, check['output'])
+ if markold:
+ check['returncode'] = 3
+ return check
+
diff --git a/nagixsc_conf2xml.py b/nagixsc_conf2xml.py
--- /dev/null
+++ b/nagixsc_conf2xml.py
@@ -0,0 +1,77 @@
+#!/usr/bin/python
+
+import ConfigParser
+import optparse
+import subprocess
+import sys
+
+##############################################################################
+
+from nagixsc import *
+
+##############################################################################
+
+checks = []
+
+
+parser = optparse.OptionParser()
+
+parser.add_option('-c', '', dest='conffile', help='Config file')
+parser.add_option('-o', '', dest='outfile', help='Output file')
+parser.add_option('-e', '', dest='encoding', help='Encoding ("%s")' % '", "'.join(available_encodings()) )
+parser.add_option('-v', '', action='count', dest='verb', help='Verbose output')
+
+parser.set_defaults(conffile='nagixsc.conf')
+parser.set_defaults(outfile='-')
+parser.set_defaults(encoding='base64')
+parser.set_defaults(verb=0)
+
+(options, args) = parser.parse_args()
+
+##############################################################################
+
+if options.encoding not in available_encodings():
+ print 'Wrong encoding method "%s"!' % options.encoding
+ print 'Could be one of: %s' % ', '.join(available_encodings)
+ sys.exit(127)
+
+##############################################################################
+
+config = ConfigParser.RawConfigParser()
+config.optionxform = str # We need case-sensitive options
+conf_list = config.read(options.conffile)
+
+if conf_list == []:
+ print 'Config file "%s" could not be read!' % options.conffile
+ sys.exit(127)
+
+# Sections are Hosts (not 'nagixsc'), options in sections are Services
+hosts = config.sections()
+if 'nagixsc' in hosts:
+ hosts.remove('nagixsc')
+
+for host in hosts:
+ if config.has_option(host,'_host_name'):
+ host_name = config.get(host,'_host_name')
+ else:
+ host_name = host
+
+ for service in config.options(host):
+ # If option starts with '_' it may be a NagixSC option in the future
+ if service[0] != '_':
+ cmdline = config.get(host, service)
+
+ cmd = subprocess.Popen(cmdline.split(' '), stdout=subprocess.PIPE)
+ output = cmd.communicate()[0].rstrip()
+ retcode = cmd.returncode
+
+ checks.append({'host_name':host_name, 'service_description':service, 'returncode':retcode, 'output':output, 'timestamp':datetime.datetime.now().strftime('%s')})
+
+
+xmldoc = xml_from_dict(checks, options.encoding)
+if options.outfile == '-':
+ xmldoc.saveFormatFile('-', format=1)
+else:
+ xmldoc.saveFile(options.outfile)
+
+
diff --git a/nagixsc_xml2nagios.py b/nagixsc_xml2nagios.py
--- /dev/null
+++ b/nagixsc_xml2nagios.py
@@ -0,0 +1,252 @@
+#!/usr/bin/python
+
+#import base64
+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', ]
+CHECKRESULTDIRs = [ '/usr/local/nagios/var/spool/checkresults', '/var/lib/nagios3/spool/checkresults', ]
+MODEs = [ 'passive', 'passive_check', 'checkresult', 'checkresult_check', 'active', ]
+
+parser = optparse.OptionParser()
+
+parser.add_option('-u', '', dest='url', help='URL of status file (xml)')
+parser.add_option('-f', '', dest='file', help='(Path and) file name of status file')
+parser.add_option('-S', '', dest='schemacheck', help='Check XML against DTD')
+parser.add_option('-s', '', dest='seconds', type='int', help='Maximum age in seconds of xml timestamp')
+parser.add_option('-m', '', action='store_true', dest='markold', help='Mark (Set state) of too old checks as UNKNOWN')
+parser.add_option('-O', '', dest='mode', help='Where/Howto output the results ("%s")' % '", "'.join(MODEs))
+parser.add_option('-p', '', dest='pipe', help='Full path to nagios.cmd')
+parser.add_option('-r', '', dest='checkresultdir', help='Full path to checkresult directory')
+parser.add_option('-H', '', dest='host', help='Hostname to search for in XML file')
+parser.add_option('-D', '', dest='service', help='Service description to search for in XML file')
+parser.add_option('-v', '', action='count', dest='verb', help='Verbose output')
+
+parser.set_defaults(url=None)
+parser.set_defaults(file='nagixsc.xml')
+parser.set_defaults(schemacheck='')
+parser.set_defaults(seconds=14400)
+parser.set_defaults(markold=False)
+parser.set_defaults(mode=False)
+parser.set_defaults(pipe=None)
+parser.set_defaults(checkresultdir=None)
+parser.set_defaults(host=None)
+parser.set_defaults(service=None)
+parser.set_defaults(verb=0)
+
+(options, args) = parser.parse_args()
+
+##############################################################################
+
+from nagixsc import *
+
+##############################################################################
+
+if options.mode not in MODEs:
+ print 'Not an allowed mode "%s" - allowed are: %s' % (options.mode, ", ".join(MODEs))
+ sys.exit(127)
+
+# Check command line options wrt mode
+if options.mode == 'passive' or options.mode == 'passive_check':
+ debug(1, options.verb, 'Running in passive mode')
+ if options.pipe == None and options.verb <= 2:
+ for nagioscmd in NAGIOSCMDs:
+ if os.path.exists(nagioscmd):
+ options.pipe = nagioscmd
+
+ if options.pipe == None and options.verb <= 2:
+ print 'Need full path to the nagios.cmd pipe!'
+ sys.exit(127)
+
+ debug(2, options.verb, 'nagios.cmd found at %s' % options.pipe)
+
+elif options.mode == 'checkresult' or options.mode == 'checkresult_check':
+ debug(1, options.verb, 'Running in checkresult mode')
+ if options.checkresultdir == None and options.verb <= 2:
+ for crd in CHECKRESULTDIRs:
+ if os.path.exists(crd):
+ options.checkresultdir = crd
+
+ if options.checkresultdir == None and options.verb <= 2:
+ print 'Need full path to the checkresultdir!'
+ sys.exit(127)
+
+ debug(2, options.verb, 'Checkresult dir: %s' % options.checkresultdir)
+
+elif options.mode == 'active':
+ debug(1, options.verb, 'Running in active/plugin mode')
+ if options.host == None:
+ debug(1, options.verb, 'No host specified, using first host in XML file')
+ if options.service == None:
+ print 'No service specified on command line!'
+ sys.exit(127)
+
+##############################################################################
+
+now = int(datetime.datetime.now().strftime('%s'))
+
+# Get URL or file
+if options.url != None:
+ import urllib2
+
+ response = urllib2.urlopen(options.url)
+ doc = libxml2.parseDoc(response.read())
+ response.close()
+else:
+ doc = libxml2.parseFile(options.file)
+
+
+# Check XML against DTD
+if options.schemacheck:
+ dtd = libxml2.parseDTD(None, options.schemacheck)
+ ctxt = libxml2.newValidCtxt()
+ ret = doc.validateDtd(ctxt, dtd)
+ if ret != 1:
+ print "error doing DTD validation"
+ sys.exit(1)
+ dtd.freeDtd()
+ del dtd
+ del ctxt
+
+
+# Check XML file basics
+(status, statusstring) = xml_check_version(doc)
+debug(1, options.verb, statusstring)
+if not status:
+ print statusstring
+ sys.exit(127)
+
+
+# Get timestamp and check it
+filetimestamp = xml_get_timestamp(doc)
+if not filetimestamp:
+ print 'No timestamp found in XML file, exiting because of invalid XML data...'
+ sys.exit(127)
+
+timedelta = int(now) - int(filetimestamp)
+debug(1, options.verb, 'Age of XML file: %s seconds, max allowed: %s seconds' % (timedelta, options.seconds))
+
+
+# Put XML to Python dict
+checks = xml_to_dict(doc, options.verb, options.host, options.service)
+
+# Loop over check results and perhaps mark them as outdated
+for check in checks:
+ check = check_mark_outdated(check, now, options.seconds, options.markold)
+
+
+# Next steps depend on mode, output results
+# MODE: passive
+if options.mode == 'passive' or options.mode == 'passive_check':
+ # Prepare
+ if options.verb <= 2:
+ pipe = open(options.pipe, "w")
+ else:
+ pipe = None
+
+ # Output
+ for check in checks:
+ 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!"
+
+ # Return/Exit as a Nagios Plugin if called with mode 'passive_check'
+ if options.mode == 'passive_check':
+ returncode = 0
+ returnstring = 'OK'
+ output = ''
+
+ if options.markold:
+ if (now - filetimestamp) > options.seconds:
+ returnstring = 'WARNING'
+ output = 'Check results are %s(>%s) seconds old' % ((now-filetimestamp), options.seconds)
+ returncode = 1
+
+ if not output:
+ output = 'Check results are %s seconds old' % (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
+
+ chars = string.letters + string.digits
+
+ for check in checks:
+ count_services += 1
+ if check.has_key('timestamp'):
+ timestamp = check['timestamp']
+ 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")
+ 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':
+ returnstring = ''
+ output = ''
+ if count_failed == 0:
+ returnstring = 'OK'
+ returncode = 0
+ output = 'Wrote checkresult files for %s services' % count_services
+ elif count_failed == count_services:
+ returnstring = 'CRITICAL'
+ returncode = 2
+ output = 'No checkresult files could be writen!'
+ else:
+ returnstring = 'WARNING'
+ returncode = 1
+ output = 'Could not write %s out of %s checkresult files!' % (count_failed, count_services)
+
+ 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':
+
+ if len(checks) > 1:
+ print 'Nag(ix)SC UNKNOWN - Found more (%s) than one host/service!' % len(checks)
+ sys.exit(3)
+ elif len(checks) == 0:
+ print 'Nag(ix)SC UNKNOWN - No check for "%s"/"%s" found in XML' % (options.host, options.service)
+ sys.exit(3)
+
+ print checks[0]['output']
+ sys.exit(int(checks[0]['returncode']))
+
+else:
+ print 'Unknown mode! This should NEVER happen!'
+ sys.exit(127)
+
diff --git a/xml/dtd_no_timestamp.xml b/xml/dtd_no_timestamp.xml
--- /dev/null
+++ b/xml/dtd_no_timestamp.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE nagixsc SYSTEM "nagixsc.dtd">
+
+<nagixsc version="1.0">
+
+ <host>
+ <name encoding="plain">host1</name>
+
+ <service>
+ <description encoding="base64">RGlza19Ib21l</description>
+ <returncode>2</returncode>
+ <output encoding="base64">RElTSyBDUklUSUNBTCAtIGZyZWUgc3BhY2U6IC9ob21lIDc3NSBNQiAoMSUgaW5vZGU9OTElKTt8IC9ob21lPTY3NTg0TUI7NjE1MjM7NjQ5NDE7MDs2ODM1OQo=</output>
+ <timestamp>1234443420</timestamp>
+ </service>
+
+ <service>
+ <description encoding="base64">RGlza19Sb290</description>
+ <returncode>0</returncode>
+ <output encoding="base64">RElTSyBPSyAtIGZyZWUgc3BhY2U6IC8gMjE2NyBNQiAoMjIlIGlub2RlPTk3JSk7fCAvPTczNTNNQjs4NTY4OzkwNDQ7MDs5NTIwCg==</output>
+ <timestamp>1234443420</timestamp>
+ </service>
+
+ <service>
+ <description encoding="base64">TG9hZA==</description>
+ <returncode>0</returncode>
+ <output encoding="base64">T0sgLSBsb2FkIGF2ZXJhZ2U6IDAuMDAsIDAuMDAsIDAuMDB8bG9hZDE9MC4wMDA7NS4wMDA7MTAuMDAwOzA7IGxvYWQ1PTAuMDAwOzUuMDAwOzEwLjAwMDswOyBsb2FkMTU9MC4wMDA7NS4wMDA7MTAuMDAwOzA7IAo=</output>
+ <timestamp>1234443420</timestamp>
+ </service>
+
+ </host>
+
+
+ <host>
+ <name encoding="plain">host2</name>
+
+ <service>
+ <description encoding="base64">UHJvY3NfVG90YWw=</description>
+ <returncode>0</returncode>
+ <output encoding="base64">UFJPQ1MgT0s6IDE2MyBwcm9jZXNzZXMK</output>
+ <timestamp>1234443410</timestamp>
+ </service>
+
+ <service>
+ <description encoding="base64">U3dhcA==</description>
+ <returncode>0</returncode>
+ <output encoding="base64">U1dBUCBPSyAtIDc5JSBmcmVlICgxNDkyIE1CIG91dCBvZiAxOTA2IE1CKSB8c3dhcD0xNDkyTUI7OTUzOzQ3NjswOzE5MDYK</output>
+ </service>
+
+ <service>
+ <description encoding="base64">VXNlcnM=</description>
+ <returncode>1</returncode>
+ <output encoding="base64">VVNFUlMgV0FSTklORyAtIDExIHVzZXJzIGN1cnJlbnRseSBsb2dnZWQgaW4gfHVzZXJzPTExOzEwOzE1OzAKMyByb290IHNlc3Npb25zCjggbm9uLXJvb3Qgc2Vzc2lvbnM=</output>
+ <timestamp>1234443410</timestamp>
+ </service>
+
+ </host>
+
+
+</nagixsc>
+
diff --git a/xml/dtd_test.xml b/xml/dtd_test.xml
--- /dev/null
+++ b/xml/dtd_test.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+<!DOCTYPE nagixsc SYSTEM "nagixsc.dtd">
+<nagixsc version="1.0">
+ <timestamp>1234443430</timestamp>
+
+ <host>
+ <name encoding="plain">host1</name>
+
+ <service>
+ <description encoding="base64">RGlza19Ib21l</description>
+ <returncode>2</returncode>
+ <output encoding="base64">RElTSyBDUklUSUNBTCAtIGZyZWUgc3BhY2U6IC9ob21lIDc3NSBNQiAoMSUgaW5vZGU9OTElKTt8IC9ob21lPTY3NTg0TUI7NjE1MjM7NjQ5NDE7MDs2ODM1OQo=</output>
+ <timestamp>1234443420</timestamp>
+ </service>
+
+ <service>
+ <description encoding="base64">RGlza19Sb290</description>
+ <returncode>0</returncode>
+ <output encoding="base64">RElTSyBPSyAtIGZyZWUgc3BhY2U6IC8gMjE2NyBNQiAoMjIlIGlub2RlPTk3JSk7fCAvPTczNTNNQjs4NTY4OzkwNDQ7MDs5NTIwCg==</output>
+ <timestamp>1234443420</timestamp>
+ </service>
+
+ <service>
+ <description encoding="base64">TG9hZA==</description>
+ <returncode>0</returncode>
+ <output encoding="base64">T0sgLSBsb2FkIGF2ZXJhZ2U6IDAuMDAsIDAuMDAsIDAuMDB8bG9hZDE9MC4wMDA7NS4wMDA7MTAuMDAwOzA7IGxvYWQ1PTAuMDAwOzUuMDAwOzEwLjAwMDswOyBsb2FkMTU9MC4wMDA7NS4wMDA7MTAuMDAwOzA7IAo=</output>
+ <timestamp>1234443420</timestamp>
+ </service>
+
+ </host>
+
+
+ <host>
+ <name encoding="plain">host2</name>
+
+ <service>
+ <description encoding="base64">UHJvY3NfVG90YWw=</description>
+ <returncode>0</returncode>
+ <output encoding="base64">UFJPQ1MgT0s6IDE2MyBwcm9jZXNzZXMK</output>
+ <timestamp>1234443410</timestamp>
+ </service>
+
+ <service>
+ <description encoding="base64">U3dhcA==</description>
+ <returncode>0</returncode>
+ <output encoding="base64">U1dBUCBPSyAtIDc5JSBmcmVlICgxNDkyIE1CIG91dCBvZiAxOTA2IE1CKSB8c3dhcD0xNDkyTUI7OTUzOzQ3NjswOzE5MDYK</output>
+ </service>
+
+ <service>
+ <description encoding="base64">VXNlcnM=</description>
+ <returncode>1</returncode>
+ <output encoding="base64">VVNFUlMgV0FSTklORyAtIDExIHVzZXJzIGN1cnJlbnRseSBsb2dnZWQgaW4gfHVzZXJzPTExOzEwOzE1OzAKMyByb290IHNlc3Npb25zCjggbm9uLXJvb3Qgc2Vzc2lvbnM=</output>
+ <timestamp>1234443410</timestamp>
+ </service>
+
+ </host>
+
+
+</nagixsc>
+
diff --git a/xml/nagixsc.dtd b/xml/nagixsc.dtd
--- /dev/null
+++ b/xml/nagixsc.dtd
@@ -0,0 +1,21 @@
+<!ELEMENT nagixsc (timestamp,host+)>
+<!ATTLIST nagixsc version CDATA #IMPLIED >
+
+<!ELEMENT timestamp (#PCDATA)>
+
+<!ELEMENT host (name,service+)>
+<!ATTLIST host encoding CDATA #IMPLIED >
+
+<!ELEMENT name (#PCDATA)>
+<!ATTLIST name encoding CDATA #IMPLIED >
+
+<!ELEMENT service (description,returncode,output,timestamp?)>
+
+<!ELEMENT description (#PCDATA)>
+<!ATTLIST description encoding CDATA #IMPLIED >
+
+<!ELEMENT returncode (#PCDATA)>
+
+<!ELEMENT output (#PCDATA)>
+<!ATTLIST output encoding CDATA #IMPLIED >
+
diff --git a/xml/nagixsc.xml b/xml/nagixsc.xml
--- /dev/null
+++ b/xml/nagixsc.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+
+<nagixsc version="1.0">
+ <timestamp>1234443430</timestamp>
+
+ <host>
+ <name encoding="plain">host1</name>
+
+ <service>
+ <description encoding="base64">RGlza19Ib21l</description>
+ <returncode>2</returncode>
+ <output encoding="base64">RElTSyBDUklUSUNBTCAtIGZyZWUgc3BhY2U6IC9ob21lIDc3NSBNQiAoMSUgaW5vZGU9OTElKTt8IC9ob21lPTY3NTg0TUI7NjE1MjM7NjQ5NDE7MDs2ODM1OQo=</output>
+ <timestamp>1234443420</timestamp>
+ </service>
+
+ <service>
+ <description encoding="base64">RGlza19Sb290</description>
+ <returncode>0</returncode>
+ <output encoding="base64">RElTSyBPSyAtIGZyZWUgc3BhY2U6IC8gMjE2NyBNQiAoMjIlIGlub2RlPTk3JSk7fCAvPTczNTNNQjs4NTY4OzkwNDQ7MDs5NTIwCg==</output>
+ <timestamp>1234443420</timestamp>
+ </service>
+
+ <service>
+ <description encoding="base64">TG9hZA==</description>
+ <returncode>0</returncode>
+ <output encoding="base64">T0sgLSBsb2FkIGF2ZXJhZ2U6IDAuMDAsIDAuMDAsIDAuMDB8bG9hZDE9MC4wMDA7NS4wMDA7MTAuMDAwOzA7IGxvYWQ1PTAuMDAwOzUuMDAwOzEwLjAwMDswOyBsb2FkMTU9MC4wMDA7NS4wMDA7MTAuMDAwOzA7IAo=</output>
+ <timestamp>1234443420</timestamp>
+ </service>
+
+ </host>
+
+
+ <host>
+ <name encoding="plain">host2</name>
+
+ <service>
+ <description encoding="base64">UHJvY3NfVG90YWw=</description>
+ <returncode>0</returncode>
+ <output encoding="base64">UFJPQ1MgT0s6IDE2MyBwcm9jZXNzZXMK</output>
+ <timestamp>1234443410</timestamp>
+ </service>
+
+ <service>
+ <description encoding="base64">U3dhcA==</description>
+ <returncode>0</returncode>
+ <output encoding="base64">U1dBUCBPSyAtIDc5JSBmcmVlICgxNDkyIE1CIG91dCBvZiAxOTA2IE1CKSB8c3dhcD0xNDkyTUI7OTUzOzQ3NjswOzE5MDYK</output>
+ </service>
+
+ <service>
+ <description encoding="base64">VXNlcnM=</description>
+ <returncode>1</returncode>
+ <output encoding="base64">VVNFUlMgV0FSTklORyAtIDExIHVzZXJzIGN1cnJlbnRseSBsb2dnZWQgaW4gfHVzZXJzPTExOzEwOzE1OzAKMyByb290IHNlc3Npb25zCjggbm9uLXJvb3Qgc2Vzc2lvbnM=</output>
+ <timestamp>1234443410</timestamp>
+ </service>
+
+ </host>
+
+
+</nagixsc>
+
diff --git a/xml/nagixsc.xsd b/xml/nagixsc.xsd
--- /dev/null
+++ b/xml/nagixsc.xsd
@@ -0,0 +1,28 @@
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <xsd:element name="nagixsc" minOccurs="1" maxOccurs="1">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="timestamp" type="xsd:long" minOccurs="1" maxOccurs="1" />
+
+ <xsd:element name="host" type="xsd:string" minOccurs="1" maxOccurs="unbounded">
+ <xsd:sequence>
+
+ <xsd:element name="service" type="xsd:string" minOccurs="1" maxOccurs="unbounded">
+ <xsd:sequence>
+ <xsd:element name="description" type="xsd:string" minOccurs="1" maxOccurs="1" />
+ <xsd:element name="returncode" type="xsd:string" minOccurs="1" maxOccurs="1" />
+ <xsd:element name="output" type="xsd:int" minOccurs="1" maxOccurs="1" />
+ <xsd:element name="timestamp" type="xsd:string" minOccurs="0" maxOccurs="1" />
+ </xsd:sequence>
+ </xsd:element>
+
+ </xsd:sequence>
+ </xsd:element>
+
+ </xsd:sequence>
+ </xsd:complexType>
+
+ </xsd:element>
+
+</xsd:schema>
+