mirror of
https://github.com/librenms/librenms.git
synced 2024-09-21 10:28:13 +00:00
Disable plugins that have errors (#14383)
* Disable plugins that have errors Disable plugin if a hook throws an error and set a notification Move notification code to class, so we can access it Clear notification when plugin is attempted to be enabled again * fix style and lint fixes * another lint fix and handle if property is missing
This commit is contained in:
parent
333ba7c2cd
commit
e990dfcb35
@ -27,6 +27,7 @@
|
||||
namespace LibreNMS;
|
||||
|
||||
use App\Models\Plugin;
|
||||
use LibreNMS\Util\Notifications;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
@ -190,8 +191,14 @@ class Plugins
|
||||
} else {
|
||||
@call_user_func_array([$plugin, $hook], $params);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Exception|\Error $e) {
|
||||
Log::error($e);
|
||||
|
||||
$class = (string) get_class($plugin);
|
||||
$name = property_exists($class, 'name') ? $class::$name : basename(str_replace('\\', '/', $class));
|
||||
|
||||
Notifications::create("Plugin $name disabled", "$name caused an error and was disabled, please check with the plugin creator to fix the error. The error can be found in logs/librenms.log", 'plugins', 2);
|
||||
Plugin::where('plugin_name', $name)->update(['plugin_active' => 0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
153
LibreNMS/Util/Notifications.php
Normal file
153
LibreNMS/Util/Notifications.php
Normal file
@ -0,0 +1,153 @@
|
||||
<?php
|
||||
/**
|
||||
* Notifications.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 2015 Daniel Preussker, QuxLabs UG
|
||||
* @copyright 2022 Tony Murray
|
||||
* @author Daniel Preussker
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
*/
|
||||
|
||||
namespace LibreNMS\Util;
|
||||
|
||||
use App\Models\Notification;
|
||||
use Illuminate\Support\Arr;
|
||||
use LibreNMS\Config;
|
||||
|
||||
class Notifications
|
||||
{
|
||||
/**
|
||||
* Post notifications to users
|
||||
*/
|
||||
public static function post(): void
|
||||
{
|
||||
$notifications = self::fetch();
|
||||
echo '[ ' . date('r') . ' ] Updating DB ';
|
||||
foreach ($notifications as $notif) {
|
||||
if (! Notification::where('checksum', $notif['checksum'])->exists()) {
|
||||
Notification::create($notif);
|
||||
echo '.';
|
||||
}
|
||||
}
|
||||
echo ' Done' . PHP_EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new custom notification. Duplicate title+message notifications will not be created.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $message
|
||||
* @param string $source A string describing what created this notification
|
||||
* @param int $severity 0=ok, 1=warning, 2=critical
|
||||
* @param string|null $date
|
||||
* @return bool
|
||||
*/
|
||||
public static function create(string $title, string $message, string $source, int $severity = 0, ?string $date = null): bool
|
||||
{
|
||||
$checksum = hash('sha512', $title . $message);
|
||||
|
||||
return Notification::firstOrCreate([
|
||||
'checksum' => $checksum,
|
||||
], [
|
||||
'title' => $title,
|
||||
'body' => $message,
|
||||
'severity' => $severity,
|
||||
'source' => $source,
|
||||
'checksum' => $checksum,
|
||||
'datetime' => date('Y-m-d', is_null($date) ? time() : strtotime($date)),
|
||||
])->wasRecentlyCreated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all notifications with the given title.
|
||||
* This should be used with care.
|
||||
*/
|
||||
public static function remove(string $title): void
|
||||
{
|
||||
Notification::where('title', $title)->get()->each->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull notifications from remotes
|
||||
*
|
||||
* @return array Notifications
|
||||
*/
|
||||
protected static function fetch(): array
|
||||
{
|
||||
$notifications = [];
|
||||
foreach (Config::get('notifications') as $name => $url) {
|
||||
echo '[ ' . date('r') . " ] $name $url ";
|
||||
|
||||
$feed = json_decode(json_encode(simplexml_load_string(file_get_contents($url))), true);
|
||||
$feed = isset($feed['channel']) ? self::parseRss($feed) : self::parseAtom($feed);
|
||||
|
||||
array_walk($feed, function (&$items, $key, $url) {
|
||||
$items['source'] = $url;
|
||||
}, $url);
|
||||
$notifications = array_merge($notifications, $feed);
|
||||
|
||||
echo '(' . count($notifications) . ')' . PHP_EOL;
|
||||
}
|
||||
|
||||
return Arr::sort($notifications, 'datetime');
|
||||
}
|
||||
|
||||
protected static function parseRss(array $feed): array
|
||||
{
|
||||
$obj = [];
|
||||
if (! array_key_exists('0', $feed['channel']['item'])) {
|
||||
$feed['channel']['item'] = [$feed['channel']['item']];
|
||||
}
|
||||
foreach ($feed['channel']['item'] as $item) {
|
||||
$obj[] = [
|
||||
'title' => $item['title'],
|
||||
'body' => $item['description'],
|
||||
'checksum' => hash('sha512', $item['title'] . $item['description']),
|
||||
'datetime' => date('Y-m-d', strtotime($item['pubDate']) ?: time()),
|
||||
];
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse Atom
|
||||
*
|
||||
* @param array $feed Atom Object
|
||||
* @return array Parsed Object
|
||||
*/
|
||||
protected static function parseAtom(array $feed): array
|
||||
{
|
||||
$obj = [];
|
||||
if (! array_key_exists('0', $feed['entry'])) {
|
||||
$feed['entry'] = [$feed['entry']];
|
||||
}
|
||||
foreach ($feed['entry'] as $item) {
|
||||
$obj[] = [
|
||||
'title' => $item['title'],
|
||||
'body' => $item['content'],
|
||||
'checksum' => hash('sha512', $item['title'] . $item['content']),
|
||||
'datetime' => date('Y-m-d', strtotime($item['updated']) ?: time()),
|
||||
];
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ use App\Models\Plugin;
|
||||
use App\Plugins\Hooks\SettingsHook;
|
||||
use App\Plugins\PluginManager;
|
||||
use Illuminate\Http\Request;
|
||||
use LibreNMS\Util\Notifications;
|
||||
|
||||
class PluginSettingsController extends Controller
|
||||
{
|
||||
@ -31,12 +32,17 @@ class PluginSettingsController extends Controller
|
||||
|
||||
public function update(Request $request, Plugin $plugin): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
$validated = $this->validate($request, [
|
||||
$plugin->fill($this->validate($request, [
|
||||
'plugin_active' => 'in:0,1',
|
||||
'settings' => 'array',
|
||||
]);
|
||||
]));
|
||||
|
||||
$plugin->fill($validated)->save();
|
||||
if ($plugin->isDirty('plugin_active') && $plugin->plugin_active == 1) {
|
||||
// enabling plugin delete notifications assuming they are fixed
|
||||
Notifications::remove("Plugin $plugin->plugin_name disabled");
|
||||
}
|
||||
|
||||
$plugin->save();
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
|
@ -28,6 +28,24 @@ class Notification extends Model
|
||||
* @var string
|
||||
*/
|
||||
protected $primaryKey = 'notifications_id';
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'body',
|
||||
'severity',
|
||||
'source',
|
||||
'checksum',
|
||||
'datetime',
|
||||
];
|
||||
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
// delete attribs for this notification
|
||||
static::deleting(function (Notification $notification) {
|
||||
$notification->attribs()->delete();
|
||||
});
|
||||
}
|
||||
|
||||
// ---- Helper Functions ----
|
||||
|
||||
|
@ -30,6 +30,7 @@ use App\Models\Plugin;
|
||||
use Exception;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Support\Collection;
|
||||
use LibreNMS\Util\Notifications;
|
||||
use Log;
|
||||
|
||||
class PluginManager
|
||||
@ -107,16 +108,22 @@ class PluginManager
|
||||
*/
|
||||
public function call(string $hookType, array $args = [], ?string $plugin = null): Collection
|
||||
{
|
||||
try {
|
||||
return $this->hooksFor($hookType, $args, $plugin)
|
||||
->map(function ($hook) use ($args) {
|
||||
return $this->hooksFor($hookType, $args, $plugin)
|
||||
->map(function ($hook) use ($args, $hookType) {
|
||||
try {
|
||||
return app()->call([$hook['instance'], 'handle'], $this->fillArgs($args, $hook['plugin_name']));
|
||||
});
|
||||
} catch (Exception $e) {
|
||||
Log::error("Error calling hook $hookType: " . $e->getMessage());
|
||||
} catch (Exception|\Error $e) {
|
||||
$name = $hook['plugin_name'];
|
||||
Log::error("Error calling hook $hookType for $name: " . $e->getMessage());
|
||||
|
||||
return new Collection;
|
||||
}
|
||||
Notifications::create("Plugin $name disabled", "$name caused an error and was disabled, please check with the plugin creator to fix the error. The error can be found in logs/librenms.log", 'plugins', 2);
|
||||
Plugin::where('plugin_name', $name)->update(['plugin_active' => 0]);
|
||||
|
||||
return 'HOOK FAILED';
|
||||
}
|
||||
})->filter(function ($hook) {
|
||||
return $hook === 'HOOK FAILED';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -22,6 +22,7 @@
|
||||
"ext-pcre": "*",
|
||||
"ext-pdo": "*",
|
||||
"ext-session": "*",
|
||||
"ext-simplexml": "*",
|
||||
"ext-xml": "*",
|
||||
"ext-zlib": "*",
|
||||
"amenadiel/jpgraph": "^4",
|
||||
|
2
composer.lock
generated
2
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "e21d9022b60ccf67f7b0c0b622348ced",
|
||||
"content-hash": "5f22d1ad8c4de6777b2690d0115f7afe",
|
||||
"packages": [
|
||||
{
|
||||
"name": "amenadiel/jpgraph",
|
||||
|
39
daily.php
39
daily.php
@ -12,6 +12,7 @@ use Illuminate\Database\Eloquent\Collection;
|
||||
use LibreNMS\Alert\AlertDB;
|
||||
use LibreNMS\Config;
|
||||
use LibreNMS\Util\Debug;
|
||||
use LibreNMS\Util\Notifications;
|
||||
use LibreNMS\Validations\Php;
|
||||
|
||||
$options = getopt('df:o:t:r:');
|
||||
@ -39,7 +40,6 @@ if ($options['f'] === 'composer_get_plugins') {
|
||||
*/
|
||||
$init_modules = ['alerts'];
|
||||
require __DIR__ . '/includes/init.php';
|
||||
include_once __DIR__ . '/includes/notifications.php';
|
||||
|
||||
if (isset($options['d'])) {
|
||||
echo "DEBUG\n";
|
||||
@ -158,16 +158,14 @@ if ($options['f'] === 'handle_notifiable') {
|
||||
|
||||
if ($options['r']) {
|
||||
// result was a success (1), remove the notification
|
||||
remove_notification($title);
|
||||
Notifications::remove($title);
|
||||
} else {
|
||||
// result was a failure (0), create the notification
|
||||
new_notification(
|
||||
$title,
|
||||
"The daily update script (daily.sh) has failed on $poller_name."
|
||||
Notifications::create($title, "The daily update script (daily.sh) has failed on $poller_name."
|
||||
. 'Please check output by hand. If you need assistance, '
|
||||
. 'visit the <a href="https://www.librenms.org/#support">LibreNMS Website</a> to find out how.',
|
||||
2,
|
||||
'daily.sh'
|
||||
'daily.sh',
|
||||
2
|
||||
);
|
||||
}
|
||||
} elseif ($options['t'] === 'phpver') {
|
||||
@ -183,17 +181,16 @@ if ($options['f'] === 'handle_notifiable') {
|
||||
$eol_date = Php::PHP_MIN_VERSION_DATE;
|
||||
}
|
||||
if (isset($phpver)) {
|
||||
new_notification(
|
||||
$error_title,
|
||||
Notifications::create($error_title,
|
||||
"PHP version $phpver is the minimum supported version as of $eol_date. We recommend you update to PHP a supported version of PHP (" . Php::PHP_RECOMMENDED_VERSION . ' suggested) to continue to receive updates. If you do not update PHP, LibreNMS will continue to function but stop receiving bug fixes and updates.',
|
||||
2,
|
||||
'daily.sh'
|
||||
'daily.sh',
|
||||
2
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
remove_notification($error_title);
|
||||
Notifications::remove($error_title);
|
||||
exit(0);
|
||||
} elseif ($options['t'] === 'pythonver') {
|
||||
$error_title = 'Error: Python requirements not met';
|
||||
@ -201,25 +198,23 @@ if ($options['f'] === 'handle_notifiable') {
|
||||
// if update is not set to false and version is min or newer
|
||||
if (Config::get('update') && $options['r']) {
|
||||
if ($options['r'] === 'python3-missing') {
|
||||
new_notification(
|
||||
$error_title,
|
||||
Notifications::create($error_title,
|
||||
'Python 3 is required to run LibreNMS as of May, 2020. You need to install Python 3 to continue to receive updates. If you do not install Python 3 and required packages, LibreNMS will continue to function but stop receiving bug fixes and updates.',
|
||||
2,
|
||||
'daily.sh'
|
||||
'daily.sh',
|
||||
2
|
||||
);
|
||||
exit(1);
|
||||
} elseif ($options['r'] === 'python3-deps') {
|
||||
new_notification(
|
||||
$error_title,
|
||||
Notifications::create($error_title,
|
||||
'Python 3 dependencies are missing. You need to install them via pip3 install -r requirements.txt or system packages to continue to receive updates. If you do not install Python 3 and required packages, LibreNMS will continue to function but stop receiving bug fixes and updates.',
|
||||
2,
|
||||
'daily.sh'
|
||||
'daily.sh',
|
||||
2
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
remove_notification($error_title);
|
||||
Notifications::remove($error_title);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
@ -227,7 +222,7 @@ if ($options['f'] === 'handle_notifiable') {
|
||||
if ($options['f'] === 'notifications') {
|
||||
$lock = Cache::lock('notifications', 86000);
|
||||
if ($lock->get()) {
|
||||
post_notifications();
|
||||
Notifications::post();
|
||||
$lock->release();
|
||||
}
|
||||
}
|
||||
|
@ -1,162 +0,0 @@
|
||||
<?php
|
||||
/* Copyright (C) 2015 Daniel Preussker, QuxLabs UG <preussker@quxlabs.com>
|
||||
* 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/>. */
|
||||
|
||||
/**
|
||||
* Notification Poller
|
||||
*
|
||||
* @copyright 2015 Daniel Preussker, QuxLabs UG
|
||||
* @copyright 2017 Tony Murray
|
||||
* @author Daniel Preussker
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
* @license GPL
|
||||
*
|
||||
* @link https://www.librenms.org
|
||||
*/
|
||||
|
||||
/**
|
||||
* Pull notifications from remotes
|
||||
*
|
||||
* @return array Notifications
|
||||
*/
|
||||
function get_notifications()
|
||||
{
|
||||
$obj = [];
|
||||
foreach (\LibreNMS\Config::get('notifications') as $name => $url) {
|
||||
echo '[ ' . date('r') . ' ] ' . $url . ' ';
|
||||
$feed = json_decode(json_encode(simplexml_load_string(file_get_contents($url))), true);
|
||||
if (isset($feed['channel'])) {
|
||||
$feed = parse_rss($feed);
|
||||
} else {
|
||||
$feed = parse_atom($feed);
|
||||
}
|
||||
array_walk($feed, function (&$items, $key, $url) {
|
||||
$items['source'] = $url;
|
||||
}, $url);
|
||||
$obj = array_merge($obj, $feed);
|
||||
echo '(' . sizeof($obj) . ')' . PHP_EOL;
|
||||
}
|
||||
$obj = array_sort_by_column($obj, 'datetime');
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Post notifications to users
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
function post_notifications()
|
||||
{
|
||||
$notifs = get_notifications();
|
||||
echo '[ ' . date('r') . ' ] Updating DB ';
|
||||
foreach ($notifs as $notif) {
|
||||
if (dbFetchCell('select 1 from notifications where checksum = ?', [$notif['checksum']]) != 1 && dbInsert($notif, 'notifications') > 0) {
|
||||
echo '.';
|
||||
}
|
||||
}
|
||||
echo ' Done';
|
||||
echo PHP_EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse RSS
|
||||
*
|
||||
* @param array $feed RSS Object
|
||||
* @return array Parsed Object
|
||||
*/
|
||||
function parse_rss($feed)
|
||||
{
|
||||
$obj = [];
|
||||
if (! array_key_exists('0', $feed['channel']['item'])) {
|
||||
$feed['channel']['item'] = [$feed['channel']['item']];
|
||||
}
|
||||
foreach ($feed['channel']['item'] as $item) {
|
||||
$obj[] = [
|
||||
'title'=>$item['title'],
|
||||
'body'=>$item['description'],
|
||||
'checksum'=>hash('sha512', $item['title'] . $item['description']),
|
||||
'datetime'=>date('Y-m-d', strtotime($item['pubDate']) ?: time()),
|
||||
];
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse Atom
|
||||
*
|
||||
* @param array $feed Atom Object
|
||||
* @return array Parsed Object
|
||||
*/
|
||||
function parse_atom($feed)
|
||||
{
|
||||
$obj = [];
|
||||
if (! array_key_exists('0', $feed['entry'])) {
|
||||
$feed['entry'] = [$feed['entry']];
|
||||
}
|
||||
foreach ($feed['entry'] as $item) {
|
||||
$obj[] = [
|
||||
'title'=>$item['title'],
|
||||
'body'=>$item['content'],
|
||||
'checksum'=>hash('sha512', $item['title'] . $item['content']),
|
||||
'datetime'=>date('Y-m-d', strtotime($item['updated']) ?: time()),
|
||||
];
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new custom notification. Duplicate title+message notifications will not be created.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $message
|
||||
* @param int $severity 0=ok, 1=warning, 2=critical
|
||||
* @param string $source A string describing what created this notification
|
||||
* @param string $date
|
||||
* @return bool
|
||||
*/
|
||||
function new_notification($title, $message, $severity = 0, $source = 'adhoc', $date = null)
|
||||
{
|
||||
$notif = [
|
||||
'title' => $title,
|
||||
'body' => $message,
|
||||
'severity' => $severity,
|
||||
'source' => $source,
|
||||
'checksum' => hash('sha512', $title . $message),
|
||||
'datetime' => date('Y-m-d', is_null($date) ? time() : strtotime($date)),
|
||||
];
|
||||
|
||||
if (dbFetchCell('SELECT 1 FROM `notifications` WHERE `checksum` = ?', [$notif['checksum']]) != 1) {
|
||||
return dbInsert($notif, 'notifications') > 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all notifications with the given title.
|
||||
* This should be used with care.
|
||||
*
|
||||
* @param string $title
|
||||
*/
|
||||
function remove_notification($title)
|
||||
{
|
||||
$ids = dbFetchColumn('SELECT `notifications_id` FROM `notifications` WHERE `title`=?', [$title]);
|
||||
foreach ($ids as $id) {
|
||||
dbDelete('notifications', '`notifications_id`=?', [$id]);
|
||||
dbDelete('notifications_attribs', '`notifications_id`=?', [$id]);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user