1 import BaseHTTPServer
2 import ConfigParser
3 import SocketServer
4 import base64
5 import datetime
6 import libxml2
7 import os
8 import random
9 import shlex
10 import socket
11 import string
12 import subprocess
13 import sys
15 def debug(level, verb, string):
16 if level <= verb:
17 print "%s: %s" % (level, string)
20 ##############################################################################
22 def available_encodings():
23 return ['base64', 'plain',]
26 def check_encoding(enc):
27 if enc in available_encodings():
28 return True
29 else:
30 return False
33 def decode(data, encoding):
34 if encoding == 'plain':
35 return data
36 else:
37 return base64.b64decode(data)
40 def encode(data, encoding=None):
41 if encoding == 'plain':
42 return data
43 else:
44 return base64.b64encode(data)
47 ##############################################################################
49 def read_inifile(inifile):
50 config = ConfigParser.RawConfigParser()
51 config.optionxform = str # We need case-sensitive options
52 ini_list = config.read(inifile)
54 if ini_list:
55 return config
56 else:
57 return False
60 ##############################################################################
62 def exec_check(host_name, service_descr, cmdline):
63 try:
64 cmd = subprocess.Popen(shlex.split(cmdline), stdout=subprocess.PIPE)
65 output = cmd.communicate()[0].rstrip()
66 retcode = cmd.returncode
67 except OSError:
68 output = 'Could not execute "%s"' % cmdline
69 retcode = 127
71 return {'host_name':host_name, 'service_description':service_descr, 'returncode':retcode, 'output':output, 'timestamp':datetime.datetime.now().strftime('%s')}
74 ##############################################################################
76 def conf2dict(config, opt_host=None, opt_service=None):
77 checks = []
79 # Sections are Hosts (not 'nagixsc'), options in sections are Services
80 hosts = config.sections()
81 if 'nagixsc' in hosts:
82 hosts.remove('nagixsc')
84 # Filter out host/section if it exists
85 if opt_host:
86 if opt_host in hosts:
87 hosts = [opt_host,]
88 else:
89 hosts = []
91 for host in hosts:
92 # Overwrite section/host name with '_host_name'
93 if config.has_option(host,'_host_name'):
94 host_name = config.get(host,'_host_name')
95 else:
96 host_name = host
99 services = config.options(host)
100 # Look for host check
101 if '_host_check' in services and not opt_service:
102 cmdline = config.get(host, '_host_check')
103 check = exec_check(host_name, None, cmdline)
104 checks.append(check)
107 # Filter out service if given in cmd line options
108 if opt_service:
109 if opt_service in services:
110 services = [opt_service,]
111 else:
112 services = []
114 for service in services:
115 # If option starts with '_' it may be a NagixSC option in the future
116 if service[0] != '_':
117 cmdline = config.get(host, service)
119 check = exec_check(host_name, service, cmdline)
120 checks.append(check)
122 return checks
125 ##############################################################################
127 def dict2out_passive(checks, xmltimestamp, opt_pipe, opt_verb=0):
128 FORMAT_HOST = '[%s] PROCESS_HOST_CHECK_RESULT;%s;%s;%s'
129 FORMAT_SERVICE = '[%s] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%s;%s'
130 count_services = 0
131 now = datetime.datetime.now().strftime('%s')
133 # Prepare
134 if opt_verb <= 2:
135 pipe = open(opt_pipe, "w")
136 else:
137 pipe = None
139 # Output
140 for check in checks:
141 count_services += 1
142 if check.has_key('timestamp'):
143 timestamp = check['timestamp']
144 else:
145 timestamp = xmltimestamp
146 count_services += 1
148 if check['service_description'] == None or check['service_description'] == '':
149 # Host check
150 line = FORMAT_HOST % (now, check['host_name'], check['returncode'], check['output'].replace('\n', '\\n'))
151 else:
152 # Service check
153 line = FORMAT_SERVICE % (now, check['host_name'], check['service_description'], check['returncode'], check['output'].replace('\n', '\\n'))
155 if pipe:
156 pipe.write(line + '\n')
157 debug(2, opt_verb, line)
159 # Close
160 if pipe:
161 pipe.close()
162 else:
163 print "Passive check results NOT written to Nagios pipe due to -vvv!"
165 return count_services
168 def dict2out_checkresult(checks, xmltimestamp, opt_checkresultdir, opt_verb):
169 count_services = 0
170 count_failed = 0
171 list_failed = []
172 chars = string.letters + string.digits
173 ctimestamp = datetime.datetime.now().ctime()
175 for check in checks:
176 count_services += 1
177 if check.has_key('timestamp'):
178 timestamp = check['timestamp']
179 else:
180 timestamp = xmltimestamp
182 filename = os.path.join(opt_checkresultdir, 'c' + ''.join([random.choice(chars) for i in range(6)]))
183 try:
184 crfile = open(filename, "w")
185 if check['service_description'] == None or check['service_description'] == '':
186 # Host check
187 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') ) )
188 else:
189 # Service check
190 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') ) )
191 crfile.close()
193 # Create OK file
194 open(filename + '.ok', 'w').close()
195 except:
196 count_failed += 1
197 list_failed.append([filename, check['host_name'], check['service_description']])
199 return (count_services, count_failed, list_failed)
202 ##############################################################################
204 def read_xml(options):
205 if options.url != None:
206 import urllib2
208 if options.httpuser and options.httppasswd:
209 passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
210 passman.add_password(None, options.url, options.httpuser, options.httppasswd)
211 authhandler = urllib2.HTTPBasicAuthHandler(passman)
212 opener = urllib2.build_opener(authhandler)
213 urllib2.install_opener(opener)
215 try:
216 response = urllib2.urlopen(options.url)
217 except urllib2.HTTPError, error:
218 print error
219 sys.exit(0)
220 except urllib2.URLError, error:
221 print error.reason[1]
222 sys.exit(0)
224 doc = libxml2.parseDoc(response.read())
225 response.close()
227 else:
228 doc = libxml2.parseFile(options.file)
230 return doc
233 def read_xml_from_string(content):
234 return libxml2.parseDoc(content)
237 ##############################################################################
239 def xml_check_version(xmldoc):
240 # FIXME: Check XML structure
241 try:
242 xmlnagixsc = xmldoc.xpathNewContext().xpathEval('/nagixsc')[0]
243 except:
244 return (False, 'Not a Nag(IX)SC XML file!')
246 try:
247 if xmlnagixsc.prop('version') != "1.0":
248 return (False, 'Wrong version (found "%s", need "1.0") of XML file!' % xmlnagixsc.prop('version'))
249 except:
250 return (False, 'No version information found in XML file!')
252 return (True, 'XML seems to be ok')
255 def xml_get_timestamp(xmldoc):
256 try:
257 timestamp = int(xmldoc.xpathNewContext().xpathEval('/nagixsc/timestamp')[0].get_content())
258 except:
259 return False
261 return timestamp
264 def xml_to_dict(xmldoc, verb=0, hostfilter=None, servicefilter=None):
265 checks = []
266 now = int(datetime.datetime.now().strftime('%s'))
267 filetimestamp = reset_future_timestamp(xml_get_timestamp(xmldoc), now)
269 if hostfilter:
270 hosts = xmldoc.xpathNewContext().xpathEval('/nagixsc/host[name="%s"] | /nagixsc/host[name="%s"]' % (hostfilter, encode(hostfilter)))
271 else:
272 hosts = xmldoc.xpathNewContext().xpathEval('/nagixsc/host')
274 for host in hosts:
275 xmlhostname = host.xpathEval('name')[0]
276 hostname = decode(xmlhostname.get_content(), xmlhostname.prop('encoding'))
277 debug(2, verb, 'Found host "%s"' % hostname)
279 # Look for Host check result
280 if host.xpathEval('returncode'):
281 retcode = host.xpathEval('returncode')[0].get_content()
282 else:
283 retcode = None
285 if host.xpathEval('output'):
286 xmloutput = host.xpathEval('output')[0]
287 output = decode(xmloutput.get_content(), xmloutput.prop('encoding')).rstrip()
288 else:
289 output = None
291 if host.xpathEval('timestamp'):
292 timestamp = reset_future_timestamp(int(host.xpathEval('timestamp')[0].get_content()), now)
293 else:
294 timestamp = filetimestamp
296 if retcode and output:
297 checks.append({'host_name':hostname, 'service_description':None, 'returncode':retcode, 'output':output, 'timestamp':timestamp})
300 # Look for service filter
301 if servicefilter:
302 services = host.xpathEval('service[description="%s"] | service[description="%s"]' % (servicefilter, encode(servicefilter)))
303 else:
304 services = host.xpathEval('service')
306 # Loop over services in host
307 for service in services:
308 service_dict = {}
310 xmldescr = service.xpathEval('description')[0]
311 xmloutput = service.xpathEval('output')[0]
313 srvdescr = decode(xmldescr.get_content(), xmldescr.prop('encoding'))
314 retcode = service.xpathEval('returncode')[0].get_content()
315 output = decode(xmloutput.get_content(), xmloutput.prop('encoding')).rstrip()
317 try:
318 timestamp = reset_future_timestamp(int(service.xpathEval('timestamp')[0].get_content()), now)
319 except:
320 timestamp = filetimestamp
322 debug(2, verb, 'Found service "%s"' % srvdescr)
324 service_dict = {'host_name':hostname, 'service_description':srvdescr, 'returncode':retcode, 'output':output, 'timestamp':timestamp}
325 checks.append(service_dict)
327 debug(1, verb, 'Host: "%s" - Service: "%s" - RetCode: "%s" - Output: "%s"' % (hostname, srvdescr, retcode, output) )
329 return checks
332 def xml_from_dict(checks, encoding='base64'):
333 lasthost = None
335 db = [(check['host_name'], check) for check in checks]
336 db.sort()
338 xmldoc = libxml2.newDoc('1.0')
339 xmlroot = xmldoc.newChild(None, 'nagixsc', None)
340 xmlroot.setProp('version', '1.0')
341 xmltimestamp = xmlroot.newChild(None, 'timestamp', datetime.datetime.now().strftime('%s'))
343 for entry in db:
344 check = entry[1]
346 if check['host_name'] != lasthost:
347 xmlhost = xmlroot.newChild(None, 'host', None)
348 xmlhostname = xmlhost.newChild(None, 'name', encode(check['host_name'], encoding))
349 lasthost = check['host_name']
351 if check['service_description'] == '' or check['service_description'] == None:
352 # Host check result
353 xmlreturncode = xmlhost.newChild(None, 'returncode', str(check['returncode']))
354 xmloutput = xmlhost.newChild(None, 'output', encode(check['output'], encoding))
355 xmloutput.setProp('encoding', encoding)
356 if check.has_key('timestamp'):
357 xmltimestamp = xmlhost.newChild(None, 'timestamp', str(check['timestamp']))
358 else:
359 # Service check result
360 xmlservice = xmlhost.newChild(None, 'service', None)
361 xmlname = xmlservice.newChild(None, 'description', encode(check['service_description'], encoding))
362 xmlname.setProp('encoding', encoding)
363 xmlreturncode = xmlservice.newChild(None, 'returncode', str(check['returncode']))
364 xmloutput = xmlservice.newChild(None, 'output', encode(check['output'], encoding))
365 xmloutput.setProp('encoding', encoding)
366 if check.has_key('timestamp'):
367 xmltimestamp = xmlservice.newChild(None, 'timestamp', str(check['timestamp']))
369 return xmldoc
372 def check_mark_outdated(check, now, maxtimediff, markold):
373 timedelta = now - check['timestamp']
374 if timedelta > maxtimediff:
375 check['output'] = 'Nag(ix)SC: Check result is %s(>%s) seconds old - %s' % (timedelta, maxtimediff, check['output'])
376 if markold:
377 check['returncode'] = 3
378 return check
381 def reset_future_timestamp(timestamp, now):
382 if timestamp <= now:
383 return timestamp
384 else:
385 return now
387 ##############################################################################
389 class MyHTTPServer(BaseHTTPServer.HTTPServer):
390 def __init__(self, server_address, HandlerClass, ssl=False, sslpemfile=None):
391 if ssl:
392 # FIXME: SSL is in Py2.6
393 try:
394 from OpenSSL import SSL
395 except:
396 print 'No Python OpenSSL wrapper/bindings found!'
397 sys.exit(127)
399 SocketServer.BaseServer.__init__(self, server_address, HandlerClass)
400 context = SSL.Context(SSL.SSLv23_METHOD)
401 context.use_privatekey_file (sslpemfile)
402 context.use_certificate_file(sslpemfile)
403 self.socket = SSL.Connection(context, socket.socket(self.address_family, self.socket_type))
404 else:
405 SocketServer.BaseServer.__init__(self, server_address, HandlerClass)
406 self.socket = socket.socket(self.address_family, self.socket_type)
408 self.server_bind()
409 self.server_activate()
412 class MyHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
413 def setup(self):
414 self.connection = self.request
415 self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
416 self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
418 ##############################################################################