#!/bin/sh
#
# Unbound usage statistics ready for cacti.
#
# Version 0.7.0
#
# (C) 2009 terminus.  BSD Licensed.
#
######################################################################################
######################################################################################
#
# To use this script you should setup unbound-control and enable it in unbound.conf.
# 	server:		extended-statistics: yes
#			statistics-cumulative: no
#			statistics-interval: 0
# 	remote-control:	control-enable: yes
#
# If extended-statistics is not enabled you will get output with limited meaning.
#
# Script also requires write access to some directory where it can store it's output.
# I assume /usr/local/etc/unbound/statistics. Create it before run this script.
#
# Put script somewhere in filesystem (I assume /root/bin is good one).
#
# Run this script from crontab.
# */5 * * * * root /root/bin/unbound_cacti
#
# The following is the example of output generated by this script:
# cat /usr/local/etc/unbound/statistics/cache_hits
# thread0_num_queries:8 thread1_num_queries:0 thread2_num_queries:0 thread3_num_queries:2 
# total_num_queries:10 total_num_cachehits:7 total_requestlist_overwritten:0 
# total_requestlist_exceeded:0 total_requestlist_current_all:0 num_query_tcp:0 
# num_query_ipv6:0 unwanted_queries:0 unwanted_replies:0
#
# Put in snmpd.conf something like this to feed it to net-snmp:
# extend  .1.3.6.1.3.1983.1.1 cache_hits /bin/cat /usr/local/etc/unbound/statistics/cache_hits
# extend  .1.3.6.1.3.1983.1.2 memory_usage /bin/cat /usr/local/etc/unbound/statistics/memory_usage
# extend  .1.3.6.1.3.1983.1.3 queues_by_type /bin/cat /usr/local/etc/unbound/statistics/queues_by_type
# extend  .1.3.6.1.3.1983.1.4 answers_to_queries /bin/cat /usr/local/etc/unbound/statistics/answers_to_queries
# extend  .1.3.6.1.3.1983.1.5 histogram /bin/cat /usr/local/etc/unbound/statistics/histogram
# extend  .1.3.6.1.3.1983.1.6 queues_by_flags /bin/cat /usr/local/etc/unbound/statistics/queues_by_flags
# extend  .1.3.6.1.3.1983.1.7 requestlist /bin/cat /usr/local/etc/unbound/statistics/requestlist
#
# Don't forget to do snmpwalk -v2c -c youcommunity unbound.server.name .1.3.6.1.3.1983 and check for OID's
# By default you should find statistics under this OIDs:
# for cache_hits 		.1.3.6.1.3.1983.1.1.4.1.2.10.99.97.99.104.101.95.104.105.116.115.1
# for memory_usage 		.1.3.6.1.3.1983.1.2.4.1.2.12.109.101.109.111.114.121.95.117.115.97.103.101.1
# for queues_by_type 		.1.3.6.1.3.1983.1.3.4.1.2.14.113.117.101.117.101.115.95.98.121.95.116.121.112.101.1
# for answers_to_queries 	.1.3.6.1.3.1983.1.4.4.1.2.18.97.110.115.119.101.114.115.95.116.111.95.113.117.101.114.105.101.115.1
# for historgam			.1.3.6.1.3.1983.1.5.4.1.2.9.104.105.115.116.111.103.114.97.109.1
# for queues_by_flags		.1.3.6.1.3.1983.1.6.4.1.2.15.113.117.101.117.101.115.95.98.121.95.102.108.97.103.115.1
# for requestlist 		.1.3.6.1.3.1983.1.7.4.1.2.11.114.101.113.117.101.115.116.108.105.115.116.1
#
# Setup cacti and you a done.
#
######################################################################################
######################################################################################

ctrl=/usr/local/sbin/unbound-control
datadir=/usr/local/etc/unbound/statistics
pid=/usr/local/etc/unbound/unbound.pid

rawstats=`$ctrl stats`


#############
cache_hits=""

thread0_num_queries=0
thread1_num_queries=0
thread2_num_queries=0
thread3_num_queries=0
thread4_num_queries=0
thread5_num_queries=0
thread6_num_queries=0
thread7_num_queries=0
total_num_queries=0
total_num_cachehits=0
num_query_tcp=0
num_query_ipv6=0
unwanted_queries=0
unwanted_replies=0
#############

#############
requestlist=""

total_requestlist_overwritten=0
total_requestlist_exceeded=0
total_requestlist_current_all=0
total_requestlist_current_user=0
total_requestlist_avg=0
total_requestlist_max=0
#############

#############
memory_usage=""

mem_total_sbrk=0
mem_cache_rrset=0
mem_cache_message=0
mem_mod_iterator=0
mem_mod_validator=0
#############

