Dieser Beitrag beschreibt ein Shell Script um die Interface Auslastung eines Routers anzuzeigen. Die Abfragen erfolgen per SNMP Protokoll. Bezüglich der Zeitintervalle ist zu beachten, dass nicht alle Router Interface Daten im Sekunden Takt liefern (z.B. bei Cisco ASR Routern werden nur alle 10 Sekunden die Interface Counter aktualisiert).

Das Script ifload nutzt graph.fnc für die Grafische ASCII Darstellung. Beide Dateien müssen sich im gleichen Verzeichniss befinden. Ansonsten müssen die SNMP Tools (snmpget, snmpwalk) installiert sein. Alle Scripte sind in meinem Home Directory im Subfolder „snmp“ abgelegt, von wo ich ifload starte.

Usage of ifload:

  ifload <HOSTNAME> [INTERFACE] [-i|-h|-g|-c <community>]

  -i <nnn> : Interval in Seconds, default = 10
  -h       : this Help message
  -g       : enable Graphic (ASCII) view
  -d       : enable Debug

  -c <community> : set community String, default: public

 

Ein Beispiel:

ifload - Mon Jan 13 12:19:46 CET 2014  Start: 12:18:27  Interval: 63  Range: 60 s
Device Name: test_router_de
Interface: Gi0/1.101  Descr.: *** Primary VPN WAN Link ***
Bandwidth: 300M
IOS:  7300 Software (C7300-JS-M), Version XX.X(XX)XXX, RELEASE SOFTWARE (fc1)

------------------------------------------------------------------------------------------
Date       Data (Bps)    Error         Discard       Load %        CPU Load %
Time       In     Out    In     Out    In     Out    In     Out    5sec   1min   5min
------------------------------------------------------------------------------------------
18:19:46   28M    81M    0      0      0      0      9      27     48     42     39

INPUT:

 61M2 |                   O                                         | 61M2
      |                   O                                    .    |
      |                   O                                    O    |
      |                   O               o                    O    |
      |                   O               O                    O    |
      |           o       O               O                   oOO   |
      |           O  o   oO   O           O   .           . ..OOO   |
 27M6 | ----------O--O---OO--OO.-O.o-O.o--O--OO.--O.--oo-OOOOOOOO.o | 27M6
 19M4 | .     ...oO..OO .OOOoOOOoOOO.OOOooOoOOOOoOOOoOOO.OOOOOOOOOO | 19M4

OUTPUT:

 92M5 |                      O                                      | 92M5
      |                      O                                  O   |
      |             .        O.                                 O . |
      |             O . O    OO       o                       ooO O |
      | .  .        OoO O  o OO.  . o O               oO     oOOOOO |
 68M8 | OooO.-----OOOOO-O.oO-OOO..OOOOO---------------OO.---.OOOOOO | 68M8
      | OOOOO Oo OOOOOOoOOOOOOOOOOOOOOOOoO      O    oOOO ..OOOOOOO |
      | OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO.O.  OOO   OOOOOOOOOOOOOO |
 51M8 | OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO. OOOO.OOOOOOOOOOOOOOO | 51M8

 

Quelltext zum Script ifload

#!/bin/bash
#
# Klaus Scheffer

DEBUG=0
HOST=$1
IFNAME=""
INTERVAL=10
GRAPH=0
STARTTIME=$(date "+%H:%M:%S")
SNMPBINPATH=/usr/bin
AWKBIN=/bin/awk
COMMUNITY=public

ifloadhelp() {
cat <<EOF
Usage of $(basename $0):

  $(basename $0) <HOSTNAME> [INTERFACE] [-i|-h|-g|-c <community>]

  -i <nnn> : Interval in Seconds, default = $INTERVAL
  -h       : this Help message
  -g       : enable Graphic (ASCII) view
  -d       : enable Debug

  -c <community> : set community String, default: public

EOF

exit 0
}

# load my presettings from homedirectory
[ -e ~/.ifload.conf ] && . ~/.ifload.conf

if [ "$HOST" == "" ]; then
  echo -e "Error: Wrong usage!\n\n"
  ifloadhelp
  exit 1
