*Remove the dot from some import paths, so python 2.4 is happy
[shinken.git] / shinken / objects / escalation.py
blob3bee6d289063642af176580f8da569fa50a0c43f
1 #!/usr/bin/env python
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),
55 macros = {}
58 # For debugging purpose only (nice name)
59 def get_name(self):
60 return self.escalation_name
63 # Return True if :
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:
79 return False
81 #self.last_notification = 0 mean no end
82 if self.last_notification != 0 and notif_number > self.last_notification:
83 return False
84 # Else we are time based, we must check for the good value
85 else:
86 # Begin with the easy cases
87 if in_notif_time < self.first_notification_time * interval:
88 return False
90 #self.last_notification = 0 mean no end
91 if self.last_notification_time != 0 and in_notif_time > self.last_notification_time * interval:
92 return False
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:
96 return False
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):
100 return False
102 #Ok, I do not see why not escalade. So it's True :)
103 return 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:
114 return None
116 # Check if we are valid
117 if status in small_states and small_states[status] not in self.escalation_options:
118 return None
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
124 if start > t_wished:
125 return None
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):
129 return None
131 # Ok so I ask for my start as a possibility for the next notification time
132 return start
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? :)
140 cls = self.__class__
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
146 else: #classic ones
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 != []:
157 state = False
158 for err in self.configuration_errors:
159 logger.log(err)
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())
164 state = False
166 # If time_based or not, we do not check all properties
167 if self.time_based:
168 if not hasattr(self, 'first_notification_time'):
169 logger.log('%s : I do not have first_notification_time' % self.get_name())
170 state = False
171 if not hasattr(self, 'last_notification_time'):
172 logger.log('%s : I do not have last_notification_time' % self.get_name())
173 state = False
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())
177 state = False
178 if not hasattr(self, 'last_notification'):
179 logger.log('%s : I do not have last_notification' % self.get_name())
180 state = False
182 return state
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):
203 for es in self:
204 #If no host, no hope of having a service
205 if not (hasattr(es, 'host_name') and hasattr(es, 'service_description')):
206 continue
207 es_hname, sdesc = es.host_name, es.service_description
208 if '' in (es_hname.strip(), sdesc.strip()):
209 continue
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)
213 if s != None:
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):
221 for es in self:
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() != '')):
225 continue
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)
229 if h != None:
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)