Code

Rename scripts and python module
authorSven Velt <sven@velt.de>
Thu, 4 Mar 2010 09:18:02 +0000 (10:18 +0100)
committerSven Velt <sven@velt.de>
Thu, 4 Mar 2010 13:52:51 +0000 (14:52 +0100)
Put nagixsc.py into nagixsc/__init__.py to use and install it as an usul
Python module.

Renamed dummy_* to more "offical" names.

Signed-off-by: Sven Velt <sven@velt.de>
QUICKSTART.de.txt
dummy_read_xml.py [deleted file]
dummy_write_xml.py [deleted file]
nagixsc.py [deleted file]
nagixsc/__init__.py [new file with mode: 0644]
nagixsc_read_xml.py [new file with mode: 0755]
nagixsc_write_xml.py [new file with mode: 0755]

index aabaac418dd111ae8ad29d724405ed64bee1f1ba..92d4875935e8ca0ef1827b01381accb668746f9c 100644 (file)
@@ -69,10 +69,10 @@ 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
+Mit Hilfe des Skripts "nagixsc_read_xml.py" kann man sich den Inhalt einer
 Nag(ix)SC-XML-Datei ansehen:
 
-% ./dummy_read_xml.py -f /tmp/nagixsc.xml
+% ./nagixsc_read_xml.py -f /tmp/nagixsc.xml
 
 Der Inhalt wird zuerst blockweise, dann als formatiertes Python Dict
 ausgegeben.
