| 1 | 1 | simandl | # |
| 2 | | | # $Id: HotSaNICdiagram.pm,v 1.29 2004/09/19 10:07:19 bernisys Exp $ |
| 3 | | | # |
| 4 | | | |
| 5 | | | package HotSaNICdiagram; |
| 6 | | | |
| 7 | | | use RRDs; |
| 8 | | | |
| 9 | | | ($VERSION = '$Revision: 1.29 $') =~ s/.*(\d+\.\d+).*/$1/; |
| 10 | | | |
| 11 | | | my @Weekday=("Sun","Mon","Tue","Wed","Thu","Fri","Sat"); |
| 12 | | | |
| 13 | | | ###################################################################### |
| 14 | | | # |
| 15 | | | # evaluates some diagram properties from the given range |
| 16 | | | # |
| 17 | | | # Usage: |
| 18 | | | # ($description,$file_description,$build_interval,$diagram_range,$timestring)=get_diagram_properties($range); |
| 19 | | | # |
| 20 | | | sub get_diagram_properties { |
| 21 | | | my ($range)=@_; |
| 22 | | | my ($descr,$file,$build,$fullrange); |
| 23 | | | if ($range eq "1h") { $descr="hour"; $file=$descr; $build=0; $fullrange=3600; } |
| 24 | | | elsif ($range eq "6h") { $descr="6 hours"; $file="6h"; $build=30; $fullrange=21600; } |
| 25 | | | elsif ($range eq "1day") { $descr="day"; $file=$descr; $build=120; $fullrange=86400; } |
| 26 | | | elsif ($range eq "1week") { $descr="week"; $file=$descr; $build=1000; $fullrange=604800; } |
| 27 | | | elsif ($range eq "1month") { $descr="month"; $file=$descr; $build=4300; $fullrange=2592000; } |
| 28 | | | elsif ($range eq "1year") { $descr="year"; $file=$descr; $build=52500; $fullrange=31536000; } |
| 29 | | | |
| 30 | | | ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); |
| 31 | | | $year+=1900; |
| 32 | | | $mon++; |
| 33 | | | if ($mon<10) { $mon="0".$mon; } |
| 34 | | | if ($mday<10) { $mday="0".$mday; } |
| 35 | | | if ($hour<10) { $hour="0".$hour; } |
| 36 | | | if ($min<10) { $min="0".$min; } |
| 37 | | | $buildtime="$Weekday[$wday] $year-$mon-$mday $hour:$min"; |
| 38 | | | |
| 39 | | | return ($descr,$file,$build,$fullrange,$buildtime); |
| 40 | | | } |
| 41 | | | |
| 42 | | | ###################################################################### |
| 43 | | | # |
| 44 | | | # Creates a diagram from the options passed within a config hash |
| 45 | | | # |
| 46 | | | # Usage: |
| 47 | | | # graph(%CONFIGHASH); |
| 48 | | | # |
| 49 | | | # Hints: |
| 50 | | | # The hash has to contain the following items: |
| 51 | | | # |
| 52 | | | # %CONFIGHASH=( FILENAME => "full path to output file without suffix" |
| 53 | | | # FORMAT => "gif" # ("png" or "gif") |
| 54 | | | # INTERVAL => <seconds between neccessary update> |
| 55 | | | # FORCE => 0 for automatic checking, >0 forces plot |
| 56 | | | # RANGE => plot range of diagram (1year 6h 1h ...) |
| 57 | | | # DEBUGLEVEL => everything >1 plots complete optiin array |
| 58 | | | # OPTIONS => array of options that will be passed tpo RRDs |
| 59 | | | # assign it like this: => [(@a1,@a2,...)] |
| 60 | | | # |
| 61 | | | sub graph { |
| 62 | | | my %CONF=@_; |
| 63 | | | |
| 64 | | | my $FILE=$CONF{FILENAME}.".".$CONF{FORMAT}; |
| 65 | | | my $TEMPFILE=$CONF{FILENAME}."-temp.".$CONF{FORMAT}; |
| 66 | | | |
| 67 | | | my @OPTIONS=@{$CONF{OPTIONS}}; |
| 68 | | | |
| 69 | | | if (! -e $FILE) { $CONF{FORCE}++; } |
| 70 | | | else { $CONF{FORCE}++ if time >= (stat($FILE))[9]+$CONF{INTERVAL}; } |
| 71 | | | |
| 72 | | | if ($CONF{FORCE} > 0) { |
| 73 | | | if ($CONF{DEBUGLEVEL} > 1) { print join("\n",$TEMPFILE,@OPTIONS),"\n"; } |
| 74 | | | my ($prints,$xs,$ys)=RRDs::graph $TEMPFILE,@OPTIONS; |
| 75 | | | if ($ERROR = RRDs::error) { print time," ",$CONF{MODNAME},": $ERROR\n"; } |
| 76 | | | else { |
| 77 | | | rename $TEMPFILE,$FILE; |
| 78 | | | printf " %9s %4d x %4d %s\n", $CONF{RANGE},$xs,$ys,$FILE; |
| 79 | | | } |
| 80 | | | } |
| 81 | | | } |
| 82 | | | |
| 83 | | | sub check_args { |
| 84 | | | my $OPT=0; |
| 85 | | | $FORCE=0; |
| 86 | | | $DEBUGLEVEL=-1; |
| 87 | | | %PLOT=(); |
| 88 | | | foreach (@_) { |
| 89 | | | $FORCE=1 if $_ eq "a"; |
| 90 | | | $DEBUGLEVEL=100 if $_ eq "d"; |
| 91 | | | if ($OPT==1) { $PLOT{$_}=1; $OPT=0; } |
| 92 | | | $OPT=1 if $_ eq "g"; |
| 93 | | | if ( ($_ eq "?") or ($_ eq "h") ) { |
| 94 | | | print "options:\n |
| 95 | | | g <name> only graph item <name> |
| 96 | | | (can be used multiple times, default: plot all)\n |
| 97 | | | a plot all time-graphs of selected items |
| 98 | | | (default: plot only expired graphs)\n |
| 99 | | | d enable debug mode (prints all graph options - |
| 100 | | | much output!)\n |
| 101 | | | ? or h display this help-text |
| 102 | | | \n"; |
| 103 | | | exit; |
| 104 | | | } |
| 105 | | | } |
| 106 | | | return ($FORCE,$DEBUGLEVEL,%PLOT); |
| 107 | | | } |
| 108 | | | |
| 109 | | | ######################################## |
| 110 | | | # get_common_options calculates an array of common module-specific and |
| 111 | | | # global diagram options to be passed to RRDs::graph. it accepts a hash |
| 112 | | | # containing both modulespecific and global settings. |
| 113 | | | # |
| 114 | | | sub get_common_options { |
| 115 | | | my %CONFIG=@_; |
| 116 | | | my @OPTIONS=(); |
| 117 | | | if ($CONFIG{GRAPH_RIGID} == 1 ) { push @OPTIONS,"-r"; } |
| 118 | | | if ($CONFIG{GRAPH_FORCE_LEGEND} == 1 ) { push @OPTIONS,"-F"; } |
| 119 | | | if ($CONFIG{GRAPH_STYLE} eq "log" ) { |
| 120 | | | push @OPTIONS,"-o"; |
| 121 | | | $CONFIG{GRAPH_MIN}=$CONFIG{GRAPH_MIN_LOG}; |
| 122 | | | } |
| 123 | | | if (defined $CONFIG{GRAPH_BASE}) { |
| 124 | | | if ( ($CONFIG{GRAPH_BASE} eq "percent") or ($CONFIG{GRAPH_BASE} eq "%") ) { |
| 125 | | | if (defined $CONFIG{GRAPH_MAX}) { $CONFIG{GRAPH_MAX}*=100; } |
| 126 | | | if (defined $CONFIG{GRAPH_MIN}) { $CONFIG{GRAPH_MIN}*=100; } |
| 127 | | | } |
| 128 | | | if ($CONFIG{GRAPH_BASE} eq "bits") { |
| 129 | | | if (defined $CONFIG{GRAPH_MAX}) { $CONFIG{GRAPH_MAX}*=8; } |
| 130 | | | if (defined $CONFIG{GRAPH_MIN}) { $CONFIG{GRAPH_MIN}*=8; } |
| 131 | | | } |
| 132 | | | elsif ($CONFIG{GRAPH_BASE} eq "ms") { |
| 133 | | | if (defined $CONFIG{GRAPH_MAX}) { $CONFIG{GRAPH_MAX}/=1000; } |
| 134 | | | if (defined $CONFIG{GRAPH_MIN}) { $CONFIG{GRAPH_MIN}/=1000; } |
| 135 | | | } |
| 136 | | | elsif ($CONFIG{GRAPH_BASE} eq "us") { |
| 137 | | | if (defined $CONFIG{GRAPH_MAX}) { $CONFIG{GRAPH_MAX}/=1000000; } |
| 138 | | | if (defined $CONFIG{GRAPH_MIN}) { $CONFIG{GRAPH_MIN}/=1000000; } |
| 139 | | | } |
| 140 | | | } |
| 141 | | | |
| 142 | | | push @OPTIONS,( |
| 143 | | | "-i", # graphic interlaced mode |
| 144 | | | "-w",$CONFIG{WIDTH}, # drawing area width |
| 145 | | | "-h",$CONFIG{HEIGHT}, # drawing area height |
| 146 | | | "-b",$CONFIG{GRAPH_UNIT}, # value of "1k" (1000 or 1024) |
| 147 | | | "-u",$CONFIG{GRAPH_MAX}, # upper initial border |
| 148 | | | "-l",$CONFIG{GRAPH_MIN}, # lower initial border |
| 149 | | | "-a",uc($CONFIG{IMAGEFORMAT}) # image format |
| 150 | | | ); |
| 151 | | | return @OPTIONS; |
| 152 | | | } |
| 153 | | | |
| 154 | | | |
| 155 | | | ###################################################################### |
| 156 | | | # |
| 157 | | | # generates an array suitable to feed to RRDs::graph to build a graph |
| 158 | | | # including a legend with the desired consolidation functions. |
| 159 | | | # |
| 160 | | | # Usage: |
| 161 | | | # insert_data(type,var,color,description,legends,unit,use_SI); |
| 162 | | | # |
| 163 | | | # type: AREA LINE1 LINE2 LINE3 STACK |
| 164 | | | # |
| 165 | | | # var: RRD variable to be graphed and variables which will |
| 166 | | | # be assigned to min, average, max and now. They |
| 167 | | | # have to be separated by spaces. If less than five |
| 168 | | | # variables are stated, the LAST entry will be copied |
| 169 | | | # to the remaining positions. |
| 170 | | | # If you just want to insert a legend without adding a |
| 171 | | | # graph, the first element must be a numerical zero "0" |
| 172 | | | # The "description" can be used for padding the legend. |
| 173 | | | # |
| 174 | | | # color: graph's color in RRDs::graph notation ( RRGGBB ) |
| 175 | | | # |
| 176 | | | # description: name of the graph or short description that will be |
| 177 | | | # used in the legend |
| 178 | | | # |
| 179 | | | # legends: one or more space-separated occurances of: |
| 180 | | | # min avg max cur now ("now" is the same as "cur") |
| 181 | | | # |
| 182 | | | # unit: the unit that will be appended after the last legend |
| 183 | | | # |
| 184 | | | # use_SI: 0 -> just print the plain value |
| 185 | | | # 1 -> print values in SI units (micro, milli, kilo, mega, ...) |
| 186 | | | # -1 -> same as "1", but SI-units will be added without a space |
| 187 | | | # |
| 188 | | | # EXAMPLE: |
| 189 | | | # |
| 190 | | | # insert_data("AREA","abc xyz","#0000ff","test legend","min max","bytes",1); |
| 191 | | | # |
| 192 | | | # -> draw a blue area with the rrd variable "abc" and add a corrosponding |
| 193 | | | # legend to it, but refer the legend to the variable "xyz". The legend |
| 194 | | | # will use SI-units and the unit "bytes". It will look like this: |
| 195 | | | # |
| 196 | | | # [#] test legend (min: 1.23k max: 12.34M bytes) |
| 197 | | | # |
| 198 | | | sub insert_data { |
| 199 | | | my $graphtype=shift || "LINE1"; |
| 200 | | | my $rrdvar=shift || ""; |
| 201 | | | my $color=shift || "000000"; |
| 202 | | | my $description=shift || ""; |
| 203 | | | my $legends=shift || "min avg max"; |
| 204 | | | my $unit=shift || ""; |
| 205 | | | my $use_SI_units=shift || 0; |
| 206 | | | |
| 207 | | | my @legends=split / /,$legends; |
| 208 | | | |
| 209 | | | my @vars=split / /,$rrdvar; |
| 210 | | | while ($#vars<4) { push @vars,$vars[$#vars]; } |
| 211 | | | |
| 212 | | | my $lead=""; |
| 213 | | | my @array=(); |
| 214 | | | if ($vars[0] ne "0") { push @array,"$graphtype:$vars[0]#$color:$description"; $description=""; } |
| 215 | | | else { $lead=" "; } |
| 216 | | | |
| 217 | | | $SI=""; |
| 218 | | | if ($use_SI_units == 1) { $SI=" %s"; } |
| 219 | | | elsif ($use_SI_units == -1) { $SI="%s"; } |
| 220 | | | |
| 221 | | | if ($unit ne "") { $unit=" $unit"; } |
| 222 | | | |
| 223 | | | my $num=0; |
| 224 | | | foreach $legend (@legends) { |
| 225 | | | my $open=""; |
| 226 | | | my $close=""; |
| 227 | | | if ($num == 0) { $open="$lead$description$lead("; } |
| 228 | | | if ($num == $#legends) { $close="$unit)\\n"; } |
| 229 | | | $num++; |
| 230 | | | if ($legend eq "min") { push @array,"GPRINT:$vars[1]:MIN:$open"."min\\:%7.2lf$SI$close"; } |
| 231 | | | elsif ($legend eq "avg") { push @array,"GPRINT:$vars[2]:AVERAGE:$open"."avg\\:%7.2lf$SI$close"; } |
| 232 | | | elsif ($legend eq "max") { push @array,"GPRINT:$vars[3]:MAX:$open"."max\\:%7.2lf$SI$close"; } |
| 233 | | | elsif ($legend eq "now") { push @array,"GPRINT:$vars[4]:LAST:$open"."now\\:%7.2lf$SI$close"; } |
| 234 | | | elsif ($legend eq "cur") { push @array,"GPRINT:$vars[4]:LAST:$open"."now\\:%7.2lf$SI$close"; } |
| 235 | | | $description=""; |
| 236 | | | $lead=""; |
| 237 | | | } |
| 238 | | | |
| 239 | | | return @array; |
| 240 | | | } |
| 241 | | | |
| 242 | | | ###################################################################### |
| 243 | | | # |
| 244 | | | # generates an array suitable to feed to RRDs::graph to build a min/max |
| 245 | | | # graph including a legend with the desired consolidation functions. |
| 246 | | | # |
| 247 | | | # Usage: |
| 248 | | | # insert_minmax(var,areacolor,bordercolor,description,unit,use_SI); |
| 249 | | | # |
| 250 | | | # var: RRD variables to be graphed and assigned to min and max. |
| 251 | | | # They have to be separated by spaces. |
| 252 | | | # If you just want to insert a legend without adding a |
| 253 | | | # graph, the first element must be a numerical zero "0" |
| 254 | | | # The "description" can be used for padding the legend. |
| 255 | | | # |
| 256 | | | # areacolor: graph area color in RRDs::graph notation ( #RRGGBB ) |
| 257 | | | # |
| 258 | | | # bordercolor: border color in RRDs::graph notation ( #RRGGBB ) |
| 259 | | | # |
| 260 | | | # description: short description that will be appended in the legend |
| 261 | | | # |
| 262 | | | # unit: the unit that will be appended after the last legend |
| 263 | | | # |
| 264 | | | # use_SI: 0 -> just print the plain value |
| 265 | | | # 1 -> print values in SI units (micro, milli, kilo, mega, ...) |
| 266 | | | # -1 -> same as "1", but SI-units will be added without a space |
| 267 | | | # |
| 268 | | | # EXAMPLE: |
| 269 | | | # |
| 270 | | | # insert_data("AREA","abc xyz","0000ff","test legend","min max","bytes",1); |
| 271 | | | # |
| 272 | | | # -> draw a blue area with the rrd variable "abc" and add a corrosponding |
| 273 | | | # legend to it, but refer the legend to the variable "xyz". The legend |
| 274 | | | # will use SI-units and the unit "bytes". It will look like this: |
| 275 | | | # |
| 276 | | | # [#] test legend (min: 1.23k max: 12.34M bytes) |
| 277 | | | # |
| 278 | | | sub insert_minmax { |
| 279 | | | my $rrdvar=shift || ""; |
| 280 | | | my $areacolor=shift || "000000"; |
| 281 | | | my $bordercolor=shift || ""; |
| 282 | | | my $description=shift || ""; |
| 283 | | | my $unit=shift || ""; |
| 284 | | | my $use_SI_units=shift || 0; |
| 285 | | | |
| 286 | | | my @vars=split / /,$rrdvar; |
| 287 | | | |
| 288 | | | my $lead=""; |
| 289 | | | my @array=(); |
| 290 | | | if ($vars[0] ne "0") { |
| 291 | | | push @array,( |
| 292 | | | "CDEF:negmin$vars[0]=$vars[0],0,LT,$vars[0],0,IF", |
| 293 | | | "CDEF:negmax$vars[1]=$vars[1],0,LT,$vars[1],0,IF", |
| 294 | | | "AREA:$vars[1]#$areacolor:$description", |
| 295 | | | "AREA:$vars[0]#FFFFFF:", |
| 296 | | | "AREA:negmin$vars[0]#$areacolor:", |
| 297 | | | "AREA:negmax$vars[1]#FFFFFF:" |
| 298 | | | ); |
| 299 | | | if ($bordercolor ne "") { |
| 300 | | | push @array,( |
| 301 | | | "LINE1:$vars[0]#$bordercolor:", |
| 302 | | | "LINE1:$vars[1]#$bordercolor:" |
| 303 | | | ); |
| 304 | | | } |
| 305 | | | $description=""; |
| 306 | | | } |
| 307 | | | else { $lead=" "; } |
| 308 | | | |
| 309 | | | if (! defined $vars[2]) { push @vars,$vars[0]; } |
| 310 | | | if (! defined $vars[3]) { push @vars,$vars[1]; } |
| 311 | | | |
| 312 | | | $SI=""; |
| 313 | | | if ($use_SI_units == 1) { $SI=" %s"; } |
| 314 | | | elsif ($use_SI_units == -1) { $SI="%s"; } |
| 315 | | | |
| 316 | | | if ($unit ne "") { $unit=" $unit"; } |
| 317 | | | |
| 318 | | | push @array,( |
| 319 | | | "GPRINT:$vars[2]:MIN:$lead$description$lead(min\\:%7.2lf$SI", |
| 320 | | | "GPRINT:$vars[2]:MAX:$lead$description$lead/%7.2lf$SI", |
| 321 | | | "GPRINT:$vars[3]:MIN:max\\:%7.2lf$SI", |
| 322 | | | "GPRINT:$vars[3]:MAX:/%7.2lf$SI$unit)\\n" |
| 323 | | | ); |
| 324 | | | |
| 325 | | | return @array; |
| 326 | | | } |
| 327 | | | |
| 328 | | | |
| 329 | | | ###################################################################### |
| 330 | | | # |
| 331 | | | # generates an array suitable to feed to RRDs::graph to mark |
| 332 | | | # areas of unknown values |
| 333 | | | # |
| 334 | | | # Usage: |
| 335 | | | # insert_unknown_area($vars,$color,$description); |
| 336 | | | # |
| 337 | | | # vars: space separated list of all variables to be taken |
| 338 | | | # into account. If one of them is unknown, the area |
| 339 | | | # will be marked |
| 340 | | | # |
| 341 | | | # color: graph's color in RRDs::graph notation ( RRGGBB ) |
| 342 | | | # |
| 343 | | | # description: some additional legend that will (if defined) show |
| 344 | | | # up in brackets after the string "data unknown". |
| 345 | | | # |
| 346 | | | sub insert_unknown_area { |
| 347 | | | my $vars=shift; |
| 348 | | | my $color=shift || "ffffa0"; |
| 349 | | | my $description=shift || ""; |
| 350 | | | |
| 351 | | | if ($description ne "") { $description=" ($description)"; } |
| 352 | | | |
| 353 | | | # the scaring ;) definition of "wrongdata": |
| 354 | | | # if TIME < now { if x unknown { INF else 0 } else 0 } |
| 355 | | | # CDEF:wrongdata=TIME,$DATE,LT,x,UN,INF,0,IF,0,IF \ |
| 356 | | | # where "x" is the result of the addition of all variables |
| 357 | | | |
| 358 | | | my @vars=split /\s+/,$vars; |
| 359 | | | my $string=shift @vars; |
| 360 | | | if (@vars) { $string = $string.",".join (",+,",@vars).",+"; } |
| 361 | | | |
| 362 | | | return ( |
| 363 | | | "CDEF:wrongdata=TIME,".time.",LT,$string,UN,INF,0,IF,0,IF", |
| 364 | | | "AREA:wrongdata#$color:data unknown$description\\n", |
| 365 | | | "CDEF:wrongdatainv=0,wrongdata,-", |
| 366 | | | "AREA:wrongdatainv#$color:" |
| 367 | | | ); |
| 368 | | | } |
| 369 | | | |
| 370 | | | |
| 371 | | | sub insert_lines { |
| 372 | | | my %CONFIG=@_; |
| 373 | | | my @array=("HRULE:0#000000"); |
| 374 | | | if (defined $CONFIG{GRAPH_ADDLINE}) { |
| 375 | | | foreach (@{$CONFIG{GRAPH_ADDLINE}}) { |
| 376 | | | my ($pos,$color,$comment)=split /,/; |
| 377 | | | if (defined $CONFIG{$color}) { $color=$CONFIG{$color}; } |
| 378 | | | push @array,"HRULE:$pos#$color:$comment"; |
| 379 | | | } |
| 380 | | | } |
| 381 | | | return @array |
| 382 | | | } |
| 383 | | | |
| 384 | | | sub insert_vars { |
| 385 | | | my $dbname=shift; |
| 386 | | | my $dbvar=shift; |
| 387 | | | my $graphvar=shift; |
| 388 | | | my $which=shift; |
| 389 | | | my @array=(); |
| 390 | | | foreach my $consolidation (split /\s+/,$which) { |
| 391 | | | my $consname=lc $consolidation; |
| 392 | | | if ($consname eq "average") { $consname="avg"; } |
| 393 | | | push @array,"DEF:$graphvar"."_"."$consname=$dbname:$dbvar:$consolidation" |
| 394 | | | } |
| 395 | | | return @array; |
| 396 | | | } |
| 397 | | | |
| 398 | | | 1; |
| 399 | | | |