Linux Shell Script example1 (Cisco-up/download)


#!/bin/sh
#------------------------------------------------
# $Id: confg2.sh,v 1.1 2000/11/10 09:14:18 mfk Exp $
#------------------------------------------------
#
# What hardware does this script support?
# 
# Cisco routers running IOS 11 and IOS 12
# Future support to be provided for Foundry and 
# Cisco Catalyst Switches.
#------------------------------------------------

#------------------------------------------------
# Setting up this script for another site
#
# Carefully look at the variables in the Global
# section.  Make changes that are appropriate.
# At a minimum this will include:
# 
# COMMUNITY
# TFTPSRV
# TFTPDIR
# RCSDIR
# SNMPBIN
# and all 'shell commands' variables
#------------------------------------------------

#------------------------------------------------
# GLOBAL SECTION
#------------------------------------------------

# GENERAL vars
VENDOR=""       # e.g., "Cisco"
HW=""       # e.g., "MSFC"  or "3600"
VER=""      # Major Version number (just 12 etc)
OPT=0       # the options mask to be built

# snmp vars
COMMUNITY=plenTpak
TFTPSRV=202.1.232.15
TFTPDIR=/tftp
DELAY=5         # seconds to wait for a snmp* command to finish
RETRY=5     # the number of time to retry 'conf net'

# RCS vars (note CO in shell commands)
# note:  if ! -f $RCSDIR/RCS  then look for $RCSDIR/$RTR-NAME/RCS
RCSDIR=$TFTPDIR

# shell commands
SNMPBIN=/usr/local/bin
SNMPOPTS="-t 5"
SNMPSET="$SNMPBIN/snmpset $SNMPOPTS"
SNMPGET="$SNMPBIN/snmpget $SNMPOPTS"
NSLOOKUP=/usr/sbin/nslookup
PING=/sbin/ping
CO=/usr/bin/co
EXPR=/bin/expr
DIFF=/usr/bin/diff
ID=/usr/bin/id
PWDC=/bin/pwd      # append C for command, some shells use $PWD
RM=/bin/rm
CP=/bin/cp
AWK=/usr/bin/awk

# debug help DEBUG 1 echos commands, while 2 ignores them
DEBUG1=/bin/echo
DEBUG2=/usr/in/false

# cisco router mibs
CISCO_MIB_BASE=".1.3.6.1.4.1.9.2.1"
CISCO_MIB_TFTP2RUN_NEW="${CISCO_MIB_BASE}.50"
CISCO_MIB_TFTP2RUN_OLD="${CISCO_MIB_BASE}.53"
CISCO_MIB_RUN2START="${CISCO_MIB_BASE}.54"
CISCO_MIB_RUN2TFTP="${CISCO_MIB_BASE}.55"

# cisco Catalyst mibs
CAT_MIB_BASE=".1.3.6.1.4.1.9.5.1.5"

# generic MIBS to be used in code

# Global error codes, convention: all begin with 'E'
EUSAGE=1       # bad command line
ESNMP=2        # for generic SNMP errors
ECONFNET=3     # error configuring from net
EWRNET=4       # error performing wr net
EWRMEM=5       # error performing copy running-config startup-config
ENOCONFG=6     # cannot find rtr-confg
ENOFILE=7      # file doesn't exist
ENOSTRIP=8     # cannot strip the passed file
EMIBS=9        # cannot setup MIBS in setupMIBS function
EERROR=10      # general error for weird things

# pseudo enum again, this time for opt args the way I like them
# we set this if an option is found
opt_w=0
opt_c=0
opt_s=0
opt_r=0
opt_d=0

#------------------------------------------------
# END GLOBAL SECTION
#------------------------------------------------

#------------------------------------------------
# PROCS
#------------------------------------------------

usage(){
  echo "$0 [<-w | -s | -c> <-r rtr-name> <-f confg-file>] | [<-n file>]"
  echo "   | <-n file>"
  echo "   | <-d> <-r rtr-name>"
  echo "   | <-a acl-name> <-r rtr-name>"
  echo "   | <-h>"
  echo "  -w  wr net, write running-config tftp://rtr-name-confg.run"
  echo "  -c  conf net, copy tftp running-config using safety net"
  echo "  -s  copy running-config startup-config"
  echo "  -r  the name of the router to configure"
  echo "  -n  file to make naked (stripped), output is file.stripped"
  echo "      This can include a path.  The stripped file is placed"
  echo "      in the same directory as the file."
  echo "  -f  config file to use rather than rtr-name-confg"
  echo "  -d  perform a diff between rtr-name-confg and rtr-name-confg.run"
  echo "  -a  upload the acl to the router (does a conf net on this file)"
  echo "  -h  this message"
  exit $EUSAGE;
}

