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 ##############################################################################