summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config/Makefile.am1
-rw-r--r--config/gluster-commands.cfg2
-rw-r--r--config/gluster-host.cfg.template34
-rw-r--r--nagios-server-addons.spec.in4
-rw-r--r--plugins/Makefile.am1
-rw-r--r--plugins/config_generator.py176
-rwxr-xr-xplugins/discovery.py291
-rwxr-xr-xplugins/server_utils.py78
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/test_config_generator.py82
10 files changed, 531 insertions, 140 deletions
diff --git a/config/Makefile.am b/config/Makefile.am
index 60da34c..3176aa7 100644
--- a/config/Makefile.am
+++ b/config/Makefile.am
@@ -12,7 +12,6 @@ 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 57b76ce..8c5594f 100644
--- a/config/gluster-commands.cfg
+++ b/config/gluster-commands.cfg
@@ -73,7 +73,7 @@ define command{
define command{
command_name gluster_auto_discovery
- command_line $USER1$/gluster/discovery.py -H $ARG1$ -c $HOSTNAME$
+ command_line sudo $USER1$/gluster/discovery.py -H $ARG1$ -c $HOSTNAME$ -m auto
}
define command{
diff --git a/config/gluster-host.cfg.template b/config/gluster-host.cfg.template
deleted file mode 100644
index 0b5173d..0000000
--- a/config/gluster-host.cfg.template
+++ /dev/null
@@ -1,34 +0,0 @@
-###@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'] is defined -%}
-check_command {{host['check_command']}}
-{% endif -%}
-{% if host['hostgroups'] is defined -%}
-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/nagios-server-addons.spec.in b/nagios-server-addons.spec.in
index 68c6e8d..997c07f 100644
--- a/nagios-server-addons.spec.in
+++ b/nagios-server-addons.spec.in
@@ -50,10 +50,10 @@ BuildRequires: python-netaddr
BuildRequires: libselinux-python
BuildRequires: python-nose
BuildRequires: python-devel
-BuildRequires: python-jinja2
BuildRequires: python-argparse
BuildRequires: python-ethtool
BuildRequires: gluster-nagios-common
+BuildRequires: pynag
%if ( 0%{?_with_systemd:1} )
BuildRequires: systemd-units
Requires(post): systemd-units
@@ -82,7 +82,7 @@ Requires: python-pthreading
Requires: python-inotify
Requires: libselinux-python
Requires: rrdtool-perl
-Requires: python-jinja2
+Requires: pynag
Requires: net-snmp-utils
%description
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index a6cc905..5400d5c 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -12,6 +12,7 @@ dist_glusternagiosplugins_PYTHON = \
config_generator.py \
servicesnmptrapgenerator.py \
submit_external_command.py \
+ server_utils.py \
$(NULL)
EXTRA_DIST = \
diff --git a/plugins/config_generator.py b/plugins/config_generator.py
index d383b0b..f76279f 100644
--- a/plugins/config_generator.py
+++ b/plugins/config_generator.py
@@ -18,29 +18,28 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#
-from jinja2 import Environment, FileSystemLoader
-import os
-import shutil
+from pynag import Model
+import server_utils
-class GlusterNagiosConfManager:
+"""
+Change mode helps to identify the change in the defintion.
+"ADD" means the entity and all its sub entities are added.
+"REMOVE" means the entity and all its sub entities are removed
+"UPDATE" means the entity is changes. It may also means sub entities
+are added or removed to the entity.
+"""
+CHANGE_MODE_ADD = "ADD"
+CHANGE_MODE_REMOVE = "REMOVE"
+CHANGE_MODE_UPDATE = "UPDATE"
- 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)
+class GlusterNagiosConfManager:
- def __createHostConfig(self, host):
- hostConfigStr = self.hostTemplate.render(host=host)
- hostConfig = {'name': host['host_name'], 'config': hostConfigStr}
- return hostConfig
+ def __init__(self, configDir):
+ self.configDir = configDir
+ #Create nagios host configuration with the given attributes
def createHost(self, hostName, alias, template,
address, hostGroups, checkCommand, services):
host = {}
@@ -52,7 +51,8 @@ class GlusterNagiosConfManager:
host['check_command'] = checkCommand
if hostGroups:
host['hostgroups'] = hostGroups
-
+ #Host service is not a field in host configuration. It helps to
+ #aggregate all the host services under the host
if services:
host['host_services'] = services
return host
@@ -119,12 +119,13 @@ class GlusterNagiosConfManager:
def createClusterAutoConfigService(self, clusterName, hostIp):
service = {}
service['host_name'] = clusterName
- service['use'] = 'generic-service'
+ service['use'] = 'gluster-service'
+ service['check_interval'] = '1440'
service['service_description'] = 'Cluster Auto Config'
service['check_command'] = "gluster_auto_discovery!%s" % (hostIp)
- service['check_interval'] = '1440'
return service
+ #Create all volume related services for the given volume
def createrVolumeServices(self, volumes, clusterName):
volumeServices = []
for volume in volumes:
@@ -168,6 +169,7 @@ class GlusterNagiosConfManager:
brickService['notes'] = "Volume : %s" % (brick['volumeName'])
return brickService
+ #Create all Brick related service here.
def createBrickServices(self, host):
brickServices = []
for brick in host['bricks']:
@@ -179,7 +181,20 @@ class GlusterNagiosConfManager:
brickServices.append(brickService)
return brickServices
- def generateNagiosConfigFromGlusterCluster(self, cluster):
+ #Create a host group with the name
+ def createHostGroup(self, name):
+ return {'hostgroup_name': name, 'alias': name}
+
+ #Create the Nagios configuration model in run time using list and
+ #dictionary
+ #Nagios config model hierarchy
+ #########################################################################
+ #Hostgroup
+ # --'_host' ---> List of host configurations in the host group
+ # --'host_services' ----> List of services in the host
+ #########################################################################
+ def generateNagiosConfig(self, cluster):
+ hostGroup = self.createHostGroup(cluster['name'])
hostsConfigs = []
clusterServices = self.createrVolumeServices(
cluster.get('volumes'), cluster['name'])
@@ -189,42 +204,107 @@ class GlusterNagiosConfManager:
cluster['name']))
clusterServices.append(self.createClusterAutoConfigService(
cluster['name'], cluster['hosts'][0]['hostip']))
+ #Create host config for Gluster cluster with volume related services
clusterHostConfig = self.createHost(
cluster['name'], cluster['name'], "gluster-cluster",
cluster['name'], None, None, clusterServices)
hostsConfigs.append(clusterHostConfig)
+ #Create host config for all hosts in the cluster with brick related
+ #services
for host in cluster['hosts']:
brickServices = self.createBrickServices(host)
hostGroups = "gluster_hosts,%s" % (cluster['name'])
hostConfig = self.createHost(
host['hostname'], host['hostname'], "gluster-host",
- host['hostip'], hostGroups, "", brickServices)
+ host['hostip'], hostGroups, None, brickServices)
hostsConfigs.append(hostConfig)
- self.generateConfigFiles(hostsConfigs)
- return hostsConfigs
- def generateConfigFiles(self, hosts):
- clusterConfig = {'name': None, 'hostConfigs': []}
- clusterConfigDir = None
+ hostGroup["_hosts"] = hostsConfigs
+ return hostGroup
+
+ #Get the config file name for the given hostname
+ def getCfgFileName(self, hostname):
+ return self.configDir + "/" + hostname + ".cfg"
+
+ #Create Nagios config service for the given host group with all hosts.
+ #Host group should contain the delta to be written to the configuration.
+ #Delta will be processed using the change mode.
+ def writeHostGroup(self, hostgroup):
+ changeMode = hostgroup['changeMode']
+ if changeMode == CHANGE_MODE_ADD:
+ hostgroupModel = Model.Hostgroup()
+ hostgroupModel['hostgroup_name'] = hostgroup['hostgroup_name']
+ hostgroupModel['alias'] = hostgroup['alias']
+ hostgroupModel.set_filename(
+ self.getCfgFileName(hostgroup['hostgroup_name']))
+ hostgroupModel.save()
+ #Process all the hosts in the hostgroup. ChangeMode of the hostgroup
+ #will be used to proces the host if there is not changeMode specified
+ #in the host.
+ if hostgroup['_hosts']:
+ self.writeHosts(hostgroup['_hosts'], changeMode)
+
+ #Fill the pynag model with the given values.
+ #'changeMode' and 'host_services' are special fields which are
+ #not meant to be writen to the nagios config, These fields are
+ #used to represent the config model and changes.
+ def fillModel(self, model, values):
+ for key, value in values.iteritems():
+ if key not in ['changeMode', 'host_services']:
+ model[key] = value
+ return model
+
+ #Write service to nagios config
+ def writeService(self, service, hostname):
+ if service['changeMode'] == CHANGE_MODE_ADD:
+ serviceModel = Model.Service()
+ serviceModel = self.fillModel(serviceModel, service)
+ serviceModel.set_filename(self.getCfgFileName(hostname))
+ serviceModel.save()
+ elif service['changeMode'] == CHANGE_MODE_REMOVE:
+ serviceModel = Model.Service.objects.filter(
+ host_name=hostname,
+ service_description=service['service_description'])
+ if serviceModel:
+ serviceModel[0].delete()
+ elif service['changeMode'] == CHANGE_MODE_UPDATE:
+ serviceModel = server_utils.getServiceConfig(
+ service['service_description'], service['host_name'])
+ self.fillModel(serviceModel, service)
+ serviceModel.save()
+
+ #Write all services in the host.
+ #host_services filed contains the list of services to be written to
+ #nagios configuration
+ def writeHostServices(self, host):
+ for service in host['host_services']:
+ if service.get('changeMode') is None:
+ service['changeMode'] = host['changeMode']
+ self.writeService(service, host['host_name'])
+
+ #Write the host configuration with list of services to nagios configuration
+ def writeHost(self, host):
+ if host['changeMode'] == CHANGE_MODE_REMOVE:
+ hostModel = Model.Host.objects.filter(
+ host_name=host['host_name'])
+ if hostModel:
+ hostModel[0].delete(recursive=True)
+ return
+ if host['changeMode'] == CHANGE_MODE_ADD:
+ hostModel = Model.Host()
+ hostModel = self.fillModel(hostModel, host)
+ hostModel.set_filename(self.getCfgFileName(host['host_name']))
+ hostModel.save()
+
+ if host['host_services']:
+ self.writeHostServices(host)
+
+ def writeHosts(self, hosts, chageMode):
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 not clusterConfigDir:
- raise Exception("Cluster configuration directory can't None")
- configFilePath = clusterConfigDir + "/" + hostConfig['name'] + ".cfg"
- with open(configFilePath, 'w') as configFile:
- configFile.write(hostConfig['config'])
+ if host.get('changeMode') is None:
+ host['changeMode'] = chageMode
+ self.writeHost(host)
+
+ #Write the hostgroup delta to nagios configuration.
+ def generateConfigFiles(self, delta):
+ self.writeHostGroup(delta)
diff --git a/plugins/discovery.py b/plugins/discovery.py
index b2cc21a..3ee102b 100755
--- a/plugins/discovery.py
+++ b/plugins/discovery.py
@@ -18,24 +18,27 @@
#
import argparse
import json
-import datetime
+
+import os
+import shutil
import sys
from glusternagios import utils
from config_generator import GlusterNagiosConfManager
+import server_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
-import submit_external_command
-serviceCmdPath = utils.CommandPath("service", "/sbin/service", )
+from config_generator import CHANGE_MODE_ADD
+from config_generator import CHANGE_MODE_REMOVE
+from config_generator import CHANGE_MODE_UPDATE
+
+
nrpeCmdPath = utils.CommandPath("nrpe", NRPE_PATH, )
def excecNRPECommand(host, command):
- output = {}
(returncode, outputStr, err) = utils.execCmd([nrpeCmdPath.cmd,
"-H", host, "-c", command])
#convert to dictionary
@@ -48,13 +51,36 @@ def excecNRPECommand(host, command):
def discoverCluster(hostip, cluster):
+ """
+ This method helps to discover the nodes, volumes and bricks in the given
+ gluster. It uses NRPE commands to contact the gluster nodes.
+
+ Assumptions:
+ First node returned by the "discoverpeers" NRPE command should be the same
+ node where "discoverpeers" is executed.
+
+ Parameters
+ ----------
+ hostip: Address of a node in the gluster cluster.
+ cluster: Cluster Name
+
+ Returns
+ ---------
+ Returns cluster details in the following dictinary format
+ {
+ 'name': cluster-name,
+ 'volumes': [list-volumes],
+ 'host': [list-hosts]
+ }
+ each host in the list will a have list of bricks from the host.
+ """
+
clusterdata = {}
#Discover the logical components
componentlist = excecNRPECommand(hostip, "discoverlogicalcomponents")
#Discover the peers
hostlist = excecNRPECommand(hostip, "discoverpeers")
- #Add the ip address of the root node to the peer list
- #to generate the configuration
+ #Add the ip address of the root node given by the user to the peer list
hostlist[0]['hostip'] = hostip
for host in hostlist:
#Get host names
@@ -70,16 +96,22 @@ def discoverCluster(hostip, cluster):
clusterdata['hosts'] = hostlist
clusterdata['volumes'] = componentlist['volumes']
clusterdata['name'] = cluster
+ #Host names returned by "discoverhostparams" supposed to be unique. So host
+ #name can be used to configure the host_name in nagios host.
+ #But if host names are not unique then we have to use IP address to
+ #configure host_name in nagios.
if not isHostsNamesUnique(clusterdata):
setHostNameWithIP(clusterdata)
return clusterdata
+# Set host address as the hostname
def setHostNameWithIP(clusterdata):
for host in clusterdata['hosts']:
host['hostname'] = host['hostip']
+# Check host names are unique
def isHostsNamesUnique(clusterdata):
hostnames = {}
for host in clusterdata['hosts']:
@@ -90,51 +122,244 @@ def isHostsNamesUnique(clusterdata):
return True
+def getConfigManager(args):
+ configDir = DEFAULT_AUTO_CONFIG_DIR
+ if args.configDir is not None:
+ configDir = args.configDir
+ clusterConfigDir = configDir + "/" + args.cluster
+ configManager = GlusterNagiosConfManager(clusterConfigDir)
+ return configManager
+
+
+#Find the given service in service list.
+def findServiceInList(serviceList, serviceDescription):
+ for service in serviceList:
+ if service['service_description'] == serviceDescription:
+ return service
+ return None
+
+
+#Find the given host in host list
+def findHostInList(hostList, hostName):
+ for host in hostList:
+ if host['host_name'] == hostName:
+ return host
+ return None
+
+
+#Find all deleted services in the host.
+def findDeletedServices(host):
+ deletedService = []
+ serviceConfigs = server_utils.getServiceConfigByHost(host['host_name'])
+ for serviceConfig in serviceConfigs:
+ service = findServiceInList(host['host_services'],
+ serviceConfig['service_description'])
+ if service is None:
+ deletedService.append(
+ {'service_description': serviceConfig['service_description'],
+ 'changeMode': CHANGE_MODE_REMOVE})
+ return deletedService
+
+
+#Check if auto config is changed. IP address in the check command will change
+#when user runs the auto config using different host.
+def findChangeInAutoConfig(newService, oldService):
+ if newService['check_command'] != oldService['check_command']:
+ changes = {}
+ changes['check_command'] = newService['check_command']
+ changes['service_description'] = newService['service_description']
+ changes['host_name'] = newService['host_name']
+ changes['changeMode'] = CHANGE_MODE_UPDATE
+ return changes
+ return None
+
+
+#Find all Added/Deleted services in the given host.
+#Note: 'Cluster Auto Config' is a special service. When user runs the
+#auto-config using different host instead what is used previously then we
+#have to update the host ip in existing auto-config service.
+def findServiceDelta(host):
+ serviceDelta = []
+ for service in host['host_services']:
+ serviceConfig = server_utils.getServiceConfig(
+ service['service_description'], service['host_name'])
+ if serviceConfig is None:
+ service['changeMode'] = CHANGE_MODE_ADD
+ serviceDelta.append(service)
+ elif serviceConfig['service_description'] == "Cluster Auto Config":
+ changes = findChangeInAutoConfig(service, serviceConfig)
+ if changes:
+ serviceDelta.append(changes)
+ serviceDelta.extend(findDeletedServices(host))
+ return serviceDelta
+
+
+#Find newly added hosts and newly added services to the existing hosts
+def findAddUpdateHosts(hosts):
+ delta = []
+ for host in hosts:
+ hostConfing = server_utils.getHostConfigByName(host['host_name'])
+ if hostConfing is None:
+ host['changeMode'] = CHANGE_MODE_ADD
+ delta.append(host)
+ else:
+ serviceDelta = findServiceDelta(host)
+ if serviceDelta:
+ host['changeMode'] = CHANGE_MODE_UPDATE
+ host['host_services'] = serviceDelta
+ delta.append(host)
+ return delta
+
+
+#Find deleted hosts in the given cluster.
+def findDeletedHosts(hostgroup, hosts):
+ deletedHosts = []
+ hostConfigs = server_utils.getHostConfigsForCluster(hostgroup)
+ for hostConfig in hostConfigs:
+ host = findHostInList(hosts, hostConfig['host_name'])
+ if host is None:
+ deletedHosts.append({'host_name': hostConfig['host_name'],
+ 'changeMode': CHANGE_MODE_REMOVE})
+ return deletedHosts
+
+
+#Find Added/Deleted/Updated hosts in cluster
+def findHostDelta(clusterConfig):
+ hostDelta = []
+ updated = findAddUpdateHosts(clusterConfig['_hosts'])
+ hostDelta.extend(updated)
+ hostDelta.extend(findDeletedHosts(clusterConfig['hostgroup_name'],
+ clusterConfig['_hosts']))
+ return hostDelta
+
+
+#Find changes to the cluster
+def findDelta(clusterConfig):
+ delta = {}
+ delta['hostgroup_name'] = clusterConfig['hostgroup_name']
+ delta['alias'] = clusterConfig['alias']
+
+ hostgroup = server_utils.getHostGroup(clusterConfig['hostgroup_name'])
+ if hostgroup is None:
+ delta['changeMode'] = CHANGE_MODE_ADD
+ delta['_hosts'] = clusterConfig['_hosts']
+ return delta
+
+ hostDelta = findHostDelta(clusterConfig)
+ delta['_hosts'] = hostDelta
+ if hostDelta:
+ delta['changeMode'] = CHANGE_MODE_UPDATE
+ return delta
+
+
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('-m', '--mode', action='store', dest='mode',
+ choices=['auto', 'manual'], required=False,
+ default='manual', help='Mode')
parser.add_argument('-d', '--configdir', action='store', dest='configDir',
type=str, required=False,
- help='Configuration directory '
- 'where output files will be written')
+ default=DEFAULT_AUTO_CONFIG_DIR,
+ help='Configuration directory where'
+ ' output files will be written')
+ parser.add_argument('-f', '--force', action='store_true', dest='force',
+ help="Force sync the Cluster configuration")
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
+#Clean the config directory
+def cleanConfigDir(dir):
+ if os.path.exists(dir):
+ # Deleting the config dir to write new configs
+ shutil.rmtree(dir)
+ os.mkdir(dir)
-def _restartNagios():
- now = datetime.datetime.now()
- cmdStr = "[%s] RESTART_PROGRAM\n" % (now)
- submit_external_command.submitExternalCommand(cmdStr)
+#Preview the changes to cluster config
+def previewChanges(clusterDelta):
+ print "Changes :"
+ clusterChangeMode = clusterDelta['changeMode']
+ print "Hostgroup %s - %s" % (clusterDelta['hostgroup_name'],
+ clusterChangeMode)
+ for host in clusterDelta['_hosts']:
+ if host.get('changeMode'):
+ changeMode = host.get('changeMode')
+ else:
+ changeMode = clusterChangeMode
+ print "Host %s - %s" % (host['host_name'], changeMode)
+ for service in host.get('host_services'):
+ if service.get('changeMode'):
+ changeMode = service.get('changeMode')
+ print "\t Service - %s -%s " % (service['service_description'],
+ changeMode)
-def _isNagiosRunning():
- (rc, out, err) = utils.execCmd([serviceCmdPath.cmd, 'nagios', 'status'])
- if rc == 0:
- return True
- else:
- return False
+#Write the cluster configurations. If force mode is used then it will clean
+#the config directory before writing the changes.
+def writeDelta(clusterDelta, configManager, force):
+ if force:
+ cleanConfigDir(configManager.configDir)
+ configManager.generateConfigFiles(clusterDelta)
+
+def getConfirmation(message, default):
+ while True:
+ ans = raw_input("%s (Yes, No) [%s]: " % (message, default))
+ if not ans:
+ ans = default
+ ans = ans.upper()
+ if ans not in ['YES', 'NO']:
+ print 'please enter Yes or No'
+ if ans == 'YES':
+ return True
+ if ans == 'NO':
+ return False
if __name__ == '__main__':
args = parse_input()
clusterdata = discoverCluster(args.hostip, args.cluster)
configManager = getConfigManager(args)
- clusterConfing = configManager.generateNagiosConfigFromGlusterCluster(
- clusterdata)
- print " Cluster configurations re-synced successfully from host %s" % \
- (args.hostip)
- if _isNagiosRunning():
- _restartNagios()
+ clusterDelta = configManager.generateNagiosConfig(clusterdata)
+ if args.force:
+ clusterDelta['changeMode'] = CHANGE_MODE_ADD
+ else:
+ clusterDelta = findDelta(clusterDelta)
+
+ if clusterDelta.get('changeMode') is None:
+ print "Cluster configurations are in sync"
+ sys.exit(utils.PluginStatusCode.OK)
+ #When auto config is run in manual mode, we will ask confirmation
+ #before writing the config file and before restarting the Nagios
+ if args.mode == "manual":
+ print "Cluster configurations changed"
+ previewChanges(clusterDelta)
+ confirmation = getConfirmation(
+ "Are you sure, you want to commit the changes?", "Yes")
+ if confirmation:
+ writeDelta(clusterDelta, configManager, args.force)
+ print "Cluster configurations synced successfully from host %s" % \
+ (args.hostip)
+ #If Nagios is running then try to restart. Otherwise don't do
+ #anything.
+ if server_utils.isNagiosRunning():
+ confirmation = getConfirmation(
+ "Do you want to restart Nagios to start monitoring newly "
+ "discovered entities?", "Yes")
+ if confirmation:
+ server_utils.restartNagios()
+ print "Nagios re-started successfully"
+ else:
+ print "Start the Nagios service to monitor"
+ #auto mode means write the configurations without asking confirmation
+ elif args.mode == "auto":
+ writeDelta(clusterDelta, configManager, args.force)
+ print "Cluster configurations synced successfully from host %s" % \
+ (args.hostip)
+ if server_utils.isNagiosRunning():
+ server_utils.restartNagios()
sys.exit(utils.PluginStatusCode.OK)
diff --git a/plugins/server_utils.py b/plugins/server_utils.py
new file mode 100755
index 0000000..b3767bd
--- /dev/null
+++ b/plugins/server_utils.py
@@ -0,0 +1,78 @@
+#!/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 datetime
+from pynag import Model
+
+from glusternagios import utils
+import submit_external_command
+
+
+serviceCmdPath = utils.CommandPath("service", "/sbin/service", )
+
+
+def restartNagios():
+ now = datetime.datetime.now()
+ cmdStr = "[%s] RESTART_PROGRAM\n" % (now)
+ submit_external_command.submitExternalCommand(cmdStr)
+
+
+def isNagiosRunning():
+ (rc, out, err) = utils.execCmd([serviceCmdPath.cmd, 'nagios', 'status'])
+ if rc == 0:
+ return True
+ else:
+ return False
+
+
+def getServiceConfig(serviceDesc, hostName):
+ serviceConfig = Model.Service.objects.filter(
+ service_description=serviceDesc, host_name=hostName)
+ if serviceConfig:
+ return serviceConfig[0]
+ else:
+ return None
+
+
+def getServiceConfigByHost(hostName):
+ serviceConfigs = Model.Service.objects.filter(host_name=hostName)
+ return serviceConfigs
+
+
+def getHostConfigByName(hostName):
+ hostConfigs = Model.Host.objects.filter(host_name=hostName)
+ if hostConfigs:
+ return hostConfigs[0]
+ else:
+ return None
+
+
+def getHostConfigsForCluster(clusterName):
+ hostgroup = getHostGroup(clusterName)
+ if hostgroup:
+ return hostgroup.get_effective_hosts()
+ else:
+ return []
+
+
+def getHostGroup(name):
+ hostgroup = Model.Hostgroup.objects.filter(hostgroup_name=name)
+ if hostgroup:
+ return hostgroup[0]
+ else:
+ return None
diff --git a/tests/Makefile.am b/tests/Makefile.am
index be571d6..7527b23 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -23,7 +23,7 @@ test_modules = \
test_check_remote_host.py \
test_notify_ovirt_engine_handler.py \
test_config_generator.py \
- test_discovery.py
+ test_discovery.py \
$(NULL)
dist_nagiosserveraddonstests_DATA = \
diff --git a/tests/test_config_generator.py b/tests/test_config_generator.py
index 6fe62d3..8c79057 100644
--- a/tests/test_config_generator.py
+++ b/tests/test_config_generator.py
@@ -16,42 +16,85 @@
# 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(
+ def testGenerateNagiosConfig(self):
+ confManager = self._getGlusterNagiosConfManager()
+ clusterData = self._createDummyCluster()
+ clusterConfig = confManager.generateNagiosConfig(
clusterData)
- mock_generateConfigFiles.assert_called()
- self.__verifyConfig(clusterConfig, clusterData)
+ self._verifyConfig(clusterConfig, clusterData)
- def __verifyConfig(self, clusterConfig, clusterData):
+ def _verifyConfig(self, clusterConfig, clusterData):
self.assertTrue(len(clusterConfig), len(clusterData['hosts']) + 1)
- self.__verifyClusterConfig(clusterConfig[0], clusterData)
+ self._verifyClusterConfig(clusterConfig["_hosts"][0], clusterData)
for index in range(0, len(clusterData['hosts'])):
- self.__verifyHostConfig(clusterConfig[index + 1],
- clusterData['hosts'][index])
+ self._verifyHostConfig(clusterConfig['_hosts'][index + 1],
+ clusterData['hosts'][index])
- def __verifyHostConfig(self, hostConfig, hostData):
+ def _verifyHostConfig(self, hostConfig, hostData):
self.assertEqual(hostConfig['host_name'], hostData['hostname'])
self.assertEqual(hostConfig['alias'], hostData['hostname'])
self.assertEqual(hostConfig['address'], hostData['hostip'])
self.assertEqual(hostConfig['use'], 'gluster-host')
+ self._verifyHostServices(hostConfig, hostData)
- def __verifyClusterConfig(self, config, clusterData):
+ def _verifyHostServices(self, hostConfig, hostData):
+ for brick in hostData['bricks']:
+ serviceDesc = "Brick Status - %s:%s" % (hostData['hostname'],
+ brick['brickpath'])
+ service = self._findServiceInList(hostConfig['host_services'],
+ serviceDesc)
+ self.assertNotEqual(service, None,
+ "Brick status service is not created")
+ serviceDesc = "Brick Utilization - %s:%s" % (hostData['hostname'],
+ brick['brickpath'])
+ service = self._findServiceInList(hostConfig['host_services'],
+ serviceDesc)
+ self.assertNotEqual(service, None,
+ "Brick Utilization service is not created")
+
+ 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.get('check_command'), None)
self.assertEqual(config['use'], 'gluster-cluster')
+ self._verifyClusterServices(config, clusterData)
+
+ def _verifyClusterServices(self, clusterConfig, clusterData):
+ self.assertEqual(len(clusterConfig['host_services']), 6)
+ for volume in clusterData['volumes']:
+ self._verifyVolumeServices(clusterConfig['host_services'], volume)
+
+ def _verifyVolumeServices(self, serviceList, volume):
+ serviceDesc = 'Volume Utilization - %s' % (volume['name'])
+ service = self._findServiceInList(serviceList, serviceDesc)
+ self.assertNotEqual(service, None,
+ "Volume utilization service is not created")
+ serviceDesc = 'Volume Status - %s' % (volume['name'])
+ service = self._findServiceInList(serviceList, serviceDesc)
+ self.assertNotEqual(service, None,
+ "Volume Status service is not created")
+ serviceDesc = 'Volume Status Quota - %s' % (volume['name'])
+ service = self._findServiceInList(serviceList, serviceDesc)
+ self.assertNotEqual(service, None,
+ "Volume Status Quota service is not created")
+ if 'Replicate' in volume['type']:
+ serviceDesc = 'Volume Self-Heal - %s' % (volume['name'])
+ service = self._findServiceInList(serviceList, serviceDesc)
+ self.assertNotEqual(service, None,
+ "Volume Self-Heal service is not created")
+
+ def _findServiceInList(self, serviceList, serviceDescription):
+ for service in serviceList:
+ if service['service_description'] == serviceDescription:
+ return service
+ return None
def createBricks(self, count, volume, hostip):
bricks = []
@@ -62,7 +105,7 @@ class TestGlusterNagiosConfManager(TestCaseBase):
'hostip': hostip})
return bricks
- def __createDummyCluster(self):
+ def _createDummyCluster(self):
cluster = {'name': 'Test-Cluster', 'hosts': [], 'volumes': []}
cluster['hosts'].append({'hostip': '10.70.43.1',
'hostname': 'host-1',
@@ -72,9 +115,8 @@ class TestGlusterNagiosConfManager(TestCaseBase):
'hostname': 'host-2',
'bricks': self.createBricks(2, "Volume1",
'10.70.43.2')})
- cluster['volumes'].append({'name': 'Volume1', "type": "T"})
+ cluster['volumes'].append({'name': 'Volume1', "type": "Replicate"})
return cluster
- def __getGlusterNagiosConfManager(self):
- return config_generator.GlusterNagiosConfManager(
- "/tmp/nagios/gluster", "../config", "gluster-host.cfg.template")
+ def _getGlusterNagiosConfManager(self):
+ return config_generator.GlusterNagiosConfManager("/tmp/nagios/gluster")