Merge "Make it possible to sort on simple custom columns"
[ninja.git] / modules / reports / libraries / StateCalculator.php
blob0d9c0eca3e90f795ad9f5b6ec6b0a3477152f005
1 <?php
3 /**
4 * Class that provides API/helpers for generating the state of a report based on
5 * a number of events.
6 */
7 abstract class StateCalculator
9 /**
10 * For hysterical reasons, we expect every single report type to return
11 * the report data in a brand new way. Jay.
13 * @return array
15 abstract public function get_data();
16 /**
17 * Given an event (a database row), add it to the current report
19 abstract public function add_event($row);
21 protected $st_text = array(); /**< Mapping between state integers and state text */
23 protected $st_source = false; /**< The source object. Can be object array, can be host_name, can be host_name;service_description, can drive you mad. */
24 protected $host_name = false; /**< The source object's host name, if it's just one. Set for services. */
25 protected $service_description = false; /**< The source object's service description, if it's just one. Only description for services */
27 /** The state template for hosts */
28 protected $state_tpl_host = array(
29 'HOST_NAME' => '',
30 'TIME_UP_SCHEDULED' => 0,
31 'TIME_UP_UNSCHEDULED' => 0,
32 'TIME_DOWN_SCHEDULED' => 0,
33 'TIME_DOWN_UNSCHEDULED' => 0,
34 'TIME_UNREACHABLE_SCHEDULED' => 0,
35 'TIME_UNREACHABLE_UNSCHEDULED' => 0,
36 'TIME_UNDETERMINED_NOT_RUNNING' => 0,
37 'TIME_UNDETERMINED_NO_DATA' => 0,
40 /** The state template for services */
41 protected $state_tpl_svc = array(
42 'HOST_NAME' => '',
43 'SERVICE_DESCRIPTION' => '',
44 'TIME_OK_SCHEDULED' => 0,
45 'TIME_OK_UNSCHEDULED' => 0,
46 'TIME_WARNING_SCHEDULED' => 0,
47 'TIME_WARNING_UNSCHEDULED' => 0,
48 'TIME_UNKNOWN_SCHEDULED' => 0,
49 'TIME_UNKNOWN_UNSCHEDULED' => 0,
50 'TIME_CRITICAL_SCHEDULED' => 0,
51 'TIME_CRITICAL_UNSCHEDULED' => 0,
52 'TIME_UNDETERMINED_NOT_RUNNING' => 0,
53 'TIME_UNDETERMINED_NO_DATA' => 0,
56 protected $st_is_service = false; /**< Whether this is a service */
57 protected $st_running = false; /**< Is nagios running? */
58 protected $st_inactive = 0; /**< Time we have no information about */
59 protected $st_dt_depth = 0; /**< The downtime depth */
60 /**
61 * The calculated state of the object, taking such things
62 * as scheduled downtime counted as uptime into consideration
64 protected $st_obj_state = false;
65 protected $st_raw = array(); /**< Mapping between the raw states and the time spent there */
66 protected $prev_row; /**< The last db row, so we can get duration */
68 protected $options; /**< A Report_options object for this report */
70 /**
71 * Create a new state calculator
72 * @param $options A report_options object that describes the report
73 * @param $timeperiod A (resolved) timeperiod to use throughout calculations
75 public function __construct(Report_options $options, $timeperiod)
77 $this->options = $options;
78 $this->timeperiod = $timeperiod;
81 /**
82 * Prepare a state calculator for action
83 * Takes a number of initialization arguments, simply because they can be
84 * more efficiently retrieved in bulk somewhere else.
85 * @param $initial_state The state for the object when the report starts
86 * @param $initial_depth The downtime depth when the report starts - in practice a boolean
87 * @param $is_running Is nagios itself running when the report starts?
89 public function initialize($initial_state, $initial_depth, $is_running)
91 $this->st_running = $is_running;
92 $this->st_obj_state = $initial_state;
93 $this->st_dt_depth = $initial_depth;
95 if ($this->options['service_description'] || $this->options['servicegroup']) {
96 $this->st_is_service = true;
98 else {
99 # we need at least a service or a host
100 if (!$this->options['host_name'] && !$this->options['hostgroup'])
101 return false;
104 $this->st_text = empty($this->st_is_service) ? Reports_Model::$host_states : Reports_Model::$service_states;
105 $this->st_text = array_map('strtoupper', $this->st_text);
107 $fout = "";
108 if (!$this->st_running)
109 $fevent_type = Reports_Model::PROCESS_SHUTDOWN;
110 else {
111 if ($this->st_is_service)
112 $fevent_type = Reports_Model::SERVICECHECK;
113 else
114 $fevent_type = Reports_Model::HOSTCHECK;
116 $state = ($this->st_running || $this->options['assumestatesduringnotrunning']) ? $this->st_obj_state : -1;
117 $this->prev_row = array
118 ('state' => $state,
119 'the_time' => $this->options['start_time'],
120 'event_type' => $fevent_type,
121 'downtime_depth' => $this->st_dt_depth);
123 # if we're actually going to use the log, we'll need
124 # to generate a faked initial message for it.
125 if ($this->options['include_trends']) {
126 $fout = sprintf("Report period start. Daemon is%s running, " .
127 "we're%s in scheduled downtime, state is %s (%d)",
128 $this->st_running ? '' : ' not',
129 $this->st_dt_depth ? '' : ' not',
130 $this->st_text[$state], $state);
131 $this->prev_row['output'] = $fout;
133 if (!empty($hostname) && is_string($hostname))
134 $this->prev_row['host_name'] = $hostname;
136 if (!empty($servicename) && is_string($servicename))
137 $this->prev_row['service_description'] = $servicename;
142 * Update the raw uptime array
144 * @param $end_time When the event ends - start time is taken from prev_row
146 public function st_update($end_time)
148 $prev_time = $this->prev_row['the_time'];
149 $duration = $end_time - $prev_time;
150 $active = intval($this->timeperiod->active_time($prev_time, $end_time));
151 $this->st_inactive += ($end_time - $prev_time) - $active;
153 $st = "$this->st_running:$this->st_dt_depth:$this->st_obj_state";
154 if ($active) {
155 if (!isset($this->st_raw[$st]))
156 $this->st_raw[$st] = $active;
157 else
158 $this->st_raw[$st] += $active;
163 * Calculate the time spent in different states as total and percentage.
165 * @param $state State times. Has the format:<br>
166 * array("X:Y:Z" => seconds, ...). Where X, Y and Z are numeric states and rhs argument is the number of seconds in that state
167 * @param $conv State translation table. E.g. for hostgroups:<br> array(0 => 'UP', '1' => 'DOWN', '2' => 'UNREACHABLE', '-1' => 'PENDING')
168 * @return array A huge array with all possible states and time spent in that state. States called PERCENT_* contains percentages rather than a number of seconds.
170 public function convert_state_table($state, $conv)
172 $cstate = array();
173 $cstate['TIME_UNDETERMINED_NO_DATA'] = 0;
174 $cstate['TIME_UNDETERMINED_NOT_RUNNING'] = 0;
175 $cstate['TIME_DOWN_COUNTED_AS_UP'] = 0;
176 $cstate['TOTAL_TIME_UNSCHEDULED'] = 0;
177 $cstate['TOTAL_TIME_SCHEDULED'] = 0;
178 $cstate['TOTAL_TIME_UNDETERMINED'] = 0;
179 $cstate['TOTAL_TIME_KNOWN'] = 0;
180 $cstate['TOTAL_TIME_ACTIVE'] = 0;
181 foreach ($state as $s => $duration) {
182 $known = false;
183 $cstate['TOTAL_TIME_ACTIVE'] += $duration;
184 $ary = explode(':', $s);
185 $is_running = intval($ary[0]);
186 $current_state = intval($ary[2]);
187 $in_dt = $ary[1] != 0;
188 $p3 = $in_dt ? '' : 'UN';
189 $p3 .= 'SCHEDULED';
191 if (!$is_running)
192 $cstate['TIME_UNDETERMINED_NOT_RUNNING'] += $duration;
194 $p1 = $is_running ? '_' : '_UNKNOWN_';
196 # this is where we hack in scheduled downtime as uptime
197 if ($in_dt && $this->options['scheduleddowntimeasuptime']) {
198 $real_state = $conv[$current_state];
199 $p2 = $conv[0];
200 if ($real_state !== 'UP' && $real_state !== 'OK')
201 $cstate['TIME_DOWN_COUNTED_AS_UP'] += $duration;
203 elseif (isset($conv[$current_state])) {
204 $p2 = $conv[$current_state];
206 if ($p2 === 'PENDING')
207 $cstate['TIME_UNDETERMINED_NO_DATA'] += $duration;
209 else {
210 $p2 = "BAD_BUG_ERROR";
213 if (!$is_running || $p2 === 'PENDING') {
214 $known = false;
215 $cstate['TOTAL_TIME_UNDETERMINED'] += $duration;
217 else {
218 $cstate['TOTAL_TIME_KNOWN'] += $duration;
219 $known = true;
222 $tot_state = "TOTAL_TIME_$p2";
223 if (!isset($cstate[$tot_state]))
224 $cstate[$tot_state] = $duration;
225 else
226 $cstate[$tot_state] += $duration;
228 if ($known) {
229 $kstate = "KNOWN_TIME_$p2";
230 if (!isset($cstate[$kstate]))
231 $cstate[$kstate] = $duration;
232 else
233 $cstate[$kstate] += $duration;
236 # scheduled/unscheduled totals
237 $cstate['TOTAL_TIME_' . $p3] += $duration;
239 $cname = 'TIME' . $p1 . $p2 . '_' . $p3;
240 if (!isset($cstate[$cname]))
241 $cstate[$cname] = $duration;
242 else
243 $cstate[$cname] += $duration;
245 if ($known) {
246 $cname = 'KNOWN_' . $cname;
247 if (!isset($cstate[$cname]))
248 $cstate[$cname] = $duration;
249 else
250 $cstate[$cname] += $duration;
254 $sched_junk = array('_SCHEDULED', '_UNSCHEDULED');
255 foreach (array('KNOWN_', '') as $known) {
256 foreach ($conv as $s) {
257 foreach ($sched_junk as $dt_str) {
258 $entry = $known . "TIME_$s" . $dt_str;
259 if (!isset($cstate[$entry]))
260 $cstate[$entry] = 0;
261 $entry = "KNOWN_TIME_$s" . $dt_str;
266 # For each $state, we need to calculate
267 # PERCENT_TOTAL_TIME_$state,
268 # PERCENT_TIME_$state_SCHEDULED,
269 # PERCENT_TIME_$state_UNSCHEDULED,
270 # PERCENT_KNOWN_TIME_$state,
271 # PERCENT_KNOWN_TIME_$state_SCHEDULED,
272 # PERCENT_KNOWN_TIME_$state_UNSCHEDULED
273 $conv['UNDETERMINED'] = 'UNDETERMINED';
274 $div = $cstate['TOTAL_TIME_ACTIVE'];
275 foreach ($conv as $state) {
276 $str = 'TIME_' . $state;
277 foreach (array('TOTAL_', 'KNOWN_') as $prefix) {
278 $full_str = $prefix . $str;
280 if (!isset($cstate[$full_str]))
281 $cstate[$full_str] = 0;
282 $cstate['PERCENT_' . $full_str] =
283 reports::percent($cstate[$full_str], $div);
286 foreach (array('', 'KNOWN_') as $known) {
287 foreach ($sched_junk as $dt_str) {
288 $perc_str = 'PERCENT_' . $known . $str . $dt_str;
289 $cstate[$perc_str] =
290 reports::percent(arr::search($cstate, $str . $dt_str), $div);
294 $str = 'PERCENT_KNOWN_TIME_' . $state;
295 $cstate[$str] =
296 $cstate[$str . '_SCHEDULED'] + $cstate[$str . '_UNSCHEDULED'];
299 # mop up the oddballs and special cases
300 $cstate['PERCENT_TIME_UNDETERMINED_NOT_RUNNING'] =
301 reports::percent($cstate['TIME_UNDETERMINED_NOT_RUNNING'], $div);
302 $cstate['PERCENT_TIME_UNDETERMINED_NO_DATA'] =
303 reports::percent($cstate['TIME_UNDETERMINED_NO_DATA'], $div);
304 $cstate['PERCENT_TIME_DOWN_COUNTED_AS_UP'] =
305 reports::percent($cstate['TIME_DOWN_COUNTED_AS_UP'], $div);
307 return $cstate;
311 * Finalize the report, calculating real uptime from our internal
312 * meta-format.
314 public function finalize()
316 // gather remaining time. If they match, it'll be 0
317 $this->st_update($this->options['end_time']);