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 = "∞"; 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 = "∞"; 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 = "∞"; 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 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