fi
shift
[ $# -gt 0 ] && IFNAME=$1

while [ $# -gt 0 ]; do
  case $1 in
    -i)
      INTERVAL=$2
      shift
    ;;
    -h)
      ifloadhelp
    ;;
    -g)
      GRAPH=1
    ;;
    -d)
      DEBUG=1
    ;;
    -c)
      COMMUNITY=$2
      shift
    ;;
  esac
  shift
done

#echo "$HOST $IFNAME $INTERVAL $GRAPH"
#exit 0

if [ $GRAPH -gt 0 ]; then
  . ./graph.fnc
  DATA_IN=$(for i in $(seq 60); do echo "0"; done)
  DATA_OUT=$(for i in $(seq 60); do echo "0"; done)
  i=0
fi

DEVNAME=$($SNMPBINPATH/snmpget -v 2c -O v -c $COMMUNITY $HOST SNMPv2-MIB::sysName.0 | awk '{print $2}' | awk -F. '{print $1}')
SYSDESCR=$($SNMPBINPATH/snmpget -v 2c -O v -c $COMMUNITY $HOST SNMPv2-MIB::sysDescr.0 | head -n1 | cut -d',' -f2-60)
# echo "$SNMPBINPATH/snmpget -v 2c -O v -c $COMMUNITY $HOST SNMPv2-MIB::sysName.0"
if [ -z $DEVNAME ]; then
  echo "Error: Device unreacheable or without Name!"
  exit 1
#else
[ $DEBUG ] && echo "Device Name: $DEVNAME"
fi

if [ "${IFNAME}" == "" ]; then
  echo "Choose one of the following Interfaces:"
  $SNMPBINPATH/snmpwalk -v 2c -c $COMMUNITY $HOST IF-MIB::ifName | awk '{ print $1 " : " $4 }'
  exit 0
fi

CHKNUM() {
  if [[ $1 = *[[:digit:]]* ]]; then
    echo $1
  else
    echo "0"
  fi
}

# $1 = N0  (T = now)
# $2 = N1  (T = last intervall)
# $3 = COUNTERLENGTH (Bits)
CALCDIFF() {
  N0=$(CHKNUM $1)
  N1=$(CHKNUM $2)
  COUNTERLENGTH=$3
  if [ $N0 -ge $N1 ]; then
    let D=N0-N1
  else
    D=$(bc <<< "(2^$COUNTERLENGTH)-$N1+$N0")
  fi
  echo $D
}

GETCOUNTER() {
  local ct=$(echo $COUNTER | cut -d" " -f $1)
  if [ "$ct" == 'No' ]; then
    echo 0
  else
    echo $ct | sed 's/[A-Za-z: ]//g'
  fi
}

AUTORANGE() {
  local f=$1
  local ar=${f/\.*}
  local div=1
  [ $ar -ge 1 ] && div=1 && x=" "
  [ $ar -ge 1000 ] && div=1000 && x="k"
  [ $ar -ge 1000000 ] && div=1000000 && x="M"
  [ $ar -ge 1000000000 ] && div=1000000000 && x="G"
  let arr=ar/div
  echo "$arr$x"
}

GREP_IFNAME="${IFNAME}$"
#echo $GREP_IFNAME
#echo "snmpwalk -v 2c -c $COMMUNITY $HOST IF-MIB::ifName | grep \"$GREP_IFNAME\""
IFINDEX=$($SNMPBINPATH/snmpwalk -v 2c -c $COMMUNITY $HOST IF-MIB::ifName | grep "$GREP_IFNAME" | awk '{ print $1 }' | awk -F"." '{ print $2 }')
[ $DEBUG -gt 0 ] && echo "Intercae $IFNAME = Interface ID $IFINDEX"
if [ "$IFINDEX" == "" ]; then
  echo "Error: ${IFNAME} unkown on this Device!"
  exit 1
fi

[ $DEBUG -gt 0 ] && echo "IFSPEED=$($SNMPBINPATH/snmpget -v 2c -c $COMMUNITY $HOST IF-MIB::ifSpeed.$IFINDEX)"
IFSPEED=$($SNMPBINPATH/snmpget -v 2c -c $COMMUNITY $HOST IF-MIB::ifSpeed.$IFINDEX | awk '{ print $4 }')
IFALIAS=$($SNMPBINPATH/snmpget -v 2c -c $COMMUNITY $HOST IF-MIB::ifAlias.$IFINDEX | awk '{print substr($0,index($0,$4))}')

