mirror of
https://github.com/nidebr/as-stats-gui.git
synced 2025-02-20 11:23:18 +08:00
add graph top
This commit is contained in:
parent
73e49e99cb
commit
46f344c181
@ -7,6 +7,14 @@ config:
|
||||
showv6: true
|
||||
outispositive: false
|
||||
brighten_negative: true
|
||||
vertical_label: true
|
||||
showtitledetail: true
|
||||
show95th: true
|
||||
rrdpath: "/home/nidebr/data/DEV/asstats/rrd"
|
||||
rrdtool: "/usr/bin/rrdtool"
|
||||
## Default graph size if no parameters are specified ##
|
||||
default_graph_width: 600
|
||||
default_graph_height: 360
|
||||
|
||||
##
|
||||
# Custom time intervals for top N AS */
|
||||
|
107404
ressources/asinfo.txt
107404
ressources/asinfo.txt
File diff suppressed because it is too large
Load Diff
@ -46,14 +46,17 @@ class IndexController extends BaseController
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$data = $asDataRepository::get($this->base_data['top'], null, (array) $form->getData());
|
||||
$selectedLinks = KnowlinksRepository::select((array) $form->getData());
|
||||
} else {
|
||||
$data = $asDataRepository::get($this->base_data['top']);
|
||||
$selectedLinks = [];
|
||||
}
|
||||
|
||||
return $this->render('pages/index.html.twig', [
|
||||
'base_data' => $this->base_data,
|
||||
'data' => $data,
|
||||
'knownlinks' => KnowlinksRepository::get(),
|
||||
'selected_links' => $selectedLinks,
|
||||
'form' => [
|
||||
'legend' => $form->createView(),
|
||||
],
|
||||
|
221
src/Controller/RenderController.php
Normal file
221
src/Controller/RenderController.php
Normal file
@ -0,0 +1,221 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Application\ConfigApplication;
|
||||
use App\Exception\ConfigErrorException;
|
||||
use App\Exception\KnownLinksEmptyException;
|
||||
use App\Repository\KnowlinksRepository;
|
||||
use App\Repository\RRDRepository;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class RenderController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @throws ConfigErrorException
|
||||
* @throws KnownLinksEmptyException
|
||||
*/
|
||||
#[Route(
|
||||
path: '/render/graph',
|
||||
name: 'render',
|
||||
methods: ['GET'],
|
||||
)]
|
||||
public function renderGraph(Request $request): Response
|
||||
{
|
||||
$req = $request->query->all();
|
||||
$as = (int) $req['as'];
|
||||
|
||||
if (!\preg_match('/^\d+$/', \sprintf('%s', $as))) {
|
||||
die('Invalid AS');
|
||||
}
|
||||
|
||||
$width = ConfigApplication::getAsStatsConfigGraph()['default_graph_width'];
|
||||
$height = ConfigApplication::getAsStatsConfigGraph()['default_graph_height'];
|
||||
|
||||
if (isset($req['width'])) {
|
||||
$width = (int) $req['width'];
|
||||
}
|
||||
|
||||
if (isset($req['height'])) {
|
||||
$height = (int) $req['height'];
|
||||
}
|
||||
|
||||
$v6_el = '';
|
||||
if ($req['v'] === '6') {
|
||||
$v6_el = 'v6_';
|
||||
}
|
||||
|
||||
$knownlinks = KnowlinksRepository::get();
|
||||
|
||||
if (isset($req['selected_links'])) {
|
||||
$reverse = [];
|
||||
|
||||
foreach ($knownlinks as $link) {
|
||||
$reverse[$link['tag']] = [
|
||||
'color' => $link['color'],
|
||||
'descr' => $link['descr'],
|
||||
];
|
||||
}
|
||||
|
||||
$links = [];
|
||||
foreach (\explode(',', \sprintf('%s', $req['selected_links'])) as $tag) { // @phpstan-ignore-line
|
||||
if (\preg_match('/[^a-zA-Z0-9_]/', $tag)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($reverse[$tag])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$links[] = [
|
||||
'tag' => $tag,
|
||||
'color' => $reverse[$tag]['color'],
|
||||
'descr' => $reverse[$tag]['descr'],
|
||||
];
|
||||
}
|
||||
|
||||
$knownlinks = $links;
|
||||
}
|
||||
|
||||
$rrdfile = RRDRepository::getRRDFileForAS($as);
|
||||
|
||||
$cmd = \sprintf(
|
||||
'%s graph - --slope-mode --alt-autoscale --upper-limit 0 --lower-limit 0 --imgformat=PNG \
|
||||
--base=1000 --height=%s --width=%s --alt-autoscale-max --full-size-mode \
|
||||
--color BACK#ffffff00 --color SHADEA#ffffff00 --color SHADEB#ffffff00 ',
|
||||
ConfigApplication::getAsStatsConfigGraph()['rrdtool'],
|
||||
$height,
|
||||
$width
|
||||
);
|
||||
|
||||
if (ConfigApplication::getAsStatsConfigGraph()['vertical_label']) {
|
||||
if (ConfigApplication::getAsStatsConfigGraph()['outispositive']) {
|
||||
$cmd .= '--vertical-label \'<- IN | OUT ->\' ';
|
||||
} else {
|
||||
$cmd .= '--vertical-label \'<- OUT | IN ->\' ';
|
||||
}
|
||||
}
|
||||
|
||||
if (ConfigApplication::getAsStatsConfigGraph()['showtitledetail'] && isset($req['title']) && $req['title'] !== '') {
|
||||
/** @phpstan-ignore-next-line */
|
||||
$cmd .= \sprintf('--title %s ', \escapeshellarg(\sprintf('%s', $req['title'])));
|
||||
} elseif (isset($req['v']) && is_numeric($req['v'])) {
|
||||
$cmd .= \sprintf('--title IPv%s ', $req['v']);
|
||||
}
|
||||
|
||||
if (isset($req['nolegend']) && $req['nolegend']) {
|
||||
$cmd .= '--no-legend ';
|
||||
}
|
||||
|
||||
if (isset($req['start']) && is_numeric($req['start'])) {
|
||||
$cmd .= \sprintf('--start %s ', $req['start']);
|
||||
}
|
||||
|
||||
if (isset($req['end']) && is_numeric($req['end'])) {
|
||||
$cmd .= \sprintf('--end %s ', $req['end']);
|
||||
}
|
||||
|
||||
foreach ($knownlinks as $link) {
|
||||
$cmd .= \sprintf('DEF:%1$s_%2$sin="%3$s":%1$s_%2$sin:AVERAGE ', $link['tag'], $v6_el, $rrdfile);
|
||||
$cmd .= \sprintf('DEF:%1$s_%2$sout="%3$s":%1$s_%2$sout:AVERAGE ', $link['tag'], $v6_el, $rrdfile);
|
||||
}
|
||||
|
||||
$tot_in_bits = 'CDEF:tot_in_bits=0';
|
||||
$tot_out_bits = 'CDEF:tot_out_bits=0';
|
||||
|
||||
/* generate a CDEF for each DEF to multiply by 8 (bytes to bits), and reverse for outbound */
|
||||
foreach ($knownlinks as $link) {
|
||||
$cmd .= \sprintf('CDEF:%1$s_%2$sin_bits_pos=%1$s_%2$sin,8,* ', $link['tag'], $v6_el);
|
||||
$cmd .= \sprintf('CDEF:%1$s_%2$sout_bits_pos=%1$s_%2$sout,8,* ', $link['tag'], $v6_el);
|
||||
$tot_in_bits .= \sprintf(',%s_%sin_bits_pos,ADDNAN', $link['tag'], $v6_el);
|
||||
$tot_out_bits .= \sprintf(',%s_%sout_bits_pos,ADDNAN', $link['tag'], $v6_el);
|
||||
}
|
||||
|
||||
$cmd .= \sprintf('%s ', $tot_in_bits);
|
||||
$cmd .= \sprintf('%s ', $tot_out_bits);
|
||||
|
||||
$cmd .= 'VDEF:tot_in_bits_95th_pos=tot_in_bits,95,PERCENT ';
|
||||
$cmd .= 'VDEF:tot_out_bits_95th_pos=tot_out_bits,95,PERCENT ';
|
||||
|
||||
if (ConfigApplication::getAsStatsConfigGraph()['outispositive']) {
|
||||
$cmd .= 'CDEF:tot_in_bits_95th=tot_in_bits,POP,tot_in_bits_95th_pos,-1,* ';
|
||||
$cmd .= 'CDEF:tot_out_bits_95th=tot_out_bits,POP,tot_out_bits_95th_pos,1,* ';
|
||||
} else {
|
||||
$cmd .= 'CDEF:tot_in_bits_95th=tot_in_bits,POP,tot_in_bits_95th_pos,1,* ';
|
||||
$cmd .= 'CDEF:tot_out_bits_95th=tot_out_bits,POP,tot_out_bits_95th_pos,-1,* ';
|
||||
}
|
||||
|
||||
foreach ($knownlinks as $link) {
|
||||
if (ConfigApplication::getAsStatsConfigGraph()['outispositive']) {
|
||||
$cmd .= \sprintf('CDEF:%1$s_%2$sin_bits=%1$s_%2$sin_bits_pos,-1,* ', $link['tag'], $v6_el);
|
||||
$cmd .= \sprintf('CDEF:%1$s_%2$sout_bits=%1$s_%2$sout_bits_pos,1,* ', $link['tag'], $v6_el);
|
||||
} else {
|
||||
$cmd .= \sprintf('CDEF:%1$s_%2$sin_bits=%1$s_%2$sin_bits_pos,1,* ', $link['tag'], $v6_el);
|
||||
$cmd .= \sprintf('CDEF:%1$s_%2$sout_bits=%1$s_%2$sout_bits_pos,-1,* ', $link['tag'], $v6_el);
|
||||
}
|
||||
}
|
||||
|
||||
/* generate graph area/stack for inbound */
|
||||
$i = 0;
|
||||
foreach ($knownlinks as $link) {
|
||||
if (ConfigApplication::getAsStatsConfigGraph()['outispositive'] && ConfigApplication::getAsStatsConfigGraph()['brighten_negative']) {
|
||||
$col = \sprintf('%sBB', $link['color']);
|
||||
} else {
|
||||
$col = $link['color'];
|
||||
}
|
||||
|
||||
$descr = \str_replace(':', '\:', $link['descr']); # Escaping colons in description
|
||||
$cmd .= \sprintf('AREA:%s_%sin_bits#%s:"%s"', $link['tag'], $v6_el, $col, $descr);
|
||||
|
||||
if ($i > 0) {
|
||||
$cmd .= ':STACK';
|
||||
}
|
||||
|
||||
$cmd .= ' ';
|
||||
$i++;
|
||||
}
|
||||
|
||||
/* generate graph area/stack for outbound */
|
||||
$i = 0;
|
||||
foreach ($knownlinks as $link) {
|
||||
if (ConfigApplication::getAsStatsConfigGraph()['outispositive'] || !ConfigApplication::getAsStatsConfigGraph()['brighten_negative']) {
|
||||
$col = $link['color'];
|
||||
} else {
|
||||
$col = \sprintf('%sBB', $link['color']);
|
||||
}
|
||||
|
||||
$cmd .= \sprintf('AREA:%s_%sout_bits#%s:', $link['tag'], $v6_el, $col);
|
||||
|
||||
if ($i > 0) {
|
||||
$cmd .= ':STACK';
|
||||
}
|
||||
|
||||
$cmd .= ' ';
|
||||
$i++;
|
||||
}
|
||||
|
||||
$cmd .= 'COMMENT:\' \\n\' ';
|
||||
|
||||
if (ConfigApplication::getAsStatsConfigGraph()['show95th']) {
|
||||
$cmd .= 'LINE1:tot_in_bits_95th#FF0000 ';
|
||||
$cmd .= 'LINE1:tot_out_bits_95th#FF0000 ';
|
||||
$cmd .= 'GPRINT:tot_in_bits_95th_pos:\'95th in %6.2lf%s\' ';
|
||||
$cmd .= 'GPRINT:tot_out_bits_95th_pos:\'/ 95th out %6.2lf%s\\n\' ';
|
||||
}
|
||||
|
||||
# zero line
|
||||
$cmd .= 'HRULE:0#00000080';
|
||||
|
||||
$response = new Response();
|
||||
$response->headers->set('Content-type', 'image/png');
|
||||
$response->sendHeaders();
|
||||
$response->setContent(\sprintf('%s', passthru($cmd)));
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
20
src/Repository/RRDRepository.php
Normal file
20
src/Repository/RRDRepository.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Application\ConfigApplication;
|
||||
|
||||
class RRDRepository
|
||||
{
|
||||
public static function getRRDFileForAS(int $as): string
|
||||
{
|
||||
return \sprintf(
|
||||
'%s/%s/%s.rrd',
|
||||
ConfigApplication::getAsStatsConfigGraph()['rrdpath'],
|
||||
\sprintf('%02x', $as % 256),
|
||||
$as
|
||||
);
|
||||
}
|
||||
}
|
36
src/Twig/GenGraphExtension.php
Normal file
36
src/Twig/GenGraphExtension.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Twig;
|
||||
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFunction;
|
||||
|
||||
class GenGraphExtension extends AbstractExtension
|
||||
{
|
||||
public function __construct(private RouterInterface $router)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<TwigFunction>
|
||||
*/
|
||||
public function getFunctions(): array
|
||||
{
|
||||
return [
|
||||
new TwigFunction('gen_graph', [$this, 'genGraph'], ['is_safe' => ['html']]),
|
||||
];
|
||||
}
|
||||
|
||||
public function genGraph(int $as, int $ipversion): string
|
||||
{
|
||||
return \sprintf(
|
||||
'<img class="img-fuild" alt="Graph IPv%s for AS%s" src="%s">',
|
||||
$ipversion,
|
||||
$as,
|
||||
$this->router->generate('render', ['as' => $as, 'v' => 4, 'title' => 'dede', 'nolegend' => 1, 'start' => 1705921337, 'end' => 1706007737, 'width' => 600, 'height' => 220])
|
||||
);
|
||||
}
|
||||
}
|
@ -17,12 +17,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col mt-1">
|
||||
graph_v4
|
||||
<div class="col-6">
|
||||
{{ gen_graph(as, 4) }}
|
||||
</div>
|
||||
{% if configapplication_graph_showv6() %}
|
||||
<div class="col-5">
|
||||
graph_v6
|
||||
<div class="col-6">
|
||||
{{ gen_graph(as, 6) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
@ -18,7 +18,7 @@
|
||||
{% set counter = counter + 1 %}
|
||||
{% embed 'core/card_top.html.twig' with {as: as, as_data: as_data, data: data, counter: counter, hours: hours|default('24 hours')} only %}
|
||||
{% block rank %}
|
||||
<div class="display-6 fw-bold my-3 rank">
|
||||
<div class="display-6 fw-bold rank">
|
||||
# {{ counter }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
Loading…
x
Reference in New Issue
Block a user