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; |
||
0 ignored issues
–
show
|
|||
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; |
||
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))) { |
||
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)) { |
||
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) { |
||
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); |
||
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']); |
||
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); |
||
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 |
Let?s assume that you have a directory layout like this:
and let?s assume the following content of
Bar.php
:If both files
OtherDir/Foo.php
andSomeDir/Foo.php
are loaded in the same runtime, you will see a PHP error such as the following:PHP Fatal error: Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php
However, as
OtherDir/Foo.php
does not necessarily have to be loaded and the error is only triggered if it is loaded beforeOtherDir/Bar.php
, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias: