Project

General

Profile

Bug #9807 ยป rrd_fetch_json.php

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

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

    
26
$nocsrf = true;
27

    
28
require("guiconfig.inc");
29

    
30
//TODO security/validation checks
31

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

    
34
/* Check if an RRD file exists */
35
function monitoring_rrd_check($name) {
36
	global $rrd_location;
37
	if (file_exists($rrd_location . basename($name))) {
38
		return true;
39
	}
40
	return false;
41
}
42

    
43
//lookup end time based on resolution (ensure resolution interval)
44
$resolutionLookup = array(
45
	"60"    => "1min",
46
	"300"   => "5min",
47
	"3600"  => "1hour",
48
	"86400" => "1day"
49
);
50

    
51
//Set units based on RRD database name
52
$graph_unit_lookup = array(
53
	"traffic"    => "b/s",
54
	"packets"    => "pps",
55
	"states"     => "cps",
56
	"quality"    => "ms",
57
	"processor"  => "%",
58
	"memory"     => "%",
59
	"wireless"   => "dBi",
60
	"mbuf"       => "",
61
	"dhcpd"      => "",
62
	"ntpd"       => "",
63
	"vpnusers"   => "",
64
	"queues"     => "b/s",
65
	"queuedrops" => "drops",
66
	"cellular"   => "dB"
67
);
68

    
69
//Overrides units based on line name
70
$line_unit_lookup = array(
71
	"Packet Loss" => "%",
72
	"Processes"   => ""
73
);
74

    
75
//lookup table for acronym to full description
76
$unit_desc_lookup = array(
77
	"b/s" => "Bits Per Second",
78
	"pps" => "Packets Per Second",
79
	"cps" => "Changes Per Second",
80
	"ms"  => "Milliseconds",
81
	"%"   => "Percent",
82
	"Mb"  => "Megabit",
83
	"dBi" => "Decibels Relative to Isotropic",
84
	""    => ""
85
);
86

    
87
// Posted settings that apply to both left and right axes
88
$start = $_POST['start'];
89
$end = $_POST['end'];
90
$timePeriod = $_POST['timePeriod'];
91
$resolution = $_POST['resolution'];
92
$graphtype = $_POST['graphtype'];
93
$invert_graph = ($_POST['invert'] === 'true');
94

    
95
// Selections that are unique to each axis
96
$side = array();
97
$side['l']['selection'] = $_POST['left'];
98
$side['r']['selection'] = $_POST['right'];
99
$side['l']['yAxis'] = 1;
100
$side['r']['yAxis'] = 2;
101

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

    
116
//grab the older last updated time of the two databases
117
if (empty($side['r']['last_update'])) {
118
	$last_updated = $side['l']['last_update'];
119
} elseif (empty($side['l']['last_update'])) {
120
	$last_updated = $side['r']['last_update'];
121
} else {
122
	$last_updated = min($side['l']['last_update'], $side['r']['last_update']);
123
}
124

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

    
130
	if (monitoring_rrd_check($side['l']['rrd_file'])) {
131
		$left_rrd_array  = rrd_fetch($side['l']['rrd_file'], $rrd_options);
132
	} else {
133
		$left_rrd_array = array();
134
	}
135
	if (monitoring_rrd_check($side['r']['rrd_file'])) {
136
		$right_rrd_array = rrd_fetch($side['r']['rrd_file'], $rrd_options);
137
	} else {
138
		$right_rrd_array = array();
139
	}
