2 #Copyright (C) 2009-2010 :
3 # Gabes Jean, naparuba@gmail.com
4 # Gerhard Lausser, Gerhard.Lausser@consol.de
5 # Gregory Starck, g.starck@gmail.com
7 #This file is part of Shinken.
9 #Shinken is free software: you can redistribute it and/or modify
10 #it under the terms of the GNU Affero General Public License as published by
11 #the Free Software Foundation, either version 3 of the License, or
12 #(at your option) any later version.
14 #Shinken is distributed in the hope that it will be useful,
15 #but WITHOUT ANY WARRANTY; without even the implied warranty of
16 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 #GNU Affero General Public License for more details.
19 #You should have received a copy of the GNU Affero General Public License
20 #along with Shinken. If not, see <http://www.gnu.org/licenses/>.
22 from item
import Item
, Items
23 from shinken
.util
import to_int
, to_split
, strip_and_uniq
24 from shinken
.property import UnusedProp
, BoolProp
, IntegerProp
, FloatProp
, CharProp
, StringProp
, ListProp
25 from shinken
.log
import logger
28 _special_properties
= ( 'contacts', 'contact_groups', 'first_notification_time', 'last_notification_time' )
29 _special_properties_time_based
= ( 'contacts', 'contact_groups', 'first_notification', 'last_notification' )
32 class Escalation(Item
):
33 id = 1 #0 is always special in database, so we do not take risk here
34 my_type
= 'escalation'
36 properties
={'escalation_name': StringProp(),
37 'first_notification': IntegerProp(),
38 'last_notification': IntegerProp(),
39 'first_notification_time': IntegerProp(),
40 'last_notification_time': IntegerProp(),
41 'notification_interval': IntegerProp(),
42 'escalation_period': StringProp(default
=None),
43 'escalation_options': ListProp(default
='d,u,r,w,c'),
44 'contacts': StringProp(),
45 'contact_groups': StringProp(),
47 running_properties
= {
48 # All errors and warning raised during the configuration parsing
49 # and that will raised real warning/errors during the is_correct
50 'configuration_warnings': StringProp(default
=[]),
51 'configuration_errors': StringProp(default
=[]),
52 'time_based' : BoolProp(default
=False),
58 # For debugging purpose only (nice name)
60 return self
.escalation_name
64 # *time in in escalation_period or we do not have escalation_period
65 # *status is in escalation_options
66 # *the notification number is in our interval [[first_notification .. last_notification]]
67 # if we are a classic escalation.
68 # *If we are time based, we check if the time that we were in notification
69 # is in our time interval
70 def is_eligible(self
, t
, status
, notif_number
, in_notif_time
, interval
):
71 small_states
= {'WARNING' : 'w', 'UNKNOWN' : 'u', 'CRITICAL' : 'c',
72 'RECOVERY' : 'r', 'FLAPPING' : 'f', 'DOWNTIME' : 's',
73 'DOWN' : 'd', 'UNREACHABLE' : 'u', 'OK' : 'o', 'UP' : 'o'}
75 # If we are not time based, we check notification numbers:
76 if not self
.time_based
:
77 # Begin with the easy cases
78 if notif_number
< self
.first_notification
:
81 #self.last_notification = 0 mean no end
82 if self
.last_notification
!= 0 and notif_number
> self
.last_notification
:
84 # Else we are time based, we must check for the good value
86 # Begin with the easy cases
87 if in_notif_time
< self
.first_notification_time
* interval
:
90 #self.last_notification = 0 mean no end
91 if self
.last_notification_time
!= 0 and in_notif_time
> self
.last_notification_time
* interval
:
94 # If our status is not good, we bail out too
95 if status
in small_states
and small_states
[status
] not in self
.escalation_options
:
98 #Maybe the time is not in our escalation_period
99 if self
.escalation_period
!= None and not self
.escalation_period
.is_time_valid(t
):
102 #Ok, I do not see why not escalade. So it's True :)
106 # t = the reference time
107 def get_next_notif_time(self
, t_wished
, status
, creation_time
, interval
):
108 small_states
= {'WARNING' : 'w', 'UNKNOWN' : 'u', 'CRITICAL' : 'c',
109 'RECOVERY' : 'r', 'FLAPPING' : 'f', 'DOWNTIME' : 's',
110 'DOWN' : 'd', 'UNREACHABLE' : 'u', 'OK' : 'o', 'UP' : 'o'}
112 # If we are not time based, we bail out!
113 if not self
.time_based
:
116 # Check if we are valid
117 if status
in small_states
and small_states
[status
] not in self
.escalation_options
:
120 # Look for the min of our future validify
121 start
= self
.first_notification_time
* interval
+ creation_time
123 # If we are after the classic next time, we are not asking for a smaller interval
127 # Maybe the time we found is not a valid one....
128 if self
.escalation_period
!= None and not self
.escalation_period
.is_time_valid(start
):
131 # Ok so I ask for my start as a possibility for the next notification time
135 # Check is required prop are set:
136 # template are always correct
137 # contacts OR contactgroups is need
138 def is_correct(self
):
139 state
= True # guilty or not? :)
142 # If we got the _time parameters, we are time based. Unless, we are not :)
143 if hasattr(self
, 'first_notification_time') or hasattr(self
, 'last_notification_time'):
144 self
.time_based
= True
145 special_properties
= _special_properties_time_based
147 special_properties
= _special_properties
149 for prop
in cls
.properties
:
150 if prop
not in special_properties
:
151 if not hasattr(self
, prop
) and cls
.properties
[prop
].required
:
152 logger
.log('%s : I do not have %s' % (self
.get_name(), prop
))
153 state
= False # Bad boy...
155 # Raised all previously saw errors like unknown contacts and co
156 if self
.configuration_errors
!= []:
158 for err
in self
.configuration_errors
:
161 # Ok now we manage special cases...
162 if not hasattr(self
, 'contacts') and not hasattr(self
, 'contact_groups'):
163 logger
.log('%s : I do not have contacts nor contact_groups' % self
.get_name())
166 # If time_based or not, we do not check all properties
168 if not hasattr(self
, 'first_notification_time'):
169 logger
.log('%s : I do not have first_notification_time' % self
.get_name())
171 if not hasattr(self
, 'last_notification_time'):
172 logger
.log('%s : I do not have last_notification_time' % self
.get_name())
174 else: # we check classical properties
175 if not hasattr(self
, 'first_notification'):
176 logger
.log('%s : I do not have first_notification' % self
.get_name())
178 if not hasattr(self
, 'last_notification'):
179 logger
.log('%s : I do not have last_notification' % self
.get_name())
186 class Escalations(Items
):
187 name_property
= "escalation_name"
188 inner_class
= Escalation
190 def linkify(self
, timeperiods
, contacts
, services
, hosts
):
191 self
.linkify_with_timeperiods(timeperiods
, 'escalation_period')
192 self
.linkify_with_contacts(contacts
)
193 self
.linkify_es_by_s(services
)
194 self
.linkify_es_by_h(hosts
)
197 def add_escalation(self
, es
):
198 self
.items
[es
.id] = es
201 #Will register esclations into service.escalations
202 def linkify_es_by_s(self
, services
):
204 #If no host, no hope of having a service
205 if not (hasattr(es
, 'host_name') and hasattr(es
, 'service_description')):
207 es_hname
, sdesc
= es
.host_name
, es
.service_description
208 if '' in (es_hname
.strip(), sdesc
.strip()):
210 for hname
in strip_and_uniq( es_hname
.split(',') ):
211 for sname
in strip_and_uniq( sdesc
.split(',') ):
212 s
= services
.find_srv_by_name_and_hostname(hname
, sname
)
214 #print "Linking service", s.get_name(), 'with me', es.get_name()
215 s
.escalations
.append(es
)
216 #print "Now service", s.get_name(), 'have', s.escalations
219 #Will rgister escalations into host.escalations
220 def linkify_es_by_h(self
, hosts
):
222 #If no host, no hope of having a service
223 if (not hasattr(es
, 'host_name') or es
.host_name
.strip() == ''
224 or (hasattr(es
, 'service_description') and es
.service_description
.strip() != '')):
226 #I must be NOT a escalati on for service
227 for hname
in strip_and_uniq(es
.host_name
.split(',')):
228 h
= hosts
.find_by_name(hname
)
230 #print "Linking host", h.get_name(), 'with me', es.get_name()
231 h
.escalations
.append(es
)
232 #print "Now host", h.get_name(), 'have', h.escalations
235 #We look for contacts property in contacts and
236 def explode(self
, hosts
, hostgroups
, contactgroups
):
238 #items::explode_host_groups_into_hosts
239 #take all hosts from our hostgroup_name into our host_name property
240 self
.explode_host_groups_into_hosts(hosts
, hostgroups
)
242 #items::explode_contact_groups_into_contacts
243 #take all contacts from our contact_groups into our contact property
244 self
.explode_contact_groups_into_contacts(contactgroups
)