Refactoring: Moved check parameters from unsorted.py to dedicated modules (CMK-1393)
[check_mk.git] / cmk / utils / schedule.py
blob46d7cbd4c5798e38b7adf98077e12ba6c2e65889
1 #!/usr/bin/python
2 # -*- encoding: utf-8; py-indent-offset: 4 -*-
3 # +------------------------------------------------------------------+
4 # | ____ _ _ __ __ _ __ |
5 # | / ___| |__ ___ ___| | __ | \/ | |/ / |
6 # | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
7 # | | |___| | | | __/ (__| < | | | | . \ |
8 # | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
9 # | |
10 # | Copyright Mathias Kettner 2016 mk@mathias-kettner.de |
11 # +------------------------------------------------------------------+
13 # This file is part of Check_MK.
14 # The official homepage is at http://mathias-kettner.de/check_mk.
16 # check_mk is free software; you can redistribute it and/or modify it
17 # under the terms of the GNU General Public License as published by
18 # the Free Software Foundation in version 2. check_mk is distributed
19 # in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
20 # out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
21 # PARTICULAR PURPOSE. See the GNU General Public License for more de-
22 # tails. You should have received a copy of the GNU General Public
23 # License along with GNU Make; see the file COPYING. If not, write
24 # to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
25 # Boston, MA 02110-1301 USA.
27 # Computes for a scheduling entry the last/next time that this entry
28 # should have run or will be run. Such a scheduling entry is specified
29 # by a period specification as produced by the SchedulePeriod() valuespec
30 # and a timeofday specification which is a two element tuple of hours and minutes
32 import abc
33 import datetime
34 import time
36 from dateutil.relativedelta import relativedelta
37 from dateutil.rrule import rrule, DAILY, WEEKLY, MONTHLY
40 class Schedule(object):
41 """
42 Abstract base class for schedules. A default implementation
43 for the last and next event at a given datetime are provided.
44 Subclasses have to define the class attribute _delta and the
45 instance attribute _rule.
46 """
47 __metaclass__ = abc.ABCMeta
49 @abc.abstractproperty
50 def rule(self):
51 pass
53 @abc.abstractproperty
54 def delta(self):
55 pass
57 def next(self, t):
58 return self.rule.replace(dtstart=t).after(t)
60 def last(self, t):
61 from_ = t + relativedelta(**self.delta)
62 return self.rule.replace(dtstart=from_, until=t).before(t)
65 class DaySchedule(Schedule):
66 """
67 A daily schedule.
68 """
70 def __init__(self, timeofday):
71 self._rule = rrule(DAILY, byhour=timeofday.hour, byminute=timeofday.minute, bysecond=0)
73 @property
74 def rule(self):
75 return self._rule
77 @property
78 def delta(self):
79 return {"days": -1}
82 class WeekSchedule(Schedule):
83 """
84 A weekly schedule.
85 """
87 def __init__(self, weekday, timeofday):
88 if not 0 <= weekday <= 6:
89 raise ValueError('weekday must be between 0 and 6')
90 self._rule = rrule(
91 WEEKLY, byweekday=weekday, byhour=timeofday.hour, byminute=timeofday.minute, bysecond=0)
93 @property
94 def rule(self):
95 return self._rule
97 @property
98 def delta(self):
99 return {"weeks": -1}
102 class StartMonthSchedule(Schedule):
104 A monthly schedule initialized relatively to the first day of the month.
107 def __init__(self, day, timeofday):
108 if not 1 <= day <= 31:
109 raise ValueError('day must be between 1 and 31')
110 self._rule = rrule(
111 MONTHLY, bymonthday=day, byhour=timeofday.hour, byminute=timeofday.minute, bysecond=0)
113 @property
114 def rule(self):
115 return self._rule
117 @property
118 def delta(self):
119 return {"months": -2}
122 class EndMonthSchedule(Schedule):
124 A monthly schedule initialized relatively to the last day of the month.
127 def __init__(self, days_from_end, timeofday):
128 if not 1 <= days_from_end <= 31:
129 raise ValueError('days_from_end must be between 1 and 31')
130 day = -days_from_end
131 self._rule = rrule(
132 MONTHLY, bymonthday=day, byhour=timeofday.hour, byminute=timeofday.minute, bysecond=0)
134 @property
135 def rule(self):
136 return self._rule
138 @property
139 def delta(self):
140 return {"months": -2}
143 def _get_schedule(period, timeofday):
145 Returns a schedule instance for a given period and timeofday.
147 t = datetime.time(*timeofday)
149 if period == "day":
150 schedule = DaySchedule(t)
151 elif period[0] == "week":
152 weekday = period[1]
153 schedule = WeekSchedule(weekday, t)
154 elif period[0] == "month_begin":
155 day = period[1]
156 schedule = StartMonthSchedule(day, t)
157 elif period[0] == "month_end":
158 days_from_end = period[1]
159 schedule = EndMonthSchedule(days_from_end, t)
160 else:
161 raise ValueError('Unknown period')
163 return schedule
166 def last_scheduled_time(period, timeofday, dt=None):
167 if dt is None:
168 dt = datetime.datetime.today()
169 schedule = _get_schedule(period, timeofday)
170 return time.mktime(schedule.last(dt).timetuple())
173 def next_scheduled_time(period, timeofday, dt=None):
174 if dt is None:
175 dt = datetime.datetime.today()
176 schedule = _get_schedule(period, timeofday)
177 return time.mktime(schedule.next(dt).timetuple())