Graphing cleanup (#14492)

* WIP

* Fixes and cleanups

* Move parseAtTime to Time util class

* lint fixes, explicitly define variables

* Style fixes
This commit is contained in:
Tony Murray 2022-10-28 08:06:29 -05:00 committed by GitHub
parent d5fcf53e01
commit 4b65dc41d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 420 additions and 308 deletions

View File

@ -25,36 +25,12 @@
namespace LibreNMS\Data\Graphing;
use LibreNMS\Enum\ImageFormat;
class GraphImage
{
/**
* @var string
*/
private $type;
/**
* @var string
*/
private $title;
/**
* @var string
*/
private $data;
public function __construct(string $type, string $title, string $data)
public function __construct(public readonly ImageFormat $format, public readonly string $title, public readonly string $data)
{
$this->type = $type;
$this->title = $title;
$this->data = $data;
}
public function title(): string
{
return $this->title;
}
public function data(): string
{
return $this->data;
}
public function base64(): string
@ -64,28 +40,21 @@ class GraphImage
public function inline(): string
{
return 'data:' . $this->imageType() . ';base64,' . $this->base64();
return 'data:' . $this->contentType() . ';base64,' . $this->base64();
}
public function fileExtension(): string
{
switch ($this->imageType()) {
case 'image/svg+xml':
return 'svg';
case 'image/png':
// fallthrough
default:
return 'png';
}
return $this->format->name;
}
public function imageType(): string
public function contentType(): string
{
return $this->type;
return $this->format->contentType();
}
public function __toString()
{
return $this->data();
return $this->data;
}
}

View File

@ -0,0 +1,242 @@
<?php
/**
* GraphParameters.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 2022 Tony Murray
* @author Tony Murray <murraytony@gmail.com>
*/
namespace LibreNMS\Data\Graphing;
use App\Facades\DeviceCache;
use Illuminate\Support\Str;
use LibreNMS\Config;
use LibreNMS\Enum\ImageFormat;
use LibreNMS\Util\Clean;
use LibreNMS\Util\Time;
class GraphParameters
{
public readonly array $visibleElements;
public string $title;
public readonly string $type;
public readonly string $subtype;
public readonly ImageFormat $imageFormat;
public readonly string $font;
public readonly string $font_color;
public readonly int $font_size;
public readonly string $background;
public readonly string $canvas;
public readonly int $width;
public readonly int $height;
public readonly bool $full_size;
public readonly bool $is_small;
public readonly int $from;
public readonly int $to;
public readonly int $period;
public readonly int $prev_from;
public readonly bool $inverse;
public readonly string $in;
public readonly string $out;
public ?int $scale_max = null;
public ?int $scale_min = null;
public ?bool $scale_rigid = null;
public bool $sloped = true;
public int $float_precision = 2;
private const TINY = 99;
private const SMALL = 224;
private const MEDIUM_SMALL = 300;
private const MEDIUM = 350;
public function __construct(array $vars)
{
$this->imageFormat = ImageFormat::forGraph($vars['graph_type'] ?? null);
[$this->type, $this->subtype] = $this->extractType($vars['type'] ?? '');
$this->width = (int) ($vars['width'] ?? 400);
$this->height = (int) ($vars['height'] ?? $this->width / 3);
$this->full_size = ! empty($vars['absolute']);
$this->is_small = $this->width < self::SMALL;
$this->font = Config::get('mono_font');
$this->font_color = Clean::alphaDash($vars['font']);
$this->font_size = $this->width <= self::MEDIUM_SMALL ? 8 : 7;
$this->canvas = Clean::alphaDash($vars['bg'] ?? '');
$this->background = Clean::alphaDash($vars['bbg'] ?? '');
$this->title = $vars['graph_title'] ?? $this->defaultTitle();
$this->visibleElements = [
'title' => empty($vars['title']) || $vars['title'] !== 'no',
'legend' => empty($vars['legend']) || $vars['legend'] !== 'no',
'total' => ! ($vars['nototal'] ?? $this->is_small),
'details' => ! ($vars['nodetails'] ?? $this->is_small),
'aggregate' => ! empty($vars['noagg']),
];
$this->from = Time::parseAt($vars['from'] ?? '-1d');
$this->to = empty($vars['to']) ? time() : Time::parseAt($vars['to']);
$this->period = $this->to - $this->from;
$this->prev_from = $this->from - $this->period;
$this->inverse = ! empty($vars['inverse']);
$this->in = $this->inverse ? 'out' : 'in';
$this->out = $this->inverse ? 'in' : 'out';
}
public function visible(string $element): bool
{
return $this->visibleElements[$element] ?? true;
}
public function all(): array
{
$variables = get_object_vars($this);
// legacy compat
$variables['nototal'] = ! $this->visibleElements['total'];
$variables['nodetails'] = ! $this->visibleElements['details'];
$variables['noagg'] = ! $this->visibleElements['aggregate'];
$variables['title'] = $this->visibleElements['title'];
return $variables;
}
public function toRrdOptions(): array
{
$options = ['--start', $this->from, '--end', $this->to, '--width', $this->width, '--height', $this->height];
// image must fit final dimensions
if ($this->full_size) {
$options[] = '--full-size-mode';
}
if ($this->imageFormat === ImageFormat::svg) {
$options[] = '--imgformat=SVG';
if ($this->width < self::MEDIUM) {
array_push($options, '-m', 0.75, '-R', 'light');
}
}
// set up fonts
array_push($options, '--font', 'LEGEND:' . $this->font_size . ':' . $this->font);
array_push($options, '--font', 'AXIS:' . ($this->font_size - 1) . ':' . $this->font);
array_push($options, '--font-render-mode', 'normal');
// set up colors
foreach ($this->graphColors() as $name => $color) {
array_push($options, '-c', $name . '#' . $color);
}
// set up scaling scaling
if (! $this->scale_min && ! $this->scale_max) {
$options[] = '--alt-autoscale-max';
if ($this->scale_rigid === null) {
$this->scale_rigid = true;
}
}
if ($this->scale_min) {
array_push($options, '-l', $this->scale_min);
}
if ($this->scale_max) {
array_push($options, '-u', $this->scale_max);
}
if ($this->scale_rigid) {
$options[] = '--rigid';
}
if ($this->sloped) {
$options[] = '--slope-mode';
}
// remove all text, height is too small
if ($this->height < self::TINY) {
$options[] = '--only-graph';
}
if (! $this->visible('legend')) {
$options[] = '-g';
}
if ($this->visible('title') && $this->title) {
$options[] = "--title='" . Clean::alphaDashSpace($this->title) . "'";
}
return $options;
}
public function __toString(): string
{
return implode(' ', $this->toRrdOptions());
}
private function graphColors(): array
{
$config_suffix = Config::get('applied_site_style') == 'dark' ? '_dark' : '';
$def_colors = Config::get('rrdgraph_def_text' . $config_suffix);
$def_font = ltrim(Config::get('rrdgraph_def_text_color' . $config_suffix), '#');
preg_match_all('/-c ([A-Z]+)#([0-9A-Fa-f]{6,8})/', $def_colors, $matches);
$colors = ['FONT' => $def_font];
foreach ($matches[1] as $index => $key) {
$colors[$key] = $matches[2][$index];
}
// user overrides
if ($this->font_color) {
$colors['FONT'] = $this->font_color;
}
if ($this->canvas) {
$colors['CANVAS'] = $this->canvas;
}
if ($this->background) {
$colors['BACK'] = $this->background;
}
return $colors;
}
private function extractType(string $type): array
{
preg_match('/^(?P<type>[A-Za-z0-9]+)_(?P<subtype>.+)/', $type, $graphtype);
$type = basename($graphtype['type']);
$subtype = basename($graphtype['subtype']);
return [$type, $subtype];
}
private function defaultTitle(): string
{
$title = DeviceCache::getPrimary()->displayName() ?: ucfirst($this->type);
$title .= ' - ';
$title .= Str::title(str_replace('_', ' ', $this->subtype));
return $title;
}
}

View File

@ -0,0 +1,44 @@
<?php
/**
* GraphType.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 2022 Tony Murray
* @author Tony Murray <murraytony@gmail.com>
*/
namespace LibreNMS\Enum;
use LibreNMS\Config;
enum ImageFormat : string
{
case png = 'png';
case svg = 'svg';
public static function forGraph(?string $type = null): ImageFormat
{
return ImageFormat::tryFrom($type ?? Config::get('webui.graph_type')) ?? ImageFormat::png;
}
public function contentType(): string
{
return $this->value == 'svg' ? 'image/svg+xml' : 'image/png';
}
}

View File

@ -27,9 +27,12 @@ namespace LibreNMS\Util;
use App\Facades\DeviceCache;
use App\Models\Device;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
use LibreNMS\Config;
use LibreNMS\Data\Graphing\GraphImage;
use LibreNMS\Data\Graphing\GraphParameters;
use LibreNMS\Enum\ImageFormat;
use LibreNMS\Exceptions\RrdGraphException;
use Rrd;
@ -65,7 +68,7 @@ class Graph
return self::getImage($vars)->base64();
}
return self::getImage($vars)->data();
return self::getImage($vars)->data;
}
/**
@ -84,7 +87,7 @@ class Graph
throw $e;
}
return new GraphImage(self::imageType(), 'Error', $e->generateErrorImage());
return new GraphImage(ImageFormat::forGraph(), 'Error', $e->generateErrorImage());
}
}
@ -111,28 +114,31 @@ class Graph
$vars = Url::parseLegacyPathVars($vars);
}
[$type, $subtype] = extract_graph_type($vars['type']);
$graph_title = '';
if (isset($vars['device'])) {
$device = device_by_id_cache(is_numeric($vars['device']) ? $vars['device'] : getidbyname($vars['device']));
DeviceCache::setPrimary($device['device_id']);
//set default graph title
$graph_title = DeviceCache::getPrimary()->displayName();
}
// variables for included graphs
$width = $vars['width'] ?? 400;
$height = $vars['height'] ?? $width / 3;
$title = $vars['title'] ?? '';
$vertical = $vars['vertical'] ?? '';
$legend = $vars['legend'] ?? false;
$from = parse_at_time($vars['from'] ?? '-1d');
$to = empty($vars['to']) ? time() : parse_at_time($vars['to']);
$period = ($to - $from);
$prev_from = ($from - $period);
$graph_image_type = $vars['graph_type'] ?? Config::get('webui.graph_type');
$graph_params = new GraphParameters($vars);
// set php variables for legacy graphs
$type = $graph_params->type;
$subtype = $graph_params->subtype;
$height = $graph_params->height;
$width = $graph_params->width;
$from = $graph_params->from;
$to = $graph_params->to;
$period = $graph_params->period;
$prev_from = $graph_params->prev_from;
$inverse = $graph_params->inverse;
$in = $graph_params->in;
$out = $graph_params->out;
$float_precision = $graph_params->float_precision;
$title = $graph_params->visible('title');
$nototal = ! $graph_params->visible('total');
$nodetails = ! $graph_params->visible('details');
$noagg = ! $graph_params->visible('aggregate');
$rrd_options = '';
$rrd_filename = null;
@ -152,22 +158,17 @@ class Graph
throw new RrdGraphException("{$type}_$subtype template missing", "{$type}_$subtype missing", $width, $height);
}
if ($graph_image_type === 'svg') {
$rrd_options .= ' --imgformat=SVG';
if ($width < 350) {
$rrd_options .= ' -m 0.75 -R light';
}
}
if (empty($rrd_options)) {
throw new RrdGraphException('Graph Definition Error', 'Def Error', $width, $height);
}
$rrd_options = $graph_params . ' ' . $rrd_options;
// Generating the graph!
try {
$image_data = Rrd::graph($rrd_options);
return new GraphImage(self::imageType($graph_image_type), $title ?? $graph_title, $image_data);
return new GraphImage($graph_params->imageFormat, $graph_params->title, $image_data);
} catch (RrdGraphException $e) {
// preserve original error if debug is enabled, otherwise make it a little more user friendly
if (Debug::isEnabled()) {
@ -236,34 +237,19 @@ class Graph
return Config::get("graph_types.$type.$subtype.section") == 'mib';
}
public static function getOverviewGraphsForDevice($device)
public static function getOverviewGraphsForDevice(Device $device): array
{
if ($device->snmp_disable) {
return Config::getOsSetting('ping', 'over');
return Arr::wrap(Config::getOsSetting('ping', 'over'));
}
if ($graphs = Config::getOsSetting($device->os, 'over')) {
return $graphs;
return Arr::wrap($graphs);
}
$os_group = Config::getOsSetting($device->os, 'group');
return Config::get("os_group.$os_group.over", Config::get('os.default.over'));
}
/**
* Get the http content type of the image
*
* @param string|null $type svg or png
* @return string
*/
public static function imageType(?string $type = null): string
{
if ($type === null) {
$type = Config::get('webui.graph_type');
}
return $type === 'svg' ? 'image/svg+xml' : 'image/png';
return Arr::wrap(Config::get("os_group.$os_group.over", Config::get('os.default.over')));
}
/**

View File

@ -148,13 +148,13 @@ class Mail
$image = Graph::getImage($url);
// attach image
$fileName = substr(Clean::fileName($image->title() ?: $cid), 0, 250);
$fileName = substr(Clean::fileName($image->title ?: $cid), 0, 250);
$mail->addStringEmbeddedImage(
$image->data(),
$image->data,
$cid,
$fileName . '.' . $image->fileExtension(),
PHPMailer::ENCODING_BASE64,
$image->imageType()
$image->format->contentType()
);
// update image tag to link to attached image, or just the image name

View File

@ -132,4 +132,35 @@ class Time
return implode(' ,', $ret);
}
/**
* Parse a time string into a timestamp including signed relative times using:
* m - month
* d - day
* h - hour
* y - year
*/
public static function parseAt(string|int $time): int
{
if (is_numeric($time)) {
return $time < 0 ? time() + $time : intval($time);
}
if (preg_match('/^[+-]\d+[hdmy]$/', $time)) {
$units = [
'm' => 60,
'h' => 3600,
'd' => 86400,
'y' => 31557600,
];
$value = Number::cast(substr($time, 1, -1));
$unit = substr($time, -1);
$offset = ($time[0] == '-' ? -1 : 1) * $units[$unit] * $value;
return time() + $offset;
}
return (int) strtotime($time);
}
}

