2 #Copyright (C) 2009-2010 :
3 # Gabes Jean, naparuba@gmail.com
4 # Gerhard Lausser, Gerhard.Lausser@consol.de
6 #This file is part of Shinken.
8 #Shinken is free software: you can redistribute it and/or modify
9 #it under the terms of the GNU Affero General Public License as published by
10 #the Free Software Foundation, either version 3 of the License, or
11 #(at your option) any later version.
13 #Shinken is distributed in the hope that it will be useful,
14 #but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 #GNU Affero General Public License for more details.
18 #You should have received a copy of the GNU Affero General Public License
19 #along with Shinken. If not, see <http://www.gnu.org/licenses/>.
21 from shinken
.item
import Item
, Items
22 from shinken
.util
import to_int
, to_split
, strip_and_uniq
23 from shinken
.property import UnusedProp
, BoolProp
, IntegerProp
, FloatProp
, CharProp
, StringProp
, ListProp
24 from shinken
.log
import logger
26 class Escalation(Item
):
27 id = 1 #0 is always special in database, so we do not take risk here
28 my_type
= 'escalation'
30 properties
={'escalation_name': StringProp(),
31 'first_notification': IntegerProp(),
32 'last_notification': IntegerProp(),
33 'first_notification_time': IntegerProp(),
34 'last_notification_time': IntegerProp(),
35 'notification_interval': IntegerProp(),
36 'escalation_period': StringProp(default
=None),
37 'escalation_options': ListProp(default
='d,u,r,w,c'),
38 'contacts': StringProp(),
39 'contact_groups': StringProp(),
41 running_properties
= {
42 # All errors and warning raised during the configuration parsing
43 # and that will raised real warning/errors during the is_correct
44 'configuration_warnings': StringProp(default
=[]),
45 'configuration_errors': StringProp(default
=[]),
46 'time_based' : BoolProp(default
=False),
52 # For debugging purpose only (nice name)
54 return self
.escalation_name
58 # *time in in escalation_period or we do not have escalation_period
59 # *status is in escalation_options
60 # *the notification number is in our interval [[first_notification .. last_notification]]
61 # if we are a classic escalation.
62 # *If we are time based, we check if the time that we were in notification
63 # is in our time interval
64 def is_eligible(self
, t
, status
, notif_number
, in_notif_time
, interval
):
65 small_states
= {'WARNING' : 'w', 'UNKNOWN' : 'u', 'CRITICAL' : 'c',
66 'RECOVERY' : 'r', 'FLAPPING' : 'f', 'DOWNTIME' : 's',
67 'DOWN' : 'd', 'UNREACHABLE' : 'u', 'OK' : 'o', 'UP' : 'o'}
69 # If we are not time based, we check notification numbers:
70 if not self
.time_based
:
71 # Begin with the easy cases
72 if notif_number
< self
.first_notification
:
75 #self.last_notification = 0 mean no end
76 if self
.last_notification
!= 0 and notif_number
> self
.last_notification
:
78 # Else we are time based, we must check for the good value
80 # Begin with the easy cases
81 if in_notif_time
< self
.first_notification_time
* interval
:
84 #self.last_notification = 0 mean no end
85 if self
.last_notification_time
!= 0 and in_notif_time
> self
.last_notification_time
* interval
:
88 # If our status is not good, we bail out too
89 if status
in small_states
and small_states
[status
] not in self
.escalation_options
:
92 #Maybe the time is not in our escalation_period
93 if self
.escalation_period
!= None and not self
.escalation_period
.is_time_valid(t
):
96 #Ok, I do not see why not escalade. So it's True :)
100 # t = the reference time
101 def get_next_notif_time(self
, t_wished
, status
, creation_time
, interval
):
102 small_states
= {'WARNING' : 'w', 'UNKNOWN' : 'u', 'CRITICAL' : 'c',
103 'RECOVERY' : 'r', 'FLAPPING' : 'f', 'DOWNTIME' : 's',
104 'DOWN' : 'd', 'UNREACHABLE' : 'u', 'OK' : 'o', 'UP' : 'o'}
106 # If we are not time based, we bail out!
107 if not self
.time_based
:
110 # Check if we are valid
111 if status
in small_states
and small_states
[status
] not in self
.escalation_options
:
114 # Look for the min of our future validify
115 start
= self
.first_notification_time
* interval
+ creation_time
117 # If we are after the classic next time, we are not asking for a smaller interval
121 # Maybe the time we found is not a valid one....
122 if self
.escalation_period
!= None and not self
.escalation_period
.is_time_valid(start
):
125 # Ok so I ask for my start as a possibility for the next notification time
131 # Check is required prop are set:
132 # template are always correct
133 # contacts OR contactgroups is need
134 def is_correct(self
):
135 state
= True # guilty or not? :)
138 # If we got the _time parameters, we are time based. Unless, we are not :)
139 if hasattr(self
, 'first_notification_time') or hasattr(self
, 'last_notification_time'):
140 self
.time_based
= True
141 special_properties
= ['contacts', 'contact_groups', 'first_notification', 'last_notification']
143 special_properties
= ['contacts', 'contact_groups', 'first_notification_time', 'last_notification_time']
145 for prop
in cls
.properties
:
146 if prop
not in special_properties
:
147 if not hasattr(self
, prop
) and cls
.properties
[prop
].required
:
148 logger
.log('%s : I do not have %s' % (self
.get_name(), prop
))
149 state
= False # Bad boy...
151 # Raised all previously saw errors like unknown contacts and co
152 if self
.configuration_errors
!= []:
154 for err
in self
.configuration_errors
:
157 # Ok now we manage special cases...
158 if not hasattr(self
, 'contacts') and not hasattr(self
, 'contact_groups'):
159 logger
.log('%s : I do not have contacts nor contact_groups' % self
.get_name())
162 # If time_based or not, we do not check all properties
164 if not hasattr(self
, 'first_notification_time'):
165 logger
.log('%s : I do not have first_notification_time' % self
.get_name())
167 if not hasattr(self
, 'last_notification_time'):
168 logger
.log('%s : I do not have last_notification_time' % self
.get_name())
170 else: # we check classical properties
171 if not hasattr(self
, 'first_notification'):
172 logger
.log('%s : I do not have first_notification' % self
.get_name())
174 if not hasattr(self
, 'last_notification'):
175 logger
.log('%s : I do not have last_notification' % self
.get_name())
182 class Escalations(Items
):
183 name_property
= "escalation_name"
184 inner_class
= Escalation
186 def linkify(self
, timeperiods
, contacts
, services
, hosts
):
187 self
.linkify_with_timeperiods(timeperiods
, 'escalation_period')
188 self
.linkify_with_contacts(contacts
)
189 self
.linkify_es_by_s(services
)
190 self
.linkify_es_by_h(hosts
)
193 def add_escalation(self
, es
):
194 self
.items
[es
.id] = es
197 #Will register esclations into service.escalations
198 def linkify_es_by_s(self
, services
):
200 #If no host, no hope of having a service
201 if not (hasattr(es
, 'host_name') and hasattr(es
, 'service_description')):
203 es_hname
, sdesc
= es
.host_name
, es
.service_description
204 if '' in (es_hname
.strip(), sdesc
.strip()):
206 for hname
in strip_and_uniq( es_hname
.split(',') ):
207 for sname
in strip_and_uniq( sdesc
.split(',') ):
208 s
= services
.find_srv_by_name_and_hostname(hname
, sname
)
210 #print "Linking service", s.get_name(), 'with me', es.get_name()
211 s
.escalations
.append(es
)
212 #print "Now service", s.get_name(), 'have', s.escalations
215 #Will rgister escalations into host.escalations
216 def linkify_es_by_h(self
, hosts
):
218 #If no host, no hope of having a service
219 if (not hasattr(es
, 'host_name') or es
.host_name
.strip() == ''
220 or (hasattr(es
, 'service_description') and es
.service_description
.strip() != '')):
222 #I must be NOT a escalati on for service
223 for hname
in strip_and_uniq(es
.host_name
.split(',')):
224 h
= hosts
.find_by_name(hname
)
226 #print "Linking host", h.get_name(), 'with me', es.get_name()
227 h
.escalations
.append(es
)
228 #print "Now host", h.get_name(), 'have', h.escalations
231 #We look for contacts property in contacts and
232 def explode(self
, hosts
, hostgroups
, contactgroups
):
234 #items::explode_host_groups_into_hosts
235 #take all hosts from our hostgroup_name into our host_name property
236 self
.explode_host_groups_into_hosts(hosts
, hostgroups
)
238 #items::explode_contact_groups_into_contacts
239 #take all contacts from our contact_groups into our contact property
240 self
.explode_contact_groups_into_contacts(contactgroups
)