#!/usr/bin/perl -w
# cdp-n.pl
# pod at tail
use strict;
use Net::Telnet::Cisco;
use Term::ReadKey;
use Tie::IxHash;
### Begin Config Parameters ###
my %parm = (
tntimeout => 10,
pwtimeout => 60,
errmode => 'return',
);
my %file = (
in => 'cdp-n.in',
tmp0 => 'cdp-n.tmp0',
tmp1 => 'cdp-n.tmp1',
tmp2 => 'cdp-n.tmp2',
tmp3 => 'cdp-n.tmp3',
tmp4 => 'cdp-n.tmp4',
uniqout => 'cdp-n.uniq',
);
my @tmpfiles = qw(
$file{tmp0}
$file{tmp1}
$file{tmp2}
$file{tmp3}
$file{tmp4}
);
my @commands = (
'term leng 0', # IOS
'set leng 0', # CatOS
'sho cdp n de', # distinguish 'details' from 'duplex'
);
my @keeplines = ( # lines of text to be kept
'scrpne', # Corporate switches
'cff', # old Frozen switches at Campus 5
'pdl', # old Frozen switches at Campus 6
);
my @stripchars = ( # specific text to be stripped from what's l
+eft
'\.\w.*', # domain name (if present) through end-of-li
+ne
'^.*\(', # characters and paren before devicename
'\).*$', # paren after devicename through end-of-line
'device-id:\s', # preceeds some switches, dunno why
'device id:\s', # preceeds some switches, dunno why
'^.*>.*$', # command prompts
'^\s+', # leading spaces
);
my @striplines = ( # strip all lines containing this text
'-MSFC',
'-msfc',
'RSM',
'rsm',
'Password',
'password',
'unintialized value',
);
### End Config Parameters ###
my ($pass, %seen);
Usage() unless ((@ARGV) or (-s $file{in} && -T_));
open (TMP1, ">$file{tmp1}") or die "Error opening $file{tmp1} WO: $!";
close (TMP1) or die "Error closing $file{tmp1}: $1";
# $file{tmp1} must be present to prevent errors if first device unreac
+hable
print "\n",
" Prompting for password\n",
" (*not* echoed to screen nor written to disk)\n\n",
' Enter password:';
ReadMode('noecho');
eval {
local $SIG{ALRM} = sub { die "ALARUM" };
alarm("$parm{pwtimeout}");
chomp($pass = <STDIN>);
alarm(0);
};
if ($@ =~ /ALARUM/) {
print "\n\n",
" Sorry - you waited too long before entering a password.\n",
" Try again, if you want.\n\n";
ReadMode(0);
exit;
}
ReadMode(0);
unless (@ARGV) {
print " using $file{in} for input\n";
@ARGV = $file{in};
chomp (@ARGV = <>);
}
print "\n Starting CDP neighbor check, shouldn't take long...\n";
for my $cisco (@ARGV) {
if (my $cs=Net::Telnet::Cisco->new(
host => $cisco,
timeout => $parm{tntimeout},
errmode => $parm{errmode},
input_log => $file{tmp0},
)
)
{
$cs->login('',$pass); # vty login
print ' ', $cs->last_prompt, ' '; # print to cons
+ole
for(@commands) {
my @output = $cs->cmd($_);
}
print $cs->last_prompt, "\n"; # print to console
$cs->close; # exit session
open (TMP0, "<$file{tmp0}")
or die "Error opening $file{tmp0} RO: $!";
open (TMP1, ">>$file{tmp1}")
or die "Error opening $file{tmp1} for append: $!";
while (<TMP0>) { print TMP1 $_; }
close (TMP0) or die "Error closing $file{t
+mp0}: $!";
close (TMP1) or die "Error closing $file{t
+mp1}: $!";
} else { warn "Error connecting to $cisco\n"; } # if telnet connec
+t fail
}
unlink ($file{tmp0}) or die "Error unlinking $file
+{tmp0}: $!";
print " Finished CDP neighbor check.\n";
print " Extracting lines of interest...\n";
open (TMP1, "<$file{tmp1}") or die "Error opening $file{tmp1} RO: $!";
open (TMP2, ">$file{tmp2}") or die "Error opening $file{tmp2} RW: $!";
while (<TMP1>) {
foreach my $keep(@keeplines) {
if (/$keep/i) {
print TMP2 $_;
}
}
}
close (TMP1) or die "Error closing $file{tmp1}: $!";
unlink ($file{tmp1}) or die "Error unlinking $file{tmp1}: $!";
close (TMP2) or die "Error closing $file{tmp2}: $!";
print " Stripping unwanted characters...\n";
open (TMP2, "<$file{tmp2}") or die "Error opening $file{tmp2} RO: $!";
open (TMP3, ">$file{tmp3}") or die "Error opening $file{tmp3} RW: $!";
while (<TMP2>) {
foreach my $strip(@stripchars) {
s/$strip//gi;
}
tr/A-Z/a-z/;
print TMP3 $_;
}
close (TMP2) or die "Error closing $file{tmp2}: $!";
unlink ($file{tmp2}) or die "Error unlinking $file{tmp2}: $!";
close (TMP3) or die "Error closing $file{tmp3}: $!";
print " Stripping unwanted lines...\n";
open (TMP3, "<$file{tmp3}") or die "Error opening $file{tmp3} RO: $!"
+;
open (TMP4, ">$file{tmp4}") or die "Error opening $file{tmp4} RW: $!"
+;
while (<TMP3>) {
foreach my $strip(@striplines) {
s/^.*$strip.*//gi;
}
s/^\s+$//gi;
print TMP4 $_;
}
close (TMP3) or die "Error closing $file{tmp3}: $!";
unlink ($file{tmp3}) or die "Error unlinking$file{tmp3}: $!";
close (TMP4) or die "Error closing $file{tmp4}: $!";
print " Stripping duplicate lines...\n";
open (TMP4, "<$file{tmp4}") or die "Error opening $file{tmp4} RO: $
+!";
open (UNIQ, ">$file{uniqout}") or die "Error opening $file{uniqout} RW
+: $!";
foreach my $item (<TMP4>) {
++$seen{$item}; # parse $file{in} into a hash
} # and remove duplicate keys
print UNIQ sort keys %seen; # sort keys and save to $outfile
close TMP4 or die "Cannot close $file{tmp4}: $!";
unlink ($file{tmp4}) or die "Error unlinking$file{tmp4}: $!";
close UNIQ or die "Cannot close $file{uniqout}: $!";
print
" Finished parsing results.\n",
" Output at $file{uniqout}\n\n\a";
######################################################################
+#####
sub Usage {
print <<EOF;
D'oh! You didn't specify any target device(s) on command-line or at
+$file{in}.
If you want to try again, use the following syntax:
cdp-n.pl switch1 switch2 switch3
will query the 3 named (or numbered) switches for CDP neighbors
cdp-n.pl
with no arguments will read $file{in} for list of switches to quer
+y.
$file{in} would be a text file that looks like this:
switch1
switch2
switch3
ASCII text file
One IP address or DNS name per line
FQDN if targets in different DNS domain
No leading/trailing spaces
No blank lines
First run this program against a core switch, then recurse
down, using output as next input. Review and edit .in files between
recursions, and specify different outfile for each iteration also.
Remember to add each recursion back into final results, then run
uniq.pl to sort and extract unique entries.
Net::Telnet::Cisco $Net::Telnet::Cisco::VERSION
Net::Telnet $Net::Telnet::VERSION
Term::Readkey $Term::ReadKey::VERSION
Tie::IxHash $Tie::IxHash::VERSION
Perl $]
OS $^O
Program $0
EOF
exit; # exit instead of die, so no error to cons
+ole
}
######################################################################
+#####
sub Pause {
print "<enter> to continue, ctrl-C to quit.\n";
(<STDIN>);
}
######################################################################
+#####
=head1 Name
cdp-n.pl
=head1 Summary
Generate comprehensive list of Cisco LAN switches installed.
=head1 Usage
cdp-n.pl switch1 switch2 switch3
will query the 3 named (or numbered) switches for CDP neighbors
cdp-n.pl
with no arguments will read $file{in} for list of switches to quer
+y.
$file{in} would be a text file that looks like this:
switch1
switch2
switch3
ASCII text file
One IP address or DNS name per line
FQDN if targets in different DNS domain
No leading/trailing spaces
No blank lines
First run this program against a core switch, then recurse
down, using output as next input. Review and edit .in files between
recursions, and move outfile for each iteration also. Remember to
add each recursion back into final results, then run uniq.pl to
sort and extract unique entries from combined outfile.
=head1 Tested
with: Perl 5.00503 on Debian 2.2r2 "Espy"
against: Catalyst 2916/24, 5500,
=head1 Updated
2001-04-25 14:15
Add password timeout
Simplify foreach $scalar(@array) to 'for(@array)' and '$_'
Hashimafy file scalars
Unsubify non-redundant code
Mixed-case subroutines
Eliminate global vars with my(,) instead of use vars qw()
Reformat to 75 chars/line max
2001-03-29
Eliminate unnecessary quotes
Change double to single quotes for strings
@keep case-insensitive
2000-09-20
Initial working code
=head1 ToDos
Use File::Slurp to append log (avoid race condition)
Use File::Temp instead of multiple temp files
=head1 Author
Don [ybiC] Royer
=head1 Credits
Thanks to tilly, fastolfe, japhy, geektron and chromatic for
suggestions and critique,
and to tilly, mkmcconn and boo_radley for
cat filename | sed -e 's/$/.subdomain.dom/' > newfilename
to fully-qualify hostnames in resulting outfiles so they can be fed
back in as infiles.
=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.