mirror of
https://github.com/librenms/librenms.git
synced 2024-10-07 16:52:45 +00:00
Fully convert core to a modern module (#13347)
* Core module WIP * update OS::make() * core WIP * try to finish up * switch all core do os Model * Mock WIP * Working tests * cleanup * phpstan fixes * style fixes * fix agent * trim space too and a couple of cleanups * corrected ios test data * missed space * update test data * put escapes back * another net-snmp difference * Fix class description * revert snmp.inc.php change, that can be a different PR * revert snmp.inc.php change, that can be a different PR
This commit is contained in:
@@ -37,7 +37,7 @@ use LibreNMS\Util\Rewrite;
|
||||
use Log;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class SnmpQuery
|
||||
class SnmpQuery implements SnmpQueryInterface
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
@@ -98,7 +98,7 @@ class SnmpQuery
|
||||
/**
|
||||
* Easy way to start a new instance
|
||||
*/
|
||||
public static function make(): SnmpQuery
|
||||
public static function make(): SnmpQueryInterface
|
||||
{
|
||||
return new static;
|
||||
}
|
||||
@@ -107,7 +107,7 @@ class SnmpQuery
|
||||
* Specify a device to make the snmp query against.
|
||||
* By default the query will use the primary device.
|
||||
*/
|
||||
public function device(Device $device): SnmpQuery
|
||||
public function device(Device $device): SnmpQueryInterface
|
||||
{
|
||||
$this->device = $device;
|
||||
|
||||
@@ -118,7 +118,7 @@ class SnmpQuery
|
||||
* Specify a device by a device array.
|
||||
* The device will be fetched from the cache if it is loaded, otherwise, it will fill the array into a new Device
|
||||
*/
|
||||
public function deviceArray(array $device): SnmpQuery
|
||||
public function deviceArray(array $device): SnmpQueryInterface
|
||||
{
|
||||
if (isset($device['device_id']) && DeviceCache::has($device['device_id'])) {
|
||||
$this->device = DeviceCache::get($device['device_id']);
|
||||
@@ -135,7 +135,7 @@ class SnmpQuery
|
||||
* Set a context for the snmp query
|
||||
* This is most commonly used to fetch alternate sets of data, such as different VRFs
|
||||
*/
|
||||
public function context(string $context): SnmpQuery
|
||||
public function context(string $context): SnmpQueryInterface
|
||||
{
|
||||
$this->context = $context;
|
||||
|
||||
@@ -146,7 +146,7 @@ class SnmpQuery
|
||||
* Set an additional MIB directory to search for MIBs.
|
||||
* You do not need to specify the base and os directories, they are already included.
|
||||
*/
|
||||
public function mibDir(string $dir): SnmpQuery
|
||||
public function mibDir(?string $dir): SnmpQueryInterface
|
||||
{
|
||||
$this->mibDir = $dir;
|
||||
|
||||
@@ -156,7 +156,7 @@ class SnmpQuery
|
||||
/**
|
||||
* Output all OIDs numerically
|
||||
*/
|
||||
public function numeric(): SnmpQuery
|
||||
public function numeric(): SnmpQueryInterface
|
||||
{
|
||||
$this->options = array_merge($this->options, ['-On']);
|
||||
|
||||
@@ -172,7 +172,7 @@ class SnmpQuery
|
||||
* @param array|string $options
|
||||
* @return $this
|
||||
*/
|
||||
public function options($options = []): SnmpQuery
|
||||
public function options($options = []): SnmpQueryInterface
|
||||
{
|
||||
$this->options = Arr::wrap($options);
|
||||
|
||||
@@ -218,10 +218,6 @@ class SnmpQuery
|
||||
/**
|
||||
* Translate an OID.
|
||||
* call numeric() on the query to output numeric OID
|
||||
*
|
||||
* @param array|string $oid
|
||||
* @param string $oid
|
||||
* @return \LibreNMS\Data\Source\SnmpResponse
|
||||
*/
|
||||
public function translate(string $oid, ?string $mib = null): SnmpResponse
|
||||
{
|
||||
|
109
LibreNMS/Data/Source/SnmpQueryInterface.php
Normal file
109
LibreNMS/Data/Source/SnmpQueryInterface.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
/*
|
||||
* SnmpQueryInterface.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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @package LibreNMS
|
||||
* @link http://librenms.org
|
||||
* @copyright 2021 Tony Murray
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
*/
|
||||
|
||||
namespace LibreNMS\Data\Source;
|
||||
|
||||
use App\Models\Device;
|
||||
|
||||
interface SnmpQueryInterface
|
||||
{
|
||||
/**
|
||||
* Easy way to start a new instance
|
||||
*/
|
||||
public static function make(): SnmpQueryInterface;
|
||||
|
||||
/**
|
||||
* Specify a device to make the snmp query against.
|
||||
* By default the query will use the primary device.
|
||||
*/
|
||||
public function device(Device $device): SnmpQueryInterface;
|
||||
|
||||
/**
|
||||
* Specify a device by a device array.
|
||||
* The device will be fetched from the cache if it is loaded, otherwise, it will fill the array into a new Device
|
||||
*/
|
||||
public function deviceArray(array $device): SnmpQueryInterface;
|
||||
|
||||
/**
|
||||
* Set a context for the snmp query
|
||||
* This is most commonly used to fetch alternate sets of data, such as different VRFs
|
||||
*/
|
||||
public function context(string $context): SnmpQueryInterface;
|
||||
|
||||
/**
|
||||
* Set an additional MIB directory to search for MIBs.
|
||||
* You do not need to specify the base and os directories, they are already included.
|
||||
*/
|
||||
public function mibDir(?string $dir): SnmpQueryInterface;
|
||||
|
||||
/**
|
||||
* Output all OIDs numerically
|
||||
*/
|
||||
public function numeric(): SnmpQueryInterface;
|
||||
|
||||
/**
|
||||
* Set option(s) for net-snmp command line.
|
||||
* Some options may break parsing, but you can manually parse the raw output if needed.
|
||||
* This will override other options set such as setting numeric. Call with no options to reset to default.
|
||||
* Try to avoid setting options this way to keep the API generic.
|
||||
*
|
||||
* @param array|string $options
|
||||
* @return $this
|
||||
*/
|
||||
public function options($options = []): SnmpQueryInterface;
|
||||
|
||||
/**
|
||||
* snmpget an OID
|
||||
* Commonly used to fetch a single or multiple explicit values.
|
||||
*
|
||||
* @param array|string $oid
|
||||
* @return \LibreNMS\Data\Source\SnmpResponse
|
||||
*/
|
||||
public function get($oid): SnmpResponse;
|
||||
|
||||
/**
|
||||
* snmpwalk an OID
|
||||
* Fetches all OIDs under a given OID, commonly used with tables.
|
||||
*
|
||||
* @param array|string $oid
|
||||
* @return \LibreNMS\Data\Source\SnmpResponse
|
||||
*/
|
||||
public function walk($oid): SnmpResponse;
|
||||
|
||||
/**
|
||||
* snmpnext for the given oid
|
||||
* snmpnext retrieves the first oid after the given oid.
|
||||
*
|
||||
* @param array|string $oid
|
||||
* @return \LibreNMS\Data\Source\SnmpResponse
|
||||
*/
|
||||
public function next($oid): SnmpResponse;
|
||||
|
||||
/**
|
||||
* Translate an OID.
|
||||
* Call numeric method prior output numeric OID.
|
||||
*/
|
||||
public function translate(string $oid, ?string $mib = null): SnmpResponse;
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
/*
|
||||
/**
|
||||
* Core.php
|
||||
*
|
||||
* -Description-
|
||||
@@ -17,37 +17,137 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @package LibreNMS
|
||||
* @link https://www.librenms.org
|
||||
* @copyright 2020 Tony Murray
|
||||
*
|
||||
* @copyright 2021 Tony Murray
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
*/
|
||||
|
||||
namespace LibreNMS\Modules;
|
||||
|
||||
use App\Models\Device;
|
||||
use Illuminate\Support\Str;
|
||||
use LibreNMS\Config;
|
||||
use LibreNMS\Interfaces\Module;
|
||||
use LibreNMS\OS;
|
||||
use LibreNMS\RRD\RrdDefinition;
|
||||
use LibreNMS\Util\Time;
|
||||
use Log;
|
||||
use SnmpQuery;
|
||||
|
||||
class Core
|
||||
class Core implements Module
|
||||
{
|
||||
public function discover(OS $os)
|
||||
{
|
||||
$snmpdata = SnmpQuery::numeric()->get(['SNMPv2-MIB::sysObjectID.0', 'SNMPv2-MIB::sysDescr.0', 'SNMPv2-MIB::sysName.0'])
|
||||
->values();
|
||||
|
||||
$device = $os->getDevice();
|
||||
$device->fill([
|
||||
'sysObjectID' => $snmpdata['.1.3.6.1.2.1.1.2.0'] ?? null,
|
||||
'sysName' => strtolower(trim($snmpdata['.1.3.6.1.2.1.1.5.0'] ?? '')),
|
||||
'sysDescr' => isset($snmpdata['.1.3.6.1.2.1.1.1.0']) ? str_replace(chr(218), "\n", $snmpdata['.1.3.6.1.2.1.1.1.0']) : null,
|
||||
]);
|
||||
|
||||
foreach ($device->getDirty() as $attribute => $value) {
|
||||
Log::event($value . ' -> ' . $device->$attribute, $device, 'system', 3);
|
||||
$os->getDeviceArray()[$attribute] = $value; // update device array
|
||||
}
|
||||
|
||||
// detect OS
|
||||
$device->os = self::detectOS($device, false);
|
||||
|
||||
if ($device->isDirty('os')) {
|
||||
Log::event('Device OS changed: ' . $device->getOriginal('os') . ' -> ' . $device->os, $device, 'system', 3);
|
||||
$os->getDeviceArray()['os'] = $device->os;
|
||||
|
||||
echo 'Changed ';
|
||||
}
|
||||
|
||||
// Set type to a predefined type for the OS if it's not already set
|
||||
$loaded_os_type = Config::get("os.$device->os.type");
|
||||
if (! $device->getAttrib('override_device_type') && $loaded_os_type != $device->type) {
|
||||
$device->type = $loaded_os_type;
|
||||
Log::debug("Device type changed to $loaded_os_type!");
|
||||
}
|
||||
|
||||
$device->save();
|
||||
|
||||
echo 'OS: ' . Config::getOsSetting($device->os, 'text') . " ($device->os)\n\n";
|
||||
}
|
||||
|
||||
public function poll(OS $os)
|
||||
{
|
||||
global $agent_data;
|
||||
|
||||
$snmpdata = SnmpQuery::numeric()
|
||||
->get(['SNMPv2-MIB::sysDescr.0', 'SNMPv2-MIB::sysObjectID.0', 'SNMPv2-MIB::sysUpTime.0', 'SNMPv2-MIB::sysName.0'])
|
||||
->values();
|
||||
|
||||
$device = $os->getDevice();
|
||||
$device->fill([
|
||||
'uptime' => $snmpdata['.1.3.6.1.2.1.1.3.0'],
|
||||
'sysName' => str_replace("\n", '', strtolower($snmpdata['.1.3.6.1.2.1.1.5.0'])),
|
||||
'sysObjectID' => $snmpdata['.1.3.6.1.2.1.1.2.0'],
|
||||
'sysDescr' => str_replace(chr(218), "\n", $snmpdata['.1.3.6.1.2.1.1.1.0']),
|
||||
]);
|
||||
|
||||
if (! empty($agent_data['uptime'])) {
|
||||
[$uptime] = explode(' ', $agent_data['uptime']);
|
||||
$uptime = round((float) $uptime);
|
||||
echo "Using UNIX Agent Uptime ($uptime)\n";
|
||||
} else {
|
||||
$uptime_data = SnmpQuery::make()->get(['SNMP-FRAMEWORK-MIB::snmpEngineTime.0', 'HOST-RESOURCES-MIB::hrSystemUptime.0'])->values();
|
||||
|
||||
$uptime = max(
|
||||
round($device->uptime / 100),
|
||||
Config::get("os.$device->os.bad_snmpEngineTime") ? 0 : $uptime_data['SNMP-FRAMEWORK-MIB::snmpEngineTime.0'],
|
||||
Config::get("os.$device->os.bad_hrSystemUptime") ? 0 : round($uptime_data['HOST-RESOURCES-MIB::hrSystemUptime.0'] / 100)
|
||||
);
|
||||
Log::debug("Uptime seconds: $uptime\n");
|
||||
}
|
||||
|
||||
if ($uptime != 0 && Config::get("os.$device->os.bad_uptime") !== true) {
|
||||
if ($uptime < $device->uptime) {
|
||||
Log::event('Device rebooted after ' . Time::formatInterval($device->uptime) . " -> {$uptime}s", $device, 'reboot', 4, $device->uptime);
|
||||
}
|
||||
|
||||
app('Datastore')->put($os->getDeviceArray(), 'uptime', [
|
||||
'rrd_def' => RrdDefinition::make()->addDataset('uptime', 'GAUGE', 0),
|
||||
], $uptime);
|
||||
|
||||
$os->enableGraph('uptime');
|
||||
|
||||
echo 'Uptime: ' . Time::formatInterval($uptime) . PHP_EOL;
|
||||
$device->uptime = $uptime;
|
||||
|
||||
$device->save();
|
||||
}
|
||||
}
|
||||
|
||||
public function cleanup(OS $os)
|
||||
{
|
||||
// nothing to cleanup
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the os of the given device.
|
||||
*
|
||||
* @param array $device device to check
|
||||
* @param Device $device device to check
|
||||
* @param bool $fetch fetch sysDescr and sysObjectID fresh from the device
|
||||
* @return string the name of the os
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function detectOS($device, $fetch = true)
|
||||
public static function detectOS(Device $device, bool $fetch = true): string
|
||||
{
|
||||
if ($fetch) {
|
||||
// some devices act odd when getting both oids at once
|
||||
$device['sysDescr'] = snmp_get($device, 'SNMPv2-MIB::sysDescr.0', '-Ovq');
|
||||
$device['sysObjectID'] = snmp_get($device, 'SNMPv2-MIB::sysObjectID.0', '-Ovqn');
|
||||
// some devices act oddly when getting both OIDs at once
|
||||
$device->sysDescr = SnmpQuery::device($device)->get('SNMPv2-MIB::sysDescr.0')->value();
|
||||
$device->sysObjectID = SnmpQuery::device($device)->numeric()->get('SNMPv2-MIB::sysObjectID.0')->value();
|
||||
}
|
||||
|
||||
d_echo("| {$device['sysDescr']} | {$device['sysObjectID']} | \n");
|
||||
Log::debug("| $device->sysDescr | $device->sysObjectID | \n");
|
||||
|
||||
$deferred_os = [];
|
||||
$generic_os = [
|
||||
@@ -96,12 +196,12 @@ class Core
|
||||
*
|
||||
* Appending _except to any condition will invert the match.
|
||||
*
|
||||
* @param array $device
|
||||
* @param Device $device
|
||||
* @param array $array Array of items, keys should be sysObjectID, sysDescr, or sysDescr_regex
|
||||
* @param string|array $mibdir MIB directory for evaluated OS
|
||||
* @return bool the result (all items passed return true)
|
||||
*/
|
||||
protected static function checkDiscovery($device, $array, $mibdir)
|
||||
protected static function checkDiscovery(Device $device, array $array, $mibdir): bool
|
||||
{
|
||||
// all items must be true
|
||||
foreach ($array as $key => $value) {
|
||||
@@ -126,24 +226,20 @@ class Core
|
||||
return false;
|
||||
}
|
||||
} elseif ($key == 'snmpget') {
|
||||
$get_value = snmp_get(
|
||||
$device,
|
||||
$value['oid'],
|
||||
$value['options'] ?? '-Oqv',
|
||||
$value['mib'] ?? null,
|
||||
$value['mib_dir'] ?? $mibdir
|
||||
);
|
||||
$get_value = SnmpQuery::device($device)
|
||||
->options($value['options'] ?? [])
|
||||
->mibDir($value['mib_dir'] ?? $mibdir)
|
||||
->get(isset($value['mib']) ? "{$value['mib']}::{$value['oid']}" : $value['oid'])
|
||||
->value();
|
||||
if (compare_var($get_value, $value['value'], $value['op'] ?? 'contains') == $check) {
|
||||
return false;
|
||||
}
|
||||
} elseif ($key == 'snmpwalk') {
|
||||
$walk_value = snmp_walk(
|
||||
$device,
|
||||
$value['oid'],
|
||||
$value['options'] ?? '-Oqv',
|
||||
$value['mib'] ?? null,
|
||||
$value['mib_dir'] ?? $mibdir
|
||||
);
|
||||
$walk_value = SnmpQuery::device($device)
|
||||
->options($value['options'] ?? [])
|
||||
->mibDir($value['mib_dir'] ?? $mibdir)
|
||||
->walk(isset($value['mib']) ? "{$value['mib']}::{$value['oid']}" : $value['oid'])
|
||||
->raw();
|
||||
if (compare_var($walk_value, $value['value'], $value['op'] ?? 'contains') == $check) {
|
||||
return false;
|
||||
}
|
||||
@@ -153,7 +249,7 @@ class Core
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static function discoveryIsSlow($def)
|
||||
protected static function discoveryIsSlow($def): bool
|
||||
{
|
||||
foreach ($def['discovery'] as $item) {
|
||||
if (array_key_exists('snmpget', $item) || array_key_exists('snmpwalk', $item)) {
|
||||
|
@@ -2,7 +2,7 @@
|
||||
/**
|
||||
* OS.php
|
||||
*
|
||||
* Base OS class
|
||||
* -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
|
||||
@@ -19,7 +19,7 @@
|
||||
*
|
||||
* @link https://www.librenms.org
|
||||
*
|
||||
* @copyright 2017 Tony Murray
|
||||
* @copyright 2021 Tony Murray
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
*/
|
||||
|
||||
@@ -39,6 +39,7 @@ use LibreNMS\OS\Traits\HostResources;
|
||||
use LibreNMS\OS\Traits\UcdResources;
|
||||
use LibreNMS\OS\Traits\YamlMempoolsDiscovery;
|
||||
use LibreNMS\OS\Traits\YamlOSDiscovery;
|
||||
use LibreNMS\Util\StringHelpers;
|
||||
|
||||
class OS implements ProcessorDiscovery, OSDiscovery, MempoolsDiscovery
|
||||
{
|
||||
@@ -212,7 +213,7 @@ class OS implements ProcessorDiscovery, OSDiscovery, MempoolsDiscovery
|
||||
unset($device['os_group']);
|
||||
}
|
||||
|
||||
$class = str_to_class($device['os'], 'LibreNMS\\OS\\');
|
||||
$class = StringHelpers::toClass($device['os'], 'LibreNMS\\OS\\');
|
||||
d_echo('Attempting to initialize OS: ' . $device['os'] . PHP_EOL);
|
||||
if (class_exists($class)) {
|
||||
d_echo("OS initialized: $class\n");
|
||||
@@ -221,9 +222,9 @@ class OS implements ProcessorDiscovery, OSDiscovery, MempoolsDiscovery
|
||||
}
|
||||
|
||||
// If not a specific OS, check for a group one.
|
||||
if ($os_group) {
|
||||
$class = str_to_class($device['os_group'], 'LibreNMS\\OS\\Shared\\');
|
||||
d_echo('Attempting to initialize OS: ' . $device['os_group'] . PHP_EOL);
|
||||
if ($os_group = Config::get("os.{$device['os']}.group")) {
|
||||
$class = StringHelpers::toClass($os_group, 'LibreNMS\\OS\\Shared\\');
|
||||
d_echo("Attempting to initialize Group OS: $os_group\n");
|
||||
if (class_exists($class)) {
|
||||
d_echo("OS initialized: $class\n");
|
||||
|
||||
|
@@ -125,4 +125,25 @@ class StringHelpers
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a class name from a lowercase string containing - or _
|
||||
* Remove - and _ and camel case words
|
||||
*
|
||||
* @param string $name The string to convert to a class name
|
||||
* @param string|null $namespace namespace to prepend to the name for example: LibreNMS\
|
||||
* @return string Class name
|
||||
*/
|
||||
public static function toClass(string $name, ?string $namespace = null): string
|
||||
{
|
||||
$pre_format = str_replace(['-', '_'], ' ', $name);
|
||||
$class = str_replace(' ', '', ucwords(strtolower($pre_format)));
|
||||
$class = preg_replace_callback('/^(\d)(.)/', function ($matches) {
|
||||
$numbers = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine'];
|
||||
|
||||
return $numbers[$matches[1]] . strtoupper($matches[2]);
|
||||
}, $class);
|
||||
|
||||
return $namespace . $class;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user