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
|
?>
|