4 * Responsible for fetching data for avail and SLA reports. This class
5 * must be instantiated to work properly.
7 * ## State interaction in subreports
8 * Given two objects, assuming only two states per object type, would interact
9 * such that the non-OK state overrules the OK state completely as such:
12 * | scheduled | unscheduled | scheduled | unscheduled
13 * ------------++++++++++++++++++++++++++++++++++++++++++++++++++++++
14 * scheduled + sched up | unsched up | sched down | unsched down
15 * UP ------------+------------+-------------+------------+-------------
16 * unscheduled+ unsched up | unsched up | sched down | unsched down
17 * host ----------------+------------+-------------+------------+-------------
18 * scheduled + sched down | sched down | sched down | unsched down
19 * DOWN------------+------------+-------------+------------+-------------
20 * unscheduled+unsched down| unsched down|unsched down| unsched down
22 * When two sub-objects have different non-OK states, the outcome depends on
23 * whether scheduleddowntimeasuptime is used or not. If the option is used,
24 * then the service with the worst non-scheduled state is used. If the option
25 * is not used, the worst state is used, prioritizing any non-scheduled state.
27 * This applies to non-"cluster mode" reports. If you're in cluster mode, this
28 * applies backwards exactly.
30 class Status_Reports_Model
extends Reports_Model
32 protected $st_is_service = false; /**< Whether the objects in this report are services */
33 protected $st_source = false; /**< Array of (non-group) objects that are part of this report */
34 protected $calculator = false; /**< The top-level calculator that represents this report */
38 * @param $options An instance of Report_options
39 * @param $db_table Database name
41 public function __construct(Report_options
$options, $db_table='report_data')
43 $this->db_table
= $db_table;
44 parent
::__construct($options);
48 * Get log details for host/service
50 * @return PDO result object on success. FALSE on error.
52 public function uptime_query()
54 $event_type = Reports_Model
::HOSTCHECK
;
55 if ($this->st_is_service
) {
56 $event_type = Reports_Model
::SERVICECHECK
;
59 # this query works out automatically, as we definitely don't
60 # want to get all state entries for a hosts services when
61 # we're only asking for uptime of the host
62 $sql = "SELECT host_name, service_description, " .
63 "state,timestamp AS the_time, hard, event_type";
64 # output is a TEXT field, so it needs an extra disk
65 # lookup to fetch and we don't always need it
66 if ($this->options
['include_trends'])
69 $sql .= " FROM ".$this->db_table
." ";
71 $time_first = 'timestamp >='.$this->options
['start_time'];
72 $time_last = 'timestamp <='.$this->options
['end_time'];
76 $downtime = 'event_type=' . Reports_Model
::DOWNTIME_START
. ' OR event_type=' . Reports_Model
::DOWNTIME_STOP
;
77 $softorhardcheck = 'event_type=' . $event_type;
79 if (!$this->options
['assumestatesduringnotrunning'])
80 $process = 'event_type < 200';
82 if (!$this->options
['includesoftstates']) {
83 $softorhardcheck .= ' AND hard=1';
86 if ($this->st_is_service
) {
88 $servicename = array();
89 foreach ($this->st_source
as $hst_srv) {
90 $ary = explode(';', $hst_srv, 2);
91 $hostname[] = $this->db
->escape($ary[0]);
92 $servicename[] = $this->db
->escape($ary[1]);
94 $purehost = "host_name IN (".join(", ", $hostname) . ") AND (service_description = '' OR service_description IS NULL)";
96 if (count($hostname) == 1) {
97 $hostname = array_pop($hostname);
98 $objsel = "host_name = $hostname AND service_description IN (".join(", ", $servicename) . ")";
100 foreach ($hostname as $i => $host) {
101 $svc = $servicename[$i];
102 $objsel[] = "host_name = $host AND service_description = $svc";
104 $objsel = '('.implode(') OR (', $objsel).')';
107 $sql_where = sql
::combine('and',
120 $softorhardcheck)))));
122 $objsel = "host_name IN ('" . join("', '", $this->st_source
) . "') AND (service_description = '' OR service_description IS NULL)";
124 $sql_where = sql
::combine('and',
133 $softorhardcheck))));
136 $sql .= 'WHERE ' .$sql_where . ' ORDER BY timestamp';
138 return $this->db
->query($sql)->result(false);
142 * Calculate uptime between two timestamps for host/service
143 * @return array or false on error
146 public function get_uptime()
148 $is_running = !$this->get_last_shutdown();
150 switch ($this->options
['report_type']) {
152 case 'servicegroups':
153 $this->st_is_service
= true;
156 $objects = $this->options
->get_report_members();
157 $this->st_source
= $objects;
159 $calculator_type = false;
160 switch ((int)$this->options
['sla_mode']) {
162 $calculator_type = 'WorstStateCalculator';
165 $calculator_type = 'AverageStateCalculator';
168 $calculator_type = 'BestStateCalculator';
171 die("Don't know how to do anything with this\n");
175 $this->calculator
= new $calculator_type($this->options
, $this->timeperiod
);
176 $optclass = get_class($this->options
);
180 $initial_states = $this->get_initial_states($this->st_is_service ?
'service' : 'host', $objects);
181 $downtimes = $this->get_initial_dt_depths($this->st_is_service ?
'service' : 'host', $objects);
182 foreach ($objects as $object) {
183 $opts = new $optclass($this->options
);
184 $opts['report_type'] = $this->st_is_service ?
'services' : 'hosts';
185 $opts['objects'] = array($object);
186 $sub = new SingleStateCalculator($opts, $this->timeperiod
);
187 if (isset( $initial_states[$object]))
188 $initial_state = $initial_states[$object];
190 $initial_state = Reports_Model
::STATE_PENDING
;
192 if (isset( $downtimes[$object]))
193 $initial_depth = $downtimes[$object];
197 if (!$initial_depth && $this->st_is_service
) { /* Is host scheduled? */
198 $srv = explode(';', $object);
199 if (isset($downtimes[$srv[0].';']) && $downtimes[$srv[0].';'])
202 $sub->initialize($initial_state, $initial_depth, $is_running);
203 $subs[$object] = $sub;
206 switch ($this->options
['report_type']) {
207 case 'servicegroups':
209 $groups = $this->options
['objects'];
212 foreach ($groups as $group) {
213 $opts = new $optclass($this->options
);
214 $opts['objects'] = array($group);
215 $members = $opts->get_report_members();
216 $these_subs = array();
217 foreach ($members as $member)
218 $these_subs[$member] = $all_subs[$member];
219 $this_sub = new $calculator_type($opts, $this->timeperiod
);
220 $this_sub->set_sub_reports($these_subs);
221 $this_sub->initialize(Reports_Model
::STATE_PENDING
, Reports_Model
::STATE_PENDING
, $is_running);
222 $subs[$group] = $this_sub;
227 $this_sub = new $calculator_type($this->options
, $this->timeperiod
);
228 $this_sub->set_sub_reports($subs);
229 $this_sub->initialize(Reports_Model
::STATE_PENDING
, Reports_Model
::STATE_PENDING
, $is_running);
230 $subs = array($this_sub);
234 $this->calculator
->set_sub_reports($subs);
235 $this->calculator
->initialize(Reports_Model
::STATE_PENDING
, Reports_Model
::STATE_PENDING
, $is_running);
237 $this->st_parse_all_rows();
238 $this->calculator
->finalize();
239 return $this->calculator
->get_data();
243 * Get latest (useful) process shutdown event
245 * @return Timestamp when of last shutdown event prior to $start_time
247 public function get_last_shutdown()
249 # If we're assuming states during program downtime,
250 # we don't really need to know when the last shutdown
251 # took place, as the initial state will be used regardless
252 # of whether or not Monitor was up and running.
253 if ($this->options
['assumestatesduringnotrunning']) {
257 $query = "SELECT timestamp, event_type FROM ".
259 " WHERE timestamp <".$this->options
['start_time'].
260 " ORDER BY timestamp DESC LIMIT 1";
261 $dbr = $this->db
->query($query)->result(false);
263 if (!$dbr ||
!($row = $dbr->current()))
266 $event_type = $row['event_type'];
267 if ($event_type==Reports_Model
::PROCESS_SHUTDOWN ||
$event_type==Reports_Model
::PROCESS_RESTART
)
268 $last_shutdown = $row['timestamp'];
272 return $last_shutdown;
277 * Runs the main query and loops through the results one by one
279 private function st_parse_all_rows()
281 $dbr = $this->uptime_query();
282 foreach ($dbr as $row) {
283 $this->calculator
->add_event($row);
288 * Fetch information about SCHEDULED_DOWNTIME status for multiple objects
290 * @return array of Depth of initial downtime.
292 protected function get_initial_dt_depths( $type = 'host', $names = array() )
294 $objectmatches = array();
295 if( $type == 'service' ) {
296 foreach( $names as $name ) {
297 list( $host, $srv ) = explode( ';', $name, 2 );
298 $objectmatches[] = '(host_name = '
299 . $this->db
->escape($host)
300 . ' AND (service_description = "" OR service_description IS NULL'
301 . ' OR service_description = '
302 . $this->db
->escape($srv)
306 foreach( $names as $name ) {
307 $objectmatches[] = '(host_name = '
308 . $this->db
->escape($name)
309 . ' AND (service_description = "" OR service_description IS NULL))';
313 $sql = "SELECT DISTINCT lsc.host_name as host_name, lsc.service_description as service_description, rd.event_type as event_type FROM (";
314 $sql .= "SELECT host_name, service_description, max( timestamp ) as timestamp FROM ".$this->db_table
;
315 $sql .= " WHERE (".implode(' OR ',$objectmatches).")";
316 $sql .= " AND (event_type = ".Reports_Model
::DOWNTIME_START
." OR event_type = ".Reports_Model
::DOWNTIME_STOP
.")";
317 $sql .= " AND timestamp < ".$this->options
['start_time'];
318 $sql .= " GROUP BY host_name,service_description";
320 $sql .= " LEFT JOIN ".$this->db_table
." AS rd";
321 $sql .= " ON lsc.host_name = rd.host_name";
322 $sql .= " AND lsc.service_description = rd.service_description";
323 $sql .= " AND lsc.timestamp = rd.timestamp";
324 $sql .= " AND (event_type = ".Reports_Model
::DOWNTIME_START
." OR event_type = ".Reports_Model
::DOWNTIME_STOP
.")";
326 $dbr = $this->db
->query($sql)->result(false);
328 $downtimes = array();
329 foreach( $dbr as $staterow ) {
330 $in_downtime = (int)($staterow['event_type'] == Reports_Model
::DOWNTIME_START
);
331 if ( $type == 'service' ) {
332 $downtimes[ $staterow['host_name'] . ';' . $staterow['service_description'] ] = $in_downtime;
334 $downtimes[ $staterow['host_name'] ] = $in_downtime;
342 * Get inital states of a set of objects
344 * @return array of initial states
346 protected function get_initial_states( $type = 'host', $names = array() )
348 $objectmatches = array();
349 if( $type == 'service' ) {
350 foreach( $names as $name ) {
351 list( $host, $srv ) = explode( ';', $name, 2 );
352 $objectmatches[] = '(host_name = '
353 . $this->db
->escape($host)
354 . ' AND service_description = '
355 . $this->db
->escape($srv)
359 foreach( $names as $name ) {
360 $objectmatches[] = '(host_name = '
361 . $this->db
->escape($name)
362 . ' AND (service_description = "" OR service_description IS NULL))';
366 $sql = "SELECT DISTINCT lsc.host_name as host_name, lsc.service_description as service_description, rd.state as state FROM (";
367 $sql .= "SELECT host_name, service_description, max( timestamp ) as timestamp FROM ".$this->db_table
;
368 $sql .= " WHERE (".implode(' OR ',$objectmatches).")";
369 if ( $type == 'service' ) {
370 $sql .= " AND event_type = ".Reports_Model
::SERVICECHECK
;
372 $sql .= " AND event_type = ".Reports_Model
::HOSTCHECK
;
374 if (!$this->options
['includesoftstates'])
375 $sql .= " AND hard = 1";
376 $sql .= " AND timestamp < ".$this->options
['start_time'];
377 $sql .= " GROUP BY host_name,service_description";
379 $sql .= " LEFT JOIN ".$this->db_table
." AS rd";
380 $sql .= " ON lsc.host_name = rd.host_name";
381 $sql .= " AND lsc.service_description = rd.service_description";
382 $sql .= " AND lsc.timestamp = rd.timestamp";
383 if ( $type == 'service' ) {
384 $sql .= " AND event_type = ".Reports_Model
::SERVICECHECK
;
386 $sql .= " AND event_type = ".Reports_Model
::HOSTCHECK
;
389 $dbr = $this->db
->query($sql)->result(false);
392 if ( $type == 'service' ) {
393 foreach( $dbr as $staterow ) {
394 $states[ $staterow['host_name'] . ';' . $staterow['service_description'] ] = $staterow['state'];
397 foreach( $dbr as $staterow ) {
398 $states[ $staterow['host_name'] ] = $staterow['state'];