Files

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

367 lines
11 KiB
PHP
Raw Permalink Normal View History

2017-05-01 23:49:11 -05:00
<?php
/**
* OS.php
*
* -Description-
2017-05-01 23:49:11 -05:00
*
* 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
2021-02-09 00:29:04 +01:00
* along with this program. If not, see <https://www.gnu.org/licenses/>.
2017-05-01 23:49:11 -05:00
*
2021-02-09 00:29:04 +01:00
* @link https://www.librenms.org
2021-09-10 20:09:53 +02:00
*
* @copyright 2021 Tony Murray
2017-05-01 23:49:11 -05:00
* @author Tony Murray <murraytony@gmail.com>
*/
namespace LibreNMS;
use App\Models\Device;
use App\Models\DeviceGraph;
use DeviceCache;
2020-04-17 17:37:56 -05:00
use Illuminate\Support\Str;
2017-05-01 23:49:11 -05:00
use LibreNMS\Device\WirelessSensor;
2018-02-05 07:39:13 -06:00
use LibreNMS\Device\YamlDiscovery;
2020-11-23 15:35:35 -06:00
use LibreNMS\Interfaces\Discovery\MempoolsDiscovery;
2020-09-18 08:12:07 -05:00
use LibreNMS\Interfaces\Discovery\OSDiscovery;
2018-02-05 07:39:13 -06:00
use LibreNMS\Interfaces\Discovery\ProcessorDiscovery;
2022-01-30 16:28:18 -06:00
use LibreNMS\Interfaces\Discovery\StpInstanceDiscovery;
use LibreNMS\Interfaces\Discovery\StpPortDiscovery;
2021-10-19 19:32:28 -05:00
use LibreNMS\Interfaces\Polling\Netstats\IcmpNetstatsPolling;
use LibreNMS\Interfaces\Polling\Netstats\IpForwardNetstatsPolling;
use LibreNMS\Interfaces\Polling\Netstats\IpNetstatsPolling;
use LibreNMS\Interfaces\Polling\Netstats\SnmpNetstatsPolling;
use LibreNMS\Interfaces\Polling\Netstats\TcpNetstatsPolling;
use LibreNMS\Interfaces\Polling\Netstats\UdpNetstatsPolling;
2022-01-30 16:28:18 -06:00
use LibreNMS\Interfaces\Polling\StpInstancePolling;
use LibreNMS\Interfaces\Polling\StpPortPolling;
2017-05-01 23:49:11 -05:00
use LibreNMS\OS\Generic;
2022-01-30 16:28:18 -06:00
use LibreNMS\OS\Traits\BridgeMib;
2018-02-05 07:39:13 -06:00
use LibreNMS\OS\Traits\HostResources;
2021-10-19 19:32:28 -05:00
use LibreNMS\OS\Traits\NetstatsPolling;
2022-01-30 16:28:18 -06:00
use LibreNMS\OS\Traits\ResolvesPortIds;
2018-02-05 07:39:13 -06:00
use LibreNMS\OS\Traits\UcdResources;
2020-11-23 15:35:35 -06:00
use LibreNMS\OS\Traits\YamlMempoolsDiscovery;
2020-09-18 08:12:07 -05:00
use LibreNMS\OS\Traits\YamlOSDiscovery;
use LibreNMS\Util\StringHelpers;
2017-05-01 23:49:11 -05:00
2021-10-19 19:32:28 -05:00
class OS implements
ProcessorDiscovery,
OSDiscovery,
MempoolsDiscovery,
2022-01-30 16:28:18 -06:00
StpInstanceDiscovery,
StpPortDiscovery,
2021-10-19 19:32:28 -05:00
IcmpNetstatsPolling,
IpNetstatsPolling,
IpForwardNetstatsPolling,
SnmpNetstatsPolling,
2022-01-30 16:28:18 -06:00
StpInstancePolling,
StpPortPolling,
2021-10-19 19:32:28 -05:00
TcpNetstatsPolling,
UdpNetstatsPolling
2017-05-01 23:49:11 -05:00
{
2018-02-05 07:39:13 -06:00
use HostResources {
HostResources::discoverProcessors as discoverHrProcessors;
2020-11-23 15:35:35 -06:00
HostResources::discoverMempools as discoverHrMempools;
2018-02-05 07:39:13 -06:00
}
use UcdResources {
UcdResources::discoverProcessors as discoverUcdProcessors;
2020-11-23 15:35:35 -06:00
UcdResources::discoverMempools as discoverUcdMempools;
2018-02-05 07:39:13 -06:00
}
2020-09-18 08:12:07 -05:00
use YamlOSDiscovery;
2020-11-23 15:35:35 -06:00
use YamlMempoolsDiscovery;
2021-10-19 19:32:28 -05:00
use NetstatsPolling;
2022-01-30 16:28:18 -06:00
use ResolvesPortIds;
use BridgeMib;
2018-02-05 07:39:13 -06:00
2022-01-30 16:28:18 -06:00
/**
* @var float|null
*/
public $stpTimeFactor; // for stp time quirks
2017-05-01 23:49:11 -05:00
private $device; // annoying use of references to make sure this is in sync with global $device variable
private $graphs; // stores device graphs
2018-02-05 07:39:13 -06:00
private $cache; // data cache
private $pre_cache; // pre-fetch data cache
2017-05-01 23:49:11 -05:00
/**
* OS constructor. Not allowed to be created directly. Use OS::make()
*/
2023-10-05 19:49:26 -05:00
protected function __construct(array &$device)
2017-05-01 23:49:11 -05:00
{
$this->device = &$device;
$this->graphs = [];
2017-05-01 23:49:11 -05:00
}
/**
* Get the device array that owns this OS instance
*
* @return array
*/
2020-09-18 08:12:07 -05:00
public function &getDeviceArray()
2017-05-01 23:49:11 -05:00
{
return $this->device;
}
/**
* Get the device_id of the device that owns this OS instance
*
* @return int
*/
public function getDeviceId()
{
return (int) $this->device['device_id'];
}
/**
* Get the Eloquent Device Model for the current device
*
* @return Device
*/
2020-09-18 08:12:07 -05:00
public function getDevice()
{
return DeviceCache::get($this->getDeviceId());
}
/**
* Enable a graph for this device
*
2021-09-08 23:35:56 +02:00
* @param string $name
*/
public function enableGraph($name)
{
$this->graphs[$name] = true;
}
public function persistGraphs(bool $cleanup = true): void
{
2020-09-18 08:12:07 -05:00
$device = $this->getDevice();
$graphs = collect(array_keys($this->graphs));
if ($cleanup) {
// delete extra graphs
$device->graphs->keyBy('graph')->collect()->except($graphs)->each->delete();
}
// create missing graphs
$device->graphs()->saveMany($graphs->diff($device->graphs->pluck('graph'))->map(function ($graph) {
return new DeviceGraph(['graph' => $graph]);
}));
}
2018-02-05 07:39:13 -06:00
public function preCache()
{
if (is_null($this->pre_cache)) {
$this->pre_cache = YamlDiscovery::preCache($this);
}
return $this->pre_cache;
}
2017-05-01 23:49:11 -05:00
/**
* Snmpwalk the specified oid and return an array of the data indexed by the oid index.
* If the data is cached, return the cached data.
* DO NOT use numeric oids with this function! The snmp result must contain only one oid.
*
2021-09-08 23:35:56 +02:00
* @param string $oid textual oid
* @param string $mib mib for this oid
* @param string $snmpflags snmpflags for this oid
2021-03-31 17:18:43 +02:00
* @return array|null array indexed by the snmp index with the value as the data returned by snmp
2017-05-01 23:49:11 -05:00
*/
2019-02-07 07:42:23 -05:00
public function getCacheByIndex($oid, $mib = null, $snmpflags = '-OQUs')
2017-05-01 23:49:11 -05:00
{
2020-04-17 17:37:56 -05:00
if (Str::contains($oid, '.')) {
2018-02-05 07:39:13 -06:00
echo "Error: don't use this with numeric oids!\n";
2020-09-21 14:54:51 +02:00
2018-02-05 07:39:13 -06:00
return null;
2017-05-01 23:49:11 -05:00
}
if (! isset($this->cache['cache_oid'][$oid])) {
2020-09-18 08:12:07 -05:00
$data = snmpwalk_cache_oid($this->getDeviceArray(), $oid, [], $mib, null, $snmpflags);
$this->cache['cache_oid'][$oid] = array_map('current', $data);
2018-02-05 07:39:13 -06:00
}
return $this->cache['cache_oid'][$oid];
2018-02-05 07:39:13 -06:00
}
/**
* Snmpwalk the specified oid table and return an array of the data indexed by the oid index.
* If the data is cached, return the cached data.
* DO NOT use numeric oids with this function! The snmp result must contain only one oid.
*
2021-09-08 23:35:56 +02:00
* @param string $oid textual oid
* @param string $mib mib for this oid (optional)
* @param int $depth depth for snmpwalk_group (optional)
2021-03-31 17:18:43 +02:00
* @return array|null array indexed by the snmp index with the value as the data returned by snmp
2018-02-05 07:39:13 -06:00
*/
public function getCacheTable($oid, $mib = null, $depth = 1)
2018-02-05 07:39:13 -06:00
{
2020-04-17 17:37:56 -05:00
if (Str::contains($oid, '.')) {
2018-02-05 07:39:13 -06:00
echo "Error: don't use this with numeric oids!\n";
2020-09-21 14:54:51 +02:00
2018-02-05 07:39:13 -06:00
return null;
}
if (! isset($this->cache['group'][$depth][$oid])) {
2020-09-18 08:12:07 -05:00
$this->cache['group'][$depth][$oid] = snmpwalk_group($this->getDeviceArray(), $oid, $mib, $depth);
2017-05-01 23:49:11 -05:00
}
return $this->cache['group'][$depth][$oid];
2018-02-05 07:39:13 -06:00
}
/**
* Check if an OID has been cached
*
2021-09-08 23:35:56 +02:00
* @param string $oid
2018-02-05 07:39:13 -06:00
* @return bool
*/
public function isCached($oid)
{
return isset($this->cache['cache_oid'][$oid]);
2017-05-01 23:49:11 -05:00
}
/**
* OS Factory, returns an instance of the OS for this device
2018-02-05 07:39:13 -06:00
* If no specific OS is found, Try the OS group.
* Otherwise, returns Generic
2017-05-01 23:49:11 -05:00
*/
2023-10-05 19:49:26 -05:00
public static function make(array &$device): OS
2017-05-01 23:49:11 -05:00
{
if (isset($device['os'])) {
// load os definition and populate os_group
\LibreNMS\Util\OS::loadDefinition($device['os']);
2022-08-30 12:55:37 -05:00
$device['os_group'] = Config::get("os.{$device['os']}.group");
2017-05-01 23:49:11 -05:00
$class = StringHelpers::toClass($device['os'], 'LibreNMS\\OS\\');
d_echo('Attempting to initialize OS: ' . $device['os'] . PHP_EOL);
2018-02-05 07:39:13 -06:00
if (class_exists($class)) {
d_echo("OS initialized: $class\n");
2020-09-21 14:54:51 +02:00
2018-02-05 07:39:13 -06:00
return new $class($device);
}
// If not a specific OS, check for a group one.
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");
return new $class($device);
}
}
2018-02-05 07:39:13 -06:00
}
2023-06-24 12:12:16 -05:00
d_echo("OS initialized as Generic ({$device['os']})\n");
2020-09-21 14:54:51 +02:00
2017-05-01 23:49:11 -05:00
return new Generic($device);
}
2018-02-05 07:39:13 -06:00
public function getName()
{
if (isset($this->device['os'])) {
return $this->device['os'];
}
$rf = new \ReflectionClass($this);
$name = $rf->getShortName();
preg_match_all('/[A-Z][a-z]*/', $name, $segments);
return implode('-', array_map('strtolower', $segments[0]));
}
2017-05-01 23:49:11 -05:00
/**
* Poll a channel based OID, but return data in MHz
*
2021-09-08 23:35:56 +02:00
* @param array $sensors
* @param callable $callback Function to modify the value before converting it to a frequency
2017-05-01 23:49:11 -05:00
* @return array
*/
protected function pollWirelessChannelAsFrequency($sensors, $callback = null)
2017-05-01 23:49:11 -05:00
{
if (empty($sensors)) {
2020-09-18 08:12:07 -05:00
return [];
2017-05-01 23:49:11 -05:00
}
2020-09-18 08:12:07 -05:00
$oids = [];
2017-05-01 23:49:11 -05:00
foreach ($sensors as $sensor) {
$oids[$sensor['sensor_id']] = current($sensor['sensor_oids']);
}
2020-09-18 08:12:07 -05:00
$snmp_data = snmp_get_multi_oid($this->getDeviceArray(), $oids);
2017-05-01 23:49:11 -05:00
2020-09-18 08:12:07 -05:00
$data = [];
2017-05-01 23:49:11 -05:00
foreach ($oids as $id => $oid) {
if (isset($callback)) {
$channel = call_user_func($callback, $snmp_data[$oid]);
} else {
$channel = $snmp_data[$oid];
}
$data[$id] = WirelessSensor::channelToFrequency($channel);
2017-05-01 23:49:11 -05:00
}
return $data;
}
2018-02-05 07:39:13 -06:00
/**
* Discover processors.
* Returns an array of LibreNMS\Device\Processor objects that have been discovered
*
* @return array Processors
*/
public function discoverProcessors()
{
$processors = $this->discoverHrProcessors();
if (empty($processors)) {
$processors = $this->discoverUcdProcessors();
}
return $processors;
}
2020-09-18 08:12:07 -05:00
2020-11-23 15:35:35 -06:00
public function discoverMempools()
{
if ($this->hasYamlDiscovery('mempools')) {
return $this->discoverYamlMempools();
}
$mempools = $this->discoverHrMempools();
if ($mempools->isNotEmpty()) {
return $mempools;
}
return $this->discoverUcdMempools();
}
public function getDiscovery($module = null)
2020-09-18 08:12:07 -05:00
{
if (! array_key_exists('dynamic_discovery', $this->device)) {
$file = base_path('/includes/definitions/discovery/' . $this->getName() . '.yaml');
if (file_exists($file)) {
$this->device['dynamic_discovery'] = \Symfony\Component\Yaml\Yaml::parse(file_get_contents($file));
}
}
2020-11-23 15:35:35 -06:00
if ($module) {
return $this->device['dynamic_discovery']['modules'][$module] ?? [];
}
2020-09-18 08:12:07 -05:00
return $this->device['dynamic_discovery'] ?? [];
}
public function hasYamlDiscovery(string $module = null)
{
return $module ? isset($this->getDiscovery()['modules'][$module]) : ! empty($this->getDiscovery());
}
2017-05-01 23:49:11 -05:00
}