diff --git a/nagixsc/__init__.py b/nagixsc/__init__.py
index 327208453ff60fb001768708c698a4f67a8301f6..827a104b26c849d855a4e919d3afcad22e342484 100644 (file)
--- a/nagixsc/__init__.py
+++ b/nagixsc/__init__.py
import os
import random
import shlex
+import signal
import socket
import string
import subprocess
import sys
+import urllib2
def debug(level, verb, string):
if level <= verb:
##############################################################################
+class ExecTimeoutError(Exception):
+ pass
+
+##############################################################################
+
def available_encodings():
return ['base64', 'plain',]
##############################################################################
-def exec_check(host_name, service_descr, cmdline):
+def exec_timeout_handler(signum, frame):
+ raise ExecTimeoutError
+
+def exec_check(host_name, service_descr, cmdline, cmdprefix='', timeout=None, timeout_returncode=2):
+ cmdarray = shlex.split(cmdline)
+
+ check = {}
+ check['host_name'] = host_name
+ check['service_description'] = service_descr
+
+ if len(cmdarray) == 0:
+ check['output'] = 'No command line specified!'
+ check['returncode'] = 127
+ return check
+
+ check['commandline'] = cmdline
+ check['command'] = cmdarray[0].split(os.path.sep)[-1]
+
+ if cmdprefix:
+ check['fullcommandline'] = cmdprefix + ' ' + cmdline
+ cmdarray = shlex.split(cmdprefix) + cmdarray
+ else:
+ check['fullcommandline'] = cmdline
+
+ if timeout:
+ signal.signal(signal.SIGALRM, exec_timeout_handler)
+ signal.alarm(timeout)
+
try:
- cmd = subprocess.Popen(shlex.split(cmdline), stdout=subprocess.PIPE)
- output = cmd.communicate()[0].rstrip()
- retcode = cmd.returncode
+ cmd = subprocess.Popen(cmdarray, stdout=subprocess.PIPE)
+ check['output'] = cmd.communicate()[0].rstrip()
+ check['returncode'] = cmd.returncode
except OSError:
- output = 'Could not execute "%s"' % cmdline
- retcode = 127
+ check['output'] = 'Could not execute "%s"' % cmdline
+ check['returncode'] = 127
+ except ExecTimeoutError:
+ check['output'] = 'Plugin timed out after %s seconds' % timeout
+ check['returncode'] = timeout_returncode
+
+ if timeout:
+ signal.alarm(0)
+ try:
+ if sys.version_info >= (2, 6):
+ cmd.terminate()
+ else:
+ os.kill(cmd.pid, 15)
+ except OSError:
+ pass
- return {'host_name':host_name, 'service_description':service_descr, 'returncode':retcode, 'output':output, 'timestamp':datetime.datetime.now().strftime('%s')}
+ check['timestamp'] = datetime.datetime.now().strftime('%s')
+ return check
##############################################################################
def conf2dict(config, opt_host=None, opt_service=None):
checks = []
+ # Read "plugin_timeout" from "[nagixsc]", default "None" (no timeout)
+ try:
+ timeout = config.getint('nagixsc','plugin_timeout')
+ except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
+ timeout = None
+
+ # Read "plugin_timeout_returncode" from "[nagixsc]", default "2" (CRITICAL)
+ try:
+ timeout_returncode = config.getint('nagixsc','plugin_timeout_returncode')
+ except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
+ timeout_returncode = 2
+
+ # Read "add_pnp4nagios_template_hint" from "[nagixsc]", default "False"
+ try:
+ add_pnp4nagios_template_hint = config.getboolean('nagixsc','add_pnp4nagios_template_hint')
+ except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
+ add_pnp4nagios_template_hint = False
+
+ # Read "command_prefix" from "[nagixsc]", default "" (empty string)
+ try:
+ cmdprefix_conffile = config.get('nagixsc','command_prefix')
+ except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
+ cmdprefix_conffile = ''
+
# Sections are Hosts (not 'nagixsc'), options in sections are Services
hosts = config.sections()
if 'nagixsc' in hosts:
services = config.options(host)
+ # Look for host/section specific "command_prefix"
+ if '_command_prefix' in services:
+ cmdprefix = config.get(host, '_command_prefix')
+ else:
+ cmdprefix = cmdprefix_conffile
+
# 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)
+ check = exec_check(host_name, None, cmdline, cmdprefix, timeout, timeout_returncode)
+ if add_pnp4nagios_template_hint and '|' in check['output']:
+ check['output'] += ' [%s]' % check['command']
checks.append(check)
if service[0] != '_':
cmdline = config.get(host, service)
- check = exec_check(host_name, service, cmdline)
+ check = exec_check(host_name, service, cmdline, cmdprefix, timeout, timeout_returncode)
+ if add_pnp4nagios_template_hint and '|' in check['output']:
+ check['output'] += ' [%s]' % check['command']
checks.append(check)
return checks
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'))
+ line = FORMAT_HOST % (timestamp, 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'))
+ line = FORMAT_SERVICE % (timestamp, check['host_name'], check['service_description'], check['returncode'], check['output'].replace('\n', '\\n'))
if pipe:
pipe.write(line + '\n')
return count_services
-def dict2out_checkresult(checks, xmltimestamp, opt_checkresultdir, opt_verb):
+def dict2out_checkresult(checks, xmltimestamp, opt_checkresultdir, opt_verb=0):
count_services = 0
count_failed = 0
list_failed = []
chars = string.letters + string.digits
ctimestamp = datetime.datetime.now().ctime()
+ random.seed()
for check in checks:
count_services += 1
def read_xml(options):
if options.url != None:
- import urllib2
if options.httpuser and options.httppasswd:
passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
return libxml2.parseDoc(content)
+def write_xml(xmldoc, outfile, httpuser=None, httppasswd=None):
+ if outfile.startswith('http'):
+ (headers, body) = encode_multipart(xmldoc, httpuser, httppasswd)
+ response = urllib2.urlopen(urllib2.Request(outfile, body, headers)).read()
+ return response
+
+ elif outfile == '-':
+ xmldoc.saveFormatFile('-', format=1)
+ return None
+
+ else:
+ xmldoc.saveFile(outfile)
+ return None
+
+
+def write_xml_or_die(xmldoc, outfile, httpuser=None, httppasswd=None):
+ try:
+ response = write_xml(xmldoc, outfile, httpuser, httppasswd)
+ except urllib2.HTTPError, error:
+ print error
+ sys.exit(11)
+ except urllib2.URLError, error:
+ print error.reason[1]
+ sys.exit(12)
+
+ return response
+
+
##############################################################################
def xml_check_version(xmldoc):
else:
timestamp = filetimestamp
- if retcode and output:
+ # Append only if no service filter
+ if not servicefilter and retcode and output:
checks.append({'host_name':hostname, 'service_description':None, 'returncode':retcode, 'output':output, 'timestamp':timestamp})
return xmldoc
+def xml_merge(xmldocs):
+ checks = []
+ for xmldoc in xmldocs:
+ checks.extend(xml_to_dict(xmldoc))
+ newxmldoc = xml_from_dict(checks)
+ return newxmldoc
+
+
def check_mark_outdated(check, now, maxtimediff, markold):
timedelta = now - check['timestamp']
if timedelta > maxtimediff:
##############################################################################
-def encode_multipart(xmldoc, httpuser, httppasswd):
+def encode_multipart(xmldoc, httpuser=None, httppasswd=None):
BOUNDARY = mimetools.choose_boundary()
CRLF = '\r\n'
L = []
@@ -416,7 +534,7 @@ def daemonize(pidfile=None, stdin='/dev/null', stdout='/dev/null', stderr='/dev/
if pid > 0:
sys.exit(0)
except OSError, e:
- sys.stderr.write("1st fork failed: (%d) %sn" % (e.errno, e.strerror))
+ sys.stderr.write("1st fork failed: (%d) %s\n" % (e.errno, e.strerror))
sys.exit(1)
# Prepare 2nd fork
os.chdir("/")
@@ -428,8 +546,18 @@ def daemonize(pidfile=None, stdin='/dev/null', stdout='/dev/null', stderr='/dev/
if pid > 0:
sys.exit(0)
except OSError, e:
- sys.stderr.write("2nd fork failed: (%d) %sn" % (e.errno, e.strerror))
+ sys.stderr.write("2nd fork failed: (%d) %s\n" % (e.errno, e.strerror))
sys.exit(1)
+
+ # Try to write PID file
+ if pidfile:
+ pid = str(os.getpid())
+ try:
+ file(pidfile, 'w+').write('%s\n' % pid)
+ except IOError:
+ sys.stderr.write("Could not write PID file, exiting...\n")
+ sys.exit(1)
+
# Redirect stdin, stdout, stderr
sys.stdout.flush()
sys.stderr.flush()
@@ -440,15 +568,11 @@ def daemonize(pidfile=None, stdin='/dev/null', stdout='/dev/null', stderr='/dev/
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
- if pidfile:
- pid = str(os.getpid())
- file(pidfile, 'w+').write('%s\n' % pid)
-
return
##############################################################################
-class MyHTTPServer(BaseHTTPServer.HTTPServer):
+class MyHTTPServer(SocketServer.ForkingMixIn, BaseHTTPServer.HTTPServer):
def __init__(self, server_address, HandlerClass, ssl=False, sslpemfile=None):
if ssl:
# FIXME: SSL is in Py2.6
##############################################################################
+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
+##############################################################################
+