1#!/usr/local/bin/perl 2##!/usr/bin/speedy 3# vim:ts=4 4# trend.cgi 5# Generate a trending graph, based on yearly data, for the specified 6# mrtg target. A module to be called from routers.cgi Extension interface 7# 8# Copyright S Shipway 2003 9# version 0.1: first working attempt, released with routers.cgi v2.13 10# version 0.2: fix bugs (routers.cgi v2.13a) 11# version 0.3: Directory[] directive was broken 12# version 0.4: some reporting issues 13# version 0.5: error checking, more comments etc 14# version 0.6: not much 15# version 0.7: more configuration in routers2.conf, better error messages 16# version 1.0: Removed external rrdtool exe call and XML stage. Now all 17# processing of the temporary rrd is done internally. Also, 18# appears to be Windows compatible. 19# version 1.1: added different base options 20# version 1.2: added decolon function for RRD v1.2.x compatibility 21# version 1.3: added stylesheet support 22# version 1.4: added MAX/AVG option 23# version 1.5: more RRD1.2.x support 24# version 2.0: more RRD version compatibility stuff 25# 2.1: better libadd support 26# 27########################################################################## 28# To install this script: 29# Change the #! line at the beginning to give correct Perl location 30# Set correct Temporary working directory in Constants 31# $TMPPATH 32# Copy trend.cgi into web server cgi-bin, correct permissions 33# Define an Extension in the .cfg file for a particular target 34# routers.cgi*Extension[targetname]: "Trending analysis" /cgi-bin/trend.cgi graph-sm.gif 35# Try out different decay factors 36# 37# You can put 'trendurl = /cgi-bin/trend.cgi' into your routers2.conf 38# to automatically add trending analysis to all targets (undocumented) 39# 40########################################################################## 41 42use strict; 43use CGI; 44use FileHandle; 45use Text::ParseWords; 46use File::Basename; 47 48# Constants 49######################################################################### 50###################### CHANGE THESE TO MAKE IT WORK #################### 51######################################################################### 52# This is the temporary working directory. Must have write permissions. 53my( $TMPPATH ) = "/tmp"; # eg: "C:\\temp" for windows 54######################################################################### 55##################### END OF SECTION YOU HAVE TO CHANGE ################# 56######################################################################### 57my( $DEFPREDICT ) = 50; 58my( $VERSION ) = "2.1"; 59# decay factor can be between 1.0 and 0. 0=history unimportant, 1.0=all 60# data equally important. 61my( @decays ) = ( 1.0, 0.99, 0.95, 0.9, 0.8, 0.66, 0.5 ); 62 63###GLOBAL#START######################################################## 64# Global Variables (change to our() for speedycgi and perl 5.8) 65# To log errors and progress, set debug=1 and set LOG 66my( $debug ) = 0; # override in routers2.conf 'debug = 1' 67my( $LOG ) = "$TMPPATH/trend.log"; # this must be writeable! override with 68 # routers2.conf 'logfile = ...' 69my( $DECAY ) = 0.95; 70my( $PREDICT ) = $DEFPREDICT; 71my( $BASE ) = 0; # 0=current, 1=average 72my( $device, $community, $targetwindow, $target, $file, $backurl ) 73 = ( "","public","graph","","",""); 74my( $conffile ) = "/u01/etc/routers2.conf"; # overridden by passed parameters 75my( $routersurl ) = ''; 76my( $q ) = new CGI; 77my( %headeropts ) = (); 78my( %config ) = (); 79my( $authuser ) = ''; 80my( $pathsep ) = '/'; 81my( %target ) = (); 82my( $tempfile ) = '/tmp/trend.foo'; 83my( $ds, $rrddata , $starttime, $endtime, $interval) = ('','','','',''); 84my( $trendstart, $trenddelta ); # array references 85my( $dwmy ) = 'm'; 86my( %interfaces ); 87my( $workdir ) = ''; 88my( $monthlylabel ) = '%W'; 89my( $dailylabel ) = '%H'; 90my( @params ) = (); 91my( $graphsuffix ) = 'png'; 92my( $lastupdate ) = 0; 93my( $gstyle ) = 'l2'; 94my( $ksym, $k, $M, $G ) = ( 'K', 1024,1024000, 1024000000); 95my( @info ) = (); 96my( $cfile ); 97my( $fgcolour, $bgcolour, $linkcolour ) = ( "#000000", "#ffffff", "#000080" ); 98my( $consolidation, $conspfx ) = ("AVERAGE","Avg"); 99#my( $consolidation, $conspfx ) = ("MAX","Max"); 100my( $myurl ) = $q->url(); 101###GLOBAL#END########################################################## 102 103 104################################# 105# For RRD v1.2 compatibility: remove colons for COMMENT directive if 106# we are in v1.2 or later, else leave them there 107sub decolon($) { 108 my($s) = $_[0]; 109 return $s if($RRDs::VERSION < 1.002 ); 110 $s =~ s/:/\\:/g; 111 return $s; 112} 113 114####################################################################### 115sub errlog($) 116{ 117 return if(!$debug); 118 open LOG, ">>$LOG" or return; 119 LOG->autoflush(1); 120 print LOG "".localtime(time).": ".(join " ",@_)."\n"; 121 close LOG; 122} 123####################################################################### 124sub inlist($@) 125{ 126 my($pat) = shift @_; 127 return 0 if(!defined $pat or !$pat or !@_); 128 foreach (@_) { return 1 if( $_ and /$pat/i ); } 129 return 0; 130} 131 132###################### 133# calculate short date string from given time index 134 135sub shortdate($) 136{ 137 my( $dformat ) = "%c"; # windows perl doesnt have %R 138 my( $datestr, $fmttime ) = ("",0); 139 return "DATE ERROR 1" if(!$_[0]); 140 $fmttime = $_[0]; 141 $fmttime = time if(!$fmttime); 142 my( $sec, $min, $hour, $mday, $mon, $year ) = localtime($fmttime); 143 # try to get local formatting 144 $dformat = $config{'web-shortdateformat'} 145 if(defined $config{'web-shortdateformat'}); 146 $dformat =~ s/ / /g; 147 eval { require POSIX; }; 148 if($@) { 149 $datestr = $mday."/".($mon+1)."/".($year-100); 150 } else { 151 $datestr = POSIX::strftime($dformat, 152 0,$min,$hour,$mday,$mon,$year); 153 } 154 return "DATE ERROR 2" if(!$datestr); 155 return $datestr; 156} 157 158################################# 159# For string trims. Remove leading and trailing blanks 160sub trim($) 161{ 162 my($x)=$_[0]; 163 $x=~s/\s*$//; 164 $x=~s/^\s*//; 165 $x; 166} 167############################################################################# 168# reformat to look nice 169# params -- number, fix flag, integer flag 170sub doformat($$$) 171{ 172 my( $sufx ) = ""; 173 my( $val, $fix, $intf ) = @_; 174 175 return "???" if(!defined $val or $val !~ /\d/ ); 176 return $val if( $val == 0 ); 177 178 if(!$fix) { 179 if( $val >= $G ) { 180 $val /= $G; $sufx = "G"; 181 } elsif( $val >= $M ) { 182 $val /= $M; $sufx = "M"; 183 } elsif( $val >= $k ) { 184 $val /= $k; $sufx = $ksym; 185 } 186 } 187 188 return sprintf "%.0f %s",$val,$sufx 189 if( $intf or ( int($val*100) == (100*int($val)) ) ); 190 return sprintf "%.2f %s",$val,$sufx; 191} 192# Round the number to a set no of decimal places 193sub dp($$) { 194 my($num,$dcp) =@_; 195 my($rv); 196 return '0' if(!$num); 197 $rv = sprintf '%.'.$dcp.'f',$num; 198 $rv =~ s/\.0+$//; # remove trailing .0 199 return $rv; 200} 201 202####################################################################### 203# Read in configuration file 204 205# readconf: pass it a list of section names 206sub readconf(@) 207{ 208 my ($inlist, $i, @secs, $sec, $usersec); 209 @secs = @_; 210 %config = (); 211 $usersec = "\177"; 212 $usersec = "user-".(lc $authuser) if( $authuser ); 213 214 # set defaults 215 %config = ( 216 'routers.cgi-confpath' => ".", 217 'routers.cgi-cfgfiles' => "*.conf *.cfg", 218 'web-png' => 0 219 ); 220 221 ( open CFH, "<".$conffile ) || do { 222 print $q->header({-expires=>"now"}); 223 start_html_ss({ -title => "Error", -bgcolor => "#ffd0d0", 224 -class=>'error' }); 225 print $q->h1("Error").$q->p("Cannot read config file $conffile."); 226 print $q->end_html(); 227 exit(0); 228 }; 229 230 $inlist=0; 231 $sec = ""; 232 while( <CFH> ) { 233 /^\s*#/ && next; 234 /\[(.*)\]/ && do { 235 $sec = lc $1; 236 $inlist=0; 237 foreach $i ( @secs ) { 238 if ( (lc $i) eq $sec ) { $inlist=1; last; } 239 } 240 # override for additional sections 241 # put it here so people cant break things easily 242 next if($inlist); 243 if( ( $sec eq $usersec ) or ( $sec eq 'routers2.cgi' ) ) { 244 $sec = 'routers.cgi'; $inlist = 1; 245 } 246 next; 247 }; 248 # note final \s* to strip all trailing spaces (which works because 249 # the *? operator is non-greedy!) This should also take care of 250 # stripping trailing CR if file created in DOS mode (yeuchk). 251 if ( $inlist ) { /(\S+)\s*=\s*(\S.*?)\s*$/ and $config{"$sec-$1"}=$2; } 252 } 253 close CFH; 254 255 # Activate NT compatibility options. 256 # $^O is the OS name, NT usually produces 'MSWin32'. By checking for 'Win' 257 # we should be able to cover most possibilities. 258 if ( (defined $config{'web-NT'} and $config{'web-NT'}=~/[1y]/i) 259 or $^O =~ /Win/ or $^O =~ /DOS/i ) { 260 $pathsep = "\\"; 261 } 262 263 # backwards compatibility for old v1.x users 264 $config{'routers.cgi-iconurl'} = $config{'routers.cgi-iconpath'} 265 if( !defined $config{'routers.cgi-iconurl'} 266 and defined $config{'routers.cgi-iconpath'} ); 267 268 # some path corrections: remove trailing path separators on f/s paths 269 foreach ( qw/dbpath confpath graphpath graphurl/ ) { 270 $config{"routers.cgi-$_"} =~ s/[\/\\]$//; 271 } 272 # and add a trailing path separator on URL paths... 273 $config{'routers.cgi-iconurl'} = "/rrdicons/" 274 if(!defined $config{'routers.cgi-iconurl'} ); 275 $config{'routers.cgi-iconurl'} .= "/" 276 if( $config{'routers.cgi-iconurl'} !~ /\/$/ ); 277 278 # allow [routers.cgi] section to override [web] section for some 279 # parameters 280 $config{'web-backurl'} = $config{'routers.cgi-backurl'} 281 if(defined $config{'routers.cgi-backurl'}); 282 283 # For broken web servers (eg thttpd) 284 $myurl = $config{'routers.cgi-trendurl'} 285 if(defined $config{'routers.cgi-trendurl'} ); 286 $myurl = $config{'trend.cgi-myurl'} 287 if(defined $config{'trend.cgi-myurl'} ); 288} 289####################################################################### 290# stylesheet start_html 291sub start_html_ss 292{ 293 my($opts,$bgopt) = @_; 294 my($ssheet) = ""; 295 296 $opts->{-encoding} = $config{'web-charset'} if($config{'web-charset'}); 297 298# if(!defined $opts->{'-link'}) { 299# $opts->{'-link'}=$linkcolour; 300# $opts->{'-vlink'}=$linkcolour; 301# $opts->{'-alink'}=$linkcolour; 302# } 303# $opts->{'-text'}=$deffgcolour if(!defined $opts->{'-text'}); 304# $opts->{'-bgcolor'}=$defbgcolour if(!defined $opts->{'-bgcolor'}); 305 306 # If we have overridden things, then put it into the sheet here. 307 # overriding style sheet using mrtg .cfg file options 308 if( $bgopt and $opts->{-class}) { 309 $ssheet .= "body.".$opts->{'-class'}." { background: $bgopt }\n"; 310 } 311 # overriding style sheet using routers2.conf options 312 # default pages 313 if( $config{"routers.cgi-bgcolour"} or $config{"routers.cgi-fgcolour"} ) { 314 $ssheet .= "body { "; 315 $ssheet .= " color: ".$config{"routers.cgi-fgcolour"}."; " 316 if($config{"routers.cgi-fgcolour"}); 317 $ssheet .= " background: ".$config{"routers.cgi-bgcolour"} 318 if($config{"routers.cgi-bgcolour"}); 319 $ssheet .= "}\n"; 320 } 321 # links 322 $ssheet .= "A:link { color: ".$config{'routers.cgi-linkcolour'}. " }\n " 323 ."A:visited { color: ".$config{'routers.cgi-linkcolour'}. " }\n " 324 ."A:hover { color: ".$config{'routers.cgi-linkcolour'}. " } \n" 325 if($config{'routers.cgi-linkcolour'}); 326 327 if($config{'routers.cgi-stylesheet'}) { 328 $opts->{'-style'} = { -src=>$config{'routers.cgi-stylesheet'}, -code=>$ssheet }; 329 } 330 print $q->start_html($opts)."\n"; 331} 332 333####################################################################### 334# possible MODEs: interface, cpu, memory, generic (more to come) 335sub identify($) { 336 my( $key, %identify, $k, @d, $mode ); 337 my($unit,$totunit,$okfile); 338 my($timel, $times); 339 340 $k = $_[0]; 341 342 # description defaults 343 if(defined $config{"targetnames-$k"}) { 344 $interfaces{$k}{shdesc} = $config{"targetnames-$k"}; 345 } 346 if(defined $config{"targettitles-$k"}) { 347 $interfaces{$k}{desc} = $config{"targettitles-$k"}; 348 } 349 if(!defined $interfaces{$k}{shdesc}) { 350 if(!defined $config{'targetnames-ifdefault'} 351 or $config{'targetnames-ifdefault'} !~ /target/ ) { 352 if(defined $interfaces{$k}{ipaddress}) { 353 $interfaces{$k}{shdesc} = $interfaces{$k}{ipaddress}; 354 } elsif(defined $interfaces{$k}{ifdesc}) { 355 $interfaces{$k}{shdesc} = $interfaces{$k}{ifdesc}; 356 } elsif(defined $interfaces{$k}{ifno}) { 357 $interfaces{$k}{shdesc} = "#".$interfaces{$k}{ifno}; 358 } else { 359# $interfaces{$k}{desc} =~ /^(\S+)/; 360# $interfaces{$k}{shdesc} = $1; 361 $interfaces{$k}{shdesc} = $interfaces{$k}{desc}; 362 } 363 } 364 $interfaces{$k}{shdesc} = $k if(!defined $interfaces{$k}{shdesc}); 365 } 366 367 # try and identify the interface 368 @d = ( $k, $interfaces{$k}{desc}, $interfaces{$k}{shdesc} ); 369 $mode = ""; 370 $mode = $interfaces{$k}{mode} if(defined $interfaces{$k}{mode}); 371 %identify = (); 372 if(! $mode) { 373 if( inlist( "cpu", @d ) and ($interfaces{$k}{maxbytes}==100) ) 374 { $mode = "cpu"; } 375 elsif( defined $interfaces{$k}{ifno} or defined $interfaces{$k}{ifdesc} 376 or $interfaces{$k}{isif} or defined $interfaces{$k}{ipaddress} ) 377 { $mode = "interface"; $interfaces{$k}{isif} = 1; } 378 elsif( inlist( "interface", @d ) or inlist("serial",@d) 379 or inlist( "ATM", @d ) or inlist( "[^x]port", @d )) 380 { $mode = "interface"; } 381 elsif( inlist( "mem", @d ) ) { $mode = "memory"; } 382 else { $mode = "generic"; } 383 $interfaces{$k}{mode} = $mode; 384 } 385 386 # set appropriate defaults for thismode 387 if(!defined $interfaces{$k}{mult}) { 388 if($mode eq "interface" and 389 (!defined $config{'routers.cgi-bytes'} 390 or $config{'routers.cgi-bytes'} !~ /y/ ) 391 ) { $interfaces{$k}{mult} = 8; } 392 else { $interfaces{$k}{mult} = 1; } 393 } 394 395 # defaults for everything... 396 $timel = "second"; $times = "s"; $unit = ""; $totunit = ""; 397 if($mode eq "interface") { $totunit = "bytes"; } 398 if($interfaces{$k}{mult} > 3599 ) { 399 $timel = "hour"; $times = "hr"; 400 if($interfaces{$k}{mult} > 3600) { $totunit = "bits"; } 401 } elsif($interfaces{$k}{mult} >59 ) { 402 $timel = "minute"; $times = "min"; 403 if($interfaces{$k}{mult} > 60) { $totunit = "bits"; } 404 } elsif($interfaces{$k}{mult} > 1) { $totunit = "bits"; } 405 $unit = "$totunit/$times"; 406 $unit = "bps" if($unit eq "bits/s"); 407 $identify{ylegend} = "per $timel"; 408 $identify{background} = $bgcolour; 409 $identify{legendi} = "In: "; 410 $identify{legendo} = "Out:"; 411 $identify{legend1} = "Incoming" ; 412 $identify{legend2} = "Outgoing"; 413 $identify{legend3} = "Peak inbound"; 414 $identify{legend4} = "Peak outbound"; 415 $identify{total} = 1; 416 $identify{percentile} = 1; 417 $identify{percent} = 1; 418 $identify{unit} = $unit; 419 $identify{totunit} = $totunit; 420 $identify{unscaled} = ""; 421 422 if($mode eq "interface") { 423 $identify{ylegend} = "traffic in $unit"; 424 $identify{legendi} = "In: "; 425 $identify{legendo} = "Out:"; 426 $identify{legend1} = "Incoming traffic"; 427 $identify{legend2} = "Outgoing traffic"; 428 $identify{legend3} = "Peak inbound traffic"; 429 $identify{legend4} = "Peak outbound traffic"; 430 $identify{icon} = "interface-sm.gif"; 431# $identify{background} = "#ffffff"; 432 $identify{unscaled} = "6dwmy"; 433 $identify{total} = 1; 434 } elsif( $mode eq "cpu" ) { 435 $identify{ylegend} = "Percentage use"; 436 $identify{legendi} = "CPU"; 437 $identify{unit} = "%"; 438 $identify{fixunits} = 1; 439 $identify{totunit} = ""; 440 $identify{legend1} = "CPU usage"; 441 $identify{legend3} = "Peak CPU usage"; 442 $identify{legend2} = ""; 443 $identify{legend4} = ""; 444 $identify{icon} = "cpu-sm.gif"; 445# $identify{background} = "#ffffd0"; 446 $identify{unscaled} = "6dwmy"; 447 $identify{percent} = 0; 448 $identify{total} = 0; 449 } elsif( $mode eq "memory" ) { 450 $identify{ylegend} = "Bytes used"; 451 $identify{legendi} = "MEM"; 452 $identify{legendo} = "MEM"; 453 $identify{legend1} = "Memory usage"; 454 $identify{legend3} = "Peak memory usage"; 455 $identify{legend2} = "Sec. memory usage"; 456 $identify{legend4} = "Peak sec memory usage"; 457 $identify{icon} = "cpu-sm.gif"; 458# $identify{background} = "#d0d0ff"; 459 $identify{total} = 0; 460 $identify{unit} = "bytes"; 461 $identify{unit} = "bits" if($interfaces{$k}{bits}); 462 $identify{totunit} = ""; 463 } elsif( $mode eq "ping" ) { 464 $identify{totunit} = ""; 465 $identify{unit} = "ms"; 466 $identify{fixunit} = 1; 467 $identify{ylegend} = "milliseconds"; 468 $identify{legendi} = "High:"; 469 $identify{legendo} = "Low:"; 470 $identify{legend1} = "Round trip time range"; 471 $identify{legend2} = "Round trip time range"; 472 $identify{legend3} = "High peak 5min RTT"; 473 $identify{legend4} = "Low peak 5min RTT"; 474 $identify{icon} = "clock-sm.gif"; 475# $identify{background} = "#ffffdd"; 476 $identify{total} = 0; 477 $identify{percent} = 0; 478 $identify{percentile} = 0; 479 $identify{unscaled} = ""; 480 } 481 482 # unscaled default option 483 if( defined $config{'routers.cgi-unscaled'} ) { 484 if( $config{'routers.cgi-unscaled'} =~ /y/i ) { 485 $identify{unscaled} = "6dwmy" ; 486 } else { 487 $identify{unscaled} = "" ; 488 } 489 } 490 491 # set icon 492 $identify{icon} = guess_icon( 0, $k, $interfaces{$k}{desc}, $interfaces{$k}{shdesc} ) if(!defined $identify{icon}); 493 494 # different default for totunit 495 # if we have a custom 'unit' but no custom 'totunit', then try to be 496 # a bit more clever. 497 if( defined $interfaces{$k}{unit} ) { 498 my( $u ) = $interfaces{$k}{unit}; 499 if( $u =~ /^(.*)\// ) { 500 $identify{totunit} = $1; 501 } elsif( $u =~ /^(.*)ps$/ ) { 502 $identify{totunit} = $1; 503 } else { 504 $identify{totunit} = $u; 505 } 506 } 507 508 # set the defaults 509 foreach $key ( keys %identify ) { 510 $interfaces{$k}{$key} = $identify{$key} 511 if(!defined $interfaces{$k}{$key} ); 512 } 513 514 $interfaces{$k}{mult} = 1 if(!defined $interfaces{$k}{mult}); 515 $interfaces{$k}{maxbytes} = 0 if(!defined $interfaces{$k}{maxbytes}); 516 $interfaces{$k}{max} = $interfaces{$k}{maxbytes} * $interfaces{$k}{mult}; 517 $interfaces{$k}{max1} = $interfaces{$k}{maxbytes1} * $interfaces{$k}{mult} 518 if(defined $interfaces{$k}{maxbytes1}); 519 $interfaces{$k}{max2} = $interfaces{$k}{maxbytes2} * $interfaces{$k}{mult} 520 if(defined $interfaces{$k}{maxbytes2}); 521 $interfaces{$k}{max} = $interfaces{$k}{max1} if(defined $interfaces{$k}{max1} and $interfaces{$k}{max1} > $interfaces{$k}{max} ); 522 $interfaces{$k}{max} = $interfaces{$k}{max2} if(defined $interfaces{$k}{max2} and $interfaces{$k}{max2} > $interfaces{$k}{max} ); 523 $interfaces{$k}{absmax} 524 = $interfaces{$k}{absmaxbytes} * $interfaces{$k}{mult} 525 if(defined $interfaces{$k}{absmaxbytes}); 526 if(defined $interfaces{$k}{factor} ) { 527 $interfaces{$k}{max} *= $interfaces{$k}{factor}; 528 $interfaces{$k}{absmax} *= $interfaces{$k}{factor} 529 if(defined $interfaces{$k}{absmax}); 530 $interfaces{$k}{max1} *= $interfaces{$k}{factor} 531 if(defined $interfaces{$k}{max1}); 532 $interfaces{$k}{max2} *= $interfaces{$k}{factor} 533 if(defined $interfaces{$k}{max2}); 534 } 535 $interfaces{$k}{noo} = 1 if(!$interfaces{$k}{legend2}); 536 $interfaces{$k}{noi} = 1 if(!$interfaces{$k}{legend1}); 537 538 # catch the stupid people 539 if($interfaces{$k}{noo} and $interfaces{$k}{noi}) { 540 $interfaces{$k}{inmenu} = 0; 541 $interfaces{$k}{insummary} = 0; 542 $interfaces{$k}{inout} = 0; 543 } 544 545} 546# guess an appropriate icon. 1st param is 1 for devices menu, 0 for targets 547# other parameters are a list of attributes to check 548sub guess_icon($@) 549{ 550 my($m) = shift @_; 551 552 if($m) { 553 # these tests for devices menu only 554 return "cisco-sm.gif" if( inlist "cisco",@_ ); 555 return "3com-sm.gif" if( inlist "3com",@_ ); 556 return "intel-sm.gif" if( inlist "intel",@_ ); 557 return "router-sm.gif" if( inlist "router",@_ ); 558 return "switch-sm.gif" if( inlist "switch",@_ ); 559 return "firewall-sm.gif" if( inlist "firewall",@_ ); 560 return "ibm-sm.gif" if( inlist "ibm",@_ ); 561 return "linux-sm.gif" if( inlist "linux",@_ ); 562 return "freebsd-sm.gif" if( inlist "bsd",@_ ); 563 return "novell-sm.gif" if( inlist "novell",@_ ); 564# these commented out as patterns are too short to be reliable 565# return "mac-sm.gif" if( inlist "mac|apple",@_ ); 566# return "sun-sm.gif" if( inlist "sun",@_ ); 567# return "hp-sm.gif" if( inlist "hp",@_ ); 568 return "win-sm.gif" if( inlist "windows",@_ ); 569 } 570 return "mail-sm.gif" if( inlist 'mail|messages',@_ ); 571 return "web-sm.gif" if( inlist 'internet',@_ or inlist 'proxy',@_ ); 572 return "phone-sm.gif" if( inlist 'phone',@_ ); 573 return "modem-sm.gif" if( inlist 'modem',@_ ); 574 return "disk-sm.gif" if( inlist 'nfs\w',@_ or inlist 'dsk',@_ ); 575 return "globe-sm.gif" if( inlist 'dns\w',@_ ); 576 return "people-sm.gif" if( inlist 'user[s ]',@_ ); 577 return "server-sm.gif" if( inlist 'server|host',@_ ); 578 return "web-sm.gif" if( inlist 'web',@_ ); 579 return "traffic-sm.gif" if( inlist 'traffic',@_ ); 580 return "chip-sm.gif" if( inlist 'memory|cpu',@_ ); 581 return "interface-sm.gif" if(!$m and inlist 'interface|serial',@_ ); 582 return "disk-sm.gif" if( inlist 'dis[kc]|filesystem',@_ ); 583 return "clock-sm.gif" if( inlist 'time|rtt|ping',@_ ); 584 return "temp-sm.gif" if( inlist 'temp|climate|environment|heat',@_ ); 585 return "menu-sm.gif" if( inlist '\wlog',@_ ); 586 return "interface-sm.gif" if(!$m and inlist 'BRI|eth|tok|ATM|hme',@_ ); 587 return "load-sm.gif" if( inlist 'load|weight',@_ ); 588 return "web-sm.gif" if( inlist 'www',@_ ); 589 590 if($m) { 591 # last chance with these less reliable ones 592 return "mac-sm.gif" if( inlist "mac|apple",@_ ); 593 return "sun-sm.gif" if( inlist "sun",@_ ); 594 return "hp-sm.gif" if( inlist "hp",@_ ); 595 return "win-sm.gif" if( inlist "win|pdc|bdc",@_ ); 596 } 597 598 if($m) { 599 return $config{'targeticons-filedefault'} 600 if(defined $config{'targeticons-filedefault'}); 601 return "menu-sm.gif"; 602 } else { 603 return $config{'targeticons-ifdefault'} 604 if(defined $config{'targeticons-ifdefault'}); 605 return "target-sm.gif"; 606 } 607} 608 609####################################################################### 610# read in a specified cfg file (default to current router file) 611 612# interfaces hash: key= targetname 613# data: hash: 614# keys: lots. 615 616sub read_cfg_file 617{ 618 my($cfgfile) = $_[0]; 619 my($opts, $graph, $key, $k, $fd, $buf, $curif, @myifs, $arg,$rrd); 620 my($ifcnt, @ifarr, $t, $desc, $url, $icon, $targ, $newfile, $targfile); 621 my( $lasthostname, $lastcommunity ) = ("",""); 622 623 my( $inpagetop, $inpagefoot ) = (0,0); 624 625 return if(!$cfgfile); 626 627 $fd = new FileHandle ; 628 629 if(! $fd->open( "<$cfgfile" )) { 630 return; 631 } 632 633 $key = ""; $curif = ""; @myifs = (); 634 while ( $buf = <$fd> ) { 635 next if( $buf =~ /^\s*#/ ); 636 next if( $buf =~ /^\s*$/ ); # bit more efficient 637 if( $inpagefoot ) { 638 if( $curif and $buf =~ /^\s+\S/ ) { 639 $interfaces{$curif}{pagefoot} .= $buf; 640 next; 641 } 642 $inpagefoot = 0; 643 } 644 if( $inpagetop ) { 645 if( $curif and $buf =~ /^\s+\S/ ) { 646 $interfaces{$curif}{pagetop} .= $buf; 647 next; 648 } 649 $inpagetop = 0; 650 } 651 if( $buf =~ /^\s*Target\[(.+?)\]\s*:\s*(.+)/i ) { 652 $curif = $1; $arg = $2; 653 next if(defined $interfaces{$curif}); 654 push @myifs, $curif; 655 $interfaces{$curif} = { file=>$cfgfile, target=>$curif, 656 insummary=>1, incompact=>1, inmenu=>1, isif=>0, 657 interval=>$interval, nomax=>0, noabsmax=>0 }; 658 $interfaces{$curif}{rrd} = $workdir.$pathsep.(lc $curif).".rrd"; 659 if( $arg =~ /^-?(\d+):([^\@:\s]+)\@([\w\-\.]+)/ ) { 660 # interface number 661 $interfaces{$curif}{isif} = 1; 662 $interfaces{$curif}{ifno} = $1; 663 $interfaces{$curif}{community} = $2; 664 $interfaces{$curif}{hostname} = $3; 665 $interfaces{$curif}{mode} = "interface"; 666 } elsif( $arg =~ /^-?\/(\d+\.\d+\.\d+\.\d+):([^\@:\s]+)\@([\w\-\.]+)/ ) { 667 # IP address 668 $interfaces{$curif}{isif} = 1; 669 $interfaces{$curif}{ipaddress} = $1; 670 $interfaces{$curif}{community} = $2; 671 $interfaces{$curif}{hostname} = $3; 672 $interfaces{$curif}{mode} = "interface"; 673 } elsif( $arg =~ /^-?[\\#!](\S.*?):([^\@:\s]+)\@([\w\-\.]+)/ ) { 674 $interfaces{$curif}{isif} = 1; 675 $interfaces{$curif}{ifdesc} = $1; 676 $interfaces{$curif}{community} = $2; 677 $interfaces{$curif}{hostname} = $3; 678 $interfaces{$curif}{mode} = "interface"; 679 $interfaces{$curif}{ifdesc} =~ s/\\(.)/$1/g ; 680 } elsif( $arg =~ /&\w*[\d\.]+:(\S+)\@([\w\-\.]+)/ ) { 681 # explicit OIDs 682 $interfaces{$curif}{community} = $1; 683 $interfaces{$curif}{hostname} = $2; 684 } elsif( $arg =~ /mrtg.ping.probe/ ) { 685 # special for the mrtg-ping-probe.pl 686 $interfaces{$curif}{mode} = "ping"; 687 $interfaces{$curif}{graphstyle} = "range"; 688 $interfaces{$curif}{incompact} = 1; 689 $interfaces{$curif}{ifdesc} = "Response time" ; 690 } elsif( $arg =~ /`/ ) { 691 # external program 692 $interfaces{$curif}{insummary} = 1; 693 $interfaces{$curif}{incompact} = 1; 694 } else { # a target of some sort we dont yet know 695 $interfaces{$curif}{insummary} = 0; 696 $interfaces{$curif}{incompact} = 0; 697 } 698 $interfaces{$curif}{inout} = $interfaces{$curif}{isif}; 699 foreach $k ( qw/isif inout incompact insummary inmenu/ ) { 700 $interfaces{$curif}{$k} = $interfaces{'_'}{$k} 701 if(defined $interfaces{'_'}{$k}); 702 } 703 $lasthostname = $interfaces{$curif}{hostname} 704 if(defined $interfaces{$curif}{hostname}); 705 $lastcommunity= $interfaces{$curif}{community} 706 if(defined $interfaces{$curif}{community}); 707 next; 708 } 709 if( $buf =~ /^\s*(routers\.cgi\*)?(Title|Descr?)\[(.+?)\]\s*:\s*(\S.*)/i ) { 710 $curif = $3; $arg = $4; 711 if(!defined $interfaces{$curif}) { 712 $curif = "_$curif"; 713 next if(!defined $interfaces{$curif}); 714 } 715 $interfaces{$curif}{desc} = $arg; 716 next; 717 } 718 if( $buf =~ /^\s*Options\[(.+?)\]\s*:\s*(\S.*)/i ) { 719 $curif = $1; 720 next if(!defined $interfaces{$curif}); 721 $interfaces{$curif}{options} = "" if(!$interfaces{$curif}{options}); 722 $interfaces{$curif}{options} .= ' '.$2; 723 next; 724 } 725 if( $buf =~ /^\s*(routers\.cgi\*)?PageTop\[(.+?)\]\s*:\s*(\S.*)/i ) { 726 $curif = $2; $arg = $3; 727 next if(!defined $interfaces{$curif}); 728 $interfaces{$curif}{pagetop} = $arg; 729 $inpagetop = 1; 730 next; 731 } 732 if( $buf =~ /^\s*(routers\.cgi\*)?PageFoot\[(.+?)\]\s*:\s*(\S.*)/i ) { 733 $curif = $2; $arg = $3; 734 next if(!defined $interfaces{$curif}); 735 $interfaces{$curif}{pagefoot} = $arg; 736 $inpagefoot = 1; 737 next; 738 } 739 if( $buf =~ /^\s*SetEnv\[(.+?)\]\s*:\s*(\S.*)/i ) { 740 $curif = $1; $arg = $2; 741 next if(!defined $interfaces{$curif}); 742 foreach $k ( quotewords('\s+',0,$arg) ) { 743 if( $k =~ /MRTG_INT_IP=\s*(\d+\.\d+\.\d+\.\d+)/ ) { 744 $interfaces{$curif}{ipaddress}=$1 745 if(!defined $interfaces{$curif}{ipaddress}); 746 next; 747 } 748 if( $k =~ /MRTG_INT_DESCR?=\s*(\S.*)/ ) { 749 $interfaces{$curif}{shdesc}=$1 750 if(!defined $interfaces{$curif}{shdesc}); 751 next; 752 } 753 } 754 next; 755 } 756 if( $buf =~ /^\s*routers\.cgi\*Short(Name|Descr?)\[(.+?)\]\s*:\s*(\S.*)/i ) { 757 $curif = $2; $arg = $3; 758 $curif = "_".$curif if(!defined $interfaces{$curif}); 759 next if(!defined $interfaces{$curif}); 760 $interfaces{$curif}{shdesc} = $arg if($arg); 761 next; 762 } 763 if( $buf =~ /^\s*routers\.cgi\*Options\[(.+?)\]\s*:\s*(\S.*)/i ) { 764 next if(!defined $interfaces{$1}); 765 $interfaces{$1}{cgioptions}="" if(!$interfaces{$1}{cgioptions}); 766 $interfaces{$1}{cgioptions} .= " ".$2; 767 next; 768 } 769 if( $buf =~ /^\s*(routers\.cgi\*)?MaxBytes\[(.+?)\]\s*:\s*(\d+)/i ) { 770 next if(!defined $interfaces{$2}); 771 $interfaces{$2}{maxbytes} = $3; 772 next; 773 } 774 if($buf=~ /^\s*(routers\.cgi\*)?Unscaled\[(.+?)\]\s*:\s*([6dwmy]*)/i){ 775 next if(!defined $interfaces{$2}); 776 $interfaces{$2}{unscaled} = $3; 777 next; 778 } 779 if($buf=~ /^\s*(routers\.cgi\*)?WithPeaks?\[(.+?)\]\s*:\s*([dwmy]*)/i) { 780 next if(!defined $interfaces{$2}); 781 $interfaces{$2}{withpeak} = $3; 782 next; 783 } 784 if( $buf =~ /^\s*(routers\.cgi\*)?YLegend\[(.+?)\]\s*:\s*(\S.*)/i ) { 785 $curif = $2; 786 $curif = "_$curif" if(!defined $interfaces{$curif}); 787 next if(!defined $interfaces{$curif}); 788 $interfaces{$curif}{ylegend} = $3; 789 next; 790 } 791 if($buf=~ /^\s*(routers\.cgi\*)?ShortLegend\[(.+?)\]\s*:\s*(.*)/i){ 792 next if(!defined $interfaces{$2}); 793 $interfaces{$2}{unit} = $3; 794 $interfaces{$2}{unit} =~ s/ / /g; 795 next; 796 } 797 if($buf=~ /^\s*routers\.cgi\*TotalLegend\[(.+?)\]\s*:\s*(.*)/i){ 798 $curif = $1; $arg = $2; 799 next if(!defined $interfaces{$curif}); 800 $arg =~ s/ / /g; 801 $interfaces{$curif}{totunit} = $arg; 802 next; 803 } 804 if( $buf =~ /^\s*(routers\.cgi\*)?(Legend[IO1234TA][IO]?)\[(.+?)\]\s*:\s*(\S.*)/i ) { 805 $curif = $3; $key = lc $2; $arg = $4; 806 $curif = "_$curif" if(!defined $interfaces{$curif}); 807 next if(!defined $interfaces{$curif}); 808 $arg =~ s/ / /; 809 $interfaces{$curif}{$key} = $arg; 810 next; 811 } 812 if( $buf =~ /^\s*routers\.cgi\*Mode\[(.+?)\]\s*:\s*(\S+)/i ) { 813 next if(!defined $interfaces{$1}); 814 $interfaces{$1}{mode} = $2; 815 next; 816 } 817 if( $buf =~ /^\s*routers\.cgi\*Graph\[(.+?)\]\s*:\s*(\S.*)/i ) { 818 $curif = $1; $arg = $2; 819 if( $arg =~ /^"/ ) { 820 $arg =~ /^"([^"]+)"\s*:?(.*)/; 821 $opts = $2; $graph = $1; 822 } else { 823 $arg =~ /^(\S+)\s*:?(.*)/; 824 $opts = $2; $graph = $1; 825 } 826 next if(!$graph); 827 $interfaces{$curif}{usergraphs} = [] 828 if(!defined $interfaces{$curif}{usergraphs}); 829 push @{$interfaces{$curif}{usergraphs}}, $graph; 830 if( defined $interfaces{"_$graph"} ) { 831 push @{$interfaces{"_$graph"}{targets}}, $curif; 832 $interfaces{"_$graph"}{cgioptions} .= " $opts"; 833 } else { 834 $interfaces{"_$graph"} = { 835 shdesc=>$graph, targets=>[$curif], 836 cgioptions=>$opts, mode=>"\177_USER", 837 usergraph=>1, icon=>"cog-sm.gif", inout=>0, incompact=>0, 838 insummary=>0, inmenu=>1, desc=>"User defined graph $graph", 839 withtotal=>0, withaverage=>0 840 }; 841 $interfaces{"_$graph"}{withtotal} = 1 842 if( defined $config{'routers.cgi-showtotal'} 843 and $config{'routers.cgi-showtotal'}=~/y/i); 844 push @myifs, "_$graph"; 845 } 846 next; 847 } 848 if( $buf =~ /^\s*routers.cgi\*Icon\[(.+?)\]\s*:\s*(\S+)/i ) { 849 $curif = $1; $arg = $2; 850 if(!defined $interfaces{$curif}) { 851 $curif = "_$curif"; 852 next if(!defined $interfaces{$curif}); 853 } 854 next if(!defined $interfaces{$curif}); 855 $interfaces{$curif}{icon} = $arg; 856 next; 857 } 858 if( $buf =~ /^\s*routers.cgi\*Ignore\[(.+?)\]\s*:\s*(\S+)/i ) { 859 $curif = $1; $arg = $2; 860 next if(!defined $interfaces{$curif}); 861 if( $arg =~ /y/i ) { 862 $interfaces{$curif}{insummary} = 0; 863 $interfaces{$curif}{inmenu} = 0; 864 $interfaces{$curif}{inout} = 0; 865 $interfaces{$curif}{isif} = 0; 866 } 867 next; 868 } 869 if( $buf =~ /^\s*routers.cgi\*InSummary\[(.+?)\]\s*:\s*(\S+)/i ) { 870 $curif = $1; $arg = $2; 871 next if(!defined $interfaces{$curif}); 872 if( $arg =~ /[1y]/i ) { $interfaces{$curif}{insummary} = 1; } 873 else { $interfaces{$curif}{insummary} = 0; } 874 next; 875 } 876 if( $buf =~ /^\s*routers.cgi\*InMenu\[(.+?)\]\s*:\s*(\S+)/i ) { 877 $curif = $1; $arg = $2; 878 next if(!defined $interfaces{$curif}); 879 if( $arg =~ /[1y]/i ) { $interfaces{$curif}{inmenu} = 1; } 880 else { $interfaces{$curif}{inmenu} = 0; } 881 next; 882 } 883 if( $buf =~ /^\s*routers.cgi\*InOut\[(.+?)\]\s*:\s*(\S+)/i ) { 884 $curif = $1; $arg = $2; 885 next if(!defined $interfaces{$curif}); 886 if( $arg =~ /[1y]/i ) { $interfaces{$curif}{inout} = 1; } 887 else { $interfaces{$curif}{inout} = 0; } 888 next; 889 } 890 if( $buf =~ /^\s*routers.cgi\*InCompact\[(.+?)\]\s*:\s*(\S+)/i ) { 891 $curif = $1; $arg = $2; 892 next if(!defined $interfaces{$curif}); 893 if( $arg =~ /[1y]/i ) { $interfaces{$curif}{incompact} = 2; } 894 else { $interfaces{$curif}{incompact} = 0; } 895 next; 896 } 897 if( $buf =~ /^\s*Background\[(.+?)\]\s*:\s*(#[a-f\d]+)/i ) { 898 next if(!defined $interfaces{$1}); 899 $interfaces{$1}{background} = $2; 900 next; 901 } 902 if( $buf =~ /^\s*Timezone\[(.+?)\]\s*:\s*(\S.*)/i ) { 903 next if(!defined $interfaces{$1}); 904 $interfaces{$1}{timezone} = $2; 905 next; 906 } 907 if( $buf =~ /^\s*Directory\[(.+?)\]\s*:\s*(\S.*)/i ) { 908 $curif = $1; $arg = $2; 909 next if(!defined $interfaces{$curif}); 910 $arg =~ s/[\s\\\/]+$//; # trim trailing spaces and path separators! 911 $interfaces{$curif}{directory} = $arg; 912 $interfaces{$curif}{rrd} = 913 $workdir.$pathsep.$arg.$pathsep.(lc $curif).".rrd"; 914 next; 915 } 916 if( $buf =~ /^\s*Workdir\s*:\s*(\S+)/i ) { 917 $workdir = $1; $workdir =~ s/[\\\/]+$//; next; } 918 if( $buf =~ /^\s*Interval\s*:\s*(\d+)/i ) { $interval = $1; next; } 919 if( $buf =~ /^\s*Include\s*:\s*(\S+)/i ) { 920 $newfile = $1; 921 $newfile = (dirname $cfgfile).$pathsep.$newfile 922 if( $newfile !~ /^([a-zA-Z]:)?[\/\\]/ ); 923 read_cfg_file($newfile); 924 next; 925 } 926 if( $buf =~ /^\s*LibAdd\s*:\s*(\S+)/i ) { unshift @INC, $1; next; } 927 if($buf=~ /^\s*(routers\.cgi\*)?MaxBytes(\d)\[(\S+)\]\s*:\s*(\d+)/i ){ 928 $curif = $3; $arg = $4; 929 next if(!defined $interfaces{$curif}); 930 $interfaces{$curif}{"maxbytes$2"} = $arg; 931 $interfaces{$curif}{maxbytes} = $arg 932 if(!$interfaces{$curif}{maxbytes}); 933 next; 934 } 935 # the regexp from hell 936 if( $buf =~ /^\s*(routers\.cgi\*)?Colou?rs\[(.+?)\]\s*:\s*[^#]*(#[\da-f]{6})[\s,]+[^#]*(#[\da-f]{6})[\s,]+[^#]*(#[\da-f]{6})[\s,]+[^#]*(#[\da-f]{6})/i ) { 937 $curif = $2; 938 $curif = "_$curif" if(!defined $interfaces{$curif}); 939 next if(!defined $interfaces{$curif}); 940 $interfaces{$curif}{colours} = [ $3,$4,$5,$6 ]; 941 next; 942 } 943 if( $buf =~ /^\s*routers\.cgi\*MBLegend\[(.+?)\]\s*:\s*(\S.*)/i ) { 944 $curif = $1; 945 $curif = "_$curif" if(!defined $interfaces{$curif}); 946 $interfaces{$curif}{mblegend} = $2; 947 next; 948 } 949 if( $buf =~ /^\s*routers\.cgi\*AMLegend\[(.+?)\]\s*:\s*(\S.*)/i ) { 950 $curif = $1; 951 $curif = "_$curif" if(!defined $interfaces{$curif}); 952 $interfaces{$curif}{amlegend} = $2; 953 next; 954 } 955 if( $buf =~ /^\s*(routers\.cgi\*)?AbsMax\[(.+?)\]\s*:\s*(\d+)/i ) { 956 next if(!defined $interfaces{$2}); 957 $interfaces{$2}{absmaxbytes} = $3; 958 next; 959 } 960 if( $buf =~ /^\s*WeekFormat(\[.+?\])?\s*:\s*%?([UVW])/i ) { 961 # yes I know this is ugly, it is being retrofitted 962 $monthlylabel = "%".$2; 963 next; 964 } 965 if( $buf =~ /^\s*routers\.cgi\*GraphStyle\[(.+?)\]\s*:\s*(\S+)/i ) { 966 $curif = $1; $arg = $2; 967 $curif = "_$curif" if(!defined $interfaces{$curif}); 968 next if(!defined $interfaces{$curif}); 969 $interfaces{$curif}{graphstyle} = $arg; 970 next; 971 } 972 if( $buf =~ /^\s*(routers\.cgi\*)?Factor\[(.+?)\]\s*:\s*([\d\.]+)/i ) { 973 $curif = $2; $arg = $3; 974 next if(!defined $interfaces{$curif}); 975 $interfaces{$curif}{factor} = $arg if($arg > 0); 976 next; 977 } 978 if( $buf =~ /^\s*(routers\.cgi\*)?Supp?ress?\[(.+?)\]\s*:\s*(\S+)/i ) { 979 $curif = $2; $arg = $3; 980 $curif = "_$curif" if(!defined $interfaces{$curif}); 981 next if(!defined $interfaces{$curif}); 982 $interfaces{$curif}{suppress} = $arg; 983 next; 984 } 985 } 986 $fd->close; 987 988 # now take the current file defaults 989 foreach $key ( keys %{$interfaces{'_'}} ) { 990 foreach $curif ( @myifs ) { 991 $interfaces{$curif}{$key} = $interfaces{'_'}{$key} 992 if(!defined $interfaces{$curif}{$key}); 993 } 994 } 995 foreach $key ( keys %{$interfaces{'^'}} ) { 996 foreach $curif ( @myifs ) { 997 $interfaces{$curif}{$key} = $interfaces{'^'}{$key}.' '.$interfaces{$curif}{$key}; 998 } 999 } 1000 foreach $key ( keys %{$interfaces{'$'}} ) { 1001 foreach $curif ( @myifs ) { 1002 $interfaces{$curif}{$key} .= ' '.$interfaces{'$'}{$key}; 1003 } 1004 } 1005 1006 # now process the options 1007 foreach $curif ( @myifs ) { 1008 next if(!$curif); 1009 if(defined $interfaces{$curif}{options} ) { 1010 foreach $k ( split /[\s,]+/,$interfaces{$curif}{options} ) { 1011 $interfaces{$curif}{noo} = 1 if( $k eq "noo"); 1012 $interfaces{$curif}{noi} = 1 if( $k eq "noi"); 1013 if( $k eq "bits") { $interfaces{$curif}{bits} = 1; } 1014 if( $k eq "perminute") { 1015 $interfaces{$curif}{perminute} = 1 1016 if(!defined $interfaces{$curif}{perhour} 1017 and !defined $interfaces{$curif}{perminute}); 1018 } 1019 if( $k eq "perhour") { 1020 $interfaces{$curif}{perhour} = 1 1021 if(!defined $interfaces{$curif}{perhour} 1022 and !defined $interfaces{$curif}{perminute}); 1023 } 1024 if( $k eq "nopercent") { 1025 $interfaces{$curif}{percent} = 0 ; 1026 # default incompact to NO if target has nopercent set 1027 $interfaces{$curif}{incompact} = 0 1028 if($interfaces{$curif}{incompact} == 1); 1029 } 1030 $interfaces{$curif}{integer} = 1 if( $k eq "integer"); 1031 } } 1032 if ( defined $interfaces{$curif}{cgioptions} ) { 1033 foreach $k ( split /[\s,]+/,$interfaces{$curif}{cgioptions} ) { 1034 $interfaces{$curif}{available} = 1 if( $k eq "available"); 1035 $interfaces{$curif}{available} = 0 if( $k eq "noavailable"); 1036 $interfaces{$curif}{noo} = 1 if( $k eq "noo"); 1037 $interfaces{$curif}{noi} = 1 if( $k eq "noi"); 1038# $interfaces{$curif}{mult} = 8 if( $k eq "bits"); 1039# $interfaces{$curif}{mult} = 1 if( $k eq "bytes"); 1040 $interfaces{$curif}{noi} = 1 if( $k eq "noi"); 1041 if( $k eq "bytes") { $interfaces{$curif}{bytes} = 1; 1042 $interfaces{$curif}{bits} = 0; } 1043 if( $k eq "bits") { $interfaces{$curif}{bits} = 1; 1044 $interfaces{$curif}{bytes} = 0; } 1045 if( $k eq "perminute") { 1046 $interfaces{$curif}{perminute} = 1 1047 if(!defined $interfaces{$curif}{perhour} 1048 and !defined $interfaces{$curif}{perminute}); 1049 } 1050 if( $k eq "perhour") { 1051 $interfaces{$curif}{perhour} = 1 1052 if(!defined $interfaces{$curif}{perhour} 1053 and !defined $interfaces{$curif}{perminute}); 1054 } 1055 $interfaces{$curif}{isif} = 1 if($k eq "interface"); 1056 if( $k eq "ignore") { 1057 $interfaces{$curif}{inmenu} = 0 ; 1058 $interfaces{$curif}{insummary} = 0 ; 1059 $interfaces{$curif}{inout} = 0 ; 1060 $interfaces{$curif}{incompact} = 0 ; 1061 } 1062 $interfaces{$curif}{unscaled} = "" if( $k eq "scaled"); 1063 $interfaces{$curif}{total} = 0 if( $k eq "nototal"); 1064 $interfaces{$curif}{percentile} = 0 if( $k eq "nopercentile"); 1065 if( $k eq "summary" ) { 1066 $interfaces{$curif}{summary} = 1; 1067 $interfaces{$curif}{compact} = 0; 1068 $interfaces{$curif}{withtotal} = 0; 1069 $interfaces{$curif}{withaverage} = 0; 1070 $interfaces{$curif}{insummary} = 0 ; 1071 $interfaces{$curif}{incompact} = 0 ; 1072 } 1073 if( $k eq "compact" ) { 1074 $interfaces{$curif}{summary} = 0; 1075 $interfaces{$curif}{compact} = 1; 1076 $interfaces{$curif}{withtotal} = 0; 1077 $interfaces{$curif}{withaverage} = 0; 1078 $interfaces{$curif}{insummary} = 0 ; 1079 $interfaces{$curif}{incompact} = 0 ; 1080 } 1081 if( $k eq "total") { 1082 $interfaces{$curif}{withtotal} = 1 ; 1083 $interfaces{$curif}{total} = 1 ; 1084 } 1085 $interfaces{$curif}{withaverage} = 1 if( $k eq "average"); 1086 $interfaces{$curif}{nolegend} = 1 if( $k eq "nolegend"); 1087 $interfaces{$curif}{nodetails} = 1 if( $k eq "nodetails"); 1088 $interfaces{$curif}{nomax} = 1 if( $k eq "nomax"); 1089 $interfaces{$curif}{noabsmax} = 1 if( $k eq "noabsmax"); 1090 $interfaces{$curif}{percent} = 0 if( $k eq "nopercent"); 1091 $interfaces{$curif}{integer} = 1 if( $k eq "integer"); 1092 if( $k =~ /^#[\da-fA-F]{6}$/ ) { 1093 $interfaces{$curif}{colours} = [] 1094 if(!defined $interfaces{$curif}{colours}); 1095 push @{$interfaces{$curif}{colours}}, $k; 1096 } 1097 $interfaces{$curif}{fixunits} = 1 1098 if( $k =~ /^fixunits?/i or $k =~ /^nounits?/i ); 1099 } 1100 } 1101 # fix the mult 1102 if($interfaces{$curif}{bytes}) { 1103 $interfaces{$curif}{mult} = 1; 1104 } elsif($interfaces{$curif}{bits} ) { 1105 $interfaces{$curif}{mult} = 8; 1106 } 1107 if($interfaces{$curif}{perminute}) { 1108 $interfaces{$curif}{mult}=1 if(!$interfaces{$curif}{mult}); 1109 $interfaces{$curif}{mult} *= 60; 1110 } elsif($interfaces{$curif}{perhour}) { 1111 $interfaces{$curif}{mult}=1 if(!$interfaces{$curif}{mult}); 1112 $interfaces{$curif}{mult} *= 3600; 1113 } 1114 # sanity check 1115 if( $interfaces{$curif}{incompact} and !$interfaces{$curif}{maxbytes}){ 1116 $interfaces{$curif}{incompact} = 0; 1117 } 1118 # RRD file name 1119 if(! $interfaces{$curif}{usergraph} ) { 1120 $rrd = $workdir; 1121 $rrd .= $pathsep.$interfaces{$curif}{directory} 1122 if($interfaces{$curif}{directory}); 1123 $rrd .= $pathsep.(lc $curif).".rrd"; 1124 $interfaces{$curif}{rrd} = $rrd; 1125 } 1126 } 1127 1128 # now read the corresponding .ok file, if it exists 1129 $cfgfile =~ s/\.conf$/.ok/; 1130 $cfgfile =~ s/\.cfg$/.ok/; 1131 if( open OK, "<$cfgfile" ) { 1132 my( %ifdesc ) = (); 1133 my( %ifip ) = (); 1134 while( <OK> ) { 1135 if( /\tDescr\t(.+)\t(\d+)/ ) { 1136 $ifdesc{$2} = $1; $ifdesc{$1} = $2; 1137 } 1138 if( /\tIp\t(.+)\t(\d+)/ ) { 1139 $ifip{$2} = $1; $ifip{$1} = $2; 1140 } 1141 } 1142 close OK; 1143 1144 foreach $curif ( @myifs ) { 1145 if(!defined $interfaces{$curif}{ifno}) { 1146 $interfaces{$curif}{ifno} = $ifdesc{$interfaces{$curif}{ifdesc}} 1147 if(defined $interfaces{$curif}{ifdesc} 1148 and defined $ifdesc{$interfaces{$curif}{ifdesc}}); 1149 $interfaces{$curif}{ifno} = $ifip{$interfaces{$curif}{ipaddress}} 1150 if(defined $interfaces{$curif}{ipaddress} 1151 and defined $ifip{$interfaces{$curif}{ipaddress}}); 1152 } 1153 if(defined $interfaces{$curif}{ifno}) { 1154 $key = $interfaces{$curif}{ifno}; 1155 $interfaces{$curif}{ifdesc} = $ifdesc{$key} 1156 if(defined $ifdesc{$key} and !defined $interfaces{$curif}{ifdesc}); 1157 $interfaces{$curif}{ipaddress} = $ifip{$key} 1158 if(defined $ifip{$key} and !defined $interfaces{$curif}{ipaddress}); 1159 } 1160 } 1161 } # ok file exists 1162 1163 # now set up userdefined graphs for Incoming and Outgoing, if it is 1164 # necessary. 1165 $ifcnt = 0; @ifarr = (); $curif=""; 1166 foreach ( @myifs ) { 1167 $curif = $_ if(!$curif and $interfaces{$_}{community} 1168 and $interfaces{$_}{hostname} ); 1169 if($interfaces{$_}{inout}) { 1170 $ifcnt++; 1171 push @ifarr, $_; 1172 } 1173 } 1174 if($ifcnt) { 1175 $t = ""; 1176 if( defined $interfaces{'_incoming'} ) { 1177 push @{$interfaces{'_incoming'}{targets}},@ifarr 1178 if( $interfaces{'_incoming'}{mode} =~ /_AUTO/ ); 1179 } else { 1180 $interfaces{'_incoming'} = { 1181 usergraph=>1, insummary=>0, inmenu=>1, inout=>0, incompact=>0, 1182 shdesc=>"Incoming", targets=>[@ifarr], noo=>1, mult=>8, 1183 icon=>"incoming-sm.gif", mode=>"\177_AUTO", 1184 desc=>$t."Incoming traffic", 1185 withtotal=>0, withaverage=>0 1186 }; 1187 if(defined $config{'routers.cgi-showtotal'} 1188 and $config{'routers.cgi-showtotal'}=~ /y/i ) { 1189 $interfaces{'_incoming'}{withtotal} = 1; 1190 } 1191 } 1192 if( defined $interfaces{'_outgoing'} ) { 1193 push @{$interfaces{'_outgoing'}{targets}},@ifarr 1194 if( $interfaces{'_outgoing'}{mode} =~ /_AUTO/ ); 1195 } else { 1196 $interfaces{'_outgoing'} = { 1197 usergraph=>1, insummary=>0, inmenu=>1, inout=>0, incompact=>0, 1198 shdesc=>"Outgoing", targets=>[@ifarr], noi=>1, mult=>8, 1199 icon=>"outgoing-sm.gif", mode=>"\177_AUTO", 1200 desc=>$t."Outgoing traffic", 1201 withtotal=>0, withaverage=>0 1202 }; 1203 if(defined $config{'routers.cgi-showtotal'} 1204 and $config{'routers.cgi-showtotal'}=~ /[1y]/i ) { 1205 $interfaces{'_outgoing'}{withtotal} = 1; 1206 } 1207 } 1208 } 1209} 1210 1211sub read_cfg($) 1212{ 1213 my($cfgfile) = $_[0]; 1214 my($l,$key,$k); 1215 1216 return if (!$cfgfile); 1217 1218 %interfaces = ( '_'=>{x=>0}, '^'=>{x=>0}, '$'=>{x=>0} ); 1219 $interval = 5; 1220 $workdir = $config{'routers.cgi-dbpath'}; 1221 1222 read_cfg_file($cfgfile); # recursive 1223 1224 # zap defaults 1225 delete $interfaces{'_'}; 1226 delete $interfaces{'$'}; 1227 delete $interfaces{'^'}; 1228 delete $interfaces{''} if(defined $interfaces{''}); 1229 1230 # first pass for interfaces 1231 foreach $key ( keys %interfaces ) { 1232 next if($interfaces{$key}{usergraph}); 1233 identify $key; 1234 } 1235 # second pass for user graphs 1236 foreach $key ( keys %interfaces ) { 1237 next if(!$interfaces{$key}{usergraph}); 1238 $k = $key; $k=~ s/^_//; # chop off initial _ prefix 1239 $interfaces{$key}{shdesc} = $config{"targetnames-$k"} 1240 if(defined $config{"targetnames-$k"}); 1241 $interfaces{$key}{desc} = $config{"targettitles-$k"} 1242 if(defined $config{"targettitles-$k"}); 1243 $interfaces{$key}{icon} = $config{"targeticons-$k"} 1244 if(defined $config{"targeticons-$k"}); 1245 foreach $k ( keys %{$interfaces{$interfaces{$key}{targets}->[0]}} ) { 1246 $interfaces{$key}{$k} 1247 = $interfaces{$interfaces{$key}{targets}->[0]}{$k} 1248 if(!defined $interfaces{$key}{$k}); 1249 } 1250 } 1251} 1252 1253####################################################################### 1254# Generate the page 1255sub mypage() 1256{ 1257 my( $imgurl ); 1258 my( $ahead ); 1259 my( $javascript ) = "function RefreshMenu() 1260 { 1261 var mwin; var uopts; 1262 if( parent.menu ) { 1263 mwin = parent.menu; 1264 uopts = 'T'; 1265 if( parent.menub ) { mwin = parent.menub; uopts = 't'; } 1266 mwin.location = '".$routersurl."?if=__none&rtr=" 1267 .$q->escape($file)."&page=menu&xmtype=options&uopts='+uopts; 1268 } 1269 }"; 1270 1271 $imgurl = $myurl."?fi=".$q->escape($file) 1272 ."&ta=".$q->escape($target) 1273 ."&dwmy=$dwmy" 1274 ."&dk=$DECAY" 1275 ."&pr=$PREDICT" 1276 ."&ba=$BASE" 1277 ."&t=$targetwindow&conf=".$q->escape($conffile) 1278 ."&img=1"; 1279 1280 start_html_ss({-title=>"Trend Analysis", 1281 -script=>$javascript, -onLoad=>"RefreshMenu()", 1282 -text=>$fgcolour, -bgcolor=>$bgcolour, -class=>'trend', 1283 -link=>$linkcolour, -alink=>$linkcolour, -vlink=>$linkcolour 1284 }); 1285 1286 print $q->h1("Trend Analysis"); 1287 1288 print $q->img({ alt=>'Trend graph', src=>$imgurl }).$q->br."\n"; 1289# print $q->a({href=>$imgurl},$imgurl).$q->br; 1290 1291 print "<DIV class=icons>"; 1292 print $q->hr."\n<TABLE border=0 cellpadding=0 width=100% align=center><TR>"; 1293 print "<TD valign=top>See alternative predictions:\n<UL>"; 1294 foreach ( qw/daily weekly monthly yearly/ ) { 1295 if(substr($_,0,1) eq $dwmy) { 1296 print $q->li("Trending $_ predictions")."\n"; 1297 } else { 1298 print $q->li($q->a({href=>( 1299 $myurl."?dwmy=".(substr($_,0,1)) 1300 ."&fi=".$q->escape($file) 1301 ."&ta=".$q->escape($target) 1302 ."&b=".$q->escape($backurl) 1303 ."&t=".$q->escape($targetwindow) 1304 ."&url=".$q->escape($routersurl) 1305 ."&conf=".$q->escape($conffile) 1306 ."&pr=$PREDICT" 1307 ."&dk=$DECAY" 1308 ."&ba=$BASE" 1309 )}, 1310 "Trending $_ predictions" ) 1311 )."\n"; 1312 } 1313 } 1314 print "</UL></TD>\n"; 1315 print "<TD valign=top>See different historical weightings:\n<UL>"; 1316 foreach ( @decays ) { 1317 if($_ eq $DECAY ) { 1318 print $q->li("Decay factor $_" )."\n"; 1319 } else { 1320 print $q->li($q->a({href=>( 1321 $myurl."?dk=$_" 1322 ."&fi=".$q->escape($file) 1323 ."&ta=".$q->escape($target) 1324 ."&b=".$q->escape($backurl) 1325 ."&t=".$q->escape($targetwindow) 1326 ."&url=".$q->escape($routersurl) 1327 ."&conf=".$q->escape($conffile) 1328 ."&pr=$PREDICT" 1329 ."&dwmy=$dwmy" 1330 ."&ba=$BASE" 1331 )}, 1332 "Decay factor $_" ) 1333 )."\n"; 1334 } 1335 } 1336 print "</UL></TD>\n"; 1337 print "<TD valign=top>Different future distances:\n<UL>"; 1338 foreach ( 50,100,150,200 ) { 1339 if( $dwmy eq "d" ) { $ahead = int(5 * $_ / 60)." hours"; } 1340 elsif( $dwmy eq "w" ) { $ahead = int( $_ / 48 )." days"; } 1341 elsif( $dwmy eq "m" ) { $ahead = int( $_ / 12 )." days"; } 1342 else { $ahead = $_." days"; } 1343 if($_ eq $PREDICT ) { 1344 print $q->li("Look ahead $ahead")."\n"; 1345 } else { 1346 print $q->li($q->a({href=>( 1347 $myurl."?pr=$_" 1348 ."&fi=".$q->escape($file) 1349 ."&ta=".$q->escape($target) 1350 ."&b=".$q->escape($backurl) 1351 ."&t=".$q->escape($targetwindow) 1352 ."&url=".$q->escape($routersurl) 1353 ."&conf=".$q->escape($conffile) 1354 ."&dk=$DECAY" 1355 ."&dwmy=$dwmy" 1356 ."&ba=$BASE" 1357 )}, 1358 "Look ahead $ahead" ) 1359 )."\n"; 1360 } 1361 } 1362 print "</UL></TD>\n"; 1363 print "</TR></TABLE>\n"; 1364 print "Base predicted trend on "; 1365 print $q->a({href=>( 1366 $myurl."?pr=$PREDICT" 1367 ."&fi=".$q->escape($file) 1368 ."&ta=".$q->escape($target) 1369 ."&b=".$q->escape($backurl) 1370 ."&t=".$q->escape($targetwindow) 1371 ."&url=".$q->escape($routersurl) 1372 ."&conf=".$q->escape($conffile) 1373 ."&dk=$DECAY" 1374 ."&dwmy=$dwmy" 1375 ."&ba=0" 1376 )},"current value"); 1377 print " or on "; 1378 print $q->a({href=>( 1379 $myurl."?pr=$PREDICT" 1380 ."&fi=".$q->escape($file) 1381 ."&ta=".$q->escape($target) 1382 ."&b=".$q->escape($backurl) 1383 ."&t=".$q->escape($targetwindow) 1384 ."&url=".$q->escape($routersurl) 1385 ."&conf=".$q->escape($conffile) 1386 ."&dk=$DECAY" 1387 ."&dwmy=$dwmy" 1388 ."&ba=1" 1389 )},"weighted average value"); 1390 print $q->hr."\n"; 1391 print "Trend Decay: $DECAY".$q->br."\n"; 1392 print "(smaller numbers mean pay less attention to historical data)<br>\n"; 1393 print "See error log ".$q->a({href=>"$myurl?log=1",target=>"_new"}, 1394 "here")."<BR>\n" if($debug); 1395 print "</DIV><DIV class=footer>"; 1396 print $q->hr.$q->small("S Shipway: RRD trending analysis v$VERSION")."\n"; 1397 print "</DIV>"; 1398 1399 print $q->end_html(); 1400} 1401####################################################################### 1402sub create_rrd($) { 1403 my( $rrdfile ) = @_; 1404 my( $dblint, $curtime, $curvala, $curvalb, $i ); 1405 my( $rv, $line, $t, $v, $e, $entry); 1406 my( $hita, $hitb ) = (0,0); 1407 my(@args) = (); 1408 1409 # Create the empty RRD file with initial params 1410 1411 unlink $rrdfile if(-f $rrdfile); 1412 @args = ( $rrdfile,'-b',$starttime-1,'-s',$interval ); 1413 foreach ( @$ds, 't0', 't1' ) { 1414 push @args, "DS:$_:GAUGE:".($interval*2).":U:U"; 1415 } 1416 push @args, "RRA:AVERAGE:0.5:1:800"; 1417 push @args, "RRA:MAX:0.5:1:800"; 1418 RRDs::create(@args); 1419 $e = RRDs::error(); 1420 if( $e ) { errlog("RRDCreate ".(join " ",@args).": $e"); return; } 1421 1422 # Output existing data ($rrddata,$starttine,$endtime,$interval) 1423 $t = $starttime; 1424 foreach $line ( @$rrddata ) { 1425 $entry = "$t"; 1426 foreach $v ( @$line ) { 1427 if(defined $v) { $entry .= ":$v"; } 1428 else { $entry .= ":U"; } 1429 } 1430 $entry .= ":U:U"; 1431 RRDs::update($rrdfile,$entry); 1432# errlog($entry); 1433 $t += $interval; 1434 } 1435 1436 # output predicted data (@$trendstart, @$trenddelta) 1437 $i = 0; ( $curvala, $curvalb ) = @$trendstart; 1438 while ( $i < $PREDICT ) { 1439 $entry = "$t:U:U:$curvala:$curvalb"; 1440 RRDs::update($rrdfile,$entry); 1441 $t += $interval; 1442 $curvala += $$trenddelta[0]; $curvalb += $$trenddelta[1]; 1443 if($curvala < 0 and !$hita and !$interfaces{$target }{noi}) { 1444 $curvala = 0; $hita = 1; $$trenddelta[0]=0; 1445 push @info, "Zero for ".$interfaces{$target}{legendi} 1446 ." ".localtime($t)."\\l"; 1447 } 1448 if($curvala > $interfaces{$target }{maxbytes} and !$hita 1449 and !$interfaces{$target }{noi}) { 1450 $curvala = $interfaces{$target }{maxbytes}; $hita = 1; 1451 $$trenddelta[0]=0; 1452 push @info, "Max for ".$interfaces{$target }{legendi} 1453 ." ".localtime($t)."\\l"; 1454 } 1455 if($curvalb < 0 and !$hitb and !$interfaces{$target }{noo}) { 1456 $curvalb = 0; $hitb = 1; $$trenddelta[1]=0; 1457 push @info, "Zero for ".$interfaces{$target }{legendo} 1458 ." ".localtime($t)."\\l"; 1459 } 1460 if($curvalb > $interfaces{$target }{maxbytes} and !$hitb 1461 and !$interfaces{$target }{noo}) { 1462 $curvalb = $interfaces{$target }{maxbytes}; $hitb = 1; 1463 $$trenddelta[1]=0; 1464 push @info, "Max for ".$interfaces{$target }{legendo} 1465 ." ".localtime($t)."\\l"; 1466 } 1467 $i++; 1468 } 1469} 1470####################################################################### 1471# calculate wieghted SD for line $idx at given delta 1472# weighted sd = square root of weighted average of squares of difference 1473sub wsd($$) { 1474 my( $idx, $delta ) = @_; 1475 my( $c, $tot, $w, $row, $v ) = (0,0,0,0,0); 1476 my( $fx ); 1477 1478 $fx = $trendstart->[$idx]; 1479 $w = 1; 1480 foreach $row ( 0 .. $#$rrddata ) { 1481 $v = $rrddata->[$#$rrddata - $row]->[$idx]; 1482 if($v) { $tot += ($v-$fx)*($v-$fx) * $w; $c += $w; } 1483 $w *= $DECAY; # Next value is less significant 1484 $fx -= $delta; 1485 } 1486 if($c) { 1487 $tot = sqrt($tot / $c); 1488 } elsif( $tot ) { 1489 $tot = sqrt($tot); # should not be possible for tot>0 and c=0 1490 } 1491 1492 return $tot; 1493} 1494####################################################################### 1495# This function will retrieve the RRD data, calculate the value and 1496# slope of the trend line, then call the outputxml function and rrdtool 1497# to create XML and the RRDTool database. 1498sub do_trending { 1499 my( $timeframe ) = "1d"; 1500 my( $answer, $status, $e ); 1501 my( $n, $idx, $c, $tot, $w, $row, $v, $deltadelta ); 1502 my( $sd, $delta, $sda, $sdb, $lim ); 1503 1504 errlog("Starting trending function"); 1505 @info = (); # extra lines to add to graph 1506 push @info, 'Trending analysis decay factor: '.doformat($DECAY,1,0).'\l'; 1507 1508 # set intervals (in seconds): 5min, 30min, 2hr, 1day 1509 foreach ( $dwmy ) { 1510 if( /w/ ) { $timeframe = "200h"; $interval = 1800; last; } 1511 if( /m/ ) { $timeframe = "800h"; $interval = 7200; last; } 1512 if( /y/ ) { $timeframe = "400d"; $interval = 3600*24; last; } 1513 $timeframe = "2000m"; $interval = 300; 1514 } 1515 1516 # Read in the RRD file 1517 $endtime = RRDs::last($interfaces{$target}{rrd}); 1518 $e = RRDs::error(); 1519 errlog("RRDLast: ".$interfaces{$target}{rrd}.": $e") if ($e); 1520 return if($e); 1521 ( $starttime, $interval, $ds, $rrddata ) 1522 = RRDs::fetch($interfaces{$target}{rrd},$consolidation,"-r",$interval, 1523 "-s",($endtime-(400*$interval)),"-e",$endtime); 1524 $e = RRDs::error(); 1525 errlog("RRDFetch: ".$interfaces{$target}{rrd}.": $e") if ($e); 1526 return if($e); 1527 errlog(join(",",@$ds).": ".$#$rrddata." rows every $interval from " 1528 .localtime($starttime)); 1529 1530 # Calculate trends 1531 # this results in (a) last update time, (b) avg value then, 1532 # (c) change in value per interval 1533 $trendstart = [ 0,0 ]; 1534 $trenddelta = [ 0,0 ]; 1535 foreach $idx ( 0, 1 ) { 1536 if( $BASE ) { 1537 # calculate weighted average (start point for line) 1538 $w = 1; # weight of current sample 1539 $tot = 0; # total of values 1540 $c = 0; # total weight 1541 $n = 0; # count of samples processed 1542 foreach $row ( 0 .. $#$rrddata ) { 1543 $v = $rrddata->[$#$rrddata - $row]->[$idx]; 1544 if($v) { $tot += $v * $w; $c += $w; $n++; } 1545 $w *= $DECAY; # Next value is less significant 1546 } 1547 $trendstart->[$idx] = $tot / $c if($c); 1548 errlog("Weighted average for $idx is: ".$trendstart->[$idx]." = $tot / $c, $n valid samples"); 1549 } else { 1550 # use last value 1551 foreach $row ( 0 .. $#$rrddata ) { 1552 $v = $rrddata->[$#$rrddata - $row]->[$idx]; 1553 last if(defined $v); 1554 } 1555 $v = 0 if(!defined $v); 1556 $trendstart->[$idx] = $v; 1557 } 1558 1559 # now calculate best angle of line. 1560 # vary delta as weighted standard deviation decreases, until 1561 # we dont get a gain by going up or down. 1562 $delta = $trenddelta->[$idx]; 1563 $deltadelta = $trendstart->[$idx]/100.0; 1564 $lim = $deltadelta/32768.0; 1565 $sd = wsd($idx,$delta); 1566 $n = 0; 1567 while(($n < 100)and ($deltadelta>$lim)) { # put a cap on iterations 1568 $n++; 1569 errlog("Delta=$delta, Deviation=$sd : deltadelta=$deltadelta, limit $lim"); 1570 $sda = wsd($idx,$delta+$deltadelta); 1571 $sdb = wsd($idx,$delta-$deltadelta); 1572 errlog("up->$sda, down->$sdb"); 1573 if($sd<$sda and $sd<$sdb) { # we are in a trough 1574 $deltadelta /= 2; 1575 } elsif( $sda < $sdb ) { 1576 $delta+=$deltadelta; 1577 $sd = $sda; 1578 } else { 1579 $delta-=$deltadelta; 1580 # chage direction 1581 $deltadelta = -($deltadelta/2); 1582 $sd = $sdb; 1583 } 1584 } 1585 $trenddelta->[$idx] = $delta; 1586 errlog("Delta set to $delta"); 1587 } 1588 1589 # create $tempfile.rrd 1590 $lastupdate = $endtime + $PREDICT * $interval; 1591 create_rrd("$tempfile.rrd"); 1592} 1593####################################################################### 1594 1595sub rtr_params(@) 1596{ 1597 my($ds0,$ds1,$ds2,$mds0,$mds1, $mds2,$t0,$t1)=("","","","","","","",""); 1598 my($lin, $lout, $mlin, $mlout, $lextra); 1599 my($dwmy,$interface,$defrrd) = @_; 1600 my($ssin, $ssout, $sin, $sout, $ssext, $sext); 1601 my($l); 1602 my($workday) = 0; 1603 my($legendi,$legendo, $legendx); 1604 my(@clr, $escunit); 1605 my($max1, $max2); 1606 my($havepeaks) = 0; 1607 1608 # are we peak lines on this graph? 1609 #if(!defined $config{'routers.cgi-withpeak'} 1610 # or $config{'routers.cgi-withpeak'} =~ /y/i ) { 1611 # if( $dwmy =~ /[wmy]/ or ( $dwmy =~ /d/ and $usesixhour )) { 1612 # $havepeaks = 1; 1613 # } 1614 #} 1615 1616 # are we going to work out the 'working day' averages as well? 1617 #if( defined $config{'routers.cgi-daystart'} 1618 # and defined $config{'routers.cgi-dayend'} 1619 # and $config{'routers.cgi-daystart'}<$config{'routers.cgi-dayend'} 1620 # and $dwmy !~ /y/ ){ 1621 # $workday = 1; 1622 # $timezone = 0; 1623 # # Calculate timezone. We only need to do this if we're making a graph, 1624 # # and we have a 'working day' defined. 1625 # if( defined $config{'web-timezone'} ) { 1626 # # If its been defined explicitly, then use that. 1627 # $timezone = $config{'web-timezone'}; 1628 # } else { 1629 # # Do we have Time::Zone? 1630 # eval { require Time::Zone; }; 1631 # if ( $@ ) { 1632 # my( @gm, @loc, $hourdif ); 1633 # eval { @gm = gmtime; @loc = localtime; }; 1634 # if( $@ ) { 1635 # # Can't work out local timezone, so assume GMT 1636 # $timezone = 0; 1637 # } else { 1638 # $hourdif = $loc[2] - $gm[2]; 1639 # $hourdif += 24 if($loc[3]>$gm[3] or $loc[4]>$gm[4] ); 1640 # $hourdif -= 24 if($loc[3]<$gm[3] or $loc[4]<$gm[4] ); 1641 # $timezone = $hourdif; 1642 # } 1643 # } else { 1644 # # Use the Time::Zone package since we have it 1645 # $timezone = Time::Zone::tz_local_offset() / 3600; 1646 # # it's in seconds so /3600 1647 # } 1648 # } 1649 #} 1650 1651 # escaped unit string 1652 $escunit = $interfaces{$interface}{unit}; 1653 $escunit =~ s/%/%%/g; 1654 $escunit =~ s/:/\\:/g; 1655 $escunit =~ s/ / /g; 1656 1657 # identify colours 1658 if( defined $interfaces{$interface}{colours} ) { 1659 @clr = @{$interfaces{$interface}{colours}}; 1660 } 1661 if(! @clr ) { 1662 if( $gstyle =~ /b/ ) { 1663 @clr = ("#888888", "#000000", "#cccccc","#444444", "#222222"); 1664 } else { 1665 @clr = ("#00cc00", "#0000ff","#006600", "#ff00ff", "#ff0000" ); 1666 } 1667 } 1668 1669 #$defrrd = $interfaces{$interface}{rrd}; 1670 $defrrd =~ s/:/\\:/g; 1671 1672 $sin="In: "; $sout="Out:"; 1673 $ssin = $ssout = ""; 1674 $sin = $interfaces{$interface}{legendi} 1675 if( defined $interfaces{$interface}{legendi} ); 1676 $sout= $interfaces{$interface}{legendo} 1677 if( defined $interfaces{$interface}{legendo} ); 1678 $l = length $sin; $l = length $sout if($l < length $sout); 1679 $sin = substr($sin.' ',0,$l); 1680 $sout= substr($sout.' ',0,$l); 1681 $sin =~ s/:/\\:/g; $sout =~ s/:/\\:/g; 1682 $sin =~ s/%/%%/g; $sout =~ s/%/%%/g; 1683 if( $interfaces{$interface}{integer} ) { 1684 $ssin = "%5.0lf"; $ssout = "%5.0lf"; 1685 } elsif( $interfaces{$interface}{fixunits} ) { 1686 $ssin = "%7.2lf "; $ssout = "%7.2lf "; 1687 } else { 1688 $ssin = "%6.2lf %s"; $ssout = "%6.2lf %s"; 1689 } 1690 if( defined $config{'routers.cgi-legendunits'} 1691 and $config{'routers.cgi-legendunits'} =~ /y/i ) { 1692 $ssin .= $escunit; $ssout .= $escunit; 1693 } 1694 $sin .= $ssin; $sout .= $ssout; 1695 1696 { 1697 $lin = ":Inbound"; $lout = ":Outbound"; 1698 $mlin = ":Peak Inbound"; $mlout = ":Peak Outbound"; 1699 $lin = ":".$interfaces{$interface}{legend1} 1700 if( defined $interfaces{$interface}{legend1} ); 1701 $lout = ":".$interfaces{$interface}{legend2} 1702 if( defined $interfaces{$interface}{legend2} ); 1703 $mlin = ":".$interfaces{$interface}{legend3} 1704 if( defined $interfaces{$interface}{legend3} ); 1705 $mlout = ":".$interfaces{$interface}{legend4} 1706 if( defined $interfaces{$interface}{legend4} ); 1707 if($interfaces{$interface}{noo} or $havepeaks 1708 or ( defined $interfaces{$interface}{graphstyle} 1709 and $interfaces{$interface}{graphstyle} =~ /range/i )) { 1710 $lin .= "\\l"; 1711 } 1712 } 1713 $lout .= "\\l" if($lout); 1714 $lin = substr( $lin." ",0,30 ) 1715 if($lin and !$interfaces{$interface}{noo} 1716 and $lin !~ /\\l$/ ); 1717 $mlout = substr( $mlout." ",0,30 ) 1718 if ($mlout); 1719 $mlin = substr( $mlin." ",0,30 ) 1720 if ($mlin); 1721 1722 if( $interfaces{$interface}{nolegend} ) { $lin = $lout = ""; } 1723 1724 ($ds0, $ds1, $t0, $t1) = ("ds0", "ds1", "t0", "t1"); 1725 push @params, 1726 "DEF:in=".$defrrd.":$ds0:$consolidation", 1727 "DEF:out=".$defrrd.":$ds1:$consolidation", 1728 "DEF:tin=".$defrrd.":$t0:$consolidation", 1729 "DEF:tout=".$defrrd.":$t1:$consolidation" 1730 ; 1731 ($ds0, $ds1, $t0, $t1) = ("in", "out", "tin", "tout"); 1732### do this if we are using BITS 1733 if( $interfaces{$interface}{mult} ne 1 ) { 1734 push @params, "CDEF:fin=$ds0,".$interfaces{$interface}{mult}.",*", 1735 "CDEF:fout=$ds1,".$interfaces{$interface}{mult}.",*"; 1736 push @params, "CDEF:ftin=$t0,".$interfaces{$interface}{mult}.",*", 1737 "CDEF:ftout=$t1,".$interfaces{$interface}{mult}.",*"; 1738 ($ds0, $ds1, $t0, $t1) = ("fin", "fout", "ftin", "ftout"); 1739 } 1740### 1741 if( defined $interfaces{$interface}{factor} ) { 1742 push @params, "CDEF:ffin=$ds0,".$interfaces{$interface}{factor}.",*", 1743 "CDEF:ffout=$ds1,".$interfaces{$interface}{factor}.",*"; 1744 push @params, "CDEF:fftin=$t0,".$interfaces{$interface}{factor}.",*", 1745 "CDEF:fftout=$t1,".$interfaces{$interface}{factor}.",*"; 1746 ($ds0, $ds1, $t0, $t1) = ("ffin", "ffout", "fftin", "fftout"); 1747 } 1748# And the percentages 1749 $max1 = $max2 = $interfaces{$interface}{max}; 1750 $max1 = $interfaces{$interface}{max1} 1751 if(defined $interfaces{$interface}{max1}); 1752 $max2 = $interfaces{$interface}{max2} 1753 if(defined $interfaces{$interface}{max2}); 1754 if( $max1 ) { 1755 push @params, 1756 "CDEF:pcin=$ds0,100,*,".$max1.",/"; 1757 # "CDEF:mpcin=$mds0,100,*,".$max1.",/"; 1758 } 1759 if( $max2 ) { 1760 push @params, 1761 "CDEF:pcout=$ds1,100,*,".$max2.",/"; 1762 # "CDEF:mpcout=$mds1,100,*,".$max2.",/"; 1763 } 1764 1765 1766 if( $interfaces{$interface}{available} ) { 1767 # availability percentage 1768 push @params, "CDEF:apc=in,UN,out,UN,+,2,EQ,0,100,IF"; 1769 # Now, the average of apc is the percentage availability! 1770 } 1771 1772# now for the actual lines : put the peaklines for d only if we have a 6 hr 1773# dont forget to use more friendly colours if this is black and white mode 1774 push @params, "AREA:$ds0".$clr[0].$lin 1775 if(!$interfaces{$interface}{noi}); 1776 push @params, "AREA:$t0#80ff80" 1777 if(!$interfaces{$interface}{noi}); 1778 if(!$interfaces{$interface}{noo}) { 1779 if( defined $interfaces{$interface}{graphstyle} ) { 1780 if( $interfaces{$interface}{graphstyle} =~ /stack/i ) { 1781 push @params, "STACK:$ds1".$clr[1].$lout; 1782 } elsif( $interfaces{$interface}{graphstyle} =~ /range/i ) { 1783 push @params, "AREA:$ds1#ffffff"; # erase lower part 1784 # if workingday active, put HIGHLIGHTED lower in 1785 if( $workday and $gstyle !~ /b/) { 1786 push @params, "CDEF:lwday=wdin,UN,0,$ds1,IF", 1787 "AREA:lwday#ffffcc"; 1788 } 1789 push @params, "LINE1:$ds1".$clr[0]; # replace last pixel 1790 } else { 1791 # we do it here so it isnt overwritten by the incoming area 1792 push @params, "LINE1:$ds1".$clr[1].$lout; 1793 push @params, "LINE1:$t1#8080ff"; 1794 } 1795 } else { 1796 # we do it here so it isnt overwritten by the incoming area 1797 push @params, "LINE1:$ds1".$clr[1].$lout; 1798 push @params, "LINE1:$t1#8080ff"; 1799 } 1800 } 1801 1802# data unavailable 1803 push @params, 1804 "CDEF:down=in,UN,out,UN,tin,UN,tout,UN,+,+,+,4,EQ,INF,0,IF","AREA:down#d0d0d0"; 1805# the max line 1806 if( $interfaces{$interface}{max} 1807 and ! ( defined $config{'routers.cgi-maxima'} 1808 and $config{'routers.cgi-maxima'} =~ /n/i ) 1809 and !$interfaces{$interface}{nomax} 1810 ) { 1811 my( $lmax ) = ""; 1812 my( $lamax ) = ""; 1813# if( $dwmy !~ /s/ ) { 1814 if( defined $interfaces{$interface}{mblegend} ) { 1815 $lmax = $interfaces{$interface}{mblegend}; 1816 $lmax =~ s/:/\\:/g; $lmax = ':'.$lmax; 1817 } elsif( $interfaces{$interface}{isif} ) { 1818 $lmax =":100% Bandwidth"; 1819 } else { $lmax =":Maximum"; } 1820 if( $max1 and $max2 and ($max1 != $max2) ) { 1821 $lmax .= " (".doformat($max1,$interfaces{$interface}{fixunits},0) 1822 .$interfaces{$interface}{unit}."," 1823 .doformat($max2,$interfaces{$interface}{fixunits},0) 1824 .$interfaces{$interface}{unit}.")\\l"; 1825 } else { 1826 $lmax .= " (".doformat($interfaces{$interface}{max}, 1827 $interfaces{$interface}{fixunits},0) 1828 .$interfaces{$interface}{unit}.")\\l"; 1829 } 1830 if( defined $interfaces{$interface}{absmax} ) { 1831 if( defined $interfaces{$interface}{amlegend} ) { 1832 $lamax = ":".$interfaces{$interface}{amlegend}; 1833 } else { $lamax =":Hard Maximum"; } 1834 $lamax .= " (".doformat($interfaces{$interface}{absmax}, 1835 $interfaces{$interface}{fixunits},1) 1836 .$interfaces{$interface}{unit}.")\\l"; 1837 } 1838# } 1839 if( $max1 and $max2 and ($max1 != $max2)) { 1840 if( $gstyle =~ /b/ ) { 1841 push @params, "HRULE:".$max1."#cccccc$lmax"; 1842 push @params, "HRULE:".$max2."#cccccc"; 1843 } else { 1844 push @params, "HRULE:".$max1."#ff0000$lmax"; 1845 push @params, "HRULE:".$max2."#ff0000"; 1846 } 1847 } else { 1848 if( $gstyle =~ /b/ ) { 1849 push @params, "HRULE:".$interfaces{$interface}{max}."#cccccc$lmax"; 1850 } else { 1851 push @params, "HRULE:".$interfaces{$interface}{max}."#ff0000$lmax"; 1852 } 1853 } 1854 if( defined $interfaces{$interface}{absmax} 1855 and !$interfaces{$interface}{noabsmax} ) { 1856 if( $gstyle =~ /b/ ) { 1857 push @params, "HRULE:".$interfaces{$interface}{absmax} 1858 ."#aaaaaa$lamax"; 1859 } else { 1860 push @params, "HRULE:".$interfaces{$interface}{absmax} 1861 ."#ff0080$lamax"; 1862 } 1863 } 1864 } 1865# now for the labels at the bottom 1866 if( $max1 ) { 1867 if(!$interfaces{$interface}{noi}) { 1868# push @params, "GPRINT:$mds0:MAX:Max $sin\\g" ; 1869# push @params ,"GPRINT:mpcin:MAX: (%2.0lf%%)\\g" 1870# if($interfaces{$interface}{percent}); 1871 push @params,"GPRINT:$ds0:$consolidation: $conspfx $sin\\g" ; 1872 push @params ,"GPRINT:pcin:$consolidation: (%2.0lf%%)\\g" 1873 if($interfaces{$interface}{percent}); 1874 push @params,"GPRINT:$ds0:LAST: Cur $sin\\g" ; 1875 push @params ,"GPRINT:pcin:LAST: (%2.0lf%%)\\g" 1876 if($interfaces{$interface}{percent}); 1877 push @params, "COMMENT:\\l" ; 1878 } 1879 if(!$interfaces{$interface}{noo}) { 1880# push @params, "GPRINT:$mds1:MAX:Max $sout\\g" ; 1881# push @params ,"GPRINT:mpcout:MAX: (%2.0lf%%)\\g" 1882# if($interfaces{$interface}{percent}); 1883 push @params,"GPRINT:$ds1:$consolidation: $conspfx $sout\\g" ; 1884 push @params ,"GPRINT:pcout:$consolidation: (%2.0lf%%)\\g" 1885 if($interfaces{$interface}{percent}); 1886 push @params,"GPRINT:$ds1:LAST: Cur $sout\\g" ; 1887 push @params ,"GPRINT:pcout:LAST: (%2.0lf%%)\\g" 1888 if($interfaces{$interface}{percent}); 1889 push @params, "COMMENT:\\l" ; 1890 } 1891 if( defined $config{'routers.cgi-maxima'} 1892 and $config{'routers.cgi-maxima'} =~ /n/i 1893 and !$interfaces{$interface}{nomax} ) { 1894 my( $comment ); 1895 if(defined $interfaces{$interface}{mblegend}) { 1896 $comment = $interfaces{$interface}{mblegend}; 1897 $comment = "COMMENT:".decolon($comment); 1898 } elsif($interfaces{$interface}{isif}) { 1899 $comment = "COMMENT:100% Bandwidth"; 1900 } else { 1901 $comment = "COMMENT:Maximum value"; 1902 } 1903 $comment .= decolon(" ".doformat($interfaces{$interface}{max}, 1904 $interfaces{$interface}{fixunits},0) 1905 .$escunit."\\l"); 1906 push @params, $comment; 1907 } 1908 } else { 1909 push @params, 1910# "GPRINT:$mds0:MAX:Max $sin\\g", 1911 "GPRINT:$ds0:$consolidation: $conspfx $sin\\g", 1912 "GPRINT:$ds0:LAST: Cur $sin\\l" 1913 if(!$interfaces{$interface}{noi}); 1914 push @params, 1915# "GPRINT:$mds1:MAX:Max $sout\\g", 1916 "GPRINT:$ds1:$consolidation: $conspfx $sout\\g", 1917 "GPRINT:$ds1:LAST: Cur $sout\\l" 1918 if(!$interfaces{$interface}{noo}); 1919 } 1920 if( $interfaces{$interface}{available} ) { 1921 push @params, "GPRINT:apc:AVERAGE:Data availability\\: %.2lf%%\\l"; 1922 } 1923 foreach (@info) { 1924 push @params, "COMMENT:".decolon($_); 1925 } 1926} 1927 1928######################################################################## 1929# Actually create the necessary graph 1930 1931sub make_graph(@) 1932{ 1933 my ($e, $thisurl, $s, $autoscale); 1934 my ($tstr, $gheight, $width, $gwidth, $gtitle); 1935 my( $maxwidth ) = 30; 1936 my( $endtime ); 1937 my($thisgraph,$thisrrd) = @_; 1938 my($interface) = $target; 1939 my( $rrdoutput, $rrdxsize, $rrdysize ); 1940 1941 1942# Shall we scale it, etc 1943 $autoscale = 1; 1944 $s = $dwmy; $s =~ s/s//; 1945 if( $interfaces{$interface}{unscaled} ) { 1946 $autoscale = 0 if ($interfaces{$interface}{unscaled} =~ /$s/i); 1947 } 1948 1949 $tstr = "6-hour" if( $dwmy =~ /6/ ) ; 1950 $tstr = "Daily" if( $dwmy =~ /d/ ) ; 1951 $tstr = "Weekly" if( $dwmy =~ /w/ ) ; 1952 $tstr = "Monthly" if( $dwmy =~ /m/ ) ; 1953 $tstr = "Yearly" if( $dwmy =~ /y/ ) ; 1954 1955 $gtitle = $interfaces{$interface}{desc}; 1956 if( ($dwmy.$gstyle)=~/s/ ) { 1957 if($gstyle=~/x/) { $maxwidth = 50; } 1958 elsif($gstyle=~/l/) { $maxwidth = 40; } 1959 else { $maxwidth = 30; } 1960 } 1961 if(!$gtitle or ((length($gtitle)>$maxwidth)and(($dwmy.$gstyle) =~ /s/))) { 1962 $gtitle = ""; 1963# $gtitle .= $routers{$router}{shdesc}.": " 1964# if( defined $routers{$router}{shdesc}); 1965 $gtitle .= $interfaces{$interface}{shdesc}; 1966 } 1967 $gtitle .= ": Trend Analysis"; 1968 $gtitle = $q->unescape($gtitle); 1969 $gtitle =~ s/ / /g; $gtitle =~ s/&/&/g; 1970 1971 @params = (); 1972 1973 if ( $gstyle =~ /^s/ ) { $width = 200; $gwidth = 200; } #short 1974 elsif ( $gstyle =~ /^t/ ) { $width = 200; $gwidth = 400; } #stretch 1975 elsif ( $gstyle =~ /^l/ ) { $width = 400; $gwidth = 530; } #long 1976 elsif ( $gstyle =~ /^x/ ) { $width = 400; $gwidth = 800; } #xlong 1977 else { $width = 400; $gwidth = 400; } # default (normal) 1978 if ( $gstyle =~ /2/ ) { $gheight = 200; } 1979 else { $gheight = 100; } 1980 1981 push @params, $thisgraph; 1982 if( $graphsuffix eq "png" ) { 1983 push @params, '--imgformat',uc $graphsuffix; 1984 } 1985 push @params,"--base", $k; 1986 push @params, qw/--lazy -l 0 --interlaced/; 1987 push @params,"--units-exponent",0 1988 if($interfaces{$interface}{fixunits} 1989 and $RRDs::VERSION >= 1.00030 ); 1990 # only force the minimum upper-bound of graph if we have a max, 1991 # and we dont have maxima=n, and we dont have unscaled=n 1992 if( ! $autoscale ) { 1993 if( $interfaces{$interface}{max} and ( 1994 !defined $config{'routers.cgi-maxima'} 1995 or $config{'routers.cgi-maxima'} !~ /n/i 1996 ) ) { 1997 push @params, "-u", $interfaces{$interface}{max} ; 1998 } else { 1999 push @params, "-u", 1; 2000 } 2001 } else { 2002 push @params, "-u", 1; 2003 } 2004# could have added a "-r" there to enforce the upper limit rigidly 2005# push @params, "-v", $config{$statistic}{-v}; 2006 push @params, "-w", $gwidth, "-h", $gheight; 2007 push @params, "-e", $lastupdate; 2008 $endtime = $lastupdate; 2009 push @params, "-s", "end".(-1 * $width)."m" if ( $dwmy =~ /6/ ); 2010 push @params, "-s", "end".(-5 * $width)."m" if ( $dwmy =~ /d/ ); 2011 push @params, "-s", "end".(-25 * $width)."m" if ( $dwmy =~ /w/ ); 2012 push @params, "-s", "end".(-2 * $width)."h" if ( $dwmy =~ /m/ ); 2013 push @params, "-s", "end".(-1 * $width)."d" if ( $dwmy =~ /y/ ); 2014 push @params,"--x-grid","MINUTE:15:HOUR:1:HOUR:1:0:$dailylabel" 2015 if ( $dwmy =~ /6/ ); 2016 push @params,"--x-grid","HOUR:1:HOUR:24:HOUR:2:0:$dailylabel" 2017 if ( $dwmy =~ /d/ ); 2018 push @params,"--x-grid","HOUR:6:DAY:1:DAY:1:86400:%a" 2019 if ( $dwmy =~ /w/ ); 2020 push @params,"--x-grid","DAY:1:WEEK:1:WEEK:1:604800:Week" 2021 ." ".$monthlylabel 2022 if ( $dwmy =~ /m/ and $RRDs::VERSION >= 1.00029 ); 2023 push @params,"--title", $gtitle; 2024 2025 if ( defined $interfaces{$interface}{ylegend} ) { 2026 push @params, "--vertical-label", $interfaces{$interface}{ylegend}; 2027 } else { 2028 push @params, "--vertical-label", $interfaces{$interface}{unit}; 2029 } 2030 2031 rtr_params($dwmy,$interface,$thisrrd); 2032 2033 if ( defined $config{'routers.cgi-withdate'} 2034 and $config{'routers.cgi-withdate'}=~/y/ ) { 2035 push @params, "COMMENT:".decolon(shortdate($endtime))."\\r"; 2036 } 2037 2038 ( $rrdoutput, $rrdxsize, $rrdysize ) = RRDs::graph(@params); 2039 $e = RRDs::error(); 2040 if($e) { 2041 print $q->redirect($config{'routers.cgi-iconurl'}."error-lg.gif"); 2042 errlog("RRDGraph: $e"); 2043 } else { 2044 # output the graphic directly from disk 2045 open GRAPH, "<$thisgraph"; 2046 binmode GRAPH; 2047 binmode STDOUT; 2048 print $q->header({ '-type'=>"image/$graphsuffix", '-expires'=>"now", 2049 '-Content-Disposition'=>"filename=\"image.$graphsuffix\"" }); 2050 while( <GRAPH> ) { print; } 2051 close GRAPH; 2052 } 2053 return; 2054} 2055 2056####################################################################### 2057# create and output the GIF/PNG 2058sub makeimage { 2059 my( $tmpimage, $buf ); 2060 2061 # Create the graphic header 2062 $headeropts{'-expires'} = "+6min"; 2063 if( defined $config{'web-png'} and $config{'web-png'}=~/[1yY]/ ) { 2064 $headeropts{'-type'} = 'image/png'; 2065 $tmpimage = $tempfile.".png"; 2066 $graphsuffix = 'png'; 2067 } else { 2068 $headeropts{'-type'} = 'image/gif'; 2069 $tmpimage = $tempfile.".gif"; 2070 $graphsuffix = 'gif'; 2071 } 2072 2073 # call graphing on $tempfile.rrd to create $tmpimage 2074 make_graph($tmpimage,"$tempfile.rrd"); 2075 2076 # remove the temporary files 2077 unlink $tmpimage; 2078 unlink $tempfile.".rrd"; 2079} 2080 2081sub errorpage { 2082 if(!$q->param('img')) { 2083 print $q->header({%headeropts}); 2084 start_html_ss({-title=>"Error",-class=>'error'}); 2085 print $q->h1("Error in configuration")."\n"; 2086 print $q->p($_[0]); 2087 print $q->hr.$q->small("S Shipway: RRD trending analysis v$VERSION")."\n"; 2088 print "<!-- \nThis script should not be called from the command line!\n-->\n"; 2089 print $q->end_html; 2090 } else { 2091 print $q->redirect($config{'routers.cgi-iconurl'}."error-lg.gif"); 2092 } 2093} 2094####################################################################### 2095 2096# read in the conf file 2097$conffile = $q->param('conf') if(defined $q->param('conf')); 2098readconf('routers.cgi','web','trend.cgi'); 2099$LOG = $config{'trend.cgi-logfile'} if( defined $config{'trend.cgi-logfile'} ); 2100$debug = $config{'trend.cgi-debug'} if( defined $config{'trend.cgi-debug'} ); 2101$DECAY = $config{'trend.cgi-decay'} if( defined $config{'trend.cgi-decay'} ); 2102$TMPPATH = $config{'trend.cgi-workdir'} 2103 if( defined $config{'trend.cgi-workdir'} 2104 and -d $config{'trend.cgi-workdir'} ); 2105 2106# For debugging only 2107if( $q->param('log') ) { 2108 print $q->header(\%headeropts); 2109 start_html_ss({-class=>'error'}); 2110 if( -r $LOG ) { 2111 print "\n<PRE>\n"; 2112 open RLOG, "<$LOG"; 2113 while ( <RLOG> ) { print; } 2114 close RLOG; 2115 print "</PRE>\n"; 2116 } else { 2117 print $q->h1("Error").$q->p("No log file is available.")."\n"; 2118 } 2119 print $q->end_html."\n"; 2120 exit 0; 2121} 2122# Process parameters 2123$file = $q->param('fi') if(defined $q->param('fi')); 2124$target = $q->param('ta') if(defined $q->param('ta')); 2125$backurl = $q->param('b') if(defined $q->param('b')); 2126$targetwindow = $q->param('t') if(defined $q->param('t')); 2127$dwmy = $q->param('dwmy') if(defined $q->param('dwmy')); 2128$DECAY = $q->param('dk') if(defined $q->param('dk')); 2129$PREDICT = $q->param('pr') if(defined $q->param('pr')); 2130$BASE = $q->param('ba') if(defined $q->param('ba')); 2131$routersurl = $q->param('url') if(defined $q->param('url')); 2132 2133# HTTP headers 2134%headeropts = ( -expires=>"now" ); 2135$headeropts{target} = $targetwindow if($targetwindow); 2136 2137# caching daemon 2138if( $ENV{RRDCACHED_ADDRESS} ) { 2139 errlog("Warning: RRDCACHED_ADDRESS=".$ENV{RRDCACHED_ADDRESS}); 2140 delete $ENV{RRDCACHED_ADDRESS}; 2141} 2142 2143# Test a few things 2144$cfile = $config{'routers.cgi-confpath'}.$pathsep.$file; 2145if( ! -r $cfile ) { 2146 errorpage("Unable to read configuration file $cfile : please check that this file exists, and is readable by the web process. Check defaults in the trend.cgi script itself."); 2147 exit 0; 2148} 2149if( ! -w $TMPPATH ) { 2150 errorpage("Unable to write to temporary directory $TMPPATH : Please check the definition in the routers2.conf, and the default specified in the trend.cgi script itself."); 2151 exit 0; 2152} 2153if( $debug and -f $LOG and ! -w $LOG ) { 2154 errorpage("Cannot write to log file $LOG : Either give a correct log file location (in the routers2.conf or the trend.cgi script) or disable debug logging (in the routers2.conf)."); 2155 exit 0; 2156} 2157if( ! $target ) { 2158 errorpage("No Target defined: You cannot (yet) use trend.cgi as a device-level Extension, it must be defined as a Target-level one."); 2159 exit 0; 2160} 2161 2162# Set colour defaults 2163foreach ( 'routers.cgi', 'trend.cgi' ) { 2164 $fgcolour = $config{"$_-fgcolor"} if(defined $config{"$_-fgcolor"}); 2165 $fgcolour = $config{"$_-fgcolour"} if(defined $config{"$_-fgcolour"}); 2166 $bgcolour = $config{"$_-bgcolor"} if(defined $config{"$_-bgcolor"}); 2167 $bgcolour = $config{"$_-bgcolour"} if(defined $config{"$_-bgcolour"}); 2168 $linkcolour = $config{"$_-linkcolor"} if(defined $config{"$_-linkcolor"}); 2169 $linkcolour = $config{"$_-linkcolour"} if(defined $config{"$_-linkcolour"}); 2170} 2171 2172# read in the MRTG cfg file 2173read_cfg($cfile); 2174 2175if(!defined $interfaces{$target} ) { 2176 errorpage( "Unable to find target '$target' in file $cfile" ); 2177 exit 0; 2178} 2179 2180unshift @INC, ( split /[\s,]+/,$config{"web-libadd"} ) 2181 if( defined $config{"web-libadd"} ); 2182 2183eval { require RRDs; }; 2184if($@) { 2185 errorpage("Unable to load RRDs perl module: Have you installed it correctly?<P>$@"); 2186 exit 0; 2187} 2188 2189# Background colours 2190$bgcolour = $interfaces{$target}{background} 2191 if(defined $interfaces{$target}{background}); 2192 2193 2194# First, make the HTML page, calling self for the graphic 2195if(! $q->param('img') ) { 2196 print $q->header(\%headeropts); 2197 mypage; 2198 exit 0; 2199} 2200 2201# Now we read in the rrd file, and do the trending 2202$tempfile = $TMPPATH.$pathsep."trend.".time; 2203do_trending($target{rrd},"$tempfile.rrd"); 2204 2205if ( -f "$tempfile.rrd" ) { 2206 # Make the page 2207 makeimage(); 2208} else { 2209 print $q->redirect($config{'routers.cgi-iconurl'}."error-lg.gif"); 2210 errlog("Didn't make the working rrd file"); 2211} 2212 2213# End 2214errlog("Complete"); 2215exit(0); 2216