[rancid] rancid script for adtran 900 series routers

Ted Cabeen ted at impulse.net
Mon Aug 14 20:49:26 UTC 2006


Enclosed is a script to allow adtran 900 series routers to be used
with rancid.  Adtran 900s are very cisco-like, so this is mainly a
heavily redacted version of the main cisco rancid script, with the
show version section changed to support the adtran results.  Would it
be possible to get this added to the distribution?

---Begin adrancid---
#! /usr/bin/perl5
##
## $Id: adrancid.in,v 1.172 2004/03/14 16:57:05 heas Exp $
##
## Copyright (C) 1997-2004 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.
##
## Adtran changes written by Ted Cabeen.
#
#  RANCID - Really Awesome New Cisco confIg Differ
#
# usage: rancid [-d] [-l] [-f filename | $host]
#
use Getopt::Std;
getopts('dfl');
$log = $opt_l;
$debug = $opt_d;
$file = $opt_f;
$host = $ARGV[0];
$clean_run = 0;
$found_end = 0;
$timeo = 90;			# clogin timeout in seconds

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, at 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 routing 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 routing 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 routing 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 routing (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 parses "show version"
sub ShowVersion {
    print STDERR "    In ShowVersion: $_" if ($debug);
    my($slaveslot);

    while (<INPUT>) {
	tr/\015//d;
	last if(/^$prompt/);
	next if(/^(\s*|\s*$cmd\s*)$/);
	return(-1) if (/command authorization failed/i);
	# the pager can not be disabled per-session on the PIX
	s/^<-+ More -+>\s*//;
	if (/^Slave in slot (\d+) is running/) {
	    $slave = " Slave:";
	    $slaveslot = ", slot $1";
	    next;
	}
	/^ADTRAN,.* version\s+(.*)$/ &&
	    ProcessHistory("COMMENTS","keysort","F1",
		"!Image: Software: $1\n") && next;
	/^Checksum: (.{8}), built on: (.*)$/ &&
	    ProcessHistory("COMMENTS","keysort","F2",
		"!Image: Checksum $1, Compiled: $2\n") && next;
	/^Boot ROM version (.*)$/ &&
	    ProcessHistory("COMMENTS","keysort","G1",
		"!ROM version: $1\n") && next;
	/^Checksum: (\d{4}), built on: (.*)$/ &&
	    ProcessHistory("COMMENTS","keysort","G2",
		"!ROM: Checksum $1, Compiled: $2\n") && next;
	if (/^Platform:\s+(.*), part number (.*), CLEI code is (.*)$/) {
	    ProcessHistory("COMMENTS","keysort","A1",
		"!Platform: $1\n");
	    ProcessHistory("COMMENTS","keysort","A2",
		"!Part Number: $2, CLEI $3\n") && next;
	}
	/^Serial number\s+(.*)$/ &&
	    ProcessHistory("COMMENTS","keysort","C1", "!Serial Number: $1\n") && next;
	/^  DSP version: (.*)$/ &&
	    ProcessHistory("COMMENTS","keysort","C2", "!DSP: version $1\n") && next;
	/^Flash: (\d+) bytes\s+DRAM: (\d+) bytes$/ &&
	    ProcessHistory("COMMENTS","keysort","B1",
		"!Memory: bootflash $1, RAM $2\n") && next;
	/^Current system image file is "(.*)"$/ &&
	    ProcessHistory("COMMENTS","keysort","F3", "!Image: $1\n") && next;
	/^Primary boot system image file is "(.*)"$/ &&
	    ProcessHistory("COMMENTS","keysort","F4", "!Image: Primary Boot: $1\n") && next;
	/^Backup boot system image file is "(.*)"$/ &&
	    ProcessHistory("COMMENTS","keysort","F5", "!Image: Secondary Boot: $1\n") && next;
	/^Primary system configuration file is "(.*)"$/ &&
	    ProcessHistory("COMMENTS","keysort","E1", "!Configuration: $1\n") && next;
    }
    return(0);
}

# This routine parses "show flash"
sub ShowFlash {
    # skip if this is 7000, 7200, 7500, or 12000; else we end up with
    # redundant data from dir /all slot0:
    print STDERR "    In ShowFlash: $_" if ($debug);

    while (<INPUT>) {
        tr/\015//d;
        last if (/^$prompt/);
        next if (/^(\s*|\s*$cmd\s*)$/);
        return(1) if ($type =~ /^(12[40]|7)/);
        return(1) if /^\s*\^\s*$/;
        ProcessHistory("FLASH","","","!Flash: $_");
    }
    ProcessHistory("","","","!\n");
    return;
}


# This routine processes a "write term"
sub WriteTerm {
    print STDERR "    In WriteTerm: $_" if ($debug);
    my($lineauto,$comment,$linecnt) = (0,0,0);

    while (<INPUT>) {
	tr/\015//d;
	last if(/^$prompt/);
	return(0) if ($found_end);		# Only do this routine once
	$linecnt++;
	$lineauto = 0 if (/^[^ ]/);
	# skip the crap
	if (/^(##+$|(Building|Current) configuration)/i) {
	    while (<INPUT>) {
		next if (/^Current configuration\s*:/i);
		next if (/^:/);
		next if (/^([%!].*|\s*)$/);
		last;
	    }
	    tr/\015//d;
	}

	# skip consecutive comment lines to avoid oscillating extra comment
	# line on some access servers.  grrr.
	if (/^!/) {
	    next if ($comment);
	    ProcessHistory("","","",$_);
	    $comment++;
	    next;
	}
	$comment = 0;

	# Dog gone Cool matches to process the rest of the config
	/^ntp clock-period /    && next; # kill ntp clock-period
	/^ length /		&& next; # kill length on serial lines
	/^ width /		&& next; # kill width on serial lines
	$lineauto = 1 if /^ modem auto/;
	/^ speed / && $lineauto	&& next; # kill speed on serial lines
	/^ clockrate /		&& next; # kill clockrate on serial interfaces
	if (/^(enable )?(password|passwd) / && $filter_pwds >= 1) {
	    ProcessHistory("ENABLE","","","!$1$2 <removed>\n");
	    next;
	}
	# filter out any RCS/CVS tags to avoid confusing local CVS storage
	s/\$(Revision|Id):/ $1:/;
	# order access-lists
	/^ip access-list\s+(\d\d?)\s+(\S+)\s+(\S+)/ &&
	    ProcessHistory("ACL $1 $2","ipsort","$3","$_") && next;
	# order extended access-lists
	/^ip access-list\s+(\d\d\d)\s+(\S+)\s+ip\s+host\s+(\S+)/ &&
	    ProcessHistory("EACL $1 $2","ipsort","$3","$_") && next;
	/^ip access-list\s+(\d\d\d)\s+(\S+)\s+ip\s+(\d\S+)/ &&
	    ProcessHistory("EACL $1 $2","ipsort","$3","$_") && next;
	/^ip access-list\s+(\d\d\d)\s+(\S+)\s+ip\s+any/ &&
	    ProcessHistory("EACL $1 $2","ipsort","0.0.0.0","$_") && next;
	/^ip prefix-list\s+(\S+)\s+seq\s+(\d+)\s+(permit|deny)\s+(\d\S+)(\/.*)$/ &&
	    ProcessHistory("PACL $1 $3","ipsort","$4","ip prefix-list $1 $3 $4$5\n")
	    && next;
	# order logging statements
	/^logging (\d+\.\d+\.\d+\.\d+)/ &&
	    ProcessHistory("LOGGING","ipsort","$1","$_") && next;
	# order/prune snmp-server host statements
	# we only prune lines of the form
	# snmp-server host a.b.c.d <community>
	if (/^snmp-server host (\d+\.\d+\.\d+\.\d+) /) {
	    if (defined($ENV{'NOCOMMSTR'})) {
		my($ip) = $1;
		my($line) = "snmp-server host $ip";
		my(@tokens) = split(' ', $');
		my($token);
		while ($token = shift(@tokens)) {
		    if ($token eq 'version') {
			$line .= " " . join(' ', ($token, shift(@tokens)));
		    } elsif ($token =~ /^(informs?|traps?|(no)?auth)$/) {
			$line .= " " . $token;
		    } else {
			$line = "!$line " . join(' ', ("<removed>", join(' ', at tokens)));
			last;
		    }
		}
		ProcessHistory("SNMPSERVERHOST","ipsort","$ip","$line\n");
	    } else {
		ProcessHistory("SNMPSERVERHOST","ipsort","$1","$_");
	    }
	    next;
	}
	if (/^(snmp-server community) (\S+)/) {
	    if (defined($ENV{'NOCOMMSTR'})) {
		ProcessHistory("SNMPSERVERCOMM","keysort","$_","!$1 <removed>$'") && next;
	    } else {
		ProcessHistory("SNMPSERVERCOMM","keysort","$_","$_") && next;
	    }
	}
	# order alias statements
	/^alias / && ProcessHistory("ALIAS","keysort","$_","$_") && next;
	# order ntp peers/servers
	if (/^ntp (server|peer) (\d+)\.(\d+)\.(\d+)\.(\d+)/) {
	    $sortkey = sprintf("$1 %03d%03d%03d%03d",$2,$3,$4,$5);
	    ProcessHistory("NTP","keysort",$sortkey,"$_");
	    next;
	}
	# order ip host statements
	/^ip host (\S+) / &&
	    ProcessHistory("IPHOST","keysort","$1","$_") && next;
	# order ip nat source static statements
	/^ip nat (\S+) source static (\S+)/ &&
	    ProcessHistory("IP NAT $1","ipsort","$2","$_") && next;
	# order atm map-list statements
	/^\s+ip\s+(\d+\.\d+\.\d+\.\d+)\s+atm-vc/ &&
	    ProcessHistory("ATM map-list","ipsort","$1","$_") && next;
	# order ip rcmd lines
	/^ip rcmd/ && ProcessHistory("RCMD","keysort","$_","$_") && next;

	# catch anything that wasnt matched above.
	ProcessHistory("","","","$_");
        # end of config.  the ": " game is for the PIX
        if (/^end$/) {
            $found_end = 1;
            return(1);
        }

    }
    return(0);
}

# dummy function
sub DoNothing {print STDOUT;}

# Main
%commands=(
	'show version'		=> "ShowVersion",
	'show flash'		=> "ShowFlash",
	'show running-config'	=> "WriteTerm",
	'write term'		=> "WriteTerm"
);
# keys() doesnt return things in the order entered and the order of the
# cmds is important (show version first and write term last). pita
@commands=(
	"show version",
	"show flash",
	"show running-config",
	"write term"
);
$cisco_cmds=join(";", at commands);
$cmds_regexp=join("|", at commands);

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 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: adtran\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);
}

---end adrancid---

-- 
Ted Cabeen                http://www.cabeen.org                 ted at cabeen.org
Check Website or Keyserver for PGP/GPG Key 0xBA0349D2          ted at impulse.net
"I have taken all knowledge to be my province." -F. Bacon  secabeen at cabeen.org
"Human kind cannot bear very much reality." -T.S.Eliot      secabeen at gmail.com




More information about the Rancid-discuss mailing list