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 if (!$this->options
['host_name'] && !$this->options
['hostgroup'] && !$this->options
['service_description'] && !$this->options
['servicegroup']) {
152 $is_running = !$this->get_last_shutdown();
154 switch ($this->options
['report_type']) {
156 case 'servicegroups':
157 $this->st_is_service
= true;
160 $objects = $this->options
->get_report_members();
161 $this->st_source
= $objects;
163 $calculator_type = false;
164 switch ((int)$this->options
['sla_mode']) {
166 $calculator_type = 'WorstStateCalculator';
169 $calculator_type = 'AverageStateCalculator';
172 $calculator_type = 'BestStateCalculator';
175 die("Don't know how to do anything with this\n");
179 $this->calculator
= new $calculator_type($this->options
, $this->timeperiod
);
180 $optclass = get_class($this->options
);
184 $initial_states = $this->get_initial_states($this->st_is_service ?
'service' : 'host', $objects);
185 $downtimes = $this->get_initial_dt_depths($this->st_is_service ?
'service' : 'host', $objects);
186 foreach ($objects as $object) {
187 $opts = new $optclass($this->options
);
188 $opts[$this->st_is_service ?
'service_description' : 'host_name'] = array($object);
189 $sub = new SingleStateCalculator($opts, $this->timeperiod
);
190 if (isset( $initial_states[$object]))
191 $initial_state = $initial_states[$object];
193 $initial_state = Reports_Model
::STATE_PENDING
;
195 if (isset( $downtimes[$object]))
196 $initial_depth = $downtimes[$object];
200 if (!$initial_depth && $this->st_is_service
) { /* Is host scheduled? */
201 $srv = explode(';', $object);
202 if (isset($downtimes[$srv[0].';']) && $downtimes[$srv[0].';'])
205 $sub->initialize($initial_state, $initial_depth, $is_running);
206 $subs[$object] = $sub;
209 switch ($this->options
['report_type']) {
210 case 'servicegroups':
212 $groups = $this->options
[$this->options
->get_value('report_type')];
215 foreach ($groups as $group) {
216 $opts = new $optclass($this->options
);
217 $opts[$this->options
->get_value('report_type')] = array($group);
218 $members = $opts->get_report_members();
219 $these_subs = array();
220 foreach ($members as $member)
221 $these_subs[$member] = $all_subs[$member];
222 $this_sub = new $calculator_type($opts, $this->timeperiod
);
223 $this_sub->set_sub_reports($these_subs);
224 $this_sub->initialize(Reports_Model
::STATE_PENDING
, Reports_Model
::STATE_PENDING
, $is_running);
225 $subs[$group] = $this_sub;
230 $this_sub = new $calculator_type($this->options
, $this->timeperiod
);
231 $this_sub->set_sub_reports($subs);
232 $this_sub->initialize(Reports_Model
::STATE_PENDING
, Reports_Model
::STATE_PENDING
, $is_running);
233 $subs = array($this_sub);
237 $this->calculator
->set_sub_reports($subs);
238 $this->calculator
->initialize(Reports_Model
::STATE_PENDING
, Reports_Model
::STATE_PENDING
, $is_running);
240 $this->st_parse_all_rows();
241 $this->calculator
->finalize();
242 return $this->calculator
->get_data();
246 * Get latest (useful) process shutdown event
248 * @return Timestamp when of last shutdown event prior to $start_time
250 public function get_last_shutdown()
252 # If we're assuming states during program downtime,
253 # we don't really need to know when the last shutdown
254 # took place, as the initial state will be used regardless
255 # of whether or not Monitor was up and running.
256 if ($this->options
['assumestatesduringnotrunning']) {
260 $query = "SELECT timestamp, event_type FROM ".
262 " WHERE timestamp <".$this->options
['start_time'].
263 " ORDER BY timestamp DESC LIMIT 1";
264 $dbr = $this->db
->query($query)->result(false);
266 if (!$dbr ||
!($row = $dbr->current()))
269 $event_type = $row['event_type'];
270 if ($event_type==Reports_Model
::PROCESS_SHUTDOWN ||
$event_type==Reports_Model
::PROCESS_RESTART
)
271 $last_shutdown = $row['timestamp'];
275 return $last_shutdown;
280 * Runs the main query and loops through the results one by one
282 private function st_parse_all_rows()
284 $dbr = $this->uptime_query();
285 foreach ($dbr as $row) {
286 $this->calculator
->add_event($row);
291 * Fetch information about SCHEDULED_DOWNTIME status for multiple objects
293 * @return array of Depth of initial downtime.
295 protected function get_initial_dt_depths( $type = 'host', $names = array() )
297 $objectmatches = array();
298 if( $type == 'service' ) {
299 foreach( $names as $name ) {
300 list( $host, $srv ) = explode( ';', $name, 2 );
301 $objectmatches[] = '(host_name = '
302 . $this->db
->escape($host)
303 . ' AND (service_description = "" OR service_description IS NULL'
304 . ' OR service_description = '
305 . $this->db
->escape($srv)
309 foreach( $names as $name ) {
310 $objectmatches[] = '(host_name = '
311 . $this->db
->escape($name)
312 . ' AND (service_description = "" OR service_description IS NULL))';
316 $sql = "SELECT DISTINCT lsc.host_name as host_name, lsc.service_description as service_description, rd.event_type as event_type FROM (";
317 $sql .= "SELECT host_name, service_description, max( timestamp ) as timestamp FROM ".$this->db_table
;
318 $sql .= " WHERE (".implode(' OR ',$objectmatches).")";
319 $sql .= " AND (event_type = ".Reports_Model
::DOWNTIME_START
." OR event_type = ".Reports_Model
::DOWNTIME_STOP
.")";
320 $sql .= " AND timestamp < ".$this->options
['start_time'];
321 $sql .= " GROUP BY host_name,service_description";
323 $sql .= " LEFT JOIN ".$this->db_table
." AS rd";
324 $sql .= " ON lsc.host_name = rd.host_name";
325 $sql .= " AND lsc.service_description = rd.service_description";
326 $sql .= " AND lsc.timestamp = rd.timestamp";
327 $sql .= " AND (event_type = ".Reports_Model
::DOWNTIME_START
." OR event_type = ".Reports_Model
::DOWNTIME_STOP
.")";
329 $dbr = $this->db
->query($sql)->result(false);
331 $downtimes = array();
332 foreach( $dbr as $staterow ) {
333 $in_downtime = (int)($staterow['event_type'] == Reports_Model
::DOWNTIME_START
);
334 if ( $type == 'service' ) {
335 $downtimes[ $staterow['host_name'] . ';' . $staterow['service_description'] ] = $in_downtime;
337 $downtimes[ $staterow['host_name'] ] = $in_downtime;
345 * Get inital states of a set of objects
347 * @return array of initial states
349 protected function get_initial_states( $type = 'host', $names = array() )
351 $objectmatches = array();
352 if( $type == 'service' ) {
353 foreach( $names as $name ) {
354 list( $host, $srv ) = explode( ';', $name, 2 );
355 $objectmatches[] = '(host_name = '
356 . $this->db
->escape($host)
357 . ' AND service_description = '
358 . $this->db
->escape($srv)
362 foreach( $names as $name ) {
363 $objectmatches[] = '(host_name = '
364 . $this->db
->escape($name)
365 . ' AND (service_description = "" OR service_description IS NULL))';
369 $sql = "SELECT DISTINCT lsc.host_name as host_name, lsc.service_description as service_description, rd.state as state FROM (";
370 $sql .= "SELECT host_name, service_description, max( timestamp ) as timestamp FROM ".$this->db_table
;
371 $sql .= " WHERE (".implode(' OR ',$objectmatches).")";
372 if ( $type == 'service' ) {
373 $sql .= " AND event_type = ".Reports_Model
::SERVICECHECK
;
375 $sql .= " AND event_type = ".Reports_Model
::HOSTCHECK
;
377 if (!$this->options
['includesoftstates'])
378 $sql .= " AND hard = 1";
379 $sql .= " AND timestamp < ".$this->options
['start_time'];
380 $sql .= " GROUP BY host_name,service_description";
382 $sql .= " LEFT JOIN ".$this->db_table
." AS rd";
383 $sql .= " ON lsc.host_name = rd.host_name";
384 $sql .= " AND lsc.service_description = rd.service_description";
385 $sql .= " AND lsc.timestamp = rd.timestamp";
386 if ( $type == 'service' ) {
387 $sql .= " AND event_type = ".Reports_Model
::SERVICECHECK
;
389 $sql .= " AND event_type = ".Reports_Model
::HOSTCHECK
;
392 $dbr = $this->db
->query($sql)->result(false);
395 if ( $type == 'service' ) {
396 foreach( $dbr as $staterow ) {
397 $states[ $staterow['host_name'] . ';' . $staterow['service_description'] ] = $staterow['state'];
400 foreach( $dbr as $staterow ) {
401 $states[ $staterow['host_name'] ] = $staterow['state'];