Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

(code)) Net::SNMP bandwidth query (deprecated by node 121950)

by ybiC (Prior)
on Aug 13, 2001 at 02:16 UTC ( #104326=sourcecode: print w/ replies, xml ) Need Help??

Category: Networking Code
Author/Contact Info ybiC
Description: ## deprecated by "(code) Net::SNMP, Bandwidth, GnuPlot, PNG, PostScript, Excel" ##

netsnmpiio.pl
Periodically query SNMP-enabled devices for interface (in|out)put octets, and record results in csv and xls outfiles.

Intended for relatively short term spot-tests of individual interfaces.   Other tools like MRTG, SNMPc, and CiscoWorks do fine for large number of ports, or for ongoing monitoring.   Uses Net::SNMP instead of system call to UCD-SNMP's snmpwalk like my earlier efforts.   Mind you, snmpwalk is quite useful - I just wanted to eliminate unecessary dependancies on external libraries.

Thanks to:
tachyon, HamNRye, crazyinsomniac, tilly, lemming, and wog.
Oh yeah, and to some guy named vroom.

As always:
Comments, corrections, and criteque welcome and requested.

Most recent update:
2001-08-22   10:00   Unlink .tmp at end of run, skip Spreadsheet::WriteExcel stuff if module not installed, fix wrong %util calculation, display MBytes transferred, gracefully handle non-responsive (host|OID).

#!/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


Comment on (code)) Net::SNMP bandwidth query (deprecated by node 121950)
Download Code

Back to Code Catacombs

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: sourcecode [id://104326]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others rifling through the Monastery: (3)
As of 2015-07-06 08:13 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The top three priorities of my open tasks are (in descending order of likelihood to be worked on) ...









    Results (70 votes), past polls