I have a function to retrieve some SNMP data and print it.
The function slows down for each record printed and with a lot of routers and walks, it's very very slow. I figure it will be much more efficient to fetch all the data and print it once done, but I'm wondering how to achieve this in bash.
My question is simple, how can I make this function faster?
function primary_cpe_hsrp_snmp(){
echo
read -p "Do you REALLY want to check HSRP info from the CPEs? [y/N] " -n 2 -r
if [[ $REPLY =~ ^[Yy]$ ]]; then
snmp_community="<somecommunitystring>"
for cpe in "${pricpes[@]}"
do
hostname=$(snmpget -v 2c -c $snmp_community $cpe sysName.0 | sed -e 's/^SNMPv2-MIB::sysName.0 = STRING: //g')
printf "\nSome HSRP information for $hostname on IP $cpe\n"
printf "%-20s %-20s %-20s %-12s \n" " ___________________________________________________________________________________"
printf "| %-20s | %-20s | %-20s | %-12s | \n" "Virtual IP" "Active IP" "Standby IP" "State"
printf "| %-20s | %-20s | %-20s | %-12s | \n" "____________________" "____________________" "____________________" "____________"
while read -r line; do
ifHsrpState[$i]=$(snmpwalk -v2c -c $snmp_community $cpe 1.3.6.1.4.1.9.9.106.1.2.1.1.15.$line | awk -F ": " '{print $2}')
ifHsrpActiv[$i]=$(snmpwalk -v2c -c $snmp_community $cpe 1.3.6.1.4.1.9.9.106.1.2.1.1.13.$line | awk -F ": " '{print $2}')
ifHsrpStand[$i]=$(snmpwalk -v2c -c $snmp_community $cpe 1.3.6.1.4.1.9.9.106.1.2.1.1.14.$line | awk -F ": " '{print $2}')
ifHsrpVirip[$i]=$(snmpwalk -v2c -c $snmp_community $cpe 1.3.6.1.4.1.9.9.106.1.2.1.1.11.$line | awk -F ": " '{print $2}')
printf "| %-20s | %-20s | %-20s | %-12s |\n" "${ifHsrpVirip[$i]}" "${ifHsrpActiv[$i]}" "${ifHsrpStand[$i]}" "${ifHsrpState[$i]}"
done <<< "$(snmpwalk -v 2c -c $snmp_community $cpe CISCO-HSRP-MIB::cHsrpGrpAuth | sed 's/CISCO-HSRP-MIB::cHsrpGrpAuth*\.//' | awk -F" " '{print $1}')"
printf "%-20s %-20s %-20s %-12s \n" " ___________________________________________________________________________________"
done
else
:
fi
}
Here's an attempt at refactoring the code.
The idea is to reduce the number of snmpwalk
calls to one per IP, then use awk
to parse and extract the data of interest from the output.
The shell still does the final formatting (instead of awk
) because you were using bash arrays and I don't know what you want to do with the data afterwards.
The new primary_cpe_hsrp_snmp
function now takes at least two arguments: The SNMP community and the IP address(es) of the CISCO hardware to connect to. I also moved the user interaction outside of the function (it felt wrong to keep it there).
edit: modified the OFS
of awk
and the IFS
of read
from white-space to comma.
#!/bin/bash
# function primary_cpe_hsrp_snmp SNMP_COMMINUTY [IPADDRESS ...]
#
primary_cpe_hsrp_snmp() {
local snmp_community="$1"
local cpe
for cpe in "${@:2}"
do
local hostname="$(snmpget -v 2c -c "$snmp_community" "$cpe" sysName.0 | awk -F ': ' '{print $2}')"
printf 'Some HSRP information for %s on IP %s\n' "$hostname" "$cpe"
printf ' ——————————————————————————————————————————————————————————————————————————————————— \n'
printf '| %-20s | %-20s | %-20s | %-12s |\n' "Virtual IP" "Active IP" "Standby IP" "State"
printf '| ———————————————————— | ———————————————————— | ———————————————————— | ———————————— |\n'
local ifHsrpVirip ifHsrpActiv ifHsrpStand ifHsrpState
while IFS=',' read ifHsrpVirip ifHsrpActiv ifHsrpStand ifHsrpState
do
printf '| %-20s | %-20s | %-20s | %-12s |\n' "$ifHsrpVirip" "$ifHsrpActiv" "$ifHsrpStand" "$ifHsrpState"
done < <(
snmpwalk -v 2c -c "$snmp_community" "$cpe" .1.3.6.1.4.1.9.9.106.1.2.1.1 |
awk -v OFS=',' '
/cHsrpGrpVirtualIpAddr/ {
key = substr($1,index($1,"."))
cHsrpGrpVirtualIpAddr[key] = $NF
next
}
/cHsrpGrpActiveRouter/ {
key = substr($1,index($1,"."))
cHsrpGrpActiveRouter[key] = $NF
next
}
/cHsrpGrpStandbyRouter/ {
key = substr($1,index($1,"."))
cHsrpGrpStandbyRouter[key] = $NF
next
}
/cHsrpGrpStandbyState/ {
key = substr($1,index($1,"."))
cHsrpGrpStandbyState[key] = $NF
next
}
END {
for (key in cHsrpGrpVirtualIpAddr)
print cHsrpGrpVirtualIpAddr[key], cHsrpGrpActiveRouter[key], cHsrpGrpStandbyRouter[key], cHsrpGrpStandbyState[key]
}
'
)
done
}
################################################################################
pricpes=(
someIP_1
someIP_2
...
)
read -r -p "Do you REALLY want to check HSRP info from the CPEs? [y/N] " -n 1
echo
if [[ $REPLY =~ ^[Yy]$ ]]
then
primary_cpe_hsrp_snmp '<somecommunitystring>' "${pricpes[@]}"
fi
remark: you may want to try snmpbulkwalk
instead of snmpwalk
; it might speed up the requests.
In your code, for each pair of SNMP interface ID
.
HSRP group number
(represented by X.Y
in the table), you were querying the following OIDs/MIBs:
OID | MIB |
---|---|
1.3.6.1.4.1.9.9.106.1.2.1.1.11.X.Y | CISCO-HSRP-MIB::cHsrpGrpVirtualIpAddr.X.Y |
1.3.6.1.4.1.9.9.106.1.2.1.1.13.X.Y | CISCO-HSRP-MIB::cHsrpGrpActiveRouter.X.Y |
1.3.6.1.4.1.9.9.106.1.2.1.1.14.X.Y | CISCO-HSRP-MIB::cHsrpGrpStandbyRouter.X.Y |
1.3.6.1.4.1.9.9.106.1.2.1.1.15.X.Y | CISCO-HSRP-MIB::cHsrpGrpStandbyState.X.Y |
I have to say, firing so many forks and initiating so many TCP connections will surely make the code inefficient.
In fact, you can get the HSRP data for all pairs of SNMP interface ID
.
HSRP group number
with a single snmpwalk
call by requesting the 1.3.6.1.4.1.9.9.106.1.2.1.1
OID (aka CISCO-HSRP-MIB::cHsrpGrpEntry
). The downside is that the output won't be ordered by SNMP interface ID
.
HSRP group number
and that you'll get superfluous information, so you'll have to filter/select/order the data that you need by post-processing the output (that's what the awk
does in my solution).
Now, let's take a line "of interest" from the output of snmpwalk … 1.3.6.1.4.1.9.9.106.1.2.1.1
for explaining the awk
, for example:
CISCO-HSRP-MIB::cHsrpGrpVirtualIpAddr.2.1 = IpAddress: 2.3.4.5
In the awk
code, this line satisfies the condition /cHsrpGrpVirtualIpAddr/
(which is a regex match), so its corresponding block of code is executed:
/cHsrpGrpVirtualIpAddr/ {
key = substr($1,index($1,"."))
cHsrpGrpVirtualIpAddr[key] = $NF
next
}
The key = substr($1,index($1,"."))
extracts .2.1
from the first field and saves it as key
The cHsrpGrpVirtualIpAddr[key] = $NF
stores the last field ($NF
), whose value is 2.3.4.5
, in the associative array cHsrpGrpVirtualIpAddr
for the key .2.1
.
The next
means that we're done with this line so we can go on with the next one.
The others condition { ... }
are similar except the END
one which is executed after all the input lines were processed. In it I loop over all the defined keys and output the values stored in the arrays in a format that can be easily read
with the shell.