This file is indexed.

/usr/lib/python2.7/dist-packages/shinken/objects/escalation.py is in shinken-common 1.4-2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
#!/usr/bin/python

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

# Copyright (C) 2009-2012:
#    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.util import strip_and_uniq
from shinken.property import BoolProp, IntegerProp, StringProp, ListProp
from shinken.log import logger

_special_properties = ('contacts', 'contact_groups', 'first_notification_time', 'last_notification_time')
_special_properties_time_based = ('contacts', 'contact_groups', 'first_notification', 'last_notification')


class Escalation(Item):
    id = 1  # zero is always special in database, so we do not take risk here
    my_type = 'escalation'

    properties = Item.properties.copy()
    properties.update({
        'escalation_name':      StringProp(),
        'first_notification':   IntegerProp(),
        'last_notification':    IntegerProp(),
        'first_notification_time': IntegerProp(),
        'last_notification_time': IntegerProp(),
        # by default don't use the notification_interval defined in
        # the escalation, but the one defined by the object
        'notification_interval': IntegerProp(default='-1'),
        'escalation_period':    StringProp(default=''),
        'escalation_options':   ListProp(default='d,u,r,w,c'),
        'contacts':             StringProp(),
        'contact_groups':       StringProp(),
    })

    running_properties = Item.running_properties.copy()
    running_properties.update({
        'time_based': BoolProp(default=False),
    })

    # For debugging purpose only (nice name)
    def get_name(self):
        return self.escalation_name

    # Return True if:
    # *time in in escalation_period or we do not have escalation_period
    # *status is in escalation_options
    # *the notification number is in our interval [[first_notification .. last_notification]]
    # if we are a classic escalation.
    # *If we are time based, we check if the time that we were in notification
    # is in our time interval
    def is_eligible(self, t, status, notif_number, in_notif_time, interval):
        small_states = {
            'WARNING': 'w',    'UNKNOWN': 'u',     'CRITICAL': 'c',
            'RECOVERY': 'r',   'FLAPPING': 'f',    'DOWNTIME': 's',
            'DOWN': 'd',       'UNREACHABLE': 'u', 'OK': 'o', 'UP': 'o'
        }

        # If we are not time based, we check notification numbers:
        if not self.time_based:
            # Begin with the easy cases
            if notif_number < self.first_notification:
                return False

            #self.last_notification = 0 mean no end
            if self.last_notification != 0 and notif_number > self.last_notification:
                return False
        # Else we are time based, we must check for the good value
        else:
            # Begin with the easy cases
            if in_notif_time < self.first_notification_time * interval:
                return False

            # self.last_notification = 0 mean no end
            if self.last_notification_time != 0 and in_notif_time > self.last_notification_time * interval:
                return False

        # If our status is not good, we bail out too
        if status in small_states and small_states[status] not in self.escalation_options:
            return False

        # Maybe the time is not in our escalation_period
        if self.escalation_period is not None and not self.escalation_period.is_time_valid(t):
            return False

        # Ok, I do not see why not escalade. So it's True :)
        return True

    # t = the reference time
    def get_next_notif_time(self, t_wished, status, creation_time, interval):
        small_states = {'WARNING': 'w', 'UNKNOWN': 'u', 'CRITICAL': 'c',
             'RECOVERY': 'r', 'FLAPPING': 'f', 'DOWNTIME': 's',
             'DOWN': 'd', 'UNREACHABLE': 'u', 'OK': 'o', 'UP': 'o'}

        # If we are not time based, we bail out!
        if not self.time_based:
            return None

        # Check if we are valid
        if status in small_states and small_states[status] not in self.escalation_options:
            return None

        # Look for the min of our future validity
        start = self.first_notification_time * interval + creation_time

        # If we are after the classic next time, we are not asking for a smaller interval
        if start > t_wished:
            return None

        # Maybe the time we found is not a valid one....
        if self.escalation_period is not None and not self.escalation_period.is_time_valid(start):
            return None

        # Ok so I ask for my start as a possibility for the next notification time
        return start

    # Check is required prop are set:
    # template are always correct
    # contacts OR contactgroups is need
    def is_correct(self):
        state = True
        cls = self.__class__

        # If we got the _time parameters, we are time based. Unless, we are not :)
        if hasattr(self, 'first_notification_time') or hasattr(self, 'last_notification_time'):
            self.time_based = True
            special_properties = _special_properties_time_based
        else:  # classic ones
            special_properties = _special_properties

        for prop, entry in cls.properties.items():
            if prop not in special_properties:
                if not hasattr(self, prop) and entry.required:
                    logger.info('%s: I do not have %s' % (self.get_name(), prop))
                    state = False  # Bad boy...

        # Raised all previously saw errors like unknown contacts and co
        if self.configuration_errors != []:
            state = False
            for err in self.configuration_errors:
                logger.info(err)

        # Ok now we manage special cases...
        if not hasattr(self, 'contacts') and not hasattr(self, 'contact_groups'):
            logger.info('%s: I do not have contacts nor contact_groups' % self.get_name())
            state = False

        # If time_based or not, we do not check all properties
        if self.time_based:
            if not hasattr(self, 'first_notification_time'):
                logger.info('%s: I do not have first_notification_time' % self.get_name())
                state = False
            if not hasattr(self, 'last_notification_time'):
                logger.info('%s: I do not have last_notification_time' % self.get_name())
                state = False
        else:  # we check classical properties
            if not hasattr(self, 'first_notification'):
                logger.info('%s: I do not have first_notification' % self.get_name())
                state = False
            if not hasattr(self, 'last_notification'):
                logger.info('%s: I do not have last_notification' % self.get_name())
                state = False

        return state


