1 | <?php |
||||
2 | /* |
||||
3 | * Copyright (C) 2009 Bruno Prémont <bonbons AT linux-vserver.org> |
||||
4 | * |
||||
5 | * This program is free software; you can redistribute it and/or modify it under |
||||
6 | * the terms of the GNU General Public License as published by the Free Software |
||||
7 | * Foundation; only version 2 of the License is applicable. |
||||
8 | * |
||||
9 | * This program is distributed in the hope that it will be useful, but WITHOUT |
||||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
||||
11 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
||||
12 | * details. |
||||
13 | * |
||||
14 | * You should have received a copy of the GNU General Public License along with |
||||
15 | * this program; if not, write to the Free Software Foundation, Inc., |
||||
16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
17 | */ |
||||
18 | |||||
19 | require 'includes/html/collectd/CollectdColor.php'; |
||||
20 | |||||
21 | use LibreNMS\CollectdColor; |
||||
22 | use LibreNMS\Config; |
||||
23 | |||||
24 | define('REGEXP_HOST', '/^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/'); |
||||
25 | define('REGEXP_PLUGIN', '/^[a-zA-Z0-9_.-]+$/'); |
||||
26 | |||||
27 | /* |
||||
28 | * Read input variable from GET, POST or COOKIE taking |
||||
29 | * care of magic quotes |
||||
30 | * |
||||
31 | * @param string $name Name of value to return |
||||
32 | * @param array $array User-input array ($_GET, $_POST or $_COOKIE) |
||||
33 | * @param string $default Default value |
||||
34 | * @return string $default if name in unknown in $array, otherwise |
||||
35 | * input value with magic quotes stripped off |
||||
36 | */ |
||||
37 | function read_var($name, &$array, $default = null) |
||||
38 | { |
||||
39 | if (isset($array[$name])) { |
||||
40 | if (is_array($array[$name])) { |
||||
41 | if (get_magic_quotes_gpc()) { |
||||
42 | $ret = []; |
||||
43 | foreach ($array[$name] as $k => $v) { |
||||
44 | $ret[stripslashes($k)] = stripslashes($v); |
||||
45 | } |
||||
46 | |||||
47 | return $ret; |
||||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
Loading history...
|
|||||
48 | } else { |
||||
49 | return $array[$name]; |
||||
50 | } |
||||
51 | } elseif (is_string($array[$name]) && get_magic_quotes_gpc()) { |
||||
52 | return stripslashes($array[$name]); |
||||
53 | } else { |
||||
54 | return $array[$name]; |
||||
55 | } |
||||
56 | } else { |
||||
57 | return $default; |
||||
58 | } |
||||
59 | }//end read_var() |
||||
60 | |||||
61 | /* |
||||
62 | * Alphabetically compare host names, comparing label |
||||
63 | * from tld to node name |
||||
64 | */ |
||||
65 | function collectd_compare_host($a, $b) |
||||
66 | { |
||||
67 | $ea = explode('.', $a); |
||||
68 | $eb = explode('.', $b); |
||||
69 | $i = (count($ea) - 1); |
||||
70 | $j = (count($eb) - 1); |
||||
71 | while ($i >= 0 && $j >= 0) { |
||||
72 | if (($r = strcmp($ea[$i--], $eb[$j--])) != 0) { |
||||
73 | return $r; |
||||
74 | } |
||||
75 | } |
||||
76 | |||||
77 | return 0; |
||||
78 | }//end collectd_compare_host() |
||||
79 | |||||
80 | /** |
||||
81 | * Fetch list of hosts found in collectd's datadirs. |
||||
82 | * |
||||
83 | * @return array Sorted list of hosts (sorted by label from rigth to left) |
||||
84 | */ |
||||
85 | function collectd_list_hosts() |
||||
86 | { |
||||
87 | $hosts = []; |
||||
88 | foreach (Config::get('datadirs') as $datadir) { |
||||
89 | if ($d = @opendir($datadir)) { |
||||
90 | while (($dent = readdir($d)) !== false) { |
||||
91 | if ($dent != '.' && $dent != '..' && is_dir($datadir . '/' . $dent) && preg_match(REGEXP_HOST, $dent)) { |
||||
92 | $hosts[] = $dent; |
||||
93 | } |
||||
94 | } |
||||
95 | closedir($d); |
||||
96 | } else { |
||||
97 | error_log('Failed to open datadir: ' . $datadir); |
||||
98 | } |
||||
99 | } |
||||
100 | $hosts = array_unique($hosts); |
||||
101 | usort($hosts, 'collectd_compare_host'); |
||||
102 | |||||
103 | return $hosts; |
||||
104 | } |
||||
105 | |||||
106 | /** |
||||
107 | * Fetch list of plugins found in collectd's datadirs for given host. |
||||
108 | * |
||||
109 | * @param string $arg_host Name of host for which to return plugins |
||||
110 | * @return array Sorted list of plugins (sorted alphabetically) |
||||
111 | */ |
||||
112 | function collectd_list_plugins($arg_host) |
||||
113 | { |
||||
114 | $plugins = []; |
||||
115 | foreach (Config::get('datadirs') as $datadir) { |
||||
116 | if (preg_match(REGEXP_HOST, $arg_host) && ($d = @opendir($datadir . '/' . $arg_host))) { |
||||
117 | while (($dent = readdir($d)) !== false) { |
||||
118 | if ($dent != '.' && $dent != '..' && is_dir($datadir . '/' . $arg_host . '/' . $dent)) { |
||||
119 | if ($i = strpos($dent, '-')) { |
||||
120 | $plugins[] = substr($dent, 0, $i); |
||||
121 | } else { |
||||
122 | $plugins[] = $dent; |
||||
123 | } |
||||
124 | } |
||||
125 | } |
||||
126 | |||||
127 | closedir($d); |
||||
128 | } |
||||
129 | } |
||||
130 | |||||
131 | $plugins = array_unique($plugins); |
||||
132 | sort($plugins); |
||||
133 | |||||
134 | return $plugins; |
||||
135 | }//end collectd_list_plugins() |
||||
136 | |||||
137 | /** |
||||
138 | * Fetch list of plugin instances found in collectd's datadirs for given host+plugin |
||||
139 | * |
||||
140 | * @param string $arg_host Name of host |
||||
141 | * @param string $arg_plugin Name of plugin |
||||
142 | * @return array Sorted list of plugin instances (sorted alphabetically) |
||||
143 | */ |
||||
144 | function collectd_list_pinsts($arg_host, $arg_plugin) |
||||
145 | { |
||||
146 | $pinsts = []; |
||||
147 | foreach (Config::get('datadirs') as $datadir) { |
||||
148 | if (preg_match(REGEXP_HOST, $arg_host) && ($d = opendir($datadir . '/' . $arg_host))) { |
||||
149 | while (($dent = readdir($d)) !== false) { |
||||
150 | if ($dent != '.' && $dent != '..' && is_dir($datadir . '/' . $arg_host . '/' . $dent)) { |
||||
151 | if ($i = strpos($dent, '-')) { |
||||
152 | $plugin = substr($dent, 0, $i); |
||||
153 | $pinst = substr($dent, ($i + 1)); |
||||
154 | } else { |
||||
155 | $plugin = $dent; |
||||
156 | $pinst = ''; |
||||
157 | } |
||||
158 | |||||
159 | if ($plugin == $arg_plugin) { |
||||
160 | $pinsts[] = $pinst; |
||||
161 | } |
||||
162 | } |
||||
163 | } |
||||
164 | |||||
165 | closedir($d); |
||||
166 | } |
||||
167 | }//end foreach |
||||
168 | |||||
169 | $pinsts = array_unique($pinsts); |
||||
170 | sort($pinsts); |
||||
171 | |||||
172 | return $pinsts; |
||||
173 | }//end collectd_list_pinsts() |
||||
174 | |||||
175 | /** |
||||
176 | * Fetch list of types found in collectd's datadirs for given host+plugin+instance |
||||
177 | * |
||||
178 | * @arg_host Name of host |
||||
179 | * @arg_plugin Name of plugin |
||||
180 | * @arg_pinst Plugin instance |
||||
181 | * |
||||
182 | * @return array Sorted list of types (sorted alphabetically) |
||||
183 | */ |
||||
184 | function collectd_list_types($arg_host, $arg_plugin, $arg_pinst) |
||||
185 | { |
||||
186 | $types = []; |
||||
187 | $my_plugin = $arg_plugin . (strlen($arg_pinst) ? '-' . $arg_pinst : ''); |
||||
188 | if (! preg_match(REGEXP_PLUGIN, $my_plugin)) { |
||||
189 | return $types; |
||||
190 | } |
||||
191 | |||||
192 | foreach (Config::get('datadirs') as $datadir) { |
||||
193 | if (preg_match(REGEXP_HOST, $arg_host) && ($d = @opendir($datadir . '/' . $arg_host . '/' . $my_plugin))) { |
||||
194 | while (($dent = readdir($d)) !== false) { |
||||
195 | if ($dent != '.' && $dent != '..' && is_file($datadir . '/' . $arg_host . '/' . $my_plugin . '/' . $dent) && substr($dent, (strlen($dent) - 4)) == '.rrd') { |
||||
196 | $dent = substr($dent, 0, (strlen($dent) - 4)); |
||||
197 | if ($i = strpos($dent, '-')) { |
||||
198 | $types[] = substr($dent, 0, $i); |
||||
199 | } else { |
||||
200 | $types[] = $dent; |
||||
201 | } |
||||
202 | } |
||||
203 | } |
||||
204 | |||||
205 | closedir($d); |
||||
206 | } |
||||
207 | } |
||||
208 | |||||
209 | $types = array_unique($types); |
||||
210 | sort($types); |
||||
211 | |||||
212 | return $types; |
||||
213 | }//end collectd_list_types() |
||||
214 | |||||
215 | /** |
||||
216 | * Fetch list of type instances found in collectd's datadirs for given host+plugin+instance+type |
||||
217 | * |
||||
218 | * @arg_host Name of host |
||||
219 | * @arg_plugin Name of plugin |
||||
220 | * @arg_pinst Plugin instance |
||||
221 | * @arg_type Type |
||||
222 | * |
||||
223 | * @return array Sorted list of type instances (sorted alphabetically) |
||||
224 | */ |
||||
225 | function collectd_list_tinsts($arg_host, $arg_plugin, $arg_pinst, $arg_type) |
||||
226 | { |
||||
227 | $tinsts = []; |
||||
228 | $my_plugin = $arg_plugin . (strlen($arg_pinst) ? '-' . $arg_pinst : ''); |
||||
229 | if (! preg_match(REGEXP_PLUGIN, $my_plugin)) { |
||||
230 | return $tinsts; |
||||
231 | } |
||||
232 | |||||
233 | foreach (Config::get('datadirs') as $datadir) { |
||||
234 | if (preg_match(REGEXP_HOST, $arg_host) && ($d = @opendir($datadir . '/' . $arg_host . '/' . $my_plugin))) { |
||||
235 | while (($dent = readdir($d)) !== false) { |
||||
236 | if ($dent != '.' && $dent != '..' && is_file($datadir . '/' . $arg_host . '/' . $my_plugin . '/' . $dent) && substr($dent, (strlen($dent) - 4)) == '.rrd') { |
||||
237 | $dent = substr($dent, 0, (strlen($dent) - 4)); |
||||
238 | if ($i = strpos($dent, '-')) { |
||||
239 | $type = substr($dent, 0, $i); |
||||
240 | $tinst = substr($dent, ($i + 1)); |
||||
241 | } else { |
||||
242 | $type = $dent; |
||||
243 | $tinst = ''; |
||||
244 | } |
||||
245 | |||||
246 | if ($type == $arg_type) { |
||||
247 | $tinsts[] = $tinst; |
||||
248 | } |
||||
249 | } |
||||
250 | } |
||||
251 | |||||
252 | closedir($d); |
||||
253 | } |
||||
254 | }//end foreach |
||||
255 | |||||
256 | $tinsts = array_unique($tinsts); |
||||
257 | sort($tinsts); |
||||
258 | |||||
259 | return $tinsts; |
||||
260 | }//end collectd_list_tinsts() |
||||
261 | |||||
262 | /** |
||||
263 | * Parse symlinks in order to get an identifier that collectd understands |
||||
264 | * (e.g. virtualisation is collected on host for individual VMs and can be |
||||
265 | * symlinked to the VM's hostname, support FLUSH for these by flushing |
||||
266 | * on the host-identifier instead of VM-identifier) |
||||
267 | * |
||||
268 | * @param string $host Hostname |
||||
269 | * @param string $plugin Plugin name |
||||
270 | * @param string $type |
||||
271 | * @param string $pinst Plugin instance |
||||
272 | * @param string $tinst Type instance |
||||
273 | * @return string Identifier that collectd's FLUSH command understands |
||||
274 | */ |
||||
275 | function collectd_identifier($host, $plugin, $type, $pinst, $tinst) |
||||
276 | { |
||||
277 | $rrd_realpath = null; |
||||
278 | $orig_identifier = sprintf('%s/%s%s%s/%s%s%s', $host, $plugin, strlen($pinst) ? '-' : '', $pinst, $type, strlen($tinst) ? '-' : '', $tinst); |
||||
279 | $identifier = null; |
||||
280 | foreach (Config::get('datadirs') as $datadir) { |
||||
281 | if (is_file($datadir . '/' . $orig_identifier . '.rrd')) { |
||||
282 | $rrd_realpath = realpath($datadir . '/' . $orig_identifier . '.rrd'); |
||||
283 | break; |
||||
284 | } |
||||
285 | } |
||||
286 | |||||
287 | if ($rrd_realpath) { |
||||
288 | $identifier = basename($rrd_realpath); |
||||
289 | $identifier = substr($identifier, 0, (strlen($identifier) - 4)); |
||||
290 | $rrd_realpath = dirname($rrd_realpath); |
||||
291 | $identifier = basename($rrd_realpath) . '/' . $identifier; |
||||
292 | $rrd_realpath = dirname($rrd_realpath); |
||||
293 | $identifier = basename($rrd_realpath) . '/' . $identifier; |
||||
294 | } |
||||
295 | |||||
296 | if (is_null($identifier)) { |
||||
297 | return $orig_identifier; |
||||
298 | } else { |
||||
299 | return $identifier; |
||||
300 | } |
||||
301 | }//end collectd_identifier() |
||||
302 | |||||
303 | /** |
||||
304 | * Tell collectd that it should FLUSH all data it has regarding the |
||||
305 | * graph we are about to generate. |
||||
306 | * |
||||
307 | * @param string $identifier |
||||
308 | * @return bool |
||||
309 | */ |
||||
310 | function collectd_flush($identifier) |
||||
311 | { |
||||
312 | if (! Config::get('collectd_sock')) { |
||||
313 | return false; |
||||
314 | } |
||||
315 | |||||
316 | if (is_null($identifier) || (is_array($identifier) && count($identifier) == 0) || ! (is_string($identifier) || is_array($identifier))) { |
||||
0 ignored issues
–
show
|
|||||
317 | return false; |
||||
318 | } |
||||
319 | |||||
320 | $u_errno = 0; |
||||
321 | $u_errmsg = ''; |
||||
322 | if ($socket = @fsockopen(Config::get('collectd_sock'), 0, $u_errno, $u_errmsg)) { |
||||
323 | $cmd = 'FLUSH plugin=rrdtool'; |
||||
324 | if (is_array($identifier)) { |
||||
0 ignored issues
–
show
|
|||||
325 | foreach ($identifier as $val) { |
||||
326 | $cmd .= sprintf(' identifier="%s"', $val); |
||||
327 | } |
||||
328 | } else { |
||||
329 | $cmd .= sprintf(' identifier="%s"', $identifier); |
||||
330 | } |
||||
331 | |||||
332 | $cmd .= "\n"; |
||||
333 | |||||
334 | $r = fwrite($socket, $cmd, strlen($cmd)); |
||||
335 | if ($r === false || $r != strlen($cmd)) { |
||||
336 | error_log(sprintf('graph.php: Failed to write whole command to unix-socket: %d out of %d written', $r === false ? (-1) : $r, strlen($cmd))); |
||||
337 | } |
||||
338 | |||||
339 | $resp = fgets($socket); |
||||
340 | if ($resp === false) { |
||||
341 | error_log(sprintf('graph.php: Failed to read response from collectd for command: %s', trim($cmd))); |
||||
342 | } |
||||
343 | |||||
344 | $n = (int) $resp; |
||||
345 | while ($n-- > 0) { |
||||
346 | fgets($socket); |
||||
347 | } |
||||
348 | |||||
349 | fclose($socket); |
||||
350 | } else { |
||||
351 | error_log(sprintf('graph.php: Failed to open unix-socket to collectd: %d: %s', $u_errno, $u_errmsg)); |
||||
352 | } |
||||
353 | |||||
354 | return true; |
||||
355 | }//end collectd_flush() |
||||
356 | |||||
357 | /** |
||||
358 | * Helper function to strip quotes from RRD output |
||||
359 | * |
||||
360 | * @param string $str RRD-Info generated string |
||||
361 | * @return string String with one surrounding pair of quotes stripped |
||||
362 | */ |
||||
363 | function rrd_strip_quotes($str) |
||||
364 | { |
||||
365 | if ($str[0] == '"' && $str[(strlen($str) - 1)] == '"') { |
||||
366 | return substr($str, 1, (strlen($str) - 2)); |
||||
367 | } else { |
||||
368 | return $str; |
||||
369 | } |
||||
370 | }//end rrd_strip_quotes() |
||||
371 | |||||
372 | /** |
||||
373 | * Determine useful information about RRD file |
||||
374 | * |
||||
375 | * @param string $file Name of RRD file to analyse |
||||
376 | * @return array Array describing the RRD file |
||||
377 | */ |
||||
378 | function _rrd_info($file) |
||||
379 | { |
||||
380 | $info = ['filename' => $file]; |
||||
381 | |||||
382 | $rrd = popen(RRDTOOL . ' info ' . escapeshellarg($file), 'r'); |
||||
383 | if ($rrd) { |
||||
0 ignored issues
–
show
|
|||||
384 | while (($s = fgets($rrd)) !== false) { |
||||
385 | $p = strpos($s, '='); |
||||
386 | if ($p === false) { |
||||
387 | continue; |
||||
388 | } |
||||
389 | |||||
390 | $key = trim(substr($s, 0, $p)); |
||||
391 | $value = trim(substr($s, ($p + 1))); |
||||
392 | if (strncmp($key, 'ds[', 3) == 0) { |
||||
393 | // DS definition |
||||
394 | $p = strpos($key, ']'); |
||||
395 | $ds = substr($key, 3, ($p - 3)); |
||||
396 | if (! isset($info['DS'])) { |
||||
397 | $info['DS'] = []; |
||||
398 | } |
||||
399 | |||||
400 | $ds_key = substr($key, ($p + 2)); |
||||
401 | |||||
402 | if (strpos($ds_key, '[') === false) { |
||||
403 | if (! isset($info['DS']["$ds"])) { |
||||
404 | $info['DS']["$ds"] = []; |
||||
405 | } |
||||
406 | |||||
407 | $info['DS']["$ds"]["$ds_key"] = rrd_strip_quotes($value); |
||||
408 | } |
||||
409 | } elseif (strncmp($key, 'rra[', 4) == 0) { |
||||
410 | // RRD definition |
||||
411 | $p = strpos($key, ']'); |
||||
412 | $rra = substr($key, 4, ($p - 4)); |
||||
413 | if (! isset($info['RRA'])) { |
||||
414 | $info['RRA'] = []; |
||||
415 | } |
||||
416 | |||||
417 | $rra_key = substr($key, ($p + 2)); |
||||
418 | |||||
419 | if (strpos($rra_key, '[') === false) { |
||||
420 | if (! isset($info['RRA']["$rra"])) { |
||||
421 | $info['RRA']["$rra"] = []; |
||||
422 | } |
||||
423 | |||||
424 | $info['RRA']["$rra"]["$rra_key"] = rrd_strip_quotes($value); |
||||
425 | } |
||||
426 | } elseif (strpos($key, '[') === false) { |
||||
427 | $info[$key] = rrd_strip_quotes($value); |
||||
428 | }//end if |
||||
429 | }//end while |
||||
430 | |||||
431 | pclose($rrd); |
||||
432 | }//end if |
||||
433 | |||||
434 | return $info; |
||||
435 | }//end _rrd_info() |
||||
436 | |||||
437 | function rrd_get_color($code, $line = true) |
||||
438 | { |
||||
439 | $name = ($line ? 'f_' : 'h_') . $code; |
||||
440 | if (! Config::has("rrd_colors.$name")) { |
||||
441 | $c_f = new CollectdColor('random'); |
||||
442 | $c_h = new CollectdColor($c_f); |
||||
443 | $c_h->fade(); |
||||
444 | Config::set("rrd_colors.f_$code", $c_f->toString()); |
||||
445 | Config::set("rrd_colors.h_$code", $c_h->toString()); |
||||
446 | } |
||||
447 | |||||
448 | return Config::get("rrd_colors.$name"); |
||||
449 | }//end rrd_get_color() |
||||
450 | |||||
451 | /** |
||||
452 | * Draw RRD file based on it's structure |
||||
453 | * |
||||
454 | * @param $host |
||||
455 | * @param $plugin |
||||
456 | * @param $type |
||||
457 | * @param null $pinst |
||||
458 | * @param null $tinst |
||||
459 | * @param array $opts |
||||
460 | * @return string|false Commandline to call RRDGraph in order to generate the final graph* @internal param $ |
||||
461 | */ |
||||
462 | function collectd_draw_rrd($host, $plugin, $type, $pinst = null, $tinst = null, $opts = []) |
||||
463 | { |
||||
464 | $timespan_def = null; |
||||
465 | $timespans = Config::get('timespan'); |
||||
466 | if (! isset($opts['timespan'])) { |
||||
467 | $timespan_def = reset($timespans); |
||||
468 | } else { |
||||
469 | foreach ($timespans as &$ts) { |
||||
470 | if ($ts['name'] == $opts['timespan']) { |
||||
471 | $timespan_def = $ts; |
||||
472 | } |
||||
473 | } |
||||
474 | } |
||||
475 | |||||
476 | if (! isset($opts['rrd_opts'])) { |
||||
477 | $opts['rrd_opts'] = []; |
||||
478 | } |
||||
479 | |||||
480 | if (isset($opts['logarithmic']) && $opts['logarithmic']) { |
||||
481 | array_unshift($opts['rrd_opts'], '-o'); |
||||
482 | } |
||||
483 | |||||
484 | $rrdinfo = null; |
||||
485 | $rrdfile = sprintf('%s/%s%s%s/%s%s%s', $host, $plugin, is_null($pinst) ? '' : '-', $pinst, $type, is_null($tinst) ? '' : '-', $tinst); |
||||
0 ignored issues
–
show
|
|||||
486 | foreach (Config::get('datadirs') as $datadir) { |
||||
487 | if (is_file($datadir . '/' . $rrdfile . '.rrd')) { |
||||
488 | $rrdinfo = _rrd_info($datadir . '/' . $rrdfile . '.rrd'); |
||||
489 | if (isset($rrdinfo['RRA']) && is_array($rrdinfo['RRA'])) { |
||||
490 | break; |
||||
491 | } else { |
||||
492 | $rrdinfo = null; |
||||
493 | } |
||||
494 | } |
||||
495 | } |
||||
496 | |||||
497 | if (is_null($rrdinfo)) { |
||||
498 | return false; |
||||
499 | } |
||||
500 | |||||
501 | $graph = []; |
||||
502 | $has_avg = false; |
||||
503 | $has_max = false; |
||||
504 | $has_min = false; |
||||
505 | reset($rrdinfo['RRA']); |
||||
506 | $l_max = 0; |
||||
507 | foreach ($rrdinfo['RRA'] as $k => $v) { |
||||
508 | if ($v['cf'] == 'MAX') { |
||||
509 | $has_max = true; |
||||
510 | } elseif ($v['cf'] == 'AVERAGE') { |
||||
511 | $has_avg = true; |
||||
512 | } elseif ($v['cf'] == 'MIN') { |
||||
513 | $has_min = true; |
||||
514 | } |
||||
515 | } |
||||
516 | |||||
517 | // Build legend. This may not work for all RRDs, i don't know :) |
||||
518 | if ($has_avg) { |
||||
519 | $graph[] = 'COMMENT: Last'; |
||||
520 | } |
||||
521 | |||||
522 | if ($has_min) { |
||||
523 | $graph[] = 'COMMENT: Min'; |
||||
524 | } |
||||
525 | |||||
526 | if ($has_max) { |
||||
527 | $graph[] = 'COMMENT: Max'; |
||||
528 | } |
||||
529 | |||||
530 | if ($has_avg) { |
||||
531 | $graph[] = 'COMMENT: Avg\\n'; |
||||
532 | } |
||||
533 | |||||
534 | reset($rrdinfo['DS']); |
||||
0 ignored issues
–
show
$rrdinfo['DS'] of type string is incompatible with the type array|object expected by parameter $array of reset() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
535 | foreach ($rrdinfo['DS'] as $k => $v) { |
||||
536 | if (strlen($k) > $l_max) { |
||||
537 | $l_max = strlen($k); |
||||
538 | } |
||||
539 | |||||
540 | if ($has_min) { |
||||
541 | $graph[] = sprintf('DEF:%s_min=%s:%s:MIN', $k, $rrdinfo['filename'], $k); |
||||
542 | } |
||||
543 | |||||
544 | if ($has_avg) { |
||||
545 | $graph[] = sprintf('DEF:%s_avg=%s:%s:AVERAGE', $k, $rrdinfo['filename'], $k); |
||||
546 | } |
||||
547 | |||||
548 | if ($has_max) { |
||||
549 | $graph[] = sprintf('DEF:%s_max=%s:%s:MAX', $k, $rrdinfo['filename'], $k); |
||||
550 | } |
||||
551 | } |
||||
552 | |||||
553 | if ($has_min && $has_max || $has_min && $has_avg || $has_avg && $has_max) { |
||||
554 | $n = 1; |
||||
555 | reset($rrdinfo['DS']); |
||||
556 | foreach ($rrdinfo['DS'] as $k => $v) { |
||||
557 | $graph[] = sprintf('LINE:%s_%s', $k, $has_min ? 'min' : 'avg'); |
||||
558 | $graph[] = sprintf('CDEF:%s_var=%s_%s,%s_%s,-', $k, $k, $has_max ? 'max' : 'avg', $k, $has_min ? 'min' : 'avg'); |
||||
559 | $graph[] = sprintf('AREA:%s_var#%s::STACK', $k, rrd_get_color($n++, false)); |
||||
560 | } |
||||
561 | } |
||||
562 | |||||
563 | reset($rrdinfo['DS']); |
||||
564 | $n = 1; |
||||
565 | foreach ($rrdinfo['DS'] as $k => $v) { |
||||
566 | $graph[] = sprintf('LINE1:%s_avg#%s:%s ', $k, rrd_get_color($n++, true), $k . substr(' ', 0, ($l_max - strlen($k)))); |
||||
567 | if (isset($opts['tinylegend']) && $opts['tinylegend']) { |
||||
568 | continue; |
||||
569 | } |
||||
570 | |||||
571 | if ($has_avg) { |
||||
572 | $graph[] = sprintf('GPRINT:%s_avg:AVERAGE:%%5.1lf%%s', $k, $has_max || $has_min || $has_avg ? ',' : '\\l'); |
||||
573 | } |
||||
574 | |||||
575 | if ($has_min) { |
||||
576 | $graph[] = sprintf('GPRINT:%s_min:MIN:%%5.1lf%%s', $k, $has_max || $has_avg ? ',' : '\\l'); |
||||
577 | } |
||||
578 | |||||
579 | if ($has_max) { |
||||
580 | $graph[] = sprintf('GPRINT:%s_max:MAX:%%5.1lf%%s', $k, $has_avg ? ',' : '\\l'); |
||||
581 | } |
||||
582 | |||||
583 | if ($has_avg) { |
||||
584 | $graph[] = sprintf('GPRINT:%s_avg:LAST:%%5.1lf%%s\\l', $k); |
||||
585 | } |
||||
586 | }//end while |
||||
587 | |||||
588 | // $rrd_cmd = array(RRDTOOL, 'graph', '-', '-E', '-a', 'PNG', '-w', Config::get('rrd_width'), '-h', Config::get('rrd_height'), '-t', $rrdfile); |
||||
589 | $rrd_cmd = [ |
||||
590 | RRDTOOL, |
||||
591 | 'graph', |
||||
592 | '-', |
||||
593 | '-E', |
||||
594 | '-a', |
||||
595 | 'PNG', |
||||
596 | '-w', |
||||
597 | Config::get('rrd_width'), |
||||
598 | '-h', |
||||
599 | Config::get('rrd_height'), |
||||
600 | ]; |
||||
601 | if (Config::get('rrd_width') <= '300') { |
||||
602 | $small_opts = [ |
||||
603 | '--font', |
||||
604 | 'LEGEND:7:mono', |
||||
605 | '--font', |
||||
606 | 'AXIS:6:mono', |
||||
607 | '--font-render-mode', |
||||
608 | 'normal', |
||||
609 | ]; |
||||
610 | $rrd_cmd = array_merge($rrd_cmd, $small_opts); |
||||
611 | } |
||||
612 | |||||
613 | $rrd_cmd = array_merge($rrd_cmd, Config::get('rrd_opts_array'), $opts['rrd_opts'], $graph); |
||||
614 | |||||
615 | $cmd = RRDTOOL; |
||||
616 | $count_rrd_cmd = count($rrd_cmd); |
||||
617 | for ($i = 1; $i < $count_rrd_cmd; $i++) { |
||||
618 | $cmd .= ' ' . escapeshellarg($rrd_cmd[$i]); |
||||
619 | } |
||||
620 | |||||
621 | return $cmd; |
||||
622 | }//end collectd_draw_rrd() |
||||
623 | |||||
624 | /** |
||||
625 | * Draw RRD file based on it's structure |
||||
626 | * |
||||
627 | * @param $timespan |
||||
628 | * @param $host |
||||
629 | * @param $plugin |
||||
630 | * @param $type |
||||
631 | * @param null $pinst |
||||
632 | * @param null $tinst |
||||
633 | * @return false|string Commandline to call RRDGraph in order to generate the final graph* @internal param $ |
||||
634 | */ |
||||
635 | function collectd_draw_generic($timespan, $host, $plugin, $type, $pinst = null, $tinst = null) |
||||
636 | { |
||||
637 | global $GraphDefs; |
||||
638 | $timespan_def = null; |
||||
639 | $timespans = Config::get('timespan'); |
||||
640 | foreach ($timespans as &$ts) { |
||||
641 | if ($ts['name'] == $timespan) { |
||||
642 | $timespan_def = $ts; |
||||
643 | } |
||||
644 | } |
||||
645 | |||||
646 | if (is_null($timespan_def)) { |
||||
647 | $timespan_def = reset($timespans); |
||||
648 | } |
||||
649 | |||||
650 | if (! isset($GraphDefs[$type])) { |
||||
651 | return false; |
||||
652 | } |
||||
653 | |||||
654 | $rrd_file = sprintf('%s/%s%s%s/%s%s%s', $host, $plugin, is_null($pinst) ? '' : '-', $pinst, $type, is_null($tinst) ? '' : '-', $tinst); |
||||
0 ignored issues
–
show
|
|||||
655 | // $rrd_cmd = array(RRDTOOL, 'graph', '-', '-E', '-a', 'PNG', '-w', Config::get('rrd_width'), '-h', Config::get('rrd_height'), '-t', $rrd_file); |
||||
656 | $rrd_cmd = [ |
||||
657 | RRDTOOL, |
||||
658 | 'graph', |
||||
659 | '-', |
||||
660 | '-E', |
||||
661 | '-a', |
||||
662 | 'PNG', |
||||
663 | '-w', |
||||
664 | Config::get('rrd_width'), |
||||
665 | '-h', |
||||
666 | Config::get('rrd_height'), |
||||
667 | ]; |
||||
668 | |||||
669 | if (Config::get('rrd_width') <= '300') { |
||||
670 | $small_opts = [ |
||||
671 | '--font', |
||||
672 | 'LEGEND:7:mono', |
||||
673 | '--font', |
||||
674 | 'AXIS:6:mono', |
||||
675 | '--font-render-mode', |
||||
676 | 'normal', |
||||
677 | ]; |
||||
678 | $rrd_cmd = array_merge($rrd_cmd, $small_opts); |
||||
679 | } |
||||
680 | |||||
681 | $rrd_cmd = array_merge($rrd_cmd, Config::get('rrd_opts_array')); |
||||
682 | $rrd_args = $GraphDefs[$type]; |
||||
683 | |||||
684 | foreach (Config::get('datadirs') as $datadir) { |
||||
685 | $file = $datadir . '/' . $rrd_file . '.rrd'; |
||||
686 | if (! is_file($file)) { |
||||
687 | continue; |
||||
688 | } |
||||
689 | |||||
690 | $file = str_replace(':', '\\:', $file); |
||||
691 | $rrd_args = str_replace('{file}', $file, $rrd_args); |
||||
692 | |||||
693 | $rrdgraph = array_merge($rrd_cmd, $rrd_args); |
||||
694 | $cmd = RRDTOOL; |
||||
695 | $count_rrdgraph = count($rrdgraph); |
||||
696 | for ($i = 1; $i < $count_rrdgraph; $i++) { |
||||
697 | $cmd .= ' ' . escapeshellarg($rrdgraph[$i]); |
||||
698 | } |
||||
699 | |||||
700 | return $cmd; |
||||
701 | } |
||||
702 | |||||
703 | return false; |
||||
704 | }//end collectd_draw_generic() |
||||
705 | |||||
706 | /** |
||||
707 | * Draw stack-graph for set of RRD files |
||||
708 | * |
||||
709 | * @param array $opts Graph options like colors |
||||
710 | * @param array $sources List of array(name, file, ds) |
||||
711 | * @return string Commandline to call RRDGraph in order to generate the final graph |
||||
712 | */ |
||||
713 | function collectd_draw_meta_stack(&$opts, &$sources) |
||||
714 | { |
||||
715 | $timespan_def = null; |
||||
716 | $timespans = Config::get('timespan'); |
||||
717 | if (! isset($opts['timespan'])) { |
||||
718 | $timespan_def = reset($timespans); |
||||
719 | } else { |
||||
720 | foreach ($timespans as &$ts) { |
||||
721 | if ($ts['name'] == $opts['timespan']) { |
||||
722 | $timespan_def = $ts; |
||||
723 | } |
||||
724 | } |
||||
725 | } |
||||
726 | |||||
727 | if (! isset($opts['title'])) { |
||||
728 | $opts['title'] = 'Unknown title'; |
||||
729 | } |
||||
730 | |||||
731 | if (! isset($opts['rrd_opts'])) { |
||||
732 | $opts['rrd_opts'] = []; |
||||
733 | } |
||||
734 | |||||
735 | if (! isset($opts['colors'])) { |
||||
736 | $opts['colors'] = []; |
||||
737 | } |
||||
738 | |||||
739 | if (isset($opts['logarithmic']) && $opts['logarithmic']) { |
||||
740 | array_unshift($opts['rrd_opts'], '-o'); |
||||
741 | } |
||||
742 | |||||
743 | // $cmd = array(RRDTOOL, 'graph', '-', '-E', '-a', 'PNG', '-w', Config::get('rrd_width'), '-h', Config::get('rrd_height'), |
||||
744 | // '-t', $opts['title']); |
||||
745 | $cmd = [ |
||||
746 | RRDTOOL, |
||||
747 | 'graph', |
||||
748 | '-', |
||||
749 | '-E', |
||||
750 | '-a', |
||||
751 | 'PNG', |
||||
752 | '-w', |
||||
753 | Config::get('rrd_width'), |
||||
754 | '-h', |
||||
755 | Config::get('rrd_height'), |
||||
756 | ]; |
||||
757 | |||||
758 | if (Config::get('rrd_width') <= '300') { |
||||
759 | $small_opts = [ |
||||
760 | '--font', |
||||
761 | 'LEGEND:7:mono', |
||||
762 | '--font', |
||||
763 | 'AXIS:6:mono', |
||||
764 | '--font-render-mode', |
||||
765 | 'normal', |
||||
766 | ]; |
||||
767 | $cmd = array_merge($cmd, $small_opts); |
||||
768 | } |
||||
769 | |||||
770 | $cmd = array_merge($cmd, Config::get('rrd_opts_array'), $opts['rrd_opts']); |
||||
771 | $max_inst_name = 0; |
||||
772 | |||||
773 | foreach ($sources as &$inst_data) { |
||||
774 | $inst_name = $inst_data['name']; |
||||
775 | $file = $inst_data['file']; |
||||
776 | $ds = isset($inst_data['ds']) ? $inst_data['ds'] : 'value'; |
||||
777 | |||||
778 | if (strlen($inst_name) > $max_inst_name) { |
||||
779 | $max_inst_name = strlen($inst_name); |
||||
780 | } |
||||
781 | |||||
782 | if (! is_file($file)) { |
||||
783 | continue; |
||||
784 | } |
||||
785 | |||||
786 | $cmd[] = 'DEF:' . $inst_name . '_min=' . $file . ':' . $ds . ':MIN'; |
||||
787 | $cmd[] = 'DEF:' . $inst_name . '_avg=' . $file . ':' . $ds . ':AVERAGE'; |
||||
788 | $cmd[] = 'DEF:' . $inst_name . '_max=' . $file . ':' . $ds . ':MAX'; |
||||
789 | $cmd[] = 'CDEF:' . $inst_name . '_nnl=' . $inst_name . '_avg,UN,0,' . $inst_name . '_avg,IF'; |
||||
790 | } |
||||
791 | |||||
792 | $inst_data = end($sources); |
||||
793 | $inst_name = $inst_data['name']; |
||||
794 | $cmd[] = 'CDEF:' . $inst_name . '_stk=' . $inst_name . '_nnl'; |
||||
795 | |||||
796 | $inst_data1 = end($sources); |
||||
797 | while (($inst_data0 = prev($sources)) !== false) { |
||||
798 | $inst_name0 = $inst_data0['name']; |
||||
799 | $inst_name1 = $inst_data1['name']; |
||||
800 | |||||
801 | $cmd[] = 'CDEF:' . $inst_name0 . '_stk=' . $inst_name0 . '_nnl,' . $inst_name1 . '_stk,+'; |
||||
802 | $inst_data1 = $inst_data0; |
||||
803 | } |
||||
804 | |||||
805 | foreach ($sources as &$inst_data) { |
||||
806 | $inst_name = $inst_data['name']; |
||||
807 | // $legend = sprintf('%s', $inst_name); |
||||
808 | $legend = $inst_name; |
||||
809 | while (strlen($legend) < $max_inst_name) { |
||||
810 | $legend .= ' '; |
||||
811 | } |
||||
812 | |||||
813 | $number_format = isset($opts['number_format']) ? $opts['number_format'] : '%6.1lf'; |
||||
814 | |||||
815 | if (isset($opts['colors'][$inst_name])) { |
||||
816 | $line_color = new CollectdColor($opts['colors'][$inst_name]); |
||||
817 | } else { |
||||
818 | $line_color = new CollectdColor('random'); |
||||
819 | } |
||||
820 | |||||
821 | $area_color = new CollectdColor($line_color); |
||||
822 | $area_color->fade(); |
||||
823 | |||||
824 | $cmd[] = 'AREA:' . $inst_name . '_stk#' . $area_color->toString(); |
||||
825 | $cmd[] = 'LINE1:' . $inst_name . '_stk#' . $line_color->toString() . ':' . $legend; |
||||
826 | if (! (isset($opts['tinylegend']) && $opts['tinylegend'])) { |
||||
827 | $cmd[] = 'GPRINT:' . $inst_name . '_avg:LAST:' . $number_format . ''; |
||||
828 | $cmd[] = 'GPRINT:' . $inst_name . '_avg:AVERAGE:' . $number_format . ''; |
||||
829 | $cmd[] = 'GPRINT:' . $inst_name . '_min:MIN:' . $number_format . ''; |
||||
830 | $cmd[] = 'GPRINT:' . $inst_name . '_max:MAX:' . $number_format . '\\l'; |
||||
831 | } |
||||
832 | }//end foreach |
||||
833 | |||||
834 | $rrdcmd = RRDTOOL; |
||||
835 | $count_cmd = count($cmd); |
||||
836 | for ($i = 1; $i < $count_cmd; $i++) { |
||||
837 | $rrdcmd .= ' ' . escapeshellarg($cmd[$i]); |
||||
838 | } |
||||
839 | |||||
840 | return $rrdcmd; |
||||
841 | }//end collectd_draw_meta_stack() |
||||
842 | |||||
843 | /** |
||||
844 | * Draw stack-graph for set of RRD files |
||||
845 | * |
||||
846 | * @param array $opts Graph options like colors |
||||
847 | * @param array $sources List of array(name, file, ds) |
||||
848 | * @return string Commandline to call RRDGraph in order to generate the final graph |
||||
849 | */ |
||||
850 | function collectd_draw_meta_line(&$opts, &$sources) |
||||
851 | { |
||||
852 | $timespan_def = null; |
||||
853 | $timespans = Config::get('timespan'); |
||||
854 | if (! isset($opts['timespan'])) { |
||||
855 | $timespan_def = reset($timespans); |
||||
856 | } else { |
||||
857 | foreach ($timespans as &$ts) { |
||||
858 | if ($ts['name'] == $opts['timespan']) { |
||||
859 | $timespan_def = $ts; |
||||
860 | } |
||||
861 | } |
||||
862 | } |
||||
863 | |||||
864 | if (! isset($opts['title'])) { |
||||
865 | $opts['title'] = 'Unknown title'; |
||||
866 | } |
||||
867 | |||||
868 | if (! isset($opts['rrd_opts'])) { |
||||
869 | $opts['rrd_opts'] = []; |
||||
870 | } |
||||
871 | |||||
872 | if (! isset($opts['colors'])) { |
||||
873 | $opts['colors'] = []; |
||||
874 | } |
||||
875 | |||||
876 | if (isset($opts['logarithmic']) && $opts['logarithmic']) { |
||||
877 | array_unshift($opts['rrd_opts'], '-o'); |
||||
878 | } |
||||
879 | |||||
880 | // $cmd = array(RRDTOOL, 'graph', '-', '-E', '-a', 'PNG', '-w', Config::get('rrd_width'), '-h', Config::get('rrd_height'), '-t', $opts['title']); |
||||
881 | // $cmd = array_merge($cmd, Config::get('rrd_opts_array'), $opts['rrd_opts']); |
||||
882 | $cmd = [ |
||||
883 | RRDTOOL, |
||||
884 | 'graph', |
||||
885 | '-', |
||||
886 | '-E', |
||||
887 | '-a', |
||||
888 | 'PNG', |
||||
889 | '-w', |
||||
890 | Config::get('rrd_width'), |
||||
891 | '-h', |
||||
892 | Config::get('rrd_height'), |
||||
893 | ]; |
||||
894 | |||||
895 | if (Config::get('rrd_width') <= '300') { |
||||
896 | $small_opts = [ |
||||
897 | '--font', |
||||
898 | 'LEGEND:7:mono', |
||||
899 | '--font', |
||||
900 | 'AXIS:6:mono', |
||||
901 | '--font-render-mode', |
||||
902 | 'normal', |
||||
903 | ]; |
||||
904 | $cmd = array_merge($cmd, $small_opts); |
||||
905 | } |
||||
906 | |||||
907 | $max_inst_name = 0; |
||||
908 | |||||
909 | foreach ($sources as &$inst_data) { |
||||
910 | $inst_name = $inst_data['name']; |
||||
911 | $file = $inst_data['file']; |
||||
912 | $ds = isset($inst_data['ds']) ? $inst_data['ds'] : 'value'; |
||||
913 | |||||
914 | if (strlen($inst_name) > $max_inst_name) { |
||||
915 | $max_inst_name = strlen($inst_name); |
||||
916 | } |
||||
917 | |||||
918 | if (! is_file($file)) { |
||||
919 | continue; |
||||
920 | } |
||||
921 | |||||
922 | $cmd[] = 'DEF:' . $inst_name . '_min=' . $file . ':' . $ds . ':MIN'; |
||||
923 | $cmd[] = 'DEF:' . $inst_name . '_avg=' . $file . ':' . $ds . ':AVERAGE'; |
||||
924 | $cmd[] = 'DEF:' . $inst_name . '_max=' . $file . ':' . $ds . ':MAX'; |
||||
925 | } |
||||
926 | |||||
927 | foreach ($sources as &$inst_data) { |
||||
928 | $inst_name = $inst_data['name']; |
||||
929 | $legend = sprintf('%s', $inst_name); |
||||
930 | while (strlen($legend) < $max_inst_name) { |
||||
931 | $legend .= ' '; |
||||
932 | } |
||||
933 | |||||
934 | $number_format = isset($opts['number_format']) ? $opts['number_format'] : '%6.1lf'; |
||||
935 | |||||
936 | if (isset($opts['colors'][$inst_name])) { |
||||
937 | $line_color = new CollectdColor($opts['colors'][$inst_name]); |
||||
938 | } else { |
||||
939 | $line_color = new CollectdColor('random'); |
||||
940 | } |
||||
941 | |||||
942 | $cmd[] = 'LINE1:' . $inst_name . '_avg#' . $line_color->toString() . ':' . $legend; |
||||
943 | if (! (isset($opts['tinylegend']) && $opts['tinylegend'])) { |
||||
944 | $cmd[] = 'GPRINT:' . $inst_name . '_min:MIN:' . $number_format . ''; |
||||
945 | $cmd[] = 'GPRINT:' . $inst_name . '_avg:AVERAGE:' . $number_format . ''; |
||||
946 | $cmd[] = 'GPRINT:' . $inst_name . '_max:MAX:' . $number_format . ''; |
||||
947 | $cmd[] = 'GPRINT:' . $inst_name . '_avg:LAST:' . $number_format . '\\l'; |
||||
948 | } |
||||
949 | }//end foreach |
||||
950 | |||||
951 | $rrdcmd = RRDTOOL; |
||||
952 | $count_cmd = count($cmd); |
||||
953 | for ($i = 1; $i < $count_cmd; $i++) { |
||||
954 | $rrdcmd .= ' ' . escapeshellarg($cmd[$i]); |
||||
955 | } |
||||
956 | |||||
957 | return $rrdcmd; |
||||
958 | }//end collectd_draw_meta_line() |
||||
959 |