3 * Big, fat TODO: Almost every method asks the DB for all data and returns it.
4 * Instead, users should subscribe what they're interested in, and be fed that
5 * data once the query runs, once.
7 class Summary_Reports_Model
extends Reports_Model
9 # alert summary options
10 private $summary_result = array();
13 * Used from the HTTP API
15 * @param $auth Op5Auth
18 function get_events($auth)
20 $querym = new Report_query_builder_Model($this->db_table
, $this->options
);
21 $query = $querym->build_alert_summary_query
22 ('timestamp, event_type, host_name, service_description, ' .
23 'state, hard, retry, downtime_depth, output');
25 // investigate if there are more rows available for this query,
26 // with another set of pagination parameters
27 $limit = $this->options
['limit'] +
1;
28 $offset = $this->options
['offset'];
30 if($this->options
['include_comments']) {
36 data.service_description,
43 comments.user_comment,
44 comments.comment_timestamp
47 ninja_report_comments comments
48 ON data.timestamp = comments.timestamp
49 AND data.host_name = comments.host_name
50 AND data.service_description = comments.service_description
51 AND data.event_type = comments.event_type";
53 $query .= " LIMIT ".$limit." OFFSET ". $offset;
55 $events = $this->db
->query($query)->result(false);
56 $can_paginate = false;
57 if(count($events) > $this->options
['limit']) {
62 'can_paginate' => $can_paginate,
63 'events' => $events, // note that this is the size you asked for, plus one
64 'limit' => (int) $this->options
['limit'],
65 'offset' => (int) $this->options
['offset']
69 private function comparable_state($row)
71 return $row['state'] << 1 |
$row['hard'];
75 * Get alert summary for "top (hard) alert producers"
77 * @return Array in the form { rank => array() }
79 public function top_alert_producers()
81 $start = microtime(true);
82 $host_states = $this->options
['host_states'];
83 $service_states = $this->options
['service_states'];
84 $this->options
['host_states'] = self
::HOST_ALL
;
85 $this->options
['service_states'] = self
::SERVICE_ALL
;
86 $querym = new Report_query_builder_Model($this->db_table
, $this->options
);
87 $query = $querym->build_alert_summary_query();
88 $this->options
['host_states'] = $host_states;
89 $this->options
['service_states'] = $service_states;
91 $dbr = $this->db
->query($query);
92 if (!is_object($dbr)) {
95 $dbr = $dbr->result(false);
98 foreach ($dbr as $row) {
99 if (empty($row['service_description'])) {
100 $name = $row['host_name'];
101 $interesting_states = $host_states;
103 $name = $row['host_name'] . ';' . $row['service_description'];
104 $interesting_states = $service_states;
107 # only count true state-changes
108 $state = $this->comparable_state($row);
109 if (isset($pstate[$name]) && $pstate[$name] === $state) {
112 $pstate[$name] = $state;
114 # if we're not interested in this state, just move along
115 if (!(1 << $row['state'] & $interesting_states)) {
119 if (empty($result[$name])) {
126 # sort the result and return only the necessary items
128 if ($this->options
['summary_items'] > 0) {
129 $result = array_slice($result, 0, $this->options
['summary_items'], true);
133 $this->summary_result
= array();
134 foreach ($result as $obj => $alerts) {
136 if (strstr($obj, ';')) {
137 $obj_ary = explode(';', $obj);
138 $ary['host_name'] = $obj_ary[0];
139 $ary['service_description'] = $obj_ary[1];
140 $ary['event_type'] = self
::SERVICECHECK
;
142 $ary['host_name'] = $obj;
143 $ary['event_type'] = self
::HOSTCHECK
;
145 $ary['total_alerts'] = $alerts;
146 $this->summary_result
[$i++
] = $ary;
148 return $this->summary_result
;
151 private function set_alert_total_totals(&$result)
153 foreach ($result as $name => $ary) {
155 foreach ($ary as $type => $state_ary) {
156 if ($type === 'total')
158 $ary[$type . '_totals'] = array('soft' => 0, 'hard' => 0);
159 $ary[$type . '_total'] = 0;
160 foreach ($state_ary as $sh) {
161 $ary[$type . '_totals']['soft'] +
= $sh[0];
162 $ary[$type . '_totals']['hard'] +
= $sh[1];
163 $ary[$type . '_total'] +
= $sh[0] +
$sh[1];
164 $ary['total'] +
= $sh[0] +
$sh[1];
167 $result[$name] = $ary;
171 private function alert_totals_by_host($dbr)
173 $template = $this->summary_result
;
175 foreach ($this->options
['objects'] as $hn) {
176 $result[$hn] = $template;
179 foreach ($dbr as $row) {
180 if (empty($row['service_description'])) {
182 $sname = $row['host_name'];
185 $sname = $row['host_name'] . ';' . $row['service_description'];
188 # only count real state-changes
189 $state = $this->comparable_state($row);
190 if (isset($pstate[$sname]) && $pstate[$sname] === $state) {
193 $pstate[$sname] = $state;
195 $name = $row['host_name'];
196 $result[$name][$type][$row['state']][$row['hard']]++
;
202 private function alert_totals_by_service($dbr)
204 $template = $this->summary_result
;
206 foreach ($this->options
['objects'] as $name) {
207 list($host, $svc) = explode(';', $name);
208 # Assign host first, so it's position in the array is before services
209 $result[$host] = $template;
210 $result[$name] = $template;
213 foreach ($dbr as $row) {
214 if (!$row['service_description']) {
215 $name = $row['host_name'];
219 $name = $row['host_name'] . ';' . $row['service_description'];
222 $state = $this->comparable_state($row);
223 if (isset($pstate[$name]) && $pstate[$name] === $state) {
226 $pstate[$name] = $state;
227 $result[$name][$type][$row['state']][$row['hard']]++
;
234 private function alert_totals_by_hostgroup($dbr, $host_hostgroup)
236 # pre-load the result set to keep conditionals away
237 # from the inner loop
238 $template = $this->summary_result
;
240 foreach ($this->options
['objects'] as $hostgroup) {
241 $result[$hostgroup] = $template;
245 foreach ($dbr as $row) {
246 if (empty($row['service_description'])) {
248 $name = $row['host_name'];
251 $name = $row['host_name'] . ';' . $row['service_description'];
253 $state = $this->comparable_state($row);
254 if (isset($pstate[$name]) && $pstate[$name] === $state) {
257 $pstate[$name] = $state;
258 $hostgroups = $host_hostgroup[$row['host_name']];
259 foreach ($hostgroups as $hostgroup) {
260 $result[$hostgroup][$type][$row['state']][$row['hard']]++
;
267 private function alert_totals_by_servicegroup($dbr, $service_servicegroup)
269 # pre-load the result set to keep conditionals away
270 # from the inner loop
271 $template = $this->summary_result
;
273 foreach ($this->options
['objects'] as $servicegroup) {
274 $result[$servicegroup] = $template;
278 foreach ($dbr as $row) {
279 if (empty($row['service_description'])) {
281 $name = $row['host_name'];
284 $name = $row['host_name'] . ';' . $row['service_description'];
286 $state = $this->comparable_state($row);
287 if (isset($pstate[$name]) && $pstate[$name] === $state) {
290 $pstate[$name] = $state;
292 $servicegroups = $service_servicegroup[$type][$name];
293 foreach ($servicegroups as $sg) {
294 $result[$sg][$type][$row['state']][$row['hard']]++
;
301 * Get alert totals. This is identical to the toplist in
302 * many respects, but the result array is different.
304 * @return Array of counts divided by object types and states
306 public function alert_totals()
308 $querym = new Report_query_builder_Model($this->db_table
, $this->options
);
309 $query = $querym->build_alert_summary_query();
311 $dbr = $this->db
->query($query)->result(false);
312 if (!is_object($dbr)) {
313 echo Kohana
::debug($this->db
->errorinfo(), explode("\n", $query));
316 # preparing the result array in advance speeds up the
317 # parsing somewhat. Completing it either way makes it
318 # easier to write templates for it as well.
319 # We stash it in $this->summary_result so all functions
320 # can take advantage of it
321 for ($state = 0; $state < 4; $state++
) {
322 $this->summary_result
['host'][$state] = array(0, 0);
323 $this->summary_result
['service'][$state] = array(0, 0);
325 unset($this->summary_result
['host'][3]);
328 # groups must be first here, since the other variables
329 # are expanded in the build_alert_summary_query() method
330 switch ($this->options
['report_type']) {
331 case 'servicegroups':
332 $result = $this->alert_totals_by_servicegroup($dbr, $querym->service_servicegroup
);
335 $result = $this->alert_totals_by_hostgroup($dbr, $querym->host_hostgroup
);
338 $result = $this->alert_totals_by_service($dbr);
341 $result = $this->alert_totals_by_host($dbr);
345 $this->set_alert_total_totals($result);
346 $this->summary_result
= $result;
347 return $this->summary_result
;
351 * Find and return the latest $this->options['summary_items'] alert
352 * producers according to the search criteria.
354 public function recent_alerts()
356 $querym = new Report_query_builder_Model($this->db_table
, $this->options
);
357 $query = $querym->build_alert_summary_query('*');
359 $query .= ' ORDER BY timestamp '.(isset($this->options
['oldest_first']) && $this->options
['oldest_first']?
'ASC':'DESC');
360 if ($this->options
['summary_items'] > 0) {
361 $query .= " LIMIT " . $this->options
['summary_items'];
362 if (isset($this->options
['page']) && $this->options
['page'])
363 $query .= ' OFFSET ' . ($this->options
['summary_items'] * ($this->options
['page'] - 1));
370 comments.user_comment
371 FROM ('.$query.') data
373 ninja_report_comments comments
374 ON data.timestamp = comments.timestamp
375 AND data.host_name = comments.host_name
376 AND data.service_description = comments.service_description
377 AND data.event_type = comments.event_type';
379 $dbr = $this->db
->query($query)->result(false);
380 if (!is_object($dbr)) {
381 echo Kohana
::debug($this->db
->errorinfo(), explode("\n", $query));
384 $this->summary_result
= array();
385 foreach ($dbr as $row) {
386 if ($this->timeperiod
->inside($row['timestamp']))
387 $this->summary_result
[] = $row;
390 return $this->summary_result
;
394 * Add a new comment to the event pointed to by the timestamp/event_type/host_name/service
396 public static function add_event_comment($timestamp, $event_type, $host_name, $service, $comment, $username) {
397 $db = Database
::instance();
398 $db->query('DELETE FROM ninja_report_comments WHERE timestamp='.$db->escape($timestamp).' AND event_type = '.$db->escape($event_type).' AND host_name = '.$db->escape($host_name).' AND service_description = '.$db->escape($service));
399 $db->query('INSERT INTO ninja_report_comments(timestamp, event_type, host_name, service_description, comment_timestamp, username, user_comment) VALUES ('.$db->escape($timestamp).', '.$db->escape($event_type).', '.$db->escape($host_name).', '.$db->escape($service).', UNIX_TIMESTAMP(), '.$db->escape($username).', '.$db->escape($comment).')');
403 * Fetch alert history for histogram report
404 * @param $slots array with slots to fill with data
405 * @return array with keys: min, max, avg, data
407 public function histogram($slots=false)
409 if (empty($slots) ||
!is_array($slots))
412 $breakdown = $this->options
['breakdown'];
413 $report_type = $this->options
['report_type'];
414 $newstatesonly = $this->options
['newstatesonly'];
416 # compute what event counters we need depending on report type
418 switch ($report_type) {
419 case 'hosts': case 'hostgroups':
420 if (!$this->options
['host_states'] ||
$this->options
['host_states'] == self
::HOST_ALL
) {
421 $events = array(0 => 0, 1 => 0, 2 => 0);
424 for ($i = 0; $i <= 2; $i++
) {
425 if (1 << $i & $this->options
['host_states']) {
430 $this->options
['alert_types'] = 1;
432 case 'services': case 'servicegroups':
433 if (!$this->options
['service_states'] ||
$this->options
['service_states'] == self
::SERVICE_ALL
) {
434 $events = array(0 => 0, 1 => 0, 2 => 0, 3 => 0);
437 for ($i = 0; $i <= 3; $i++
) {
438 if (1 << $i & $this->options
['service_states']) {
443 $this->options
['alert_types'] = 2;
447 # add event (state) counters to slots
449 foreach ($slots as $s => $l) {
453 # fields to fetch from db
454 $fields = 'timestamp, event_type, host_name, service_description, state, hard, retry';
455 $querym = new Report_query_builder_Model($this->db_table
, $this->options
);
456 $query = $querym->build_alert_summary_query($fields);
458 # tell histogram_data() how to treat timestamp
460 switch ($breakdown) {
475 $res = $this->db
->query($query)->result(false);
480 foreach ($res as $row) {
481 if ($newstatesonly) {
482 if ($row['state'] != $last_state) {
483 # only count this state if it differs from the last
484 $data[date($date_str, $row['timestamp'])][$row['state']]++
;
487 $data[date($date_str, $row['timestamp'])][$row['state']]++
;
489 $last_state = $row['state'];
499 foreach ($data as $slot => $slotstates) {
500 foreach ($slotstates as $id => $val) {
501 if ($val > $max[$id]) $max[$id] = $val;
502 if ($val < $min[$id]) $min[$id] = $val;
506 foreach ($max as $v => $k) {
508 $avg[$v] = number_format(($k/count($data)), 2);
511 return array('min' => $min, 'max' => $max, 'avg' => $avg, 'sum' => $sum, 'data' => $data);