Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

(code)) The Going input/output Rate (deprecated by node 121950)

by ybiC (Prior)
on Jan 18, 2001 at 00:23 UTC ( [id://52595]=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 ##

Every so often, a sysadmin asks me to check bandwidth utilization by a particular server.   I use tools like CiscoWorks, SNMPc and MRTG for ongoing monitoring, but instead of mucking about with those for one-off checks, I wrote this ditty.

It prompts for switch name/IP, port, number of runs, delay between runs, and (no-echo) passwords, then reports estimated total runtime and output file size, and telnets to the device to collect 5 minute input/output rates.   Output file is csv for easy importing/reporting with Excel or whatever.

In it's present form, commands and parsing are tweaked for Cisco Catalyst 3548 ethernet switch, but should be trivial to add support for other IOS switches (2916, 2924) and routers.   Support for CatOS switches like 6000, 5000, 2948g may take a bit more work.

From a Perlish perspective, it's been an exercise in learning more of for, sleep, if, unless, int, plus subs for readability.   As always, critique and suggestions are more than welcome.

Thanks to: tye and chipmunk for suggestions on how to join output file lines, and to several monks whose names I missed for a flurry o' CB suggestions on integer-checking input.

Update: 2001-04-30
un-subified non-redundant code, for fewer global vars.
hashamafied passel o' scalars.

#!/usr/bin/perl -w

# shoint.pl
# pod at tail

use strict;
use Net::Telnet::Cisco;
use Term::ReadKey;
use Tie::IxHash;
use Time::localtime;

use vars qw(
    $loops
    $port
    $delay
    $cisco
    $pass
    $enable
    );


my %parm =(
    errmode => 'return',
    timeout => '10',
    );
my %file = (
    tmp0 => 'shoint.tmp0',
    tmp1 => 'shoint.tmp1',
    tmp2 => 'shoint.tmp2',
    log  => 'shoint.csv',
    );
my @tmpfiles = (
    $file{tmp0},
    $file{tmp1},
    $file{tmp2},
    );


######################################################################
+####
print(
    "\n",
    "  Record bandwidth utilization on one port of a Cisco switch for 
+set length of time.\n",
    "  Output file in CSV format for easy importing.\n\n",
    "  CTRL+C at any time to interupt.\n\n",
    );
&CLEANUP();


######################################################################
+####
tie my %inprompts, "Tie::IxHash";
%inprompts = (
    '    Name or IP:' => 'cisco',
    '    Blade/port:' => 'port',
    '    Iterations:' => 'loops',
    ' Seconds delay:' => 'delay',
    );
print "  Prompting for switch info...\n";
tie my %otherins, "Tie::IxHash";
for my $inprompt (keys %inprompts) {
    print "  $inprompt ";
    chomp(my $input = <STDIN>);
    $otherins{$inprompts{$inprompt}} = $input;
    }
print "\n";
$cisco = ($otherins{'cisco'});
$port  = ($otherins{'port'});
$loops = ($otherins{'loops'});
$delay = ($otherins{'delay'});
if ($cisco eq "") {
    die "Sorry, you must enter *something* for switch name/IP.\n\n"
    };
if ($port eq "") {
    die "Sorry, you must enter *something* for blade/port.\n\n"
    };
unless ($loops eq int($loops) and $loops > 0) {
    die "Sorry, you must enter a positive non-zero integer for \"itera
+tions\".\n\n"
    };
unless ($delay eq int($delay) and $delay >=0) {
    die "Sorry, you must enter a positive integer for \"delay\".\n\n"
    };


######################################################################
+####
my $duRun = $loops * 1.5;       # KB
my $duEnd = $loops/10;          # KB
print "$duRun KB disk space will be used (then freed up) during progra
+m run.\n";
print "$duEnd KB disk space will be used when program finished.\n";
# add 1sec/loop to prevent divide-by-zero warning if $delay=0
my $runsec  = ($loops * $delay) + ($loops);
my $runmin  = $runsec/60;
my $runhr   = $runmin/60;
my $runday  = $runhr/24;
my $runweek = $runday/7;
print "Runtime ";
# Display runtime in largest possible unit of time that shows > 0
my $run = $runsec  and my $unit = "second(s)" if ($runsec  >= 1 and $r
+unmin  < 1);
   $run = $runmin  and    $unit = "minute(s)" if ($runmin  >= 1 and $r
+unhr   < 1);
   $run = $runhr   and    $unit = "hour(s)"   if ($runhr   >= 1 and $r
+unday  < 1);
   $run = $runday  and    $unit = "day(s)"    if ($runday  >= 1 and $r
+unweek < 1);
   $run = $runweek and    $unit = "week(s)"   if ($runweek >= 1);
printf("%.1f $unit\n",$run);    # round to 1 decimal place


######################################################################
+####
tie my %passprompts, "Tie::IxHash";
%passprompts = (
    '      Password:' => 'pass',
    '        Enable:' => 'enable',
    );
print "\n",
      "  Prompting for password and enable... (no screen echo)\n",
      "  Type carefully - backspaces not accepted.\n";
tie my %passwds, "Tie::IxHash";
for my $passprompt (keys %passprompts) {
    print "  $passprompt ";
    ReadMode('noecho');            # don't echo to screen.
    chomp(my $input = <STDIN>);
    $passwds{$passprompts{$passprompt}} = $input;
    ReadMode(0);                   # re-activate screen echo.
    print "\n";
    }
print "\n";
$pass    = ($passwds{"pass"});
$enable  = ($passwds{"enable"});
if ($pass   eq "") {
    die "Sorry, you must enter *something* for Password.\n\n"
    };
if ($enable eq "") {
    die "Sorry, you must enter *something* for Enable pass.\n\n"
    };


print "Starting switchport input/output rate check.\n";
print "Running $loops iterations at $delay second intervals\n";
&PCTIME();
print "Please wait...\n\n";


######################################################################
+####
my @commands = (
    'set leng 0',
    'sho ntp',
    "sho int $port",
    'disa',
    );
    # 'term leng 0',
    # 'sho clo',
for(my $i=1; $i<=$loops; $i++) {
    if (my $cs=Net::Telnet::Cisco->new(
        host      => $cisco,
        timeout   => $parm{timeout},
        errmode   => $parm{errmode},
        input_log => $file{tmp0},
        )
        ) {
        $cs->login('',$pass);              # vty login
        if ($cs->enable($enable)) {        # privilaged mode
                foreach my $command (@commands) {
                    my @output = $cs->cmd($command);
                    }
            } else {
            warn "  shoint.pl: Enable failed: " . $cs->errmsg;
            }  # if privilaged mode failed
        $cs->close;                        # exit session

        # if multiple Cisco sessions, append each to same tmpfile
        open (TMP0, "<$file{tmp0}")
            or die "  shoint.pl: Can't open $file{tmp0} RO: $!";
        open (TMP1, ">>$file{tmp1}")
            or die "  shoint.pl: Can't open $file{tmp1} for append: $!
+";
        while (<TMP0>) { print TMP1 $_; }
        close (TMP0)
            or die "  shoint.pl: Can't close $file{tmp0}: $!";
        close (TMP1)
            or die "  shoint.pl: Can't close $file{tmp1}: $!";
        }
    else { warn "  shoint.pl: D'oh! Telnet connection failed"; } # if 
+telnet connection fails
    sleep $delay;
    }


######################################################################
+####
&PARSE();                           # Extract desired text from output
&CLEANUP();                         # Unlink tempfiles
print "Completed switchport input/output rate check.\n";
print "Results at $file{log}\n";
&PCTIME();


######################################################################
+####
# subroutines start here
######################################################################
+####
sub CLEANUP {
    foreach my $tmpfile(@tmpfiles) {
        if (-e $tmpfile && -o _) {
            unlink ($tmpfile)
                or warn "  shoint.pl: Can't unlink $tmpfile: $!";
            }
        }
    }
######################################################################
+####
sub PCTIME {
printf "PC localtime %d:%d:%d %d-%d-%d\n\n",
    localtime -> hour(),
    localtime -> min(),
    localtime -> sec(),
    localtime -> mon()+1,
    localtime -> mday(),
    localtime -> year()+1900,
}
######################################################################
+####
sub PARSE {
my @keep     = ('CST', '5 minute', );
my @strip    = (', \d+ packets/sec', '^\s+$', );
# Extract lines w/desired text and write to temp file
open (TMP1, "<$file{tmp1}") or die "  shoint.pl: Can't open $file{tmp1
+} RO: $!";
open (TMP2, ">$file{tmp2}") or die "  shoint.pl: Can't open $file{tmp2
+} WO: $!";
    while (<TMP1>) {
        foreach my $keep(@keep) {
            if (/$keep/) {
                print TMP2 $_;
            }
        }
    }
close (TMP1)              or warn "  shoint.pl: Can't close $file{tmp1
+}: $!";
close (TMP2)              or die  "  shoint.pl: Can't close $file{tmp2
+}: $!";

