Project

General

Profile

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