|
Server : Apache/2.2.2 (Fedora) System : Linux App1.pathumtani.go.th 2.6.20-1.2320.fc5smp #1 SMP Tue Jun 12 19:40:16 EDT 2007 i686 User : apache ( 48) PHP Version : 5.2.9 Disable Function : NONE Directory : /usr/share/munin/ |
Upload File : |
#!/usr/bin/perl -w
#
# Copyright (C) 2002-2004 Jimmy Olsen, Audun Ytterdal
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; version 2 dated June,
# 1991.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
# $Id: munin-graph.in 1142 2006-10-17 12:27:35Z tore $
$|=1;
use strict;
use IO::Socket;
use RRDs;
use Munin;
use POSIX qw(strftime);
use Digest::MD5;
use Getopt::Long;
use Time::HiRes;
my $graph_time= Time::HiRes::time;
my $DEBUG = 0;
my $VERSION = "1.2.5";
# Limit graphing to certain hosts and/or services
my @limit_hosts = ();
my @limit_services = ();
# RRDtool 1.2 requires \\: in comments
my $RRDkludge = $RRDs::VERSION < 1.2 ? '' : '\\';
# Force drawing of "graph no".
my $force_graphing = 0;
my $force_lazy = 1;
my $force_root = 0;
my $do_usage = 0;
my $do_version = 0;
my $cron = 0;
my $list_images = 0;
my $skip_locking = 0;
my $skip_stats = 0;
my $stdout = 0;
my $conffile = "/etc/munin/munin.conf";
my %draw = ("day" => 1, "week" => 1, "month" => 1, "year" => 1, "sumyear" => 1, "sumweek" => 1);
my $log = new IO::Handle;
# Get options
$do_usage=1 unless
GetOptions ( "force!" => \$force_graphing,
"lazy!" => \$force_lazy,
"force-root!" => \$force_root,
"host=s" => \@limit_hosts,
"service=s" => \@limit_services,
"config=s" => \$conffile,
"stdout!" => \$stdout,
"day!" => \$draw{'day'},
"week!" => \$draw{'week'},
"month!" => \$draw{'month'},
"year!" => \$draw{'year'},
"sumweek!" => \$draw{'sumweek'},
"sumyear!" => \$draw{'sumyear'},
"list-images!" => \$list_images,
"skip-locking!"=> \$skip_locking,
"skip-stats!" => \$skip_stats,
"debug!" => \$DEBUG,
"version!" => \$do_version,
"cron!" => \$cron,
"help" => \$do_usage );
if ($do_usage)
{
print "Usage: $0 [options]
Options:
--[no]force Force drawing of graphs that are not usually
drawn due to options in the config file. [--noforce]
--[no]force-root Force running, even as root. [--noforce-root]
--[no]lazy Only redraw graphs when needed. [--lazy]
--help View this message.
--version View version information.
--debug View debug messages.
--[no]cron Behave as expected when run from cron. (Used internally
in Munin.)
--service <service> Limit graphed services to <service>. Multiple --service
options may be supplied.
--host <host> Limit graphed hosts to <host>. Multiple --host options
may be supplied.
--config <file> Use <file> as configuration file. [/etc/munin/munin.conf]
--[no]list-images List the filenames of the images created.
[--nolist-images]
--[no]day Create day-graphs. [--day]
--[no]week Create week-graphs. [--week]
--[no]month Create month-graphs. [--month]
--[no]year Create year-graphs. [--year]
--[no]sumweek Create summarised week-graphs. [--summweek]
--[no]sumyear Create summarised year-graphs. [--sumyear]
";
exit 0;
}
if ($do_version)
{
print "munin-graph version $VERSION.\n";
print "Written by Audun Ytterdal, Jimmy Olsen, Tore Anderson / Linpro AS\n";
print "\n";
print "Copyright (C) 2002-2004\n";
print "This is free software released under the GNU Public License. There is NO\n";
print "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n";
exit 0;
}
if ($> == 0 and !$force_root)
{
print "You are running this program as root, which is neither smart nor necessary.
If you really want to run it as root, use the --force-root option. Else, run
it as the user \"munin\". Aborting.\n\n";
exit (1);
}
my $config= &munin_config ($conffile);
if (&munin_get ($config, "graph_strategy", "cron") ne "cron" and $cron)
{ # We're run from cron, but munin.conf says we use dynamic graph generation
exit 0;
}
munin_runlock("$config->{rundir}/munin-graph.lock") unless $skip_locking;
unless ($skip_stats)
{
open (STATS,">$config->{dbdir}/munin-graph.stats.tmp") or logger("Unable to open $config->{dbdir}/munin-graph.stats.tmp");
}
logger("Starting munin-graph");
my @COLOUR = ("#22ff22", "#0022ff", "#ff0000", "#00aaaa", "#ff00ff",
"#ffa500", "#cc0000", "#0000cc", "#0080C0", "#8080C0", "#FF0080",
"#800080", "#688e23", "#408080", "#808000", "#000000", "#00FF00",
"#0080FF", "#FF8000", "#800000", "#FB31FB");
my $range_colour = "#22ff22";
my $single_colour = "#00aa00";
my %times = (
"day" => "-30h",
"week" => "-8d",
"month" => "-33d",
"year" => "-400d");
my %resolutions = (
"day" => "300",
"week" => "1500",
"month" => "7200",
"year" => "86400");
my %sumtimes = ( # time => [ label, seconds-in-period ]
"week" => ["hour", 12],
"year" => ["day", 288]
);
for my $key ( keys %{$config->{domain}}) {
my $domain_time= Time::HiRes::time;
mkdir "$config->{htmldir}/$key",0777;
logger("Processing domain: $key");
&process_domain($key);
$domain_time = sprintf ("%.2f",(Time::HiRes::time - $domain_time));
logger("Processed domain: $key ($domain_time sec)");
print STATS "GD|$key|$domain_time\n" unless $skip_stats;
}
$graph_time = sprintf ("%.2f",(Time::HiRes::time - $graph_time));
logger("Munin-graph finished ($graph_time sec)");
print STATS "GT|total|$graph_time\n" unless $skip_stats;
rename ("$config->{dbdir}/munin-graph.stats.tmp", "$config->{dbdir}/munin-graph.stats");
close STATS unless $skip_stats;
close $log;
sub process_domain {
my ($domain) = @_;
for my $key ( keys %{$config->{domain}->{$domain}->{node}}) {
my $node_time= Time::HiRes::time;
process_node($domain,$key ,$config->{domain}->{$domain}->{node}->{$key} );
$node_time = sprintf ("%.2f",(Time::HiRes::time - $node_time));
logger ("Processed node: $key ($node_time sec)");
print STATS "GN|$domain|$key|$node_time\n" unless $skip_stats;
}
}
sub get_title {
my $node = shift;
my $service = shift;
my $scale = shift;
return ($node->{client}->{$service}->{'graph_title'}?
$node->{client}->{$service}->{'graph_title'}:$service) .
" - by $scale";
}
sub get_custom_graph_args
{
my $node = shift;
my $service = shift;
my $result = [];
if ($node->{client}->{$service}->{graph_args}) {
push @$result, split /\s/,$node->{client}->{$service}->{graph_args};
return $result;
}
else
{
return undef;
}
}
sub get_vlabel
{
my $node = shift;
my $service = shift;
my $scale = shift;
if ($node->{client}->{$service}->{graph_vlabel}) {
(my $res = $node->{client}->{$service}->{graph_vlabel}) =~ s/\$\{graph_period\}/$scale/g;
return $res;
}
elsif ($node->{client}->{$service}->{graph_vtitle})
{
return $node->{client}->{$service}->{graph_vtitle};
}
return undef;
}
sub should_scale
{
my $node = shift;
my $service = shift;
if (defined $node->{client}->{$service}->{graph_scale})
{
return &munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, 1);
}
elsif (defined $node->{client}->{$service}->{graph_noscale})
{
return ! &munin_get_bool_val ($node->{client}->{$service}->{graph_noscale}, 0);
}
return 1;
}
sub get_header {
my $node = shift;
my $config = shift;
my $domain = shift;
my $host = shift;
my $service = shift;
my $scale = shift;
my $sum = shift;
my $result = [];
my $tmp_field;
# Picture filename
push @$result, &munin_get_picture_filename ($config, $domain, $host, $service, $scale, $sum||undef);
# Title
push @$result, ("--title", &get_title ($node, $service, $scale));
# When to start the graph
push @$result, "--start",$times{$scale};
# Custom graph args, vlabel and graph title
if (defined ($tmp_field = &get_custom_graph_args ($node, $service))) {
push (@$result, @{$tmp_field});
}
if (defined ($tmp_field = &get_vlabel ($node, $service, munin_get ($config, "graph_period", "second", $domain, $host, $service)))) {
push @$result, ("--vertical-label", $tmp_field);
}
push @$result,"--height", &munin_get ($config, "graph_height", "175", $domain, $host, $service);
push @$result,"--width", &munin_get ($config, "graph_width", "400", $domain, $host, $service);
push @$result,"--imgformat", "PNG";
push @$result,"--lazy" if ($force_lazy);
push (@$result, "--units-exponent", "0")
if (! &should_scale ($node, $service));
return $result;
}
sub get_sum_command
{
my $node = shift;
my $service = shift;
my $field = shift;
if (defined $node->{client}->{$service}->{$field.".special_sum"})
{
return $node->{client}->{$service}->{$field.".special_sum"};
}
elsif (defined $node->{client}->{$service}->{$field.".sum"})
{
return $node->{client}->{$service}->{$field.".sum"};
}
return undef;
}
sub get_stack_command
{
my $node = shift;
my $service = shift;
my $field = shift;
if (defined $node->{client}->{$service}->{$field.".special_stack"})
{
return $node->{client}->{$service}->{$field.".special_stack"};
}
elsif (defined $node->{client}->{$service}->{$field.".stack"})
{
return $node->{client}->{$service}->{$field.".stack"};
}
return undef;
}
sub expand_specials
{
my $node = shift;
my $config = shift;
my $domain = shift;
my $host = shift;
my $service = shift;
my $preproc = shift;
my $order = shift;
my $single = shift;
my $result = [];
my $fieldnum = 0;
for my $field (@$order) { # Search for 'specials'...
if ($field =~ /^-(.+)$/)
{
$field = $1;
unless (defined $node->{client}->{$service}->{$field.".graph"} or
defined $node->{client}->{$service}->{$field.".skipdraw"})
{
$node->{client}->{$service}->{$field.".graph"} = "no";
}
}
$fieldnum++;
my $tmp_field;
if (defined ($tmp_field = &get_stack_command ($node, $service, $field)))
{
print "DEBUG: Doing special_stack...\n" if $DEBUG;
my @spc_stack = ();
foreach my $pre (split (/\s+/, $tmp_field))
{
(my $name = $pre) =~ s/=.+//;
if (!@spc_stack)
{
$node->{client}->{$service}->{$name.".draw"} = $node->{client}->{$service}->{$field.".draw"};
$node->{client}->{$service}->{$field.".process"} = "no";
}
else
{
$node->{client}->{$service}->{$name.".draw"} = "STACK";
}
push (@spc_stack, $name);
push (@$preproc, $pre);
push @$result, "$name.label";
push @$result, "$name.draw";
push @$result, "$name.cdef";
$node->{client}->{$service}->{$name.".label"} = $name;
$node->{client}->{$service}->{$name.".cdef"} = "$name,UN,0,$name,IF";
if (exists $node->{client}->{$service}->{$field.".cdef"} and !exists $node->{client}->{$service}->{$name.".onlynullcdef"})
{
print "NotOnlynullcdef ($field)...\n" if $DEBUG;
$node->{client}->{$service}->{$name.".cdef"} .= "," .
$node->{client}->{$service}->{$field.".cdef"};
$node->{client}->{$service}->{$name.".cdef"} =~ s/\b$field\b/$name/g;
}
else
{
print "Onlynullcdef ($field)...\n" if $DEBUG;
$node->{client}->{$service}->{$name.".onlynullcdef"} = 1;
push @$result, "$name.onlynullcdef";
}
}
}
elsif (defined ($tmp_field = &get_sum_command ($node, $service, $field)))
{
my @spc_stack = ();
my $last_name = "";
print "DEBUG: Doing special_sum...\n" if $DEBUG;
if (@$order == 1 or
@$order == 2 && $node->{client}->{$service}->{$field.".negative"})
{
$single = 1;
}
foreach my $pre (split (/\s+/, $tmp_field))
{
(my $path = $pre) =~ s/.+=//;
my $name = "z".$fieldnum."_".scalar (@spc_stack);
$last_name = $name;
$node->{client}->{$service}->{$name.".cdef"} = "$name,UN,0,$name,IF";
$node->{client}->{$service}->{$name.".graph"} = "no";
$node->{client}->{$service}->{$name.".label"} = $name;
push @$result, "$name.cdef";
push @$result, "$name.graph";
push @$result, "$name.label";
push (@spc_stack, $name);
push (@$preproc, "$name=$pre");
}
$node->{client}->{$service}->{$last_name.".cdef"} .=
"," . join (',+,', @spc_stack[0 .. @spc_stack-2]) . ',+';
if (exists $node->{client}->{$service}->{$field.".cdef"} and
length $node->{client}->{$service}->{$field.".cdef"})
{ # Oh bugger...
my $tc = $node->{client}->{$service}->{$field.".cdef"};
print "Oh bugger...($field)...\n" if $DEBUG;
$tc =~ s/\b$field\b/$node->{client}->{$service}->{$last_name.".cdef"}/;
$node->{client}->{$service}->{$last_name.".cdef"} = $tc;
}
$node->{client}->{$service}->{$field.".process"} = "no";
$node->{client}->{$service}->{$last_name.".draw"} = $node->{client}->{$service}->{$field.".draw"};
$node->{client}->{$service}->{$last_name.".label"} = $node->{client}->{$service}->{$field.".label"};
if (defined $node->{client}->{$service}->{$field.".graph"})
{
$node->{client}->{$service}->{$last_name.".graph"} = $node->{client}->{$service}->{$field.".graph"};
}
else
{
$node->{client}->{$service}->{$last_name.".graph"} = "yes";
}
if (defined $node->{client}->{$service}->{$field.".negative"})
{
$node->{client}->{$service}->{$last_name.".negative"} = $node->{client}->{$service}->{$field.".negative"};;
}
$node->{client}->{$service}->{$field.".realname"} = $last_name;
print "Setting node->{client}->{$service}->{$field} -> realname = $last_name...\n" if $DEBUG;
}
elsif (defined $node->{client}->{$service}->{$field.".negative"})
{
my $nf = $node->{client}->{$service}->{$field.".negative"};
unless (defined $node->{client}->{$service}->{$nf.".graph"} or
defined $node->{client}->{$service}->{$nf.".skipdraw"})
{
$node->{client}->{$service}->{$nf.".graph"} = "no";
}
}
}
return $result;
}
sub single_value
{
my $node = shift;
my $config = shift;
my $domain = shift;
my $host = shift;
my $service = shift;
my $field = shift;
my $order = shift;
return 1 if @$order == 1;
return 1 if (@$order == 2 and $node->{client}->{$service}->{$field.".negative"});
my $graphable = 0;
if (!defined $node->{client}->{$service}->{"graphable"})
{
# foreach my $field (keys %{$node->{client}->{$service}})
foreach my $field (&munin_get_field_order ($node, $config, $domain, $host, $service))
{
print "DEBUG: single_value: Checking field \"$field\".\n" if $DEBUG;
if ($field =~ /^([^\.]+)\.label/ or $field =~ /=/)
{
$graphable++ if &munin_draw_field ($node, $service, $1);
}
}
$node->{client}->{$service}->{"graphable"} = $graphable;
}
return 1 if ($node->{client}->{$service}->{"graphable"} == 1);
return 0;
}
sub get_field_name
{
my $name = shift;
$name = substr (Digest::MD5::md5_hex ($name), -15)
if (length $name > 15);
return $name;
}
sub process_field {
my $node = shift;
my $service = shift;
my $field = shift;
return (&munin_get_bool_val ($node->{client}->{$service}->{$field.".process"}, 1));
}
sub process_node {
my ($domain,$name,$node) = @_;
# See if we should skip it because of command-line arguments
return if (@limit_hosts and not grep (/^$name$/, @limit_hosts));
# Make my graphs
logger ("Processing $name") if $DEBUG;
for my $service (keys %{$node->{client}}) {
my $service_time= Time::HiRes::time;
my $lastupdate = 0;
my $now = time;
my $fnum = 0;
my @rrd;
my @added = ();
# See if we should skip the service
next if (&skip_service ($node, $service));
my $field_count = 0;
my $max_field_len = 0;
my @field_order = ();
my $rrdname;
my $force_single_value;
@field_order = @{&munin_get_field_order ($node, $config, $domain, $name, $service, \$force_single_value)};
# Array to keep 'preprocess'ed fields.
my @rrd_preprocess = ();
print "DEBUG: Expanding specials \"", join "\",\"", @field_order, "\".\n" if $DEBUG;
@added = @{&expand_specials ($node, $config, $domain, $name, $service, \@rrd_preprocess, \@field_order)};
@field_order = (@rrd_preprocess, @field_order);
print "DEBUG: Checking field lengths \"", join "\",\"", (@rrd_preprocess, @field_order), "\".\n" if $DEBUG;
# Get max label length
$max_field_len = &munin_get_max_label_length ($node, $config, $domain, $name, $service, \@field_order);
my $global_headers = ($max_field_len > 16);
# Array to keep negative data until we're finished with positive.
my @rrd_negatives = ();
my $filename = "unknown";
my %total_pos;
my %total_neg;
print "DEBUG: Treating fields \"", join "\",\"", @field_order, "\".\n" if $DEBUG;
for my $field (@field_order) {
my $path = undef;
if ($field =~ s/=(.+)//)
{
$path = $1;
}
next unless &process_field ($node, $service, $field);
print "DEBUG: Processing field \"$field\".\n" if $DEBUG;
if ($field_count == 0 and munin_get ($config, "draw", "LINE2", $domain, $name, $service, $field) eq "STACK")
{ # Illegal -- first field is a STACK
logger ("ERROR: First field (\"$field\") of graph \"$domain\" :: \"$name\" :: \"$service\" is STACK. STACK can only be drawn after a LINEx or AREA.");
}
# Getting name of rrd file
$filename = &munin_get_rrd_filename ($node, $config, $domain, $name, $service, $field, $path);
my $update = RRDs::last ($filename);
$update = 0 if ! defined $update;
if ($update > $lastupdate)
{
$lastupdate = $update;
}
my $rrdfield = ($node->{client}->{$service}->{$field.".rrdfield"} || "42");
my $single_value = $force_single_value || &single_value ($node, $config, $domain, $name, $service, $field, \@field_order);
my $has_negative = exists $node->{client}->{$service}->{$field.".negative"};
# Trim the fieldname to make room for other field names.
$rrdname = &get_field_name ($field);
if ($rrdname ne $field) # A change was made
{
set_cdef_name ($node->{client}->{$service}, $field, $rrdname);
}
push (@rrd, "DEF:g$rrdname=" .
$filename . ":" . $rrdfield . ":AVERAGE");
push (@rrd, "DEF:i$rrdname=" .
$filename . ":" . $rrdfield . ":MIN");
push (@rrd, "DEF:a$rrdname=" .
$filename . ":" . $rrdfield . ":MAX");
if (exists $node->{client}->{$service}->{$field.".onlynullcdef"} and $node->{client}->{$service}->{$field.".onlynullcdef"})
{
push (@rrd, "CDEF:c$rrdname=g$rrdname" . (($now-$update)>900 ? ",POP,UNKN" : ""));
}
if (($node->{client}->{$service}->{$field.".type"}||"GAUGE") ne "GAUGE" and graph_by_minute ($config, $domain, $name, $service))
{
push (@rrd, &expand_cdef($node->{client}->{$service}, \$rrdname, "$field,60,*"));
}
if ($node->{client}->{$service}->{$field.".cdef"})
{
push (@rrd, &expand_cdef($node->{client}->{$service}, \$rrdname, $node->{client}->{$service}->{$field.".cdef"}));
push (@rrd, "CDEF:c$rrdname=g$rrdname");
print "DEBUG: Field name after cdef set to $rrdname\n" if $DEBUG;
}
elsif (!(exists $node->{client}->{$service}->{$field.".onlynullcdef"} and $node->{client}->{$service}->{$field.".onlynullcdef"}))
{
push (@rrd, "CDEF:c$rrdname=g$rrdname" . (($now-$update)>900 ? ",POP,UNKN" : ""));
}
next unless &munin_draw_field ($node, $service, $field);
print "DEBUG: Drawing field \"$field\".\n" if $DEBUG;
if ($single_value) # Only one field. Do min/max range.
{
push (@rrd, "CDEF:min_max_diff=a$rrdname,i$rrdname,-");
push (@rrd, "CDEF:re_zero=min_max_diff,min_max_diff,-")
unless ($node->{client}->{$service}->{$field.".negative"});
push (@rrd, "AREA:i$rrdname#ffffff");
push (@rrd, "STACK:min_max_diff$range_colour");
push (@rrd, "LINE2:re_zero#000000")
unless ($node->{client}->{$service}->{$field.".negative"});
}
if ($has_negative and !@rrd_negatives) # Push "global" headers...
{
push (@rrd, "COMMENT:" . (" " x $max_field_len));
push (@rrd, "COMMENT:Cur (-/+)");
push (@rrd, "COMMENT:Min (-/+)");
push (@rrd, "COMMENT:Avg (-/+)");
push (@rrd, "COMMENT:Max (-/+) \\j");
}
elsif ($global_headers == 1)
{
push (@rrd, "COMMENT:" . (" " x $max_field_len));
push (@rrd, "COMMENT: Cur$RRDkludge:");
push (@rrd, "COMMENT:Min$RRDkludge:");
push (@rrd, "COMMENT:Avg$RRDkludge:");
push (@rrd, "COMMENT:Max$RRDkludge: \\j");
$global_headers++;
}
my $custom_colour = $node->{client}->{$service}->{$field.".colour"};
$custom_colour = "#" . $custom_colour if $custom_colour;
push (@rrd, ($node->{client}->{$service}->{$field.".draw"} || "LINE2") .
":g$rrdname" .
($custom_colour || ($single_value ? $single_colour : $COLOUR[$field_count++%@COLOUR])) . ":" .
(escape ($node->{client}->{$service}->{"$field.label"}) || escape ($field))
. (" " x ($max_field_len + 1 -
length ($node->{client}->{$service}->{"$field.label"} || $field))));
# Check for negative fields (typically network traffic)
if ($has_negative)
{
my $negfield = &orig_to_cdef ($node->{client}->{$service}, $node->{client}->{$service}->{$field.".negative"});
print "DEBUG: negfield = $negfield\n" if $DEBUG;
if (exists $node->{client}->{$service}->{$negfield.".realname"})
{
$negfield = $node->{client}->{$service}->{$negfield.".realname"};
}
if (!@rrd_negatives) # zero-line, to redraw zero afterwards.
{
push (@rrd_negatives, "CDEF:re_zero=g$negfield,UN,0,0,IF");
}
push (@rrd_negatives, "CDEF:ng$negfield=g$negfield,-1,*");
if ($single_value) # Only one field. Do min/max range.
{
push (@rrd, "CDEF:neg_min_max_diff=i$negfield,a$negfield,-");
push (@rrd, "CDEF:ni$negfield=i$negfield,-1,*");
push (@rrd, "AREA:ni$negfield#ffffff");
push (@rrd, "STACK:neg_min_max_diff$range_colour");
}
push (@rrd_negatives, ($node->{client}->{$service}->{$negfield.".draw"} || "LINE2") .
":ng$negfield" .
((defined $single_value and $single_value) ? $single_colour : $COLOUR[($field_count-1)%@COLOUR]));
# Draw HRULEs
my $linedef = munin_get ($config, "line", undef, $domain, $name, $service, $node->{client}->{$service}->{$field.".negative"});
if ($linedef)
{
my ($number, $colour, $label) = split (/:/, $linedef, 3);
push (@rrd_negatives, "HRULE:".$number.
($colour ? "#$colour" :
((defined $single_value and $single_value) ? "#ff0000" : $COLOUR[($field_count-1)%@COLOUR]))
);
}
elsif ($node->{client}->{$service}->{"$negfield.warn"})
{
push (@rrd_negatives, "HRULE:".$node->{client}->{$service}->{$node->{client}->{$service}->{$field.".negative"}.".warn"}.
((defined $single_value and $single_value) ? "#ff0000" : $COLOUR[($field_count-1)%@COLOUR]));
}
push (@rrd, "GPRINT:c$negfield:LAST:%6.2lf" . (munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, 1)?"%s":"") . "/\\g");
push (@rrd, "GPRINT:c$rrdname:LAST:%6.2lf" . (munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, 1)?"%s":"") . "");
push (@rrd, "GPRINT:i$negfield:MIN:%6.2lf" . (munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, 1)?"%s":"") . "/\\g");
push (@rrd, "GPRINT:i$rrdname:MIN:%6.2lf" . (munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, 1)?"%s":"") . "");
push (@rrd, "GPRINT:g$negfield:AVERAGE:%6.2lf" . (munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, 1)?"%s":"") . "/\\g");
push (@rrd, "GPRINT:g$rrdname:AVERAGE:%6.2lf" . (munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, 1)?"%s":"") . "");
push (@rrd, "GPRINT:a$negfield:MAX:%6.2lf" . (munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, 1)?"%s":"") . "/\\g");
push (@rrd, "GPRINT:a$rrdname:MAX:%6.2lf" . (munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, 1)?"%s":"") . "\\j");
push (@{$total_pos{'min'}}, "i$rrdname");
push (@{$total_pos{'avg'}}, "g$rrdname");
push (@{$total_pos{'max'}}, "a$rrdname");
push (@{$total_neg{'min'}}, "i$negfield");
push (@{$total_neg{'avg'}}, "g$negfield");
push (@{$total_neg{'max'}}, "a$negfield");
}
else
{
push (@rrd, "COMMENT: Cur$RRDkludge:") unless $global_headers;
push (@rrd, "GPRINT:c$rrdname:LAST:%6.2lf" . (munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, "yes")?"%s":"") . "");
push (@rrd, "COMMENT: Min$RRDkludge:") unless $global_headers;
push (@rrd, "GPRINT:i$rrdname:MIN:%6.2lf" . (munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, 1)?"%s":"") . "");
push (@rrd, "COMMENT: Avg$RRDkludge:") unless $global_headers;
push (@rrd, "GPRINT:g$rrdname:AVERAGE:%6.2lf" . (munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, 1)?"%s":"") . "");
push (@rrd, "COMMENT: Max$RRDkludge:") unless $global_headers;
push (@rrd, "GPRINT:a$rrdname:MAX:%6.2lf" . (munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, 1)?"%s":"") . "\\j");
push (@{$total_pos{'min'}}, "i$rrdname");
push (@{$total_pos{'avg'}}, "g$rrdname");
push (@{$total_pos{'max'}}, "a$rrdname");
}
# Draw HRULEs
my $linedef = munin_get ($config, "line", undef, $domain, $name, $service, $field);
if ($linedef)
{
my ($number, $colour, $label) = split (/:/, $linedef, 3);
$label =~ s/:/\\:/g if defined $label;
push (@rrd, "HRULE:".$number.
($colour ? "#$colour" :
((defined $single_value and $single_value) ? "#ff0000" : $COLOUR[($field_count-1)%@COLOUR])) .
((defined $label and length ($label)) ? ":$label" : ""),
"COMMENT: \\j"
);
}
elsif ($node->{client}->{$service}->{"$field.warn"})
{
push (@rrd, "HRULE:".$node->{client}->{$service}->{"$field.warn"}.($single_value ? "#ff0000" : $COLOUR[($field_count-1)%@COLOUR]));
}
}
if (@rrd_negatives)
{
push (@rrd, @rrd_negatives);
push (@rrd, "LINE2:re_zero#000000"); # Redraw zero.
if (exists $node->{client}->{$service}->{graph_total} and
exists $total_pos{'min'} and exists $total_neg{'min'} and
@{$total_pos{'min'}} and @{$total_neg{'min'}})
{
push (@rrd, "CDEF:ipostotal=".join (",", map { "$_,UN,0,$_,IF" } @{$total_pos{'min'}}).(",+" x (@{$total_pos{'min'}}-1)));
push (@rrd, "CDEF:gpostotal=".join (",", map { "$_,UN,0,$_,IF" } @{$total_pos{'avg'}}).(",+" x (@{$total_pos{'avg'}}-1)));
push (@rrd, "CDEF:apostotal=".join (",", map { "$_,UN,0,$_,IF" } @{$total_pos{'max'}}).(",+" x (@{$total_pos{'max'}}-1)));
push (@rrd, "CDEF:inegtotal=".join (",", map { "$_,UN,0,$_,IF" } @{$total_neg{'min'}}).(",+" x (@{$total_neg{'min'}}-1)));
push (@rrd, "CDEF:gnegtotal=".join (",", map { "$_,UN,0,$_,IF" } @{$total_neg{'avg'}}).(",+" x (@{$total_neg{'avg'}}-1)));
push (@rrd, "CDEF:anegtotal=".join (",", map { "$_,UN,0,$_,IF" } @{$total_neg{'max'}}).(",+" x (@{$total_neg{'max'}}-1)));
push (@rrd, "CDEF:dpostotal=ipostotal,UN,ipostotal,UNKN,IF");
push (@rrd, "LINE1:dpostotal#000000:" . $node->{client}->{$service}->{graph_total} . (" " x ($max_field_len - length ($node->{client}->{$service}->{graph_total}) + 1)));
push (@rrd, "GPRINT:gnegtotal:LAST:%6.2lf" . (munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, 1)?"%s":"") . "/\\g");
push (@rrd, "GPRINT:gpostotal:LAST:%6.2lf" . (munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, 1)?"%s":"") . "");
push (@rrd, "GPRINT:inegtotal:MIN:%6.2lf" . (munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, 1)?"%s":"") . "/\\g");
push (@rrd, "GPRINT:ipostotal:MIN:%6.2lf" . (munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, 1)?"%s":"") . "");
push (@rrd, "GPRINT:gnegtotal:AVERAGE:%6.2lf" . (munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, 1)?"%s":"") . "/\\g");
push (@rrd, "GPRINT:gpostotal:AVERAGE:%6.2lf" . (munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, 1)?"%s":"") . "");
push (@rrd, "GPRINT:anegtotal:MAX:%6.2lf" . (munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, 1)?"%s":"") . "/\\g");
push (@rrd, "GPRINT:apostotal:MAX:%6.2lf" . (munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, 1)?"%s":"") . "\\j");
}
}
elsif (exists $node->{client}->{$service}->{graph_total} and exists $total_pos{'min'} and @{$total_pos{'min'}})
{
push (@rrd, "CDEF:ipostotal=".join (",", map { "$_,UN,0,$_,IF" } @{$total_pos{'min'}}).(",+" x (@{$total_pos{'min'}}-1)));
push (@rrd, "CDEF:gpostotal=".join (",", map { "$_,UN,0,$_,IF" } @{$total_pos{'avg'}}).(",+" x (@{$total_pos{'avg'}}-1)));
push (@rrd, "CDEF:apostotal=".join (",", map { "$_,UN,0,$_,IF" } @{$total_pos{'max'}}).(",+" x (@{$total_pos{'max'}}-1)));
push (@rrd, "CDEF:dpostotal=ipostotal,UN,ipostotal,UNKN,IF");
push (@rrd, "LINE1:dpostotal#000000:" . $node->{client}->{$service}->{graph_total} . (" " x ($max_field_len - length ($node->{client}->{$service}->{graph_total}) + 1)));
push (@rrd, "COMMENT: Cur$RRDkludge:") unless $global_headers;
push (@rrd, "GPRINT:gpostotal:LAST:%6.2lf" . (munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, 1)?"%s":"") . "");
push (@rrd, "COMMENT: Min$RRDkludge:") unless $global_headers;
push (@rrd, "GPRINT:ipostotal:MIN:%6.2lf" . (munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, 1)?"%s":"") . "");
push (@rrd, "COMMENT: Avg$RRDkludge:") unless $global_headers;
push (@rrd, "GPRINT:gpostotal:AVERAGE:%6.2lf" . (munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, 1)?"%s":"") . "");
push (@rrd, "COMMENT: Max$RRDkludge:") unless $global_headers;
push (@rrd, "GPRINT:apostotal:MAX:%6.2lf" . (munin_get_bool_val ($node->{client}->{$service}->{graph_scale}, 1)?"%s":"") . "\\j");
}
for my $time (keys %times) {
next unless ($draw{$time});
my @complete = ();
if ($RRDkludge) {
push (@complete,
'--font' ,'LEGEND:7:/usr/share/munin/VeraMono.ttf',
'--font' ,'UNIT:7:/usr/share/munin/VeraMono.ttf',
'--font' ,'AXIS:7:/usr/share/munin/VeraMono.ttf');
}
logger ("Processing $name -> $time") if $DEBUG;
# Do the header (title, vtitle, size, etc...)
push @complete, @{&get_header ($node, $config, $domain, $name, $service, $time)};
push @complete, @rrd;
push (@complete, "COMMENT:Last update$RRDkludge: " . RRDescape(scalar localtime($lastupdate)) . "\\r");
if (time - 300 < $lastupdate)
{
push @complete, "--end",(int($lastupdate/$resolutions{$time}))*$resolutions{$time};
}
print "\n\nrrdtool \"graph\" \"", join ("\"\n\t\"",@complete), "\"\n" if $DEBUG;
RRDs::graph (@complete);
if (my $ERROR = RRDs::error) {
logger ("Unable to graph $filename: $ERROR");
}
elsif ($list_images) # Command-line option to list images created
{
print &munin_get_picture_filename ($config, $domain, $name, $service, $time), "\n";
}
}
if (&munin_get_bool_val ($node->{client}->{$service}->{"graph_sums"}, 0))
{
foreach my $time (keys %sumtimes)
{
next unless ($draw{"sum".$time});
my @rrd_sum;
push @rrd_sum, @{&get_header ($node, $config, $domain, $name, $service, $time, 1)};
if (time - 300 < $lastupdate)
{
push @rrd_sum, "--end",(int($lastupdate/$resolutions{$time}))*$resolutions{$time};
}
push @rrd_sum, @rrd;
push (@rrd_sum, "COMMENT:Last update$RRDkludge: " . RRDescape(scalar localtime($lastupdate)) . "\\r");
my $labelled = 0;
my @defined = ();
for (my $index = 0; $index <= $#rrd_sum; $index++)
{
if ($rrd_sum[$index] =~ /^(--vertical-label|-v)$/)
{
(my $label = $node->{client}->{$service}->{graph_vlabel}) =~ s/\$\{graph_period\}/$sumtimes{$time}[0]/g;
splice (@rrd_sum, $index, 2, ("--vertical-label", $label));
$index++;
$labelled++;
}
elsif ($rrd_sum[$index] =~ /^(LINE[123]|STACK|AREA|GPRINT):([^#:]+)([#:].+)$/)
{
my ($pre, $fname, $post) = ($1, $2, $3);
next if $fname eq "re_zero";
if ($post =~ /^:AVERAGE/)
{
splice (@rrd_sum, $index, 1, $pre . ":x$fname" . $post);
$index++;
next;
}
next if grep /^x$fname$/, @defined;
push @defined, "x$fname";
my @replace;
if (!defined ($node->{client}->{$service}->{$fname.".type"}) or $node->{client}->{$service}->{$fname.".type"} ne "GAUGE")
{
if ($time eq "week")
{ # Every plot is half an hour. Add two plots and multiply, to get per hour
if (graph_by_minute ($config, $domain, $name, $service))
{ # Already multiplied by 60
push @replace, "CDEF:x$fname=PREV($fname),UN,0,PREV($fname),IF,$fname,+,5,*,6,*";
}
else
{
push @replace, "CDEF:x$fname=PREV($fname),UN,0,PREV($fname),IF,$fname,+,300,*,6,*";
}
}
else
{ # Every plot is one day exactly. Just multiply.
if (graph_by_minute ($config, $domain, $name, $service))
{ # Already multiplied by 60
push @replace, "CDEF:x$fname=$fname,5,*,288,*";
}
else
{
push @replace, "CDEF:x$fname=$fname,300,*,288,*";
}
}
}
push @replace, $pre . ":x$fname" . $post;
splice (@rrd_sum, $index, 1, @replace);
$index++;
}
elsif ($rrd_sum[$index] =~ /^(--lower-limit|--upper-limit|-l|-u)$/)
{
$index++;
$rrd_sum[$index] = $rrd_sum[$index] * 300 * $sumtimes{$time}->[1];
}
}
unless ($labelled)
{
my $label = $node->{client}->{$service}->{"graph_vlabel_sum_$time"} || $sumtimes{$time}->[0];
unshift @rrd_sum, "--vertical-label", $label;
}
print "\n\nrrdtool \"graph\" \"", join ("\"\n\t\"",@rrd_sum), "\"\n" if $DEBUG;
RRDs::graph (@rrd_sum);
if (my $ERROR = RRDs::error) {
logger ("Unable to graph $filename: $ERROR");
}
elsif ($list_images) # Command-line option to list images created
{
print &munin_get_picture_filename ($config, $domain, $name, $service, $time, 1), "\n";
}
}
}
$service_time = sprintf ("%.2f",(Time::HiRes::time - $service_time));
logger ("Graphed service : $service ($service_time sec * 4)");
print STATS "GS|$domain|$name|$service|$service_time\n" unless $skip_stats;
foreach (@added)
{
delete $node->{client}->{$service}->{$_} if exists $node->{client}->{$service}->{$_};
}
@added = ();
}
}
sub graph_by_minute
{
my $config = shift;
my $domain = shift;
my $name = shift;
my $service = shift;
return (munin_get ($config, "graph_period", "second", $domain, $name, $service) eq "minute");
}
sub orig_to_cdef
{
my $service = shift;
my $field = shift;
if (defined $service->{$field.".cdef_name"})
{
return &orig_to_cdef ($service, $service->{$field.".cdef_name"});
}
return $field;
}
sub set_cdef_name
{
my $service = shift;
my $field = shift;
my $new = shift;
$service->{$field.".cdef_name"} = $new;
print "DEBUG: set_cdef_name from $field to $new.\n" if $DEBUG;
}
sub skip_service
{
my $node = shift;
my $service = shift;
# Check to make sure that service exists
return 1 unless (ref $node->{client}->{$service});
# See if we should skip it because of conf-options
return 1 if ($node->{client}->{$service}->{'graph'} and
($node->{client}->{$service}->{'graph'} eq "no" ||
($node->{client}->{$service}->{'graph'} eq "on-demand") && !$force_graphing));
# See if we should skip it because of command-line arguments
return 1 if (@limit_services and not grep (/^$service$/, @limit_services));
# Don't skip
return 0;
}
sub expand_cdef
{
my $service = shift;
my $cfield_ref = shift;
my $cdef = shift;
my $new_field = &get_field_name ("cdef$$cfield_ref");
my ($max, $min, $avg) = ("CDEF:a$new_field=$cdef", "CDEF:i$new_field=$cdef", "CDEF:g$new_field=$cdef");
foreach my $field (keys %$service)
{
next unless ($field =~ /^(.+)\.label$/);
my $fieldname = $1;
my $rrdname = &orig_to_cdef ($service, $fieldname);
if ($cdef =~ /\b$fieldname\b/)
{
$max =~ s/([,=])$fieldname([,=]|$)/$1a$rrdname$2/g;
$min =~ s/([,=])$fieldname([,=]|$)/$1i$rrdname$2/g;
$avg =~ s/([,=])$fieldname([,=]|$)/$1g$rrdname$2/g;
}
}
&set_cdef_name ($service, $$cfield_ref, $new_field);
$$cfield_ref = $new_field;
return ($max, $min, $avg);
}
sub logger_open {
my $dirname = shift;
if (!$log->opened)
{
unless (open ($log, ">>$dirname/munin-graph.log"))
{
print STDERR "Warning: Could not open log file \"$dirname/munin-graph.log\" for writing: $!";
}
}
}
sub logger {
my ($comment) = @_;
my $now = strftime "%b %d %H:%M:%S", localtime;
print "$now - $comment\n" if $stdout;
if ($log->opened)
{
print $log "$now - $comment\n";
}
else
{
if (defined $config->{logdir})
{
if (open ($log, ">>$config->{logdir}/munin-graph.log"))
{
print $log "$now - $comment\n";
}
else
{
print STDERR "Warning: Could not open log file \"$config->{logdir}/munin-graph.log\" for writing: $!";
print STDERR "$now - $comment\n";
}
}
else
{
print STDERR "$now - $comment\n";
}
}
}
sub parse_path
{
my ($path, $domain, $node, $service, $field) = @_;
my $filename = "unknown";
if ($path =~ /^\s*([^:]*):([^:]*):([^:]*):([^:]*)\s*$/)
{
$filename = munin_get_filename ($config, $1, $2, $3, $4);
}
elsif ($path =~ /^\s*([^:]*):([^:]*):([^:]*)\s*$/)
{
$filename = munin_get_filename ($config, $domain, $1, $2, $3);
}
elsif ($path =~ /^\s*([^:]*):([^:]*)\s*$/)
{
$filename = munin_get_filename ($config, $domain, $node, $1, $2);
}
elsif ($path =~ /^\s*([^:]*)\s*$/)
{
$filename = munin_get_filename ($config, $domain, $node, $service, $1);
}
return $filename;
}
sub escape
{
my $text = shift;
return undef if not defined $text;
$text =~ s/\\/\\\\/g;
$text =~ s/:/\\:/g;
return $text;
}
sub RRDescape
{
my $text = shift;
return $RRDs::VERSION < 1.2 ? $text : escape($text);
}
1;
=head1 NAME
munin-graph - A program to create graphs from data contained in rrd-files.
=head1 SYNOPSIS
munin-graph [--options]
=head1 OPTIONS
=over 5
=item B<< --[no]force >>
If set, force drawing of graphs that are not usually drawn due to options in the config file. [--noforce]
=item B<< --[no]lazy >>
If set, only redraw graphs when it would look different from the existing one. [--lazy]
=item B<< --help >>
View help.
=item B<< --[no]force-root >>
Force running as root (stupid and unnecessary). [--noforce-root]
=item B<< --[no]debug >>
If set, view debug messages. [--nodebug]
=item B<< --service <service> >>
Limit graphed services to E<lt>serviceE<gt>. Multiple --service options may be supplied. [unset]
=item B<< --host <host> >>
Limit graphed hosts to E<lt>hostE<gt>. Multiple --host options may be supplied. [unset]
=item B<< --config <file> >>
Use E<lt>fileE<gt> as configuration file. [/etc/munin/munin.conf]
=item B<< --[no]list-images >>
If set, list the filenames of the images created. [--nolist-images]
=item B<< --[no]day >>
If set, create day-based graphs. [--day]
=item B<< --[no]week >>
If set, create week-based graphs. [--week]
=item B<< --[no]month >>
If set, create month-based graphs. [--month]
=item B<< --[no]year >>
If set, create year-based graphs. [--year]
=back
=head1 DESCRIPTION
Munin-graph is a part of the package Munin, which is used in combination
with Munin's node. Munin is a group of programs to gather data from
Munin's nodes, graph them, create html-pages, and optionally warn Nagios
about any off-limit values.
munin-graph does the graphing. It is usually only used from within munin-cron.
It checks the rrd-files for updated values, and redraws the graphs if
needed. To force redrawing of graphs (after setup-changes et alia), use
'--nolazy'.
=head1 FILES
/etc/munin/munin.conf
/var/lib/munin/*
/var/log/munin/munin-graph
/var/run/munin/*
=head1 VERSION
This is munin-graph version 0.9.2-3
=head1 AUTHORS
Audun Ytterdal and Jimmy Olsen.
=head1 BUGS
munin-graph does, as of now, not check the syntax of the configuration file.
Please report other bugs in the bug tracker at L<http://munin.sf.net/>.
=head1 COPYRIGHT
Copyright © 2002-2004 Audun Ytterdal, Jimmy Olsen, and Tore Anderson / Linpro AS.
This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.
This program is released under the GNU General Public License
=cut
# vim: syntax=perl ts=8