# Strip unwanted text strings and write to output file
open (TMP2, "<$file{tmp2}")  or die "  shoint.pl: Can't open $file{tmp
+2} RO: $!";
open (LOG,  ">$file{log}")   or die "  shoint.pl: Can't open $file{log
+} WO: $!";
    while (<TMP2>) {
        foreach my $strip(@strip) {
            s/$strip//g;
            }
        s/  5 minute/5 minute/g;         # strip unwanted spaces
        s/CST /CST,/g;                   # csv-ify timestamp
        s/2001/2001,/g;                  # csv-ify datestamp
        s/ bits\/sec/,/g;                # csv-ify and strip "bps" fro
+m rate number
        s/put rate/put rate \(bps\),/g;  # csv-ify and insert "bps" in
+to rate descr
        chomp if (/(CST|input)/);        # merge each iteration into o
+ne line
    print LOG $_;
    }
close (TMP2)             or warn "  shoint.pl: Can't close $file{tmp2}
+: $!";
close (LOG)              or die  "  shoint.pl: Can't close $file{log}:
+ $!";
}
######################################################################
+####


=head1 Name

 shoint.pl

=head1 Summary

 Automate logging of 5 minute input & output rates for individual Cisc
+o IOS device ports.
 Does *not* work with CatOS switches like 2948g, 4000, 5000, 6000 fami
