hotsanic |
Subversion Repositories: |
Compare with Previous - Blame - Download
#
# $Id: HotSaNICdiagram.pm,v 1.29 2004/09/19 10:07:19 bernisys Exp $
#
package HotSaNICdiagram;
use RRDs;
($VERSION = '$Revision: 1.29 $') =~ s/.*(\d+\.\d+).*/$1/;
my @Weekday=("Sun","Mon","Tue","Wed","Thu","Fri","Sat");
######################################################################
#
# evaluates some diagram properties from the given range
#
# Usage:
# ($description,$file_description,$build_interval,$diagram_range,$timestring)=get_diagram_properties($range);
#
sub get_diagram_properties {
my ($range)=@_;
my ($descr,$file,$build,$fullrange);
if ($range eq "1h") { $descr="hour"; $file=$descr; $build=0; $fullrange=3600; }
elsif ($range eq "6h") { $descr="6 hours"; $file="6h"; $build=30; $fullrange=21600; }
elsif ($range eq "1day") { $descr="day"; $file=$descr; $build=120; $fullrange=86400; }
elsif ($range eq "1week") { $descr="week"; $file=$descr; $build=1000; $fullrange=604800; }
elsif ($range eq "1month") { $descr="month"; $file=$descr; $build=4300; $fullrange=2592000; }
elsif ($range eq "1year") { $descr="year"; $file=$descr; $build=52500; $fullrange=31536000; }
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
$year+=1900;
$mon++;
if ($mon<10) { $mon="0".$mon; }
if ($mday<10) { $mday="0".$mday; }
if ($hour<10) { $hour="0".$hour; }
if ($min<10) { $min="0".$min; }
$buildtime="$Weekday[$wday] $year-$mon-$mday $hour:$min";
return ($descr,$file,$build,$fullrange,$buildtime);
}
######################################################################
#
# Creates a diagram from the options passed within a config hash
#
# Usage:
# graph(%CONFIGHASH);
#
# Hints:
# The hash has to contain the following items:
#
# %CONFIGHASH=( FILENAME => "full path to output file without suffix"
# FORMAT => "gif" # ("png" or "gif")
# INTERVAL => <seconds between neccessary update>
# FORCE => 0 for automatic checking, >0 forces plot
# RANGE => plot range of diagram (1year 6h 1h ...)
# DEBUGLEVEL => everything >1 plots complete optiin array
# OPTIONS => array of options that will be passed tpo RRDs
# assign it like this: => [(@a1,@a2,...)]
#
sub graph {
my %CONF=@_;
my $FILE=$CONF{FILENAME}.".".$CONF{FORMAT};
my $TEMPFILE=$CONF{FILENAME}."-temp.".$CONF{FORMAT};
my @OPTIONS=@{$CONF{OPTIONS}};
if (! -e $FILE) { $CONF{FORCE}++; }
else { $CONF{FORCE}++ if time >= (stat($FILE))[9]+$CONF{INTERVAL}; }
if ($CONF{FORCE} > 0) {
if ($CONF{DEBUGLEVEL} > 1) { print join("\n",$TEMPFILE,@OPTIONS),"\n"; }
my ($prints,$xs,$ys)=RRDs::graph $TEMPFILE,@OPTIONS;
if ($ERROR = RRDs::error) { print time," ",$CONF{MODNAME},": $ERROR\n"; }
else {
rename $TEMPFILE,$FILE;
printf " %9s %4d x %4d %s\n", $CONF{RANGE},$xs,$ys,$FILE;
}
}
}
sub check_args {
my $OPT=0;
$FORCE=0;
$DEBUGLEVEL=-1;
%PLOT=();
foreach (@_) {
$FORCE=1 if $_ eq "a";
$DEBUGLEVEL=100 if $_ eq "d";
if ($OPT==1) { $PLOT{$_}=1; $OPT=0; }
$OPT=1 if $_ eq "g";
if ( ($_ eq "?") or ($_ eq "h") ) {
print "options:\n
g <name> only graph item <name>
(can be used multiple times, default: plot all)\n
a plot all time-graphs of selected items
(default: plot only expired graphs)\n
d enable debug mode (prints all graph options -
much output!)\n
? or h display this help-text
\n";
exit;
}
}
return ($FORCE,$DEBUGLEVEL,%PLOT);
}
########################################
# get_common_options calculates an array of common module-specific and
# global diagram options to be passed to RRDs::graph. it accepts a hash
# containing both modulespecific and global settings.
#
sub get_common_options {
my %CONFIG=@_;
my @OPTIONS=();
if ($CONFIG{GRAPH_RIGID} == 1 ) { push @OPTIONS,"-r"; }
if ($CONFIG{GRAPH_FORCE_LEGEND} == 1 ) { push @OPTIONS,"-F"; }
if ($CONFIG{GRAPH_STYLE} eq "log" ) {
push @OPTIONS,"-o";
$CONFIG{GRAPH_MIN}=$CONFIG{GRAPH_MIN_LOG};
}
if (defined $CONFIG{GRAPH_BASE}) {
if ( ($CONFIG{GRAPH_BASE} eq "percent") or ($CONFIG{GRAPH_BASE} eq "%") ) {
if (defined $CONFIG{GRAPH_MAX}) { $CONFIG{GRAPH_MAX}*=100; }
if (defined $CONFIG{GRAPH_MIN}) { $CONFIG{GRAPH_MIN}*=100; }
}
if ($CONFIG{GRAPH_BASE} eq "bits") {
if (defined $CONFIG{GRAPH_MAX}) { $CONFIG{GRAPH_MAX}*=8; }
if (defined $CONFIG{GRAPH_MIN}) { $CONFIG{GRAPH_MIN}*=8; }
}
elsif ($CONFIG{GRAPH_BASE} eq "ms") {
if (defined $CONFIG{GRAPH_MAX}) { $CONFIG{GRAPH_MAX}/=1000; }
if (defined $CONFIG{GRAPH_MIN}) { $CONFIG{GRAPH_MIN}/=1000; }
}
elsif ($CONFIG{GRAPH_BASE} eq "us") {
if (defined $CONFIG{GRAPH_MAX}) { $CONFIG{GRAPH_MAX}/=1000000; }
if (defined $CONFIG{GRAPH_MIN}) { $CONFIG{GRAPH_MIN}/=1000000; }
}
}
push @OPTIONS,(
"-i", # graphic interlaced mode
"-w",$CONFIG{WIDTH}, # drawing area width
"-h",$CONFIG{HEIGHT}, # drawing area height
"-b",$CONFIG{GRAPH_UNIT}, # value of "1k" (1000 or 1024)
"-u",$CONFIG{GRAPH_MAX}, # upper initial border
"-l",$CONFIG{GRAPH_MIN}, # lower initial border
"-a",uc($CONFIG{IMAGEFORMAT}) # image format
);
return @OPTIONS;
}
######################################################################
#
# generates an array suitable to feed to RRDs::graph to build a graph
# including a legend with the desired consolidation functions.
#
# Usage:
# insert_data(type,var,color,description,legends,unit,use_SI);
#
# type: AREA LINE1 LINE2 LINE3 STACK
#
# var: RRD variable to be graphed and variables which will
# be assigned to min, average, max and now. They
# have to be separated by spaces. If less than five
# variables are stated, the LAST entry will be copied
# to the remaining positions.
# If you just want to insert a legend without adding a
# graph, the first element must be a numerical zero "0"
# The "description" can be used for padding the legend.
#
# color: graph's color in RRDs::graph notation ( RRGGBB )
#
# description: name of the graph or short description that will be
# used in the legend
#
# legends: one or more space-separated occurances of:
# min avg max cur now ("now" is the same as "cur")
#
# unit: the unit that will be appended after the last legend
#
# use_SI: 0 -> just print the plain value
# 1 -> print values in SI units (micro, milli, kilo, mega, ...)
# -1 -> same as "1", but SI-units will be added without a space
#
# EXAMPLE:
#
# insert_data("AREA","abc xyz","#0000ff","test legend","min max","bytes",1);
#
# -> draw a blue area with the rrd variable "abc" and add a corrosponding
# legend to it, but refer the legend to the variable "xyz". The legend
# will use SI-units and the unit "bytes". It will look like this:
#
# [#] test legend (min: 1.23k max: 12.34M bytes)
#
sub insert_data {
my $graphtype=shift || "LINE1";
my $rrdvar=shift || "";
my $color=shift || "000000";
my $description=shift || "";
my $legends=shift || "min avg max";
my $unit=shift || "";
my $use_SI_units=shift || 0;
my @legends=split / /,$legends;
my @vars=split / /,$rrdvar;
while ($#vars<4) { push @vars,$vars[$#vars]; }
my $lead="";
my @array=();
if ($vars[0] ne "0") { push @array,"$graphtype:$vars[0]#$color:$description"; $description=""; }
else { $lead=" "; }
$SI="";
if ($use_SI_units == 1) { $SI=" %s"; }
elsif ($use_SI_units == -1) { $SI="%s"; }
if ($unit ne "") { $unit=" $unit"; }
my $num=0;
foreach $legend (@legends) {
my $open="";
my $close="";
if ($num == 0) { $open="$lead$description$lead("; }
if ($num == $#legends) { $close="$unit)\\n"; }
$num++;
if ($legend eq "min") { push @array,"GPRINT:$vars[1]:MIN:$open"."min\\:%7.2lf$SI$close"; }
elsif ($legend eq "avg") { push @array,"GPRINT:$vars[2]:AVERAGE:$open"."avg\\:%7.2lf$SI$close"; }
elsif ($legend eq "max") { push @array,"GPRINT:$vars[3]:MAX:$open"."max\\:%7.2lf$SI$close"; }
elsif ($legend eq "now") { push @array,"GPRINT:$vars[4]:LAST:$open"."now\\:%7.2lf$SI$close"; }
elsif ($legend eq "cur") { push @array,"GPRINT:$vars[4]:LAST:$open"."now\\:%7.2lf$SI$close"; }
$description="";
$lead="";
}
return @array;
}
######################################################################
#
# generates an array suitable to feed to RRDs::graph to build a min/max
# graph including a legend with the desired consolidation functions.
#
# Usage:
# insert_minmax(var,areacolor,bordercolor,description,unit,use_SI);
#
# var: RRD variables to be graphed and assigned to min and max.
# They have to be separated by spaces.
# If you just want to insert a legend without adding a
# graph, the first element must be a numerical zero "0"
# The "description" can be used for padding the legend.
#
# areacolor: graph area color in RRDs::graph notation ( #RRGGBB )
#
# bordercolor: border color in RRDs::graph notation ( #RRGGBB )
#
# description: short description that will be appended in the legend
#
# unit: the unit that will be appended after the last legend
#
# use_SI: 0 -> just print the plain value
# 1 -> print values in SI units (micro, milli, kilo, mega, ...)
# -1 -> same as "1", but SI-units will be added without a space
#
# EXAMPLE:
#
# insert_data("AREA","abc xyz","0000ff","test legend","min max","bytes",1);
#
# -> draw a blue area with the rrd variable "abc" and add a corrosponding
# legend to it, but refer the legend to the variable "xyz". The legend
# will use SI-units and the unit "bytes". It will look like this:
#
# [#] test legend (min: 1.23k max: 12.34M bytes)
#
sub insert_minmax {
my $rrdvar=shift || "";
my $areacolor=shift || "000000";
my $bordercolor=shift || "";
my $description=shift || "";
my $unit=shift || "";
my $use_SI_units=shift || 0;
my @vars=split / /,$rrdvar;
my $lead="";
my @array=();
if ($vars[0] ne "0") {
push @array,(
"CDEF:negmin$vars[0]=$vars[0],0,LT,$vars[0],0,IF",
"CDEF:negmax$vars[1]=$vars[1],0,LT,$vars[1],0,IF",
"AREA:$vars[1]#$areacolor:$description",
"AREA:$vars[0]#FFFFFF:",
"AREA:negmin$vars[0]#$areacolor:",
"AREA:negmax$vars[1]#FFFFFF:"
);
if ($bordercolor ne "") {
push @array,(
"LINE1:$vars[0]#$bordercolor:",
"LINE1:$vars[1]#$bordercolor:"
);
}
$description="";
}
else { $lead=" "; }
if (! defined $vars[2]) { push @vars,$vars[0]; }
if (! defined $vars[3]) { push @vars,$vars[1]; }
$SI="";
if ($use_SI_units == 1) { $SI=" %s"; }
elsif ($use_SI_units == -1) { $SI="%s"; }
if ($unit ne "") { $unit=" $unit"; }
push @array,(
"GPRINT:$vars[2]:MIN:$lead$description$lead(min\\:%7.2lf$SI",
"GPRINT:$vars[2]:MAX:$lead$description$lead/%7.2lf$SI",
"GPRINT:$vars[3]:MIN:max\\:%7.2lf$SI",
"GPRINT:$vars[3]:MAX:/%7.2lf$SI$unit)\\n"
);
return @array;
}
######################################################################
#
# generates an array suitable to feed to RRDs::graph to mark
# areas of unknown values
#
# Usage:
# insert_unknown_area($vars,$color,$description);
#
# vars: space separated list of all variables to be taken
# into account. If one of them is unknown, the area
# will be marked
#
# color: graph's color in RRDs::graph notation ( RRGGBB )
#
# description: some additional legend that will (if defined) show
# up in brackets after the string "data unknown".
#
sub insert_unknown_area {
my $vars=shift;
my $color=shift || "ffffa0";
my $description=shift || "";
if ($description ne "") { $description=" ($description)"; }
# the scaring ;) definition of "wrongdata":
# if TIME < now { if x unknown { INF else 0 } else 0 }
# CDEF:wrongdata=TIME,$DATE,LT,x,UN,INF,0,IF,0,IF \
# where "x" is the result of the addition of all variables
my @vars=split /\s+/,$vars;
my $string=shift @vars;
if (@vars) { $string = $string.",".join (",+,",@vars).",+"; }
return (
"CDEF:wrongdata=TIME,".time.",LT,$string,UN,INF,0,IF,0,IF",
"AREA:wrongdata#$color:data unknown$description\\n",
"CDEF:wrongdatainv=0,wrongdata,-",
"AREA:wrongdatainv#$color:"
);
}
sub insert_lines {
my %CONFIG=@_;
my @array=("HRULE:0#000000");
if (defined $CONFIG{GRAPH_ADDLINE}) {
foreach (@{$CONFIG{GRAPH_ADDLINE}}) {
my ($pos,$color,$comment)=split /,/;
if (defined $CONFIG{$color}) { $color=$CONFIG{$color}; }
push @array,"HRULE:$pos#$color:$comment";
}
}
return @array
}
sub insert_vars {
my $dbname=shift;
my $dbvar=shift;
my $graphvar=shift;
my $which=shift;
my @array=();
foreach my $consolidation (split /\s+/,$which) {
my $consname=lc $consolidation;
if ($consname eq "average") { $consname="avg"; }
push @array,"DEF:$graphvar"."_"."$consname=$dbname:$dbvar:$consolidation"
}
return @array;
}
1;