From 2c0d94f99c51b8ce13f901949e8283909450ff39 Mon Sep 17 00:00:00 2001 From: Nishanth Thomas Date: Wed, 19 Mar 2014 13:04:54 +0530 Subject: plugins: Added the files for volume utilization and status plugins fixed few issues with code fixed the pep8 issues Change-Id: I2c2b32e7bbdc30a7b68acc1ce4e0468fd53a3050 Signed-off-by: Nishanth Thomas Reviewed-on: https://cuckoo.blr.redhat.com:8443/42 Reviewed-by: Sahina Bose Tested-by: Sahina Bose --- plugins/Makefile.am | 6 ++ plugins/check_vol_status.py | 154 +++++++++++++++++++++++++++++++++++++++ plugins/check_vol_utilization.py | 105 ++++++++++++++++++++++++++ plugins/volcap/Makefile.am | 22 ++++++ plugins/volcap/__init__.py | 0 plugins/volcap/setup.py | 11 +++ plugins/volcap/tests.py | 0 plugins/volcap/volCap.c | 143 ++++++++++++++++++++++++++++++++++++ 8 files changed, 441 insertions(+) create mode 100755 plugins/check_vol_status.py create mode 100755 plugins/check_vol_utilization.py create mode 100644 plugins/volcap/Makefile.am create mode 100644 plugins/volcap/__init__.py create mode 100644 plugins/volcap/setup.py create mode 100644 plugins/volcap/tests.py create mode 100644 plugins/volcap/volCap.c (limited to 'plugins') diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 37da1cd..4c840c1 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -1,3 +1,7 @@ +SUBDIRS = \ + volcap \ + $(NULL) + cronddir = $(sysconfdir)/cron.d crond_DATA = \ gluster-sysstat.crontab \ @@ -5,6 +9,8 @@ crond_DATA = \ dist_glusternagiosplugins_PYTHON = \ check_disk_and_inode.py \ + check_vol_utilization.py \ + check_vol_status.py \ cpu.py \ __init__.py \ memory.py \ diff --git a/plugins/check_vol_status.py b/plugins/check_vol_status.py new file mode 100755 index 0000000..9e526da --- /dev/null +++ b/plugins/check_vol_status.py @@ -0,0 +1,154 @@ +#!/usr/bin/python +import re +import commands +import argparse +import xml.etree.ElementTree as ET +from glusternagios import utils + + +def parseXml(xmldoc, searchStr): + root = ET.fromstring(xmldoc) + #statusStr = root.findall("./volInfo/volumes/volume/bricks/brick") + statusStr = root.findall(searchStr) + return statusStr + + +def getVolumeStatus(vol_status_out): + xmlElemList = parseXml(vol_status_out, "./opRet") + #print xmlElemList[0].text + if xmlElemList[0].text == "0": + #print "Started" + vol_status = "Started" + else: + #print "Stopped" + vol_status = "Stopped" + return vol_status + + +def getNagiosServerIP(): + nagiosIP = "" + nscaConfig = open("/etc/nagios/nagios_server.cfg", "r+") + for line in nscaConfig.readlines(): + if "nagios_server" in line: + #print line.rstrip() + line = line.rstrip() + nagiosIP = line.rpartition('=')[2] + #print nagiosIP + return nagiosIP + + +def send_to_nsca(hostName, serviceName, exitStatus, resultString): + #print hostName + #print serviceName + #print exitStatus + #print resultString + f = open('out.txt', 'w') + print >> f, '%s\t%s\t%s\t%s' % (hostName, + serviceName, + exitStatus, + resultString) + f.close() + nagiosIP = getNagiosServerIP() + command_send_nsca = "send_nsca -H " + nagiosIP + \ + " -c /etc/nagios/send_nsca.cfg < out.txt" + #print command_send_nsca + commands.getoutput(command_send_nsca) + #print nsca_stat + + +def showBrickStatus(vol_status_out): + ipPat = re.compile("^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$") + xmlElemList = [] + brickName = "" + #brickStatus = "" + exitStatus = utils.PluginStatusCode.OK + resultString = "" + brickIP = "" + vol_status = getVolumeStatus(vol_status_out) + if vol_status == "Started": + xmlElemList = parseXml(vol_status_out, + "./volStatus/volumes/volume/node") + for node in xmlElemList: + if ipPat.match(node.find('hostname').text): + brickIP = node.find('hostname').text + brickName = "Brick-" + brickName += brickIP + brickName += ":" + brickName += node.find('path').text + brickName += "-Status" + #print brickName + if node.find('status').text == "1": + exitStatus = utils.PluginStatusCode.OK + resultString = "Brick Status: OK" + else: + exitStatus = utils.PluginStatusCode.CRITICAL + resultString = "Brick Status: CRITICAL" + send_to_nsca(brickIP, brickName, exitStatus, resultString) + + +def showVolumeStatus(vol_status_out, volName, clusterName): + xmlElemList = [] + no_of_bricks = 0 + brick_online = 0 + brick_offline = 0 + #brick_list = [] + resultString = "" + exitStatus = utils.PluginStatusCode.OK + serviceName = "Volume-%s-Status" % volName + ipPat = re.compile("^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$") + vol_status = getVolumeStatus(vol_status_out) + if vol_status == "Started": + xmlElemList = parseXml(vol_status_out, + "./volStatus/volumes/volume/node") + for node in xmlElemList: + if ipPat.match(node.find('hostname').text): + #brick_list.insert(0,node.find('hostname').text) + no_of_bricks += 1 + if node.find('status').text == "1": + brick_online += 1 + else: + brick_offline += 1 + #no_of_bricks = len(brick_list) + #print len(brick_list) + + if vol_status != "Started": + resultString = "Volume Status CRITICAL: Volume Stopped Total" \ + " Bricks: %s|Bricks Online=%s" % (no_of_bricks, + brick_online) + exitStatus = utils.PluginStatusCode.CRITICAL + elif brick_offline == no_of_bricks: + resultString = "Volume Status CRITICAL: All Bricks are Down Total" \ + " Bricks: %s|Bricks Online=%s" % (no_of_bricks, + brick_online) + exitStatus = utils.PluginStatusCode.CRITICAL + elif brick_online != no_of_bricks: + resultString = "Volume Status WARNING: Some Bricks are Down Total" \ + " Bricks: %s|Bricks Online=%s" % (no_of_bricks, + brick_online) + exitStatus = utils.PluginStatusCode.WARNING + else: + resultString = "Volume Status OK: Total" \ + " Bricks: %s|Bricks Online=%s" % (no_of_bricks, + brick_online) + exitStatus = utils.PluginStatusCode.OK + + send_to_nsca(clusterName, serviceName, exitStatus, resultString) + + +def parse_input(): + + parser = argparse.ArgumentParser(usage='%(prog)s [-h] ') + parser.add_argument("volume", help="Name of the volume to get the Status") + parser.add_argument("cluster", + help="Name of the cluster, volume belongs to") + args = parser.parse_args() + return args + + +if __name__ == '__main__': + args = parse_input() + #Get the volume status + command_vol_status = "sudo gluster volume status " + args.volume + " --xml" + vol_status_out = commands.getoutput(command_vol_status) + showVolumeStatus(vol_status_out, args.volume, args.cluster) + showBrickStatus(vol_status_out) diff --git a/plugins/check_vol_utilization.py b/plugins/check_vol_utilization.py new file mode 100755 index 0000000..d0ab5c6 --- /dev/null +++ b/plugins/check_vol_utilization.py @@ -0,0 +1,105 @@ +#!/usr/bin/python +# check_vol_utilization.py -- nagios plugin uses libgfapi output for perf data +# 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 sys +import argparse +import capacity +from glusternagios import utils + + +def showVolumeUtilization(vname, warnLevel, critLevel): + buf = {} + try: + buf = capacity.statvfs(vname, "localhost") + except Exception as e: + sys.stdout.write(("Volume Utilization UNKNOWN: %s\n" % str(e))) + sys.exit(utils.PluginStatusCode.UNKNOWN) +# print buf + +#################################################################### +#statvfs.frsize * statvfs.f_blocks# Size of filesystem in bytes # +#statvfs.frsize * statvfs.f_bfree # Actual number of free bytes # +#statvfs.frsize * statvfs.f_bavail# Number of free bytes that # +#ordinary users are allowed to use (excl. reserved space # +#################################################################### + #total size in KB + total_size = (buf['f_bsize'] * buf['f_blocks']) * 0.000976563 + #Available free size in KB + free_size = (buf['f_bsize'] * buf['f_bavail']) * 0.000976563 + #used size in KB + used_size = total_size - ((buf['f_bsize'] * buf['f_bfree']) * 0.000976563) + vol_utilization = (used_size / total_size) * 100 +# print int(total_size) +# print int(free_size) +# print int(used_size) +# print vol_utilization + perfLines = [] + perfLines.append(("utilization=%s%%;%s;%s total=%s " + "used=%s free=%s" % (str(int(vol_utilization)), + str(warnLevel), str(critLevel), + str(int(total_size)), + str(int(used_size)), + str(int(free_size))))) +# print perfLines + + if int(vol_utilization) > critLevel: + sys.stdout.write( + ("Volume Utilization CRITICAL: Utilization:%s%%" + "| %s\n" % (str(int(vol_utilization)), " ".join(perfLines)))) + sys.exit(utils.PluginStatusCode.CRITICAL) + elif int(vol_utilization) > warnLevel: + sys.stdout.write( + ("Volume Utilization WARNING: Utilization:%s%%" + "| %s\n" % (str(int(vol_utilization)), " ".join(perfLines)))) + sys.exit(utils.PluginStatusCode.WARNING) + else: + sys.stdout.write( + ("Volume Utilization OK: Utilization:%s%%" + "| %s\n" % (str(int(vol_utilization)), " ".join(perfLines)))) + sys.exit(utils.PluginStatusCode.OK) + + +def parse_input(): + + parser = argparse.ArgumentParser( + usage='%(prog)s [-h] -w -c ') + parser.add_argument("volume", + help="Name of the volume to get the Utilization") + parser.add_argument("-w", + "--warning", + action="store", + type=int, + help="Warning Threshold in percentage") + parser.add_argument("-c", + "--critical", + action="store", + type=int, + help="Critical Threshold in percentage") + args = parser.parse_args() + if not args.critical or not args.warning: + print "UNKNOWN:Missing critical/warning threshold value." + sys.exit(3) + if args.critical <= args.warning: + print "UNKNOWN:Critical must be greater than Warning." + sys.exit(3) + return args + +if __name__ == '__main__': + args = parse_input() + showVolumeUtilization(args.volume, args.warning, args.critical) diff --git a/plugins/volcap/Makefile.am b/plugins/volcap/Makefile.am new file mode 100644 index 0000000..6365336 --- /dev/null +++ b/plugins/volcap/Makefile.am @@ -0,0 +1,22 @@ + +dist_glusternagiosplugins_PYTHON = \ + volcap.so \ + $(NULL) + +volcap.so: volCap.c setup.py + (cd $(srcdir); $(PYTHON) setup.py build \ + --build-temp $(abs_builddir) --build-lib $(abs_builddir)) +all-local: volcap.so + +EXTRA_DIST = \ + $(NULL) + +EXTRA_DIST = \ + __init__.py \ + volCap.c \ + tests.py \ + setup.py + +CLEANFILES = \ + volcap.o \ + volcap.so diff --git a/plugins/volcap/__init__.py b/plugins/volcap/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugins/volcap/setup.py b/plugins/volcap/setup.py new file mode 100644 index 0000000..b37e090 --- /dev/null +++ b/plugins/volcap/setup.py @@ -0,0 +1,11 @@ +from distutils.core import setup, Extension + +module1 = Extension('capacity', + sources=['volCap.c'], libraries=['gfapi']) + +setup(name='capacity', + version='1.0', + description='Gets the volume capcity Utilization', + py_modules=['__init__'], + url='redhat.com', + ext_modules=[module1]) diff --git a/plugins/volcap/tests.py b/plugins/volcap/tests.py new file mode 100644 index 0000000..e69de29 diff --git a/plugins/volcap/volCap.c b/plugins/volcap/volCap.c new file mode 100644 index 0000000..5eb61db --- /dev/null +++ b/plugins/volcap/volCap.c @@ -0,0 +1,143 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "/usr/include/glusterfs/api/glfs.h" +#include "/usr/include/glusterfs/api/glfs-handles.h" +#include + +#define USAGE_ERROR -1 +#define GLFS_NEW_FAILURE -2 +#define GLFS_INIT_FAILURE -3 +#define GLFS_STATVFS_FAILURE -4 +#define GLFS_FINI_FAILURE -5 +#define DEFAULT_TRANSPORT "tcp" +#define DEFAULT_SERVER "127.0.0.1" +#define DEFAULT_SERVER_PORT 24007 + +static PyObject *StatvfsError; + +int get_volume_statvfs (const char *volume_name, const char *server_name, struct statvfs *buf) +{ + glfs_t *fs = NULL; + int ret = 0; + struct statvfs statvfsinfo = {0, }; + int rv = 0; + + if (!(volume_name && buf)) + { + return USAGE_ERROR; + } + + fs = glfs_new (volume_name); + if (!fs) + { + //fprintf (stderr, "glfs_new: returned NULL\n"); + syslog (LOG_ERR, "glfs_new: returned NULL"); + return GLFS_NEW_FAILURE; + } + + if (server_name) + { + ret = glfs_set_volfile_server(fs, DEFAULT_TRANSPORT, server_name, DEFAULT_SERVER_PORT); + } + else + { + ret = glfs_set_volfile_server(fs, DEFAULT_TRANSPORT, DEFAULT_SERVER, DEFAULT_SERVER_PORT); + } + + ret = glfs_set_logging (fs, "/tmp/libg.txt", 2); + + ret = glfs_init (fs); + if (ret != 0) + { + //fprintf (stderr, "glfs_init() failed with code %d\n", ret); + syslog (LOG_ERR, "glfs_init() failed with code %d",ret); + rv = GLFS_INIT_FAILURE; + goto out; + } + + /*fprintf (stdout, "waiting for 3 seconds to initialize\n");*/ + sleep (3); + + ret = glfs_statvfs (fs, "/", &statvfsinfo); + if (ret == 0) + { + *buf = statvfsinfo; + } + else + { + //fprintf (stderr, "glfs_statvfs() failed with [%d:%s] for \"/\"\n", ret, strerror (errno)); + syslog (LOG_ERR, "glfs_statvfs() failed with [%d:%s] for \"/\"\n", ret, strerror (errno)); + rv = GLFS_STATVFS_FAILURE; + } + + out: + ret = glfs_fini (fs); + if (ret != 0) + { + //fprintf (stderr, "glfs_fini() failed with code %d\n", ret); + syslog (LOG_ERR, "glfs_fini() failed with code %d\n", ret); + } + + return rv; +} + +static PyObject *glfspy_statvfs (PyObject *self, PyObject *args) +{ + char *volume_name = NULL; + char *server_name = NULL; + int port = 0; + char *transport = NULL; + struct statvfs buf = {0, }; + int rv = 0; +#define USAGE_ERROR -1 +#define GLFS_NEW_FAILURE -2 +#define GLFS_INIT_FAILURE -3 +#define GLFS_STATVFS_FAILURE -4 +#define GLFS_FINI_FAILURE -5 + + StatvfsError = PyErr_NewException("statvfs.error", NULL, NULL); + setlogmask (LOG_UPTO (LOG_DEBUG)); + openlog ("statvfs", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1); + syslog (LOG_INFO, "Invoking glfspy_statvfs to get the volume utlization"); + if (!PyArg_ParseTuple (args, "s|ziz", &volume_name, &server_name, &port, &transport)) + { + PyErr_SetString(StatvfsError, "Argument parsing failed"); + return NULL; + } + + rv = get_volume_statvfs (volume_name, server_name, &buf); + closelog (); + //return Py_BuildValue("i", rv); + if(rv == 0) + return Py_BuildValue("{s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i}","f_bsize",buf.f_bsize,"f_frsize",buf.f_frsize,"f_blocks",buf.f_blocks,"f_bfree",buf.f_bfree,"f_bavail",buf.f_bavail,"f_files",buf.f_files,"f_ffree",buf.f_ffree,"f_favail",buf.f_favail,"f_fsid",buf.f_fsid,"f_flag",buf.f_flag,"f_namemax",buf.f_namemax); + else { + if(rv == USAGE_ERROR) + PyErr_SetString(StatvfsError, "Usage error"); + if(rv == GLFS_NEW_FAILURE) + PyErr_SetString(StatvfsError, "glfs_new() failed"); + if(rv == GLFS_INIT_FAILURE) + PyErr_SetString(StatvfsError, "glfs_init() failed"); + if(rv == GLFS_STATVFS_FAILURE) + PyErr_SetString(StatvfsError, "glfs_statvfs() failed"); + //return Py_BuildValue("i", rv); + return NULL; + } +} + + +static PyMethodDef glfspy_methods[] = { + { "statvfs", (PyCFunction)glfspy_statvfs, METH_VARARGS, NULL }, + { NULL, NULL, 0, NULL } +}; + + +PyMODINIT_FUNC initcapacity () +{ + Py_InitModule3 ("capacity", glfspy_methods, "gluster gfapi top level extension module."); +} -- cgit