Gheek.net

November 13, 2008

Cisco IPS support for rancid

Filed under: Cisco, perl, rancid — Tags: , , , — lancevermilion @ 1:43 pm

In order to get rancid to collect the config from an IPS module you will need to make sure you have the correct login creds in the rancid users “.cloginrc”, add the type of ips to “rancid-fe” and you also need to create the “ipsrancid” script.

Sample addition for your “.cloginrc”. Make sure this is above anything else so no other wildcards in the “.cloginrc” get caught.

add autoenable ips-primary 1
add user ips-primary rancid
add password ips-primary <>
add method ips-primary ssh

Changes required for “rancid-fe”.

'ips' => 'ipsrancid',

Create the “ipsrancid” script as “<rancid_home>/bin/ipsrancid”. Make sure you “chmod 755 <rancid_home>/bin/ipsrancid” and “chown <rancid_user>:<rancid_user_group> <rancid_home>/bin/ipsrancid”.

#!/usr/bin/perl
##
## 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.
##
#
# hacked version of Hank's rancid - this one tries to deal with Hitachi's.
#
# Modified again by Lance Vermilion (11/13/08)
# Modified from htrancid by Jeremy M. Guthrie
# Created on 5/4/2007
#
# This is meant to try handle Cisco's IPS V5.X line and on
#
# RANCID - Really Awesome New Cisco confIg Differ
#
# usage: ipsrancid [-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(@commandtable, %commands, @commands);# command lists
my(%filter_pwds); # password filtering mode

# This routine is used to print out the router configuration
sub ProcessHistory {

($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 parses "show config"
sub ShowConfig {
print STDERR " In ShowConfig: $_" if ($debug);

$firstexit=0;

while () {
tr/15//d;
tr/20//d;

#strip out the stupid spinning running-config progress thingy
s/Generating current config: \.*[\|\/\-\\]//gi;
$skipprocess=0;

#sometimes an 'exit' appears at the top of the config, we don't want them
if ( (/^exit/) && ( ! $firstexit ) ) {
$firstexit=1;
$skipprocess=1;
}

#remove spaces left over from lame spinning progress thingy
if ( /^\s+! ------------------------------/ ) {
s/^\s+!/!/g
}

if (/^(read-only-community) / && $filter_pwds >= 1) {
ProcessHistory("","","","!$1 \n"); next;
}
if (/^(read-write-community) / && $filter_pwds >= 1) {
ProcessHistory("","","","!$1 \n"); next;
}
if (/^(trap-community-name) / && $filter_pwds >= 1) {
ProcessHistory("","","","!$1 \n"); next;
}
if (/^(ntp-keys \d+ md5-key) / && $filter_pwds >= 1) {
ProcessHistory("","","","!$1 \n"); next;
}
if (/^(password) / && $filter_pwds >= 1) {
ProcessHistory("","","","!$1 \n"); next;
}

last if (/^$prompt/);
next if (/^(\s*|\s*$cmd\s*)$/);
if ( ! /^$prompt/) {
if ( ! $skipprocess ) {
print STDOUT " ShowConfig Data: $_" if ($debug);
ProcessHistory("","","","$_");
}
}
}
$clean_run=1;
print STDERR " Exiting ShowConfig: $_" if ($debug);
return(0);
}

# This routine parses single command's that return no required info
sub ShowVersion {
print STDERR " In ShowVersion: $_" if ($debug);
ProcessHistory("","","","!\n!IPS Show Version Start\n");

while () {
tr/15//d;

$skipprocess=0;

if ( /^Sensor up-time/ ) { $skipprocess=1; }
if ( ( /using.*bytes of available/i ) ) { $skipprocess=1; }

last if (/^$prompt/);
next if (/^(\s*|\s*$cmd\s*)$/);
if ( ! /^$prompt/) {
if ( ! $skipprocess ) {
print STDOUT " ShowVersion Data: $_" if ($debug);
ProcessHistory("","","","! $_");
}
}
}
ProcessHistory("","","","!\n!IPS Show Version End\n");
print STDERR " Exiting ShowVersion: $_" if ($debug);
return(0)
}

# This routine parses single command's that return no required info
sub ShowUsersAll {
print STDERR " In ShowUsersAll: $_" if ($debug);
ProcessHistory("","","","!\n!IPS User Database Start\n");

while () {
tr/15//d;

$skipprocess=0;

s/^ CLI ID //g;
s/^ //g;
s/^\* +[0-9]+ +//g;

last if (/^$prompt/);
next if (/^(\s*|\s*$cmd\s*)$/);
if ( ! /^$prompt/) {
if ( ! $skipprocess ) {
print STDOUT " ShowUsersAll Data: $_" if ($debug);
ProcessHistory("","","","!$_");
}
}
}
ProcessHistory("","","","!\n!IPS User Database End\n!\n!\n");
print STDERR " Exiting ShowUsersAll: $_" if ($debug);
return(0)
}

# dummy function
sub DoNothing {print STDOUT;}

# Main
@commandtable = (
{'show version' => 'ShowVersion'},
{'show users all' => 'ShowUsersAll'},
{'show configuration' => 'ShowConfig'}
);
# 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("|",@commands);

open(OUTPUT,">$host.new") || die "Can't open $host.new for writing: $!\n";
select(OUTPUT);
# make OUTPUT unbuffered if debugging
if ($debug) { $| = 1; }

# The IPS doesn't like the TERM of network so we must change it
if ( $ENV{TERM} eq 'network' ) {
$ENV{TERM} = 'vt100';
}
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 $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";
}
}
# Change the TERM back to network
if ( $ENV{TERM} eq 'vt100' ) {
$ENV{TERM} = 'network';
}

