Initial commit

Signed-off-by: Sven Velt <sven@velt.de>
This commit is contained in:
Sven Velt 2010-09-09 18:52:56 +02:00
commit f1e35939eb
4 changed files with 1057 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
*.pyc
.*.swp

67
check_apaches.py Executable file
View file

@ -0,0 +1,67 @@
#!/usr/bin/python
from monitoringplugin import MonitoringPlugin
import re
import urllib2
plugin = MonitoringPlugin(pluginname='check_apaches', tagforstatusline='APACHE', description='Check Apache workers', version='0.1')
plugin.add_cmdlineoption('-H', '', 'host', 'Hostname/IP to check', default='localhost')
plugin.add_cmdlineoption('-p', '', 'port', 'port to connect', default=None)
plugin.add_cmdlineoption('-P', '', 'proto', 'protocol to use', default='http')
plugin.add_cmdlineoption('-u', '', 'url', 'path to "server-status"', default='/server-status')
plugin.add_cmdlineoption('-a', '', 'httpauth', 'HTTP Username and Password, separated by ":"')
plugin.add_cmdlineoption('-w', '', 'warn', 'warning thresold', default='20')
plugin.add_cmdlineoption('-c', '', 'crit', 'warning thresold', default='50')
plugin.parse_cmdlineoptions()
if plugin.options.proto not in ['http', 'https']:
plugin.back2nagios(3, 'Unknown protocol "' + plugin.options.proto + '"')
if not plugin.options.port:
if plugin.options.proto == 'https':
plugin.options.port = '443'
else:
plugin.options.port = '80'
url = plugin.options.proto + '://' + plugin.options.host + ':' + plugin.options.port + '/' + plugin.options.url + '?auto'
plugin.verbose(1, 'Status URL: ' + url)
if plugin.options.httpauth:
httpauth = plugin.options.httpauth.split(':')
if len(httpauth) != 2:
plugin.back2nagios(3, 'Wrong format of authentication data! Need "USERNAME:PASSWORD", got "' + plugin.options.httpauth + '"')
passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
passman.add_password(None, url, httpauth[0], httpauth[1])
authhandler = urllib2.HTTPBasicAuthHandler(passman)
opener = urllib2.build_opener(authhandler)
urllib2.install_opener(opener)
try:
data = urllib2.urlopen(url).read()
except urllib2.HTTPError, e:
plugin.back2nagios(2, 'Could not read data! ' + str(e))
except urllib2.URLError, e:
plugin.back2nagios(2, 'Could not connect to server!')
plugin.verbose(2, 'Got data:\n' + data)
try:
idle = int(re.search('Idle(?:Workers|Servers): (\d+)\n', data).group(1))
busy = int(re.search('Busy(?:Workers|Servers): (\d+)\n', data).group(1))
except:
plugin.back2nagios(2, 'Could not analyze data!')
returncode = plugin.value_wc_to_returncode(busy, plugin.options.warn, plugin.options.crit)
plugin.add_output(str(busy) + ' busy workers, ' + str(idle) + ' idle')
plugin.add_returncode(returncode)
plugin.format_add_performancedata('busy', busy, '', warn=plugin.options.warn, crit=plugin.options.crit, min=0.0)
plugin.format_add_performancedata('idle', idle, '')
plugin.exit()

361
check_naf.py Executable file
View file

