Dispatcher Service settings (#11760)

* Poller settings WIP

* Poller settings WIP2

* working on SettingMultiple

* setting multiple working

* settings sent with all required info

* fix translation

* Fix keys

* fix groups setting

* Apply settings to service
fixes and validations for setting

* don't error when no poller_cluster entry exists

* hid tab when no poller cluster entries

* Authorization

* make prod

* daily maintenance toggle should be advanced

* Update schema def
This commit is contained in:
Tony Murray 2020-06-08 08:27:03 -05:00 committed by GitHub
parent 946e90b68d
commit 300645388f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 1134 additions and 86 deletions

View File

@ -59,7 +59,7 @@ class ServiceConfig:
services = PollerConfig(8, 300)
discovery = PollerConfig(16, 21600)
billing = PollerConfig(2, 300, 60)
ping = PollerConfig(1, 120)
ping = PollerConfig(1, 60)
down_retry = 60
update_enabled = True
update_frequency = 86400
@ -118,7 +118,7 @@ class ServiceConfig:
self.alerting.enabled = config.get('service_alerting_enabled', True)
self.alerting.frequency = config.get('service_alerting_frequency', ServiceConfig.alerting.frequency)
self.ping.enabled = config.get('service_ping_enabled', False)
self.ping.frequency = config.get('ping_rrd_step', ServiceConfig.billing.calculate)
self.ping.frequency = config.get('ping_rrd_step', ServiceConfig.ping.frequency)
self.down_retry = config.get('service_poller_down_retry', ServiceConfig.down_retry)
self.log_level = config.get('service_loglevel', ServiceConfig.log_level)
self.update_enabled = config.get('service_update_enabled', ServiceConfig.update_enabled)
@ -155,6 +155,68 @@ class ServiceConfig:
error("Unknown log level {}, must be one of 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'".format(self.log_level))
logging.getLogger().setLevel(logging.INFO)
def load_poller_config(self, db):
try:
settings = {}
cursor = db.query('SELECT * FROM `poller_cluster` WHERE `node_id`=%s', self.node_id)
if cursor.rowcount == 0:
return
for index, setting in enumerate(cursor.fetchone()):
name = cursor.description[index][0]
settings[name] = setting
if settings['poller_name'] is not None:
self.set_name(settings['poller_name'])
if settings['poller_groups'] is not None:
self.group = ServiceConfig.parse_group(settings['poller_groups'])
if settings['poller_enabled'] is not None:
self.poller.enabled = settings['poller_enabled']
if settings['poller_frequency'] is not None:
self.poller.frequency = settings['poller_frequency']
if settings['poller_workers'] is not None:
self.poller.workers = settings['poller_workers']
if settings['poller_down_retry'] is not None:
self.down_retry = settings['poller_down_retry']
if settings['discovery_enabled'] is not None:
self.discovery.enabled = settings['discovery_enabled']
if settings['discovery_frequency'] is not None:
self.discovery.frequency = settings['discovery_frequency']
if settings['discovery_workers'] is not None:
self.discovery.workers = settings['discovery_workers']
if settings['services_enabled'] is not None:
self.services.enabled = settings['services_enabled']
if settings['services_frequency'] is not None:
self.services.frequency = settings['services_frequency']
if settings['services_workers'] is not None:
self.services.workers = settings['services_workers']
if settings['billing_enabled'] is not None:
self.billing.enabled = settings['billing_enabled']
if settings['billing_frequency'] is not None:
self.billing.frequency = settings['billing_frequency']
if settings['billing_calculate_frequency'] is not None:
self.billing.calculate = settings['billing_calculate_frequency']
if settings['alerting_enabled'] is not None:
self.alerting.enabled = settings['alerting_enabled']
if settings['alerting_frequency'] is not None:
self.alerting.frequency = settings['alerting_frequency']
if settings['ping_enabled'] is not None:
self.ping.enabled = settings['ping_enabled']
if settings['ping_frequency'] is not None:
self.ping.frequency = settings['ping_frequency']
if settings['update_enabled'] is not None:
self.update_enabled = settings['update_enabled']
if settings['update_frequency'] is not None:
self.update_frequency = settings['update_frequency']
if settings['loglevel'] is not None:
self.log_level = settings['loglevel']
if settings['watchdog_enabled'] is not None:
self.watchdog_enabled = settings['watchdog_enabled']
if settings['watchdog_log'] is not None:
self.watchdog_logfile = settings['watchdog_log']
except pymysql.err.Error:
warning('Unable to load poller (%s) config', self.node_id)
def _get_config_data(self):
try:
import dotenv
@ -205,11 +267,11 @@ class Service:
def __init__(self):
self.config.populate()
threading.current_thread().name = self.config.name # rename main thread
self.attach_signals()
self._db = LibreNMS.DB(self.config)
self.config.load_poller_config(self._db)
threading.current_thread().name = self.config.name # rename main thread
self.attach_signals()
self._lm = self.create_lock_manager()
self.daily_timer = LibreNMS.RecurringTimer(self.config.update_frequency, self.run_maintenance, 'maintenance')

View File

@ -8,6 +8,7 @@ use App\Models\PollerCluster;
use App\Models\PollerGroup;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use LibreNMS\Config;
class PollerController extends Controller
@ -21,6 +22,7 @@ class PollerController extends Controller
public function logTab(Request $request)
{
$this->authorize('viewAny', PollerCluster::class);
return view('poller.log', [
'current_tab' => 'log',
'filter' => $request->input('filter', 'active')
@ -29,6 +31,7 @@ class PollerController extends Controller
public function groupsTab()
{
$this->authorize('manage', PollerCluster::class);
return view('poller.groups', [
'current_tab' => 'groups',
'poller_groups' => PollerGroup::query()->withCount('devices')->get(),
@ -39,6 +42,7 @@ class PollerController extends Controller
public function pollerTab()
{
$this->authorize('viewAny', PollerCluster::class);
return view('poller.poller', [
'current_tab' => 'poller',
'pollers' => $this->poller(),
@ -46,8 +50,20 @@ class PollerController extends Controller
]);
}
public function settingsTab()
{
$this->authorize('manage', PollerCluster::class);
$pollerClusters = PollerCluster::all()->keyBy('id');
return view('poller.settings', [
'current_tab' => 'settings',
'settings' => $this->pollerSettings($pollerClusters),
'poller_cluster' => $pollerClusters,
]);
}
public function performanceTab()
{
$this->authorize('viewAny', PollerCluster::class);
return view('poller.performance', ['current_tab' => 'performance']);
}
@ -85,4 +101,10 @@ class PollerController extends Controller
return 'success';
}
private function pollerSettings($pollers): Collection
{
$groups = PollerGroup::list();
return $pollers->map->configDefinition($groups);
}
}

View File

@ -0,0 +1,62 @@
<?php
/**
* PollerSettingsController.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 2020 Tony Murray
* @author Tony Murray <murraytony@gmail.com>
*/
namespace App\Http\Controllers;
use App\Models\PollerCluster;
use Illuminate\Http\Request;
class PollerSettingsController extends Controller
{
public function update(Request $request, $id, $setting)
{
$poller = PollerCluster::findOrFail($id);
$this->authorize('update', $poller);
$definition = collect($poller->configDefinition())->keyBy('name');
if (!$definition->has($setting)) {
return response()->json(['error' => 'Invalid setting'], 422);
}
$poller->$setting = $request->get('value');
$poller->save();
return response()->json(['value' => $poller->$setting]);
}
public function destroy($id, $setting)
{
$poller = PollerCluster::findOrFail($id);
$this->authorize('delete', $poller);
$definition = collect($poller->configDefinition())->keyBy('name');
if (!$definition->has($setting)) {
return response()->json(['error' => 'Invalid setting'], 422);
}
$poller->$setting = $definition->get($setting)['default'];
$poller->save();
return response()->json(['value' => $poller->$setting]);
}
}

View File

@ -34,6 +34,201 @@ class PollerCluster extends Model
protected $primaryKey = 'id';
protected $fillable = ['poller_name'];
// ---- Accessors/Mutators ----
public function setPollerGroupsAttribute($groups)
{
$this->attributes['poller_groups'] = is_array($groups) ? implode(',', $groups) : $groups;
}
// ---- Helpers ----
/**
* Get the frontend config definition for this poller
*
* @param \Illuminate\Support\Collection $groups optionally supply full list of poller groups to avoid fetching multiple times
* @return array[]
*/
public function configDefinition($groups = null)
{
if (empty($groups)) {
$groups = PollerGroup::list();
}
return [
[
'name' => 'poller_groups',
'default' => \LibreNMS\Config::get('distributed_poller_group'),
'value' => $poller->poller_groups ?? \LibreNMS\Config::get('distributed_poller_group'),
'type' => 'multiple',
'options' => $groups,
],
[
'name' => 'poller_enabled',
'default' => \LibreNMS\Config::get('service_poller_enabled'),
'value' => (bool)($poller->poller_enabled ?? \LibreNMS\Config::get('service_poller_enabled')),
'type' => 'boolean',
],
[
'name' => 'poller_workers',
'default' => \LibreNMS\Config::get('service_poller_workers'),
'value' => $poller->poller_workers ?? \LibreNMS\Config::get('service_poller_workers'),
'type' => 'integer',
'units' => 'workers',
],
[
'name' => 'poller_frequency',
'default' => \LibreNMS\Config::get('service_poller_frequency'),
'value' => $poller->poller_workers ?? \LibreNMS\Config::get('service_poller_frequency'),
'type' => 'integer',
'units' => 'seconds',
'advanced' => true,
],
[
'name' => 'poller_down_retry',
'default' => \LibreNMS\Config::get('service_poller_down_retry'),
'value' => $poller->poller_down_retry ?? \LibreNMS\Config::get('service_poller_down_retry'),
'type' => 'integer',
'units' => 'seconds',
],
[
'name' => 'discovery_enabled',
'default' => \LibreNMS\Config::get('service_discovery_enabled'),
'value' => (bool)($poller->discovery_enabled ?? \LibreNMS\Config::get('service_discovery_enabled')),
'type' => 'boolean',
],
[
'name' => 'discovery_workers',
'default' => \LibreNMS\Config::get('service_discovery_workers'),
'value' => $poller->discovery_workers ?? \LibreNMS\Config::get('service_discovery_workers'),
'type' => 'integer',
'units' => 'workers',
],
[
'name' => 'discovery_frequency',
'default' => \LibreNMS\Config::get('service_discovery_frequency'),
'value' => $poller->discovery_frequency ?? \LibreNMS\Config::get('service_discovery_frequency'),
'type' => 'integer',
'units' => 'seconds',
'advanced' => true,
],
[
'name' => 'services_enabled',
'default' => \LibreNMS\Config::get('service_services_enabled'),
'value' => (bool)($poller->services_enabled ?? \LibreNMS\Config::get('service_services_enabled')),
'type' => 'boolean',
],
[
'name' => 'services_workers',
'default' => \LibreNMS\Config::get('service_services_workers'),
'value' => $poller->services_workers ?? \LibreNMS\Config::get('service_services_workers'),
'type' => 'integer',
'units' => 'workers',
],
[
'name' => 'services_frequency',
'default' => \LibreNMS\Config::get('service_services_frequency'),
'value' => $poller->services_frequency ?? \LibreNMS\Config::get('service_services_frequency'),
'type' => 'integer',
'units' => 'seconds',
'advanced' => true,
],
[
'name' => 'billing_enabled',
'default' => \LibreNMS\Config::get('service_billing_enabled'),
'value' => (bool)($poller->billing_enabled ?? \LibreNMS\Config::get('service_billing_enabled')),
'type' => 'boolean',
],
[
'name' => 'billing_frequency',
'default' => \LibreNMS\Config::get('service_billing_frequency'),
'value' => $poller->billing_frequency ?? \LibreNMS\Config::get('service_billing_frequency'),
'type' => 'integer',
'units' => 'seconds',
'advanced' => true,
],
[
'name' => 'billing_calculate_frequency',
'default' => \LibreNMS\Config::get('service_billing_calculate_frequency'),
'value' => $poller->billing_calculate_frequency ?? \LibreNMS\Config::get('service_billing_calculate_frequency'),
'type' => 'integer',
'units' => 'seconds',
'advanced' => true,
],
[
'name' => 'alerting_enabled',
'default' => \LibreNMS\Config::get('service_alerting_enabled'),
'value' => (bool)($poller->alerting_enabled ?? \LibreNMS\Config::get('service_alerting_enabled')),
'type' => 'boolean',
],
[
'name' => 'alerting_frequency',
'default' => \LibreNMS\Config::get('service_alerting_frequency'),
'value' => $poller->alerting_frequency ?? \LibreNMS\Config::get('service_alerting_frequency'),
'type' => 'integer',
'units' => 'seconds',
'advanced' => true,
],
[
'name' => 'ping_enabled',
'default' => \LibreNMS\Config::get('service_ping_enabled'),
'value' => (bool)($poller->ping_enabled ?? \LibreNMS\Config::get('service_ping_enabled')),
'type' => 'boolean',
],
[
'name' => 'ping_frequency',
'default' => \LibreNMS\Config::get('ping_rrd_step'),
'value' => $poller->ping_frequency ?? \LibreNMS\Config::get('ping_rrd_step'),
'type' => 'integer',
'units' => 'seconds',
'advanced' => true,
],
[
'name' => 'update_enabled',
'default' => \LibreNMS\Config::get('service_update_enabled'),
'value' => (bool)($poller->update_enabled ?? \LibreNMS\Config::get('service_update_enabled')),
'type' => 'boolean',
'advanced' => true,
],
[
'name' => 'update_frequency',
'default' => \LibreNMS\Config::get('service_update_frequency'),
'value' => $poller->update_frequency ?? \LibreNMS\Config::get('service_update_frequency'),
'type' => 'integer',
'units' => 'seconds',
'advanced' => true,
],
[
'name' => 'loglevel',
'default' => \LibreNMS\Config::get('service_loglevel'),
'value' => $poller->loglevel ?? \LibreNMS\Config::get('service_loglevel'),
'type' => 'select',
'options' => [
'DEBUG' => 'DEBUG',
'INFO' => 'INFO',
'WARNING' => 'WARNING',
'ERROR' => 'ERROR',
'CRITICAL' => 'CRITICAL'
],
],
[
'name' => 'watchdog_enabled',
'default' => \LibreNMS\Config::get('service_watchdog_enabled'),
'value' => (bool)($poller->watchdog_enabled ?? \LibreNMS\Config::get('service_watchdog_enabled')),
'type' => 'boolean',
],
[
'name' => 'watchdog_log',
'default' => \LibreNMS\Config::get('log_file'),
'value' => $poller->watchdog_log ?? \LibreNMS\Config::get('log_file'),
'type' => 'text',
'advanced' => true,
],
];
}
// ---- Relationships ----
public function stats()
{
return $this->hasMany(\App\Models\PollerClusterStat::class, 'parent_poller', 'id');

View File

@ -41,12 +41,17 @@ class PollerGroup extends Model
parent::boot();
static::deleting(function (PollerGroup $pollergroup) {
// handle device pollergroup fallback to default poller
// handle device poller group fallback to default poller
$default_poller_id = \LibreNMS\Config::get('default_poller_group');
$pollergroup->devices()->update(['poller_group' => $default_poller_id]);
});
}
public static function list()
{
return self::query()->pluck('group_name', 'id')->prepend(__('General'), 0);
}
public function devices()
{
return $this->hasMany(\App\Models\Device::class, 'poller_group', 'id');

View File

@ -0,0 +1,106 @@
<?php
namespace App\Policies;
use App\Models\User;
use App\Models\PollerCluster;
use Illuminate\Auth\Access\HandlesAuthorization;
class PollerClusterPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any poller clusters.
*
* @param \App\Models\User $user
* @return mixed
*/
public function viewAny(User $user)
{
return $user->hasGlobalAdmin();
}
/**
* Determine whether the user can view the poller cluster.
*
* @param \App\Models\User $user
* @param \App\Models\PollerCluster $pollerCluster
* @return mixed
*/
public function view(User $user, PollerCluster $pollerCluster)
{
//
}
/**
* Determine whether the user can create poller clusters.
*
* @param \App\Models\User $user
* @return mixed
*/
public function create(User $user)
{
//
}
/**
* Determine whether the user can update the poller cluster.
*
* @param \App\Models\User $user
* @param \App\Models\PollerCluster $pollerCluster
* @return mixed
*/
public function update(User $user, PollerCluster $pollerCluster)
{
return $user->isAdmin();
}
/**
* Determine whether the user can delete the poller cluster.
*
* @param \App\Models\User $user
* @param \App\Models\PollerCluster $pollerCluster
* @return mixed
*/
public function delete(User $user, PollerCluster $pollerCluster)
{
return $user->isAdmin();
}
/**
* Determine whether the user can restore the poller cluster.
*
* @param \App\Models\User $user
* @param \App\Models\PollerCluster $pollerCluster
* @return mixed
*/
public function restore(User $user, PollerCluster $pollerCluster)
{
//
}
/**
* Determine whether the user can permanently delete the poller cluster.
*
* @param \App\Models\User $user
* @param \App\Models\PollerCluster $pollerCluster
* @return mixed
*/
public function forceDelete(User $user, PollerCluster $pollerCluster)
{
//
}
/**
* Determine whether the user can manage the poller cluster.
*
* @param \App\Models\User $user
* @param \App\Models\PollerCluster $pollerCluster
* @return mixed
*/
public function manage(User $user)
{
return $user->isAdmin();
}
}

View File

@ -18,6 +18,7 @@ class AuthServiceProvider extends ServiceProvider
\App\Models\User::class => \App\Policies\UserPolicy::class,
\App\Models\Device::class => \App\Policies\DevicePolicy::class,
\App\Models\DeviceGroup::class => \App\Policies\DeviceGroupPolicy::class,
\App\Models\PollerCluster::class => \App\Policies\PollerClusterPolicy::class,
\App\Models\Port::class => \App\Policies\PortPolicy::class,
];

View File

@ -0,0 +1,76 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class PollerClusterSettings extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('poller_cluster', function (Blueprint $table) {
$table->boolean('poller_enabled')->nullable();
$table->integer('poller_frequency')->nullable();
$table->integer('poller_workers')->nullable();
$table->integer('poller_down_retry')->nullable();
$table->boolean('discovery_enabled')->nullable();
$table->integer('discovery_frequency')->nullable();
$table->integer('discovery_workers')->nullable();
$table->boolean('services_enabled')->nullable();
$table->integer('services_frequency')->nullable();
$table->integer('services_workers')->nullable();
$table->boolean('billing_enabled')->nullable();
$table->integer('billing_frequency')->nullable();
$table->integer('billing_calculate_frequency')->nullable();
$table->boolean('alerting_enabled')->nullable();
$table->integer('alerting_frequency')->nullable();
$table->boolean('ping_enabled')->nullable();
$table->integer('ping_frequency')->nullable();
$table->boolean('update_enabled')->nullable();
$table->integer('update_frequency')->nullable();
$table->string('loglevel', 8)->nullable();
$table->boolean('watchdog_enabled')->nullable();
$table->string('watchdog_log')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('poller_cluster', function (Blueprint $table) {
$table->dropColumn([
'poller_enabled',
'poller_frequency',
'poller_workers',
'poller_down_retry',
'discovery_enabled',
'discovery_frequency',
'discovery_workers',
'services_enabled',
'services_frequency',
'services_workers',
'billing_enabled',
'billing_frequency',
'billing_calculate_frequency',
'alerting_enabled',
'alerting_frequency',
'ping_enabled',
'ping_frequency',
'update_enabled',
'update_frequency',
'loglevel',
'watchdog_enabled',
'watchdog_log'
]);
});
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,12 +1,12 @@
{
"/js/app.js": "/js/app.js?id=63189b3aa20a0043cc38",
"/css/app.css": "/css/app.css?id=5da3bf931f2f95a17884",
"/js/app.js": "/js/app.js?id=4d14830ae8a6dfe54711",
"/css/app.css": "/css/app.css?id=ffec4165a9c98d892a32",
"/js/manifest.js": "/js/manifest.js?id=3c768977c2574a34506e",
"/js/vendor.js": "/js/vendor.js?id=29212a758157c575d7f8",
"/js/lang/de.js": "/js/lang/de.js?id=d095b82e431906ea9b0d",
"/js/lang/en.js": "/js/lang/en.js?id=de1f054e216295f509a3",
"/js/lang/fr.js": "/js/lang/fr.js?id=8e94b3986dc6fce2f3e5",
"/js/lang/ru.js": "/js/lang/ru.js?id=dc6cb9314c4903b00501",
"/js/lang/uk.js": "/js/lang/uk.js?id=669c4652f87dfd31ce7a",
"/js/lang/zh-TW.js": "/js/lang/zh-TW.js?id=82383ffa183494fa4a14"
"/js/vendor.js": "/js/vendor.js?id=c0e0ebbfd027a8baefb4",
"/js/lang/de.js": "/js/lang/de.js?id=b96f6bf4d57d6614e18f",
"/js/lang/en.js": "/js/lang/en.js?id=5e41e02c1021ebebaf1d",
"/js/lang/fr.js": "/js/lang/fr.js?id=4f329163511445d92a17",
"/js/lang/ru.js": "/js/lang/ru.js?id=f818cb1ab0a804b7bd3a",
"/js/lang/uk.js": "/js/lang/uk.js?id=c8d4937e3ca47b60b7ac",
"/js/lang/zh-TW.js": "/js/lang/zh-TW.js?id=a22406ceaffc7c936ed8"
}

View File

@ -4515,6 +4515,172 @@
"default": true,
"type": "boolean"
},
"service_poller_enabled": {
"default": true,
"group": "poller",
"section": "distributed",
"order": 5,
"type": "boolean"
},
"service_poller_workers": {
"default": 24,
"group": "poller",
"section": "distributed",
"order": 6,
"type": "integer",
"units": "Workers"
},
"service_poller_frequency": {
"default": 300,
"group": "poller",
"section": "distributed",
"order": 7,
"type": "integer",
"units": "Seconds"
},
"service_poller_down_retry": {
"default": 60,
"group": "poller",
"section": "distributed",
"order": 8,
"type": "integer",
"units": "Seconds"
},
"service_discovery_enabled": {
"default": true,
"group": "poller",
"section": "distributed",
"order": 9,
"type": "boolean"
},
"service_discovery_workers": {
"default": 16,
"group": "poller",
"section": "distributed",
"order": 10,
"type": "integer",
"units": "Workers"
},
"service_discovery_frequency": {
"default": 21600,
"group": "poller",
"section": "distributed",
"order": 11,
"type": "integer",
"units": "Seconds"
},
"service_services_enabled": {
"default": true,
"group": "poller",
"section": "distributed",
"order": 12,
"type": "boolean"
},
"service_services_workers": {
"default": 8,
"group": "poller",
"section": "distributed",
"order": 13,
"type": "integer",
"units": "Workers"
},
"service_services_frequency": {
"default": 300,
"group": "poller",
"section": "distributed",
"order": 14,
"type": "integer",
"units": "Seconds"
},
"service_billing_enabled": {
"default": true,
"group": "poller",
"section": "distributed",
"order": 15,
"type": "boolean"
},
"service_billing_frequency": {
"default": 300,
"group": "poller",
"section": "distributed",
"order": 16,
"type": "integer",
"units": "Seconds"
},
"service_billing_calculate_frequency": {
"default": 60,
"group": "poller",
"section": "distributed",
"order": 17,
"type": "integer",
"units": "Seconds"
},
"service_alerting_enabled": {
"default": true,
"group": "poller",
"section": "distributed",
"order": 18,
"type": "boolean"
},
"service_alerting_frequency": {
"default": 60,
"group": "poller",
"section": "distributed",
"order": 19,
"type": "integer",
"units": "Seconds"
},
"service_ping_enabled": {
"default": false,
"group": "poller",
"section": "distributed",
"order": 20,
"type": "boolean"
},
"ping_rrd_step": {
"default": 60,
"group": "poller",
"section": "distributed",
"order": 21,
"type": "integer",
"units": "Seconds"
},
"service_update_enabled": {
"default": true,
"group": "poller",
"section": "distributed",
"order": 22,
"type": "boolean"
},
"service_update_frequency": {
"default": 86400,
"group": "poller",
"section": "distributed",
"order": 23,
"type": "integer",
"units": "Seconds"
},
"service_loglevel": {
"default": "INFO",
"group": "poller",
"section": "distributed",
"order": 24,
"type": "select",
"options": {
"DEBUG": "DEBUG",
"INFO": "INFO",
"WARNING": "WARNING",
"ERROR": "ERROR",
"CRITICAL": "CRITICAL"
}
},
"service_watchdog_enabled": {
"default": false,
"group": "poller",
"section": "distributed",
"order": 25,
"type": "boolean"
},
"sfdp": {
"default": "/usr/bin/sfdp",
"group": "external",
@ -4639,8 +4805,12 @@
"type": "text"
},
"smokeping.pings": {
"default": 20,
"type": "integer"
"default": 5,
"group": "external",
"section": "smokeping",
"order": 2,
"type": "integer",
"units": "pings"
},
"snmp.community": {
"group": "poller",
@ -5023,14 +5193,6 @@
"order": 1,
"type": "text"
},
"smokeping.pings": {
"default": 5,
"group": "external",
"section": "smokeping",
"order": 2,
"type": "integer",
"units": "pings"
},
"smokeping.url": {
"group": "external",
"section": "smokeping",

View File

@ -1326,6 +1326,28 @@ poller_cluster:
- { Field: poller_groups, Type: varchar(255), 'Null': false, Extra: '', Default: '' }
- { Field: last_report, Type: datetime, 'Null': false, Extra: '' }
- { Field: master, Type: tinyint, 'Null': false, Extra: '' }
- { Field: poller_enabled, Type: tinyint, 'Null': true, Extra: '' }
- { Field: poller_frequency, Type: int, 'Null': true, Extra: '' }
- { Field: poller_workers, Type: int, 'Null': true, Extra: '' }
- { Field: poller_down_retry, Type: int, 'Null': true, Extra: '' }
- { Field: discovery_enabled, Type: tinyint, 'Null': true, Extra: '' }
- { Field: discovery_frequency, Type: int, 'Null': true, Extra: '' }
- { Field: discovery_workers, Type: int, 'Null': true, Extra: '' }
- { Field: services_enabled, Type: tinyint, 'Null': true, Extra: '' }
- { Field: services_frequency, Type: int, 'Null': true, Extra: '' }
- { Field: services_workers, Type: int, 'Null': true, Extra: '' }
- { Field: billing_enabled, Type: tinyint, 'Null': true, Extra: '' }
- { Field: billing_frequency, Type: int, 'Null': true, Extra: '' }
- { Field: billing_calculate_frequency, Type: int, 'Null': true, Extra: '' }
- { Field: alerting_enabled, Type: tinyint, 'Null': true, Extra: '' }
- { Field: alerting_frequency, Type: int, 'Null': true, Extra: '' }
- { Field: ping_enabled, Type: tinyint, 'Null': true, Extra: '' }
- { Field: ping_frequency, Type: int, 'Null': true, Extra: '' }
- { Field: update_enabled, Type: tinyint, 'Null': true, Extra: '' }
- { Field: update_frequency, Type: int, 'Null': true, Extra: '' }
- { Field: loglevel, Type: varchar(8), 'Null': true, Extra: '' }
- { Field: watchdog_enabled, Type: tinyint, 'Null': true, Extra: '' }
- { Field: watchdog_log, Type: varchar(255), 'Null': true, Extra: '' }
Indexes:
PRIMARY: { Name: PRIMARY, Columns: [id], Unique: true, Type: BTREE }
poller_cluster_node_id_unique: { Name: poller_cluster_node_id_unique, Columns: [node_id], Unique: true, Type: BTREE }

67
package-lock.json generated
View File

@ -1464,6 +1464,11 @@
}
}
},
"babel-helper-vue-jsx-merge-props": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz",
"integrity": "sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg=="
},
"babel-loader": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz",
@ -1716,9 +1721,9 @@
"dev": true
},
"bootstrap": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.4.1.tgz",
"integrity": "sha512-tbx5cHubwE6e2ZG7nqM3g/FZ5PQEDMWmMGNrCUBVRPHXTJaH7CBDdsLeu3eCh3B1tzAxTnAbtmrzvWEvT2NNEA==",
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.5.0.tgz",
"integrity": "sha512-Z93QoXvodoVslA+PWNdk23Hze4RBYIkpb5h8I2HY2Tu2h7A0LpAgLcyrhrSUyo2/Oxm2l1fRZPs1e5hnxnliXA==",
"dev": true
},
"brace-expansion": {
@ -2017,9 +2022,9 @@
"dev": true
},
"chokidar": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz",
"integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==",
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz",
"integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==",
"dev": true,
"requires": {
"anymatch": "~3.1.1",
@ -2029,7 +2034,7 @@
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.3.0"
"readdirp": "~3.4.0"
},
"dependencies": {
"anymatch": {
@ -2067,9 +2072,9 @@
}
},
"fsevents": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz",
"integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==",
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
"integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
"dev": true,
"optional": true
},
@ -2098,12 +2103,12 @@
"dev": true
},
"readdirp": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz",
"integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==",
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz",
"integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==",
"dev": true,
"requires": {
"picomatch": "^2.0.7"
"picomatch": "^2.2.1"
}
},
"to-regex-range": {
@ -5401,9 +5406,9 @@
"dev": true
},
"jquery": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.0.tgz",
"integrity": "sha512-Xb7SVYMvygPxbFMpTFQiHh1J7HClEaThguL15N/Gg37Lri/qKyhRGZYzHRyLH8Stq3Aow0LsHO2O2ci86fCrNQ==",
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz",
"integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==",
"dev": true
},
"js-levenshtein": {
@ -7994,9 +7999,9 @@
"dev": true
},
"sass": {
"version": "1.26.3",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.26.3.tgz",
"integrity": "sha512-5NMHI1+YFYw4sN3yfKjpLuV9B5l7MqQ6FlkTcC4FT+oHbBRUZoSjHrrt/mE0nFXJyY2kQtU9ou9HxvFVjLFuuw==",
"version": "1.26.5",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.26.5.tgz",
"integrity": "sha512-FG2swzaZUiX53YzZSjSakzvGtlds0lcbF+URuU9mxOv7WBh7NhXEVDa4kPKN4hN6fC2TkOTOKqiqp6d53N9X5Q==",
"dev": true,
"requires": {
"chokidar": ">=2.0.0 <4.0.0"
@ -9388,9 +9393,9 @@
"dev": true
},
"vue-i18n": {
"version": "8.15.5",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-8.15.5.tgz",
"integrity": "sha512-lIej02+w8lP0k1PEN1xtXqKpQ1hDh17zvDF+7Oc2qJi+cTMDlfPM771w4euVaHO67AxEz4WL9MIgkyn3tkeCtQ=="
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-8.18.0.tgz",
"integrity": "sha512-7DVu8CfoyPoCRWB5TOqfw2kIhk2umagsri4L1V1c0s+lHUC0VbQYoucFprHUELyHU9Legqzt/FxyLGDpBOlX9w=="
},
"vue-js-toggle-button": {
"version": "1.3.3",
@ -9410,15 +9415,25 @@
"vue-style-loader": "^4.1.0"
}
},
"vue-multiselect": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/vue-multiselect/-/vue-multiselect-2.1.6.tgz",
"integrity": "sha512-s7jmZPlm9FeueJg1RwJtnE9KNPtME/7C8uRWSfp9/yEN4M8XcS/d+bddoyVwVnvFyRh9msFo0HWeW0vTL8Qv+w=="
},
"vue-nav-tabs": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/vue-nav-tabs/-/vue-nav-tabs-0.5.7.tgz",
"integrity": "sha512-Oqq7qnb0/JPAVSqM0haQ9TdEZaTbQq20TVn5ZCmBOu8m9qju9bI8cDdtWGHXSiMkpmhzsT83ybRb7S/+UYXRsw=="
},
"vue-resize": {
"version": "0.4.5",
"resolved": "https://registry.npmjs.org/vue-resize/-/vue-resize-0.4.5.tgz",
"integrity": "sha512-bhP7MlgJQ8TIkZJXAfDf78uJO+mEI3CaLABLjv0WNzr4CcGRGPIAItyWYnP6LsPA4Oq0WE+suidNs6dgpO4RHg=="
},
"vue-select": {
"version": "3.9.4",
"resolved": "https://registry.npmjs.org/vue-select/-/vue-select-3.9.4.tgz",
"integrity": "sha512-91vvz2WcmlMTzKI2LbA+jxhHjVwuFhGZXV20F7XxIeF/+A7PLfBTu8mCHh51ELF/UF6YsXAvd8t7BUhx8sydzQ=="
"version": "3.10.3",
"resolved": "https://registry.npmjs.org/vue-select/-/vue-select-3.10.3.tgz",
"integrity": "sha512-SgLmiSwnJwT2erxjq42AA1iTzu1uqhA5MPuF4UDtGot5YSgJLKy71H0LO0hHaBpIjb7d/nJHiufYKcrSakaupw=="
},
"vue-style-loader": {
"version": "4.1.2",

View File

@ -13,24 +13,27 @@
},
"devDependencies": {
"axios": "^0.19.2",
"bootstrap": "^4.4.1",
"bootstrap": "^4.5.0",
"cross-env": "^5.2.1",
"jquery": "^3.5",
"jquery": "^3.5.1",
"laravel-mix": "^4.1.4",
"lodash": "^4.17.15",
"popper.js": "^1.16.1",
"resolve-url-loader": "^2.3.1",
"sass": "^1.26.3",
"sass": "^1.26.5",
"sass-loader": "^7.3.1",
"vue": "^2.6.11",
"vue-template-compiler": "^2.6.11"
},
"dependencies": {
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"es6-object-assign": "^1.1.0",
"v-tooltip": "^2.0.3",
"vue-i18n": "^8.15.5",
"vue-i18n": "^8.18.0",
"vue-js-toggle-button": "^1.3.3",
"vue-select": "^3.9.4",
"vue-multiselect": "^2.1.6",
"vue-nav-tabs": "^0.5.7",
"vue-select": "^3.10.3",
"vuedraggable": "^2.23.2"
}
}

