Code

Added livestatus as data source
authorSven Velt <sven@velt.de>
Fri, 3 Sep 2010 15:05:20 +0000 (17:05 +0200)
committerSven Velt <sven@velt.de>
Fri, 3 Sep 2010 15:05:20 +0000 (17:05 +0200)
... 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 <sven@velt.de>
nagixsc/__init__.py
nagixsc_conf2http.py
nagixsc_live2xml.py [new file with mode: 0755]
sample-configs/conf2http.cfg

index 182e7332b7c6d4704533303ac352e4c39c314ee1..c47c0ea64ae85accf780046295bd1048b1461834 100644 (file)
@@ -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
+##############################################################################
+
index 7698d43a18cfb8811a300ecf730850e69cfe9010..c738b7acfe7eaceae00e175dd8f2887e377b941a 100755 (executable)
@@ -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 (executable)
index 0000000..3e2729f
--- /dev/null
@@ -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)
+
index 4e0e5a1e9a0669010162c6001946704c7e68585d..c6001f8f407f245b301010befbc48177ca4a26f8 100644 (file)
@@ -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