sla: Stupid Rasmus and his needles and haystacks
[ninja.git] / application / models / scheduledate.php
blobc7beb48f5c6fbe7b202fcb49afafc358d3fa3e6c
1 <?php defined('SYSPATH') OR die('No direct access allowed.');
3 /**
4 * Schedule downtime
5 */
7 class ScheduleDate_Model extends Model
9 /**
10 * Fields that a schedule include. These are all valid, and all required.
11 * Mostly public for test reasons.
13 static public $valid_fields = array(
14 'author',
15 'downtime_type',
16 'objects',
17 'comment',
18 'start_time',
19 'end_time',
20 'duration',
21 'fixed',
22 'weekdays',
23 'months'
26 /**
27 * A list of valid schedule types - same format (no underscore, trailing s)
28 * as in report options.
29 * Mostly public for test reasons.
31 static public $valid_types = array(
32 'hosts',
33 'services',
34 'hostgroups',
35 'servicegroups'
38 /**
39 * Use a reasonable amount of indicators to determine whether there's
40 * already a matching downtime. This prevents downtimes from being
41 * scheduled more than once.
43 static protected function check_if_scheduled($type, $name, $start_time, $end_time, $is_fixed)
45 $ls = Livestatus::instance();
46 switch ($type) {
47 case 'hosts':
48 $res = $ls->getDowntimes(array('filter' => array('is_service' => 0, 'host_name' => $name, 'start_time' => $start_time, 'fixed' => $is_fixed, 'end_time' => $end_time)));
49 break;
50 case 'services':
51 if (!strstr($name, ';'))
52 return false;
54 $parts = explode(';', $name);
55 $host = $parts[0];
56 $service = $parts[1];
57 $res = $ls->getDowntimes(array('filter' => array('is_service' => 1, 'host_name' => $host, 'service_description' => $service, 'start_time' => $start_time, 'fixed' => $is_fixed, 'end_time' => $end_time)));
58 break;
59 case 'hostgroups':
60 $hosts = $ls->getHosts(array('filter' => array('groups' => array('>=' => $name))));
61 $in_dtime = $ls->getDowntimes(array('filter' => array('is_service' => 0, 'host_groups' => array('>=' => $name), 'start_time' => $start_time, 'fixed' => $is_fixed, 'end_time' => $end_time)));
62 return (count($hosts) <= count($in_dtime));
63 break;
65 case 'servicegroups':
66 $services = $ls->getServices(array('filter' => array('groups' => array('>=' => $name))));
67 $in_dtime = $ls->getDowntimes(array('filter' => array('is_service' => 1, 'service_groups' => array('>=' => $name), 'start_time' => $start_time, 'fixed' => $is_fixed, 'end_time' => $end_time)));
68 return (count($services) <= count($in_dtime));
69 break;
72 return (!empty($res));
75 /**
76 * Schedule a recurring downtime if tomorrow matches any saved schedules
77 * @param $timestamp int
78 * @return boolean
80 static public function schedule_downtime($timestamp=false) {
81 $schedules = RecurringDowntimePool_Model::all();
82 $result = array();
84 if ($timestamp === false)
85 $timestamp = time();
87 // Set timestamp to the following day.
88 $timestamp = strtotime('+1 day', $timestamp);
90 $tomorrow = array();
91 // Gather everything we need to know about tomorrow
92 $tomorrow['year'] = date('Y', $timestamp);
93 $tomorrow['month'] = date('n', $timestamp);
94 $tomorrow['day'] = date('d', $timestamp);
95 $tomorrow['weekday'] = date('w', $timestamp);
97 foreach ($schedules->it(array('weekdays', 'author', 'months', 'downtime_type', 'start_time', 'end_time', 'duration', 'objects', 'fixed', 'comment')) as $data) {
98 if (!in_array($tomorrow['weekday'], $data->get_weekdays()) || !in_array($tomorrow['month'], $data->get_months()))
99 continue;
101 $nagios_cmd = self::determine_downtimetype($data->get_downtime_type());
103 $start_time = mktime(0, 0, $data->get_start_time(), $tomorrow['month'], $tomorrow['day'], $tomorrow['year']);
104 $end_time = mktime(0, 0, $data->get_end_time(), $tomorrow['month'], $tomorrow['day'], $tomorrow['year']);
105 if ($end_time < $start_time)
106 $end_time = mktime(0, 0, $data->get_end_time(), $tomorrow['month'], $tomorrow['day'] + 1, $tomorrow['year']);
107 $duration = $data->get_duration();
108 foreach ($data->get_objects() as $obj) {
109 # check if object already scheduled for same start time and duration?
110 if (static::check_if_scheduled($data->get_downtime_type(), $obj, $start_time, $end_time, $data->get_fixed())) {
111 fwrite(STDERR, "skipping $obj\n");
112 continue;
114 $tmp_cmd = "$nagios_cmd;$obj;$start_time;$end_time;{$data->get_fixed()};0;$duration;{$data->get_author()};AUTO: {$data->get_comment()}";
115 $result[] = nagioscmd::submit_to_nagios($tmp_cmd);
117 return !in_array(false, $result);
122 * Schedule a downtime by submitting it to nagios
124 * @param $objects array
125 * @param $object_type string
126 * @param $start_time string
127 * @param $end_time string
128 * @param $fixed string
129 * @param $duration string
130 * @param $comment string
131 * @return boolean
133 public static function insert_downtimes($objects, $object_type, $start_time, $end_time, $fixed, $duration, $comment)
135 $result = array();
136 $nagios_cmd = self::determine_downtimetype($object_type);
137 $author = Auth::instance()->get_user()->username;
138 $month = date('n');
139 $day = date('d');
140 $year = date('Y');
141 $start_time = mktime(0, 0, self::time_to_seconds($start_time), $month, $day, $year);
142 $end_time = mktime(0, 0, self::time_to_seconds($end_time), $month, $day, $year);
143 foreach ($objects as $object) {
144 if (static::check_if_scheduled($object_type, $object, $start_time, $end_time, $fixed)) {
145 // Skip object if it is already scheduled for downtime
146 continue;
148 $tmp_cmd = "$nagios_cmd;$object;$start_time;$end_time;$fixed;0;$duration;$author;AUTO: $comment";
149 $result[] = nagioscmd::submit_to_nagios($tmp_cmd);
151 return !in_array(false, $result);
155 * Given a time-like string (hh[:mm[:ss]]),
156 * return the number of seconds involved.
158 static public function time_to_seconds($time)
160 $seconds = 0;
161 $parts = explode(':', $time);
162 if (isset($parts[0]))
163 $seconds += $parts[0] * 3600;
164 if (isset($parts[1]))
165 $seconds += $parts[1] * 60;
166 if (isset($parts[2]))
167 $seconds += $parts[2];
168 return $seconds;
172 * Returns appropriate nagios command
173 * @param $report_type string
174 * @return string
176 static protected function determine_downtimetype($report_type=false)
178 if (empty($report_type)) {
179 return false;
181 $downtime_commands = array(
182 'hosts' => 'SCHEDULE_HOST_DOWNTIME',
183 'services' => 'SCHEDULE_SVC_DOWNTIME',
184 'hostgroups' => 'SCHEDULE_HOSTGROUP_HOST_DOWNTIME',
185 'servicegroups' => 'SCHEDULE_SERVICEGROUP_SVC_DOWNTIME'
186 ); # will schedule downtime for all services - not their hosts!
187 return $downtime_commands[$report_type];
191 * Save/update a recurring schedule
192 * @param $data array
193 * @param $id int
194 * @return bool
196 public function edit_schedule($data, &$id=false)
198 if (!is_array($data)) {
199 return false;
202 foreach (static::$valid_fields as $field) {
203 if (!isset($data[$field]))
204 return false;
207 $db = Database::instance();
209 $downtime_type = $data['downtime_type'];
210 if (!in_array($downtime_type, static::$valid_types)) {
211 return false;
213 $type = substr($data['downtime_type'], 0, -1);
214 if (!Auth::instance()->authorized_for($type.'_edit_contact') && !Auth::instance()->authorized_for($type.'_edit_all'))
215 return false;
217 $start_time = static::time_to_seconds($data['start_time']);
218 $end_time = static::time_to_seconds($data['end_time']);
219 $duration = static::time_to_seconds($data['duration']);
221 if ((int)$id) {
222 $set = RecurringDowntimePool_Model::get_by_query('[recurring_downtimes] id = '.(int)$id);
223 if (!count($set))
224 return false;
225 $db->query("DELETE FROM recurring_downtime_objects WHERE recurring_downtime_id = ".(int)$id);
226 # update schedule
227 $sql = "UPDATE recurring_downtime SET author = %s," .
228 " downtime_type = %s, last_update = %s, comment = %s," .
229 " start_time = %s, end_time = %s, duration = %s, fixed = %s," .
230 " weekdays = %s, months = %s WHERE id = ".(int)$id;
231 } else {
232 # new schedule
233 $sql = "INSERT INTO recurring_downtime (author, downtime_type," .
234 " last_update, comment, start_time, end_time, duration," .
235 " fixed, weekdays, months) VALUES (%s, %s, %s, %s, %s, %s," .
236 " %s, %s, %s, %s)";
239 $res = $db->query(sprintf($sql, $db->escape($data['author']),
240 $db->escape($data['downtime_type']), $db->escape(time()),
241 $db->escape($data['comment']), $db->escape($start_time),
242 $db->escape($end_time), $db->escape($duration),
243 $db->escape($data['fixed']),
244 $db->escape(serialize($data['weekdays'])),
245 $db->escape(serialize($data['months']))));
246 if (!$id)
247 $id = $res->insert_id();
248 foreach ($data['objects'] as $object) {
249 $db->query("INSERT INTO recurring_downtime_objects" .
250 " (recurring_downtime_id, object_name) VALUES (" .
251 (int)$id.", ".$db->escape($object).")");
253 return true;
257 * Delete a scheduled recurring downtime
259 * @param $id ID of the downtime to delete
260 * @returns true on success, false otherwise
262 public function delete_schedule($id)
264 $set = RecurringDowntimePool_Model::get_by_query('[recurring_downtimes] id = '.(int)$id);
265 if (!count($set))
266 return false;
268 $obj = $set->it(array('downtime_type'))->current();
269 $type = substr($obj->get_downtime_type(), 0, -1);
270 // *_add_delete is for the objects, and because this manipulates the
271 // state of an existing object, *_add_delete is not required. OK?
272 if (!Auth::instance()->authorized_for($type.'_edit_contact') && !Auth::instance()->authorized_for($type.'_edit_all'))
273 return false;
275 $db = Database::instance();
277 $sql = "DELETE FROM recurring_downtime WHERE id=".(int)$id;
278 if (!$db->query($sql)) {
279 return false;
281 $sql = "DELETE FROM recurring_downtime_objects WHERE recurring_downtime_id=".(int)$id;
282 if (!$db->query($sql)) {
283 return false;
285 return true;