850 lines
31 KiB
PHP
850 lines
31 KiB
PHP
<?php
|
|
/**
|
|
* Observium
|
|
*
|
|
* This file is part of Observium.
|
|
*
|
|
* @package observium
|
|
* @subpackage entities
|
|
* @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited
|
|
*
|
|
*/
|
|
|
|
/// MOVEME. states.inc.php
|
|
function get_bits_state_array($hex, $mib = NULL, $object = NULL, $bits_def = NULL)
|
|
{
|
|
global $config;
|
|
|
|
// Fetch validate BITS definition
|
|
if (is_array($bits_def))
|
|
{
|
|
// Use passed bits definitions
|
|
$def = $bits_def;
|
|
}
|
|
else if (strlen($mib) && strlen($object) &&
|
|
isset($config['mibs'][$mib]['states_bits'][$object]))
|
|
{
|
|
$def = $config['mibs'][$mib]['states_bits'][$object];
|
|
}
|
|
if (empty($def))
|
|
{
|
|
print_debug("Incorrect BITS state definition passed.");
|
|
return NULL;
|
|
}
|
|
print_debug_vars($def);
|
|
|
|
//$bit_array = array_reverse(str_split(hex2binmap($hex)));
|
|
$bit_array = str_split(hex2binmap($hex));
|
|
print_debug_vars($bit_array);
|
|
|
|
$state_array = [];
|
|
foreach ($bit_array as $bit => $value)
|
|
{
|
|
if ($value)
|
|
{
|
|
$state_array[$bit] = $def[$bit]['name'];
|
|
}
|
|
}
|
|
|
|
return $state_array;
|
|
}
|
|
|
|
/**
|
|
* Return normalized state array by type, mib and value (numeric or string)
|
|
*
|
|
* @param string $type State type, in MIBs definition array $config['mibs'][$mib]['states'][$type]
|
|
* @param string $value Polled value for status
|
|
* @param string $mib MIB name
|
|
* @param string $event_map Polled value for event (this used only for statuses with event condition values)
|
|
* @param string $poller_type Poller type (snmp, ipmi, agent)
|
|
*
|
|
* @return array State array
|
|
*/
|
|
function get_state_array($type, $value, $mib = '', $event_map = NULL, $poller_type = 'snmp') {
|
|
global $config;
|
|
|
|
$state_array = [ 'value' => FALSE ];
|
|
|
|
switch ($poller_type) {
|
|
case 'agent':
|
|
case 'ipmi':
|
|
$state = state_string_to_numeric($type, $value, $mib, $poller_type);
|
|
if ($state !== FALSE) {
|
|
$state_array['value'] = $state; // Numeric value
|
|
if (isset(array_values($config[$poller_type]['states'][$type])[0]['match'])) {
|
|
// String value
|
|
$match_events = [
|
|
0 => 'exclude',
|
|
1 => 'ok',
|
|
2 => 'warning',
|
|
3 => 'alert',
|
|
4 => 'ignore',
|
|
];
|
|
$state_array['name'] = trim($value);
|
|
$state_array['event'] = $match_events[$state];
|
|
} else {
|
|
$state_array['name'] = $config[$poller_type]['states'][$type][$state]['name']; // Named value
|
|
$state_array['event'] = $config[$poller_type]['states'][$type][$state]['event']; // Event type
|
|
}
|
|
$state_array['mib'] = $mib;
|
|
}
|
|
break;
|
|
|
|
default: // SNMP
|
|
$value = trim($value);
|
|
$state = state_string_to_numeric($type, $value, $mib);
|
|
if ($state !== FALSE) {
|
|
if (safe_empty($mib)) {
|
|
$mib = state_type_to_mib($type);
|
|
}
|
|
$state_array['value'] = $state; // Numeric value
|
|
$state_def = $config['mibs'][$mib]['states'][$type];
|
|
$def_key = array_key_first($state_def);
|
|
if (isset($state_def[$def_key]['match'])) {
|
|
// String value
|
|
$match_events = [
|
|
0 => 'exclude',
|
|
1 => 'ok',
|
|
2 => 'warning',
|
|
3 => 'alert',
|
|
4 => 'ignore',
|
|
];
|
|
$state_array['name'] = $value;
|
|
if (isset($state_def[$def_key]['name'])) {
|
|
// Rare case, for convert numeric states to named, see: GEIST-MIB-V3::waterSensorDampness
|
|
foreach ($state_def as $index => $content) {
|
|
if (preg_match($content['match'], $value)) {
|
|
//return $match_events[$content['event']];
|
|
$state_array['name'] = $content['name'];
|
|
}
|
|
}
|
|
}
|
|
$state_array['event'] = $match_events[$state];
|
|
} else {
|
|
// Named value
|
|
$state_array['name'] = $config['mibs'][$mib]['states'][$type][$state]['name'];
|
|
|
|
if (isset($config['mibs'][$mib]['states'][$type][$state]['event_map'])) {
|
|
// For events based on additional Oid value, see:
|
|
// PowerNet-MIB::emsInputContactStatusInputContactState.1 = INTEGER: contactOpenEMS(2)
|
|
// PowerNet-MIB::emsInputContactStatusInputContactNormalState.1 = INTEGER: normallyOpenEMS(2)
|
|
|
|
// Find event associated event with event_value
|
|
$state_array['event'] = $config['mibs'][$mib]['states'][$type][$state]['event_map'][$event_map];
|
|
|
|
} else {
|
|
// Normal static events
|
|
$state_array['event'] = $config['mibs'][$mib]['states'][$type][$state]['event']; // Event type
|
|
}
|
|
}
|
|
|
|
// Force discovery by event
|
|
if (isset($config['mibs'][$mib]['states'][$type][$state]['discovery'])) {
|
|
$state_array['discovery'] = $config['mibs'][$mib]['states'][$type][$state]['discovery'];
|
|
}
|
|
|
|
$state_array['mib'] = $mib; // MIB name
|
|
}
|
|
}
|
|
|
|
return $state_array;
|
|
}
|
|
|
|
/**
|
|
* Converts named oid values to numerical interpretation based on oid descriptions and stored in definitions
|
|
*
|
|
* @param string $type Sensor type which has definitions in $config['mibs'][$mib]['states'][$type]
|
|
* @param mixed $value Value which must be converted
|
|
* @param string $mib MIB name
|
|
* @param string $poller_type Poller type
|
|
*
|
|
* @return integer Note, if definition not found or incorrect value, returns FALSE
|
|
*/
|
|
function state_string_to_numeric($type, $value, $mib = '', $poller_type = 'snmp') {
|
|
switch ($poller_type) {
|
|
case 'agent':
|
|
case 'ipmi':
|
|
if (!isset($GLOBALS['config'][$poller_type]['states'][$type])) {
|
|
return FALSE;
|
|
}
|
|
$state_def = $GLOBALS['config'][$poller_type]['states'][$type];
|
|
break;
|
|
|
|
default:
|
|
if (safe_empty($mib)) {
|
|
$mib = state_type_to_mib($type);
|
|
}
|
|
$state_def = $GLOBALS['config']['mibs'][$mib]['states'][$type];
|
|
}
|
|
|
|
if (!is_array($state_def)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//$state_match = isset(array_values($state_def)[0]['match']);
|
|
if (isset($state_def[array_key_first($state_def)]['match'])) {
|
|
// different type of statuses - string statuses, not enum
|
|
// Match by regex
|
|
// See QSAN-SNMP-MIB definitions and others
|
|
// 0 -> exclude, 1 -> ok, 2 -> warning, 3 -> alert, 4 -> ignore
|
|
$match_events = [
|
|
'exclude' => 0,
|
|
'ok' => 1,
|
|
'warning' => 2,
|
|
'alert' => 3,
|
|
'ignore' => 4,
|
|
];
|
|
foreach ($state_def as $index => $content) {
|
|
if (preg_match($content['match'], trim($value))) {
|
|
return $match_events[$content['event']];
|
|
}
|
|
}
|
|
return 1; // by default ok
|
|
}
|
|
|
|
if (is_intnum($value)) {
|
|
// Return value if already numeric
|
|
if (isset($state_def[$value])) {
|
|
return (int)$value;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
foreach ($state_def as $index => $content) {
|
|
if (strcasecmp($content['name'], trim($value)) == 0) { return $index; }
|
|
}
|
|
|
|
// 0x01
|
|
if (str_starts($value, '0x')) {
|
|
$int = (int) hexdec($value);
|
|
if (isset($state_def[$int])) {
|
|
return $int;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Helper function for get MIB name by status type.
|
|
* Currently we use unique status types over all MIBs
|
|
*
|
|
* @param string $state_type Unique status type
|
|
*
|
|
* @return string MIB name corresponding to this type
|
|
*/
|
|
function state_type_to_mib($state_type)
|
|
{
|
|
// By first cache all type -> mib from definitions
|
|
if (!isset($GLOBALS['cache']['state_type_mib']))
|
|
{
|
|
$GLOBALS['cache']['state_type_mib'] = array();
|
|
// $config['mibs'][$mib]['states']['dskf-mib-hum-state'][0] = array('name' => 'error', 'event' => 'alert');
|
|
foreach ($GLOBALS['config']['mibs'] as $mib => $entries)
|
|
{
|
|
if (!isset($entries['states'])) { continue; }
|
|
foreach ($entries['states'] as $type => $entry)
|
|
{
|
|
if (isset($GLOBALS['cache']['state_type_mib'][$type]))
|
|
{
|
|
// Disabling because it's annoying for now - pending some rewriting.
|
|
//print_warning('Warning, status type name "'.$type.'" for MIB "'.$mib.'" also exist in MIB "'.$GLOBALS['cache']['state_type_mib'][$type].'". Type name MUST be unique!');
|
|
}
|
|
$GLOBALS['cache']['state_type_mib'][$type] = $mib;
|
|
}
|
|
}
|
|
}
|
|
|
|
//print_vars($GLOBALS['cache']['state_type_mib']);
|
|
return $GLOBALS['cache']['state_type_mib'][$state_type];
|
|
}
|
|
|
|
function discover_status_definition($device, $mib, $entry)
|
|
{
|
|
|
|
echo($entry['oid']. ' [');
|
|
|
|
// Just append mib name to definition entry, for simple pass to external functions
|
|
if (empty($entry['mib']))
|
|
{
|
|
$entry['mib'] = $mib;
|
|
}
|
|
|
|
// Check that types listed in skip_if_valid_exist have already been found
|
|
if (discovery_check_if_type_exist($entry, 'status')) { echo '!]'; return; }
|
|
|
|
// Check array requirements list
|
|
if (discovery_check_requires_pre($device, $entry, 'status')) { echo '!]'; return; }
|
|
|
|
// Validate if Oid exist for current mib (in case when used generic definitions, ie edgecore)
|
|
if (empty($entry['oid_num']))
|
|
{
|
|
// Use snmptranslate if oid_num not set
|
|
$entry['oid_num'] = snmp_translate($entry['oid'], $mib);
|
|
if (empty($entry['oid_num']))
|
|
{
|
|
echo("]");
|
|
print_debug("Oid [".$entry['oid']."] not exist for mib [$mib]. Status skipped.");
|
|
return;
|
|
}
|
|
} else {
|
|
$entry['oid_num'] = rtrim($entry['oid_num'], '.');
|
|
}
|
|
|
|
// Fetch table or Oids
|
|
$table_oids = array('oid', 'oid_descr', 'oid_class', 'oid_map', 'oid_extra', 'oid_entPhysicalIndex');
|
|
$status_array = discover_fetch_oids($device, $mib, $entry, $table_oids);
|
|
|
|
$i = 0; // Used in descr as $i++
|
|
$status_count = count($status_array);
|
|
foreach ($status_array as $index => $status)
|
|
{
|
|
$options = array();
|
|
|
|
$dot_index = strlen($index) ? '.' . $index : '';
|
|
$oid_num = $entry['oid_num'] . $dot_index;
|
|
|
|
//echo PHP_EOL; print_vars($entry); echo PHP_EOL; print_vars($status); echo PHP_EOL; print_vars($descr); echo PHP_EOL;
|
|
|
|
// %i% can be used in description
|
|
$i++;
|
|
|
|
if (isset($entry['oid_class']) && strlen($status[$entry['oid_class']]))
|
|
{
|
|
if (isset($entry['map_class'][$status[$entry['oid_class']]]))
|
|
{
|
|
// Try override measured class by map
|
|
$class = $entry['map_class'][$status[$entry['oid_class']]];
|
|
if ($class === 'exclude' || !$class)
|
|
{
|
|
print_debug("Excluded by empty class map value.");
|
|
continue; // trigger for exclude statuses
|
|
}
|
|
} else {
|
|
$class = $status[$entry['oid_class']];
|
|
}
|
|
} else {
|
|
$class = $entry['measured'];
|
|
}
|
|
// Generate specific keys used during rewrites
|
|
|
|
$status['class'] = nicecase($class); // Class in descr
|
|
$status['index'] = $index; // Index in descr
|
|
foreach (explode('.', $index) as $k => $i)
|
|
{
|
|
$status['index'.$k] = $i; // Index parts
|
|
}
|
|
$status['i'] = $i; // i++ counter in descr
|
|
|
|
// Check valid exist with entity tags
|
|
if (discovery_check_if_type_exist($entry, 'status', $status)) { continue; }
|
|
|
|
// Check array requirements list
|
|
if (discovery_check_requires($device, $entry, $status, 'status')) { continue; }
|
|
|
|
$value = $status[$entry['oid']];
|
|
if (!strlen($value))
|
|
{
|
|
print_debug("Excluded by empty current value.");
|
|
continue;
|
|
}
|
|
|
|
$options = array('entPhysicalClass' => $class);
|
|
|
|
// Definition based events
|
|
if (isset($entry['oid_map']) && $status[$entry['oid_map']])
|
|
{
|
|
$options['status_map'] = $status[$entry['oid_map']];
|
|
}
|
|
|
|
// Rule-based entity linking.
|
|
if ($measured = entity_measured_match_definition($device, $entry, $status, 'status')) {
|
|
$options = array_merge($options, $measured);
|
|
$status = array_merge($status, $measured); // append to $status for %descr% tags, ie %port_label%
|
|
if (empty($class)) {
|
|
$options['entPhysicalClass'] = $measured['measured'];
|
|
}
|
|
} elseif (isset($entry['entPhysicalIndex'])) {
|
|
// Just set physical index
|
|
$options['entPhysicalIndex'] = array_tag_replace($status, $entry['entPhysicalIndex']);
|
|
}
|
|
|
|
// Generate Description
|
|
$descr = entity_descr_definition('status', $entry, $status, $status_count);
|
|
|
|
// Rename old (converted) RRDs to definition format
|
|
if (isset($entry['rename_rrd'])) {
|
|
$options['rename_rrd'] = $entry['rename_rrd'];
|
|
} elseif (isset($entry['rename_rrd_full'])) {
|
|
$options['rename_rrd_full'] = $entry['rename_rrd_full'];
|
|
}
|
|
|
|
discover_status_ng($device, $mib, $entry['oid'], $oid_num, $index, $entry['type'], $descr, $value, $options);
|
|
|
|
}
|
|
|
|
echo '] ';
|
|
|
|
}
|
|
|
|
// Compatibility wrapper!
|
|
function discover_status($device, $numeric_oid, $index, $type, $status_descr, $value = NULL, $options = array(), $poller_type = NULL) {
|
|
if (isset($poller_type)) { $options['poller_type'] = $poller_type; }
|
|
|
|
return discover_status_ng($device, '', '', $numeric_oid, $index, $type, $status_descr, $value, $options);
|
|
}
|
|
|
|
// TESTME needs unit testing
|
|
/**
|
|
* Discover a new status sensor on a device - called from discover_sensor()
|
|
*
|
|
* This function adds a status sensor to a device, if it does not already exist.
|
|
* Data on the sensor is updated if it has changed, and an event is logged with regards to the changes.
|
|
*
|
|
* @param array $device Device array status sensor is being discovered on
|
|
* @param string $mib SNMP MIB name
|
|
* @param string $object SNMP Named Oid of sensor (without index)
|
|
* @param string $oid SNMP Numeric Oid of sensor (without index)
|
|
* @param string $index SNMP index of status sensor
|
|
* @param string $type Type of status sensor (used as key in $config['status_states'])
|
|
* @param string $status_descr Description of status sensor
|
|
* @param string $value Current value of status sensor
|
|
* @param array $options Options
|
|
*
|
|
* @return bool
|
|
*/
|
|
function discover_status_ng($device, $mib, $object, $oid, $index, $type, $status_descr, $value = NULL, $options = []) {
|
|
global $config;
|
|
|
|
$poller_type = (isset($options['poller_type']) ? $options['poller_type'] : 'snmp');
|
|
|
|
$status_deleted = 0;
|
|
|
|
// Init main
|
|
$param_main = [
|
|
'oid' => 'status_oid', 'status_descr' => 'status_descr', 'status_deleted' => 'status_deleted',
|
|
'index' => 'status_index', 'mib' => 'status_mib', 'object' => 'status_object'
|
|
];
|
|
|
|
// Init optional
|
|
$param_opt = [
|
|
'entPhysicalIndex', 'entPhysicalClass', 'entPhysicalIndex_measured',
|
|
'measured_class', 'measured_entity', 'measured_entity_label', 'status_map'
|
|
];
|
|
foreach ($param_opt as $key) {
|
|
$$key = $options[$key] ?: NULL;
|
|
}
|
|
|
|
$state_array = get_state_array($type, $value, $mib, $status_map, $poller_type);
|
|
$state = $state_array['value'];
|
|
if ($state === FALSE) {
|
|
print_debug("Skipped by unknown state value: $value, $status_descr ");
|
|
return FALSE;
|
|
}
|
|
if ($state_array['event'] === 'exclude') {
|
|
print_debug("Skipped by 'exclude' event value: ".$config['status_states'][$type][$state]['name'].", $status_descr ");
|
|
return FALSE;
|
|
}
|
|
$value = $state;
|
|
$index = (string)$index; // Convert to string, for correct compare
|
|
|
|
print_debug("Discover status: [device: ".$device['hostname'].", oid: $oid, index: $index, type: $type, descr: $status_descr, CURRENT: $value, $entPhysicalIndex, $entPhysicalClass]");
|
|
|
|
// Check status ignore filters
|
|
if (entity_descr_check($status_descr, 'status')) { return FALSE; }
|
|
//foreach ($config['ignore_sensor'] as $bi) { if (strcasecmp($bi, $status_descr) == 0) { print_debug("Skipped by equals: $bi, $status_descr "); return FALSE; } }
|
|
//foreach ($config['ignore_sensor_string'] as $bi) { if (stripos($status_descr, $bi) !== FALSE) { print_debug("Skipped by strpos: $bi, $status_descr "); return FALSE; } }
|
|
//foreach ($config['ignore_sensor_regexp'] as $bi) { if (preg_match($bi, $status_descr) > 0) { print_debug("Skipped by regexp: $bi, $status_descr "); return FALSE; } }
|
|
|
|
$new_definition = $poller_type === 'snmp' && !safe_empty($mib) && !safe_empty($object);
|
|
if ($new_definition) {
|
|
$where = ' WHERE `device_id` = ? AND `status_mib` = ? AND `status_object` = ? AND `status_type` = ? AND `status_index` = ? AND `poller_type`= ?';
|
|
$params = array($device['device_id'], $mib, $object, $type, $index, $poller_type);
|
|
$status_exist = dbExist('status', $where, $params);
|
|
|
|
// Check if old format of status was exist, then rename rrd
|
|
if (!$status_exist) {
|
|
$old_where = ' WHERE `device_id` = ? AND `status_type` = ? AND `status_index` = ? AND `poller_type`= ?';
|
|
$old_index = $object . '.' . $index;
|
|
$old_params = array($device['device_id'], $type, $old_index, $poller_type);
|
|
|
|
if ($status_exist = dbExist('status', $old_where, $old_params)) {
|
|
$where = $old_where;
|
|
$params = $old_params;
|
|
|
|
// Rename old rrds without mib & object to new rrd name style
|
|
if (!isset($options['rename_rrd'])) {
|
|
$options['rename_rrd'] = $type . "-" . $old_index;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// Old format of definitions
|
|
$where = ' WHERE `device_id` = ? AND `status_type` = ? AND `status_index` = ? AND `poller_type`= ?';
|
|
$params = array($device['device_id'], $type, $index, $poller_type);
|
|
$status_exist = dbExist('status', $where, $params);
|
|
}
|
|
//if (dbFetchCell('SELECT COUNT(*) FROM `status`' . $where, $params) == '0')
|
|
if (!$status_exist) {
|
|
$status_insert = [
|
|
'poller_type' => $poller_type,
|
|
'device_id' => $device['device_id'],
|
|
'status_index' => $index,
|
|
'status_type' => $type,
|
|
//'status_id' => $status_id,
|
|
'status_value' => $value,
|
|
'status_polled' => time(), //array('NOW()'), // this field is INT(11)
|
|
'status_event' => $state_array['event'],
|
|
'status_name' => $state_array['name']
|
|
];
|
|
|
|
foreach ($param_main as $key => $column) {
|
|
$status_insert[$column] = $$key;
|
|
}
|
|
|
|
foreach ($param_opt as $key)
|
|
{
|
|
if (is_null($$key)) { $$key = array('NULL'); } // If param null, convert to array(NULL)
|
|
$status_insert[$key] = $$key;
|
|
}
|
|
|
|
$status_id = dbInsert($status_insert, 'status');
|
|
|
|
print_debug("( $status_id inserted )");
|
|
echo('+');
|
|
log_event("Status added: $entPhysicalClass $type $index $status_descr", $device, 'status', $status_id);
|
|
} else {
|
|
$status_entry = dbFetchRow('SELECT * FROM `status`' . $where, $params);
|
|
$status_id = $status_entry['status_id'];
|
|
|
|
print_debug_vars($status_entry);
|
|
$update = array();
|
|
foreach ($param_main as $key => $column)
|
|
{
|
|
if ($$key != $status_entry[$column])
|
|
{
|
|
$update[$column] = $$key;
|
|
}
|
|
}
|
|
foreach ($param_opt as $key) {
|
|
if ($$key != $status_entry[$key]) {
|
|
$update[$key] = !is_null($$key) ? $$key : [ 'NULL' ];
|
|
}
|
|
}
|
|
print_debug_vars($update);
|
|
|
|
if (count($update)) {
|
|
$updated = dbUpdate($update, 'status', '`status_id` = ?', array($status_entry['status_id']));
|
|
echo('U');
|
|
log_event("Status updated: $entPhysicalClass $type $index $status_descr", $device, 'status', $status_entry['status_id']);
|
|
} else {
|
|
echo('.');
|
|
}
|
|
}
|
|
|
|
// Rename old (converted) RRDs to definition format
|
|
// Allow with changing class or without
|
|
if (isset($options['rename_rrd_full'])) {
|
|
// Compatibility with sensor option
|
|
$options['rename_rrd'] = $options['rename_rrd_full'];
|
|
}
|
|
if (isset($options['rename_rrd'])) {
|
|
$rrd_tags = array('index' => $index, 'type' => $type, 'mib' => $mib, 'object' => $object, 'oid' => $object);
|
|
$options['rename_rrd'] = array_tag_replace($rrd_tags, $options['rename_rrd']);
|
|
$old_rrd = 'status-' . $options['rename_rrd'];
|
|
|
|
//$new_rrd = 'status-'.$type.'-'.$index;
|
|
$new_entry = ['status_descr' => $status_descr, 'status_mib' => $mib, 'status_object' => $object,
|
|
'status_type' => $type, 'status_index' => $index, 'poller_type' => $poller_type];
|
|
$new_rrd = get_status_rrd($device, $new_entry);
|
|
rename_rrd($device, $old_rrd, $new_rrd);
|
|
}
|
|
|
|
if ($new_definition) {
|
|
$GLOBALS['valid']['status'][$mib][$object][$index] = 1;
|
|
} else {
|
|
// without $mib/$object
|
|
$GLOBALS['valid']['status']['__'][$type][$index] = 1;
|
|
}
|
|
|
|
return $status_id;
|
|
//return TRUE;
|
|
}
|
|
|
|
// DOCME needs phpdoc block
|
|
// TESTME needs unit testing
|
|
function check_valid_status($device, $valid, $poller_type = 'snmp') {
|
|
$entries = dbFetchRows("SELECT * FROM `status` WHERE `device_id` = ? AND `poller_type` = ? AND `status_deleted` = ?", [ $device['device_id'], $poller_type, 0 ]);
|
|
|
|
if (!safe_empty($entries)) {
|
|
foreach ($entries as $entry) {
|
|
$index = $entry['status_index'];
|
|
$type = $entry['status_type'];
|
|
if ($poller_type === 'snmp') {
|
|
if (!safe_empty($entry['status_mib']) && !safe_empty($entry['status_object'])) {
|
|
// New definitions use Object instead type
|
|
$mib = $entry['status_mib'];
|
|
$type = $entry['status_object'];
|
|
} else {
|
|
$mib = '__';
|
|
}
|
|
} else {
|
|
// For ipmi and unix-agent
|
|
$mib = 'state';
|
|
}
|
|
|
|
if (!$valid[$mib][$type][$index]) {
|
|
echo("-");
|
|
print_debug("Status deleted: $index -> $type");
|
|
//dbDelete('status', "`status_id` = ?", array($entry['status_id']));
|
|
|
|
dbUpdate([ 'status_deleted' => '1' ], 'status', '`status_id` = ?', [ $entry['status_id'] ]);
|
|
|
|
foreach (get_entity_attribs('status', $entry['status_id']) as $attrib_type => $value) {
|
|
del_entity_attrib('status', $entry['status_id'], $attrib_type);
|
|
}
|
|
log_event("Status deleted: ".$entry['status_class']." ".$entry['status_type']." ". $entry['status_index']." ".$entry['status_descr'], $device, 'status', $entry['status_id']);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function poll_status($device, &$oid_cache)
|
|
{
|
|
global $config, $agent_sensors, $ipmi_sensors, $graphs, $table_rows;
|
|
|
|
$sql = "SELECT * FROM `status`";
|
|
//$sql .= " LEFT JOIN `status-state` USING(`status_id`)";
|
|
$sql .= " WHERE `device_id` = ? AND `status_deleted` = ?";
|
|
$sql .= ' ORDER BY `status_oid`'; // This fix polling some OIDs (when not ordered)
|
|
|
|
foreach (dbFetchRows($sql, array($device['device_id'], '0')) as $status_db)
|
|
{
|
|
//print_cli_heading("Status: ".$status_db['status_descr']. "(".$status_db['poller_type'].")", 3);
|
|
|
|
print_debug("Checking (" . $status_db['poller_type'] . ") " . $status_db['status_descr'] . " ");
|
|
|
|
// $status_poll = $status_db; // Cache non-humanized status array for use as new status state
|
|
|
|
if ($status_db['poller_type'] === "snmp")
|
|
{
|
|
$status_db['status_oid'] = '.' . ltrim($status_db['status_oid'], '.'); // Fix first dot in oid for caching
|
|
|
|
// Check if a specific poller file exists for this status, else collect via SNMP.
|
|
$file = $config['install_dir']."/includes/polling/status/".$status_db['status_type'].".inc.php";
|
|
|
|
if (is_file($file))
|
|
{
|
|
include($file);
|
|
} else {
|
|
// Take value from $oid_cache if we have it, else snmp_get it
|
|
if (isset($oid_cache[$status_db['status_oid']]))
|
|
{
|
|
print_debug("value taken from oid_cache");
|
|
$status_value = $oid_cache[$status_db['status_oid']];
|
|
} else {
|
|
$status_value = snmp_get_oid($device, $status_db['status_oid'], 'SNMPv2-MIB');
|
|
}
|
|
//$status_value = snmp_fix_numeric($status_value); // Do not use fix, this broke not-enum (string) statuses
|
|
}
|
|
}
|
|
elseif ($status_db['poller_type'] === "agent")
|
|
{
|
|
if (isset($agent_sensors['state']))
|
|
{
|
|
$status_value = $agent_sensors['state'][$status_db['status_type']][$status_db['status_index']]['current'];
|
|
} else {
|
|
print_warning("No agent status data available.");
|
|
continue;
|
|
}
|
|
}
|
|
elseif ($status_db['poller_type'] === "ipmi")
|
|
{
|
|
if (isset($ipmi_sensors['state']))
|
|
{
|
|
$status_value = $ipmi_sensors['state'][$status_db['status_type']][$status_db['status_index']]['current'];
|
|
} else {
|
|
print_warning("No IPMI status data available.");
|
|
continue;
|
|
}
|
|
} else {
|
|
print_warning("Unknown status poller type.");
|
|
continue;
|
|
}
|
|
|
|
$status_polled_time = time(); // Store polled time for current status
|
|
|
|
// Write new value and humanize (for alert checks)
|
|
$state_array = get_state_array($status_db['status_type'], $status_value, $status_db['status_mib'], $status_db['status_map'], $status_db['poller_type']);
|
|
$status_value = $state_array['value']; // Override status_value by numeric for "pseudo" (string) statuses
|
|
$status_poll['status_value'] = $state_array['value'];
|
|
$status_poll['status_name'] = $state_array['name'];
|
|
if ($status_db['status_ignore'] || $status_db['status_disable'])
|
|
{
|
|
$status_poll['status_event'] = 'ignore';
|
|
} else {
|
|
$status_poll['status_event'] = $state_array['event'];
|
|
}
|
|
|
|
// Force ignore state if measured entity is in Shutdown state
|
|
$measured_class = $status_db['measured_class'];
|
|
if ($status_poll['status_event'] === 'alert' && is_numeric($status_db['measured_entity']) &&
|
|
isset($config['sensors'][$measured_class]['ignore_shutdown']) && $config['sensors'][$measured_class]['ignore_shutdown'])
|
|
{
|
|
$measured_entity = get_entity_by_id_cache($measured_class, $status_db['measured_entity']);
|
|
print_debug_vars($measured_entity);
|
|
// Currently only for ports
|
|
if (isset($measured_entity['ifAdminStatus']) && $measured_entity['ifAdminStatus'] === 'down')
|
|
{
|
|
$status_poll['status_event'] = 'ignore';
|
|
}
|
|
}
|
|
|
|
// If last change never set, use current time
|
|
if (empty($status_db['status_last_change']))
|
|
{
|
|
$status_db['status_last_change'] = $status_polled_time;
|
|
}
|
|
|
|
if ($status_poll['status_event'] != $status_db['status_event'])
|
|
{
|
|
// Status event changed, log and set status_last_change
|
|
$status_poll['status_last_change'] = $status_polled_time;
|
|
|
|
if ($status_poll['status_event'] === 'ignore')
|
|
{
|
|
print_message("[%ystatus Ignored%n]", 'color');
|
|
}
|
|
elseif ($status_db['status_event'] != '')
|
|
{
|
|
// If old state not empty and new state not equals to new state
|
|
$msg = 'Status ' . ucfirst($status_poll['status_event']) . ': ' . $device['hostname'] . ' ' . $status_db['status_descr'] .
|
|
' entered ' . strtoupper($status_poll['status_event']) . ' state: ' . $status_poll['status_name'] .
|
|
' (previous: ' . $status_db['status_name'] . ')';
|
|
|
|
if (isset($config['entity_events'][$status_poll['status_event']]))
|
|
{
|
|
$severity = $config['entity_events'][$status_poll['status_event']]['severity'];
|
|
} else {
|
|
$severity = 'informational';
|
|
}
|
|
log_event($msg, $device, 'status', $status_db['status_id'], $severity);
|
|
|
|
// Trick for fast rediscover sensors if associated status changed
|
|
// See in MIKROTIK-MIB::mtxrPOEStatus definition
|
|
$old_state_array = get_state_array($status_db['status_type'], $status_db['status_value'], $status_db['status_mib'], $status_db['status_map'], $status_db['poller_type']);
|
|
if (isset($old_state_array['discovery']) && is_module_enabled($device, $old_state_array['discovery'], 'discovery'))
|
|
{
|
|
force_discovery($device, $old_state_array['discovery']);
|
|
print_debug("Module ${old_state_array['discovery']} force for discovery by changed status type ${status_db['status_mib']}::${status_db['status_object']}");
|
|
}
|
|
}
|
|
} else {
|
|
// If status not changed, leave old last_change
|
|
$status_poll['status_last_change'] = $status_db['status_last_change'];
|
|
}
|
|
|
|
print_debug_vars($status_poll);
|
|
|
|
// Send statistics array via AMQP/JSON if AMQP is enabled globally and for the ports module
|
|
if ($config['amqp']['enable'] == TRUE && $config['amqp']['modules']['status'])
|
|
{
|
|
$json_data = array('value' => $status_value);
|
|
messagebus_send(array('attribs' => array('t' => time(), 'device' => $device['hostname'], 'device_id' => $device['device_id'],
|
|
'e_type' => 'status', 'e_type' => $status_db['status_type'], 'e_index' => $status_db['status_index']), 'data' => $json_data));
|
|
}
|
|
|
|
// Update StatsD/Carbon
|
|
if ($config['statsd']['enable'] == TRUE)
|
|
{
|
|
StatsD::gauge(str_replace(".", "_", $device['hostname']).'.'.'status'.'.'.$status_db['status_class'].'.'.$status_db['status_type'].'.'.$status_db['status_index'], $status_value);
|
|
}
|
|
|
|
// Update RRD - FIXME - can't convert to NG because filename is dynamic! new function should return index instead of filename.
|
|
$rrd_file = get_status_rrd($device, $status_db);
|
|
rrdtool_create($device, $rrd_file, "DS:status:GAUGE:600:-20000:U");
|
|
rrdtool_update($device, $rrd_file,"N:$status_value");
|
|
|
|
// Enable graph
|
|
$graphs[$status_db['status']] = TRUE;
|
|
|
|
// Check alerts
|
|
$metrics = array();
|
|
|
|
$metrics['status_value'] = $status_value;
|
|
$metrics['status_name'] = $status_poll['status_name'];
|
|
$metrics['status_name_uptime'] = $status_polled_time - $status_poll['status_last_change'];
|
|
$metrics['status_event'] = $status_poll['status_event'];
|
|
|
|
//print_cli_data("Event (State)", $status_poll['status_event'] ." (".$status_poll['status_name'].")", 3);
|
|
|
|
$table_rows[] = array($status_db['status_descr'], $status_db['status_type'], $status_db['status_index'] ,$status_db['poller_type'],
|
|
$status_poll['status_name'], $status_poll['status_event'], format_unixtime($status_poll['status_last_change']));
|
|
|
|
check_entity('status', $status_db, $metrics);
|
|
|
|
// Add to MultiUpdate SQL State
|
|
|
|
$GLOBALS['multi_update_db'][] = array(
|
|
'status_id' => $status_db['status_id'], // UNIQUE index
|
|
'status_value' => $status_value,
|
|
'status_name' => $status_poll['status_name'],
|
|
'status_event' => $status_poll['status_event'],
|
|
'status_last_change' => $status_poll['status_last_change'],
|
|
'status_polled' => $status_polled_time);
|
|
//dbUpdate(array('status_value' => $status_value,
|
|
// 'status_name' => $status_poll['status_name'],
|
|
// 'status_event' => $status_poll['status_event'],
|
|
// 'status_last_change' => $status_poll['status_last_change'],
|
|
// 'status_polled' => $status_polled_time),
|
|
// 'status', '`status_id` = ?', array($status_db['status_id']));
|
|
}
|
|
}
|
|
|
|
// DOCME needs phpdoc block
|
|
// TESTME needs unit testing
|
|
function get_status_rrd($device, $status)
|
|
{
|
|
global $config;
|
|
|
|
# For IPMI, sensors tend to change order, and there is no index, so we prefer to use the description as key here.
|
|
if ((isset($config['os'][$device['os']]['sensor_descr']) && $config['os'][$device['os']]['sensor_descr']) || // per os definition
|
|
(isset($config['mibs'][$status['status_mib']]['sensor_descr']) && $config['mibs'][$status['status_mib']]['sensor_descr']) || // per mib definition
|
|
($status['poller_type'] != "snmp" && $status['poller_type'] != ''))
|
|
{
|
|
$index = $status['status_descr'];
|
|
} else {
|
|
$index = $status['status_index'];
|
|
}
|
|
|
|
if (strlen($status['status_mib']) && strlen($status['status_object']))
|
|
{
|
|
// for discover_status_ng(), note here is just status index
|
|
$rrd_file = "status-" . $status['status_mib'] . "-" . $status['status_object'] . "-" . $index . ".rrd";
|
|
} else {
|
|
// for discover_status(), note index == "%object%.%index%"
|
|
$rrd_file = "status-" . $status['status_type'] . "-" . $index . ".rrd";
|
|
}
|
|
|
|
return($rrd_file);
|
|
}
|
|
|
|
// DOCME needs phpdoc block
|
|
// TESTME needs unit testing
|
|
|
|
function get_status_by_id($status_id)
|
|
{
|
|
if (is_numeric($status_id))
|
|
{
|
|
$status = dbFetchRow("SELECT * FROM `status` WHERE `status_id` = ?", array($status_id));
|
|
}
|
|
if (is_array($status))
|
|
{
|
|
return $status;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// EOF
|