View File

@ -5,6 +5,7 @@ namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use LibreNMS\Config;
use LibreNMS\Enum\ImageFormat;
use LibreNMS\Exceptions\RrdGraphException;
use LibreNMS\Util\Debug;
use LibreNMS\Util\Graph;
@ -33,20 +34,20 @@ class GraphController extends Controller
}
$headers = [
'Content-type' => $graph->imageType(),
'Content-type' => $graph->contentType(),
];
if ($output == 'base64') {
return response($graph, 200, $headers);
}
return response($graph->data(), 200, $headers);
return response($graph->data, 200, $headers);
} catch (RrdGraphException $e) {
if (Debug::isEnabled()) {
throw $e;
}
return response($e->generateErrorImage(), 500, ['Content-type' => Graph::imageType()]);
return response($e->generateErrorImage(), 500, ['Content-type' => ImageFormat::forGraph()->contentType()]);
}
}
}

View File

@ -74,7 +74,7 @@ includes/html/graphs/device/pulse_sessions.inc.php
$rrd_filename = Rrd::name($device['hostname'], 'pulse_sessions');
require 'includes/graphs/common.inc.php';
require 'includes/html/graphs/common.inc.php';
$ds = 'sessions';

View File

@ -91,10 +91,10 @@ function api_get_graph(array $vars)
$graph = Graph::get($vars);
if ($vars['output'] === 'base64') {
return api_success(['image' => $graph->base64(), 'content-type' => $graph->imageType()], 'image');
return api_success(['image' => $graph->base64(), 'content-type' => $graph->contentType()], 'image');
}
return response($graph->data(), 200, ['Content-Type' => $graph->imageType()]);
return response($graph->data, 200, ['Content-Type' => $graph->contentType()]);
} catch (\LibreNMS\Exceptions\RrdGraphException $e) {
return api_error(500, $e->getMessage());
}