#---------------------------------------------------
# remove the various possible temp files
#---------------------------------------------------
clean(){
  $RM -f *.stripped *.run 1>/dev/null 2>&1
}

#---------------------------------------------------
# print a big error header, but don't exit
#---------------------------------------------------
error(){
  echo "==================================="
  echo " ERROR ERROR ERROR ERROR ERROR"
  echo "==================================="
}

#---------------------------------------------------
# fatalerror, exits...
#  $1 = exit code
#  $2 = exit message
#---------------------------------------------------
fatalerror(){
  error
  clean
  echo $2
  exit $1
}

#---------------------------------------------------
# do a 'conf net'  return ECONFNET on error
#   $1 = rtr to do wr net on
#   $2 = file name on tftp server to load
#---------------------------------------------------
confNet(){
  if [ ! -f $TFTPDIR/$2 ]; then return $ENOFILE; fi

i=0;
  while [ $i -lt $RETRY ]; do
    if [ $i -gt 0 ]; then echo "Retrying conf net...."; fi
    $SNMPSET $1 $COMMUNITY $MIB_TFTP2RUN.$TFTPSRV s $2
    if [ $? -eq 0 ]; then i=$RETRY; fi
    i=`$EXPR $i + 1`
    sleep $DELAY
  done

# if i == $RETRY + 1, then we succeeded to do confNet, 
  # if i == $RETRY, then we failed.

if [ $i -eq $RETRY ]; then return $ECONFNET; fi
  return 0;  # success
}

diffConfgs(){
  strip_comments $1 $1.stripped
  strip_comments $2 $2.stripped
  $DIFF $1.stripped $2.stripped > /dev/null 2>&1
  if [ $? -ne 0 ]; then
    error
    echo "DIFF IS BAD.  CONFIGURATION FAILED.  DIFF FOLLOWS."
    $DIFF -u $1.stripped $2.stripped
    return $EWRNET
  fi
  return 0; # success
}

