Merge branch 'master' of ssh://naparuba@shinken.git.sourceforge.net/gitroot/shinken...
[shinken.git] / shinken / downtime.py
blob993a39673debdf7841e99630286a852deebd2a79
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/>.
23 import time
24 from comment import Comment
26 class Downtime:
27 id = 1
29 #Just to list the properties we will send as pickle
30 #so to others daemons, so all but NOT REF
31 properties = {
32 'activate_me' : None,
33 'entry_time' : None,
34 'fixed' : None,
35 'start_time' : None,
36 'duration' : None,
37 'trigger_id' : None,
38 'end_time' : None,
39 'real_end_time' : None,
40 'author' : None,
41 'comment' : None,
42 'is_in_effect' : None,
43 'has_been_triggered' : None,
44 'can_be_deleted' : None,
48 #Schedules downtime for a specified service. If the "fixed" argument is set
49 #to one (1), downtime will start and end at the times specified by the
50 #"start" and "end" arguments.
51 #Otherwise, downtime will begin between the "start" and "end" times and last
52 #for "duration" seconds. The "start" and "end" arguments are specified
53 #in time_t format (seconds since the UNIX epoch). The specified service
54 #downtime can be triggered by another downtime entry if the "trigger_id"
55 #is set to the ID of another scheduled downtime entry.
56 #Set the "trigger_id" argument to zero (0) if the downtime for the
57 #specified service should not be triggered by another downtime entry.
58 def __init__(self, ref, start_time, end_time, fixed, trigger_id, duration, author, comment):
59 self.id = self.__class__.id
60 self.__class__.id += 1
61 self.ref = ref #pointer to srv or host we are apply
62 self.activate_me = [] #The other downtimes i need to activate
63 self.entry_time = int(time.time())
64 self.fixed = fixed
65 self.start_time = start_time
66 self.duration = duration
67 self.trigger_id = trigger_id
68 if self.trigger_id != 0: # triggered plus fixed makes no sense
69 self.fixed = False
70 self.end_time = end_time
71 if fixed:
72 self.duration = end_time - start_time
73 #This is important for flexible downtimes. Here start_time and
74 #end_time mean: in this time interval it is possible to trigger
75 #the beginning of the downtime which lasts for duration.
76 #Later, when a non-ok event happens, real_end_time will be
77 #recalculated from now+duration
78 #end_time will be displayed in the web interface, but real_end_time
79 #is used internally
80 self.real_end_time = end_time
81 self.author = author
82 self.comment = comment
83 self.is_in_effect = False # fixed: start_time has been reached, flexible: non-ok checkresult
84 self.has_been_triggered = False # another downtime has triggered me
85 self.can_be_deleted = False
86 self.add_automatic_comment()
89 def __str__(self):
90 if self.is_in_effect == True:
91 active = "active"
92 else:
93 active = "inactive"
94 if self.fixed == True:
95 type = "fixed"
96 else:
97 type = "flexible"
98 return "%s %s Downtime id=%d %s - %s" % (active, type, self.id, time.ctime(self.start_time), time.ctime(self.end_time))
101 def trigger_me(self, other_downtime):
102 self.activate_me.append(other_downtime)
105 def in_scheduled_downtime(self):
106 return self.is_in_effect
109 #The referenced host/service object enters now a (or another) scheduled
110 #downtime. Write a log message only if it was not already in a downtime
111 def enter(self):
112 res = []
113 self.is_in_effect = True
114 if self.fixed == False:
115 now = time.time()
116 self.real_end_time = now + self.duration
117 if self.ref.scheduled_downtime_depth == 0:
118 self.ref.raise_enter_downtime_log_entry()
119 self.ref.create_notifications('DOWNTIMESTART')
120 self.ref.scheduled_downtime_depth += 1
121 self.ref.in_scheduled_downtime = True
122 for dt in self.activate_me:
123 res.extend(dt.enter())
124 return res
127 #The end of the downtime was reached.
128 def exit(self):
129 res = []
130 if self.is_in_effect == True:
131 #This was a fixed or a flexible+triggered downtime
132 self.is_in_effect = False
133 self.ref.scheduled_downtime_depth -= 1
134 if self.ref.scheduled_downtime_depth == 0:
135 self.ref.raise_exit_downtime_log_entry()
136 self.ref.create_notifications('DOWNTIMEEND')
137 self.ref.in_scheduled_downtime = False
138 else:
139 #This was probably a flexible downtime which was not triggered
140 #In this case it silently disappears
141 pass
142 self.del_automatic_comment()
143 self.can_be_deleted = True
144 #when a downtime ends and the service was critical
145 #a notification is sent with the next critical check
146 #So we should set a flag here which signals consume_result
147 #to send a notification
148 self.ref.in_scheduled_downtime_during_last_check = True
149 return res
152 #A scheduled downtime was prematurely cancelled
153 def cancel(self):
154 res = []
155 self.is_in_effect = False
156 self.ref.scheduled_downtime_depth -= 1
157 if self.ref.scheduled_downtime_depth == 0:
158 self.ref.raise_cancel_downtime_log_entry()
159 self.ref.in_scheduled_downtime = False
160 self.del_automatic_comment()
161 self.can_be_deleted = True
162 self.ref.in_scheduled_downtime_during_last_check = True
163 #Nagios does not notify on cancelled downtimes
164 #res.extend(self.ref.create_notifications('DOWNTIMECANCELLED'))
165 #Also cancel other downtimes triggered by me
166 for dt in self.activate_me:
167 res.extend(dt.cancel())
168 return res
171 #Scheduling a downtime creates a comment automatically
172 def add_automatic_comment(self):
173 if self.fixed == True:
174 text = "This %s has been scheduled for fixed downtime from %s to %s. Notifications for the %s will not be sent out during that time period." % (self.ref.my_type, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.start_time)), time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.end_time)), self.ref.my_type)
175 else:
176 hours, remainder = divmod(self.duration, 3600)
177 minutes, seconds = divmod(remainder, 60)
178 text = "This %s has been scheduled for flexible downtime starting between %s and %s and lasting for a period of %d hours and %d minutes. Notifications for the %s will not be sent out during that time period." % (self.ref.my_type, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.start_time)), time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.end_time)), hours, minutes, self.ref.my_type)
179 if self.ref.my_type == 'host':
180 comment_type = 1
181 else:
182 comment_type = 2
183 c = Comment(self.ref, False, "(Nagios Process)", text, comment_type, 2, 0, False, 0)
184 self.comment_id = c.id
185 self.extra_comment = c
186 self.ref.add_comment(c)
189 def del_automatic_comment(self):
190 # Extra comment can be None if we load it from a old version of Shinken
191 # TODO : remove it in a future verszion when every oen got upgrade
192 if self.extra_comment != None:
193 self.extra_comment.can_be_deleted = True
194 #self.ref.del_comment(self.comment_id)
198 #Call by picle for dataify the downtime
199 #because we DO NOT WANT REF in this pickleisation!
200 def __getstate__(self):
201 cls = self.__class__
202 # id is not in *_properties
203 res = {'id' : self.id}
204 for prop in cls.properties:
205 if hasattr(self, prop):
206 res[prop] = getattr(self, prop)
207 return res
210 #Inversed funtion of getstate
211 def __setstate__(self, state):
212 cls = self.__class__
214 # Maybe it's not a dict but a list like in the old 0.4 format
215 # so we should call the 0.4 function for it
216 if isinstance(state, list):
217 self.__setstate_deprecated__(state)
218 return
220 self.id = state['id']
221 for prop in cls.properties:
222 if prop in state:
223 setattr(self, prop, state[prop])
226 # Theses 2 functions are DEPRECATED and will be removed in a future version of
227 # Shinken. They should not be useful any more after a first load/save pass.
229 #Inversed funtion of getstate
230 def __setstate_deprecated__(self, state):
231 cls = self.__class__
232 #Check if the len of this state is like the previous,
233 # if not, we will do errors!
234 # -1 because of the 'id' prop
235 if len(cls.properties) != (len(state) - 1):
236 print "Passing downtime"
237 return
239 self.id = state.pop()
240 for prop in cls.properties:
241 val = state.pop()
242 setattr(self, prop, val)