# 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: ipsrancid\n!\n");
TOP: while() {
tr/15//d;

#strip out the stupid spinning running-config progress thingy
s/Generating current config: \.*[\|\/\-\\]//gi;

if (/^.*logout$/) {
$clean_run=1;
last;
}
if (/^Error:/) {
print STDOUT ("$host clogin error: $_");
print STDERR ("$host clogin error: $_") if ($debug);
$clean_run=0;
last;
}
while (/($cmds_regexp)/) {
$cmd = $1;
if (!defined($prompt)) {
$prompt = ($_ =~ /^([^#]+#)/)[0];
$prompt =~ s/([][}{)(\\])/\\$1/g;
print STDERR ("PROMPT MATCH: $prompt\n") if ($debug);
}
print STDERR ("IPS COMMAND:$_") if ($debug);
if (! defined($commands{$cmd})) {
print STDERR "$host: found unexpected command - \"$cmd\"\n";
$clean_run = 0;
last TOP;
}
$rval = };
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 ) {
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 ) {
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);
}

Advertisement

June 26, 2008

rancid-rev_sh.txt

Filed under: rancid, shell scripts — lancevermilion @ 10:58 am

shell script to diff rancid configs in cvs

#!/bin/bash

# rancid-rev  -  for use with rancid implementation.
#  -- mikeb 10/05/00
#  -- lancev 01/12/06

CVSROOT='/usr/local/rancid/var/CVS';
export CVSROOT;

cd /usr/local/rancid/var/networking

if [ -z "$1" ]; then
  while [ -z "$router" ]; do
    echo -n "Router name: "
    read router
  done
else
  router=`echo $1 | sed 's/.abc.com//g'`
  router="$1"
fi

#if [ -z "$2" ]; then
#  while [ -z "$date" ]; do
#    echo -n "Date (YYYY-MM-DD): "
#    read date
#  done
#else
#  date="$2"
#fi

if [ -z "$2" ]; then
#cvs history -D $date -e -a -w $router | ( while read a
cvs history -e -a -w $router | ( while read a
do
  if [ ! -z "`echo $a | cut -f6 -d' ' | grep \"[0-9]\.[0-9][0-9]\"`" ]; then
    echo $a | sed 's/^.\ //'
  fi
done ) | egrep -v "configs$" | sort
cvsdisplayed="yes"

echo -n "Enter 1st revision number: "
while [ -z "$rev" ]; do
  read rev
done
else
  rev=$2
fi

if [ -z "$3" ]; then
  echo -n "Enter a 2nd revision number (y/N): "
  read rev2ans
  if [ "$rev2ans" = "y" ]; then
    if [ -z "$cvsdisplayed" ]; then
      cvs history -e -a -w $router | ( while read a
      do
        if [ ! -z "`echo $a | cut -f6 -d' ' | grep \"[0-9]\.[0-9][0-9]\"`" ]; then
          echo $a | sed 's/^.\ //'
        fi
      done ) | egrep -v "configs$" | sort
    fi
    echo -n "Enter 2nd revision number: "
    while [ -z "$rev2" ]; do
      read rev2
    done
  fi
else
  rev2=$3
fi

cd configs/
if [ -z "$rev2" ]; then
  cvs diff -Naur $rev $router | more
else
  cvs diff -r $rev -r $rev2 $router | more
fi

#
# Currently it will only build a config from
# the 1st rev against the current config in cvs.
#
echo -n "Build config based on diff? (y/N): "
read build
if [ "$build" = "y" ]; then
  cp $router $HOME
  cvs diff -r $rev $router | ( cd $HOME; patch -R )
  echo "Config based on revision $rev in $HOME/$router"
fi

January 28, 2008

Rancid 2.3.2a7 patched to include F5 BigIP support

Filed under: bigip, Cisco, expect, f5, perl, rancid — Tags: , , , , , , — lancevermilion @ 12:10 am

Here is a RANCID alpha version patched with a complete install that include F5 support.

To get the F5rancid script to work you will need to do something similar the following to your .cloginrc

add userprompt bigip* sshOnlyNoPrompt
add autoenable bigip* 1
add user bigip*

Rancid-2.3.2a7.1.tar.gz

wrancid patch to rancid-fe

Filed under: perl, rancid — Tags: , , — lancevermilion @ 12:02 am

This is a patch to enable the wrapper script to work with RANCID.

wlclogin_exp

Filed under: Cisco, expect, rancid — Tags: , , , , — lancevermilion @ 12:00 am

This is the standard clogin for RANCID but with a few minor changes that will account for the Cisco Wireless controllers difference in prompting.

#! /usr/bin/expect --
##
## $Id: clogin.in,v 1.89 2005/08/14 20:18:19 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.
##
#
# The login expect scripts were based on Erik Sherk's gwtn, by permission.
#
# clogin - Cisco login
#
# Most options are intuitive for logging into a Cisco router.
# The default is to enable (thus -noenable).  Some folks have
# setup tacacs to have a user login at priv-lvl = 15 (enabled)
# so the -autoenable flag was added for this case (don't go through
# the process of enabling and the prompt will be the "#" prompt.
# The default username password is the same as the vty password.
#

# Usage line
set usage "Usage: $argv0 \[-autoenable\] \[-noenable\] \[-c command\] \
\[-Evar=x\] \[-e enable-password\] \[-f cloginrc-file\] \[-p user-password\] \
\[-s script-file\] \[-t timeout\] \[-u username\] \
\[-v vty-password\] \[-w enable-username\] \[-x command-file\] \
\[-y ssh_cypher_type\] router \[router...\]\n"

# env(CLOGIN) may contain:
#       x == do not set xterm banner or name

# Password file
set password_file $env(HOME)/.cloginrc
# Default is to login to the router
set do_command 0
set do_script 0
# The default is to automatically enable
set avenable 1
# The default is that you login non-enabled (tacacs can have you login already
# enabled)
set avautoenable 0
# The default is to look in the password file to find the passwords.  This
# tracks if we receive them on the command line.
set do_passwd 1
set do_enapasswd 1
# attempt at platform switching.
set platform ""

# Find the user in the ENV, or use the unix userid.
if {[ info exists env(CISCO_USER) ]} {
    set default_user $env(CISCO_USER)
} elseif {[ info exists env(USER) ]} {
    set default_user $env(USER)
} elseif {[ info exists env(LOGNAME) ]} {
    set default_user $env(LOGNAME)
} else {
    # This uses "id" which I think is portable.  At least it has existed
    # (without options) on all machines/OSes I've been on recently -
    # unlike whoami or id -nu.
    if [ catch {exec id} reason ] {
        send_error "\nError: could not exec id: $reason\n"
        exit 1
    }
    regexp {\(([^)]*)} "$reason" junk default_user
}

# Sometimes routers take awhile to answer (the default is 10 sec)
set timeout 45

# Process the command line
for {set i 0} {$i < $argc} {incr i} {
    set arg [lindex $argv $i]

    switch  -glob -- $arg {
        # Username
        -u* -
        -U* {
            if {! [  regexp .\[uU\](.+) $arg ignore user]} {
                incr i
                set username [ lindex $argv $i ]
            }
        # VTY Password
        } -p* -
        -P* {
            if {! [  regexp .\[pP\](.+) $arg ignore userpasswd]} {
                incr i
                set userpasswd [ lindex $argv $i ]
            }
            set do_passwd 0
        # VTY Password
        } -v* -
        -v* {
            if {! [  regexp .\[vV\](.+) $arg ignore passwd]} {
                incr i
                set passwd [ lindex $argv $i ]
            }
            set do_passwd 0
        # Enable Username
        } -w* -
        -W* {
            if {! [  regexp .\[wW\](.+) $arg ignore enauser]} {
                incr i
                set enausername [ lindex $argv $i ]
            }
        # Environment variable to pass to -s scripts
        } -E*
        {
            if {[ regexp .\[E\](.+)=(.+) $arg ignore varname varvalue]} {
                set E$varname $varvalue
            } else {
                send_user "\nError: invalid format for -E in $arg\n"
                exit 1
            }
        # Enable Password
        } -e*
        {
            if {! [  regexp .\[e\](.+) $arg ignore enapasswd]} {
                incr i
                set enapasswd [ lindex $argv $i ]
            }
            set do_enapasswd 0
        # Command to run.
        } -c* -
        -C* {
            if {! [  regexp .\[cC\](.+) $arg ignore command]} {
                incr i
                set command [ lindex $argv $i ]
            }
            set do_command 1
        # Expect script to run.
        } -s* -
        -S* {
            if {! [  regexp .\[sS\](.+) $arg ignore sfile]} {
                incr i
                set sfile [ lindex $argv $i ]
            }
            if { ! [ file readable $sfile ] } {
                send_user "\nError: Can't read $sfile\n"
                exit 1
            }
            set do_script 1
        # 'ssh -c' cypher type
        } -y* -
        -Y* {
            if {! [  regexp .\[eE\](.+) $arg ignore cypher]} {
                incr i
                set cypher [ lindex $argv $i ]
            }
        # alternate cloginrc file
        } -f* -
        -F* {
            if {! [ regexp .\[fF\](.+) $arg ignore password_file]} {
                incr i
                set password_file [ lindex $argv $i ]
            }
        # Timeout
        } -t* -
        -T* {
            if {! [ regexp .\[tT\](.+) $arg ignore timeout]} {
                incr i
                set timeout [ lindex $argv $i ]
            }
        # Command file
        } -x* -
        -X {
            if {! [  regexp .\[xX\](.+) $arg ignore cmd_file]} {
                incr i
                set cmd_file [ lindex $argv $i ]
            }
            if [ catch {set cmd_fd [open $cmd_file r]} reason ] {
                send_user "\nError: $reason\n"
                exit 1
            }
            set cmd_text [read $cmd_fd]
            close $cmd_fd
            set command [join [split $cmd_text \n] \;]
            set do_command 1
        # Do we enable?
        } -noenable {
            set avenable 0
        # Does tacacs automatically enable us?
        } -autoenable {
            set avautoenable 1
            set avenable 0
        } -* {
            send_user "\nError: Unknown argument! $arg\n"
            send_user $usage
            exit 1
        } default {
            break
        }
    }
}
# Process routers...no routers listed is an error.
if { $i == $argc } {
    send_user "\nError: $usage"
}

# Only be quiet if we are running a script (it can log its output
# on its own)
if { $do_script } {
    log_user 0
} else {
    log_user 1
}

#
# Done configuration/variable setting.  Now run with it...
#

# Sets Xterm title if interactive...if its an xterm and the user cares
proc label { host } {
    global env
    # if CLOGIN has an 'x' in it, don't set the xterm name/banner
    if [info exists env(CLOGIN)] {
        if {[string first "x" $env(CLOGIN)] != -1} { return }
    }
    # take host from ENV(TERM)
    if [info exists env(TERM)] {
        if [regexp \^(xterm|vs) $env(TERM) ignore ] {
            send_user "33]1;[lindex [split $host "."] 0]\a"
            send_user "33]2;$host\a"
        }
    }
}

# This is a helper function to make the password file easier to
# maintain.  Using this the password file has the form:
# add password sl*      pete cow
# add password at*      steve
# add password *        hanky-pie
proc add {var args} { global int_$var ; lappend int_$var $args}
proc include {args} {
    global env
    regsub -all "(^{|}$)" $args {} args
    if { [ regexp "^/" $args ignore ] == 0 } {
        set args $env(HOME)/$args
    }
    source_password_file $args
}

proc find {var router} {
    upvar int_$var list
    if { [info exists list] } {
        foreach line $list {
            if { [string match [lindex $line 0] $router ] } {
                return [lrange $line 1 end]
            }
        }
    }
    return {}
}

# Loads the password file.  Note that as this file is tcl, and that
# it is sourced, the user better know what to put in there, as it
# could install more than just password info...  I will assume however,
# that a "bad guy" could just as easy put such code in the clogin
# script, so I will leave .cloginrc as just an extention of that script
proc source_password_file { password_file } {
    global env
    if { ! [file exists $password_file] } {
        send_user "\nError: password file ($password_file) does not exist\n"
        exit 1
    }
    file stat $password_file fileinfo
    if { [expr ($fileinfo(mode) & 007)] != 0000 } {
        send_user "\nError: $password_file must not be world readable/writable\n"
        exit 1
    }
    if [ catch {source $password_file} reason ] {
        send_user "\nError: $reason\n"
        exit 1
    }
}

# Log into the router.
# returns: 0 on success, 1 on failure, -1 if rsh was used successfully
proc login { router user userpswd passwd enapasswd cmethod cyphertype } {
    global spawn_id in_proc do_command do_script platform
    global prompt u_prompt p_prompt e_prompt sshcmd
    set in_proc 1
    set uprompt_seen 0

    # try each of the connection methods in $cmethod until one is successful
    set progs [llength $cmethod]
    foreach prog [lrange $cmethod 0 end] {
        incr progs -1
        if [string match "telnet*" $prog] {
            regexp {telnet(:([^[:space:]]+))*} $prog command suffix port
            if {"$port" == ""} {
                set retval [ catch {spawn telnet $router} reason ]
            } else {
                set retval [ catch {spawn telnet $router $port} reason ]
            }
            if { $retval } {
                send_user "\nError: telnet failed: $reason\n"
                return 1
            }
        } elseif [string match "ssh*" $prog] {
            regexp {ssh(:([^[:space:]]+))*} $prog command suffix port
            if {"$port" == ""} {
                set retval [ catch {spawn $sshcmd -c $cyphertype -x -l $user $router} reason ]

            } else {
                set retval [ catch {spawn $sshcmd -c $cyphertype -x -l $user -p $port $router} reason ]
            }
            if { $retval } {
                send_user "\nError: $sshcmd failed: $reason\n"
                return 1
            }
        } elseif ![string compare $prog "rsh"] {
            global command

            if { ! $do_command } {
                if { [llength $cmethod] == 1 } {
                    send_user "\nError: rsh is an invalid method for -x and "
                    send_user "interactive logins\n"
                }
                if { $progs == 0 } {
                    return 1
                }
                continue;
            }

            set commands [split $command \;]
            set num_commands [llength $commands]
            set rshfail 0
            for {set i 0} {$i  (enable) " }
        -re "(denied|Sorry|Incorrect)"  {
                          # % Access denied - from local auth and poss. others
                          send_user "\nError: Check your Enable passwd\n";
                          return 1
                        }
        "% Error in authentication" {
                          send_user "\nError: Check your Enable passwd\n"
                          return 1
                        }
        "% Bad passwords" {
                          send_user "\nError: Check your Enable passwd\n"
                          return 1
                        }
    }
    # We set the prompt variable (above) so script files don't need
    # to know what it is.
    set in_proc 0
    return 0
}

# Run commands given on the command line.
proc run_commands { prompt command } {
    global in_proc platform
    set in_proc 1

    # If the prompt is (enable), then we are on a switch and the
    # command is "set length 0"; otherwise its "term length 0".
    # skip if its an extreme (since the pager can not be disabled on a
    # per-vty basis).
    if { [ string compare "extreme" "$platform" ] } {
        if [ regexp -- ".*> .*enable" "$prompt" ] {
            send "set length 0\r"
            # This is ugly, but reduces code duplication, allowing the
            # subsequent expects to handle everything as normal.
            set command "set logging session disable;$command"
        } else {
            if [ regexp -- ".*-nc-.*" "$prompt" ] {
                send "\r"
            } else {
              # Modified for the Cisco 4xxx controller
              if [ regexp -- "\\(Cisco Controller\\) >" "$prompt" ] {
                send "\r"
              } else {
                send "term length 0\r"
              }
            }
        }
        # escape any parens in the prompt, such as "(enable)"
        regsub -all {[)(]} $prompt {\\&} reprompt
        # match cisco config mode prompts too, such as router(config-if)#,
        # but catalyst does not change in this fashion.
        regsub -all {^(.{1,14}).*([#>])$} $reprompt {\1([^#>\r\n]+)?[#>](\\([^)\\r\\n]+\\))?} reprompt
        expect {
            -re $reprompt       {}
            -re "\[\n\r]+"      { exp_continue }
        }
    } else {
        regsub -all "\[)(]" $prompt {\\&} reprompt
    }

    # this is the only way i see to get rid of more prompts in o/p..grrrrr
    log_user 0
    # Is this a multi-command?
    if [ string match "*\;*" "$command" ] {
        set commands [split $command \;]
        set num_commands [llength $commands]
        # the pager can not be turned off on the PIX, so we have to look
        # for the "More" prompt.  the extreme is equally obnoxious, with a
        # global switch in the config.
        for {set i 0} {$i < $num_commands} { incr i} {
            send "[subst -nocommands [lindex $commands $i]]\r"
            expect {
                -re "\b+"                       { exp_continue }
                -re "^\[^\n\r *]*$reprompt"     { send_user -- "$expect_out(buffer)"
                                                }
                -re "^\[^\n\r]*$reprompt."      { send_user -- "$expect_out(buffer)"
                                                  exp_continue }
                -re "\[\n\r]+"                  { send_user -- "$expect_out(buffer)"
                                                  exp_continue }
                -re "\[^\r\n]*Press  to cont\[^\r\n]*"   {
                                                  send " "
                                                  # bloody ^[[2K after " "
                                                  expect {
                                                        -re "^\[^\r\n]*\r" {}
                                                        }
                                                  exp_continue
                                                }
                -re "^ *--More--\[^\n\r]*"      {
                                                  send " "
                                                  exp_continue }
                -re "^\[^\n\r]*"    {
                                                  send_user -- "$expect_out(buffer)"
                                                  send " "
                                                  exp_continue }
            }
        }
    } else {
        # the pager can not be turned off on the PIX, so we have to look
        # for the "More" prompt.  the extreme is equally obnoxious, with a
        # global switch in the config.
        send "[subst -nocommands $command]\r"
        expect {
                -re "\b+"                       { exp_continue }
                -re "^\[^\n\r *]*$reprompt"     { send_user -- "$expect_out(buffer)"
                                                }
                -re "^\[^\n\r]*$reprompt."      { send_user -- "$expect_out(buffer)"
                                                  exp_continue }
                -re "\[\n\r]+"                  { send_user -- "$expect_out(buffer)"
                                                  exp_continue }
                -re "\[^\r\n]*Press  to cont\[^\r\n]*"   {
                                                  send " "
                                                  # bloody ^[[2K after " "
                                                  expect {
                                                        -re "^\[^\r\n]*\r" {}
                                                        }
                                                  exp_continue
                                                }
                -re "^ *--More--\[^\n\r]*"      {
                                                  send " "
                                                  exp_continue }
                -re "^\[^\n\r]*"    {
                                                  send_user -- "$expect_out(buffer)"
                                                  send " "
                                                  exp_continue }
        }
    }
    log_user 1

    # Modified for the Cisco 4xxx controller
    if [ regexp -- "\\(Cisco Controller\\) >" "$prompt" ] {
        send "logout\r\N"
    }

    if { [ string compare "extreme" "$platform" ] } {
        send "exit\r"
    } else {
        send "quit\r"
    }
    expect {
        -re "^\[^\n\r *]*$reprompt"             {
                                                  # the Cisco CE and Jnx ERX
                                                  # return to non-enabled mode
                                                  # on exit in enabled mode.
                                                  send "exit\r"
                                                  exp_continue;
                                                }
        "Do you wish to save your configuration changes" {
                                                  send "n\r"
                                                  exp_continue
                                                }
        -re "\[\n\r]+"                          { exp_continue }
        timeout                                 { return 0 }
        eof                                     { return 0 }
    }
    set in_proc 0
}

#
# For each router... (this is main loop)
#
source_password_file $password_file
set in_proc 0
foreach router [lrange $argv $i end] {
    set router [string tolower $router]
    send_user "$router\n"

    # Figure out the prompt.
    # autoenable is off by default.  If we have it defined, it was done
    # on the command line.  If it is not specifically set on the command
    # line, check the password file.
    if $avautoenable {
        set autoenable 1
        set enable 0
        set prompt "(#|>| \\(enable\\))"
    } else {
        set ae [find autoenable $router]
        if { "$ae" == "1" } {
            set autoenable 1
            set enable 0
            set prompt "(#|>| \\(enable\\))"
        } else {
            set autoenable 0
            set enable $avenable
            set prompt ">"
        }
    }

    # look for noenable option in .cloginrc
    if { [find noenable $router] != "" } {
        set enable 0
    }

    # Figure out passwords
    if { $do_passwd || $do_enapasswd } {
      set pswd [find password $router]
      if { [llength $pswd] == 0 } {
        send_user "\nError: no password for $router in $password_file.\n"
        continue
      }
      if { $enable && $do_enapasswd && $autoenable == 0 && [llength $pswd]  \\\(enable\\\)"       {
                                  set junk $expect_out(0,string);
                                  regsub -all "\[\]\[]" $junk {\\&} prompt;
                                }
    }

    if { $do_command } {
        if {[run_commands $prompt $command]} {
            continue
        }
    } elseif { $do_script } {
        # If the prompt is (enable), then we are on a switch and the
        # command is "set length 0"; otherwise its "term length 0".
        if [ regexp -- ".*> .*enable" "$prompt" ] {
            send "set length 0\r"
            send "set logging session disable\r"
        } else {
            if [ regexp -- ".*>" "$prompt" ] {
                send "\r"
            } else {
              # Modified for the Cisco 4xxx controller
              if [ regexp -- "\\(Cisco Controller\\) >" "$prompt" ] {
                send "\r"
              } else {
                send "term length 0\r"
              }
            }
        }
        expect -re $prompt      {}
        source $sfile
        close
    } else {
        label $router
        log_user 1
        interact
    }

    # End of for each router
    wait
    sleep 0.3
}
exit 0

January 27, 2008

nixlogin_exp

Filed under: bigip, expect, f5, linux, nix, rancid — Tags: , , , , , — lancevermilion @ 11:52 pm

In order to get this to work you will need to add something like this to your .cloginrc

add userprompt linuxsvr* sshOnlyNoPrompt
add autoenable linuxsvr* 1
add user linuxsvr*

#! /usr/bin/expect --
##
## $Id: clogin.in,v 1.79 2004/05/27 21:57:52 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.
##
#
# The login expect scripts were based on Erik Sherk's gwtn, by permission.
#
# clogin - Cisco login
#
# Most options are intuitive for logging into a Cisco router.
# The default is to enable (thus -noenable).  Some folks have
# setup tacacs to have a user login at priv-lvl = 15 (enabled)
# so the -autoenable flag was added for this case (don't go through
# the process of enabling and the prompt will be the "#" prompt.
# The default username password is the same as the vty password.
#

# Usage line
set usage "Usage: $argv0 \[-autoenable\] \[-noenable\] \[-c command\] \
\[-Evar=x\] \[-e enable-password\] \[-f cloginrc-file\] \[-p user-password\] \
\[-s script-file\] \[-t timeout\] \[-u username\] \
\[-v vty-password\] \[-w enable-username\] \[-x command-file\] \
\[-y ssh_cypher_type\] router \[router...\]\n"

# env(CLOGIN) may contain:
#	x == do not set xterm banner or name

# Password file
set password_file $env(HOME)/.cloginrc
# Default is to login to the router
set do_command 0
set do_script 0
# The default is to automatically enable
set avenable 1
# The default is that you login non-enabled (tacacs can have you login already
# enabled)
set avautoenable 0
# The default is to look in the password file to find the passwords.  This
# tracks if we receive them on the command line.
set do_passwd 1
set do_enapasswd 1
# attempt at platform switching.
set platform ""

# Find the user in the ENV, or use the unix userid.
if {[ info exists env(CISCO_USER) ] } {
    set default_user $env(CISCO_USER)
} elseif {[ info exists env(USER) ]} {
    set default_user $env(USER)
} elseif {[ info exists env(LOGNAME) ]} {
    set default_user $env(LOGNAME)
} else {
    # This uses "id" which I think is portable.  At least it has existed
    # (without options) on all machines/OSes I've been on recently -
    # unlike whoami or id -nu.
    if [ catch {exec id} reason ] {
	send_error "\nError: could not exec id: $reason\n"
	exit 1
    }
    regexp {\(([^)]*)} "$reason" junk default_user
}

# Sometimes routers take awhile to answer (the default is 10 sec)
set timeout 45

# Process the command line
for {set i 0} {$i  (enable) " }
	-re "(denied|Sorry|Incorrect)"	{
			  # % Access denied - from local auth and poss. others
			  send_user "\nError: Check your Enable passwd\n";
			  return 1
			}
	"% Error in authentication" {
			  send_user "\nError: Check your Enable passwd\n"
			  return 1
			}
	"% Bad passwords" {
			  send_user "\nError: Check your Enable passwd\n"
			  return 1
			}
    }
    # We set the prompt variable (above) so script files don't need
    # to know what it is.
    set in_proc 0
    return 0
    puts "past expect enable prompt"
}

# Run commands given on the command line.
proc run_commands { prompt command } {
    global in_proc platform
    set in_proc 1

    # If the prompt is (enable), then we are on a switch and the
    # command is "set length 0"; otherwise its "term length 0".
    # skip if its an extreme (since the pager can not be disabled on a
    # per-vty basis).
    if { [ string compare "extreme" "$platform" ] } {
	if [ regexp -- ".*> .*enable" "$prompt" ] {
	    send "\r"
	    #send "set length 0\r"
	    # This is ugly, but reduces code duplication, allowing the
	    # subsequent expects to handle everything as normal.
	    set command "set logging session disable;$command"
	} else {
	    #send "term length 0\r"
	    send "\r"
	}
	# escape any parens in the prompt, such as "(enable)"
	regsub -all {[)(]} $prompt {\\&} reprompt
	# match cisco config mode prompts too, such as router(config-if)#,
	# but catalyst does not change in this fashion.
	regsub -all {^(.{1,14}).*([#>])$} $reprompt {\1([^#>\r\n]+)?[#>](\\([^)\\r\\n]+\\))?} reprompt
	expect {
	    -re $reprompt	{}
	    -re "\[\n\r]+"	{ exp_continue }
	}
    } else {
	regsub -all "\[)(]" $prompt {\\&} reprompt
    }

    # this is the only way i see to get rid of more prompts in o/p..grrrrr
    log_user 0
    # Is this a multi-command?
    if [ string match "*\;*" "$command" ] {
	set commands [split $command \;]
	set num_commands [llength $commands]
	# the pager can not be turned off on the PIX, so we have to look
	# for the "More" prompt.  the extreme is equally obnoxious, with a
	# global switch in the config.
	for {set i 0} {$i < $num_commands} { incr i} {
	    send "[subst -nocommands [lindex $commands $i]]\r"
	    expect {
		-re "\b+"			{ exp_continue }
		-re "^\[^\n\r *]*$reprompt"	{ send_user -- "$expect_out(buffer)"
						}
		-re "^\[^\n\r]*$reprompt."	{ send_user -- "$expect_out(buffer)"
						  exp_continue }
		-re "\[\n\r]+"			{ send_user -- "$expect_out(buffer)"
						  exp_continue }
		-re "\[^\r\n]*Press  to cont\[^\r\n]*"	{
						  send " "
						  # bloody ^[[2K after " "
						  expect {
							-re "^\[^\r\n]*\r" {}
							}
						  exp_continue
						}
		-re "^ *--More--\[^\n\r]*"	{
						  send " "
						  exp_continue }
		-re "^\[^\n\r]*"	{
						  send_user -- "$expect_out(buffer)"
						  send " "
						  exp_continue }
	    }
	}
    } else {
	# the pager can not be turned off on the PIX, so we have to look
	# for the "More" prompt.  the extreme is equally obnoxious, with a
	# global switch in the config.
	send "[subst -nocommands $command]\r"
	expect {
		-re "\b+"			{ exp_continue }
		-re "^\[^\n\r *]*$reprompt"	{ send_user -- "$expect_out(buffer)"
						}
		-re "^\[^\n\r]*$reprompt."	{ send_user -- "$expect_out(buffer)"
						  exp_continue }
		-re "\[\n\r]+"			{ send_user -- "$expect_out(buffer)"
						  exp_continue }
		-re "\[^\r\n]*Press  to cont\[^\r\n]*"	{
						  send " "
						  # bloody ^[[2K after " "
						  expect {
							-re "^\[^\r\n]*\r" {}
							}
						  exp_continue
						}
		-re "^ *--More--\[^\n\r]*"	{
						  send " "
						  exp_continue }
		-re "^\[^\n\r]*"	{
						  send_user -- "$expect_out(buffer)"
						  send " "
						  exp_continue }
	}
    }
    log_user 1

    if { [ string compare "extreme" "$platform" ] } {
	send "\rexit\r"
    } else {
	send "\rquit\r"
    }
    expect {
	-re "^\[^\n\r *]*$reprompt"		{
						  # the Cisco CE and Jnx ERX
						  # return to non-enabled mode
						  # on exit in enabled mode.
						  send "\rexit\r"
						  exp_continue;
						}
	"Do you wish to save your configuration changes" {
						  send "n\r"
						  exp_continue
						}
	-re "\[\n\r]+"				{ exp_continue }
	timeout					{ return 0 }
	eof					{ return 0 }
    }
    set in_proc 0
}

#
# For each router... (this is main loop)
#
source_password_file $password_file
set in_proc 0
foreach router [lrange $argv $i end] {
    set router [string tolower $router]
    send_user "$router\n"

    # Figure out prompt.
    # Since autoenable is off by default, if we have it defined, it
    # was done on the command line. If it is not specifically set on the
    # command line, check the password file.
    if $avautoenable {
	set autoenable 1
	set enable 0
	set prompt "(#| \\(enable\\))"
    } else {
	set ae [find autoenable $router]
	if { "$ae" == "1" } {
	    set autoenable 1
	    set enable 0
	    set prompt "(#| \\(enable\\))"
	} else {
	    set autoenable 0
	    set enable $avenable
	    set prompt "(\\$|\\>|\\:)"
	    #set prompt ">"
            puts "prompt: $prompt"
	}
    }

    # look for noenable option in .cloginrc
    if { [find noenable $router] != "" } {
	set enable 0
    }

    # Figure out passwords
    if { $do_passwd || $do_enapasswd } {
      set pswd [find password $router]
      if { [llength $pswd] == 0 } {
	send_user "\nError: no password for $router in $password_file.\n"
	continue
      }
      if { $enable && $do_enapasswd && $autoenable == 0 && [llength $pswd]  \\\(enable\\\)"	{
				  set junk $expect_out(0,string);
				  regsub -all "\[\]\[]" $junk {\\&} prompt;
				}
    }

    if { $do_command } {
	if {[run_commands $prompt $command]} {
	    continue
	}
    } elseif { $do_script } {
	# If the prompt is (enable), then we are on a switch and the
	# command is "set length 0"; otherwise its "term length 0".
	if [ regexp -- ".*> .*enable" "$prompt" ] {
	    #send "set length 0\r"
	    send "\r"
	    send "set logging session disable\r"
	} else {
	    #send "term length 0\r"
	    send "\r"
	}
	expect -re $prompt	{}
	source $sfile
	close
    } else {
	label $router
	log_user 1
	interact
    }

    # End of for each router
    wait
    sleep 0.3
}
exit 0

f5login_exp

Filed under: bigip, expect, f5, rancid — Tags: , , , — lancevermilion @ 11:50 pm

***THIS IS NO LONGER NEEDED IF YOU USE THIS VERSION OF f5rancid***

To get this script to work with rancid you will need to add something like this to your .cloginrc

add userprompt bigip* sshOnlyNoPrompt
add autoenable bigip* 1
add user bigip*

#! /usr/bin/expect --
##
## $Id: clogin.in,v 1.79 2004/05/27 21:57:52 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.
##
#
# The login expect scripts were based on Erik Sherk's gwtn, by permission.
#
# clogin - Cisco login
#
# Most options are intuitive for logging into a Cisco router.
# The default is to enable (thus -noenable).  Some folks have
# setup tacacs to have a user login at priv-lvl = 15 (enabled)
# so the -autoenable flag was added for this case (don't go through
# the process of enabling and the prompt will be the "#" prompt.
# The default username password is the same as the vty password.
#

# Usage line
set usage "Usage: $argv0 \[-autoenable\] \[-noenable\] \[-c command\] \
\[-Evar=x\] \[-e enable-password\] \[-f cloginrc-file\] \[-p user-password\] \
\[-s script-file\] \[-t timeout\] \[-u username\] \
\[-v vty-password\] \[-w enable-username\] \[-x command-file\] \
\[-y ssh_cypher_type\] router \[router...\]\n"

# env(CLOGIN) may contain:
#       x == do not set xterm banner or name

# Password file
set password_file $env(HOME)/.cloginrc
# Default is to login to the router
set do_command 0
set do_script 0
# The default is to automatically enable
set avenable 1
# The default is that you login non-enabled (tacacs can have you login already
# enabled)
set avautoenable 0
# The default is to look in the password file to find the passwords.  This
# tracks if we receive them on the command line.
set do_passwd 1
set do_enapasswd 1
# attempt at platform switching.
set platform ""

# Find the user in the ENV, or use the unix userid.
if {[ info exists env(CISCO_USER) ] } {
    set default_user $env(CISCO_USER)
} elseif {[ info exists env(USER) ]} {
    set default_user $env(USER)
} elseif {[ info exists env(LOGNAME) ]} {
    set default_user $env(LOGNAME)
} else {
    # This uses "id" which I think is portable.  At least it has existed
    # (without options) on all machines/OSes I've been on recently -
    # unlike whoami or id -nu.
    if [ catch {exec id} reason ] {
        send_error "\nError: could not exec id: $reason\n"
        exit 1
    }
    regexp {\(([^)]*)} "$reason" junk default_user
}

# Sometimes routers take awhile to answer (the default is 10 sec)
set timeout 45

# Process the command line
for {set i 0} {$i  (enable) " }
        -re "(denied|Sorry|Incorrect)"  {
                          # % Access denied - from local auth and poss. others
                          send_user "\nError: Check your Enable passwd\n";
                          return 1
                        }
        "% Error in authentication" {
                          send_user "\nError: Check your Enable passwd\n"
                          return 1
                        }
        "% Bad passwords" {
                          send_user "\nError: Check your Enable passwd\n"
                          return 1
                        }
    }
    # We set the prompt variable (above) so script files don't need
    # to know what it is.
    set in_proc 0
    return 0
}

# Run commands given on the command line.
proc run_commands { prompt command } {
    global in_proc platform
    set in_proc 1

    # If the prompt is (enable), then we are on a switch and the
    # command is "set length 0"; otherwise its "term length 0".
    # skip if its an extreme (since the pager can not be disabled on a
    # per-vty basis).
    if { [ string compare "extreme" "$platform" ] } {
        if [ regexp -- ".*> .*enable" "$prompt" ] {
            send "\r"
            #send "set length 0\r"
            # This is ugly, but reduces code duplication, allowing the
            # subsequent expects to handle everything as normal.
            set command "set logging session disable;$command"
        } else {
            #send "term length 0\r"
            send "\r"
        }
        # escape any parens in the prompt, such as "(enable)"
        regsub -all {[)(]} $prompt {\\&} reprompt
        # match cisco config mode prompts too, such as router(config-if)#,
        # but catalyst does not change in this fashion.
        regsub -all {^(.{1,14}).*([#>])$} $reprompt {\1([^#>\r\n]+)?[#>](\\([^)\\r\\n]+\\))?} reprompt
        expect {
            -re $reprompt       {}
            -re "\[\n\r]+"      { exp_continue }
        }
    } else {
        regsub -all "\[)(]" $prompt {\\&} reprompt
    }

    # this is the only way i see to get rid of more prompts in o/p..grrrrr
    log_user 0
    # Is this a multi-command?
    if [ string match "*\;*" "$command" ] {
        set commands [split $command \;]
        set num_commands [llength $commands]
        # the pager can not be turned off on the PIX, so we have to look
        # for the "More" prompt.  the extreme is equally obnoxious, with a
        # global switch in the config.
        for {set i 0} {$i < $num_commands} { incr i} {
            send "[subst -nocommands [lindex $commands $i]]\r"
            expect {
                -re "\b+"                       { exp_continue }
                -re "^\[^\n\r *]*$reprompt"     { send_user -- "$expect_out(buffer)"
                                                }
                -re "^\[^\n\r]*$reprompt."      { send_user -- "$expect_out(buffer)"
                                                  exp_continue }
                -re "\[\n\r]+"                  { send_user -- "$expect_out(buffer)"
                                                  exp_continue }
                -re "\[^\r\n]*Press  to cont\[^\r\n]*"   {
                                                  send " "
                                                  # bloody ^[[2K after " "
                                                  expect {
                                                        -re "^\[^\r\n]*\r" {}
                                                        }
                                                  exp_continue
                                                }
                -re "^ *--More--\[^\n\r]*"      {
                                                  send " "
                                                  exp_continue }
                -re "^\[^\n\r]*"    {
                                                  send_user -- "$expect_out(buffer)"
                                                  send " "
                                                  exp_continue }
            }
        }
    } else {
        # the pager can not be turned off on the PIX, so we have to look
        # for the "More" prompt.  the extreme is equally obnoxious, with a
        # global switch in the config.
        send "[subst -nocommands $command]\r"
        expect {
                -re "\b+"                       { exp_continue }
                -re "^\[^\n\r *]*$reprompt"     { send_user -- "$expect_out(buffer)"
                                                }
                -re "^\[^\n\r]*$reprompt."      { send_user -- "$expect_out(buffer)"
                                                  exp_continue }
                -re "\[\n\r]+"                  { send_user -- "$expect_out(buffer)"
                                                  exp_continue }
                -re "\[^\r\n]*Press  to cont\[^\r\n]*"   {
                                                  send " "
                                                  # bloody ^[[2K after " "
                                                  expect {
                                                        -re "^\[^\r\n]*\r" {}
                                                        }
                                                  exp_continue
                                                }
                -re "^ *--More--\[^\n\r]*"      {
                                                  send " "
                                                  exp_continue }
                -re "^\[^\n\r]*"    {
                                                  send_user -- "$expect_out(buffer)"
                                                  send " "
                                                  exp_continue }
        }
    }
    log_user 1

    if { [ string compare "extreme" "$platform" ] } {
        send "\rexit\r"
    } else {
        send "\rquit\r"
    }
    expect {
        -re "^\[^\n\r *]*$reprompt"             {
                                                  # the Cisco CE and Jnx ERX
                                                  # return to non-enabled mode
                                                  # on exit in enabled mode.
                                                  send "\rexit\r"
                                                  exp_continue;
                                                }
        "Do you wish to save your configuration changes" {
                                                  send "n\r"
                                                  exp_continue
                                                }
        -re "\[\n\r]+"                          { exp_continue }
        timeout                                 { return 0 }
        eof                                     { return 0 }
    }
    set in_proc 0
}

#
# For each router... (this is main loop)
#
source_password_file $password_file
set in_proc 0
foreach router [lrange $argv $i end] {
    set router [string tolower $router]
    send_user "$router\n"

    # Figure out prompt.
    # Since autoenable is off by default, if we have it defined, it
    # was done on the command line. If it is not specifically set on the
    # command line, check the password file.
    if $avautoenable {
        set autoenable 1
        set enable 0
        set prompt "(#| \\(enable\\))"
    } else {
        set ae [find autoenable $router]
        if { "$ae" == "1" } {
            set autoenable 1
            set enable 0
            set prompt "(#| \\(enable\\))"
        } else {
            set autoenable 0
            set enable $avenable
            set prompt ">"
        }
    }

    # look for noenable option in .cloginrc
    if { [find noenable $router] != "" } {
        set enable 0
    }

    # Figure out passwords
    if { $do_passwd || $do_enapasswd } {
      set pswd [find password $router]
      if { [llength $pswd] == 0 } {
        send_user "\nError: no password for $router in $password_file.\n"
        continue
      }
      if { $enable && $do_enapasswd && $autoenable == 0 && [llength $pswd]  \\\(enable\\\)"       {
                                  set junk $expect_out(0,string);
                                  regsub -all "\[\]\[]" $junk {\\&} prompt;
                                }
    }

    if { $do_command } {
        if {[run_commands $prompt $command]} {
            continue
        }
    } elseif { $do_script } {
        # If the prompt is (enable), then we are on a switch and the
        # command is "set length 0"; otherwise its "term length 0".
        if [ regexp -- ".*> .*enable" "$prompt" ] {
            #send "set length 0\r"
            send "\r"
            send "set logging session disable\r"
        } else {
            #send "term length 0\r"
            send "\r"
        }
        expect -re $prompt      {}
        source $sfile
        close
    } else {
        label $router
        log_user 1
        interact
    }

    # End of for each router
    wait
    sleep 0.3
}
exit 0

January 26, 2008

nixlogin_pl

Filed under: f5, linux, nix, perl, rancid — Tags: , , , , — lancevermilion @ 7:26 am
#! /usr/bin/perl
##
## $Id: nixrancid.in,v 1.4 2008/01/21 10:43:25 lvermilion Exp $
##
#
# TODO
# * Add to personal rancid distribution.
# * OS Detection (maybe use variable to support user defined OS)
# * Collection of more system critical files
# * Fix netstat to not include Use/Expire fields on BSD
#
# Changelog
#
# v1.4  lvermilion 1-21-08
# Added support so the original F5 script works on Linux/BSD/Unix
# Added support for the command "sudo su root"
# Added BSDConfFile, since these files are BSD specific
# and we don't want them ran unless we are on a BSD system
# Added a variety of linux/BSD commands
# Removed F5 commands at this point until OS detection is completed.
#
# v1.3 lvermilion
# Add banner formatting so it is more clear what command was ran
# Add support for more F5 commands (profile/monitor/regkey/license/etc).
#
# v1.2  mashcraft 9-18-07
# Fixed invalid TERM causing intermitent failures
# Works as non-root user
# Added Platform info and static routes
#
# v1.1  mashcraft 8-2-07
# Debugging problems with HF8.
#
#
## Copyright (C) 1997-2004 by Terrapin Communications, Inc.
## All rights reserved.
##
## Copyright (C) 2007 by Omniture, Inc. Ver 1.1-1.2
## All rights reserved.
##
## Copyright (C) 2008 by Gheek.net Ver 1.3-1.4
## 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.
##
#
# Based on rancid, this script handles F5 BigIP/(Li|U)nix,BSD systems.
# Modifications by Mike Ashcraft and Lance Vermilion.
#
# This script has been tested with Big-IP Version 9.1.2, 9.2.x and 9.3.1
#
# This script uses nixlogin.  You will need to configure the
# host in .cloginrc as follows:
#
# add user hostname		username
# add userprompt hostname	sshOnlyNoPrompt # The default prompt regex matches the last login message and causes problems
# add method hostname		ssh
# add password hostname		password
# add autoenable hostname	1
#
#  RANCID - Really Awesome New Cisco confIg Differ
#
# usage: nixrancid [-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;
$found_version = 0;
$found_env = 0;
$found_diag = 0;
$timeo = 90;				# clogin timeout in seconds

my(@commandtable, %commands, @commands);# command lists
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);
}

# sub Platform  31-Jan-2007  Mike Ashcraft mashcraft@omniture.com
# This routine parses "cat filename"
sub Platform {
    print STDERR "    In Platform: $_" if ($debug);

    ProcessHistory("COMMENTS","","BO","\n!+----------------------------------------------+\n!|\n!| ####  Running $cmd\n!|\n!+----------------------------------------------+\n");

    while () {
        tr/15//d;
        last if (/^$prompt/);
        next if (/degC/);
        ProcessHistory("","","$cmd","$_");
    }
    $found_end = 1;
    return(0);
}

# sub BSDConfFile  21-Jan-2008  Lance Vermilion lvermilion@gheek.net
# This routine parses "cat filename"
sub BSDConfFile {
    print STDERR "    In BSDConfFile: $_" if ($debug);
        $uname=`uname`;

    ProcessHistory("COMMENTS","","BO","\n!+----------------------------------------------+\n!|\n!| ####  Running $cmd\n!|\n!+----------------------------------------------+\n") if ( $uname !~ /BSD/i );

    while () {
        last if ( $uname !~ /BSD/i );
        tr/15//d;
        last if (/^$prompt/);
        ProcessHistory("","","$cmd","$_") if ( $uname =~ /BSD/i );
    }
    $found_end = 1;
    return(0);
}

# sub ConfFile  31-Jan-2007  Mike Ashcraft mashcraft@omniture.com
# This routine parses "cat filename"
sub ConfFile {
    print STDERR "    In ConfFile: $_" if ($debug);

    ProcessHistory("COMMENTS","","BO","!+----------------------------------------------+\n!|\n!| ####  Running $cmd\n!|\n!+----------------------------------------------+\n");

    while () {
        tr/15//d;
        last if (/^$prompt/);
        if ( $cmd =~ /netstat/ )
        {
          next if /.*UHLW.*/
        }
        next if ($cmd =~ /config sync show/ && !/^\s+Status/);
        ProcessHistory("","","$cmd","$_");
    }
    $found_end = 1;
    return(0);
}

# sub DirList  31-Jan-2007  Mike Ashcraft mashcraft@omniture.com
# This routine parses directory listings.
#
# Disable color and make sure the date format is consistent
# For example: ll --time-style=long-iso --color=never
#
sub DirList {
    print STDERR "    In ConfFile: $_" if ($debug);

    ProcessHistory("COMMENTS","","BO","!+----------------------------------------------+\n!|\n!| ####  Running $cmd\n!|\n!+----------------------------------------------+\n");

    while () {
        tr/15//d;
        last if (/^$prompt/);
        ProcessHistory("","","$cmd","$_");
    }
    $found_end = 1;
    return(0);
}

# sub NoOutput  18-Sep-2007  Mike Ashcraft mashcraft@omniture.com
# This routine handles commands where you do not want any output
#
#
sub NoOutput {
    print STDERR "    In ConfFile: $_" if ($debug);

    while () {
        tr/15//d;
        last if (/^$prompt/);
    }
    $found_end = 1;
    return(0);
}

# dummy function
sub DoNothing {print STDOUT;}

# Main
@commandtable = (
	{'TERM=xterm'			=> 'NoOutput'},
	{'export TERM'			=> 'NoOutput'},
	{'uname -a'			=> 'Platform'},
	{'netstat -rn'			=> 'ConfFile'},
	{'cat /etc/passwd'		=> 'ConfFile'},
	{'cat /etc/group'		=> 'ConfFile'},
	{'cat /etc/hosts'		=> 'ConfFile'},
	{'cat /etc/resolv.conf'		=> 'ConfFile'},
	{'cat /etc/my.cnf'		=> 'ConfFile'},
	{'cat /etc/drbd.conf'		=> 'ConfFile'},
	{'cat /etc/fstab'		=> 'ConfFile'},
	{'cat /etc/rc.conf'		=> 'ConfFile'},
	{'cat /etc/iscsi/iscsid.conf' 	=> 'ConfFile'},
	{'cat /etc/modprobe.conf'	=> 'ConfFile'},
	{'cat /etc/sysconfig/network-scripts/ifcfg-bond0'		=> 'ConfFile'},
	{'cat /etc/sysconfig/network-scripts/ifcfg-bond1'		=> 'ConfFile'},
	{'cat /etc/sysconfig/network-scripts/ifcfg-eth0'		=> 'ConfFile'},
	{'cat /etc/sysconfig/network-scripts/ifcfg-eth1'		=> 'ConfFile'},
	{'cat /etc/sysconfig/network-scripts/ifcfg-eth2'		=> 'ConfFile'},
	{'cat /etc/sysconfig/network-scripts/ifcfg-eth3'		=> 'ConfFile'},
	{'cat /etc/sysconfig/network-scripts/ifcfg-eth4'		=> 'ConfFile'},
	{'cat /etc/sysconfig/network-scripts/ifcfg-eth5'		=> 'ConfFile'},
	{'alternatives --config java' 	=> 'ConfFile'},
	{'/sbin/chkconfig --list' 	=> 'ConfFile'},
	{'/usr/bin/yum list installed' 	=> 'ConfFile'},
#        {'bigpipe platform'             => 'Platform'},
#        {'bigpipe version'              => 'ConfFile'},
#        {'bigpipe list'                 => 'ConfFile'},
#        {'bigpipe base list'            => 'ConfFile'},
#        {'bigpipe profile list'         => 'ConfFile'},
#        {'bigpipe monitor list'         => 'ConfFile'},
#        {'bigpipe config sync show'             => 'ConfFile'},
#        {'cat /config/RegKey.license'           => 'ConfFile'},
#        {'bigpipe route static show'            => 'ConfFile'},
#        {'ls --full-time --color=never /config/ssl/ssl.crt'     => 'DirList'},
#        {'ls --full-time --color=never /config/ssl/ssl.key'     => 'DirList'},

);
# 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("|",@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 nixlogin -t $timeo -c\"$cisco_cmds\" $host\n" if ($debug);
    print STDOUT "executing nixlogin -t $timeo -c\"$cisco_cmds\" $host\n" if ($log);
    if (defined($ENV{NOPIPE})) {
	system "nixlogin -t $timeo -c \"$cisco_cmds\" $host  $host.raw 2>&1" || die "nixlogin failed for $host: $!\n";
	open(INPUT, "< $host.raw") || die "nixlogin failed for $host: $!\n";
    } else {
	open(INPUT,"nixlogin -t $timeo -c \"$cisco_cmds\" $host </dev/null |") || die "nixlogin 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: nix\n!\n");
ProcessHistory("COMMENTS","keysort","B0","!\n");
ProcessHistory("COMMENTS","keysort","D0","!\n");
ProcessHistory("COMMENTS","keysort","F0","!\n");
ProcessHistory("COMMENTS","keysort","G0","!\n");
TOP: while() {
    tr/15//d;
    if (/\s?logout$/) {
	$clean_run=1;
	last;
    }
    if (/^Error:/) {
	print STDOUT ("$host nixlogin error: $_");
	print STDERR ("$host nixlogin 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 = };
	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);
}

Blog at WordPress.com.