summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac3
-rw-r--r--plugins/Makefile.am6
-rwxr-xr-xplugins/check_vol_status.py154
-rwxr-xr-xplugins/check_vol_utilization.py105
-rw-r--r--plugins/volcap/Makefile.am22
-rw-r--r--plugins/volcap/__init__.py0
-rw-r--r--plugins/volcap/setup.py11
-rw-r--r--plugins/volcap/tests.py0
-rw-r--r--plugins/volcap/volCap.c143
9 files changed, 444 insertions, 0 deletions
diff --git a/configure.ac b/configure.ac
index 059f6a6..db4147e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -88,11 +88,14 @@ AX_PYTHON_MODULE([pthreading], [fatal])
AX_PYTHON_MODULE([pyinotify], [fatal])
AX_PYTHON_MODULE([selinux], [fatal])
+#PKG_CHECK_MODULES("libgfapi", libgfapi >= 3.4.0)
+
# Keep sorted
AC_CONFIG_FILES([
Makefile
gluster-nagios-addons.spec
plugins/Makefile
+ plugins/volcap/Makefile
tests/Makefile
tests/run_tests_local.sh
tests/run_tests.sh
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] <volume> <cluster>')
+ 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] <volume> -w <Warning> -c <Critical>')
+ 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
--- /dev/null
+++ b/plugins/volcap/__init__.py
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
--- /dev/null
+++ b/plugins/volcap/tests.py
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 <Python.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/statvfs.h>
+#include "/usr/include/glusterfs/api/glfs.h"
+#include "/usr/include/glusterfs/api/glfs-handles.h"
+#include <syslog.h>
+
+#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.");
+}