Cisco ASA – Multiple Context and RANCID Backup

rancid

I'm handling some Cisco ASA with multiple context, and I'm looking for a solution where I can have RANCID handle the backup of the multiple context, but by adding the changeto system and show tech-support command to the @commandtable in rancid, RANCID bugs out and tell me that the command was not found and other times it's telling me the command was missed, by using RANCIDs catchall last in the file.

I haven't seen anybody succeed with this, and therefor I write to you guys on this forum, have any of you succeeded with this solution to the problem.


Entries from google:

http://permalink.gmane.org/gmane.network.rancid/6220

http://www.shrubbery.net/pipermail/rancid-discuss/2012-July/006435.html

Some of these solutions seems like a good idea, but is not doable in the environment I'm working in.

The solution I'm looking for is using the RANCID scripts available and adding functionality to the @commandtable array.

Thanks

Best Answer

This took a bit of hacking of the RANCID code to deal with - nothing terribly complicated, I basically run one remote show context up front and use the output from this to build the command list for the rest of the script.

Here are a few code snippets to show what I did (sorry about the funky formatting, I'm new to this site and don't know all the tricks yet):

\##### Main

\# Get the list of contexts from the FW.  fwsmlogin handles multi-context ASAs.
open(INPUT,"fwsmlogin -t $timeo -c \"changeto system;show context\" $host </dev/null |") || die "fwsmlogin failed for $host: $!\n";

$ContextFlag = 0;
$ModeFlag = 0;

while (<INPUT>) {
    tr/\015//d;

    print STDERR ("Processing line: $_") if ($DEBUG);

    if ( /^Context Name/ ) {
        $ContextFlag = 1;
        if ( / Mode / ) {$ModeFlag = 1}
    }

    if ( /Connection.*closed/ ) {
        print STDERR ("\nDEBUG: Connection closed\n\n") if ($DEBUG);
        $clean_run=1;
        last;
    }

    if ( /^Error:/ ) {
        print STDOUT ("$host fwsmlogin error: $_");
        print STDERR ("$host fwsmlogin error: $_") if ($DEBUG);
        $clean_run=0;
        last;
    }

    if ( /^Total active Security Contexts: (\d+)/ ) {
        $ContextCount = $1;
        last;
    }

    if ( $ContextFlag ) {
        if ( $ModeFlag && /^.(\S+)\s+\S+\s+\S+\s+\S+\s+\S+$/ ||
            !$ModeFlag && /^.(\S+)\s+\S+\s+\S+\s+\S+$/ ) {
            print STDERR ("$host matched context $1\n") if ($DEBUG);
            push @ContextList, $1;
            next;
        }
    }
}
close INPUT;

print STDERR ("\nDEBUG: Preprocessing found $ContextCount contexts: @ContextList\n\n") if ($DEBUG);

if (scalar @ContextList ne $ContextCount) {
    print STDERR ("\nERROR: Context count mismatch, count = $ContextCount, List = @ContextList\n\n");
    exit 1;
}

%commands=(
        'changeto context'              => "ChangeContext",
        'changeto system'                       => "IgnoreOutput",
        'dir all-filesystems'           => "DirAll",
        'show context'                  => "ShowFWContext",
        'show failover'                 => "ShowFailover",
        'show mode'                     => "ShowMode",
        'show resource acl-partition'   => "ShowResourceACL",
        'show resource allocation'      => "ShowResourceAlloc",
        'show resource partition'       => "ShowResourcePart",
        'show resource rule'            => "ShowResourceRule",
        'show resource usage'           => "ShowResourceUsage",
        'show run'                      => "ShowRun",
        'show version'                  => "ShowVersion"
);

.
.
.

\## Add the commands to pull the config for each context
foreach my $context (@ContextList) {
    $commands{"changeto context $context"} = "ChangeContext";
    push @commands, "changeto context $context", "show failover", "show run";
}

$cisco_cmds=join(";",@commands);
$cmds_regexp=join("|",@commands);

I think this is all you really need to handle multiple contexts - if you're reasonably familiar with Perl it shouldn't be too hard to see how this works.

As an aside, I've also moved most of the subroutines into separate libraries, so when working on custom versions I can include the needed ones with appropriate "use lib" and "use" statements - for example customized scripts for CatOS devices will have this:

use lib '/home/rancid/lib';
use RANCIDsubs;
use CatOSsubs;

while for IOS devices we replace "use CatOSsubs" with "use IOSsubs"; for our ACE30 and ACE4710 devices with "use ACEsubs"; for Force10 switches with "use F10subs", etc. This makes it a lot easier to write custom RANCID scripts for whatever purpose you need.

As for fwsmlogin, over the years I've created many customized versions of clogin for various other device types; fwsmlogin is one of these. It's not very different from clogin - I mostly added comments and deleted unneeded code (for example "proc label", which is unnecessary since we don't use Xterm). The only important change is right at the end of this script, as the command needed on the firewalls to disable paging is different from that needed in IOS/CatOS.

To create your own fwsmlogin, edit a copy of clogin, find the "elseif { $do_script }" line near the end of of the file, and change the section that follows to look like this:

    :
    :
 } elseif { $do_script } {
    send "term pager 0\r"
    sleep 0.2
    expect -re $prompt  {}
    source $sfile
    close
 } else {
    :
    :

Your formatting may look different, but it shouldn't be too hard to figure this out.