#!/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
Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
Read Where should I post X? if you're not absolutely sure you're posting in the right place.
Please read these before you post! —
Posts may use any of the Perl Monks Approved HTML tags:
- a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
| |
For: |
|
Use: |
| & | | & |
| < | | < |
| > | | > |
| [ | | [ |
| ] | | ] |
Link using PerlMonks shortcuts! What shortcuts can I use for linking?
See Writeup Formatting Tips and other pages linked from there for more info.