#!/usr/bin/perl -w # # SNMP (near) Real-time grapher. # # Cameron Kerr # use strict; use warnings; use Tk; use Tk::Table; use Net::SNMP; use Getopt::Long; use POSIX qw(log10 floor); ############################################################################ # Parse options # my $usage = < \$title, "domain=s" => \$domain, "peer=s" => \$peer, "port=i" => \$port, "timeout=i" => \$timeout, "version=s" => \$version, "community=s" => \$community, "oid=s" => \$oid, "gauge" => \$gauge, "maxY=i" => \$maxY, "interval=i" => \$interval, "window=i" => \$window, "quiet" => \$quiet, "log:i" => \$log, ) or die $usage; die $usage unless defined $oid and defined $peer; $title = "$oid\@$peer" unless defined($title); ############################################################################ # Set up SNMP # if ($oid !~ m/^[0-9.]+$/) { $oid = `snmptranslate -IR -On -To "$oid"`; chomp $oid; print STDERR "OID = $oid\n"; } my @polldata; my $oldval = undef; my ( $session, $error ) = Net::SNMP->session( -domain => $domain, -hostname => $peer, -port => $port, -timeout => $timeout, -version => $version, -community => $community, ); die "Session: $error\n" unless ($session); ############################################################################ # Create the interface # my ( $width, $height ) = ( 500, 150 ); my $axemargin = 5; my ( $leftmargin, $rightmargin, $topmargin, $bottmargin ) = ( 40, 20, 20, 40 ); my $top; my $canvas; $top = MainWindow->new( title => "SNMPrtg - $title" ); $top->iconname( 'SNMPrtg' ); $canvas = $top->Canvas( width => $width, height => $height, -background => 'white' )->pack( -fill => 'both', -expand => 1 ); $top->bind( '' => sub { my $xe = $top->XEvent; $width = $xe->w; $height = $xe->h; draw_graph(); } ); draw_graph(); #Timer event to capture the data $canvas->repeat( $interval * 1000, \&do_polling ); MainLoop; $session->close; sub draw_graph { $canvas->delete( 'all' ); #Verticle Axes $canvas->createLine( $leftmargin, $height - $bottmargin + $axemargin, $leftmargin, $topmargin - 10, -fill => 'black', -arrow => 'last' ); #Horizontal Axes $canvas->createLine( $leftmargin - $axemargin, $height - $bottmargin, $width - $rightmargin + 10, $height - $bottmargin, -fill => 'black', -arrow => 'last' ); my $x = $leftmargin; my $deltax = ( $width - $leftmargin - $rightmargin ) / $window; my $horiz_axes_modulo = 5; for my $i ( 0 .. $window ) { $canvas->createLine( $x, $height - $bottmargin, $x, $height - $bottmargin + $axemargin ) unless $i >= $window; $canvas->createText( $x, $height - $bottmargin + $axemargin, -text => $i, -anchor => 'n' ) if ($i % $horiz_axes_modulo) eq 0; $x += $deltax; } $canvas->createText( ( $width - $rightmargin ) / 2, $height - 5, -text => 'Age (minutes)', -anchor => 's' ); #Draw the poll-interval $canvas->createText( $width - 5, $height - 5, -text => 'Poll Interval: '. $interval .' secs', -anchor => 'se' ); ##Title (already in window text) #$canvas->createText( $width - $rightmargin, $topmargin - 1, # -text => $title, # -anchor => 'se' ); #What is the current maximum value? my $maxval = 0; my $tmp = 0; for $tmp ( @polldata ) { $maxval = $tmp if $tmp > $maxval; } #Round up maxval, and draw it on the vertical axes my $majticks; my $numdigits = 1; { $numdigits = int(log10($maxval)) + 1 unless ($maxval == 0); #What is the most significant digit? my $msb = floor( $maxval / (10 ** ($numdigits - 1)) ); #Human beans tend to like factors of 10, 5, or 2 #to compare against on a graph. $majticks = 1; $majticks = 2 if ( $msb >= 1 ); $majticks = 5 if ( $msb >= 2 ); $majticks = 10 if ( $msb >= 5 ); $maxval = $majticks * 10 ** ($numdigits - 1); } ## ## TODO: Make y ticks ## $maxval = $maxY if $maxY; $canvas->createText( $leftmargin - 5 , $topmargin + 2, -text => rawtohr($maxval), -anchor => 'e' ); my $revision = 'SNMPrtg v2 http://humbledown.org/'; $canvas->createText( 5, $height - 5, -font => [-size => 6], -text => $revision, -anchor => 'sw' ); #Plot the data $x = $leftmargin; my ( $y, $ox, $oy ) = ( 0, -1, 0 ); $deltax = ( $width - $leftmargin - $rightmargin ) / ( 60 / $interval * $window ); for my $val (@polldata) { $y = ( $val / ( $maxval + 0.0000001 ) ) * ( $height - $topmargin - $bottmargin ); $y = $height - $bottmargin - $y; $canvas->createLine( int($x), int($y), int($ox), int($oy), -fill => 'red' ) unless $ox < 0; ( $ox, $oy ) = ( $x, $y ); $x += $deltax; } } sub do_polling { my $response = $session->get_request( $oid ); if ( not defined($response) ) { print STDERR "Error getting request: ". $session->error ."\n"; add_error_to_poll_data(); return; # empty return guaranteed to be false } my $newval = $response->{$oid}; my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime; my $d = sprintf "%d-%02d-%02d, %02d:%02d:%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec; my $f = sprintf "%d-%02d-%02d-%s.log", $year+1900, $mon+1, $mday, $peer; my $s=""; if (defined $oldval) { #print "New: $oldval -> $newval, diff ". ($newval - $oldval) ."\n" unless $quiet; } else { #print "Initial: $newval\n"; $s=sprintf "Abs/Rel, Value, IP, Date, Time, OID"; print "$s\n" unless $quiet; `echo "$s" > $f`; } if ($gauge) { add_to_poll_data($newval); if ($log > 0) { $s=sprintf ("A, %d, %s, %s, %s", $newval - $oldval, $peer, $d, $oid) if(defined $oldval); print "$s\n" unless $quiet; `echo "$s" >> $f`; } } else { add_to_poll_data($newval - $oldval) if defined $oldval; if ($log > 0) { $s=sprintf ("R, %d, %s, %s, %s", $newval - $oldval, $peer, $d, $oid) if(defined $oldval); print "$s\n" unless $quiet; `echo "$s" >> $f`; } } $oldval = $newval; draw_graph(); return 1; } sub add_error_to_poll_data { pop( @polldata ) if ( $#polldata >= 60 / $interval * $window ); unshift( @polldata, -1 ) if defined $oldval; } sub add_to_poll_data { my ($delta) = @_; pop( @polldata ) if ( $#polldata >= 60 / $interval * $window ); if ($gauge) { unshift( @polldata, $delta ) if defined $oldval; } else { unshift( @polldata, $delta / $interval ) if defined $oldval; } } sub rawtohr { #Convert a number such as 16000 to 16k. Base-2 my ($val, $suffix); ($val, $_) = @_; $suffix = ''; if ( $val >= 1000000000 ) { $val = int( $val / 1000000000 ); $suffix = 'G'; } elsif ( $val >= 1000000 ) { $val = int( $val / 1000000 ); $suffix = 'M'; } elsif ( $val >= 1000 ) { $val = int( $val / 1000 ); $suffix = 'k'; } return sprintf( "%d%s", $val, $suffix ); } sub log_values { }