<NME> notificationway.py
<BEF> #!/usr/bin/python

# -*- coding: utf-8 -*-

# Copyright (C) 2009-2014:
#    Gabes Jean, naparuba@gmail.com
#    Gerhard Lausser, Gerhard.Lausser@consol.de
#    Gregory Starck, g.starck@gmail.com
#    Hartmut Goebel, h.goebel@goebel-consult.de
#
# 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/>.

from item import Item, Items

from shinken.property import BoolProp, IntegerProp, StringProp, ListProp
from shinken.log import logger

_special_properties = ('service_notification_commands', 'host_notification_commands',
                       'service_notification_period', 'host_notification_period')


class NotificationWay(Item):
    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 Shinken deprecated names like criticity
    old_properties = {
        'min_criticity': 'min_business_impact',
    }

    macros = {}

    # For debugging purpose only (nice name)
    def get_name(self):
        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 not cmd in self.service_notification_commands:
        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

        b = self.service_notification_period.is_time_valid(t)
        if 'n' in self.service_notification_options:
            return False
        t = {'WARNING': 'w', 'UNKNOWN': 'u', 'CRITICAL': 'c',
             'RECOVERY': 'r', 'FLAPPING': 'f', 'DOWNTIME': 's'}
        if type == 'PROBLEM':
            if state in t:
                return b and t[state] in self.service_notification_options
        elif type == 'RECOVERY':
            if type in t:
                return b and t[type] in self.service_notification_options
        elif type == 'ACKNOWLEDGEMENT':
            return b
        elif type in ('FLAPPINGSTART', 'FLAPPINGSTOP', 'FLAPPINGDISABLED'):
            return b and 'f' in self.service_notification_options
        elif 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 b and 's' in self.host_notification_options

        return False


    # Search for notification_options with state and if t is in
    # host_notification_period
        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
        if cmd and cmd not in self.host_notification_commands:
            return False

        b = self.host_notification_period.is_time_valid(t)
        if 'n' in self.host_notification_options:
            return False
        t = {'DOWN': 'd', 'UNREACHABLE': 'u', 'RECOVERY': 'r',
             'FLAPPING': 'f', 'DOWNTIME': 's'}
        if type == 'PROBLEM':
            if state in t:
                return b and t[state] in self.host_notification_options
        elif type == 'RECOVERY':
            if type in t:
                return b and t[type] in self.host_notification_options
        elif type == 'ACKNOWLEDGEMENT':
            return b
        elif type in ('FLAPPINGSTART', 'FLAPPINGSTOP', 'FLAPPINGDISABLED'):
            return b and 'f' in self.host_notification_options
        elif type in ('DOWNTIMESTART', 'DOWNTIMEEND', 'DOWNTIMECANCELLED'):
            return b and 's' in self.host_notification_options

        return False


    # Call to get our commands to launch a Notification
    def get_notification_commands(self, type):
        # service_notification_commands for service
        notif_commands_prop = type + '_notification_commands'
        notif_commands = getattr(self, notif_commands_prop)
        return notif_commands


    # Check is required prop are set:
    # contacts OR contactgroups is need
    def is_correct(self):
        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 _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


    # In the scheduler we need to relink the commandCall with
    # the real commands
    def late_linkify_nw_by_commands(self, commands):
        props = ['service_notification_commands', 'host_notification_commands']
        for prop in props:
            for cc in getattr(self, prop, []):
                cc.late_linkify_with_command(commands)


class NotificationWays(Items):
    name_property = "notificationway_name"
    inner_class = NotificationWay


    def linkify(self, timeperiods, commands):
        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')


    def new_inner_member(self, name=None, params={}):
        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)
        nw = NotificationWay(params)
        self.add_item(nw)

<MSG> Merge pull request #415 from h4wkmoon/patch-25

Remove notifications during scheduled downtime.
<DFF> @@ -76,6 +76,10 @@ class NotificationWay(Item):
         if not self.service_notifications_enabled:
             return False
 
+        if self.in_scheduled_downtime:
+            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 not cmd in self.service_notification_commands:
@@ -114,6 +118,10 @@ class NotificationWay(Item):
         if not self.host_notifications_enabled:
             return False
 
