#!/usr/bin/perl -w #-d # # sar/iostat graphing tool based on rrdtool # (c) 2004 B. Cornec HP # Licensed under the GPL or Artistic License # # Original Charter: Monitor CPU/Load/Net_eth0|2 (io/s)/Disk (io/s) # 1 mesure par 20 seconde # So the script has been developped first with that context in mind # trying to be generic enough to be expandable later on # More work needs to be done, some code factorized, ... # use strict; # Always :-) use RRDs; # for data management and graphing use Data::Dumper; # for debug use HTML::AsSubs; # for HTML generation use Storable; # for data exchange use Date::Manip; # for timestamp use Config::Auto; # for config file use sario2rrd qw(from_epoch); # local common functions # Avoid localisation problems in sar $ENV{LANG}="C"; # # Begin of configuration options # my $dir = "sar2rrd"; # Place under the html dir of user apache # Under that directory you should have one # directory per machine and in it all the # files resulting form the sar and iostat # commands (saxx and ioxx). Look at the # cron file and related script my ($l,$p,$uid,$gid,$q,$c,$gc,$apache,$empty) = getpwnam("apache") or die "apache not in passwd file"; my $base = "$apache/html/$dir"; my $config = Config::Auto::parse("$base/$dir.conf"); my @machines = @{$config->{machines}}; my $debug = 0; foreach my $a (@ARGV) { $debug++ if ($a =~ /-v/); $| = 1; # Flush STDOUT } my $m; my %t; my %attrio = %{ retrieve("$base/$config->{svgio}") }; my %attrsar = %{ retrieve("$base/$config->{svgsar}") }; my $h = "$base/index.html"; open (MAIN, "> $h") || die "Unable to create $h : $!"; my @hm = (h1("sar2rrd - sar/iostats monitoring via rrdtool"), p("List of systems to monitor:")); my @index; my @tmp1; my @tmp2; print Dumper($config) if ($debug > 1); foreach $m (@machines) { @tmp1 = (); @tmp2 = (); # # The graphs/HTML generation # push @index,li(a({href=>"$m/index.html"}, "$m")); my $hm = "$base/$m/index.html"; open (MACHINE, "> $hm") || die "Unable to create $hm : $!"; my @hhm = (h1("sar2rrd - Monitoring $m")); my $img="$base/$m/img"; print "Graphing rrd base for $m under $img\n" if ($debug > 0); printf "Between %s/%s and %s/%s for sar\n",from_epoch($attrsar{$m}{mintmst}),$attrsar{$m}{mintmst},ParseDate("epoch $attrsar{$m}{maxtmst}"),$attrsar{$m}{maxtmst} if ($debug > 0); printf "Between %s (%s) and %s (%s) for iostat\n",ParseDate("epoch $attrio{$m}{mintmst}"),$attrio{$m}{mintmst},ParseDate("epoch $attrio{$m}{maxtmst}"),$attrio{$m}{maxtmst} if ($debug > 0); print "Generating " if ($debug > 0); mkdir "$img",0755 if (not (-d "$img")); unlink <$img/*.png>; print "mem.png " if ($debug > 0); RRDs::graph("$img/mem.png","-s $attrsar{$m}{mintmst}","-e $attrsar{$m}{maxtmst}","-L KB","-v KiloBytes","-aPNG","-t Memory Report","DEF:s1=$base/$m/$config->{rrdsar}:memoryfree:AVERAGE","AREA:s1#00ee22:Free Memory"); my $rrderror = RRDs::error; die "Problem while generating graph: $rrderror\n" if ($rrderror); print "load.png " if ($debug > 0); RRDs::graph("$img/load.png","-s",$attrsar{$m}{mintmst},"-e",$attrsar{$m}{maxtmst},"-S",$config->{step},"-aPNG","-t","Load Report","DEF:s1=$base/$m/$config->{rrdsar}:load1:AVERAGE","DEF:s2=$base/$m/$config->{rrdsar}:load5:AVERAGE","DEF:s3=$base/$m/$config->{rrdsar}:runq:AVERAGE","LINE2:s1#00b6e4:Load 1m","LINE2:s2#00ee22:Load 5m","LINE2:s3#ff0000:Run Queue"); $rrderror = RRDs::error; die "Problem while generating graph: $rrderror\n" if ($rrderror); push @tmp1,td(img({src=>"$config->{http}/$dir/$m/img/mem.png"})); push @tmp1,td(img({src=>"$config->{http}/$dir/$m/img/load.png"})); push @tmp1,HTML::AsSubs::tr(@tmp1); my $col = 0; foreach my $i (@{$attrsar{$m}{measures}},@{$attrio{$m}{measures}}) { if ($i =~ /^eth/) { $i =~ s/-[inout]+//; next if (-f "$img/$i.png"); print "$i.png " if ($debug > 0); RRDs::graph("$img/$i.png","-s",$attrsar{$m}{mintmst},"-e",$attrsar{$m}{maxtmst},"-S",$config->{step},"-aPNG","-t","Network Report","DEF:s1=$base/$m/$config->{rrdsar}:$i-in:AVERAGE","DEF:s2=$base/$m/$config->{rrdsar}:$i-out:AVERAGE","LINE2:s1#ff0000:$i (in)","LINE2:s2#00ee22:$i (out)"); push @tmp2,td(img({src=>"$config->{http}/$dir/$m/img/$i.png"})); $col++; if ($col == 2) { push @tmp1,HTML::AsSubs::tr(@tmp2); @tmp2 = (); $col = 0; } } if (($i =~ /^sd/) || ($i =~ /^hd/) || ($i =~ /^cciss/)) { $i =~ s/-[inout]+//; next if (-f "$img/$i.png"); print "$i.png " if ($debug > 0); RRDs::graph("$img/$i.png","-s",$attrio{$m}{mintmst},"-e",$attrio{$m}{maxtmst},"-S",$config->{step},"-aPNG","-t","Disk Report","DEF:s1=$base/$m/$config->{rrdio}:$i-in:AVERAGE","DEF:s2=$base/$m/$config->{rrdio}:$i-out:AVERAGE","LINE2:s1#ff0000:$i (in)","LINE2:s2#00ee22:$i (out)"); push @tmp2,td(img({src=>"$config->{http}/$dir/$m/img/$i.png"})); $col++; if ($col == 2) { push @tmp1,HTML::AsSubs::tr(@tmp2); @tmp2 = (); $col = 0; } } if (($i =~ /^cpu/) || ($i =~ /^all/)) { $i =~ s/-[a-z]+//; next if (-f "$img/$i.png"); print "$i.png " if ($debug > 0); RRDs::graph("$img/$i.png","-s",$attrsar{$m}{mintmst},"-e",$attrsar{$m}{maxtmst},"-S",$config->{step},"-aPNG","-t","CPU Report","DEF:s1=$base/$m/$config->{rrdsar}:$i-idle:AVERAGE","DEF:s2=$base/$m/$config->{rrdsar}:$i-nice:AVERAGE","DEF:s3=$base/$m/$config->{rrdsar}:$i-system:AVERAGE","DEF:s4=$base/$m/$config->{rrdsar}:$i-user:AVERAGE","AREA:s1#00b6e4:$i (idle)","STACK:s2#00ee22:$i (nice)","STACK:s3#ff0000:$i (system)","STACK:s4#000000:$i (user)"); push @tmp2,td(img({src=>"$config->{http}/$dir/$m/img/$i.png"})); $col++; if ($col == 2) { push @tmp1,HTML::AsSubs::tr(@tmp2); @tmp2 = (); $col = 0; } } } $rrderror = RRDs::error; die "Problem while generating graph: $rrderror\n" if ($rrderror); print "\n" if ($debug > 0); push @hhm,table(@tmp1); my $hhm = body(@hhm); print MACHINE $hhm->as_HTML; close(MACHINE); } push @hm,ul(@index); my $hm = body(@hm); print MAIN $hm->as_HTML; close MAIN; print "Point your browser to $config->{http}/$dir/index.html\n" if ($debug > 0);