From: Sven Velt Date: Thu, 4 Mar 2010 09:18:02 +0000 (+0100) Subject: Rename scripts and python module X-Git-Url: https://git.tokkee.org/?p=nagixsc.git;a=commitdiff_plain;h=a56847623bde31e758b4f92a9b5f5ede27e01262 Rename scripts and python module 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 --- diff --git a/QUICKSTART.de.txt b/QUICKSTART.de.txt index aabaac4..92d4875 100644 --- a/QUICKSTART.de.txt +++ b/QUICKSTART.de.txt @@ -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 index 1cd4363..0000000 --- a/dummy_read_xml.py +++ /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 index 008d0b7..0000000 --- a/dummy_write_xml.py +++ /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 index 56c4cf3..0000000 --- a/nagixsc.py +++ /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 index 0000000..56c4cf3 --- /dev/null +++ b/nagixsc/__init__.py @@ -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 index 0000000..1cd4363 --- /dev/null +++ b/nagixsc_read_xml.py @@ -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 index 0000000..008d0b7 --- /dev/null +++ b/nagixsc_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) +