140

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

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

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

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

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

    
160
foreach ($side as $settings) {
161
	if ($settings['selection'] == "null") {
162
		continue;
163
	}
164
	if (!monitoring_rrd_check($settings['rrd_file'])) {
165
		die ('{ "error" : "' . gettext("Invalid RRD file") . '" }');
166
	}
167

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

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

    
176
	foreach ($ds_list as $ds_key => $ds) {
177
		$data_list = $rrd_array['data'][$ds];
178
		$ignore = $invert = $ninetyfifth = false;
179
		$graph_type = $graphtype;
180
		$unit_acronym = $settings['unit_acronym'];
181
		$multiplier = 1;
182
		$format = "f";
183

    
184
		//Overrides based on line name
185
		switch($ds) {
186
			case "user":
187
				$ds = "user util.";
188
				break;
189
			case "nice":
190
				$ds = "nice util.";
191
				break;
192
			case "system":
193
				$ds = "system util.";
194
				break;
195
			case "stddev":
196
				$ds = "delay std. dev.";
197
				$multiplier = 1000;
198
				break;
199
			case "delay":
200
				$ds = "delay average";
201
				$multiplier = 1000;
202
				break;
203
			case "loss":
204
				$ds = "packet loss";
205
				$unit_acronym = "%";
206
				$invert = $invert_graph;
207
				break;
208
			case "processes":
209
				$unit_acronym = "";
210
				break;
211
			case "pfstates":
212
				$unit_acronym = "";
213
				$ds = "filter states";
214
				break;
215
			case "srcip":
216
				$unit_acronym = "";
217
				$ds = "source addr.";
218
				break;
219
			case "dstip":
220
				$unit_acronym = "";
221
				$ds = "dest. addr.";
222
				break;
223
			case "pfrate":
224
				$ds = "state changes";
225
				break;
226
			case "pfnat":
227
				$ignore = true;
228
				break;
229
			case "inpass":
230
				if ($settings['category'] === "traffic") {
231
					$multiplier = 8;
232
				}
233
				$ninetyfifth = true;
234
				$format = "s";
235
				break;
236
			case "max":
237
				$format = "s";
238
				break;
239
			case "inpass6":
240
				if ($settings['category'] === "traffic") {
241
					$multiplier = 8;
242
				}
243
				$ninetyfifth = true;
244
				$format = "s";
245
				break;
246
			case "outpass":
247
				if ($settings['category'] === "traffic") {
248
					$multiplier = 8;
249
				}
250
				$invert = $invert_graph;
251
				$ninetyfifth = true;
252
				$format = "s";
253
				break;
254
			case "outpass6":
255
				if ($settings['category'] === "traffic") {
256
					$multiplier = 8;
257
				}
258
				$invert = $invert_graph;
259
				$ninetyfifth = true;
260
				$format = "s";
261
				break;
262
			case "rate":
263
				$unit_acronym = "Mb";
264
				break;
265
			case "channel":
266
				$unit_acronym = "";
267
				break;
268
			case "concurrentusers":
269
				$unit_acronym = "";
270
				break;
271
			case "loggedinusers":
272
				$unit_acronym = "";
273
				break;
274
			case "offset":
275
			case "sjit":
276
			case "cjit":
277
			case "wander":
278
			case "disp":
279
				$unit_acronym = "ms";
280
				break;
281
			case "freq":
282
				$unit_acronym = "";
283
				break;
284
		}
285

    
286
		if (!$ignore) {
287
			$entry = array();
288
			$entry['key'] = $ds;
289
			$entry['step'] = $step;
290
			$entry['last_updated'] = $last_updated*1000;
291
			$entry['type'] = $graph_type;
292
			$entry['format'] = $format;
293
			$entry['yAxis'] = $settings['yAxis'];
294
			$entry['unit_acronym'] = $unit_acronym;
295
			$entry['unit_desc'] = $unit_desc_lookup[$unit_acronym];
296
			$entry['invert'] = $invert;
297
			$entry['ninetyfifth'] = $ninetyfifth;
298

    
299
			$data = array();
300
			$raw_data = array();
301
			$stats = array();
302

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

    
306
				if (is_nan($value)) {
307
					$data[] = array($time*1000, 0);
308
				} else {
309
					$data[] = array($time*1000, $value*$multiplier);
310
					$stats[] = $value*$multiplier;
311
				}
312
			}
313

    
314
			$entry['values'] = $data;
315
			$entry['raw'] = $raw_data;
316

    
317
			if (count($stats)) {
318
				$entry['min'] = min($stats);
319
				$entry['max'] = max($stats);
320
				$entry['avg'] = array_sum($stats) / count($stats);
321
			} else {
322
				$entry['min'] = 0;
323
				$entry['max'] = 0;
324
				$entry['avg'] = 0;
325
			}
326

    
327
			$obj[] = $entry;
328

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

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

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

    
344
				$entry['values'] = $data_abs;
345
				$entry['raw'] = $raw_data_abs;
346
				
347
				if (count($stats)) {
348
					$stats_abs = array_map('abs', $stats);
349
					$entry['min'] = min($stats_abs);
350
					$entry['max'] = max($stats_abs);
351
					$entry['avg'] = array_sum($stats_abs) / count($stats_abs);
352
				}
353

    
354
				$obj[] = $entry;
355
			}
356
		}
357
	}
358

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

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

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

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

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

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

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

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

    
402
		/* add v4 and v6 together */
403
		$inpass_total = [];
404
		$outpass_total = [];
405
		$inpass_stats = [];
406
		$outpass_stats = [];
407

    
408
		foreach ($inpass_array as $key => $value) {
409
			if (is_nan($value)) {
410
				$inpass_total[] = array($key*1000, 0);
411
			} else {
412
				$inpass_total[] = array($key*1000, $value + $inpass6_array[$key]);
413
				$inpass_stats[] = $value + $inpass6_array[$key];
414
			}
415
		}
416

    
417
		foreach ($outpass_array as $key => $value) {
418
			if (is_nan($value)) {
419
				$outpass_total[] = array($key*1000, 0);
420
			} else {
421
				$outpass_total[] = array($key*1000, $value + $outpass6_array[$key]);
422
				$outpass_stats[] = $value + $outpass6_array[$key];
423
			}
424
		}
425

    
426
		//add the new total lines to array
427
		$entry = array();
428
		$entry['key'] = "inpass total";
429
		$entry['type'] = $graphtype;
430
		$entry['format'] = "s";
431
		$entry['yAxis'] = $settings['yAxis'];
432
		$entry['unit_acronym'] = $settings['unit_acronym'];
433
		$entry['unit_desc'] = $settings['unit_desc'];
434
		$entry['invert'] = false;
435
		$entry['ninetyfifth'] = true;
436
		$entry['min'] = min($inpass_stats);
437
		$entry['max'] = max($inpass_stats);
438
		$entry['avg'] = array_sum($inpass_stats) / count($inpass_stats);
439
		$entry['values'] = $inpass_total;
440
		$obj[] = $entry;
441

    
442
		$entry = array();
443
		$entry['key'] = "outpass total";
444
		$entry['type'] = $graphtype;
445
		$entry['format'] = "s";
446
		$entry['yAxis'] = $settings['yAxis'];
447
		$entry['unit_acronym'] = $settings['unit_acronym'];
448
		$entry['unit_desc'] = $settings['unit_desc'];
449
		$entry['invert'] = $invert_graph;
450
		$entry['ninetyfifth'] = true;
451
		$entry['min'] = min($outpass_stats);
452
		$entry['max'] = max($outpass_stats);
453
		$entry['avg'] = array_sum($outpass_stats) / count($outpass_stats);
454
		$entry['values'] = $outpass_total;
455
		$obj[] = $entry;
456
	}
457

    
458
	foreach ($obj as $raw_key => &$raw_value) {
459
		unset($raw_value['raw']);
460
	}
461
}
462

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

    
466
?>
    (1-1/1)