#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015-2015: Alignak team, see AUTHORS.txt file for contributors
#
# This file is part of Alignak.
#
# Alignak is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Alignak 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Alignak. If not, see <http://www.gnu.org/licenses/>.
#
#
# This file incorporates work covered by the following copyright and
# permission notice:
#
# Copyright (C) 2009-2014:
# xkilian, fmikus@acktomic.com
# Pradeep Jindal, praddyjindal@gmail.com
# aviau, alexandre.viau@savoirfairelinux.com
# Hartmut Goebel, h.goebel@goebel-consult.de
# Nicolas Dupeux, nicolas@dupeux.net
# Andrus Viik, andrus@a7k.pri.ee
# Sebastien Coavoux, s.coavoux@free.fr
# Thibault Cohen, titilambert@gmail.com
# Jean Gabes, naparuba@gmail.com
# This file is part of Shinken.
#
# Shinken is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Shinken 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Shinken. If not, see <http://www.gnu.org/licenses/>.
"""This module provide a set of function for triggers
Basically used to handle perfdata, exit status and output
"""
import time
import re
from alignak.misc.perfdata import PerfDatas
from alignak.log import logger
OBJS = {'hosts': [], 'services': []}
TRIGGER_FUNCTIONS = {}
[docs]def declared(function):
""" Decorator to add function in trigger environment
:param function: function to add to trigger environment
:type function: types.FunctionType
:return : the function itself only update TRIGGER_FUNCTIONS variable
"""
name = function.func_name
TRIGGER_FUNCTIONS[name] = function
logger.debug("Added %s to trigger functions list ", name)
return function
@declared
[docs]def up(obj, output): # pylint: disable=C0103
""" Set a host in UP state
:param obj: object
:type obj: object
:param output:
:type output:
:return: None
"""
set_value(obj, output, None, 0)
@declared
[docs]def down(obj, output):
""" Set a host in DOWN state
:param obj: object
:type obj: object
:param output:
:type output:
:return: None
"""
set_value(obj, output, None, 1)
@declared
[docs]def ok(obj, output): # pylint: disable=C0103
""" Set a service in OK state
:param obj: object
:type obj: object
:param output:
:type output:
:return: None
"""
set_value(obj, output, None, 0)
@declared
[docs]def warning(obj, output):
""" Set a service in WARNING state
:param obj: object
:type obj: object
:param output:
:type output:
:return: None
"""
set_value(obj, output, None, 1)
@declared
[docs]def critical(obj, output):
""" Set a service in CRITICAL state
:param obj: object
:type obj: object
:param output:
:type output:
:return: None
"""
set_value(obj, output, None, 2)
@declared
[docs]def unknown(obj, output):
""" Set a service in UNKNOWN state
:param obj: object
:type obj: object
:param output:
:type output:
:return: None
"""
set_value(obj, output, None, 3)
@declared
[docs]def set_value(obj_ref, output=None, perfdata=None, return_code=None):
""" Set output, state and perfdata to a service or host
:param obj_ref:
:type obj_ref: object
:param output:
:type output: None | str
:param perfdata:
:type perfdata: None | str
:param return_code:
:type return_code: None | int
:return: None
"""
obj = get_object(obj_ref)
if not obj:
return
output = output or obj.output
perfdata = perfdata or obj.perf_data
if return_code is None:
return_code = obj.state_id
logger.debug("[trigger] Setting %s %s %s for object %s",
output,
perfdata,
return_code,
obj.get_full_name())
if perfdata:
output = output + ' | ' + perfdata
now = time.time()
chk = obj.launch_check(now, force=True)
if chk is None:
logger.debug("[trigger] %s > none check launched", obj.get_full_name())
else:
logger.debug("[trigger] %s > I found the check I want to change",
obj.get_full_name())
# Now we 'transform the check into a result'
# So exit_status, output and status is eaten by the host
chk.exit_status = return_code
chk.get_outputs(output, obj.max_plugins_output_length)
chk.status = 'waitconsume'
chk.check_time = now
# IMPORTANT: tag this check as from a trigger, so we will not
# loop in an infinite way for triggers checks!
chk.from_trigger = True
# Ok now this result will be read by scheduler the next loop
@declared
[docs]def perf(obj_ref, metric_name):
""" Get perf data from a service
:param obj_ref:
:type obj_ref: object
:param metric_name:
:type metric_name: str
:return: None
"""
obj = get_object(obj_ref)
perfdata = PerfDatas(obj.perf_data)
if metric_name in perfdata:
logger.debug("[trigger] I found the perfdata")
return perfdata[metric_name].value
logger.debug("[trigger] I am in perf command")
return None
@declared
[docs]def get_custom(obj_ref, cname, default=None):
""" Get custom variable from a service or a host
:param obj_ref:
:type obj_ref: object
:param cname:
:type cname: str
:param default:
:type default:
:return:
:rtype:
"""
objs = get_objects(obj_ref)
if len(objs) != 1:
return default
obj = objs[0]
if not obj:
return default
cname = cname.upper().strip()
if not cname.startswith('_'):
cname = '_' + cname
return obj.customs.get(cname, default)
@declared
[docs]def perfs(objs_ref, metric_name):
""" TODO: check this description
Get perfdatas from multiple services/hosts
:param objs_ref:
:type objs_ref: object
:param metric_name:
:type metric_name: str
:return: list of metrics
:rtype: list
"""
objs = get_objects(objs_ref)
res = []
for obj in objs:
val = perf(obj, metric_name)
res.append(val)
return res
@declared
[docs]def allperfs(obj_ref):
""" Get all perfdatas from a service or a host
:param obj_ref:
:type obj_ref: object
:return: dictionary with perfdatas
:rtype: dict
"""
obj = get_object(obj_ref)
perfdata = PerfDatas(obj.perf_data)
logger.debug("[trigger] I get all perfdatas")
return dict([(metric.name, perfdata[metric.name]) for metric in perfdata])
@declared
[docs]def get_object(ref):
""" Retrive object (service/host) from name
:param ref:
:type ref:
:return:
:rtype:
"""
# Maybe it's already a real object, if so, return it :)
if not isinstance(ref, basestring):
return ref
# Ok it's a string
name = ref
if '/' not in name:
return OBJS['hosts'].find_by_name(name)
else:
elts = name.split('/', 1)
return OBJS['services'].find_srv_by_name_and_hostname(elts[0], elts[1])
@declared
[docs]def get_objects(ref):
""" TODO: check this description
Retrive objects (service/host) from names
:param ref:
:type ref:
:return: list of object (service/host)
:rtype: list
"""
# Maybe it's already a real object, if so, return it :)
if not isinstance(ref, basestring):
return [ref]
name = ref
# Maybe there is no '*'? if so, it's one element
if '*' not in name:
return [get_object(name)]
# Ok we look for spliting the host or service thing
hname = ''
sdesc = ''
if '/' not in name:
hname = name
else:
elts = name.split('/', 1)
hname = elts[0]
sdesc = elts[1]
logger.debug("[trigger get_objects] Look for %s %s", hname, sdesc)
res = []
hosts = []
services = []
# Look for host, and if need, look for service
if '*' not in hname:
host = OBJS['hosts'].find_by_name(hname)
if host:
hosts.append(host)
else:
hname = hname.replace('*', '.*')
regex = re.compile(hname)
for host in OBJS['hosts']:
logger.debug("[trigger] Compare %s with %s", hname, host.get_name())
if regex.search(host.get_name()):
hosts.append(host)
# Maybe the user ask for justs hosts :)
if not sdesc:
return hosts
for host in hosts:
if '*' not in sdesc:
serv = host.find_service_by_name(sdesc)
if serv:
services.append(serv)
else:
sdesc = sdesc.replace('*', '.*')
regex = re.compile(sdesc)
for serv in host.services:
logger.debug("[trigger] Compare %s with %s", serv.service_description, sdesc)
if regex.search(serv.service_description):
services.append(serv)
logger.debug("Found the following services: %s", services)
return services