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