#----------------------------------------------------
#   $1 = rtr
#  return 0 if rtr is pingable
#----------------------------------------------------
doesRtrExist(){
  if [ $# -ne 1 ]; then
    error
    echo "No router given"
    return $EUSAGE
  fi

$PING -c 1 $1 > /dev/null 2>&1
  return $?
}

#----------------------------------------------------
# returns 0 if the config does not exist in $TFTPDIR
# or if the config file is zero sized.
# $1 is the router name 
#  the config file is $1
#----------------------------------------------------
doesConfgExist(){
  if [ ! -f $TFTPDIR/$1 ]; then return 0; fi
  if [ ! -s $TFTPDIR/$1 ]; then return 0; fi
  return 1;
}

#----------------------------------------------------
# Check out $1-confg
#  return  status of $CO
#  $1 = rtr-name
#----------------------------------------------------
getConfg(){
  # we expect to be in $TFTPDIR already, but
  lpwd=`$PWDC`
  if [ -d $RCSDIR/RCS ]; then  cd $RCSDIR; fi
  if [ -d $RCSDIR/$1/RCS ]; then cd $RCSDIR/$1; fi
  $CO $1-confg 1>/dev/null 2>&1
  if [ $lpwd != `$PWDC` ]; then
    $CP $1-confg $TFTPDIR
    $RM -f $1-confg
  fi
  cd $lpwd
  return $?
}

#----------------------------------------------------
# load acl in $2 onto router in $1
# We try to do this safely...
#   1.  we get the current running-config.run
#   2.  we upload the acl
#   3.  we get the new running-config.new.run
#   4.  we diff old and new confgs and show it to user
#   5.  If diffs do not match, we copy run to start
#----------------------------------------------------
loadACL(){
  echo "downloading curring running config to $1-confg.old.run"
  wrNet $1 $1-confg.old.run
  if [ $? -ne 0 ]; then
    error
    echo "Cannot get the current router config."
    echo "ACL NOT UPLOADED, either retry this script or do it by hand"
    return $EWRNET
  fi

echo "Now, we do a conf net with the new ACL file"
  confNet $1 $2
  if [ $? -ne 0 ]; then
    error
    echo "Conf Net failed.  Failed to load new acl on router."
    return $ECONFNET
  fi

echo "Making a copy of the NEW running config in $1-confg for comparison"
  wrNet $1 $1-confg.new.run
  if [ $? -ne 0 ]; then
    error
    echo "I had trouble downloading the new running-config"
    echo "While the conf net appears to have succeeded, I cannot"
    echo "guarentee it without being able to diff the old and new confgs"
    echo "I AM NOT doing a copy run start....."
    return $EWRNET
  fi

echo " "
  echo "Comparing  old and new configs"
  diffConfgs $1-confg.old.run $1-confg.new.run
  diffRes=$?
  echo "------------------------------------------------"
  done=f
  while [ $done = "f" ]; do
    echo "Do you want to 'copy running-config startup-config'? ([y]/n)"
    read ans
    if [ "X$ans" = "X" ]; then ans="y"; fi
    if [ $ans = "y" ]; then
      wrMem $1
      if [ $? -ne 0 ]; then
        error "COPY FAILED!!!!"
        return $EWRMEM
      fi
      done=$ans
    fi
    if [ $ans = "n" ]; then
      if [ $diffRes -ne 0 ]; then
        echo "Ok... your choice, but the running-config doesn't match startup"
      else
        echo "Ok... just FYI, the running-config = startup-config"
      fi
      done=$ans
    fi
  done   
  return 0
}

#----------------------------------------------------
# perform a 'wr net' return EWRNET on error
#    $1 = rtr to do the wr net on
#    $2 = file name on tftp server to write to
#----------------------------------------------------
wrNet(){
  FILE=$2
  touch $TFTPDIR/$FILE 
  if [ $? -ne 0 ]; then
    echo "Cannot create file"
    return $EWRNET
  fi

chmod 666 $TFTPDIR/$FILE
  if [ $? -ne 0 ]; then
    echo "Cannot chmod file"
    return $EWRNET
  fi

i=0;
  while [ $i -lt $RETRY ]; do
    if [ $i -gt 0 ]; then echo "Retrying wr net...."; fi
    $SNMPSET $1 $COMMUNITY $MIB_RUN2TFTP.$TFTPSRV s $FILE
    if [ $? -eq 0 ]; then i=$RETRY; fi
    i=`$EXPR $i + 1`
    sleep $DELAY
  done

# if i == $RETRY + 1, then we succeeded to do confNet,
  # if i == $RETRY, then we failed.

if [ $i -eq $RETRY ]; then return $EWRNET; fi
  return 0;  # success
}

#----------------------------------------------------
# perform a 'wr mem' return EWRMEM on error
#    $1 = rtr to do the wr mem on
#----------------------------------------------------
wrMem(){

i=0;
  while [ $i -lt $RETRY ]; do
    if [ $i -gt 0 ]; then echo "Retrying wr mem...."; fi
    $SNMPSET $1 $COMMUNITY $MIB_RUN2START.0 integer 1
    if [ $? -eq 0 ]; then i=$RETRY; fi
    i=`$EXPR $i + 1`
    sleep $DELAY
  done

# if i == $RETRY + 1, then we succeeded to do confNet,
  # if i == $RETRY, then we failed.

if [ $i -eq $RETRY ]; then return $EWRMEM; fi
  return 0;  # success           
}

#-----------------------------------------------------
# Verify that the router is a cisco by using 
# 'system.sysDescr'
#      returns 0 if cisco, $? if not cisco
#   $1= rtr do check 
#-----------------------------------------------------
# isCisco(){
#   $SNMPGET $1 $COMMUNITY system.sysDescr.0
#   return $?
# }

#-----------------------------------------------
# stolen from jkb, this fn takes 2 args, 
#   $1 = source file name
#   $2 = dst filename
# writes $2 with the stripped form of $1
#-----------------------------------------------
strip_comments(){
    src=$1
    dst=$2
    rm -f ${dst} 1>/dev/null 2>&1

cat ${src}          |
        egrep -v '^!'   |                
        sed -e '/^ int FastEthernet/,/no ip access-list/d' \
            -e '/^ int Ethernet/,/no ip access-list/d' \
            -e '/^ int ATM/,/no ip access-list/d' \
            -e '/^ int Hssi/,/no ip access-list/d' \
            -e '/^no ip access-list extended /d'    \
            -e '/^$/d' \
            -e '/^ no ip access-list standard 13/d' \
            -e 's/ eq 500/ eq isakmp/g' \
            -e 's/ permit 50 / permit esp /' \
            -e '/^line vty 0/,/ntp (peer|server|clock-period)/d'  > ${dst}

return $?
}

#-----------------------------------------------------
# called iff  the -n option is provided
# Takes filename as arg 1 and calls Jan's fn if the
# file exists.  Returns 0 on success and ENOFILE,
# or ENOSTRIP on error
#-----------------------------------------------------
stripFile(){
  # remember that this prog works in $TFTPDIR, but this method
  # is independent of that DIR so it takes us back to PWD0
  lpwd=$PWD
  cd $PWD0 
  if [ ! -f "$1" ]; then
    error
    echo "Cannot find $1 to strip"
    return $ENOFILE
  fi

strip_comments $1 $1.stripped
  if [ $? -ne 0 ]; then  error; echo "Cannot strip file"; return $ENOSTRIP; fi
  cd $lpwd 
  return 0
}

#-----------------------------------------------------
# safeConfNet performs the following:
#   1. confNet $1 $2
#   2. wrNet $1 $2.run
#   3. evaluates the diff between $2 and $2.run
#   3a.   gives errors if != (and tells you to do copy startup-confg to run..
#   3b.   copies run to start on success
# 
#   $1 = rtr
#   $2 = confg file on tftp server
#   returns 0 on success 1 on failure.
#-----------------------------------------------------
safeConfNet(){
  echo "running conf net"
  confNet $1 $2
  ERR=$?
  if [ $ERR -ne 0 ]; then  return $ERR; fi

echo "running wr net for testing new confg"
  wrNet $1 $2.run
  if [ $? -ne 0 ]; then
    error
    echo "Cannot verify new running configuration on router"
    echo "You should verify by hand or 'copy startup-config running-confg'"
    return $EWRNET
  fi

diffConfgs $2 $2.run
  if [ $? -ne 0 ]; then return $?; fi

echo "copying running-confg to startup-confg"
  wrMem $1
  if [ $? -ne 0 ]; then
    error
    echo "copy running-confg startup-confg  failed"
    echo "you should verify the router configuration by hand"
    echo "and isue 'copy running-confg startup-confg' by hand"
    return $EWRMEM
  fi
}

#------------------------------------------------------
#   $1 = vendor name
#   $2 = HW info
#   $3 = Major Version
#   return 0 if MIBS can be set; otherwise, EMIBS
#------------------------------------------------------
# setupMIBS...
setupMIBS(){
  if [ "X$1" = "XCisco" ]; then
    MIB_RUN2START="${CISCO_MIB_RUN2START}"
    MIB_RUN2TFTP="${CISCO_MIB_RUN2TFTP}"
    if [ "X$3" = "X12" ]; then
      MIB_TFTP2RUN="${CISCO_MIB_TFTP2RUN_NEW}"
    else
      MIB_TFTP2RUN="${CISCO_MIB_TFTP2RUN_OLD}"
    fi
  else
    return $EMIBS
  fi

return 0
}

#------------------------------------------------------
#   $1 = router name
#   return $EERROR if router vendor or hw cannot be determined
#------------------------------------------------------
setupRtr(){
  echo "Getting vendor and hardware information..."
  VENDOR=`$SNMPGET $1 $COMMUNITY system.sysDescr.0 | head -1 | $AWK '{print $3}'`
  if [ $? -ne 0 ]; then  return $EERROR; fi
  sleep 1
  HW=`$SNMPGET $1 $COMMUNITY system.sysDescr.0 | head -2 | tail -1 | $AWK '{print $3}'`
  if [ $? -ne 0 ]; then  return $EERROR; fi
  VER=`$SNMPGET $1 $COMMUNITY system.sysDescr.0 | head -2 | tail -1 | $AWK '{print $7}' | cut -c1-2`

echo "Hmmm, looks like a  $VENDOR, $HW, running version $VER"
  return 0;
}

#------------------------------------------------------
# A Lot of flow control.  called directly from
# main passing $*
#------------------------------------------------------
doitAll(){
  if [ $# -le 0 ]; then
    usage;
  fi

doesRtrExist $1
  if [ $? -ne 0 ]; then
    fatalerror $EUSAGE "router:$1 does not exist."
  fi

# setup the mibs (i.e., perform any runtime required setup)
  # sets globals VENDOR and HW.
  if [ "X$opt_n" = "X" ]; then
    setupRtr $1
    if [ $? -ne 0 ]; then
      fatalerror $EERROR "Cannot determine vendor/os of $1"
    fi

setupMIBS $VENDOR $HW $VER
    if [ $? -ne 0 ]; then
      fatalerror $EMIBS "Cannot find mibs for $VENDOR:$HW"
    fi
  fi

# do a 'write net'
  if [ $opt_w -ne 0 ]; then  
    if [ "X$2" = "X" ]; then
      wrNet $1 $1-confg.run
    else
      wrNet $1 $2
    fi
  fi

if [ $opt_s -ne 0 ]; then  wrMem $1; fi
  if [ "X$opt_n" != "X" ]; then stripFile $opt_n; fi
  if [ $opt_c -ne 0 ]; then
    doesConfgExist $1-confg 
    if [ $? -eq 0 ]; then getConfg $1; fi
    # confg should exist now, so test and exit on failure
    doesConfgExist $1-confg 
    if [ $? -eq 0 ]; then
      fatalerror $ENOCONFG "Config: $1-confg does not exist"
    fi

if [ "X$2" = "X" ]; then
      safeConfNet $1 $1-confg  
      ERR=$?
      if [ $ERR -ne 0 ]; then
        if [ $ERR -eq $ENOFILE ]; then 
          fatalerror $ERR "Cannot find config file"
        fi
        fatalerror $ERR "Cannot perform safe conf net"
      fi
    else
      ERR=$?
      safeConfNet $1 $2  
      if [ $ERR -ne 0 ]; then
        if [ $ERR -eq $ENOFILE ]; then 
          fatalerror $ERR "Cannot find config file"
        fi
        fatalerror $ERR "Cannot perform safe conf net"
      fi
    fi
  fi

# diff a config with the router
  if [ $opt_d -ne 0 ]; then
    doesConfgExist $1-confg 
    if [ $? -eq 0 ]; then getConfg $1; fi
    # confg should exist now, so test and exit on failure
    doesConfgExist $1-confg 
    if [ $? -eq 0 ]; then
      fatalerror $ENOCONFG "Config: $1-confg does not exist"
    fi
    wrNet $1 $1-confg.run

diffConfgs $1-confg $1-confg.run
    if [ $? -eq 0 ]; then
      echo "GOOD NEWS, Stripped configs are identical."
      exit 0;
    else
      fatalerror $? "stripped configs differ!"
    fi
  fi

# do a confNet (not safe) with the acl
  if [ "X$opt_a" != "X" ]; then
    loadACL $1 $opt_a
    if [ $? -ne 0 ]; then error; fi
  fi

}

#------------------------------------------------------
# M A I N   S T U F F
#  options are:
#   -r rtr-name
#   -w copy running config to tftp server (wr net)
#   -c copy config on tftp server to running config (conf net)
#   -s copy running-config startup-confg
#   -n file to make naked (stripped)
#   -f file use file as config file rather than rtr-name-confg
#   
#   -r and one of (wcs) is required if -n is not given
#------------------------------------------------------

# make temp files writable by others
umask 002
PWD0=$PWD
cd $TFTPDIR || (error; echo "Cannot cd $TFTPDIR"; exit $EERROR);

while getopts hdwcsr:n:f:a: opt
do
  case "$opt" in
          w)    OPT=`$EXPR $OPT + 1`;  opt_w=1;;
          c)    OPT=`$EXPR $OPT + 2`;  opt_c=2;;
          s)    OPT=`$EXPR $OPT + 4`;  opt_s=4;;
          r)    OPT=`$EXPR $OPT + 8`;  opt_r=$OPTARG;;
      n)    OPT=`$EXPR $OPT + 16`; opt_n=$OPTARG;;
      f)    OPT=`$EXPR $OPT + 32`; opt_f=$OPTARG;;
          d)    OPT=`$EXPR $OPT + 64`; opt_d=64;;
      a)    OPT=`$EXPR $OPT + 128`; opt_a=$OPTARG;;
          [?])  usage ; exit 1;;
  esac 
done

#  9: -w -r rtr
# 10: -c -r rtr
# 12: -s -r rtr
# 16: -n file
# 41: -w -r rtr -f file 
# 42: -c -r rtr -f file
# 44: -s -r rtr -f file
# 72: -d -r rtr
# 136: -a acl_file -r rtr
# I used the bits so summing them in is always unique
if [ $OPT -eq  9 -o $OPT -eq 10 -o $OPT -eq 12 -o $OPT -eq 16 -o $OPT -eq 41 -o $OPT -eq 42 -o $OPT -eq 44 -o $OPT -eq 72 -o $OPT -eq 136 ]; then
  :
else
  usage
fi

doitAll $opt_r $opt_f
# don't clean if we are writing a confg.
if [ $opt_w -eq 1 -o "X$opt_n" != "X" ]; then 
  :
else
  clean
fi

Labels: cheatsheet, linux.