#!/usr/bin/perl -w
# netsnmpiio.pl
# pod at tail
use strict;
use Net::SNMP;
my $target = shift or Usage();
my $iters = (shift or 2);
my $delay = (shift or 30);
my $ifIndex = (shift or 2);
my $outdir = (shift or '.');
my $community = (shift or 'public');
my $timeout = 15;
my $port = 161;
Usage() unless (
$target =~ (/\w+/) and
$iters =~ (/\d+/) and
$delay =~ (/\d+/) and
$ifIndex =~ (/\d+/) and
$outdir =~ (/.*/) and
$community =~ (/[\w+\.\/]/)
);
# ifName, ifSpeed, ifInOctets, ifOutOctets
# ifDescr ".1.3.6.1.2.1.2.2.1.2.$ifIndex",
my @oids = (
".1.3.6.1.2.1.31.1.1.1.1.$ifIndex",
".1.3.6.1.2.1.2.2.1.5.$ifIndex",
".1.3.6.1.2.1.2.2.1.10.$ifIndex",
".1.3.6.1.2.1.2.2.1.16.$ifIndex",
);
my ($mday,$mon,$year) = (localtime)[3..5];
my $ymd = sprintf("%04d%02d%02d",$year+1900,$mon+1,$mday);
my %file = (
tmp => "$outdir/$target-$ifIndex-$ymd.tmp",
csv => "$outdir/$target-$ifIndex-$ymd.csv",
);
$file{xls} = "$outdir/$target-$ifIndex-$ymd.xls" if (my $have_SWE == 1
+);
unless(-d $outdir && -w _) {
print "\n\nSorry, $outdir isn't a directory,",
" or you don't have write perms there.\n\n";
exit;
}
my @unlink1 = (
$file{tmp},
$file{csv},
);
push @unlink1, $file{xls} if ($have_SWE == 1);
# Files writable only by this user for security purposes:
# delete pre-existing out file because subsequent writes append:
print "\nStart run of $0\n";
umask oct 133;
for(@unlink1) { Unlink($_, 'verbose'); }
print " Print csv header...";
open (CSV, ">$file{csv}") or die "Error opening $file{csv}: $!";
print CSV "Date,Time,Target,ifName,Seconds,MBytes,Mbits/s,%Util,\n";
close CSV or die "Error closing $file{csv}: $!";
print " done!\n";
print " Query target device...";
# Get down to business:
my $i = 1;
for(1..$iters) {
open (TMP, ">>$file{tmp}") or die "Error opening $file{tmp}: $!";
# Localtime for human consumption, epoch for bw calcs.
# Prepend relevant info to csv line:
my ($sec,$min,$hour,$mday,$mon,$year) = localtime(time);
my $ymdhms = sprintf( "%04d-%02d-%02d,%02d:%02d:%02d",
$year+1900,$mon+1,$mday,$hour,$min,$sec);
my $epochsec = time;
print(TMP "$ymdhms,$epochsec,$target,$ifIndex,")
or die "Error printing to $file{tmp}: $!";
# Here's where da *real* stuf happens:
# my ($session, $error) = Net::SNMP->session(
my $session = Net::SNMP->session(
-hostname => $target,
-community => $community,
-port => $port,
-timeout => $timeout,
);
unless (defined($session)) { print TMP "SNMP SESSION ERROR,"; }
for my $oid(@oids) {
if (defined(my $response = $session->get_request($oid))) {
print(TMP $response->{$oid});
}
else { print TMP '0'; }
print TMP ',';
}
$session->close();
# One line per iteration:
# End of iteration:
# delay between iterations, but not after last iteration:
print TMP "\n";
close TMP or die "Error closing $file{tmp}: $!";
sleep $delay unless($i==$iters);
$i++;
}
print " done!\n";
print " Munge .tmp to .csv (Mbps and %util)...";
{
# Contained block to limit 'print csv with $,'.
# Initialize prior array to 0 to avoid annoying 'not numeric' error
+.
local $, = ',';
my @prior = (0) x 9;
open (TMP, "<$file{tmp}") or die "Error opening $file{tmp}: $!";
open (CSV, ">>$file{csv}") or die "Error opening $file{csv}: $!";
# date,time,epochSecs,target,ifIndex,ifName,ifSpeed,ifInOctets,ifOu
+tOctets,
# 0 1 2 3 4 5 6 7
+8
while(<TMP>) {
chomp;
my @data = split ',';
# Subtract epochSecs for deltaTime.
# Protect against 'divide by zero' error.
my $dtime = $data[2] - $prior[2];
next unless $dtime;
my $inBytes = $data[7];
my $outBytes = $data[8];
my $priorInBytes = $prior[7];
my $priorOutBytes = $prior[8];
my $speed = $data[6];
# Handle counter-roll-over-to-zero in approximate fashion,
# and prevent 'divide by zero' error if no OID response.
$priorInBytes = 1 if(
($inBytes < $priorInBytes) or ($priorInBytes == 0)
);
$priorOutBytes = 1 if(
($outBytes < $priorOutBytes) or ($priorOutBytes == 0)
);
$speed = 1 if($speed == 0);
my $recv = $inBytes - $priorInBytes;
my $xmit = $outBytes - $priorOutBytes;
my $Bytes = $recv + $xmit;
my $MBytes = $Bytes / 1048576;
my $Mbits = $MBytes * 8;
my $Mbps = $Mbits / $dtime;
# * 100 convert decimal to percent
# * 1000000 move decimal point to match $speed
# * 1.048576 bits per Megabit
my $util = ($Mbps * 100 * 1000000) / ($speed * 1.048576);
# Round output, but not @data(next iteration's @prior).
my $MBytesRound = sprintf("%.3f",$MBytes);
my $MbpsRound = sprintf("%.3f",$Mbps);
my $utilRound = sprintf("%.3f",$util);
# Don't output bogus first iteration.
if($prior[2] > 0) {
print CSV @data[0,1,3,5,],
"$dtime,$MBytesRound,$MbpsRound,$utilRound,\n";
# print CSV @data[0,1,3,5,], "$dtime,$MBytes,$Mbps,$util,\n";
}
# Set current values for 'prior' on next iteration.
@prior = @data;
}
close (TMP) or die "Error closing $file{tmp}: $!";
close (CSV) or die "Error closing $file{csv}: $!";
}
print " done!\n";
# Munge csv to xls *if* Spreadsheet::WriteExcel module installed.
BEGIN {
$have_SWE = 0;
eval { require Spreadsheet::WriteExcel };
unless ($@) {
Spreadsheet::WriteExcel->import();
$have_SWE =1;
}
}
if ($have_SWE == 1) {
print " Create xls outfile from csv...";
open (CSVFILE, $file{csv}) or die "Error opening $file{csv}: $!";
my $workbook = Spreadsheet::WriteExcel -> new($file{xls});
my $worksheet = $workbook -> addworksheet();
my $row = 0;
while (<CSVFILE>) {
chomp;
my @field = split(',', $_);
my $column = 0;
foreach my $token (@field) {
$worksheet -> write($row, $column, $token);
$column++;
}
$row++;
}
my $format1 = $workbook -> addformat();
$format1 -> set_bold();
$format1 -> set_align('center');
$format1 -> set_bg_color('tan');
# $format1 -> set_border();
$worksheet -> set_row(0, undef, $format1);
$workbook -> close() or die "Error closing $workbook: $!";
print " done!\n";
}
# Wrap it all up:
my @unlink2 = (
$file{tmp},
);
for(@unlink2) { Unlink($_, 'verbose'); }
print "Completed run of $0.\n";
print " $file{xls}\n" if ($have_SWE == 1);
print " $file{csv}\n",
;
# " $file{tmp}\n",
exit 0;
######################################################################
+#####
# Delete outfiles from prior run (if same name as this run)
sub Unlink {
my $file = $_[0];
my $echo = $_[1];
if (-e $file && -w _) {
print " Unlink $file..." if ($echo eq 'verbose');
unlink $file or die "Error unlinking $file: $!";
print " *poof*\n" if ($echo eq 'verbose');
}
}
######################################################################
+#####
sub Usage {
print "
Ooot!
You forgot to enter necessary information, or entered bad information!
Usage:
netsnmpiio.pl target iterations delay ifIndex outdir ROcommunitystring
target: an IPaddress, DNS name, or FQDN.
iterations: how many queries you wish to run. (default 2)
delay: seconds to wait between iterations. (default 30)
ifIndex: SNMP parameter specifying port or interface. (default 2)
outdir: destination dir for outfile. No trailing '/'. (default '.')
ROcoumminitystring: SNMP string. (default 'public')
Example:
netsnmpiio.pl routerC 30 600 7 MyCommunityString /datadir
";
exit 1;
}
######################################################################
+#####
=head1 NAME
netsnmpiio.pl
=head1 SYNOPSIS
Query SNMP-enabled devices for interface (in|out)put octets.
Csv outfile ~ 70KB per iteration.
"clear counters" before running; CatOSv5 rollover > 4,000,000,000
Intended for spot-tests of individual interfaces.
Other tools (MRTG, SNMPc, CWSI) do well for large number of ports.
netsnmpiio.pl target iterations delay ifIndex outdir ROcommunitystrin
+g
target: an IPaddress, DNS name, or FQDN.
iterations: how many queries you wish to run. (default of 2)
delay: seconds to wait between iterations. (default 30)
ifIndex: SNMP parameter specifying port or interface. (default of 2)
outdir: destination dir for outfile. No trailing '/'. (default '.')
ROcoumminitystring: SNMP string. (default 'public')
Example:
netsnmpiio.pl hq5500 30 600 333 MyCommunityString /datadir
Assorted conversions:
144 iterations 600 sec delay = 24 hours
432 iterations 600 sec delay = 3 days
1008 iterations 600 sec delay = 7 days
2016 iterations 600 sec delay = 14 days
100Mbps = 12.5MBps
1GB = 8,589,934,592 bits (1,073,741,824 * 8)
1MB = 8,388,608 bits (1,048,576 * 8)
1KB = 8,192 bits (1024 * 8)
1Gb = 1,073,741,824 bits (1024-e3)
1Mb = 1,048,576 bits (1024-e2)
1Kb = 1,024 bits (1024-e1)
ifInOctets+ifOutOctets = Bytes
((Bytes*8)/1024)/seconds = Kbps
((Bytes*8)/1048576)/seconds = Mbps
=head1 TESTED
Net::SNMP 3.6
Perl 5.00503 Debian 2.2r3
Perl 5.00601 Cygwin on Win2kPro
ActivePerl 5.6.1 Win2kPro
=head1 UPDATED
2001-08-22 10:00 CDT
Unlink .tmp at end of run.
Confirm counter-roll-over-to-zero Does The Right Thing.
2001-08-17 10:30 CDT
No xls outfile if Spreadsheet::WriteExcel module not installed.
2001-08-15 18:10 CDT
Fix broken %util calculation.
Correct minor tyops.
2001-08-14 21:15 CDT
Display MBytes transferred.
Gracefully handle non-responsive (host|OID):
(MBytes|Mbps|%Util) = -0.000
Snmp query syntax cleaner.
Don't print snmp query or session errors.
Fix incorrect Bps calculations.
Create xls outfile from csv.
Correct error in Usage and pod.
2001-08-13 10:35 CDT
Gracefully handle counter-roll-over-to-zero;
~4,000,000,000 on CatOS 5.
Use ifName instead of ifDescr
Cisco CatOS 5
report 'blade #/port #' instead of '10/100 utp ethernet (cat 3/5)
+'.
2001-08-12 21:20 CDT
Test with ActivePerl on Win2k.
Fix sundry tyops.
Post to PerlMonks networking code (requesting critique).
Net::SNMP instead of UCD-SNMP system call:
Advantages:
Reduce non-Perl requirements.
Eliminate annoying 'one-less-than-actual-ifIndex'.
About one second quicker response.
Drawbacks:
Must use numeric OIDs.
UCD SNMP utilities still quite handy.
Initialize 'prior' array at '0' to
eliminate annoying 'not numeric' message.
Fix bad conversion math in pod.
Add ifDescr.
2001-08-11 22:50 CDT
Add example temp+csv output to pod.
Round Bps to 0 decimal places.
Round %util to 4 decimal places (100ths of %).
Utilization percentage calculations, corrected for MegaBytes.
Bypass bogus first Bps output since no prior epochSec.
Bps calculations from raw SNMP data.
Query ifSpeed for %util calculations.
Defaults for all input values except target.
Sanity-check $outdir: letters, numbers, dots, dashes, underscores.
Filetest for write in outdir and outdir *is* directory.
Simplify iteration localtime syntax.
Cleaner syntax for no sleep after last iteration.
Debug errant commas in csv.
Unsubify snmp query localtime+time since only called once.
@raw array instead of initial temp file.
Filename of target-ifIndex-yyyymmdd.csv.
Add hostname,oid,ifIndex to csv output.
Use PcTime return values (print the values in main).
Add $outdir.
2001-08-10 21:35 CDT
Sanity-check input for (wordchars|numbers) as appropriate.
More informative Usage().
Input from commandline instead of hardcoded.
Efforts to improve data structure.
No sleep after last iteration.
2001-08-09 16:10 CDT
Leading-zero padded time/date in outfiles.
yyyymmdd filenames w/sprintf.
2001-08-08 16:00 CDT
Initial working code.
=head1 TODOS
Use hash instead of array for OIDs:
OID name for key, numeric OID for value.
Use DBI or MySQL or Postgres instead of csv.
Use Text::xSV to parse csv.
Bar graph output:
GD::Graph + GD.pm + libgd
Debian stable libgd1g too old for current GD.pm
but needed by Webalyzer. 8^(
Use Pod:Usage to automagically synchronize pod with Usage().
=head1 AUTHOR
ybiC
=head1 CREDITS
Thanks to tachyon, HamNRye, tilly, lemming, wog, and crazyinsomniac.
Oh yeah, and to some guy named vroom. ;^)
=cut
-
Are you posting in the right place? Check out Where do I post X? to know for sure.
-
Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
<code> <a> <b> <big>
<blockquote> <br /> <dd>
<dl> <dt> <em> <font>
<h1> <h2> <h3> <h4>
<h5> <h6> <hr /> <i>
<li> <nbsp> <ol> <p>
<small> <strike> <strong>
<sub> <sup> <table>
<td> <th> <tr> <tt>
<u> <ul>
-
Snippets of code should be wrapped in
<code> tags not
<pre> tags. In fact, <pre>
tags should generally be avoided. If they must
be used, extreme care should be
taken to ensure that their contents do not
have long lines (<70 chars), in order to prevent
horizontal scrolling (and possible janitor
intervention).
-
Want more info? How to link
or How to display code and escape characters
are good places to start.