Backup/Restore from RANCID
-
mnitech - Member
- Posts: 21
- Joined: Wed Oct 21, 2015 1:21 pm
- Location: Morgan Hill, CA
- Has thanked: 0 time
- Been thanked: 0 time
Re: Backup/Restore from RANCID
What you have sounds good. I just want to track changes. The binary method seems to be good for a backup but won't track changes.
-
SyncKev - Member
- Posts: 6
- Joined: Wed Dec 09, 2015 3:24 pm
- Location: Bremen, GA
- Has thanked: 0 time
- Been thanked: 1 time
Re: Backup/Restore from RANCID
mnitech wrote:What you have sounds good. I just want to track changes. The binary method seems to be good for a backup but won't track changes.
Here is the script I created and tested. Since it does not contain all of the information needed for a full restore, I have not worked on it further. Some additional code cleanup could be done. I named the script 'ntxrancid'
- Code: Select all
#! /usr/bin/perl
##
## $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.
##
##
##
## Netonix - Will perform 'show config' on netonix switches; however, this is not enough information
## for a full config restore.
#
# 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,@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 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;
}
if (/^(enable secret) / && $filter_pwds >= 2) {
ProcessHistory("ENABLE","","","!$1 <removed>\n");
next;
}
if (/^username (\S+)(\s.*)? secret /) {
if ($filter_pwds >= 2) {
ProcessHistory("USER","keysort","$1","!username $1$2 secret <removed>\n");
} else {
ProcessHistory("USER","keysort","$1","$_");
}
next;
}
if (/^username (\S+)(\s.*)? password ((\d) \S+|\S+)/) {
if ($filter_pwds >= 2) {
ProcessHistory("USER","keysort","$1","!username $1$2 password <removed>\n");
} elsif ($filter_pwds >= 1 && $4 ne "5"){
ProcessHistory("USER","keysort","$1","!username $1$2 password <removed>\n");
} else {
ProcessHistory("USER","keysort","$1","$_");
}
next;
}
if (/^( set session-key (in|out)bound ah \d+ )/ && $filter_pwds >= 1) {
ProcessHistory("","","","!$1<removed>\n");
next;
}
if (/^( set session-key (in|out)bound esp \d+ (authenticator|cypher) )/ && $filter_pwds >= 1) {
ProcessHistory("","","","!$1<removed>\n");
next;
}
if (/^(\s*)password / && $filter_pwds >= 1) {
ProcessHistory("LINE-PASS","","","!$1password <removed>\n");
next;
}
if (/^(\s*)secret / && $filter_pwds >= 2) {
ProcessHistory("LINE-PASS","","","!$1secret <removed>\n");
next;
}
if (/^\s*neighbor (\S*) password / && $filter_pwds >= 1) {
ProcessHistory("","","","! neighbor $1 password <removed>\n");
next;
}
if (/^(ppp .* password) 7 .*/ && $filter_pwds >= 1) {
ProcessHistory("","","","!$1 <removed>\n"); next;
}
if (/^(ip ftp password) / && $filter_pwds >= 1) {
ProcessHistory("","","","!$1 <removed>\n"); next;
}
if (/^( ip ospf authentication-key) / && $filter_pwds >= 1) {
ProcessHistory("","","","!$1 <removed>\n"); next;
}
# isis passwords appear to be completely plain-text
if (/^\s+isis password (\S+)( .*)?/ && $filter_pwds >= 1) {
ProcessHistory("","","","!isis password <removed>$2\n"); next;
}
if (/^\s+(domain-password|area-password) (\S+)( .*)?/
&& $filter_pwds >= 1) {
ProcessHistory("","","","!$1 <removed>$3\n"); next;
}
# this is reversable, despite 'md5' in the cmd
if (/^( ip ospf message-digest-key \d+ md5) / && $filter_pwds >= 1) {
ProcessHistory("","","","!$1 <removed>\n"); next;
}
if (/^((crypto )?isakmp key) \S+ / && $filter_pwds >= 1) {
ProcessHistory("","","","!$1 <removed> $'"); next;
}
# filter HSRP passwords
if (/^(\s+standby \d authentication) / && $filter_pwds >= 1) {
ProcessHistory("","","","!$1 <removed>\n"); next;
}
# this appears in "measurement/sla" images
if (/^(\s+key-string \d?)/ && $filter_pwds >= 1) {
ProcessHistory("","","","!$1 <removed>\n"); next;
}
if (/^( l2tp tunnel \S+ password)/ && $filter_pwds >= 1) {
ProcessHistory("","","","!$1 <removed>\n"); next;
}
# i am told these are plain-text on the PIX
if (/^(vpdn username \S+ password)/ && $filter_pwds >= 1) {
ProcessHistory("","","","!$1 <removed>\n"); next;
}
if (/^( cable shared-secret ) / && $filter_pwds >= 1) {
ProcessHistory("","","","!$1 <removed>\n");
next;
}
/fair-queue individual-limit/ && next;
# sort ip explicit-paths.
if (/^ip explicit-path name (\S+)/) {
my($key) = $1;
my($expath) = $_;
while (<INPUT>) {
tr/\015//d;
last if (/^$prompt/);
last if (/^$prompt/ || ! /^(ip explicit-path name |[ !])/);
if (/^ip explicit-path name (\S+)/) {
ProcessHistory("EXPATH","keysort","$key","$expath");
$key = $1;
$expath = $_;
} else {
$expath .= $_;
}
}
ProcessHistory("EXPATH","keysort","$key","$expath");
}
# sort route-maps
if (/^route-map (\S+)/) {
my($key) = $1;
my($routemap) = $_;
while (<INPUT>) {
tr/\015//d;
last if (/^$prompt/ || ! /^(route-map |[ !])/);
if (/^route-map (\S+)/) {
ProcessHistory("ROUTEMAP","keysort","$key","$routemap");
$key = $1;
$routemap = $_;
} else {
$routemap .= $_;
}
}
ProcessHistory("ROUTEMAP","keysort","$key","$routemap");
}
# 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);
}
}
$found_end = 1; # no config end marker, just hard set this.
#Force clean run acceptance
#$clean_run = 1;
ProcessHistory("","","","end\n");
return(0);
}
# dummy function
sub DoNothing {print STDOUT;}
# Main
%commands=(
'show config' => "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 config",
);
$cisco_cmds=join(";",@commands);
$cmds_regexp=join("|",@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: Netonix\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;
}
}
# Force clean run
$clean_run=1;
}
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);
}
print STDERR "clean $clean_run end $found_end\n";
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);
}
-
mnitech - Member
- Posts: 21
- Joined: Wed Oct 21, 2015 1:21 pm
- Location: Morgan Hill, CA
- Has thanked: 0 time
- Been thanked: 0 time
Re: Backup/Restore from RANCID
I was just looking for something that would provide some config management rather than a backup. Your script might be a good starting place
-
SyncKev - Member
- Posts: 6
- Joined: Wed Dec 09, 2015 3:24 pm
- Location: Bremen, GA
- Has thanked: 0 time
- Been thanked: 1 time
Re: Backup/Restore from RANCID
mnitech wrote:I was just looking for something that would provide some config management rather than a backup. Your script might be a good starting place
It does work for that purpose; I hope it is helpful.
-
jbaird - Member
- Posts: 43
- Joined: Fri Oct 09, 2015 3:47 pm
- Location: Danville, KY
- Has thanked: 0 time
- Been thanked: 2 times
Re: Backup/Restore from RANCID
Thanks for the script. I hope at some point we'll be able to do full restores from this.
- belsamber
- Member
- Posts: 1
- Joined: Thu Nov 05, 2015 10:32 pm
- Has thanked: 0 time
- Been thanked: 0 time
Re: Backup/Restore from RANCID
Sorry to resurrect an old thread but this seemed like an appropriate place to post.
Based on Eric's comment earlier in the thread I put together the attached RANCID scripts.
As written they log into the switch, run the backup command, SCP the file to your host, untar, then pass the config.json file up the line so it gets committed to CVS.
It keeps a single copy of the tar file for each switch for restore purposes (by default this is in the /tmp/ directory)
There's very little error checking and I make no claims about code quality - it's just something I hacked together this morning to solve an immediate problem. Hopefully it'll be helpful to someone else as well
Based on Eric's comment earlier in the thread I put together the attached RANCID scripts.
As written they log into the switch, run the backup command, SCP the file to your host, untar, then pass the config.json file up the line so it gets committed to CVS.
It keeps a single copy of the tar file for each switch for restore purposes (by default this is in the /tmp/ directory)
There's very little error checking and I make no claims about code quality - it's just something I hacked together this morning to solve an immediate problem. Hopefully it'll be helpful to someone else as well
- Attachments
-
- wisprancid.txt
- (1.34 KiB) Downloaded 432 times
-
- wisplogin.txt
- (6.82 KiB) Downloaded 444 times
Who is online
Users browsing this forum: No registered users and 11 guests