Permalink
| #! @PERLV_PATH@ | |
| ## | |
| ## $Id: rancid.in 2168 2010-03-23 23:19:15Z heas $ | |
| ## | |
| ## @PACKAGE@ @VERSION@ | |
| ## Copyright (c) 1997-2009 by Terrapin Communications, Inc. | |
| ## All rights reserved. | |
| ## | |
| ## This code is derived from software contributed to and maintained by | |
| ## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan, | |
| ## Pete Whiting, Austin Schutz, and Andrew Fort. | |
| # | |
| ## Redistribution and use in source and binary forms, with or without | |
| ## modification, are permitted provided that the following conditions | |
| ## are met: | |
| ## 1. Redistributions of source code must retain the above copyright | |
| ## notice, this list of conditions and the following disclaimer. | |
| ## 2. Redistributions in binary form must reproduce the above copyright | |
| ## notice, this list of conditions and the following disclaimer in the | |
| ## documentation and/or other materials provided with the distribution. | |
| ## 3. All advertising materials mentioning features or use of this software | |
| ## must display the following acknowledgement: | |
| ## This product includes software developed by Terrapin Communications, | |
| ## Inc. and its contributors for RANCID. | |
| ## 4. Neither the name of Terrapin Communications, Inc. nor the names of its | |
| ## contributors may be used to endorse or promote products derived from | |
| ## this software without specific prior written permission. | |
| ## 5. It is requested that non-binding fixes and modifications be contributed | |
| ## back to Terrapin Communications, Inc. | |
| ## | |
| ## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS | |
| ## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | |
| ## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
| ## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTOR# | |
| ## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
| ## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
| ## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
| ## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
| ## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
| ## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
| ## POSSIBILITY OF SUCH DAMAGE. | |
| # | |
| # RANCID - Really Awesome New Cisco confIg Differ | |
| # | |
| # usage: rancid [-dV] [-l] [-f filename | hostname] | |
| # | |
| use Getopt::Std; | |
| getopts('dflV'); | |
| if ($opt_V) { | |
| print "@PACKAGE@ @VERSION@\n"; | |
| exit(0); | |
| } | |
| $log = $opt_l; | |
| $debug = $opt_d; | |
| $file = $opt_f; | |
| $host = $ARGV[0]; | |
| $proc = ""; | |
| $ios = "IOS"; | |
| $clean_run = 0; | |
| $found_end = 0; | |
| $found_version = 0; | |
| $found_env = 0; | |
| $found_diag = 0; | |
| $timeo = 90; # clogin timeout in seconds | |
| my(@commandtable, %commands, @commands);# command lists | |
| my($aclsort) = ("ipsort"); # ACL sorting mode | |
| my($config_register); # configuration register value | |
| my($filter_commstr); # SNMP community string filtering | |
| 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 "configShow" | |
| sub ConfigShow { | |
| print STDERR " In configShow: $_" if ($debug); | |
| my($lineauto,$comment,$linecnt) = (0,0,0); | |
| ProcessHistory("CONFIGSHOW","","","!CONFIGURATION\n"); | |
| ProcessHistory("CONFIGSHOW","","","!--------------\n!\n"); | |
| while (<INPUT>) { | |
| tr/\015//d; | |
| last if (/^$prompt/); | |
| return(1) if (!$linecnt && /^\s+\^\s*$/); | |
| return(1) if (/Line has invalid autocommand /); | |
| return(1) if (/(Invalid (input|command) detected|Type help or )/i); | |
| return(1) if (/\%Error: No such file or directory/); | |
| return(0) if ($found_end); # Only do this routine once | |
| return(-1) if (/command authorization failed/i); | |
| return(-1) if (/% ?configuration buffer full/i); | |
| # the pager can not be disabled per-session on the PIX | |
| if (/^(<-+ More -+>)/) { | |
| my($len) = length($1); | |
| s/^$1\s{$len}//; | |
| } | |
| /^(date =)/ && next; | |
| /^! no configuration change since last restart/i && next; | |
| # skip emtpy lines at the beginning | |
| if (!$linecnt && /^\s*$/) { | |
| next; | |
| } | |
| # catch anything that wasnt matched above. | |
| ProcessHistory("","","","$_"); | |
| # end of config. the ": " game is for the PIX | |
| #if (/^(: +)?end$/) { | |
| if (/End : 0/) { | |
| $found_end = 1; | |
| return(0); | |
| } | |
| if (/^LicenseDB:/) { | |
| # We are very close to the end, and I don't know any better marker | |
| # on the old FabOS version (v5.3.0) | |
| $found_end = 1; | |
| } | |
| } | |
| return(0); | |
| } | |
| # This routine parses "chassisShow". | |
| sub ChassisShow { | |
| print STDERR " In ChassisShow: $_" if ($debug); | |
| while (<INPUT>) { | |
| tr/\015//d; | |
| return if (/^\s*\^$/); | |
| last if (/^$prompt/); | |
| next if (/^(\s*|\s*$cmd\s*)$/); | |
| return(1) if (/Line has invalid autocommand /); | |
| return(1) if (/(Invalid (input|command) detected|Type help or )/i); | |
| return(-1) if (/command authorization failed/i); | |
| # the pager can not be disabled per-session on the PIX | |
| if (/^(<-+ More -+>)/) { | |
| my($len) = length($1); | |
| s/^$1\s{$len}//; | |
| } | |
| # skip emtpy lines at the beginning | |
| if (!$linecnt && /^\s*$/) { | |
| next; | |
| } | |
| if (/^Header Version:/) { | |
| ProcessHistory("INVENTORY","","Header Version:","!$_"); | |
| next; | |
| } | |
| if (/^Factory Part Num:/) { | |
| ProcessHistory("INVENTORY","","Factory Part #:","!$_"); | |
| next; | |
| } | |
| if (/^Factory Serial Num:/) { | |
| ProcessHistory("INVENTORY","","Factory Serial #:","!$_"); | |
| next; | |
| } | |
| if (/^ID:/) { | |
| ProcessHistory("INVENTORY","","ID:","!$_"); | |
| next; | |
| } | |
| if (/^Part Num:/) { | |
| ProcessHistory("INVENTORY","","Part Num:","!$_"); | |
| next; | |
| } | |
| if (/^Serial Num:/) { | |
| ProcessHistory("INVENTORY","","Serial Num:","!$_"); | |
| next; | |
| } | |
| /^(POWER SUPPLY Unit:|FAN Unit:|Time Awake:|Time Alive:|Time Awake:|CHASSIS|Update:|Manufacture:)/ && next; | |
| ProcessHistory("INVENTORY","","","!$_"); | |
| } | |
| ProcessHistory("INVENTORY","","","!\n"); | |
| return(0); | |
| } | |
| # This routine parses "firmwareShow". | |
| sub FirmwareShow { | |
| print STDERR " In FirmwareShow: $_" if ($debug); | |
| ProcessHistory("FIRMWARE","","","!FIRMWARE\n"); | |
| ProcessHistory("FIRMWARE","","","!---------\n!\n"); | |
| while (<INPUT>) { | |
| tr/\015//d; | |
| return if (/^\s*\^$/); | |
| last if (/^$prompt/); | |
| next if (/^(\s*|\s*$cmd\s*)$/); | |
| return(1) if (/Line has invalid autocommand /); | |
| return(1) if (/(Invalid (input|command) detected|Type help or )/i); | |
| return(-1) if (/command authorization failed/i); | |
| # the pager can not be disabled per-session on the PIX | |
| if (/^(<-+ More -+>)/) { | |
| my($len) = length($1); | |
| s/^$1\s{$len}//; | |
| } | |
| # skip emtpy lines at the beginning | |
| if (!$linecnt && /^\s*$/) { | |
| next; | |
| } | |
| if (/^FOS:/) { | |
| ProcessHistory("FIRMWARE","","Header Version:","!$_"); | |
| next; | |
| } | |
| /^(Appl|-)/ && next; | |
| ProcessHistory("FIRMWARE","","","!$_"); | |
| } | |
| ProcessHistory("FIRMWARE","","","!\n"); | |
| return(0); | |
| } | |
| # This routine parses "fosConfig". | |
| sub FosConfig { | |
| print STDERR " In FosConfig: $_" if ($debug); | |
| while (<INPUT>) { | |
| tr/\015//d; | |
| return if (/^\s*\^$/); | |
| last if (/^$prompt/); | |
| next if (/^(\s*|\s*$cmd\s*)$/); | |
| return(1) if (/Line has invalid autocommand /); | |
| return(1) if (/(Invalid (input|command) detected|Type help or )/i); | |
| return(-1) if (/command authorization failed/i); | |
| # the pager can not be disabled per-session on the PIX | |
| if (/^(<-+ More -+>)/) { | |
| my($len) = length($1); | |
| s/^$1\s{$len}//; | |
| } | |
| # skip emtpy lines at the beginning | |
| if (!$linecnt && /^\s*$/) { | |
| next; | |
| } | |
| ProcessHistory("FOSCONFIG","","","!$_"); | |
| } | |
| ProcessHistory("FOSCONFIG","","","!\n"); | |
| return(0); | |
| } | |
| # This routine parses "licenseShow". | |
| sub LicenseShow { | |
| print STDERR " In LicenseShow: $_" if ($debug); | |
| ProcessHistory("LICENSE","","","!LICENSE KEYS\n"); | |
| ProcessHistory("LICENSE","","","!-------------\n!\n"); | |
| while (<INPUT>) { | |
| tr/\015//d; | |
| return if (/^\s*\^$/); | |
| last if (/^$prompt/); | |
| next if (/^(\s*|\s*$cmd\s*)$/); | |
| return(1) if (/Line has invalid autocommand /); | |
| return(1) if (/(Invalid (input|command) detected|Type help or )/i); | |
| return(-1) if (/command authorization failed/i); | |
| # the pager can not be disabled per-session on the PIX | |
| if (/^(<-+ More -+>)/) { | |
| my($len) = length($1); | |
| s/^$1\s{$len}//; | |
| } | |
| # skip emtpy lines at the beginning | |
| if (!$linecnt && /^\s*$/) { | |
| next; | |
| } | |
| ProcessHistory("LICENSE","","","!$_"); | |
| } | |
| ProcessHistory("LICENSE","","","!\n"); | |
| return(0); | |
| } | |
| # This routine parses "ipAddrShow". | |
| sub IpAddrShow { | |
| print STDERR " In IpAddrShow: $_" if ($debug); | |
| ProcessHistory("IPSHOW","","","!IP ADDRESSING\n"); | |
| ProcessHistory("IPSHOW","","","!--------------\n!\n"); | |
| while (<INPUT>) { | |
| tr/\015//d; | |
| return if (/^\s*\^$/); | |
| last if (/^$prompt/); | |
| next if (/^(\s*|\s*$cmd\s*)$/); | |
| return(1) if (/Line has invalid autocommand /); | |
| return(1) if (/(Invalid (input|command) detected|Type help or )/i); | |
| return(-1) if (/command authorization failed/i); | |
| # the pager can not be disabled per-session on the PIX | |
| if (/^(<-+ More -+>)/) { | |
| my($len) = length($1); | |
| s/^$1\s{$len}//; | |
| } | |
| # skip emtpy lines at the beginning | |
| if (!$linecnt && /^\s*$/) { | |
| next; | |
| } | |
| /^(SWITCH)/ && next; | |
| ProcessHistory("IPADDRSHOW","","","!$_"); | |
| } | |
| ProcessHistory("IPADDRSHOW","","","!\n"); | |
| return(0); | |
| } | |
| # This routine parses "domainsShow". | |
| sub DomainsShow { | |
| print STDERR " In DomainsShow: $_" if ($debug); | |
| ProcessHistory("DOMAINS","","","!DOMAINS\n"); | |
| ProcessHistory("DOMAINS","","","!--------\n!\n"); | |
| while (<INPUT>) { | |
| tr/\015//d; | |
| return if (/^\s*\^$/); | |
| last if (/^$prompt/); | |
| next if (/^(\s*|\s*$cmd\s*)$/); | |
| return(1) if (/Line has invalid autocommand /); | |
| return(1) if (/(Invalid (input|command) detected|Type help or )/i); | |
| return(-1) if (/command authorization failed/i); | |
| # the pager can not be disabled per-session on the PIX | |
| if (/^(<-+ More -+>)/) { | |
| my($len) = length($1); | |
| s/^$1\s{$len}//; | |
| } | |
| # skip emtpy lines at the beginning | |
| if (!$linecnt && /^\s*$/) { | |
| next; | |
| } | |
| ProcessHistory("DOMAINS","","","!$_"); | |
| } | |
| ProcessHistory("DOMAINS","","","!\n"); | |
| return(0); | |
| } | |
| # dummy function | |
| sub DoNothing {print STDOUT;} | |
| # Main | |
| @commandtable = ( | |
| {'chassisShow' => 'ChassisShow'}, | |
| {'firmwareShow' => 'FirmwareShow'}, | |
| {'version' => 'FirmwareShow'}, | |
| #{'fosConfig --show' => 'Fosconfig'}, | |
| {'ipAddrShow' => 'IpAddrShow'}, | |
| {'licenseShow' => 'LicenseShow'}, | |
| {'domainsShow' => 'DomainsShow'}, | |
| {'configShow|grep .' => 'ConfigShow'} | |
| ); | |
| # 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("|", map quotemeta($_), @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 clogin -t $timeo -c\"$cisco_cmds\" $host\n" if ($debug); | |
| print STDOUT "executing clogin -t $timeo -c\"$cisco_cmds\" $host\n" if ($log); | |
| if (defined($ENV{NOPIPE})) { | |
| system "clogin -t $timeo -c \"$cisco_cmds\" $host | |
| </dev/null > $host.raw 2>&1" || die "clogin failed for $host: $!\n"; | |
| open(INPUT, "< $host.raw") || die "clogin failed for $host: $!\n"; | |
| } else { | |
| open(INPUT,"clogin -t $timeo -c \"$cisco_cmds\" $host | |
| </dev/null |") || die "clogin failed for $host: $!\n"; | |
| } | |
| } | |
| # determine ACL sorting mode | |
| if ($ENV{"ACLSORT"} =~ /no/i) { | |
| $aclsort = ""; | |
| } | |
| # determine community string filtering mode | |
| if (defined($ENV{"NOCOMMSTR"}) && | |
| ($ENV{"NOCOMMSTR"} =~ /yes/i || $ENV{"NOCOMMSTR"} =~ /^$/)) { | |
| $filter_commstr = 1; | |
| } else { | |
| $filter_commstr = 0; | |
| } | |
| # determine password filtering mode | |
| if ($ENV{"FILTER_PWDS"} =~ /no/i) { | |
| $filter_pwds = 0; | |
| } elsif ($ENV{"FILTER_PWDS"} =~ /all/i) { | |
| $filter_pwds = 2; | |
| } else { | |
| $filter_pwds = 1; | |
| } | |
| ProcessHistory("","","","!RANCID-CONTENT-TYPE: Brocade\n!\n"); | |
| ProcessHistory("COMMENTS","keysort","B0","!\n"); | |
| #ProcessHistory("COMMENTS","keysort","D0","!\n"); | |
| #ProcessHistory("COMMENTS","keysort","F0","!\n"); | |
| #ProcessHistory("COMMENTS","keysort","G0","!\n"); | |
| TOP: while(<INPUT>) { | |
| tr/\015//d; | |
| if (/[>]\s?exit$/) { | |
| $clean_run = 1; | |
| last; | |
| } | |
| if (/^Error:/) { | |
| print STDOUT ("$host clogin error: $_"); | |
| print STDERR ("$host clogin error: $_") if ($debug); | |
| $clean_run = 0; | |
| last; | |
| } | |
| while (/>\s*($cmds_regexp)\s*$/) { | |
| $cmd = $1; | |
| if (!defined($prompt)) { | |
| $prompt = ($_ =~ /^([^>]+>)/)[0]; | |
| $prompt =~ s/([][}{)(\\])/\\$1/g; | |
| print STDERR ("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; | |
| } | |
| $rval = &{$commands{$cmd}}; | |
| 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); | |
| } | |
| 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); | |
| } |