#############
queues_by_type=""

num_query_type_A=0
num_query_type_AAAA=0
num_query_type_CNAME=0
num_query_type_MX=0
num_query_type_NS=0
num_query_type_PTR=0
num_query_type_SOA=0
num_query_type_SRV=0
num_query_type_TXT=0
num_query_type_other=0
#############

#############
answers_to_queries=""

num_answer_rcode_NOERROR=0
num_answer_rcode_NXDOMAIN=0
num_answer_rcode_SERVFAULT=0
num_answer_rcode_nodata=0
num_answer_secure=0
num_answer_bogus=0
num_rrset_bogus=0
#############

#############
histogram=""

histogram_000000_000000_to_000000_065536=0
histogram_000000_065536_to_000000_131072=0
histogram_000000_131072_to_000000_262144=0
histogram_000000_262144_to_000000_524288=0
histogram_000000_524288_to_000001_000000=0
histogram_000000_000001_to_000002_000000=0
histogram_000000_000002_to_000004_000000=0
histogram_000000_000004_to_000008_000000=0
histogram_000000_000008_to_524288_000000=0
#############

#############
queues_by_flags=""

num_query_flags_RD=0
num_query_flags_QR=0
num_query_flags_AA=0
num_query_flags_TC=0
num_query_flags_RA=0
num_query_flags_Z=0
num_query_flags_AD=0
num_query_flags_CD=0
num_query_flags_DO=0
num_query_edns_present=0
#############


for str in $rawstats
do

# for something like "num.query.type.A=3" we will get "num.query.type.A" here
whata=`echo $str | cut -f 1 -d "="`

# for something like "num.query.type.A=3" we will get "3" here
howmuch=`echo $str | cut -f 2 -d "="`


case $whata in

h*)
	case $whata in

	histogram.000000.000000.to.000000.000001 | histogram.000000.000001.to.000000.000002 | histogram.000000.000002.to.000000.000004 |  histogram.000000.000004.to.000000.000008 | histogram.000000.000008.to.000000.000016 | histogram.000000.000016.to.000000.000032 | histogram.000000.000032.to.000000.000064 | histogram.000000.000064.to.000000.000128 | histogram.000000.000128.to.000000.000256 | histogram.000000.000256.to.000000.000512 | histogram.000000.000512.to.000000.001024 | histogram.000000.001024.to.000000.002048 | histogram.000000.002048.to.000000.004096 | histogram.000000.004096.to.000000.008192 | histogram.000000.008192.to.000000.016384 | histogram.000000.016384.to.000000.032768 | histogram.000000.032768.to.000000.065536)
	histogram_000000_000000_to_000000_065536=$(($histogram_000000_000000_to_000000_065536+$howmuch))
	;;

	histogram.000000.065536.to.000000.131072)
	histogram_000000_065536_to_000000_131072=$howmuch
	;;

	histogram.000000.131072.to.000000.262144)
	histogram_000000_131072_to_000000_262144=$howmuch
	;;

	histogram.000000.262144.to.000000.524288)
	histogram_000000_262144_to_000000_524288=$howmuch
	;;

	histogram.000000.524288.to.000001.000000)
	histogram_000000_524288_to_000001_000000=$howmuch
	;;

	histogram.000000.000001.to.000002.000000)
	histogram_000000_000001_to_000002_000000=$howmuch
	;;

	histogram.000000.000002.to.000004.000000)
	histogram_000000_000002_to_000004_000000=$howmuch
	;;

	histogram.000000.000004.to.000008.000000)
	histogram_000000_000004_to_000008_000000=$howmuch
	;;

	histogram.000000.000004.to.000008.000000)
	histogram_000000_000004_to_000008_000000=$howmuch
	;;

	histogram.000008.000000.to.000016.000000 | histogram.000016.000000.to.000032.000000 | histogram.000032.000000.to.000064.000000 | histogram.000064.000000.to.000128.000000 | histogram.000128.000000.to.000256.000000 | histogram.000256.000000.to.000512.000000 | histogram.000512.000000.to.001024.000000 | histogram.001024.000000.to.002048.000000 | histogram.002048.000000.to.004096.000000 | histogram.004096.000000.to.008192.000000 | histogram.008192.000000.to.016384.000000 | histogram.016384.000000.to.032768.000000 | histogram.032768.000000.to.065536.000000 | histogram.065536.000000.to.131072.000000 | histogram.131072.000000.to.262144.000000 | histogram.262144.000000.to.524288.000000)
	histogram_000000_000008_to_524288_000000=$(($histogram_000000_000008_to_524288_000000+$howmuch))
	;;

	*)
	;;
	esac

