diff --git a/includes/html/graphs/application/wireguard-common.inc.php b/includes/html/graphs/application/wireguard-common.inc.php
new file mode 100644
index 0000000000..28d880b028
--- /dev/null
+++ b/includes/html/graphs/application/wireguard-common.inc.php
@@ -0,0 +1,6 @@
+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';
diff --git a/includes/html/graphs/application/wireguard_traffic.inc.php b/includes/html/graphs/application/wireguard_traffic.inc.php
index a5d0b39d84..4cc4a919d3 100644
--- a/includes/html/graphs/application/wireguard_traffic.inc.php
+++ b/includes/html/graphs/application/wireguard_traffic.inc.php
@@ -1,44 +1,59 @@
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';
diff --git a/includes/html/pages/device/apps/wireguard.inc.php b/includes/html/pages/device/apps/wireguard.inc.php
index 4f115f0a13..d564deea5e 100644
--- a/includes/html/pages/device/apps/wireguard.inc.php
+++ b/includes/html/pages/device/apps/wireguard.inc.php
@@ -1,5 +1,42 @@
+
+
' .
+ $gtext .
+ '
+
+
+
';
+ include 'includes/html/print-graphrow.inc.php';
+ echo '
';
+ echo '
';
+ echo '';
+}
+
$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 '
-
-
' . $gtext . '
-
-
-
';
- include 'includes/html/print-graphrow.inc.php';
- echo '
';
- echo '
';
- echo '
';
+// 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
+ );
+ }
+ }
+ }
+ }
}
}
}
diff --git a/includes/polling/applications/wireguard.inc.php b/includes/polling/applications/wireguard.inc.php
index 6c6b1ea4f6..fccdb64be6 100644
--- a/includes/polling/applications/wireguard.inc.php
+++ b/includes/polling/applications/wireguard.inc.php
@@ -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');
}
}
diff --git a/tests/data/linux_wireguard-v1.json b/tests/data/linux_wireguard-v1.json
index 54b42fff60..f969fd45d7 100644
--- a/tests/data/linux_wireguard-v1.json
+++ b/tests/data/linux_wireguard-v1.json
@@ -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"
diff --git a/tests/snmpsim/linux_wireguard-v1.snmprec b/tests/snmpsim/linux_wireguard-v1.snmprec
index 4a7e183b53..081bb5091e 100644
--- a/tests/snmpsim/linux_wireguard-v1.snmprec
+++ b/tests/snmpsim/linux_wireguard-v1.snmprec
@@ -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