initial commit; version 22.5.12042
This commit is contained in:
893
includes/entities/counter.inc.php
Normal file
893
includes/entities/counter.inc.php
Normal file
@ -0,0 +1,893 @@
|
||||
<?php
|
||||
/**
|
||||
* Observium
|
||||
*
|
||||
* This file is part of Observium.
|
||||
*
|
||||
* @package observium
|
||||
* @subpackage entities
|
||||
* @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited
|
||||
*
|
||||
*/
|
||||
|
||||
function discover_counter_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, 'counter')) { echo '!]'; return; }
|
||||
|
||||
// Check array requirements list
|
||||
if (discovery_check_requires_pre($device, $entry, 'counter')) { 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]. Counter skipped.");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
$entry['oid_num'] = rtrim($entry['oid_num'], '.');
|
||||
}
|
||||
|
||||
// Fetch table or Oids
|
||||
$table_oids = array('oid', 'oid_descr', 'oid_scale', 'oid_unit', 'oid_class', 'oid_add',
|
||||
'oid_limit_low', 'oid_limit_low_warn', 'oid_limit_high_warn', 'oid_limit_high', 'oid_limit_warn',
|
||||
'oid_limit_nominal', 'oid_limit_delta_warn', 'oid_limit_delta', 'oid_limit_scale',
|
||||
'oid_extra', 'oid_entPhysicalIndex');
|
||||
$counter_array = discover_fetch_oids($device, $mib, $entry, $table_oids);
|
||||
|
||||
$counters = array(); // Reset per-class counters for each MIB
|
||||
|
||||
$counters_count = count($counter_array);
|
||||
foreach ($counter_array as $index => $counter)
|
||||
{
|
||||
$options = array();
|
||||
|
||||
if (isset($entry['class']))
|
||||
{
|
||||
// Hardcoded counter class
|
||||
$class = $entry['class'];
|
||||
} else {
|
||||
// If no 'class' hardcoded, see if we can get class from the map_class via oid_class
|
||||
if (isset($entry['oid_class']) && isset($counter[$entry['oid_class']]))
|
||||
{
|
||||
if (isset($entry['map_class'][$counter[$entry['oid_class']]]))
|
||||
{
|
||||
$class = $entry['map_class'][$counter[$entry['oid_class']]];
|
||||
} else {
|
||||
print_debug('Value from oid_class (' . $counter[$entry['oid_class']] . ') does not match any configured values in map_class!');
|
||||
continue; // Break foreach. Next counter!
|
||||
}
|
||||
} else {
|
||||
print_debug('No class hardcoded, but no oid_class (' . $entry['oid_class'] . ') found in table walk!');
|
||||
continue; // Break foreach. Next counter!
|
||||
}
|
||||
}
|
||||
|
||||
$dot_index = strlen($index) ? '.' . $index : '';
|
||||
$oid_num = $entry['oid_num'] . $dot_index;
|
||||
|
||||
// echo PHP_EOL; print_vars($entry); echo PHP_EOL; print_vars($counter); echo PHP_EOL; print_vars($descr); echo PHP_EOL;
|
||||
|
||||
// %i% can be used in description, a counter is kept per counter class
|
||||
$counters[$class]++;
|
||||
|
||||
// Generate specific keys used during rewrites
|
||||
|
||||
$counter['class'] = nicecase($class); // Class in descr
|
||||
$counter['index'] = $index; // Index in descr
|
||||
foreach (explode('.', $index) as $k => $i)
|
||||
{
|
||||
$counter['index'.$k] = $i; // Index parts
|
||||
}
|
||||
$counter['i'] = $counters[$class]; // i++ counter in descr (per counter class)
|
||||
|
||||
// Check valid exist with entity tags
|
||||
if (discovery_check_if_type_exist($entry, 'counter', $counter)) { continue; }
|
||||
|
||||
// Check array requirements list
|
||||
if (discovery_check_requires($device, $entry, $counter, 'counter')) { continue; }
|
||||
|
||||
$value = snmp_fix_numeric($counter[$entry['oid']]);
|
||||
if (!is_numeric($value))
|
||||
{
|
||||
print_debug("Excluded by current value ($value) is not numeric.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for min/max values, when counters report invalid data as counter does not exist
|
||||
if (isset($entry['min']) && $value <= $entry['min'])
|
||||
{
|
||||
print_debug("Excluded by current value ($value) is equals or below min (".$entry['min'].").");
|
||||
continue;
|
||||
}
|
||||
elseif (isset($entry['max']) && $value >= $entry['max'])
|
||||
{
|
||||
print_debug("Excluded by current value ($value) is equals or above max (".$entry['max'].").");
|
||||
continue;
|
||||
}
|
||||
elseif (isset($entry['invalid']) && in_array($value, (array)$entry['invalid']))
|
||||
{
|
||||
print_debug("Excluded by current value ($value) in invalid range [".implode(', ', (array)$entry['invalid'])."].");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Scale
|
||||
$scale = entity_scale_definition($device, $entry, $counter);
|
||||
|
||||
// Limits
|
||||
$options = array_merge($options, entity_limits_definition($device, $entry, $counter, $scale));
|
||||
|
||||
// Unit
|
||||
if (isset($entry['unit'])) { $options['counter_unit'] = $entry['unit']; }
|
||||
if (isset($entry['oid_unit']) && isset($counter[$entry['oid_unit']]))
|
||||
{
|
||||
// Translate unit from specific Oid
|
||||
$unit = $counter[$entry['oid_unit']];
|
||||
if (isset($entry['map_unit'][$unit]))
|
||||
{
|
||||
$options['counter_unit'] = $entry['map_unit'][$unit];
|
||||
}
|
||||
}
|
||||
|
||||
// Rule-based entity linking.
|
||||
if ($measured = entity_measured_match_definition($device, $entry, $counter, 'counter'))
|
||||
{
|
||||
$options = array_merge($options, $measured);
|
||||
$counter = array_merge($counter, $measured); // append to $counter for %descr% tags, ie %port_label%
|
||||
}
|
||||
// End rule-based entity linking
|
||||
elseif (isset($entry['entPhysicalIndex']))
|
||||
{
|
||||
// Just set physical index
|
||||
$options['entPhysicalIndex'] = array_tag_replace($counter, $entry['entPhysicalIndex']);
|
||||
}
|
||||
|
||||
// Add extra value, dirty hack for DANTHERM-COOLING-MIB:
|
||||
// heaterOpertdurHour.0 = INTEGER: 13 Hours
|
||||
// heaterOpertdurMin.0 = INTEGER: 59
|
||||
if (isset($entry['oid_add']) && isset($counter[$entry['oid_add']]))
|
||||
{
|
||||
$options['value_add'] = snmp_fix_numeric($counter[$entry['oid_add']]);
|
||||
$options['oid_add'] = snmp_translate($entry['oid_add'] . $dot_index, $mib);
|
||||
if (isset($entry['scale_add']))
|
||||
{
|
||||
$options['scale_add'] = $entry['scale_add'];
|
||||
}
|
||||
}
|
||||
|
||||
// Generate Description
|
||||
$descr = entity_descr_definition('counter', $entry, $counter, $counters_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_counter($device, $class, $mib, $entry['oid'], $oid_num, $index, $descr, $scale, $value, $options);
|
||||
}
|
||||
|
||||
echo '] ';
|
||||
|
||||
}
|
||||
|
||||
// TESTME needs unit testing
|
||||
/**
|
||||
* Discover a new counter on a device
|
||||
*
|
||||
* This function adds a status counter to a device, if it does not already exist.
|
||||
* Data on the counter is updated if it has changed, and an event is logged with regards to the changes.
|
||||
*
|
||||
* Status counters are handed off to discover_status().
|
||||
* Current counter values are rectified in case they are broken (added spaces, etc).
|
||||
*
|
||||
* @param array $device Device array counter is being discovered on
|
||||
* @param string $class Class of counter (voltage, temperature, etc.)
|
||||
* @param string $mib SNMP MIB name
|
||||
* @param string $object SNMP Named Oid of counter (without index)
|
||||
* @param string $oid SNMP Numeric Oid of counter (without index)
|
||||
* @param string $index SNMP index of counter
|
||||
* @param string $counter_descr Description of counter
|
||||
* @param int $scale Scale of counter (0.1 for 1:10 scale, 10 for 10:1 scale, etc)
|
||||
* @param string $value Current counter value
|
||||
* @param array $options Options (counter_unit, limit_auto, limit*, poller_type, scale, measured_*)
|
||||
* @return bool
|
||||
*/
|
||||
function discover_counter($device, $class, $mib, $object, $oid, $index, $counter_descr, $scale = 1, $value = NULL, $options = []) {
|
||||
|
||||
//echo 'MIB:'; print_vars($mib);
|
||||
|
||||
$poller_type = isset($options['poller_type']) ? $options['poller_type'] : 'snmp';
|
||||
// Class for counter is free text string (not limited by known classes)
|
||||
$class = safe_empty($class) ? 'counter' : strtolower($class);
|
||||
// Use MIB & Object or Numeric Oid?
|
||||
$use_mib_object = $mib && $object;
|
||||
|
||||
$counter_deleted = 0;
|
||||
|
||||
// Init main
|
||||
$param_main = [ 'oid' => 'counter_oid', 'counter_descr' => 'counter_descr', 'scale' => 'counter_multiplier',
|
||||
'counter_deleted' => 'counter_deleted', 'mib' => 'counter_mib', 'object' => 'counter_object' ];
|
||||
|
||||
// Init numeric values
|
||||
if (!is_numeric($scale) || $scale == 0) { $scale = 1; }
|
||||
|
||||
// Skip discovery counter if value not numeric or null (default)
|
||||
if (!safe_empty($value)) {
|
||||
// Some retarded devices report data with spaces and commas
|
||||
// STRING: " 20,4"
|
||||
$value = snmp_fix_numeric($value);
|
||||
}
|
||||
|
||||
if (is_numeric($value)) {
|
||||
$value = scale_value($value, $scale);
|
||||
// $value *= $scale; // Scale before unit conversion
|
||||
$value = value_to_si($value, $options['counter_unit'], $class); // Convert if not SI unit
|
||||
|
||||
// Extra add value
|
||||
if (isset($options['value_add']) && is_numeric($options['value_add'])) {
|
||||
$value += scale_value($options['value_add'], $options['scale_add']);
|
||||
}
|
||||
} else {
|
||||
print_debug("Counter skipped by not numeric value: '$value', '$counter_descr'");
|
||||
if (strlen($value))
|
||||
{
|
||||
print_debug("Perhaps this is named status, use discover_status() instead.");
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$param_limits = array('limit_high' => 'counter_limit', 'limit_high_warn' => 'counter_limit_warn',
|
||||
'limit_low' => 'counter_limit_low', 'limit_low_warn' => 'counter_limit_low_warn');
|
||||
foreach ($param_limits as $key => $column) {
|
||||
// Set limits vars and unit convert if required
|
||||
$$key = (is_numeric($options[$key]) ? value_to_si($options[$key], $options['counter_unit'], $class) : NULL);
|
||||
}
|
||||
// Set by which param use limits
|
||||
switch (strtolower($options['limit_by'])) {
|
||||
case 's':
|
||||
case 'sec':
|
||||
case 'second':
|
||||
$limit_by = 'sec';
|
||||
break;
|
||||
case 'm':
|
||||
case 'min':
|
||||
case 'minute':
|
||||
$limit_by = 'min';
|
||||
break;
|
||||
case 'h':
|
||||
case 'hour':
|
||||
$limit_by = 'hour';
|
||||
break;
|
||||
case 'val':
|
||||
case 'value':
|
||||
$limit_by = 'value';
|
||||
break;
|
||||
default:
|
||||
//case 'poll':
|
||||
//case '5min':
|
||||
$limit_by = '5min';
|
||||
break;
|
||||
}
|
||||
// Auto calculate high/low limits if not passed (for counter must be explicit passed)
|
||||
//$limit_auto = !isset($options['limit_auto']) || (bool)$options['limit_auto'];
|
||||
$limit_auto = isset($options['limit_auto']) && (bool)$options['limit_auto'];
|
||||
|
||||
// Init optional
|
||||
$param_opt = array('entPhysicalIndex', 'entPhysicalClass', 'entPhysicalIndex_measured', 'measured_class', 'measured_entity', 'counter_unit');
|
||||
foreach ($param_opt as $key) {
|
||||
$$key = ($options[$key] ? $options[$key] : NULL);
|
||||
}
|
||||
|
||||
print_debug("Discover counter: [class: $class, device: ".$device['hostname'].", oid: $oid, index: $index, descr: $counter_descr, scale: $scale, limits: ($limit_low, $limit_low_warn, $limit_high_warn, $limit_high), CURRENT: $value, $entPhysicalIndex, $entPhysicalClass");
|
||||
|
||||
// Check counter ignore filters
|
||||
if (entity_descr_check($counter_descr, 'counter')) { return FALSE; }
|
||||
//foreach ($config['ignore_counter'] as $bi) { if (strcasecmp($bi, $counter_descr) == 0) { print_debug("Skipped by equals: $bi, $counter_descr "); return FALSE; } }
|
||||
//foreach ($config['ignore_counter_string'] as $bi) { if (stripos($counter_descr, $bi) !== FALSE) { print_debug("Skipped by strpos: $bi, $counter_descr "); return FALSE; } }
|
||||
//foreach ($config['ignore_counter_regexp'] as $bi) { if (preg_match($bi, $counter_descr) > 0) { print_debug("Skipped by regexp: $bi, $counter_descr "); return FALSE; } }
|
||||
|
||||
if (!is_null($limit_low_warn) && !is_null($limit_high_warn) && ($limit_low_warn > $limit_high_warn)) {
|
||||
// Fix high/low thresholds (i.e. on negative numbers)
|
||||
list($limit_high_warn, $limit_low_warn) = array($limit_low_warn, $limit_high_warn);
|
||||
}
|
||||
print_debug_vars($limit_high);
|
||||
print_debug_vars($limit_high_warn);
|
||||
print_debug_vars($limit_low_warn);
|
||||
print_debug_vars($limit_low);
|
||||
|
||||
if ($use_mib_object) {
|
||||
$where = '`device_id` = ? AND `counter_class` = ? AND `counter_mib` = ? AND `counter_object` = ? AND `counter_index` = ? AND `poller_type`= ?';
|
||||
$params = [$device['device_id'], $class, $mib, $object, $index, $poller_type];
|
||||
} else {
|
||||
// Rare case, when MIB and Object unknown
|
||||
$where = '`device_id` = ? AND `counter_class` = ? AND `counter_oid` = ? AND `counter_index` = ? AND `poller_type`= ?';
|
||||
$params = [$device['device_id'], $class, $oid, $index, $poller_type];
|
||||
}
|
||||
|
||||
if (!dbExist('counters', $where, $params)) {
|
||||
if (!$limit_high) { $limit_high = sensor_limit_high($class, $value, $limit_auto); }
|
||||
if (!$limit_low) { $limit_low = sensor_limit_low($class, $value, $limit_auto); }
|
||||
|
||||
if (!is_null($limit_low) && !is_null($limit_high) && ($limit_low > $limit_high)) {
|
||||
// Fix high/low thresholds (i.e. on negative numbers)
|
||||
list($limit_high, $limit_low) = array($limit_low, $limit_high);
|
||||
print_debug("High/low limits swapped.");
|
||||
}
|
||||
|
||||
$counter_insert = [
|
||||
'poller_type' => $poller_type, 'device_id' => $device['device_id'],
|
||||
'counter_class' => $class, 'counter_index' => $index
|
||||
];
|
||||
|
||||
foreach ($param_main as $key => $column) {
|
||||
$counter_insert[$column] = $$key;
|
||||
}
|
||||
|
||||
foreach ($param_limits as $key => $column) {
|
||||
// Convert strings/numbers to (float) or to array('NULL')
|
||||
$$key = is_numeric($$key) ? (float)$$key : array('NULL');
|
||||
$counter_insert[$column] = $$key;
|
||||
}
|
||||
$counter_insert['counter_limit_by'] = $limit_by;
|
||||
|
||||
foreach ($param_opt as $key) {
|
||||
if (is_null($$key)) { $$key = array('NULL'); }
|
||||
$counter_insert[$key] = $$key;
|
||||
}
|
||||
|
||||
$counter_insert['counter_value'] = $value;
|
||||
$counter_insert['counter_polled'] = time();
|
||||
|
||||
$counter_id = dbInsert($counter_insert, 'counters');
|
||||
|
||||
// Extra oid add
|
||||
if ($counter_id && isset($options['oid_add'])) {
|
||||
set_entity_attrib('counter', $counter_id, 'oid_add', $options['oid_add'], $device['device_id']);
|
||||
if (isset($options['scale_add'])) {
|
||||
set_entity_attrib('counter', $counter_id, 'scale_add', $options['scale_add'], $device['device_id']);
|
||||
}
|
||||
}
|
||||
|
||||
print_debug("( $counter_id inserted )");
|
||||
echo('+');
|
||||
|
||||
log_event("Counter added: $class $mib::$object.$index $counter_descr", $device, 'counter', $counter_id);
|
||||
} else {
|
||||
$counter_entry = dbFetchRow("SELECT * FROM `counters` WHERE " . $where, $params);
|
||||
$counter_id = $counter_entry['counter_id'];
|
||||
|
||||
// Limits
|
||||
if (!$counter_entry['counter_custom_limit'])
|
||||
{
|
||||
if (!is_numeric($limit_high))
|
||||
{
|
||||
if ($counter_entry['counter_limit'] !== '')
|
||||
{
|
||||
// Calculate a reasonable limit
|
||||
$limit_high = sensor_limit_high($class, $value, $limit_auto);
|
||||
} else {
|
||||
// Use existing limit. (this is wrong! --mike)
|
||||
$limit_high = $counter_entry['counter_limit'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_numeric($limit_low))
|
||||
{
|
||||
if ($counter_entry['counter_limit_low'] !== '')
|
||||
{
|
||||
// Calculate a reasonable limit
|
||||
$limit_low = sensor_limit_low($class, $value, $limit_auto);
|
||||
} else {
|
||||
// Use existing limit. (this is wrong! --mike)
|
||||
$limit_low = $counter_entry['counter_limit_low'];
|
||||
}
|
||||
}
|
||||
|
||||
// Fix high/low thresholds (i.e. on negative numbers)
|
||||
if (!is_null($limit_low) && !is_null($limit_high) && ($limit_low > $limit_high))
|
||||
{
|
||||
list($limit_high, $limit_low) = array($limit_low, $limit_high);
|
||||
print_debug("High/low limits swapped.");
|
||||
}
|
||||
|
||||
// Update limits
|
||||
$update = array();
|
||||
$update_msg = array();
|
||||
$debug_msg = 'Current counter value: "'.$value.'", scale: "'.$scale.'"'.PHP_EOL;
|
||||
foreach ($param_limits as $key => $column)
|
||||
{
|
||||
// $key - param name, $$key - param value, $column - column name in DB for $key
|
||||
$debug_msg .= ' '.$key.': "'.$counter_entry[$column].'" -> "'.$$key.'"'.PHP_EOL;
|
||||
//convert strings/numbers to identical type (float) or to array('NULL') for correct comparison
|
||||
$$key = is_numeric($$key) ? (float)$$key : array('NULL');
|
||||
$counter_entry[$column] = is_numeric($counter_entry[$column]) ? (float)$counter_entry[$column] : array('NULL');
|
||||
if (float_cmp($$key, $counter_entry[$column], 0.01) !== 0) // FIXME, need compare autogenerated and hard passed limits by different ways
|
||||
{
|
||||
$update[$column] = $$key;
|
||||
$update_msg[] = $key.' -> "'.(is_array($$key) ? 'NULL' : $$key).'"';
|
||||
}
|
||||
}
|
||||
if ($counter_entry['counter_limit_by'] != $limit_by)
|
||||
{
|
||||
$update['counter_limit_by'] = $limit_by;
|
||||
$update_msg[] = 'limit_by -> "'.$limit_by.'"';
|
||||
}
|
||||
if (count($update))
|
||||
{
|
||||
echo("L");
|
||||
print_debug($debug_msg);
|
||||
log_event('Counter updated (limits): '.implode(', ', $update_msg), $device, 'counter', $counter_entry['counter_id']);
|
||||
$updated = dbUpdate($update, 'counters', '`counter_id` = ?', array($counter_entry['counter_id']));
|
||||
}
|
||||
}
|
||||
|
||||
$update = array();
|
||||
foreach ($param_main as $key => $column)
|
||||
{
|
||||
if (float_cmp($$key, $counter_entry[$column]) !== 0)
|
||||
{
|
||||
$update[$column] = $$key;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($param_opt as $key)
|
||||
{
|
||||
if ($$key != $counter_entry[$key])
|
||||
{
|
||||
$update[$key] = $$key;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($update))
|
||||
{
|
||||
$updated = dbUpdate($update, 'counters', '`counter_id` = ?', array($counter_entry['counter_id']));
|
||||
|
||||
// Extra oid add
|
||||
if (isset($options['oid_add']))
|
||||
{
|
||||
set_entity_attrib('counter', $counter_entry['counter_id'], 'oid_add', $options['oid_add'], $device['device_id']);
|
||||
if (isset($options['scale_add']))
|
||||
{
|
||||
set_entity_attrib('counter', $counter_entry['counter_id'], 'scale_add', $options['scale_add'], $device['device_id']);
|
||||
}
|
||||
}
|
||||
|
||||
echo('U');
|
||||
log_event("Counter updated: $class $mib::$object.$index $counter_descr", $device, 'counter', $counter_entry['counter_id']);
|
||||
} else {
|
||||
if (isset($options['oid_add']) &&
|
||||
!dbExist('entity_attribs', '`entity_type` = ? AND `entity_id` = ? AND `attrib_type` = ?', array('counter', $counter_entry['counter_id'], 'oid_add')))
|
||||
{
|
||||
set_entity_attrib('counter', $counter_entry['counter_id'], 'oid_add', $options['oid_add'], $device['device_id']);
|
||||
if (isset($options['scale_add']))
|
||||
{
|
||||
set_entity_attrib('counter', $counter_entry['counter_id'], 'scale_add', $options['scale_add'], $device['device_id']);
|
||||
}
|
||||
}
|
||||
echo('.');
|
||||
}
|
||||
}
|
||||
|
||||
// Rename old (converted) RRDs to definition format
|
||||
// Allow with changing class or without
|
||||
if (isset($options['rename_rrd']) || isset($options['rename_rrd_full'])) {
|
||||
$rrd_tags = array('index' => $index, 'mib' => $mib, 'object' => $object, 'oid' => $object);
|
||||
if (isset($options['rename_rrd'])) {
|
||||
$options['rename_rrd'] = array_tag_replace($rrd_tags, $options['rename_rrd']);
|
||||
$old_rrd = 'counter-' . $class . '-' . $options['rename_rrd'];
|
||||
} elseif (isset($options['rename_rrd_full'])) {
|
||||
$options['rename_rrd_full'] = array_tag_replace($rrd_tags, $options['rename_rrd_full']);
|
||||
$old_rrd = 'counter-' . $options['rename_rrd_full'];
|
||||
}
|
||||
$new_rrd = "counter-" . $class . "-" . $mib . "-" . $object . "-" . $index . ".rrd";
|
||||
rename_rrd($device, $old_rrd, $new_rrd);
|
||||
}
|
||||
|
||||
$GLOBALS['valid']['counter'][$class][$mib][$object][$index] = 1;
|
||||
|
||||
return $counter_id;
|
||||
//return TRUE;
|
||||
}
|
||||
|
||||
// Poll a counter
|
||||
function poll_counter($device, &$oid_cache)
|
||||
{
|
||||
global $config, $agent_sensors, $ipmi_counters, $graphs, $table_rows;
|
||||
|
||||
$sql = "SELECT * FROM `counters`";
|
||||
$sql .= " WHERE `device_id` = ? AND `counter_deleted` = ?";
|
||||
$sql .= ' ORDER BY `counter_oid`'; // This fix polling some OIDs (when not ordered)
|
||||
|
||||
//print_debug_vars($GLOBALS['cache']['entity_attribs']);
|
||||
foreach (dbFetchRows($sql, array($device['device_id'], '0')) as $counter_db)
|
||||
{
|
||||
$counter_poll = array();
|
||||
$counter_attribs = get_entity_attribs('counter', $counter_db);
|
||||
//print_debug_vars($GLOBALS['cache']['entity_attribs']);
|
||||
//print_debug_vars($counter_attribs);
|
||||
$class = $counter_db['counter_class'];
|
||||
// Counter not have type attribute, this need for compat with agent or ipmi
|
||||
$type = $counter_db['counter_mib'] . '-' . $counter_db['counter_object'];
|
||||
|
||||
//print_cli_heading("Counter: ".$counter_db['counter_descr'], 3);
|
||||
|
||||
if (OBS_DEBUG)
|
||||
{
|
||||
echo("Checking (" . $counter_db['poller_type'] . ") $class " . $counter_db['counter_descr'] . " ");
|
||||
print_debug_vars($counter_db, 1);
|
||||
}
|
||||
|
||||
if ($counter_db['poller_type'] === "snmp")
|
||||
{
|
||||
$counter_db['counter_oid'] = '.' . ltrim($counter_db['counter_oid'], '.'); // Fix first dot in oid for caching
|
||||
|
||||
// Take value from $oid_cache if we have it, else snmp_get it
|
||||
if (isset($oid_cache[$counter_db['counter_oid']]))
|
||||
{
|
||||
$oid_cache_tmp = $oid_cache[$counter_db['counter_oid']]; // keep original value, while cache entry can reused
|
||||
$oid_cache[$counter_db['counter_oid']] = snmp_fix_numeric($oid_cache[$counter_db['counter_oid']], $counter_db['counter_unit']);
|
||||
}
|
||||
if (is_numeric($oid_cache[$counter_db['counter_oid']]))
|
||||
{
|
||||
print_debug("value taken from oid_cache");
|
||||
$counter_poll['counter_value'] = $oid_cache[$counter_db['counter_oid']];
|
||||
$oid_cache[$counter_db['counter_oid']] = $oid_cache_tmp; // restore original cached value
|
||||
} else {
|
||||
// Get by numeric oid
|
||||
$counter_poll['counter_value'] = snmp_get_oid($device, $counter_db['counter_oid'], 'SNMPv2-MIB');
|
||||
$counter_poll['counter_value'] = snmp_fix_numeric($counter_poll['counter_value'], $counter_db['counter_unit']);
|
||||
}
|
||||
$unit = $counter_db['counter_unit'];
|
||||
|
||||
// Extra add
|
||||
if (isset($counter_attribs['oid_add']))
|
||||
{
|
||||
$counter_poll['counter_value_add'] = snmp_fix_numeric(snmp_get_oid($device, $counter_attribs['oid_add'], 'SNMPv2-MIB'));
|
||||
}
|
||||
}
|
||||
elseif ($counter_db['poller_type'] === "agent")
|
||||
{
|
||||
if (isset($agent_sensors))
|
||||
{
|
||||
$counter_poll['counter_value'] = $agent_sensors[$class][$type][$counter_db['counter_index']]['current'];
|
||||
} else {
|
||||
print_warning("No agent counter data available.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
elseif ($counter_db['poller_type'] === "ipmi")
|
||||
{
|
||||
if (isset($ipmi_counters))
|
||||
{
|
||||
$counter_poll['counter_value'] = snmp_fix_numeric($ipmi_counters[$class][$type][$counter_db['counter_index']]['current']);
|
||||
$unit = $ipmi_counters[$class][$type][$counter_db['counter_index']]['unit'];
|
||||
} else {
|
||||
print_warning("No IPMI counter data available.");
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
print_warning("Unknown counter poller type.");
|
||||
continue;
|
||||
}
|
||||
|
||||
$counter_polled_time = time(); // Store polled time for current counter
|
||||
$counter_polled_period = $counter_polled_time - $counter_db['counter_polled'];
|
||||
|
||||
if (isset($counter_db['counter_multiplier']) && $counter_db['counter_multiplier'] != 0)
|
||||
{
|
||||
//$counter_poll['counter_value'] *= $counter_db['counter_multiplier'];
|
||||
$counter_poll['counter_value'] = scale_value($counter_poll['counter_value'], $counter_db['counter_multiplier']);
|
||||
}
|
||||
|
||||
// Unit conversion to SI (if required)
|
||||
$counter_poll['counter_value'] = value_to_si($counter_poll['counter_value'], $counter_db['counter_unit'], $class);
|
||||
|
||||
// Extra add
|
||||
if (isset($counter_attribs['oid_add']) && is_numeric($counter_poll['counter_value_add']))
|
||||
{
|
||||
print_debug("Extra value add: ".$counter_poll['counter_value']." + (".$counter_poll['counter_value_add']." * ".$counter_attribs['scale_add'].") =");
|
||||
$counter_poll['counter_value'] += scale_value($counter_poll['counter_value_add'], $counter_attribs['scale_add']);
|
||||
print_debug($counter_poll['counter_value']);
|
||||
}
|
||||
|
||||
// Rate /s
|
||||
$value_diff = int_sub($counter_poll['counter_value'], $counter_db['counter_value']);
|
||||
$counter_poll['counter_rate'] = float_div($value_diff, $counter_polled_period);
|
||||
$counter_poll['counter_rate_min'] = float_div($value_diff, ($counter_polled_period / 60));
|
||||
$counter_poll['counter_rate_5min'] = float_div($value_diff, ($counter_polled_period / 300)); // This is mostly same as count per poll period
|
||||
print_debug('Rate /sec: (' . $counter_poll['counter_value'] . ' - ' . $counter_db['counter_value'] . '(='.$value_diff.')) / ' . $counter_polled_period . ' = ' . $counter_poll['counter_rate']);
|
||||
print_debug('Rate /min: ' . $counter_poll['counter_rate_min']);
|
||||
print_debug('Rate /5min: ' . $counter_poll['counter_rate_5min']);
|
||||
// Rate /h (more complex since counters grow up very rarely
|
||||
$counter_poll['counter_history'] = $counter_db['counter_history'] != '' ? json_decode($counter_db['counter_history'], TRUE) : [];
|
||||
// Now find first polled time around 3600s (1h)
|
||||
foreach ($counter_poll['counter_history'] as $polled_time => $value)
|
||||
{
|
||||
$diff = $counter_polled_time - $polled_time;
|
||||
if ($diff < (3600 + ($config['rrd']['step'] / 2))) // 3600 + 150 (possible change step in future)
|
||||
{
|
||||
if ($diff < 3300)
|
||||
{
|
||||
// If not have full hour history, use approximate to hour rate
|
||||
$period = $diff / 3600; // Period in hours (around 1)
|
||||
$counter_poll['counter_rate_hour'] = int_sub($counter_poll['counter_value'], $value) / $period;
|
||||
print_debug("Hour rate by approximate: ".$counter_poll['counter_value']." - $value / $period");
|
||||
} else {
|
||||
$counter_poll['counter_rate_hour'] = int_sub($counter_poll['counter_value'], $value); // Keep this value as integer, since we keep in history only 1 hour
|
||||
print_debug("Hour rate by history: ".$counter_poll['counter_value']." - $value");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Clear old entries
|
||||
unset($counter_poll['counter_history'][$polled_time]);
|
||||
}
|
||||
// Just if initially not exist history
|
||||
if (!isset($counter_poll['counter_rate_hour']))
|
||||
{
|
||||
$counter_poll['counter_rate_hour'] = $counter_poll['counter_rate_5min'] * 12;
|
||||
print_debug("Hour rate initially: ".$counter_poll['counter_rate_5min']." * 12");
|
||||
}
|
||||
print_debug('Rate /hour: ' . $counter_poll['counter_rate_hour']);
|
||||
|
||||
// Append last value to history and json it
|
||||
$counter_poll['counter_history'][$counter_polled_time] = $counter_poll['counter_value'];
|
||||
print_debug_vars($counter_poll['counter_history']);
|
||||
$counter_poll['counter_history'] = json_encode($counter_poll['counter_history']);
|
||||
|
||||
print_debug_vars($counter_poll, 1);
|
||||
|
||||
//print_cli_data("Value", $counter_poll['counter_value'] . "$unit ", 3);
|
||||
|
||||
// FIXME this block and the other block below it are kinda retarded. They should be merged and simplified.
|
||||
|
||||
if ($counter_db['counter_disable'])
|
||||
{
|
||||
$counter_poll['counter_event'] = 'ignore';
|
||||
$counter_poll['counter_status'] = 'Counter disabled.';
|
||||
} else {
|
||||
// Select param for calculate limit events
|
||||
switch ($counter_db['counter_limit_by'])
|
||||
{
|
||||
case 'sec':
|
||||
$limit_by = 'counter_rate';
|
||||
$limit_unit = "$unit/sec";
|
||||
break;
|
||||
case 'min':
|
||||
$limit_by = 'counter_rate_min';
|
||||
$limit_unit = "$unit/min";
|
||||
break;
|
||||
case '5min':
|
||||
$limit_by = 'counter_rate_5min';
|
||||
$limit_unit = "$unit/5min";
|
||||
break;
|
||||
case 'hour':
|
||||
$limit_by = 'counter_rate_hour';
|
||||
$limit_unit = "$unit/hour";
|
||||
break;
|
||||
case 'value':
|
||||
$limit_by = 'counter_value';
|
||||
$limit_unit = $unit;
|
||||
break;
|
||||
}
|
||||
|
||||
$counter_poll['counter_event'] = check_thresholds($counter_db['counter_limit_low'], $counter_db['counter_limit_low_warn'],
|
||||
$counter_db['counter_limit_warn'], $counter_db['counter_limit'],
|
||||
$counter_poll[$limit_by]);
|
||||
if ($counter_poll['counter_event'] === 'alert')
|
||||
{
|
||||
$counter_poll['counter_status'] = 'Counter critical thresholds exceeded.';
|
||||
}
|
||||
elseif ($counter_poll['counter_event'] === 'warning')
|
||||
{
|
||||
$counter_poll['counter_status'] = 'Counter warning thresholds exceeded.';
|
||||
} else {
|
||||
$counter_poll['counter_status'] = '';
|
||||
}
|
||||
|
||||
// Reset Alert if counter ignored
|
||||
if ($counter_poll['counter_event'] !== 'ok' && $counter_db['counter_ignore'])
|
||||
{
|
||||
$counter_poll['counter_event'] = 'ignore';
|
||||
$counter_poll['counter_status'] = 'Counter thresholds exceeded, but ignored.';
|
||||
}
|
||||
}
|
||||
|
||||
// If last change never set, use current time
|
||||
if (empty($counter_db['counter_last_change']))
|
||||
{
|
||||
$counter_db['counter_last_change'] = $counter_polled_time;
|
||||
}
|
||||
|
||||
if ($counter_poll['counter_event'] != $counter_db['counter_event'])
|
||||
{
|
||||
// Counter event changed, log and set counter_last_change
|
||||
$counter_poll['counter_last_change'] = $counter_polled_time;
|
||||
|
||||
if ($counter_db['counter_event'] === 'ignore')
|
||||
{
|
||||
print_message("[%yCounter Ignored%n]", 'color');
|
||||
}
|
||||
elseif (is_numeric($counter_db['counter_limit_low']) &&
|
||||
$counter_db[$limit_by] >= $counter_db['counter_limit_low'] &&
|
||||
$counter_poll[$limit_by] < $counter_db['counter_limit_low'])
|
||||
{
|
||||
// If old value greater than low limit and new value less than low limit
|
||||
$msg = ucfirst($class) . " Alarm: " . $device['hostname'] . " " . $counter_db['counter_descr'] . " is under threshold: " . $counter_poll[$limit_by] . "$limit_unit (< " . $counter_db['counter_limit_low'] . "$limit_unit)";
|
||||
log_event(ucfirst($class) . ' ' . $counter_db['counter_descr'] . " under threshold: " . $counter_poll[$limit_by] . " $limit_unit (< " . $counter_db['counter_limit_low'] . " $limit_unit)", $device, 'counter', $counter_db['counter_id'], 'warning');
|
||||
}
|
||||
elseif (is_numeric($counter_db['counter_limit']) &&
|
||||
$counter_db[$limit_by] <= $counter_db['counter_limit'] &&
|
||||
$counter_poll[$limit_by] > $counter_db['counter_limit'])
|
||||
{
|
||||
// If old value less than high limit and new value greater than high limit
|
||||
$msg = ucfirst($class) . " Alarm: " . $device['hostname'] . " " . $counter_db['counter_descr'] . " is over threshold: " . $counter_poll[$limit_by] . "$limit_unit (> " . $counter_db['counter_limit'] . "$limit_unit)";
|
||||
log_event(ucfirst($class) . ' ' . $counter_db['counter_descr'] . " above threshold: " . $counter_poll[$limit_by] . " $limit_unit (> " . $counter_db['counter_limit'] . " $limit_unit)", $device, 'counter', $counter_db['counter_id'], 'warning');
|
||||
}
|
||||
} else {
|
||||
// If counter not changed, leave old last_change
|
||||
$counter_poll['counter_last_change'] = $counter_db['counter_last_change'];
|
||||
}
|
||||
|
||||
// Send statistics array via AMQP/JSON if AMQP is enabled globally and for the ports module
|
||||
if ($config['amqp']['enable'] == TRUE && $config['amqp']['modules']['counters'])
|
||||
{
|
||||
$json_data = array('value' => $counter_poll['counter_value']);
|
||||
messagebus_send(array('attribs' => array('t' => $counter_polled_time,
|
||||
'device' => $device['hostname'],
|
||||
'device_id' => $device['device_id'],
|
||||
'e_type' => 'counter',
|
||||
'e_class' => $counter_db['counter_class'],
|
||||
'e_type' => $type,
|
||||
'e_index' => $counter_db['counter_index']),
|
||||
'data' => $json_data));
|
||||
}
|
||||
|
||||
// Add table row
|
||||
|
||||
$type = $counter_db['counter_mib'] . '::' . $counter_db['counter_object'] . '.' . $counter_db['counter_index'];
|
||||
$format = (string)$config['counter_types'][$counter_db['counter_class']]['format'];
|
||||
$table_rows[] = array($counter_db['counter_descr'],
|
||||
$counter_db['counter_class'],
|
||||
$type,
|
||||
$counter_poll['counter_value'] . $unit,
|
||||
format_value($counter_poll['counter_rate'], $format) . '/s | ' . format_value($counter_poll['counter_rate_min'], $format) . '/min | ' .
|
||||
format_value($counter_poll['counter_rate_5min'], $format) . '/5min | ' . format_value($counter_poll['counter_rate_hour'], $format) . '/h',
|
||||
$counter_poll['counter_event'],
|
||||
format_unixtime($counter_poll['counter_last_change']),
|
||||
$counter_db['poller_type']);
|
||||
|
||||
// Update StatsD/Carbon
|
||||
if ($config['statsd']['enable'] == TRUE)
|
||||
{
|
||||
StatsD::gauge(str_replace(".", "_", $device['hostname']) . '.' . 'counter' . '.' . $counter_db['counter_class'] . '.' . $type . '.' . $counter_db['counter_index'], $counter_poll['counter_value']);
|
||||
}
|
||||
|
||||
// Update RRD (Counter store both rate(counter) and value(sensor)
|
||||
//$rrd_file = get_counter_rrd($device, $counter_db);
|
||||
//rrdtool_create($device, $rrd_file, "DS:counter:GAUGE:600:-20000:U");
|
||||
//rrdtool_update($device, $rrd_file, "N:" . $counter_poll['counter_value']);
|
||||
$ds = [
|
||||
'sensor' => $counter_poll['counter_value'],
|
||||
// RRD COUNTER must be integer
|
||||
'counter' => (is_numeric($counter_poll['counter_value']) ? round($counter_poll['counter_value'], 0) : 0)
|
||||
];
|
||||
rrdtool_update_ng($device, 'counter', $ds, $counter_db['counter_id']);
|
||||
|
||||
// Enable graph
|
||||
$graphs[$counter_db['counter_class']] = TRUE;
|
||||
|
||||
// Check alerts
|
||||
$metrics = array();
|
||||
|
||||
$metrics['counter_value'] = $counter_poll['counter_value'];
|
||||
$metrics['counter_rate'] = $counter_poll['counter_rate'];
|
||||
$metrics['counter_rate_min'] = $counter_poll['counter_rate_min'];
|
||||
$metrics['counter_rate_5min'] = $counter_poll['counter_rate_5min'];
|
||||
$metrics['counter_rate_hour'] = $counter_poll['counter_rate_hour'];
|
||||
$metrics['counter_event'] = $counter_poll['counter_event'];
|
||||
$metrics['counter_event_uptime'] = $counter_polled_time - $counter_poll['counter_last_change'];
|
||||
$metrics['counter_status'] = $counter_poll['counter_status'];
|
||||
|
||||
check_entity('counter', $counter_db, $metrics);
|
||||
|
||||
// Add to MultiUpdate SQL State
|
||||
|
||||
$GLOBALS['multi_update_db'][] = array(
|
||||
'counter_id' => $counter_db['counter_id'], // UNIQUE index
|
||||
//'device_id' => $counter_db['device_id'], // Required
|
||||
'counter_value' => $counter_poll['counter_value'],
|
||||
'counter_rate' => $counter_poll['counter_rate'],
|
||||
'counter_rate_5min' => $counter_poll['counter_rate_5min'],
|
||||
'counter_rate_hour' => $counter_poll['counter_rate_hour'],
|
||||
'counter_history' => $counter_poll['counter_history'],
|
||||
'counter_event' => $counter_poll['counter_event'],
|
||||
'counter_status' => $counter_poll['counter_status'],
|
||||
'counter_last_change' => $counter_poll['counter_last_change'],
|
||||
'counter_polled' => $counter_polled_time);
|
||||
}
|
||||
}
|
||||
|
||||
// DOCME needs phpdoc block
|
||||
// TESTME needs unit testing
|
||||
function check_valid_counter($device, $poller_type = 'snmp') {
|
||||
$valid = &$GLOBALS['valid']['counter'];
|
||||
|
||||
$entries = dbFetchRows("SELECT * FROM `counters` WHERE `device_id` = ? AND `poller_type` = ? AND `counter_deleted` = '0'", array($device['device_id'], $poller_type));
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
$index = $entry['counter_index'];
|
||||
$class = $entry['counter_class'];
|
||||
$object = $entry['counter_object'];
|
||||
$mib = strlen($entry['counter_mib']) ? $entry['counter_mib'] : '__';
|
||||
if (!$valid[$class][$mib][$object][$index] ||
|
||||
$valid[$class][$mib][$object][$index] > 1) {// Duplicate entry
|
||||
echo("-");
|
||||
print_debug("Status deleted: $mib::$object.$index");
|
||||
//dbDelete('counter', "`counter_id` = ?", array($entry['counter_id']));
|
||||
|
||||
dbUpdate(array('counter_deleted' => '1'), 'counters', '`counter_id` = ?', array($entry['counter_id']));
|
||||
|
||||
foreach (get_entity_attribs('counter', $entry['counter_id']) as $attrib_type => $value) {
|
||||
del_entity_attrib('counter', $entry['counter_id'], $attrib_type);
|
||||
}
|
||||
log_event("Counter deleted: ".$entry['counter_class']." ". $entry['counter_index']." ".$entry['counter_descr'], $device, 'counter', $entry['counter_id']);
|
||||
} else {
|
||||
// Increase counter as hint for find duplicates
|
||||
$valid[$class][$mib][$object][$index]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DOCME needs phpdoc block
|
||||
// TESTME needs unit testing
|
||||
function get_counter_rrd($device, $counter)
|
||||
{
|
||||
global $config;
|
||||
|
||||
# For IPMI/agent, counters 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'][$counter['counter_mib']]['sensor_descr']) && $config['mibs'][$counter['counter_mib']]['sensor_descr']) || // per mib definition
|
||||
($counter['poller_type'] != "snmp" && $counter['poller_type'] != ''))
|
||||
{
|
||||
$index = $counter['counter_descr'];
|
||||
} else {
|
||||
$index = $counter['counter_index'];
|
||||
}
|
||||
|
||||
if ($counter['poller_type'] != "snmp" && $counter['poller_type'] != '')
|
||||
{
|
||||
$rrd_file = "counter-" . $counter['counter_class'] . "-" . $counter['poller_type'] . "-" . $index . ".rrd";
|
||||
}
|
||||
elseif ($counter['counter_mib'] == '' || $counter['counter_object'] == '')
|
||||
{
|
||||
// Seems as numeric Oid polling (not known MIB & Oid)
|
||||
// counter_oid is full numeric oid with index
|
||||
$rrd_file = "counter-" . $counter['counter_class'] . "-" . $counter['counter_oid'] . ".rrd";
|
||||
} else {
|
||||
$rrd_file = "counter-" . $counter['counter_class'] . "-" . $counter['counter_mib'] . "-" . $counter['counter_object'] . "-" . $index . ".rrd";
|
||||
}
|
||||
|
||||
return($rrd_file);
|
||||
}
|
||||
|
||||
// EOF
|
1819
includes/entities/device.inc.php
Normal file
1819
includes/entities/device.inc.php
Normal file
File diff suppressed because it is too large
Load Diff
987
includes/entities/ip-address.inc.php
Normal file
987
includes/entities/ip-address.inc.php
Normal file
@ -0,0 +1,987 @@
|
||||
<?php
|
||||
/**
|
||||
* Observium
|
||||
*
|
||||
* This file is part of Observium.
|
||||
*
|
||||
* @package observium
|
||||
* @subpackage entities
|
||||
* @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2020 Observium Limited
|
||||
*
|
||||
*/
|
||||
|
||||
function discover_ip_address_definition($device, $mib, $entry)
|
||||
{
|
||||
echo(' ['); // FIXME
|
||||
|
||||
// Check that types listed in skip_if_valid_exist have already been found
|
||||
//if (discovery_check_if_type_exist($entry, 'ip-address')) { echo '!]'; return; }
|
||||
|
||||
// Check array requirements list
|
||||
//if (discovery_check_requires_pre($device, $entry, 'ip-address')) { echo '!]'; return; }
|
||||
|
||||
// Fetch table or Oids
|
||||
$table_oids = array('oid_address', 'oid_ifIndex', 'oid_prefix', 'oid_mask', 'oid_gateway', // 'oid_version',
|
||||
'oid_origin', 'oid_type', 'oid_vrf', 'oid_mac', 'oid_extra');
|
||||
$ip_array = discover_fetch_oids($device, $mib, $entry, $table_oids);
|
||||
|
||||
// Just append mib name to definition entry, for simple pass to external functions
|
||||
if (empty($entry['mib'])) {
|
||||
$entry['mib'] = $mib;
|
||||
}
|
||||
|
||||
// Index delimiter, for split index parts
|
||||
// I.e. in HUAWEI-IF-EXT-MIB, CISCOSB-IPv6
|
||||
$index_delimiter = is_flag_set(OBS_SNMP_INDEX_PARTS, $entry['snmp_flags']) ? '->' : '.';
|
||||
|
||||
// not indexed part, see CPI-UNIFIED-MIB
|
||||
if (is_flag_set(OBS_SNMP_NOINDEX, $entry['snmp_flags']) &&
|
||||
isset($ip_array['']) && count($ip_array) > 1) {
|
||||
$ip_array_extra = $ip_array[''];
|
||||
unset($ip_array['']);
|
||||
}
|
||||
|
||||
//$ips_count = count($ip_array);
|
||||
foreach ($ip_array as $index => $ip_address) {
|
||||
// add index and index parts for tags replace
|
||||
$ip_address['index'] = $index;
|
||||
foreach (explode($index_delimiter, $index) as $i => $part) {
|
||||
$ip_address['index' . $i] = $part;
|
||||
}
|
||||
// append extra (not indexed) array
|
||||
if (isset($ip_array_extra)) {
|
||||
$ip_address = array_merge($ip_address, $ip_array_extra);
|
||||
}
|
||||
print_debug_vars($ip_address);
|
||||
|
||||
// ifIndex
|
||||
$ifIndex = set_value_param_definition('ifIndex', $entry, $ip_address);
|
||||
if (str_contains($ifIndex, '%')) {
|
||||
// I.e. in CISCOSB-IPv6, for addresses with ifIndex parts:
|
||||
// ipv6z->ff:02:00:00:00:00:00:00:00:00:00:01:ff:a3:3f:49%100000
|
||||
list(, $ifIndex) = explode('%', $ifIndex);
|
||||
}
|
||||
// Rule-based entity linking
|
||||
if (!is_numeric($ifIndex) &&
|
||||
$measured = entity_measured_match_definition($device, $entry, $ip_address, 'ip-address')) {
|
||||
$ifIndex = $measured['ifIndex'];
|
||||
//print_debug_vars($measured);
|
||||
}
|
||||
// Link by MAC
|
||||
if (!is_numeric($ifIndex) || $ifIndex == 0) {
|
||||
$mac = set_value_param_definition('mac', $entry, $ip_address);
|
||||
if (strlen($mac) && $port_id = get_port_id_by_mac($device, $mac)) {
|
||||
$port = get_port_by_id_cache($port_id);
|
||||
$ifIndex = $port['ifIndex'];
|
||||
}
|
||||
}
|
||||
if (!is_numeric($ifIndex)) {
|
||||
print_debug("Excluded by unknown ifIndex [$ifIndex]");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set base ip array
|
||||
$data = [ 'ifIndex' => $ifIndex ];
|
||||
|
||||
// IP address
|
||||
$ip = set_value_param_definition('address', $entry, $ip_address);
|
||||
if (str_contains($ip, '%')) {
|
||||
// I.e. in CISCOSB-IPv6, for addresses with ifIndex parts:
|
||||
// ipv6z->ff:02:00:00:00:00:00:00:00:00:00:01:ff:a3:3f:49%100000
|
||||
list($ip) = explode('%', $ip);
|
||||
}
|
||||
// IP address with prefix
|
||||
if (str_contains($ip, '/')) {
|
||||
// I.e. VIPTELA-OPER-VPN address+prefix: 10.123.10.69/32
|
||||
list($ip, $prefix) = explode('/', $ip);
|
||||
if (is_numeric($prefix)) {
|
||||
$data['prefix'] = $prefix;
|
||||
} else {
|
||||
$data['mask'] = $prefix;
|
||||
}
|
||||
}
|
||||
$ip = hex2ip($ip);
|
||||
|
||||
$data['ip'] = $ip;
|
||||
|
||||
// Other ip params: origin, type, prefix, mask
|
||||
foreach ([ 'origin', 'type', 'prefix', 'mask', 'gateway', 'vrf' ] as $param) {
|
||||
if (!isset($data[$param]) && $value = set_value_param_definition($param, $entry, $ip_address)) {
|
||||
if ($param === 'prefix' && !is_numeric($value)) {
|
||||
// Always explode prefix part from oid value, ie:
|
||||
// cIpAddressPrefix.ipv6."20:01:04:70:00:15:00:bb:00:00:00:00:00:00:00:02" = cIpAddressPfxOrigin.450.ipv6."20:01:04:70:00:15:00:bb:00:00:00:00:00:00:00:00".64
|
||||
$tmp_prefix = explode('.', $value);
|
||||
$value = end($tmp_prefix);
|
||||
}
|
||||
$data[$param] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Check array requirements list
|
||||
if (discovery_check_requires($device, $entry, array_merge($ip_address, $data), 'ip-address')) { continue; }
|
||||
|
||||
discover_add_ip_address($device, $mib, $data);
|
||||
}
|
||||
|
||||
echo '] ';
|
||||
|
||||
}
|
||||
|
||||
function discover_add_ip_address($device, $mib, $entry) {
|
||||
global $ip_data;
|
||||
|
||||
$ip = $entry['ip'];
|
||||
if (isset($entry['prefix']) && ($entry['prefix'] === 'zeroDotZero' || safe_empty($entry['prefix']))) {
|
||||
unset($entry['prefix']);
|
||||
}
|
||||
|
||||
// IP version
|
||||
$ip_version = get_ip_version($ip);
|
||||
$ip_version = 'ipv' . $ip_version;
|
||||
|
||||
// ifIndex
|
||||
if ($entry['ifIndex'] == 0 && !isset($ip_data[$ip_version][0])) {
|
||||
// When used system table/oids without known ifIndex,
|
||||
// try to find correct ifIndex and update entry
|
||||
// ie: SWSYSTEM-MIB, ENVIROMUX16D
|
||||
foreach ($ip_data[$ip_version] as $ind => $tmp) {
|
||||
if (isset($tmp[$ip])) {
|
||||
print_debug("Found ifIndex $ind for IP $ip");
|
||||
$entry['ifIndex'] = $ind;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$ifIndex = $entry['ifIndex'];
|
||||
|
||||
switch ($ip_version) {
|
||||
case 'ipv4':
|
||||
// IPv4
|
||||
$prefix = get_ip_prefix($entry);
|
||||
if (!is_ipv4_valid($ip, $prefix)) {
|
||||
print_debug("Address '$ip/$prefix' skipped as invalid.");
|
||||
return;
|
||||
}
|
||||
$entry['prefix'] = $prefix;
|
||||
break;
|
||||
|
||||
case 'ipv6':
|
||||
// IPv6
|
||||
$prefix = get_ip_prefix($entry);
|
||||
if (!is_ipv6_valid($ip, $prefix)) {
|
||||
print_debug("Address '$ip/$prefix' skipped as invalid.");
|
||||
return;
|
||||
}
|
||||
$entry['prefix'] = $prefix;
|
||||
break;
|
||||
|
||||
default:
|
||||
print_debug("Excluded by unknown IP address [$ip]");
|
||||
return;
|
||||
}
|
||||
|
||||
// Always re-detect unicast type, while device can report it incorrectly
|
||||
if (empty($entry['type']) || $entry['type'] === 'unicast') {
|
||||
$entry['type'] = get_ip_type("$ip/$prefix");
|
||||
}
|
||||
|
||||
if (!isset($ip_data[$ip_version][$ifIndex][$ip])) {
|
||||
$ip_data[$ip_version][$ifIndex][$ip] = $entry;
|
||||
print_debug("Added $ip");
|
||||
print_debug_vars($entry);
|
||||
echo '.';
|
||||
} else {
|
||||
// Compare both and merge best
|
||||
$old_entry = $ip_data[$ip_version][$ifIndex][$ip];
|
||||
if (isset($old_entry['prefix']) && $old_entry['prefix'] === 'zeroDotZero') {
|
||||
unset($old_entry['prefix']);
|
||||
}
|
||||
$check_prefix = $old_entry['prefix'] < 3 ||
|
||||
($ip_version === 'ipv4' && $old_entry['prefix'] == '32') ||
|
||||
($ip_version === 'ipv6' && $old_entry['prefix'] == '128');
|
||||
$updated = [];
|
||||
foreach (array_keys($entry) as $param) {
|
||||
//print_debug_vars($old_entry);
|
||||
//print_debug_vars($entry);
|
||||
if (!isset($old_entry[$param]) || safe_empty($old_entry[$param])) {
|
||||
$ip_data[$ip_version][$ifIndex][$ip][$param] = $entry[$param];
|
||||
$updated[] = $param;
|
||||
} elseif (($param === 'type' && $old_entry[$param] === 'anycast' && strlen($entry[$param])) ||
|
||||
($param === 'prefix' && $check_prefix && $entry[$param] > 0)) {
|
||||
$ip_data[$ip_version][$ifIndex][$ip][$param] = $entry[$param];
|
||||
$updated[] = $param;
|
||||
}
|
||||
}
|
||||
if (count($updated)) {
|
||||
print_debug("Already exist $ip, updated params [".implode(', ', $updated)."]");
|
||||
} else {
|
||||
print_debug("Already exist $ip, skip");
|
||||
}
|
||||
//print_debug_vars($ip_data[$ip_version][$ifIndex][$ip]);
|
||||
//print_debug_vars($entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert IPv4 netmask to CIDR
|
||||
*
|
||||
* @param string $netmask
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
function netmask2cidr($netmask) {
|
||||
$addr = Net_IPv4::parseAddress("1.2.3.4/$netmask");
|
||||
return is_intnum($addr->bitmask) ? $addr->bitmask : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert CIDR to IPv4 netmask
|
||||
* @param $cidr
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
function cidr2netmask($cidr) {
|
||||
if (!is_intnum($cidr) || $cidr < 0 || $cidr > 32) {
|
||||
return NULL;
|
||||
}
|
||||
return long2ip(ip2long("255.255.255.255") << (32 - $cidr));
|
||||
}
|
||||
|
||||
function get_ip_prefix($entry) {
|
||||
if (!is_array($entry)) {
|
||||
// Convert ip/prefix string to common array
|
||||
$address = $entry;
|
||||
$entry = [];
|
||||
list($entry['ip'], $prefix) = explode('/', $address);
|
||||
if (!safe_empty($prefix)) {
|
||||
if (is_numeric($prefix)) {
|
||||
$entry['prefix'] = $prefix;
|
||||
} else {
|
||||
$entry['mask'] = $prefix;
|
||||
}
|
||||
}
|
||||
}
|
||||
print_debug_vars($entry);
|
||||
|
||||
switch (get_ip_version($entry['ip'])) {
|
||||
case 4:
|
||||
if (!safe_empty($entry['prefix']) && $entry['prefix'] !== 'zeroDotZero') {
|
||||
$prefix = netmask2cidr($entry['prefix']);
|
||||
} else { //if (!safe_empty($entry['mask']) && $entry['mask'] !== 'zeroDotZero') {
|
||||
$prefix = netmask2cidr($entry['mask']);
|
||||
}
|
||||
if (!is_intnum($prefix)) {
|
||||
if (isset($entry['gateway']) && get_ip_version($entry['gateway']) === 4) {
|
||||
// Derp way for detect prefix by gateway (limit by /24 - /32)
|
||||
$prefix = 24;
|
||||
while ($prefix < 32) {
|
||||
$net = Net_IPv4::parseAddress($entry['ip'].'/'.$prefix);
|
||||
if (Net_IPv4::ipInNetwork($entry['gateway'], $net->network.'/'.$prefix)) {
|
||||
// Gateway IP in network, stop loop
|
||||
print_debug("Prefix '$prefix' detected by IP '${entry['ip']}' and Gateway '${entry['gateway']}'.");
|
||||
break;
|
||||
}
|
||||
$prefix++;
|
||||
}
|
||||
// Still not found prefix, try increase now /23 - /8
|
||||
if ($prefix === 32 && $entry['ip'] !== $entry['gateway']) {
|
||||
$tmp_prefix = 23;
|
||||
while ($tmp_prefix >= 8) {
|
||||
$net = Net_IPv4::parseAddress($entry['ip'].'/'.$tmp_prefix);
|
||||
if (Net_IPv4::ipInNetwork($entry['gateway'], $net->network.'/'.$tmp_prefix)) {
|
||||
// Gateway IP in network, stop loop
|
||||
$prefix = $tmp_prefix;
|
||||
print_debug("Prefix '$prefix' detected by IP '${entry['ip']}' and Gateway '${entry['gateway']}'.");
|
||||
break;
|
||||
}
|
||||
$tmp_prefix--;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$prefix = 32;
|
||||
}
|
||||
}
|
||||
return (int) $prefix;
|
||||
case 6:
|
||||
if (!safe_empty($entry['prefix']) && $entry['prefix'] !== 'zeroDotZero') {
|
||||
$prefix = $entry['prefix'];
|
||||
}
|
||||
if (!is_intnum($prefix) || $prefix < 0 || $prefix > 128) {
|
||||
$prefix = 128;
|
||||
}
|
||||
return (int) $prefix;
|
||||
}
|
||||
|
||||
// Incorrect IP
|
||||
print_debug("Incorrect: ${entry['ip']}");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns IP version for string or FALSE if string not an IP
|
||||
*
|
||||
* Examples:
|
||||
* get_ip_version('127.0.0.1') === 4
|
||||
* get_ip_version('::1') === 6
|
||||
* get_ip_version('my_hostname') === FALSE
|
||||
*
|
||||
* @param string $address IP address string
|
||||
* @return mixed IP version or FALSE if passed incorrect address
|
||||
*/
|
||||
function get_ip_version($address) {
|
||||
|
||||
if (str_contains($address, '/')) {
|
||||
// Dump condition,
|
||||
// IPs with CIDR not correct for us here
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$address_version = FALSE;
|
||||
if (preg_match('%^'.OBS_PATTERN_IPV4.'$%', $address)) {
|
||||
$address_version = 4;
|
||||
} elseif (preg_match('%^'.OBS_PATTERN_IPV6.'$%i', $address)) {
|
||||
$address_version = 6;
|
||||
}
|
||||
|
||||
return $address_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for compress IPv6 address and keep as is IPv4 (already compressed)
|
||||
*
|
||||
* @param string|array $address IPv4 or IPv6 address
|
||||
* @param bool $force If true, an already compressed IP address will be compressed again
|
||||
*
|
||||
* @return string|array Compressed address string
|
||||
*/
|
||||
function ip_compress($address, $force = TRUE) {
|
||||
|
||||
// Recursive compress for arrays
|
||||
if (is_array($address)) {
|
||||
$array = [];
|
||||
foreach ($address as $entry) {
|
||||
$array[] = ip_compress($entry, $force);
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
list($ip, $net) = explode('/', $address);
|
||||
if (get_ip_version($ip) === 6) {
|
||||
$address = Net_IPv6::compress($ip, $force);
|
||||
if (is_numeric($net) && ($net >= 0) && ($net <= 128)) {
|
||||
$address .= '/'.$net;
|
||||
}
|
||||
}
|
||||
|
||||
return $address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for uncompress IPv6 address and keep as is IPv4
|
||||
*
|
||||
* @param string|array $address IPv4 or IPv6 address
|
||||
* @param bool $leading_zeros If true, the uncompressed address has a fixed length
|
||||
*
|
||||
* @return string|array Uncompressed address string
|
||||
*/
|
||||
function ip_uncompress($address, $leading_zeros = TRUE) {
|
||||
|
||||
// Recursive uncompress for arrays
|
||||
if (is_array($address)) {
|
||||
$array = [];
|
||||
foreach ($address as $entry) {
|
||||
$array[] = ip_uncompress($entry, $leading_zeros);
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
list($ip, $net) = explode('/', $address);
|
||||
if (get_ip_version($ip) === 6) {
|
||||
$address = Net_IPv6::uncompress($ip, $leading_zeros);
|
||||
if (is_numeric($net) && ($net >= 0) && ($net <= 128)) {
|
||||
$address .= '/'.$net;
|
||||
}
|
||||
}
|
||||
|
||||
return $address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return safe key for use in arrays by ip address (replaced with __ if IP is empty or 0.0.0.0/::0)
|
||||
* Additionally, passed hostname and/or IPv6 address compressed.
|
||||
* @param string $hostname Hostname (or IP address)
|
||||
* @param string|null $ip IPv4/IPv6 addresss
|
||||
*
|
||||
* @return string Return compressed IP address or __ if IP is empty
|
||||
*/
|
||||
function safe_ip_hostname_key(&$hostname, &$ip = NULL)
|
||||
{
|
||||
$hostname = strtolower($hostname);
|
||||
|
||||
// Hostname is IP address
|
||||
if ($hostname_ip_version = get_ip_version($hostname))
|
||||
{
|
||||
$hostname = ip_compress($hostname);
|
||||
if (empty($ip))
|
||||
{
|
||||
// If hostname is IP, set remote_ip same
|
||||
$ip = $hostname;
|
||||
}
|
||||
}
|
||||
|
||||
$ip_type = get_ip_type($ip);
|
||||
if ($ip_type && $ip_type != 'unspecified')
|
||||
{
|
||||
$ip = ip_compress($ip);
|
||||
$ip_key = $ip;
|
||||
} else {
|
||||
$ip_key = '__';
|
||||
}
|
||||
|
||||
return $ip_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given IPv4 address (and prefix length) is valid.
|
||||
*
|
||||
* @param string $ipv4_address IPv4 Address
|
||||
* @param string $ipv4_prefixlen IPv4 Prefix length (optional, either 24 or 255.255.255.0)
|
||||
*
|
||||
* @return bool Returns TRUE if address is valid, FALSE if not valid.
|
||||
*/
|
||||
// TESTME needs unit testing
|
||||
function is_ipv4_valid($ipv4_address, $ipv4_prefixlen = NULL) {
|
||||
|
||||
if (str_contains($ipv4_address, '/')) {
|
||||
list($ipv4_address, $ipv4_prefixlen) = explode('/', $ipv4_address);
|
||||
}
|
||||
$ip_full = $ipv4_address . '/' . $ipv4_prefixlen;
|
||||
|
||||
// False if invalid IPv4 syntax
|
||||
if (strlen((string)$ipv4_prefixlen) &&
|
||||
!preg_match('%^'.OBS_PATTERN_IPV4_NET.'$%', $ip_full)) {
|
||||
// Address with prefix
|
||||
return FALSE;
|
||||
}
|
||||
if (!preg_match('%^'.OBS_PATTERN_IPV4.'$%i', $ipv4_address)) {
|
||||
// Address without prefix
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$ipv4_type = get_ip_type($ip_full);
|
||||
|
||||
// False if unspecified
|
||||
$ignore_type = [ 'unspecified' ];
|
||||
// It this address types (broadcast) removed from config by user, allow to discover it too
|
||||
// Do not ignore link-local for IPv4, this is not same as for IPv6
|
||||
// if (in_array('link-local', $GLOBALS['config']['ip-address']['ignore_type']))
|
||||
// {
|
||||
// $ignore_type[] = 'link-local';
|
||||
// }
|
||||
if (in_array('broadcast', $GLOBALS['config']['ip-address']['ignore_type'])) {
|
||||
$ignore_type[] = 'broadcast';
|
||||
}
|
||||
if (in_array($ipv4_type, $ignore_type)) {
|
||||
print_debug("Address $ip_full marked as invalid by type [$ipv4_type].");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given IPv6 address (and prefix length) is valid.
|
||||
* Link-local addresses are considered invalid.
|
||||
*
|
||||
* @param string $ipv6_address IPv6 Address
|
||||
* @param string $ipv6_prefixlen IPv6 Prefix length (optional)
|
||||
*
|
||||
* @return bool Returns TRUE if address is valid, FALSE if not valid.
|
||||
*/
|
||||
// TESTME needs unit testing
|
||||
function is_ipv6_valid($ipv6_address, $ipv6_prefixlen = NULL) {
|
||||
if (str_contains($ipv6_address, '/')) {
|
||||
list($ipv6_address, $ipv6_prefixlen) = explode('/', $ipv6_address);
|
||||
}
|
||||
$ip_full = $ipv6_address . '/' . $ipv6_prefixlen;
|
||||
|
||||
// False if invalid IPv6 syntax
|
||||
if (strlen((string)$ipv6_prefixlen) &&
|
||||
!preg_match('%^'.OBS_PATTERN_IPV6_NET.'$%i', $ip_full)) {
|
||||
// Address with prefix
|
||||
return FALSE;
|
||||
}
|
||||
if (!preg_match('%^'.OBS_PATTERN_IPV6.'$%i', $ipv6_address)) {
|
||||
// Address without prefix
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$ipv6_type = get_ip_type($ip_full);
|
||||
|
||||
// False if link-local, unspecified
|
||||
$ignore_type = [ 'unspecified' ];
|
||||
// It this address types (link-local, broadcast) removed from config by user, allow to discover it too
|
||||
if (in_array('link-local', $GLOBALS['config']['ip-address']['ignore_type'])) {
|
||||
$ignore_type[] = 'link-local';
|
||||
}
|
||||
if (in_array('broadcast', $GLOBALS['config']['ip-address']['ignore_type'])) {
|
||||
$ignore_type[] = 'broadcast';
|
||||
}
|
||||
if (in_array($ipv6_type, $ignore_type)) {
|
||||
print_debug("Address $ip_full marked as invalid by type [$ipv6_type].");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect IP type.
|
||||
* Based on https://www.ripe.net/manage-ips-and-asns/ipv6/ipv6-address-types/ipv6-address-types
|
||||
*
|
||||
* Known types:
|
||||
* - unspecified : ::/128, 0.0.0.0
|
||||
* - loopback : ::1/128, 127.0.0.1
|
||||
* - ipv4mapped : only for IPv6 ::ffff/96 (::ffff:192.0.2.47)
|
||||
* - private : fc00::/7 (fdf8:f53b:82e4::53),
|
||||
* 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
|
||||
* - link-local : fe80::/10 (fe80::200:5aee:feaa:20a2),
|
||||
* 169.254.0.0/16
|
||||
* - teredo : only for IPv6 2001:0000::/32 (2001:0000:4136:e378:8000:63bf:3fff:fdd2)
|
||||
* - benchmark : 2001:0002::/48 (2001:0002:6c::430),
|
||||
* 198.18.0.0/15
|
||||
* - orchid : only for IPv6 2001:0010::/28 (2001:10:240:ab::a)
|
||||
* - 6to4 : 2002::/16 (2002:cb0a:3cdd:1::1),
|
||||
* 192.88.99.0/24
|
||||
* - documentation : 2001:db8::/32 (2001:db8:8:4::2),
|
||||
* 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24
|
||||
* - global-unicast : only for IPv6 2000::/3
|
||||
* - multicast : ff00::/8 (ff01:0:0:0:0:0:0:2), 224.0.0.0/4
|
||||
* - unicast : all other
|
||||
*
|
||||
* @param string $address IPv4 or IPv6 address string
|
||||
* @return string IP type
|
||||
*/
|
||||
function get_ip_type($address)
|
||||
{
|
||||
global $config;
|
||||
|
||||
list($ip, $bits) = explode('/', trim($address)); // Remove subnet/mask if exist
|
||||
|
||||
$ip_version = get_ip_version($ip);
|
||||
switch ($ip_version)
|
||||
{
|
||||
case 4:
|
||||
|
||||
// Detect IPv4 broadcast
|
||||
if (strlen($bits))
|
||||
{
|
||||
$ip_parse = Net_IPv4::parseAddress($address);
|
||||
if ($ip == $ip_parse->broadcast && $ip_parse->bitmask < 31) // Do not set /31 and /32 as broadcast!
|
||||
{
|
||||
$ip_type = 'broadcast';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// no break here!
|
||||
case 6:
|
||||
|
||||
$ip_type = ($ip_version == 4) ? 'unicast': 'reserved'; // Default for any valid address
|
||||
foreach ($config['ip_types'] as $type => $entry)
|
||||
{
|
||||
if (isset($entry['networks']) && match_network($ip, $entry['networks'], TRUE))
|
||||
{
|
||||
// Stop loop if IP founded in networks
|
||||
$ip_type = $type;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Not valid IP address
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return $ip_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse string for valid IP/Network queries.
|
||||
*
|
||||
* Valid queries example:
|
||||
* - 10.0.0.0/8 - exactly IPv4 network, matches to 10.255.0.2, 10.0.0.0, 10.255.255.255
|
||||
* - 10.12.0.3/8 - same as previous, NOTE network detected by prefix: 10.0.0.0/8
|
||||
* - 10.12.0.3 - single IPv4 address
|
||||
* - *.12.0.3, 10.*.3 - * matching by any string in address
|
||||
* - ?.12.0.3, 10.?.?.3 - ? matching by single char
|
||||
* - 10.12 - match by part of address, matches to 10.12.2.3, 10.122.3.3, 1.2.110.120
|
||||
* - 1762::b03:1:ae00/119 - exactly IPv6 network, matches to 1762::b03:1:ae00, 1762::B03:1:AF18, 1762::b03:1:afff
|
||||
* - 1762:0:0:0:0:B03:1:AF18/119 - same as previous, NOTE network detected by prefix: 1762::b03:1:ae00/119
|
||||
* - 1762:0:0:0:0:B03:1:AF18 - single IPv6 address
|
||||
* - *::B03:1:AF18, 1762::*:AF18 - * matching by any string in address
|
||||
* - ?::B03:1:AF18, 1762::?:AF18 - ? matching by single char
|
||||
* - 1762:b03 - match by part of address, matches to 1:AF18:1762::B03, 1762::b03:1:ae00
|
||||
*
|
||||
* Return array contain this params:
|
||||
* 'query_type' - which query type required.
|
||||
* Possible: single (single IP address),
|
||||
* network (addresses inside network),
|
||||
* like, %like% (part of address with masks *, ?)
|
||||
* 'ip_version' - numeric IP version (4, 6)
|
||||
* 'ip_type' - ipv4 or ipv6
|
||||
* 'address' - string with passed IP address without prefixes or address part
|
||||
* 'prefix' - detected network prefix
|
||||
* 'network' - detected network with prefix
|
||||
* 'network_start' - first address of network
|
||||
* 'network_end' - last address of network (broadcast)
|
||||
*
|
||||
* @param string $network Network/IP query string
|
||||
* @return array Array with parsed network params
|
||||
*/
|
||||
function parse_network($network)
|
||||
{
|
||||
$network = trim($network);
|
||||
|
||||
$array = array(
|
||||
'query_type' => 'network', // Default query type by valid network with prefix
|
||||
);
|
||||
|
||||
if (preg_match('%^'.OBS_PATTERN_IPV4_NET.'$%', $network, $matches))
|
||||
{
|
||||
// Match by valid IPv4 network
|
||||
$array['ip_version'] = 4;
|
||||
$array['ip_type'] = 'ipv4';
|
||||
$array['address'] = $matches['ipv4']; // Same as IP
|
||||
//$array['prefix'] = $matches['ipv4_prefix'];
|
||||
|
||||
// Convert Cisco Inverse netmask to normal mask
|
||||
if (isset($matches['ipv4_inverse_mask']))
|
||||
{
|
||||
$matches['ipv4_mask'] = inet_pton($matches['ipv4_inverse_mask']);
|
||||
$matches['ipv4_mask'] = inet_ntop(~$matches['ipv4_mask']); // Binary inverse and back to IP string
|
||||
$matches['ipv4_network'] = $matches['ipv4'] . '/' . $matches['ipv4_mask'];
|
||||
}
|
||||
|
||||
if ($matches['ipv4_prefix'] == '32' || $matches['ipv4_mask'] == '255.255.255.255')
|
||||
{
|
||||
$array['prefix'] = '32';
|
||||
$array['network_start'] = $array['address'];
|
||||
$array['network_end'] = $array['address'];
|
||||
$array['network'] = $matches['ipv4_network']; // Network with prefix
|
||||
$array['query_type'] = 'single'; // Single IP query
|
||||
} else {
|
||||
$address = Net_IPv4::parseAddress($matches['ipv4_network']);
|
||||
//print_vars($address);
|
||||
$array['prefix'] = $address->bitmask . '';
|
||||
$array['network_start'] = $address->network;
|
||||
$array['network_end'] = $address->broadcast;
|
||||
$array['network'] = $array['network_start'] . '/' . $array['prefix']; // Network with prefix
|
||||
}
|
||||
}
|
||||
elseif (preg_match('%^'.OBS_PATTERN_IPV6_NET.'$%i', $network, $matches))
|
||||
{
|
||||
// Match by valid IPv6 network
|
||||
$array['ip_version'] = 6;
|
||||
$array['ip_type'] = 'ipv6';
|
||||
$array['address'] = $matches['ipv6']; // Same as IP
|
||||
$array['prefix'] = $matches['ipv6_prefix'];
|
||||
if ($array['prefix'] == 128)
|
||||
{
|
||||
$array['network_start'] = $array['address'];
|
||||
$array['network_end'] = $array['address'];
|
||||
$array['network'] = $matches['ipv6_network']; // Network with prefix
|
||||
$array['query_type'] = 'single'; // Single IP query
|
||||
} else {
|
||||
$address = Net_IPv6::parseAddress($array['address'], $array['prefix']);
|
||||
//print_vars($address);
|
||||
$array['network_start'] = $address['start'];
|
||||
$array['network_end'] = $address['end'];
|
||||
$array['network'] = $array['network_start'] . '/' . $array['prefix']; // Network with prefix
|
||||
}
|
||||
}
|
||||
elseif ($ip_version = get_ip_version($network))
|
||||
{
|
||||
// Single IPv4/IPv6
|
||||
if ($ip_version === 6 && preg_match('/::ffff:(\d+\.\d+\.\d+\.\d+)/', $network, $matches))
|
||||
{
|
||||
// IPv4 mapped to IPv6, like ::ffff:192.0.2.128
|
||||
// See: http://jira.observium.org/browse/OBSERVIUM-1274
|
||||
$network = $matches[1];
|
||||
$ip_version = 4;
|
||||
}
|
||||
$array['ip_version'] = $ip_version;
|
||||
$array['address'] = $network;
|
||||
//$array['prefix'] = $matches['ipv6_prefix'];
|
||||
if ($ip_version === 4)
|
||||
{
|
||||
$array['ip_type'] = 'ipv4';
|
||||
$array['prefix'] = '32';
|
||||
} else {
|
||||
$array['ip_type'] = 'ipv6';
|
||||
$array['prefix'] = '128';
|
||||
}
|
||||
$array['network_start'] = $array['address'];
|
||||
$array['network_end'] = $array['address'];
|
||||
$array['network'] = $network . '/' . $array['prefix']; // Add prefix
|
||||
$array['query_type'] = 'single'; // Single IP query
|
||||
}
|
||||
elseif (preg_match('/^[\d\.\?\*]+$/', $network))
|
||||
{
|
||||
// Match IPv4 by mask
|
||||
$array['ip_version'] = 4;
|
||||
$array['ip_type'] = 'ipv4';
|
||||
$array['address'] = $network;
|
||||
if (str_contains_array($network, [ '?', '*' ])) {
|
||||
// If network contains * or !
|
||||
$array['query_type'] = 'like';
|
||||
} else {
|
||||
// All other cases
|
||||
$array['query_type'] = '%like%';
|
||||
}
|
||||
}
|
||||
elseif (preg_match('/^[abcdef\d\:\?\*]+$/i', $network))
|
||||
{
|
||||
// Match IPv6 by mask
|
||||
$array['ip_version'] = 6;
|
||||
$array['ip_type'] = 'ipv6';
|
||||
$array['address'] = $network;
|
||||
if (str_contains_array($network, [ '?', '*' ])) {
|
||||
// If network contains * or !
|
||||
$array['query_type'] = 'like';
|
||||
} else {
|
||||
// All other cases
|
||||
$array['query_type'] = '%like%';
|
||||
}
|
||||
} else {
|
||||
// Not valid network string passed
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Add binary addresses for single and network queries
|
||||
switch ($array['query_type'])
|
||||
{
|
||||
case 'single':
|
||||
$array['address_binary'] = inet_pton($array['address']);
|
||||
break;
|
||||
case 'network':
|
||||
$array['network_start_binary'] = inet_pton($array['network_start']);
|
||||
$array['network_end_binary'] = inet_pton($array['network_end']);
|
||||
break;
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not the supplied IP address is within the supplied network (IPv4 or IPv6).
|
||||
*
|
||||
* @param string $ip IP Address
|
||||
* @param string $nets IPv4/v6 networks
|
||||
* @param bool $first FIXME
|
||||
*
|
||||
* @return bool Returns TRUE if address is found in supplied network, FALSE if it is not.
|
||||
*/
|
||||
// TESTME needs unit testing
|
||||
function match_network($ip, $nets, $first = FALSE)
|
||||
{
|
||||
$return = FALSE;
|
||||
$ip_version = get_ip_version($ip);
|
||||
if ($ip_version)
|
||||
{
|
||||
foreach ((array)$nets as $net)
|
||||
{
|
||||
$revert = (bool) preg_match('/^\!/', $net); // NOT match network
|
||||
if ($revert)
|
||||
{
|
||||
$net = preg_replace('/^\!/', '', $net);
|
||||
}
|
||||
|
||||
if ($ip_version === 4)
|
||||
{
|
||||
if (strpos($net, '.') === FALSE) { continue; } // NOT IPv4 net, skip
|
||||
if (strpos($net, '/') === FALSE) { $net .= '/32'; } // NET without mask as single IP
|
||||
$ip_in_net = Net_IPv4::ipInNetwork($ip, $net);
|
||||
} else {
|
||||
//print_vars($ip); echo(' '); print_vars($net); echo(PHP_EOL);
|
||||
if (strpos($net, ':') === FALSE) { continue; } // NOT IPv6 net, skip
|
||||
if (strpos($net, '/') === FALSE) { $net .= '/128'; } // NET without mask as single IP
|
||||
$ip_in_net = Net_IPv6::isInNetmask($ip, $net);
|
||||
}
|
||||
|
||||
if ($revert && $ip_in_net) { return FALSE; } // Return FALSE if IP found in network where should NOT match
|
||||
if ($first && $ip_in_net) { return TRUE; } // Return TRUE if IP found in first match
|
||||
$return = $return || $ip_in_net;
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert HEX encoded IP value to pretty IP string
|
||||
*
|
||||
* Examples:
|
||||
* IPv4 "C1 9C 5A 26" => "193.156.90.38"
|
||||
* IPv4 "J}4:" => "74.125.52.58"
|
||||
* IPv6 "20 01 07 F8 00 12 00 01 00 00 00 00 00 05 02 72" => "2001:07f8:0012:0001:0000:0000:0005:0272"
|
||||
* IPv6 "20:01:07:F8:00:12:00:01:00:00:00:00:00:05:02:72" => "2001:07f8:0012:0001:0000:0000:0005:0272"
|
||||
*
|
||||
* @param string $ip_hex HEX encoded IP address
|
||||
*
|
||||
* @return string IP address or original input string if not contains IP address
|
||||
*/
|
||||
function hex2ip($ip_hex) {
|
||||
//$ip = trim($ip_hex, "\"\t\n\r\0\x0B"); // Strange case, cleaned incorrectly
|
||||
$ip = trim($ip_hex, "\"\t\n\r\0");
|
||||
|
||||
// IPv6z, ie: 2a:02:a0:10:80:03:00:00:00:00:00:00:00:00:00:01%503316482
|
||||
if (str_contains_array($ip, '%')) {
|
||||
list($ip) = explode('%', $ip);
|
||||
}
|
||||
|
||||
$len = strlen($ip);
|
||||
if ($len === 5 && $ip[0] === ' ') {
|
||||
$ip = substr($ip, 1);
|
||||
$len = 4;
|
||||
}
|
||||
if ($len === 4) {
|
||||
// IPv4 hex string converted to SNMP string
|
||||
$ip = str2hex($ip);
|
||||
$len = strlen($ip);
|
||||
}
|
||||
|
||||
$ip = str_replace(' ', '', $ip);
|
||||
|
||||
if ($len > 8) {
|
||||
// For IPv6
|
||||
$ip = str_replace(':', '', $ip);
|
||||
$len = strlen($ip);
|
||||
}
|
||||
|
||||
if (!ctype_xdigit($ip)) {
|
||||
return $ip_hex;
|
||||
}
|
||||
|
||||
if ($len === 6) {
|
||||
// '90 7F 8A ', should be '90 7F 8A 00 ' ?
|
||||
$ip .= '00';
|
||||
$len = 8;
|
||||
}
|
||||
|
||||
//print_cli("IP: '$ip', LEN: $len\n");
|
||||
switch ($len) {
|
||||
case 8:
|
||||
// IPv4
|
||||
$ip_array = array();
|
||||
foreach (str_split($ip, 2) as $entry) {
|
||||
$ip_array[] = hexdec($entry);
|
||||
}
|
||||
$separator = '.';
|
||||
break;
|
||||
|
||||
case 16:
|
||||
// Cisco incorrect IPv4 (54 2E 68 02 FF FF FF FF)
|
||||
$ip_array = array();
|
||||
foreach (str_split($ip, 2) as $i => $entry)
|
||||
{
|
||||
if ($i == 4) { break; }
|
||||
$ip_array[] = hexdec($entry);
|
||||
}
|
||||
$separator = '.';
|
||||
break;
|
||||
|
||||
case 32:
|
||||
// IPv6
|
||||
$ip_array = str_split(strtolower($ip), 4);
|
||||
$separator = ':';
|
||||
break;
|
||||
|
||||
default:
|
||||
// Try convert hex string to string
|
||||
$ip = snmp_hexstring($ip_hex);
|
||||
if (get_ip_version($ip)) {
|
||||
return $ip;
|
||||
}
|
||||
return $ip_hex;
|
||||
}
|
||||
$ip = implode($separator, $ip_array);
|
||||
|
||||
return $ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert IP string to HEX encoded value.
|
||||
*
|
||||
* Examples:
|
||||
* IPv4 "193.156.90.38" => "C1 9C 5A 26"
|
||||
* IPv6 "2001:07f8:0012:0001:0000:0000:0005:0272" => "20 01 07 f8 00 12 00 01 00 00 00 00 00 05 02 72"
|
||||
* IPv6 "2001:7f8:12:1::5:0272" => "20 01 07 f8 00 12 00 01 00 00 00 00 00 05 02 72"
|
||||
*
|
||||
* @param string $ip IP address string
|
||||
* @param string $separator Separator for HEX parts
|
||||
*
|
||||
* @return string HEX encoded address
|
||||
*/
|
||||
function ip2hex($ip, $separator = ' ')
|
||||
{
|
||||
$ip_hex = trim($ip, " \"\t\n\r\0\x0B");
|
||||
$ip_version = get_ip_version($ip_hex);
|
||||
|
||||
if ($ip_version === 4)
|
||||
{
|
||||
// IPv4
|
||||
$ip_array = array();
|
||||
foreach (explode('.', $ip_hex) as $entry)
|
||||
{
|
||||
$ip_array[] = zeropad(dechex($entry));
|
||||
}
|
||||
}
|
||||
elseif ($ip_version === 6)
|
||||
{
|
||||
// IPv6
|
||||
$ip_hex = str_replace(':', '', Net_IPv6::uncompress($ip_hex, TRUE));
|
||||
$ip_array = str_split($ip_hex, 2);
|
||||
} else {
|
||||
return $ip;
|
||||
}
|
||||
$ip_hex = implode($separator, $ip_array);
|
||||
|
||||
return $ip_hex;
|
||||
}
|
||||
|
||||
// DOCME needs phpdoc block
|
||||
// TESTME needs unit testing
|
||||
function snmp2ipv6($ipv6_snmp)
|
||||
{
|
||||
$ipv6 = explode('.',$ipv6_snmp);
|
||||
|
||||
// Workaround stupid Microsoft bug in Windows 2008 -- this is fixed length!
|
||||
// < fenestro> "because whoever implemented this mib for Microsoft was ignorant of RFC 2578 section 7.7 (2)"
|
||||
if (count($ipv6) == 17 && $ipv6[0] == 16)
|
||||
{
|
||||
array_shift($ipv6);
|
||||
}
|
||||
|
||||
for ($i = 0;$i <= 15;$i++) { $ipv6[$i] = zeropad(dechex($ipv6[$i])); }
|
||||
for ($i = 0;$i <= 15;$i+=2) { $ipv6_2[] = $ipv6[$i] . $ipv6[$i+1]; }
|
||||
|
||||
return implode(':',$ipv6_2);
|
||||
}
|
||||
|
||||
// DOCME needs phpdoc block
|
||||
// TESTME needs unit testing
|
||||
function ipv62snmp($ipv6)
|
||||
{
|
||||
$ipv6_ex = explode(':',Net_IPv6::uncompress($ipv6));
|
||||
for ($i = 0;$i < 8;$i++) { $ipv6_ex[$i] = zeropad($ipv6_ex[$i],4); }
|
||||
$ipv6_ip = implode('',$ipv6_ex);
|
||||
for ($i = 0;$i < 32;$i+=2) $ipv6_split[] = hexdec(substr($ipv6_ip,$i,2));
|
||||
|
||||
return implode('.',$ipv6_split);
|
||||
}
|
||||
|
||||
|
||||
// EOF
|
942
includes/entities/port.inc.php
Normal file
942
includes/entities/port.inc.php
Normal file
@ -0,0 +1,942 @@
|
||||
<?php
|
||||
/**
|
||||
* Observium
|
||||
*
|
||||
* This file is part of Observium.
|
||||
*
|
||||
* @package observium
|
||||
* @subpackage entities
|
||||
* @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited
|
||||
*
|
||||
*/
|
||||
|
||||
// Get port id by ip address (using cache)
|
||||
// DOCME needs phpdoc block
|
||||
// TESTME needs unit testing
|
||||
function get_port_id_by_ip_cache($device, $ip)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
$ip_version = get_ip_version($ip);
|
||||
|
||||
if (is_array($device) && isset($device['device_id']))
|
||||
{
|
||||
$device_id = $device['device_id'];
|
||||
}
|
||||
elseif (is_numeric($device))
|
||||
{
|
||||
$device_id = $device;
|
||||
}
|
||||
if (!isset($device_id) || !$ip_version)
|
||||
{
|
||||
print_error("Invalid arguments passed into function get_port_id_by_ip_cache(). Please report to developers.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ($ip_version == 6)
|
||||
{
|
||||
$ip = Net_IPv6::uncompress($ip, TRUE);
|
||||
}
|
||||
|
||||
if (isset($cache['port_ip'][$device_id][$ip]))
|
||||
{
|
||||
return $cache['port_ip'][$device_id][$ip];
|
||||
}
|
||||
|
||||
$ips = dbFetchRows('SELECT `port_id`, `ifOperStatus`, `ifAdminStatus` FROM `ipv'.$ip_version.'_addresses`
|
||||
LEFT JOIN `ports` USING(`port_id`)
|
||||
WHERE `deleted` = 0 AND `device_id` = ? AND `ipv'.$ip_version.'_address` = ?', array($device_id, $ip));
|
||||
if (count($ips) === 1)
|
||||
{
|
||||
// Simple
|
||||
$port = current($ips);
|
||||
//return $port['port_id'];
|
||||
} else {
|
||||
foreach ($ips as $entry)
|
||||
{
|
||||
if ($entry['ifAdminStatus'] === 'up' && $entry['ifOperStatus'] === 'up')
|
||||
{
|
||||
// First UP entry
|
||||
$port = $entry;
|
||||
break;
|
||||
}
|
||||
elseif ($entry['ifAdminStatus'] === 'up')
|
||||
{
|
||||
// Admin up, but port down or other state
|
||||
$ips_up[] = $entry;
|
||||
} else {
|
||||
// Admin down
|
||||
$ips_down[] = $entry;
|
||||
}
|
||||
}
|
||||
if (!isset($port))
|
||||
{
|
||||
if ($ips_up)
|
||||
{
|
||||
$port = current($ips_up);
|
||||
} else {
|
||||
$port = current($ips_down);
|
||||
}
|
||||
}
|
||||
}
|
||||
$cache['port_ip'][$device_id][$ip] = $port['port_id'] ? $port['port_id'] : FALSE;
|
||||
|
||||
return $cache['port_ip'][$device_id][$ip];
|
||||
|
||||
}
|
||||
|
||||
// DOCME needs phpdoc block
|
||||
// TESTME needs unit testing
|
||||
function get_port_id_by_mac($device, $mac)
|
||||
{
|
||||
if (is_array($device) && isset($device['device_id']))
|
||||
{
|
||||
$device_id = $device['device_id'];
|
||||
}
|
||||
elseif (is_numeric($device))
|
||||
{
|
||||
$device_id = $device;
|
||||
}
|
||||
|
||||
$remote_mac = mac_zeropad($mac);
|
||||
if ($remote_mac && $remote_mac != '000000000000')
|
||||
{
|
||||
return dbFetchCell("SELECT `port_id` FROM `ports` WHERE `deleted` = '0' AND `ifPhysAddress` = ? AND `device_id` = ? LIMIT 1", [ $remote_mac, $device_id ]);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
function get_port_by_ent_index($device, $entPhysicalIndex, $allow_snmp = FALSE) {
|
||||
$mib = 'ENTITY-MIB';
|
||||
if (!is_numeric($entPhysicalIndex) ||
|
||||
!is_numeric($device['device_id']) ||
|
||||
!is_device_mib($device, $mib)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$allow_snmp = $allow_snmp || is_cli(); // Allow snmpwalk queries in poller/discovery or if in wui passed TRUE!
|
||||
|
||||
if (isset($GLOBALS['cache']['snmp'][$mib][$device['device_id']])) {
|
||||
// Cached
|
||||
$entity_array = $GLOBALS['cache']['snmp'][$mib][$device['device_id']];
|
||||
if (safe_empty($entity_array)) {
|
||||
// Force DB queries
|
||||
$allow_snmp = FALSE;
|
||||
}
|
||||
} elseif ($allow_snmp) {
|
||||
// Inventory module disabled, this DB empty, try to cache
|
||||
$entity_array = [];
|
||||
$oids = array('entPhysicalDescr', 'entPhysicalName', 'entPhysicalClass', 'entPhysicalContainedIn', 'entPhysicalParentRelPos');
|
||||
if (is_device_mib($device, 'ARISTA-ENTITY-SENSOR-MIB')) {
|
||||
$oids[] = 'entPhysicalAlias';
|
||||
}
|
||||
foreach ($oids as $oid) {
|
||||
$entity_array = snmpwalk_cache_oid($device, $oid, $entity_array, snmp_mib_entity_vendortype($device, 'ENTITY-MIB'));
|
||||
if (!snmp_status()) { break; }
|
||||
}
|
||||
$entity_array = snmpwalk_cache_twopart_oid($device, 'entAliasMappingIdentifier', $entity_array, 'ENTITY-MIB:IF-MIB');
|
||||
if (safe_empty($entity_array)) {
|
||||
// Force DB queries
|
||||
$allow_snmp = FALSE;
|
||||
}
|
||||
$GLOBALS['cache']['snmp'][$mib][$device['device_id']] = $entity_array;
|
||||
} else {
|
||||
// Or try to use DB
|
||||
}
|
||||
|
||||
//print_debug_vars($entity_array);
|
||||
$sensor_index = $entPhysicalIndex; // Initial ifIndex
|
||||
$sensor_name = '';
|
||||
do {
|
||||
if ($allow_snmp) {
|
||||
// SNMP (discovery)
|
||||
$sensor_port = $entity_array[$sensor_index];
|
||||
} else {
|
||||
// DB (web)
|
||||
$sensor_port = dbFetchRow('SELECT * FROM `entPhysical` WHERE `device_id` = ? AND `entPhysicalIndex` = ? AND `deleted` IS NULL', array($device['device_id'], $sensor_index));
|
||||
}
|
||||
print_debug_vars($sensor_index, 1);
|
||||
print_debug_vars($sensor_port, 1);
|
||||
|
||||
if ($sensor_port['entPhysicalClass'] === 'sensor') {
|
||||
// Need to store initial sensor name, for multi-lane ports
|
||||
$sensor_name = $sensor_port['entPhysicalName'];
|
||||
}
|
||||
|
||||
if ($sensor_port['entPhysicalClass'] === 'port') {
|
||||
// Port found, get mapped ifIndex
|
||||
unset($entAliasMappingIdentifier);
|
||||
foreach (array(0, 1, 2) as $i) {
|
||||
if (isset($sensor_port[$i]['entAliasMappingIdentifier'])) {
|
||||
$entAliasMappingIdentifier = $sensor_port[$i]['entAliasMappingIdentifier'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($entAliasMappingIdentifier) && str_contains($entAliasMappingIdentifier, 'fIndex')) {
|
||||
list(, $ifIndex) = explode('.', $entAliasMappingIdentifier);
|
||||
|
||||
$port = get_port_by_index_cache($device['device_id'], $ifIndex);
|
||||
if (is_array($port)) {
|
||||
// Hola, port really found
|
||||
print_debug("Port is found: ifIndex = $ifIndex, port_id = " . $port['port_id']);
|
||||
return $port;
|
||||
}
|
||||
} elseif (!$allow_snmp && $sensor_port['ifIndex']) {
|
||||
// ifIndex already stored by inventory module
|
||||
$ifIndex = $sensor_port['ifIndex'];
|
||||
$port = get_port_by_index_cache($device['device_id'], $ifIndex);
|
||||
print_debug("Port is found: ifIndex = $ifIndex, port_id = " . $port['port_id']);
|
||||
return $port;
|
||||
} else {
|
||||
// This is another case for Cisco IOSXR, when have incorrect entAliasMappingIdentifier association,
|
||||
// https://jira.observium.org/browse/OBS-3654
|
||||
$port_id = get_port_id_by_ifDescr($device['device_id'], $sensor_port['entPhysicalName']);
|
||||
if (is_numeric($port_id)) {
|
||||
// Hola, port really found
|
||||
$port = get_port_by_id($port_id);
|
||||
$ifIndex = $port['ifIndex'];
|
||||
print_debug("Port is found: ifIndex = $ifIndex, port_id = " . $port_id);
|
||||
|
||||
return $port;
|
||||
}
|
||||
}
|
||||
|
||||
break; // Exit do-while
|
||||
} elseif ($device['os'] === 'arista_eos' &&
|
||||
$sensor_port['entPhysicalClass'] === 'container' && !safe_empty($sensor_port['entPhysicalAlias'])) {
|
||||
// Arista not have entAliasMappingIdentifier, but used entPhysicalAlias as ifDescr
|
||||
$port_id = get_port_id_by_ifDescr($device['device_id'], $sensor_port['entPhysicalAlias']);
|
||||
if (is_numeric($port_id)) {
|
||||
// Hola, port really found
|
||||
$port = get_port_by_id($port_id);
|
||||
$ifIndex = $port['ifIndex'];
|
||||
print_debug("Port is found: ifIndex = $ifIndex, port_id = " . $port_id);
|
||||
return $port; // Exit do-while
|
||||
}
|
||||
if ($port_id = get_port_id_by_ifDescr($device['device_id'], $sensor_port['entPhysicalAlias'] . '/1')) {
|
||||
// Multi-lane Tranceivers
|
||||
$port = get_port_by_id($port_id);
|
||||
$port['sensor_multilane'] = TRUE;
|
||||
$ifIndex = $port['ifIndex'];
|
||||
print_debug("Port is found: ifIndex = $ifIndex, port_id = " . $port_id);
|
||||
return $port; // Exit do-while
|
||||
}
|
||||
$sensor_index = $sensor_port['entPhysicalContainedIn']; // Next ifIndex
|
||||
} elseif ($sensor_index == $sensor_port['entPhysicalContainedIn']) {
|
||||
break; // Break if current index same as next to avoid loop
|
||||
} elseif ($sensor_port['entPhysicalClass'] === 'module' &&
|
||||
(isset($sensor_port[0]['entAliasMappingIdentifier']) ||
|
||||
isset($sensor_port[1]['entAliasMappingIdentifier']) ||
|
||||
isset($sensor_port[2]['entAliasMappingIdentifier']))) {
|
||||
// Cisco IOSXR 6.5.x ASR 9900 platform && NCS 5500
|
||||
$sensor_index = $sensor_port['entPhysicalContainedIn']; // Next ifIndex
|
||||
|
||||
// By first try if entAliasMappingIdentifier correct
|
||||
unset($entAliasMappingIdentifier);
|
||||
foreach (array(0, 1, 2) as $i) {
|
||||
if (isset($sensor_port[$i]['entAliasMappingIdentifier'])) {
|
||||
$entAliasMappingIdentifier = $sensor_port[$i]['entAliasMappingIdentifier'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isset($entAliasMappingIdentifier) && str_contains($entAliasMappingIdentifier, 'fIndex')) {
|
||||
list(, $ifIndex) = explode('.', $entAliasMappingIdentifier);
|
||||
|
||||
$port = get_port_by_index_cache($device['device_id'], $ifIndex);
|
||||
if (is_array($port)) {
|
||||
// Hola, port really found
|
||||
print_debug("Port is found: ifIndex = $ifIndex, port_id = " . $port['port_id']);
|
||||
return $port;
|
||||
}
|
||||
}
|
||||
|
||||
// This case for Cisco IOSXR ASR 9900 platform, when have incorrect entAliasMappingIdentifier association,
|
||||
// https://jira.observium.org/browse/OBS-3147
|
||||
$port_id = FALSE;
|
||||
if (str_contains($sensor_port['entPhysicalName'], '-PORT-')) {
|
||||
// Second, try detect port by entPhysicalDescr/entPhysicalName
|
||||
if (str_starts($sensor_port['entPhysicalDescr'], [ '10GBASE', '10GE' ]) ||
|
||||
str_icontains_array($sensor_port['entPhysicalDescr'], [ ' 10GBASE', ' 10GE', ' 10G ' ])) {
|
||||
$ifDescr_base = 'TenGigE';
|
||||
} elseif (str_starts($sensor_port['entPhysicalDescr'], [ '25GBASE', '25GE' ]) ||
|
||||
str_icontains_array($sensor_port['entPhysicalDescr'], [ ' 25GBASE', ' 25GE', ' 25G ' ])) {
|
||||
$ifDescr_base = 'TwentyFiveGigE';
|
||||
} elseif (str_starts($sensor_port['entPhysicalDescr'], [ '40GBASE', '40GE' ]) ||
|
||||
str_icontains_array($sensor_port['entPhysicalDescr'], [ ' 40GBASE', ' 40GE', ' 40G ' ])) {
|
||||
$ifDescr_base = 'FortyGigE';
|
||||
} elseif (str_starts($sensor_port['entPhysicalDescr'], [ '100GBASE', '100GE' ]) ||
|
||||
str_icontains_array($sensor_port['entPhysicalDescr'], [ ' 100GBASE', ' 100GE', ' 100G ' ])) {
|
||||
// Ie:
|
||||
// Cisco CPAK 100GBase-SR4, 100m, MMF
|
||||
// 100GBASE-ER4 CFP2 Module for SMF (<40 km)
|
||||
// Non-Cisco QSFP28 100G ER4 Pluggable Optics Module
|
||||
$ifDescr_base = 'HundredGigE';
|
||||
}
|
||||
$ifDescr_num = str_replace('-PORT-', '/', $sensor_port['entPhysicalName']);
|
||||
$port_id = get_port_id_by_ifDescr($device['device_id'], $ifDescr_base . $ifDescr_num);
|
||||
if (!is_numeric($port_id)) {
|
||||
// FIXME, I think first node number '0/' should be detected by some how
|
||||
$port_id = get_port_id_by_ifDescr($device['device_id'], $ifDescr_base . '0/' . $ifDescr_num);
|
||||
}
|
||||
} elseif (str_contains_array($sensor_port['entPhysicalName'], [ 'TenGigE', 'TwentyFiveGigE', 'FortyGigE', 'HundredGigE', 'Ethernet' ])) {
|
||||
// Same as previous, but entPhysicalName contain correct ifDescr, ie:
|
||||
// NCS platform: FortyGigE0/0/0/20
|
||||
// NXOS platform: "Ethernet1/1(volt)
|
||||
list($ifDescr, ) = explode('(', $sensor_port['entPhysicalName'], 2);
|
||||
$port_id = get_port_id_by_ifDescr($device['device_id'], $ifDescr);
|
||||
}
|
||||
|
||||
if (is_numeric($port_id)) {
|
||||
// Hola, port really found
|
||||
$port = get_port_by_id($port_id);
|
||||
$ifIndex = $port['ifIndex'];
|
||||
print_debug("Port is found: ifIndex = $ifIndex, port_id = " . $port_id);
|
||||
|
||||
return $port;
|
||||
}
|
||||
|
||||
} elseif ((($sensor_port['entPhysicalClass'] === 'sensor' && $sensor_port['entPhysicalContainedIn'] == 0) ||
|
||||
($sensor_port['entPhysicalClass'] === 'module' && $sensor_port['entPhysicalIsFRU'] === 'true')) &&
|
||||
str_starts($sensor_port['entPhysicalName'], [ 'TenGigE', 'TwentyFiveGigE', 'FortyGigE', 'HundredGigE', 'Ethernet' ])) {
|
||||
$sensor_index = $sensor_port['entPhysicalContainedIn']; // Next ifIndex
|
||||
// entPhysicalName contain correct ifDescr, ie:
|
||||
// NCS platform: FortyGigE0/0/0/20
|
||||
// NXOS 6.x platform: "Ethernet1/1(volt)
|
||||
list($ifDescr, ) = explode('(', $sensor_port['entPhysicalName'], 2);
|
||||
if ($port_id = get_port_id_by_ifDescr($device['device_id'], $ifDescr)) {
|
||||
// Hola, port really found
|
||||
$port = get_port_by_id($port_id);
|
||||
$ifIndex = $port['ifIndex'];
|
||||
print_debug("Port is found: ifIndex = $ifIndex, port_id = " . $port_id);
|
||||
|
||||
return $port;
|
||||
}
|
||||
|
||||
} else {
|
||||
$sensor_index = $sensor_port['entPhysicalContainedIn']; // Next ifIndex
|
||||
|
||||
// See: http://jira.observium.org/browse/OBS-2295
|
||||
// IOS-XE and IOS-XR can store in module index both: sensors and port
|
||||
$sensor_transceiver = $sensor_port['entPhysicalClass'] === 'sensor' &&
|
||||
str_icontains_array($sensor_port['entPhysicalName'] . $sensor_port['entPhysicalDescr'] . $sensor_port['entPhysicalVendorType'], [ 'transceiver', '-PORT-' ]);
|
||||
// This is multi-lane optical transceiver, ie 100G, 40G, multiple sensors for each port
|
||||
$sensor_multilane = $sensor_port['entPhysicalClass'] === 'container' &&
|
||||
(in_array($sensor_port['entPhysicalVendorType'], [ 'cevContainer40GigBasePort', 'cevContainerCXP', 'cevContainerCPAK' ]) || // Known Cisco specific containers
|
||||
str_contains_array($sensor_port['entPhysicalName'] . $sensor_port['entPhysicalDescr'], [ 'Optical' ])); // Pluggable Optical Module Container
|
||||
if ($sensor_transceiver) {
|
||||
$tmp_index = dbFetchCell('SELECT `entPhysicalIndex` FROM `entPhysical` WHERE `device_id` = ? AND `entPhysicalContainedIn` = ? AND `entPhysicalClass` = ? AND `deleted` IS NULL', array($device['device_id'], $sensor_index, 'port'));
|
||||
if (is_numeric($tmp_index) && $tmp_index > 0) {
|
||||
// If port index found, try this entPhysicalIndex in next round
|
||||
$sensor_index = $tmp_index;
|
||||
}
|
||||
} elseif ($sensor_multilane) {
|
||||
$entries = dbFetchRows('SELECT `entPhysicalIndex`, `entPhysicalName` FROM `entPhysical` WHERE `device_id` = ? AND `entPhysicalContainedIn` = ? AND `entPhysicalClass` = ? AND `deleted` IS NULL', array($device['device_id'], $sensor_index, 'port'));
|
||||
print_debug("Multi-Lane entries:");
|
||||
print_debug_vars($entries, 1);
|
||||
if (count($entries) > 1 &&
|
||||
preg_match('/(?<start>\D{2})(?<num>\d+(?:\/\d+)+).*Lane\s*(?<lane>\d+)/', $sensor_name, $matches)) { // detect port numeric part and lane
|
||||
// There is each Line associated with breakout port, mostly is QSFP+ 40G
|
||||
// FortyGigE0/0/0/0-Tx Lane 1 Power -> 0/RP0-TenGigE0/0/0/0/1
|
||||
// FortyGigE0/0/0/0-Tx Lane 2 Power -> 0/RP0-TenGigE0/0/0/0/2
|
||||
$lane_num = $matches['start'] . $matches['num'] . '/' . $matches['lane']; // FortyGigE0/0/0/0-Tx Lane 1 -> gE0/0/0/0/1
|
||||
foreach ($entries as $entry) {
|
||||
if (str_ends($entry['entPhysicalName'], $lane_num)) {
|
||||
$sensor_index = $entry['entPhysicalIndex'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} elseif (is_numeric($entries[0]['entPhysicalIndex']) && $entries[0]['entPhysicalIndex'] > 0) {
|
||||
// Single multi-lane port association, ie 100G
|
||||
$sensor_index = $entries[0]['entPhysicalIndex'];
|
||||
}
|
||||
}
|
||||
}
|
||||
// NOTE for self: entPhysicalParentRelPos >= 0 because on iosxr trouble
|
||||
} while ($sensor_port['entPhysicalClass'] !== 'port' && $sensor_port['entPhysicalContainedIn'] > 0 &&
|
||||
($sensor_port['entPhysicalParentRelPos'] >= 0 || $device['os'] === 'arista_eos'));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get port array by ID (using cache)
|
||||
// DOCME needs phpdoc block
|
||||
// TESTME needs unit testing
|
||||
function get_port_by_id_cache($port_id) {
|
||||
return get_entity_by_id_cache('port', $port_id);
|
||||
}
|
||||
|
||||
// Get port array by ID (with port state)
|
||||
// NOTE get_port_by_id(ID) != get_port_by_id_cache(ID)
|
||||
// DOCME needs phpdoc block
|
||||
// TESTME needs unit testing
|
||||
function get_port_by_id($port_id)
|
||||
{
|
||||
if (is_numeric($port_id))
|
||||
{
|
||||
//$port = dbFetchRow("SELECT * FROM `ports` LEFT JOIN `ports-state` ON `ports`.`port_id` = `ports-state`.`port_id` WHERE `ports`.`port_id` = ?", array($port_id));
|
||||
$port = dbFetchRow("SELECT * FROM `ports` WHERE `ports`.`port_id` = ?", array($port_id));
|
||||
}
|
||||
|
||||
if (is_array($port))
|
||||
{
|
||||
//$port['port_id'] = $port_id; // It corrects the situation, when `ports-state` is empty
|
||||
humanize_port($port);
|
||||
return $port;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Get port array by ifIndex (using cache)
|
||||
// DOCME needs phpdoc block
|
||||
// TESTME needs unit testing
|
||||
function get_port_by_index_cache($device, $ifIndex, $deleted = 0) {
|
||||
global $cache;
|
||||
|
||||
if (is_array($device) && isset($device['device_id'])) {
|
||||
$device_id = $device['device_id'];
|
||||
} elseif (is_numeric($device)) {
|
||||
$device_id = $device;
|
||||
}
|
||||
if (!isset($device_id) || !is_intnum($ifIndex)) {
|
||||
print_error("Invalid arguments passed into function get_port_by_index_cache(). Please report to developers.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (OBS_PROCESS_NAME === 'poller' && !isset($cache['port_index'][$device_id]) && !$deleted) {
|
||||
// Pre-cache all ports in poller for speedup db queries
|
||||
foreach (dbFetchRows('SELECT * FROM `ports` WHERE `device_id` = ? AND `deleted` = ?', [ $device_id, 0 ]) as $entity) {
|
||||
if (is_numeric($entity['port_id'])) {
|
||||
// Cache ifIndex to port ID translations
|
||||
$cache['port_index'][$device_id][$entity['ifIndex']] = $entity['port_id'];
|
||||
|
||||
// Same caching as in get_entity_by_id_cache()
|
||||
humanize_port($entity);
|
||||
entity_rewrite('port', $entity);
|
||||
$cache['port'][$entity['port_id']] = $entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($cache['port_index'][$device_id][$ifIndex]) && is_numeric($cache['port_index'][$device_id][$ifIndex])) {
|
||||
$id = $cache['port_index'][$device_id][$ifIndex];
|
||||
} else {
|
||||
$deleted = $deleted ? 1 : 0; // Just convert boolean to 0 or 1
|
||||
|
||||
$id = dbFetchCell("SELECT `port_id` FROM `ports` WHERE `device_id` = ? AND `ifIndex` = ? AND `deleted` = ? LIMIT 1", [ $device_id, $ifIndex, $deleted ]);
|
||||
if (!$deleted && is_numeric($id)) {
|
||||
// Cache port IDs (except deleted)
|
||||
$cache['port_index'][$device_id][$ifIndex] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
$port = get_entity_by_id_cache('port', $id);
|
||||
if (is_array($port)) { return $port; }
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Get port array by ifIndex
|
||||
// DOCME needs phpdoc block
|
||||
// TESTME needs unit testing
|
||||
function get_port_by_ifIndex($device_id, $ifIndex) {
|
||||
if (is_array($device_id)) {
|
||||
$device_id = $device_id['device_id'];
|
||||
}
|
||||
|
||||
$port = dbFetchRow("SELECT * FROM `ports` WHERE `device_id` = ? AND `ifIndex` = ? LIMIT 1", array($device_id, $ifIndex));
|
||||
|
||||
if (is_array($port)) {
|
||||
humanize_port($port);
|
||||
return $port;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Get port ID by ifDescr (i.e. 'TenGigabitEthernet1/1') or ifName (i.e. 'Te1/1')
|
||||
// DOCME needs phpdoc block
|
||||
// TESTME needs unit testing
|
||||
function get_port_id_by_ifDescr($device_id, $ifDescr, $deleted = 0) {
|
||||
if (is_array($device_id)) {
|
||||
$device_id = $device_id['device_id'];
|
||||
}
|
||||
|
||||
$port_id = dbFetchCell("SELECT `port_id` FROM `ports` WHERE `device_id` = ? AND (`ifDescr` = ? OR `ifName` = ? OR `port_label_short` = ?) AND `deleted` = ? LIMIT 1", [ $device_id, $ifDescr, $ifDescr, $ifDescr, $deleted ]);
|
||||
|
||||
if (is_numeric($port_id)) {
|
||||
return $port_id;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Get port ID by ifAlias (interface description)
|
||||
// DOCME needs phpdoc block
|
||||
// TESTME needs unit testing
|
||||
function get_port_id_by_ifAlias($device_id, $ifAlias, $deleted = 0) {
|
||||
if (is_array($device_id)) {
|
||||
$device_id = $device_id['device_id'];
|
||||
}
|
||||
|
||||
$port_id = dbFetchCell("SELECT `port_id` FROM `ports` WHERE `device_id` = ? AND `ifAlias` = ? AND `deleted` = ? LIMIT 1", array($device_id, $ifAlias, $deleted));
|
||||
|
||||
if (is_numeric($port_id)) {
|
||||
return $port_id;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Get port ID by customer params (see http://www.observium.org/wiki/Interface_Description_Parsing)
|
||||
// DOCME needs phpdoc block
|
||||
// TESTME needs unit testing
|
||||
function get_port_id_by_customer($customer)
|
||||
{
|
||||
$where = ' WHERE 1';
|
||||
if (is_array($customer))
|
||||
{
|
||||
foreach ($customer as $var => $value)
|
||||
{
|
||||
if ($value != '')
|
||||
{
|
||||
switch ($var)
|
||||
{
|
||||
case 'device':
|
||||
case 'device_id':
|
||||
$where .= generate_query_values($value, 'device_id');
|
||||
break;
|
||||
case 'type':
|
||||
case 'descr':
|
||||
case 'circuit':
|
||||
case 'speed':
|
||||
case 'notes':
|
||||
$where .= generate_query_values($value, 'port_descr_'.$var);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$query = 'SELECT `port_id` FROM `ports` ' . $where . ' ORDER BY `ifOperStatus` DESC';
|
||||
$ids = dbFetchColumn($query);
|
||||
|
||||
//print_vars($ids);
|
||||
switch (count($ids))
|
||||
{
|
||||
case 0:
|
||||
return FALSE;
|
||||
case 1:
|
||||
return $ids[0];
|
||||
break;
|
||||
default:
|
||||
foreach ($ids as $port_id)
|
||||
{
|
||||
$port = get_port_by_id_cache($port_id);
|
||||
$device = device_by_id_cache($port['device_id']);
|
||||
if ($device['disabled'] || !$device['status'])
|
||||
{
|
||||
continue; // switch to next ID
|
||||
}
|
||||
break;
|
||||
}
|
||||
return $port_id;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
function get_device_ids_by_customer($type, $customer)
|
||||
{
|
||||
if ($customer === '' || is_null($customer)) { return NULL; }
|
||||
|
||||
// Recursive merge
|
||||
if (is_array($customer))
|
||||
{
|
||||
$ids = [];
|
||||
foreach ($customer as $entry)
|
||||
{
|
||||
if ($entry_ids = get_device_ids_by_customer($type, $entry))
|
||||
{
|
||||
$ids = array_merge($ids, $entry_ids);
|
||||
}
|
||||
}
|
||||
return $ids;
|
||||
}
|
||||
|
||||
$where = ' WHERE 1';
|
||||
switch ($type)
|
||||
{
|
||||
case 'device':
|
||||
case 'device_id':
|
||||
$where .= generate_query_values($customer, 'device_id');
|
||||
break;
|
||||
case 'type':
|
||||
case 'descr':
|
||||
case 'circuit':
|
||||
case 'speed':
|
||||
case 'notes':
|
||||
$where .= generate_query_values($customer, 'port_descr_'.$type);
|
||||
break;
|
||||
default:
|
||||
$where .= generate_query_values($customer, 'port_descr_descr');
|
||||
}
|
||||
|
||||
$query = 'SELECT DISTINCT `device_id` FROM `ports` ' . $where;
|
||||
|
||||
return dbFetchColumn($query);
|
||||
}
|
||||
|
||||
// DOCME needs phpdoc block
|
||||
// TESTME needs unit testing
|
||||
function is_port_valid($port, $device) {
|
||||
global $config;
|
||||
|
||||
// Ignore non standard ifOperStatus
|
||||
// See http://tools.cisco.com/Support/SNMP/do/BrowseOID.do?objectInput=ifOperStatus
|
||||
$valid_ifOperStatus = [ 'testing', 'dormant', 'down', 'lowerLayerDown', 'unknown', 'up', 'monitoring' ];
|
||||
if (isset($port['ifOperStatus']) && strlen($port['ifOperStatus']) &&
|
||||
!in_array($port['ifOperStatus'], $valid_ifOperStatus)) {
|
||||
print_debug("ignored (by ifOperStatus = notPresent or invalid value).");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Per os definition
|
||||
$def = isset($config['os'][$device['os']]['ports_ignore']) ? $config['os'][$device['os']]['ports_ignore'] : [];
|
||||
|
||||
// Ignore ports with empty ifType
|
||||
if (isset($def['allow_empty'])) {
|
||||
$ports_allow_empty = (bool) $def['allow_empty'];
|
||||
unset($def['allow_empty']);
|
||||
} else {
|
||||
$ports_allow_empty = FALSE;
|
||||
}
|
||||
if (!isset($port['ifType']) && !$ports_allow_empty) {
|
||||
/* Some devices (ie D-Link) report ports without any useful info, example:
|
||||
[74] => Array
|
||||
(
|
||||
[ifName] => po22
|
||||
[ifInMulticastPkts] => 0
|
||||
[ifInBroadcastPkts] => 0
|
||||
[ifOutMulticastPkts] => 0
|
||||
[ifOutBroadcastPkts] => 0
|
||||
[ifLinkUpDownTrapEnable] => enabled
|
||||
[ifHighSpeed] => 0
|
||||
[ifPromiscuousMode] => false
|
||||
[ifConnectorPresent] => false
|
||||
[ifAlias] => po22
|
||||
[ifCounterDiscontinuityTime] => 0:0:00:00.00
|
||||
)
|
||||
*/
|
||||
print_debug("ignored (by empty ifType).");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// This happens on some liebert UPS devices or when device have memory leak (ie Eaton Powerware)
|
||||
if (isset($config['os'][$device['os']]['ifType_ifDescr']) &&
|
||||
$config['os'][$device['os']]['ifType_ifDescr'] && $port['ifIndex']) {
|
||||
$len = strlen($port['ifDescr']);
|
||||
$type = rewrite_iftype($port['ifType']);
|
||||
if ($type && ($len === 0 || $len > 255 ||
|
||||
isHexString($port['ifDescr']) ||
|
||||
preg_match('/(.)\1{4,}/', $port['ifDescr']))) {
|
||||
$port['ifDescr'] = $type . ' ' . $port['ifIndex'];
|
||||
}
|
||||
}
|
||||
|
||||
//$if = ($config['os'][$device['os']]['ifname'] ? $port['ifName'] : $port['ifDescr']);
|
||||
$valid_ifDescr = !safe_empty($port['ifDescr']);
|
||||
$valid_ifName = !safe_empty($port['ifName']);
|
||||
|
||||
// Ignore ports with empty ifName and ifDescr (while not possible store in db)
|
||||
if (!$valid_ifDescr && !$valid_ifName) {
|
||||
print_debug("ignored (by empty ifDescr and ifName).");
|
||||
return FALSE;
|
||||
}
|
||||
// ifName not same as ifDescr (speedup label checks)
|
||||
$nosame_ifName = $port['ifDescr'] !== $port['ifName'];
|
||||
|
||||
// Complex Oid based checks
|
||||
foreach ($def as $i => $array) {
|
||||
if (!is_numeric($i)) { continue; } // Ignore old definitions
|
||||
$count = safe_count($array); // count required Oids
|
||||
|
||||
// Oids: ifType, ifAlias, ifDescr, ifName, label
|
||||
$found = 0;
|
||||
$table_rows = [];
|
||||
foreach ($array as $oid => $entry) {
|
||||
switch (strtolower($oid)) {
|
||||
case 'type':
|
||||
case 'iftype':
|
||||
if (str_contains_array($port['ifType'], $entry)) {
|
||||
$table_rows[] = [ 'ifType', $GLOBALS['str_last_needle'], $port['ifType'] ];
|
||||
$found++;
|
||||
}
|
||||
break;
|
||||
|
||||
// This defs always regex!
|
||||
case 'descr':
|
||||
case 'ifdescr':
|
||||
if (!$valid_ifDescr) { break; }
|
||||
foreach ((array)$entry as $pattern) {
|
||||
if (preg_match($pattern, $port['ifDescr'])) {
|
||||
$table_rows[] = [ 'ifDescr', $pattern, $port['ifDescr'] ];
|
||||
$found++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'name':
|
||||
case 'ifname':
|
||||
if (!$valid_ifName) { break; }
|
||||
foreach ((array)$entry as $pattern) {
|
||||
if (preg_match($pattern, $port['ifName'])) {
|
||||
$table_rows[] = [ 'ifName', $pattern, $port['ifName'] ];
|
||||
$found++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'label':
|
||||
if ($valid_ifDescr) {
|
||||
foreach ((array)$entry as $pattern) {
|
||||
if (preg_match($pattern, $port['ifDescr'])) {
|
||||
$table_rows[] = [ 'ifDescr', $pattern, $port['ifDescr'] ];
|
||||
$found++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($valid_ifName && $nosame_ifName) {
|
||||
foreach ((array)$entry as $pattern) {
|
||||
if (preg_match($pattern, $port['ifName'])) {
|
||||
$table_rows[] = [ 'ifName', $pattern, $port['ifName'] ];
|
||||
$found++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'alias':
|
||||
case 'ifalias':
|
||||
foreach ((array)$entry as $pattern) {
|
||||
if (preg_match($pattern, $port['ifAlias'])) {
|
||||
$table_rows[] = [ 'ifAlias', $pattern, $port['ifAlias'] ];
|
||||
$found++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($count && $found === $count) {
|
||||
// Show matched Oids
|
||||
print_debug("ignored (by Oids):");
|
||||
$table_headers = [ '%WOID%n', '%WMatched definition%n', '%WValue%n' ];
|
||||
print_cli_table($table_rows, $table_headers);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Global Configs */
|
||||
|
||||
// Ignore ports by ifAlias
|
||||
if (isset($config['bad_ifalias_regexp'])) {
|
||||
foreach ((array)$config['bad_ifalias_regexp'] as $bi) {
|
||||
if (preg_match($bi, $port['ifAlias'])) {
|
||||
print_debug("ignored (by ifAlias): ".$port['ifAlias']." [ $bi ]");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore ports by ifName/ifDescr (do not forced as case insensitive)
|
||||
if (isset($config['bad_if_regexp'])) {
|
||||
foreach ((array)$config['bad_if_regexp'] as $bi) {
|
||||
if ($valid_ifDescr && preg_match($bi, $port['ifDescr'])) {
|
||||
print_debug("ignored (by ifDescr regexp): ".$port['ifDescr']." [ $bi ]");
|
||||
return FALSE;
|
||||
}
|
||||
if ($valid_ifName && $nosame_ifName && preg_match($bi, $port['ifName'])) {
|
||||
print_debug("ignored (by ifName regexp): ".$port['ifName']." [ $bi ]");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
// FIXME. Prefer regexp
|
||||
if ($valid_ifDescr && str_icontains_array($port['ifDescr'], (array)$config['bad_if'])) {
|
||||
$bi = $GLOBALS['str_last_needle'];
|
||||
print_debug("ignored (by ifDescr): ".$port['ifDescr']." [ $bi ]");
|
||||
return FALSE;
|
||||
}
|
||||
if ($valid_ifName && $nosame_ifName && str_icontains_array($port['ifName'], (array)$config['bad_if'])) {
|
||||
$bi = $GLOBALS['str_last_needle'];
|
||||
print_debug("ignored (by ifName): ".$port['ifName']." [ $bi ]");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Ignore ports by ifType
|
||||
if (isset($config['bad_iftype']) && str_contains_array($port['ifType'], (array)$config['bad_iftype'])) {
|
||||
$bi = $GLOBALS['str_last_needle'];
|
||||
print_debug("ignored (by ifType): ".$port['ifType']." [ $bi ]");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Delete port from database and associated rrd files
|
||||
// DOCME needs phpdoc block
|
||||
// TESTME needs unit testing
|
||||
function delete_port($int_id, $delete_rrd = TRUE) {
|
||||
global $config;
|
||||
|
||||
$port = dbFetchRow("SELECT * FROM `ports`
|
||||
LEFT JOIN `devices` USING (`device_id`)
|
||||
WHERE `port_id` = ?", array($int_id));
|
||||
$ret = "> Deleted interface from ".$port['hostname'].": id=$int_id (".$port['ifDescr'].")\n";
|
||||
|
||||
// Remove entities from common tables
|
||||
$deleted_entities = array();
|
||||
foreach ($config['entity_tables'] as $table)
|
||||
{
|
||||
$where = '`entity_type` = ?' . generate_query_values($int_id, 'entity_id');
|
||||
$table_status = dbDelete($table, $where, array('port'));
|
||||
if ($table_status) { $deleted_entities['port'] = 1; }
|
||||
}
|
||||
if (count($deleted_entities))
|
||||
{
|
||||
$ret .= ' * Deleted common entity entries linked to port.' . PHP_EOL;
|
||||
}
|
||||
|
||||
// FIXME, move to definitions
|
||||
$port_tables = array('eigrp_ports', 'ipv4_addresses', 'ipv6_addresses',
|
||||
'ip_mac', 'juniAtmVp', 'mac_accounting', 'ospf_nbrs', 'ospf_ports',
|
||||
'ports_adsl', 'ports_cbqos', 'ports_vlans', 'pseudowires', 'vlans_fdb',
|
||||
'neighbours', 'ports');
|
||||
$deleted_tables = [];
|
||||
foreach ($port_tables as $table)
|
||||
{
|
||||
$table_status = dbDelete($table, "`port_id` = ?", array($int_id));
|
||||
if ($table_status) { $deleted_tables[] = $table; }
|
||||
}
|
||||
|
||||
$table_status = dbDelete('ports_stack', "`port_id_high` = ? OR `port_id_low` = ?", array($port['ifIndex'], $port['ifIndex']));
|
||||
if ($table_status) { $deleted_tables[] = 'ports_stack'; }
|
||||
$table_status = dbDelete('entity_permissions', "`entity_type` = 'port' AND `entity_id` = ?", array($int_id));
|
||||
if ($table_status) { $deleted_tables[] = 'entity_permissions'; }
|
||||
$table_status = dbDelete('alert_table', "`entity_type` = 'port' AND `entity_id` = ?", array($int_id));
|
||||
if ($table_status) { $deleted_tables[] = 'alert_table'; }
|
||||
$table_status = dbDelete('group_table', "`entity_type` = 'port' AND `entity_id` = ?", array($int_id));
|
||||
if ($table_status) { $deleted_tables[] = 'group_table'; }
|
||||
|
||||
$ret .= ' * Deleted interface entries from tables: '.implode(', ', $deleted_tables).PHP_EOL;
|
||||
|
||||
if ($delete_rrd) {
|
||||
$rrd_types = array('adsl', 'dot3', 'fdbcount', 'poe', NULL);
|
||||
$deleted_rrds = [];
|
||||
foreach ($rrd_types as $type) {
|
||||
$rrdfile = get_port_rrdfilename($port, $type, TRUE);
|
||||
if (is_file($rrdfile)) {
|
||||
unlink($rrdfile);
|
||||
$deleted_rrds[] = $rrdfile;
|
||||
}
|
||||
}
|
||||
$ret .= ' * Deleted interface RRD files: ' . implode(', ', $deleted_rrds) . PHP_EOL;
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
// DOCME needs phpdoc block
|
||||
// TESTME needs unit testing
|
||||
function port_html_class($ifOperStatus, $ifAdminStatus, $encrypted = FALSE)
|
||||
{
|
||||
$ifclass = "interface-upup";
|
||||
if ($ifAdminStatus == "down") { $ifclass = "gray"; }
|
||||
elseif ($ifAdminStatus == "up")
|
||||
{
|
||||
if ($ifOperStatus == "down") { $ifclass = "red"; }
|
||||
elseif ($ifOperStatus == "lowerLayerDown") { $ifclass = "orange"; }
|
||||
elseif ($ifOperStatus == "monitoring") { $ifclass = "green"; }
|
||||
//elseif ($encrypted === '1') { $ifclass = "olive"; }
|
||||
elseif ($encrypted) { $ifclass = "olive"; }
|
||||
elseif ($ifOperStatus == "up") { $ifclass = ""; }
|
||||
else { $ifclass = "purple"; }
|
||||
}
|
||||
|
||||
return $ifclass;
|
||||
}
|
||||
|
||||
// DOCME needs phpdoc block
|
||||
// TESTME needs unit testing
|
||||
function get_port_rrdindex($port)
|
||||
{
|
||||
global $config;
|
||||
|
||||
if (isset($port['device_id']))
|
||||
{
|
||||
$device_id = $port['device_id'];
|
||||
} else {
|
||||
// In poller, device_id not always passed
|
||||
$port_tmp = get_port_by_id_cache($port['port_id']);
|
||||
$device_id = $port_tmp['device_id'];
|
||||
}
|
||||
$device = device_by_id_cache($device_id);
|
||||
|
||||
if (isset($config['os'][$device['os']]['port_rrd_identifier']))
|
||||
{
|
||||
$device_identifier = strtolower($config['os'][$device['os']]['port_rrd_identifier']);
|
||||
} else {
|
||||
$device_identifier = 'ifindex';
|
||||
}
|
||||
|
||||
// default to ifIndex
|
||||
$this_port_identifier = $port['ifIndex'];
|
||||
|
||||
if ($device_identifier == "ifname" && $port['ifName'] != "")
|
||||
{
|
||||
$this_port_identifier = strtolower(str_replace("/", "-", $port['ifName']));
|
||||
}
|
||||
|
||||
return $this_port_identifier;
|
||||
}
|
||||
|
||||
// CLEANME DEPRECATED
|
||||
function get_port_rrdfilename($port, $suffix = NULL, $fullpath = FALSE)
|
||||
{
|
||||
$this_port_identifier = get_port_rrdindex($port);
|
||||
|
||||
if ($suffix == "")
|
||||
{
|
||||
$filename = "port-" . $this_port_identifier . ".rrd";
|
||||
} else {
|
||||
$filename = "port-" . $this_port_identifier . "-" . $suffix . ".rrd";
|
||||
}
|
||||
|
||||
if ($fullpath)
|
||||
{
|
||||
if (isset($port['device_id']))
|
||||
{
|
||||
$device_id = $port['device_id'];
|
||||
} else {
|
||||
// In poller, device_id not always passed
|
||||
$port_tmp = get_port_by_id_cache($port['port_id']);
|
||||
$device_id = $port_tmp['device_id'];
|
||||
}
|
||||
$device = device_by_id_cache($device_id);
|
||||
$filename = get_rrd_path($device, $filename);
|
||||
}
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
// EOF
|
821
includes/entities/routing.inc.php
Normal file
821
includes/entities/routing.inc.php
Normal file
@ -0,0 +1,821 @@
|
||||
<?php
|
||||
/**
|
||||
* Observium
|
||||
*
|
||||
* This file is part of Observium.
|
||||
*
|
||||
* @package observium
|
||||
* @subpackage entities
|
||||
* @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Validate BGP peer
|
||||
*
|
||||
* @param array $peer BGP peer array from discovery or polling process
|
||||
* @param array $device Common device array
|
||||
* @return boolean TRUE if peer array valid
|
||||
*/
|
||||
function is_bgp_peer_valid($peer, $device) {
|
||||
$valid = TRUE;
|
||||
|
||||
if (isset($peer['admin_status']) && empty($peer['admin_status'])) {
|
||||
$valid = FALSE;
|
||||
print_debug("Peer ignored (by empty Admin Status).");
|
||||
}
|
||||
|
||||
if ($valid && !(is_numeric($peer['as']) && $peer['as'] != 0)) {
|
||||
$valid = FALSE;
|
||||
print_debug("Peer ignored (by invalid AS number '".$peer['as']."').");
|
||||
}
|
||||
|
||||
if ($valid && !get_ip_version($peer['ip'])) {
|
||||
$valid = FALSE;
|
||||
print_debug("Peer ignored (by invalid Remote IP '".$peer['ip']."').");
|
||||
}
|
||||
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect is BGP AS number in private range, see:
|
||||
* https://tools.ietf.org/html/rfc6996
|
||||
* https://tools.ietf.org/html/rfc7300
|
||||
*
|
||||
* @param string|int $as AS number
|
||||
* @return boolean TRUE if AS number in private range
|
||||
*/
|
||||
function is_bgp_as_private($as) {
|
||||
$as = bgp_asdot_to_asplain($as); // Convert ASdot to ASplain
|
||||
|
||||
// Note 65535 and 5294967295 not really Private ASNs,
|
||||
// this is Reserved for use by Well-known Communities
|
||||
$private = ($as >= 64512 && $as <= 65535) || // 16-bit private ASn
|
||||
($as >= 4200000000 && $as <= 5294967295); // 32-bit private ASn
|
||||
|
||||
return $private;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert AS number from asplain to asdot format (for 32bit ASn).
|
||||
*
|
||||
* @param string|int|array $as AS number in plain or dot format
|
||||
* @return string|array AS number in dot format (for 32bit ASn)
|
||||
*/
|
||||
function bgp_asplain_to_asdot($as) {
|
||||
if (is_array($as)) {
|
||||
// Recursive for arrays
|
||||
$return = [];
|
||||
foreach ($as as $entry) {
|
||||
$return[] = bgp_asplain_to_asdot($entry);
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
if (str_contains($as, '.') || // Already asdot format
|
||||
($as < 65536)) { // 16bit ASn no need to formatting
|
||||
return $as;
|
||||
}
|
||||
|
||||
$as2 = $as % 65536;
|
||||
$as1 = ($as - $as2) / 65536;
|
||||
|
||||
return (int)$as1 . '.' . (int)$as2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert AS number from asdot to asplain format (for 32bit ASn).
|
||||
*
|
||||
* @param string|int|array $as AS number in plain or dot format
|
||||
* @return string|array AS number in plain format (for 32bit ASn)
|
||||
*/
|
||||
function bgp_asdot_to_asplain($as) {
|
||||
if (is_array($as)) {
|
||||
// Recursive for arrays
|
||||
$return = [];
|
||||
foreach ($as as $entry) {
|
||||
$return[] = bgp_asdot_to_asplain($entry);
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
if (!str_contains($as, '.')) { // Already asplain format
|
||||
return $as;
|
||||
}
|
||||
|
||||
list($as1, $as2) = explode('.', $as, 2);
|
||||
$as = $as1 * 65536 + $as2;
|
||||
|
||||
return (string) $as;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $device
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function get_bgp_localas_array($device) {
|
||||
global $config, $attribs;
|
||||
|
||||
if (OBS_PROCESS_NAME === 'poller') {
|
||||
// In poller return cached array
|
||||
if (isset($attribs['bgp_localas'])) {
|
||||
print_debug("Get Cached BGP LocalAs array.");
|
||||
$return = safe_json_decode($attribs['bgp_localas']);
|
||||
print_debug_vars($return);
|
||||
return $return;
|
||||
}
|
||||
if (safe_empty($device['bgpLocalAs']) || $device['bgpLocalAs'] == 0) {
|
||||
// Old empty peers (before rewrite or caching)
|
||||
print_debug("BGP not discovered on device.");
|
||||
return [];
|
||||
}
|
||||
} else {
|
||||
$db_bgp_localas = get_entity_attrib('device', $device['device_id'], 'bgp_localas');
|
||||
}
|
||||
|
||||
// use correct MIBs order:
|
||||
// first always CISCO-BGP4-MIB (before BGP4-MIB)
|
||||
// second BGP4-MIB, then all others
|
||||
$bgp_mibs = [];
|
||||
foreach (get_device_mibs_permitted($device) as $mib) {
|
||||
// check bgp definitions
|
||||
if (!isset($config['mibs'][$mib]['bgp'])) { continue; }
|
||||
|
||||
if ($mib === 'CISCO-BGP4-MIB') {
|
||||
array_unshift($bgp_mibs , $mib);
|
||||
} elseif ($mib === 'BGP4-MIB') {
|
||||
if ($bgp_mibs[0] === 'CISCO-BGP4-MIB') {
|
||||
// put BGP4-MIB after CISCO-BGP4-MIB
|
||||
array_splice($bgp_mibs, 1, 0, $mib);
|
||||
} else {
|
||||
array_unshift($bgp_mibs, $mib);
|
||||
}
|
||||
} else {
|
||||
$bgp_mibs[] = $mib;
|
||||
}
|
||||
}
|
||||
print_debug_vars($bgp_mibs);
|
||||
|
||||
if (empty($bgp_mibs)) {
|
||||
if (!safe_empty($db_bgp_localas)) {
|
||||
// Clean cache when BGP removed
|
||||
del_entity_attrib('device', $device['device_id'], 'bgp_localas');
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
// Common LocalAs before all
|
||||
$bgp4_local_as = snmp_get_oid($device, 'bgpLocalAs.0', 'BGP4-MIB');
|
||||
$bgp4_mib = is_numeric($bgp4_local_as);
|
||||
|
||||
$entries = [];
|
||||
foreach ($bgp_mibs as $mib) {
|
||||
$def = $config['mibs'][$mib]['bgp'];
|
||||
|
||||
// Main
|
||||
if ($mib === 'CISCO-BGP4-MIB') {
|
||||
// Cisco can report Afi/Safi but without cbgpLocalAs.0 and cbgpPeer2RemoteAs,
|
||||
// Not sure, probably check cbgpPeerAcceptedPrefixes / cbgpPeer2AcceptedPrefixes here
|
||||
if ($local_as = snmp_get_oid($device, 'cbgpLocalAs.0', $mib)) {
|
||||
$ctest = snmp_getnext_oid($device, 'cbgpPeer2AcceptedPrefixes', $mib);
|
||||
$cisco_version = safe_empty($ctest) ? 1 : 2;
|
||||
$entries[] = [ 'mib' => $mib, 'oid' => 'cbgpLocalAs.0', 'LocalAs' => snmp_dewrap32bit($local_as), 'cisco_version' => $cisco_version ];
|
||||
} else {
|
||||
$local_as = snmp_getnext_oid($device, 'cbgpPeer2LocalAs', $mib);
|
||||
// cbgpPeer2LocalAs in many devices is zero, but peers exist
|
||||
if (is_numeric($local_as)) {
|
||||
$entries[] = [ 'mib' => $mib, 'oid' => 'cbgpPeer2LocalAs', 'LocalAs' => snmp_dewrap32bit($local_as), 'cisco_version' => 2 ];
|
||||
} elseif ($bgp4_mib) {
|
||||
// Only check AcceptedPrefixes
|
||||
$ctest = snmp_getnext_oid($device, 'cbgpPeer2AcceptedPrefixes', $mib);
|
||||
$cisco_version = 2;
|
||||
if (!snmp_status()) {
|
||||
$ctest = snmp_getnext_oid($device, 'cbgpPeerAcceptedPrefixes', $mib);
|
||||
$cisco_version = 1;
|
||||
// CISCO-BGP4-MIB not exist
|
||||
if (!snmp_status()) { continue; }
|
||||
}
|
||||
$entries[] = [ 'mib' => $mib, 'cisco_version' => $cisco_version ];
|
||||
}
|
||||
}
|
||||
} elseif ($mib === 'BGP4-MIB') {
|
||||
if ($bgp4_mib) {
|
||||
$entries[] = [ 'mib' => $mib, 'oid' => $def['oids']['LocalAs']['oid'], 'LocalAs' => snmp_dewrap32bit($bgp4_local_as) ];
|
||||
}
|
||||
} elseif (isset($def['oids']['LocalAs']['oid'])) {
|
||||
// Vendor definitions
|
||||
$local_as = snmp_get_oid($device, $def['oids']['LocalAs']['oid'], $mib);
|
||||
if (is_numeric($local_as)) {
|
||||
$entries[] = [ 'mib' => $mib, 'oid' => $def['oids']['LocalAs']['oid'], 'LocalAs' => snmp_dewrap32bit($local_as) ];
|
||||
}
|
||||
|
||||
} elseif (isset($def['oids']['LocalAs']['oid_next'])) {
|
||||
// Vendor definitions
|
||||
$local_as = snmp_getnext_oid($device, $def['oids']['LocalAs']['oid_next'], $mib);
|
||||
if (is_numeric($local_as)) {
|
||||
$entries[] = [ 'mib' => $mib, 'oid' => $def['oids']['LocalAs']['oid_next'], 'LocalAs' => snmp_dewrap32bit($local_as) ];
|
||||
}
|
||||
|
||||
} elseif ($bgp4_mib) {
|
||||
// When MIB specific LocalAs not exist, but BGP4-MIB already return valid LocasAs
|
||||
// append this mib to entries, ie HUAWEI-BGP-VPN-MIB, CUMULUS-BGPUN-MIB
|
||||
snmp_getnext_oid($device, $def['oids']['PeerRemoteAs']['oid'], $mib);
|
||||
if (snmp_status()) {
|
||||
$entries[] = [ 'mib' => $mib ];
|
||||
}
|
||||
}
|
||||
|
||||
// Check in Virtual Routing
|
||||
$check_vrfs = (safe_empty($device['snmp_context']) && // Device not already with context
|
||||
isset($config['os'][$device['os']]['snmp']['virtual']) && $config['os'][$device['os']]['snmp']['virtual'] && // Context permitted for os
|
||||
$vrf_contexts = safe_json_decode(get_entity_attrib('device', $device, 'vrf_contexts')));
|
||||
|
||||
if ($check_vrfs) {
|
||||
|
||||
$bgp4_mib_vrf = [];
|
||||
foreach ($vrf_contexts as $vrf_name => $snmp_virtual) {
|
||||
print_debug("Check LocalAs in Virtual Routing: $vrf_name...");
|
||||
|
||||
$device_vrf = snmp_virtual_device($device, $snmp_virtual);
|
||||
|
||||
// Common LocalAs before all
|
||||
$bgp4_local_as = snmp_get_oid($device_vrf, 'bgpLocalAs.0', 'BGP4-MIB');
|
||||
$bgp4_mib_vrf[$vrf_name] = is_numeric($bgp4_local_as);
|
||||
|
||||
if ($mib === 'CISCO-BGP4-MIB') {
|
||||
// Cisco can report Afi/Safi but without cbgpLocalAs.0 and cbgpPeer2RemoteAs,
|
||||
// Not sure, probably check cbgpPeerAcceptedPrefixes / cbgpPeer2AcceptedPrefixes here
|
||||
if ($local_as = snmp_get_oid($device_vrf, 'cbgpLocalAs.0', $mib)) {
|
||||
$ctest = snmp_getnext_oid($device_vrf, 'cbgpPeer2AcceptedPrefixes', $mib);
|
||||
$cisco_version = safe_empty($ctest) ? 1 : 2;
|
||||
$entries[] = [ 'mib' => $mib, 'oid' => 'cbgpLocalAs.0', 'LocalAs' => snmp_dewrap32bit($local_as), 'cisco_version' => $cisco_version,
|
||||
'virtual_name' => $vrf_name, 'snmp_context' => $snmp_virtual ];
|
||||
} else {
|
||||
$local_as = snmp_getnext_oid($device_vrf, 'cbgpPeer2LocalAs', $mib);
|
||||
// cbgpPeer2LocalAs in many devices is zero, but peers exist
|
||||
if (is_numeric($local_as)) {
|
||||
$entries[] = [ 'mib' => $mib, 'oid' => 'cbgpPeer2LocalAs', 'LocalAs' => snmp_dewrap32bit($local_as), 'cisco_version' => 2,
|
||||
'virtual_name' => $vrf_name, 'snmp_context' => $snmp_virtual ];
|
||||
} elseif ($bgp4_mib_vrf[$vrf_name]) {
|
||||
// Only check AcceptedPrefixes
|
||||
$ctest = snmp_getnext_oid($device_vrf, 'cbgpPeer2AcceptedPrefixes', $mib);
|
||||
$cisco_version = 2;
|
||||
if (!snmp_status()) {
|
||||
$ctest = snmp_getnext_oid($device_vrf, 'cbgpPeerAcceptedPrefixes', $mib);
|
||||
$cisco_version = 1;
|
||||
// CISCO-BGP4-MIB not exist
|
||||
if (!snmp_status()) { continue; }
|
||||
}
|
||||
$entries[] = [ 'mib' => $mib, 'cisco_version' => $cisco_version,
|
||||
'virtual_name' => $vrf_name, 'snmp_context' => $snmp_virtual ];
|
||||
}
|
||||
}
|
||||
} elseif ($mib === 'BGP4-MIB') {
|
||||
if ($bgp4_mib_vrf[$vrf_name]) {
|
||||
$entries[] = [ 'mib' => $mib, 'oid' => $def['oids']['LocalAs']['oid'], 'LocalAs' => snmp_dewrap32bit($bgp4_local_as),
|
||||
'virtual_name' => $vrf_name, 'snmp_context' => $snmp_virtual ];
|
||||
}
|
||||
} elseif (isset($def['oids']['LocalAs']['oid'])) {
|
||||
|
||||
$local_as = snmp_get_oid($device_vrf, $def['oids']['LocalAs']['oid'], $mib);
|
||||
if (is_numeric($local_as)) {
|
||||
$entries[] = [ 'mib' => $mib, 'oid' => $def['oids']['LocalAs']['oid'], 'LocalAs' => snmp_dewrap32bit($local_as),
|
||||
'virtual_name' => $vrf_name, 'snmp_context' => $snmp_virtual ];
|
||||
}
|
||||
|
||||
} elseif (isset($def['oids']['LocalAs']['oid_next'])) {
|
||||
|
||||
$local_as = snmp_getnext_oid($device_vrf, $def['oids']['LocalAs']['oid_next'], $mib);
|
||||
if (is_numeric($local_as)) {
|
||||
$entries[] = [ 'mib' => $mib, 'oid' => $def['oids']['LocalAs']['oid_next'], 'LocalAs' => snmp_dewrap32bit($local_as),
|
||||
'virtual_name' => $vrf_name, 'snmp_context' => $snmp_virtual ];
|
||||
}
|
||||
|
||||
} elseif (isset($bgp4_mib_vrf[$vrf_name])) {
|
||||
// When MIB specific LocalAs not exist, but BGP4-MIB already return valid LocasAs
|
||||
// append this mib to entries, ie HUAWEI-BGP-VPN-MIB
|
||||
snmp_getnext_oid($device_vrf, $def['oids']['PeerRemoteAs']['oid'], $mib);
|
||||
if (snmp_status()) {
|
||||
$entries[] = [ 'mib' => $mib, 'virtual_name' => $vrf_name, 'snmp_context' => $snmp_virtual ];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
print_debug_vars($entries);
|
||||
|
||||
// Cache entries (in discovery)
|
||||
if (safe_count($entries)) {
|
||||
$attrib = safe_json_encode($entries);
|
||||
if ($attrib !== $db_bgp_localas) {
|
||||
set_entity_attrib('device', $device['device_id'], 'bgp_localas', safe_json_encode($entries));
|
||||
if (OBS_PROCESS_NAME === 'poller') {
|
||||
$attribs['bgp_localas'] = safe_json_encode($entries);
|
||||
}
|
||||
}
|
||||
} elseif (!safe_empty($db_bgp_localas)) {
|
||||
// Clean cache when BGP removed
|
||||
del_entity_attrib('device', $device['device_id'], 'bgp_localas');
|
||||
}
|
||||
|
||||
return $entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert BGP peer index to vendor MIB specific entries
|
||||
*
|
||||
* @param array $peer Array with walked peer oids
|
||||
* @param string $index Peer index
|
||||
* @param string $mib MIB name
|
||||
*/
|
||||
function parse_bgp_peer_index(&$peer, $index, $mib = 'BGP4V2-MIB') {
|
||||
global $config;
|
||||
|
||||
$address_types = $config['mibs']['INET-ADDRESS-MIB']['rewrite']['InetAddressType'];
|
||||
$index_parts = explode('.', $index);
|
||||
switch ($mib) {
|
||||
case 'BGP4-MIB':
|
||||
// bgpPeerRemoteAddr
|
||||
$peer_ip = $index;
|
||||
if (safe_count($index_parts) > 4) {
|
||||
// Aruba case:
|
||||
// BGP4-MIB::bgpPeerRemoteAddr.4.18.1.23.109 = IpAddress: 18.1.23.109
|
||||
$ip_len = array_shift($index_parts);
|
||||
$peer_ip = implode('.', $index_parts);
|
||||
}
|
||||
if (get_ip_version($peer_ip)) {
|
||||
$peer['bgpPeerRemoteAddr'] = $peer_ip;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'ARISTA-BGP4V2-MIB':
|
||||
// 1. aristaBgp4V2PeerInstance
|
||||
$peer['aristaBgp4V2PeerInstance'] = array_shift($index_parts);
|
||||
// 2. aristaBgp4V2PeerRemoteAddrType
|
||||
$peer_addr_type = array_shift($index_parts);
|
||||
if (safe_empty($peer['aristaBgp4V2PeerRemoteAddrType'])) {
|
||||
$peer['aristaBgp4V2PeerRemoteAddrType'] = $peer_addr_type;
|
||||
}
|
||||
if (isset($address_types[$peer['aristaBgp4V2PeerRemoteAddrType']])) {
|
||||
$peer['aristaBgp4V2PeerRemoteAddrType'] = $address_types[$peer['aristaBgp4V2PeerRemoteAddrType']];
|
||||
}
|
||||
// 3. length of the IP address
|
||||
$ip_len = array_shift($index_parts);
|
||||
// 4. IP address
|
||||
$ip_parts = array_slice($index_parts, 0, $ip_len);
|
||||
|
||||
// 5. aristaBgp4V2PeerRemoteAddr
|
||||
$peer_ip = implode('.', $ip_parts);
|
||||
if ((int)$ip_len === 16) {
|
||||
$peer_ip = snmp2ipv6($peer_ip);
|
||||
}
|
||||
if ($peer_addr_type = get_ip_version($peer_ip)) {
|
||||
$peer['aristaBgp4V2PeerRemoteAddr'] = $peer_ip;
|
||||
$peer['aristaBgp4V2PeerRemoteAddrType'] = 'ipv' . $peer_addr_type; // FIXME. not sure, but seems as Arista use only ipv4/ipv6 for afi
|
||||
}
|
||||
break;
|
||||
|
||||
case 'BGP4V2-MIB':
|
||||
case 'FOUNDRY-BGP4V2-MIB': // BGP4V2-MIB draft
|
||||
// 1. bgp4V2PeerInstance
|
||||
$peer['bgp4V2PeerInstance'] = array_shift($index_parts);
|
||||
// 2. bgp4V2PeerLocalAddrType
|
||||
$local_addr_type = array_shift($index_parts);
|
||||
if (safe_empty($peer['bgp4V2PeerLocalAddrType'])) {
|
||||
$peer['bgp4V2PeerLocalAddrType'] = $local_addr_type;
|
||||
}
|
||||
if (isset($address_types[$peer['bgp4V2PeerLocalAddrType']])) {
|
||||
$peer['bgp4V2PeerLocalAddrType'] = $address_types[$peer['bgp4V2PeerLocalAddrType']];
|
||||
}
|
||||
// 3. length of the local IP address
|
||||
$ip_len = array_shift($index_parts);
|
||||
// 4. IP address
|
||||
$ip_parts = array_slice($index_parts, 0, $ip_len);
|
||||
|
||||
// 5. bgp4V2PeerLocalAddr
|
||||
$local_ip = implode('.', $ip_parts);
|
||||
if ((int)$ip_len === 16) {
|
||||
$local_ip = snmp2ipv6($local_ip);
|
||||
}
|
||||
if (get_ip_version($local_ip)) {
|
||||
$peer['bgp4V2PeerLocalAddr'] = $local_ip;
|
||||
}
|
||||
|
||||
// Get second part of index
|
||||
$index_parts = array_slice($index_parts, $ip_len);
|
||||
$peer_addr_type = array_shift($index_parts);
|
||||
if (safe_empty($peer['bgp4V2PeerRemoteAddrType'])) {
|
||||
$peer['bgp4V2PeerRemoteAddrType'] = $peer_addr_type;
|
||||
}
|
||||
if (isset($address_types[$peer['bgp4V2PeerRemoteAddrType']])) {
|
||||
$peer['bgp4V2PeerRemoteAddrType'] = $address_types[$peer['bgp4V2PeerRemoteAddrType']];
|
||||
}
|
||||
// 6. length of the IP address
|
||||
$ip_len = array_shift($index_parts);
|
||||
// 7. IP address
|
||||
$ip_parts = array_slice($index_parts, 0, $ip_len);
|
||||
|
||||
// 8. bgp4V2PeerRemoteAddr
|
||||
$peer_ip = implode('.', $ip_parts);
|
||||
if ((int)$ip_len === 16) {
|
||||
$peer_ip = snmp2ipv6($peer_ip);
|
||||
}
|
||||
if ($peer_addr_type = get_ip_version($peer_ip)) {
|
||||
$peer['bgp4V2PeerRemoteAddr'] = $peer_ip;
|
||||
$peer['bgp4V2PeerRemoteAddrType'] = 'ipv' . $peer_addr_type;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'HUAWEI-BGP-VPN-MIB':
|
||||
// HUAWEI-BGP-VPN-MIB::hwBgpPeerVrfName.0.ipv4.unicast.ipv4."10.100.0.5" = STRING: "Public"
|
||||
// HUAWEI-BGP-VPN-MIB::hwBgpPeerVrfName.0.1.1.1.4.10.100.0.5 = STRING: "Public"
|
||||
// 1. hwBgpPeerInstanceId
|
||||
$peer['hwBgpPeerInstanceId'] = array_shift($index_parts);
|
||||
|
||||
// 2. hwBgpPeerAddrFamilyAfi
|
||||
$afi = array_shift($index_parts);
|
||||
$afis = [ 1 => 'ipv4', 2 => 'ipv6', 25 => 'vpls', 196 => 'l2vpn' ]; // Huawei specific AFI numbers (HWBgpAfi)
|
||||
if (isset($afis[$afi])) {
|
||||
$peer['hwBgpPeerAddrFamilyAfi'] = $afis[$afi];
|
||||
} else {
|
||||
// or use common afis
|
||||
$peer['hwBgpPeerAddrFamilyAfi'] = $config['routing_afis'][$afi];
|
||||
}
|
||||
|
||||
// 3. hwBgpPeerAddrFamilySafi
|
||||
$safi = array_shift($index_parts);
|
||||
$safis = [ 1 => 'unicast', 2 => 'multicast', 4 => 'mpls', 5 => 'mcast-vpn', 65 => 'vpls', 66 => 'mdt', 128 => 'vpn', 132 => 'route-target' ]; // Huawei specific SAFI numbers (HWBgpSafi)
|
||||
if (isset($safis[$safi])) {
|
||||
$peer['hwBgpPeerAddrFamilySafi'] = $safis[$safi];
|
||||
} else {
|
||||
// or use common safi
|
||||
$peer['hwBgpPeerAddrFamilySafi'] = $config['routing_safis'][$safi];
|
||||
}
|
||||
// 4. hwBgpPeerRemoteAddrType (hwBgpPeerType)
|
||||
$peer_addr_type = array_shift($index_parts);
|
||||
if (isset($address_types[$peer_addr_type])) {
|
||||
$peer['hwBgpPeerRemoteAddrType'] = $address_types[$peer_addr_type];
|
||||
}
|
||||
// 5. hwBgpPeerRemoteAddr
|
||||
$ip_len = array_shift($index_parts);
|
||||
$ip_parts = $index_parts;
|
||||
$remote_ip = implode('.', $ip_parts);
|
||||
if ((int)$ip_len === 16) {
|
||||
$remote_ip = snmp2ipv6($remote_ip);
|
||||
}
|
||||
if ($peer_addr_type = get_ip_version($remote_ip)) {
|
||||
$peer['hwBgpPeerRemoteAddr'] = $remote_ip;
|
||||
$peer['hwBgpPeerRemoteIdentifier'] = $peer['hwBgpPeerRemoteAddr'];
|
||||
if (safe_empty($peer['hwBgpPeerRemoteAddrType'])) {
|
||||
$peer['hwBgpPeerRemoteAddrType'] = 'ipv' . $peer_addr_type;
|
||||
}
|
||||
}
|
||||
// 6. hwBgpPeerAdminStatus
|
||||
if (!isset($peer['hwBgpPeerAdminStatus'])) {
|
||||
// Always set this Oid to start, while not really exist and while peer entry exist in this table
|
||||
$peer['hwBgpPeerAdminStatus'] = 'start';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'BGP4-V2-MIB-JUNIPER':
|
||||
// 1. jnxBgpM2PeerRoutingInstance
|
||||
$peer['jnxBgpM2PeerRoutingInstance'] = array_shift($index_parts);
|
||||
// 2. jnxBgpM2PeerLocalAddrType
|
||||
$local_addr_type = array_shift($index_parts);
|
||||
if (safe_empty($peer['jnxBgpM2PeerLocalAddrType'])) {
|
||||
$peer['jnxBgpM2PeerLocalAddrType'] = $local_addr_type;
|
||||
}
|
||||
if (isset($address_types[$peer['jnxBgpM2PeerLocalAddrType']])) {
|
||||
$peer['jnxBgpM2PeerLocalAddrType'] = $address_types[$peer['jnxBgpM2PeerLocalAddrType']];
|
||||
}
|
||||
// 3. length of the local IP address
|
||||
$ip_len = str_contains($peer['jnxBgpM2PeerLocalAddrType'], 'ipv6') ? 16 : 4;
|
||||
// 4. IP address
|
||||
$ip_parts = array_slice($index_parts, 0, $ip_len);
|
||||
|
||||
// 5. jnxBgpM2PeerLocalAddr
|
||||
$local_ip = implode('.', $ip_parts);
|
||||
if ((int)$ip_len === 16) {
|
||||
$local_ip = snmp2ipv6($local_ip);
|
||||
}
|
||||
if (get_ip_version($local_ip)) {
|
||||
$peer['jnxBgpM2PeerLocalAddr'] = $local_ip;
|
||||
}
|
||||
|
||||
// Get second part of index
|
||||
$index_parts = array_slice($index_parts, $ip_len);
|
||||
// 6. jnxBgpM2PeerRemoteAddrType
|
||||
$peer_addr_type = array_shift($index_parts);
|
||||
if (safe_empty($peer['jnxBgpM2PeerRemoteAddrType'])) {
|
||||
$peer['jnxBgpM2PeerRemoteAddrType'] = $peer_addr_type;
|
||||
}
|
||||
if (isset($address_types[$peer['jnxBgpM2PeerRemoteAddrType']])) {
|
||||
$peer['jnxBgpM2PeerRemoteAddrType'] = $address_types[$peer['jnxBgpM2PeerRemoteAddrType']];
|
||||
}
|
||||
// 7. length of the remote IP address
|
||||
$ip_len = str_contains($peer['jnxBgpM2PeerRemoteAddrType'], 'ipv6') ? 16 : 4;
|
||||
// 8. IP address
|
||||
$ip_parts = array_slice($index_parts, 0, $ip_len);
|
||||
|
||||
// 9. jnxBgpM2PeerRemoteAddr
|
||||
$peer_ip = implode('.', $ip_parts);
|
||||
if ((int)$ip_len === 16) {
|
||||
$peer_ip = snmp2ipv6($peer_ip);
|
||||
}
|
||||
if (get_ip_version($peer_ip)) {
|
||||
$peer['jnxBgpM2PeerRemoteAddr'] = $peer_ip;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'DELLEMC-OS10-BGP4V2-MIB':
|
||||
// 1. os10bgp4V2PeerInstance
|
||||
$peer['os10bgp4V2PeerInstance'] = array_shift($index_parts);
|
||||
// 2. os10bgp4V2PeerRemoteAddrType
|
||||
$peer_addr_type = array_shift($index_parts);
|
||||
if (safe_empty($peer['os10bgp4V2PeerRemoteAddrType'])) {
|
||||
$peer['os10bgp4V2PeerRemoteAddrType'] = $peer_addr_type;
|
||||
}
|
||||
if (isset($address_types[$peer['os10bgp4V2PeerRemoteAddrType']])) {
|
||||
$peer['os10bgp4V2PeerRemoteAddrType'] = $address_types[$peer['os10bgp4V2PeerRemoteAddrType']];
|
||||
}
|
||||
// 3. length of the remote IP address
|
||||
$ip_len = array_shift($index_parts);
|
||||
// 4. IP address
|
||||
$ip_parts = array_slice($index_parts, 0, $ip_len);
|
||||
// 5. os10bgp4V2PeerRemoteAddr
|
||||
$peer_ip = implode('.', $ip_parts);
|
||||
if ((int)$ip_len === 16) {
|
||||
$peer_ip = snmp2ipv6($peer_ip);
|
||||
}
|
||||
if (get_ip_version($peer_ip)) {
|
||||
$peer['os10bgp4V2PeerRemoteAddr'] = $peer_ip;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'FORCE10-BGP4-V2-MIB':
|
||||
// 1. f10BgpM2PeerInstance
|
||||
$peer['f10BgpM2PeerInstance'] = array_shift($index_parts);
|
||||
// 2. f10BgpM2PeerLocalAddrType
|
||||
$local_addr_type = array_shift($index_parts);
|
||||
if (safe_empty($peer['f10BgpM2PeerLocalAddrType'])) {
|
||||
$peer['f10BgpM2PeerLocalAddrType'] = $local_addr_type;
|
||||
}
|
||||
if (isset($address_types[$peer['f10BgpM2PeerLocalAddrType']])) {
|
||||
$peer['f10BgpM2PeerLocalAddrType'] = $address_types[$peer['f10BgpM2PeerLocalAddrType']];
|
||||
}
|
||||
// 3. length of the local IP address
|
||||
$ip_len = str_contains($peer['f10BgpM2PeerLocalAddrType'], 'ipv6') ? 16 : 4;
|
||||
// 4. IP address
|
||||
$ip_parts = array_slice($index_parts, 0, $ip_len);
|
||||
|
||||
// 5. f10BgpM2PeerLocalAddr
|
||||
$local_ip = implode('.', $ip_parts);
|
||||
if ((int)$ip_len === 16) {
|
||||
$local_ip = snmp2ipv6($local_ip);
|
||||
}
|
||||
if (get_ip_version($local_ip)) {
|
||||
$peer['f10BgpM2PeerLocalAddr'] = $local_ip;
|
||||
}
|
||||
|
||||
// Get second part of index
|
||||
$index_parts = array_slice($index_parts, $ip_len);
|
||||
// 6. f10BgpM2PeerRemoteAddrType
|
||||
$peer_addr_type = array_shift($index_parts);
|
||||
if (safe_empty($peer['f10BgpM2PeerRemoteAddrType'])) {
|
||||
$peer['f10BgpM2PeerRemoteAddrType'] = $peer_addr_type;
|
||||
}
|
||||
if (isset($address_types[$peer['f10BgpM2PeerRemoteAddrType']])) {
|
||||
$peer['f10BgpM2PeerRemoteAddrType'] = $address_types[$peer['f10BgpM2PeerRemoteAddrType']];
|
||||
}
|
||||
// 7. length of the remote IP address
|
||||
$ip_len = str_contains($peer['f10BgpM2PeerRemoteAddrType'], 'ipv6') ? 16 : 4;
|
||||
// 8. IP address
|
||||
$ip_parts = array_slice($index_parts, 0, $ip_len);
|
||||
|
||||
// 9. f10BgpM2PeerRemoteAddr
|
||||
$peer_ip = implode('.', $ip_parts);
|
||||
if ((int)$ip_len === 16) {
|
||||
$peer_ip = snmp2ipv6($peer_ip);
|
||||
}
|
||||
if (get_ip_version($peer_ip)) {
|
||||
$peer['f10BgpM2PeerRemoteAddr'] = $peer_ip;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'VIPTELA-OPER-BGP':
|
||||
// 1. bgpBgpNeighborVpnId
|
||||
$peer['bgpBgpNeighborVpnId'] = array_shift($index_parts);
|
||||
|
||||
// 2. hwBgpPeerRemoteAddr
|
||||
$ip_len = safe_count($index_parts);
|
||||
$peer_ip = implode('.', $index_parts);
|
||||
if ((int)$ip_len === 16) {
|
||||
$peer_ip = snmp2ipv6($peer_ip);
|
||||
}
|
||||
if ($peer_addr_type = get_ip_version($peer_ip)) {
|
||||
$peer['bgpBgpNeighborPeerAddr'] = $peer_ip;
|
||||
$peer['bgpBgpNeighborPeerAddrType'] = 'ipv' . $peer_addr_type;
|
||||
}
|
||||
|
||||
// 6. hwBgpPeerAdminStatus
|
||||
if (!isset($peer['bgpBgpNeighborAdminState'])) {
|
||||
// Always set this Oid to start, while not really exist and while peer entry exist in this table
|
||||
$peer['bgpBgpNeighborAdminState'] = in_array($peer['bgpBgpNeighborState'], [ 'clearing', 'deleted' ]) ? 'stop' : 'start';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function discovery_bgp_afisafi($device, $peer, $afi, $safi, &$af_list) {
|
||||
global $table_rows;
|
||||
|
||||
$index = $peer['index'];
|
||||
$peer_id = $peer['id'];
|
||||
$peer_ip = $peer['ip'];
|
||||
$peer_as = $peer['as'];
|
||||
if (isset($GLOBALS['config']['routing_afis'][$afi])) {
|
||||
$afi = $GLOBALS['config']['routing_afis'][$afi]['name'];
|
||||
}
|
||||
if (isset($GLOBALS['config']['routing_safis'][$safi])) {
|
||||
$safi = $GLOBALS['config']['routing_safis'][$safi]['name'];
|
||||
}
|
||||
print_debug("INDEX: $index, AS: $peer_as, IP: $peer_ip, AFI: $afi, SAFI: $safi");
|
||||
print_debug_vars($peer);
|
||||
$af_list[$peer_id][$afi][$safi] = 1;
|
||||
|
||||
if (!safe_empty($table_rows[$peer_ip][4])) {
|
||||
$table_rows[$peer_ip][4] .= ', ';
|
||||
}
|
||||
$table_rows[$peer_ip][4] .= $afi . '.' . $safi;
|
||||
|
||||
if (!dbExist('bgpPeers_cbgp', '`device_id` = ? AND `bgpPeer_id` = ? AND `afi` = ? AND `safi` = ?', [ $device['device_id'], $peer_id, $afi, $safi ])) {
|
||||
$params = [ 'bgpPeer_id' => $peer_id, 'device_id' => $device['device_id'], 'bgpPeerIndex' => $index, 'afi' => $afi, 'safi' => $safi ];
|
||||
dbInsert($params, 'bgpPeers_cbgp');
|
||||
} elseif (!safe_empty($index)) {
|
||||
// Update Index
|
||||
dbUpdate([ 'bgpPeerIndex' => $index ], 'bgpPeers_cbgp', 'device_id = ? AND `bgpPeer_id` = ? AND `afi` = ? AND `safi` = ?', [ $device['device_id'], $peer_id, $afi, $safi ]);
|
||||
}
|
||||
}
|
||||
|
||||
// DOCME needs phpdoc block
|
||||
// TESTME needs unit testing
|
||||
function get_astext($asn) {
|
||||
global $config, $cache;
|
||||
|
||||
// Fetch pre-set AS text from config first
|
||||
if (isset($config['astext'][$asn])) {
|
||||
return $config['astext'][$asn];
|
||||
}
|
||||
|
||||
// Not preconfigured, check cache before doing a new DNS request
|
||||
if (!isset($cache['astext'][$asn])) {
|
||||
$result = dns_get_record("AS$asn.asn.cymru.com", DNS_TXT);
|
||||
print_debug_vars($result);
|
||||
$txt = explode('|', $result[0]['txt']);
|
||||
$cache['astext'][$asn] = trim(str_replace('"', '', $txt[4]));
|
||||
}
|
||||
|
||||
return $cache['astext'][$asn];
|
||||
}
|
||||
|
||||
function discover_vrf($device, $vrf)
|
||||
{
|
||||
global $cache_discovery, $valid, $table_rows;
|
||||
|
||||
$module = 'vrf';
|
||||
|
||||
if (empty($vrf['vrf_name'])) { return; }
|
||||
$vrf_name = $vrf['vrf_name'];
|
||||
|
||||
// Pre-cache VRFs from DB
|
||||
if (!isset($cache_discovery['vrf_db']))
|
||||
{
|
||||
$cache_discovery['vrf_db'] = [];
|
||||
foreach (dbFetchRows("SELECT * FROM `vrfs` WHERE `device_id` = ?", [ $device['device_id'] ]) as $entry)
|
||||
{
|
||||
// Strange case with duplicate entries: https://jira.observium.org/browse/OBS-3600
|
||||
if (isset($cache_discovery['vrf_db'][$entry['vrf_name']])) {
|
||||
print_debug("Duplicate VRF entry in DB found: ".$entry['vrf_name']);
|
||||
print_debug_vars($entry);
|
||||
dbDelete('vrfs', '`vrf_id` = ?', [ $entry['vrf_id'] ]);
|
||||
continue;
|
||||
}
|
||||
$cache_discovery['vrf_db'][$entry['vrf_name']] = $entry;
|
||||
}
|
||||
}
|
||||
|
||||
$params_main = [ 'vrf_mib', 'vrf_name', 'vrf_descr', 'vrf_rd' ];
|
||||
$params_state = [ 'vrf_admin_status', 'vrf_oper_status',
|
||||
'vrf_active_ports', 'vrf_total_ports',
|
||||
'vrf_added_routes', 'vrf_deleted_routes', 'vrf_total_routes' ];
|
||||
|
||||
$insert_array = [ 'device_id' => $device['device_id'] ];
|
||||
foreach ($params_main as $param)
|
||||
{
|
||||
$insert_array[$param] = isset($vrf[$param]) ? $vrf[$param] : '';
|
||||
}
|
||||
|
||||
// Set added/changed params
|
||||
$current_time = (int)time();
|
||||
$param = 'vrf_added';
|
||||
$insert_array[$param] = isset($vrf[$param]) && $vrf[$param] < $current_time ? (int)$vrf[$param] : $device['last_rebooted'];
|
||||
|
||||
if (!isset($cache_discovery['vrf_db'][$vrf['vrf_name']]))
|
||||
{
|
||||
// Added
|
||||
$param = 'vrf_last_change';
|
||||
$insert_array[$param] = isset($vrf[$param]) && $vrf[$param] < $current_time ? (int)$vrf[$param] : $current_time;
|
||||
|
||||
// When insert, also add state params
|
||||
foreach ($params_state as $param)
|
||||
{
|
||||
if (isset($vrf[$param]))
|
||||
{
|
||||
// When not set, use db default
|
||||
$insert_array[$param] = $vrf[$param];
|
||||
}
|
||||
}
|
||||
|
||||
$vrf_id = dbInsert($insert_array, 'vrfs');
|
||||
$GLOBALS['module_stats'][$module]['added']++; //echo "+";
|
||||
} else {
|
||||
// Compare/update
|
||||
|
||||
$update_array = [];
|
||||
$entry = $cache_discovery['vrf_db'][$vrf['vrf_name']];
|
||||
$vrf_id = $entry['vrf_id'];
|
||||
|
||||
foreach ($params_main as $param)
|
||||
{
|
||||
if ($insert_array[$param] !== $entry[$param])
|
||||
{
|
||||
$update_array[$param] = $insert_array[$param];
|
||||
}
|
||||
}
|
||||
|
||||
// Update old entries (after migrate)
|
||||
if (empty($entry['vrf_added']))
|
||||
{
|
||||
// State params
|
||||
foreach ($params_state as $param)
|
||||
{
|
||||
if (isset($vrf[$param]))
|
||||
{
|
||||
// When not set, use db default
|
||||
$update_array[$param] = $vrf[$param];
|
||||
}
|
||||
}
|
||||
|
||||
$update_array['vrf_added'] = $insert_array['vrf_added'];
|
||||
$update_array['vrf_last_change'] = isset($vrf['vrf_last_change']) && $vrf['vrf_last_change'] < $current_time ? (int)$vrf['vrf_last_change'] : $current_time;
|
||||
} else {
|
||||
|
||||
if (count($update_array)) {
|
||||
$update_array['vrf_last_change'] = isset($vrf['vrf_last_change']) && $vrf['vrf_last_change'] < $current_time ? (int)$vrf['vrf_last_change'] : $current_time;
|
||||
}
|
||||
// For old entries only validate added/changed times (more than 60 sec)
|
||||
/*foreach ([ 'vrf_added', 'vrf_last_change' ] as $param) {
|
||||
foreach ([ 'vrf_last_change' ] as $param) {
|
||||
if (abs($insert_array[$param] - $entry[$param]) > 60)
|
||||
{
|
||||
$update_array[$param] = $insert_array[$param];
|
||||
print_debug("$param: ".$insert_array[$param]." - ".$entry[$param]." = ".($insert_array[$param] - $entry[$param]));
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
if (safe_count($update_array))
|
||||
{
|
||||
dbUpdate($update_array, 'vrfs', '`vrf_id` = ?', [ $vrf_id ]);
|
||||
|
||||
$GLOBALS['module_stats'][$module]['updated']++;
|
||||
} else {
|
||||
$GLOBALS['module_stats'][$module]['unchanged']++;
|
||||
}
|
||||
}
|
||||
|
||||
$valid['vrf'][$vrf_name] = $vrf_id;
|
||||
|
||||
// VRF ports
|
||||
if (safe_count($vrf['ifIndex']))
|
||||
{
|
||||
$db_update = [];
|
||||
foreach ($vrf['ifIndex'] as $ifIndex)
|
||||
{
|
||||
if ($port = get_port_by_index_cache($device, $ifIndex))
|
||||
{
|
||||
$db_update[] = [ 'port_id' => $port['port_id'], 'ifIndex' => $port['ifIndex'], 'device_id' => $device['device_id'], 'ifVrf' => $vrf_id ];
|
||||
|
||||
$valid['vrf-ports'][$vrf_name][$port['port_id']] = $vrf_id;
|
||||
}
|
||||
}
|
||||
|
||||
dbUpdateMulti($db_update, 'ports', [ 'ifVrf' ]);
|
||||
}
|
||||
}
|
||||
|
||||
// EOF
|
1155
includes/entities/sensor.inc.php
Normal file
1155
includes/entities/sensor.inc.php
Normal file
File diff suppressed because it is too large
Load Diff
849
includes/entities/status.inc.php
Normal file
849
includes/entities/status.inc.php
Normal file
@ -0,0 +1,849 @@
|
||||
<?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
|
248
includes/entities/wifi.inc.php
Normal file
248
includes/entities/wifi.inc.php
Normal file
@ -0,0 +1,248 @@
|
||||
<?php
|
||||
/**
|
||||
* Observium
|
||||
*
|
||||
* This file is part of Observium.
|
||||
*
|
||||
* @package observium
|
||||
* @subpackage discovery
|
||||
* @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited
|
||||
*
|
||||
*/
|
||||
|
||||
//MOVEME. to includes/entities/wifi.inc.php
|
||||
// Discover an entity by populating/updating a database table and returning an id
|
||||
|
||||
/* I think this is still experimental by adama, pls see discover_entity_definition() common as other entities
|
||||
function discover_entity($device_id, $entity_type, $data)
|
||||
{
|
||||
|
||||
if (is_array($GLOBALS['config']['entities'][$entity_type])) {
|
||||
|
||||
$def = $GLOBALS['config']['entities'][$entity_type];
|
||||
$index = $data[$def['table_fields']['index']];
|
||||
$params = $def['params'];
|
||||
|
||||
if (isset($params['table_fields']['index']) && is_array())
|
||||
{
|
||||
|
||||
|
||||
|
||||
} elseif (isset($params['table_fields']['index'])) {
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (is_array($GLOBALS['cache'][$def['table']][$index])) {
|
||||
echo 'Exists';
|
||||
|
||||
$db = $GLOBALS['cache'][$def['table']][$index];
|
||||
$id = $db[$def['table_fields']['id']];
|
||||
|
||||
echo 'exists:'.$id.PHP_EOL;
|
||||
|
||||
$update = array();
|
||||
foreach ($params as $param)
|
||||
{
|
||||
if ($data[$param] != $db[$param]) { $update[$param] = $data[$param]; }
|
||||
}
|
||||
if (count($update))
|
||||
{
|
||||
dbUpdate($update, $def['table'], '`'.$def['table_fields']['id'].'` = ?', array($id));
|
||||
echo('U');
|
||||
} else {
|
||||
echo('.');
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
echo 'Doesnt Exist';
|
||||
|
||||
$insert = array();
|
||||
$insert['device_id'] = $device_id;
|
||||
foreach ($params as $param)
|
||||
{
|
||||
$insert[$param] = $data[$param];
|
||||
if ($data[$param] == NULL) { $insert[$param] = array('NULL'); }
|
||||
}
|
||||
$id = dbInsert($insert, $def['table']);
|
||||
echo("+");
|
||||
|
||||
$params[$def['table_fields']['id']] = $id;
|
||||
|
||||
// Populate cache with this entry. Maybe we need it.
|
||||
$GLOBALS['cache'][$def['table']][$index] = $params;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
print_error("Entity Type does not exist. This is a relatively serious error.");
|
||||
|
||||
return FALSE;
|
||||
|
||||
}
|
||||
|
||||
return $id;
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Discover WIFI Access Point. Returns ap_id.
|
||||
*
|
||||
* @param array|integer $device
|
||||
* @param array $ap
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
function discover_wifi_ap($device, $ap) {
|
||||
|
||||
$device_id = is_array($device) ? $device['device_id'] : $device;
|
||||
$params = [ 'ap_mib', 'ap_index',
|
||||
'ap_number', 'ap_name', 'ap_address', 'ap_serial', 'ap_model', 'ap_location', 'ap_fingerprint', // not required
|
||||
'ap_status', 'ap_admin_status' ];
|
||||
|
||||
if (is_array($GLOBALS['cache']['wifi_aps'][$ap['ap_index']])) {
|
||||
/* Only insert new APs, for polling ability
|
||||
// Database entry exists. Lets update it!
|
||||
|
||||
$ap_db = $GLOBALS['cache']['wifi_aps'][$ap['ap_index']];
|
||||
$ap_id = $ap_db['wifi_ap_id'];
|
||||
|
||||
echo 'exists:'.$ap_id.PHP_EOL;
|
||||
|
||||
$update = [];
|
||||
foreach ($params as $param) {
|
||||
if ($ap[$param] != $ap_db[$param]) {
|
||||
if (safe_empty($ap[$param])) {
|
||||
$update[$param] = [ 'NULL' ];
|
||||
} else {
|
||||
$update[$param] = $ap[$param];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count($update)) {
|
||||
dbUpdate($update, 'wifi_aps', '`wifi_ap_id` = ?', array($ap_db['wifi_ap_id']));
|
||||
echo('U');
|
||||
} else {
|
||||
echo('.');
|
||||
}
|
||||
*/
|
||||
$ap_id = $GLOBALS['cache']['wifi_aps'][$ap['ap_index']]['wifi_ap_id'];
|
||||
if ($GLOBALS['cache']['wifi_aps'][$ap['ap_index']]['deleted']) {
|
||||
dbUpdate([ 'deleted' => 0 ], 'wifi_aps', '`wifi_ap_id` = ?', [ $ap_id ]);
|
||||
$GLOBALS['cache']['wifi_aps'][$ap['ap_index']]['deleted'] = 0;
|
||||
}
|
||||
} else {
|
||||
// Database entry doesn't exist. Lets create it!
|
||||
$insert = [];
|
||||
$insert['device_id'] = $device_id;
|
||||
foreach ($params as $param) {
|
||||
$insert[$param] = $ap[$param];
|
||||
if (safe_empty($ap[$param])) {
|
||||
$insert[$param] = array('NULL');
|
||||
}
|
||||
}
|
||||
print_debug_vars($insert);
|
||||
$ap_id = dbInsert($insert, 'wifi_aps');
|
||||
echo("+");
|
||||
|
||||
$params['wifi_ap_id'] = $ap_id;
|
||||
|
||||
// Populate cache with this entry. Maybe we need it.
|
||||
$GLOBALS['cache']['wifi_aps'][$ap['ap_index']] = $params;
|
||||
}
|
||||
|
||||
$GLOBALS['valid']['wifi']['aps'][$ap['ap_index']] = $ap_id;
|
||||
|
||||
return $ap_id;
|
||||
}
|
||||
|
||||
|
||||
function discover_wifi_wlan($device_id, $wlan)
|
||||
{
|
||||
|
||||
$params = array('wlan_admin_status', 'wlan_beacon_period', 'wlan_bssid', 'wlan_bss_type', 'wlan_channel', 'wlan_dtim_period', 'wlan_frag_thresh',
|
||||
'wlan_index', 'wlan_igmp_snoop', 'wlan_name', 'wlan_prot_mode', 'wlan_radio_mode', 'wlan_rts_thresh',
|
||||
'wlan_ssid', 'wlan_ssid_bcast', 'wlan_vlan_id');
|
||||
|
||||
if (is_array($GLOBALS['cache']['wifi_wlans'][$wlan['wlan_index']]))
|
||||
{
|
||||
// Database entry exists. Lets update it!
|
||||
$wlan_db = $GLOBALS['cache']['wifi_wlans'][$wlan['wlan_index']];
|
||||
$wlan_id = $wlan_db['wlan_id'];
|
||||
|
||||
$update = array();
|
||||
foreach ($params as $param)
|
||||
{
|
||||
if ($wlan[$param] != $wlan_db[$param]) { $update[$param] = $wlan[$param]; }
|
||||
}
|
||||
if (count($update))
|
||||
{
|
||||
dbUpdate($update, 'wifi_wlans', '`wlan_id` = ?', array($wlan_db['wlan_id']));
|
||||
echo('U');
|
||||
} else {
|
||||
echo('.');
|
||||
}
|
||||
|
||||
} else {
|
||||
// Database entry doesn't exist. Lets create it!
|
||||
|
||||
$insert = array();
|
||||
$insert['device_id'] = $device_id;
|
||||
foreach ($params as $param)
|
||||
{
|
||||
$insert[$param] = $wlan[$param];
|
||||
if (is_null($wlan[$param])) { $insert[$param] = array('NULL'); }
|
||||
}
|
||||
$wlan_id = dbInsert($insert, 'wifi_wlans');
|
||||
echo("+");
|
||||
|
||||
}
|
||||
|
||||
return $wlan_id;
|
||||
|
||||
}
|
||||
|
||||
function discover_wifi_radio($device_id, $radio) {
|
||||
$params = [ 'radio_ap', 'radio_mib', 'radio_number', 'radio_util', 'radio_type', 'radio_status',
|
||||
'radio_clients', 'radio_txpower', 'radio_channel', 'radio_mac', 'radio_protection', 'radio_bsstype' ];
|
||||
|
||||
if (is_array($GLOBALS['cache']['wifi_radios'][$radio['radio_ap']][$radio['radio_number']])) {
|
||||
$radio_db = $GLOBALS['cache']['wifi_radios'][$radio['radio_ap']][$radio['radio_number']];
|
||||
}
|
||||
|
||||
if (!isset($radio_db['wifi_radio_id']))
|
||||
{
|
||||
$insert = array();
|
||||
$insert['device_id'] = $device_id;
|
||||
foreach ($params as $param)
|
||||
{
|
||||
$insert[$param] = $radio[$param];
|
||||
if (is_null($radio[$param])) { $insert[$param] = array('NULL'); }
|
||||
}
|
||||
$wifi_radio_id = dbInsert($insert, 'wifi_radios');
|
||||
echo("+");
|
||||
} else {
|
||||
$update = array();
|
||||
foreach ($params as $param)
|
||||
{
|
||||
if ($radio[$param] != $radio_db[$param]) { $update[$param] = $radio[$param]; }
|
||||
}
|
||||
if (count($update))
|
||||
{
|
||||
dbUpdate($update, 'wifi_radios', '`wifi_radio_id` = ?', array($radio_db['wifi_radio_id']));
|
||||
echo('U');
|
||||
} else {
|
||||
echo('.');
|
||||
}
|
||||
}
|
||||
|
||||
$GLOBALS['valid']['wifi']['radio'][$radio['radio_mib']][$wifi_radio_id] = 1;
|
||||
}
|
||||
|
||||
// EOF
|
Reference in New Issue
Block a user