mirror of
https://github.com/librenms/librenms.git
synced 2024-10-07 16:52:45 +00:00
Rrd graph optimizations (#12806)
* RRD Graph optimization Do not use temporary files to generate graphs Don't start up a process to pipe commands to, just run the command Error image improvements * fix style issues * run the rrdgraph command in the rrd directory
This commit is contained in:
@@ -28,10 +28,12 @@ use Illuminate\Support\Str;
|
||||
use LibreNMS\Config;
|
||||
use LibreNMS\Data\Measure\Measurement;
|
||||
use LibreNMS\Exceptions\FileExistsException;
|
||||
use LibreNMS\Exceptions\RrdGraphException;
|
||||
use LibreNMS\Proc;
|
||||
use LibreNMS\Util\Debug;
|
||||
use LibreNMS\Util\Rewrite;
|
||||
use Log;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class Rrd extends BaseDatastore
|
||||
{
|
||||
@@ -545,25 +547,26 @@ class Rrd extends BaseDatastore
|
||||
|
||||
/**
|
||||
* Generates a graph file at $graph_file using $options
|
||||
* Opens its own rrdtool pipe.
|
||||
* Graphs are a single command per run, so this just runs rrdtool
|
||||
*
|
||||
* @param string $graph_file
|
||||
* @param string $options
|
||||
* @return string|int
|
||||
* @param string $options
|
||||
* @return string
|
||||
* @throws \LibreNMS\Exceptions\FileExistsException
|
||||
* @throws \LibreNMS\Exceptions\RrdGraphException
|
||||
*/
|
||||
public function graph($graph_file, $options)
|
||||
public function graph(string $options): string
|
||||
{
|
||||
if ($this->init(false)) {
|
||||
$cmd = $this->buildCommand('graph', $graph_file, $options);
|
||||
$cmd = $this->buildCommand('graph', '-', $options);
|
||||
$process = Process::fromShellCommandline(Config::get('rrdtool') . ' ' . $cmd, $this->rrd_dir);
|
||||
$process->setTimeout(300);
|
||||
$process->setIdleTimeout(300);
|
||||
$process->run();
|
||||
|
||||
$output = implode($this->sync_process->sendCommand($cmd));
|
||||
|
||||
d_echo("<p>$cmd</p>\n<p>command returned ($output)</p>");
|
||||
|
||||
return $output;
|
||||
} else {
|
||||
return 0;
|
||||
if (! $process->isSuccessful()) {
|
||||
throw new RrdGraphException($process->getErrorOutput(), $process->getExitCode(), $process->getOutput());
|
||||
}
|
||||
|
||||
return $process->getOutput();
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
|
44
LibreNMS/Exceptions/RrdGraphException.php
Normal file
44
LibreNMS/Exceptions/RrdGraphException.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/*
|
||||
* RrdGraphException.php
|
||||
*
|
||||
* Exception generated when there is an error creating the graph image
|
||||
*
|
||||
* 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;
|
||||
|
||||
use Exception;
|
||||
|
||||
class RrdGraphException extends Exception
|
||||
{
|
||||
protected $image_output;
|
||||
|
||||
public function __construct($error, $exit_code, $image_output)
|
||||
{
|
||||
parent::__construct($error, $exit_code);
|
||||
$this->image_output = $image_output;
|
||||
}
|
||||
|
||||
public function getImage()
|
||||
{
|
||||
return $this->image_output;
|
||||
}
|
||||
}
|
@@ -81,8 +81,7 @@ function api_get_graph(array $vars)
|
||||
{
|
||||
global $dur; // Needed for callback within graph code
|
||||
|
||||
$auth = '1';
|
||||
$base64_output = '';
|
||||
$auth = true;
|
||||
|
||||
// prevent ugly error for undefined graphs from being passed to the user
|
||||
[$type, $subtype] = extract_graph_type($vars['type']);
|
||||
@@ -99,10 +98,10 @@ function api_get_graph(array $vars)
|
||||
ob_end_clean();
|
||||
|
||||
if ($vars['output'] === 'base64') {
|
||||
return api_success(['image' => $base64_output, 'content-type' => get_image_type()], 'image');
|
||||
return api_success(['image' => $image, 'content-type' => get_image_type(Config::get('webui.graph_type'))], 'image');
|
||||
}
|
||||
|
||||
return response($image, 200, ['Content-Type' => get_image_type()]);
|
||||
return response($image, 200, ['Content-Type' => get_image_type(Config::get('webui.graph_type'))]);
|
||||
}
|
||||
|
||||
function check_bill_permission($bill_id, $callback)
|
||||
|
@@ -47,14 +47,6 @@ function var_get($v)
|
||||
return false;
|
||||
}
|
||||
|
||||
function data_uri($file, $mime)
|
||||
{
|
||||
$contents = file_get_contents($file);
|
||||
$base64 = base64_encode($contents);
|
||||
|
||||
return 'data:' . $mime . ';base64,' . $base64;
|
||||
}//end data_uri()
|
||||
|
||||
function toner2colour($descr, $percent)
|
||||
{
|
||||
$colour = \LibreNMS\Util\Colors::percentage(100 - $percent, null);
|
||||
@@ -465,19 +457,30 @@ function graph_error($text, $color = [128, 0, 0])
|
||||
{
|
||||
global $vars;
|
||||
|
||||
$type = Config::get('webui.graph_type');
|
||||
if (! Debug::isEnabled()) {
|
||||
set_image_type();
|
||||
header('Content-type: ' . get_image_type($type));
|
||||
}
|
||||
|
||||
$width = $vars['width'] ?? 150;
|
||||
$height = $vars['height'] ?? 60;
|
||||
$width = (int) ($vars['width'] ?? 150);
|
||||
$height = (int) ($vars['height'] ?? 60);
|
||||
|
||||
if (Config::get('webui.graph_type') === 'svg') {
|
||||
if ($type === 'svg') {
|
||||
$rgb = implode(', ', $color);
|
||||
$font_size = 20;
|
||||
$svg_x = 100;
|
||||
$svg_y = min($font_size, $width ? (($height / $width) * $svg_x) : 1);
|
||||
echo "<svg viewBox=\"0 0 $svg_x $svg_y\" xmlns=\"http://www.w3.org/2000/svg\"><text x=\"50%\" y=\"50%\" dominant-baseline=\"middle\" text-anchor=\"middle\" style=\"font-family: sans-serif; fill: rgb($rgb);\">$text</text></svg>";
|
||||
echo <<<SVG
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xhtml="http://www.w3.org/1999/xhtml"
|
||||
viewBox="0 0 $width $height"
|
||||
preserveAspectRatio="xMinYMin">
|
||||
<foreignObject x="0" y="0" width="$width" height="$height" transform="translate(0,0)">
|
||||
<xhtml:div style="display:table; width:{$width}px; height:{$height}px; overflow:hidden;">
|
||||
<xhtml:div style="display:table-cell; vertical-align:middle;">
|
||||
<xhtml:div style="color:rgb($rgb); text-align:center; font-family:sans-serif; font-size:0.6em;">$text</xhtml:div>
|
||||
</xhtml:div>
|
||||
</xhtml:div>
|
||||
</foreignObject>
|
||||
</svg>
|
||||
SVG;
|
||||
} else {
|
||||
$img = imagecreate($width, $height);
|
||||
imagecolorallocatealpha($img, 255, 255, 255, 127); // transparent background
|
||||
@@ -1020,18 +1023,14 @@ function eventlog_severity($eventlog_severity)
|
||||
}
|
||||
} // end eventlog_severity
|
||||
|
||||
function set_image_type()
|
||||
/**
|
||||
* Get the http content type of the image
|
||||
* @param string $type svg or png
|
||||
* @return string
|
||||
*/
|
||||
function get_image_type(string $type)
|
||||
{
|
||||
header('Content-type: ' . get_image_type());
|
||||
}
|
||||
|
||||
function get_image_type()
|
||||
{
|
||||
if (Config::get('webui.graph_type') === 'svg') {
|
||||
return 'image/svg+xml';
|
||||
} else {
|
||||
return 'image/png';
|
||||
}
|
||||
return $type === 'svg' ? 'image/svg+xml' : 'image/png';
|
||||
}
|
||||
|
||||
function get_oxidized_nodes_list()
|
||||
|
@@ -38,8 +38,8 @@ foreach (dbFetchRows('SELECT * FROM `sensors` WHERE `sensor_class` = ? AND `devi
|
||||
}//end switch
|
||||
|
||||
$sensor_descr_fixed = \LibreNMS\Data\Store\Rrd::fixedSafeDescr($sensor['sensor_descr'], 12);
|
||||
$rrd_file = get_sensor_rrd($device, $sensor);
|
||||
$rrd_options .= " DEF:sensor{$sensor['sensor_id']}=$rrd_file:sensor:AVERAGE";
|
||||
$rrd_filename = get_sensor_rrd($device, $sensor);
|
||||
$rrd_options .= " DEF:sensor{$sensor['sensor_id']}=$rrd_filename:sensor:AVERAGE";
|
||||
$rrd_options .= " LINE1:sensor{$sensor['sensor_id']}#$colour:'$sensor_descr_fixed'";
|
||||
$rrd_options .= " GPRINT:sensor{$sensor['sensor_id']}:LAST:%5.1lf$unit";
|
||||
$rrd_options .= " GPRINT:sensor{$sensor['sensor_id']}:MIN:%5.1lf$unit";
|
||||
|
@@ -2,6 +2,8 @@
|
||||
|
||||
use LibreNMS\Config;
|
||||
|
||||
global $debug;
|
||||
|
||||
// Push $_GET into $vars to be compatible with web interface naming
|
||||
foreach ($_GET as $name => $value) {
|
||||
$vars[$name] = $value;
|
||||
@@ -24,13 +26,12 @@ $legend = $vars['legend'];
|
||||
$output = (! empty($vars['output']) ? $vars['output'] : 'default');
|
||||
$from = parse_at_time($_GET['from']) ?: Config::get('time.day');
|
||||
$to = parse_at_time($_GET['to']) ?: Config::get('time.now');
|
||||
$graph_type = (isset($vars['graph_type']) ? $vars['graph_type'] : Config::get('webui.graph_type'));
|
||||
|
||||
$period = ($to - $from);
|
||||
$base64_output = '';
|
||||
$prev_from = ($from - $period);
|
||||
|
||||
$graphfile = Config::get('temp_dir') . '/' . strgen();
|
||||
$graph_image_type = $vars['graph_type'] ?? Config::get('webui.graph_type');
|
||||
$rrd_options = '';
|
||||
$auth = false;
|
||||
|
||||
require Config::get('install_dir') . "/includes/html/graphs/$type/auth.inc.php";
|
||||
|
||||
@@ -47,61 +48,75 @@ if ($auth && is_customoid_graph($type, $subtype)) {
|
||||
// Graph Template Missing");
|
||||
}
|
||||
|
||||
if ($error_msg) {
|
||||
if (! empty($error_msg)) {
|
||||
// We have an error :(
|
||||
graph_error($graph_error);
|
||||
} elseif ($auth === null) {
|
||||
graph_error($error_msg);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($auth === null) {
|
||||
// We are unauthenticated :(
|
||||
graph_error($width < 200 ? 'No Auth' : 'No Authorization');
|
||||
} else {
|
||||
// $rrd_options .= " HRULE:0#999999";
|
||||
if ($graph_type === 'svg') {
|
||||
$rrd_options .= ' --imgformat=SVG';
|
||||
if ($width < 350) {
|
||||
$rrd_options .= ' -m 0.75 -R light';
|
||||
}
|
||||
}
|
||||
|
||||
if ($command_only) {
|
||||
echo "<div class='infobox'>";
|
||||
echo "<p style='font-size: 16px; font-weight: bold;'>RRDTool Command</p>";
|
||||
echo "<pre class='rrd-pre'>";
|
||||
echo 'rrdtool ' . Rrd::buildCommand('graph', $graphfile, $rrd_options);
|
||||
echo '</pre>';
|
||||
$return = Rrd::graph($graphfile, $rrd_options);
|
||||
echo "<p style='font-size: 16px; font-weight: bold;'>RRDTool Output</p>";
|
||||
echo "<pre class='rrd-pre'>";
|
||||
echo "$return";
|
||||
echo '</pre>';
|
||||
unlink($graphfile);
|
||||
echo '</div>';
|
||||
} elseif ($no_file) {
|
||||
graph_error($width < 200 ? 'No Data' : 'No Data file');
|
||||
} elseif ($rrd_options) {
|
||||
Rrd::graph($graphfile, $rrd_options);
|
||||
d_echo($rrd_cmd);
|
||||
if (is_file($graphfile)) {
|
||||
if (! \LibreNMS\Util\Debug::isEnabled()) {
|
||||
set_image_type();
|
||||
if ($output === 'base64') {
|
||||
$imagedata = file_get_contents($graphfile);
|
||||
$base64_output = base64_encode($imagedata);
|
||||
} else {
|
||||
$fd = fopen($graphfile, 'r');
|
||||
fpassthru($fd);
|
||||
fclose($fd);
|
||||
}
|
||||
} else {
|
||||
echo `ls -l $graphfile`;
|
||||
echo '<img src="' . data_uri($graphfile, 'image/svg+xml') . '" alt="graph" />';
|
||||
}
|
||||
unlink($graphfile);
|
||||
} elseif (isset($rrd_filename) && ! Rrd::checkRrdExists($rrd_filename)) {
|
||||
graph_error($width < 200 ? 'No Data' : 'No Data file');
|
||||
} else {
|
||||
graph_error($width < 200 ? 'Draw Error' : 'Error Drawing Graph');
|
||||
}
|
||||
} else {
|
||||
graph_error($width < 200 ? 'Def Error' : 'Graph Definition Error');
|
||||
return;
|
||||
}
|
||||
|
||||
if ($graph_image_type === 'svg') {
|
||||
$rrd_options .= ' --imgformat=SVG';
|
||||
if ($width < 350) {
|
||||
$rrd_options .= ' -m 0.75 -R light';
|
||||
}
|
||||
}
|
||||
|
||||
// command output requested
|
||||
if (! empty($command_only)) {
|
||||
echo "<div class='infobox'>";
|
||||
echo "<p style='font-size: 16px; font-weight: bold;'>RRDTool Command</p>";
|
||||
echo "<pre class='rrd-pre'>";
|
||||
echo 'rrdtool ' . Rrd::buildCommand('graph', '-', $rrd_options);
|
||||
echo '</pre>';
|
||||
try {
|
||||
Rrd::graph($rrd_options);
|
||||
} catch (\LibreNMS\Exceptions\RrdGraphException $e) {
|
||||
echo "<p style='font-size: 16px; font-weight: bold;'>RRDTool Output</p>";
|
||||
echo "<pre class='rrd-pre'>";
|
||||
echo $e->getMessage();
|
||||
echo '</pre>';
|
||||
}
|
||||
echo '</div>';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// graph sent file not found flag
|
||||
if (! empty($no_file)) {
|
||||
graph_error($width < 200 ? 'No Data' : 'No Data file ' . $no_file);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($rrd_options)) {
|
||||
graph_error($width < 200 ? 'Def Error' : 'Graph Definition Error');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Generating the graph!
|
||||
try {
|
||||
$image_data = Rrd::graph($rrd_options);
|
||||
|
||||
// output the graph
|
||||
if (\LibreNMS\Util\Debug::isEnabled()) {
|
||||
echo '<img src="data:' . get_image_type($graph_image_type) . ';base64,' . base64_encode($image_data) . '" alt="graph" />';
|
||||
} else {
|
||||
header('Content-type: ' . get_image_type(Config::get('webui.graph_type')));
|
||||
echo $output === 'base64' ? base64_encode($image_data) : $image_data;
|
||||
}
|
||||
} catch (\LibreNMS\Exceptions\RrdGraphException $e) {
|
||||
if (isset($rrd_filename) && ! Rrd::checkRrdExists($rrd_filename)) {
|
||||
graph_error($width < 200 ? 'No Data' : 'No Data file ' . basename($rrd_filename));
|
||||
} else {
|
||||
graph_error($width < 200 ? 'Draw Error' : 'Error Drawing Graph: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
@@ -92,8 +92,8 @@ if (! $auth) {
|
||||
$link_array['page'] = 'graphs';
|
||||
$link = \LibreNMS\Util\Url::generate($link_array);
|
||||
|
||||
echo '<td align=center>';
|
||||
echo '<b>' . $text . '</b><br>';
|
||||
echo '<td style="text-align: center;">';
|
||||
echo '<b>' . $text . '</b>';
|
||||
echo '<a href="' . $link . '">';
|
||||
echo \LibreNMS\Util\Url::lazyGraphTag($graph_array);
|
||||
echo '</a>';
|
||||
|
Reference in New Issue
Block a user