Wireguard application graph cleanup and new wireguard interface/global metrics. (#15847)

This commit is contained in:
bnerickson
2024-03-07 10:27:26 -08:00
committed by GitHub
parent c855d6c2fe
commit d7c31e0ae3
7 changed files with 356 additions and 123 deletions

View File

@@ -0,0 +1,6 @@
<?php
$name = 'wireguard';
$polling_type = 'app';
$bigdescrlen = 100;
$smalldescrlen = 100;

View File

@@ -1,41 +1,60 @@
<?php
$name = 'wireguard';
$polling_type = 'app';
if (isset($vars['interface']) && isset($vars['client'])) {
$interface = $vars['interface'];
$client = $vars['client'];
$interface_client = $vars['interface'] . '-' . $vars['client'];
} else {
$interface_client_list = Rrd::getRrdApplicationArrays($device, $app->app_id, $name);
$interface_client = $interface_client_list[0] ?? '';
}
require 'wireguard-common.inc.php';
$unit_text = 'Minutes';
$interface_client_map = $app->data['mappings'] ?? [];
$colours = 'psychedelic';
$metric_desc = 'Last Handshake';
$metric_name = 'minutes_since_last_handshake';
$rrd_list = [];
$rrdArray = [];
$scale_min = 0;
$unitlen = 7;
$rrdArray = [
'minutes_since_last_handshake' => ['descr' => 'Last Handshake'],
];
$rrd_filename = Rrd::name($device['hostname'], [
$polling_type,
$name,
$app->app_id,
$interface_client,
]);
if (Rrd::checkRrdExists($rrd_filename)) {
foreach ($rrdArray as $rrdVar => $rrdValues) {
$rrd_list[] = [
'filename' => $rrd_filename,
'descr' => $rrdValues['descr'],
'ds' => $rrdVar,
];
if (isset($vars['interface']) && isset($vars['client'])) {
// This section draws the individual graphs in the device application page
// displaying the SPECIFIED wireguard interface and client metric.
$wg_intf_client = $vars['interface'] . '-' . $vars['client'];
$rrdArray[$wg_intf_client] = [
$metric_name => ['descr' => $metric_desc],
];
} elseif (! isset($vars['interface']) && ! isset($vars['client'])) {
// This section draws the graph for the application-specific pages
// displaying ALL wireguard interfaces' clients' metrics.
foreach ($interface_client_map as $wg_intf => $wg_client_list) {
foreach ($wg_client_list as $wg_client) {
$wg_intf_client = $wg_intf . '-' . $wg_client;
$rrdArray[$wg_intf_client] = [
$metric_name => [
'descr' => $wg_intf_client . ' ' . $metric_desc,
],
];
}
}
}
if (! $rrdArray) {
graph_error('No Data to Display', 'No Data');
}
$i = 0;
foreach ($rrdArray as $wg_intf_client => $wg_metric) {
$rrd_filename = Rrd::name($device['hostname'], [
$polling_type,
$name,
$app->app_id,
$wg_intf_client,
]);
if (Rrd::checkRrdExists($rrd_filename)) {
$rrd_list[$i]['filename'] = $rrd_filename;
$rrd_list[$i]['descr'] = $wg_metric[$metric_name]['descr'];
$rrd_list[$i]['ds'] = $metric_name;
$i++;
} else {
graph_error('No Data file ' . basename($rrd_filename), 'No Data');
}
} else {
d_echo('RRD ' . $rrd_filename . ' not found');
}
require 'includes/html/graphs/generic_multi_line_exact_numbers.inc.php';

View File

@@ -1,44 +1,59 @@
<?php
$name = 'wireguard';
$polling_type = 'app';
if (isset($vars['interface']) && isset($vars['client'])) {
$interface = $vars['interface'];
$client = $vars['client'];
$interface_client = $vars['interface'] . '-' . $vars['client'];
} else {
$interface_client_list = Rrd::getRrdApplicationArrays($device, $app->app_id, $name);
$interface_client = $interface_client_list[0] ?? '';
}
$unit_text = 'Bytes';
$ds_in = 'bytes_rcvd';
$in_text = 'Rcvd';
$ds_out = 'bytes_sent';
$out_text = 'Sent';
require 'wireguard-common.inc.php';
$unit_text = 'Bytes/s';
$format = 'bytes';
$print_total = true;
$in_text = 'In';
$out_text = 'Out';
$colour_area_in = 'FF3300';
$colour_line_in = 'FF0000';
$colour_area_out = 'FF6633';
$colour_line_out = 'CC3300';
$colour_area_in_max = 'FF6633';
$colour_area_out_max = 'FF9966';
$rrd_filename = Rrd::name($device['hostname'], [
$polling_type,
$name,
$app->app_id,
$interface_client,
]);
if (! isset($vars['interface']) && ! isset($vars['client'])) {
// This section is called if we're being asked to graph
// the host's wireguard global metrics.
$ds_in = 'bytes_rcvd_total';
$ds_out = 'bytes_sent_total';
} elseif (isset($vars['interface']) && isset($vars['client'])) {
// This section is called if we're being asked to graph
// a wireguard interface's client metrics.
$flattened_name = $vars['interface'] . '-' . $vars['client'];
$ds_in = 'bytes_rcvd';
$ds_out = 'bytes_sent';
} elseif (isset($vars['interface'])) {
// This section is called if we're being asked to graph
// a wireguard interface's metrics.
$flattened_name = $vars['interface'];
$ds_in = 'bytes_rcvd_total_intf';
$ds_out = 'bytes_sent_total_intf';
}
if (! isset($vars['interface']) && ! isset($vars['client'])) {
$rrd_filename = Rrd::name($device['hostname'], [
$polling_type,
$name,
$app->app_id,
]);
} elseif (isset($vars['interface'])) {
$rrd_filename = Rrd::name($device['hostname'], [
$polling_type,
$name,
$app->app_id,
$flattened_name,
]);
}
if (! isset($rrd_filename)) {
graph_error('No Data to Display', 'No Data');
}
if (! Rrd::checkRrdExists($rrd_filename)) {
d_echo('RRD ' . $rrd_filename . ' not found');
graph_error('No Data file ' . basename($rrd_filename), 'No Data');
}
require 'includes/html/graphs/generic_duplex.inc.php';

View File

@@ -1,5 +1,42 @@
<?php
/**
* Builds a graph array and outputs the graph.
*
* @param string $gtype
* @param string $app_id
* @param null|string $interface
* @param null|string $client
* @param string $gtext
*/
function wireguard_graph_printer($gtype, $app_id, $interface, $client, $gtext)
{
$graph_type = $gtype;
$graph_array['height'] = '100';
$graph_array['width'] = '215';
$graph_array['to'] = time();
$graph_array['id'] = $app_id;
$graph_array['type'] = 'application_' . $gtype;
if (! is_null($interface)) {
$graph_array['interface'] = $interface;
}
if (! is_null($client)) {
$graph_array['client'] = $client;
}
echo '<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">' .
$gtext .
'</h3>
</div>
<div class="panel-body">
<div class="row">';
include 'includes/html/print-graphrow.inc.php';
echo '</div>';
echo '</div>';
echo '</div>';
}
$link_array = [
'page' => 'device',
'device' => $device['device_id'],
@@ -7,14 +44,21 @@ $link_array = [
'app' => 'wireguard',
];
$interface_client_map = $app->data['mappings'] ?? [];
$graph_map = [
'interface' => [
'clients' => [],
'total' => [],
],
'total' => [],
];
print_optionbar_start();
echo generate_link('All Interfaces', $link_array);
echo ' | Interfaces: ';
$interface_client_map = $app->data['mappings'] ?? [];
// generate interface links
// generate interface links on the host application page
$i = 0;
foreach ($interface_client_map as $interface => $client_list) {
$label =
@@ -32,14 +76,25 @@ foreach ($interface_client_map as $interface => $client_list) {
print_optionbar_end();
// build the interface/client -> graph map
// generates the global wireguard graph mapping
if (! isset($vars['interface'])) {
$graph_map['total'] = [
'wireguard_traffic' => 'Wireguard Total Traffic',
];
}
foreach ($interface_client_map as $interface => $client_list) {
if (
! isset($vars['interface']) ||
(isset($vars['interface']) && $interface == $vars['interface'])
) {
// generates the interface graph mapping
$graph_map['interface']['total'][$interface] = [
'wireguard_traffic' => $interface . ' ' . 'Total Traffic',
];
foreach ($client_list as $client) {
$interface_client_map[$interface][$client] = [
// generates the interface+client graph mapping
$graph_map['interface']['clients'][$interface][$client] = [
'wireguard_traffic' => $interface . ' ' . $client . ' Traffic',
'wireguard_time' => $interface .
' ' .
@@ -50,29 +105,44 @@ foreach ($interface_client_map as $interface => $client_list) {
}
}
// generate graphs on a per-interface, per-client basis
foreach ($interface_client_map as $interface => $client_list) {
foreach ($client_list as $client => $graphs) {
foreach ($graphs as $gtype => $gtext) {
$graph_type = $gtype;
$graph_array['height'] = '100';
$graph_array['width'] = '215';
$graph_array['to'] = time();
$graph_array['id'] = $app['app_id'];
$graph_array['type'] = 'application_' . $gtype;
$graph_array['interface'] = $interface;
$graph_array['client'] = $client;
echo '<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">' . $gtext . '</h3>
</div>
<div class="panel-body">
<div class="row">';
include 'includes/html/print-graphrow.inc.php';
echo '</div>';
echo '</div>';
echo '</div>';
// print graphs
foreach ($graph_map as $category => $category_map) {
foreach ($category_map as $subcategory => $subcategory_map) {
if ($category === 'total') {
// print graphs for global wireguard metrics
wireguard_graph_printer(
$subcategory,
$app['app_id'],
null,
null,
$subcategory_map
);
} elseif ($category === 'interface') {
foreach ($subcategory_map as $interface => $interface_map) {
foreach ($interface_map as $client => $client_map) {
if ($subcategory === 'total') {
// print graphs for wireguard interface metrics
wireguard_graph_printer(
$client,
$app['app_id'],
$interface,
null,
$client_map
);
} elseif ($subcategory === 'clients') {
foreach ($client_map as $gtype => $gtext) {
// print graphs for wireguard interface+client metrics
wireguard_graph_printer(
$gtype,
$app['app_id'],
$interface,
$client,
$gtext
);
}
}
}
}
}
}
}

View File

@@ -13,37 +13,72 @@ try {
} catch (JsonAppMissingKeysException $e) {
$interface_client_map = $e->getParsedJson();
} catch (JsonAppException $e) {
echo PHP_EOL . $name . ':' . $e->getCode() . ':' . $e->getMessage() . PHP_EOL;
echo PHP_EOL .
$name .
':' .
$e->getCode() .
':' .
$e->getMessage() .
PHP_EOL;
update_application($app, $e->getCode() . ':' . $e->getMessage(), []);
return;
}
$rrd_name = [$polling_type, $name, $app->app_id];
$rrd_def = RrdDefinition::make()
// RRD definition for interface+client metrics.
$rrd_def_intfclient = RrdDefinition::make()
->addDataset('bytes_rcvd', 'DERIVE', 0)
->addDataset('bytes_sent', 'DERIVE', 0)
->addDataset('minutes_since_last_handshake', 'GAUGE', 0);
// RRD definition for interface metrics.
$rrd_def_intf = RrdDefinition::make()
->addDataset('bytes_rcvd_total_intf', 'DERIVE', 0)
->addDataset('bytes_sent_total_intf', 'DERIVE', 0);
// RRD definition for global wireguard metrics.
$rrd_def_total = RrdDefinition::make()
->addDataset('bytes_rcvd_total', 'DERIVE', 0)
->addDataset('bytes_sent_total', 'DERIVE', 0);
$metrics = [];
$mappings = [];
$bytes_rcvd_total = null;
$bytes_sent_total = null;
// Parse json data for interfaces and their respective clients' metrics.
// Add any relevant data to the interface and global metrics within.
foreach ($interface_client_map as $interface => $client_list) {
$finterface = is_string($interface) ? filter_var($interface, FILTER_SANITIZE_STRING) : null;
$bytes_rcvd_total_intf = null;
$bytes_sent_total_intf = null;
$finterface = is_string($interface)
? filter_var($interface, FILTER_SANITIZE_STRING)
: null;
if (is_null($finterface)) {
echo PHP_EOL . $name . ':' . ' Invalid or no interface found.' . PHP_EOL;
echo PHP_EOL .
$name .
':' .
' Invalid or no interface found.' .
PHP_EOL;
continue;
}
$mappings[$finterface] = [];
foreach ($client_list as $client => $client_data) {
$fclient = is_string($client) ? filter_var($client, FILTER_SANITIZE_STRING) : null;
$fclient = is_string($client)
? filter_var($client, FILTER_SANITIZE_STRING)
: null;
if (is_null($fclient)) {
echo PHP_EOL . $name . ':' . ' Invalid or no client found.' . PHP_EOL;
echo PHP_EOL .
$name .
':' .
' Invalid or no client found.' .
PHP_EOL;
continue;
}
@@ -55,28 +90,80 @@ foreach ($interface_client_map as $interface => $client_list) {
$bytes_sent = is_int($client_data['bytes_sent'])
? $client_data['bytes_sent']
: null;
$minutes_since_last_handshake = is_int($client_data['minutes_since_last_handshake'])
$minutes_since_last_handshake = is_int(
$client_data['minutes_since_last_handshake']
)
? $client_data['minutes_since_last_handshake']
: null;
$fields = [
if (is_int($bytes_rcvd)) {
$bytes_rcvd_total_intf += $bytes_rcvd;
$bytes_rcvd_total += $bytes_rcvd;
}
if (is_int($bytes_sent)) {
$bytes_sent_total_intf += $bytes_sent;
$bytes_sent_total += $bytes_sent;
}
$fields_intfclient = [
'bytes_rcvd' => $bytes_rcvd,
'bytes_sent' => $bytes_sent,
'minutes_since_last_handshake' => $minutes_since_last_handshake,
];
// create flattened metrics
$metrics[$finterface . '_' . $fclient] = $fields;
$tags = [
$metrics['intf_' . $finterface . '_client_' . $fclient] = $fields_intfclient;
$tags_intfclient = [
'name' => $name,
'app_id' => $app->app_id,
'rrd_def' => $rrd_def,
'rrd_name' => [$polling_type, $name, $app->app_id, $finterface, $fclient],
'rrd_def' => $rrd_def_intfclient,
'rrd_name' => [
$polling_type,
$name,
$app->app_id,
$finterface,
$fclient,
],
];
data_update($device, $polling_type, $tags, $fields);
data_update($device, $polling_type, $tags_intfclient, $fields_intfclient);
}
// create interface fields
$fields_intf = [
'bytes_rcvd_total_intf' => $bytes_rcvd_total_intf,
'bytes_sent_total_intf' => $bytes_sent_total_intf,
];
// create interface metrics
$metrics['intf_' . $finterface] = $fields_intf;
$tags_intf = [
'name' => $name,
'app_id' => $app->app_id,
'rrd_def' => $rrd_def_intf,
'rrd_name' => [$polling_type, $name, $app->app_id, $finterface],
];
data_update($device, $polling_type, $tags_intf, $fields_intf);
}
// create total fields
$fields_all = [
'bytes_rcvd_total' => $bytes_rcvd_total,
'bytes_sent_total' => $bytes_sent_total,
];
// create total metrics
$metrics['global'] = $fields_all;
$tags_all = [
'name' => $name,
'app_id' => $app->app_id,
'rrd_def' => $rrd_def_total,
'rrd_name' => [$polling_type, $name, $app->app_id],
];
data_update($device, $polling_type, $tags_all, $fields_all);
// variable tracks whether we updated mappings so it only happens once
$mappings_updated = false;
@@ -90,8 +177,14 @@ if (count($added_interfaces) > 0 || count($removed_interfaces) > 0) {
$app->data = ['mappings' => $mappings];
$mappings_updated = true;
$log_message = 'Wireguard Interfaces Change:';
$log_message .= count($added_interfaces) > 0 ? ' Added ' . implode(',', $added_interfaces) : '';
$log_message .= count($removed_interfaces) > 0 ? ' Removed ' . implode(',', $removed_interfaces) : '';
$log_message .=
count($added_interfaces) > 0
? ' Added ' . implode(',', $added_interfaces)
: '';
$log_message .=
count($removed_interfaces) > 0
? ' Removed ' . implode(',', $removed_interfaces)
: '';
log_event($log_message, $device, 'application');
}
@@ -107,8 +200,14 @@ foreach ($mappings as $interface => $client_list) {
$mappings_updated = true;
}
$log_message = 'Wireguard Interface ' . $interface . ' Clients Change:';
$log_message .= count($added_clients) > 0 ? ' Added ' . implode(',', $added_clients) : '';
$log_message .= count($removed_clients) > 0 ? ' Removed ' . implode(',', $removed_clients) : '';
$log_message .=
count($added_clients) > 0
? ' Added ' . implode(',', $added_clients)
: '';
$log_message .=
count($removed_clients) > 0
? ' Removed ' . implode(',', $removed_clients)
: '';
log_event($log_message, $device, 'application');
}
}

View File

@@ -50,91 +50,115 @@
],
"application_metrics": [
{
"metric": "wg0_client1.domain.com_bytes_rcvd",
"value": 1534068,
"metric": "global_bytes_rcvd_total",
"value": 14891148,
"value_prev": null,
"app_type": "wireguard"
},
{
"metric": "wg0_client1.domain.com_bytes_sent",
"value": 97772,
"metric": "global_bytes_sent_total",
"value": 105501824,
"value_prev": null,
"app_type": "wireguard"
},
{
"metric": "wg0_client1.domain.com_minutes_since_last_handshake",
"value": 1,
"metric": "intf_wg0_bytes_rcvd_total_intf",
"value": 14891148,
"value_prev": null,
"app_type": "wireguard"
},
{
"metric": "wg0_client2_bytes_rcvd",
"metric": "intf_wg0_bytes_sent_total_intf",
"value": 105501824,
"value_prev": null,
"app_type": "wireguard"
},
{
"metric": "intf_wg0_client_client1.domain.com_bytes_rcvd",
"value": 0,
"value_prev": null,
"app_type": "wireguard"
},
{
"metric": "wg0_client2_bytes_sent",
"metric": "intf_wg0_client_client1.domain.com_bytes_sent",
"value": 0,
"value_prev": null,
"app_type": "wireguard"
},
{
"metric": "wg0_client2_minutes_since_last_handshake",
"metric": "intf_wg0_client_client1.domain.com_minutes_since_last_handshake",
"value": 0,
"value_prev": null,
"app_type": "wireguard"
},
{
"metric": "wg0_computer_bytes_rcvd",
"metric": "intf_wg0_client_client2_bytes_rcvd",
"value": 0,
"value_prev": null,
"app_type": "wireguard"
},
{
"metric": "wg0_computer_bytes_sent",
"metric": "intf_wg0_client_client2_bytes_sent",
"value": 0,
"value_prev": null,
"app_type": "wireguard"
},
{
"metric": "wg0_computer_minutes_since_last_handshake",
"metric": "intf_wg0_client_client2_minutes_since_last_handshake",
"value": 0,
"value_prev": null,
"app_type": "wireguard"
},
{
"metric": "wg0_it_admin.domain.org_bytes_rcvd",
"metric": "intf_wg0_client_computer_bytes_rcvd",
"value": 14891148,
"value_prev": null,
"app_type": "wireguard"
},
{
"metric": "intf_wg0_client_computer_bytes_sent",
"value": 105501824,
"value_prev": null,
"app_type": "wireguard"
},
{
"metric": "intf_wg0_client_computer_minutes_since_last_handshake",
"value": 2,
"value_prev": null,
"app_type": "wireguard"
},
{
"metric": "intf_wg0_client_it_admin.domain.org_bytes_rcvd",
"value": 0,
"value_prev": null,
"app_type": "wireguard"
},
{
"metric": "wg0_it_admin.domain.org_bytes_sent",
"metric": "intf_wg0_client_it_admin.domain.org_bytes_sent",
"value": 0,
"value_prev": null,
"app_type": "wireguard"
},
{
"metric": "wg0_it_admin.domain.org_minutes_since_last_handshake",
"metric": "intf_wg0_client_it_admin.domain.org_minutes_since_last_handshake",
"value": 0,
"value_prev": null,
"app_type": "wireguard"
},
{
"metric": "wg0_my_phone_bytes_rcvd",
"metric": "intf_wg0_client_my_phone_bytes_rcvd",
"value": 0,
"value_prev": null,
"app_type": "wireguard"
},
{
"metric": "wg0_my_phone_bytes_sent",
"metric": "intf_wg0_client_my_phone_bytes_sent",
"value": 0,
"value_prev": null,
"app_type": "wireguard"
},
{
"metric": "wg0_my_phone_minutes_since_last_handshake",
"metric": "intf_wg0_client_my_phone_minutes_since_last_handshake",
"value": 0,
"value_prev": null,
"app_type": "wireguard"

View File

@@ -7,4 +7,4 @@
1.3.6.1.2.1.25.1.1.0|67|77552962
1.3.6.1.4.1.8072.1.3.2.2.1.21.6.100.105.115.116.114.111|2|1
1.3.6.1.4.1.8072.1.3.2.2.1.21.9.119.105.114.101.103.117.97.114.100|2|1
1.3.6.1.4.1.8072.1.3.2.3.1.2.9.119.105.114.101.103.117.97.114.100|4x|7b226572726f72537472696e67223a2022222c20226572726f72223a20302c202276657273696f6e223a20312c202264617461223a207b22776730223a207b22636c69656e74312e646f6d61696e2e636f6d223a207b226d696e757465735f73696e63655f6c6173745f68616e647368616b65223a20312c202262797465735f72637664223a20313533343036382c202262797465735f73656e74223a2039373737327d2c2022636c69656e7432223a207b226d696e757465735f73696e63655f6c6173745f68616e647368616b65223a206e756c6c2c202262797465735f72637664223a20302c202262797465735f73656e74223a20307d2c20226d795f70686f6e65223a207b226d696e757465735f73696e63655f6c6173745f68616e647368616b65223a206e756c6c2c202262797465735f72637664223a20302c202262797465735f73656e74223a20307d2c202269745f61646d696e2e646f6d61696e2e6f7267223a207b226d696e757465735f73696e63655f6c6173745f68616e647368616b65223a206e756c6c2c202262797465735f72637664223a20302c202262797465735f73656e74223a20307d2c2022636f6d7075746572223a207b226d696e757465735f73696e63655f6c6173745f68616e647368616b65223a206e756c6c2c202262797465735f72637664223a20302c202262797465735f73656e74223a20307d7d7d7d0a
1.3.6.1.4.1.8072.1.3.2.3.1.2.9.119.105.114.101.103.117.97.114.100|4x|7b226572726f72537472696e67223a2022222c20226572726f72223a20302c202276657273696f6e223a20312c202264617461223a207b22776730223a207b22636c69656e74312e646f6d61696e2e636f6d223a207b226d696e757465735f73696e63655f6c6173745f68616e647368616b65223a206e756c6c2c202262797465735f72637664223a20302c202262797465735f73656e74223a20307d2c2022636c69656e7432223a207b226d696e757465735f73696e63655f6c6173745f68616e647368616b65223a206e756c6c2c202262797465735f72637664223a20302c202262797465735f73656e74223a20307d2c20226d795f70686f6e65223a207b226d696e757465735f73696e63655f6c6173745f68616e647368616b65223a206e756c6c2c202262797465735f72637664223a20302c202262797465735f73656e74223a20307d2c202269745f61646d696e2e646f6d61696e2e6f7267223a207b226d696e757465735f73696e63655f6c6173745f68616e647368616b65223a206e756c6c2c202262797465735f72637664223a20302c202262797465735f73656e74223a20307d2c2022636f6d7075746572223a207b226d696e757465735f73696e63655f6c6173745f68616e647368616b65223a20322c202262797465735f72637664223a2031343839313134382c202262797465735f73656e74223a203130353530313832347d7d7d7d0a