View File

@ -989,17 +989,6 @@ function eventlog_severity($eventlog_severity)
}
} // end eventlog_severity
/**
* Get the http content type of the image
*
* @param string $type svg or png
* @return string
*/
function get_image_type(string $type)
{
return \LibreNMS\Util\Graph::imageType($type);
}
function get_oxidized_nodes_list()
{
$context = stream_context_create([
@ -1045,36 +1034,6 @@ function generate_stacked_graphs($transparency = '88')
}
}
/**
* Parse AT time spec, does not handle the entire spec.
*
* @param string|int $time
* @return int
*/
function parse_at_time($time)
{
if (is_numeric($time)) {
return $time < 0 ? time() + $time : intval($time);
}
if (preg_match('/^[+-]\d+[hdmy]$/', $time)) {
$units = [
'm' => 60,
'h' => 3600,
'd' => 86400,
'y' => 31557600,
];
$value = substr($time, 1, -1);
$unit = substr($time, -1);
$offset = ($time[0] == '-' ? -1 : 1) * $units[$unit] * $value;
return time() + $offset;
}
return (int) strtotime($time);
}
/**
* Returns the sysname of a device with a html line break prepended.
* if the device has an empty sysname it will return device's hostname instead

View File

@ -1,119 +1,7 @@
<?php
use LibreNMS\Config;
use LibreNMS\Util\Clean;
if (isset($_GET['from']) && $_GET['from']) {
$from = parse_at_time($_GET['from']);
}
if (isset($_GET['to']) && $_GET['to']) {
$to = parse_at_time($_GET['to']);
}
if (isset($_GET['width']) && $_GET['width']) {
$width = (int) $_GET['width'];
}
if (isset($_GET['height']) && $_GET['height']) {
$height = (int) $_GET['height'];
}
if (! empty($_GET['inverse'])) {
$in = 'out';
$out = 'in';
$inverse = true;
} else {
$in = 'in';
$out = 'out';
}
if (isset($_GET['legend']) && $_GET['legend'] == 'no') {
$rrd_options .= ' -g';
}
if (isset($_GET['nototal'])) {
$nototal = ((bool) $_GET['nototal']);
} else {
$nototal = true;
}
if (isset($_GET['nodetails'])) {
$nodetails = ((bool) $_GET['nodetails']);
} else {
$nodetails = false;
}
if (isset($_GET['noagg'])) {
$noagg = ((bool) $_GET['noagg']);
} else {
$noagg = true;
}
if (isset($_GET['title']) && $_GET['title'] == 'yes') {
$rrd_options .= " --title='" . $graph_title . "' ";
}
if (isset($_GET['graph_title'])) {
$rrd_options .= " --title='" . Clean::alphaDashSpace($_GET['graph_title']) . "' ";
}
if (! isset($scale_min) && ! isset($scale_max)) {
$rrd_options .= ' --alt-autoscale-max';
}
if (! isset($scale_min) && ! isset($scale_max) && ! isset($norigid)) {
$rrd_options .= ' --rigid';
}
if (isset($scale_min)) {
$rrd_options .= " -l $scale_min";
}
if (isset($scale_max)) {
$rrd_options .= " -u $scale_max";
}
if (isset($scale_rigid)) {
$rrd_options .= ' -r';
}
if (! isset($float_precision)) {
$float_precision = 2;
}
$rrd_options .= ' -E --start ' . $from . ' --end ' . $to . ' --width ' . $width . ' --height ' . $height . ' ';
if (Config::get('applied_site_style') == 'dark') {
$rrd_options .= \LibreNMS\Config::get('rrdgraph_def_text_dark') . ' -c FONT#' . ltrim(\LibreNMS\Config::get('rrdgraph_def_text_color_dark'), '#');
} else {
$rrd_options .= \LibreNMS\Config::get('rrdgraph_def_text') . ' -c FONT#' . ltrim(\LibreNMS\Config::get('rrdgraph_def_text_color'), '#');
}
if (! empty($_GET['bg'])) {
$rrd_options .= ' -c CANVAS#' . Clean::alphaDash($_GET['bg']) . ' ';
}
if (! empty($_GET['bbg'])) {
$rrd_options .= ' -c BACK#' . Clean::alphaDash($_GET['bbg']) . ' ';
}
if (! empty($_GET['font'])) {
$rrd_options .= ' -c FONT#' . Clean::alphaDash($_GET['font']) . ' ';
}
if ($height < '99') {
$rrd_options .= ' --only-graph';
}
if ($width <= '300') {
$rrd_options .= ' --font LEGEND:7:' . \LibreNMS\Config::get('mono_font') . ' --font AXIS:6:' . \LibreNMS\Config::get('mono_font');
} else {
$rrd_options .= ' --font LEGEND:8:' . \LibreNMS\Config::get('mono_font') . ' --font AXIS:7:' . \LibreNMS\Config::get('mono_font');
}
$rrd_options .= ' --font-render-mode normal';
if (isset($_GET['absolute']) && $_GET['absolute'] == '1') {
$rrd_options .= ' --full-size-mode';
}
// populate legacy global variables
$graph_params->scale_max = isset($scale_max) ? (int) $scale_max : $graph_params->scale_max;
$graph_params->scale_min = isset($scale_min) ? (int) $scale_min : $graph_params->scale_min;
$graph_params->scale_rigid = $scale_rigid ?? $graph_params->scale_rigid;
$graph_params->title = $graph_title ?? $graph_params->title;

View File

@ -1,37 +1,41 @@
<?php
use LibreNMS\Config;
use LibreNMS\Data\Graphing\GraphParameters;
use LibreNMS\Enum\ImageFormat;
global $debug;
[$type, $subtype] = extract_graph_type($vars['type']);
if (isset($vars['device'])) {
$device = is_numeric($vars['device'])
? device_by_id_cache($vars['device'])
: device_by_name($vars['device']);
DeviceCache::setPrimary($device['device_id']);
}
// FIXME -- remove these
$width = $vars['width'] ?? 400;
$height = $vars['height'] ?? round($width / 3);
$title = $vars['title'] ?? '';
$vertical = $vars['vertical'] ?? '';
$legend = $vars['legend'] ?? false;
$output = (! empty($vars['output']) ? $vars['output'] : 'default');
$from = empty($vars['from']) ? Config::get('time.day') : parse_at_time($vars['from']);
$to = empty($vars['to']) ? Config::get('time.now') : parse_at_time($vars['to']);
$period = ($to - $from);
$prev_from = ($from - $period);
$graph_image_type = $vars['graph_type'] ?? Config::get('webui.graph_type');
// variables for included graphs
$graph_params = new GraphParameters($vars);
// set php variables for legacy graphs
$type = $graph_params->type;
$subtype = $graph_params->subtype;
$height = $graph_params->height;
$width = $graph_params->width;
$from = $graph_params->from;
$to = $graph_params->to;
$period = $graph_params->period;
$prev_from = $graph_params->prev_from;
$inverse = $graph_params->inverse;
$in = $graph_params->in;
$out = $graph_params->out;
$float_precision = $graph_params->float_precision;
$title = $graph_params->visible('title');
$nototal = ! $graph_params->visible('total');
$nodetails = ! $graph_params->visible('details');
$noagg = ! $graph_params->visible('aggregate');
$rrd_options = '';
require Config::get('install_dir') . "/includes/html/graphs/$type/auth.inc.php";
//set default graph title
$graph_title = format_hostname($device);
if ($auth && is_customoid_graph($type, $subtype)) {
$unit = $vars['unit'];
include Config::get('install_dir') . '/includes/html/graphs/customoid/customoid.inc.php';
@ -49,12 +53,7 @@ if ($auth === null) {
return;
}
if ($graph_image_type === 'svg') {
$rrd_options .= ' --imgformat=SVG';
if ($width < 350) {
$rrd_options .= ' -m 0.75 -R light';
}
}
$rrd_options = $graph_params . ' ' . $rrd_options;
// command output requested
if (! empty($command_only)) {
@ -88,9 +87,9 @@ try {
// 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" />';
echo '<img src="data:' . ImageFormat::forGraph()->contentType() . ';base64,' . base64_encode($image_data) . '" alt="graph" />';
} else {
header('Content-type: ' . get_image_type(Config::get('webui.graph_type')));
header('Content-type: ' . ImageFormat::forGraph()->contentType());
echo $output === 'base64' ? base64_encode($image_data) : $image_data;
}
} catch (\LibreNMS\Exceptions\RrdGraphException $e) {

View File

@ -1,6 +1,7 @@
<?php
use LibreNMS\Config;
use LibreNMS\Util\Time;
unset($vars['page']);
@ -14,8 +15,8 @@ if (session('widescreen')) {
$thumb_width = 113;
}
$vars['from'] = parse_at_time($vars['from'] ?? '') ?: Config::get('time.day');
$vars['to'] = parse_at_time($vars['to'] ?? '') ?: Config::get('time.now');
$vars['from'] = Time::parseAt($vars['from'] ?? '') ?: Config::get('time.day');
$vars['to'] = Time::parseAt($vars['to'] ?? '') ?: Config::get('time.now');
preg_match('/^(?P<type>[A-Za-z0-9]+)_(?P<subtype>.+)/', $vars['type'], $graphtype);
@ -188,6 +189,7 @@ if (! $auth) {
}
if (! empty($vars['showcommand'])) {
$vars = $graph_array;
$_GET = $graph_array;
$command_only = 1;

View File

@ -4935,11 +4935,11 @@
"type": "text"
},
"rrdgraph_def_text": {
"default": "-c BACK#EEEEEE00 -c SHADEA#EEEEEE00 -c SHADEB#EEEEEE00 -c CANVAS#FFFFFF00 -c GRID#a5a5a5 -c MGRID#FF9999 -c FRAME#5e5e5e -c ARROW#5e5e5e -R normal",
"default": "-c BACK#EEEEEE00 -c SHADEA#EEEEEE00 -c SHADEB#EEEEEE00 -c CANVAS#FFFFFF00 -c GRID#a5a5a5 -c MGRID#FF9999 -c FRAME#5e5e5e -c ARROW#5e5e5e",
"type": "text"
},
"rrdgraph_def_text_dark": {
"default": "-c BACK#EEEEEE00 -c SHADEA#EEEEEE00 -c SHADEB#EEEEEE00 -c CANVAS#FFFFFF00 -c GRID#292929 -c MGRID#2f343e -c FRAME#5e5e5e -c ARROW#5e5e5e -R normal",
"default": "-c BACK#EEEEEE00 -c SHADEA#EEEEEE00 -c SHADEB#EEEEEE00 -c CANVAS#FFFFFF00 -c GRID#292929 -c MGRID#2f343e -c FRAME#5e5e5e -c ARROW#5e5e5e",
"type": "text"
},
"rrdgraph_def_text_color": {

View File

@ -5411,22 +5411,12 @@ parameters:
path: LibreNMS/Util/GitHub.php
-
message: "#^Method LibreNMS\\\\Util\\\\Graph\\:\\:getOverviewGraphsForDevice\\(\\) has no return type specified\\.$#"
message: "#^Unreachable statement \\- code above always terminates\\.$#"
count: 1
path: LibreNMS/Util/Graph.php
-
message: "#^Method LibreNMS\\\\Util\\\\Graph\\:\\:getOverviewGraphsForDevice\\(\\) has parameter \\$device with no type specified\\.$#"
count: 1
path: LibreNMS/Util/Graph.php
-
message: "#^Result of && is always false\\.$#"
count: 1
path: LibreNMS/Util/Graph.php
-
message: "#^Variable \\$rrd_filename in isset\\(\\) always exists and is always null\\.$#"
message: "#^Variable \\$rrd_options in empty\\(\\) always exists and is always falsy\\.$#"
count: 1
path: LibreNMS/Util/Graph.php

View File

@ -27,6 +27,7 @@ namespace LibreNMS\Tests;
use LibreNMS\Device\YamlDiscovery;
use LibreNMS\Util\Rewrite;
use LibreNMS\Util\Time;
class FunctionsTest extends TestCase
{
@ -102,20 +103,20 @@ class FunctionsTest extends TestCase
public function testParseAtTime()
{
$this->assertEquals(time(), parse_at_time('now'), 'now did not match');
$this->assertEquals(time() + 180, parse_at_time('+3m'), '+3m did not match');
$this->assertEquals(time() + 7200, parse_at_time('+2h'), '+2h did not match');
$this->assertEquals(time() + 172800, parse_at_time('+2d'), '+2d did not match');
$this->assertEquals(time() + 63115200, parse_at_time('+2y'), '+2y did not match');
$this->assertEquals(time() - 180, parse_at_time('-3m'), '-3m did not match');
$this->assertEquals(time() - 7200, parse_at_time('-2h'), '-2h did not match');
$this->assertEquals(time() - 172800, parse_at_time('-2d'), '-2d did not match');
$this->assertEquals(time() - 63115200, parse_at_time('-2y'), '-2y did not match');
$this->assertEquals(429929439, parse_at_time('429929439'));
$this->assertEquals(212334234, parse_at_time(212334234));
$this->assertEquals(time() - 43, parse_at_time('-43'), '-43 did not match');
$this->assertEquals(0, parse_at_time('invalid'));
$this->assertEquals(606614400, parse_at_time('March 23 1989 UTC'));
$this->assertEquals(time() + 86400, parse_at_time('+1 day'));
$this->assertEquals(time(), Time::parseAt('now'), 'now did not match');
$this->assertEquals(time() + 180, Time::parseAt('+3m'), '+3m did not match');
$this->assertEquals(time() + 7200, Time::parseAt('+2h'), '+2h did not match');
$this->assertEquals(time() + 172800, Time::parseAt('+2d'), '+2d did not match');
$this->assertEquals(time() + 63115200, Time::parseAt('+2y'), '+2y did not match');
$this->assertEquals(time() - 180, Time::parseAt('-3m'), '-3m did not match');
$this->assertEquals(time() - 7200, Time::parseAt('-2h'), '-2h did not match');
$this->assertEquals(time() - 172800, Time::parseAt('-2d'), '-2d did not match');
$this->assertEquals(time() - 63115200, Time::parseAt('-2y'), '-2y did not match');
$this->assertEquals(429929439, Time::parseAt('429929439'));
$this->assertEquals(212334234, Time::parseAt(212334234));
$this->assertEquals(time() - 43, Time::parseAt('-43'), '-43 did not match');
$this->assertEquals(0, Time::parseAt('invalid'));
$this->assertEquals(606614400, Time::parseAt('March 23 1989 UTC'));
$this->assertEquals(time() + 86400, Time::parseAt('+1 day'));
}
}