DIV=1
[ $IFSPEED -gt 1000000 ] && DIV=1000
[ $IFSPEED -gt 10000000 ] && DIV=1000000
[ $IFSPEED -gt 100000000 ] && DIV=1000000

case $DIV in
  1)
    OF=Bps
  ;;
  1000)
    OF=kBps
  ;;
  1000000)
    OF=MBps
  ;;
  1000000000)
    OF=GBps
  ;;
  *)
    OF=Bps
  ;;
esac

# echo "IFSPEED($IFSPEED)=$(AUTORANGE $IFSPEED)"

N=0
D_TS=0
D_IN=0
D_OUT=0
D_EIN=0
D_EOUT=0
D_DISCIN=0
D_DISCOUT=0
A_TS=0
A_IN=0
A_OUT=0
A_EIN=0
A_EOUT=0
A_DISCIN=0
A_DISCOUT=0

# IF-MIB::ifHCInOctets

if [ $DIV -ge 1000000 ]; then
  MIBLIST="SNMPv2-MIB::sysUpTime.0 IF-MIB::ifHCInOctets.$IFINDEX IF-MIB::ifHCOutOctets.$IFINDEX IF-MIB::ifInErrors.$IFINDEX IF-MIB::ifOutErrors.$IFINDEX IF-MIB::ifInDiscards.$IFINDEX IF-MIB::ifOutDiscards.$IFINDEX SNMPv2-SMI::enterprises.9.2.1.56.0 SNMPv2-SMI::enterprises.9.2.1.57.0 SNMPv2-SMI::enterprises.9.2.1.58.0"
else
  MIBLIST="SNMPv2-MIB::sysUpTime.0 IF-MIB::ifInOctets.$IFINDEX IF-MIB::ifOutOctets.$IFINDEX IF-MIB::ifInErrors.$IFINDEX IF-MIB::ifOutErrors.$IFINDEX IF-MIB::ifInDiscards.$IFINDEX IF-MIB::ifOutDiscards.$IFINDEX SNMPv2-SMI::enterprises.9.2.1.56.0 SNMPv2-SMI::enterprises.9.2.1.57.0 SNMPv2-SMI::enterprises.9.2.1.58.0"
fi

COUNTER=$($SNMPBINPATH/snmpget -v 2c -c $COMMUNITY $HOST $MIBLIST | sed 's/://g' | awk '{ print $3 }')

