#!/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:
# Hartmut Goebel, h.goebel@goebel-consult.de
# Guillaume Bour, guillaume@bour.cc
# Gerhard Lausser, gerhard.lausser@consol.de
# Grégory Starck, g.starck@gmail.com
# Frédéric Pégé, frederic.pege@gmail.com
# Sebastien Coavoux, s.coavoux@free.fr
# Thibault Cohen, titilambert@gmail.com
# Jean Gabes, naparuba@gmail.com
# Christophe Simon, geektophe@gmail.com
# Romain Forlot, rforlot@yahoo.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 provides NotificationWay and NotificationWays classes that
implements way of sending notifications. Basically used for parsing.
"""
from alignak.objects.item import Item, Items
from alignak.property import BoolProp, IntegerProp, StringProp, ListProp
from alignak.log import logger
[docs]class NotificationWay(Item):
"""NotificationWay class is used to implement way of sending notifications (command, periods..)
"""
_id = 1 # zero is always special in database, so we do not take risk here
my_type = 'notificationway'
properties = Item.properties.copy()
properties.update({
'notificationway_name':
StringProp(fill_brok=['full_status']),
'host_notifications_enabled':
BoolProp(default=True, fill_brok=['full_status']),
'service_notifications_enabled':
BoolProp(default=True, fill_brok=['full_status']),
'host_notification_period':
StringProp(fill_brok=['full_status']),
'service_notification_period':
StringProp(fill_brok=['full_status']),
'host_notification_options':
ListProp(default=[''], fill_brok=['full_status'], split_on_coma=True),
'service_notification_options':
ListProp(default=[''], fill_brok=['full_status'], split_on_coma=True),
'host_notification_commands':
ListProp(fill_brok=['full_status']),
'service_notification_commands':
ListProp(fill_brok=['full_status']),
'min_business_impact':
IntegerProp(default=0, fill_brok=['full_status']),
})
running_properties = Item.running_properties.copy()
# This tab is used to transform old parameters name into new ones
# so from Nagios2 format, to Nagios3 ones.
# Or Alignak deprecated names like criticity
old_properties = {
'min_criticity': 'min_business_impact',
}
macros = {}
special_properties = ('service_notification_commands', 'host_notification_commands',
'service_notification_period', 'host_notification_period')
[docs] def get_name(self):
"""Accessor to notificationway_name attribute
:return: notificationway name
:rtype: str
"""
return self.notificationway_name
[docs] def want_service_notification(self, timestamp, state, n_type, business_impact, cmd=None):
"""Check if notification options match the state of the service
Notification is NOT wanted in ONE of the following case::
* service notifications are disabled
* cmd is not in service_notification_commands
* business_impact < self.min_business_impact
* service_notification_period is not valid
* state does not match service_notification_options for problem, recovery and flapping
* state does not match host_notification_options for downtime
:param timestamp: time we want to notify the contact (usually now)
:type timestamp: int
:param state: host or service state ("WARNING", "CRITICAL" ..)
:type state: str
:param n_type: type of notification ("PROBLEM", "RECOVERY" ..)
:type n_type: str
:param business_impact: impact of this service
:type business_impact: int
:param cmd: command launched to notify the contact
:type cmd: str
:return: True if no condition is matched, otherwise False
:rtype: bool
TODO: Simplify function
"""
if not self.service_notifications_enabled:
return False
# Maybe the command we ask for are not for us, but for another notification ways
# on the same contact. If so, bail out
if cmd and cmd not in self.service_notification_commands:
return False
# If the business_impact is not high enough, we bail out
if business_impact < self.min_business_impact:
return False
valid = self.service_notification_period.is_time_valid(timestamp)
if 'n' in self.service_notification_options:
return False
timestamp = {'WARNING': 'w', 'UNKNOWN': 'u', 'CRITICAL': 'c',
'RECOVERY': 'r', 'FLAPPING': 'f', 'DOWNTIME': 's'}
if n_type == 'PROBLEM':
if state in timestamp:
return valid and timestamp[state] in self.service_notification_options
elif n_type == 'RECOVERY':
if n_type in timestamp:
return valid and timestamp[n_type] in self.service_notification_options
elif n_type == 'ACKNOWLEDGEMENT':
return valid
elif n_type in ('FLAPPINGSTART', 'FLAPPINGSTOP', 'FLAPPINGDISABLED'):
return valid and 'f' in self.service_notification_options
elif n_type in ('DOWNTIMESTART', 'DOWNTIMEEND', 'DOWNTIMECANCELLED'):
# No notification when a downtime was cancelled. Is that true??
# According to the documentation we need to look at _host_ options
return valid and 's' in self.host_notification_options
return False
[docs] def want_host_notification(self, timestamp, state, n_type, business_impact, cmd=None):
"""Check if notification options match the state of the host
Notification is NOT wanted in ONE of the following case::
* host notifications are disabled
* cmd is not in host_notification_commands
* business_impact < self.min_business_impact
* host_notification_period is not valid
* state does not match host_notification_options for problem, recovery, flapping and dt
:param timestamp: time we want to notify the contact (usually now)
:type timestamp: int
:param state: host or service state ("WARNING", "CRITICAL" ..)
:type state: str
:param n_type: type of notification ("PROBLEM", "RECOVERY" ..)
:type n_type: str
:param business_impact: impact of this service
:type business_impact: int
:param cmd: command launched to notify the contact
:type cmd: str
:return: True if no condition is matched, otherwise False
:rtype: bool
TODO: Simplify function
"""
if not self.host_notifications_enabled:
return False
# If the business_impact is not high enough, we bail out
if business_impact < self.min_business_impact:
return False
# Maybe the command we ask for are not for us, but for another notification ways
# on the same contact. If so, bail out
if cmd and cmd not in self.host_notification_commands:
return False
valid = self.host_notification_period.is_time_valid(timestamp)
if 'n' in self.host_notification_options:
return False
timestamp = {'DOWN': 'd', 'UNREACHABLE': 'u', 'RECOVERY': 'r',
'FLAPPING': 'f', 'DOWNTIME': 's'}
if n_type == 'PROBLEM':
if state in timestamp:
return valid and timestamp[state] in self.host_notification_options
elif n_type == 'RECOVERY':
if n_type in timestamp:
return valid and timestamp[n_type] in self.host_notification_options
elif n_type == 'ACKNOWLEDGEMENT':
return valid
elif n_type in ('FLAPPINGSTART', 'FLAPPINGSTOP', 'FLAPPINGDISABLED'):
return valid and 'f' in self.host_notification_options
elif n_type in ('DOWNTIMESTART', 'DOWNTIMEEND', 'DOWNTIMECANCELLED'):
return valid and 's' in self.host_notification_options
return False
[docs] def get_notification_commands(self, o_type):
"""Get notification commands for object type
:param o_type: object type (host or service)
:type o_type: str
:return: command list
:rtype: list[alignak.objects.command.Command]
"""
# service_notification_commands for service
notif_commands_prop = o_type + '_notification_commands'
notif_commands = getattr(self, notif_commands_prop)
return notif_commands
[docs] def is_correct(self):
"""Check if this host configuration is correct ::
* All required parameter are specified
* Go through all configuration warnings and errors that could have been raised earlier
:return: True if the configuration is correct, otherwise False
:rtype: bool
"""
state = True
cls = self.__class__
# Raised all previously saw errors like unknown commands or timeperiods
if self.configuration_errors != []:
state = False
for err in self.configuration_errors:
logger.error("[item::%s] %s", self.get_name(), err)
# A null notif way is a notif way that will do nothing (service = n, hot =n)
is_null_notifway = False
if (hasattr(self, 'service_notification_options') and
self.service_notification_options == ['n']):
if (hasattr(self, 'host_notification_options') and
self.host_notification_options == ['n']):
is_null_notifway = True
return True
for prop, entry in cls.properties.items():
if prop not in self.special_properties:
if not hasattr(self, prop) and entry.required:
logger.warning("[notificationway::%s] %s property not set",
self.get_name(), prop)
state = False # Bad boy...
# Ok now we manage special cases...
# Service part
if not hasattr(self, 'service_notification_commands'):
logger.warning("[notificationway::%s] do not have any "
"service_notification_commands defined", self.get_name())
state = False
else:
for cmd in self.service_notification_commands:
if cmd is None:
logger.warning("[notificationway::%s] a "
"service_notification_command is missing", self.get_name())
state = False
if not cmd.is_valid():
logger.warning("[notificationway::%s] a "
"service_notification_command is invalid", self.get_name())
state = False
if getattr(self, 'service_notification_period', None) is None:
logger.warning("[notificationway::%s] the "
"service_notification_period is invalid", self.get_name())
state = False
# Now host part
if not hasattr(self, 'host_notification_commands'):
logger.warning("[notificationway::%s] do not have any "
"host_notification_commands defined", self.get_name())
state = False
else:
for cmd in self.host_notification_commands:
if cmd is None:
logger.warning("[notificationway::%s] a "
"host_notification_command is missing", self.get_name())
state = False
if not cmd.is_valid():
logger.warning("[notificationway::%s] a host_notification_command "
"is invalid (%s)", cmd.get_name(), str(cmd.__dict__))
state = False
if getattr(self, 'host_notification_period', None) is None:
logger.warning("[notificationway::%s] the host_notification_period "
"is invalid", self.get_name())
state = False
return state
[docs]class NotificationWays(Items):
"""NotificationWays manage a list of NotificationWay objects, used for parsing configuration
"""
name_property = "notificationway_name"
inner_class = NotificationWay
[docs] def linkify(self, timeperiods, commands):
"""Create link between objects::
* notificationways -> timeperiods
* notificationways -> commands
:param timeperiods: timeperiods to link
:type timeperiods: alignak.objects.timeperiod.Timeperiods
:param commands: commands to link
:type commands: alignak.objects.command.Commands
:return: None
"""
self.linkify_with_timeperiods(timeperiods, 'service_notification_period')
self.linkify_with_timeperiods(timeperiods, 'host_notification_period')
self.linkify_command_list_with_commands(commands, 'service_notification_commands')
self.linkify_command_list_with_commands(commands, 'host_notification_commands')
[docs] def new_inner_member(self, name=None, params={}):
"""Create new instance of NotificationWay with given name and parameters
and add it to the item list
:param name: notification way name
:type name: str
:param params: notification wat parameters
:type params: dict
:return: None
"""
if name is None:
name = NotificationWay._id
params['notificationway_name'] = name
# print "Asking a new inner notificationway from name %s with params %s" % (name, params)
notificationway = NotificationWay(params)
self.add_item(notificationway)