1 <?php // vim:fenc=utf-8:filetype=php:ts=4
3 * Copyright (C) 2009 Bruno Prémont <bonbons AT linux-vserver.org>
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.
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
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.
19 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])?)*$/');
20 define('REGEXP_PLUGIN', '/^[a-zA-Z0-9_.-]+$/');
23 * Read input variable from GET, POST or COOKIE taking
24 * care of magic quotes
25 * @name Name of value to return
26 * @array User-input array ($_GET, $_POST or $_COOKIE)
27 * @default Default value
28 * @return $default if name in unknown in $array, otherwise
29 * input value with magic quotes stripped off
31 function read_var($name, &$array, $default = null) {
32 if (isset($array[$name])) {
33 if (is_array($array[$name])) {
34 if (get_magic_quotes_gpc()) {
36 while (list($k, $v) = each($array[$name]))
37 $ret[stripslashes($k)] = stripslashes($v);
41 } else if (is_string($array[$name]) && get_magic_quotes_gpc()) {
42 return stripslashes($array[$name]);
50 * Alphabetically compare host names, comparing label
51 * from tld to node name
53 function collectd_compare_host($a, $b) {
54 $ea = explode('.', $a);
55 $eb = explode('.', $b);
58 while ($i >= 0 && $j >= 0)
59 if (($r = strcmp($ea[$i--], $eb[$j--])) != 0)
65 * Fetch list of hosts found in collectd's datadirs.
66 * @return Sorted list of hosts (sorted by label from rigth to left)
68 function collectd_list_hosts() {
72 foreach($config['datadirs'] as $datadir)
73 if ($d = @opendir($datadir)) {
74 while (($dent = readdir($d)) !== false)
75 if ($dent != '.' && $dent != '..' && is_dir($datadir.'/'.$dent) && preg_match(REGEXP_HOST, $dent))
79 $hosts = array_unique($hosts);
80 usort($hosts, 'collectd_compare_host');
85 * Fetch list of plugins found in collectd's datadirs for given host.
86 * @arg_host Name of host for which to return plugins
87 * @return Sorted list of plugins (sorted alphabetically)
89 function collectd_list_plugins($arg_host) {
93 foreach ($config['datadirs'] as $datadir)
94 if (preg_match(REGEXP_HOST, $arg_host) && ($d = @opendir($datadir.'/'.$arg_host))) {
95 while (($dent = readdir($d)) !== false)
96 if ($dent != '.' && $dent != '..' && is_dir($datadir.'/'.$arg_host.'/'.$dent)) {
97 if ($i = strpos($dent, '-'))
98 $plugins[] = substr($dent, 0, $i);
104 $plugins = array_unique($plugins);
110 * Fetch list of plugin instances found in collectd's datadirs for given host+plugin
111 * @arg_host Name of host
112 * @arg_plugin Name of plugin
113 * @return Sorted list of plugin instances (sorted alphabetically)
115 function collectd_list_pinsts($arg_host, $arg_plugin) {
119 foreach ($config['datadirs'] as $datadir)
120 if (preg_match(REGEXP_HOST, $arg_host) && ($d = opendir($datadir.'/'.$arg_host))) {
121 while (($dent = readdir($d)) !== false)
122 if ($dent != '.' && $dent != '..' && is_dir($datadir.'/'.$arg_host.'/'.$dent)) {
123 if ($i = strpos($dent, '-')) {
124 $plugin = substr($dent, 0, $i);
125 $pinst = substr($dent, $i+1);
130 if ($plugin == $arg_plugin)
135 $pinsts = array_unique($pinsts);
141 * Fetch list of types found in collectd's datadirs for given host+plugin+instance
142 * @arg_host Name of host
143 * @arg_plugin Name of plugin
144 * @arg_pinst Plugin instance
145 * @return Sorted list of types (sorted alphabetically)
147 function collectd_list_types($arg_host, $arg_plugin, $arg_pinst) {
151 $my_plugin = $arg_plugin . (strlen($arg_pinst) ? '-'.$arg_pinst : '');
152 if (!preg_match(REGEXP_PLUGIN, $my_plugin))
154 foreach ($config['datadirs'] as $datadir)
155 if (preg_match(REGEXP_HOST, $arg_host) && ($d = @opendir($datadir.'/'.$arg_host.'/'.$my_plugin))) {
156 while (($dent = readdir($d)) !== false)
157 if ($dent != '.' && $dent != '..' && is_file($datadir.'/'.$arg_host.'/'.$my_plugin.'/'.$dent) && substr($dent, strlen($dent)-4) == '.rrd') {
158 $dent = substr($dent, 0, strlen($dent)-4);
159 if ($i = strpos($dent, '-'))
160 $types[] = substr($dent, 0, $i);
166 $types = array_unique($types);
172 * Fetch list of type instances found in collectd's datadirs for given host+plugin+instance+type
173 * @arg_host Name of host
174 * @arg_plugin Name of plugin
175 * @arg_pinst Plugin instance
177 * @return Sorted list of type instances (sorted alphabetically)
179 function collectd_list_tinsts($arg_host, $arg_plugin, $arg_pinst, $arg_type) {
183 $my_plugin = $arg_plugin . (strlen($arg_pinst) ? '-'.$arg_pinst : '');
184 if (!preg_match(REGEXP_PLUGIN, $my_plugin))
186 foreach ($config['datadirs'] as $datadir)
187 if (preg_match(REGEXP_HOST, $arg_host) && ($d = @opendir($datadir.'/'.$arg_host.'/'.$my_plugin))) {
188 while (($dent = readdir($d)) !== false)
189 if ($dent != '.' && $dent != '..' && is_file($datadir.'/'.$arg_host.'/'.$my_plugin.'/'.$dent) && substr($dent, strlen($dent)-4) == '.rrd') {
190 $dent = substr($dent, 0, strlen($dent)-4);
191 if ($i = strpos($dent, '-')) {
192 $type = substr($dent, 0, $i);
193 $tinst = substr($dent, $i+1);
198 if ($type == $arg_type)
203 $tinsts = array_unique($tinsts);
209 * Parse symlinks in order to get an identifier that collectd understands
210 * (e.g. virtualisation is collected on host for individual VMs and can be
211 * symlinked to the VM's hostname, support FLUSH for these by flushing
212 * on the host-identifier instead of VM-identifier)
214 * @plugin Plugin name
215 * @pinst Plugin instance
217 * @tinst Type instance
218 * @return Identifier that collectd's FLUSH command understands
220 function collectd_identifier($host, $plugin, $pinst, $type, $tinst) {
222 $rrd_realpath = null;
223 $orig_identifier = sprintf('%s/%s%s%s/%s%s%s', $host, $plugin, strlen($pinst) ? '-' : '', $pinst, $type, strlen($tinst) ? '-' : '', $tinst);
225 foreach ($config['datadirs'] as $datadir)
226 if (is_file($datadir.'/'.$orig_identifier.'.rrd')) {
227 $rrd_realpath = realpath($datadir.'/'.$orig_identifier.'.rrd');
231 $identifier = basename($rrd_realpath);
232 $identifier = substr($identifier, 0, strlen($identifier)-4);
233 $rrd_realpath = dirname($rrd_realpath);
234 $identifier = basename($rrd_realpath).'/'.$identifier;
235 $rrd_realpath = dirname($rrd_realpath);
236 $identifier = basename($rrd_realpath).'/'.$identifier;
239 if (is_null($identifier))
240 return $orig_identifier;
246 * Tell collectd that it should FLUSH all data it has regarding the
247 * graph we are about to generate.
249 * @plugin Plugin name
250 * @pinst Plugin instance
252 * @tinst Type instance
254 function collectd_flush($identifier) {
257 if (!$config['collectd_sock'])
259 if (is_null($identifier) || (is_array($identifier) && count($identifier) == 0) || !(is_string($identifier) || is_array($identifier)))
262 if (is_null($host) || !is_string($host) || strlen($host) == 0)
264 if (is_null($plugin) || !is_string($plugin) || strlen($plugin) == 0)
266 if (is_null($pinst) || !is_string($pinst))
268 if (is_null($type) || !is_string($type) || strlen($type) == 0)
270 if (is_null($tinst) || (is_array($tinst) && count($tinst) == 0) || !(is_string($tinst) || is_array($tinst)))
275 if ($socket = @fsockopen($config['collectd_sock'], 0, $u_errno, $u_errmsg)) {
276 $cmd = 'FLUSH plugin=rrdtool';
277 if (is_array($identifier)) {
278 foreach ($identifier as $val)
279 $cmd .= sprintf(' identifier="%s"', $val);
281 $cmd .= sprintf(' identifier="%s"', $identifier);
284 $r = fwrite($socket, $cmd, strlen($cmd));
285 if ($r === false || $r != strlen($cmd))
286 error_log(sprintf("graph.php: Failed to write whole command to unix-socket: %d out of %d written", $r === false ? -1 : $r, strlen($cmd)));
288 $resp = fgets($socket);
290 error_log(sprintf("graph.php: Failed to read response from collectd for command: %s", trim($cmd)));
298 error_log(sprintf("graph.php: Failed to open unix-socket to collectd: %d: %s", $u_errno, $u_errmsg));
301 class CollectdColor {
306 function __construct($value = null) {
307 if (is_null($value)) {
308 } else if (is_array($value)) {
309 if (isset($value['r']))
310 $this->r = $value['r'] > 0 ? ($value['r'] > 1 ? 1 : $value['r']) : 0;
311 if (isset($value['g']))
312 $this->g = $value['g'] > 0 ? ($value['g'] > 1 ? 1 : $value['g']) : 0;
313 if (isset($value['b']))
314 $this->b = $value['b'] > 0 ? ($value['b'] > 1 ? 1 : $value['b']) : 0;
315 } else if (is_string($value)) {
317 if ($value == 'random') {
319 } else if (preg_match('/([0-9A-Fa-f][0-9A-Fa-f])([0-9A-Fa-f][0-9A-Fa-f])([0-9A-Fa-f][0-9A-Fa-f])/', $value, $matches)) {
320 $this->r = ('0x'.$matches[1]) / 255.0;
321 $this->g = ('0x'.$matches[2]) / 255.0;
322 $this->b = ('0x'.$matches[3]) / 255.0;
324 } else if (is_a($value, 'CollectdColor')) {
325 $this->r = $value->r;
326 $this->g = $value->g;
327 $this->b = $value->b;
331 function randomize() {
332 $this->r = rand(0, 255) / 255.0;
333 $this->g = rand(0, 255) / 255.0;
338 if (($this->r + $this->g) < 1.0) {
339 $min = 1.0 - ($this->r + $this->g);
341 $max = 2.0 - ($this->r + $this->g);
343 $this->b = $min + ((rand(0, 255)/255.0) * ($max - $min));
346 function fade($bkgnd = null, $alpha = 0.25) {
347 if (is_null($bkgnd) || !is_a($bkgnd, 'CollectdColor')) {
357 $this->r = $alpha * $this->r + ((1.0 - $alpha) * $bg_r);
358 $this->g = $alpha * $this->g + ((1.0 - $alpha) * $bg_g);
359 $this->b = $alpha * $this->b + ((1.0 - $alpha) * $bg_b);
362 function as_array() {
363 return array('r'=>$this->r, 'g'=>$this->g, 'b'=>$this->b);
366 function as_string() {
367 $r = (int)($this->r*255);
368 $g = (int)($this->g*255);
369 $b = (int)($this->b*255);
370 return sprintf('%02x%02x%02x', $r > 255 ? 255 : $r, $g > 255 ? 255 : $g, $b > 255 ? 255 : $b);
376 * Helper function to strip quotes from RRD output
377 * @str RRD-Info generated string
378 * @return String with one surrounding pair of quotes stripped
380 function rrd_strip_quotes($str) {
381 if ($str[0] == '"' && $str[strlen($str)-1] == '"')
382 return substr($str, 1, strlen($str)-2);
388 * Determine useful information about RRD file
389 * @file Name of RRD file to analyse
390 * @return Array describing the RRD file
392 function rrd_info($file) {
393 $info = array('filename'=>$file);
395 $rrd = popen(RRDTOOL.' info '.escapeshellarg($file), 'r');
397 while (($s = fgets($rrd)) !== false) {
398 $p = strpos($s, '=');
401 $key = trim(substr($s, 0, $p));
402 $value = trim(substr($s, $p+1));
403 if (strncmp($key,'ds[', 3) == 0) {
405 $p = strpos($key, ']');
406 $ds = substr($key, 3, $p-3);
407 if (!isset($info['DS']))
408 $info['DS'] = array();
409 $ds_key = substr($key, $p+2);
411 if (strpos($ds_key, '[') === false) {
412 if (!isset($info['DS']["$ds"]))
413 $info['DS']["$ds"] = array();
414 $info['DS']["$ds"]["$ds_key"] = rrd_strip_quotes($value);
416 } else if (strncmp($key, 'rra[', 4) == 0) {
418 $p = strpos($key, ']');
419 $rra = substr($key, 4, $p-4);
420 if (!isset($info['RRA']))
421 $info['RRA'] = array();
422 $rra_key = substr($key, $p+2);
424 if (strpos($rra_key, '[') === false) {
425 if (!isset($info['RRA']["$rra"]))
426 $info['RRA']["$rra"] = array();
427 $info['RRA']["$rra"]["$rra_key"] = rrd_strip_quotes($value);
429 } else if (strpos($key, '[') === false) {
430 $info[$key] = rrd_strip_quotes($value);
438 function rrd_get_color($code, $line = true) {
440 $name = ($line ? 'f_' : 'h_').$code;
441 if (!isset($config['rrd_colors'][$name])) {
442 $c_f = new CollectdColor('random');
443 $c_h = new CollectdColor($c_f);
445 $config['rrd_colors']['f_'.$code] = $c_f->as_string();
446 $config['rrd_colors']['h_'.$code] = $c_h->as_string();
448 return $config['rrd_colors'][$name];
452 * Draw RRD file based on it's structure
459 * @return Commandline to call RRDGraph in order to generate the final graph
461 function collectd_draw_rrd($host, $plugin, $pinst = null, $type, $tinst = null, $opts = array()) {
463 $timespan_def = null;
464 if (!isset($opts['timespan']))
465 $timespan_def = reset($config['timespan']);
466 else foreach ($config['timespan'] as &$ts)
467 if ($ts['name'] == $opts['timespan'])
470 if (!isset($opts['rrd_opts']))
471 $opts['rrd_opts'] = array();
472 if (isset($opts['logarithmic']) && $opts['logarithmic'])
473 array_unshift($opts['rrd_opts'], '-o');
476 $rrdfile = sprintf('%s/%s%s%s/%s%s%s', $host, $plugin, is_null($pinst) ? '' : '-', $pinst, $type, is_null($tinst) ? '' : '-', $tinst);
477 foreach ($config['datadirs'] as $datadir)
478 if (is_file($datadir.'/'.$rrdfile.'.rrd')) {
479 $rrdinfo = rrd_info($datadir.'/'.$rrdfile.'.rrd');
480 if (isset($rrdinfo['RRA']) && is_array($rrdinfo['RRA']))
486 if (is_null($rrdinfo))
493 reset($rrdinfo['RRA']);
495 while (list($k, $v) = each($rrdinfo['RRA'])) {
496 if ($v['cf'] == 'MAX')
498 else if ($v['cf'] == 'AVERAGE')
500 else if ($v['cf'] == 'MIN')
503 reset($rrdinfo['DS']);
504 while (list($k, $v) = each($rrdinfo['DS'])) {
505 if (strlen($k) > $l_max)
508 $graph[] = sprintf('DEF:%s_min=%s:%s:MIN', $k, $rrdinfo['filename'], $k);
510 $graph[] = sprintf('DEF:%s_avg=%s:%s:AVERAGE', $k, $rrdinfo['filename'], $k);
512 $graph[] = sprintf('DEF:%s_max=%s:%s:MAX', $k, $rrdinfo['filename'], $k);
514 if ($has_min && $has_max || $has_min && $has_avg || $has_avg && $has_max) {
516 reset($rrdinfo['DS']);
517 while (list($k, $v) = each($rrdinfo['DS'])) {
518 $graph[] = sprintf('LINE:%s_%s', $k, $has_min ? 'min' : 'avg');
519 $graph[] = sprintf('CDEF:%s_var=%s_%s,%s_%s,-', $k, $k, $has_max ? 'max' : 'avg', $k, $has_min ? 'min' : 'avg');
520 $graph[] = sprintf('AREA:%s_var#%s::STACK', $k, rrd_get_color($n++, false));
524 reset($rrdinfo['DS']);
526 while (list($k, $v) = each($rrdinfo['DS'])) {
527 $graph[] = sprintf('LINE1:%s_avg#%s:%s ', $k, rrd_get_color($n++, true), $k.substr(' ', 0, $l_max-strlen($k)));
528 if (isset($opts['tinylegend']) && $opts['tinylegend'])
531 $graph[] = sprintf('GPRINT:%s_avg:AVERAGE:%%5.1lf%%s Avg%s', $k, $has_max || $has_min || $has_avg ? ',' : "\\l");
533 $graph[] = sprintf('GPRINT:%s_min:MIN:%%5.1lf%%s Max%s', $k, $has_max || $has_avg ? ',' : "\\l");
535 $graph[] = sprintf('GPRINT:%s_max:MAX:%%5.1lf%%s Max%s', $k, $has_avg ? ',' : "\\l");
537 $graph[] = sprintf('GPRINT:%s_avg:LAST:%%5.1lf%%s Last\\l', $k);
540 $rrd_cmd = array(RRDTOOL, 'graph', '-', '-a', 'PNG', '-w', $config['rrd_width'], '-h', $config['rrd_height'], '-s', -1*$timespan_def['seconds'], '-t', $rrdfile);
541 $rrd_cmd = array_merge($rrd_cmd, $config['rrd_opts'], $opts['rrd_opts'], $graph);
544 for ($i = 1; $i < count($rrd_cmd); $i++)
545 $cmd .= ' '.escapeshellarg($rrd_cmd[$i]);
551 * Draw RRD file based on it's structure
559 * @return Commandline to call RRDGraph in order to generate the final graph
561 function collectd_draw_generic($timespan, $host, $plugin, $pinst = null, $type, $tinst = null) {
562 global $config, $GraphDefs;
563 $timespan_def = null;
564 foreach ($config['timespan'] as &$ts)
565 if ($ts['name'] == $timespan)
567 if (is_null($timespan_def))
568 $timespan_def = reset($config['timespan']);
570 if (!isset($GraphDefs[$type]))
573 $rrd_file = sprintf('%s/%s%s%s/%s%s%s', $host, $plugin, is_null($pinst) ? '' : '-', $pinst, $type, is_null($tinst) ? '' : '-', $tinst);
574 $rrd_cmd = array(RRDTOOL, 'graph', '-', '-a', 'PNG', '-w', $config['rrd_width'], '-h', $config['rrd_height'], '-s', -1*$timespan_def['seconds'], '-t', $rrd_file);
575 $rrd_cmd = array_merge($rrd_cmd, $config['rrd_opts']);
576 $rrd_args = $GraphDefs[$type];
578 foreach ($config['datadirs'] as $datadir) {
579 $file = $datadir.'/'.$rrd_file.'.rrd';
583 $file = str_replace(":", "\\:", $file);
584 $rrd_args = str_replace('{file}', $file, $rrd_args);
586 $rrdgraph = array_merge($rrd_cmd, $rrd_args);
588 for ($i = 1; $i < count($rrdgraph); $i++)
589 $cmd .= ' '.escapeshellarg($rrdgraph[$i]);
597 * Draw stack-graph for set of RRD files
598 * @opts Graph options like colors
599 * @sources List of array(name, file, ds)
600 * @return Commandline to call RRDGraph in order to generate the final graph
602 function collectd_draw_meta_stack(&$opts, &$sources) {
604 $timespan_def = null;
605 if (!isset($opts['timespan']))
606 $timespan_def = reset($config['timespan']);
607 else foreach ($config['timespan'] as &$ts)
608 if ($ts['name'] == $opts['timespan'])
611 if (!isset($opts['title']))
612 $opts['title'] = 'Unknown title';
613 if (!isset($opts['rrd_opts']))
614 $opts['rrd_opts'] = array();
615 if (!isset($opts['colors']))
616 $opts['colors'] = array();
617 if (isset($opts['logarithmic']) && $opts['logarithmic'])
618 array_unshift($opts['rrd_opts'], '-o');
620 $cmd = array(RRDTOOL, 'graph', '-', '-a', 'PNG', '-w', $config['rrd_width'], '-h', $config['rrd_height'], '-s', -1*$timespan_def['seconds'], '-t', $opts['title']);
621 $cmd = array_merge($cmd, $config['rrd_opts'], $opts['rrd_opts']);
624 foreach($sources as &$inst_data) {
625 $inst_name = $inst_data['name'];
626 $file = $inst_data['file'];
627 $ds = isset($inst_data['ds']) ? $inst_data['ds'] : 'value';
629 if (strlen($inst_name) > $max_inst_name)
630 $max_inst_name = strlen($inst_name);
635 $cmd[] = 'DEF:'.$inst_name.'_min='.$file.':'.$ds.':MIN';
636 $cmd[] = 'DEF:'.$inst_name.'_avg='.$file.':'.$ds.':AVERAGE';
637 $cmd[] = 'DEF:'.$inst_name.'_max='.$file.':'.$ds.':MAX';
638 $cmd[] = 'CDEF:'.$inst_name.'_nnl='.$inst_name.'_avg,UN,0,'.$inst_name.'_avg,IF';
640 $inst_data = end($sources);
641 $inst_name = $inst_data['name'];
642 $cmd[] = 'CDEF:'.$inst_name.'_stk='.$inst_name.'_nnl';
644 $inst_data1 = end($sources);
645 while (($inst_data0 = prev($sources)) !== false) {
646 $inst_name0 = $inst_data0['name'];
647 $inst_name1 = $inst_data1['name'];
649 $cmd[] = 'CDEF:'.$inst_name0.'_stk='.$inst_name0.'_nnl,'.$inst_name1.'_stk,+';
650 $inst_data1 = $inst_data0;
653 foreach($sources as &$inst_data) {
654 $inst_name = $inst_data['name'];
655 $legend = sprintf('%s', $inst_name);
656 while (strlen($legend) < $max_inst_name)
658 $number_format = isset($opts['number_format']) ? $opts['number_format'] : '%6.1lf';
660 if (isset($opts['colors'][$inst_name]))
661 $line_color = new CollectdColor($opts['colors'][$inst_name]);
663 $line_color = new CollectdColor('random');
664 $area_color = new CollectdColor($line_color);
667 $cmd[] = 'AREA:'.$inst_name.'_stk#'.$area_color->as_string();
668 $cmd[] = 'LINE1:'.$inst_name.'_stk#'.$line_color->as_string().':'.$legend;
669 if (!(isset($opts['tinylegend']) && $opts['tinylegend'])) {
670 $cmd[] = 'GPRINT:'.$inst_name.'_min:MIN:'.$number_format.' Min,';
671 $cmd[] = 'GPRINT:'.$inst_name.'_avg:AVERAGE:'.$number_format.' Avg,';
672 $cmd[] = 'GPRINT:'.$inst_name.'_max:MAX:'.$number_format.' Max,';
673 $cmd[] = 'GPRINT:'.$inst_name.'_avg:LAST:'.$number_format.' Last\\l';
678 for ($i = 1; $i < count($cmd); $i++)
679 $rrdcmd .= ' '.escapeshellarg($cmd[$i]);
684 * Draw stack-graph for set of RRD files
685 * @opts Graph options like colors
686 * @sources List of array(name, file, ds)
687 * @return Commandline to call RRDGraph in order to generate the final graph
689 function collectd_draw_meta_line(&$opts, &$sources) {
691 $timespan_def = null;
692 if (!isset($opts['timespan']))
693 $timespan_def = reset($config['timespan']);
694 else foreach ($config['timespan'] as &$ts)
695 if ($ts['name'] == $opts['timespan'])
698 if (!isset($opts['title']))
699 $opts['title'] = 'Unknown title';
700 if (!isset($opts['rrd_opts']))
701 $opts['rrd_opts'] = array();
702 if (!isset($opts['colors']))
703 $opts['colors'] = array();
704 if (isset($opts['logarithmic']) && $opts['logarithmic'])
705 array_unshift($opts['rrd_opts'], '-o');
707 $cmd = array(RRDTOOL, 'graph', '-', '-a', 'PNG', '-w', $config['rrd_width'], '-h', $config['rrd_height'], '-s', -1*$timespan_def['seconds'], '-t', $opts['title']);
708 $cmd = array_merge($cmd, $config['rrd_opts'], $opts['rrd_opts']);
711 foreach ($sources as &$inst_data) {
712 $inst_name = $inst_data['name'];
713 $file = $inst_data['file'];
714 $ds = isset($inst_data['ds']) ? $inst_data['ds'] : 'value';
716 if (strlen($inst_name) > $max_inst_name)
717 $max_inst_name = strlen($inst_name);
722 $cmd[] = 'DEF:'.$inst_name.'_min='.$file.':'.$ds.':MIN';
723 $cmd[] = 'DEF:'.$inst_name.'_avg='.$file.':'.$ds.':AVERAGE';
724 $cmd[] = 'DEF:'.$inst_name.'_max='.$file.':'.$ds.':MAX';
727 foreach ($sources as &$inst_data) {
728 $inst_name = $inst_data['name'];
729 $legend = sprintf('%s', $inst_name);
730 while (strlen($legend) < $max_inst_name)
732 $number_format = isset($opts['number_format']) ? $opts['number_format'] : '%6.1lf';
734 if (isset($opts['colors'][$inst_name]))
735 $line_color = new CollectdColor($opts['colors'][$inst_name]);
737 $line_color = new CollectdColor('random');
739 $cmd[] = 'LINE1:'.$inst_name.'_avg#'.$line_color->as_string().':'.$legend;
740 if (!(isset($opts['tinylegend']) && $opts['tinylegend'])) {
741 $cmd[] = 'GPRINT:'.$inst_name.'_min:MIN:'.$number_format.' Min,';
742 $cmd[] = 'GPRINT:'.$inst_name.'_avg:AVERAGE:'.$number_format.' Avg,';
743 $cmd[] = 'GPRINT:'.$inst_name.'_max:MAX:'.$number_format.' Max,';
744 $cmd[] = 'GPRINT:'.$inst_name.'_avg:LAST:'.$number_format.' Last\\l';
749 for ($i = 1; $i < count($cmd); $i++)
750 $rrdcmd .= ' '.escapeshellarg($cmd[$i]);