clear
while [ 1 == 1 ]; do

  LOAD=$($SNMPBINPATH/snmpget -v 2c -c $COMMUNITY $HOST $MIBLIST | awk '{ print $4 }' | xargs)

  if [ $DEBUG -eq 1 ]; then
    echo "IFINDEX = $IFINDEX"
    echo "$SNMPBINPATH/snmpget -v 2c -c $COMMUNITY $HOST $MIBLIST"
    echo "n=$N load=$LOAD"
  else

  # echo "n=$N load=$LOAD"

  RANGE=

  TS=$(echo $LOAD | cut -d" " -f 1 | sed 's/[()]//g')
  IN=$(echo $LOAD | cut -d" " -f 2)
  OUT=$(echo $LOAD | cut -d" " -f 3)
  ERRIN=$(echo $LOAD | cut -d" " -f 4)
  ERROUT=$(echo $LOAD | cut -d" " -f 5)
  DISCIN=$(echo $LOAD | cut -d" " -f 6)
  DISCOUT=$(echo $LOAD | cut -d" " -f 7)
  CPU5=$(echo $LOAD | cut -d" " -f 8)
  CPU60=$(echo $LOAD | cut -d" " -f 9)
  CPU300=$(echo $LOAD | cut -d" " -f 10)

  let D_TS=TS-A_TS
  let RANGE=INTERVAL*60
  D_IN=$(CALCDIFF $IN $A_IN $(GETCOUNTER 2))
  D_OUT=$(CALCDIFF $OUT $A_OUT $(GETCOUNTER 3))
  D_EIN=$(CALCDIFF $ERRIN $A_EIN $(GETCOUNTER 4))
  D_EOUT=$(CALCDIFF $ERROUT $A_EOUT $(GETCOUNTER 5))
  D_DISCIN=$(CALCDIFF $DISCIN $A_DISCIN $(GETCOUNTER 6))
  D_DISCOUT=$(CALCDIFF $DISCOUT $A_DISCOUT $(GETCOUNTER 7))

  A_TS=$TS
  A_IN=$IN
  A_OUT=$OUT
  A_EIN=$ERRIN
  A_EOUT=$ERROUT
  A_DISCIN=$DISCIN
  A_DISCOUT=$DISCOUT

  # 1 Octett = 8 Bit
  #let BPS_IN=D_IN*8*100/D_TS
  #let BPS_OUT=D_OUT*8*100/D_TS
  #let BPS_EIN=D_EIN*8*100/D_TS
  #let BPS_EOUT=D_EOUT*8*100/D_TS

  BPS_IN=$(echo "$D_IN*8*100/$D_TS" | bc -l)
  BPS_OUT=$(echo "$D_OUT*8*100/$D_TS" | bc -l)
  BPS_EIN=$(echo "$D_EIN*8*100/$D_TS" | bc -l)
  BPS_EOUT=$(echo "$D_EOUT*8*100/$D_TS" | bc -l)

  IFLOAD_IN=$(echo "$BPS_IN*100/$IFSPEED" | bc -l)
  IFLOAD_OUT=$(echo "$BPS_OUT*100/$IFSPEED" | bc -l)

  if [ $GRAPH -eq 0 ]; then
    BPS_IN=$(AUTORANGE $BPS_IN)
    BPS_OUT=$(AUTORANGE $BPS_OUT)
    BPS_EIN=$(AUTORANGE $BPS_EIN)
    BPS_EOUT=$(AUTORANGE $BPS_EOUT)

    if [ $N -eq 0 ]; then
      echo -e "\e[H$(basename $0) - $(date)  Start: $STARTTIME  Interval: $N  Range: $RANGE s"
      echo "Device Name: $DEVNAME"
      echo "Interface: $IFNAME  Descr.: $IFALIAS"
      echo "Bandwidth: $(AUTORANGE $IFSPEED)"
      echo -e "IOS: $SYSDESCR\n"
      printf "%49s\n" "------------------------------------------------------------------------------------------"
      printf "%-10s %-13s %-13s %-13s %-13s %-13s\n" Date "Data (Bps)" Error Discard "Load %" "CPU Load %"
      printf "%-10s %-6s %-6s %-6s %-6s %-6s %-6s %-6s %-6s %-6s %-6s %-6s\n" Time In Out In Out In Out In Out 5sec 1min 5min
      printf "%49s\n" "------------------------------------------------------------------------------------------"
    else
      # echo "$(date "+%H:%M:%S") ($TS) In/Out=$BPS_IN/$BPS_OUT $OF  Err(In/Out)=$D_EIN/$D_EOUT  Load(In/Out)=$IFLOAD_IN/$IFLOAD_OUT%"
      # printf "%-10s %-6s %-6s %-6s %-6s %-6s %-6s %-6s %-6s %-6s %-6s %-6s\n" $(date "+%H:%M:%S") $BPS_IN $BPS_OUT $D_EIN $D_EOUT $D_DISCIN $D_DISCOUT $IFLOAD_IN $IFLOAD_OUT $CPU5 $CPU60 $CPU300
      printf "%-10s %-6s %-6s %-6s %-6s %-6s %-6s %-6s %-6s %-6s %-6s %-6s\n" $(date "+%H:%M:%S") $BPS_IN $BPS_OUT $D_EIN $D_EOUT $D_DISCIN $D_DISCOUT ${IFLOAD_IN/\.*} ${IFLOAD_OUT/\.*} $CPU5 $CPU60 $CPU300
    fi
  else

    DATA_IN=$(echo $DATA_IN $BPS_IN | cut -d' ' -f2-60)
    DATA_OUT=$(echo $DATA_OUT $BPS_OUT | cut -d' ' -f2-60)

    echo -e "\e[H$(basename $0) - $(date)  Start: $STARTTIME  Interval: $N  Range: $RANGE s"
    echo "Device Name: $DEVNAME"
    echo "Interface: $IFNAME  Descr.: $IFALIAS"
    echo "Bandwidth: $(AUTORANGE $IFSPEED)"
    echo -e "IOS: $SYSDESCR\n"
    printf "%49s\n" "------------------------------------------------------------------------------------------"
    printf "%-10s %-13s %-13s %-13s %-13s %-13s\n" Date "Data (Bps)" Error Discard "Load %" "CPU Load %"
    printf "%-10s %-6s %-6s %-6s %-6s %-6s %-6s %-6s %-6s %-6s %-6s %-6s\n" Time In Out In Out In Out In Out 5sec 1min 5min
    printf "%49s\n" "------------------------------------------------------------------------------------------"
    printf "%-10s %-6s %-6s %-6s %-6s %-6s %-6s %-6s %-6s %-6s %-6s %-6s\n" $(date "+%H:%M:%S") $(AUTORANGE $BPS_IN) $(AUTORANGE $BPS_OUT) $D_EIN $D_EOUT $D_DISCIN $D_DISCOUT ${IFLOAD_IN/\.*} ${IFLOAD_OUT/\.*} $CPU5 $CPU60 $CPU300

    echo -e "\nINPUT:\n"
    graph 9 $DATA_IN
    echo -e "\nOUTPUT:\n"
    graph 9 $DATA_OUT

  fi
  fi
  sleep $INTERVAL
  let N=N+1

  [ $N -gt 600 ] && exit 0

