mirror of
https://github.com/librenms/librenms.git
synced 2024-10-07 16:52:45 +00:00
APC: Add support for Battery Recommended Days Remaining (#14653)
* APC: Add support for Battery Recommended Days Remaining * Extract number from string later during polling Odd that this is different from the discovery process * wip * wip * Apply fixes from StyleCI * wip * wip * Apply fixes from StyleCI --------- Co-authored-by: Tony Murray <murraytony@gmail.com>
This commit is contained in:
27
LibreNMS/Exceptions/UserFunctionExistException.php
Normal file
27
LibreNMS/Exceptions/UserFunctionExistException.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* UserFunctionExistException.php
|
||||
*
|
||||
* -Description-
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @link https://www.librenms.org
|
||||
*/
|
||||
|
||||
namespace LibreNMS\Exceptions;
|
||||
|
||||
class UserFunctionExistException extends \Exception
|
||||
{
|
||||
}
|
@@ -93,7 +93,7 @@ class Number
|
||||
* @param mixed $number
|
||||
* @return float|int
|
||||
*/
|
||||
public static function cast($number)
|
||||
public static function cast(mixed $number): float|int
|
||||
{
|
||||
if (! is_numeric($number)) {
|
||||
// match pre-PHP8 behavior
|
||||
@@ -109,6 +109,21 @@ class Number
|
||||
return $float == $int ? $int : $float;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the first number found from a string
|
||||
*/
|
||||
public static function extract(mixed $string): float|int
|
||||
{
|
||||
if (! is_numeric($string)) {
|
||||
preg_match('/-?\d*\.?\d+/', $string, $matches);
|
||||
if (! empty($matches[0])) {
|
||||
$string = $matches[0];
|
||||
}
|
||||
}
|
||||
|
||||
return self::cast($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a percent, but make sure to not divide by zero. In that case, return 0.
|
||||
*
|
||||
|
@@ -25,6 +25,7 @@
|
||||
|
||||
namespace LibreNMS\Util;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Carbon\CarbonInterface;
|
||||
use Carbon\CarbonInterval;
|
||||
|
||||
@@ -113,4 +114,14 @@ class Time
|
||||
|
||||
return (int) strtotime($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a date and return the number of days from now
|
||||
*/
|
||||
public static function dateToDays(string|int $date): int
|
||||
{
|
||||
$carbon = new Carbon();
|
||||
|
||||
return $carbon->diffInDays($date, false);
|
||||
}
|
||||
}
|
||||
|
45
LibreNMS/Util/UserFuncHelper.php
Normal file
45
LibreNMS/Util/UserFuncHelper.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/**
|
||||
* UserFuncHelper.php
|
||||
*
|
||||
* Helper class for "user_func"
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @link https://www.librenms.org
|
||||
*/
|
||||
|
||||
namespace LibreNMS\Util;
|
||||
|
||||
use LibreNMS\Exceptions\UserFunctionExistException;
|
||||
|
||||
class UserFuncHelper
|
||||
{
|
||||
public function __construct(
|
||||
public string|int|float $value,
|
||||
public string|int|float|null $value_raw = null,
|
||||
public array $sensor = [],
|
||||
) {
|
||||
}
|
||||
|
||||
public function __call(string $name, array $arguments): mixed
|
||||
{
|
||||
throw new UserFunctionExistException("Invalid user function: $name");
|
||||
}
|
||||
|
||||
public function dateToDays(): int
|
||||
{
|
||||
return \LibreNMS\Util\Time::dateToDays($this->value_raw);
|
||||
}
|
||||
}
|
@@ -1099,3 +1099,11 @@ modules:
|
||||
warn_limit: 1
|
||||
high_limit: 3
|
||||
|
||||
-
|
||||
oid: upsAdvBatteryRecommendedReplaceDate
|
||||
num_oid: '.1.3.6.1.4.1.318.1.1.1.2.2.21.{{ $index }}'
|
||||
index: 'upsAdvBatteryRecommendedReplaceDate.{{ $index }}'
|
||||
descr: 'Battery Recommended Days Remaining'
|
||||
user_func: dateToDays
|
||||
low_warn_limit: 30
|
||||
low_limit: 0
|
||||
|
@@ -90,14 +90,13 @@ modules:
|
||||
snmp_flags: '-OQUsb'
|
||||
index: 'c1Temperature.{{ $index }}'
|
||||
descr: Chassis 1
|
||||
user_func: snmp_hexstring, Number::cast
|
||||
high_limit: 90
|
||||
|
||||
- oid: dpwmTemperature
|
||||
num_oid: '.1.3.6.1.4.1.823.34441.1.10.16.{{ $index }}'
|
||||
snmp_flags: '-OQUsb'
|
||||
index: ' dpwmTemperature.{{ $index }}'
|
||||
descr: DPWM Temperature
|
||||
user_func: snmp_hexstring, Number::cast
|
||||
high_limit: 90
|
||||
|
||||
voltage:
|
||||
|
@@ -288,7 +288,7 @@ modules:
|
||||
descr: 'Int Battery'
|
||||
group: 'HSM'
|
||||
index: pkAHsmBatteryInt
|
||||
user_func: Number::cast
|
||||
user_func: \LibreNMS\Util\Number::cast
|
||||
-
|
||||
oid: PRIMEKEY-APPLIANCE-MIB::pkAHsmBatteryExt
|
||||
value: PRIMEKEY-APPLIANCE-MIB::pkAHsmBatteryExt
|
||||
@@ -296,4 +296,4 @@ modules:
|
||||
descr: 'Ext Battery'
|
||||
group: 'HSM'
|
||||
index: pkAHsmBatteryExt
|
||||
user_func: Number::cast
|
||||
user_func: \LibreNMS\Util\Number::cast
|
||||
|
@@ -24,6 +24,7 @@ use LibreNMS\Exceptions\InvalidIpException;
|
||||
use LibreNMS\OS;
|
||||
use LibreNMS\Util\IP;
|
||||
use LibreNMS\Util\IPv6;
|
||||
use LibreNMS\Util\UserFuncHelper;
|
||||
|
||||
function discover_new_device($hostname, $device = [], $method = '', $interface = '')
|
||||
{
|
||||
@@ -943,8 +944,12 @@ function discovery_process(&$valid, $os, $sensor_class, $pre_cache)
|
||||
if (is_numeric($$limit)) {
|
||||
$$limit = ($$limit / $divisor) * $multiplier;
|
||||
}
|
||||
if (is_numeric($$limit) && isset($user_function) && is_callable($user_function)) {
|
||||
if (is_numeric($$limit) && isset($user_function)) {
|
||||
if (is_callable($user_function)) {
|
||||
$$limit = $user_function($$limit);
|
||||
} else {
|
||||
$$limit = (new UserFuncHelper($$limit))->{$user_function}();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -964,8 +969,12 @@ function discovery_process(&$valid, $os, $sensor_class, $pre_cache)
|
||||
$entPhysicalIndex_measured = isset($data['entPhysicalIndex_measured']) ? $data['entPhysicalIndex_measured'] : null;
|
||||
|
||||
//user_func must be applied after divisor/multiplier
|
||||
if (isset($user_function) && is_callable($user_function)) {
|
||||
if (isset($user_function)) {
|
||||
if (is_callable($user_function)) {
|
||||
$value = $user_function($value);
|
||||
} else {
|
||||
$value = (new UserFuncHelper($value, $snmp_data[$data['value']], $data))->{$user_function}();
|
||||
}
|
||||
}
|
||||
|
||||
$uindex = $index;
|
||||
|
@@ -14,6 +14,8 @@ use LibreNMS\Exceptions\JsonAppPollingFailedException;
|
||||
use LibreNMS\Exceptions\JsonAppWrongVersionException;
|
||||
use LibreNMS\RRD\RrdDefinition;
|
||||
use LibreNMS\Util\Debug;
|
||||
use LibreNMS\Util\Number;
|
||||
use LibreNMS\Util\UserFuncHelper;
|
||||
|
||||
function bulk_sensor_snmpget($device, $sensors)
|
||||
{
|
||||
@@ -83,13 +85,6 @@ function poll_sensor($device, $class)
|
||||
require 'includes/polling/sensors/' . $class . '/' . $device['os_group'] . '.inc.php';
|
||||
}
|
||||
|
||||
if (! is_numeric($sensor_value)) {
|
||||
preg_match('/-?\d*\.?\d+/', $sensor_value, $temp_response);
|
||||
if (! empty($temp_response[0])) {
|
||||
$sensor_value = $temp_response[0];
|
||||
}
|
||||
}
|
||||
|
||||
if ($class == 'state') {
|
||||
if (! is_numeric($sensor_value)) {
|
||||
$state_value = dbFetchCell(
|
||||
@@ -164,7 +159,7 @@ function record_sensor_data($device, $all_sensors)
|
||||
foreach ($all_sensors as $sensor) {
|
||||
$class = ucfirst($sensor['sensor_class']);
|
||||
$unit = $supported_sensors[$sensor['sensor_class']];
|
||||
$sensor_value = cast_number($sensor['new_value']);
|
||||
$sensor_value = Number::extract($sensor['new_value']);
|
||||
$prev_sensor_value = $sensor['sensor_current'];
|
||||
|
||||
if ($sensor_value == -32768 || is_nan($sensor_value)) {
|
||||
@@ -180,8 +175,12 @@ function record_sensor_data($device, $all_sensors)
|
||||
$sensor_value = ($sensor_value * $sensor['sensor_multiplier']);
|
||||
}
|
||||
|
||||
if (isset($sensor['user_func']) && is_callable($sensor['user_func'])) {
|
||||
if (isset($sensor['user_func'])) {
|
||||
if (is_callable($sensor['user_func'])) {
|
||||
$sensor_value = $sensor['user_func']($sensor_value);
|
||||
} else {
|
||||
$sensor_value = (new UserFuncHelper($sensor_value, $sensor['new_value'], $sensor))->{$sensor['user_func']}();
|
||||
}
|
||||
}
|
||||
|
||||
$rrd_name = get_sensor_rrd_name($device, $sensor);
|
||||
|
@@ -88,6 +88,8 @@ class OSModulesTest extends DBTestCase
|
||||
*/
|
||||
public function testOS($os, $variant, $modules)
|
||||
{
|
||||
// Lock testing time
|
||||
$this->travelTo(new \DateTime('2022-01-01 00:00:00'));
|
||||
$this->requireSnmpsim(); // require snmpsim for tests
|
||||
// stub out Eventlog::log and Fping->ping, we don't need to store them for these tests
|
||||
$this->stubClasses();
|
||||
@@ -145,6 +147,7 @@ class OSModulesTest extends DBTestCase
|
||||
}
|
||||
|
||||
DeviceCache::flush(); // clear cached devices
|
||||
$this->travelBack();
|
||||
}
|
||||
|
||||
public function dumpedDataProvider()
|
||||
|
@@ -478,6 +478,31 @@
|
||||
"rrd_type": "GAUGE",
|
||||
"state_name": null
|
||||
},
|
||||
{
|
||||
"sensor_deleted": 0,
|
||||
"sensor_class": "count",
|
||||
"poller_type": "snmp",
|
||||
"sensor_oid": ".1.3.6.1.4.1.318.1.1.1.2.2.21.0",
|
||||
"sensor_index": "upsAdvBatteryRecommendedReplaceDate.0",
|
||||
"sensor_type": "apc",
|
||||
"sensor_descr": "Battery Recommended Days Remaining",
|
||||
"group": null,
|
||||
"sensor_divisor": 1,
|
||||
"sensor_multiplier": 1,
|
||||
"sensor_current": 9,
|
||||
"sensor_limit": null,
|
||||
"sensor_limit_warn": null,
|
||||
"sensor_limit_low": 0,
|
||||
"sensor_limit_low_warn": 30,
|
||||
"sensor_alert": 1,
|
||||
"sensor_custom": "No",
|
||||
"entPhysicalIndex": null,
|
||||
"entPhysicalIndex_measured": null,
|
||||
"sensor_prev": null,
|
||||
"user_func": "dateToDays",
|
||||
"rrd_type": "GAUGE",
|
||||
"state_name": null
|
||||
},
|
||||
{
|
||||
"sensor_deleted": 0,
|
||||
"sensor_class": "current",
|
||||
@@ -952,6 +977,31 @@
|
||||
"rrd_type": "GAUGE",
|
||||
"state_name": null
|
||||
},
|
||||
{
|
||||
"sensor_deleted": 0,
|
||||
"sensor_class": "count",
|
||||
"poller_type": "snmp",
|
||||
"sensor_oid": ".1.3.6.1.4.1.318.1.1.1.2.2.21.0",
|
||||
"sensor_index": "upsAdvBatteryRecommendedReplaceDate.0",
|
||||
"sensor_type": "apc",
|
||||
"sensor_descr": "Battery Recommended Days Remaining",
|
||||
"group": null,
|
||||
"sensor_divisor": 1,
|
||||
"sensor_multiplier": 1,
|
||||
"sensor_current": 9,
|
||||
"sensor_limit": null,
|
||||
"sensor_limit_warn": null,
|
||||
"sensor_limit_low": 0,
|
||||
"sensor_limit_low_warn": 30,
|
||||
"sensor_alert": 1,
|
||||
"sensor_custom": "No",
|
||||
"entPhysicalIndex": null,
|
||||
"entPhysicalIndex_measured": null,
|
||||
"sensor_prev": null,
|
||||
"user_func": "dateToDays",
|
||||
"rrd_type": "GAUGE",
|
||||
"state_name": null
|
||||
},
|
||||
{
|
||||
"sensor_deleted": 0,
|
||||
"sensor_class": "current",
|
||||
|
@@ -1334,7 +1334,7 @@
|
||||
"entPhysicalIndex": null,
|
||||
"entPhysicalIndex_measured": null,
|
||||
"sensor_prev": null,
|
||||
"user_func": "snmp_hexstring, Number::cast",
|
||||
"user_func": null,
|
||||
"rrd_type": "GAUGE",
|
||||
"state_name": null
|
||||
},
|
||||
@@ -1359,7 +1359,7 @@
|
||||
"entPhysicalIndex": null,
|
||||
"entPhysicalIndex_measured": null,
|
||||
"sensor_prev": null,
|
||||
"user_func": "snmp_hexstring, Number::cast",
|
||||
"user_func": null,
|
||||
"rrd_type": "GAUGE",
|
||||
"state_name": null
|
||||
},
|
||||
@@ -2438,7 +2438,7 @@
|
||||
"entPhysicalIndex": null,
|
||||
"entPhysicalIndex_measured": null,
|
||||
"sensor_prev": 20,
|
||||
"user_func": "snmp_hexstring, Number::cast",
|
||||
"user_func": null,
|
||||
"rrd_type": "GAUGE",
|
||||
"state_name": null
|
||||
},
|
||||
@@ -2463,7 +2463,7 @@
|
||||
"entPhysicalIndex": null,
|
||||
"entPhysicalIndex_measured": null,
|
||||
"sensor_prev": 34,
|
||||
"user_func": "snmp_hexstring, Number::cast",
|
||||
"user_func": null,
|
||||
"rrd_type": "GAUGE",
|
||||
"state_name": null
|
||||
},
|
||||
|
@@ -694,7 +694,7 @@
|
||||
"entPhysicalIndex": null,
|
||||
"entPhysicalIndex_measured": null,
|
||||
"sensor_prev": null,
|
||||
"user_func": "Number::cast",
|
||||
"user_func": "\\LibreNMS\\Util\\Number::cast",
|
||||
"rrd_type": "GAUGE",
|
||||
"state_name": null
|
||||
}
|
||||
|
@@ -143,6 +143,7 @@
|
||||
1.3.6.1.4.1.318.1.1.1.1.2.3.0|4|FFFFFFFFFFFF
|
||||
1.3.6.1.4.1.318.1.1.1.2.2.3.0|67|438000
|
||||
1.3.6.1.4.1.318.1.1.1.2.2.4.0|2|1
|
||||
1.3.6.1.4.1.318.1.1.1.2.2.21.0|4|01/10/2022
|
||||
1.3.6.1.4.1.318.1.1.1.2.3.1.0|66|1000
|
||||
1.3.6.1.4.1.318.1.1.1.2.3.2.0|66|369
|
||||
1.3.6.1.4.1.318.1.1.1.2.3.4.0|2|272
|
||||
|
Reference in New Issue
Block a user