+        if self.in_scheduled_downtime:
+            return False
+
+
         # If the business_impact is not high enough, we bail out
         if business_impact < self.min_business_impact:
             return False

{'commit': 'diff --git a/shinken/objects/notificationway.py b/shinken/objects/notificationway.py\nindex a07f006db..297780157 100644\n--- a/shinken/objects/notificationway.py\n+++ b/shinken/objects/notificationway.py\n@@ -76,6 +76,10 @@ class NotificationWay(Item):\n         if not self.service_notifications_enabled:\n             return False\n \n+        if self.in_scheduled_downtime:\n+            return False\n+\n+\n         # Maybe the command we ask for are not for us, but for another notification ways\n         # on the same contact. If so, bail out\n         if cmd and not cmd in self.service_notification_commands:\n@@ -114,6 +118,10 @@ class NotificationWay(Item):\n         if not self.host_notifications_enabled:\n             return False\n \n+        if self.in_scheduled_downtime:\n+            return False\n+\n+\n         # If the business_impact is not high enough, we bail out\n         if business_impact < self.min_business_impact:\n             return False\n', 'commit_subject': 'Merge pull request #415 from h4wkmoon/patch-25', 'repo_name': 'naparuba/shinken', 'committer': {'name': 'Gabès Jean', 'email': 'c8e019fb2d4dd0d355144f4fa37aa9b8f4b57451@gmail.com', 'time_sec': '1341324618', 'tz_offset': '-420', 'date': {'seconds': '1341324618'}}, 'commit_tree': 'fcb748775852784cb2e991162de04c3012934518', 'commit_message': 'Merge pull request #415 from h4wkmoon/patch-25\n\nRemove notifications during scheduled downtime.', 'language_name': 'Python', 'license': 'agpl-3.0', 'hunks': '@@ -76,6 +76,10 @@ class NotificationWay(Item):\n         if not self.service_notifications_enabled:\n             return False\n \n+        if self.in_scheduled_downtime:\n+            return False\n+\n+\n         # Maybe the command we ask for are not for us, but for another notification ways\n         # on the same contact. If so, bail out\n         if cmd and not cmd in self.service_notification_commands:\n@@ -114,6 +118,10 @@ class NotificationWay(Item):\n         if not self.host_notifications_enabled:\n             return False\n \n+        if self.in_scheduled_downtime:\n+            return False\n+\n+\n         # If the business_impact is not high enough, we bail out\n         if business_impact < self.min_business_impact:\n             return False\n', 'addition_count': 8, 'deletion_count': 0, 'src_file': 'a/shinken/objects/notificationway.py', 'tgt_file': 'b/shinken/objects/notificationway.py', 'file_extension': '.py', 'before_file': '#!/usr/bin/python\n\n# -*- coding: utf-8 -*-\n\n# Copyright (C) 2009-2014:\n#    Gabes Jean, naparuba@gmail.com\n#    Gerhard Lausser, Gerhard.Lausser@consol.de\n#    Gregory Starck, g.starck@gmail.com\n#    Hartmut Goebel, h.goebel@goebel-consult.de\n#\n# This file is part of Shinken.\n#\n# Shinken is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# Shinken is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with Shinken.  If not, see <http://www.gnu.org/licenses/>.\n\nfrom item import Item, Items\n\nfrom shinken.property import BoolProp, IntegerProp, StringProp, ListProp\nfrom shinken.log import logger\n\n_special_properties = (\'service_notification_commands\', \'host_notification_commands\',\n                       \'service_notification_period\', \'host_notification_period\')\n\n\nclass NotificationWay(Item):\n    id = 1  # zero is always special in database, so we do not take risk here\n    my_type = \'notificationway\'\n\n    properties = Item.properties.copy()\n    properties.update({\n        \'notificationway_name\':\n            StringProp(fill_brok=[\'full_status\']),\n        \'host_notifications_enabled\':\n            BoolProp(default=True, fill_brok=[\'full_status\']),\n        \'service_notifications_enabled\':\n            BoolProp(default=True, fill_brok=[\'full_status\']),\n        \'host_notification_period\':\n            StringProp(fill_brok=[\'full_status\']),\n        \'service_notification_period\':\n            StringProp(fill_brok=[\'full_status\']),\n        \'host_notification_options\':\n            ListProp(default=[\'\'], fill_brok=[\'full_status\'], split_on_coma=True),\n        \'service_notification_options\':\n            ListProp(default=[\'\'], fill_brok=[\'full_status\'], split_on_coma=True),\n        \'host_notification_commands\':\n            ListProp(fill_brok=[\'full_status\']),\n        \'service_notification_commands\':\n            ListProp(fill_brok=[\'full_status\']),\n        \'min_business_impact\':\n            IntegerProp(default=0, fill_brok=[\'full_status\']),\n    })\n\n    running_properties = Item.running_properties.copy()\n\n    # This tab is used to transform old parameters name into new ones\n    # so from Nagios2 format, to Nagios3 ones.\n    # Or Shinken deprecated names like criticity\n    old_properties = {\n        \'min_criticity\': \'min_business_impact\',\n    }\n\n    macros = {}\n\n    # For debugging purpose only (nice name)\n    def get_name(self):\n        if not self.service_notifications_enabled:\n            return False\n\n        # Maybe the command we ask for are not for us, but for another notification ways\n        # on the same contact. If so, bail out\n        if cmd and not cmd in self.service_notification_commands:\n        if cmd and cmd not in self.service_notification_commands:\n            return False\n\n        # If the business_impact is not high enough, we bail out\n        if business_impact < self.min_business_impact:\n            return False\n\n        b = self.service_notification_period.is_time_valid(t)\n        if \'n\' in self.service_notification_options:\n            return False\n        t = {\'WARNING\': \'w\', \'UNKNOWN\': \'u\', \'CRITICAL\': \'c\',\n             \'RECOVERY\': \'r\', \'FLAPPING\': \'f\', \'DOWNTIME\': \'s\'}\n        if type == \'PROBLEM\':\n            if state in t:\n                return b and t[state] in self.service_notification_options\n        elif type == \'RECOVERY\':\n            if type in t:\n                return b and t[type] in self.service_notification_options\n        elif type == \'ACKNOWLEDGEMENT\':\n            return b\n        elif type in (\'FLAPPINGSTART\', \'FLAPPINGSTOP\', \'FLAPPINGDISABLED\'):\n            return b and \'f\' in self.service_notification_options\n        elif type in (\'DOWNTIMESTART\', \'DOWNTIMEEND\', \'DOWNTIMECANCELLED\'):\n            # No notification when a downtime was cancelled. Is that true??\n            # According to the documentation we need to look at _host_ options\n            return b and \'s\' in self.host_notification_options\n\n        return False\n\n\n    # Search for notification_options with state and if t is in\n    # host_notification_period\n        if not self.host_notifications_enabled:\n            return False\n\n        # If the business_impact is not high enough, we bail out\n        if business_impact < self.min_business_impact:\n            return False\n        if cmd and cmd not in self.host_notification_commands:\n            return False\n\n        b = self.host_notification_period.is_time_valid(t)\n        if \'n\' in self.host_notification_options:\n            return False\n        t = {\'DOWN\': \'d\', \'UNREACHABLE\': \'u\', \'RECOVERY\': \'r\',\n             \'FLAPPING\': \'f\', \'DOWNTIME\': \'s\'}\n        if type == \'PROBLEM\':\n            if state in t:\n                return b and t[state] in self.host_notification_options\n        elif type == \'RECOVERY\':\n            if type in t:\n                return b and t[type] in self.host_notification_options\n        elif type == \'ACKNOWLEDGEMENT\':\n            return b\n        elif type in (\'FLAPPINGSTART\', \'FLAPPINGSTOP\', \'FLAPPINGDISABLED\'):\n            return b and \'f\' in self.host_notification_options\n        elif type in (\'DOWNTIMESTART\', \'DOWNTIMEEND\', \'DOWNTIMECANCELLED\'):\n            return b and \'s\' in self.host_notification_options\n\n        return False\n\n\n    # Call to get our commands to launch a Notification\n    def get_notification_commands(self, type):\n        # service_notification_commands for service\n        notif_commands_prop = type + \'_notification_commands\'\n        notif_commands = getattr(self, notif_commands_prop)\n        return notif_commands\n\n\n    # Check is required prop are set:\n    # contacts OR contactgroups is need\n    def is_correct(self):\n        state = True\n        cls = self.__class__\n\n        # Raised all previously saw errors like unknown commands or timeperiods\n        if self.configuration_errors != []:\n            state = False\n            for err in self.configuration_errors:\n                logger.error("[item::%s] %s", self.get_name(), err)\n\n\n        # A null notif way is a notif way that will do nothing (service = n, hot =n)\n        is_null_notifway = False\n        if (hasattr(self, \'service_notification_options\') and\n                self.service_notification_options == [\'n\']):\n            if (hasattr(self, \'host_notification_options\') and\n                    self.host_notification_options == [\'n\']):\n                is_null_notifway = True\n                return True\n\n        for prop, entry in cls.properties.items():\n            if prop not in _special_properties:\n                if not hasattr(self, prop) and entry.required:\n                    logger.warning("[notificationway::%s] %s property not set",\n                                   self.get_name(), prop)\n                    state = False  # Bad boy...\n\n        # Ok now we manage special cases...\n        # Service part\n        if not hasattr(self, \'service_notification_commands\'):\n            logger.warning("[notificationway::%s] do not have any "\n                           "service_notification_commands defined", self.get_name())\n            state = False\n        else:\n            for cmd in self.service_notification_commands:\n                if cmd is None:\n                    logger.warning("[notificationway::%s] a "\n                                   "service_notification_command is missing", self.get_name())\n                    state = False\n                if not cmd.is_valid():\n                    logger.warning("[notificationway::%s] a "\n                                   "service_notification_command is invalid", self.get_name())\n                    state = False\n\n        if getattr(self, \'service_notification_period\', None) is None:\n            logger.warning("[notificationway::%s] the "\n                           "service_notification_period is invalid", self.get_name())\n            state = False\n\n        # Now host part\n        if not hasattr(self, \'host_notification_commands\'):\n            logger.warning("[notificationway::%s] do not have any "\n                           "host_notification_commands defined", self.get_name())\n            state = False\n        else:\n            for cmd in self.host_notification_commands:\n                if cmd is None:\n                    logger.warning("[notificationway::%s] a "\n                                   "host_notification_command is missing", self.get_name())\n                    state = False\n                if not cmd.is_valid():\n                    logger.warning("[notificationway::%s] a host_notification_command "\n                                   "is invalid (%s)", cmd.get_name(), str(cmd.__dict__))\n                    state = False\n\n        if getattr(self, \'host_notification_period\', None) is None:\n            logger.warning("[notificationway::%s] the host_notification_period "\n                           "is invalid", self.get_name())\n            state = False\n\n        return state\n\n\n    # In the scheduler we need to relink the commandCall with\n    # the real commands\n    def late_linkify_nw_by_commands(self, commands):\n        props = [\'service_notification_commands\', \'host_notification_commands\']\n        for prop in props:\n            for cc in getattr(self, prop, []):\n                cc.late_linkify_with_command(commands)\n\n\nclass NotificationWays(Items):\n    name_property = "notificationway_name"\n    inner_class = NotificationWay\n\n\n    def linkify(self, timeperiods, commands):\n        self.linkify_with_timeperiods(timeperiods, \'service_notification_period\')\n        self.linkify_with_timeperiods(timeperiods, \'host_notification_period\')\n        self.linkify_command_list_with_commands(commands, \'service_notification_commands\')\n        self.linkify_command_list_with_commands(commands, \'host_notification_commands\')\n\n\n    def new_inner_member(self, name=None, params={}):\n        if name is None:\n            name = NotificationWay.id\n        params[\'notificationway_name\'] = name\n        # print "Asking a new inner notificationway from name %s with params %s" % (name, params)\n        nw = NotificationWay(params)\n        self.add_item(nw)\n', 'message': 'Merge pull request #415 from h4wkmoon/patch-25\n\nRemove notifications during scheduled downtime.', 'author_name': 'Gabès Jean', 'lang': 'py'}