#! /usr/bin/perl ## ## $Id: rancid.in,v 1.218 2006/10/05 04:27:43 heas Exp $ ## ## rancid 2.3.2a7 ## Copyright (C) 1997-2006 by Terrapin Communications, Inc. ## All rights reserved. ## ## This software may be freely copied, modified and redistributed ## without fee for non-commerical purposes provided that this license ## remains intact and unmodified with any RANCID distribution. ## ## There is no warranty or other guarantee of fitness of this software. ## It is provided solely "as is". The author(s) disclaim(s) all ## responsibility and liability with respect to this software's usage ## or its effect upon hardware, computer systems, other software, or ## anything else. ## ## Except where noted otherwise, rancid was written by and is maintained by ## Henry Kilmer, John Heasley, Andrew Partan, Pete Whiting, and Austin Schutz. ## # # RANCID - Really Awesome New Cisco confIg Differ # # usage: rancid [-dV] [-l] [-f filename | hostname] # use Getopt::Std; getopts('dflV'); if ($opt_V) { print "rancid 2.3.2a7\n"; exit(0); } $log = $opt_l; $debug = $opt_d; $file = $opt_f; $host = $ARGV[0]; $clean_run = 0; $found_end = 0; $found_version = 0; $found_env = 0; $found_diag = 0; $timeo = 10; # zyxP791rlogin timeout in seconds my(@commandtable, %commands, @commands);# command lists my(%filter_pwds); # password filtering mode # This routine is used to print out the router configuration sub ProcessHistory { my($new_hist_tag,$new_command,$command_string,@string) = (@_); if ((($new_hist_tag ne $hist_tag) || ($new_command ne $command)) && defined %history) { print eval "$command \%history"; undef %history; } if (($new_hist_tag) && ($new_command) && ($command_string)) { if ($history{$command_string}) { $history{$command_string} = "$history{$command_string}@string"; } else { $history{$command_string} = "@string"; } } elsif (($new_hist_tag) && ($new_command)) { $history{++$#history} = "@string"; } else { print "@string"; } $hist_tag = $new_hist_tag; $command = $new_command; 1; } sub numerically { $a <=> $b; } # This is a sort routine that will sort numerically on the # keys of a hash as if it were a normal array. sub keynsort { local(%lines) = @_; local($i) = 0; local(@sorted_lines); foreach $key (sort numerically keys(%lines)) { $sorted_lines[$i] = $lines{$key}; $i++; } @sorted_lines; } # This is a sort routine that will sort on the # keys of a hash as if it were a normal array. sub keysort { local(%lines) = @_; local($i) = 0; local(@sorted_lines); foreach $key (sort keys(%lines)) { $sorted_lines[$i] = $lines{$key}; $i++; } @sorted_lines; } # This is a sort routine that will sort on the # values of a hash as if it were a normal array. sub valsort{ local(%lines) = @_; local($i) = 0; local(@sorted_lines); foreach $key (sort values %lines) { $sorted_lines[$i] = $key; $i++; } @sorted_lines; } # This is a numerical sort routine (ascending). sub numsort { local(%lines) = @_; local($i) = 0; local(@sorted_lines); foreach $num (sort {$a <=> $b} keys %lines) { $sorted_lines[$i] = $lines{$num}; $i++; } @sorted_lines; } # This is a sort routine that will sort on the # ip address when the ip address is anywhere in # the strings. sub ipsort { local(%lines) = @_; local($i) = 0; local(@sorted_lines); foreach $addr (sort sortbyipaddr keys %lines) { $sorted_lines[$i] = $lines{$addr}; $i++; } @sorted_lines; } # These two routines will sort based upon IP addresses sub ipaddrval { my(@a) = ($_[0] =~ m#^(\d+)\.(\d+)\.(\d+)\.(\d+)$#); $a[3] + 256 * ($a[2] + 256 * ($a[1] +256 * $a[0])); } # sub sortbyipaddr { &ipaddrval($a) <=> &ipaddrval($b); } # This routine processes a "switch enet show" sub SwitchEnetShow { print STDERR " In SwitchEnetShow: $_" if ($debug); # now just copy it verbatim to the history file while () { # tr/\015//d; if (/^$prompt/) { $found_env=1; last}; next if (/^(\s*|\s*$cmd\s*)$/); if (/^\/?exit/) { $found_end = 1; ProcessHistory("","","","$_\n"); return(1); } $found_end = 1; ProcessHistory("","","","$_"); } return(0); } # This routine processes a "config show sys" sub ShowConfigSys { print STDERR " In ShowConfigSys: $_" if ($debug); # print STDERR ("PROMPT MATCH $prompt near $_\n"); # eat the header line #$junk = ; # now just copy it verbatim to the history file while () { # print STDERR ("pr = $prompt fo = $found_end cl = $clean_run near $_\n"); # tr/\015//d; if (/^$prompt/) { $found_env=1; last}; next if (/^(\s*|\s*$cmd\s*)$/); # if (/(community)(\s+)(.*)/ && # defined($ENV{'NOCOMMSTR'})) { # ProcessHistory("","","","\/\*\t$1$2\"\"\n") && next; # } # /^(\s+.{2,3}pw )\S+/ && # ProcessHistory("","","","\/\*$1\n") && next; next if (/System up time/); next if (/^current time is.*/); next if (/^current date is.*/); if (/^\/?exit/) { $found_end = 1; ProcessHistory("","","","$_\n"); return(1); } $found_end = 1; ProcessHistory("","","","$_"); } return(0); } # This routine processes a "config show sw" sub ShowConfigSw { print STDERR " In ShowConfigSw: $_" if ($debug); # print STDERR ("PROMPT MATCH $prompt near $_\n"); # eat the header line #$junk = ; # now just copy it verbatim to the history file while () { # print STDERR ("pr = $prompt fo = $found_end cl = $clean_run near $_\n"); # tr/\015//d; if (/^$prompt/) { $found_env=1; last}; next if (/^(\s*|\s*$cmd\s*)$/); s/\0337//g; # if (/(community)(\s+)(.*)/ && # defined($ENV{'NOCOMMSTR'})) { # ProcessHistory("","","","\/\*\t$1$2\"\"\n") && next; # } # /^(\s+.{2,3}pw )\S+/ && # ProcessHistory("","","","\/\*$1\n") && next; # next if (/^\/\* Configuration dump taken/i); # next if (/^\/\* Version.*Base MAC.*/i); if (/^\/?exit/) { $found_end = 1; ProcessHistory("","","","!ETHER CONFIG: $_\n"); return(1); } $found_end = 1; ProcessHistory("","","","$_"); } return(0); } # This routine processes a "ip route status" sub ShowRouteStatus { print STDERR " In ShowRouteStatus: $_" if ($debug); # print STDERR ("PROMPT MATCH $prompt near $_\n"); # now just copy it verbatim to the history file while () { # print STDERR ("pr = $prompt fo = $found_end cl = $clean_run near $_\n"); # tr/\015//d; s/\0337//g; if (/^$prompt/) { $found_env=1; last}; if (/(^default)(.*00ab 0 )/) { ProcessHistory("","","","!IP ROUTE: $1$2 \n") && next; } if (/^\/?xit/) { $found_end = 1; ProcessHistory("","","","!IP ROUTE: $_\n"); return(1); } $found_end = 1; ProcessHistory("","","","!IP ROUTE: $_"); } return(0); } # This routine processes a "config show ip" sub ShowConfigIP { print STDERR " In ShowConfigIP: $_" if ($debug); # print STDERR ("PROMPT MATCH $prompt near $_\n"); # eat the header line #$junk = ; # now just copy it verbatim to the history file while () { # print STDERR ("pr = $prompt fo = $found_end cl = $clean_run near:$_\n"); # tr/\015//d; if (/>/) { $clean_run = 1; return(1); } if (/^$prompt/) { $found_env=1; last}; next if (/^(\s*|\s*$cmd\s*)$/); next if (/In|Out/); s/\0337//g; # if (/(community)(\s+)(.*)/ && # defined($ENV{'NOCOMMSTR'})) { # ProcessHistory("","","","\/\*\t$1$2\"\"\n") && next; # } # /^(\s+.{2,3}pw )\S+/ && # ProcessHistory("","","","\/\*$1\n") && next; # next if (/^\/\* Configuration dump taken/i); # next if (/^\/\* Version.*Base MAC.*/i); if (/>/) { $found_end = 1; $clean_run = 1; ProcessHistory("","","","!IP CONFIG: $_\n"); return(1); } $found_end = 1; ProcessHistory("","","","!IP CONFIG: $_"); } return(0); } # This routine processes a "ether config" sub ShowEtherConfig { print STDERR " In ShowEtherConfig: $_" if ($debug); # print STDERR ("PROMPT MATCH $prompt near $_\n"); # now just copy it verbatim to the history file while () { # print STDERR ("pr = $prompt fo = $found_end cl = $clean_run near $_\n"); tr/\015//d; s/\0337//g; if (/^$prompt/) { $found_env=1; last}; if (/>/) { $found_end = 1; ProcessHistory("","","","$_\n"); return(1); } $found_end = 1; ProcessHistory("","","","!ETHER CONFIG: $_"); } return(0); } # This routine processes a "sys version" sub ShowVersion { print STDERR " In ShowVersion: $_" if ($debug); # print STDERR ("PROMPT MATCH $prompt near $_\n"); # now just copy it verbatim to the history file while () { # print STDERR ("pr = $prompt fo = $found_end cl = $clean_run near $_\n"); tr/\015//d; s/\0337//g; if (/^$prompt/) { $found_env=1; last}; next if (/system up time/); if (/>/) { $found_end = 1; ProcessHistory("","","","$_\n"); return(1); } $found_end = 1; ProcessHistory("","","","!SYS VERSION: $_"); } return(0); } # This routine processes a "sys feature" sub ShowFeature { print STDERR " In ShowFeature: $_" if ($debug); # print STDERR ("PROMPT MATCH $prompt near $_\n"); # now just copy it verbatim to the history file while () { # print STDERR ("pr = $prompt fo = $found_end cl = $clean_run near $_\n"); tr/\015//d; s/\0337//g; if (/^$prompt/) { $found_env=1; last}; if (/>/) { $found_end = 1; ProcessHistory("","","","$_\n"); return(1); } $found_end = 1; ProcessHistory("","","","!SYS FEATURE: $_"); } return(0); } # is routine processes a "sys view autoexec.net" sub ShowAutoexec { print STDERR " In ShowAutoexec: $_" if ($debug); # print STDERR ("PROMPT MATCH $prompt near $_\n"); # now just copy it verbatim to the history file while () { # print STDERR ("pr = $prompt fo = $found_end cl = $clean_run near $_\n"); tr/\015//d; s/\0337//g; if (/^$prompt/) { $found_env=1; last}; if (/>/) { $found_end = 1; ProcessHistory("","","","!AUTOEXEC.NET: $_"); return(1); } $found_end = 1; ProcessHistory("","","","!AUTOEXEC.NET: $_"); } return(0); } # is routine processes a "sys hostname" sub ShowHostname { print STDERR " In ShowHostname: $_" if ($debug); # print STDERR ("PROMPT MATCH $prompt near $_\n"); # now just copy it verbatim to the history file while () { # print STDERR ("pr = $prompt fo = $found_end cl = $clean_run near $_\n"); s/\0337//g; if (/^$prompt/) { $found_env=1; last}; if (/>/) { $found_end = 1; ProcessHistory("","","","$_\n"); return(1); } $found_end = 1; ProcessHistory("","","","!HOSTNAME:$_"); print STDERR ("$_"); } return(0); } # dummy function sub DoNothing {print STDOUT;} # Main @commandtable = ( # {'sys hostname' => 'ShowHostname'}, {'sys view autoexec.net' => 'ShowAutoexec'}, {'ip route status' => 'ShowRouteStatus'}, {'sys feature' => 'ShowFeature'}, {'ip ifconfig' => 'ShowConfigIP'}, {'sys version' => 'ShowVersion'}, {'ether config' => 'ShowEtherConfig'}, # {'ip route status' => 'ShowRouteStatus'}, # {'ip ifconfig' => 'ShowConfigIP'}, # {'' => 'DoNothing'}, {'sys hostname' => 'ShowHostname'} ); # Use an array to preserve the order of the commands and a hash for mapping # commands to the subroutine and track commands that have been completed. @commands = map(keys(%$_), @commandtable); %commands = map(%$_, @commandtable); $cisco_cmds=join(";",@commands); $cmds_regexp=join("|",@commands); if (length($host) == 0) { if ($file) { print(STDERR "Too few arguments: file name required\n"); exit(1); } else { print(STDERR "Too few arguments: host name required\n"); exit(1); } } open(OUTPUT,">$host.new") || die "Can't open $host.new for writing: $!\n"; select(OUTPUT); # make OUTPUT unbuffered if debugging if ($debug) { $| = 1; } if ($file) { print STDERR "opening file $host\n" if ($debug); print STDOUT "opening file $host\n" if ($log); open(INPUT,"<$host") || die "open failed for $host: $!\n"; } else { print STDERR "executing zyxP791rlogin -t $timeo -c\"$cisco_cmds\" $host\n" if ($debug); print STDOUT "executing zyxP791rlogin -t $timeo -c\"$cisco_cmds\" $host\n" if ($log); if (defined($ENV{NOPIPE})) { system "zyxP791rlogin -t $timeo -c \"$cisco_cmds\" $host $host.raw 2>&1" || die "zyxP791rlogin failed for $host: $!\n"; open(INPUT, "< $host.raw") || die "zyxP791rlogin failed for $host: $!\n"; } else { open(INPUT,"zyxP791rlogin -t $timeo -c \"$cisco_cmds\" $host ) { # print STDERR ("clean_run = $clean_run prompt = $prompt near $_"); tr/\055//d; if (/\076exit/) { $found_end = 1; # $clean_run=1; # print STDERR ("MATCH clean_run clean_run near $_\n"); last; } # print STDERR ("clean_run = $clean_run near $_\n"); if (/^Error:/) { print STDOUT ("$host zyxP791rlogin error: $_"); print STDERR ("$host zyxP791rlogin error: $_") if ($debug); $clean_run=0; last; } while (/\s*($cmds_regexp)\s*$/) { # print STDERR ("PROMPT MATCH $prompt near while\n"); $cmd = $1; if (!defined($prompt)) { $prompt = ($_ =~ /^([^>]+>)/)[0]; # print STDERR ("try PROMPT MATCH: $prompt\n") if ($debug); $prompt =~ s/([][}{)(\\])/\\$1/g; # print STDERR ("try PROMPT MATCH: $prompt\n") if ($debug); } print STDERR ("HIT COMMAND:$_") if ($debug); if (! defined($commands{$cmd})) { print STDERR "$host: found unexpected command - \"$cmd\"\n"; $clean_run = 0; last TOP; } # print STDERR ("before rval = $rval $prompt near while\n"); $rval = &{$commands{$cmd}}; # print STDERR ("after rval = $rval $prompt near while\n"); delete($commands{$cmd}); # print STDERR ("delete($commands{$cmd}) "); if ($rval == -1) { $clean_run = 0; last TOP; } } } print STDOUT "Done $logincmd: $_\n" if ($log); # Flush History ProcessHistory("","","",""); # Cleanup close(INPUT); close(OUTPUT); if (defined($ENV{NOPIPE})) { unlink("$host.raw") if (! $debug); } # check for completeness if (scalar(%commands) || !$clean_run || !$found_end) { if (scalar(%commands)) { printf(STDOUT "$host: missed cmd(s): %s\n", join(',', keys(%commands))); printf(STDERR "$host: missed cmd(s): %s\n", join(',', keys(%commands))) if ($debug); } # $found_end = 0; # $clean_run = 1; if (!$clean_run || !$found_end) { print STDOUT "$host: End of run not found\n"; print STDERR "$host: End of run not found\n" if ($debug); system("/usr/bin/tail -1 $host.new"); } unlink "$host.new" if (! $debug); }