From 779ef24a34fda65966d0c143a48a489aeee9acd0 Mon Sep 17 00:00:00 2001 From: Sven Velt Date: Fri, 3 Sep 2010 17:05:20 +0200 Subject: [PATCH] Added livestatus as data source ... to conf2http (try "_livestatus" as config file name after putting path/ip&port into conf2http.cfg) ... new command live2xml and be a little bit more verbose in the config file of conf2http Signed-off-by: Sven Velt --- nagixsc/__init__.py | 83 ++++++++++++++++++++++++++++++++++++ nagixsc_conf2http.py | 30 ++++++++++--- nagixsc_live2xml.py | 75 ++++++++++++++++++++++++++++++++ sample-configs/conf2http.cfg | 41 +++++++++++++++++- 4 files changed, 221 insertions(+), 8 deletions(-) create mode 100755 nagixsc_live2xml.py diff --git a/nagixsc/__init__.py b/nagixsc/__init__.py index 182e733..c47c0ea 100644 --- a/nagixsc/__init__.py +++ b/nagixsc/__init__.py @@ -544,3 +544,86 @@ class MyHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): ############################################################################## +def prepare_socket(socket_path): + try: + if socket_path.startswith('/'): + s_family=socket.AF_UNIX + s_sockaddr = socket_path + elif socket_path.startswith('unix:'): + s_family=socket.AF_UNIX + s_sockaddr = socket_path[5:] + elif socket_path.find(':') >= 0: + s_port = socket_path.split(':')[-1] + s_host = ':'.join(socket_path.split(':')[:-1]) + if s_host.startswith('[') and s_host.endswith(']'): + s_host = s_host[1:-1] + (s_family, s_socktype, s_proto, s_canonname, s_sockaddr) = socket.getaddrinfo(s_host, s_port, 0, socket.SOCK_STREAM)[0] + else: + return None + except: + return None + + return (s_family, s_sockaddr) + + +def read_socket(s_opts, commands): + # print '%20s => %s %s' % (sock, s_family, s_sockaddr) + + s = socket.socket(s_opts[0], socket.SOCK_STREAM) + s.connect(s_opts[1]) + for line in commands: + if not line.endswith('\n'): + line += '\n' + s.send(line) + s.shutdown(socket.SHUT_WR) + + answer = '' + try: + while True: + s.settimeout(10) + data = s.recv(32768) + if data: + answer += data + else: + break + except socket.timeout: + return '' + + return answer + + +def livestatus2dict(s_opts, host=None, service=None): + checks = [] + + # Get host information only if NO service specified + if not service: + commands = [] + commands.append('GET hosts\n') + commands.append('Columns: name state plugin_output long_plugin_output last_check\n') + if host: + commands.append('Filter: name = %s' % host) + answer = read_socket(s_opts, commands) + + for line in answer.split('\n')[:-1]: + line = line.split(';') + checks.append({'host_name':line[0], 'service_description':None, 'returncode':line[1], 'output':'\n'.join([line[2], line[3]]).rstrip(), 'timestamp':str(line[4])}) + + # Get service information(s) + commands = [] + commands.append('GET services\n') + commands.append('Columns: host_name description state plugin_output long_plugin_output last_check\n') + if host: + commands.append('Filter: host_name = %s' % host) + if service: + commands.append('Filter: description = %s' % service) + + answer = read_socket(s_opts, commands) + + for line in answer.split('\n')[:-1]: + line = line.split(';') + checks.append({'host_name':line[0], 'service_description':line[1], 'returncode':line[2], 'output':'\n'.join([line[3], line[4]]).rstrip(), 'timestamp':str(line[5])}) + + + return checks +############################################################################## + diff --git a/nagixsc_conf2http.py b/nagixsc_conf2http.py index 7698d43..c738b7a 100755 --- a/nagixsc_conf2http.py +++ b/nagixsc_conf2http.py @@ -43,7 +43,8 @@ config = { 'ssl': False, 'sslcert': None, 'conf_dir': '', - 'pidfile': '/var/run/nagixsc_conf2http.pid' + 'pidfile': '/var/run/nagixsc_conf2http.pid', + 'livestatus_socket' : None, } if 'ip' in cfgread.options('server'): @@ -80,6 +81,9 @@ except ConfigParser.NoOptionError: if 'pidfile' in cfgread.options('server'): config['pidfile'] = cfgread.get('server', 'pidfile') +if 'livestatus_socket' in cfgread.options('server'): + config['livestatus_socket'] = prepare_socket(cfgread.get('server', 'livestatus_socket')) + users = {} for u in cfgread.options('users'): @@ -137,14 +141,26 @@ class Conf2HTTPHandler(MyHTTPRequestHandler): self.http_error(500, 'Config file name contains invalid characters') return - check_config = read_inifile(os.path.join(config['conf_dir'], configfile)) - if not check_config: - self.http_error(500, 'Could not read config file "%s"' % configfile) - return + # Just be sure it exists + checks = None + + # If config file name starts with "_" it's something special + if not configfile.startswith('_'): + # Try to read config file, execute checks + check_config = read_inifile(os.path.join(config['conf_dir'], configfile)) + if not check_config: + self.http_error(500, 'Could not read config file "%s"' % configfile) + return + checks = conf2dict(check_config, host, service) + + elif configfile=='_livestatus.conf' and config['livestatus_socket']: + # Read mk-livestatus and translate into XML + checks = livestatus2dict(config['livestatus_socket'], host, service) + - checks = conf2dict(check_config, host, service) + # No check results? No (good) answer... if not checks: - self.http_error(500, 'No checks executed') + self.http_error(500, 'No check results') return self.send_response(200) diff --git a/nagixsc_live2xml.py b/nagixsc_live2xml.py new file mode 100755 index 0000000..3e2729f --- /dev/null +++ b/nagixsc_live2xml.py @@ -0,0 +1,75 @@ +#!/usr/bin/python + +import optparse +import sys + +############################################################################## + +from nagixsc import * + +############################################################################## + +parser = optparse.OptionParser() + +parser.add_option('-s', '', dest='socket', help='Livestatus socket to connect') +parser.add_option('-o', '', dest='outfile', help='Output file name, "-" for STDOUT or HTTP POST URL') +parser.add_option('-e', '', dest='encoding', help='Encoding ("%s")' % '", "'.join(available_encodings()) ) +parser.add_option('-H', '', dest='host', help='Hostname/section to search for in config file') +parser.add_option('-D', '', dest='service', help='Service description to search for in config file (needs -H)') +parser.add_option('-l', '', dest='httpuser', help='HTTP user name, if outfile is HTTP(S) URL') +parser.add_option('-a', '', dest='httppasswd', help='HTTP password, if outfile is HTTP(S) URL') +parser.add_option('-v', '', action='count', dest='verb', help='Verbose output') + +parser.set_defaults(socket=None) +parser.set_defaults(outfile='-') +parser.set_defaults(encoding='base64') +parser.set_defaults(host=None) +parser.set_defaults(service=None) +parser.set_defaults(verb=0) + +(options, args) = parser.parse_args() + +############################################################################## + +if not options.socket: + print 'No socket specified!\n' + parser.print_help() + sys.exit(1) + +if not check_encoding(options.encoding): + print 'Wrong encoding method "%s"!' % options.encoding + print 'Could be one of: "%s"' % '", "'.join(available_encodings()) + sys.exit(1) + +############################################################################## + +# Prepare socket +s_opts = prepare_socket(options.socket) + +# Read informations from livestatus +checks = livestatus2dict(s_opts, options.host, options.service) + +# Convert to XML +xmldoc = xml_from_dict(checks, options.encoding) + +# Output +if options.outfile.startswith('http'): + (headers, body) = encode_multipart(xmldoc, options.httpuser, options.httppasswd) + + try: + response = urllib2.urlopen(urllib2.Request(options.outfile, body, headers)).read() + except urllib2.HTTPError, error: + print error + sys.exit(6) + except urllib2.URLError, error: + print error.reason[1] + sys.exit(7) + + print response + +elif options.outfile == '-': + xmldoc.saveFormatFile('-', format=1) + +else: + xmldoc.saveFile(options.outfile) + diff --git a/sample-configs/conf2http.cfg b/sample-configs/conf2http.cfg index 4e0e5a1..c6001f8 100644 --- a/sample-configs/conf2http.cfg +++ b/sample-configs/conf2http.cfg @@ -1,16 +1,55 @@ ; Lines starting with "#" are defaults [server] +; ### ip (0.0.0.0) ### +; IP to bind server #ip: 0.0.0.0 + +; ### port (15666) ### +; Port to bind server #port: 15666 + +; ### ssl (false) ### +; Use SSL for server communication #ssl: false -#sslcert: server.pem +; ### sslcert (no default) ### +; Read server key and certificate from this file +; +; Example +;sslcert: server.pem + +; ### conf_dir (no default) ### +; Directory where your config files are stored +; +; Absolute file names recommended, but for quick start it is set relative +; to the source directory conf_dir: ./sample-configs/conf +; ### pidfile (/var/run/nagixsc_conf2http.pid) ### +; Where to store pid file if daemonizing +; #pidfile: /var/run/nagixsc_conf2http.pid +; ### livestatus_socket (no default) ### +; Query mk-livestatus (http://mathias-kettner.de/checkmk_livestatus.html) +; "livestatus_socket" could be +; - local path and filename of socket ("/var/lib/nagios3/rw/live.sock") +; - IPv4 address and port ("127.0.0.1:6557") +; - IPv6 address and port ("[::1]:6557" or "::1:6557") +; - DNS resolvable name and port ("localhost:6557") - could be IPv4 *or* IPv6! +; +; Examples: +;livestatus_socket: /var/lib/nagios3/rw/live.sock +;livestatus_socket: /usr/local/nagios/var/rw/live +;livestatus_socket: 127.0.0.1:6557 +;livestatus_socket: [::1]:6557 +;livestatus_socket: localhost:6557 +livestatus_socket: 172.21.254.99:6557 + [users] +; All users who are allowed to connect and request informations are stored here. +; Passwords must be md5 encrypted, for example in shell use: ; echo -n "Password" | md5sum - nagixsc: 019b0966d98fb71d1a4bc4ca0c81d5cc ; PW: nagixsc -- 2.30.2