View File

@ -30,11 +30,17 @@ Vue.use(VTooltip);
import vSelect from 'vue-select'
Vue.component('v-select', vSelect);
Vue.mixin({
methods: {
route: route
}
});
import Multiselect from 'vue-multiselect'
Vue.component('multiselect', Multiselect)
import VueTabs from 'vue-nav-tabs'
Vue.use(VueTabs)
// Vue.mixin({
// methods: {
// route: route
// }
// });
Vue.filter('ucfirst', function (value) {
if (!value) return '';

View File

@ -1,7 +1,7 @@
<!--
- LibrenmsSetting.vue
-
- 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
@ -26,9 +26,9 @@
<div :class="['form-group', 'has-feedback', setting.class, feedback]">
<label :for="setting.name" class="col-sm-5 control-label" v-tooltip="{ content: setting.name }">
{{ getDescription() }}
<span v-if="setting.units !== null">({{ setting.units }})</span>
<span v-if="setting.units">({{ getUnits() }})</span>
</label>
<div class="col-sm-5" v-tooltip="{ content: setting.disabled ? $t('settings.readonly') : false }">
<div class="col-sm-5" v-tooltip="{ content: setting.disabled ? $t(this.prefix + '.readonly') : false }">
<component :is="getComponent()"
:value="value"
:name="setting.name"
@ -53,7 +53,9 @@
export default {
name: "LibrenmsSetting",
props: {
'setting': {type: Object, required: true}
'setting': {type: Object, required: true},
'prefix': {type: String, default: 'settings'},
'id': {required: false}
},
data() {
return {
@ -63,7 +65,7 @@
},
methods: {
persistValue(value) {
axios.put(route('settings.update', this.setting.name), {value: value})
axios.put(route(this.prefix + '.update', this.getRouteParams()), {value: value})
.then((response) => {
this.value = response.data.value;
this.$emit('setting-updated', {name: this.setting.name, value: this.value});
@ -91,7 +93,7 @@
this.persistValue(value)
}, 500),
changeValue(value) {
if (['select', 'boolean'].includes(this.setting.type)) {
if (['select', 'boolean', 'multiple'].includes(this.setting.type)) {
// no need to debounce
this.persistValue(value);
} else {
@ -99,24 +101,28 @@
}
this.value = value
},
getUnits() {
let key = this.prefix + '.units.' + this.setting.units;
return this.$te(key) ? this.$t(key) : this.setting.units
},
getDescription() {
let key = 'settings.settings.' + this.setting.name + '.description';
let key = this.prefix + '.settings.' + this.setting.name + '.description';
return (this.$te(key) || this.$te(key, this.$i18n.fallbackLocale)) ? this.$t(key) : this.setting.name;
},
getHelp() {
let help = this.$t('settings.settings.' + this.setting.name + '.help');
let help = this.$t(this.prefix + '.settings.' + this.setting.name + '.help');
if (this.setting.overridden) {
help += "</p><p>" + this.$t('settings.readonly')
help += "</p><p>" + this.$t(this.prefix + '.readonly')
}
return help
},
hasHelp() {
var key = 'settings.settings.' + this.setting.name + '.help';
let key = this.prefix + '.settings.' + this.setting.name + '.help';
return this.$te(key) || this.$te(key, this.$i18n.fallbackLocale)
},
resetToDefault() {
axios.delete(route('settings.destroy', this.setting.name))
axios.delete(route(this.prefix + '.destroy', this.getRouteParams()))
.then((response) => {
this.value = response.data.value;
this.feedback = 'has-success';
@ -138,6 +144,13 @@
showUndo() {
return !_.isEqual(this.setting.value, this.value);
},
getRouteParams() {
let parameters = [this.setting.name];
if (this.id) {
parameters.unshift(this.id);
}
return parameters;
},
getComponent() {
// snake to studly
const component = 'Setting' + this.setting.type.toString()

View File

@ -0,0 +1,77 @@
<!--
- PollerSettings.vue
-
- 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 2020 Tony Murray
- @author Tony Murray <murraytony@gmail.com>
-->
<template>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
{{ $t('Poller Settings') }}
<span class="pull-right">Advanced <toggle-button v-model="advanced"></toggle-button></span>
</h3>
</div>
<div class="panel-body">
<vue-tabs direction="vertical" type="pills">
<v-tab :title="poller.poller_name" v-for="(poller, id) in pollers" :key="id">
<div class="setting-container clearfix"
v-for="setting in settings[id]"
v-if="!setting.advanced || advanced"
:key="setting.name">
<librenms-setting
prefix="poller.settings"
:setting='setting'
:id="poller.id"
></librenms-setting>
</div>
</v-tab>
</vue-tabs>
</div>
</div>
</template>
<script>
export default {
name: "PollerSettings",
props: {
'pollers': Object,
'settings': Object
},
data() {
return {
advanced: false
}
},
}
</script>
<style>
.tab-content {
width: 100%;
}
</style>
<style scoped>
.setting-container {
margin-bottom: 10px;
}
</style>

View File

@ -0,0 +1,75 @@
<!--
- SettingMultiple.vue
-
- Setting for multiple select option. Value is expected to be a comma delimited string
-
- 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 2020 Tony Murray
- @author Tony Murray <murraytony@gmail.com>
-->
<template>
<div>
<multiselect
@input="$emit('input', mutateInputEvent($event))"
:value="formattedValue"
:required="required"
:disabled="disabled"
:name="name"
label="label"
track-by="value"
:options="formattedOptions"
:allow-empty="false"
:multiple="true"
>
</multiselect>
</div>
</template>
<script>
import BaseSetting from "./BaseSetting";
export default {
name: "SettingMultiple",
mixins: [BaseSetting],
computed: {
formattedValue() {
if (this.value === undefined) {
return []
}
let values = this.value.toString().split(',')
return this.formatOptions(_.pick(this.options, ...values))
},
formattedOptions() {
return this.formatOptions(this.options)
}
},
methods: {
formatOptions(options) {
return Object.entries(options).map(([k, v]) => ({label: v, value: k}))
},
mutateInputEvent(options) {
return options.map(option => option.value).join(',');
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,103 @@
<?php
return [
'settings' => [
'settings' => [
'poller_groups' => [
'description' => 'Assigned Groups',
'help' => 'This node will only take action on devices in these poller groups.'
],
'poller_enabled' => [
'description' => 'Poller Enabled',
'help' => 'Enable poller workers on this node.'
],
'poller_workers' => [
'description' => 'Poller Workers',
'help' => 'Amount of poller workers to spawn on this node.'
],
'poller_frequency' => [
'description' => 'Poller Frequency',
'help' => 'How often to to poll devices on this node. Warning! Changing this is not recommended. See docs for more info.'
],
'poller_down_retry' => [
'description' => 'Device Down Retry',
'help' => 'If a device is down when polling is attempted on this node. This is the amount of time to wait before retrying.'
],
'discovery_enabled' => [
'description' => 'Discovery Enabled',
'help' => 'Enable discovery workers on this node.'
],
'discovery_workers' => [
'description' => 'Discovery Workers',
'help' => 'Amount of discovery workers to run on this node. Setting too high can cause overload.'
],
'discovery_frequency' => [
'description' => 'Discovery Frequency',
'help' => 'How often to run device discovery on this node. Default is 4 times a day.'
],
'services_enabled' => [
'description' => 'Services Enabled',
'help' => 'Enable services workers on this node.'
],
'services_workers' => [
'description' => 'Services Workers',
'help' => 'Amount of services workers on this node.'
],
'services_frequency' => [
'description' => 'Services Frequency',
'help' => 'How often to run services on this node. This should match poller frequency.'
],
'billing_enabled' => [
'description' => 'Billing Enabled',
'help' => 'Enable billing workers on this node.'
],
'billing_frequency' => [
'description' => 'Billing Frequency',
'help' => 'How often to collect billing data on this node.'
],
'billing_calculate_frequency' => [
'description' => 'Billing Calculate Frequency',
'help' => 'How often to calculate bill usage on this node.'
],
'alerting_enabled' => [
'description' => 'Alerting Enabled',
'help' => 'Enable the alerting worker on this node.'
],
'alerting_frequency' => [
'description' => 'Alerting Frequency',
'help' => 'How often alert rules are checked on this node. Note that data is only updated based on poller frequency.'
],
'ping_enabled' => [
'description' => 'Fast Ping Enabled',
'help' => 'Fast Ping just pings devices to check if they are up or down'
],
'ping_frequency' => [
'description' => 'Ping Frequency',
'help' => 'How often to check ping on this node. Warning! If you change this you must make additional changes. Check the Fast Ping docs.'
],
'update_enabled' => [
'description' => 'Daily Maintenance Enabled',
'help' => 'Run daily.sh maintenance script and restart the dispatcher service afterwards.'
],
'update_frequency' => [
'description' => 'Maintenance Frequency',
'help' => 'How often to run daily maintenance on this node. Default is 1 Day. It is highly suggested not to change this.'
],
'loglevel' => [
'description' => 'Log Level',
'help' => 'Log level of the dispatch service.'
],
'watchdog_enabled' => [
'description' => 'Watchdog Enabled',
'help' => 'Watchdog monitors the log file and restarts the service it it has not been updated'
],
'watchdog_log' => [
'description' => 'Log File to Watch',
'help' => 'Default is the LibreNMS log file.'
],
],
'units' => [
'seconds' => 'Seconds',
'workers' => 'Workers',
],
],
];

View File

@ -9,3 +9,9 @@
// Vue Select
@import "~vue-select/src/scss/vue-select.scss";
// Vue Multiselect
@import "~vue-multiselect/dist/vue-multiselect.min.css";
// Vue Tabs
@import '~vue-nav-tabs/themes/vue-tabs.css';

View File

@ -540,11 +540,12 @@
aria-hidden="true"></i> @lang('Auth History')</a></li>
<li role="presentation" class="divider"></li>
<li class="dropdown-submenu">
<a href="{{ url('poller') }}"><i class="fa fa-th-large fa-fw fa-lg" aria-hidden="true"></i> @lang('Poller')</a>
<a href="{{ route('poller.index') }}"><i class="fa fa-th-large fa-fw fa-lg" aria-hidden="true"></i> @lang('Poller')</a>
<ul class="dropdown-menu">
<li><a href="{{ route('poller') }}"><i class="fa fa-th-large fa-fw fa-lg" aria-hidden="true"></i> @lang('Poller')</a></li>
<li><a href="{{ route('poller.index') }}"><i class="fa fa-th-large fa-fw fa-lg" aria-hidden="true"></i> @lang('Poller')</a></li>
@config('distributed_poller')
<li><a href="{{ route('poller.groups') }}"><i class="fa fa-th fa-fw fa-lg" aria-hidden="true"></i> @lang('Groups')</a></li>
<li><a href="{{ route('poller.settings') }}"><i class="fa fa-gears fa-fw fa-lg" aria-hidden="true"></i> @lang('Settings')</a></li>
@endconfig
<li><a href="{{ route('poller.performance') }}"><i class="fa fa-line-chart fa-fw fa-lg" aria-hidden="true"></i> @lang('Performance')</a></li>
<li><a href="{{ route('poller.log') }}"><i class="fa fa-file-text fa-fw fa-lg" aria-hidden="true"></i> @lang('Log')</a></li>

View File

@ -6,7 +6,6 @@
@parent
<br />
<button type="button" class="btn btn-primary btn-sm" data-toggle="modal" data-target="#poller-groups">@lang('Create new poller group')</button>
<br /><br />
<div class="table-responsive">

View File

@ -6,12 +6,17 @@
<div class="col-md-12">
<ul class="nav nav-tabs">
<li role="presentation" @if( $current_tab == 'poller' ) class="active" @endif>
<a href="{{ route('poller') }}"><i class="fa fa-th-large fa-lg icon-theme" aria-hidden="true"></i> @lang('Poller')</a>
<a href="{{ route('poller.index') }}"><i class="fa fa-th-large fa-lg icon-theme" aria-hidden="true"></i> @lang('Poller')</a>
</li>
@config('distributed_poller')
<li role="presentation" @if( $current_tab == 'groups' ) class="active" @endif>
<a href="{{ route('poller.groups') }}"><i class="fa fa-th fa-lg icon-theme" aria-hidden="true"></i> @lang('Groups')</a>
</li>
@if(\App\Models\PollerCluster::exists())
<li role="presentation" @if( $current_tab == 'settings' ) class="active" @endif>
<a href="{{ route('poller.settings') }}"><i class="fa fa-gears fa-lg icon-theme" aria-hidden="true"></i> @lang('Settings')</a>
</li>
@endif
@endconfig
<li role="presentation" @if( $current_tab == 'performance' ) class="active" @endif>
<a href="{{ route('poller.performance') }}"><i class="fa fa-line-chart fa-lg icon-theme" aria-hidden="true"></i> @lang('Performance')</a>
@ -20,6 +25,7 @@
<a href="{{ route('poller.log') }}"><i class="fa fa-file-text fa-lg icon-theme" aria-hidden="true"></i> @lang('Log')</a>
</li>
</ul>
<br />
@endsection
@section('content_footer')

View File

@ -6,7 +6,6 @@
@parent
<br />
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Total Poller Time</h3>

View File

@ -6,7 +6,6 @@
@parent
<br />
@if( $pollers->isNotEmpty() )
<div class="panel panel-default">
<div class="panel-heading">

View File

@ -0,0 +1,29 @@
@extends('poller.index')
@section('title', __('Poller Settings'))
@section('content')
@parent
<div id="app">
<poller-settings
:pollers='@json($poller_cluster, JSON_FORCE_OBJECT)'
:settings='@json($settings, JSON_FORCE_OBJECT)'
></poller-settings>
</div>
@endsection
@push('styles')
<link href="{{ asset(mix('/css/app.css')) }}" rel="stylesheet">
@endpush
@section('javascript')
<script src="{{ asset(mix('/js/lang/en.js')) }}"></script>
<script src="{{ asset(mix('/js/lang/' . app()->getLocale() . '.js')) }}"></script>
<script src="{{ asset(mix('/js/manifest.js')) }}"></script>
<script src="{{ asset(mix('/js/vendor.js')) }}"></script>
@routes
@endsection
@push('scripts')
<script src="{{ asset(mix('/js/app.js')) }}"></script>
@endpush

View File

@ -19,10 +19,14 @@ Route::group(['middleware' => ['auth.web'], 'guard' => 'auth'], function () {
// pages
Route::resource('device-groups', 'DeviceGroupController');
Route::get('poller', 'PollerController@pollerTab')->name('poller');
Route::get('poller/log', 'PollerController@logTab')->name('poller.log');
Route::get('poller/groups', 'PollerController@groupsTab')->name('poller.groups');
Route::get('poller/performance', 'PollerController@performanceTab')->name('poller.performance');
Route::group(['prefix' => 'poller'], function () {
Route::get('', 'PollerController@pollerTab')->name('poller.index');
Route::get('log', 'PollerController@logTab')->name('poller.log');
Route::get('groups', 'PollerController@groupsTab')->name('poller.groups');
Route::get('settings', 'PollerController@settingsTab')->name('poller.settings');
Route::get('performance', 'PollerController@performanceTab')->name('poller.performance');
Route::resource('{id}/settings', 'PollerSettingsController', ['as' => 'poller'])->only(['update', 'destroy']);
});
Route::get('locations', 'LocationController@index');
Route::resource('preferences', 'UserPreferencesController', ['only' => ['index', 'store']]);
Route::resource('users', 'UserController');