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
}
}'
}