mirror of
https://github.com/librenms/librenms.git
synced 2024-10-07 16:52:45 +00:00
Transceiver Support (#16165)
* Add transceivers module Move os specific code to OS Fix errors and updated connector names Add RouterOS, a lot less data there. Add Comware Add Exa, required a transformer function (mw to dBm) Add Junos, revision was too short Just starting on ui Graphs, and more ui some polling fixes collapse header for small screens refactor a bit Missed graphs Transceivers icon inline Use @once on popup javascript update db_schema.yaml Don't show transceivers in basic view basic view could use a review Apply fixes from StyleCI API functions Comware don't fail if port is missing Apply fixes from StyleCI Add alert rules to collection Device Overview Attempt to fix bad alert rule, probably needs more Fix up Comware and remove old sensors Mark transceiver metrics without thresholds as Unknown Routeros cleanup Exa cleanup Handle missing port Graph allow filter by channel More translations Add transceiver graphs to port graphs Add Cisco support, use entPhyscial module data if available Fix OcNos divisors Labels on transceiver page Show encoding if available Hacky OcNos port mapping Fix up Junos optics and remove old sensors FS switch support Metric casts to prevent thrashing Extra transform_function support Add link to transceivers page from overview Change default sort to group by type, then channel move some code out of overview blade template Fix bad type ocnos Apply fixes from StyleCI Add scales to graphs Add some test data Default sort order for metrcis in SQL applied by default Transceiver metrics threshold manual settings via WebUI and API Fixes to channels verbiage Fix severity calculations Add cable field for SM/MM/Copper Apply fixes from StyleCI update test data Show DDM Update DB schema file Extend serial field to 32, even though devices shouldn't be able to have one longer than 16 Missing import Add status field to database, that way we can support snmp implementations that only return an enum Add missing files Fix db_schema Fix style Fixes Style fix Work around phpstan issue Update transceivers.blade.php Missed getStatus() call Prevent extra dots when channels are not changed Update module to match upstream Save ocnos metrics as sensors Move to regular sensors add entity physical index Update UI to sensors WIP Apply fixes from StyleCI Forgot one change Update ui to use sensors Remove transceiver metrics Remove metric os discover code fs-switch pending Remove transceiver metrics for fs-centec Exa link up Revert all test data Fix up transceiver module interface Remove unused Convert class comware cache and transceiver type Fix some transceiver metrics filtering and formatting issues Consolidate display formatting Coalesce commare hh3cTransceiverTable walks to prevent double walk Use group to identify transceiver sensors Fixup routeros Fix up cisco update db_schema Small addition to docs Improve overview layout and add graph popup Update Junos update css files ddm should be nullable Increase the field length for type and model Cisco Improve detection when there is an intermediary container Add transceiver test data Apply fixes from StyleCI Fix incorrect test data Improve display formatting Fix test data Apply fixes from StyleCI Fix up more data Fix up more data Fix incorrect return type in routeros Update ocnos data * Remove some remaining references to transceiver_metrics table
This commit is contained in:
39
LibreNMS/Interfaces/Discovery/TransceiverDiscovery.php
Normal file
39
LibreNMS/Interfaces/Discovery/TransceiverDiscovery.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/*
|
||||
* TransceiverDiscovery.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 2024 Tony Murray
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
*/
|
||||
|
||||
namespace LibreNMS\Interfaces\Discovery;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
interface TransceiverDiscovery
|
||||
{
|
||||
/**
|
||||
* Discover transceivers.
|
||||
* Distance is in meters.
|
||||
*
|
||||
* @return Collection<\App\Models\Transceiver>
|
||||
*/
|
||||
public function discoverTransceivers(): Collection;
|
||||
}
|
@@ -84,6 +84,8 @@ interface Module
|
||||
* You should always order the data by a non-transient column.
|
||||
* Some id fields may need to be joined to tie back to non-transient data.
|
||||
* Module may return null if testing is not supported or required.
|
||||
*
|
||||
* @param string $type Type is either discovery or poller
|
||||
*/
|
||||
public function dump(Device $device, string $type): ?array;
|
||||
}
|
||||
|
96
LibreNMS/Modules/Transceivers.php
Normal file
96
LibreNMS/Modules/Transceivers.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/*
|
||||
* Transceivers.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 2024 Tony Murray
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
*/
|
||||
|
||||
namespace LibreNMS\Modules;
|
||||
|
||||
use App\Models\Device;
|
||||
use App\Models\Transceiver;
|
||||
use App\Observers\ModuleModelObserver;
|
||||
use LibreNMS\DB\SyncsModels;
|
||||
use LibreNMS\Interfaces\Data\DataStorageInterface;
|
||||
use LibreNMS\Interfaces\Discovery\TransceiverDiscovery;
|
||||
use LibreNMS\Interfaces\Module;
|
||||
use LibreNMS\OS;
|
||||
use LibreNMS\Polling\ModuleStatus;
|
||||
|
||||
class Transceivers implements Module
|
||||
{
|
||||
use SyncsModels;
|
||||
|
||||
public function dependencies(): array
|
||||
{
|
||||
return ['ports'];
|
||||
}
|
||||
|
||||
public function shouldDiscover(OS $os, ModuleStatus $status): bool
|
||||
{
|
||||
return $status->isEnabledAndDeviceUp($os->getDevice()) && $os instanceof TransceiverDiscovery;
|
||||
}
|
||||
|
||||
public function shouldPoll(OS $os, ModuleStatus $status): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function discover(OS $os): void
|
||||
{
|
||||
if ($os instanceof TransceiverDiscovery) {
|
||||
$discoveredTransceivers = $os->discoverTransceivers();
|
||||
|
||||
// save transceivers
|
||||
ModuleModelObserver::observe(Transceiver::class);
|
||||
$this->syncModels($os->getDevice(), 'transceivers', $discoveredTransceivers);
|
||||
}
|
||||
}
|
||||
|
||||
public function poll(OS $os, DataStorageInterface $datastore): void
|
||||
{
|
||||
// no polling
|
||||
}
|
||||
|
||||
public function dataExists(Device $device): bool
|
||||
{
|
||||
return $device->transceivers()->exists();
|
||||
}
|
||||
|
||||
public function cleanup(Device $device): int
|
||||
{
|
||||
return $device->transceivers()->delete();
|
||||
}
|
||||
|
||||
public function dump(Device $device, string $type): ?array
|
||||
{
|
||||
if ($type == 'poller') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'transceivers' => $device->transceivers()->orderBy('index')
|
||||
->leftJoin('ports', 'transceivers.port_id', 'ports.port_id')
|
||||
->select(['transceivers.*', 'ifIndex'])
|
||||
->get()->map->makeHidden(['id', 'created_at', 'updated_at', 'device_id', 'port_id']),
|
||||
];
|
||||
}
|
||||
}
|
@@ -27,13 +27,15 @@ namespace LibreNMS\OS;
|
||||
|
||||
use App\Models\Device;
|
||||
use App\Models\Mempool;
|
||||
use App\Models\Transceiver;
|
||||
use Illuminate\Support\Collection;
|
||||
use LibreNMS\Device\Processor;
|
||||
use LibreNMS\Interfaces\Discovery\MempoolsDiscovery;
|
||||
use LibreNMS\Interfaces\Discovery\ProcessorDiscovery;
|
||||
use LibreNMS\Interfaces\Discovery\TransceiverDiscovery;
|
||||
use LibreNMS\OS;
|
||||
|
||||
class Comware extends OS implements MempoolsDiscovery, ProcessorDiscovery
|
||||
class Comware extends OS implements MempoolsDiscovery, ProcessorDiscovery, TransceiverDiscovery
|
||||
{
|
||||
public function discoverOS(Device $device): void
|
||||
{
|
||||
@@ -107,4 +109,27 @@ class Comware extends OS implements MempoolsDiscovery, ProcessorDiscovery
|
||||
|
||||
return $mempools;
|
||||
}
|
||||
|
||||
public function discoverTransceivers(): Collection
|
||||
{
|
||||
$ifIndexToPortId = $this->getDevice()->ports()->pluck('port_id', 'ifIndex');
|
||||
|
||||
return \SnmpQuery::cache()->enumStrings()->walk('HH3C-TRANSCEIVER-INFO-MIB::hh3cTransceiverInfoTable')->mapTable(function ($data, $ifIndex) use ($ifIndexToPortId) {
|
||||
return new Transceiver([
|
||||
'port_id' => $ifIndexToPortId->get($ifIndex, 0),
|
||||
'index' => $ifIndex,
|
||||
'type' => $data['HH3C-TRANSCEIVER-INFO-MIB::hh3cTransceiverType'] ?? null,
|
||||
'vendor' => $data['HH3C-TRANSCEIVER-INFO-MIB::hh3cTransceiverVendorName'] ?? null,
|
||||
'oui' => $data['HH3C-TRANSCEIVER-INFO-MIB::hh3cTransceiverVendorOUI'] ?? null,
|
||||
'revision' => $data['HH3C-TRANSCEIVER-INFO-MIB::hh3cTransceiverRevisionNumber'] ?? null,
|
||||
'model' => $data['HH3C-TRANSCEIVER-INFO-MIB::hh3cTransceiverPartNumber'] ?? null,
|
||||
'serial' => $data['HH3C-TRANSCEIVER-INFO-MIB::hh3cTransceiverSerialNumber'] ?? null,
|
||||
'ddm' => isset($data['HH3C-TRANSCEIVER-INFO-MIB::hh3cTransceiverDiagnostic']) && $data['HH3C-TRANSCEIVER-INFO-MIB::hh3cTransceiverDiagnostic'] == 'true',
|
||||
'cable' => $data['HH3C-TRANSCEIVER-INFO-MIB::hh3cTransceiverHardwareType'] ?? null,
|
||||
'distance' => $data['HH3C-TRANSCEIVER-INFO-MIB::hh3cTransceiverTransferDistance'] ?? null,
|
||||
'wavelength' => isset($data['HH3C-TRANSCEIVER-INFO-MIB::hh3cTransceiverWaveLength']) && $data['HH3C-TRANSCEIVER-INFO-MIB::hh3cTransceiverWaveLength'] != 2147483647 ? $data['HH3C-TRANSCEIVER-INFO-MIB::hh3cTransceiverWaveLength'] : null,
|
||||
'entity_physical_index' => $ifIndex,
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -26,10 +26,13 @@
|
||||
namespace LibreNMS\OS;
|
||||
|
||||
use App\Models\Device;
|
||||
use App\Models\Transceiver;
|
||||
use Illuminate\Support\Collection;
|
||||
use LibreNMS\Interfaces\Discovery\OSDiscovery;
|
||||
use LibreNMS\Interfaces\Discovery\TransceiverDiscovery;
|
||||
use LibreNMS\OS;
|
||||
|
||||
class Exa extends OS implements OSDiscovery
|
||||
class Exa extends OS implements OSDiscovery, TransceiverDiscovery
|
||||
{
|
||||
public function discoverOS(Device $device): void
|
||||
{
|
||||
@@ -47,4 +50,34 @@ class Exa extends OS implements OSDiscovery
|
||||
return ($card_count[$card] > 1 ? $card_count[$card] . 'x ' : '') . $card;
|
||||
}, array_keys($card_count)));
|
||||
}
|
||||
|
||||
public function discoverTransceivers(): Collection
|
||||
{
|
||||
$ifIndexToPortId = $this->getDevice()->ports()->pluck('port_id', 'ifIndex');
|
||||
|
||||
return \SnmpQuery::cache()->walk('E7-Calix-MIB::e7OltPonPortTable')->mapTable(function ($data, $shelf, $card, $port) use ($ifIndexToPortId) {
|
||||
if ($data['E7-Calix-MIB::e7OltPonPortStatus'] == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$ifIndex = self::getIfIndex($shelf, $card, $port, 'gpon');
|
||||
|
||||
return new Transceiver([
|
||||
'port_id' => $ifIndexToPortId->get($ifIndex),
|
||||
'index' => "$shelf.$card.$port",
|
||||
'entity_physical_index' => $ifIndex,
|
||||
]);
|
||||
})->filter();
|
||||
}
|
||||
|
||||
public static function getIfIndex(int $chassis, int $slot, int $id, string $type): int
|
||||
{
|
||||
// doesn't work for stacked chassis, I don't have enough info to figure out how it works
|
||||
$offset = match ($type) {
|
||||
'gpon' => 20000,
|
||||
default => 0,
|
||||
};
|
||||
|
||||
return $offset + (10000 * $chassis) + ($slot * 100) + $id;
|
||||
}
|
||||
}
|
||||
|
55
LibreNMS/OS/FsCentec.php
Normal file
55
LibreNMS/OS/FsCentec.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace LibreNMS\OS;
|
||||
|
||||
use App\Models\Transceiver;
|
||||
use Illuminate\Support\Collection;
|
||||
use LibreNMS\Interfaces\Discovery\TransceiverDiscovery;
|
||||
use LibreNMS\OS;
|
||||
use SnmpQuery;
|
||||
|
||||
class FsCentec extends OS implements TransceiverDiscovery
|
||||
{
|
||||
public function discoverTransceivers(): Collection
|
||||
{
|
||||
$ifIndexToPortId = $this->getDevice()->ports()->pluck('port_id', 'ifIndex');
|
||||
|
||||
return SnmpQuery::cache()->walk('FS-SWITCH-V2-MIB::transbasicinformationTable')->mapTable(function ($data, $ifIndex) use ($ifIndexToPortId) {
|
||||
if ($data['FS-SWITCH-V2-MIB::transceiveStatus'] == 'inactive') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$distance = null;
|
||||
$cable = null;
|
||||
if (isset($data['FS-SWITCH-V2-MIB::link9SinglemodeLengthKm']) && $data['FS-SWITCH-V2-MIB::link9SinglemodeLengthKm'] != 0) {
|
||||
$distance = $data['FS-SWITCH-V2-MIB::link9SinglemodeLengthKm'] * 1000;
|
||||
$cable = 'SM';
|
||||
} elseif (isset($data['FS-SWITCH-V2-MIB::link9SinglemodeLengthM']) && $data['FS-SWITCH-V2-MIB::link9SinglemodeLengthM'] != 0) {
|
||||
$distance = $data['FS-SWITCH-V2-MIB::link9SinglemodeLengthM'];
|
||||
$cable = 'SM';
|
||||
} elseif (isset($data['FS-SWITCH-V2-MIB::link50MultimodeLength']) && $data['FS-SWITCH-V2-MIB::link50MultimodeLength'] != 0) {
|
||||
$distance = $data['FS-SWITCH-V2-MIB::link50MultimodeLength'];
|
||||
$cable = 'MM';
|
||||
} elseif (isset($data['FS-SWITCH-V2-MIB::link62MultimodeLength']) && $data['FS-SWITCH-V2-MIB::link62MultimodeLength'] != 0) {
|
||||
$distance = $data['FS-SWITCH-V2-MIB::link62MultimodeLength'];
|
||||
$cable = 'MM';
|
||||
} elseif (isset($data['FS-SWITCH-V2-MIB::linkCopperLength']) && $data['FS-SWITCH-V2-MIB::linkCopperLength'] != 0) {
|
||||
$distance = $data['FS-SWITCH-V2-MIB::linkCopperLength'];
|
||||
$cable = 'Copper';
|
||||
}
|
||||
|
||||
return new Transceiver([
|
||||
'port_id' => $ifIndexToPortId->get($ifIndex),
|
||||
'index' => $ifIndex,
|
||||
'vendor' => $data['FS-SWITCH-V2-MIB::transceiveVender'] ?? null,
|
||||
'type' => $data['FS-SWITCH-V2-MIB::transceiveType'] ?? null,
|
||||
'model' => $data['FS-SWITCH-V2-MIB::transceivePartNumber'] ?? null,
|
||||
'serial' => $data['FS-SWITCH-V2-MIB::transceiveSerialNumber'] ?? null,
|
||||
'cable' => $cable,
|
||||
'distance' => $distance,
|
||||
'wavelength' => $data['FS-SWITCH-V2-MIB::transceiveWaveLength'] ?? null,
|
||||
'entity_physical_index' => $ifIndex,
|
||||
]);
|
||||
})->filter();
|
||||
}
|
||||
}
|
@@ -26,10 +26,9 @@
|
||||
namespace LibreNMS\OS;
|
||||
|
||||
use LibreNMS\Device\Processor;
|
||||
use LibreNMS\Interfaces\Discovery\ProcessorDiscovery;
|
||||
use LibreNMS\OS;
|
||||
|
||||
class FsSwitch extends OS implements ProcessorDiscovery
|
||||
class FsSwitch extends OS
|
||||
{
|
||||
public static function normalizeTransceiverValues($value): float
|
||||
{
|
||||
|
@@ -27,19 +27,22 @@ namespace LibreNMS\OS;
|
||||
|
||||
use App\Models\Device;
|
||||
use App\Models\EntPhysical;
|
||||
use App\Models\Port;
|
||||
use App\Models\Sla;
|
||||
use App\Models\Transceiver;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use LibreNMS\Interfaces\Data\DataStorageInterface;
|
||||
use LibreNMS\Interfaces\Discovery\SlaDiscovery;
|
||||
use LibreNMS\Interfaces\Discovery\TransceiverDiscovery;
|
||||
use LibreNMS\Interfaces\Polling\OSPolling;
|
||||
use LibreNMS\Interfaces\Polling\SlaPolling;
|
||||
use LibreNMS\OS\Traits\EntityMib;
|
||||
use LibreNMS\RRD\RrdDefinition;
|
||||
use SnmpQuery;
|
||||
|
||||
class Junos extends \LibreNMS\OS implements SlaDiscovery, OSPolling, SlaPolling
|
||||
class Junos extends \LibreNMS\OS implements SlaDiscovery, OSPolling, SlaPolling, TransceiverDiscovery
|
||||
{
|
||||
use EntityMib {
|
||||
EntityMib::discoverEntityPhysical as discoverBaseEntityPhysical;
|
||||
@@ -291,4 +294,58 @@ class Junos extends \LibreNMS\OS implements SlaDiscovery, OSPolling, SlaPolling
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
public function discoverTransceivers(): Collection
|
||||
{
|
||||
$ifIndexToPortId = Port::query()->where('device_id', $this->getDeviceId())->select(['port_id', 'ifIndex', 'ifName'])->get()->keyBy('ifIndex');
|
||||
$entPhysical = SnmpQuery::walk('ENTITY-MIB::entityPhysical')->table(1);
|
||||
|
||||
$jnxDomCurrentTable = SnmpQuery::cache()->walk('JUNIPER-DOM-MIB::jnxDomCurrentTable')->mapTable(function ($data, $ifIndex) use ($ifIndexToPortId, $entPhysical) {
|
||||
$ent = $this->findTransceiverEntityByPortName($entPhysical, $ifIndexToPortId->get($ifIndex)?->ifName);
|
||||
if (empty($ent)) {
|
||||
return null; // no module
|
||||
}
|
||||
|
||||
return new Transceiver([
|
||||
'port_id' => $ifIndexToPortId->get($ifIndex)->port_id,
|
||||
'index' => $ifIndex,
|
||||
'type' => $ent['ENTITY-MIB::entPhysicalName'] ?? null,
|
||||
'vendor' => $ent['ENTITY-MIB::entPhysicalMfgName'] ?? null,
|
||||
'model' => $ent['ENTITY-MIB::entPhysicalModelName'] ?? null,
|
||||
'revision' => $ent['ENTITY-MIB::entPhysicalHardwareRev'] ?? null,
|
||||
'serial' => $ent['ENTITY-MIB::entPhysicalSerialNum'] ?? null,
|
||||
'channels' => $data['JUNIPER-DOM-MIB::jnxDomCurrentModuleLaneCount'] ?? 0,
|
||||
'entity_physical_index' => $ifIndex,
|
||||
]);
|
||||
})->filter();
|
||||
|
||||
if ($jnxDomCurrentTable->isNotEmpty()) {
|
||||
return $jnxDomCurrentTable;
|
||||
}
|
||||
|
||||
// could use improvement by mapping JUNIPER-IFOPTICS-MIB::jnxOpticsConfigTable for a tiny bit more info
|
||||
return SnmpQuery::cache()->walk('JUNIPER-IFOPTICS-MIB::jnxOpticsPMCurrentTable')
|
||||
->mapTable(function ($data, $ifIndex) use ($ifIndexToPortId) {
|
||||
return new Transceiver([
|
||||
'port_id' => $ifIndexToPortId->get($ifIndex)->port_id,
|
||||
'index' => $ifIndex,
|
||||
'entity_physical_index' => $ifIndex,
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
private function findTransceiverEntityByPortName(array $entPhysical, string $ifName): array
|
||||
{
|
||||
if (preg_match('#-(\d+/\d+/\d+)#', $ifName, $matches)) {
|
||||
$expected_tail = ' @ ' . $matches[1];
|
||||
|
||||
foreach ($entPhysical as $entity) {
|
||||
if (isset($entity['ENTITY-MIB::entPhysicalDescr']) && str_ends_with($entity['ENTITY-MIB::entPhysicalDescr'], $expected_tail)) {
|
||||
return $entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
@@ -3,14 +3,17 @@
|
||||
namespace LibreNMS\OS;
|
||||
|
||||
use App\Models\EntPhysical;
|
||||
use App\Models\Transceiver;
|
||||
use Illuminate\Support\Collection;
|
||||
use LibreNMS\Interfaces\Discovery\EntityPhysicalDiscovery;
|
||||
use LibreNMS\Interfaces\Discovery\TransceiverDiscovery;
|
||||
use LibreNMS\OS;
|
||||
use SnmpQuery;
|
||||
|
||||
class Ocnos extends OS implements EntityPhysicalDiscovery
|
||||
class Ocnos extends OS implements EntityPhysicalDiscovery, TransceiverDiscovery
|
||||
{
|
||||
private bool $sfpSeen = false;
|
||||
private ?Collection $ifNamePortIdMap = null;
|
||||
|
||||
public function discoverEntityPhysical(): Collection
|
||||
{
|
||||
@@ -75,6 +78,12 @@ class Ocnos extends OS implements EntityPhysicalDiscovery
|
||||
}
|
||||
|
||||
$transceivers = SnmpQuery::enumStrings()->walk('IPI-CMM-CHASSIS-MIB::cmmTransEEPROMTable')->table(2);
|
||||
|
||||
// load port name to port_id map
|
||||
if (! empty($transceivers)) {
|
||||
$ifNameToIndex = array_flip(SnmpQuery::cache()->walk('IF-MIB::ifName')->pluck());
|
||||
}
|
||||
|
||||
foreach ($transceivers as $cmmStackUnitIndex => $chassisTransceivers) {
|
||||
foreach ($chassisTransceivers as $cmmTransIndex => $transceiver) {
|
||||
$inventory->push(new EntPhysical([
|
||||
@@ -89,7 +98,7 @@ class Ocnos extends OS implements EntityPhysicalDiscovery
|
||||
'entPhysicalParentRelPos' => $cmmTransIndex,
|
||||
'entPhysicalHardwareRev' => $transceiver['IPI-CMM-CHASSIS-MIB::cmmTransVendorRevision'] ?? null,
|
||||
'entPhysicalIsFRU' => 'true',
|
||||
'ifIndex' => $this->guessPortId($cmmTransIndex, $transceiver['IPI-CMM-CHASSIS-MIB::cmmTransType'] ?? 'missing'),
|
||||
'ifIndex' => $ifNameToIndex[$this->guessIfName($cmmTransIndex, $transceiver['IPI-CMM-CHASSIS-MIB::cmmTransType'] ?? 'missing')] ?? null,
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -152,7 +161,7 @@ class Ocnos extends OS implements EntityPhysicalDiscovery
|
||||
return $description;
|
||||
}
|
||||
|
||||
private function guessPortId($cmmTransIndex, $cmmTransType): int
|
||||
public function guessIfName($cmmTransIndex, $cmmTransType): ?string
|
||||
{
|
||||
// IP Infusion has no reliable way of mapping a transceiver to a port it varies by hardware
|
||||
|
||||
@@ -167,7 +176,7 @@ class Ocnos extends OS implements EntityPhysicalDiscovery
|
||||
$this->sfpSeen = true;
|
||||
}
|
||||
|
||||
$portName = match ($this->getDevice()->hardware) {
|
||||
return match ($this->getDevice()->hardware) {
|
||||
'Ufi Space S9600-32X-R' => $prefix . ($this->sfpSeen ? ($cmmTransType == 'qsfp' ? $cmmTransIndex - 5 : $cmmTransIndex - 2) : $cmmTransIndex - 1),
|
||||
'Ufi Space S9510-28DC-B' => $prefix . ($cmmTransIndex - 1),
|
||||
'Ufi Space S9500-30XS-P' => $prefix . ($cmmTransType == 'qsfp' ? $cmmTransIndex - 29 : $cmmTransIndex - 1),
|
||||
@@ -176,14 +185,79 @@ class Ocnos extends OS implements EntityPhysicalDiscovery
|
||||
'Edgecore 7712-32X-O-AC-F' => $prefix . $cmmTransIndex . '/1',
|
||||
default => null, // no port map, so we can't guess
|
||||
};
|
||||
}
|
||||
|
||||
if ($portName === null) {
|
||||
return 0; // give up
|
||||
}
|
||||
public function discoverTransceivers(): Collection
|
||||
{
|
||||
return SnmpQuery::enumStrings()->walk('IPI-CMM-CHASSIS-MIB::cmmTransEEPROMTable')->mapTable(function ($data, $cmmStackUnitIndex, $cmmTransIndex) {
|
||||
$distance = 0;
|
||||
if (! empty($data['IPI-CMM-CHASSIS-MIB::cmmTransLengthMtrs']) && $data['IPI-CMM-CHASSIS-MIB::cmmTransLengthMtrs'] !== '-100002') {
|
||||
$distance = (int) $data['IPI-CMM-CHASSIS-MIB::cmmTransLengthMtrs'];
|
||||
} elseif (! empty($data['IPI-CMM-CHASSIS-MIB::cmmTransLengthKmtrs']) && $data['IPI-CMM-CHASSIS-MIB::cmmTransLengthKmtrs'] !== '-100002') {
|
||||
$distance = $data['IPI-CMM-CHASSIS-MIB::cmmTransLengthKmtrs'] * 1000;
|
||||
} elseif (! empty($data['IPI-CMM-CHASSIS-MIB::cmmTransLengthOM4']) && $data['IPI-CMM-CHASSIS-MIB::cmmTransLengthOM4'] !== '-100002') {
|
||||
$distance = (int) $data['IPI-CMM-CHASSIS-MIB::cmmTransLengthOM4'];
|
||||
} elseif (! empty($data['IPI-CMM-CHASSIS-MIB::cmmTransLengthOM3']) && $data['IPI-CMM-CHASSIS-MIB::cmmTransLengthOM3'] !== '-100002') {
|
||||
$distance = (int) $data['IPI-CMM-CHASSIS-MIB::cmmTransLengthOM3'];
|
||||
} elseif (! empty($data['IPI-CMM-CHASSIS-MIB::cmmTransLengthOM2']) && $data['IPI-CMM-CHASSIS-MIB::cmmTransLengthOM2'] !== '-100002') {
|
||||
$distance = (int) $data['IPI-CMM-CHASSIS-MIB::cmmTransLengthOM2'];
|
||||
} elseif (! empty($data['IPI-CMM-CHASSIS-MIB::cmmTransLengthOM1']) && $data['IPI-CMM-CHASSIS-MIB::cmmTransLengthOM1'] !== '-100002') {
|
||||
$distance = (int) $data['IPI-CMM-CHASSIS-MIB::cmmTransLengthOM1'];
|
||||
}
|
||||
|
||||
// load port name to port_id map
|
||||
$ifNameToIndex = array_flip(SnmpQuery::cache()->walk('IF-MIB::ifName')->pluck());
|
||||
$connector = match ($data['IPI-CMM-CHASSIS-MIB::cmmTransconnectortype'] ?? null) {
|
||||
'bayonet-or-threaded-neill-concelman' => 'ST',
|
||||
'copper-pigtail' => 'DAC',
|
||||
'fiber-jack' => 'FJ',
|
||||
'fibrechannel-style1-copperconnector', 'fibrechannel-style2-copperconnector', 'fibrechannel-coaxheaders' => 'FC',
|
||||
'hssdcii' => 'HSSDC',
|
||||
'lucent-connector' => 'LC',
|
||||
'mechanical-transfer-registeredjack' => 'MTRJ',
|
||||
'multifiber-paralleloptic-1x12' => 'MPO-12',
|
||||
'multifiber-paralleloptic-1x16' => 'MPO-16',
|
||||
'multiple-optical' => 'MPO',
|
||||
'mxc2-x16' => 'MXC2-X16',
|
||||
'no-separable-connector' => 'None',
|
||||
'optical-pigtail' => 'AOC',
|
||||
'rj45' => 'RJ45',
|
||||
'sg' => 'SG',
|
||||
'subscriber-connector' => 'SC',
|
||||
default => 'unknown',
|
||||
};
|
||||
|
||||
return $ifNameToIndex[$portName] ?? 0;
|
||||
$date = $data['IPI-CMM-CHASSIS-MIB::cmmTransDateCode'] ?? '0000-00-00';
|
||||
if (preg_match('/^(\d{2,4})(\d{2})(\d{2})$/', $date, $date_matches)) {
|
||||
$year = $date_matches[1];
|
||||
if (strlen($year) == 2) {
|
||||
$year = '20' . $year;
|
||||
}
|
||||
$date = $year . '-' . $date_matches[2] . '-' . $date_matches[3];
|
||||
}
|
||||
|
||||
$cmmTransType = $data['IPI-CMM-CHASSIS-MIB::cmmTransType'] ?? 'missing';
|
||||
|
||||
if ($this->ifNamePortIdMap === null) {
|
||||
$this->ifNamePortIdMap = $this->getDevice()->ports()->toBase()->pluck('port_id', 'ifName');
|
||||
}
|
||||
|
||||
return new Transceiver([
|
||||
'port_id' => $this->ifNamePortIdMap[$this->guessIfName($cmmTransIndex, $cmmTransType)] ?? 0,
|
||||
'index' => "$cmmStackUnitIndex.$cmmTransIndex",
|
||||
'type' => $cmmTransType,
|
||||
'vendor' => $data['IPI-CMM-CHASSIS-MIB::cmmTransVendorName'] ?? 'missing',
|
||||
'oui' => $data['IPI-CMM-CHASSIS-MIB::cmmTransVendorOUI'] ?? 'missing',
|
||||
'model' => $data['IPI-CMM-CHASSIS-MIB::cmmTransVendorPartNumber'] ?? 'missing',
|
||||
'revision' => $data['IPI-CMM-CHASSIS-MIB::cmmTransVendorRevision'] ?? 'missing',
|
||||
'serial' => $data['IPI-CMM-CHASSIS-MIB::cmmTransVendorSerialNumber'] ?? 'missing',
|
||||
'date' => $date,
|
||||
'ddm' => isset($data['IPI-CMM-CHASSIS-MIB::cmmTransDDMSupport']) && $data['IPI-CMM-CHASSIS-MIB::cmmTransDDMSupport'] == 'yes',
|
||||
'encoding' => $data['IPI-CMM-CHASSIS-MIB::cmmTransEncoding'] ?? 'missing',
|
||||
'distance' => $distance,
|
||||
'wavelength' => isset($data['IPI-CMM-CHASSIS-MIB::cmmTransWavelength']) && $data['IPI-CMM-CHASSIS-MIB::cmmTransWavelength'] !== '-100002' ? $data['IPI-CMM-CHASSIS-MIB::cmmTransWavelength'] : null,
|
||||
'connector' => $connector,
|
||||
'channels' => $data['IPI-CMM-CHASSIS-MIB::cmmTransNoOfChannels'] ?? 0,
|
||||
'entity_physical_index' => $cmmStackUnitIndex * 10000 + $cmmTransIndex,
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -25,6 +25,8 @@
|
||||
|
||||
namespace LibreNMS\OS;
|
||||
|
||||
use App\Models\Transceiver;
|
||||
use Illuminate\Support\Collection;
|
||||
use LibreNMS\Device\WirelessSensor;
|
||||
use LibreNMS\Interfaces\Data\DataStorageInterface;
|
||||
use LibreNMS\Interfaces\Discovery\Sensors\WirelessCcqDiscovery;
|
||||
@@ -38,12 +40,15 @@ use LibreNMS\Interfaces\Discovery\Sensors\WirelessRsrpDiscovery;
|
||||
use LibreNMS\Interfaces\Discovery\Sensors\WirelessRsrqDiscovery;
|
||||
use LibreNMS\Interfaces\Discovery\Sensors\WirelessRssiDiscovery;
|
||||
use LibreNMS\Interfaces\Discovery\Sensors\WirelessSinrDiscovery;
|
||||
use LibreNMS\Interfaces\Discovery\TransceiverDiscovery;
|
||||
use LibreNMS\Interfaces\Polling\OSPolling;
|
||||
use LibreNMS\OS;
|
||||
use LibreNMS\RRD\RrdDefinition;
|
||||
use LibreNMS\Util\Number;
|
||||
|
||||
class Routeros extends OS implements
|
||||
OSPolling,
|
||||
TransceiverDiscovery,
|
||||
WirelessCcqDiscovery,
|
||||
WirelessClientsDiscovery,
|
||||
WirelessFrequencyDiscovery,
|
||||
@@ -497,4 +502,22 @@ class Routeros extends OS implements
|
||||
$this->enableGraph('routeros_pppoe_sessions');
|
||||
}
|
||||
}
|
||||
|
||||
public function discoverTransceivers(): Collection
|
||||
{
|
||||
$ifIndexToPortId = $this->getDevice()->ports()->pluck('port_id', 'ifIndex');
|
||||
|
||||
return \SnmpQuery::walk('MIKROTIK-MIB::mtxrOpticalTable')->mapTable(function ($data, $ifIndex) use ($ifIndexToPortId) {
|
||||
$wavelength = isset($data['MIKROTIK-MIB::mtxrOpticalWavelength']) && $data['MIKROTIK-MIB::mtxrOpticalWavelength'] != '.00' ? Number::cast($data['MIKROTIK-MIB::mtxrOpticalWavelength']) : null;
|
||||
|
||||
return new Transceiver([
|
||||
'port_id' => $ifIndexToPortId->get($ifIndex, 0),
|
||||
'index' => $ifIndex,
|
||||
'vendor' => $data['MIKROTIK-MIB::mtxrOpticalVendorName'] ?? null,
|
||||
'serial' => $data['MIKROTIK-MIB::mtxrOpticalVendorSerial'] ?? null,
|
||||
'wavelength' => $wavelength == 65535 ? null : $wavelength, // NA value = 65535.00
|
||||
'entity_physical_index' => $ifIndex,
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -31,6 +31,7 @@ use App\Models\EntPhysical;
|
||||
use App\Models\Mempool;
|
||||
use App\Models\PortsNac;
|
||||
use App\Models\Sla;
|
||||
use App\Models\Transceiver;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
@@ -40,6 +41,7 @@ use LibreNMS\Interfaces\Discovery\OSDiscovery;
|
||||
use LibreNMS\Interfaces\Discovery\ProcessorDiscovery;
|
||||
use LibreNMS\Interfaces\Discovery\SlaDiscovery;
|
||||
use LibreNMS\Interfaces\Discovery\StpInstanceDiscovery;
|
||||
use LibreNMS\Interfaces\Discovery\TransceiverDiscovery;
|
||||
use LibreNMS\Interfaces\Polling\NacPolling;
|
||||
use LibreNMS\Interfaces\Polling\SlaPolling;
|
||||
use LibreNMS\OS;
|
||||
@@ -54,7 +56,8 @@ class Cisco extends OS implements
|
||||
ProcessorDiscovery,
|
||||
MempoolsDiscovery,
|
||||
NacPolling,
|
||||
SlaPolling
|
||||
SlaPolling,
|
||||
TransceiverDiscovery
|
||||
{
|
||||
use YamlOSDiscovery {
|
||||
YamlOSDiscovery::discoverOS as discoverYamlOS;
|
||||
@@ -622,4 +625,52 @@ class Cisco extends OS implements
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function discoverTransceivers(): Collection
|
||||
{
|
||||
// use data collected by entPhysical module if available
|
||||
$dbSfpCages = $this->getDevice()->entityPhysical()->where('entPhysicalVendorType', 'cevContainerSFP')->pluck('ifIndex', 'entPhysicalIndex');
|
||||
if ($dbSfpCages->isNotEmpty()) {
|
||||
$data = $this->getDevice()->entityPhysical()->whereIn('entPhysicalContainedIn', $dbSfpCages->keys())->get()->map(function ($ent) use ($dbSfpCages) {
|
||||
if (empty($ent->ifIndex) && $dbSfpCages->has($ent->entPhysicalContainedIn)) {
|
||||
$ent->ifIndex = $dbSfpCages->get($ent->entPhysicalContainedIn);
|
||||
}
|
||||
|
||||
return $ent;
|
||||
})->keyBy('entPhysicalIndex');
|
||||
} else {
|
||||
// fetch data via snmp
|
||||
$snmpData = \SnmpQuery::cache()->hideMib()->mibs(['CISCO-ENTITY-VENDORTYPE-OID-MIB'])->walk('ENTITY-MIB::entPhysicalTable')->table(1);
|
||||
if (empty($snmpData)) {
|
||||
return new Collection;
|
||||
}
|
||||
|
||||
$snmpData = collect(\SnmpQuery::hideMib()->mibs(['IF-MIB'])->walk('ENTITY-MIB::entAliasMappingIdentifier')->table(1, $snmpData));
|
||||
|
||||
$sfpCages = $snmpData->filter(fn ($ent) => isset($ent['entPhysicalVendorType']) && $ent['entPhysicalVendorType'] == 'cevContainerSFP');
|
||||
$data = $snmpData->filter(fn ($ent) => $sfpCages->has($ent['entPhysicalContainedIn'] ?? null))->map(function ($e) {
|
||||
if (isset($e['entAliasMappingIdentifier'][0])) {
|
||||
$e['ifIndex'] = preg_replace('/^.*ifIndex[.[](\d+).*$/', '$1', $e['entAliasMappingIdentifier'][0]);
|
||||
}
|
||||
|
||||
return $e;
|
||||
});
|
||||
}
|
||||
$ifIndexToPortId = $this->getDevice()->ports()->pluck('port_id', 'ifIndex');
|
||||
|
||||
return $data->map(function ($ent, $index) use ($ifIndexToPortId) {
|
||||
$ifIndex = $ent['ifIndex'] ?? null;
|
||||
|
||||
return new Transceiver([
|
||||
'port_id' => $ifIndexToPortId->get($ifIndex, 0),
|
||||
'index' => $index,
|
||||
'type' => $ent['entPhysicalDescr'] ?? null,
|
||||
'vendor' => $ent['entPhysicalMfgName'] ?? null,
|
||||
'revision' => $ent['entPhysicalHardwareRev'] ?? null,
|
||||
'model' => $ent['entPhysicalModelName'] ?? null,
|
||||
'serial' => $ent['entPhysicalSerialNum'] ?? null,
|
||||
'entity_physical_index' => $ifIndex,
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -47,7 +47,8 @@ trait EntityMib
|
||||
return $data->mapTable(function ($data, $entityPhysicalIndex) use ($entPhysicalToIfIndexMap) {
|
||||
$entityPhysical = new EntPhysical($data);
|
||||
$entityPhysical->entPhysicalIndex = $entityPhysicalIndex;
|
||||
$entityPhysical->ifIndex = $entPhysicalToIfIndexMap[$entityPhysicalIndex] ?? null;
|
||||
// get ifIndex. also if parent has an ifIndex, set it too
|
||||
$entityPhysical->ifIndex = $entPhysicalToIfIndexMap[$entityPhysicalIndex] ?? $entPhysicalToIfIndexMap[$entityPhysical->entPhysicalContainedIn] ?? null;
|
||||
|
||||
return $entityPhysical;
|
||||
});
|
||||
|
@@ -22,6 +22,7 @@
|
||||
|
||||
namespace LibreNMS\Util;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use LibreNMS\Exceptions\UserFunctionExistException;
|
||||
|
||||
class UserFuncHelper
|
||||
@@ -42,4 +43,11 @@ class UserFuncHelper
|
||||
{
|
||||
return \LibreNMS\Util\Time::dateToDays($this->value_raw);
|
||||
}
|
||||
|
||||
public function fsParseChannelValue(): float
|
||||
{
|
||||
$channel = Str::afterLast($this->sensor['sensor_index'], '.');
|
||||
|
||||
return Number::cast(explode(',', $this->value_raw)[$channel] ?? '');
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user