Skip to content

Commit

Permalink
Add apex charts
Browse files Browse the repository at this point in the history
  • Loading branch information
murrant committed Nov 30, 2024
1 parent 6638e71 commit c2409fb
Show file tree
Hide file tree
Showing 10 changed files with 1,105 additions and 76 deletions.
59 changes: 59 additions & 0 deletions LibreNMS/Data/ChartData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace LibreNMS\Data;

class ChartData
{

protected function __construct(
protected string $format,
protected array $data,
protected array $map = [],
) {}

public static function createFromRrd(array $legend, array $data): static
{
$map = ['_timestamp' => 0];
// RRD legend is shifted by one when timestamp is included
foreach ($legend as $index => $name) {
$map[$name] = $index + 1;
}

return new static('grouped_by_point', $data, $map);
}

/**
* @param ChartDataset[] $chartDatasets
* @return array
*/
public function forChartJs(array $chartDatasets): array
{
$data = [];

if ($this->format == 'grouped_by_point') {
$timestamp_index = $this->map['_timestamp'];
$ds = array_diff_key($this->map, ['_timestamp' => null]);

foreach($this->data as $point) {
foreach($ds as $name => $index) {
$data[$name][] = ['x' => (int) $point[$timestamp_index], 'y' => $point[$index]];
}
}
}

$datasets = [];
foreach($chartDatasets as $ds) {
$datasets[] = [
'label' => $ds->label,
'data' => $data[$ds->name],
'fill' => true,
'borderColor' => $ds->color,
'backgroundColor' => $ds->fill,
];
}

return [
'datasets' => $datasets,
];
}
}
13 changes: 13 additions & 0 deletions LibreNMS/Data/ChartDataset.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace LibreNMS\Data;

class ChartDataset
{
public function __construct(
public string $name,
public string $label,
public ?string $color = null,
public ?string $fill = null,
){}
}
108 changes: 53 additions & 55 deletions LibreNMS/Data/Source/Rrd.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,95 +3,93 @@
namespace LibreNMS\Data\Source;

use App\Facades\LibrenmsConfig;
use LibreNMS\Util\Number;
use LibreNMS\Data\ChartData;
use LibreNMS\Enum\RrdCF;
use Symfony\Component\Process\Process;

class Rrd
{
private array $options;
private int $start;
private ?int $end = null;
private array $xport = [];
private array $options = [];

private function __construct(
public int $start,
public ?int $end = null,
) {
$this->start = time() - 3600;
$rrdcached = LibrenmsConfig::get('rrdcached');
$this->options = [LibrenmsConfig::get('rrdtool'), 'xport'];
if ($rrdcached) {
array_push($this->options, '--daemon', $rrdcached);
}
}

public static function make(): static
public static function make(int $start, ?int $end = null): static
{
return new static;
return new static($start, $end);
}

public function get(): array
{
$timeframe = ['--start', $this->start];
if ($this->end) {
array_push($timeframe, '--end', $this->end);
}
foreach($this->xport as $def) {
$this->options[] = "XPORT:$def";
}

$proc = new Process($this->options + $timeframe);
$proc->run();

$xport_data = [];
$timestamps = [];
$output = $proc->getOutput();
$error = $proc->getErrorOutput();
if($error) {
throw new \Exception($error);
}

$data = new \SimpleXMLElement($output);
$current_timestamp = (int) $data->meta->start;
$step = (int) $data->meta->step;

foreach($data->data->row as $row) {
foreach ($this->xport as $index => $def) {
$value = (string) $row->v[$index];
$xport_data[$def][] = $value == 'NaN' ? null : Number::cast($value);
}
$timestamps[] = $current_timestamp;
$current_timestamp += $step;
}

return [
'data' => $xport_data,
'timestamps' => $timestamps,
];
}

public function def(string $label, string $dataset, string $rrd = null, string $aggregation = 'AVERAGE'): static
public function def(string $label, string $dataset, string $rrd = null, RrdCF $consolidationFunction = RrdCF::AVERAGE): static
{
$def = "DEF:$label";
if ($rrd) {
$def .= "=$rrd";
}
$def .= ":$dataset:$aggregation";
$def .= ":$dataset:$consolidationFunction->name";

$this->options[] = $def;

return $this;
}

public function cdef(string $label, string $source): static
public function cdef(string $label, string $rpnExpression): static
{
$this->options[] = "CDEF:$label=$source";
$this->options[] = "CDEF:$label=$rpnExpression";

return $this;
}

public function xport(array $defs): static
public function graph(): string
{
$this->xport = $defs;
return $this->run('graph');
}

return $this;
public function xport(array $defs): ChartData
{
// set up XPORT options either ds only or ds => label
foreach($defs as $def) {
$this->options[] = "XPORT:$def:$def";
}

$xport = json_decode($this->run('xport'));

return ChartData::createFromRrd($xport->meta->legend, $xport->data);
}

private function run(string $command): string
{
$cli = [
LibrenmsConfig::get('rrdtool'),
$command,
'--showtime',
'--json',
'--start',
$this->start,
];
if ($this->end) {
array_push($cli, '--end', $this->end);
}
$rrdcached = LibrenmsConfig::get('rrdcached');
if ($rrdcached) {
array_push($cli, '--daemon', $rrdcached);
}

$proc = new Process(array_merge($cli, $this->options));
$proc->run();

$error = $proc->getErrorOutput();
if($error) {
throw new \Exception($error);
}

return $proc->getOutput();
}
}
11 changes: 11 additions & 0 deletions LibreNMS/Enum/RrdCF.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace LibreNMS\Enum;