;;

m*)
	case $whata in

	mem.total.sbrk)
	mem_total_sbrk=$howmuch
	;;

	mem.cache.rrset)
	mem_cache_rrset=$howmuch
	;;

	mem.cache.message)
	mem_cache_message=$howmuch
	;;

	mem.mod.iterator)
	mem_mod_iterator=$howmuch
	;;

	mem.mod.validator)
	mem_mod_validator=$howmuch
	;;

	*)
	;;
	esac

;;


n*)
	case $whata in

	num.query.type.A)
	num_query_type_A=$howmuch
	;;

	num.query.type.AAAA)
	num_query_type_AAAA=$howmuch
	;;

	num.query.type.CNAME)
	num_query_type_CNAME=$howmuch
	;;

	num.query.type.MX)
	num_query_type_MX=$howmuch
	;;

	num.query.type.NS)
	num_query_type_NS=$howmuch
	;;

	num.query.type.PTR)
	num_query_type_PTR=$howmuch
	;;

	num.query.type.SOA)
	num_query_type_SOA=$howmuch
	;;

	num.query.type.SRV)
	num_query_type_SRV=$howmuch
	;;

	num.query.type.TXT)
	num_query_type_TXT=$howmuch
	;;

	num.query.type.other)
	num_query_type_other=$howmuch
	;;

	num.answer.rcode.NOERROR)
	num_answer_rcode_NOERROR=$howmuch
	;;

	num.answer.rcode.NXDOMAIN)
	num_answer_rcode_NXDOMAIN=$howmuch
	;;

	num.answer.rcode.SERVFAULT)
	num_answer_rcode_SERVFAULT=$howmuch
	;;

	num.answer.rcode.nodata)
	num_answer_rcode_nodata=$howmuch
	;;

	num.answer.secure)
	num_answer_secure=$howmuch
	;;

	num.answer.bogus)
	num_answer_bogus=$howmuch
	;;

	num.rrset.bogus)
	num_rrset_bogus=$howmuch
	;;

	num.query.tcp)
	num_query_tcp=$howmuch
	;;

	num.query.ipv6)
	num_query_ipv6=$howmuch
	;;

	num.query.flags.RD)
	num_query_flags_RD=$howmuch
	;;

	num.query.flags.QR)
	num_query_flags_QR=$howmuch
	;;

	num.query.flags.AA)
	num_query_flags_AA=$howmuch
	;;

	num.query.flags.TC)
	num_query_flags_TC=$howmuch
	;;

	num.query.flags.RA)
	num_query_flags_RA=$howmuch
	;;

	num.query.flags.Z)
	num_query_flags_Z=$howmuch
	;;

	num.query.flags.AD)
	num_query_flags_AD=$howmuch
	;;

	num.query.flags.CD)
	num_query_flags_CD=$howmuch
	;;

	num.query.edns.DO)
	num_query_edns_DO=$howmuch
	;;

	num.query.edns.present)
	num_query_edns_present=$howmuch
	;;

	*)
	;;
	esac

;;


t*)
	case $whata in

	thread0.num.queries)
	thread0_num_queries=$howmuch
	cache_hits=$cache_hits"thread0_num_queries:"$thread0_num_queries" "
	;;

	thread1.num.queries)
	thread1_num_queries=$howmuch
	cache_hits=$cache_hits"thread1_num_queries:"$thread1_num_queries" "
	;;

	thread2.num.queries)
	thread2_num_queries=$howmuch
	cache_hits=$cache_hits"thread2_num_queries:"$thread2_num_queries" "
	;;

	thread3.num.queries)
	thread3_num_queries=$howmuch
	cache_hits=$cache_hits"thread3_num_queries:"$thread3_num_queries" "
	;;

	thread4.num.queries)
	thread4_num_queries=$howmuch
	cache_hits=$cache_hits"thread4_num_queries:"$thread4_num_queries" "
	;;

	thread5.num.queries)
	thread5_num_queries=$howmuch
	cache_hits=$cache_hits"thread5_num_queries:"$thread5_num_queries" "
	;;

	thread6.num.queries)
	thread6_num_queries=$howmuch
	cache_hits=$cache_hits"thread6_num_queries:"$thread6_num_queries" "
	;;

	thread7.num.queries)
	thread7_num_queries=$howmuch
	cache_hits=$cache_hits"thread7_num_queries:"$thread7_num_queries" "
	;;

	total.num.queries)
	total_num_queries=$howmuch
	;;

	total.num.cachehits)
	total_num_cachehits=$howmuch
	;;

	total.requestlist.overwritten)
	total_requestlist_overwritten=$howmuch
	;;

	total.requestlist.exceeded)
	total_requestlist_exceeded=$howmuch
	;;

	total.requestlist.current.all)
	total_requestlist_current_all=$howmuch
	;;

	total.requestlist.current.user)
	total_requestlist_current_user=$howmuch
	;;

	total.requestlist.avg)
	total_requestlist_avg=$howmuch
	;;

	total.requestlist.max)
	total_requestlist_max=$howmuch
	;;

	*)
	;;
	esac

