Code

be3613d8d57ead1a9907bd52ed4bcb17516d8907
[nagixsc.git] / nagixsc.py
1 import ConfigParser
2 import base64
3 import datetime
4 import libxml2
5 import shlex
6 import subprocess
7 import sys
9 def debug(level, verb, string):
10         if level <= verb:
11                 print "%s: %s" % (level, string)
14 ##############################################################################
16 def available_encodings():
17         return ['base64', 'plain',]
20 def check_encoding(enc):
21         if enc in available_encodings():
22                 return True
23         else:
24                 return False
27 def decode(data, encoding):
28         if encoding == 'plain':
29                 return data
30         else:
31                 return base64.b64decode(data)
34 def encode(data, encoding=None):
35         if encoding == 'plain':
36                 return data
37         else:
38                 return base64.b64encode(data)
41 ##############################################################################
43 def read_inifile(inifile):
44         config = ConfigParser.RawConfigParser()
45         config.optionxform = str # We need case-sensitive options
46         ini_list = config.read(inifile)
48         if ini_list:
49                 return config
50         else:
51                 return False
54 ##############################################################################
56 def exec_check(host_name, service_descr, cmdline):
57         try:
58                 cmd     = subprocess.Popen(shlex.split(cmdline), stdout=subprocess.PIPE)
59                 output  = cmd.communicate()[0].rstrip()
60                 retcode = cmd.returncode
61         except OSError:
62                 output  = 'Could not execute "%s"' % cmdline
63                 retcode = 127
65         return {'host_name':host_name, 'service_description':service_descr, 'returncode':retcode, 'output':output, 'timestamp':datetime.datetime.now().strftime('%s')}
68 ##############################################################################
70 def conf2dict(config, opt_host=None, opt_service=None):
71         checks = []
73         # Sections are Hosts (not 'nagixsc'), options in sections are Services
74         hosts = config.sections()
75         if 'nagixsc' in hosts:
76                 hosts.remove('nagixsc')
78         # Filter out host/section if it exists
79         if opt_host:
80                 if opt_host in hosts:
81                         hosts = [opt_host,]
82                 else:
83                         hosts = []
85         for host in hosts:
86                 # Overwrite section/host name with '_host_name'
87                 if config.has_option(host,'_host_name'):
88                         host_name = config.get(host,'_host_name')
89                 else:
90                         host_name = host
93                 services = config.options(host)
94                 # Look for host check
95                 if '_host_check' in services and not opt_service:
96                         cmdline = config.get(host, '_host_check')
97                         check = exec_check(host_name, None, cmdline)
98                         checks.append(check)
101                 # Filter out service if given in cmd line options
102                 if opt_service:
103                         if opt_service in services:
104                                 services = [opt_service,]
105                         else:
106                                 services = []
108                 for service in services:
109                         # If option starts with '_' it may be a NagixSC option in the future
110                         if service[0] != '_':
111                                 cmdline = config.get(host, service)
113                                 check = exec_check(host_name, service, cmdline)
114                                 checks.append(check)
116         return checks
119 ##############################################################################
121 def read_xml(options):
122         if options.url != None:
123                 import urllib2
125                 if options.httpuser and options.httppasswd:
126                         passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
127                         passman.add_password(None, options.url, options.httpuser, options.httppasswd)
128                         authhandler = urllib2.HTTPBasicAuthHandler(passman)
129                         opener = urllib2.build_opener(authhandler)
130                         urllib2.install_opener(opener)
132                 try:
133                         response = urllib2.urlopen(options.url)
134                 except urllib2.HTTPError, error:
135                         print error
136                         sys.exit(0)
137                 except urllib2.URLError, error:
138                         print error.reason[1]
139                         sys.exit(0)
141                 doc = libxml2.parseDoc(response.read())
142                 response.close()
144         else:
145                 doc = libxml2.parseFile(options.file)
147         return doc
150 ##############################################################################
152 def xml_check_version(xmldoc):
153         # FIXME: Check XML structure
154         try:
155                 xmlnagixsc = xmldoc.xpathNewContext().xpathEval('/nagixsc')[0]
156         except:
157                 return (False, 'Not a Nag(IX)SC XML file!')
159         try:
160                 if xmlnagixsc.prop('version') != "1.0":
161                         return (False, 'Wrong version (found "%s", need "1.0") of XML file!' % xmlnagixsc.prop('version'))
162         except:
163                 return (False, 'No version information found in XML file!')
165         return (True, 'XML seems to be ok')
168 def xml_get_timestamp(xmldoc):
169         try:
170                 timestamp = int(xmldoc.xpathNewContext().xpathEval('/nagixsc/timestamp')[0].get_content())
171         except:
172                 return False
174         return timestamp
177 def xml_to_dict(xmldoc, verb=0, hostfilter=None, servicefilter=None):
178         checks = []
179         now = int(datetime.datetime.now().strftime('%s'))
180         filetimestamp = reset_future_timestamp(xml_get_timestamp(xmldoc), now)
182         if hostfilter:
183                 hosts = xmldoc.xpathNewContext().xpathEval('/nagixsc/host[name="%s"] | /nagixsc/host[name="%s"]' % (hostfilter, encode(hostfilter)))
184         else:
185                 hosts = xmldoc.xpathNewContext().xpathEval('/nagixsc/host')
187         for host in hosts:
188                 xmlhostname = host.xpathEval('name')[0]
189                 hostname = decode(xmlhostname.get_content(), xmlhostname.prop('encoding'))
190                 debug(2, verb, 'Found host "%s"' % hostname)
192                 # Look for Host check result
193                 if host.xpathEval('returncode'):
194                         retcode   = host.xpathEval('returncode')[0].get_content()
195                 else:
196                         retcode   = None
198                 if host.xpathEval('output'):
199                         xmloutput = host.xpathEval('output')[0]
200                         output    = decode(xmloutput.get_content(), xmloutput.prop('encoding')).rstrip()
201                 else:
202                         output    = None
204                 if host.xpathEval('timestamp'):
205                         timestamp = reset_future_timestamp(int(host.xpathEval('timestamp')[0].get_content()), now)
206                 else:
207                         timestamp = filetimestamp
209                 if retcode and output:
210                         checks.append({'host_name':hostname, 'service_description':None, 'returncode':retcode, 'output':output, 'timestamp':timestamp})
213                 # Look for service filter
214                 if servicefilter:
215                         services = host.xpathEval('service[description="%s"] | service[description="%s"]' % (servicefilter, encode(servicefilter)))
216                 else:
217                         services = host.xpathEval('service')
219                 # Loop over services in host
220                 for service in services:
221                         service_dict = {}
223                         xmldescr  = service.xpathEval('description')[0]
224                         xmloutput = service.xpathEval('output')[0]
226                         srvdescr = decode(xmldescr.get_content(), xmldescr.prop('encoding'))
227                         retcode  = service.xpathEval('returncode')[0].get_content()
228                         output   = decode(xmloutput.get_content(), xmloutput.prop('encoding')).rstrip()
230                         try:
231                                 timestamp = reset_future_timestamp(int(service.xpathEval('timestamp')[0].get_content()), now)
232                         except:
233                                 timestamp = filetimestamp
235                         debug(2, verb, 'Found service "%s"' % srvdescr)
237                         service_dict = {'host_name':hostname, 'service_description':srvdescr, 'returncode':retcode, 'output':output, 'timestamp':timestamp}
238                         checks.append(service_dict)
240                         debug(1, verb, 'Host: "%s" - Service: "%s" - RetCode: "%s" - Output: "%s"' % (hostname, srvdescr, retcode, output) )
242         return checks
245 def xml_from_dict(checks, encoding='base64'):
246         lasthost = None
248         db = [(check['host_name'], check) for check in checks]
249         db.sort()
251         xmldoc = libxml2.newDoc('1.0')
252         xmlroot = xmldoc.newChild(None, 'nagixsc', None)
253         xmlroot.setProp('version', '1.0')
254         xmltimestamp = xmlroot.newChild(None, 'timestamp', datetime.datetime.now().strftime('%s'))
256         for entry in db:
257                 check = entry[1]
259                 if check['host_name'] != lasthost:
260                         xmlhost = xmlroot.newChild(None, 'host', None)
261                         xmlhostname = xmlhost.newChild(None, 'name', encode(check['host_name'], encoding))
262                         lasthost = check['host_name']
264                 if check['service_description'] == '' or check['service_description'] == None:
265                         # Host check result
266                         xmlreturncode = xmlhost.newChild(None, 'returncode', str(check['returncode']))
267                         xmloutput     = xmlhost.newChild(None, 'output', encode(check['output'], encoding))
268                         xmloutput.setProp('encoding', encoding)
269                         if check.has_key('timestamp'):
270                                 xmltimestamp  = xmlhost.newChild(None, 'timestamp', str(check['timestamp']))
271                 else:
272                         # Service check result
273                         xmlservice    = xmlhost.newChild(None, 'service', None)
274                         xmlname       = xmlservice.newChild(None, 'description', encode(check['service_description'], encoding))
275                         xmlname.setProp('encoding', encoding)
276                         xmlreturncode = xmlservice.newChild(None, 'returncode', str(check['returncode']))
277                         xmloutput     = xmlservice.newChild(None, 'output', encode(check['output'], encoding))
278                         xmloutput.setProp('encoding', encoding)
279                         if check.has_key('timestamp'):
280                                 xmltimestamp  = xmlservice.newChild(None, 'timestamp', str(check['timestamp']))
282         return xmldoc
285 def check_mark_outdated(check, now, maxtimediff, markold):
286         timedelta = now - check['timestamp']
287         if timedelta > maxtimediff:
288                 check['output'] = 'Nag(ix)SC: Check result is %s(>%s) seconds old - %s' % (timedelta, maxtimediff, check['output'])
289                 if markold:
290                         check['returncode'] = 3
291         return check
294 def reset_future_timestamp(timestamp, now):
295         if timestamp <= now:
296                 return timestamp
297         else:
298                 return now