Graph of overall poller performance (#9381)

* Graph definitions

* Stacked.

* Add display

* remove old poll-log, add redirect

* Handle undefined data in graphs

* Standard pollers

* Change doc to send to the global poller module page.

* remove test code :)

* Link parent menu
This commit is contained in:
Tony Murray
2018-10-26 15:04:30 -05:00
committed by Neil Lathwood
parent 03e8bd743e
commit 0ec8f18459
14 changed files with 290 additions and 76 deletions

View File

@@ -116,7 +116,7 @@ class Poller extends BaseValidation
$result->setFix('Check your poll log and see: http://docs.librenms.org/Support/Performance/'); $result->setFix('Check your poll log and see: http://docs.librenms.org/Support/Performance/');
} else { } else {
$base_url = $validator->getBaseURL(); $base_url = $validator->getBaseURL();
$result->setFix("Check $base_url/poll-log/ and see: http://docs.librenms.org/Support/Performance/"); $result->setFix("Check $base_url/pollers/tab=log and see: http://docs.librenms.org/Support/Performance/");
} }
$validator->result($result); $validator->result($result);
@@ -135,7 +135,7 @@ class Poller extends BaseValidation
$result->setFix('Check your poll log and see: http://docs.librenms.org/Support/Performance/'); $result->setFix('Check your poll log and see: http://docs.librenms.org/Support/Performance/');
} else { } else {
$base_url = $validator->getBaseURL(); $base_url = $validator->getBaseURL();
$result->setFix("Check $base_url/poll-log/ and see: http://docs.librenms.org/Support/Performance/"); $result->setFix("Check $base_url/pollers/tab=log/ and see: http://docs.librenms.org/Support/Performance/");
} }
$validator->result($result); $validator->result($result);

View File

@@ -83,7 +83,7 @@ class Checks
} }
if (Device::isUp()->where('last_polled', '<=', Carbon::now()->subMinutes(15))->exists()) { if (Device::isUp()->where('last_polled', '<=', Carbon::now()->subMinutes(15))->exists()) {
Toastr::warning('<a href="poll-log/filter=unpolled/">It appears as though you have some devices that haven\'t completed polling within the last 15 minutes, you may want to check that out :)</a>', 'Devices unpolled'); Toastr::warning('<a href="pollers/tab=log/filter=unpolled/">It appears as though you have some devices that haven\'t completed polling within the last 15 minutes, you may want to check that out :)</a>', 'Devices unpolled');
} }
// Directory access checks // Directory access checks

View File

@@ -27,7 +27,8 @@ MySQL crashes or your server does but it provides an amazing difference in IO us
### Polling modules ### Polling modules
Review the graph of poller module time take under device > graphs > poller to see what modules are consuming poller time. Review the graph of poller module time take under gear > pollers > performance to see what modules are consuming poller time.
This data is shown per device under device > graphs > poller.
Disable polling (and discovery) modules that you do not need. You can do this globally in `config.php` like: Disable polling (and discovery) modules that you do not need. You can do this globally in `config.php` like:

View File

