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:
Tony Murray
2021-10-19 15:43:43 -05:00
committed by GitHub
parent 17b97abbd2
commit f94f7f23b8
24 changed files with 639 additions and 503 deletions

View File

@@ -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
{

View 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;
}

View File

@@ -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)) {

View File

@@ -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");

View File

@@ -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;
}
}

View File

@@ -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 = [

View File

@@ -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);
}
/**

View File

@@ -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'
-

View File

@@ -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);

View File

@@ -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');

View File

@@ -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';

View File

@@ -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);

View File

@@ -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);

View File

@@ -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)");

View 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;
}
}

View File

@@ -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' => [],
];
]);
}
/**

View File

@@ -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);

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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;
}
}