Code

21d2b0c1b9e5b5f46e400674e3e6ac65489b2891
[nagixsc.git] / nagixsc.py
1 import BaseHTTPServer
2 import ConfigParser
3 import SocketServer
4 import base64
5 import datetime
6 import libxml2
7 import shlex
8 import socket
9 import subprocess
10 import sys
12 def debug(level, verb, string):
13         if level <= verb:
14                 print "%s: %s" % (level, string)
17 ##############################################################################
19 def available_encodings():
20         return ['base64', 'plain',]
23 def check_encoding(enc):
24         if enc in available_encodings():
25                 return True
26         else:
27                 return False
30 def decode(data, encoding):
31         if encoding == 'plain':
32                 return data
33         else:
34                 return base64.b64decode(data)
37 def encode(data, encoding=None):
38         if encoding == 'plain':
39                 return data
40         else:
41                 return base64.b64encode(data)
44 ##############################################################################
46 def read_inifile(inifile):
47         config = ConfigParser.RawConfigParser()
48         config.optionxform = str # We need case-sensitive options
49         ini_list = config.read(inifile)
51         if ini_list:
52                 return config
53         else:
54                 return False
57 ##############################################################################
59 def exec_check(host_name, service_descr, cmdline):
60         try:
61                 cmd     = subprocess.Popen(shlex.split(cmdline), stdout=subprocess.PIPE)
62                 output  = cmd.communicate()[0].rstrip()
63                 retcode = cmd.returncode
64         except OSError:
65                 output  = 'Could not execute "%s"' % cmdline
66                 retcode = 127
68         return {'host_name':host_name, 'service_description':service_descr, 'returncode':retcode, 'output':output, 'timestamp':datetime.datetime.now().strftime('%s')}
71 ##############################################################################
73 def conf2dict(config, opt_host=None, opt_service=None):
74         checks = []
76         # Sections are Hosts (not 'nagixsc'), options in sections are Services
77         hosts = config.sections()
78         if 'nagixsc' in hosts:
79                 hosts.remove('nagixsc')
81         # Filter out host/section if it exists
82         if opt_host:
83                 if opt_host in hosts:
84                         hosts = [opt_host,]
85                 else:
86                         hosts = []
88         for host in hosts:
89                 # Overwrite section/host name with '_host_name'
90                 if config.has_option(host,'_host_name'):
91                         host_name = config.get(host,'_host_name')
92                 else:
93                         host_name = host
96                 services = config.options(host)
97                 # Look for host check
98                 if '_host_check' in services and not opt_service:
99                         cmdline = config.get(host, '_host_check')
100                         check = exec_check(host_name, None, cmdline)
101                         checks.append(check)
104                 # Filter out service if given in cmd line options
105                 if opt_service:
106                         if opt_service in services:
107                                 services = [opt_service,]
108                         else:
109                                 services = []
111                 for service in services:
112                         # If option starts with '_' it may be a NagixSC option in the future
113                         if service[0] != '_':
114                                 cmdline = config.get(host, service)
116                                 check = exec_check(host_name, service, cmdline)
117                                 checks.append(check)
119         return checks
122 ##############################################################################
124 def read_xml(options):
125         if options.url != None:
126                 import urllib2
128                 if options.httpuser and options.httppasswd:
129                         passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
130                         passman.add_password(None, options.url, options.httpuser, options.httppasswd)
131                         authhandler = urllib2.HTTPBasicAuthHandler(passman)
132                         opener = urllib2.build_opener(authhandler)
133                         urllib2.install_opener(opener)
135                 try:
136                         response = urllib2.urlopen(options.url)
137                 except urllib2.HTTPError, error:
138                         print error
139                         sys.exit(0)
140                 except urllib2.URLError, error:
141                         print error.reason[1]
142                         sys.exit(0)
144                 doc = libxml2.parseDoc(response.read())
145                 response.close()
147         else:
148                 doc = libxml2.parseFile(options.file)
150         return doc
153 ##############################################################################
155 def xml_check_version(xmldoc):
156         # FIXME: Check XML structure
157         try:
158                 xmlnagixsc = xmldoc.xpathNewContext().xpathEval('/nagixsc')[0]
159         except:
160                 return (False, 'Not a Nag(IX)SC XML file!')
162         try:
163                 if xmlnagixsc.prop('version') != "1.0":
164                         return (False, 'Wrong version (found "%s", need "1.0") of XML file!' % xmlnagixsc.prop('version'))
165         except:
166                 return (False, 'No version information found in XML file!')
168         return (True, 'XML seems to be ok')
171 def xml_get_timestamp(xmldoc):
172         try:
173                 timestamp = int(xmldoc.xpathNewContext().xpathEval('/nagixsc/timestamp')[0].get_content())
174         except:
175                 return False
177         return timestamp
180 def xml_to_dict(xmldoc, verb=0, hostfilter=None, servicefilter=None):
181         checks = []
182         now = int(datetime.datetime.now().strftime('%s'))
183         filetimestamp = reset_future_timestamp(xml_get_timestamp(xmldoc), now)
185         if hostfilter:
186                 hosts = xmldoc.xpathNewContext().xpathEval('/nagixsc/host[name="%s"] | /nagixsc/host[name="%s"]' % (hostfilter, encode(hostfilter)))
187         else:
188                 hosts = xmldoc.xpathNewContext().xpathEval('/nagixsc/host')
190         for host in hosts:
191                 xmlhostname = host.xpathEval('name')[0]
192                 hostname = decode(xmlhostname.get_content(), xmlhostname.prop('encoding'))
193                 debug(2, verb, 'Found host "%s"' % hostname)
195                 # Look for Host check result
196                 if host.xpathEval('returncode'):
197                         retcode   = host.xpathEval('returncode')[0].get_content()
198                 else:
199                         retcode   = None
201                 if host.xpathEval('output'):
202                         xmloutput = host.xpathEval('output')[0]
203                         output    = decode(xmloutput.get_content(), xmloutput.prop('encoding')).rstrip()
204                 else:
205                         output    = None
207                 if host.xpathEval('timestamp'):
208                         timestamp = reset_future_timestamp(int(host.xpathEval('timestamp')[0].get_content()), now)
209                 else:
210                         timestamp = filetimestamp
212                 if retcode and output:
213                         checks.append({'host_name':hostname, 'service_description':None, 'returncode':retcode, 'output':output, 'timestamp':timestamp})
216                 # Look for service filter
217                 if servicefilter:
218                         services = host.xpathEval('service[description="%s"] | service[description="%s"]' % (servicefilter, encode(servicefilter)))
219                 else:
220                         services = host.xpathEval('service')
222                 # Loop over services in host
223                 for service in services:
224                         service_dict = {}
226                         xmldescr  = service.xpathEval('description')[0]
227                         xmloutput = service.xpathEval('output')[0]
229                         srvdescr = decode(xmldescr.get_content(), xmldescr.prop('encoding'))
230                         retcode  = service.xpathEval('returncode')[0].get_content()
231                         output   = decode(xmloutput.get_content(), xmloutput.prop('encoding')).rstrip()
233                         try:
234                                 timestamp = reset_future_timestamp(int(service.xpathEval('timestamp')[0].get_content()), now)
235                         except:
236                                 timestamp = filetimestamp
238                         debug(2, verb, 'Found service "%s"' % srvdescr)
240                         service_dict = {'host_name':hostname, 'service_description':srvdescr, 'returncode':retcode, 'output':output, 'timestamp':timestamp}
241                         checks.append(service_dict)
243                         debug(1, verb, 'Host: "%s" - Service: "%s" - RetCode: "%s" - Output: "%s"' % (hostname, srvdescr, retcode, output) )
245         return checks
248 def xml_from_dict(checks, encoding='base64'):
249         lasthost = None
251         db = [(check['host_name'], check) for check in checks]
252         db.sort()
254         xmldoc = libxml2.newDoc('1.0')
255         xmlroot = xmldoc.newChild(None, 'nagixsc', None)
256         xmlroot.setProp('version', '1.0')
257         xmltimestamp = xmlroot.newChild(None, 'timestamp', datetime.datetime.now().strftime('%s'))
259         for entry in db:
260                 check = entry[1]
262                 if check['host_name'] != lasthost:
263                         xmlhost = xmlroot.newChild(None, 'host', None)
264                         xmlhostname = xmlhost.newChild(None, 'name', encode(check['host_name'], encoding))
265                         lasthost = check['host_name']
267                 if check['service_description'] == '' or check['service_description'] == None:
268                         # Host check result
269                         xmlreturncode = xmlhost.newChild(None, 'returncode', str(check['returncode']))
270                         xmloutput     = xmlhost.newChild(None, 'output', encode(check['output'], encoding))
271                         xmloutput.setProp('encoding', encoding)
272                         if check.has_key('timestamp'):
273                                 xmltimestamp  = xmlhost.newChild(None, 'timestamp', str(check['timestamp']))
274                 else:
275                         # Service check result
276                         xmlservice    = xmlhost.newChild(None, 'service', None)
277                         xmlname       = xmlservice.newChild(None, 'description', encode(check['service_description'], encoding))
278                         xmlname.setProp('encoding', encoding)
279                         xmlreturncode = xmlservice.newChild(None, 'returncode', str(check['returncode']))
280                         xmloutput     = xmlservice.newChild(None, 'output', encode(check['output'], encoding))
281                         xmloutput.setProp('encoding', encoding)
282                         if check.has_key('timestamp'):
283                                 xmltimestamp  = xmlservice.newChild(None, 'timestamp', str(check['timestamp']))
285         return xmldoc
288 def check_mark_outdated(check, now, maxtimediff, markold):
289         timedelta = now - check['timestamp']
290         if timedelta > maxtimediff:
291                 check['output'] = 'Nag(ix)SC: Check result is %s(>%s) seconds old - %s' % (timedelta, maxtimediff, check['output'])
292                 if markold:
293                         check['returncode'] = 3
294         return check
297 def reset_future_timestamp(timestamp, now):
298         if timestamp <= now:
299                 return timestamp
300         else:
301                 return now
303 ##############################################################################
305 class MyHTTPServer(BaseHTTPServer.HTTPServer):
306         def __init__(self, server_address, HandlerClass, ssl=False, sslpemfile=None):
307                 if ssl:
308                         # FIXME: SSL is in Py2.6
309                         try:
310                                 from OpenSSL import SSL
311                         except:
312                                 print 'No Python OpenSSL wrapper/bindings found!'
313                                 sys.exit(127)
315                         SocketServer.BaseServer.__init__(self, server_address, HandlerClass)
316                         context = SSL.Context(SSL.SSLv23_METHOD)
317                         context.use_privatekey_file (sslpemfile)
318                         context.use_certificate_file(sslpemfile)
319                         self.socket = SSL.Connection(context, socket.socket(self.address_family, self.socket_type))
320                 else:
321                         SocketServer.BaseServer.__init__(self, server_address, HandlerClass)
322                         self.socket = socket.socket(self.address_family, self.socket_type)
324                 self.server_bind()
325                 self.server_activate()
328 class MyHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
329         def setup(self):
330                 self.connection = self.request
331                 self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
332                 self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
334 ##############################################################################