summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRamesh Nachimuthu <rnachimu@redhat.com>2014-03-28 17:48:54 +0530
committerBala.FA <barumuga@redhat.com>2014-04-29 10:21:37 +0530
commit05278f31d54d89ef0a68e94af389d66a3c3b6f74 (patch)
tree933d5ece03e544b190a5fac9a97d5b647b41d748
parentb0f3cfc18011f149e1ce4b885fd47a6828a3c2ca (diff)
AutoDiscovery: Auto discovery for gluster entities
Basic plugin which will discover all the basic gluster entities and creates nagios configuration. Change-Id: I71f05dec9bcce74969db300393f7f7c178161dba Signed-off-by: Ramesh Nachimuthu <rnachimu@redhat.com> Reviewed-on: https://code.engineering.redhat.com/gerrit/22100 Reviewed-by: Sahina Bose <sabose@redhat.com>
-rw-r--r--config/Makefile.am1
-rw-r--r--config/gluster-commands.cfg5
-rw-r--r--config/gluster-host.cfg.template34
-rw-r--r--configure.ac3
-rw-r--r--nagios-server-addons.spec.in2
-rw-r--r--plugins/Makefile.am2
-rw-r--r--plugins/config_generator.py186
-rw-r--r--plugins/constants.py.in5
-rwxr-xr-xplugins/discovery.py138
-rw-r--r--tests/Makefile.am1
-rw-r--r--tests/test_config_generator.py74
11 files changed, 451 insertions, 0 deletions
diff --git a/config/Makefile.am b/config/Makefile.am
index 7ee5a11..0f7f73a 100644
--- a/config/Makefile.am
+++ b/config/Makefile.am
@@ -11,6 +11,7 @@ glusternagiosconf_DATA = \
glusternagiosdefaultconfdir = $(sysconfdir)/nagios/gluster/default
glusternagiosdefaultconf_DATA = \
glustercluster.cfg.sample \
+ gluster-host.cfg.template \
node1.cfg \
$(NULL)
diff --git a/config/gluster-commands.cfg b/config/gluster-commands.cfg
index c2d70a7..41aa99b 100644
--- a/config/gluster-commands.cfg
+++ b/config/gluster-commands.cfg
@@ -62,6 +62,11 @@ define command{
}
define command{
+ command_name gluster_auto_discovery
+ command_line $USER1$/gluster/discovery.py -H $ARG1$ -c $ARG2$
+}
+
+define command{
command_name check_dummy
command_line $USER1$/check_dummy 0
}
diff --git a/config/gluster-host.cfg.template b/config/gluster-host.cfg.template
new file mode 100644
index 0000000..bd1b2af
--- /dev/null
+++ b/config/gluster-host.cfg.template
@@ -0,0 +1,34 @@
+###@GENERATED@###
+# AUTO-GENERATED FILE. DO NOT MODIFY.
+#
+# This confinguration file was automatically generated by the
+# Gluster Auto Discovery tool. It should not be modified by hand.
+#
+
+{% if host['use'] == 'gluster-cluster' -%}
+define hostgroup{
+hostgroup_name {{host['host_name']}}
+alias {{host['alias']}}
+}
+{%- endif %}
+
+define host{
+use {{host['use']}}
+host_name {{host['host_name']}}
+alias {{host['alias']}}
+address {{host['address']}}
+{% if host['check_command'] != "" -%}
+check_command {{host['check_command']}}
+{% endif -%}
+{% if host['hostgroups'] != "" -%}
+hostgroups {{host['hostgroups']}}
+{% endif -%}
+}
+
+{% for service in host['host_services'] %}
+define service{
+{%- for key, value in service.iteritems() %}
+{{ key.ljust(20) }} {{ value }}
+{%- endfor %}
+}
+{% endfor %}
diff --git a/configure.ac b/configure.ac
index e9067e1..93254a4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -56,6 +56,9 @@ AC_SUBST([nagiosserveraddonstestsdir], ['${datarootdir}/${PACKAGE_NAME}/tests'])
AC_SUBST([nagioslivestatussocketpath], ['/var/spool/nagios/cmd/live'])
AC_SUBST([nagioscommandfilepath], ['/var/spool/nagios/cmd/nagios.cmd'])
AC_SUBST([hostmonitoringserviceslist], ['/etc/nagios/gluster/host-monitoring-services.in'])
+AC_SUBST([nrpepath], ['/usr/lib64/nagios/plugins/check_nrpe'])
+AC_SUBST([glusterautoconfdir], ['/etc/nagios/gluster'])
+AC_SUBST([glusterhostconfigtemplatedir], ['/etc/nagios/gluster/default'])
# Checking for pyflakes
AC_PATH_PROG([PYFLAKES], [pyflakes])
diff --git a/nagios-server-addons.spec.in b/nagios-server-addons.spec.in
index b8d6c44..0fe22a1 100644
--- a/nagios-server-addons.spec.in
+++ b/nagios-server-addons.spec.in
@@ -47,6 +47,7 @@ BuildRequires: python-pep8
BuildRequires: python-mock
BuildRequires: python-nose
BuildRequires: python-devel
+BuildRequires: python-jinja2
%if ( 0%{?_with_systemd:1} )
BuildRequires: systemd-units
Requires(post): systemd-units
@@ -74,6 +75,7 @@ Requires: python-pthreading
Requires: python-inotify
Requires: libselinux-python
Requires: rrdtool-perl
+Requires: python-jinja2
%description
Nagios plugin, scripts, configuration files etc for gluster nodes.
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 3787e9a..5606fb3 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -6,6 +6,8 @@ dist_glusternagiosplugins_PYTHON = \
gluster_host_service_handler.py \
livestatus.py \
notify_ovirt_engine_handler.py \
+ discovery.py \
+ config_generator.py \
$(NULL)
EXTRA_DIST = \
diff --git a/plugins/config_generator.py b/plugins/config_generator.py
new file mode 100644
index 0000000..041ee8b
--- /dev/null
+++ b/plugins/config_generator.py
@@ -0,0 +1,186 @@
+#!/usr/bin/python
+#
+# config_generator.py - Nagios configuration generator for gluster
+# entities. 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
+#
+
+from jinja2 import Environment, FileSystemLoader
+import os
+import shutil
+
+
+class GlusterNagiosConfManager:
+
+ def __init__(self, configDir, configTemplateDir, hostTemplateName):
+ self.configDir = configDir
+ self.configTemplateDir = configTemplateDir
+ self.hostTemplateName = hostTemplateName
+ self.__loadJinja()
+
+ def __loadJinja(self):
+ self.jinjaEnv = Environment(
+ loader=FileSystemLoader(self.configTemplateDir))
+ self.hostTemplate = self.jinjaEnv.get_template(self.hostTemplateName)
+
+ def __createHostConfig(self, host):
+ hostConfigStr = self.hostTemplate.render(host=host)
+ hostConfig = {'name': host['host_name'], 'config': hostConfigStr}
+ return hostConfig
+
+ def createHost(self, hostName, alias, template,
+ address, hostGroups, checkCommand, services):
+ host = {}
+ host['host_name'] = hostName
+ host['alias'] = alias
+ host['use'] = template
+ host['address'] = address
+ if checkCommand is not None:
+ host['check_command'] = checkCommand
+ if hostGroups is not None:
+ host['hostgroups'] = hostGroups
+
+ if services is not None:
+ host['host_services'] = services
+ return host
+
+ def __createVolumeUtilizationService(self, volume, clusterName):
+ volumeService = {}
+ volumeService['host_name'] = clusterName
+ volumeService['use'] = 'gluster-service-with-graph'
+ serviceDesc = 'Volume Utilization - %s' % (volume['name'])
+ volumeService['service_description'] = serviceDesc
+ volumeService['_VOL_NAME'] = volume['name']
+ checkCommand = 'check_vol_utilization!%s!%s!70!90' % \
+ (clusterName, volume['name'])
+ volumeService['check_command'] = checkCommand
+ volumeService['notes'] = "Volume type : %s" % (volume['typeStr'])
+ return volumeService
+
+ def __createVolumeStatusService(self, volume, clusterName):
+ volumeService = {}
+ volumeService['host_name'] = clusterName
+ volumeService['use'] = 'gluster-service-with-graph'
+ serviceDesc = 'Volume Status - %s' % (volume['name'])
+ volumeService['service_description'] = serviceDesc
+ volumeService['_VOL_NAME'] = volume['name']
+ checkCommand = 'check_vol_utilization!%s!%s!70!90' % \
+ (clusterName, volume['name'])
+ volumeService['check_command'] = checkCommand
+ volumeService['notes'] = "Volume type : %s" % (volume['typeStr'])
+ return volumeService
+
+ def createClusterUtilizationService(self, clusterName):
+ service = {}
+ service['host_name'] = clusterName
+ service['use'] = 'gluster-service-with-graph'
+ service['service_description'] = 'Cluster Utilization'
+ service['check_command'] = 'check_cluster_vol_usage!80!90'
+ return service
+
+ def createClusterAutoConfigService(self, clusterName, hostIp):
+ service = {}
+ service['host_name'] = clusterName
+ service['use'] = 'generic-service'
+ service['service_description'] = 'Cluster Auto Config'
+ service['check_command'] = "gluster_auto_discovery!%s!%s" % (
+ hostIp, clusterName)
+ service['check_interval'] = '1440'
+ return service
+
+ def createrVolumeServices(self, volumes, clusterName):
+ volumeServices = []
+ for volume in volumes:
+ volumeService = self.__createVolumeUtilizationService(volume,
+ clusterName)
+ volumeServices.append(volumeService)
+ return volumeServices
+
+ def __createBrickUtilizationService(self, brick, hostName):
+ brickService = {}
+ brickService['use'] = 'brick-service'
+ brickService['host_name'] = hostName
+ serviceDesc = "Brick-%s:%s" % (hostName, brick['brickpath'])
+ brickService['service_description'] = serviceDesc
+ brickService['display_name'] = serviceDesc
+ brickService['_BRICK_DIR'] = brick['brickpath']
+ return brickService
+
+ def __createBrickStatusService(self, brick, hostName):
+ brickService = {}
+ brickService['use'] = 'brick-service'
+ brickService['host_name'] = hostName
+ serviceDesc = "Brick-%s:%s-Status" % (hostName, brick['brickpath'])
+ brickService['service_description'] = serviceDesc
+ brickService['display_name'] = serviceDesc
+ brickService['_BRICK_DIR'] = brick['brickpath']
+ return brickService
+
+ def createBrickServices(self, host):
+ brickServices = []
+ for brick in host['bricks']:
+ brickService = self.__createBrickUtilizationService(
+ brick, host['hostip'])
+ brickServices.append(brickService)
+ return brickServices
+
+ def generateNagiosConfigFromGlusterCluster(self, cluster):
+ hostsConfigs = []
+ clusterServices = self.createrVolumeServices(
+ cluster.get('volumes'), cluster['name'])
+ clusterServices.append(self.createClusterUtilizationService(
+ cluster['name']))
+ clusterServices.append(self.createClusterAutoConfigService(
+ cluster['name'], cluster['hosts'][0]['hostip']))
+ clusterHostConfig = self.createHost(
+ cluster['name'], cluster['name'], "gluster-cluster",
+ cluster['name'], "", "check_dummy", clusterServices)
+ hostsConfigs.append(clusterHostConfig)
+ for host in cluster['hosts']:
+ brickServices = self.createBrickServices(host)
+ hostGroups = "gluster_hosts,%s" % (cluster['name'])
+ hostConfig = self.createHost(
+ host['hostip'], host['hostip'], "gluster-host",
+ host['hostip'], hostGroups, "", brickServices)
+ hostsConfigs.append(hostConfig)
+ self.generateConfigFiles(hostsConfigs)
+ return hostsConfigs
+
+ def generateConfigFiles(self, hosts):
+ clusterConfig = {'name': None, 'hostConfigs': []}
+ clusterConfigDir = None
+ for host in hosts:
+ if host['use'] == 'gluster-cluster':
+ clusterConfigDir = self.configDir + "/" + host['host_name']
+ self.__prepareConfDir(clusterConfigDir)
+ clusterConfig['name'] = host['host_name']
+ hostConfig = self.__createHostConfig(host)
+ clusterConfig['hostConfigs'].append(hostConfig)
+ for hostConfig in clusterConfig['hostConfigs']:
+ self.__writeHostConfig(clusterConfigDir, hostConfig)
+
+ def __prepareConfDir(self, confDir):
+ if os.path.exists(confDir):
+ # Deleting the config dir to write new configs
+ shutil.rmtree(confDir)
+ os.mkdir(confDir)
+
+ def __writeHostConfig(self, clusterConfigDir, hostConfig):
+ if clusterConfigDir is None:
+ raise Exception("Cluster configuration directory can't None")
+ configFilePath = clusterConfigDir + "/" + hostConfig['name'] + ".cfg"
+ with open(configFilePath, 'w') as configFile:
+ configFile.write(hostConfig['config'])
diff --git a/plugins/constants.py.in b/plugins/constants.py.in
index 40dd304..bf649de 100644
--- a/plugins/constants.py.in
+++ b/plugins/constants.py.in
@@ -1 +1,6 @@
LIVESTATUS_SOCKETPATH = "@nagioslivestatussocketpath@"
+DEFAULT_AUTO_CONFIG_DIR = "@glusterautoconfdir@"
+HOST_TEMPLATE_DIR = "@glusterhostconfigtemplatedir@"
+HOST_TEMPLATE_NAME = "gluster-host.cfg.template"
+NRPE_PATH = "@nrpepath@"
+NAGIOS_COMMAND_FILE_PATH = "@nagioscommandfilepath@"
diff --git a/plugins/discovery.py b/plugins/discovery.py
new file mode 100755
index 0000000..3fd5573
--- /dev/null
+++ b/plugins/discovery.py
@@ -0,0 +1,138 @@
+#!/usr/bin/python
+# discovery.py Nagios plugin to discover Gluster entities using NRPE
+# 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 argparse
+import commands
+import json
+import datetime
+import re
+from config_generator import GlusterNagiosConfManager
+
+#from glusternagios import utils
+from constants import DEFAULT_AUTO_CONFIG_DIR
+from constants import HOST_TEMPLATE_DIR
+from constants import HOST_TEMPLATE_NAME
+from constants import NRPE_PATH
+from constants import NAGIOS_COMMAND_FILE_PATH
+
+
+def excecNRPECommand(command):
+ """
+ This function executes NRPE command and return the result
+ """
+ status = commands.getoutput(command)
+ return status
+
+
+def discoverhostdetails(host, args):
+ hostparamsdict = {}
+ command = NRPE_PATH + " -H " + host + " -c discoverhostparams"
+ hostparams = excecNRPECommand(command)
+ #convert to dictionary
+ try:
+ hostparamsdict = json.loads(hostparams)
+ except Exception, e:
+ e.args += (hostparams,)
+ raise
+ return hostparamsdict
+
+
+def discoverlogicalcomponents(host):
+ componentlist = []
+ command = NRPE_PATH + " -H " + host + " -c discoverlogicalcomponents"
+ components = excecNRPECommand(command)
+ try:
+ componentlist = json.loads(components)
+ except Exception, e:
+ e.args += (components,)
+ #print e.args
+ raise
+ return componentlist
+
+
+def discovercluster(args):
+ """
+
+ :rtype : None
+ """
+ ipPat = re.compile("^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$")
+ clusterdata = {}
+ #Discover the logical components
+ componentlist = discoverlogicalcomponents(args.hostip)
+ #Discover the peers
+ command = NRPE_PATH + " -H " + args.hostip + " -c discoverpeers"
+ hosts = excecNRPECommand(command)
+ hostlist = json.loads(hosts)
+
+ #Add the ip address of the root node to the peer list
+ #to generate the configuration
+ hostlist.append({"hostip": args.hostip})
+ for host in hostlist:
+ if(ipPat.match(host['hostip'])):
+ host.update(discoverhostdetails(host['hostip'], args))
+ #Get the list of bricks for this host and add to dictionary
+ host['bricks'] = \
+ [brick for brick in componentlist
+ if brick["hostip"] == host['hostip']]
+ clusterdata['hosts'] = hostlist
+ clusterdata['volumes'] =\
+ [volume for volume in componentlist
+ if volume["srvctype"] == "volume"]
+ clusterdata['name'] = args.cluster
+ return clusterdata
+
+
+def parse_input():
+ parser = argparse.ArgumentParser(description="Gluster Auto Discover Tool")
+ parser.add_argument('-c', '--cluster', action='store', dest='cluster',
+ type=str, required=True, help='Cluster name')
+ parser.add_argument('-H', '--hostip', action='store', dest='hostip',
+ type=str, required=True, help='Host IP')
+ parser.add_argument('-d', '--configdir', action='store', dest='configDir',
+ type=str, required=False,
+ help='Configuration directory '
+ 'where output files will be written')
+ args = parser.parse_args()
+ return args
+
+
+def getConfigManager(args):
+ configDir = DEFAULT_AUTO_CONFIG_DIR
+ if args.configDir is not None:
+ configDir = args.configDir
+ configManager = GlusterNagiosConfManager(
+ configDir, HOST_TEMPLATE_DIR, HOST_TEMPLATE_NAME)
+ return configManager
+
+
+def __restartNagios():
+ now = datetime.datetime.now()
+ cmdStr = "[%s] RESTART_PROGRAM\n" % (now)
+ with open(NAGIOS_COMMAND_FILE_PATH, "w") as f:
+ f.write(cmdStr)
+
+
+if __name__ == '__main__':
+ args = parse_input()
+ clusterdata = discovercluster(args)
+ configManager = getConfigManager(args)
+ clusterConfing = configManager.generateNagiosConfigFromGlusterCluster(
+ clusterdata)
+ print " Cluster configurations re-synced successfully from host %s" % \
+ (args.hostip)
+ __restartNagios()
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 40a09a3..9e860b7 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -22,6 +22,7 @@ test_modules = \
test_check_cluster_volusage.py \
test_check_remote_host.py \
test_notify_ovirt_engine_handler.py \
+ test_config_generator.py \
$(NULL)
dist_nagiosserveraddonstests_DATA = \
diff --git a/tests/test_config_generator.py b/tests/test_config_generator.py
new file mode 100644
index 0000000..21500a9
--- /dev/null
+++ b/tests/test_config_generator.py
@@ -0,0 +1,74 @@
+#!/usr/bin/python
+# Copyright 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 mock
+from plugins import config_generator
+from testrunner import PluginsTestCase as TestCaseBase
+
+
+class TestGlusterNagiosConfManager(TestCaseBase):
+ # Method to test the generateNagiosConfigFromGlusterCluster() method
+ @mock.patch('plugins.config_generator.GlusterNagiosConfManager.'
+ 'generateConfigFiles')
+ def testGenerateConfigFiles(self, mock_generateConfigFiles):
+ confManager = self.__getGlusterNagiosConfManager()
+ clusterData = self.__createDummyCluster()
+ clusterConfig = confManager.generateNagiosConfigFromGlusterCluster(
+ clusterData)
+ mock_generateConfigFiles.assert_called()
+ self.__verifyConfig(clusterConfig, clusterData)
+
+ def __verifyConfig(self, clusterConfig, clusterData):
+ self.assertTrue(len(clusterConfig), len(clusterData['hosts']) + 1)
+ self.__verifyClusterConfig(clusterConfig[0], clusterData)
+ for index in range(0, len(clusterData['hosts'])):
+ self.__verifyHostConfig(clusterConfig[index + 1],
+ clusterData['hosts'][index])
+
+ def __verifyHostConfig(self, hostConfig, hostData):
+ self.assertEqual(hostConfig['host_name'], hostData['hostip'])
+ self.assertEqual(hostConfig['alias'], hostData['hostip'])
+ self.assertEqual(hostConfig['address'], hostData['hostip'])
+ self.assertEqual(hostConfig['use'], 'gluster-host')
+
+ def __verifyClusterConfig(self, config, clusterData):
+ self.assertEqual(config['host_name'], clusterData['name'])
+ self.assertEqual(config['alias'], clusterData['name'])
+ self.assertEqual(config['address'], clusterData['name'])
+ self.assertEqual(config['check_command'], "check_dummy")
+ self.assertEqual(config['use'], 'gluster-cluster')
+
+ def createBricks(self, count):
+ bricks = []
+ for number in range(count):
+ brickDir = "/mnt/Brick-%s" % (number + 1)
+ bricks.append({'brickpath': brickDir})
+ return bricks
+
+ def __createDummyCluster(self):
+ cluster = {'name': 'Test-Cluster', 'hosts': [], 'volumes': []}
+ cluster['hosts'].append({'hostip': '10.70.43.1',
+ 'bricks': self.createBricks(1)})
+ cluster['hosts'].append({'hostip': '10.70.43.2',
+ 'bricks': self.createBricks(2)})
+ cluster['volumes'].append({'name': 'Volume1', "typeStr": "T"})
+ return cluster
+
+ def __getGlusterNagiosConfManager(self):
+ return config_generator.GlusterNagiosConfManager(
+ "/tmp/nagios/gluster", "../config", "gluster-host.cfg.template")