1#!/usr/local/bin/perl 2## 3## @PACKAGE@ @VERSION@ 4## Copyright (c) @COPYYEARS@ by Henry Kilmer and John Heasley 5## All rights reserved. 6## 7## This code is derived from software contributed to and maintained by 8## Henry Kilmer, John Heasley, Andrew Partan, 9## Pete Whiting, Austin Schutz, and Andrew Fort. 10## 11## Redistribution and use in source and binary forms, with or without 12## modification, are permitted provided that the following conditions 13## are met: 14## 1. Redistributions of source code must retain the above copyright 15## notice, this list of conditions and the following disclaimer. 16## 2. Redistributions in binary form must reproduce the above copyright 17## notice, this list of conditions and the following disclaimer in the 18## documentation and/or other materials provided with the distribution. 19## 3. Neither the name of RANCID nor the names of its 20## contributors may be used to endorse or promote products derived from 21## this software without specific prior written permission. 22## 23## THIS SOFTWARE IS PROVIDED BY Henry Kilmer, John Heasley AND CONTRIBUTORS 24## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 26## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS 27## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33## POSSIBILITY OF SUCH DAMAGE. 34## 35## It is the request of the authors, but not a condition of license, that 36## parties packaging or redistributing RANCID NOT distribute altered versions 37## of the etc/rancid.types.base file nor alter how this file is processed nor 38## when in relation to etc/rancid.types.conf. The goal of this is to help 39## suppress our support costs. If it becomes a problem, this could become a 40## condition of license. 41# 42# The expect login scripts were based on Erik Sherk's gwtn, by permission. 43# 44# The original looking glass software was written by Ed Kern, provided by 45# permission and modified beyond recognition. 46# 47# hacked version of Hank's rancid - this one tries to deal with Netscalers. 48# Hacks from Anshuman Kanwar. 49# 50# RANCID - Really Awesome New Cisco confIg Differ 51# 52# usage: nsrancid [-dltCV] [-f filename | hostname] 53# 54use Getopt::Std; 55getopts('dflt:CV'); 56if ($opt_V) { 57 print "@PACKAGE@ @VERSION@\n"; 58 exit(0); 59} 60$log = $opt_l; 61$debug = $opt_d; 62$file = $opt_f; 63$host = $ARGV[0]; 64$clean_run = 0; 65$found_end = 0; 66$timeo = 90; # nslogin timeout in seconds 67 68my(@commandtable, %commands, @commands);# command lists 69my($aclsort) = ("ipsort"); # ACL sorting mode 70my($filter_commstr); # SNMP community string filtering 71my($filter_osc); # oscillating data filtering mode 72my($filter_pwds); # password filtering mode 73 74$prompt = ">"; 75 76# This routine is used to print out the router configuration 77sub ProcessHistory { 78 my($new_hist_tag,$new_command,$command_string,@string) = (@_); 79 if ((($new_hist_tag ne $hist_tag) || ($new_command ne $command)) 80 && scalar(%history)) { 81 print eval "$command \%history"; 82 undef %history; 83 } 84 if (($new_hist_tag) && ($new_command) && ($command_string)) { 85 if ($history{$command_string}) { 86 $history{$command_string} = "$history{$command_string}@string"; 87 } else { 88 $history{$command_string} = "@string"; 89 } 90 } elsif (($new_hist_tag) && ($new_command)) { 91 $history{++$#history} = "@string"; 92 } else { 93 print "@string"; 94 } 95 $hist_tag = $new_hist_tag; 96 $command = $new_command; 97 1; 98} 99 100sub numerically { $a <=> $b; } 101 102# This is a sort routine that will sort numerically on the 103# keys of a hash as if it were a normal array. 104sub keynsort { 105 local(%lines) = @_; 106 local($i) = 0; 107 local(@sorted_lines); 108 foreach $key (sort numerically keys(%lines)) { 109 $sorted_lines[$i] = $lines{$key}; 110 $i++; 111 } 112 @sorted_lines; 113} 114 115# This is a sort routine that will sort on the 116# keys of a hash as if it were a normal array. 117sub keysort { 118 local(%lines) = @_; 119 local($i) = 0; 120 local(@sorted_lines); 121 foreach $key (sort keys(%lines)) { 122 $sorted_lines[$i] = $lines{$key}; 123 $i++; 124 } 125 @sorted_lines; 126} 127 128# This is a sort routine that will sort on the 129# values of a hash as if it were a normal array. 130sub valsort{ 131 local(%lines) = @_; 132 local($i) = 0; 133 local(@sorted_lines); 134 foreach $key (sort values %lines) { 135 $sorted_lines[$i] = $key; 136 $i++; 137 } 138 @sorted_lines; 139} 140 141# This is a numerical sort routine (ascending). 142sub numsort { 143 local(%lines) = @_; 144 local($i) = 0; 145 local(@sorted_lines); 146 foreach $num (sort {$a <=> $b} keys %lines) { 147 $sorted_lines[$i] = $lines{$num}; 148 $i++; 149 } 150 @sorted_lines; 151} 152 153# This is a sort routine that will sort on the 154# ip address when the ip address is anywhere in 155# the strings. 156sub ipsort { 157 local(%lines) = @_; 158 local($i) = 0; 159 local(@sorted_lines); 160 foreach $addr (sort sortbyipaddr keys %lines) { 161 $sorted_lines[$i] = $lines{$addr}; 162 $i++; 163 } 164 @sorted_lines; 165} 166 167# These two routines will sort based upon IP addresses 168sub ipaddrval { 169 my(@a) = ($_[0] =~ m#^(\d+)\.(\d+)\.(\d+)\.(\d+)$#); 170 $a[3] + 256 * ($a[2] + 256 * ($a[1] +256 * $a[0])); 171} 172sub sortbyipaddr { 173 &ipaddrval($a) <=> &ipaddrval($b); 174} 175 176# This routine parses "show ns ns.conf" 177sub ShowConfig { 178 print STDERR " In ShowConfig: $_" if ($debug); 179 180 while (<INPUT>) { 181 tr/\015//d; 182 last if (/ Done$/); 183 last if (/^$prompt/); 184 next if (/^(\s*|\s*$cmd\s*)$/); 185 next if (/^Reading configuration information/); 186 next if (/^Can\'t find object or class named \"\-all\"\s*$/); 187 next if (/lock-address .*$/); 188 next if (/^\# *uptime +\d+\s*$/); 189 next if (/^\# last modified/i); 190 if (/community label /) { 191 if ($filter_commstr) { 192 $_ =~ s/community label .*$/community label <removed>/; 193 } 194 } 195 return(1) if /(invalid command name)/; 196 ProcessHistory("","","","$_"); 197 } 198 199 if (/ Done$/) { 200 $found_end = 1; 201 $clean_run = 1; 202 return(1); 203 } 204 return(0); 205} 206 207# This routine parses single command's that return no required info 208sub RunCommand { 209 print STDERR " In RunCommand: $_" if ($debug); 210 211 while (<INPUT>) { 212 tr/\015//d; 213 last if (/^$prompt/); 214 next if (/^(\s*|\s*$cmd\s*)$/); 215 } 216 return(0) 217} 218 219# Main 220@commandtable = ( 221 {'show ns ns.conf' => 'ShowConfig'}, 222); 223# Use an array to preserve the order of the commands and a hash for mapping 224# commands to the subroutine and track commands that have been completed. 225@commands = map(keys(%$_), @commandtable); 226%commands = map(%$_, @commandtable); 227$commandcnt = scalar(keys %commands); 228 229$commandstr=join(";",@commands); 230$cmds_regexp = join("|", map quotemeta($_), @commands); 231 232if (length($host) == 0) { 233 if ($file) { 234 print(STDERR "Too few arguments: file name required\n"); 235 exit(1); 236 } else { 237 print(STDERR "Too few arguments: host name required\n"); 238 exit(1); 239 } 240} 241if ($opt_C) { 242 print "nslogin -t $timeo -c\'$commandstr\' $host\n"; 243 exit(0); 244} 245open(OUTPUT,">$host.new") || die "Can't open $host.new for writing: $!\n"; 246select(OUTPUT); 247# make OUTPUT unbuffered if debugging 248if ($debug) { $| = 1; } 249 250if ($file) { 251 print(STDERR "opening file $host\n") if ($debug || $log); 252 open(INPUT,"<$host") || die "open failed for $host: $!\n"; 253} else { 254 print(STDERR "executing nslogin -t $timeo -c\"$commandstr\" $host\n") if ($debug || $log); 255 system "nslogin -t $timeo -c \"$commandstr\" $host </dev/null > $host.raw 2>&1" || die "nslogin failed for $host: $!\n"; 256 open(INPUT, "< $host.raw") || die "nslogin failed for $host: $!\n"; 257} 258 259# determine ACL sorting mode 260if ($ENV{"ACLSORT"} =~ /no/i) { 261 $aclsort = ""; 262} 263# determine community string filtering mode 264if (defined($ENV{"NOCOMMSTR"}) && 265 ($ENV{"NOCOMMSTR"} =~ /yes/i || $ENV{"NOCOMMSTR"} =~ /^$/)) { 266 $filter_commstr = 1; 267} else { 268 $filter_commstr = 0; 269} 270# determine oscillating data filtering mode 271if (defined($ENV{"FILTER_OSC"}) && $ENV{"FILTER_OSC"} =~ /no/i) { 272 $filter_osc = 0; 273} else { 274 $filter_osc = 1; 275} 276# determine password filtering mode 277if ($ENV{"FILTER_PWDS"} =~ /no/i) { 278 $filter_pwds = 0; 279} elsif ($ENV{"FILTER_PWDS"} =~ /all/i) { 280 $filter_pwds = 2; 281} else { 282 $filter_pwds = 1; 283} 284 285#print STDOUT "$prompt \n"; 286ProcessHistory("","","","!RANCID-CONTENT-TYPE: netscaler\n!\n"); 287TOP: while(<INPUT>) { 288 tr/\015//d; 289 290 if (/^Error:/) { 291 print STDOUT ("$host nslogin error: $_"); 292 print STDERR ("$host nslogin error: $_") if ($debug); 293 $clean_run = 0; 294 last; 295 } 296 while (/$prompt\s*($cmds_regexp)\s*$/) { 297 $cmd = $1; 298 print STDERR ("HIT COMMAND:$_") if ($debug); 299 if (! defined($commands{$cmd})) { 300 print STDERR "$host: found unexpected command - \"$cmd\"\n"; 301 $clean_run = 0; 302 last TOP; 303 } 304 $rval = &{$commands{$cmd}}(*INPUT, *OUTPUT, $cmd); 305 delete($commands{$cmd}); 306 if ($rval == -1) { 307 $clean_run = 0; 308 last TOP; 309 } 310 } 311} 312print STDOUT "Done $logincmd: $_\n" if ($log); 313# Flush History 314ProcessHistory("","","",""); 315# Cleanup 316close(INPUT); 317close(OUTPUT); 318 319unlink("$host.raw") if (! $debug); 320 321# check for completeness 322if (scalar(%commands) || !$clean_run || !$found_end) { 323 if (scalar(keys %commands) eq $commandcnt) { 324 printf(STDERR "$host: missed cmd(s): all commands\n"); 325 } elsif (scalar(%commands)) { 326 my($count, $i) = 0; 327 for ($i = 0; $i < $#commands; $i++) { 328 if ($commands{$commands[$i]}) { 329 if (!$count) { 330 printf(STDERR "$host: missed cmd(s): %s", $commands[$i]); 331 } else { 332 printf(STDERR ", %s", $commands[$i]); 333 } 334 $count++; 335 } 336 } 337 if ($count) { 338 printf(STDERR "\n"); 339 } 340 } 341 if (!$clean_run || !$found_end) { 342 print(STDERR "$host: End of run not found\n"); 343 if ($debug) { 344 print(STDERR "$host: clean_run is false\n") if (!$clean_run); 345 print(STDERR "$host: found_end is false\n") if (!$found_end); 346 } 347 system("/usr/bin/tail -1 $host.new"); 348 } 349 unlink "$host.new" if (! $debug); 350} 351