;;


u*)
	case $whata in

	unwanted.queries)
	unwanted_queries=$howmuch
	;;

	unwanted.replies)
	unwanted_replies=$howmuch
	;;

	*)
	;;
	esac

;;


*)
;;
esac


done

######

cache_hits=$cache_hits\
"total_num_queries:"$total_num_queries\
" total_num_cachehits:"$total_num_cachehits\
" num_query_tcp:"$num_query_tcp\
" num_query_ipv6:"$num_query_ipv6\
" unwanted_queries:"$unwanted_queries\
" unwanted_replies:"$unwanted_replies

echo $cache_hits > $datadir/cache_hits

######

requestlist=\
"total_req_overw:"$total_requestlist_overwritten\
" total_req_excd:"$total_requestlist_exceeded\
" total_req_cur_all:"$total_requestlist_current_all\
" total_req_cur_usr:"$total_requestlist_current_user\
" total_req_avg:"$total_requestlist_avg\
" total_req_max:"$total_requestlist_max

echo $requestlist > $datadir/requestlist

######

if [ $mem_total_sbrk -eq 0 ]
then
unbpid=`cat $pid`
mem_total_sbrk=`ps -p $unbpid -o rss= 2>&1`
mem_total_sbrk=$(($mem_total_sbrk \* 1024))
fi

memory_usage=\
"mem_total_sbrk:"$mem_total_sbrk\
" mem_cache_rrset:"$mem_cache_rrset\
" mem_cache_message:"$mem_cache_message\
" mem_mod_iterator:"$mem_mod_iterator\
" mem_mod_validator:"$mem_mod_validator

echo $memory_usage > $datadir/memory_usage

######

queues_by_type=\
"num_q_t_A:"$num_query_type_A\
" num_q_t_AAAA:"$num_query_type_AAAA\
" num_q_t_CNAME:"$num_query_type_CNAME\
" num_q_t_MX:"$num_query_type_MX\
" num_q_t_NS:"$num_query_type_NS\
" num_q_t_PTR:"$num_query_type_PTR\
" num_q_t_SOA:"$num_query_type_SOA\
" num_q_t_SRV:"$num_query_type_SRV\
" num_q_t_TXT:"$num_query_type_TXT\
" num_q_t_other:"$num_query_type_other

echo $queues_by_type > $datadir/queues_by_type

######

answers_to_queries=\
"num_a_NOER:"$num_answer_rcode_NOERROR\
" num_a_NXDOMAIN:"$num_answer_rcode_NXDOMAIN\
" num_a_SERVFAULT:"$num_answer_rcode_SERVFAULT\
" num_a_nodata:"$num_answer_rcode_nodata\
" num_a_secure:"$num_answer_secure\
" num_a_bogus:"$num_answer_bogus\
" num_r_bogus:"$num_rrset_bogus

echo $answers_to_queries > $datadir/answers_to_queries

######

histogram=\
"h64ms:"$histogram_000000_000000_to_000000_065536\
" h128ms:"$histogram_000000_065536_to_000000_131072\
" h256ms:"$histogram_000000_131072_to_000000_262144\
" h512ms:"$histogram_000000_262144_to_000000_524288\
" h1s:"$histogram_000000_524288_to_000001_000000\
" h2s:"$histogram_000000_000001_to_000002_000000\
" h4s:"$histogram_000000_000002_to_000004_000000\
" h8s:"$histogram_000000_000004_to_000008_000000\
" h16s:"$histogram_000000_000008_to_524288_000000\
" total_num_cachehits:"$total_num_cachehits

echo $histogram > $datadir/histogram

######

queues_by_flags=\
"num_q_f_RD:"$num_query_flags_RD\
" num_q_f_QR:"$num_query_flags_QR\
" num_q_f_AA:"$num_query_flags_AA\
" num_q_f_TC:"$num_query_flags_TC\
" num_q_f_RA:"$num_query_flags_RA\
" num_q_f_Z:"$num_query_flags_Z\
" num_q_f_AD:"$num_query_flags_AD\
" num_q_f_CD:"$num_query_flags_CD\
" num_q_f_DO:"$num_query_edns_DO\
" num_q_edns:"$num_query_edns_present

echo $queues_by_flags > $datadir/queues_by_flags

