1 package Collectd::Graph::Type;
5 Collectd::Graph::Type - Base class for the collectd graphing infrastructure
9 # Copyright (C) 2008 Florian octo Forster <octo at verplant.org>
11 # This program is free software; you can redistribute it and/or modify it under
12 # the terms of the GNU General Public License as published by the Free Software
13 # Foundation; only version 2 of the License is applicable.
15 # This program is distributed in the hope that it will be useful, but WITHOUT
16 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
20 # You should have received a copy of the GNU General Public License along with
21 # this program; if not, write to the Free Software Foundation, Inc.,
22 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 use Carp (qw(confess cluck));
29 use URI::Escape (qw(uri_escape));
31 use Collectd::Graph::Common (qw($ColorCanvas $ColorFullBlue $ColorHalfBlue
40 This module serves as base class for more specialized classes realizing
43 =head1 MEMBER VARIABLES
45 As typical in Perl, a Collectd::Graph::Type object is a blessed hash reference.
46 Member variables are entries in that hash. Inheriting classes are free to add
47 additional entries. To set the member variable B<foo> to B<42>, do:
51 The following members control the behavior of Collectd::Graph::Type.
55 =item B<files> (array reference)
57 List of RRD files. Each file is passed as "ident", i.E<nbsp>e. broken up into
58 "hostname", "plugin", "type" and optionally "plugin_instance" and
59 "type_instance". Use the B<addFiles> method rather than setting this directly.
61 =item B<data_sources> (array reference)
63 List of data sources in the RRD files. If this is not given, the default
64 implementation of B<getDataSources> will use B<RRDs::info> to find out which
65 data sources are contained in the files.
67 =item B<ds_names> (array reference)
69 Names of the data sources as printed in the graph. Should be in the same order
70 as the data sources are returned by B<getDataSources>.
72 =item B<rrd_title> (string)
74 Title of the RRD graph. The title can contain "{hostname}", "{plugin}" and so
75 on which are replaced with their actual value. See the B<getTitle> method
78 =item B<rrd_opts> (array reference)
80 List of options directly passed to B<RRDs::graph>.
82 =item B<rrd_format> (string)
84 Format to use with B<GPRINT>. Defaults to C<%5.1lf>.
86 =item B<rrd_colors> (hash reference)
88 Mapping of data source names to colors, used when graphing the different data
89 sources. Colors are given in the typical hexadecimal RGB form, but without
90 leading "#", e.E<nbsp>g.:
92 $obj->{'rrd_colors'} = {foo => 'ff0000', bar => '00ff00'};
98 The following methods are used by the graphing front end and may be overwritten
99 to customize their behavior.
105 sub _get_ds_from_file
108 my $info = RRDs::info ($file);
112 if (!$info || (ref ($info) ne 'HASH'))
119 if (m/^ds\[([^\]]+)\]/)
138 } # _get_ds_from_file
143 my $obj = bless ({files => []}, $pkg);
153 =item B<addFiles> ({ I<ident> }, [...])
155 Adds the given idents (which are hash references) to the B<files> member
163 push (@{$obj->{'files'}}, @_);
166 =item B<getGraphsNum> ()
168 Returns the number of graphs that can be generated from the added files. By
169 default this number equals the number of files.
176 return (scalar @{$obj->{'files'}});
179 =item B<getDataSources> ()
181 Returns the names of the data sources. If the B<data_sources> member variable
182 is unset B<RRDs::info> is used to read that information from the first file.
183 Set the B<data_sources> member variable instead of overloading this method!
191 if (!defined $obj->{'data_sources'})
196 if (!@{$obj->{'files'}})
201 $ident = $obj->{'files'}[0];
202 $filename = ident_to_filename ($ident);
204 $obj->{'data_sources'} = _get_ds_from_file ($filename);
205 if (!$obj->{'data_sources'})
207 cluck ("_get_ds_from_file ($filename) failed.");
211 if (!defined $obj->{'data_sources'})
217 return (@{$obj->{'data_sources'}})
221 $obj->{'data_sources'};
226 =item B<getTitle> (I<$index>)
228 Returns the title of the I<$index>th B<graph> (not necessarily file!). If the
229 B<rrd_title> member variable is unset, a generic title is generated from the
230 ident. Otherwise the substrings "{hostname}", "{plugin}", "{plugin_instance}",
231 "{type}", and "{type_instance}" are replaced by their respective values.
239 my $title = $obj->{'rrd_title'};
243 return (ident_to_string ($ident));
246 my $hostname = $ident->{'hostname'};
247 my $plugin = $ident->{'plugin'};
248 my $plugin_instance = $ident->{'plugin_instance'};
249 my $type = $ident->{'type'};
250 my $type_instance = $ident->{'type_instance'};
253 if ((defined $type_instance) && (defined $plugin_instance))
255 $instance = "$plugin_instance/$type_instance";
257 elsif (defined $type_instance)
259 $instance = $type_instance;
261 elsif (defined $plugin_instance)
263 $instance = $plugin_instance;
267 $instance = 'no instance';
270 if (!defined $plugin_instance)
272 $plugin_instance = 'no instance';
275 if (!defined $type_instance)
277 $type_instance = 'no instance';
280 $title =~ s#{hostname}#$hostname#g;
281 $title =~ s#{plugin}#$plugin#g;
282 $title =~ s#{plugin_instance}#$plugin_instance#g;
283 $title =~ s#{type}#$type#g;
284 $title =~ s#{type_instance}#$type_instance#g;
285 $title =~ s#{instance}#$instance#g;
290 =item B<getRRDArgs> (I<$index>)
292 Return the arguments needed to generate the graph from the RRD file(s). If the
293 file has only one data source, this default implementation will generate that
294 typical min, average, max graph you probably know from temperatures and such.
295 If the RRD files have multiple data sources, the average of each data source is
296 printes as simple line.
305 my $ident = $obj->{'files'}[$index];
308 cluck ("Invalid index: $index");
311 my $filename = ident_to_filename ($ident);
313 my $rrd_opts = $obj->{'rrd_opts'} || [];
314 my $rrd_title = $obj->getTitle ($ident);
315 my $format = $obj->{'rrd_format'} || '%5.1lf';
317 my $rrd_colors = $obj->{'rrd_colors'};
318 my @ret = ('-t', $rrd_title, @$rrd_opts);
320 if (defined $obj->{'rrd_vertical'})
322 push (@ret, '-v', $obj->{'rrd_vertical'});
325 my $ds_names = $obj->{'ds_names'};
331 my $ds = $obj->getDataSources ();
334 confess ("obj->getDataSources failed.");
339 my @tmp = ('0000ff', 'ff0000', '00ff00', 'ff00ff', '00ffff', 'ffff00');
341 for (my $i = 0; $i < @$ds; $i++)
343 $rrd_colors->{$ds->[$i]} = $tmp[$i % @tmp];
347 for (my $i = 0; $i < @$ds; $i++)
350 my $ds_name = $ds->[$i];
352 # We need to escape colons for RRDTool..
354 $ds_name =~ s#:#\\:#g;
356 if (exists ($obj->{'scale'}))
358 my $scale = 0.0 + $obj->{'scale'};
360 "DEF:min${i}_raw=${f}:${ds_name}:MIN",
361 "DEF:avg${i}_raw=${f}:${ds_name}:AVERAGE",
362 "DEF:max${i}_raw=${f}:${ds_name}:MAX",
363 "CDEF:max${i}=max${i}_raw,$scale,*",
364 "CDEF:avg${i}=avg${i}_raw,$scale,*",
365 "CDEF:min${i}=min${i}_raw,$scale,*");
370 "DEF:min${i}=${f}:${ds_name}:MIN",
371 "DEF:avg${i}=${f}:${ds_name}:AVERAGE",
372 "DEF:max${i}=${f}:${ds_name}:MAX");
378 my $ds_name = $ds->[0];
379 my $color_fg = $rrd_colors->{$ds_name} || '000000';
380 my $color_bg = get_faded_color ($color_fg);
382 if ($ds_names->{$ds_name})
384 $ds_name = $ds_names->{$ds_name};
386 $ds_name =~ s#:#\\:#g;
389 "AREA:max0#${color_bg}",
390 "AREA:min0#${ColorCanvas}",
391 "LINE1:avg0#${color_fg}:${ds_name}",
392 "GPRINT:min0:MIN:${format} Min,",
393 "GPRINT:avg0:AVERAGE:${format} Avg,",
394 "GPRINT:max0:MAX:${format} Max,",
395 "GPRINT:avg0:LAST:${format} Last\\l");
399 for (my $i = 0; $i < @$ds; $i++)
401 my $ds_name = $ds->[$i];
402 my $color = $rrd_colors->{$ds_name} || '000000';
404 if ($ds_names->{$ds_name})
406 $ds_name = $ds_names->{$ds_name};
410 "LINE1:avg${i}#${color}:${ds_name}",
411 "GPRINT:min${i}:MIN:${format} Min,",
412 "GPRINT:avg${i}:AVERAGE:${format} Avg,",
413 "GPRINT:max${i}:MAX:${format} Max,",
414 "GPRINT:avg${i}:LAST:${format} Last\\l");
421 =item B<getGraphArgs> (I<$index>)
423 Returns the parameters that should be passed to the CGI script to generate the
424 I<$index>th graph. The returned string is already URI-encoded and will possibly
425 set the "hostname", "plugin", "plugin_instance", "type", and "type_instance"
428 The default implementation simply uses the ident of the I<$index>th file to
437 my $ident = $obj->{'files'}[$index];
440 for (qw(hostname plugin plugin_instance type type_instance))
442 if (defined ($ident->{$_}))
444 push (@args, uri_escape ($_) . '=' . uri_escape ($ident->{$_}));
448 return (join (';', @args));
451 =item B<getLastModified> ([I<$index>])
453 If I<$index> is not given, the modification time of all files is scanned and the most recent modification is returned. If I<$index> is given, only the files belonging to the I<$index>th graph will be considered.
460 my $index = @_ ? shift : -1;
466 for (@{$obj->{'files'}})
469 my $filename = ident_to_filename ($ident);
470 my @statbuf = stat ($filename);
477 if ($mtime < $statbuf[9])
479 $mtime = $statbuf[9];
485 my $ident = $obj->{'files'}[$index];
486 my $filename = ident_to_filename ($ident);
487 my @statbuf = stat ($filename);
489 $mtime = $statbuf[9];
503 L<Collectd::Graph::Type::GenericStacked>
505 =head1 AUTHOR AND LICENSE
507 Copyright (c) 2008 by Florian Forster
508 E<lt>octoE<nbsp>atE<nbsp>verplant.orgE<gt>. Licensed under the terms of the GNU
509 General Public License, VersionE<nbsp>2 (GPLv2).
513 # vim: set shiftwidth=2 softtabstop=2 tabstop=8 :