Skip to content
83 changes: 83 additions & 0 deletions LibreNMS/Data/Graphing/AbstractGraph.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

/**
* AbstractGraph.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 2026 Tony Murray
* @author Tony Murray <murraytony@gmail.com>
*/

namespace LibreNMS\Data\Graphing;

use App\Facades\DeviceCache;
use App\Facades\PortCache;
use App\Models\Device;
use App\Models\Port;
use LibreNMS\Interfaces\Data\Graphing\GraphInterface;

abstract class AbstractGraph implements GraphInterface
{
protected Device $device;
protected ?Port $port;

public function __construct(
protected readonly GraphParameters $params,
protected readonly array $vars = [],
) {
$device_id = $this->vars['device'] ?? ($this->params->type == 'device' ? ($this->vars['id'] ?? null) : null);
$this->device = DeviceCache::get($device_id);

$port_id = $this->vars['port'] ?? ($this->params->type == 'port' ? ($this->vars['id'] ?? null) : null);
$this->port = PortCache::get($port_id);

$this->init();
}

public function getParams(): GraphParameters
{
return $this->params;
}

public function validation(): array
{
return [
'type' => ['required', 'string', 'regex:/^[a-z][a-z0-9]*_[a-zA-Z0-9_]+$/'],
'id' => ['nullable', 'regex:/^[A-Za-z0-9,._-]+$/'],
'from' => ['nullable', 'regex:/^(-?\d+|-?\d+[smhdwMy]|now|end)$/'],
'to' => ['nullable', 'regex:/^(-?\d+|-?\d+[smhdwMy]|now|end)$/'],
'width' => ['nullable', 'integer', 'min:10', 'max:10000'],
'height' => ['nullable', 'integer', 'min:10', 'max:8000'],
'legend' => ['nullable', 'in:yes,no,0,1'],
'bg' => ['nullable', 'regex:/^[0-9A-Fa-f]{6}$/'],
'title' => ['nullable', 'string', 'max:255'],
'output' => ['nullable', 'in:png,svg,json'],
];
}

public function getPageTitle(): string
{
return $this->getGraphTitle();
}

protected function init(): void
{
// for child init
}
}
187 changes: 187 additions & 0 deletions LibreNMS/Data/Graphing/Builders/MultiLineGraphBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
<?php

/**
* MultiLineGraphBuilder.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 2026 Tony Murray
* @author Tony Murray <murraytony@gmail.com>
*/

namespace LibreNMS\Data\Graphing\Builders;

use App\Facades\LibrenmsConfig;
use LibreNMS\Data\Graphing\GraphParameters;
use LibreNMS\Data\Store\Rrd;

class MultiLineGraphBuilder
{
private string $unitText = '';
private string $units = '';
private string $colours = 'mixed';
private ?float $scaleMin = null;
private ?float $scaleMax = null;
private bool $nototal = false;
private int $descrLen = 12;

private array $datasets = [];

public function unitText(string $unitText): self
{
$this->unitText = $unitText;

return $this;
}

public function units(string $units): self
{
$this->units = $units;

return $this;
}

public function colours(string $colours): self
{
$this->colours = $colours;

return $this;
}

public function scaleMin(float $scaleMin): self
{
$this->scaleMin = $scaleMin;

return $this;
}

public function scaleMax(float $scaleMax): self
{
$this->scaleMax = $scaleMax;

return $this;
}

public function noTotal(bool $noTotal = true): self
{
$this->nototal = $noTotal;

return $this;
}

public function descrLen(int $descrLen): self
{
$this->descrLen = $descrLen;

return $this;
}

public function addDataset(
string $filename,
string $ds,
string $description,
?string $colour = null,
bool $area = false,
?string $areaColour = null,
bool $invert = false
): self {
$this->datasets[] = [
'filename' => $filename,
'ds' => $ds,
'descr' => $description,
'colour' => $colour,
'area' => $area,
'areacolour' => $areaColour,
'invert' => $invert,
];

return $this;
}

public function build(GraphParameters $graph_params): array
{
$float_precision = $graph_params->float_precision;

if ($this->scaleMin !== null) {
$graph_params->scale_min = (int) $this->scaleMin;
}
if ($this->scaleMax !== null) {
$graph_params->scale_max = (int) $this->scaleMax;
}

$descr_len = $this->descrLen;
if ($this->nototal) {
$descr_len += 2;
}

$rrd_options = [];
$rrd_options[] = 'COMMENT:' . Rrd::fixedSafeDescr($this->unitText, $descr_len) . " Now Min Max Avg\l";

$stackedVal = LibrenmsConfig::get('webui.graph_stacked') ? '1' : '-1';
$rrd_optionsb = [];
$colour_iter = 0;

foreach ($this->datasets as $i => $rrd) {
$colour = $rrd['colour'];
if ($colour === null) {
if (! LibrenmsConfig::get("graph_colours.{$this->colours}.{$colour_iter}")) {
$colour_iter = 0;
}
$colour = LibrenmsConfig::get("graph_colours.{$this->colours}.{$colour_iter}");
$colour_iter++;
}

$areacolour = $rrd['areacolour'];
if ($rrd['area'] && empty($areacolour)) {
$areacolour = $colour . '20';
}

$ds = $rrd['ds'];
$filename = $rrd['filename'];
$descr = Rrd::fixedSafeDescr($rrd['descr'], $descr_len);
$id = 'ds' . $i;

$rrd_options[] = 'DEF:' . $id . "=$filename:$ds:AVERAGE";
$rrd_options[] = 'DEF:' . $id . "min=$filename:$ds:MIN";
$rrd_options[] = 'DEF:' . $id . "max=$filename:$ds:MAX";

if ($rrd['invert']) {
$rrd_options[] = 'CDEF:' . $id . 'i=' . $id . ',' . $stackedVal . ',*';
$rrd_optionsb[] = 'LINE1.25:' . $id . 'i#' . $colour . ":$descr";
if (! empty($areacolour)) {
$rrd_optionsb[] = 'AREA:' . $id . 'i#' . $areacolour;
}
} else {
$rrd_optionsb[] = 'LINE1.25:' . $id . '#' . $colour . ":$descr";
if (! empty($areacolour)) {
$rrd_optionsb[] = 'AREA:' . $id . '#' . $areacolour;
}
}

$rrd_optionsb[] = 'GPRINT:' . $id . ':LAST:%5.' . $float_precision . 'lf%s' . $this->units;
$rrd_optionsb[] = 'GPRINT:' . $id . 'min:MIN:%5.' . $float_precision . 'lf%s' . $this->units;
$rrd_optionsb[] = 'GPRINT:' . $id . 'max:MAX:%5.' . $float_precision . 'lf%s' . $this->units;
$rrd_optionsb[] = 'GPRINT:' . $id . ':AVERAGE:%5.' . $float_precision . "lf%s{$this->units}\\n";
}

array_push($rrd_options, ...$rrd_optionsb);
$rrd_options[] = 'HRULE:0#555555';

return $rrd_options;
}
}
Loading
Loading