@@ -0,0 +1,72 @@
<?php
/**
* poller_modules_perf.inc.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 2018 Tony Murray
* @author Tony Murray <murraytony@gmail.com>
*/
use App\Models\Device;
use LibreNMS\Config;
$scale_min = '0';
$colors = Config::get('graph_colours.manycolours');
require 'includes/graphs/common.inc.php';
$hostnames = Device::pluck('hostname');
$modules = array_keys(Config::get('poller_modules'));
sort($modules);
foreach ($modules as $module_index => $module) {
$cdef = [];
$suffix = '';
foreach ($hostnames as $index => $hostname) {
$rrd_filename = rrd_name($hostname, ['poller-perf', $module]);
if (rrdtool_check_rrd_exists($rrd_filename)) {
$rrd_options .= " DEF:{$module}Raw$index=$rrd_filename:poller:AVERAGE";
// change undefined to 0
$rrd_options .= " CDEF:$module$index={$module}Raw$index,UN,0,{$module}Raw$index,IF";
$cdef[] = $module . $index . $suffix;
$suffix = ',+';
}
}
if (empty($cdef)) {
//remove the module so we don't print it in the legend
unset($modules[$module_index]);
} else {
// have data for this module, display it
$rrd_options .= " CDEF:$module=" . implode(',', $cdef);
}
}
$rrd_options .= " 'COMMENT:Seconds Cur Min Max Avg\\n'";
foreach ($modules as $index => $module) {
$color = $colors[$index % count($colors)];
$rrd_options .= " AREA:$module#$color:'" . rrdtool_escape($module, 16) ."':STACK";
$rrd_options .= " GPRINT:$module:LAST:%6.2lf GPRINT:$module:MIN:%6.2lf";
$rrd_options .= " GPRINT:$module:MAX:%6.2lf 'GPRINT:$module:AVERAGE:%6.2lf\\n'";
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* poller_perf.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 2018 Tony Murray
* @author Tony Murray <murraytony@gmail.com>
*/
use App\Models\Device;
$scale_min = '0';
require 'includes/graphs/common.inc.php';
$cdef = [];
$suffix = '';
foreach (Device::pluck('hostname') as $index => $hostname) {
$rrd_filename = rrd_name($hostname, 'poller-perf');
if (rrdtool_check_rrd_exists($rrd_filename)) {
$rrd_options .= " DEF:pollerRaw$index=$rrd_filename:poller:AVERAGE";
// change undefined to 0
$rrd_options .= " CDEF:poller$index=pollerRaw$index,UN,0,pollerRaw$index,IF";
$cdef[] = 'poller' . $index . $suffix;
$suffix = ',+';
}
}
$rrd_options .= " CDEF:poller=" . implode(',', $cdef);
$rrd_options .= " 'COMMENT:Seconds Cur Min Max Avg\\n'";
$rrd_options .= ' LINE1.25:poller#36393D:Poller';
$rrd_options .= ' GPRINT:poller:LAST:%6.2lf GPRINT:poller:MIN:%6.2lf';
$rrd_options .= " GPRINT:poller:MAX:%6.2lf 'GPRINT:poller:AVERAGE:%6.2lf\\n'";

View File

@@ -691,15 +691,16 @@ if (Auth::user()->hasGlobalAdmin()) {
<li role="presentation" class="divider"></li> '); <li role="presentation" class="divider"></li> ');
echo(' echo('
<li class="dropdown-submenu"> <li class="dropdown-submenu">
<a href="#"><i class="fa fa-th-large fa-fw fa-lg" aria-hidden="true"></i> Pollers</a> <a href="pollers"><i class="fa fa-th-large fa-fw fa-lg" aria-hidden="true"></i> Pollers</a>
<ul class="dropdown-menu scrollable-menu"> <ul class="dropdown-menu scrollable-menu">
<li><a href="poll-log/"><i class="fa fa-file-text fa-fw fa-lg" aria-hidden="true"></i> Poller History</a></li>
<li><a href="pollers/tab=pollers/"><i class="fa fa-th-large fa-fw fa-lg" aria-hidden="true"></i> Pollers</a></li>'); <li><a href="pollers/tab=pollers/"><i class="fa fa-th-large fa-fw fa-lg" aria-hidden="true"></i> Pollers</a></li>');
if ($config['distributed_poller'] === true) { if ($config['distributed_poller'] === true) {
echo (' echo ('
<li><a href="pollers/tab=groups/"><i class="fa fa-th fa-fw fa-lg" aria-hidden="true"></i> Poller Groups</a></li>'); <li><a href="pollers/tab=groups/"><i class="fa fa-th fa-fw fa-lg" aria-hidden="true"></i> Groups</a></li>');
} }
echo ' <li><a href="pollers/tab=performance/"><i class="fa fa-line-chart fa-fw fa-lg" aria-hidden="true"></i> Performance</a></li>';
echo ' <li><a href="pollers/tab=log/"><i class="fa fa-file-text fa-fw fa-lg" aria-hidden="true"></i> History</a></li>';
echo (' echo ('
</ul> </ul>
</li> </li>

View File

@@ -1,44 +0,0 @@
<?php
$no_refresh = true;
$pagetitle[] = 'Poll Log';
if (isset($vars['filter'])) {
$type = $vars['filter'];
}
?>
<table id="poll-log" class="table table-condensed table-hover table-striped">
<thead>
<tr>
<th data-column-id="hostname">Hostname</th>
<th data-column-id="last_polled">Last Polled</th>
<th data-column-id="poller_group">Poller Group</th>
<th data-column-id="last_polled_timetaken" data-order="desc">Polling Duration (Seconds)</th>
</tr>
</thead>
</table>
<script>
searchbar = "<div id=\"{{ctx.id}}\" class=\"{{css.header}}\"><div class=\"row\">"+
"<div class=\"col-sm-8 actionBar\"><span class=\"pull-left\">"+
"<a href='<?php echo generate_url(array('page' => 'poll-log')); ?>' class='btn btn-primary btn-sm'>All devices</a> "+
"<a href='<?php echo generate_url(array('page' => 'poll-log', 'filter' => 'unpolled')); ?>' class='btn btn-danger btn-sm'>Unpolled devices</a>"+
"</div><div class=\"col-sm-4 actionBar\"><p class=\"{{css.search}}\"></p><p class=\"{{css.actions}}\"></p></div>";
var grid = $("#poll-log").bootgrid({
ajax: true,
rowCount: [50, 100, 250, -1],
columnSelection: false,
templates: {
header: searchbar
},
post: function ()
{
return {
id: "poll-log",
type: "<?php echo $type;?>"
};
},
url: "ajax_table.php"
});
</script>

View File

@@ -14,33 +14,42 @@
$no_refresh = true; $no_refresh = true;
echo '<ul class="nav nav-tabs">'; echo '<ul class="nav nav-tabs">';
$poll_tabs = array( $poll_tabs = [
array( [
'name' => 'Pollers', 'name' => 'Pollers',
'icon' => 'fa-th-large', 'icon' => 'fa-th-large',
), ],
); ];
if ($config['distributed_poller'] === true) { if (\LibreNMS\Config::get('distributed_poller')) {
$poll_tabs[] = array( $poll_tabs[] = array(
'name' => 'Groups', 'name' => 'Groups',
'icon' => 'fa-th', 'icon' => 'fa-th',
); );
} }
$poll_tabs[] = [
'name' => 'Performance',
'icon' => 'fa-line-chart',
];
$poll_tabs[] = [
'name' => 'Log',
'icon' => 'fa-file-text',
];
$current_tab = isset($vars['tab']) ? str_replace('/', '', $vars['tab']) : 'pollers';
foreach ($poll_tabs as $tab) { foreach ($poll_tabs as $tab) {
echo ' $taburl = strtolower($tab['name']);
<li> echo '<li role="presentation" ' . ($current_tab == $taburl ? ' class="active"' : '') . '><a href="';
<a href="' . generate_url(array('page'=>'pollers','tab'=>lcfirst($tab['name']))) . '"> echo generate_url(['page' => 'pollers', 'tab' => $taburl]);
<i class="fa '.$tab['icon'].' fa-lg icon-theme" aria-hidden="true"></i>'.$tab['name'].' echo '"><i class="fa ' . $tab['icon'] . ' fa-lg icon-theme" aria-hidden="true"></i> ' . $tab['name'];
</a> echo '</a></li>';
</li>';
} }
echo '</ul>'; echo '</ul>';
if (isset($vars['tab'])) { include_once 'pages/pollers/'.$current_tab.'.inc.php';
include_once 'pages/pollers/'.mres($vars['tab']).'.inc.php';
}

View File

@@ -12,6 +12,8 @@
* the source code distribution for details. * the source code distribution for details.
*/ */
$pagetitle[] = 'Poller Groups';
require_once 'includes/modal/poller_groups.inc.php'; require_once 'includes/modal/poller_groups.inc.php';
?> ?>

View File

@@ -0,0 +1,68 @@
<?php
/**
* log.inc.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 2018 Tony Murray
* @author Tony Murray <murraytony@gmail.com>
*/
$no_refresh = true;
$pagetitle[] = 'Poll Log';
if (isset($vars['filter'])) {
$type = $vars['filter'];
}
?>
<table id="poll-log" class="table table-condensed table-hover table-striped">
<thead>
<tr>
<th data-column-id="hostname">Hostname</th>
<th data-column-id="last_polled">Last Polled</th>
<th data-column-id="poller_group">Poller Group</th>
<th data-column-id="last_polled_timetaken" data-order="desc">Polling Duration (Seconds)</th>
</tr>
</thead>
</table>
<script>
searchbar = "<div id=\"{{ctx.id}}\" class=\"{{css.header}}\"><div class=\"row\">"+
"<div class=\"col-sm-8 actionBar\"><span class=\"pull-left\">"+
"<a href='<?php echo generate_url(['page' => 'pollers', 'tab' => 'log']); ?>' class='btn btn-primary btn-sm <?php echo $vars['filter'] == 'unpolled' ? '' : 'active' ?>'>All devices</a> "+
"<a href='<?php echo generate_url(['page' => 'pollers', 'tab' => 'log', 'filter' => 'unpolled']); ?>' class='btn btn-danger btn-sm <?php echo $vars['filter'] == 'unpolled' ? 'active' : '' ?>'>Unpolled devices</a>"+
"</div><div class=\"col-sm-4 actionBar\"><p class=\"{{css.search}}\"></p><p class=\"{{css.actions}}\"></p></div>";
var grid = $("#poll-log").bootgrid({
ajax: true,
rowCount: [50, 100, 250, -1],
columnSelection: false,
templates: {
header: searchbar
},
post: function ()
{
return {
id: "poll-log",
type: "<?php echo $type;?>"
};
},
url: "ajax_table.php"
});
</script>

View File

@@ -0,0 +1,37 @@
<?php
$pagetitle[] = 'Poll Performance';
?>
<br />
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Total Poller Time</h3>
</div>
<div class="panel-body">
<?php
$graph_array = [
'type' => 'global_poller_perf',
'legend' => 'yes',
'height' => 100,
];
require 'includes/print-graphrow.inc.php';
?>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Total Poller Time Per Module</h3>
</div>
<div class="panel-body">
<?php
$graph_array = [
'type' => 'global_poller_modules_perf',
'legend' => 'yes',
'height' => 100,
];
require 'includes/print-graphrow.inc.php';
?>
</div>
</div>

View File

@@ -14,6 +14,8 @@
use LibreNMS\Config; use LibreNMS\Config;
$pagetitle[] = 'Pollers';
require_once 'includes/modal/delete_poller.inc.php'; require_once 'includes/modal/delete_poller.inc.php';
?> ?>
@@ -25,8 +27,11 @@ $rows = dbFetchRows($query);
if (count($rows) !== 0) { if (count($rows) !== 0) {
echo ' echo '
<h2>Standard Distributed Pollers</h2> <div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Standard Pollers</h3>
</div>
<div class="panel-body">
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped table-bordered table-hover table-condensed"> <table class="table table-striped table-bordered table-hover table-condensed">
<tr> <tr>
@@ -68,7 +73,9 @@ if (count($rows) !== 0) {
echo ' echo '
</table> </table>
</div>'; </div>
</div>
</div>';
} }
$query = 'SELECT *,UNIX_TIMESTAMP(NOW()) AS `now`, UNIX_TIMESTAMP(`last_report`) AS `then` FROM `poller_cluster` ORDER BY poller_name'; $query = 'SELECT *,UNIX_TIMESTAMP(NOW()) AS `now`, UNIX_TIMESTAMP(`last_report`) AS `then` FROM `poller_cluster` ORDER BY poller_name';
@@ -76,8 +83,11 @@ $rows = dbFetchRows($query);
if (count($rows) !== 0) { if (count($rows) !== 0) {
echo ' echo '
<h2>Poller Cluster Health</h2> <div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Poller Cluster Health</h3>
</div>
<div class="panel-body">
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped table-bordered table-condensed"> <table class="table table-striped table-bordered table-condensed">
<tr> <tr>
@@ -158,6 +168,8 @@ if (count($rows) !== 0) {
Worker seconds indicates the maximum polling throughput a node can achieve in perfect conditions. If the consumed is close to the maximum, consider adding more threads, or better tuning your groups.<br> Worker seconds indicates the maximum polling throughput a node can achieve in perfect conditions. If the consumed is close to the maximum, consider adding more threads, or better tuning your groups.<br>
If there are devices pending but consumed worker seconds is low, your hardware is not sufficient for the number of devices and the poller cannot reach maximum throughput. If there are devices pending but consumed worker seconds is low, your hardware is not sufficient for the number of devices and the poller cannot reach maximum throughput.
</small> </small>
</div>'; </div>
</div>
</div>';
} }
?> ?>

View File

@@ -364,13 +364,14 @@
<li><a href="{{ url('authlog') }}"><i class="fa fa-shield fa-fw fa-lg" aria-hidden="true"></i> Auth History</a></li> <li><a href="{{ url('authlog') }}"><i class="fa fa-shield fa-fw fa-lg" aria-hidden="true"></i> Auth History</a></li>
<li role="presentation" class="divider"></li> <li role="presentation" class="divider"></li>
<li class="dropdown-submenu"> <li class="dropdown-submenu">
<a href="#"><i class="fa fa-th-large fa-fw fa-lg" aria-hidden="true"></i> Pollers</a> <a href="{{ url('pollers') }}"><i class="fa fa-th-large fa-fw fa-lg" aria-hidden="true"></i> @lang('Pollers')</a>
<ul class="dropdown-menu scrollable-menu"> <ul class="dropdown-menu scrollable-menu">
<li><a href="{{ url('poll-log') }}"><i class="fa fa-file-text fa-fw fa-lg" aria-hidden="true"></i> Poller History</a></li> <li><a href="{{ url('pollers/tab=pollers') }}"><i class="fa fa-th-large fa-fw fa-lg" aria-hidden="true"></i> @lang('Pollers')</a></li>
<li><a href="{{ url('pollers/tab=pollers') }}"><i class="fa fa-th-large fa-fw fa-lg" aria-hidden="true"></i> Pollers</a></li>
@config('distributed_poller') @config('distributed_poller')
<li><a href="{{ url('pollers/tab=groups') }}"><i class="fa fa-th fa-fw fa-lg" aria-hidden="true"></i> Poller Groups</a></li> <li><a href="{{ url('pollers/tab=groups') }}"><i class="fa fa-th fa-fw fa-lg" aria-hidden="true"></i> @lang('Groups')</a></li>
@endconfig @endconfig
<li><a href="{{ url('pollers/tab=performance') }}"><i class="fa fa-th-large fa-fw fa-lg" aria-hidden="true"></i> @lang('Performance')</a></li>
<li><a href="{{ url('pollers/tab=log') }}"><i class="fa fa-file-text fa-fw fa-lg" aria-hidden="true"></i> @lang('History')</a></li>
</ul> </ul>
</li> </li>
<li role="presentation" class="divider"></li> <li role="presentation" class="divider"></li>

View File

@@ -21,6 +21,11 @@ Route::group(['middleware' => ['auth', '2fa'], 'guard' => 'auth'], function () {
return view('laravel'); return view('laravel');
}); });
// old route redirects
Route::get('poll-log', function () {
return redirect('pollers/tab=log/');
});
// Two Factor Auth // Two Factor Auth
Route::get('2fa', 'TwoFactorController@showTwoFactorForm')->name('2fa.form'); Route::get('2fa', 'TwoFactorController@showTwoFactorForm')->name('2fa.form');
Route::post('2fa', 'TwoFactorController@verifyTwoFactor')->name('2fa.verify'); Route::post('2fa', 'TwoFactorController@verifyTwoFactor')->name('2fa.verify');