1<?php
2
3/**
4 * Observium
5 *
6 *   This file is part of Observium.
7 *
8 * @package    observium
9 * @subpackage web
10 * @copyright  (C) 2006-2013 Adam Armstrong, (C) 2013-2019 Observium Limited
11 *
12 */
13
14/**
15 * Humanize sensor.
16 *
17 * Returns a the $sensor array with processed information:
18 * sensor_state (TRUE: state sensor, FALSE: normal sensor)
19 * human_value, sensor_symbol, state_name, state_event, state_class
20 *
21 * @param array $sensor
22 * @return array $sensor
23 *
24 */
25// TESTME needs unit testing
26function humanize_sensor(&$sensor)
27{
28  global $config;
29
30  // Exit if already humanized
31  if ($sensor['humanized']) { return; }
32
33  $sensor['sensor_symbol'] = $GLOBALS['config']['sensor_types'][$sensor['sensor_class']]['symbol'];
34  $sensor['sensor_format'] = strval($GLOBALS['config']['sensor_types'][$sensor['sensor_class']]['format']);
35  $sensor['state_class']   = ''; //'text-success';
36
37  // Generate "pretty" thresholds
38  if (is_numeric($sensor['sensor_limit_low']))
39  {
40    $sensor_threshold_low = format_value($sensor['sensor_limit_low'], $sensor['sensor_format']) . $sensor['sensor_symbol'];
41  } else {
42    $sensor_threshold_low = "&infin;";
43  }
44
45  if (is_numeric($sensor['sensor_limit_low_warn']))
46  {
47    $sensor_warn_low = format_value($sensor['sensor_limit_low_warn'], $sensor['sensor_format']) . $sensor['sensor_symbol'];
48  } else {
49    $sensor_warn_low = NULL;
50  }
51
52  if ($sensor_warn_low) { $sensor_threshold_low = $sensor_threshold_low . " (".$sensor_warn_low.")"; }
53
54  if (is_numeric($sensor['sensor_limit']))
55  {
56    $sensor_threshold_high = format_value($sensor['sensor_limit'], $sensor['sensor_format']) . $sensor['sensor_symbol'];
57  } else {
58    $sensor_threshold_high = "&infin;";
59  }
60
61  if (is_numeric($sensor['sensor_limit_warn']))
62  {
63    $sensor_warn_high = format_value($sensor['sensor_limit_warn'], $sensor['sensor_format']) . $sensor['sensor_symbol'];
64  } else {
65    $sensor_warn_high = "&infin;";
66  }
67
68  if ($sensor_warn_high) { $sensor_threshold_high = "(".$sensor_warn_high.") " . $sensor_threshold_high; }
69
70  $sensor['sensor_thresholds'] = $sensor_threshold_low . ' - ' . $sensor_threshold_high;
71
72  // generate pretty value
73  if (!is_numeric($sensor['sensor_value']))
74  {
75    $sensor['human_value'] = 'NaN';
76    $sensor['sensor_symbol'] = '';
77  } else {
78    $sensor['human_value'] = format_value($sensor['sensor_value'], $sensor['sensor_format']);
79  }
80
81  if (isset($config['entity_events'][$sensor['sensor_event']]))
82  {
83    $sensor = array_merge($sensor, $config['entity_events'][$sensor['sensor_event']]);
84  } else {
85    $sensor['event_class'] = 'label label-primary';
86    $sensor['row_class']   = '';
87  }
88  //r($sensor);
89  if ($sensor['sensor_deleted'])
90  {
91    $sensor['row_class']   = 'disabled';
92  }
93
94  $device = &$GLOBALS['cache']['devices']['id'][$sensor['device_id']];
95  if ((isset($device['status']) && !$device['status']) || (isset($device['disabled']) && $device['disabled']))
96  {
97    $sensor['row_class']     = 'error';
98  }
99
100  // Set humanized entry in the array so we can tell later
101  $sensor['humanized'] = TRUE;
102}
103
104function build_sensor_query($vars, $query_count = FALSE)
105{
106
107  if ($query_count)
108  {
109    $sql = "SELECT COUNT(*) FROM `sensors`";
110  } else {
111    $sql  = "SELECT * FROM `sensors`";
112
113    if ($vars['sort'] == 'hostname' || $vars['sort'] == 'device' || $vars['sort'] == 'device_id')
114    {
115      $sql .= ' LEFT JOIN `devices` USING(`device_id`)';
116    }
117  }
118
119  $sql .= " WHERE `sensor_deleted` = 0";
120
121  // Build query
122  foreach($vars as $var => $value)
123  {
124    switch ($var)
125    {
126      case "group":
127      case "group_id":
128        $values = get_group_entities($value);
129        $sql .= generate_query_values($values, 'sensors.sensor_id');
130        break;
131      case "device":
132      case "device_id":
133        $sql .= generate_query_values($value, 'sensors.device_id');
134        break;
135      case "id":
136      case "sensor_id":
137        $sql .= generate_query_values($value, 'sensors.sensor_id');
138        break;
139      case "entity_id":
140        $sql .= generate_query_values($value, 'sensors.measured_entity');
141        break;
142      case "entity_type":
143        $sql .= generate_query_values($value, 'sensors.measured_class');
144        break;
145      case 'entity_state':
146      case "measured_state":
147        $sql .= build_entity_measured_where('sensor', ['measured_state' => $value]);
148        break;
149      case "metric":
150        // old metric param not allow array
151        if (!isset($GLOBALS['config']['sensor_types'][$value]))
152        {
153          break;
154        }
155      case 'class':
156      case "sensor_class":
157        $sql .= generate_query_values($value, 'sensor_class');
158        break;
159      case "descr":
160      case "sensor_descr":
161        $sql .= generate_query_values($value, 'sensors.sensor_descr', '%LIKE%');
162        break;
163      case "type":
164      case "sensor_type":
165        $sql .= generate_query_values($value, 'sensor_type', '%LIKE%');
166        break;
167      case "event":
168      case "sensor_event":
169        $sql .= generate_query_values($value, 'sensor_event');
170        break;
171    }
172  }
173  // $sql .= $GLOBALS['cache']['where']['devices_permitted'];
174
175  $sql .= generate_query_permitted(array('device', 'sensor'));
176
177  // If need count, just return sql without sorting
178  if ($query_count)
179  {
180    return $sql;
181  }
182
183  switch ($vars['sort_order'])
184  {
185    case 'desc':
186      $sort_order = 'DESC';
187      $sort_neg   = 'ASC';
188      break;
189    case 'reset':
190      unset($vars['sort'], $vars['sort_order']);
191      // no break here
192    default:
193      $sort_order = 'ASC';
194      $sort_neg   = 'DESC';
195  }
196
197
198  switch($vars['sort'])
199  {
200    case 'device':
201      $sql .= ' ORDER BY `hostname` '.$sort_order;
202      break;
203    case 'descr':
204    case 'event':
205      $sql .= ' ORDER BY `sensor_'.$vars['sort'].'` '.$sort_order;
206      break;
207    case 'value':
208    case 'last_change':
209      $sql .= ' ORDER BY `sensor_'.$vars['sort'].'` '.$sort_order;
210      break;
211    default:
212      // $sql .= ' ORDER BY `hostname` '.$sort_order.', `sensor_descr` '.$sort_order;
213  }
214
215  if(isset($vars['pageno']))
216  {
217    $start = $vars['pagesize'] * ($vars['pageno'] - 1);
218    $sql .= ' LIMIT '.$start.','.$vars['pagesize'];
219  }
220
221  return $sql;
222}
223
224function print_sensor_table($vars)
225{
226
227  pagination($vars, 0, TRUE); // Get default pagesize/pageno
228
229  $sql = build_sensor_query($vars);
230
231  //r($vars);
232  //r($sql);
233
234  $sensors = array();
235  //foreach(dbFetchRows($sql, NULL, TRUE) as $sensor)
236  foreach(dbFetchRows($sql) as $sensor)
237  {
238    //if (isset($GLOBALS['cache']['devices']['id'][$sensor['device_id']]))
239    //{
240      $sensor['hostname'] = $GLOBALS['cache']['devices']['id'][$sensor['device_id']]['hostname'];
241      $sensors[] = $sensor;
242    //}
243  }
244
245  //$sensors_count = count($sensors); // This is count incorrect, when pagination used!
246  //$sensors_count = dbFetchCell(build_sensor_query($vars, TRUE), NULL, TRUE);
247  $sensors_count = dbFetchCell(build_sensor_query($vars, TRUE));
248
249  // Pagination
250  $pagination_html = pagination($vars, $sensors_count);
251  echo $pagination_html;
252
253  echo generate_box_open();
254
255  print_sensor_table_header($vars);
256
257  foreach($sensors as $sensor)
258  {
259    print_sensor_row($sensor, $vars);
260  }
261
262  echo("</tbody></table>");
263
264  echo generate_box_close();
265
266  echo $pagination_html;
267}
268
269function print_sensor_table_header($vars)
270{
271  if ($vars['view'] == "graphs" || $vars['graph'] || isset($vars['id']))
272  {
273    $stripe_class = "table-striped-two";
274  } else {
275    $stripe_class = "table-striped";
276  }
277
278  echo('<table class="table ' . $stripe_class . ' table-condensed ">' . PHP_EOL);
279  $cols = [];
280  $cols[]              = array(NULL, 'class="state-marker"');
281  $cols['device']      = array('Device', 'style="width: 250px;"');
282  $cols[]              = array(NULL, 'class="no-width"'); // Measure entity link
283  $cols['descr']       = array('Description');
284  $cols['class']       = array('Class', 'style="width: 100px;"');
285  $cols['mib']         = array('MIB::Object');
286  $cols[]              = array('Thresholds', 'style="width: 100px;"');
287  $cols[]              = array('History');
288  $cols['last_change'] = array('Last&nbsp;changed', 'style="width: 80px;"');
289  $cols['event']       = array('Event', 'style="width: 60px; text-align: right;"');
290  $cols['value']       = array('Value', 'style="width: 80px; text-align: right;"');
291
292  if ($vars['page'] == "device")  { unset($cols['device']); }
293  if ($vars['page'] != "device" || $vars['tab'] == "overview")  { unset($cols['mib']); unset($cols['object']); }
294  if (!$vars['show_class'])       { unset($cols['class']); }
295  if ($vars['tab'] == "overview") { unset($cols[2]); } // Thresholds
296
297  echo(get_table_header($cols, $vars));
298  echo('<tbody>' . PHP_EOL);
299}
300
301function print_sensor_row($sensor, $vars)
302{
303  echo generate_sensor_row($sensor, $vars);
304}
305
306function generate_sensor_row($sensor, $vars)
307{
308  global $config;
309
310  humanize_sensor($sensor);
311
312  $table_cols = 4;
313
314  $graph_array           = array();
315  $graph_array['to']     = $config['time']['now'];
316  $graph_array['id']     = $sensor['sensor_id'];
317  $graph_array['type']   = "sensor_graph";
318  $graph_array['width']  = 80;
319  $graph_array['height'] = 20;
320  $graph_array['bg']     = 'ffffff00';
321  $graph_array['from']   = $config['time']['day'];
322
323  if ($sensor['sensor_event'] && is_numeric($sensor['sensor_value']))
324  {
325    $mini_graph = generate_graph_tag($graph_array);
326  } else {
327    // Do not show "Draw Error" minigraph
328    $mini_graph = '';
329  }
330
331  $row = '
332      <tr class="'.$sensor['row_class'].'">
333        <td class="state-marker"></td>';
334
335  if ($vars['page'] != "device" && $vars['popup'] != TRUE)
336  {
337    $row .= '        <td class="entity">' . generate_device_link($sensor) . '</td>' . PHP_EOL;
338    $table_cols++;
339  }
340
341  // Measured link & icon
342  $row .= '        <td style="padding-right: 0px;" class="no-width vertical-align">'; // minify column if empty
343  if ($vars['entity_icon']) // this used for entity popup
344  {
345    $row .= get_icon($config['sensor_types'][$sensor['sensor_class']]['icon']);
346  }
347  else if ($sensor['measured_entity'] &&
348           (!isset($vars['measured_icon']) || $vars['measured_icon'])) // hide measured icon if not required
349  {
350    //$row .= generate_entity_link($sensor['measured_class'], $sensor['measured_entity'], get_icon($sensor['measured_class']));
351    $row .= generate_entity_icon_link($sensor['measured_class'], $sensor['measured_entity']);
352  }
353  $row .= '</td>';
354  $table_cols++;
355
356  $row .= '        <td class="entity">' . generate_entity_link("sensor", $sensor) . '</td>';
357  $table_cols++;
358
359  if ($vars['show_class'])
360  {
361    $row .= '        <td>' . nicecase($sensor['sensor_class']). '</td>' . PHP_EOL;
362    $table_cols++;
363  }
364
365  // FIXME -- Generify this. It's not just for sensors.
366  if ($vars['page'] == "device" && $vars['tab'] != "overview")
367  {
368    $row .= '        <td>' .  (strlen($sensor['sensor_mib']) ? '<a href="https://mibs.observium.org/mib/'.$sensor['sensor_mib'].'/" target="_blank">' .nicecase($sensor['sensor_mib']) .'</a>' : '') . ( ( strlen($sensor['sensor_mib']) && strlen($sensor['sensor_object'])) ? '::' : '') .(strlen($sensor['sensor_mib']) ? '<a href="https://mibs.observium.org/mib/'.$sensor['sensor_mib'].'/#'.$sensor['sensor_object'].'" target="_blank">' .$sensor['sensor_object'] .'</a>' : ''). '.'.$sensor['sensor_index'].'</td>' . PHP_EOL;
369    $table_cols++;
370
371  }
372
373
374  if ($vars['tab'] != 'overview')
375  {
376    $row .= '        <td><span class="label ' . ($sensor['sensor_custom_limit'] ? 'label-warning' : '') . '">' . $sensor['sensor_thresholds'] . '</span></td>' . PHP_EOL;
377    $table_cols++;
378  }
379  $row .= '        <td style="width: 90px; text-align: right;">' . generate_entity_link('sensor', $sensor, $mini_graph, NULL, FALSE) . '</td>';
380
381  if ($vars['tab'] != 'overview')
382  {
383    $row .= '        <td style="white-space: nowrap">' . ($sensor['sensor_last_change'] == '0' ? 'Never' : generate_tooltip_link(NULL, format_uptime(($config['time']['now'] - $sensor['sensor_last_change']), 'short-2') . ' ago', format_unixtime($sensor['sensor_last_change']))) . '</td>';
384    $table_cols++;
385    $row .= '        <td style="text-align: right;"><strong>' . generate_tooltip_link('', $sensor['sensor_event'], $sensor['event_descr'], $sensor['event_class']) . '</strong></td>';
386    $table_cols++;
387  }
388  $sensor_tooltip = $sensor['event_descr'];
389
390  // Append value in alternative units to tooltip
391  if (isset($config['sensor_types'][$sensor['sensor_class']]['alt_units']))
392  {
393    foreach (value_to_units($sensor['sensor_value'],
394                            $config['sensor_types'][$sensor['sensor_class']]['symbol'],
395                            $sensor['sensor_class'],
396                            $config['sensor_types'][$sensor['sensor_class']]['alt_units']) as $unit => $unit_value)
397    {
398      if (is_numeric($unit_value)) { $sensor_tooltip .= "<br />${unit_value}${unit}"; }
399    }
400  }
401
402  $row .= '        <td style="width: 80px; text-align: right;"><strong>' . generate_tooltip_link('', $sensor['human_value'] . $sensor['sensor_symbol'], $sensor_tooltip, $sensor['event_class']) . '</strong>
403        </tr>' . PHP_EOL;
404
405  if ($vars['view'] == "graphs" || $vars['id'] == $sensor['sensor_id']) { $vars['graph'] = "graph"; }
406  if ($vars['graph'])
407  {
408    $row .= '
409      <tr class="'.$sensor['row_class'].'">
410        <td class="state-marker"></td>
411        <td colspan="'.$table_cols.'">';
412
413    $graph_array = array();
414    $graph_array['to']     = $config['time']['now'];
415    $graph_array['id']     = $sensor['sensor_id'];
416    $graph_array['type']   = 'sensor_'.$vars['graph'];
417
418    $row .= generate_graph_row($graph_array, TRUE);
419
420    $row .= '</td></tr>';
421  } # endif graphs
422
423  return $row;
424}
425
426function print_sensor_form($vars, $single_device = FALSE)
427{
428  global $config;
429
430  $form = array('type'  => 'rows',
431                'space' => '10px',
432                'submit_by_key' => TRUE,
433                'url'   => generate_url($vars));
434
435  $form_items = array();
436
437  if ($single_device)
438  {
439    // Single device, just hidden field
440    $form['row'][0]['device_id'] = array(
441      'type'        => 'hidden',
442      'name'        => 'Device',
443      'value'       => $vars['device_id'],
444      'grid'        => 2,
445      'width'       => '100%');
446  } else {
447    $form_items['devices'] = generate_form_values('device', dbFetchColumn('SELECT DISTINCT `device_id` FROM `sensors`'));
448
449    $form['row'][0]['device_id'] = array(
450      'type'        => 'multiselect',
451      'name'        => 'Device',
452      'value'       => $vars['device_id'],
453      'grid'        => 2,
454      'width'       => '100%', //'180px',
455      'values'      => $form_items['devices']);
456  }
457
458  $sensor_permitted = generate_query_permitted(array('device', 'sensor'));
459  foreach (['sensor_class' => 'Sensor Class', 'sensor_event' => 'Sensor Event'] as $param => $param_name)
460  {
461    $sql = 'SELECT DISTINCT `'.$param.'` FROM `sensors` WHERE `sensor_deleted` = ?' . $sensor_permitted;
462    $entries = dbFetchColumn($sql, [0]);
463    asort($entries);
464    foreach ($entries as $entry)
465    {
466      if ($entry == '') { $entry = OBS_VAR_UNSET; }
467      if ($param == 'sensor_class')
468      {
469        $name = nicecase($entry);
470        if (isset($config['sensor_types'][$entry]['icon']))
471        {
472          $name = ['name' => $name, 'icon' => $config['sensor_types'][$entry]['icon']];
473        } else {
474          $name = ['name' => $name, 'icon' => $config['icon']['sensor']];
475        }
476      } else {
477        $name = $entry;
478      }
479      $form_items[$param][$entry] = $name;
480    }
481
482    // Alternative param name, ie event
483    $short_param = str_replace('sensor_', '', $param);
484    if (!isset($vars[$param]) && isset($vars[$short_param]))
485    {
486      $vars[$param] = $vars[$short_param];
487    }
488
489    $form['row'][0][$param]    = array(
490      'type'        => 'multiselect',
491      'name'        => $param_name,
492      'width'       => '100%', //'180px',
493      'grid'        => 2,
494      'value'       => $vars[$param],
495      'values'      => $form_items[$param]);
496  }
497  // Currently unused, just dumb space
498  $form['row'][0]['sensor_value'] = array(
499    'type'        => 'hidden',
500    'name'        => 'Value',
501    'width'       => '100%', //'180px',
502    'grid'        => 2,
503    'value'       => $vars['sensor_value']);
504
505  // Measured entities
506  $form['row'][0]['measured_state'] = array(
507    'type'        => 'multiselect',
508    'name'        => 'Measured State',
509    'width'       => '100%', //'180px',
510    'grid'        => 2,
511    'value'       => $vars['measured_state'],
512    'values'      => ['none'     => ['name' => 'Without Measure',   'icon' => $config['icon']['filter']],
513                      'up'       => ['name' => 'Measured UP',       'icon' => $config['icon']['up']],
514                      'down'     => ['name' => 'Measured DOWN',     'icon' => $config['icon']['down']],
515                      'shutdown' => ['name' => 'Measured SHUTDOWN', 'icon' => $config['icon']['shutdown']]]);
516
517
518  $form['row'][1]['sensor_descr']    = array(
519    'type'        => 'text',
520    'placeholder' => 'Sensor description',
521    'width'       => '100%', //'180px',
522    'grid'        => 4,
523    'value'       => $vars['sensor_descr']);
524
525
526  $form['row'][1]['sensor_type']    = array(
527    'type'        => 'text',
528    'placeholder' => 'Sensor type',
529    'width'       => '100%', //'180px',
530    'grid'        => 4,
531    'value'       => $vars['status_descr']);
532
533  // Groups
534  foreach (get_type_groups('sensor') as $entry)
535  {
536    $form_items['group'][$entry['group_id']] = $entry['group_name'];
537  }
538  $form['row'][1]['group']    = array(
539    'community'   => FALSE,
540    'type'        => 'multiselect',
541    'name'        => 'Select Groups',
542    'width'       => '100%', //'180px',
543    'grid'        => 2,
544    'value'       => $vars['group'],
545    'values'      => $form_items['group']);
546
547  $form['row'][1]['search']   = array(
548    'type'        => 'submit',
549    'grid'        => 2,
550    //'name'        => 'Search',
551    //'icon'        => 'icon-search',
552    'right'       => TRUE);
553
554
555  // Show search form
556  echo '<div class="hidden-xl">';
557  print_form($form);
558  echo '</div>';
559
560  // Custom panel form
561  $panel_form = array('type'  => 'rows',
562                      'title' => 'Search Sensors',
563                      'space' => '10px',
564                      //'brand' => NULL,
565                      //'class' => '',
566                      'submit_by_key' => TRUE,
567                      'url'   => generate_url($vars));
568
569  // Clean grids
570  foreach (array_keys($form['row'][0]) as $param)
571  {
572    unset($form['row'][0][$param]['grid']);
573  }
574  foreach (array_keys($form['row'][1]) as $param)
575  {
576    unset($form['row'][1][$param]['grid']);
577  }
578  // Copy forms
579  $panel_form['row'][0]['device_id']      = $form['row'][0]['device_id'];
580  $panel_form['row'][0]['sensor_class']   = $form['row'][0]['sensor_class'];
581
582  $panel_form['row'][1]['sensor_event']   = $form['row'][0]['sensor_event'];
583  $panel_form['row'][1]['sensor_value']   = $form['row'][0]['sensor_value'];
584
585  $panel_form['row'][2]['measured_state'] = $form['row'][0]['measured_state'];
586  $panel_form['row'][2]['group']          = $form['row'][1]['group'];
587
588  $panel_form['row'][3]['sensor_type']    = $form['row'][1]['sensor_type'];
589
590  $panel_form['row'][4]['sensor_descr']   = $form['row'][1]['sensor_descr'];
591
592  //$panel_form['row'][5]['sort'] = $form['row'][0]['sort'];
593  $panel_form['row'][5]['search'] = $form['row'][1]['search'];
594
595  // Register custom panel
596  register_html_panel(generate_form($panel_form));
597}
598
599// EOF
600