320 lines
7.8 KiB
Perl
320 lines
7.8 KiB
Perl
#!/usr/bin/perl -w
|
|
#
|
|
# SNMP (near) Real-time grapher.
|
|
#
|
|
# Cameron Kerr <cameron@humbledown.org>
|
|
#
|
|
|
|
use strict;
|
|
use warnings;
|
|
use Tk;
|
|
use Tk::Table;
|
|
use Net::SNMP;
|
|
use Getopt::Long;
|
|
use POSIX qw(log10 floor);
|
|
|
|
############################################################################
|
|
# Parse options
|
|
#
|
|
|
|
my $usage = <<EOF;
|
|
snmprtg [--title "title"]
|
|
[--domain udp|udp6|tcp|tcp6|tcp/ipv6|...]
|
|
--peer hostname|address
|
|
[--port 161]
|
|
[--version v2c (default) | v1]
|
|
[--community "public"]
|
|
--oid OID
|
|
[--interval 15] (seconds)
|
|
[--window 10] (minutes)
|
|
[--gauge]
|
|
[--maxY maxYval]
|
|
[--quiet]
|
|
[--log 0] (0 no, 1 yes)
|
|
EOF
|
|
|
|
my $title;
|
|
my $domain = "udp";
|
|
my $peer = "localhost";
|
|
my $port = 161;
|
|
my $timeout = 2; # seconds
|
|
my $version = "v2c";
|
|
my $community = "public";
|
|
my $oid;
|
|
my $gauge = 0;
|
|
my $interval = 15; # seconds
|
|
my $window = 10; # minutes
|
|
my $maxY;
|
|
my $quiet = 0;
|
|
my $log = 0;
|
|
|
|
GetOptions(
|
|
"title=s" => \$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( '<Configure>' => 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 {
|
|
|
|
}
|
|
|
|
|