#!/usr/bin/perl # File: $Id: show.cgi,v 1.22 2006/04/12 09:42:16 sauber Exp $ # Author: (c) Soren Dossing, 2005 # License: OSI Artistic License # http://www.opensource.org/licenses/artistic-license.php use strict; use RRDs; use CGI qw/:standard/; # Configuration my $configfile = '/home/nagios/ng/etc/nagiosgraph.conf'; # Main program - change nothing below my %Config; # Read in configuration data # sub readconfig { die "config file not found" unless -r $configfile; # Read configuration data open FH, $configfile; while () { s/\s*#.*//; # Strip comments /^(\w+)\s*=\s*(.*?)\s*$/ and do { $Config{$1} = $2; debug(5, "CGI Config $1:$2"); }; } close FH; # Make sure log file can be written to unless ( -w $Config{logfile} ) { my $msg = "Log file $Config{logfile} not writable"; print header(-type => "text/html", -expires => 0); print p($msg); debug (2, "CGI Config $msg"); return undef; } # Make sure rrddir is readable unless ( -r $Config{rrddir} ) { my $msg = "rrd dir $Config{rrddir} not readable"; print header(-type => "text/html", -expires => 0); print p($msg); debug (2, "CGI Config $msg"); return undef; } return 1; } # Write debug information to log file # sub debug { my($l, $text) = @_; if ( $l <= $Config{debug} ) { $l = qw(none critical error warn info debug)[$l]; $text =~ s/(\w+)/$1 $l:/; open LOG, ">>$Config{logfile}"; print LOG scalar localtime; print LOG " $text\n"; close LOG; } } # URL encode a string # sub urlencode { $_[0] =~ s/([\W])/"%" . uc(sprintf("%2.2x",ord($1)))/eg; return $_[0]; } # Get list of matching rrd files # sub dbfilelist { my($host,$service) = @_; my $hs = urlencode "${host}_${service}"; my @rrd; opendir DH, $Config{rrddir}; @rrd = grep s/^${hs}_(.+)\.rrd$/$1/, readdir DH; closedir DH; return @rrd; } # Find graphs and values # sub graphinfo { my($host,$service,@db) = @_; my(@rrd,$ds,$f,$dsout,@values,$hs,%H,%R); $hs = urlencode "${host}_${service}"; debug(5, 'CGI @db=' . join '&', @db); # Determine which files to read lines from if ( @db ) { my $n = 0; for my $d ( @db ) { my($db,@lines) = split ',', $d; $rrd[$n]{file} = $hs . urlencode("_$db") . '.rrd'; for my $l ( @lines ) { my($line,$unit) = split '~', $l; if ( $unit ) { $rrd[$n]{line}{$line}{unit} = $unit if $unit; } else { $rrd[$n]{line}{$line} = 1; } } $n++; } debug(4, "CGI Specified $hs db files in $Config{rrddir}: " . join ', ', map { $_->{file} } @rrd); } else { @rrd = map {{ file=>$_ }} map { "${hs}_${_}.rrd" } dbfilelist($host,$service); debug(4, "CGI Listing $hs db files in $Config{rrddir}: " . join ', ', map { $_->{file} } @rrd); } for $f ( @rrd ) { unless ( $f->{line} ) { $ds = RRDs::info "$Config{rrddir}/$f->{file}"; debug(2, "CGI RRDs::info ERR " . RRDs::error) if RRDs::error; map { $f->{line}{$_} = 1} grep {!$H{$_}++} map { /ds\[(.*)\]/; $1 } grep /ds\[(.*)\]/, keys %$ds; } debug(5, "CGI DS $f->{file} lines: " . join ', ', keys %{ $f->{line} } ); } return \@rrd; } # Choose a color for service # sub hashcolor { my$c=$Config{colorscheme}; map{ $c=(51*$c+ord)%(216) } split//,"$_[0]x"; my($i,$n,$m,@h); @h=(51*int$c/36, 51*int$c/6%6, 51*($c%6)); #debug(2, "hashcolor $_[0], $c, $h[0]"); for$i(0..2){ $m=$i if$h[$i]<$h[$m]; $n=$i if$h[$i]>$h[$n] } $h[$m]=102 if$h[$m]>102; $h[$n]=153 if$h[$n]<153; #debug(2, "hashcolor $_[0]\t$c\t$h[0]\t$h[1]\t$h[2]"); #$c=sprintf"%06X",$h[2]+$h[1]*256+$h[0]*16**4; $n = $h[2]+$h[1]*256+$h[0]*16**4; $c=sprintf"%06X",$n; #debug(2, "hashcolor $_[0]\t$n\t$c"); return $c; } # Generate all the parameters for rrd to produce a graph # sub rrdline { my($host,$service,$geom,$rrdopts,$G,$time) = @_; my($g,$f,$v,$c,@ds); @ds = ('-', '-a', 'PNG', '--start', "-$time"); # Identify where to pull data from and what to call it for $g ( @$G ) { $f = $g->{file}; debug(5, "CGI file=$f"); for $v ( sort keys %{ $g->{line} } ) { $c = hashcolor($v); debug(5, "CGI file=$f line=$v color=$c"); my $sv = "$v"; push @ds , "DEF:$sv=$Config{rrddir}/$f:$v:AVERAGE" , "LINE2:${sv}#$c:$sv" , "GPRINT:$sv:MAX:Max\\: %6.2lf%s" , "GPRINT:$sv:AVERAGE:Avg\\: %6.2lf%s" , "GPRINT:$sv:MIN:Min\\: %6.2lf%s" , "GPRINT:$sv:LAST:Cur\\: %6.2lf%s\\n"; } } # Dimensions of graph if geom is specified if ( $geom ) { my($w,$h) = split 'x', $geom; push @ds, '-w', $w, '-h', $h; } # Additional parameters to rrd graph, if specified if ( $rrdopts ) { push @ds, split /\s+/, $rrdopts; } return @ds; } # Write a pretty page with various graphs # sub page { my($h,$s,$d,$o,@db) = @_; # Reencode rrdopts $o = urlencode $o; # Detect available db files @db = dbfilelist($h,$s) unless @db; debug(5, "CGI dbfilelist @db"); # Define graph sizes # Daily = 33h = 118800s # Weekly = 9d = 777600s # Monthly = 5w = 3024000s # Yearly = 400d = 34560000s my @T=(['dai',118800], ['week',777600], ['month',3024000], ['year',34560000]); print h1("Nagiosgraph"); print p("Performance data for ".strong("Host: ").tt($h).' · '.strong("Service: ").tt($s)); for my $l ( @T ) { my($p,$t) = ($l->[0],$l->[1]); print h2(ucfirst $p . "ly"); if ( @db ) { for my $g ( @db ) { my $arg = join '&', "host=$h", "service=$s", "db=$g", "graph=$t", "geom=$d", "rrdopts=$o"; my @gl = split ',', $g; my $ds = shift @gl; print div({-class => "graphs"}, img( {-src => "?$arg", -alt => "Graph"} ) ); print div({-class => "graph_description"}, cite(strong($ds).br().small(join(", ", @gl)))); } } else { my $arg = join '&', "host=$h", "service=$s", "graph=$t", "geom=$d", "rrdopts=$o"; print div({-class => "graphs"}, img( {-src => "?$arg", -alt => "Graph"} ) ); } } } exit unless readconfig(); # Expect host, service and db input my $host = param('host') if param('host'); my $service = param('service') if param('service'); my @db = param('db') if param('db'); my $graph = param('graph') if param('graph'); my $geom = param('geom') if param('geom'); my $rrdopts = param('rrdopts') if param('rrdopts'); # Draw a graph or a page if ( $graph ) { $| = 1; # Make sure headers arrive before image data print header(-type => "image/png"); # Figure out db files and line labels my $G = graphinfo($host,$service,@db); my @ds = rrdline($host,$service,$geom,$rrdopts,$G,$graph); debug(4, "CGI RRDs::graph ". join ' ', @ds); RRDs::graph(@ds); debug(2, "CGI RRDs::graph ERR " . RRDs::error) if RRDs::error; exit; } else { my @style; if ($Config{stylesheet}) { @style = ( -style => {-src => "$Config{stylesheet}"} ); } print header, start_html(-id=>"nagiosgraph", -title => "nagiosgraph: $host-$service", -meta => { -http_equiv => "Refresh", -content => "300" }, @style ); page($host,$service,$geom,$rrdopts,@db); print div({-id => "footer"}, hr(), small( "Created by ". a( {-href=>"http://nagiosgraph.sf.net/"}, "nagiosgraph"). "." )); print end_html(); }