From 0bb9c00a64b981d4b793f618c5dbda8337b99ce0 Mon Sep 17 00:00:00 2001 From: Ramesh Nachimuthu Date: Sun, 27 Apr 2014 19:03:45 +0530 Subject: auto config : Enhance the auto config to preserve user changes Enhace the auto configuration in nagios to preserve the user changes. pynag apis are used to read/write/update the nagios config files. Change-Id: I6820928fb2b0baff5d6e65c811a48b5ff718f9e3 Signed-off-by: Ramesh Nachimuthu --- config/Makefile.am | 1 - config/gluster-commands.cfg | 2 +- config/gluster-host.cfg.template | 34 ----- nagios-server-addons.spec.in | 4 +- plugins/Makefile.am | 1 + plugins/config_generator.py | 176 ++++++++++++++++------- plugins/discovery.py | 291 ++++++++++++++++++++++++++++++++++----- plugins/server_utils.py | 78 +++++++++++ tests/Makefile.am | 2 +- tests/test_config_generator.py | 82 ++++++++--- 10 files changed, 531 insertions(+), 140 deletions(-) delete mode 100644 config/gluster-host.cfg.template create mode 100755 plugins/server_utils.py 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") -- cgit