class Escalations(Items):
    name_property = "escalation_name"
    inner_class = Escalation

    def linkify(self, timeperiods, contacts, services, hosts):
        self.linkify_with_timeperiods(timeperiods, 'escalation_period')
        self.linkify_with_contacts(contacts)
        self.linkify_es_by_s(services)
        self.linkify_es_by_h(hosts)

    def add_escalation(self, es):
        self.items[es.id] = es

    # Will register escalations into service.escalations
    def linkify_es_by_s(self, services):
        for es in self:
            # If no host, no hope of having a service
            if not (hasattr(es, 'host_name') and hasattr(es, 'service_description')):
                continue
            es_hname, sdesc = es.host_name, es.service_description
            if '' in (es_hname.strip(), sdesc.strip()):
                continue
            for hname in strip_and_uniq(es_hname.split(',')):
                for sname in strip_and_uniq(sdesc.split(',')):
                    s = services.find_srv_by_name_and_hostname(hname, sname)
                    if s is not None:
                        #print "Linking service", s.get_name(), 'with me', es.get_name()
                        s.escalations.append(es)
                                #print "Now service", s.get_name(), 'have', s.escalations


    # Will register escalations into host.escalations
    def linkify_es_by_h(self, hosts):
        for es in self:
            # If no host, no hope of having a service
            if (not hasattr(es, 'host_name') or es.host_name.strip() == ''
                    or (hasattr(es, 'service_description') and es.service_description.strip() != '')):
                continue
            # I must be NOT a escalation on for service
            for hname in strip_and_uniq(es.host_name.split(',')):
                h = hosts.find_by_name(hname)
                if h is not None:
                    #print "Linking host", h.get_name(), 'with me', es.get_name()
                    h.escalations.append(es)
                    #print "Now host", h.get_name(), 'have', h.escalations


    # We look for contacts property in contacts and
    def explode(self, hosts, hostgroups, contactgroups):

        # items::explode_host_groups_into_hosts
        # take all hosts from our hostgroup_name into our host_name property
        self.explode_host_groups_into_hosts(hosts, hostgroups)

        # items::explode_contact_groups_into_contacts
        # take all contacts from our contact_groups into our contact property
        self.explode_contact_groups_into_contacts(contactgroups)