@ -0,0 +1,361 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
#####################################################################
# (c) 2006-2010 by Sven Velt and team(ix) GmbH, Nuernberg, Germany #
# sv@teamix.net #
# #
# This file is part of check_naf (FKA check_netappfiler) #
# #
# check_naf is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published #
# by the Free Software Foundation, either version 2 of the License, #
# or (at your option) any later version. #
# #
# check_naf is distributed in the hope that it will be useful, but #
# WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with check_naf. If not, see <http://www.gnu.org/licenses/>. #
#####################################################################
from monitoringplugin import SNMPMonitoringPlugin
class CheckNAF(SNMPMonitoringPlugin):
OID = {
'CPU_Arch': '.1.3.6.1.4.1.789.1.1.11.0',
'CPU_Time_Busy': '.1.3.6.1.4.1.789.1.2.1.3.0',
'CPU_Time_Idle': '.1.3.6.1.4.1.789.1.2.1.5.0',
'CPU_Context_Switches': '.1.3.6.1.4.1.789.1.2.1.8.0',
'Disks_Total': '.1.3.6.1.4.1.789.1.6.4.1.0',
'Disks_Active': '.1.3.6.1.4.1.789.1.6.4.2.0',
'Disks_Reconstructing': '.1.3.6.1.4.1.789.1.6.4.3.0',
'Disks_ReconstParity': '.1.3.6.1.4.1.789.1.6.4.4.0',
'Disks_Scrubbing': '.1.3.6.1.4.1.789.1.6.4.6.0',
'Disks_Failed': '.1.3.6.1.4.1.789.1.6.4.7.0',
'Disks_Spare': '.1.3.6.1.4.1.789.1.6.4.8.0',
'Disks_ZeroDisks': '.1.3.6.1.4.1.789.1.6.4.9.0',
'Disks_Failed_Descr': '.1.3.6.1.4.1.789.1.6.4.10.0',
'Global_Status': '.1.3.6.1.4.1.789.1.2.2.4.0',
'Global_Status_Message': '.1.3.6.1.4.1.789.1.2.2.25.0',
'NVRAM_Status': '.1.3.6.1.4.1.789.1.2.5.1.0',
'Model': '.1.3.6.1.4.1.789.1.1.5.0',
'ONTAP_Version': '.1.3.6.1.4.1.789.1.1.2.0',
'df_FS_Index': '.1.3.6.1.4.1.789.1.5.4.1.1',
'df_FS_Name': '.1.3.6.1.4.1.789.1.5.4.1.2',
'df_FS_Mounted_On': '.1.3.6.1.4.1.789.1.5.4.1.10',
'df_FS_Status': '.1.3.6.1.4.1.789.1.5.4.1.20',
'df_FS_Type': '.1.3.6.1.4.1.789.1.5.4.1.23',
'df_FS_kBTotal': ['.1.3.6.1.4.1.789.1.5.4.1.29', '.1.3.6.1.4.1.789.1.5.4.1.15', '.1.3.6.1.4.1.789.1.5.4.1.14',],
'df_FS_kBUsed': ['.1.3.6.1.4.1.789.1.5.4.1.30', '.1.3.6.1.4.1.789.1.5.4.1.17', '.1.3.6.1.4.1.789.1.5.4.1.16',],
'df_FS_kBAvail': ['.1.3.6.1.4.1.789.1.5.4.1.31', '.1.3.6.1.4.1.789.1.5.4.1.19', '.1.3.6.1.4.1.789.1.5.4.1.18',],
'df_FS_INodeUsed': '.1.3.6.1.4.1.789.1.5.4.1.7',
'df_FS_INodeFree': '.1.3.6.1.4.1.789.1.5.4.1.8',
'df_FS_MaxFilesAvail': '.1.3.6.1.4.1.789.1.5.4.1.11',
'df_FS_MaxFilesUsed': '.1.3.6.1.4.1.789.1.5.4.1.12',
'df_FS_MaxFilesPossible': '.1.3.6.1.4.1.789.1.5.4.1.13',
}
OWC = {
'Global_Status': ( (3,), (4,), (5,6), ),
'NVRAM_Status': ( (1,9), (2,5,8), (3,4,6), ),
}
Status2String = {
'CPU_Arch' : { '1' : 'x86', '2' : 'alpha', '3' : 'mips', '4' : 'sparc', '5' : 'amd64', },
'NVRAM_Status' : { '1' : 'ok', '2' : 'partiallyDischarged', '3' : 'fullyDischarged', '4' : 'notPresent', '5' : 'nearEndOfLife', '6' : 'atEndOfLife', '7' : 'unknown', '8' : 'overCharged', '9' : 'fullyCharged', },
'df_FS_Status' : { '1' : 'unmounted', '2' : 'mounted', '3' : 'frozen', '4' : 'destroying', '5' : 'creating', '6' : 'mounting', '7' : 'unmounting', '8' : 'nofsinfo', '9' : 'replaying', '10': 'replayed', },
'df_FS_Type' : { '1' : 'traditionalVolume', '2' : 'flexibleVolume', '3' : 'aggregate', },
}
def map_status_to_returncode(self, value, mapping):
for returncode in xrange(0,3):
if value in mapping[returncode]:
return returncode
return 3
def check_cpu(self, warn='', crit=''):
cpu_arch = self.SNMPGET(self.OID['CPU_Arch'])
cpu_timebusy = int(self.SNMPGET(self.OID['CPU_Time_Busy']))
# cputimeidle = int(self.SNMPGET(self.OID['CPU_Time_Idle']))
cpu_cs = self.SNMPGET(self.OID['CPU_Context_Switches'])
returncode = self.value_wc_to_returncode(cpu_timebusy, warn, crit)
output = 'CPU ' + str(cpu_timebusy) + '% busy, CPU architecture: ' + self.Status2String['CPU_Arch'].get(cpu_arch)
perfdata = []
pd = {'label':'nacpu', 'value':cpu_timebusy, 'unit':'%', 'min':0, 'max':100}
if warn:
pd['warn'] = warn
if crit:
pd['crit'] = crit
perfdata.append(pd)
perfdata.append({'label':'nacs', 'value':cpu_cs, 'unit':'c'})
return self.remember_check('cpu', returncode, output, perfdata=perfdata)
def check_disk(self, target='failed', warn='', crit=''):
di_total = int(self.SNMPGET(self.OID['Disks_Total']))
di_active = int(self.SNMPGET(self.OID['Disks_Active']))
di_reconstructing = int(self.SNMPGET(self.OID['Disks_Reconstructing']))
di_reconstparity = int(self.SNMPGET(self.OID['Disks_ReconstParity']))
# di_scrubbing = int(self.SNMPGET(self.OID['Disks_Scrubbing']))
di_failed = int(self.SNMPGET(self.OID['Disks_Failed']))
di_spare = int(self.SNMPGET(self.OID['Disks_Spare']))
# di_zerodisks = int(self.SNMPGET(self.OID['Disks_ZeroDisks']))
di_reconstr = di_reconstructing + di_reconstparity
if target == 'spare':
returncode = self.value_wc_to_returncode(di_spare, warn, crit)
output = str(di_spare) + ' spare disk'
if di_spare > 1:
output += 's'
else:
returncode = self.value_wc_to_returncode(di_failed, warn, crit)
if returncode == 0:
output = 'No failed disks'
else:
output = self.SNMPGET(self.OID['Disks_Failed_Descr'])
perfdata = []
perfdata.append({'label':'nadisk_total', 'value':di_total, 'unit':'', 'min':0})
perfdata.append({'label':'nadisk_active', 'value':di_active, 'unit':'', 'min':0})
pd = {'label':'nadisk_spare', 'value':di_spare, 'unit':'', 'min':0}
if warn and target=='spare':
pd['warn'] = warn
if crit and target=='spare':
pd['crit'] = crit
perfdata.append(pd)
pd = {'label':'nadisk_failed', 'value':di_failed, 'unit':'', 'min':0}
if warn and target=='failed':
pd['warn'] = warn
if crit and target=='failed':
pd['crit'] = crit
perfdata.append(pd)
return self.remember_check('disk', returncode, output, perfdata=perfdata, target=target)
def check_global(self):
model = self.SNMPGET(self.OID['Model'])
globalstatus = int(self.SNMPGET(self.OID['Global_Status']))
globalstatusmsg = self.SNMPGET(self.OID['Global_Status_Message'])[:255]
returncode = self.map_status_to_returncode(globalstatus, self.OWC['Global_Status'])
output = model + ': ' + globalstatusmsg
return self.remember_check('global', returncode, output)
def check_nvram(self):
nvramstatus = int(self.SNMPGET(self.OID['NVRAM_Status']))
returncode = self.map_status_to_returncode(nvramstatus, self.OWC['NVRAM_Status'])
output = 'NVRAM battery status is "' + self.Status2String['NVRAM_Status'].get(str(nvramstatus)) + '"'
return self.remember_check('nvram', returncode, output)
def check_version(self):
model = self.SNMPGET(self.OID['Model'])
ontapversion = self.SNMPGET(self.OID['ONTAP_Version'])
return self.remember_check('version', 0, model + ': ' + ontapversion)
def common_vol_idx(self, volume):
if volume.endswith('.snapshot'):
return None
idx = str(self.find_in_table(self.OID['df_FS_Index'], self.OID['df_FS_Name'] , volume))
sn_idx = int(idx) + 1
return (idx, sn_idx)
def check_vol_data(self, volume, warn, crit):
(idx, sn_idx) = self.common_vol_idx(volume)
fs_total = long(self.SNMPGET(self.OID['df_FS_kBTotal'], idx)) * 1024L
fs_used = long(self.SNMPGET(self.OID['df_FS_kBUsed'], idx)) * 1024L
# fs_avail = long(self.SNMPGET(self.OID['df_FS_kBAvail'], idx)) * 1024L
sn_total = long(self.SNMPGET(self.OID['df_FS_kBTotal'], sn_idx)) * 1024L
sn_used = long(self.SNMPGET(self.OID['df_FS_kBUsed'], sn_idx)) * 1024L
# sn_avail = long(self.SNMPGET(self.OID['df_FS_kBAvail'], sn_idx)) * 1024L
mountedon = self.SNMPGET(self.OID['df_FS_Mounted_On'] + "." + idx)
status = self.Status2String['df_FS_Status'].get(self.SNMPGET(self.OID['df_FS_Status'] + "." + idx))
fstype = self.Status2String['df_FS_Type'].get(self.SNMPGET(self.OID['df_FS_Type'] + "." + idx))
# print [mountedon, status, fstype]
fs_pctused = float(fs_used) / float(fs_total) * 100.0
warn = self.range_dehumanize(warn, fs_total)
crit = self.range_dehumanize(crit, fs_total)
returncode = self.value_wc_to_returncode(fs_used, warn, crit)
output = volume + ': Used ' + self.value_to_human_binary(fs_used, 'B')
output += ' (' + '%3.1f' % fs_pctused + '%)'+ ' out of ' + self.value_to_human_binary(fs_total, 'B')
target = volume.replace('/vol/', '')[:-1]
perfdata = []
perfdata.append({'label':'navdu_' + target, 'value':fs_used, 'unit':'B', 'warn':warn, 'crit':crit, 'min':0})
perfdata.append({'label':'navdt_' + target, 'value':fs_total, 'unit':'B'})
perfdata.append({'label':'navsu_' + target, 'value':sn_used, 'unit':'B', 'min':0})
perfdata.append({'label':'navst_' + target, 'value':sn_total, 'unit':'B'})
return self.remember_check('vol_data', returncode, output, perfdata=perfdata, target=target)
def check_vol_snap(self, volume, warn, crit):
(idx, sn_idx) = self.common_vol_idx(volume)
# fs_total = long(self.SNMPGET(self.OID['df_FS_kBTotal'], idx)) * 1024L
# fs_used = long(self.SNMPGET(self.OID['df_FS_kBUsed'], idx)) * 1024L
# fs_avail = long(self.SNMPGET(self.OID['df_FS_kBAvail'], idx)) * 1024L
sn_total = long(self.SNMPGET(self.OID['df_FS_kBTotal'], sn_idx)) * 1024L
sn_used = long(self.SNMPGET(self.OID['df_FS_kBUsed'], sn_idx)) * 1024L
# sn_avail = long(self.SNMPGET(self.OID['df_FS_kBAvail'], sn_idx)) * 1024L
sn_pctused = float(sn_used) / float(sn_total) * 100.0
warn = self.range_dehumanize(warn, sn_total)
crit = self.range_dehumanize(crit, sn_total)
returncode = self.value_wc_to_returncode(sn_used, warn, crit)
output = volume + '.snapshot: Used ' + self.value_to_human_binary(sn_used, 'B')
output += ' (' + '%3.1f' % sn_pctused + '%)'+ ' out of ' + self.value_to_human_binary(sn_total, 'B')
target = volume.replace('/vol/', '')[:-1]
perfdata = []
perfdata.append({'label':'navsu_' + target, 'value':sn_used, 'unit':'B', 'warn':warn, 'crit':crit, 'min':0})
perfdata.append({'label':'navst_' + target, 'value':sn_total, 'unit':'B'})
return self.remember_check('vol_snap', returncode, output, perfdata=perfdata, target=target)
def check_vol_inode(self, volume, warn, crit):
(idx, sn_idx) = self.common_vol_idx(volume)
in_used = long(self.SNMPGET(self.OID['df_FS_INodeUsed'] + '.' + idx))
in_free = long(self.SNMPGET(self.OID['df_FS_INodeFree'] + '.' + idx))
in_total = in_used + in_free
in_pctused = float(in_used) / float(in_total) * 100.0
warn = self.range_dehumanize(warn, in_total)
crit = self.range_dehumanize(crit, in_total)
returncode = self.value_wc_to_returncode(in_used, warn, crit)
output = volume + ': Used inodes ' + self.value_to_human_si(in_used)
output += ' (' + '%3.1f' % in_pctused + '%)'+ ' out of ' + self.value_to_human_si(in_total)
target = volume.replace('/vol/', '')[:-1]
perfdata = []
perfdata.append({'label':'naviu_' + target, 'value':in_used, 'unit':None, 'warn':warn, 'crit':crit, 'min':0})
perfdata.append({'label':'navit_' + target, 'value':in_total, 'unit':None})
return self.remember_check('vol_inode', returncode, output, perfdata=perfdata, target=target)
def check_vol_files(self, volume, warn, crit):
(idx, sn_idx) = self.common_vol_idx(volume)
fi_avail = long(self.SNMPGET(self.OID['df_FS_MaxFilesAvail'] + '.' + idx))
fi_used = long(self.SNMPGET(self.OID['df_FS_MaxFilesUsed'] + '.' + idx))
fi_possible = long(self.SNMPGET(self.OID['df_FS_MaxFilesPossible'] + '.' + idx))
fi_total = fi_used + fi_avail
fi_pctused = float(fi_used) / float(fi_total) * 100.0
warn = self.range_dehumanize(warn, fi_total)
crit = self.range_dehumanize(crit, fi_total)
returncode = self.value_wc_to_returncode(fi_used, warn, crit)
output = volume + ': Used files ' + self.value_to_human_si(fi_used)
output += ' (' + '%3.1f' % fi_pctused + '%)'+ ' out of ' + self.value_to_human_si(fi_total)
output += ', may raised to ' + self.value_to_human_si(fi_possible)
target = volume.replace('/vol/', '')[:-1]
perfdata = []
perfdata.append({'label':'navfu_' + target, 'value':fi_used, 'unit':None, 'warn':warn, 'crit':crit, 'min':0})
perfdata.append({'label':'navft_' + target, 'value':fi_total, 'unit':None})
return self.remember_check('vol_files', returncode, output, perfdata=perfdata, target=target)
def main():
plugin = CheckNAF(pluginname='check_naf', tagforstatusline='NAF', description=u'Monitoring NetApp™ FAS systems', version='0.9')
plugin.add_cmdlineoption('', '--check', 'check', 'check or list of checks', default='')
plugin.add_cmdlineoption('', '--target', 'target', 'target or list of targets', default='')
plugin.add_cmdlineoption('-w', '', 'warn', 'warning thresold or a list of it', default='')
plugin.add_cmdlineoption('-c', '', 'crit', 'warning thresold or a list of it', default='')
plugin.parse_cmdlineoptions()
plugin.prepare_snmp()
if ',' in plugin.options.check:
checks = plugin.options.check.split(',')
checks.reverse()
else:
checks = [plugin.options.check,]
if ',' in plugin.options.target:
targets = plugin.options.target.split(',')
else:
targets = [plugin.options.target,]
while len(checks):
check = checks.pop()
if check == 'global':
result = plugin.check_global()
elif check == 'cpu':
result = plugin.check_cpu()
elif check == 'disk':
result = plugin.check_disk(target='spare')
elif check == 'nvram':
result = plugin.check_nvram()
elif check == 'version':
result = plugin.check_version()
elif check.startswith('vol_'):
combinedchecks = [check,]
while len(checks) > 0 and checks[0].startswith('vol_'):
combinedchecks.append(checks.pop())
for target in targets:
for check in combinedchecks:
if check == 'vol_data':
result = plugin.check_vol_data(target, plugin.options.warn, plugin.options.crit)
elif check == 'vol_snap':
result = plugin.check_vol_snap(target, plugin.options.warn, plugin.options.crit)
elif check =='vol_inode':
result = plugin.check_vol_inode(target, plugin.options.warn, plugin.options.crit)
elif check =='vol_files':
result = plugin.check_vol_files(target, plugin.options.warn, plugin.options.crit)
# from pprint import pprint
# pprint(plugin.dump_brain())
plugin.brain2output()
plugin.exit()
if __name__ == '__main__':
main()
#vim: ts=4 sw=4 foldmethod=indent

