mirror of
https://github.com/librenms/librenms.git
synced 2024-10-07 16:52:45 +00:00
* AuthSSOTest: clear roles cache * PollingJob: When a poller module doesn't exist, return null instead of false. Skip all other checks and disable polling in that case. * Sensors: Guess high and low limits separately as needed * Sensors: drac test psu current data was wrong, referencing the snmprec, 8 / 10 and 0 / 10 should be the values NOTE: drac is messed up and runs a billion snmp queriess for no reason * please phpstan * Sensors: limits reference old code move to "creating" * Fix gw-eydfa accidental assignment * Fix ies5000 test data now that the bad state is removed * Fix ies5000 test data part 2 * Move sensor discovery reset into discover_device() * infinera remove duplicate sensor (also a lot of trailing whitespace apparently) * Fix innovaphone bad yaml discovery * module tests should be using null when test data doesn't exist, not an empty array * When discovery or polling is not supported, dump null instead of an array. Account for nulls in testing * update ISE serial * Janitza was seemingly wrong before * Remove some private data * bgp-peers requires ipv4-addresses and ipv6-addresses for bgpPeerIface * fix polycomLens broken state sensor discovery * Raritan pdu and pdu2 test data was combined in one test file, split it out * scs-ks duplicate temperature sensor indexes * sentry3 someone tried to avoid breaking stuff but just broke things more * smartos-dcp-m fix incorrect numeric oids * ssu2000 apparently test data was wrong, must have fixed a bug in the code. * timos remove duplicate dbm sensor definitions * bgpPeerIface is working in tests now * Fix moxa-etherdevice when mibs are a bit different * xw_to_dbm negative values should return null * Update cisco test data due previous fixes/changes * One more bgpPeerIface * Add orderBy to ospf module db dumps * Remove links test data for now * Improve handling of bad data in ipv6-addresses module
335 lines
11 KiB
PHP
335 lines
11 KiB
PHP
<?php
|
|
/**
|
|
* PrinterSupplies.php
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
namespace LibreNMS\Modules;
|
|
|
|
use App\Models\Device;
|
|
use App\Models\Eventlog;
|
|
use App\Models\PrinterSupply;
|
|
use App\Observers\ModuleModelObserver;
|
|
use Illuminate\Support\Collection;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Str;
|
|
use LibreNMS\DB\SyncsModels;
|
|
use LibreNMS\Enum\Severity;
|
|
use LibreNMS\Interfaces\Data\DataStorageInterface;
|
|
use LibreNMS\Interfaces\Module;
|
|
use LibreNMS\OS;
|
|
use LibreNMS\Polling\ModuleStatus;
|
|
use LibreNMS\RRD\RrdDefinition;
|
|
use LibreNMS\Util\Number;
|
|
|
|
class PrinterSupplies implements Module
|
|
{
|
|
use SyncsModels;
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function dependencies(): array
|
|
{
|
|
return [];
|
|
}
|
|
|
|
public function shouldDiscover(OS $os, ModuleStatus $status): bool
|
|
{
|
|
return $status->isEnabledAndDeviceUp($os->getDevice());
|
|
}
|
|
|
|
/**
|
|
* Discover this module. Heavier processes can be run here
|
|
* Run infrequently (default 4 times a day)
|
|
*
|
|
* @param \LibreNMS\OS $os
|
|
*/
|
|
public function discover(OS $os): void
|
|
{
|
|
$device = $os->getDeviceArray();
|
|
|
|
$data = collect()
|
|
->concat($this->discoveryLevels($device))
|
|
->concat($this->discoveryPapers($device));
|
|
|
|
ModuleModelObserver::observe(PrinterSupply::class);
|
|
$this->syncModels($os->getDevice(), 'printerSupplies', $data);
|
|
}
|
|
|
|
public function shouldPoll(OS $os, ModuleStatus $status): bool
|
|
{
|
|
return $status->isEnabledAndDeviceUp($os->getDevice());
|
|
}
|
|
|
|
/**
|
|
* Poll data for this module and update the DB / RRD.
|
|
* Try to keep this efficient and only run if discovery has indicated there is a reason to run.
|
|
* Run frequently (default every 5 minutes)
|
|
*
|
|
* @param \LibreNMS\OS $os
|
|
*/
|
|
public function poll(OS $os, DataStorageInterface $datastore): void
|
|
{
|
|
$device = $os->getDeviceArray();
|
|
$toner_data = $os->getDevice()->printerSupplies;
|
|
|
|
if (empty($toner_data)) {
|
|
return; // no data to poll
|
|
}
|
|
|
|
$toner_snmp = snmp_get_multi_oid($device, $toner_data->pluck('supply_oid')->toArray());
|
|
|
|
foreach ($toner_data as $toner) {
|
|
$raw_toner = $toner_snmp[$toner['supply_oid']] ?? null;
|
|
$tonerperc = self::getTonerLevel($device, $raw_toner, $toner['supply_capacity'] ?? null);
|
|
Log::info('Checking toner ' . $toner['supply_descr'] . "... $tonerperc %");
|
|
|
|
$tags = [
|
|
'rrd_def' => RrdDefinition::make()->addDataset('toner', 'GAUGE', 0, 20000),
|
|
'rrd_name' => ['toner', $toner['supply_type'], $toner['supply_index']],
|
|
'rrd_oldname' => ['toner', $toner['supply_descr']],
|
|
'index' => $toner['supply_index'],
|
|
];
|
|
$datastore->put($device, 'toner', $tags, $tonerperc);
|
|
|
|
// Log empty supplies (but only once)
|
|
if ($tonerperc == 0 && $toner['supply_current'] > 0) {
|
|
Eventlog::log(
|
|
'Toner ' . $toner['supply_descr'] . ' is empty',
|
|
$os->getDevice(),
|
|
'toner',
|
|
Severity::Error,
|
|
$toner['supply_id']
|
|
);
|
|
}
|
|
|
|
// Log toner swap
|
|
if ($tonerperc > $toner['supply_current']) {
|
|
Eventlog::log(
|
|
'Toner ' . $toner['supply_descr'] . ' was replaced (new level: ' . $tonerperc . '%)',
|
|
$os->getDevice(),
|
|
'toner',
|
|
Severity::Notice,
|
|
$toner['supply_id']
|
|
);
|
|
}
|
|
|
|
$toner->supply_current = $tonerperc;
|
|
$toner->supply_capacity = $toner['supply_capacity'];
|
|
$toner->save();
|
|
}
|
|
}
|
|
|
|
public function dataExists(Device $device): bool
|
|
{
|
|
return $device->printerSupplies()->exists();
|
|
}
|
|
|
|
/**
|
|
* Remove all DB data for this module.
|
|
* This will be run when the module is disabled.
|
|
*/
|
|
public function cleanup(Device $device): int
|
|
{
|
|
return $device->printerSupplies()->delete();
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function dump(Device $device, string $type): ?array
|
|
{
|
|
return [
|
|
'printer_supplies' => $device->printerSupplies()->orderBy('supply_oid')->orderBy('supply_index')
|
|
->get()->map->makeHidden(['device_id', 'supply_id']),
|
|
];
|
|
}
|
|
|
|
private function discoveryLevels($device): Collection
|
|
{
|
|
$levels = new Collection();
|
|
|
|
$oids = snmpwalk_cache_oid($device, 'prtMarkerSuppliesLevel', [], 'Printer-MIB');
|
|
if (! empty($oids)) {
|
|
$oids = snmpwalk_cache_oid($device, 'prtMarkerSuppliesType', $oids, 'Printer-MIB');
|
|
$oids = snmpwalk_cache_oid($device, 'prtMarkerSuppliesMaxCapacity', $oids, 'Printer-MIB');
|
|
$oids = snmpwalk_cache_oid($device, 'prtMarkerSuppliesDescription', $oids, 'Printer-MIB', null, '-OQUsa');
|
|
}
|
|
|
|
foreach ($oids as $index => $data) {
|
|
$last_index = substr($index, strrpos($index, '.') + 1);
|
|
|
|
$descr = $data['prtMarkerSuppliesDescription'];
|
|
$raw_capacity = $data['prtMarkerSuppliesMaxCapacity'];
|
|
$raw_toner = $data['prtMarkerSuppliesLevel'];
|
|
$supply_oid = ".1.3.6.1.2.1.43.11.1.1.9.$index";
|
|
$capacity_oid = ".1.3.6.1.2.1.43.11.1.1.8.$index";
|
|
|
|
// work around weird HP bug where descriptions are on two lines and the second line is hex
|
|
if (Str::contains($descr, "\n")) {
|
|
$new_descr = '';
|
|
foreach (explode("\n", $descr) as $line) {
|
|
if (preg_match('/^([A-F\d]{2} )*[A-F\d]{1,2} ?$/', $line)) {
|
|
$line = snmp_hexstring($line);
|
|
}
|
|
$new_descr .= $line;
|
|
}
|
|
$descr = trim($new_descr);
|
|
}
|
|
|
|
// Ricoh - TONERCurLevel
|
|
if (empty($raw_toner)) {
|
|
$supply_oid = ".1.3.6.1.4.1.367.3.2.1.2.24.1.1.5.$last_index";
|
|
$raw_toner = snmp_get($device, $supply_oid, '-Oqv');
|
|
}
|
|
|
|
// Ricoh - TONERNameLocal
|
|
if (empty($descr)) {
|
|
$descr_oid = ".1.3.6.1.4.1.367.3.2.1.2.24.1.1.3.$last_index";
|
|
$descr = snmp_get($device, $descr_oid, '-Oqva');
|
|
}
|
|
|
|
// trim part & serial number from devices that include it
|
|
if (Str::contains($descr, ', PN')) {
|
|
$descr = explode(', PN', $descr)[0];
|
|
}
|
|
|
|
$capacity = self::getTonerCapacity($raw_capacity);
|
|
$current = self::getTonerLevel($device, $raw_toner, $capacity);
|
|
|
|
if (is_numeric($current)) {
|
|
$levels->push(new PrinterSupply([
|
|
'device_id' => $device['device_id'],
|
|
'supply_oid' => $supply_oid,
|
|
'supply_capacity_oid' => $capacity_oid,
|
|
'supply_index' => $last_index,
|
|
'supply_type' => $data['prtMarkerSuppliesType'] ?? 'markerSupply',
|
|
'supply_descr' => $descr,
|
|
'supply_capacity' => $capacity,
|
|
'supply_current' => $current,
|
|
]));
|
|
}
|
|
}
|
|
|
|
return $levels;
|
|
}
|
|
|
|
private function discoveryPapers($device): Collection
|
|
{
|
|
Log::info('Tray Paper Level: ');
|
|
$papers = new Collection();
|
|
|
|
$tray_oids = snmpwalk_cache_oid($device, 'prtInputName', [], 'Printer-MIB');
|
|
if (! empty($tray_oids)) {
|
|
$tray_oids = snmpwalk_cache_oid($device, 'prtInputCurrentLevel', $tray_oids, 'Printer-MIB');
|
|
$tray_oids = snmpwalk_cache_oid($device, 'prtInputMaxCapacity', $tray_oids, 'Printer-MIB');
|
|
}
|
|
|
|
foreach ($tray_oids as $index => $data) {
|
|
$last_index = substr($index, strrpos($index, '.') + 1);
|
|
|
|
$capacity = $data['prtInputMaxCapacity'];
|
|
$current = $data['prtInputCurrentLevel'];
|
|
|
|
if (! is_numeric($current) || $current == -2) {
|
|
// capacity unsupported
|
|
d_echo('Input Capacity unsupported', 'X');
|
|
continue;
|
|
} elseif ($current == -3) {
|
|
// at least one piece of paper in tray
|
|
$current = 50;
|
|
} else {
|
|
$current = Number::calculatePercent($current, $capacity);
|
|
}
|
|
|
|
$papers->push(new PrinterSupply([
|
|
'device_id' => $device['device_id'],
|
|
'supply_oid' => ".1.3.6.1.2.1.43.8.2.1.10.$index",
|
|
'supply_capacity_oid' => ".1.3.6.1.2.1.43.8.2.1.9.$index",
|
|
'supply_index' => $last_index,
|
|
'supply_type' => 'input',
|
|
'supply_descr' => $data['prtInputName'],
|
|
'supply_capacity' => $capacity,
|
|
'supply_current' => $current,
|
|
]));
|
|
}
|
|
|
|
return $papers;
|
|
}
|
|
|
|
/**
|
|
* @param array $device
|
|
* @param int|string $raw_value The value returned from snmp
|
|
* @param int $capacity the normalized capacity
|
|
* @return int|float|bool the toner level as a percentage
|
|
*/
|
|
private static function getTonerLevel($device, $raw_value, $capacity)
|
|
{
|
|
// -3 means some toner is left
|
|
if ($raw_value == '-3') {
|
|
return 50;
|
|
}
|
|
|
|
// -2 means unknown
|
|
if ($raw_value == '-2') {
|
|
return false;
|
|
}
|
|
|
|
// -1 mean no restrictions
|
|
if ($raw_value == '-1') {
|
|
return 0; // FIXME: is 0 what we should return?
|
|
}
|
|
|
|
// Non-standard snmp values
|
|
if ($device['os'] == 'ricoh' || $device['os'] == 'nrg' || $device['os'] == 'lanier') {
|
|
if ($raw_value == '-100') {
|
|
return 0;
|
|
}
|
|
} elseif ($device['os'] == 'brother') {
|
|
if (! Str::contains($device['hardware'] ?? '', 'MFC-L8850')) {
|
|
switch ($raw_value) {
|
|
case '0':
|
|
return 100;
|
|
case '1':
|
|
return 5;
|
|
case '2':
|
|
return 0;
|
|
case '3':
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Number::calculatePercent($raw_value, $capacity, 0);
|
|
}
|
|
|
|
/**
|
|
* @param int $raw_capacity The value return from snmp
|
|
* @return int normalized capacity value
|
|
*/
|
|
private static function getTonerCapacity($raw_capacity)
|
|
{
|
|
// unknown or unrestricted capacity, assume 100
|
|
if (empty($raw_capacity) || $raw_capacity < 0) {
|
|
return 100;
|
|
}
|
|
|
|
return $raw_capacity;
|
|
}
|
|
}
|