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;
|
||||
}
|
||||
}
|
||||
|
@@ -37,34 +37,36 @@ class Device extends BaseModel
|
||||
public $timestamps = false;
|
||||
protected $primaryKey = 'device_id';
|
||||
protected $fillable = [
|
||||
'hostname',
|
||||
'ip',
|
||||
'status',
|
||||
'status_reason',
|
||||
'sysName',
|
||||
'sysDescr',
|
||||
'sysObjectID',
|
||||
'hardware',
|
||||
'version',
|
||||
'features',
|
||||
'serial',
|
||||
'icon',
|
||||
'overwrite_ip',
|
||||
'os',
|
||||
'community',
|
||||
'port',
|
||||
'transport',
|
||||
'snmpver',
|
||||
'poller_group',
|
||||
'port_association_mode',
|
||||
'snmp_disable',
|
||||
// ---- snmpV3 fields ----
|
||||
'authalgo',
|
||||
'authlevel',
|
||||
'authname',
|
||||
'authpass',
|
||||
'authalgo',
|
||||
'cryptopass',
|
||||
'community',
|
||||
'cryptoalgo',
|
||||
'cryptopass',
|
||||
'features',
|
||||
'hardware',
|
||||
'hostname',
|
||||
'icon',
|
||||
'ip',
|
||||
'os',
|
||||
'overwrite_ip',
|
||||
'poller_group',
|
||||
'port',
|
||||
'port_association_mode',
|
||||
'retries',
|
||||
'serial',
|
||||
'snmp_disable',
|
||||
'snmp_max_repeaters',
|
||||
'snmpver',
|
||||
'status',
|
||||
'status_reason',
|
||||
'sysDescr',
|
||||
'sysName',
|
||||
'sysObjectID',
|
||||
'timeout',
|
||||
'transport',
|
||||
'version',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
|
@@ -1075,15 +1075,7 @@ function get_vm_parent_id($device)
|
||||
*/
|
||||
function str_to_class($name, $namespace = null)
|
||||
{
|
||||
$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;
|
||||
return \LibreNMS\Util\StringHelpers::toClass($name, $namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -28,7 +28,6 @@ discovery:
|
||||
'FreeBSD'
|
||||
snmpget:
|
||||
oid: 1.3.6.1.4.1.674.11000.2000.500.1.2.1.0
|
||||
mib: DELL-STORAGE-SC-MIB
|
||||
op: starts
|
||||
value: 'Dell-Compellent'
|
||||
-
|
||||
|
@@ -1,44 +1,10 @@
|
||||
<?php
|
||||
|
||||
use LibreNMS\Config;
|
||||
use LibreNMS\Modules\Core;
|
||||
use LibreNMS\OS;
|
||||
use LibreNMS\OS\Generic;
|
||||
|
||||
$snmpdata = snmp_get_multi_oid($device, ['sysName.0', 'sysObjectID.0', 'sysDescr.0'], '-OUQn', 'SNMPv2-MIB');
|
||||
|
||||
$deviceModel = DeviceCache::getPrimary();
|
||||
$deviceModel->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 ($deviceModel->getDirty() as $attribute => $value) {
|
||||
Log::event($value . ' -> ' . $deviceModel->$attribute, $deviceModel, 'system', 3);
|
||||
$device[$attribute] = $value; // update device array
|
||||
}
|
||||
|
||||
// detect OS
|
||||
$deviceModel->os = Core::detectOS($device, false);
|
||||
|
||||
if ($deviceModel->isDirty('os')) {
|
||||
Log::event('Device OS changed: ' . $deviceModel->getOriginal('os') . ' -> ' . $deviceModel->os, $deviceModel, 'system', 3);
|
||||
$device['os'] = $deviceModel->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 (! $deviceModel->getAttrib('override_device_type') && $loaded_os_type != $deviceModel->type) {
|
||||
$deviceModel->type = $loaded_os_type;
|
||||
Log::debug("Device type changed to $loaded_os_type!");
|
||||
}
|
||||
|
||||
$deviceModel->save();
|
||||
// start assuming no os
|
||||
(new \LibreNMS\Modules\Core())->discover(Generic::make($device));
|
||||
|
||||
// then create with actual OS
|
||||
$os = OS::make($device);
|
||||
|
||||
echo 'OS: ' . Config::getOsSetting($device['os'], 'text') . " ({$device['os']})\n\n";
|
||||
|
||||
unset($snmpdata, $attribute, $value, $deviceModel);
|
||||
|
@@ -572,7 +572,7 @@ function createHost(
|
||||
$port_assoc_mode = get_port_assoc_mode_id($port_assoc_mode);
|
||||
}
|
||||
|
||||
$device = [
|
||||
$device = new Device(array_merge([
|
||||
'hostname' => $host,
|
||||
'overwrite_ip' => $overwrite_ip,
|
||||
'sysName' => $additional['sysName'] ?? $host,
|
||||
@@ -587,22 +587,18 @@ function createHost(
|
||||
'status_reason' => '',
|
||||
'port_association_mode' => $port_assoc_mode,
|
||||
'snmp_disable' => $additional['snmp_disable'] ?? 0,
|
||||
];
|
||||
|
||||
$device = array_merge($device, $v3); // merge v3 settings
|
||||
], $v3));
|
||||
|
||||
if ($force_add !== true) {
|
||||
$device['os'] = Core::detectOS($device);
|
||||
$device->os = Core::detectOS($device);
|
||||
|
||||
$snmphost = snmp_get($device, 'sysName.0', '-Oqv', 'SNMPv2-MIB');
|
||||
if (host_exists($host, $snmphost)) {
|
||||
throw new HostExistsException("Already have host $host ($snmphost) due to duplicate sysName");
|
||||
$device->sysName = SnmpQuery::device($device)->get('SNMPv2-MIB::sysName.0')->value();
|
||||
if (host_exists($host, $device->sysName)) {
|
||||
throw new HostExistsException("Already have host $host ({$device->sysName}) due to duplicate sysName");
|
||||
}
|
||||
}
|
||||
|
||||
$deviceModel = Device::create($device);
|
||||
if ($deviceModel->device_id) {
|
||||
return $deviceModel->device_id;
|
||||
if ($device->save()) {
|
||||
return $device->device_id;
|
||||
}
|
||||
|
||||
throw new \Exception('Failed to add host to the database, please run ./validate.php');
|
||||
|
@@ -60,11 +60,7 @@ require_once $install_dir . '/includes/dbFacile.php';
|
||||
require_once $install_dir . '/includes/datastore.inc.php';
|
||||
require_once $install_dir . '/includes/billing.php';
|
||||
require_once $install_dir . '/includes/syslog.php';
|
||||
if (module_selected('mocksnmp', $init_modules)) {
|
||||
require_once $install_dir . '/tests/mocks/mock.snmp.inc.php';
|
||||
} elseif (! in_array($install_dir . '/tests/mocks/mock.snmp.inc.php', get_included_files())) {
|
||||
require_once $install_dir . '/includes/snmp.inc.php';
|
||||
}
|
||||
require_once $install_dir . '/includes/snmp.inc.php';
|
||||
require_once $install_dir . '/includes/services.inc.php';
|
||||
require_once $install_dir . '/includes/functions.php';
|
||||
require_once $install_dir . '/includes/rewrites.php';
|
||||
|
@@ -11,57 +11,4 @@
|
||||
* See COPYING for more details.
|
||||
*/
|
||||
|
||||
use LibreNMS\Config;
|
||||
use LibreNMS\RRD\RrdDefinition;
|
||||
use LibreNMS\Util\Time;
|
||||
|
||||
$snmpdata = snmp_get_multi_oid($device, ['sysUpTime.0', 'sysName.0', 'sysObjectID.0', 'sysDescr.0'], '-OQnUt', 'SNMPv2-MIB');
|
||||
|
||||
$poll_device['sysUptime'] = $snmpdata['.1.3.6.1.2.1.1.3.0'];
|
||||
$poll_device['sysName'] = str_replace("\n", '', strtolower($snmpdata['.1.3.6.1.2.1.1.5.0']));
|
||||
$poll_device['sysObjectID'] = $snmpdata['.1.3.6.1.2.1.1.2.0'];
|
||||
$poll_device['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($uptime);
|
||||
echo "Using UNIX Agent Uptime ($uptime)\n";
|
||||
} else {
|
||||
$uptime_data = snmp_get_multi($device, ['snmpEngineTime.0', 'hrSystemUptime.0'], '-OQnUst', 'HOST-RESOURCES-MIB:SNMP-FRAMEWORK-MIB');
|
||||
|
||||
$uptime = max(
|
||||
round($poll_device['sysUptime'] / 100),
|
||||
Config::get("os.{$device['os']}.bad_snmpEngineTime") ? 0 : $uptime_data[0]['snmpEngineTime'],
|
||||
Config::get("os.{$device['os']}.bad_hrSystemUptime") ? 0 : round($uptime_data[0]['hrSystemUptime'] / 100)
|
||||
);
|
||||
d_echo("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']);
|
||||
}
|
||||
|
||||
$tags = [
|
||||
'rrd_def' => RrdDefinition::make()->addDataset('uptime', 'GAUGE', 0),
|
||||
];
|
||||
data_update($device, 'uptime', $tags, $uptime);
|
||||
|
||||
$os->enableGraph('uptime');
|
||||
|
||||
echo 'Uptime: ' . Time::formatInterval($uptime) . PHP_EOL;
|
||||
|
||||
$update_array['uptime'] = $uptime;
|
||||
$device['uptime'] = $uptime;
|
||||
}//end if
|
||||
|
||||
// Save results of various polled values to the database
|
||||
foreach (['sysObjectID', 'sysName', 'sysDescr'] as $elem) {
|
||||
if ($poll_device[$elem] != $device[$elem]) {
|
||||
$update_array[$elem] = $poll_device[$elem];
|
||||
$device[$elem] = $poll_device[$elem];
|
||||
log_event("$elem -> " . $poll_device[$elem], $device, 'system', 3);
|
||||
}
|
||||
}
|
||||
|
||||
unset($snmpdata, $uptime_data, $uptime, $tags, $poll_device);
|
||||
(new \LibreNMS\Modules\Core())->poll($os);
|
||||
|
@@ -63,6 +63,8 @@ if ($device['os_group'] == 'unix' || $device['os'] == 'windows') {
|
||||
'gpsd',
|
||||
];
|
||||
|
||||
global $agent_data;
|
||||
$agent_data = [];
|
||||
foreach (explode('<<<', $agent_raw) as $section) {
|
||||
[$section, $data] = explode('>>>', $section);
|
||||
[$sa, $sb] = explode('-', $section, 2);
|
||||
|
@@ -35,7 +35,7 @@ sysObjectID: $full_sysObjectID
|
||||
|
||||
");
|
||||
|
||||
$os = Core::detectOS($device);
|
||||
$os = Core::detectOS(new \App\Models\Device($device));
|
||||
$continue = 'n';
|
||||
if ($os != 'generic') {
|
||||
$continue = get_user_input("We already detect this device as OS $os type, do you want to continue to add sensors? (Y/n)");
|
||||
|
307
tests/Mocks/SnmpQueryMock.php
Normal file
307
tests/Mocks/SnmpQueryMock.php
Normal file
@@ -0,0 +1,307 @@
|
||||
<?php
|
||||
/*
|
||||
* SnmpQueryMock.php
|
||||
*
|
||||
* Load data from snmprec files
|
||||
*
|
||||
* 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\Tests\Mocks;
|
||||
|
||||
use App\Models\Device;
|
||||
use DeviceCache;
|
||||
use Exception;
|
||||
use Illuminate\Support\Str;
|
||||
use LibreNMS\Data\Source\SnmpQuery;
|
||||
use LibreNMS\Data\Source\SnmpQueryInterface;
|
||||
use LibreNMS\Data\Source\SnmpResponse;
|
||||
use LibreNMS\Device\YamlDiscovery;
|
||||
use Log;
|
||||
|
||||
class SnmpQueryMock implements SnmpQueryInterface
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $cache;
|
||||
|
||||
/**
|
||||
* @var Device
|
||||
*/
|
||||
private $device;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $context;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $mibDir;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $numeric = false;
|
||||
/**
|
||||
* @var array|mixed
|
||||
*/
|
||||
private $options = [];
|
||||
|
||||
public static function make(): SnmpQueryInterface
|
||||
{
|
||||
$new = new static;
|
||||
$new->device = DeviceCache::getPrimary();
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
public function device(Device $device): SnmpQueryInterface
|
||||
{
|
||||
$this->device = $device;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function deviceArray(array $device): SnmpQueryInterface
|
||||
{
|
||||
$this->device = new Device($device);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function context(string $context): SnmpQueryInterface
|
||||
{
|
||||
$this->context = $context;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function translate(string $oid, ?string $mib = null): SnmpResponse
|
||||
{
|
||||
// call real snmptranslate
|
||||
$options = $this->options;
|
||||
if ($this->numeric) {
|
||||
$options[] = '-On';
|
||||
}
|
||||
|
||||
return SnmpQuery::make()
|
||||
->mibDir($this->mibDir)
|
||||
->options($options)
|
||||
->translate($oid, $mib);
|
||||
}
|
||||
|
||||
public function numeric(): SnmpQueryInterface
|
||||
{
|
||||
$this->numeric = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function options($options = []): SnmpQueryInterface
|
||||
{
|
||||
$this->options = $options;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function mibDir(?string $dir): SnmpQueryInterface
|
||||
{
|
||||
$this->mibDir = $dir;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function get($oid): SnmpResponse
|
||||
{
|
||||
$community = $this->device->community;
|
||||
$num_oid = $this->translateNumber($oid);
|
||||
$data = $this->getSnmprec($community)[$num_oid] ?? [0, ''];
|
||||
|
||||
Log::debug("[SNMP] snmpget $community $num_oid: ");
|
||||
|
||||
return new SnmpResponse($this->outputLine($oid, $num_oid, $data[0], $data[1]));
|
||||
}
|
||||
|
||||
public function walk($oid): SnmpResponse
|
||||
{
|
||||
$community = $this->device->community;
|
||||
$num_oid = $this->translateNumber($oid);
|
||||
$dev = $this->getSnmprec($community);
|
||||
|
||||
$output = '';
|
||||
foreach ($dev as $key => $data) {
|
||||
if (Str::startsWith($key, $num_oid)) {
|
||||
$output .= $this->outputLine($oid, $num_oid, $data[0], $data[1]);
|
||||
}
|
||||
}
|
||||
|
||||
Log::debug("[SNMP] snmpwalk $community $num_oid");
|
||||
|
||||
return new SnmpResponse($output);
|
||||
}
|
||||
|
||||
public function next($oid): SnmpResponse
|
||||
{
|
||||
$community = $this->device->community;
|
||||
$num_oid = $this->translateNumber($oid);
|
||||
$dev = $this->getSnmprec($community);
|
||||
|
||||
Log::debug("[SNMP] snmpnext $community $num_oid: ");
|
||||
while (Str::contains($num_oid, '.')) {
|
||||
foreach ($dev as $key => $data) {
|
||||
if (Str::startsWith($key, $num_oid)) {
|
||||
return new SnmpResponse($this->outputLine($oid, $num_oid, $data[0], $data[1]));
|
||||
}
|
||||
}
|
||||
|
||||
$num_oid = substr($num_oid, 0, strrpos($num_oid, '.'));
|
||||
}
|
||||
|
||||
return new SnmpResponse('');
|
||||
}
|
||||
|
||||
private function cacheSnmprec(string $file): void
|
||||
{
|
||||
if (isset(self::$cache[$file])) {
|
||||
return;
|
||||
}
|
||||
self::$cache[$file] = [];
|
||||
|
||||
$data = file_get_contents(base_path("/tests/snmpsim/$file.snmprec"));
|
||||
$line = strtok($data, "\r\n");
|
||||
while ($line !== false) {
|
||||
[$oid, $type, $data] = explode('|', $line, 3);
|
||||
if ($type == '4') {
|
||||
$data = trim($data);
|
||||
} elseif ($type == '6') {
|
||||
$data = trim($data, '.');
|
||||
} elseif ($type == '4x') {
|
||||
// MacAddress type is stored as hex string, but we don't understand mibs
|
||||
if (Str::startsWith($oid, [
|
||||
'1.3.6.1.2.1.2.2.1.6', // IF-MIB::ifPhysAddress
|
||||
'1.3.6.1.2.1.17.1.1.0', // BRIDGE-MIB::dot1dBaseBridgeAddress.0
|
||||
'1.3.6.1.4.1.890.1.5.13.13.8.1.1.20', // IES5206-MIB::slotModuleMacAddress
|
||||
])) {
|
||||
$data = \LibreNMS\Util\Rewrite::readableMac($data);
|
||||
} else {
|
||||
$data = hex2str($data);
|
||||
}
|
||||
}
|
||||
|
||||
self::$cache[$file][$oid] = [$type, $data];
|
||||
$line = strtok("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all data of the specified $community from the snmprec cache
|
||||
*
|
||||
* @param string $community snmp community to return
|
||||
* @return array array of the data containing: [$oid][$type, $data]
|
||||
*
|
||||
* @throws Exception this $community is not cached
|
||||
*/
|
||||
private function getSnmprec(string $community): array
|
||||
{
|
||||
if (! isset(self::$cache[$community])) {
|
||||
$this->cacheSnmprec($community);
|
||||
}
|
||||
|
||||
if (isset(self::$cache[$community])) {
|
||||
return self::$cache[$community];
|
||||
}
|
||||
|
||||
throw new Exception("SNMPREC: community $community not cached");
|
||||
}
|
||||
|
||||
private function outputLine(string $oid, string $num_oid, string $type, string $data): string
|
||||
{
|
||||
if ($type == 6) {
|
||||
$data = $this->numeric ? ".$data" : $this->translate($data, $this->extractMib($oid))->value();
|
||||
}
|
||||
|
||||
if ($this->numeric) {
|
||||
return "$num_oid = $data";
|
||||
}
|
||||
|
||||
if (! empty($oid) && YamlDiscovery::oidIsNumeric($oid)) {
|
||||
$oid = $this->translate($oid)->value();
|
||||
}
|
||||
|
||||
return "$oid = $data";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the numeric oid of an oid
|
||||
* The leading dot is ommited by default to be compatible with snmpsim
|
||||
*
|
||||
* @param string $oid the oid to tranlslate
|
||||
* @param string $mib mib to use
|
||||
* @return string the oid in numeric format (1.3.4.5)
|
||||
*
|
||||
* @throws Exception Could not translate the oid
|
||||
*/
|
||||
private function translateNumber($oid, $mib = null)
|
||||
{
|
||||
// optimizations (35s -> 1.6s on my laptop)
|
||||
switch ($oid) {
|
||||
case 'SNMPv2-MIB::sysDescr.0':
|
||||
return '1.3.6.1.2.1.1.1.0';
|
||||
case 'SNMPv2-MIB::sysObjectID.0':
|
||||
return '1.3.6.1.2.1.1.2.0';
|
||||
case 'ENTITY-MIB::entPhysicalDescr.1':
|
||||
return '1.3.6.1.2.1.47.1.1.1.1.2.1';
|
||||
case 'SML-MIB::product-Name.0':
|
||||
return '1.3.6.1.4.1.2.6.182.3.3.1.0';
|
||||
case 'ENTITY-MIB::entPhysicalMfgName.1':
|
||||
return '1.3.6.1.2.1.47.1.1.1.1.12.1';
|
||||
case 'GAMATRONIC-MIB::psUnitManufacture.0':
|
||||
return '1.3.6.1.4.1.6050.1.1.2.0';
|
||||
case 'SYNOLOGY-SYSTEM-MIB::systemStatus.0':
|
||||
return '1.3.6.1.4.1.6574.1.1.0';
|
||||
}
|
||||
|
||||
if (YamlDiscovery::oidIsNumeric($oid)) {
|
||||
return ltrim($oid, '.');
|
||||
}
|
||||
|
||||
$options = ['-IR'];
|
||||
if ($mib) {
|
||||
$options[] = "-m $mib";
|
||||
}
|
||||
|
||||
$number = SnmpQuery::make()->mibDir($this->mibDir)
|
||||
->options(array_merge($options, $this->options))->numeric()->translate($oid)->value();
|
||||
|
||||
if (empty($number)) {
|
||||
throw new Exception('Could not translate oid: ' . $oid . PHP_EOL);
|
||||
}
|
||||
|
||||
return ltrim($number, '.');
|
||||
}
|
||||
|
||||
private function extractMib(string $oid): ?string
|
||||
{
|
||||
if (Str::contains($oid, '::')) {
|
||||
return explode('::', $oid, 2)[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -25,9 +25,12 @@
|
||||
|
||||
namespace LibreNMS\Tests;
|
||||
|
||||
use App\Models\Device;
|
||||
use Illuminate\Support\Str;
|
||||
use LibreNMS\Config;
|
||||
use LibreNMS\Data\Source\SnmpQuery;
|
||||
use LibreNMS\Modules\Core;
|
||||
use LibreNMS\Tests\Mocks\SnmpQueryMock;
|
||||
use LibreNMS\Util\Debug;
|
||||
use LibreNMS\Util\OS;
|
||||
|
||||
@@ -64,6 +67,10 @@ class OSDiscoveryTest extends TestCase
|
||||
*/
|
||||
public function testOSDetection($os_name)
|
||||
{
|
||||
if (! getenv('SNMPSIM')) {
|
||||
$this->app->bind(SnmpQuery::class, SnmpQueryMock::class);
|
||||
}
|
||||
|
||||
$glob = Config::get('install_dir') . "/tests/snmpsim/$os_name*.snmprec";
|
||||
$files = array_map(function ($file) {
|
||||
return basename($file, '.snmprec');
|
||||
@@ -104,6 +111,8 @@ class OSDiscoveryTest extends TestCase
|
||||
*/
|
||||
private function checkOS($expected_os, $filename = null)
|
||||
{
|
||||
$start = microtime(true);
|
||||
|
||||
$community = $filename ?: $expected_os;
|
||||
Debug::set();
|
||||
Debug::setVerbose();
|
||||
@@ -112,6 +121,7 @@ class OSDiscoveryTest extends TestCase
|
||||
$output = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
$this->assertLessThan(5, microtime(true) - $start, "OS $expected_os took longer than 5s to detect");
|
||||
$this->assertEquals($expected_os, $os, "Test file: $community.snmprec\n$output");
|
||||
}
|
||||
|
||||
@@ -119,12 +129,11 @@ class OSDiscoveryTest extends TestCase
|
||||
* Generate a fake $device array
|
||||
*
|
||||
* @param string $community The snmp community to set
|
||||
* @return array resulting device array
|
||||
* @return Device resulting device array
|
||||
*/
|
||||
private function genDevice($community)
|
||||
private function genDevice($community): Device
|
||||
{
|
||||
return [
|
||||
'device_id' => 1,
|
||||
return new Device([
|
||||
'hostname' => $this->getSnmpsim()->getIP(),
|
||||
'snmpver' => 'v2c',
|
||||
'port' => $this->getSnmpsim()->getPort(),
|
||||
@@ -133,9 +142,7 @@ class OSDiscoveryTest extends TestCase
|
||||
'snmp_max_repeaters' => 10,
|
||||
'community' => $community,
|
||||
'os' => 'generic',
|
||||
'os_group' => '',
|
||||
'attribs' => [],
|
||||
];
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -30,10 +30,6 @@ $install_dir = realpath(__DIR__ . '/..');
|
||||
|
||||
$init_modules = ['web', 'discovery', 'polling', 'nodb'];
|
||||
|
||||
if (! getenv('SNMPSIM')) {
|
||||
$init_modules[] = 'mocksnmp';
|
||||
}
|
||||
|
||||
require $install_dir . '/includes/init.php';
|
||||
chdir($install_dir);
|
||||
|
||||
|
@@ -13235,7 +13235,7 @@
|
||||
{
|
||||
"sysName": "<private>",
|
||||
"sysObjectID": ".1.3.6.1.4.1.9.1.359",
|
||||
"sysDescr": "Cisco Internetwork Operating System Software\nIOS (tm) C2950 Software (C2950-I6K2L2Q4-M), Version 12.1(22)EA14, RELEASE SOFTWARE (fc1)\r\nTechnical Support: http://www.cisco.com/techsupport\r\nCopyright (c) 1986-2010 by cisco Systems, Inc.\r\nCompiled Tue 26-O",
|
||||
"sysDescr": "Cisco Internetwork Operating System Software \r\nIOS (tm) C2950 Software (C2950-I6K2L2Q4-M), Version 12.1(22)EA14, RELEASE SOFTWARE (fc1)\r\nTechnical Support: http://www.cisco.com/techsupport\r\nCopyright (c) 1986-2010 by cisco Systems, Inc.\r\nCompiled Tue 26-O",
|
||||
"sysContact": "<private>",
|
||||
"version": "12.1(22)EA14",
|
||||
"hardware": "catalyst2950t24",
|
||||
|
@@ -23,7 +23,7 @@
|
||||
{
|
||||
"sysName": "",
|
||||
"sysObjectID": ".1.3.6.1.4.1.9.1.1226",
|
||||
"sysDescr": "Cisco IOS Software, C3560E Software (C3560E-UNIVERSALK9-M), Version 15.0(2)SE7, RELEASE SOFTWARE (fc1)\nTechnical Support: http://www.cisco.com/techsupport\r\nCopyright (c) 1986-2014 by Cisco Systems, Inc.\r\nCompiled Thu 23-Oct-14 13:27 by prod_rel_team",
|
||||
"sysDescr": "Cisco IOS Software, C3560E Software (C3560E-UNIVERSALK9-M), Version 15.0(2)SE7, RELEASE SOFTWARE (fc1)\r\nTechnical Support: http://www.cisco.com/techsupport\r\nCopyright (c) 1986-2014 by Cisco Systems, Inc.\r\nCompiled Thu 23-Oct-14 13:27 by prod_rel_team",
|
||||
"sysContact": null,
|
||||
"version": "15.0(2)SE7",
|
||||
"hardware": "cat3560x24",
|
||||
|
@@ -387,7 +387,7 @@
|
||||
{
|
||||
"sysName": "<private>",
|
||||
"sysObjectID": ".1.3.6.1.4.1.9.1.1116",
|
||||
"sysDescr": "Cisco IOS Software, ASR1000 Software (PPC_LINUX_IOSD-ADVENTERPRISEK9-M), Version 15.5(3)S1a, RELEASE SOFTWARE (fc1)\nTechnical Support: http://www.cisco.com/techsupport\r\nCopyright (c) 1986-2015 by Cisco Systems, Inc.\r\nCompiled Wed 04-Nov-15 17:40 by mcpre",
|
||||
"sysDescr": "Cisco IOS Software, ASR1000 Software (PPC_LINUX_IOSD-ADVENTERPRISEK9-M), Version 15.5(3)S1a, RELEASE SOFTWARE (fc1)\r\nTechnical Support: http://www.cisco.com/techsupport\r\nCopyright (c) 1986-2015 by Cisco Systems, Inc.\r\nCompiled Wed 04-Nov-15 17:40 by mcpre",
|
||||
"sysContact": "<private>",
|
||||
"version": "15.5(3)S1a",
|
||||
"hardware": "ciscoASR1002F",
|
||||
|
@@ -5,7 +5,7 @@
|
||||
{
|
||||
"sysName": "<private>",
|
||||
"sysObjectID": ".1.3.6.1.4.1.9.1.1116",
|
||||
"sysDescr": "Cisco IOS Software, ASR1000 Software (PPC_LINUX_IOSD-ADVENTERPRISEK9-M), Version 15.5(3)S1a, RELEASE SOFTWARE (fc1)\nTechnical Support: http://www.cisco.com/techsupport\r\nCopyright (c) 1986-2015 by Cisco Systems, Inc.\r\nCompiled Wed 04-Nov-15 17:40 by mcpre",
|
||||
"sysDescr": "Cisco IOS Software, ASR1000 Software (PPC_LINUX_IOSD-ADVENTERPRISEK9-M), Version 15.5(3)S1a, RELEASE SOFTWARE (fc1)\r\nTechnical Support: http://www.cisco.com/techsupport\r\nCopyright (c) 1986-2015 by Cisco Systems, Inc.\r\nCompiled Wed 04-Nov-15 17:40 by mcpre",
|
||||
"sysContact": "<private>",
|
||||
"version": "15.5(3)S1a",
|
||||
"hardware": "C9404R",
|
||||
|
@@ -28500,7 +28500,7 @@
|
||||
{
|
||||
"sysName": "<private>",
|
||||
"sysObjectID": ".1.3.6.1.4.1.9.1.1018",
|
||||
"sysDescr": "Cisco IOS XR Software (Cisco ASR9K Series), Version 5.3.4[Default]\nCopyright (c) 2017 by Cisco Systems, Inc.",
|
||||
"sysDescr": "Cisco IOS XR Software (Cisco ASR9K Series), Version 5.3.4[Default]\r\nCopyright (c) 2017 by Cisco Systems, Inc.",
|
||||
"sysContact": "<private>",
|
||||
"version": "5.3.4",
|
||||
"hardware": "ASR9K Series",
|
||||
|
@@ -5,7 +5,7 @@
|
||||
{
|
||||
"sysName": "<private>",
|
||||
"sysObjectID": ".1.3.6.1.4.1.23867.1.2.48",
|
||||
"sysDescr": "Silver Peak Systems, Inc. ECS\nLinux hostname 2.6.38.6-rc1 #1 VXOA 8.1.9.8_77260 SMP Tue Dec 31 14:19:17 PST 2019 x86_64",
|
||||
"sysDescr": "Silver Peak Systems, Inc. ECS\r\nLinux hostname 2.6.38.6-rc1 #1 VXOA 8.1.9.8_77260 SMP Tue Dec 31 14:19:17 PST 2019 x86_64",
|
||||
"sysContact": "<private>",
|
||||
"version": "8.1.9.8_77260",
|
||||
"hardware": "ECS",
|
||||
|
@@ -1,297 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* mock.snmp.inc.php
|
||||
*
|
||||
* Mock functions from includes/snmp.inc.php to allow tests to run without real snmp
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* @copyright 2016 Tony Murray
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
*/
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use LibreNMS\Config;
|
||||
|
||||
$snmpMockCache = [];
|
||||
|
||||
/**
|
||||
* Cache the data from an snmprec file
|
||||
* in ./tests/snmpsim/
|
||||
*
|
||||
* @param string $file the snmprec file name (excluding .snmprec)
|
||||
*/
|
||||
function cache_snmprec($file)
|
||||
{
|
||||
global $snmpMockCache;
|
||||
if (isset($snmpMockCache[$file])) {
|
||||
return;
|
||||
}
|
||||
$snmpMockCache[$file] = [];
|
||||
|
||||
$data = file_get_contents(Config::get('install_dir') . "/tests/snmpsim/$file.snmprec");
|
||||
$line = strtok($data, "\r\n");
|
||||
while ($line !== false) {
|
||||
[$oid, $type, $data] = explode('|', $line, 3);
|
||||
if ($type == '4') {
|
||||
$data = trim($data);
|
||||
} elseif ($type == '6') {
|
||||
$data = trim($data, '.');
|
||||
} elseif ($type == '4x') {
|
||||
// MacAddress type is stored as hex string, but we don't understand mibs
|
||||
if (Str::startsWith($oid, [
|
||||
'1.3.6.1.2.1.2.2.1.6', // IF-MIB::ifPhysAddress
|
||||
'1.3.6.1.2.1.17.1.1.0', // BRIDGE-MIB::dot1dBaseBridgeAddress.0
|
||||
'1.3.6.1.4.1.890.1.5.13.13.8.1.1.20', // IES5206-MIB::slotModuleMacAddress
|
||||
])) {
|
||||
$data = \LibreNMS\Util\Rewrite::readableMac($data);
|
||||
} else {
|
||||
$data = hex2str($data);
|
||||
}
|
||||
}
|
||||
|
||||
$snmpMockCache[$file][$oid] = [$type, $data];
|
||||
$line = strtok("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all data of the specified $community from the snmprec cache
|
||||
*
|
||||
* @param string $community snmp community to return
|
||||
* @return array array of the data containing: [$oid][$type, $data]
|
||||
*
|
||||
* @throws Exception this $community is not cached
|
||||
*/
|
||||
function snmprec_get($community)
|
||||
{
|
||||
global $snmpMockCache;
|
||||
cache_snmprec($community);
|
||||
d_echo($snmpMockCache);
|
||||
|
||||
if (isset($snmpMockCache[$community])) {
|
||||
return $snmpMockCache[$community];
|
||||
}
|
||||
|
||||
throw new Exception("SNMPREC: community $community not cached");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an $oid from the specified $community
|
||||
*
|
||||
* @param string $community the community to fetch data from
|
||||
* @param string $oid numeric oid of data to fetch
|
||||
* @return array array of the data containing: [$type, $data]
|
||||
*
|
||||
* @throws Exception this $oid is not cached
|
||||
*/
|
||||
function snmprec_get_oid($community, $oid)
|
||||
{
|
||||
global $snmpMockCache;
|
||||
cache_snmprec($community);
|
||||
|
||||
if (isset($snmpMockCache[$community]) && isset($snmpMockCache[$community][$oid])) {
|
||||
return $snmpMockCache[$community][$oid];
|
||||
}
|
||||
|
||||
throw new Exception("SNMPREC: oid $community:$oid not cached");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the numeric oid of an oid
|
||||
* The leading dot is ommited by default to be compatible with snmpsim
|
||||
*
|
||||
* @param string $oid the oid to tranlslate
|
||||
* @param string $mib mib to use
|
||||
* @param string $mibdir mib dir to look for mib in
|
||||
* @return string the oid in numeric format (.1.3.4.5)
|
||||
*
|
||||
* @throws Exception Could not translate the oid
|
||||
*/
|
||||
function snmp_translate_number($oid, $mib = null, $mibdir = null)
|
||||
{
|
||||
// optimizations (35s -> 1.6s on my laptop)
|
||||
if ($oid == 'SNMPv2-MIB::sysDescr.0') {
|
||||
return '1.3.6.1.2.1.1.1.0';
|
||||
}
|
||||
if ($oid == 'SNMPv2-MIB::sysObjectID.0') {
|
||||
return '1.3.6.1.2.1.1.2.0';
|
||||
}
|
||||
if ($oid == 'ENTITY-MIB::entPhysicalDescr.1') {
|
||||
return '1.3.6.1.2.1.47.1.1.1.1.2.1';
|
||||
}
|
||||
if ($oid == 'SML-MIB::product-Name.0') {
|
||||
return '1.3.6.1.4.1.2.6.182.3.3.1.0';
|
||||
}
|
||||
if ($oid == 'ENTITY-MIB::entPhysicalMfgName.1') {
|
||||
return '1.3.6.1.2.1.47.1.1.1.1.12.1';
|
||||
}
|
||||
if ($oid == 'GAMATRONIC-MIB::psUnitManufacture.0') {
|
||||
return '1.3.6.1.4.1.6050.1.1.2.0';
|
||||
}
|
||||
if ($oid === 'SYNOLOGY-SYSTEM-MIB::systemStatus.0') {
|
||||
return '1.3.6.1.4.1.6574.1.1.0';
|
||||
}
|
||||
// end optimizations
|
||||
|
||||
if (preg_match('/^[\.\d]*$/', $oid)) {
|
||||
return ltrim($oid, '.');
|
||||
}
|
||||
|
||||
$cmd = "snmptranslate -IR -On '$oid'";
|
||||
$cmd .= ' -M ' . (isset($mibdir) ? Config::get('mib_dir') . ':' . Config::get('mib_dir') . "/$mibdir" : Config::get('mib_dir'));
|
||||
if (isset($mib) && $mib) {
|
||||
$cmd .= " -m $mib";
|
||||
}
|
||||
|
||||
$number = shell_exec($cmd);
|
||||
|
||||
if (empty($number)) {
|
||||
throw new Exception('Could not translate oid: ' . $oid . PHP_EOL . 'Tried: ' . $cmd);
|
||||
}
|
||||
|
||||
return trim($number, ". \n\r");
|
||||
}
|
||||
|
||||
function snmp_translate_type($oid, $mib = null, $mibdir = null)
|
||||
{
|
||||
$cmd = "snmptranslate -IR -Td $oid";
|
||||
$cmd .= ' -M ' . (isset($mibdir) ? Config::get('mib_dir') . ':' . Config::get('mib_dir') . "/$mibdir" : Config::get('mib_dir'));
|
||||
if (isset($mib) && $mib) {
|
||||
$cmd .= " -m $mib";
|
||||
}
|
||||
|
||||
$result = shell_exec($cmd);
|
||||
|
||||
if (empty($result)) {
|
||||
throw new Exception('Could not translate oid: ' . $oid . PHP_EOL . 'Tried: ' . $cmd);
|
||||
}
|
||||
|
||||
if (Str::contains($result, 'OCTET STRING')) {
|
||||
return 4;
|
||||
}
|
||||
if (Str::contains($result, 'Integer32')) {
|
||||
return 2;
|
||||
}
|
||||
if (Str::contains($result, 'NULL')) {
|
||||
return 5;
|
||||
}
|
||||
if (Str::contains($result, 'OBJECT IDENTIFIER')) {
|
||||
return 6;
|
||||
}
|
||||
if (Str::contains($result, 'IpAddress')) {
|
||||
return 64;
|
||||
}
|
||||
if (Str::contains($result, 'Counter32')) {
|
||||
return 65;
|
||||
}
|
||||
if (Str::contains($result, 'Gauge32')) {
|
||||
return 66;
|
||||
}
|
||||
if (Str::contains($result, 'TimeTicks')) {
|
||||
return 67;
|
||||
}
|
||||
if (Str::contains($result, 'Opaque')) {
|
||||
return 68;
|
||||
}
|
||||
if (Str::contains($result, 'Counter64')) {
|
||||
return 70;
|
||||
}
|
||||
|
||||
throw new Exception('Unknown type');
|
||||
}
|
||||
|
||||
// Mocked functions
|
||||
|
||||
function snmp_get($device, $oid, $options = null, $mib = null, $mibdir = null)
|
||||
{
|
||||
$community = $device['community'];
|
||||
$num_oid = snmp_translate_number($oid, $mib, $mibdir);
|
||||
|
||||
try {
|
||||
$data = snmprec_get_oid($community, $num_oid);
|
||||
|
||||
$result = $data[1];
|
||||
if ($data[0] == 6) {
|
||||
$result = '.' . $data[1];
|
||||
}
|
||||
|
||||
d_echo("[SNMP] snmpget $community $oid ($num_oid): $result\n");
|
||||
|
||||
return $result;
|
||||
} catch (Exception $e) {
|
||||
d_echo("[SNMP] snmpget $community $oid ($num_oid): no data\n");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function snmp_get_multi_oid($device, $oids, $options = '-OUQn', $mib = null, $mibdir = null)
|
||||
{
|
||||
if (! is_array($oids)) {
|
||||
$oids = explode(' ', $oids);
|
||||
}
|
||||
|
||||
$data = [];
|
||||
foreach ($oids as $index => $oid) {
|
||||
if (Str::contains($options, 'n')) {
|
||||
$oid_name = '.' . snmp_translate_number($oid, $mib, $mibdir);
|
||||
$val = snmp_get($device, $oid_name, $options, $mib, $mibdir);
|
||||
} elseif (Str::contains($options, 's') && Str::contains($oid, '::')) {
|
||||
$tmp = explode('::', $oid);
|
||||
$oid_name = $tmp[1];
|
||||
$val = snmp_get($device, $oid, $options, $mib, $mibdir);
|
||||
} else {
|
||||
$oid_name = $oid;
|
||||
$val = snmp_get($device, $oid, $options, $mib, $mibdir);
|
||||
}
|
||||
|
||||
if ($val !== false) {
|
||||
$data[$oid_name] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
function snmp_walk($device, $oid, $options = null, $mib = null, $mibdir = null)
|
||||
{
|
||||
$community = $device['community'];
|
||||
$dev = snmprec_get($community);
|
||||
$num_oid = snmp_translate_number($oid, $mib, $mibdir);
|
||||
|
||||
$output = '';
|
||||
foreach ($dev as $key => $data) {
|
||||
if (Str::startsWith($key, $num_oid)) {
|
||||
if ($data[0] == 6) {
|
||||
$output .= '.' . $data[1] . PHP_EOL;
|
||||
} else {
|
||||
$output .= $data[1] . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
d_echo("[SNMP] snmpwalk $community $num_oid: ");
|
||||
if (empty($output)) {
|
||||
d_echo("no data\n");
|
||||
// does this match the behavior of the real snmp_walk()?
|
||||
return false;
|
||||
} else {
|
||||
d_echo($output);
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user