+lies
 ~100 bytes of data generated for each iteration (after tmpfiles unlin
+ked)
 ~1.5KB per iteration while running (before tmpfiles unlinked)

=head1 Usage

 shoint.pl<enter>
 (script prompts for all needed info)

=head1 Tested

 with:    Perl 5.00503 on Debian 2.2 "Espy"
 against: Cisco Catalyst 3524xl IOS 12.0
                         2924xl IOS 11.28sa5

=head1 Comments

 Only works if device has standard Cisco prompts: ">", "#", "(enable)"
 Would rather use SNMP instead of telnet, but no easy way to automate 
+mapping
    of Cisco "blade/port" to SNMP "intName" 8^(

=head1 Updated

 2001-04-30   17:20
   Un-subify for fewer global vars.
   Hashamafy passel o' scalars.
 2001-01-11
   Initial working code.

=head1 ToDos

 Mixed-case subroutine names w/o ampersan.
 Unlink files from prior runs.
 Better error handling if bad switchname or password entered
 Use Proc::Daemon to background, allow disconnect of initiating sessio
+n
 Use File::Slurp to append log - avoid race condition?
 Use File::Temp for tmpfiles

=head1 Author

 [ybiC]

=head1 Credits

 [tye] and [chipmunk] for suggestions on how to join output file lines
+.
 Several monks whose names I forget for CB suggestions on integer-chec
+king input.

=cut

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others imbibing at the Monastery: (4)
As of 2024-04-19 23:56 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found