mirror of
https://github.com/librenms/librenms.git
synced 2024-09-21 02:18:39 +00:00
Poller rewrite (Try 2) (#13525)
* core WIP * try to finish up * trim space too and a couple of cleanups * update test data * put escapes back * another net-snmp difference * correct copy paste error * WIP * Use new code YAY * a tiny bit more * Kind of working * Handle manual modules correctly * convert core to modern module * Only save metrics if modules is not overridden * correct module exists check * database error handling * debug handling * restore bad changes * Introduce Actions RunAlertRulesAction UpdateDeviceGroupsAction * tweaks to output * Fix some issues in outside code * Style fixes * fixes to module status checks * typehints! * Use logger only and DI * OS module not named correctly * Work on quiet output a bit more * generically don't change output when disabling debug if the driver is already stack * Fix missing $device variable for legacy os polling Fix missing dbFacile functions when no legacy modules polled in RunAlertRulesAction * restore legacy os module shim * use the new poller code for tests * PollingDevice event * Fix some issues and enable/disable error reporting around legacy modules * typehints * fully update baseline * Use Process for version commands so we don't leak debug output. * don't detect rrdtool version in ci every time * style fixes * Warning fixes * more fixes * re-update baseline * remove diff noise * fix up alerts * Catch exceptions in device ip lookup * Revert accidental snmp.inc.php poller target change (should have been ?: not ??)
This commit is contained in:
parent
7893b8bebe
commit
c79b187d72
@ -33,6 +33,7 @@ namespace LibreNMS\Alert;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use LibreNMS\Enum\AlertState;
|
||||
use Log;
|
||||
|
||||
class AlertRules
|
||||
{
|
||||
@ -57,7 +58,7 @@ class AlertRules
|
||||
}
|
||||
//Checks each rule.
|
||||
foreach (AlertUtil::getRules($device_id) as $rule) {
|
||||
c_echo('Rule %p#' . $rule['id'] . ' (' . $rule['name'] . '):%n ');
|
||||
Log::info('Rule %p#' . $rule['id'] . ' (' . $rule['name'] . '):%n ', ['color' => true]);
|
||||
$extra = json_decode($rule['extra'], true);
|
||||
if (isset($extra['invert'])) {
|
||||
$inv = (bool) $extra['invert'];
|
||||
@ -90,9 +91,9 @@ class AlertRules
|
||||
$current_state = dbFetchCell('SELECT state FROM alerts WHERE rule_id = ? AND device_id = ? ORDER BY id DESC LIMIT 1', [$rule['id'], $device_id]);
|
||||
if ($doalert) {
|
||||
if ($current_state == AlertState::ACKNOWLEDGED) {
|
||||
c_echo('Status: %ySKIP');
|
||||
Log::info('Status: %ySKIP%n', ['color' => true]);
|
||||
} elseif ($current_state >= AlertState::ACTIVE) {
|
||||
c_echo('Status: %bNOCHG');
|
||||
Log::info('Status: %bNOCHG%n', ['color' => true]);
|
||||
// NOCHG here doesn't mean no change full stop. It means no change to the alert state
|
||||
// So we update the details column with any fresh changes to the alert output we might have.
|
||||
$alert_log = dbFetchRow('SELECT alert_log.id, alert_log.details FROM alert_log,alert_rules WHERE alert_log.rule_id = alert_rules.id && alert_log.device_id = ? && alert_log.rule_id = ? && alert_rules.disabled = 0
|
||||
@ -113,12 +114,12 @@ class AlertRules
|
||||
} else {
|
||||
dbUpdate(['state' => AlertState::ACTIVE, 'open' => 1, 'timestamp' => Carbon::now()], 'alerts', 'device_id = ? && rule_id = ?', [$device_id, $rule['id']]);
|
||||
}
|
||||
c_echo(PHP_EOL . 'Status: %rALERT');
|
||||
Log::info(PHP_EOL . 'Status: %rALERT%n', ['color' => true]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (! is_null($current_state) && $current_state == AlertState::RECOVERED) {
|
||||
c_echo('Status: %bNOCHG');
|
||||
Log::info('Status: %bNOCHG%n', ['color' => true]);
|
||||
} else {
|
||||
if (dbInsert(['state' => AlertState::RECOVERED, 'device_id' => $device_id, 'rule_id' => $rule['id']], 'alert_log')) {
|
||||
if (is_null($current_state)) {
|
||||
@ -127,11 +128,10 @@ class AlertRules
|
||||
dbUpdate(['state' => AlertState::RECOVERED, 'open' => 1, 'note' => '', 'timestamp' => Carbon::now()], 'alerts', 'device_id = ? && rule_id = ?', [$device_id, $rule['id']]);
|
||||
}
|
||||
|
||||
c_echo(PHP_EOL . 'Status: %gOK');
|
||||
Log::info(PHP_EOL . 'Status: %gOK%n', ['color' => true]);
|
||||
}
|
||||
}
|
||||
}
|
||||
c_echo('%n' . PHP_EOL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,14 @@ class Device
|
||||
$this->primary = $device_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a primary device is set
|
||||
*/
|
||||
public function hasPrimary(): bool
|
||||
{
|
||||
return $this->primary !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a device by device_id
|
||||
*
|
||||
|
@ -168,7 +168,7 @@ class Rrd extends BaseDatastore
|
||||
$fields = array_filter($fields, function ($key) use ($rrd_def) {
|
||||
$valid = $rrd_def->isValidDataset($key);
|
||||
if (! $valid) {
|
||||
Log::warning("RRD warning: unused data sent $key");
|
||||
Log::debug("RRD warning: unused data sent $key");
|
||||
}
|
||||
|
||||
return $valid;
|
||||
|
30
LibreNMS/Exceptions/PollerException.php
Normal file
30
LibreNMS/Exceptions/PollerException.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/*
|
||||
* PollerException.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\Exceptions;
|
||||
|
||||
class PollerException extends \Exception
|
||||
{
|
||||
}
|
@ -33,7 +33,7 @@ interface Module
|
||||
* Discover this module. Heavier processes can be run here
|
||||
* Run infrequently (default 4 times a day)
|
||||
*
|
||||
* @param OS $os
|
||||
* @param \LibreNMS\OS $os
|
||||
*/
|
||||
public function discover(OS $os);
|
||||
|
||||
@ -42,7 +42,7 @@ interface Module
|
||||
* 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 OS $os
|
||||
* @param \LibreNMS\OS $os
|
||||
*/
|
||||
public function poll(OS $os);
|
||||
|
||||
@ -50,7 +50,7 @@ interface Module
|
||||
* Remove all DB data for this module.
|
||||
* This will be run when the module is disabled.
|
||||
*
|
||||
* @param OS $os
|
||||
* @param \LibreNMS\OS $os
|
||||
*/
|
||||
public function cleanup(OS $os);
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ class Isis implements Module
|
||||
* Discover this module. Heavier processes can be run here
|
||||
* Run infrequently (default 4 times a day)
|
||||
*
|
||||
* @param OS $os
|
||||
* @param \LibreNMS\OS $os
|
||||
*/
|
||||
public function discover(OS $os)
|
||||
{
|
||||
@ -68,7 +68,7 @@ class Isis implements Module
|
||||
* 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 OS $os
|
||||
* @param \LibreNMS\OS $os
|
||||
*/
|
||||
public function poll(OS $os)
|
||||
{
|
||||
@ -89,7 +89,7 @@ class Isis implements Module
|
||||
* Remove all DB data for this module.
|
||||
* This will be run when the module is disabled.
|
||||
*
|
||||
* @param OS $os
|
||||
* @param Os $os
|
||||
*/
|
||||
public function cleanup(OS $os)
|
||||
{
|
||||
|
65
LibreNMS/Modules/LegacyModule.php
Normal file
65
LibreNMS/Modules/LegacyModule.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* LegacyModule.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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @link https://www.librenms.org
|
||||
*
|
||||
* @copyright 2021 Tony Murray
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
*/
|
||||
|
||||
namespace LibreNMS\Modules;
|
||||
|
||||
use LibreNMS\Interfaces\Module;
|
||||
use LibreNMS\OS;
|
||||
use LibreNMS\Util\Debug;
|
||||
|
||||
class LegacyModule implements Module
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
public function __construct(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function discover(OS $os): void
|
||||
{
|
||||
// TODO: Implement discover() method.
|
||||
}
|
||||
|
||||
public function poll(OS $os): void
|
||||
{
|
||||
$device = &$os->getDeviceArray();
|
||||
$device['attribs'] = $os->getDevice()->attribs->toArray();
|
||||
Debug::disableErrorReporting(); // ignore errors in legacy code
|
||||
|
||||
include_once base_path('includes/dbFacile.php');
|
||||
include base_path("includes/polling/$this->name.inc.php");
|
||||
|
||||
Debug::enableErrorReporting(); // and back to normal
|
||||
}
|
||||
|
||||
public function cleanup(OS $os): void
|
||||
{
|
||||
// TODO: Implement cleanup() method.
|
||||
}
|
||||
}
|
@ -108,7 +108,7 @@ class Mempools implements Module
|
||||
}
|
||||
|
||||
/**
|
||||
* @param OS $os
|
||||
* @param \LibreNMS\OS $os
|
||||
* @param \Illuminate\Support\Collection $mempools
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
|
@ -42,7 +42,7 @@ class Mpls implements Module
|
||||
* Discover this module. Heavier processes can be run here
|
||||
* Run infrequently (default 4 times a day)
|
||||
*
|
||||
* @param OS $os
|
||||
* @param \LibreNMS\OS $os
|
||||
*/
|
||||
public function discover(OS $os)
|
||||
{
|
||||
@ -88,7 +88,7 @@ class Mpls implements Module
|
||||
* 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 OS $os
|
||||
* @param \LibreNMS\OS $os
|
||||
*/
|
||||
public function poll(OS $os)
|
||||
{
|
||||
@ -151,7 +151,7 @@ class Mpls implements Module
|
||||
* Remove all DB data for this module.
|
||||
* This will be run when the module is disabled.
|
||||
*
|
||||
* @param OS $os
|
||||
* @param Os $os
|
||||
*/
|
||||
public function cleanup(OS $os)
|
||||
{
|
||||
|
@ -37,7 +37,7 @@ class Nac implements Module
|
||||
* Discover this module. Heavier processes can be run here
|
||||
* Run infrequently (default 4 times a day)
|
||||
*
|
||||
* @param OS $os
|
||||
* @param Os $os
|
||||
*/
|
||||
public function discover(OS $os)
|
||||
{
|
||||
@ -49,7 +49,7 @@ class Nac implements Module
|
||||
* 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 OS $os
|
||||
* @param \LibreNMS\OS $os
|
||||
*/
|
||||
public function poll(OS $os)
|
||||
{
|
||||
@ -82,7 +82,7 @@ class Nac implements Module
|
||||
* Remove all DB data for this module.
|
||||
* This will be run when the module is disabled.
|
||||
*
|
||||
* @param OS $os
|
||||
* @param \LibreNMS\OS $os
|
||||
*/
|
||||
public function cleanup(OS $os)
|
||||
{
|
||||
|
@ -30,9 +30,9 @@ use LibreNMS\Interfaces\Module;
|
||||
use LibreNMS\Interfaces\Polling\OSPolling;
|
||||
use LibreNMS\Util\Url;
|
||||
|
||||
class OS implements Module
|
||||
class Os implements Module
|
||||
{
|
||||
public function discover(\LibreNMS\OS $os)
|
||||
public function discover(\LibreNMS\OS $os): void
|
||||
{
|
||||
$this->updateLocation($os);
|
||||
$this->sysContact($os);
|
||||
@ -50,7 +50,7 @@ class OS implements Module
|
||||
$this->handleChanges($os);
|
||||
}
|
||||
|
||||
public function poll(\LibreNMS\OS $os)
|
||||
public function poll(\LibreNMS\OS $os): void
|
||||
{
|
||||
$deviceModel = $os->getDevice(); /** @var \App\Models\Device $deviceModel */
|
||||
if ($os instanceof OSPolling) {
|
||||
@ -58,6 +58,11 @@ class OS implements Module
|
||||
} else {
|
||||
// legacy poller files
|
||||
global $graphs, $device;
|
||||
|
||||
if (empty($device)) {
|
||||
$device = $os->getDeviceArray();
|
||||
}
|
||||
|
||||
$location = null;
|
||||
|
||||
if (is_file(base_path('/includes/polling/os/' . $device['os'] . '.inc.php'))) {
|
||||
@ -85,12 +90,12 @@ class OS implements Module
|
||||
$this->handleChanges($os);
|
||||
}
|
||||
|
||||
public function cleanup(\LibreNMS\OS $os)
|
||||
public function cleanup(\LibreNMS\OS $os): void
|
||||
{
|
||||
// no cleanup needed?
|
||||
}
|
||||
|
||||
private function handleChanges(\LibreNMS\OS $os)
|
||||
private function handleChanges(\LibreNMS\OS $os): void
|
||||
{
|
||||
$device = $os->getDevice();
|
||||
|
||||
@ -104,7 +109,7 @@ class OS implements Module
|
||||
$device->save();
|
||||
}
|
||||
|
||||
private function updateLocation(\LibreNMS\OS $os)
|
||||
private function updateLocation(\LibreNMS\OS $os): void
|
||||
{
|
||||
$device = $os->getDevice();
|
||||
$new_location = $device->override_sysLocation ? new Location() : $os->fetchLocation(); // fetch location data from device
|
||||
@ -112,7 +117,7 @@ class OS implements Module
|
||||
optional($device->location)->save();
|
||||
}
|
||||
|
||||
private function sysContact(\LibreNMS\OS $os)
|
||||
private function sysContact(\LibreNMS\OS $os): void
|
||||
{
|
||||
$device = $os->getDevice();
|
||||
$device->sysContact = snmp_get($os->getDeviceArray(), 'sysContact.0', '-Ovq', 'SNMPv2-MIB');
|
@ -39,7 +39,7 @@ class PrinterSupplies implements Module
|
||||
* Discover this module. Heavier processes can be run here
|
||||
* Run infrequently (default 4 times a day)
|
||||
*
|
||||
* @param OS $os
|
||||
* @param \LibreNMS\OS $os
|
||||
*/
|
||||
public function discover(OS $os)
|
||||
{
|
||||
@ -58,7 +58,7 @@ class PrinterSupplies implements Module
|
||||
* 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 OS $os
|
||||
* @param \LibreNMS\OS $os
|
||||
*/
|
||||
public function poll(OS $os)
|
||||
{
|
||||
@ -114,7 +114,7 @@ class PrinterSupplies implements Module
|
||||
* Remove all DB data for this module.
|
||||
* This will be run when the module is disabled.
|
||||
*
|
||||
* @param OS $os
|
||||
* @param Os $os
|
||||
*/
|
||||
public function cleanup(OS $os)
|
||||
{
|
||||
|
@ -36,7 +36,7 @@ class Slas implements Module
|
||||
* Discover this module. Heavier processes can be run here
|
||||
* Run infrequently (default 4 times a day)
|
||||
*
|
||||
* @param OS $os
|
||||
* @param \LibreNMS\OS $os
|
||||
*/
|
||||
public function discover(OS $os)
|
||||
{
|
||||
@ -52,7 +52,7 @@ class Slas implements Module
|
||||
* 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 OS $os
|
||||
* @param \LibreNMS\OS $os
|
||||
*/
|
||||
public function poll(OS $os)
|
||||
{
|
||||
@ -73,7 +73,7 @@ class Slas implements Module
|
||||
* Remove all DB data for this module.
|
||||
* This will be run when the module is disabled.
|
||||
*
|
||||
* @param OS $os
|
||||
* @param \LibreNMS\OS $os
|
||||
*/
|
||||
public function cleanup(OS $os)
|
||||
{
|
||||
|
@ -127,7 +127,7 @@ class OS implements
|
||||
$this->graphs[$name] = true;
|
||||
}
|
||||
|
||||
public function persistGraphs()
|
||||
public function persistGraphs(): void
|
||||
{
|
||||
$device = $this->getDevice();
|
||||
$graphs = collect(array_keys($this->graphs));
|
||||
|
374
LibreNMS/Poller.php
Normal file
374
LibreNMS/Poller.php
Normal file
@ -0,0 +1,374 @@
|
||||
<?php
|
||||
/**
|
||||
* Poller.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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @link https://www.librenms.org
|
||||
*
|
||||
* @copyright 2021 Tony Murray
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
*/
|
||||
|
||||
namespace LibreNMS;
|
||||
|
||||
use App\Events\DevicePolled;
|
||||
use App\Events\PollingDevice;
|
||||
use App\Models\Device;
|
||||
use App\Polling\Measure\Measurement;
|
||||
use App\Polling\Measure\MeasurementManager;
|
||||
use Carbon\Carbon;
|
||||
use DB;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Str;
|
||||
use LibreNMS\Exceptions\PollerException;
|
||||
use LibreNMS\Modules\LegacyModule;
|
||||
use LibreNMS\Polling\ConnectivityHelper;
|
||||
use LibreNMS\RRD\RrdDefinition;
|
||||
use LibreNMS\Util\Debug;
|
||||
use LibreNMS\Util\Dns;
|
||||
use LibreNMS\Util\Git;
|
||||
use LibreNMS\Util\StringHelpers;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class Poller
|
||||
{
|
||||
/** @var string */
|
||||
private $device_spec;
|
||||
/** @var array */
|
||||
private $module_override;
|
||||
|
||||
/**
|
||||
* @var Device
|
||||
*/
|
||||
private $device;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $deviceArray;
|
||||
/**
|
||||
* @var \LibreNMS\OS|\LibreNMS\OS\Generic
|
||||
*/
|
||||
private $os;
|
||||
/**
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
public function __construct(string $device_spec, array $module_override, LoggerInterface $logger)
|
||||
{
|
||||
$this->device_spec = $device_spec;
|
||||
$this->module_override = $module_override;
|
||||
$this->logger = $logger;
|
||||
$this->parseModules();
|
||||
}
|
||||
|
||||
public function poll(): int
|
||||
{
|
||||
$polled = 0;
|
||||
$this->printHeader();
|
||||
|
||||
if (Debug::isEnabled()) {
|
||||
\LibreNMS\Util\OS::updateCache(true); // Force update of OS Cache
|
||||
}
|
||||
|
||||
$this->logger->info("Starting polling run:\n");
|
||||
|
||||
foreach ($this->buildDeviceQuery()->pluck('device_id') as $device_id) {
|
||||
$this->initDevice($device_id);
|
||||
PollingDevice::dispatch($this->device);
|
||||
$this->os = OS::make($this->deviceArray);
|
||||
|
||||
$helper = new ConnectivityHelper($this->device);
|
||||
$helper->saveMetrics();
|
||||
|
||||
$measurement = Measurement::start('poll');
|
||||
$measurement->manager()->checkpoint(); // don't count previous stats
|
||||
|
||||
if ($helper->isUp()) {
|
||||
$this->pollModules();
|
||||
}
|
||||
$measurement->end();
|
||||
|
||||
if (empty($this->module_override)) {
|
||||
// record performance
|
||||
$measurement->manager()->record('device', $measurement);
|
||||
$this->device->last_polled = Carbon::now();
|
||||
$this->device->last_ping_timetaken = $measurement->getDuration();
|
||||
app('Datastore')->put($this->deviceArray, 'poller-perf', [
|
||||
'rrd_def' => RrdDefinition::make()->addDataset('poller', 'GAUGE', 0),
|
||||
'module' => 'ALL',
|
||||
], [
|
||||
'poller' => $measurement->getDuration(),
|
||||
]);
|
||||
$this->os->enableGraph('poller_perf');
|
||||
|
||||
if ($helper->canPing()) {
|
||||
$this->os->enableGraph('ping_perf');
|
||||
}
|
||||
|
||||
$this->os->persistGraphs();
|
||||
$this->logger->info(sprintf("Enabled graphs (%s): %s\n\n",
|
||||
$this->device->graphs->count(),
|
||||
$this->device->graphs->pluck('graph')->implode(' ')
|
||||
));
|
||||
}
|
||||
|
||||
$this->device->save();
|
||||
$polled++;
|
||||
|
||||
DevicePolled::dispatch($this->device);
|
||||
|
||||
$this->logger->info(sprintf("\n>>> Polled %s (%s) in %0.3f seconds <<<",
|
||||
$this->device->displayName(),
|
||||
$this->device->device_id,
|
||||
$measurement->getDuration()));
|
||||
|
||||
// check if the poll took too long and log an event
|
||||
if ($measurement->getDuration() > Config::get('rrd.step')) {
|
||||
\Log::event('Polling took longer than ' . round(Config::get('rrd.step') / 60, 2) .
|
||||
' minutes! This will cause gaps in graphs.', $this->device, 'system', 5);
|
||||
}
|
||||
}
|
||||
|
||||
return $polled;
|
||||
}
|
||||
|
||||
private function pollModules(): void
|
||||
{
|
||||
$this->filterModules();
|
||||
|
||||
// update $device array status
|
||||
$this->deviceArray['status'] = $this->device->status;
|
||||
$this->deviceArray['status_reason'] = $this->device->status_reason;
|
||||
|
||||
// import legacy garbage
|
||||
include_once base_path('includes/functions.php');
|
||||
include_once base_path('includes/common.php');
|
||||
include_once base_path('includes/polling/functions.inc.php');
|
||||
include_once base_path('includes/snmp.inc.php');
|
||||
include_once base_path('includes/datastore.inc.php'); // remove me
|
||||
|
||||
foreach (Config::get('poller_modules') as $module => $module_status) {
|
||||
if ($this->isModuleEnabled($module, $module_status)) {
|
||||
$start_memory = memory_get_usage();
|
||||
$module_start = microtime(true);
|
||||
$this->logger->info("\n#### Load poller module $module ####");
|
||||
|
||||
try {
|
||||
$module_class = StringHelpers::toClass($module, '\\LibreNMS\\Modules\\');
|
||||
$instance = class_exists($module_class) ? new $module_class : new LegacyModule($module);
|
||||
$instance->poll($this->os);
|
||||
} catch (Exception $e) {
|
||||
// isolate module exceptions so they don't disrupt the polling process
|
||||
$this->logger->error("Error in $module module. " . $e->getMessage() . PHP_EOL . $e->getTraceAsString() . PHP_EOL);
|
||||
}
|
||||
|
||||
app(MeasurementManager::class)->printChangedStats();
|
||||
$this->saveModulePerformance($module, $module_start, $start_memory);
|
||||
$this->logger->info("#### Unload poller module $module ####\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function saveModulePerformance(string $module, float $start_time, int $start_memory): void
|
||||
{
|
||||
$module_time = microtime(true) - $start_time;
|
||||
$module_mem = (memory_get_usage() - $start_memory);
|
||||
|
||||
$this->logger->info(sprintf(">> Runtime for poller module '%s': %.4f seconds with %s bytes", $module, $module_time, $module_mem));
|
||||
|
||||
app('Datastore')->put($this->deviceArray, 'poller-perf', [
|
||||
'module' => $module,
|
||||
'rrd_def' => RrdDefinition::make()->addDataset('poller', 'GAUGE', 0),
|
||||
'rrd_name' => ['poller-perf', $module],
|
||||
], [
|
||||
'poller' => $module_time,
|
||||
]);
|
||||
$this->os->enableGraph('poller_modules_perf');
|
||||
}
|
||||
|
||||
private function isModuleEnabled(string $module, bool $global_status): bool
|
||||
{
|
||||
if (! empty($this->module_override)) {
|
||||
if (in_array($module, $this->module_override)) {
|
||||
$this->logger->debug("Module $module manually enabled");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$os_module_status = Config::get("os.{$this->device->os}.poller_modules.$module");
|
||||
$device_attrib = $this->device->getAttrib('poll_' . $module);
|
||||
$this->logger->debug(sprintf('Modules status: Global %s OS %s Device %s',
|
||||
$global_status ? '+' : '-',
|
||||
$os_module_status === null ? ' ' : ($os_module_status ? '+' : '-'),
|
||||
$device_attrib === null ? ' ' : ($device_attrib ? '+' : '-')
|
||||
));
|
||||
|
||||
if ($device_attrib
|
||||
|| ($os_module_status && $device_attrib === null)
|
||||
|| ($global_status && $os_module_status === null && $device_attrib === null)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$reason = $device_attrib !== null ? 'by device'
|
||||
: ($os_module_status === null || $os_module_status ? 'globally' : 'by OS');
|
||||
$this->logger->debug("Module [ $module ] disabled $reason");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function moduleExists(string $module): bool
|
||||
{
|
||||
return class_exists(StringHelpers::toClass($module, '\\LibreNMS\\Modules\\'))
|
||||
|| is_file("includes/polling/$module.inc.php");
|
||||
}
|
||||
|
||||
private function buildDeviceQuery(): Builder
|
||||
{
|
||||
$query = Device::query();
|
||||
|
||||
if (empty($this->device_spec)) {
|
||||
throw new PollerException('Invalid device spec');
|
||||
} elseif ($this->device_spec == 'all') {
|
||||
return $query;
|
||||
} elseif ($this->device_spec == 'even') {
|
||||
return $query->where(DB::raw('device_id % 2'), 0);
|
||||
} elseif ($this->device_spec == 'odd') {
|
||||
return $query->where(DB::raw('device_id % 2'), 1);
|
||||
} elseif (is_numeric($this->device_spec)) {
|
||||
return $query->where('device_id', $this->device_spec);
|
||||
} elseif (Str::contains($this->device_spec, '*')) {
|
||||
return $query->where('hostname', 'like', str_replace('*', '%', $this->device_spec));
|
||||
}
|
||||
|
||||
return $query->where('hostname', $this->device_spec);
|
||||
}
|
||||
|
||||
private function initDevice(int $device_id): void
|
||||
{
|
||||
\DeviceCache::setPrimary($device_id);
|
||||
$this->device = \DeviceCache::getPrimary();
|
||||
$this->device->ip = $this->device->overwrite_ip ?: Dns::lookupIp($this->device);
|
||||
|
||||
$this->deviceArray = $this->device->toArray();
|
||||
if ($os_group = Config::get("os.{$this->device->os}.group")) {
|
||||
$this->deviceArray['os_group'] = $os_group;
|
||||
}
|
||||
|
||||
$this->printDeviceInfo($os_group);
|
||||
$this->initRrdDirectory();
|
||||
}
|
||||
|
||||
private function initRrdDirectory(): void
|
||||
{
|
||||
$host_rrd = \Rrd::name($this->device->hostname, '', '');
|
||||
if (Config::get('rrd.enable', true) && ! is_dir($host_rrd)) {
|
||||
mkdir($host_rrd);
|
||||
$this->logger->info("Created directory : $host_rrd");
|
||||
}
|
||||
}
|
||||
|
||||
private function parseModules(): void
|
||||
{
|
||||
foreach ($this->module_override as $index => $module) {
|
||||
// parse submodules (only supported by some modules)
|
||||
if (Str::contains($module, '/')) {
|
||||
[$module, $submodule] = explode('/', $module, 2);
|
||||
$existing_submodules = Config::get("poller_submodules.$module", []);
|
||||
$existing_submodules[] = $submodule;
|
||||
Config::set("poller_submodules.$module", $existing_submodules);
|
||||
}
|
||||
|
||||
if (! $this->moduleExists($module)) {
|
||||
unset($this->module_override[$index]);
|
||||
continue;
|
||||
}
|
||||
|
||||
Config::set("poller_modules.$module", 1);
|
||||
}
|
||||
|
||||
$this->printModules();
|
||||
}
|
||||
|
||||
private function filterModules(): void
|
||||
{
|
||||
if ($this->device->snmp_disable) {
|
||||
// only non-snmp modules
|
||||
Config::set('poller_modules', array_intersect_key(Config::get('poller_modules'), [
|
||||
'availability' => true,
|
||||
'ipmi' => true,
|
||||
'unix-agent' => true,
|
||||
]));
|
||||
} else {
|
||||
// we always want the core module to be included, prepend it
|
||||
Config::set('poller_modules', ['core' => true] + Config::get('poller_modules'));
|
||||
}
|
||||
}
|
||||
|
||||
private function printDeviceInfo(?string $group): void
|
||||
{
|
||||
$this->logger->info(sprintf(<<<'EOH'
|
||||
Hostname: %s %s
|
||||
ID: %s
|
||||
OS: %s
|
||||
IP: %s
|
||||
|
||||
EOH, $this->device->hostname, $group ? " ($group)" : '', $this->device->device_id, $this->device->os, $this->device->ip));
|
||||
}
|
||||
|
||||
private function printModules(): void
|
||||
{
|
||||
$modules = array_map(function ($module) {
|
||||
$submodules = Config::get("poller_submodules.$module");
|
||||
|
||||
return $module . ($submodules ? '(' . implode(',', $submodules) . ')' : '');
|
||||
}, array_keys(Config::get('poller_modules', [])));
|
||||
|
||||
$this->logger->debug('Override poller modules: ' . implode(', ', $modules));
|
||||
}
|
||||
|
||||
private function printHeader(): void
|
||||
{
|
||||
if (Debug::isEnabled() || Debug::isVerbose()) {
|
||||
$version = \LibreNMS\Util\Version::get();
|
||||
$this->logger->info(sprintf(<<<'EOH'
|
||||
===================================
|
||||
Version info:
|
||||
Commit SHA: %s
|
||||
Commit Date: %s
|
||||
DB Schema: %s
|
||||
PHP: %s
|
||||
MySQL: %s
|
||||
RRDTool: %s
|
||||
SNMP: %s
|
||||
==================================
|
||||
EOH,
|
||||
Git::localCommit(),
|
||||
Git::localDate(),
|
||||
vsprintf('%s (%s)', $version->database()),
|
||||
phpversion(),
|
||||
\LibreNMS\DB\Eloquent::isConnected() ? \LibreNMS\DB\Eloquent::version() : '?',
|
||||
$version->rrdtool(),
|
||||
$version->netSnmp()
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
@ -32,7 +32,13 @@ use Log;
|
||||
|
||||
class Debug
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private static $debug = false;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private static $verbose = false;
|
||||
|
||||
/**
|
||||
@ -49,20 +55,12 @@ class Debug
|
||||
restore_error_handler(); // disable Laravel error handler
|
||||
|
||||
if (self::$debug) {
|
||||
ini_set('display_errors', '1');
|
||||
ini_set('display_startup_errors', '1');
|
||||
ini_set('log_errors', '0');
|
||||
error_reporting(E_ALL & ~E_NOTICE);
|
||||
|
||||
self::enableErrorReporting();
|
||||
self::enableCliDebugOutput();
|
||||
self::enableQueryDebug();
|
||||
} else {
|
||||
ini_set('display_errors', '0');
|
||||
ini_set('display_startup_errors', '0');
|
||||
ini_set('log_errors', '1');
|
||||
error_reporting($silence ? 0 : E_ERROR);
|
||||
|
||||
self::disableCliDebugOutput();
|
||||
self::disableErrorReporting($silence);
|
||||
self::disableCliDebugOutput($silence);
|
||||
self::disableQueryDebug();
|
||||
}
|
||||
|
||||
@ -92,7 +90,7 @@ class Debug
|
||||
return self::$verbose;
|
||||
}
|
||||
|
||||
public static function disableQueryDebug()
|
||||
public static function disableQueryDebug(): void
|
||||
{
|
||||
$db = Eloquent::DB();
|
||||
|
||||
@ -102,21 +100,21 @@ class Debug
|
||||
}
|
||||
}
|
||||
|
||||
public static function enableCliDebugOutput()
|
||||
public static function enableCliDebugOutput(): void
|
||||
{
|
||||
if (Laravel::isBooted() && App::runningInConsole()) {
|
||||
Log::setDefaultDriver('console');
|
||||
Log::setDefaultDriver('console_debug');
|
||||
}
|
||||
}
|
||||
|
||||
public static function disableCliDebugOutput()
|
||||
public static function disableCliDebugOutput(bool $silence): void
|
||||
{
|
||||
if (Laravel::isBooted()) {
|
||||
Log::setDefaultDriver('stack');
|
||||
if (Laravel::isBooted() && Log::getDefaultDriver() !== 'stack') {
|
||||
Log::setDefaultDriver(app()->runningInConsole() && ! $silence ? 'console' : 'stack');
|
||||
}
|
||||
}
|
||||
|
||||
public static function enableQueryDebug()
|
||||
public static function enableQueryDebug(): void
|
||||
{
|
||||
static $sql_debug_enabled;
|
||||
$db = Eloquent::DB();
|
||||
@ -141,4 +139,26 @@ class Debug
|
||||
$sql_debug_enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable error reporting, do not use with new code
|
||||
*/
|
||||
public static function disableErrorReporting(bool $silence = false): void
|
||||
{
|
||||
ini_set('display_errors', '0');
|
||||
ini_set('display_startup_errors', '0');
|
||||
ini_set('log_errors', '1');
|
||||
error_reporting($silence ? 0 : E_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable error reporting. Please call after disabling for legacy code
|
||||
*/
|
||||
public static function enableErrorReporting(): void
|
||||
{
|
||||
ini_set('display_errors', '1');
|
||||
ini_set('display_startup_errors', '1');
|
||||
ini_set('log_errors', '0');
|
||||
error_reporting(E_ALL & ~E_NOTICE);
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
namespace LibreNMS\Util;
|
||||
|
||||
use App\Models\Device;
|
||||
use LibreNMS\Interfaces\Geocoder;
|
||||
|
||||
class Dns implements Geocoder
|
||||
@ -36,6 +37,23 @@ class Dns implements Geocoder
|
||||
$this->resolver = new \Net_DNS2_Resolver();
|
||||
}
|
||||
|
||||
public static function lookupIp(Device $device): ?string
|
||||
{
|
||||
if (IP::isValid($device->hostname)) {
|
||||
return $device->hostname;
|
||||
}
|
||||
|
||||
try {
|
||||
if ($device->transport == 'udp6' || $device->transport == 'tcp6') {
|
||||
return dns_get_record($device['hostname'], DNS_AAAA)[0]['ipv6'] ?? null;
|
||||
}
|
||||
|
||||
return dns_get_record($device['hostname'], DNS_A)[0]['ip'] ?? null;
|
||||
} catch (\Exception $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $domain Domain which has to be parsed
|
||||
* @param string $record DNS Record which should be searched
|
||||
|
@ -25,21 +25,32 @@
|
||||
|
||||
namespace LibreNMS\Util;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use LibreNMS\Config;
|
||||
|
||||
class Git
|
||||
{
|
||||
public static function repoPresent()
|
||||
public static function repoPresent(): bool
|
||||
{
|
||||
$install_dir = Config::get('install_dir', realpath(__DIR__ . '/../..'));
|
||||
|
||||
return file_exists("$install_dir/.git");
|
||||
}
|
||||
|
||||
public static function binaryExists()
|
||||
public static function binaryExists(): bool
|
||||
{
|
||||
exec('git > /dev/null 2>&1', $response, $exit_code);
|
||||
|
||||
return $exit_code === 1;
|
||||
}
|
||||
|
||||
public static function localCommit(): string
|
||||
{
|
||||
return rtrim(exec("git show --pretty='%H' -s HEAD"));
|
||||
}
|
||||
|
||||
public static function localDate(): Carbon
|
||||
{
|
||||
return \Date::createFromTimestamp(exec("git show --pretty='%ct' -s HEAD"));
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ use LibreNMS\Config;
|
||||
use LibreNMS\Data\Source\SnmpResponse;
|
||||
use LibreNMS\Exceptions\FileNotFoundException;
|
||||
use LibreNMS\Exceptions\InvalidModuleException;
|
||||
use LibreNMS\Poller;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class ModuleTestHelper
|
||||
@ -79,8 +80,6 @@ class ModuleTestHelper
|
||||
*/
|
||||
public function __construct($modules, $os, $variant = '')
|
||||
{
|
||||
global $influxdb;
|
||||
|
||||
$this->modules = self::resolveModuleDependencies((array) $modules);
|
||||
$this->os = strtolower($os);
|
||||
$this->variant = strtolower($variant);
|
||||
@ -196,8 +195,10 @@ class ModuleTestHelper
|
||||
$save_vdebug = Debug::isVerbose();
|
||||
Debug::set();
|
||||
Debug::setVerbose(false);
|
||||
\Log::setDefaultDriver('console');
|
||||
discover_device($device, $this->parseArgs('discovery'));
|
||||
poll_device($device, $this->parseArgs('poller'));
|
||||
$poller = app(Poller::class, ['device_spec' => $device_id, 'module_override' => $this->modules]);
|
||||
$poller->poll();
|
||||
Debug::set($save_debug);
|
||||
Debug::setVerbose($save_vdebug);
|
||||
$collection_output = ob_get_contents();
|
||||
@ -533,6 +534,7 @@ class ModuleTestHelper
|
||||
{
|
||||
global $device;
|
||||
Config::set('rrd.enable', false); // disable rrd
|
||||
Config::set('rrdtool_version', '1.7.2'); // don't detect rrdtool version, rrdtool is not install on ci
|
||||
|
||||
if (! is_file($this->snmprec_file)) {
|
||||
throw new FileNotFoundException("$this->snmprec_file does not exist!");
|
||||
@ -592,7 +594,7 @@ class ModuleTestHelper
|
||||
|
||||
// Dump the discovered data
|
||||
$data = array_merge_recursive($data, $this->dumpDb($device['device_id'], $discovered_modules, 'discovery'));
|
||||
$device = device_by_id_cache($device_id, true); // refresh the device array
|
||||
DeviceCache::get($device_id)->refresh(); // refresh the device
|
||||
|
||||
// Run the poller
|
||||
if ($this->quiet) {
|
||||
@ -601,7 +603,9 @@ class ModuleTestHelper
|
||||
}
|
||||
ob_start();
|
||||
|
||||
poll_device($device, $this->parseArgs('poller'));
|
||||
\Log::setDefaultDriver('console');
|
||||
$poller = app(Poller::class, ['device_spec' => $device_id, 'module_override' => $this->modules]);
|
||||
$poller->poll();
|
||||
|
||||
$this->poller_output = ob_get_contents();
|
||||
if ($this->quiet) {
|
||||
@ -617,12 +621,12 @@ class ModuleTestHelper
|
||||
$polled_modules = array_keys($this->poller_module_output);
|
||||
|
||||
// Dump polled data
|
||||
$data = array_merge_recursive($data, $this->dumpDb($device['device_id'], $polled_modules, 'poller'));
|
||||
$data = array_merge_recursive($data, $this->dumpDb($device_id, $polled_modules, 'poller'));
|
||||
|
||||
// Remove the test device, we don't need the debug from this
|
||||
if ($device['hostname'] == $snmpsim->getIp()) {
|
||||
Debug::set(false);
|
||||
delete_device($device['device_id']);
|
||||
delete_device($device_id);
|
||||
}
|
||||
|
||||
if (! $no_save) {
|
||||
@ -721,7 +725,7 @@ class ModuleTestHelper
|
||||
// build joins
|
||||
$join = '';
|
||||
$select = ["`$table`.*"];
|
||||
foreach ($info['joins'] ?: [] as $join_info) {
|
||||
foreach ($info['joins'] ?? [] as $join_info) {
|
||||
if (isset($join_info['custom'])) {
|
||||
$join .= ' ' . $join_info['custom'];
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* Text.php
|
||||
* StringHelpers.php
|
||||
*
|
||||
* -Description-
|
||||
*
|
||||
@ -19,7 +19,7 @@
|
||||
*
|
||||
* @link https://www.librenms.org
|
||||
*
|
||||
* @copyright 2018 Tony Murray
|
||||
* @copyright 2021 Tony Murray
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
*/
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
namespace LibreNMS\Util;
|
||||
|
||||
use LibreNMS\Config;
|
||||
use LibreNMS\DB\Eloquent;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
@ -33,6 +34,9 @@ class Version
|
||||
// Update this on release
|
||||
const VERSION = '21.11.0';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $is_git_install = false;
|
||||
|
||||
public function __construct()
|
||||
@ -40,12 +44,12 @@ class Version
|
||||
$this->is_git_install = Git::repoPresent() && Git::binaryExists();
|
||||
}
|
||||
|
||||
public static function get()
|
||||
public static function get(): Version
|
||||
{
|
||||
return new static;
|
||||
}
|
||||
|
||||
public function local()
|
||||
public function local(): string
|
||||
{
|
||||
if ($this->is_git_install && $version = $this->fromGit()) {
|
||||
return $version;
|
||||
@ -54,7 +58,7 @@ class Version
|
||||
return self::VERSION;
|
||||
}
|
||||
|
||||
public function database()
|
||||
public function database(): array
|
||||
{
|
||||
if (Eloquent::isConnected()) {
|
||||
try {
|
||||
@ -72,34 +76,52 @@ class Version
|
||||
return ['last' => 'Not Connected', 'total' => 0];
|
||||
}
|
||||
|
||||
private function fromGit()
|
||||
private function fromGit(): string
|
||||
{
|
||||
return rtrim(shell_exec('git describe --tags 2>/dev/null'));
|
||||
}
|
||||
|
||||
public function gitChangelog()
|
||||
public function gitChangelog(): string
|
||||
{
|
||||
return $this->is_git_install
|
||||
? rtrim(shell_exec('git log -10'))
|
||||
: '';
|
||||
}
|
||||
|
||||
public function gitDate()
|
||||
public function gitDate(): string
|
||||
{
|
||||
return $this->is_git_install
|
||||
? rtrim(shell_exec("git show --pretty='%ct' -s HEAD"))
|
||||
: '';
|
||||
}
|
||||
|
||||
public static function python()
|
||||
public function python(): string
|
||||
{
|
||||
$proc = new Process(['python3', '--version']);
|
||||
$proc->run();
|
||||
|
||||
if ($proc->getExitCode() !== 0) {
|
||||
return null;
|
||||
return '';
|
||||
}
|
||||
|
||||
return explode(' ', rtrim($proc->getOutput()), 2)[1] ?? null;
|
||||
return explode(' ', rtrim($proc->getOutput()), 2)[1] ?? '';
|
||||
}
|
||||
|
||||
public function rrdtool(): string
|
||||
{
|
||||
$process = new Process([Config::get('rrdtool', 'rrdtool'), '--version']);
|
||||
$process->run();
|
||||
preg_match('/^RRDtool ([\w.]+) /', $process->getOutput(), $matches);
|
||||
|
||||
return str_replace('1.7.01.7.0', '1.7.0', $matches[1] ?? '');
|
||||
}
|
||||
|
||||
public function netSnmp(): string
|
||||
{
|
||||
$process = new Process([Config::get('snmpget', 'snmpget'), '-V']);
|
||||
$process->run();
|
||||
preg_match('/[\w.]+$/', $process->getErrorOutput(), $matches);
|
||||
|
||||
return $matches[0] ?? '';
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ class Python extends BaseValidation
|
||||
*/
|
||||
public function validate(Validator $validator)
|
||||
{
|
||||
$version = Version::python();
|
||||
$version = Version::get()->python();
|
||||
|
||||
if (empty($version)) {
|
||||
$validator->fail('python3 not found', 'Install Python 3 for your system.');
|
||||
|
41
app/Action.php
Normal file
41
app/Action.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/*
|
||||
* Action.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 App;
|
||||
|
||||
class Action
|
||||
{
|
||||
/**
|
||||
* Execute an action and return the results
|
||||
*
|
||||
* @param string $action
|
||||
* @param mixed ...$parameters
|
||||
* @return mixed
|
||||
*/
|
||||
public static function execute(string $action, ...$parameters)
|
||||
{
|
||||
return app($action, $parameters)->execute();
|
||||
}
|
||||
}
|
55
app/Actions/Alerts/RunAlertRulesAction.php
Normal file
55
app/Actions/Alerts/RunAlertRulesAction.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* RunAlertRulesAction.php
|
||||
*
|
||||
* Check alert rules for status changes
|
||||
*
|
||||
* 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 http://librenms.org
|
||||
*
|
||||
* @copyright 2021 Tony Murray
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
*/
|
||||
|
||||
namespace App\Actions\Alerts;
|
||||
|
||||
use App\Models\Device;
|
||||
use LibreNMS\Alert\AlertRules;
|
||||
|
||||
class RunAlertRulesAction
|
||||
{
|
||||
/**
|
||||
* @var \LibreNMS\Alert\AlertRules
|
||||
*/
|
||||
private $rules;
|
||||
/**
|
||||
* @var \App\Models\Device
|
||||
*/
|
||||
private $device;
|
||||
|
||||
public function __construct(Device $device, AlertRules $rules)
|
||||
{
|
||||
$this->rules = $rules;
|
||||
$this->device = $device;
|
||||
}
|
||||
|
||||
public function execute(): void
|
||||
{
|
||||
// TODO inline logic
|
||||
include_once base_path('includes/common.php');
|
||||
include_once base_path('includes/dbFacile.php');
|
||||
$this->rules->runRules($this->device->device_id);
|
||||
}
|
||||
}
|
85
app/Actions/Device/UpdateDeviceGroupsAction.php
Normal file
85
app/Actions/Device/UpdateDeviceGroupsAction.php
Normal file
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* UpdateDeviceGroupsAction.php
|
||||
*
|
||||
* Update device group associations by re-checking rules
|
||||
*
|
||||
* 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 http://librenms.org
|
||||
*
|
||||
* @copyright 2021 Tony Murray
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
*/
|
||||
|
||||
namespace App\Actions\Device;
|
||||
|
||||
use App\Models\Device;
|
||||
use App\Models\DeviceGroup;
|
||||
use Log;
|
||||
|
||||
class UpdateDeviceGroupsAction
|
||||
{
|
||||
/**
|
||||
* @var \App\Models\Device
|
||||
*/
|
||||
private $device;
|
||||
|
||||
public function __construct(Device $device)
|
||||
{
|
||||
$this->device = $device;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
*/
|
||||
public function execute(): array
|
||||
{
|
||||
if (! $this->device->exists) {
|
||||
// Device not saved to DB, cowardly refusing
|
||||
return [
|
||||
'attached' => [],
|
||||
'detached' => [],
|
||||
'updated' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$device_group_ids = DeviceGroup::query()
|
||||
->with(['devices' => function ($query) {
|
||||
$query->select('devices.device_id');
|
||||
}])
|
||||
->get()
|
||||
->filter(function (DeviceGroup $device_group) {
|
||||
if ($device_group->type == 'dynamic') {
|
||||
try {
|
||||
return $device_group->getParser()
|
||||
->toQuery()
|
||||
->where('devices.device_id', $this->device->device_id)
|
||||
->exists();
|
||||
} catch (\Illuminate\Database\QueryException $e) {
|
||||
Log::error("Device Group '$device_group->name' generates invalid query: " . $e->getMessage());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// for static, if this device is include, keep it.
|
||||
return $device_group->devices
|
||||
->where('device_id', $this->device->device_id)
|
||||
->isNotEmpty();
|
||||
})->pluck('id');
|
||||
|
||||
return $this->device->groups()->sync($device_group_ids);
|
||||
}
|
||||
}
|
76
app/Console/Commands/DevicePoll.php
Normal file
76
app/Console/Commands/DevicePoll.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Console\LnmsCommand;
|
||||
use App\Polling\Measure\MeasurementManager;
|
||||
use Illuminate\Database\QueryException;
|
||||
use LibreNMS\Config;
|
||||
use LibreNMS\Poller;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
class DevicePoll extends LnmsCommand
|
||||
{
|
||||
protected $name = 'device:poll';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->addArgument('device spec', InputArgument::REQUIRED);
|
||||
$this->addOption('modules', 'm', InputOption::VALUE_REQUIRED);
|
||||
$this->addOption('no-data', 'x', InputOption::VALUE_NONE);
|
||||
}
|
||||
|
||||
public function handle(MeasurementManager $measurements): int
|
||||
{
|
||||
$this->configureOutputOptions();
|
||||
|
||||
if ($this->option('no-data')) {
|
||||
Config::set('rrd.enable', false);
|
||||
Config::set('influxdb.enable', false);
|
||||
Config::set('prometheus.enable', false);
|
||||
Config::set('graphite.enable', false);
|
||||
}
|
||||
|
||||
try {
|
||||
$poller = app(Poller::class, ['device_spec' => $this->argument('device spec'), 'module_override' => explode(',', $this->option('modules'))]);
|
||||
$polled = $poller->poll();
|
||||
|
||||
if ($polled > 0) {
|
||||
if (! $this->output->isQuiet()) {
|
||||
if ($polled > 1) {
|
||||
$this->output->newLine();
|
||||
$this->line(sprintf('Polled %d devices in %0.3fs', $polled, $measurements->getCategory('device')->getSummary('poll')->getDuration()));
|
||||
}
|
||||
$this->output->newLine();
|
||||
$measurements->printStats();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} catch (QueryException $e) {
|
||||
if ($e->getCode() == 2002) {
|
||||
$this->error(trans('commands.device:poll.errors.db_connect'));
|
||||
|
||||
return 1;
|
||||
} elseif ($e->getCode() == 1045) {
|
||||
// auth failed, don't need to include the query
|
||||
$this->error(trans('commands.device:poll.errors.db_auth', ['error' => $e->getPrevious()->getMessage()]));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->error($e->getMessage());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1; // failed to poll
|
||||
}
|
||||
}
|
@ -27,6 +27,7 @@ namespace App\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use LibreNMS\Util\Debug;
|
||||
use Symfony\Component\Console\Exception\InvalidArgumentException;
|
||||
use Validator;
|
||||
|
||||
@ -45,7 +46,7 @@ abstract class LnmsCommand extends Command
|
||||
$this->setDescription(__('commands.' . $this->getName() . '.description'));
|
||||
}
|
||||
|
||||
public function isHidden()
|
||||
public function isHidden(): bool
|
||||
{
|
||||
$env = $this->getLaravel() ? $this->getLaravel()->environment() : getenv('APP_ENV');
|
||||
|
||||
@ -125,4 +126,15 @@ abstract class LnmsCommand extends Command
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
protected function configureOutputOptions(): void
|
||||
{
|
||||
\Log::setDefaultDriver($this->getOutput()->isQuiet() ? 'stack' : 'console');
|
||||
if (($verbosity = $this->getOutput()->getVerbosity()) >= 128) {
|
||||
Debug::set();
|
||||
if ($verbosity >= 256) {
|
||||
Debug::setVerbose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
39
app/Events/DevicePolled.php
Normal file
39
app/Events/DevicePolled.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Events;
|
||||
|
||||
use App\Models\Device;
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Broadcasting\PrivateChannel;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class DevicePolled
|
||||
{
|
||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||
|
||||
/**
|
||||
* @var \App\Models\Device
|
||||
*/
|
||||
public $device;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Device $device)
|
||||
{
|
||||
$this->device = $device;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the channels the event should broadcast on.
|
||||
*
|
||||
* @return \Illuminate\Broadcasting\Channel|array
|
||||
*/
|
||||
public function broadcastOn()
|
||||
{
|
||||
return new PrivateChannel('channel-name');
|
||||
}
|
||||
}
|
39
app/Events/PollingDevice.php
Normal file
39
app/Events/PollingDevice.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Events;
|
||||
|
||||
use App\Models\Device;
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Broadcasting\PrivateChannel;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class PollingDevice
|
||||
{
|
||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||
|
||||
/**
|
||||
* @var \App\Models\Device
|
||||
*/
|
||||
public $device;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Device $device)
|
||||
{
|
||||
$this->device = $device;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the channels the event should broadcast on.
|
||||
*
|
||||
* @return \Illuminate\Broadcasting\Channel|array
|
||||
*/
|
||||
public function broadcastOn()
|
||||
{
|
||||
return new PrivateChannel('channel-name');
|
||||
}
|
||||
}
|
@ -76,7 +76,7 @@ class AboutController extends Controller
|
||||
'version_mysql' => Eloquent::version(),
|
||||
'version_php' => phpversion(),
|
||||
'version_laravel' => App::VERSION(),
|
||||
'version_python' => Version::python(),
|
||||
'version_python' => $version->python(),
|
||||
'version_webserver' => $request->server('SERVER_SOFTWARE'),
|
||||
'version_rrdtool' => Rrd::version(),
|
||||
'version_netsnmp' => str_replace('version: ', '', rtrim(shell_exec(Config::get('snmpget', 'snmpget') . ' -V 2>&1'))),
|
||||
|
37
app/Listeners/CheckAlerts.php
Normal file
37
app/Listeners/CheckAlerts.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace App\Listeners;
|
||||
|
||||
use App\Action;
|
||||
use App\Actions\Alerts\RunAlertRulesAction;
|
||||
use App\Events\DevicePolled;
|
||||
use Log;
|
||||
|
||||
class CheckAlerts
|
||||
{
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param DevicePolled $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle(DevicePolled $event)
|
||||
{
|
||||
Log::info('#### Start Alerts ####');
|
||||
$start = microtime(true);
|
||||
|
||||
Action::execute(RunAlertRulesAction::class, $event->device);
|
||||
|
||||
$end = round(microtime(true) - $start, 4);
|
||||
Log::info("#### End Alerts ({$end}s) ####\n");
|
||||
}
|
||||
}
|
43
app/Listeners/UpdateDeviceGroups.php
Normal file
43
app/Listeners/UpdateDeviceGroups.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Listeners;
|
||||
|
||||
use App\Action;
|
||||
use App\Actions\Device\UpdateDeviceGroupsAction;
|
||||
use App\Events\DevicePolled;
|
||||
use Log;
|
||||
|
||||
class UpdateDeviceGroups
|
||||
{
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param DevicePolled $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle(DevicePolled $event)
|
||||
{
|
||||
Log::info('### Start Device Groups ###');
|
||||
$dg_start = microtime(true);
|
||||
|
||||
// update device groups
|
||||
$group_changes = Action::execute(UpdateDeviceGroupsAction::class, $event->device);
|
||||
|
||||
$added = implode(',', $group_changes['attached']);
|
||||
$removed = implode(',', $group_changes['detached']);
|
||||
$elapsed = round(microtime(true) - $dg_start, 4);
|
||||
|
||||
Log::debug("Groups Added: $added Removed: $removed");
|
||||
Log::info("### End Device Groups ({$elapsed}s) ### \n");
|
||||
}
|
||||
}
|
@ -27,7 +27,6 @@ namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use LibreNMS\Alerting\QueryBuilderFluentParser;
|
||||
use Log;
|
||||
use Permissions;
|
||||
|
||||
class DeviceGroup extends BaseModel
|
||||
@ -70,53 +69,6 @@ class DeviceGroup extends BaseModel
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the device groups for the given device or device_id
|
||||
*
|
||||
* @param Device|int $device
|
||||
* @return array
|
||||
*/
|
||||
public static function updateGroupsFor($device)
|
||||
{
|
||||
$device = ($device instanceof Device ? $device : Device::find($device));
|
||||
if (! $device instanceof Device) {
|
||||
// could not load device
|
||||
return [
|
||||
'attached' => [],
|
||||
'detached' => [],
|
||||
'updated' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$device_group_ids = static::query()
|
||||
->with(['devices' => function ($query) {
|
||||
$query->select('devices.device_id');
|
||||
}])
|
||||
->get()
|
||||
->filter(function ($device_group) use ($device) {
|
||||
/** @var DeviceGroup $device_group */
|
||||
if ($device_group->type == 'dynamic') {
|
||||
try {
|
||||
return $device_group->getParser()
|
||||
->toQuery()
|
||||
->where('devices.device_id', $device->device_id)
|
||||
->exists();
|
||||
} catch (\Illuminate\Database\QueryException $e) {
|
||||
Log::error("Device Group '$device_group->name' generates invalid query: " . $e->getMessage());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// for static, if this device is include, keep it.
|
||||
return $device_group->devices
|
||||
->where('device_id', $device->device_id)
|
||||
->isNotEmpty();
|
||||
})->pluck('id');
|
||||
|
||||
return $device->groups()->sync($device_group_ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a query builder parser instance from this device group
|
||||
*
|
||||
|
@ -39,7 +39,7 @@ class DeviceObserver
|
||||
}
|
||||
|
||||
// key attribute changes
|
||||
foreach (['os', 'sysName', 'version', 'hardware', 'features', 'serial', 'icon', 'type'] as $attribute) {
|
||||
foreach (['os', 'sysName', 'version', 'hardware', 'features', 'serial', 'icon', 'type', 'ip'] as $attribute) {
|
||||
if ($device->isDirty($attribute)) {
|
||||
Log::event(self::attributeChangedMessage($attribute, $device->$attribute, $device->getOriginal($attribute)), $device, 'system', 3);
|
||||
}
|
||||
|
@ -27,6 +27,8 @@ namespace App\Polling\Measure;
|
||||
|
||||
use DB;
|
||||
use Illuminate\Database\Events\QueryExecuted;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
class MeasurementManager
|
||||
{
|
||||
@ -36,20 +38,16 @@ class MeasurementManager
|
||||
const NO_COLOR = "\e[0m";
|
||||
|
||||
/**
|
||||
* @var MeasurementCollection
|
||||
* @var \Illuminate\Support\Collection<MeasurementCollection>
|
||||
*/
|
||||
private static $snmp;
|
||||
|
||||
/**
|
||||
* @var MeasurementCollection
|
||||
*/
|
||||
private static $db;
|
||||
private static $categories;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
if (self::$snmp === null) {
|
||||
self::$snmp = new MeasurementCollection();
|
||||
self::$db = new MeasurementCollection();
|
||||
if (self::$categories === null) {
|
||||
self::$categories = new Collection;
|
||||
self::$categories->put('snmp', new MeasurementCollection());
|
||||
self::$categories->put('db', new MeasurementCollection());
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,12 +62,20 @@ class MeasurementManager
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update statistics for the given category
|
||||
*/
|
||||
public function record(string $category, Measurement $measurement): void
|
||||
{
|
||||
$this->getCategory($category)->record($measurement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update statistics for db operations
|
||||
*/
|
||||
public function recordDb(Measurement $measurement): void
|
||||
{
|
||||
self::$db->record($measurement);
|
||||
$this->record('db', $measurement);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -77,25 +83,24 @@ class MeasurementManager
|
||||
*/
|
||||
public function printChangedStats(): void
|
||||
{
|
||||
printf(
|
||||
'>> %sSNMP%s: [%d/%.2fs] %sMySQL%s: [%d/%.2fs]',
|
||||
self::SNMP_COLOR,
|
||||
self::NO_COLOR,
|
||||
self::$snmp->getCountDiff(),
|
||||
self::$snmp->getDurationDiff(),
|
||||
self::DB_COLOR,
|
||||
self::NO_COLOR,
|
||||
self::$db->getCountDiff(),
|
||||
self::$db->getDurationDiff()
|
||||
);
|
||||
|
||||
app('Datastore')->getStats()->each(function (MeasurementCollection $stats, $datastore) {
|
||||
printf(' %s%s%s: [%d/%.2fs]', self::DATASTORE_COLOR, $datastore, self::NO_COLOR, $stats->getCountDiff(), $stats->getDurationDiff());
|
||||
$dsStats = app('Datastore')->getStats()->map(function (MeasurementCollection $stats, $datastore) {
|
||||
return sprintf('%s%s%s: [%d/%.2fs]', self::DATASTORE_COLOR, $datastore, self::NO_COLOR, $stats->getCountDiff(), $stats->getDurationDiff());
|
||||
});
|
||||
|
||||
$this->checkpoint();
|
||||
Log::info(sprintf(
|
||||
'>> %sSNMP%s: [%d/%.2fs] %sMySQL%s: [%d/%.2fs] %s',
|
||||
self::SNMP_COLOR,
|
||||
self::NO_COLOR,
|
||||
$this->getCategory('snmp')->getCountDiff(),
|
||||
$this->getCategory('snmp')->getDurationDiff(),
|
||||
self::DB_COLOR,
|
||||
self::NO_COLOR,
|
||||
$this->getCategory('db')->getCountDiff(),
|
||||
$this->getCategory('db')->getDurationDiff(),
|
||||
$dsStats->implode(' ')
|
||||
));
|
||||
|
||||
echo PHP_EOL;
|
||||
$this->checkpoint();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -103,8 +108,7 @@ class MeasurementManager
|
||||
*/
|
||||
public function checkpoint(): void
|
||||
{
|
||||
self::$snmp->checkpoint();
|
||||
self::$db->checkpoint();
|
||||
self::$categories->each->checkpoint();
|
||||
app('Datastore')->getStats()->each->checkpoint();
|
||||
}
|
||||
|
||||
@ -113,7 +117,7 @@ class MeasurementManager
|
||||
*/
|
||||
public function recordSnmp(Measurement $measurement): void
|
||||
{
|
||||
self::$snmp->record($measurement);
|
||||
$this->record('snmp', $measurement);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -121,22 +125,36 @@ class MeasurementManager
|
||||
*/
|
||||
public function printStats(): void
|
||||
{
|
||||
$this->printSummary('SNMP', self::$snmp, self::SNMP_COLOR);
|
||||
$this->printSummary('SQL', self::$db, self::DB_COLOR);
|
||||
$this->printSummary('SNMP', $this->getCategory('snmp'), self::SNMP_COLOR);
|
||||
$this->printSummary('SQL', $this->getCategory('db'), self::DB_COLOR);
|
||||
|
||||
app('Datastore')->getStats()->each(function (MeasurementCollection $stats, string $datastore) {
|
||||
$this->printSummary($datastore, $stats, self::DATASTORE_COLOR);
|
||||
});
|
||||
}
|
||||
|
||||
private function printSummary(string $name, MeasurementCollection $collection, string $color = ''): void
|
||||
public function getCategory(string $category): MeasurementCollection
|
||||
{
|
||||
printf('%s%s%s [%d/%.2fs]:', $color, $name, $color ? self::NO_COLOR : '', $collection->getTotalCount(), $collection->getTotalDuration());
|
||||
if (! self::$categories->has($category)) {
|
||||
self::$categories->put($category, new MeasurementCollection());
|
||||
}
|
||||
|
||||
$collection->each(function (MeasurementSummary $stat) {
|
||||
printf(' %s[%d/%.2fs]', ucfirst($stat->getType()), $stat->getCount(), $stat->getDuration());
|
||||
return self::$categories->get($category);
|
||||
}
|
||||
|
||||
public function printSummary(string $name, MeasurementCollection $collection, string $color = ''): void
|
||||
{
|
||||
$summaries = $collection->map(function (MeasurementSummary $stat) {
|
||||
return sprintf('%s[%d/%.2fs]', ucfirst($stat->getType()), $stat->getCount(), $stat->getDuration());
|
||||
});
|
||||
|
||||
echo PHP_EOL;
|
||||
Log::info(sprintf('%s%s%s [%d/%.2fs]: %s',
|
||||
$color,
|
||||
$name,
|
||||
$color ? self::NO_COLOR : '',
|
||||
$collection->getTotalCount(),
|
||||
$collection->getTotalDuration(),
|
||||
$summaries->implode(' ')
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,13 @@ class AppServiceProvider extends ServiceProvider
|
||||
$this->app->singleton('device-cache', function ($app) {
|
||||
return new \LibreNMS\Cache\Device();
|
||||
});
|
||||
|
||||
$this->app->bind(\App\Models\Device::class, function () {
|
||||
/** @var \LibreNMS\Cache\Device $cache */
|
||||
$cache = $this->app->make('device-cache');
|
||||
|
||||
return $cache->hasPrimary() ? $cache->getPrimary() : new \App\Models\Device;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Listeners\MarkNotificationsRead;
|
||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||
|
||||
class EventServiceProvider extends ServiceProvider
|
||||
@ -15,7 +14,15 @@ class EventServiceProvider extends ServiceProvider
|
||||
protected $listen = [
|
||||
\Illuminate\Auth\Events\Login::class => ['App\Listeners\AuthEventListener@login'],
|
||||
\Illuminate\Auth\Events\Logout::class => ['App\Listeners\AuthEventListener@logout'],
|
||||
\App\Events\UserCreated::class => [MarkNotificationsRead::class],
|
||||
\App\Events\UserCreated::class => [
|
||||
\App\Listeners\MarkNotificationsRead::class,
|
||||
],
|
||||
\App\Events\PollingDevice::class => [
|
||||
],
|
||||
\App\Events\DevicePolled::class => [
|
||||
\App\Listeners\CheckAlerts::class,
|
||||
\App\Listeners\UpdateDeviceGroups::class,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -55,6 +55,12 @@ return [
|
||||
'ignore_exceptions' => false,
|
||||
],
|
||||
|
||||
'console_debug' => [
|
||||
'driver' => 'stack',
|
||||
'channels' => ['single', 'stdout_debug'],
|
||||
'ignore_exceptions' => false,
|
||||
],
|
||||
|
||||
'single' => [
|
||||
'driver' => 'single',
|
||||
'path' => env('APP_LOG', \LibreNMS\Config::get('log_file', base_path('logs/librenms.log'))),
|
||||
@ -96,7 +102,7 @@ return [
|
||||
'level' => 'debug',
|
||||
],
|
||||
|
||||
'stdout' => [
|
||||
'stdout_debug' => [
|
||||
'driver' => 'monolog',
|
||||
'handler' => StreamHandler::class,
|
||||
'formatter' => \LibreNMS\Util\CliColorFormatter::class,
|
||||
@ -106,6 +112,16 @@ return [
|
||||
'level' => 'debug',
|
||||
],
|
||||
|
||||
'stdout' => [
|
||||
'driver' => 'monolog',
|
||||
'handler' => StreamHandler::class,
|
||||
'formatter' => \LibreNMS\Util\CliColorFormatter::class,
|
||||
'with' => [
|
||||
'stream' => 'php://output',
|
||||
],
|
||||
'level' => 'info',
|
||||
],
|
||||
|
||||
'syslog' => [
|
||||
'driver' => 'syslog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
|
@ -15,6 +15,7 @@ $init_modules = ['discovery'];
|
||||
require __DIR__ . '/includes/init.php';
|
||||
|
||||
$start = microtime(true);
|
||||
Log::setDefaultDriver('console');
|
||||
$sqlparams = [];
|
||||
$options = getopt('h:m:i:n:d::v::a::q', ['os:', 'type:']);
|
||||
|
||||
|
@ -623,14 +623,10 @@ function version_info($remote = false)
|
||||
}
|
||||
$output['db_schema'] = vsprintf('%s (%s)', $version->database());
|
||||
$output['php_ver'] = phpversion();
|
||||
$output['python_ver'] = \LibreNMS\Util\Version::python();
|
||||
$output['python_ver'] = $version->python();
|
||||
$output['mysql_ver'] = \LibreNMS\DB\Eloquent::isConnected() ? \LibreNMS\DB\Eloquent::version() : '?';
|
||||
$output['rrdtool_ver'] = str_replace('1.7.01.7.0', '1.7.0', implode(' ', array_slice(explode(' ', shell_exec(
|
||||
Config::get('rrdtool', 'rrdtool') . ' --version |head -n1'
|
||||
)), 1, 1)));
|
||||
$output['netsnmp_ver'] = str_replace('version: ', '', rtrim(shell_exec(
|
||||
Config::get('snmpget', 'snmpget') . ' -V 2>&1'
|
||||
)));
|
||||
$output['rrdtool_ver'] = $version->rrdtool();
|
||||
$output['netsnmp_ver'] = $version->netSnmp();
|
||||
|
||||
return $output;
|
||||
}//end version_info()
|
||||
|
@ -1,3 +1,3 @@
|
||||
<?php
|
||||
|
||||
(new \LibreNMS\Modules\OS())->discover($os);
|
||||
(new \LibreNMS\Modules\Os())->discover($os);
|
||||
|
@ -74,7 +74,7 @@ function parse_modules($type, $options)
|
||||
{
|
||||
$override = false;
|
||||
|
||||
if ($options['m']) {
|
||||
if (! empty($options['m'])) {
|
||||
Config::set("{$type}_modules", []);
|
||||
foreach (explode(',', $options['m']) as $module) {
|
||||
// parse submodules (only supported by some modules)
|
||||
|
@ -5,4 +5,4 @@ use LibreNMS\OS;
|
||||
if (! $os instanceof OS) {
|
||||
$os = OS::make($device);
|
||||
}
|
||||
(new \LibreNMS\Modules\OS())->poll($os);
|
||||
(new \LibreNMS\Modules\Os())->poll($os);
|
||||
|
@ -3665,60 +3665,30 @@ parameters:
|
||||
count: 1
|
||||
path: LibreNMS/Modules/Nac.php
|
||||
|
||||
-
|
||||
message: "#^Method LibreNMS\\\\Modules\\\\OS\\:\\:cleanup\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Modules/OS.php
|
||||
|
||||
-
|
||||
message: "#^Method LibreNMS\\\\Modules\\\\OS\\:\\:discover\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Modules/OS.php
|
||||
|
||||
-
|
||||
message: "#^Method LibreNMS\\\\Modules\\\\OS\\:\\:handleChanges\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Modules/OS.php
|
||||
|
||||
-
|
||||
message: "#^Method LibreNMS\\\\Modules\\\\OS\\:\\:poll\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Modules/OS.php
|
||||
|
||||
-
|
||||
message: "#^Method LibreNMS\\\\Modules\\\\OS\\:\\:sysContact\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Modules/OS.php
|
||||
|
||||
-
|
||||
message: "#^Method LibreNMS\\\\Modules\\\\OS\\:\\:updateLocation\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Modules/OS.php
|
||||
|
||||
-
|
||||
message: "#^Variable \\$features on left side of \\?\\? is never defined\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Modules/OS.php
|
||||
path: LibreNMS/Modules/Os.php
|
||||
|
||||
-
|
||||
message: "#^Variable \\$hardware on left side of \\?\\? is never defined\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Modules/OS.php
|
||||
path: LibreNMS/Modules/Os.php
|
||||
|
||||
-
|
||||
message: "#^Variable \\$location in empty\\(\\) always exists and is always falsy\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Modules/OS.php
|
||||
path: LibreNMS/Modules/Os.php
|
||||
|
||||
-
|
||||
message: "#^Variable \\$serial on left side of \\?\\? is never defined\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Modules/OS.php
|
||||
path: LibreNMS/Modules/Os.php
|
||||
|
||||
-
|
||||
message: "#^Variable \\$version on left side of \\?\\? is never defined\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Modules/OS.php
|
||||
path: LibreNMS/Modules/Os.php
|
||||
|
||||
-
|
||||
message: "#^Method LibreNMS\\\\Modules\\\\PrinterSupplies\\:\\:cleanup\\(\\) has no return type specified\\.$#"
|
||||
@ -3915,11 +3885,6 @@ parameters:
|
||||
count: 1
|
||||
path: LibreNMS/OS.php
|
||||
|
||||
-
|
||||
message: "#^Method LibreNMS\\\\OS\\:\\:persistGraphs\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/OS.php
|
||||
|
||||
-
|
||||
message: "#^Method LibreNMS\\\\OS\\:\\:preCache\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
@ -5440,36 +5405,6 @@ parameters:
|
||||
count: 1
|
||||
path: LibreNMS/Util/Colors.php
|
||||
|
||||
-
|
||||
message: "#^Method LibreNMS\\\\Util\\\\Debug\\:\\:disableCliDebugOutput\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Util/Debug.php
|
||||
|
||||
-
|
||||
message: "#^Method LibreNMS\\\\Util\\\\Debug\\:\\:disableQueryDebug\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Util/Debug.php
|
||||
|
||||
-
|
||||
message: "#^Method LibreNMS\\\\Util\\\\Debug\\:\\:enableCliDebugOutput\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Util/Debug.php
|
||||
|
||||
-
|
||||
message: "#^Method LibreNMS\\\\Util\\\\Debug\\:\\:enableQueryDebug\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Util/Debug.php
|
||||
|
||||
-
|
||||
message: "#^Property LibreNMS\\\\Util\\\\Debug\\:\\:\\$debug has no type specified\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Util/Debug.php
|
||||
|
||||
-
|
||||
message: "#^Property LibreNMS\\\\Util\\\\Debug\\:\\:\\$verbose has no type specified\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Util/Debug.php
|
||||
|
||||
-
|
||||
message: "#^Property LibreNMS\\\\Util\\\\Dns\\:\\:\\$resolver has no type specified\\.$#"
|
||||
count: 1
|
||||
@ -5765,16 +5700,6 @@ parameters:
|
||||
count: 1
|
||||
path: LibreNMS/Util/FileCategorizer.php
|
||||
|
||||
-
|
||||
message: "#^Method LibreNMS\\\\Util\\\\Git\\:\\:binaryExists\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Util/Git.php
|
||||
|
||||
-
|
||||
message: "#^Method LibreNMS\\\\Util\\\\Git\\:\\:repoPresent\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Util/Git.php
|
||||
|
||||
-
|
||||
message: "#^Method LibreNMS\\\\Util\\\\GitHub\\:\\:__construct\\(\\) has parameter \\$file with no type specified\\.$#"
|
||||
count: 1
|
||||
@ -6845,46 +6770,6 @@ parameters:
|
||||
count: 1
|
||||
path: LibreNMS/Util/Validate.php
|
||||
|
||||
-
|
||||
message: "#^Method LibreNMS\\\\Util\\\\Version\\:\\:database\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Util/Version.php
|
||||
|
||||
-
|
||||
message: "#^Method LibreNMS\\\\Util\\\\Version\\:\\:fromGit\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Util/Version.php
|
||||
|
||||
-
|
||||
message: "#^Method LibreNMS\\\\Util\\\\Version\\:\\:get\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Util/Version.php
|
||||
|
||||
-
|
||||
message: "#^Method LibreNMS\\\\Util\\\\Version\\:\\:gitChangelog\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Util/Version.php
|
||||
|
||||
-
|
||||
message: "#^Method LibreNMS\\\\Util\\\\Version\\:\\:gitDate\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Util/Version.php
|
||||
|
||||
-
|
||||
message: "#^Method LibreNMS\\\\Util\\\\Version\\:\\:local\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Util/Version.php
|
||||
|
||||
-
|
||||
message: "#^Method LibreNMS\\\\Util\\\\Version\\:\\:python\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Util/Version.php
|
||||
|
||||
-
|
||||
message: "#^Property LibreNMS\\\\Util\\\\Version\\:\\:\\$is_git_install has no type specified\\.$#"
|
||||
count: 1
|
||||
path: LibreNMS/Util/Version.php
|
||||
|
||||
-
|
||||
message: "#^Method LibreNMS\\\\ValidationResult\\:\\:consolePrint\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
|
@ -24,6 +24,8 @@
|
||||
* @author Heath Barnhart <hbarnhart@kanren.net>
|
||||
*/
|
||||
|
||||
use App\Action;
|
||||
use App\Actions\Device\UpdateDeviceGroupsAction;
|
||||
use LibreNMS\Alert\AlertRules;
|
||||
use LibreNMS\Config;
|
||||
use LibreNMS\Data\Store\Datastore;
|
||||
@ -33,6 +35,7 @@ $init_modules = ['polling', 'alerts', 'laravel'];
|
||||
require __DIR__ . '/includes/init.php';
|
||||
|
||||
$poller_start = microtime(true);
|
||||
Log::setDefaultDriver('console');
|
||||
echo Config::get('project_name') . " Poller\n";
|
||||
|
||||
$options = getopt('h:m:i:n:r::d::v::a::f::q');
|
||||
@ -139,7 +142,7 @@ foreach (dbFetch($query) as $device) {
|
||||
// Update device_groups
|
||||
echo "### Start Device Groups ###\n";
|
||||
$dg_start = microtime(true);
|
||||
$group_changes = \App\Models\DeviceGroup::updateGroupsFor($device['device_id']);
|
||||
$group_changes = Action::execute(UpdateDeviceGroupsAction::class);
|
||||
d_echo('Groups Added: ' . implode(',', $group_changes['attached']) . PHP_EOL);
|
||||
d_echo('Groups Removed: ' . implode(',', $group_changes['detached']) . PHP_EOL);
|
||||
echo '### End Device Groups, runtime: ' . round(microtime(true) - $dg_start, 4) . "s ### \n\n";
|
||||
|
@ -69,6 +69,20 @@ return [
|
||||
'device spec' => 'Device to ping one of: <Device ID>, <Hostname/IP>, all',
|
||||
],
|
||||
],
|
||||
'device:poll' => [
|
||||
'description' => 'Poll data from device(s) as defined by discovery',
|
||||
'arguments' => [
|
||||
'device spec' => 'Device spec to poll: device_id, hostname, wildcard, odd, even, all',
|
||||
],
|
||||
'options' => [
|
||||
'modules' => 'Specify single module to be run. Comma separate modules, submodules may be added with /',
|
||||
'no-data' => 'Do not update datastores (RRD, InfluxDB, etc)',
|
||||
],
|
||||
'errors' => [
|
||||
'db_connect' => 'Failed to connect to database. Verify database service is running and connection settings.',
|
||||
'db_auth' => 'Failed to connect to database. Verify credentials: :error',
|
||||
],
|
||||
],
|
||||
'key:rotate' => [
|
||||
'description' => 'Rotate APP_KEY, this decrypts all encrypted data with the given old key and stores it with the new key in APP_KEY.',
|
||||
'arguments' => [
|
||||
|
@ -111,8 +111,7 @@ Artisan::command('device:add
|
||||
if (($verbosity = $this->getOutput()->getVerbosity()) >= 128) {
|
||||
Debug::set();
|
||||
if ($verbosity >= 256) {
|
||||
global $verbose;
|
||||
$verbose = true;
|
||||
Debug::setVerbose();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,10 +100,8 @@ class OSModulesTest extends DBTestCase
|
||||
$filename = $helper->getJsonFilepath(true);
|
||||
$expected_data = $helper->getTestData();
|
||||
$results = $helper->generateTestData($this->getSnmpsim(), true);
|
||||
} catch (FileNotFoundException $e) {
|
||||
return $this->fail($e->getMessage());
|
||||
} catch (InvalidModuleException $e) {
|
||||
return $this->fail($e->getMessage());
|
||||
} catch (FileNotFoundException|InvalidModuleException $e) {
|
||||
$this->fail($e->getMessage());
|
||||
}
|
||||
|
||||
if (is_null($results)) {
|
||||
@ -168,8 +166,10 @@ class OSModulesTest extends DBTestCase
|
||||
private function stubClasses(): void
|
||||
{
|
||||
$this->app->bind('log', function ($app) {
|
||||
return \Mockery::mock('\App\Facades\LogManager[event]', [$app])
|
||||
->shouldReceive('event');
|
||||
$mock = \Mockery::mock('\App\Facades\LogManager[event]', [$app]);
|
||||
$mock->shouldReceive('event');
|
||||
|
||||
return $mock;
|
||||
});
|
||||
|
||||
$this->app->bind(Fping::class, function ($app) {
|
||||
|
Loading…
Reference in New Issue
Block a user