enum RrdCF
{
case AVERAGE;
case MIN;
case MAX;
case LAST;
}
41 changes: 21 additions & 20 deletions app/Filament/Widgets/Graph.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,41 @@

namespace App\Filament\Widgets;

use App\Models\Device;
use Filament\Widgets\ChartWidget;
use Illuminate\Support\Facades\Date;
use LibreNMS\Data\ChartDataset;
use LibreNMS\Data\ChartDatasets;
use LibreNMS\Data\Source\Rrd;

class Graph extends ChartWidget
{
protected static ?string $heading = 'Graph';

protected static ?array $options = [
'scales' => [
'x' => [
'type' => 'timeseries',
],
],
];

protected function getData(): array
{
$port_id = 294;
$hostname = 'palmer.rtr.ncn.net';
$port_id = 3752;
$device_id = 986;
$hostname = Device::whereDeviceId($device_id)->value('hostname');
$rrd = \App\Facades\Rrd::name($hostname, \App\Facades\Rrd::portName($port_id));
$data = Rrd::make()
$data = Rrd::make(time() - 3600)
->def('in_oct', 'INOCTETS', $rrd)
->def('out_oct', 'OUTOCTETS', $rrd)
->cdef('in_bits', 'in_oct,8,*')
->cdef('out_bits', 'out_oct,8,*')
->xport(['in_bits', 'out_bits'])
->get();

return [
'datasets' => [
[
'label' => 'In Bits/s',
'data' => $data['data']['in_bits'],
],
[
'label' => 'Out Bits/s',
'data' => $data['data']['out_bits'],
]
],
'labels' => array_map(fn($timestamp) => Date::createFromTimestamp($timestamp)->toDateTimeString(), $data['timestamps']),
];
->xport(['in_bits', 'out_bits']);

return $data->forChartJs([
new ChartDataset('in_bits', 'In Bits/s', '#608720', '#90B040'),
new ChartDataset('out_bits', 'Out Bits/s', '#606090', '#8080C0'),
]);
}

protected function getType(): string
Expand Down
68 changes: 68 additions & 0 deletions app/Filament/Widgets/NetworkChart.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

namespace App\Filament\Widgets;

use Leandrocfe\FilamentApexCharts\Widgets\ApexChartWidget;

class NetworkChart extends ApexChartWidget
{
/**
* Chart Id
*
* @var string
*/
protected static ?string $chartId = 'networkChart';

/**
* Widget Title
*
* @var string|null
*/
protected static ?string $heading = 'NetworkChart';



/**
* Chart options (series, labels, types, size, animations...)
* https://apexcharts.com/docs/options
*
* @return array
*/
protected function getOptions(): array
{
return [
'chart' => [
'type' => 'area',
'height' => 300,
],
'series' => [
[
'name' => 'NetworkChart',
'data' => [7, 4, 6, 10, 14, 7, 5, 9, 10, 15, 13, 18],
],
],
'xaxis' => [
'categories' => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
'labels' => [
'style' => [
'fontFamily' => 'inherit',
],
],
],
'yaxis' => [
'labels' => [
'style' => [
'fontFamily' => 'inherit',
],
],
],
'colors' => ['#f59e0b'],
'stroke' => [
'curve' => 'smooth',
],
'dataLabels' => [
'enabled' => false,
],
];
}
}
4 changes: 4 additions & 0 deletions app/Providers/Filament/LibrenmsPanelProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Illuminate\Session\Middleware\AuthenticateSession;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
use Leandrocfe\FilamentApexCharts\FilamentApexChartsPlugin;

class LibrenmsPanelProvider extends PanelProvider
{
Expand All @@ -29,6 +30,9 @@ public function panel(Panel $panel): Panel
->colors([
'primary' => Color::Amber,
])
->plugins([
FilamentApexChartsPlugin::make()
])
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
->pages([])
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"laravel/framework": "^10.8",
"laravel/tinker": "^2.8",
"laravel/ui": "^4.2",
"leandrocfe/filament-apex-charts": "^3.1",
"librenms/laravel-vue-i18n-generator": "dev-master",
"librenms/plugin-interfaces": "^1.0",
"mews/purifier": "^3.4",
Expand Down
Loading

0 comments on commit c2409fb

Please sign in to comment.