diff --git a/dummy_read_xml.py b/dummy_read_xml.py
deleted file mode 100755 (executable)
index 1cd4363..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/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('-l', '', dest='httpuser', help='HTTP user name')
-parser.add_option('-a', '', dest='httppasswd', help='HTTP password')
-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(httpuser=None)
-parser.set_defaults(httppasswd=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
-doc = read_xml(options)
-
-
-# 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
deleted file mode 100755 (executable)
index 008d0b7..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/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/nagixsc.py b/nagixsc.py
deleted file mode 100644 (file)
index 56c4cf3..0000000
+++ /dev/null
@@ -1,442 +0,0 @@
-import BaseHTTPServer
-import ConfigParser
-import SocketServer
-import base64
-import datetime
-import libxml2
-import mimetools
-import os
-import random
-import shlex
-import socket
-import string
-import subprocess
-import sys
-
-def debug(level, verb, string):
-       if level <= verb:
-               print "%s: %s" % (level, string)
-
-
-##############################################################################
-
-def available_encodings():
-       return ['base64', 'plain',]
-
-
-def check_encoding(enc):
-       if enc in available_encodings():
-               return True
-       else:
-               return False
-
-
-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 read_inifile(inifile):
-       config = ConfigParser.RawConfigParser()
-       config.optionxform = str # We need case-sensitive options
-       ini_list = config.read(inifile)
-
-       if ini_list:
-               return config
-       else:
-               return False
-
-
-##############################################################################
-
-def exec_check(host_name, service_descr, cmdline):
-       try:
-               cmd     = subprocess.Popen(shlex.split(cmdline), stdout=subprocess.PIPE)
-               output  = cmd.communicate()[0].rstrip()
-               retcode = cmd.returncode
-       except OSError:
-               output  = 'Could not execute "%s"' % cmdline
-               retcode = 127
-
-       return {'host_name':host_name, 'service_description':service_descr, 'returncode':retcode, 'output':output, 'timestamp':datetime.datetime.now().strftime('%s')}
-
-
-##############################################################################
-
-def conf2dict(config, opt_host=None, opt_service=None):
-       checks = []
-
-       # Sections are Hosts (not 'nagixsc'), options in sections are Services
-       hosts = config.sections()
-       if 'nagixsc' in hosts:
-               hosts.remove('nagixsc')
-
-       # Filter out host/section if it exists
-       if opt_host:
-               if opt_host in hosts:
-                       hosts = [opt_host,]
-               else:
-                       hosts = []
-
-       for host in hosts:
-               # Overwrite section/host name with '_host_name'
-               if config.has_option(host,'_host_name'):
-                       host_name = config.get(host,'_host_name')
-               else:
-                       host_name = host
-
-
-               services = config.options(host)
-               # Look for host check
-               if '_host_check' in services and not opt_service:
-                       cmdline = config.get(host, '_host_check')
-                       check = exec_check(host_name, None, cmdline)
-                       checks.append(check)
-
-
-               # Filter out service if given in cmd line options
-               if opt_service:
-                       if opt_service in services:
-                               services = [opt_service,]
-                       else:
-                               services = []
-
-               for service in services:
-                       # If option starts with '_' it may be a NagixSC option in the future
-                       if service[0] != '_':
-                               cmdline = config.get(host, service)
-
-                               check = exec_check(host_name, service, cmdline)
-                               checks.append(check)
-
-       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):
-       if options.url != None:
-               import urllib2
-
-               if options.httpuser and options.httppasswd:
-                       passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
-                       passman.add_password(None, options.url, options.httpuser, options.httppasswd)
-                       authhandler = urllib2.HTTPBasicAuthHandler(passman)
-                       opener = urllib2.build_opener(authhandler)
-                       urllib2.install_opener(opener)
-
-               try:
-                       response = urllib2.urlopen(options.url)
-               except urllib2.HTTPError, error:
-                       print error
-                       sys.exit(0)
-               except urllib2.URLError, error:
-                       print error.reason[1]
-                       sys.exit(0)
-
-               doc = libxml2.parseDoc(response.read())
-               response.close()
-
-       else:
-               doc = libxml2.parseFile(options.file)
-
-       return doc
-
-
-def read_xml_from_string(content):
-       return libxml2.parseDoc(content)
-
-
-##############################################################################
-
-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 = []
-       now = int(datetime.datetime.now().strftime('%s'))
-       filetimestamp = reset_future_timestamp(xml_get_timestamp(xmldoc), now)
-
-       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)
-
-               # Look for Host check result
-               if host.xpathEval('returncode'):
-                       retcode   = host.xpathEval('returncode')[0].get_content()
-               else:
-                       retcode   = None
-
-               if host.xpathEval('output'):
-                       xmloutput = host.xpathEval('output')[0]
-                       output    = decode(xmloutput.get_content(), xmloutput.prop('encoding')).rstrip()
-               else:
-                       output    = None
-
-               if host.xpathEval('timestamp'):
-                       timestamp = reset_future_timestamp(int(host.xpathEval('timestamp')[0].get_content()), now)
-               else:
-                       timestamp = filetimestamp
-
-               if retcode and output:
-                       checks.append({'host_name':hostname, 'service_description':None, 'returncode':retcode, 'output':output, 'timestamp':timestamp})
-
-
-               # Look for service filter
-               if servicefilter:
-                       services = host.xpathEval('service[description="%s"] | service[description="%s"]' % (servicefilter, encode(servicefilter)))
-               else:
-                       services = host.xpathEval('service')
-
-               # Loop over services in host
-               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(), xmloutput.prop('encoding')).rstrip()
-
-                       try:
-                               timestamp = reset_future_timestamp(int(service.xpathEval('timestamp')[0].get_content()), now)
-                       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']
-
-               if check['service_description'] == '' or check['service_description'] == None:
-                       # Host check result
-                       xmlreturncode = xmlhost.newChild(None, 'returncode', str(check['returncode']))
-                       xmloutput     = xmlhost.newChild(None, 'output', encode(check['output'], encoding))
-                       xmloutput.setProp('encoding', encoding)
-                       if check.has_key('timestamp'):
-                               xmltimestamp  = xmlhost.newChild(None, 'timestamp', str(check['timestamp']))
-               else:
-                       # Service check result
-                       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
-
-
-def reset_future_timestamp(timestamp, now):
-       if timestamp <= now:
-               return timestamp
-       else:
-               return now
-
-##############################################################################
-
-def encode_multipart(xmldoc, httpuser, httppasswd):
-       BOUNDARY = mimetools.choose_boundary()
-       CRLF = '\r\n'
-       L = []
-       L.append('--' + BOUNDARY)
-       L.append('Content-Disposition: form-data; name="xmlfile"; filename="xmlfile"')
-       L.append('Content-Type: application/xml')
-       L.append('')
-       L.append(xmldoc.serialize())
-       L.append('--' + BOUNDARY + '--')
-       L.append('')
-       body = CRLF.join(L)
-       content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
-       headers = {'Content-Type': content_type, 'Content-Length': str(len(body))}
-
-       if httpuser and httppasswd:
-               headers['Authorization'] = 'Basic %s' % base64.b64encode(':'.join([httpuser, httppasswd]))
-
-       return (headers, body)
-
-##############################################################################
-
-class MyHTTPServer(BaseHTTPServer.HTTPServer):
-       def __init__(self, server_address, HandlerClass, ssl=False, sslpemfile=None):
-               if ssl:
-                       # FIXME: SSL is in Py2.6
-                       try:
-                               from OpenSSL import SSL
-                       except:
-                               print 'No Python OpenSSL wrapper/bindings found!'
-                               sys.exit(127)
-
-                       SocketServer.BaseServer.__init__(self, server_address, HandlerClass)
-                       context = SSL.Context(SSL.SSLv23_METHOD)
-                       context.use_privatekey_file (sslpemfile)
-                       context.use_certificate_file(sslpemfile)
-                       self.socket = SSL.Connection(context, socket.socket(self.address_family, self.socket_type))
-               else:
-                       SocketServer.BaseServer.__init__(self, server_address, HandlerClass)
-                       self.socket = socket.socket(self.address_family, self.socket_type)
-
-               self.server_bind()
-               self.server_activate()
-
-
-class MyHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
-       def setup(self):
-               self.connection = self.request
-               self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
-               self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
-
-##############################################################################
-
diff --git a/nagixsc/__init__.py b/nagixsc/__init__.py
new file mode 100644 (file)
index 0000000..56c4cf3
--- /dev/null
@@ -0,0 +1,442 @@
+import BaseHTTPServer
+import ConfigParser
+import SocketServer
+import base64
+import datetime
+import libxml2
+import mimetools
+import os
+import random
+import shlex
+import socket
+import string
+import subprocess
+import sys
+
+def debug(level, verb, string):
+       if level <= verb:
+               print "%s: %s" % (level, string)
+
+
+##############################################################################
+
+def available_encodings():
+       return ['base64', 'plain',]
+
+
+def check_encoding(enc):
+       if enc in available_encodings():
+               return True
+       else:
+               return False
+
+
+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 read_inifile(inifile):
+       config = ConfigParser.RawConfigParser()
+       config.optionxform = str # We need case-sensitive options
+       ini_list = config.read(inifile)
+
+       if ini_list:
+               return config
+       else:
+               return False
+
+
+##############################################################################
+
+def exec_check(host_name, service_descr, cmdline):
+       try:
+               cmd     = subprocess.Popen(shlex.split(cmdline), stdout=subprocess.PIPE)
+               output  = cmd.communicate()[0].rstrip()
+               retcode = cmd.returncode
+       except OSError:
+               output  = 'Could not execute "%s"' % cmdline
+               retcode = 127
+
+       return {'host_name':host_name, 'service_description':service_descr, 'returncode':retcode, 'output':output, 'timestamp':datetime.datetime.now().strftime('%s')}
+
+
+##############################################################################
+
+def conf2dict(config, opt_host=None, opt_service=None):
+       checks = []
+
+       # Sections are Hosts (not 'nagixsc'), options in sections are Services
+       hosts = config.sections()
+       if 'nagixsc' in hosts:
+               hosts.remove('nagixsc')
+
+       # Filter out host/section if it exists
+       if opt_host:
+               if opt_host in hosts:
+                       hosts = [opt_host,]
+               else:
+                       hosts = []
+
+       for host in hosts:
+               # Overwrite section/host name with '_host_name'
+               if config.has_option(host,'_host_name'):
+                       host_name = config.get(host,'_host_name')
+               else:
+                       host_name = host
+
+
+               services = config.options(host)
+               # Look for host check
+               if '_host_check' in services and not opt_service:
+                       cmdline = config.get(host, '_host_check')
+                       check = exec_check(host_name, None, cmdline)
+                       checks.append(check)
+
+
+               # Filter out service if given in cmd line options
+               if opt_service:
+                       if opt_service in services:
+                               services = [opt_service,]
+                       else:
+                               services = []
+
+               for service in services:
+                       # If option starts with '_' it may be a NagixSC option in the future
+                       if service[0] != '_':
+                               cmdline = config.get(host, service)
+
+                               check = exec_check(host_name, service, cmdline)
+                               checks.append(check)
+
+       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):
+       if options.url != None:
+               import urllib2
+
+               if options.httpuser and options.httppasswd:
+                       passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
+                       passman.add_password(None, options.url, options.httpuser, options.httppasswd)
+                       authhandler = urllib2.HTTPBasicAuthHandler(passman)
+                       opener = urllib2.build_opener(authhandler)
+                       urllib2.install_opener(opener)
+
+               try:
+                       response = urllib2.urlopen(options.url)
+               except urllib2.HTTPError, error:
+                       print error
+                       sys.exit(0)
+               except urllib2.URLError, error:
+                       print error.reason[1]
+                       sys.exit(0)
+
+               doc = libxml2.parseDoc(response.read())
+               response.close()
+
+       else:
+               doc = libxml2.parseFile(options.file)
+
+       return doc
+
+
+def read_xml_from_string(content):
+       return libxml2.parseDoc(content)
+
+
+##############################################################################
+
+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 = []
+       now = int(datetime.datetime.now().strftime('%s'))
+       filetimestamp = reset_future_timestamp(xml_get_timestamp(xmldoc), now)
+
+       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)
+
+               # Look for Host check result
+               if host.xpathEval('returncode'):
+                       retcode   = host.xpathEval('returncode')[0].get_content()
+               else:
+                       retcode   = None
+
+               if host.xpathEval('output'):
+                       xmloutput = host.xpathEval('output')[0]
+                       output    = decode(xmloutput.get_content(), xmloutput.prop('encoding')).rstrip()
+               else:
+                       output    = None
+
+               if host.xpathEval('timestamp'):
+                       timestamp = reset_future_timestamp(int(host.xpathEval('timestamp')[0].get_content()), now)
+               else:
+                       timestamp = filetimestamp
+
+               if retcode and output:
+                       checks.append({'host_name':hostname, 'service_description':None, 'returncode':retcode, 'output':output, 'timestamp':timestamp})
+
+
+               # Look for service filter
+               if servicefilter:
+                       services = host.xpathEval('service[description="%s"] | service[description="%s"]' % (servicefilter, encode(servicefilter)))
+               else:
+                       services = host.xpathEval('service')
+
+               # Loop over services in host
+               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(), xmloutput.prop('encoding')).rstrip()
+
+                       try:
+                               timestamp = reset_future_timestamp(int(service.xpathEval('timestamp')[0].get_content()), now)
+                       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']
+
+               if check['service_description'] == '' or check['service_description'] == None:
+                       # Host check result
+                       xmlreturncode = xmlhost.newChild(None, 'returncode', str(check['returncode']))
+                       xmloutput     = xmlhost.newChild(None, 'output', encode(check['output'], encoding))
+                       xmloutput.setProp('encoding', encoding)
+                       if check.has_key('timestamp'):
+                               xmltimestamp  = xmlhost.newChild(None, 'timestamp', str(check['timestamp']))
+               else:
+                       # Service check result
+                       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
+
+
+def reset_future_timestamp(timestamp, now):
+       if timestamp <= now:
+               return timestamp
+       else:
+               return now
+
+##############################################################################
+
+def encode_multipart(xmldoc, httpuser, httppasswd):
+       BOUNDARY = mimetools.choose_boundary()
+       CRLF = '\r\n'
+       L = []
+       L.append('--' + BOUNDARY)
+       L.append('Content-Disposition: form-data; name="xmlfile"; filename="xmlfile"')
+       L.append('Content-Type: application/xml')
+       L.append('')
+       L.append(xmldoc.serialize())
+       L.append('--' + BOUNDARY + '--')
+       L.append('')
+       body = CRLF.join(L)
+       content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
+       headers = {'Content-Type': content_type, 'Content-Length': str(len(body))}
+
+       if httpuser and httppasswd:
+               headers['Authorization'] = 'Basic %s' % base64.b64encode(':'.join([httpuser, httppasswd]))
+
+       return (headers, body)
+
+##############################################################################
+
+class MyHTTPServer(BaseHTTPServer.HTTPServer):
+       def __init__(self, server_address, HandlerClass, ssl=False, sslpemfile=None):
+               if ssl:
+                       # FIXME: SSL is in Py2.6
+                       try:
+                               from OpenSSL import SSL
+                       except:
+                               print 'No Python OpenSSL wrapper/bindings found!'
+                               sys.exit(127)
+
+                       SocketServer.BaseServer.__init__(self, server_address, HandlerClass)
+                       context = SSL.Context(SSL.SSLv23_METHOD)
+                       context.use_privatekey_file (sslpemfile)
+                       context.use_certificate_file(sslpemfile)
+                       self.socket = SSL.Connection(context, socket.socket(self.address_family, self.socket_type))
+               else:
+                       SocketServer.BaseServer.__init__(self, server_address, HandlerClass)
+                       self.socket = socket.socket(self.address_family, self.socket_type)
+
+               self.server_bind()
+               self.server_activate()
+
+
+class MyHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+       def setup(self):
+               self.connection = self.request
+               self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
+               self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
+
+##############################################################################
+
diff --git a/nagixsc_read_xml.py b/nagixsc_read_xml.py
new file mode 100755 (executable)
index 0000000..1cd4363
--- /dev/null
@@ -0,0 +1,69 @@
+#!/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('-l', '', dest='httpuser', help='HTTP user name')
+parser.add_option('-a', '', dest='httppasswd', help='HTTP password')
+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(httpuser=None)
+parser.set_defaults(httppasswd=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
+doc = read_xml(options)
+
+
+# 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/nagixsc_write_xml.py b/nagixsc_write_xml.py
new file mode 100755 (executable)
index 0000000..008d0b7
--- /dev/null
@@ -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)
+