Project

General

Profile

Bug #9807 ยป rrd_fetch_json.php

fixed version - Viktor Gurov, 12/12/2019 06:39 AM

 
<?php
/*
* rrd_fetch_json.php
*
* part of pfSense (https://www.pfsense.org)
* Copyright (c) 2008-2016 Rubicon Communications, LLC (Netgate)
* All rights reserved.
*
* originally part of m0n0wall (http://m0n0.ch/wall)
* Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

$nocsrf = true;

require("guiconfig.inc");

//TODO security/validation checks

$rrd_location = "/var/db/rrd/";

/* Check if an RRD file exists */
function monitoring_rrd_check($name) {
global $rrd_location;
if (file_exists($rrd_location . basename($name))) {
return true;
}
return false;
}

//lookup end time based on resolution (ensure resolution interval)
$resolutionLookup = array(
"60" => "1min",
"300" => "5min",
"3600" => "1hour",
"86400" => "1day"
);

//Set units based on RRD database name
$graph_unit_lookup = array(
"traffic" => "b/s",
"packets" => "pps",
"states" => "cps",
"quality" => "ms",
"processor" => "%",
"memory" => "%",
"wireless" => "dBi",
"mbuf" => "",
"dhcpd" => "",
"ntpd" => "",
"vpnusers" => "",
"queues" => "b/s",
"queuedrops" => "drops",
"cellular" => "dB"
);

//Overrides units based on line name
$line_unit_lookup = array(
"Packet Loss" => "%",
"Processes" => ""
);

//lookup table for acronym to full description
$unit_desc_lookup = array(
"b/s" => "Bits Per Second",
"pps" => "Packets Per Second",
"cps" => "Changes Per Second",
"ms" => "Milliseconds",
"%" => "Percent",
"Mb" => "Megabit",
"dBi" => "Decibels Relative to Isotropic",
"" => ""
);

// Posted settings that apply to both left and right axes
$start = $_POST['start'];
$end = $_POST['end'];
$timePeriod = $_POST['timePeriod'];
$resolution = $_POST['resolution'];
$graphtype = $_POST['graphtype'];
$invert_graph = ($_POST['invert'] === 'true');

// Selections that are unique to each axis
$side = array();
$side['l']['selection'] = $_POST['left'];
$side['r']['selection'] = $_POST['right'];
$side['l']['yAxis'] = 1;
$side['r']['yAxis'] = 2;

foreach ($side as $lr => $settings) {
$pieces = explode("-", $side[$lr]['selection']);
$side[$lr]['category'] = $pieces[1];
$side[$lr]['rrd_file'] = $rrd_location . $side[$lr]['selection'] . '.rrd';
/* Check if the RRD file exists before using it, continue if it's not found because that axis has nothing selected. */
if (!monitoring_rrd_check($side[$lr]['rrd_file'])) {
continue;
}
$rrd_info_array = rrd_info($side[$lr]['rrd_file']);
$side[$lr]['last_update'] = $rrd_info_array['last_update'];
$side[$lr]['unit_acronym'] = $graph_unit_lookup[$side[$lr]['category']];
$side[$lr]['unit_desc'] = $unit_desc_lookup[$side[$lr]['unit_acronym']];
}

//grab the older last updated time of the two databases
if (empty($side['r']['last_update'])) {
$last_updated = $side['l']['last_update'];
} elseif (empty($side['l']['last_update'])) {
$last_updated = $side['r']['last_update'];
} else {
$last_updated = min($side['l']['last_update'], $side['r']['last_update']);
}

if ($timePeriod === "custom") {
// Determine highest resolution available for requested time period
// Should be possible to determine programmatically from the RRD header info array (rrd_info).
$rrd_options = array( 'AVERAGE', '-a', '-s', $start, '-e', $start );

if (monitoring_rrd_check($side['l']['rrd_file'])) {
$left_rrd_array = rrd_fetch($side['l']['rrd_file'], $rrd_options);
} else {
$left_rrd_array = array();
}
if (monitoring_rrd_check($side['r']['rrd_file'])) {
$right_rrd_array = rrd_fetch($side['r']['rrd_file'], $rrd_options);
} else {
$right_rrd_array = array();
}

$resolution = max($left_rrd_array['step'], $right_rrd_array['step']);

// make sure end time isn't later than last updated time entry
if ( $end > $last_updated ) { $end = $last_updated; }

// Minus resolution to prevent last value 0 (zero).
$end -= $resolution;

// make sure start time isn't later than end time
if ($start > $end) { $start = $end; }
} else {
// Use end time reference in 'start' to retain time period length.
$start = 'end' . $timePeriod . '+'.$resolutionLookup[$resolution];
// Use the RRD last updated time as end, minus resolution to prevent last value 0 (zero).
$end = $last_updated . '-'.$resolutionLookup[$resolution];
}

