Add : the full demoCA certification autority so people can sign their own keys.
[shinken.git] / shinken / daterange.py
blob45c27d29700460fd040fc49ff8f0eeec5cc61760
1 #!/usr/bin/env python
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.util import *
24 #Get the day number (like 27 in July tuesday 27 2010 for call:
25 #2010, july, tuesday, -1 (last tuesday of july 2010)
26 def find_day_by_weekday_offset(year, month, weekday, offset):
27 #et the id if teh weekday (1 for tuesday)
28 weekday_id = Daterange.get_weekday_id(weekday)
29 if weekday_id is None:
30 return None
32 #same for month
33 month_id = Daterange.get_month_id(month)
34 if month_id is None:
35 return None
37 #thanks calendar :)
38 cal = calendar.monthcalendar(year, month_id)
40 #If we ask for a -1 day, just reverse cal
41 if offset < 0:
42 offset = abs(offset)
43 cal.reverse()
45 #ok go for it
46 nb_found = 0
47 try:
48 for i in xrange(0, offset + 1):
49 #in cal 0 mean "there are no day here :)"
50 if cal[i][weekday_id] != 0:
51 nb_found += 1
52 if nb_found == offset:
53 return cal[i][weekday_id]
54 return None
55 except:
56 return None
59 def find_day_by_offset(year, month, offset):
60 month_id = Daterange.get_month_id(month)
61 if month_id is None:
62 return None
63 (tmp, days_in_month) = calendar.monthrange(year, month_id)
64 if offset >= 0:
65 return min(offset, days_in_month)
66 else:
67 return max(1, days_in_month + offset + 1)
70 class Timerange:
71 #entry is like 00:00-24:00
72 def __init__(self, entry):
73 entries = entry.split('-')
74 start = entries[0]
75 end = entries[1]
76 sentries = start.split(':')
77 self.hstart = int(sentries[0])
78 self.mstart = int(sentries[1])
79 eentries = end.split(':')
80 self.hend = int(eentries[0])
81 self.mend = int(eentries[1])
83 def __str__(self):
84 return str(self.__dict__)
86 def get_sec_from_morning(self):
87 return self.hstart*3600 + self.mstart*60
90 def get_first_sec_out_from_morning(self):
91 #If start at 0:0, the min out is the end
92 if self.hstart == 0 and self.mstart == 0:
93 return self.hend*3600 + self.mend*60
94 return 0
97 def is_time_valid(self, t):
98 sec_from_morning = get_sec_from_morning(t)
99 return self.hstart*3600 + self.mstart* 60 <= sec_from_morning <= self.hend*3600 + self.mend* 60
103 class Daterange:
104 weekdays = {'monday' : 0, 'tuesday' : 1, 'wednesday' : 2, 'thursday' : 3, \
105 'friday' : 4, 'saturday' : 5, 'sunday': 6 }
106 months = {'january' : 1, 'february': 2, 'march' : 3, 'april' : 4, 'may' : 5, \
107 'june' : 6, 'july' : 7, 'august' : 8, 'september' : 9, \
108 'october' : 10, 'november' : 11, 'december' : 12}
109 def __init__(self, syear, smon, smday, swday, swday_offset,
110 eyear, emon, emday, ewday, ewday_offset, skip_interval, other):
111 self.syear = int(syear)
112 self.smon = smon
113 self.smday = int(smday)
114 self.swday = swday
115 self.swday_offset = int(swday_offset)
116 self.eyear = int(eyear)
117 self.emon = emon
118 self.emday = int(emday)
119 self.ewday = ewday
120 self.ewday_offset = int(ewday_offset)
121 self.skip_interval = int(skip_interval)
122 self.other = other
123 self.timeranges = []
125 for timeinterval in other.split(','):
126 self.timeranges.append(Timerange(timeinterval.strip()))
129 def __str__(self):
130 return ''#str(self.__dict__)
133 #By default, daterange are correct
134 def is_correct(self):
135 return True
138 def get_month_id(cls, month):
139 try:
140 return Daterange.months[month]
141 except:
142 return None
143 get_month_id = classmethod(get_month_id)
146 #@memoized
147 def get_month_by_id(cls, id):
148 id = id % 12
149 for key in Daterange.months:
150 if id == Daterange.months[key]:
151 return key
152 return None
153 get_month_by_id = classmethod(get_month_by_id)
156 def get_weekday_id(cls, weekday):
157 try:
158 return Daterange.weekdays[weekday]
159 except:
160 return None
161 get_weekday_id = classmethod(get_weekday_id)
164 def get_weekday_by_id(cls, id):
165 id = id % 7
166 for key in Daterange.weekdays:
167 if id == Daterange.weekdays[key]:
168 return key
169 return None
170 get_weekday_by_id = classmethod(get_weekday_by_id)
174 def get_start_and_end_time(self, ref=None):
175 print "Not implemented"
178 def is_time_valid(self, t):
179 if self.is_time_day_valid(t):
180 for tr in self.timeranges:
181 if tr.is_time_valid(t):
182 return True
183 return False
186 def get_min_sec_from_morning(self):
187 mins = []
188 for tr in self.timeranges:
189 mins.append(tr.get_sec_from_morning())
190 return min(mins)
193 def get_min_sec_out_from_morning(self):
194 mins = []
195 for tr in self.timeranges:
196 mins.append(tr.get_first_sec_out_from_morning())
197 return min(mins)
200 def get_min_from_t(self, t):
201 if self.is_time_valid(t):
202 return t
203 t_day_epoch = get_day(t)
204 tr_mins = self.get_min_sec_from_morning()
205 return t_day_epoch + tr_mins
208 def is_time_day_valid(self, t):
209 (start_time, end_time) = self.get_start_and_end_time(t)
210 # print "My class", self.__class__
211 # print "Search for t", time.asctime(time.localtime(start_time))
212 # print "Start time, endtime", time.asctime(time.localtime(start_time)), time.asctime(time.localtime(end_time))
213 if start_time <= t <= end_time:
214 return True
215 else:
216 return False
219 def is_time_day_invalid(self, t):
220 (start_time, end_time) = self.get_start_and_end_time(t)
221 if start_time <= t <= end_time:
222 return False
223 else:
224 return True
227 #def have_future_tiremange_valid(self, t):
228 # starts = []
229 # for tr in self.timeranges:
230 # tr_start = tr.hstart * 3600 + tr.mstart * 3600
231 # if tr_start >= sec_from_morning:
232 # return True
233 # return False
236 def get_next_future_timerange_valid(self, t):
237 sec_from_morning = get_sec_from_morning(t)
238 starts = []
239 for tr in self.timeranges:
240 tr_start = tr.hstart * 3600 + tr.mstart * 60
241 if tr_start >= sec_from_morning:
242 starts.append(tr_start)
243 if starts != []:
244 return min(starts)
245 else:
246 return None
249 def get_next_future_timerange_invalid(self, t):
250 #print 'Call for get_next_future_timerange_invalid from ', time.asctime(time.localtime(t))
251 sec_from_morning = get_sec_from_morning(t)
252 #print 'sec from morning', sec_from_morning
253 ends = []
254 for tr in self.timeranges:
255 tr_start = tr.hstart * 3600 + tr.mstart * 60
256 if tr_start >= sec_from_morning:
257 ends.append(tr_start)
258 tr_end = tr.hend * 3600 + tr.mend * 60
259 if tr_end >= sec_from_morning:
260 ends.append(tr_end)
261 #print "Ends:", ends
262 #Remove the last second of the day for 00->24h"
263 if 86400 in ends:
264 ends.remove(86400)
265 if ends != []:
266 return min(ends)
267 else:
268 return None
271 def get_next_valid_day(self, t):
272 if self.get_next_future_timerange_valid(t) is None:
273 #this day is finish, we check for next period
274 (start_time, end_time) = self.get_start_and_end_time(get_day(t)+86400)
275 else:
276 (start_time, end_time) = self.get_start_and_end_time(t)
278 #print self.__class__
279 #print "Get next valid day start/end for", time.asctime(time.localtime(t))
280 #print "Start", time.asctime(time.localtime(start_time))
281 #print "End", time.asctime(time.localtime(end_time))
283 if t <= start_time:
284 return get_day(start_time)
286 if self.is_time_day_valid(t):
287 return get_day(t)
288 return None
292 def get_next_valid_time_from_t(self, t):
293 #print "DR Get next valid from:", time.asctime(time.localtime(t))
294 #print "DR Get next valid from:", t
295 if self.is_time_valid(t):
296 return t
298 #print "DR Get next valid from:", time.asctime(time.localtime(t))
299 #First we search fot the day of t
300 t_day = self.get_next_valid_day(t)
301 sec_from_morning = self.get_min_sec_from_morning()
303 #print "Search for t", time.asctime(time.localtime(t))
304 #print "DR: next day", time.asctime(time.localtime(t_day))
305 #print "DR: sec from morning", time.asctime(time.localtime(sec_from_morning))
307 #We search for the min of all tr.start > sec_from_morning
308 starts = []
309 for tr in self.timeranges:
310 tr_start = tr.hstart * 3600 + tr.mstart * 3600
311 if tr_start >= sec_from_morning:
312 starts.append(tr_start)
314 #tr can't be valid, or it will be return at the begining
315 sec_from_morning = self.get_next_future_timerange_valid(t)
316 #print "DR: sec from morning", time.asctime(time.localtime(sec_from_morning))
317 #print "Sec from morning", t_day
318 if sec_from_morning is not None:
319 if t_day is not None and sec_from_morning is not None:
320 return t_day + sec_from_morning
322 #Then we search for the next day of t
323 #The sec will be the min of the day
324 t = get_day(t)+86400
325 t_day2 = self.get_next_valid_day(t)
326 sec_from_morning = self.get_next_future_timerange_valid(t_day2)
327 if t_day2 is not None and sec_from_morning is not None:
328 return t_day2 + sec_from_morning
329 else:
330 #I'm not find any valid time
331 return None
334 def get_next_invalid_day(self, t):
335 #print 'DR: get_next_invalid_day for', time.asctime(time.localtime(t))
336 if self.is_time_day_invalid(t):
337 return t
339 next_future_timerange_invalid = self.get_next_future_timerange_invalid(t)
340 #print "next_future_timerange_invalid:", next_future_timerange_invalid
342 #If today there is no more unavalable timerange, search the next day
343 if next_future_timerange_invalid is None:
344 #print 'DR: get_next_future_timerange_invalid is None'
345 #this day is finish, we check for next period
346 (start_time, end_time) = self.get_start_and_end_time(get_day(t)+86400)
347 else:
348 #print 'DR: get_next_future_timerange_invalid is', time.asctime(time.localtime(next_future_timerange_invalid))
349 (start_time, end_time) = self.get_start_and_end_time(t)
350 #res = get_day(t) + next_future_timerange_invalid
351 #print "Early return"
352 #return res
354 #print 'DR:Start:', time.asctime(time.localtime(start_time))
355 #print 'DR:End:', time.asctime(time.localtime(end_time))
356 #The next invalid day can be t day if there a possible
357 #invalid time range (timerange is not 00->24
358 if next_future_timerange_invalid != None:
359 if start_time <= t <= end_time:
360 #print "Early Return next invalid day:", time.asctime(time.localtime(get_day(t)))
361 return get_day(t)
362 if start_time >= t :
363 return get_day(start_time)
364 else:#Else, there is no possibility than in our start_time<->end_time we got
365 #any invalid time (full period out). So it's end_time+1 sec (tomorow of end_time)
366 #print "Full period out, got end_time", time.asctime(time.localtime(get_day(end_time +1)))
367 return get_day(end_time +1)
369 return None
372 def get_next_invalid_time_from_t(self, t):
373 #print 'DR:get_next_invalid_time_from_t', time.asctime(time.localtime(t))
374 if not self.is_time_valid(t):
375 #print "DR: cool, t is invalid", time.asctime(time.localtime(t))
376 return t
377 #else:
378 # print "DR: Arg, t is valid", time.asctime(time.localtime(t))
380 #First we search fot the day of t
381 t_day = self.get_next_invalid_day(t)
382 #print "Get next invalid day:", time.asctime(time.localtime(t_day))
383 #print "Is valid day?", self.is_time_valid(t_day)
385 #We search for the min of all tr.start > sec_from_morning
386 #starts = []
387 #for tr in self.timeranges:
388 # tr_start = tr.hstart * 3600 + tr.mstart * 3600
389 # if tr_start >= sec_from_morning:
390 # starts.append(tr_start)
392 #tr can't be valid, or it will be return at the begining
393 sec_from_morning = self.get_next_future_timerange_invalid(t)
394 #print "TOTO sec_from_morning:", sec_from_morning
395 #Ok we've got a next invalid day and a invalid possibility in
396 #timerange, so the next invalid is this day+sec_from_morning
397 #print "T_day", t_day, "Sec from morning", sec_from_morning
398 if t_day is not None and sec_from_morning is not None:
399 return t_day + sec_from_morning + 1
401 #We've got a day but no sec_from_morning : the timerange is full (0->24h)
402 #so the next invalid is this day at the day_start
403 if t_day is not None and sec_from_morning == None:
404 return t_day
406 #Then we search for the next day of t
407 #The sec will be the min of the day
408 t = get_day(t)+86400
409 t_day2 = self.get_next_invalid_day(t)
410 sec_from_morning = self.get_next_future_timerange_invalid(t_day2)
411 if t_day2 is not None and sec_from_morning is not None:
412 return t_day2 + sec_from_morning + 1
414 if t_day2 is not None and sec_from_morning == None:
415 return t_day2
416 else:
417 #I'm not find any valid time
418 return None
423 #ex: 2007-01-01 - 2008-02-01
424 class CalendarDaterange(Daterange):
425 def get_start_and_end_time(self, ref=None):
426 start_time = get_start_of_day(self.syear, int(self.smon), self.smday)
427 end_time = get_end_of_day(self.eyear, int(self.emon), self.emday)
428 return (start_time, end_time)
432 #Like tuesday 00:00-24:00
433 class StandardDaterange(Daterange):
434 def __init__(self, day, other):
435 self.other = other
436 self.timeranges = []
438 for timeinterval in other.split(','):
439 self.timeranges.append(Timerange(timeinterval.strip()))
440 self.day = day
443 #It's correct only if the weekday (sunday, etc) is a valid one
444 def is_correct(self):
445 b = self.day in Daterange.weekdays
446 if not b:
447 print "Error : %s is not a valid day" % self.day
448 return b
451 def get_start_and_end_time(self, ref=None):
452 now = time.localtime(ref)
453 self.syear = now.tm_year
454 self.month = now.tm_mon
455 #month_start_id = now.tm_mon
456 #month_start = Daterange.get_month_by_id(month_start_id)
457 self.wday = now.tm_wday
458 day_id = Daterange.get_weekday_id(self.day)
459 today_morning = get_start_of_day(now.tm_year, now.tm_mon, now.tm_mday)
460 tonight = get_end_of_day(now.tm_year, now.tm_mon, now.tm_mday)
461 day_diff = (day_id - now.tm_wday) % 7
462 return (today_morning + day_diff*86400, tonight + day_diff*86400)
464 #thusday 3 february
465 class MonthWeekDayDaterange(Daterange):
466 #It's correct only if the weekday (sunday, etc) is a valid one
467 def is_correct(self):
468 b = True
469 b &= self.swday in Daterange.weekdays
470 if not b:
471 print "Error : %s is not a valid day" % self.swday
473 b &= self.ewday in Daterange.weekdays
474 if not b:
475 print "Error : %s is not a valid day" % self.ewday
477 return b
480 def get_start_and_end_time(self, ref=None):
481 now = time.localtime(ref)
483 if self.syear == 0:
484 self.syear = now.tm_year
485 month_id = Daterange.get_month_id(self.smon)
486 day_start = find_day_by_weekday_offset(self.syear, self.smon, self.swday, self.swday_offset)
487 start_time = get_start_of_day(self.syear, month_id, day_start)
489 if self.eyear == 0:
490 self.eyear = now.tm_year
491 month_end_id = Daterange.get_month_id(self.emon)
492 day_end = find_day_by_weekday_offset(self.eyear, self.emon, self.ewday, self.ewday_offset)
493 end_time = get_end_of_day(self.eyear, month_end_id, day_end)
495 now_epoch = time.mktime(now)
496 if start_time > end_time: #the period is between years
497 if now_epoch > end_time:#check for next year
498 day_end = find_day_by_weekday_offset(self.eyear + 1, self.emon, self.ewday, self.ewday_offset)
499 end_time = get_end_of_day(self.eyear + 1, month_end_id, day_end)
500 else:#it s just that start was the last year
501 day_start = find_day_by_weekday_offset(self.syear - 1, self.smon, self.swday, self.swday_offset)
502 start_time = get_start_of_day(self.syear - 1, month_id, day_start)
503 else:
504 if now_epoch > end_time:#just have to check for next year if necessery
505 day_start = find_day_by_weekday_offset(self.syear + 1, self.smon, self.swday, self.swday_offset)
506 start_time = get_start_of_day(self.syear + 1, month_id, day_start)
507 day_end = find_day_by_weekday_offset(self.eyear + 1, self.emon, self.ewday, self.ewday_offset)
508 end_time = get_end_of_day(self.eyear + 1, month_end_id, day_end)
510 return (start_time, end_time)
514 class MonthDateDaterange(Daterange):
515 def get_start_and_end_time(self, ref=None):
516 now = time.localtime(ref)
517 if self.syear == 0:
518 self.syear = now.tm_year
519 month_start_id = Daterange.get_month_id(self.smon)
520 day_start = find_day_by_offset(self.syear, self.smon, self.smday)
521 start_time = get_start_of_day(self.syear, month_start_id, day_start)
523 if self.eyear == 0:
524 self.eyear = now.tm_year
525 month_end_id = Daterange.get_month_id(self.emon)
526 day_end = find_day_by_offset(self.eyear, self.emon, self.emday)
527 end_time = get_end_of_day(self.eyear, month_end_id, day_end)
529 now_epoch = time.mktime(now)
530 if start_time > end_time: #the period is between years
531 if now_epoch > end_time:#check for next year
532 day_end = find_day_by_offset(self.eyear + 1, self.emon, self.emday)
533 end_time = get_end_of_day(self.eyear + 1, month_end_id, day_end)
534 else:#it s just that start was the last year
535 day_start = find_day_by_offset(self.syear-1, self.smon, self.emday)
536 start_time = get_start_of_day(self.syear-1, month_start_id, day_start)
537 else:
538 if now_epoch > end_time:#just have to check for next year if necessery
539 day_start = find_day_by_offset(self.syear+1, self.smon, self.emday)
540 start_time = get_start_of_day(self.syear+1, month_start_id, day_start)
541 day_end = find_day_by_offset(self.eyear+1, self.emon, self.emday)
542 end_time = get_end_of_day(self.eyear+1, month_end_id, day_end)
544 return (start_time, end_time)
547 class WeekDayDaterange(Daterange):
548 def get_start_and_end_time(self, ref=None):
549 now = time.localtime(ref)
551 #If no year, it's our year
552 if self.syear == 0:
553 self.syear = now.tm_year
554 month_start_id = now.tm_mon
555 month_start = Daterange.get_month_by_id(month_start_id)
556 day_start = find_day_by_weekday_offset(self.syear, month_start, self.swday, self.swday_offset)
557 start_time = get_start_of_day(self.syear, month_start_id, day_start)
559 #Same for end year
560 if self.eyear == 0:
561 self.eyear = now.tm_year
562 month_end_id = now.tm_mon
563 month_end = Daterange.get_month_by_id(month_end_id)
564 day_end = find_day_by_weekday_offset(self.eyear, month_end, self.ewday, self.ewday_offset)
565 end_time = get_end_of_day(self.eyear, month_end_id, day_end)
567 #Maybe end_time is before start. So look for the
568 #next month
569 if start_time > end_time:
570 month_end_id = month_end_id + 1
571 if month_end_id > 12:
572 month_end_id = 1
573 self.eyear += 1
574 month_end = Daterange.get_month_by_id(month_end_id)
575 day_end = find_day_by_weekday_offset(self.eyear, month_end, self.ewday, self.ewday_offset)
576 end_time = get_end_of_day(self.eyear, month_end_id, day_end)
578 now_epoch = time.mktime(now)
579 #But maybe we look not ethouth far. We should add a month
580 if end_time < now_epoch:
581 month_end_id = month_end_id + 1
582 month_start_id = month_start_id + 1
583 if month_end_id > 12:
584 month_end_id = 1
585 self.eyear += 1
586 if month_start_id > 12:
587 month_start_id = 1
588 self.syear += 1
589 #First start
590 month_start = Daterange.get_month_by_id(month_start_id)
591 day_start = find_day_by_weekday_offset(self.syear, month_start, self.swday, self.swday_offset)
592 start_time = get_start_of_day(self.syear, month_start_id, day_start)
593 #Then end
594 month_end = Daterange.get_month_by_id(month_end_id)
595 day_end = find_day_by_weekday_offset(self.eyear, month_end, self.ewday, self.ewday_offset)
596 end_time = get_end_of_day(self.eyear, month_end_id, day_end)
598 return (start_time, end_time)
601 class MonthDayDaterange(Daterange):
602 def get_start_and_end_time(self, ref=None):
603 now = time.localtime(ref)
604 if self.syear == 0:
605 self.syear = now.tm_year
606 month_start_id = now.tm_mon
607 month_start = Daterange.get_month_by_id(month_start_id)
608 day_start = find_day_by_offset(self.syear, month_start, self.smday)
609 start_time = get_start_of_day(self.syear, month_start_id, day_start)
611 if self.eyear == 0:
612 self.eyear = now.tm_year
613 month_end_id = now.tm_mon
614 month_end = Daterange.get_month_by_id(month_end_id)
615 day_end = find_day_by_offset(self.eyear, month_end, self.emday)
616 end_time = get_end_of_day(self.eyear, month_end_id, day_end)
618 now_epoch = time.mktime(now)
620 if start_time > end_time:
621 month_end_id = month_end_id + 1
622 if month_end_id > 12:
623 month_end_id = 1
624 self.eyear += 1
625 day_end = find_day_by_offset(self.eyear, month_end, self.emday)
626 end_time = get_end_of_day(self.eyear, month_end_id, day_end)
628 if end_time < now_epoch:
629 month_end_id = month_end_id + 1
630 month_start_id = month_start_id + 1
631 if month_end_id > 12:
632 month_end_id = 1
633 self.eyear += 1
634 if month_start_id > 12:
635 month_start_id = 1
636 self.syear += 1
638 #For the start
639 month_start = Daterange.get_month_by_id(month_start_id)
640 day_start = find_day_by_offset(self.syear, month_start, self.smday)
641 start_time = get_start_of_day(self.syear, month_start_id, day_start)
643 #For the end
644 month_end = Daterange.get_month_by_id(month_end_id)
645 day_end = find_day_by_offset(self.eyear, month_end, self.emday)
646 end_time = get_end_of_day(self.eyear, month_end_id, day_end)
648 return (start_time, end_time)