626
monitoringplugin.py Normal file
View file

@ -0,0 +1,626 @@
__version__ = '0.0.100802'
__all__ = ['MonitoringPlugin', 'SNMPMonitoringPlugin']
import optparse, os, re, sys
try:
import netsnmp
except ImportError:
pass
class MonitoringPlugin(object):
RETURNSTRINGS = { 0: "OK", 1: "WARNING", 2: "CRITICAL", 3: "UNKNOWN", 127: "UNKNOWN" }
RETURNCODE = { 'OK': 0, 'WARNING': 1, 'CRITICAL': 2, 'UNKNOWN': 3 }
returncode_priority = [2, 1, 3, 0]
powers_binary = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']
powers_binary_lower = [ p.lower() for p in powers_binary]
powers_si = ['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z']
powers_si_lower = [ p.lower() for p in powers_si]
def __init__(self, *args, **kwargs):
self.__pluginname = kwargs.get('pluginname') or ''
self.__version = kwargs.get('version') or None
self.__tagforstatusline = kwargs.get('tagforstatusline') or ''
self.__tagforstatusline = self.__tagforstatusline.replace('|', ' ')
self.__description = kwargs.get('description') or None
self.__output = []
self.__multilineoutput = []
self.__performancedata = []
self.__returncode = []
self.__brain_checks = []
self.__brain_perfdata = []
self.__brain_perfdatalabels = []
self.__optparser = optparse.OptionParser(version=self.__version, description=self.__description)
self._cmdlineoptions_parsed = False
def add_cmdlineoption(self, shortoption, longoption, dest, help, **kwargs):
self.__optparser.add_option(shortoption, longoption, dest=dest, help=help, **kwargs)
def parse_cmdlineoptions(self):
if self._cmdlineoptions_parsed:
return
# self.__optparser.add_option('-V', '--version', action='version', help='show version number and exit')
self.__optparser.add_option('-v', '--verbose', dest='verbose', help='Verbosity, more for more ;-)', action='count')
(self.options, self.args) = self.__optparser.parse_args()
self._cmdlineoptions_parsed = True
def range_to_limits(self, range):
# Check if we must negate result
if len(range) > 0 and range[0] == '@':
negate = True
range = range[1:]
else:
negate = False
# Look for a ':'...
if range.find(':') >= 0:
# ... this is a range
(low, high) = range.split(':')
if not low:
low = float(0.0)
elif low[0] == '~':
low = float('-infinity')
else:
low = float(low)
if high:
high = float(high)
else:
high = float('infinity')
elif len(range) == 0:
low = float('-infinity')
high = float('infinity')
else:
# ... this is just a number
low = float(0.0)
high = float(range)
return (low, high, negate)
def value_in_range(self, value, range):
(low, high, negate) = self.range_to_limits(range)
if value < low or value > high:
result = False
else:
result = True
if negate:
result = not result
return result
def value_wc_to_returncode(self, value, range_warn, range_crit):
if not self.value_in_range(value, range_crit):
return 2
elif not self.value_in_range(value, range_warn):
return 1
return 0
def is_float(self, string):
try:
float(string)
return True
except ValueError:
return False
def special_value_wc_to_returncode(self, value, warn, crit):
# Special add on: WARN > CRIT
if self.is_float(warn) and self.is_float(crit) and float(warn) > float(crit):
# Test if value is *smaller* than thresholds
warn = '@0:' + warn
crit = '@0:' + crit
return self.value_wc_to_returncode(value, warn, crit)
def add_output(self, value):
self.__output.append(value)
def add_multilineoutput(self, value):
self.__multilineoutput.append(value)
def format_performancedata(self, label, value, unit, *args, **kwargs):
label = label.lstrip().rstrip()
if re.search('[=\' ]', label):
label = '\'' + label + '\''
perfdata = label + '=' + str(value)
if unit:
perfdata += str(unit).lstrip().rstrip()
for key in ['warn', 'crit', 'min', 'max']:
perfdata += ';'
if key in kwargs:
perfdata += str(kwargs[key])
return perfdata
def add_performancedata(self, perfdata):
self.__performancedata.append(perfdata)
def format_add_performancedata(self, label, value, unit, *args, **kwargs):
self.add_performancedata(self.format_performancedata(label, value, unit, *args, **kwargs))
def add_returncode(self, value):
self.__returncode.append(value)
def tagtarget(self, tag, target):
if target:
return str(tag) + ':' + str(target)
else:
return str(tag)
def remember_check(self, tag, returncode, output, multilineoutput=None, perfdata=None, target=None):
check = {}
check['tag'] = tag
check['returncode'] = returncode
check['output'] = output
check['multilineoutout'] = multilineoutput
check['perfdata'] = perfdata
check['target'] = target
self.remember_perfdata(perfdata)
self.__brain_checks.append(check)
return check
def remember_perfdata(self, perfdata=None):
if perfdata:
for pd in perfdata:
if pd['label'] in self.__brain_perfdatalabels:
pdidx = self.__brain_perfdatalabels.index(pd['label'])
self.__brain_perfdata[pdidx] = pd
else:
self.__brain_perfdata.append(pd)
self.__brain_perfdatalabels.append(pd['label'])
def dump_brain(self):
return (self.__brain_checks, self.__brain_perfdata)
def brain2output(self):
out = [[], [], [], []]
for check in self.__brain_checks:
tagtarget = self.tagtarget(check['tag'], check.get('target'))
returncode = check.get('returncode') or 0
self.add_returncode(returncode)
out[returncode].append(tagtarget)
#if returncode == 0:
# self.add_output(tagtarget)
#else:
# self.add_output(tagtarget + '(' + check.get('output') + ') ')
self.add_multilineoutput(self.RETURNSTRINGS[returncode] + ' ' + tagtarget + ' - ' + check.get('output'))
if check.get('multilineoutput'):
self.add_multilineoutput(check.get('multilineoutput'))
statusline = []
for retcode in [2, 1, 3, 0]:
if len(out[retcode]):
statusline.append(str(len(out[retcode])) + ' ' + self.RETURNSTRINGS[retcode] + ': ' + '/'.join(out[retcode]))
statusline = ', '.join(statusline)
self.add_output(statusline)
for pd in self.__brain_perfdata:
self.format_add_performancedata(**pd)
def value_to_human_binary(self, value, unit=''):
for power in self.powers_binary:
if value < 1024.0:
return "%3.1f%s%s" % (value, power, unit)
value /= 1024.0
if float(value) not in [float('inf'), float('-inf')]:
return "%3.1fYi%s" % (value, unit)
else:
return value
def value_to_human_si(self, value, unit=''):
for power in self.powers_si:
if value < 1000.0:
return "%3.1f%s%s" % (value, power, unit)
value /= 1000.0
if float(value) not in [float('inf'), float('-inf')]:
return "%3.1fY%s" % (value, unit)
else:
return value
def human_to_number(self, value, total=None):
if total:
if not self.is_float(total):
total = self.human_to_number(total)
if self.is_float(value):
return float(value)
elif value[-1] == '%':
if total:
return float(value[:-1])/100.0 * float(total)
else:
return float(value[:-1])
elif value[-1].lower() in self.powers_si_lower:
return 1000.0 ** self.powers_si_lower.index(value[-1].lower()) * float(value[:-1])
elif value[-2:].lower() in self.powers_binary_lower:
return 1024.0 ** self.powers_binary_lower.index(value[-2:].lower()) * float(value[:-2])
else:
return value
def range_dehumanize(self, range, total=None):
newrange = ''
if len(range):
if range[0] == '@':
newrange += '@'
range = range[1:]
parts = range.split(':')
newrange += str(self.human_to_number(parts[0], total))
if len(parts) > 1:
newrange += ':' + str(self.human_to_number(parts[1], total))
if range != newrange:
self.verbose(3, 'Changed range/thresold from "' + range + '" to "' + newrange + '"')
return newrange
else:
return ''
def verbose(self, level, output):
if level <= self.options.verbose:
print 'V' + str(level) + ': ' + output
def exit(self):
for returncode in self.returncode_priority:
if returncode in self.__returncode:
break
self.back2nagios(returncode, statusline=self.__output, multiline=self.__multilineoutput, performancedata=self.__performancedata)
def back2nagios(self, returncode, statusline=None, multiline=None, performancedata=None, subtag=None, exit=True):
# FIXME: Make 'returncode' also accept strings
# Build status line
out = self.__tagforstatusline
if subtag:
out += '(' + subtag.replace('|', ' ') + ')'
out += ' ' + self.RETURNSTRINGS[returncode]
# Check if there's a status line text and build it
if statusline:
out += ' - '
if type(statusline) == str:
out += statusline
elif type(statusline) in [list, tuple]:
out += ', '.join(statusline).replace('|', ' ')
# Check if we have multi line output and build it
if multiline:
if type(multiline) == str:
out += '\n' + multiline.replace('|', ' ')
elif type(multiline) in [list, tuple]:
out += '\n' + '\n'.join(multiline).replace('|', ' ')
# Check if there's perfdata
if performancedata:
out += '|'
if type(performancedata) == str:
out += performancedata
elif type(performancedata) in [list, tuple]:
out += ' '.join(performancedata).replace('|', ' ')
# Exit program or return output line(s)
if exit:
print out
sys.exit(returncode)
else:
return (returncode, out)
##############################################################################
class SNMPMonitoringPlugin(MonitoringPlugin):
def __init__(self, *args, **kwargs):
# Same as "MonitoringPlugin.__init__(*args, **kwargs)" but a little bit more flexible
#super(MonitoringPlugin, self).__init__(*args, **kwargs)
MonitoringPlugin.__init__(self, *args, **kwargs)
self.add_cmdlineoption('-H', '', 'host', 'Host to check', default='127.0.0.1')
self.add_cmdlineoption('-P', '', 'snmpversion', 'SNMP protocol version', default='1')
self.add_cmdlineoption('-C', '', 'snmpauth', 'SNMP v1/v2c community OR SNMP v3 quadruple', default='public')
self.add_cmdlineoption('', '--snmpcmdlinepath', 'snmpcmdlinepath', 'Path to "snmpget" and "snmpwalk"', default='/usr/bin')
# FIXME
self.add_cmdlineoption('', '--nonetsnmp', 'nonetsnmp', 'Do not use NET-SNMP python bindings', action='store_true')
# self.__optparser.add_option('', '--nonetsnmp', dest='nonetsnmp', help='Do not use NET-SNMP python bindings', action='store_true')
self.__SNMP_Cache = {}
self.__use_netsnmp = False
self.__prepared_snmp = False
def prepare_snmp(self):
if not self._cmdlineoptions_parsed:
self.parse_cmdlineoptions()
if not self.options.nonetsnmp:
try:
import netsnmp
self.__use_netsnmp = True
except ImportError:
pass
if self.__use_netsnmp:
self.verbose(1, 'Using NET-SNMP Python bindings')
self.SNMPGET_wrapper = self.__SNMPGET_netsnmp
self.SNMPWALK_wrapper = self.__SNMPWALK_netsnmp
if self.options.snmpversion == '2c':
self.options.snmpversion = '2'
else:
self.verbose(1, 'Using NET-SNMP command line tools')
self.SNMPGET_wrapper = self.__SNMPGET_cmdline
self.SNMPWALK_wrapper = self.__SNMPWALK_cmdline
# Building command lines
self.__CMDLINE_get = os.path.join(self.options.snmpcmdlinepath, 'snmpget') + ' -OqevtU '
self.__CMDLINE_walk = os.path.join(self.options.snmpcmdlinepath, 'snmpwalk') + ' -OqevtU '
if self.options.snmpversion in [1, 2, '1', '2', '2c']:
if self.options.snmpversion in [2, '2']:
self.options.snmpversion = '2c'
self.__CMDLINE_get += ' -v' + str(self.options.snmpversion) + ' -c' + self.options.snmpauth + ' '
self.__CMDLINE_walk += ' -v' + str(self.options.snmpversion) + ' -c' + self.options.snmpauth + ' '
elif options.snmpversion == [3, '3']:
# FIXME: Better error handling
try:
snmpauth = self.options.snmpauth.split(':')
self.__CMDLINE_get += ' -v3 -l' + snmpauth[0] + ' -u' + snmpauth[1] + ' -a' + snmpauth[2] + ' -A' + snmpauth[3] + ' '
self.__CMDLINE_walk += ' -v3 -l' + snmpauth[0] + ' -u' + snmpauth[1] + ' -a' + snmpauth[2] + ' -A' + snmpauth[3] + ' '
except:
self.back2nagios(3, 'Could not build SNMPv3 command line, need "SecLevel:SecName:AuthProtocol:AuthKey"')
else:
self.back2nagios(3, 'Unknown SNMP version "' + str(self.options.snmpversion) + '"')
self.__CMDLINE_get += ' ' + self.options.host + ' %s 2>/dev/null'
self.__CMDLINE_walk += ' ' + self.options.host + ' %s 2>/dev/null'
self.verbose(3, 'Using commandline: ' + self.__CMDLINE_get)
self.verbose(3, 'Using commandline: ' + self.__CMDLINE_walk)
self.__prepared_snmp = True
def find_index_for_value(self, list_indexes, list_values, wanted):
self.verbose(2, 'Look for "' + str(wanted) + '"')
index = None
if len(list_indexes) != len(list_values):
self.verbose(1, 'Length of index and value lists do not match!')
return None
try:
index = list_values.index(wanted)
index = list_indexes[index]
except ValueError:
pass
if index:
self.verbose(2, 'Found "' + str(wanted) +'" with index "' + str(index) + '"')
else:
self.verbose(2, 'Nothing found!')
return str(index)
def find_in_table(self, oid_index, oid_values, wanted):
self.verbose(2, 'Look for "' + str(wanted) + '" in "' + str(oid_values) +'"')
index = None
indexes = list(self.SNMPWALK(oid_index))
values = list(self.SNMPWALK(oid_values))
if len(indexes) != len(values):
self.back2nagios(3, 'Different data from 2 SNMP Walks!')
return str(self.find_index_for_value(indexes, values, wanted))
def SNMPGET(self, baseoid, idx=None, exitonerror=True):
if type(baseoid) in (list, tuple) and idx != None:
idx = str(idx)
if self.options.snmpversion in [1, '1']:
value_low = long(self.SNMPGET_wrapper(baseoid[1] + '.' + idx, exitonerror=exitonerror))
if value_low < 0L:
value_low += 2 ** 32
value_hi = long(self.SNMPGET_wrapper(baseoid[2] + '.' + idx, exitonerror=exitonerror))
if value_hi < 0L:
value_hi += 2 ** 32
return value_hi * 2L ** 32L + value_low
elif self.options.snmpversion in [2, 3, '2', '2c', '3']:
return long(self.SNMPGET_wrapper(baseoid[0] + '.' + idx, exitonerror=exitonerror))
elif type(baseoid) in (str, ) and idx != None:
return self.SNMPGET_wrapper(baseoid + str(idx), exitonerror=exitonerror)
else:
return self.SNMPGET_wrapper(baseoid, exitonerror=exitonerror)
def SNMPWALK(self, baseoid, exitonerror=True):
return self.SNMPWALK_wrapper(baseoid, exitonerror=exitonerror)
def __SNMPGET_netsnmp(self, oid, exitonerror=True):
if not self.__prepared_snmp:
self.prepare_snmp()
if oid in self.__SNMP_Cache:
self.verbose(2, "%40s -> (CACHED) %s" % (oid, self.__SNMP_Cache[oid]))
return self.__SNMP_Cache[oid]
result = netsnmp.snmpget(oid, Version=int(self.options.snmpversion), DestHost=self.options.host, Community=self.options.snmpauth)[0]
if not result:
if exitonerror:
self.back2nagios(3, 'Timeout or no answer from "%s" looking for "%s"' % (self.options.host, oid))
else:
return None
self.__SNMP_Cache[oid] = result
self.verbose(2, "%40s -> %s" % (oid, result))
return result
def __SNMPWALK_netsnmp(self, oid, exitonerror=True):
if not self.__prepared_snmp:
self.prepare_snmp()
if oid in self.__SNMP_Cache:
self.verbose(2, "%40s -> (CACHED) %s" % (oid, self.__SNMP_Cache[oid]))
return self.__SNMP_Cache[oid]
result = netsnmp.snmpwalk(oid, Version=int(self.options.snmpversion), DestHost=self.options.host, Community=self.options.snmpauth)
if not result:
if exitonerror:
self.back2nagios(3, 'Timeout or no answer from "%s" looking for "%s"' % (self.options.host, oid))
else:
return None
self.__SNMP_Cache[oid] = result
self.verbose(2, "%40s -> %s" % (oid, result))
return result
def __SNMPGET_cmdline(self, oid, exitonerror=True):
if not self.__prepared_snmp:
self.prepare_snmp()
cmdline = self.__CMDLINE_get % oid
self.verbose(2, cmdline)
cmd = os.popen(cmdline)
out = cmd.readline().rstrip().replace('"','')
retcode = cmd.close()
if retcode:
if not exitonerror:
return None
if retcode == 256:
self.back2nagios(3, 'Timeout - no SNMP answer from "' + self.options.host + '"')
elif retcode ==512:
self.back2nagios(3, 'OID "' + oid + '" not found')
else:
self.back2nagios(3, 'Unknown error code "' + str(retcode) + '" from command line utils')
self.verbose(1, out)
return out
def __SNMPWALK_cmdline(self, oid, exitonerror=True):
if not self.__prepared_snmp:
self.prepare_snmp()
cmdline = self.__CMDLINE_walk % oid
self.verbose(2, cmdline)
cmd = os.popen(cmdline)
out = cmd.readlines()
retcode = cmd.close()
if retcode:
if not exitonerror:
return None
if retcode == 256:
self.back2nagios(3, 'Timeout - no SNMP answer from "' + self.options.host + '"')
elif retcode ==512:
self.back2nagios(3, 'OID "' + oid + '" not found')
else:
self.back2nagios(3, 'Unknown error code "' + str(retcode) + '" from command line utils')
for line in range(0,len(out)):
out[line] = out[line].rstrip().replace('"','')
self.verbose(1, out)
return out
##############################################################################
def main():
myplugin = MonitoringPlugin(pluginname='check_testplugin', tagforstatusline='TEST')
from pprint import pprint
pprint(myplugin.back2nagios(0, 'Nr. 01: Simple plugin', exit=False) )
pprint(myplugin.back2nagios(0, 'Nr. 02: Simple plugin with sub tag', subtag='MySubTag', exit=False) )
pprint(myplugin.back2nagios(0, 'Nr. 10: Exit Code OK', exit=False) )
pprint(myplugin.back2nagios(1, 'Nr. 11: Exit Code WARNING', exit=False) )
pprint(myplugin.back2nagios(2, 'Nr. 12: Exit Code CRITICAL', exit=False) )
pprint(myplugin.back2nagios(3, 'Nr. 13: Exit Code UNKNOWN', exit=False) )
ret = myplugin.back2nagios(0, 'Nr. 20: Plugin with string-based multiline output', 'Line 2\nLine 3\nLine4', exit=False)
print ret[1]
print 'Returncode: ' + str(ret[0])
ret = myplugin.back2nagios(0, 'Nr. 21: Plugin with list-based multiline output', ['Line 2', 'Line 3', 'Line4'], exit=False)
print ret[1]
print 'Returncode: ' + str(ret[0])
ret = myplugin.back2nagios(0, 'Nr. 22: Plugin with tuple-based multiline output', ('Line 2', 'Line 3', 'Line4'), exit=False)
print ret[1]
print 'Returncode: ' + str(ret[0])
myplugin.add_performancedata('Val1', 42, '')
myplugin.add_performancedata('Val2', 23, 'c', warn=10, crit=20, min=0, max=100)
myplugin.add_performancedata('Val 3', '2342', 'c', warn=10, crit=20, min=0, max=100)
pprint(myplugin.back2nagios(0, 'Nr. 30: With perfdatas', exit=False) )
myplugin.back2nagios(0, 'Nr. 99: Exit test suite with OK')
if __name__ == '__main__':
main()
#vim: ts=4 sw=4