diff options
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/Makefile.am | 2 | ||||
-rwxr-xr-x | plugins/check_disk_and_inode.py | 443 | ||||
-rwxr-xr-x | plugins/check_mounts.py | 265 |
3 files changed, 266 insertions, 444 deletions
diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 47ff64d..ffc121f 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -9,8 +9,8 @@ crond_DATA = \ $(NULL) dist_glusternagiosplugins_PYTHON = \ - check_disk_and_inode.py \ check_gluster_syslog.py \ + check_mounts.py \ check_vol_utilization.py \ check_volume_status.py \ check_proc_status.py \ diff --git a/plugins/check_disk_and_inode.py b/plugins/check_disk_and_inode.py deleted file mode 100755 index d1fc3f5..0000000 --- a/plugins/check_disk_and_inode.py +++ /dev/null @@ -1,443 +0,0 @@ -#!/usr/bin/python -# Copyright (C) 2014 Red Hat Inc -# -# This program 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. -# -# This program 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 this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA -# - - -import os -import re -import sys -import commands -from optparse import OptionParser -from glusternagios import utils - -WARNING_LEVEL = 80 -CRITICAL_LEVEL = 90 -INVALID_STATUS_CODE = -1 - - -def getVal(val): - dmatch = re.compile('[0-9]+').match(val) - if (dmatch): - return float(eval(dmatch.group(0))) - else: - return 0 - - -def getLVdetails(filename, lvs): - dev_name = os.path.realpath(filename) - - pool = "" - disk = {} - for a in lvs: - dev = os.path.realpath(a['LVM2_LV_PATH']) - if dev_name == dev: - if a['LVM2_LV_ATTR'][0] == 'V': - pool = a['LVM2_POOL_LV'] - disk['thinLv'] = True - break - else: - disk['thinLv'] = False - return disk - else: - return None - for b in lvs: - if b['LVM2_LV_NAME'] == pool: - disk['actualUsedPercent'] = float(b['LVM2_DATA_PERCENT']) - disk['actualTotalSize'] = float(b['LVM2_LV_SIZE']) - disk['actualUsed'] = disk['actualTotalSize'] * disk[ - 'actualUsedPercent'] / 100 - return disk - else: - return None - - -def getUsageAndFree(command, path, crit, warn, lvm): - disk = {'path': None, 'usePcent': 0, 'avail': 0, - 'used': 0, 'size': 0, 'fs': None, - 'status': None, 'msg': None, 'availPcent': 0, - 'statusCode': utils.PluginStatusCode.UNKNOWN} - - # Check if device exists and permissions are ok - if not os.access(path, os.F_OK): - disk['status'] = "Device not found!" - disk['msg'] = 'no device' - disk['fs'] = path - disk['statusCode'] = utils.PluginStatusCode.CRITICAL - return disk - - if not os.access(path, os.R_OK): - disk['status'] = "Unable to access the device" - disk['msg'] = 'no access' - disk['fs'] = path - disk['statusCode'] = utils.PluginStatusCode.CRITICAL - return disk - - status = commands.getstatusoutput(command) - # Sample output - # (0, 'Filesystem 1G-blocks Used Available Use% Mounted on\n/dev/sda1 - # 290G 196G 79G 72% /') - if status[0] != 0: - disk['msg'] = 'error:%s' % status[0] - if status[0] == 256: - disk['status'] = "Brick/Device path not found!" - else: - disk['status'] = status[1] - disk['statusCode'] = utils.PluginStatusCode.CRITICAL - return disk - - status = status[1].split() - disk['path'] = status[-1] - disk['avail'] = getVal(status[-3]) - disk['used'] = getVal(status[-4]) - disk['size'] = getVal(status[-5]) - disk['fs'] = status[-6] - disk['usePcent'] = getVal(status[-2]) - - if disk['usePcent'] >= crit: - disk['statusCode'] = utils.PluginStatusCode.CRITICAL - elif disk['usePcent'] >= warn: - disk['statusCode'] = utils.PluginStatusCode.WARNING - elif disk['usePcent'] < warn: - disk['statusCode'] = utils.PluginStatusCode.OK - disk['availPcent'] = 100 - disk['usePcent'] - return disk - - -def getDisk(path, crit, warn, usage=None, lvm=False): - if usage: - return getUsageAndFree("df -B%s %s" % (usage, path), - path, crit, warn, lvm) - else: - return getUsageAndFree("df -BG %s" % path, - path, crit, warn, lvm) - - -def getInode(path, crit, warn, lvm=False): - return getUsageAndFree("df -i %s" % path, - path, crit, warn, lvm) - - -def getMounts(searchQuery, excludeList=[]): - mountPaths = [] - f = open("/etc/mtab") - for i in f.readlines(): - if i.startswith(searchQuery): - if not excludeList: - mountPaths.append(i.split()[0]) - else: - device = i.split() - if not device[0] in options.exclude and\ - not device[1] in options.exclude: - mountPaths.append(device[0]) - f.close() - return mountPaths - - -def parse_input(): - parser = OptionParser() - parser.add_option('-w', '--warning', action='store', type='int', - dest='warn', - help='Warning count in %', default=WARNING_LEVEL) - parser.add_option('-c', '--critical', action='store', type='int', - dest='crit', - help='Critical count in %', default=CRITICAL_LEVEL) - parser.add_option('-u', '--usage', action="store", dest='usage', - help='Usage in %/GB/MB/KB', default=None) - parser.add_option('-l', '--lvm', action="store_true", - dest='lvm', - help='List lvm mounts', default=False) - parser.add_option('-d', '--inode', action="store_true", dest='inode', - help='List inode usage along with disk/brick usage', - default=False) - parser.add_option('-a', '--all', action="store_true", - dest='all', - help='List all mounts', default=False) - parser.add_option('-n', '--ignore', action="store_true", - dest='ignore', - help='Ignore errors', default=False) - parser.add_option('-i', '--include', action='append', type='string', - dest='mountPath', - help='Mount path', default=[]) - parser.add_option('-x', '--exclude', action="append", type='string', - dest='exclude', - help='Exclude disk/brick') - parser.add_option('-s', action="store_true", default=False, - dest='showErrorDisk', - help='Show critical or warning disks in the status') - parser.add_option('-t', '--thinPool', action="store_true", - dest='thinPool', - help='Lists detail of underlying thin pool', - default=False) - return parser.parse_args() - - -def _getMsg(okList, warnList, critList): - msg = ", ".join(critList) - errorDiskMsg = msg - if critList and (warnList or okList): - msg = "CRITICAL: " + msg - if warnList: - if msg: - msg += "; WARNING: " - msg += ", ".join(warnList) - if errorDiskMsg: - errorDiskMsg += "; WARNING: " + ", ".join(warnList) - else: - errorDiskMsg = msg - if okList: - if msg: - msg += "; OK: " - msg += ", ".join(okList) - return msg, errorDiskMsg - - -def _getUnitAndType(val): - unit = utils.convertSize(val, "GB", "TB") - if unit >= 1: - return unit, "TB" - else: - return val, "GB" - - -def showDiskUsage(warn, crit, mountPaths, toListInode, usage=False, - isLvm=False, ignoreError=False, showErrorDisk=True, - thinPool=False): - diskPerf = [] - warnList = [] - critList = [] - okList = [] - mounts = [] - statusCode = INVALID_STATUS_CODE - totalUsed = 0 - totalSize = 0 - noOfMounts = len(mountPaths) - maxPercentUsed = 0 - - if thinPool: - rc, out, err = utils.execCmd(["lvm"] + - ("vgs --unquoted --noheading " + - "--nameprefixes --separator : " + - "--nosuffix --units g -o " + - "lv_path,data_percent,pool_lv,lv_attr," - "lv_size,lv_name").split()) - if rc == 0: - res = [] - for i in out: - tem = i.split(":") - dct = {} - for j in tem: - tp = j.split("=") - dct[tp[0].replace(" ", "")] = tp[1] - res.append(dct) - else: - thinPool = False - - for path in mountPaths: - disk = getDisk(path, crit, warn, usage, isLvm) - inode = getInode(path, crit, warn, isLvm) - if thinPool: - thinLv = getLVdetails(disk['fs'], res) - if thinLv and thinLv['thinLv']: - if disk['usePcent'] >= crit or thinLv[ - 'actualUsedPercent'] >= crit: - disk['statusCode'] = utils.PluginStatusCode.CRITICAL - elif disk['usePcent'] >= warn or thinLv[ - 'actualUsedPercent'] >= warn: - disk['statusCode'] = utils.PluginStatusCode.WARNING - elif disk['usePcent'] < warn or thinLv[ - 'actualUsedPercent'] < warn: - disk['statusCode'] = utils.PluginStatusCode.OK - else: - thinPool = False - - if disk['path'] in mounts: - continue - if not disk['used'] or not inode['used']: - if not ignoreError: - sys.exit(utils.PluginStatusCode.UNKNOWN) - - if disk['path']: - mounts.append(disk['path']) - data = "" - if usage and disk['path']: - data = "%s=%.1f%s;%.1f;%.1f;0;%.1f" % ( - disk['path'], - disk['used'], - usage, - warn * disk['size'] / 100, - crit * disk['size'] / 100, - disk['size']) - if toListInode: - data += " %s=%.1f;%.1f;%.1f;0;%.1f" % ( - inode['path'], - inode['used'], - warn * inode['used'] / 100, - crit * inode['used'] / 100, - inode['size']) - elif disk['path']: - if thinPool and thinLv['thinLv']: - data = "%s=%.2f%%;%s;%s;0;%s" % ( - disk['path'], - disk['usePcent'], - warn, - crit, - disk['size']) - data += " Thin-pool=%.2f%%;%s;%s;0;%.1f" % ( - thinLv['actualUsedPercent'], - warn, - crit, - thinLv['actualTotalSize']) - else: - data = "%s=%.2f%%;%s;%s;0;%s" % ( - disk['path'], - disk['usePcent'], - warn, - crit, - disk['size']) - if toListInode: - data += " %s=%.2f%%;%s;%s;0;%s" % ( - inode['path'], - inode['usePcent'], - warn, - crit, - inode['size']) - diskPerf.append(data) - - totalUsed += disk['used'] - totalSize += disk['size'] - if disk['usePcent'] > maxPercentUsed: - maxPercentUsed = disk['usePcent'] - - # adding into status message if there is any - # specfic status found (short msg for list of disks) - msg = "" - if disk['status'] and disk['msg']: - if noOfMounts == 1: - msg = "%s=%s(%s)" % (disk['fs'], disk['path'], - disk['status']) - else: - msg = "%s(%s)" % (disk['fs'], disk['msg']) - else: - if noOfMounts == 1: - msg = "%s=%s" % (disk['fs'], disk['path']) - else: - msg = "%s" % (disk['path']) - - if disk['statusCode'] == utils.PluginStatusCode.CRITICAL or \ - inode['statusCode'] == utils.PluginStatusCode.CRITICAL: - statusCode = utils.PluginStatusCode.CRITICAL - critList.append(msg) - elif (disk['statusCode'] == utils.PluginStatusCode.WARNING or - inode['statusCode'] == utils.PluginStatusCode.WARNING): - # if any previous disk statusCode is not critical - # we should not change the statusCode into warning - if statusCode != utils.PluginStatusCode.CRITICAL: - statusCode = utils.PluginStatusCode.WARNING - # just adding warning values into the list - warnList.append(msg) - elif disk['statusCode'] == utils.PluginStatusCode.OK: - if statusCode == INVALID_STATUS_CODE or \ - statusCode == utils.PluginStatusCode.OK: - statusCode = utils.PluginStatusCode.OK - okList.append(msg) - else: - # added \ to fix E125 pep8 error - if statusCode != utils.PluginStatusCode.CRITICAL or \ - statusCode != utils.PluginStatusCode.WARNING: - statusCode = utils.PluginStatusCode.UNKNOWN - okList.append(msg) - - msg, errorDiskMsg = _getMsg(okList, warnList, critList) - - if totalUsed == 0 and totalSize == 0: - # avoid zero div error - return statusCode, "mount: %s" % msg, diskPerf - if totalUsed == 0: - # avoid zero div error - totUsagePercent = 0 - elif len(mounts) > 1: - totUsagePercent = totalUsed / totalSize * 100 - else: - totUsagePercent = maxPercentUsed - usageMsg = "" - if not usage: - totUsedSz, totUsedSzUnit = _getUnitAndType(totalUsed) - totSpaceSz, totSpaceSzUnit = _getUnitAndType(totalSize) - usageMsg = "%.1f%% used (%s%s out of %s%s)" % (totUsagePercent, - totUsedSz, - totUsedSzUnit, - totSpaceSz, - totSpaceSzUnit) - if thinPool and thinLv['thinLv']: - usageMsg += " [Thin-pool: %.1f%% (%.1fG out of %.1fG)]" % ( - thinLv['actualUsedPercent'], - thinLv['actualUsed'], - thinLv['actualTotalSize']) - else: - usageMsg = "%.1f%% used (%s%s out of %s%s)" % (totUsagePercent, - totalUsed, - usage, - totalSize, - usage) - if showErrorDisk: - msg = "%s\n:mount(s): (%s)" % (errorDiskMsg, msg) - else: - msg = "%s\n:mount(s): (%s)" % (usageMsg, msg) - - return statusCode, msg, diskPerf - - -if __name__ == '__main__': - (options, args) = parse_input() - - if options.lvm: - searchQuery = "/dev/mapper" - else: - searchQuery = "/" - - if not options.mountPath or options.lvm or options.all: - options.mountPath += getMounts(searchQuery, options.exclude) - - statusCode, msg, diskPerf = showDiskUsage(options.warn, - options.crit, - options.mountPath, - options.inode, - options.usage, - options.lvm, - options.ignore, - options.showErrorDisk, - options.thinPool) - - if utils.PluginStatusCode.CRITICAL == statusCode: - sys.stdout.write("%s : %s | %s\n" % ( - utils.PluginStatus.CRITICAL, - msg, - " ".join(diskPerf))) - sys.exit(utils.PluginStatusCode.CRITICAL) - elif utils.PluginStatusCode.WARNING == statusCode: - sys.stdout.write("%s : %s | %s\n" % ( - utils.PluginStatus.WARNING, - msg, - " ".join(diskPerf))) - sys.exit(utils.PluginStatusCode.WARNING) - else: - if options.showErrorDisk: - sys.stdout.write("%s %s | %s\n" % ( - utils.PluginStatus.OK, msg, " ".join(diskPerf))) - else: - sys.stdout.write("%s : %s | %s\n" % ( - utils.PluginStatus.OK, msg, " ".join(diskPerf))) diff --git a/plugins/check_mounts.py b/plugins/check_mounts.py new file mode 100755 index 0000000..342c20a --- /dev/null +++ b/plugins/check_mounts.py @@ -0,0 +1,265 @@ +#!/usr/bin/python +# Copyright (C) 2015 Red Hat Inc +# +# This program 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. +# +# This program 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 this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# + + +import os +import sys +import argparse +import logging + +from glusternagios import utils +from glusternagios.utils import PluginStatusCode, PluginStatus + +ONE_GB_BYTES = 1073741824.0 + + +def _getMountPoint(path): + mount = os.path.realpath(path) + while not os.path.ismount(mount): + mount = os.path.dirname(mount) + return mount + + +def _parseProcMounts(filter=True): + mountPoints = {} + with open('/proc/mounts', 'r') as f: + for line in f: + if line.startswith("/") or not filter: + mount = {} + tokens = line.split() + mount['device'] = tokens[0] + mount['fsType'] = tokens[2] + mount['mountOptions'] = tokens[3] + mountPoints[tokens[1]] = mount + return mountPoints + + +def _getStats(mountPoint): + data = os.statvfs(mountPoint) + total = (data.f_blocks * data.f_bsize) / ONE_GB_BYTES + free = (data.f_bfree * data.f_bsize) / ONE_GB_BYTES + used_percent = 100 - (100.0 * free / total) + total_inode = data.f_files + free_inode = data.f_ffree + used_percent_inode = 100 - (100.0 * free_inode / total_inode) + used = total - free + used_inode = total_inode - free_inode + return {'total': total, + 'free': free, + 'used_percent': used_percent, + 'total_inode': total_inode, + 'free_inode': free_inode, + 'used_inode': used_inode, + 'used': used, + 'used_percent_inode': used_percent_inode} + + +def _getOutputText(detail): + template = "{mount_point} - {{space - free: {free:0.3f} GiB, " \ + "used: {used_percent:0.3f}%}}, {{inode - free: {free_inode}" \ + " , used: {used_percent_inode:0.3f}%}}" + + if detail['thinpool_size']: + template += ", {{thinpool-data - free: {thinpool_free:.3f} GiB," \ + " used: {thinpool_used_percent:.3f}%}}, " \ + "{{thinpool-metadata - free: {metadata_free:.3f} GiB," \ + " used: {metadata_used_percent:.3f}%}}" + return template.format(**detail) + + +def _getPerfdata(detail, warn, crit): + template = "{mount_point}={used_percent:.3f}%;{warn};{crit};0;{total:.3f}"\ + " {mount_point}.inode={used_percent_inode:.3f}%;{warn};{crit}" \ + ";0;{total_inode}" + if detail['thinpool_size']: + template += " {mount_point}.thinpool={thinpool_used_percent:.3f}%;" \ + "{warn};{crit};0;{thinpool_size:.3f} {mount_point}." \ + "thinpool-metadata={metadata_used_percent:.3f}%;{warn};{crit};0;" \ + "{metadata_size:.3f}" + return template.format(warn=warn, crit=crit, **detail) + + +def _getStatusInfo(detail, warn, crit): + rc = PluginStatus.OK + msg = [] + + parameter = {'metadata_used_percent': ['thinpool-metadata', + 'metadata_used', + 'metadata_size', ' GiB'], + 'thinpool_used_percent': ['thinpool-data', 'thinpool_used', + 'thinpool_size', ' GiB'], + 'used_percent': ['space', 'used', + 'total', ' GiB'], + 'used_percent_inode': ['inode', 'used_inode', + 'total_inode', '']} + + for k, v in parameter.iteritems(): + if not detail[k]: + continue + if k == 'used_percent_inode': + m = "%s used %d / %d%s" % (v[0], detail[v[1]], + detail[v[2]], v[3]) + else: + m = "%s used %0.3f / %0.3f%s" % (v[0], detail[v[1]], + detail[v[2]], v[3]) + if detail[k] >= crit: + rc = PluginStatus.CRITICAL + msg.append(m) + elif detail[k] >= warn: + if rc != PluginStatus.CRITICAL: + rc = PluginStatus.WARNING + msg.append(m) + + if rc == PluginStatus.OK: + out = '' + else: + out = "mount point {mount_point} {{{msg}}}".format(msg=", ".join(msg), + **detail) + return rc, out + + +def parse_input(): + parser = argparse.ArgumentParser() + parser.add_argument("-w", "--warning", action="store", + required=True, type=int, + help="Warning threshold in percentage") + parser.add_argument("-c", "--critical", action="store", + required=True, type=int, + help="Critical threshold in percentage") + group = parser.add_mutually_exclusive_group() + group.add_argument("-e", "--exclude", action="append", default=[], + help="exclude given interface") + group.add_argument("-i", "--include", action="append", default=[], + help="add given interface for monitoring") + args = parser.parse_args() + return args + + +def getLvs(): + lvmCommand = ["lvm", "vgs", "--unquoted", "--noheading", + "--nameprefixes", "--separator", "$", + "--nosuffix", "--units", "m", "-o", + "lv_uuid,lv_name,data_percent,pool_lv,lv_attr," + "lv_size,lv_path,lv_metadata_size," + "metadata_percent,vg_name"] + rc, out, err = utils.execCmd(lvmCommand) + if rc != 0: + logging.error( + "lvm command failed.\nCommand=%s\nrc=%s\nout=%s\nerr=%s" + % (lvmCommand, rc, out, err) + ) + return None + l = map(lambda x: dict(x), + map(lambda x: [e.split('=') for e in x], + map(lambda x: x.strip().split('$'), out))) + + d = {} + for i in l: + if i['LVM2_LV_ATTR'][0] == 't': + k = "%s/%s" % (i['LVM2_VG_NAME'], i['LVM2_LV_NAME']) + else: + k = os.path.realpath(i['LVM2_LV_PATH']) + d.update({k: i}) + return d + + +def getMountStats(exclude, include): + def _getMounts(exclude=[], include=[]): + excludeList = map(_getMountPoint, exclude) + includeList = map(_getMountPoint, include) + mountPoints = _parseProcMounts() + if excludeList: + outList = set(mountPoints) - set(excludeList) + elif includeList: + outList = set(mountPoints).intersection(set(includeList)) + else: + return mountPoints + # list comprehension to build dictionary does not work in python 2.6.6 + mounts = {} + for k in outList: + mounts[k] = mountPoints[k] + return mounts + + def _getThinPoolStat(device): + out = {'thinpool_size': None, + 'thinpool_used_percent': None, + 'metadata_size': None, + 'metadata_used_percent': None, + 'thinpool_free': None, + 'metadata_free': None, + 'thinpool_used': None, + 'metadata_used': None} + + if lvs and device in lvs and \ + lvs[device]['LVM2_LV_ATTR'][0] == 'V': + thinpool = "%s/%s" % (lvs[device]['LVM2_VG_NAME'], + lvs[device]['LVM2_POOL_LV']) + out['thinpool_size'] = float( + lvs[thinpool]['LVM2_LV_SIZE']) / 1024 + out['thinpool_used_percent'] = float( + lvs[thinpool]['LVM2_DATA_PERCENT']) + out['metadata_size'] = float( + lvs[thinpool]['LVM2_LV_METADATA_SIZE']) / 1024 + out['metadata_used_percent'] = float( + lvs[thinpool]['LVM2_METADATA_PERCENT']) + out['thinpool_free'] = out['thinpool_size'] * ( + 1 - out['thinpool_used_percent']/100.0) + out['thinpool_used'] = out['thinpool_size'] - out['thinpool_free'] + out['metadata_free'] = out['metadata_size'] * ( + 1 - out['metadata_used_percent']/100.0) + out['metadata_used'] = out['metadata_size'] - out['metadata_free'] + return out + + mountPoints = _getMounts(exclude, include) + lvs = getLvs() + mountDetail = {} + for mount, info in mountPoints.iteritems(): + mountDetail[mount] = _getStats(mount) + mountDetail[mount].update( + _getThinPoolStat(os.path.realpath(info['device'])) + ) + mountDetail[mount].update({'mount_point': mount}) + return mountDetail + + +def getPrintableStatus(mountDetail, warning, critical): + finalRc = utils.PluginStatus.OK + finalMsg = [] + finalOut = [] + finalPerfdata = [] + for mount, detail in mountDetail.iteritems(): + finalOut.append(_getOutputText(detail)) + finalPerfdata.append(_getPerfdata(detail, warning, critical)) + rc, msg = _getStatusInfo(detail, warning, critical) + if msg: + finalMsg.append(msg) + if getattr(PluginStatusCode, rc) > getattr(PluginStatusCode, finalRc): + finalRc = rc + return finalRc, finalMsg, finalOut, finalPerfdata + + +if __name__ == '__main__': + args = parse_input() + mountDetail = getMountStats(args.exclude, args.include) + rc, msg, msgDet, perfdata = getPrintableStatus(mountDetail, + args.warning, + args.critical) + + print "%s: %s" % (rc, ", ".join(msg)) + print "%s | %s" % ("\n".join(msgDet), "\n".join(perfdata)) + sys.exit(getattr(PluginStatusCode, rc)) |