done

#
#end

 

Funktion für Ascii Grafik Ausgabe: graph.fnc (Original Source: Bashscripts.org, Ascii Graphing)

#!/bin/bash
## graph.fnc -- by Patsie
# Shell function that draws a graph with average line in ASCII
# The graph will have a width equal to the number of data points
# and a height equal to the first argument
# Usage: graph <height> <val1> [<val2> [<...>]]

function graph {
  height=$1; shift;

  echo "$@" | awk -v height=$height 'BEGIN { }
    ## convert big numbers to short kilo/mega/giga etc numbers
    function pow1k(bignum) {
      metric=1;
      num=bignum;

      # devide by 1000 until we have got a small enough number
      while (num>=1000) { metric++; num/=1000; }
      num=int(num);

      # get SI prefix (kilo, mega, giga, tera, peta, exa, zotta, yotta)
      si=substr(" KMGTPEZY", metric, 1);

      # get a division remainder to total our number of characters to a maximum of 4
      division=substr(bignum, length(num)+1, 3-length(num));

      # right align the output
      str=sprintf("%s%c%s", num, si, division);
      return(sprintf("% 4s", str));
    }
  {
    # get smallest, largest and total of all numbers
    min=max=tot=$1;
    for (x=2; x<=NF; x++) { tot+=$x; if ($x>max) max=$x; if ($x<min) min=$x; }

    # the difference between largest and smallest number is out working area
    diff=max-min;
    if (diff==0) diff=1;                                # all numbers are the same?!
    # some fancy math to get the average of all numbers
    avg=(tot/NF);
    avgfull=int(((avg-min)*height/diff));               # average full
    avghalf=int(((avg-min)*height/diff)%1+0.5);         # average half

    # fill arrays bars
    for (x=1; x<=NF; x++) {
      v=$x-min;                                         # our value
      i=0;array[x]="";                                  # blank the array
      full=int((v*height/diff));                        # how many full?
      ttrd=int((v*height/diff)%1+0.333);                # 2/3rd full?
      otrd=int((v*height/diff)%1+0.666);                # 1/3rd full?
      while (i<full)   { array[x]=array[x]"O"; i++; }   # fill all fulls
      if    (ttrd>0)   { array[x]=array[x]"o"; i++; }   # fill 2/3rd
      else if (otrd>0) { array[x]=array[x]"."; i++; }   # or 1/3rd

      # average line or blank
      while (i<height) {                                # fill to the top
         if (i==avgfull)                                # with average line
           if (avghalf>0) array[x]=array[x]"-";
           else array[x]=array[x]"_";
         else array[x]=array[x]" ";                     # or mostly blanks
         i++;
      }
    }

    # display output
    for (y=height; y>0; y--) {
      line=""; num="    ";                              # blank line and number
      if (y==avgfull+1) { num=pow1k(int(avg)); }        # show average number
      if (y==height)    { num=pow1k(max); }             # show maximum number
      if (y==1)         { num=pow1k(min); }             # show minimum number

      for (x=1; x<=NF; x++)                             # do for all data values
        line=line""substr(array[x],y,1);                # create line from arrays
      printf(" %s | %s | %s\n", num, line, num);        # display 1 line
    }
  }'
}