$rrd_options = array( 'AVERAGE', '-a', '-r', $resolution, '-s', $start, '-e', $end );

foreach ($side as $settings) {
if ($settings['selection'] == "null") {
continue;
}
if (!monitoring_rrd_check($settings['rrd_file'])) {
die ('{ "error" : "' . gettext("Invalid RRD file") . '" }');
}

$rrd_array = rrd_fetch($settings['rrd_file'], $rrd_options);
if (!($rrd_array)) {
die ('{ "error" : ' . json_encode(rrd_error()) . ' }');
}

$ds_list = array_keys ($rrd_array['data']);
$step = $rrd_array['step'];

foreach ($ds_list as $ds_key => $ds) {
$data_list = $rrd_array['data'][$ds];
$ignore = $invert = $ninetyfifth = false;
$graph_type = $graphtype;
$unit_acronym = $settings['unit_acronym'];
$multiplier = 1;
$format = "f";

//Overrides based on line name
switch($ds) {
case "user":
$ds = "user util.";
break;
case "nice":
$ds = "nice util.";
break;
case "system":
$ds = "system util.";
break;
case "stddev":
$ds = "delay std. dev.";
$multiplier = 1000;
break;
case "delay":
$ds = "delay average";
$multiplier = 1000;
break;
case "loss":
$ds = "packet loss";
$unit_acronym = "%";
$invert = $invert_graph;
break;
case "processes":
$unit_acronym = "";
break;
case "pfstates":
$unit_acronym = "";
$ds = "filter states";
break;
case "srcip":
$unit_acronym = "";
$ds = "source addr.";
break;
case "dstip":
$unit_acronym = "";
$ds = "dest. addr.";
break;
case "pfrate":
$ds = "state changes";
break;
case "pfnat":
$ignore = true;
break;
case "inpass":
if ($settings['category'] === "traffic") {
$multiplier = 8;
}
$ninetyfifth = true;
$format = "s";
break;
case "max":
$format = "s";
break;
case "inpass6":
if ($settings['category'] === "traffic") {
$multiplier = 8;
}
$ninetyfifth = true;
$format = "s";
break;
case "outpass":
if ($settings['category'] === "traffic") {
$multiplier = 8;
}
$invert = $invert_graph;
$ninetyfifth = true;
$format = "s";
break;
case "outpass6":
if ($settings['category'] === "traffic") {
$multiplier = 8;
}
$invert = $invert_graph;
$ninetyfifth = true;
$format = "s";
break;
case "rate":
$unit_acronym = "Mb";
break;
case "channel":
$unit_acronym = "";
break;
case "concurrentusers":
$unit_acronym = "";
break;
case "loggedinusers":
$unit_acronym = "";
break;
case "offset":
case "sjit":
case "cjit":
case "wander":
case "disp":
$unit_acronym = "ms";
break;
case "freq":
$unit_acronym = "";
break;
}

if (!$ignore) {
$entry = array();
$entry['key'] = $ds;
$entry['step'] = $step;
$entry['last_updated'] = $last_updated*1000;
$entry['type'] = $graph_type;
$entry['format'] = $format;
$entry['yAxis'] = $settings['yAxis'];
$entry['unit_acronym'] = $unit_acronym;
$entry['unit_desc'] = $unit_desc_lookup[$unit_acronym];
$entry['invert'] = $invert;
$entry['ninetyfifth'] = $ninetyfifth;

$data = array();
$raw_data = array();
$stats = array();

foreach ($data_list as $time => $value) {
$raw_data[] = array($time*1000, $value*$multiplier);

if (is_nan($value)) {
$data[] = array($time*1000, 0);
} else {
$data[] = array($time*1000, $value*$multiplier);
$stats[] = $value*$multiplier;
}
}

$entry['values'] = $data;
$entry['raw'] = $raw_data;

if (count($stats)) {
$entry['min'] = min($stats);
$entry['max'] = max($stats);
$entry['avg'] = array_sum($stats) / count($stats);
} else {
$entry['min'] = 0;
$entry['max'] = 0;
$entry['avg'] = 0;
}

$obj[] = $entry;

if ($ds == 'offset') {
// Make an entry for the absolute value of NTP time offset
// Start with the existing entry and just modify it
$entry['key'] = 'abs ' . $ds;
$raw_data_abs = array();
$data_abs = array();

foreach ($raw_data as $raw_data_entry) {
$raw_data_abs[] = array($raw_data_entry[0], abs($raw_data_entry[1]));
}

foreach ($data as $data_entry) {
$data_abs[] = array($data_entry[0], abs($data_entry[1]));
}

$entry['values'] = $data_abs;
$entry['raw'] = $raw_data_abs;
if (count($stats)) {
$stats_abs = array_map('abs', $stats);
$entry['min'] = min($stats_abs);
$entry['max'] = max($stats_abs);
$entry['avg'] = array_sum($stats_abs) / count($stats_abs);
}

$obj[] = $entry;
}
}
}

/* calculate the total lines */
if ( ($settings['category'] === "traffic") || ($settings['category'] === "packets") ) {
foreach ($obj as $key => $value) {
//grab inpass and outpass attributes and values only for entries on the current axis
if ($value['yAxis'] === $settings['yAxis']) {
if ($value['key'] === "inpass") {
$inpass_array = array();

//loop through values and use time
foreach ($value['raw'] as $datapoint) {
$inpass_array[$datapoint[0]/1000] = $datapoint[1]; //divide by thousand to avoid key size limitations
}
}

if ($value['key'] === "inpass6") {
$inpass6_array = [];

//loop through values and use time
foreach ($value['raw'] as $datapoint6) {
$inpass6_array[$datapoint6[0]/1000] = $datapoint6[1]; //divide by thousand to avoid key size limitations
}
}

if ($value['key'] === "outpass") {
$outpass_array = [];

//loop through values and use time
foreach ($value['raw'] as $datapoint) {
$outpass_array[$datapoint[0]/1000] = $datapoint[1]; //divide by thousand to avoid key size limitations
}
}

if ($value['key'] === "outpass6") {
$outpass6_array = [];

//loop through values and use time
foreach ($value['raw'] as $datapoint6) {
$outpass6_array[$datapoint6[0]/1000] = $datapoint6[1]; //divide by thousand to avoid key size limitations
}
}
}
}

/* add v4 and v6 together */
$inpass_total = [];
$outpass_total = [];
$inpass_stats = [];
$outpass_stats = [];

foreach ($inpass_array as $key => $value) {
if (is_nan($value)) {
$inpass_total[] = array($key*1000, 0);
} else {
$inpass_total[] = array($key*1000, $value + $inpass6_array[$key]);
$inpass_stats[] = $value + $inpass6_array[$key];
}
}

foreach ($outpass_array as $key => $value) {
if (is_nan($value)) {
$outpass_total[] = array($key*1000, 0);
} else {
$outpass_total[] = array($key*1000, $value + $outpass6_array[$key]);
$outpass_stats[] = $value + $outpass6_array[$key];
}
}

//add the new total lines to array
$entry = array();
$entry['key'] = "inpass total";
$entry['type'] = $graphtype;
$entry['format'] = "s";
$entry['yAxis'] = $settings['yAxis'];
$entry['unit_acronym'] = $settings['unit_acronym'];
$entry['unit_desc'] = $settings['unit_desc'];
$entry['invert'] = false;
$entry['ninetyfifth'] = true;
$entry['min'] = min($inpass_stats);
$entry['max'] = max($inpass_stats);
$entry['avg'] = array_sum($inpass_stats) / count($inpass_stats);
$entry['values'] = $inpass_total;
$obj[] = $entry;

$entry = array();
$entry['key'] = "outpass total";
$entry['type'] = $graphtype;
$entry['format'] = "s";
$entry['yAxis'] = $settings['yAxis'];
$entry['unit_acronym'] = $settings['unit_acronym'];
$entry['unit_desc'] = $settings['unit_desc'];
$entry['invert'] = $invert_graph;
$entry['ninetyfifth'] = true;
$entry['min'] = min($outpass_stats);
$entry['max'] = max($outpass_stats);
$entry['avg'] = array_sum($outpass_stats) / count($outpass_stats);
$entry['values'] = $outpass_total;
$obj[] = $entry;
}

foreach ($obj as $raw_key => &$raw_value) {
unset($raw_value['raw']);
}
}

header('Content-Type: application/json');
echo json_encode($obj,JSON_PRETTY_PRINT|JSON_PARTIAL_OUTPUT_ON_ERROR|JSON_NUMERIC_CHECK);

?>
    (1-1/1)