Home | History | Annotate | Line # | Download | only in contrib
      1      1.1  christos #!/bin/sh
      2      1.1  christos #
      3      1.1  christos # plugin for munin to monitor usage of NSD.
      4      1.1  christos #
      5      1.1  christos # (C) 2008 W.C.A. Wijngaards.  BSD Licensed.
      6      1.1  christos #
      7      1.1  christos # To install; compile with --enable-bind8-stats (enabled by default)
      8      1.1  christos #	and enable nsd-control in nsd.conf with the line
      9      1.1  christos #	remote-control:	control-enable: yes
     10      1.1  christos # Run the command nsd-control-setup as root to generate the key files.
     11      1.1  christos #
     12      1.1  christos # Environment variables for this script
     13      1.1  christos #	statefile	- where to put temporary statefile.
     14      1.1  christos #	nsd_conf	- where the nsd.conf file is located.
     15      1.1  christos #	nsd_control	- where to find nsd-control executable.
     16      1.1  christos #	nsd_checkconf	- where to find nsd-checkconf executable.
     17      1.1  christos #
     18      1.1  christos # You can set them in your munin/plugin-conf.d/plugins.conf file
     19      1.1  christos # with:
     20      1.1  christos # [nsd_munin*]
     21      1.1  christos # user root
     22      1.1  christos # env.statefile /usr/local/var/munin/plugin-state/nsd-state
     23      1.1  christos # env.nsd_conf /usr/local/etc/nsd.conf
     24      1.1  christos # env.nsd_control /usr/local/sbin/nsd-control
     25      1.1  christos # env.nsd_checkconf /usr/local/sbin/nsd-checkconf
     26      1.1  christos #
     27      1.1  christos # This plugin can create different graphs depending on what name
     28      1.1  christos # you link it as (with ln -s) into the plugins directory
     29      1.1  christos # You can link it multiple times.
     30      1.1  christos # If you are only a casual user, the _hits and _by_type are most interesting,
     31      1.1  christos # possibly followed by _by_rcode.
     32      1.1  christos #
     33      1.1  christos #	nsd_munin_hits		- base volume, transport type, failures 
     34      1.1  christos #	nsd_munin_memory	- memory usage
     35      1.1  christos #	nsd_munin_by_type	- incoming queries by type
     36      1.1  christos #	nsd_munin_by_class	- incoming queries by class
     37      1.1  christos #	nsd_munin_by_opcode	- incoming queries by opcode
     38      1.1  christos #	nsd_munin_by_rcode	- answers by rcode
     39      1.1  christos #	nsd_munin_zones		- number of zones
     40      1.1  christos #
     41      1.1  christos # Magic markers - optional - used by installation scripts and
     42      1.1  christos # munin-config:
     43      1.1  christos #
     44      1.1  christos #%# family=contrib
     45      1.1  christos #%# capabilities=autoconf suggest
     46      1.1  christos 
     47      1.1  christos # POD documentation
     48      1.1  christos : <<=cut
     49      1.1  christos =head1 NAME
     50      1.1  christos 
     51      1.1  christos nsd_munin_ - Munin plugin to monitor the NSD server.
     52      1.1  christos 
     53      1.1  christos =head1 APPLICABLE SYSTEMS
     54      1.1  christos 
     55      1.1  christos System with NSD daemon.
     56      1.1  christos 
     57      1.1  christos =head1 CONFIGURATION
     58      1.1  christos 
     59      1.1  christos   [nsd_munin*]
     60      1.1  christos   user root
     61      1.1  christos   env.statefile /usr/local/var/munin/plugin-state/nsd-state
     62      1.1  christos   env.nsd_conf /usr/local/etc/nsd.conf
     63      1.1  christos   env.nsd_control /usr/local/sbin/nsd-control
     64      1.1  christos   env.nsd_checkconf /usr/local/sbin/nsd-checkconf
     65      1.1  christos 
     66      1.1  christos Use the .env settings to override the defaults.
     67      1.1  christos 
     68      1.1  christos =head1 USAGE
     69      1.1  christos 
     70      1.1  christos Can be used to present different graphs. Use ln -s for that name in
     71      1.1  christos the plugins directory to enable the graph.
     72      1.1  christos nsd_munin_hits		- base volume, transport type, failures 
     73      1.1  christos nsd_munin_memory	- memory usage
     74      1.1  christos nsd_munin_by_type	- incoming queries by type
     75      1.1  christos nsd_munin_by_class	- incoming queries by class
     76      1.1  christos nsd_munin_by_opcode	- incoming queries by opcode
     77      1.1  christos nsd_munin_by_rcode	- answers by rcode
     78      1.1  christos nsd_munin_zones		- number of zones
     79      1.1  christos 
     80      1.1  christos =head1 AUTHOR
     81      1.1  christos 
     82      1.1  christos Copyright 2008 W.C.A. Wijngaards
     83      1.1  christos 
     84      1.1  christos =head1 LICENSE
     85      1.1  christos 
     86      1.1  christos BSD
     87      1.1  christos 
     88      1.1  christos =cut
     89      1.1  christos 
     90      1.1  christos state=${statefile:-/usr/local/var/munin/plugin-state/nsd-state}
     91      1.1  christos conf=${nsd_conf:-/usr/local/etc/nsd.conf}
     92      1.1  christos ctrl=${nsd_control:-/usr/local/sbin/nsd-control}
     93      1.1  christos chkconf=${nsd_checkconf:-/usr/local/sbin/nsd-checkconf}
     94      1.1  christos lock=$state.lock
     95      1.1  christos 
     96      1.1  christos # number of seconds between polling attempts.
     97      1.1  christos # makes the statefile hang around for at least this many seconds,
     98      1.1  christos # so that multiple links of this script can share the results.
     99      1.1  christos lee=55
    100      1.1  christos 
    101      1.1  christos # to keep things within 19 characters
    102      1.1  christos ABBREV="-e s/num/n/ -e s/type/t/ -e s/opcode/o/ -e s/rcode/r/ -e s/class/c/"
    103      1.1  christos 
    104      1.1  christos # get value from $1 into return variable $value
    105      1.1  christos get_value ( ) {
    106      1.1  christos 	value="`grep '^'$1'=' $state | sed -e 's/^.*=//'`"
    107      1.1  christos 	if test "$value"x = ""x; then
    108      1.1  christos 		value="0"
    109      1.1  christos 	fi
    110      1.1  christos }
    111      1.1  christos 
    112      1.1  christos # download the state from NSD.
    113      1.1  christos get_state ( ) {
    114      1.1  christos 	# obtain lock for fetching the state
    115      1.1  christos 	# because there is a race condition in fetching and writing to file
    116      1.1  christos 
    117      1.1  christos 	# see if the lock is stale, if so, take it 
    118      1.1  christos 	if test -f $lock ; then
    119      1.1  christos 		pid="`cat $lock 2>&1`"
    120      1.1  christos 		kill -0 "$pid" >/dev/null 2>&1
    121      1.1  christos 		if test $? -ne 0 -a "$pid" != $$ ; then
    122      1.1  christos 			echo $$ >$lock
    123      1.1  christos 		fi
    124      1.1  christos 	fi
    125      1.1  christos 
    126      1.1  christos 	i=0
    127      1.1  christos 	while test ! -f $lock || test "`cat $lock 2>&1`" != $$; do
    128      1.1  christos 		while test -f $lock; do
    129      1.1  christos 			# wait
    130      1.1  christos 			i=`expr $i + 1`
    131      1.1  christos 			if test $i -gt 1000; then
    132      1.1  christos 				sleep 1;
    133      1.1  christos 			fi
    134      1.1  christos 			if test $i -gt 1500; then
    135      1.1  christos 				echo "error locking $lock" "=" `cat $lock`
    136      1.1  christos 				rm -f $lock
    137      1.1  christos 				exit 1
    138      1.1  christos 			fi
    139      1.1  christos 		done
    140      1.1  christos 		# try to get it
    141      1.1  christos 		echo $$ >$lock
    142      1.1  christos 	done
    143      1.1  christos 	# do not refetch if the file exists and only LEE seconds old
    144      1.1  christos 	if test -f $state; then
    145      1.1  christos 		now=`date +%s`
    146      1.1  christos 		get_value "timestamp"
    147      1.1  christos 		if test $now -lt `expr $value + $lee`; then
    148      1.1  christos 			rm -f $lock
    149      1.1  christos 			return
    150      1.1  christos 		fi
    151      1.1  christos 	fi
    152      1.1  christos 	$ctrl -c $conf stats > $state
    153      1.1  christos 	if test $? -ne 0; then
    154      1.1  christos 		echo "error retrieving data from the server"
    155      1.1  christos 		rm -f $lock
    156      1.1  christos 		exit 1
    157      1.1  christos 	fi
    158      1.1  christos 	echo "timestamp="`date +%s` >> $state
    159      1.1  christos 	rm -f $lock
    160      1.1  christos }
    161      1.1  christos 
    162      1.1  christos if test "$1" = "autoconf" ; then
    163      1.1  christos 	if test ! -f $conf; then
    164      1.1  christos 		echo no "($conf does not exist)"
    165      1.1  christos 		exit 1
    166      1.1  christos 	fi
    167      1.1  christos 	if test ! -d `dirname $state`; then
    168      1.1  christos 		mkdir -p `dirname $state`
    169      1.1  christos 		if test ! -d `dirname $state`; then
    170      1.1  christos 			echo no "($state directory does not exist)"
    171      1.1  christos 			exit 1
    172      1.1  christos 		fi
    173      1.1  christos 	fi
    174      1.1  christos 	echo yes
    175      1.1  christos 	exit 0
    176      1.1  christos fi
    177      1.1  christos 
    178      1.1  christos if test "$1" = "suggest" ; then
    179      1.1  christos 	echo "hits"
    180      1.1  christos 	echo "memory"
    181      1.1  christos 	echo "by_type"
    182      1.1  christos 	echo "by_class"
    183      1.1  christos 	echo "by_opcode"
    184      1.1  christos 	echo "by_rcode"
    185      1.1  christos 	echo "zones"
    186      1.1  christos 	exit 0
    187      1.1  christos fi
    188      1.1  christos 
    189      1.1  christos # determine my type, by name
    190      1.1  christos id=`echo $0 | sed -e 's/^.*nsd_munin_//'`
    191      1.1  christos if test "$id"x = ""x; then
    192      1.1  christos 	# some default to keep people sane.
    193      1.1  christos 	id="hits"
    194      1.1  christos fi
    195      1.1  christos 
    196      1.1  christos # if $1 exists in statefile, config is echoed with label $2
    197      1.1  christos exist_config ( ) {
    198      1.1  christos 	mn=`echo $1 | sed $ABBREV | tr . _`
    199      1.1  christos 	if grep '^'$1'=' $state >/dev/null 2>&1; then
    200      1.1  christos 		echo "$mn.label $2"
    201      1.1  christos 		echo "$mn.min 0"
    202      1.1  christos 		echo "$mn.type ABSOLUTE"
    203      1.1  christos 	fi
    204      1.1  christos }
    205      1.1  christos 
    206      1.1  christos # print label and min 0 for a name $1 in nsd format
    207      1.1  christos p_config ( ) {
    208      1.1  christos 	mn=`echo $1 | sed $ABBREV | tr . _`
    209      1.1  christos 	echo $mn.label "$2"
    210      1.1  christos 	echo $mn.min 0
    211      1.1  christos 	echo $mn.type $3
    212      1.1  christos }
    213      1.1  christos 
    214      1.1  christos if test "$1" = "config" ; then
    215      1.1  christos 	if test ! -f $state; then
    216      1.1  christos 		get_state
    217      1.1  christos 	fi
    218      1.1  christos 	case $id in
    219      1.1  christos 	hits)
    220      1.1  christos 		echo "graph_title NSD traffic"
    221      1.1  christos 		echo "graph_args --base 1000 -l 0"
    222      1.1  christos 		echo "graph_vlabel queries / \${graph_period}"
    223      1.1  christos 		echo "graph_scale no"
    224      1.1  christos 		echo "graph_category DNS"
    225      1.1  christos 		for x in server0.queries server1.queries server2.queries \
    226      1.1  christos 			server3.queries server4.queries server5.queries \
    227      1.1  christos 			server6.queries server7.queries server8.queries \
    228      1.1  christos 			server9.queries server10.queries server11.queries \
    229      1.1  christos 			server12.queries server13.queries server14.queries \
    230      1.1  christos 			server15.queries ; do
    231      1.1  christos 			exist_config $x "queries handled by `basename $x .queries`"
    232      1.1  christos 		done
    233      1.1  christos 		p_config "num.queries" "total queries" "ABSOLUTE"
    234      1.1  christos 		p_config "num.udp" "UDP ip4 queries" "ABSOLUTE"
    235      1.1  christos 		p_config "num.udp6" "UDP ip6 queries" "ABSOLUTE"
    236      1.1  christos 		p_config "num.tcp" "TCP ip4 queries" "ABSOLUTE"
    237      1.1  christos 		p_config "num.tcp6" "TCP ip6 queries" "ABSOLUTE"
    238      1.1  christos 		p_config "num.edns" "queries with EDNS OPT" "ABSOLUTE"
    239      1.1  christos 		p_config "num.ednserr" "queries failed EDNS parse" "ABSOLUTE"
    240      1.1  christos 		p_config "num.answer_wo_aa" "nonauthor. queries (referrals)" "ABSOLUTE"
    241      1.1  christos 		p_config "num.rxerr" "receive failed" "ABSOLUTE"
    242      1.1  christos 		p_config "num.txerr" "transmit failed" "ABSOLUTE"
    243      1.1  christos 		p_config "num.truncated" "truncated replies with TC" "ABSOLUTE"
    244      1.1  christos 		p_config "num.raxfr" "AXFR from allowed client" "ABSOLUTE"
    245  1.1.1.2  christos 		p_config "num.rixfr" "IXFR from allowed client" "ABSOLUTE"
    246      1.1  christos 		p_config "num.dropped" "dropped due to sanity check" "ABSOLUTE"
    247      1.1  christos 		echo "graph_info DNS queries."
    248      1.1  christos 		;;
    249      1.1  christos 	memory)
    250      1.1  christos 		echo "graph_title NSD memory usage"
    251      1.1  christos 		echo "graph_args --base 1024 -l 0"
    252      1.1  christos 		echo "graph_vlabel memory used in bytes"
    253      1.1  christos 		echo "graph_category DNS"
    254      1.1  christos 		p_config "size.vsz" "Total virtual memory (VSZ)" "GAUGE"
    255      1.1  christos 		p_config "size.rss" "Total resident memory (RSS)" "GAUGE"
    256      1.1  christos 		p_config "size.db.mem" "data in memory" "GAUGE"
    257      1.1  christos 		p_config "size.xfrd.mem" "xfr and notify memory" "GAUGE"
    258      1.1  christos 		p_config "size.config.mem" "config memory" "GAUGE"
    259      1.1  christos 		p_config "size.db.disk" "mmap of nsd.db file" "GAUGE"
    260      1.1  christos 		p_config "size.config.disk" "config zonelist on disk" "GAUGE"
    261      1.1  christos 		echo "graph_info The memory used by NSD, xfrd and config. Disk size of nsd.db and zonelist."
    262      1.1  christos 		;;
    263      1.1  christos 	by_type)
    264      1.1  christos 		echo "graph_title NSD queries by type"
    265      1.1  christos 		echo "graph_args --base 1000 -l 0"
    266      1.1  christos 		echo "graph_vlabel queries / \${graph_period}"
    267      1.1  christos 		echo "graph_scale no"
    268      1.1  christos 		echo "graph_category DNS"
    269      1.1  christos 		for x in `grep "^num.type" $state`; do
    270      1.1  christos 			nm=`echo $x | sed -e 's/=.*$//'`
    271      1.1  christos 			tp=`echo $nm | sed -e s/num.type.//`
    272      1.1  christos 			p_config "$nm" "$tp" "ABSOLUTE"
    273      1.1  christos 		done
    274      1.1  christos 		echo "graph_info queries by DNS RR type queried for"
    275      1.1  christos 		;;
    276      1.1  christos 	by_class)
    277      1.1  christos 		echo "graph_title NSD queries by class"
    278      1.1  christos 		echo "graph_args --base 1000 -l 0"
    279      1.1  christos 		echo "graph_vlabel queries / \${graph_period}"
    280      1.1  christos 		echo "graph_scale no"
    281      1.1  christos 		echo "graph_category DNS"
    282      1.1  christos 		for x in `grep "^num.class" $state`; do
    283      1.1  christos 			nm=`echo $x | sed -e 's/=.*$//'`
    284      1.1  christos 			tp=`echo $nm | sed -e s/num.class.//`
    285      1.1  christos 			p_config "$nm" "$tp" "ABSOLUTE"
    286      1.1  christos 		done
    287      1.1  christos 		echo "graph_info queries by DNS RR class queried for."
    288      1.1  christos 		;;
    289      1.1  christos 	by_opcode)
    290      1.1  christos 		echo "graph_title NSD queries by opcode"
    291      1.1  christos 		echo "graph_args --base 1000 -l 0"
    292      1.1  christos 		echo "graph_vlabel queries / \${graph_period}"
    293      1.1  christos 		echo "graph_scale no"
    294      1.1  christos 		echo "graph_category DNS"
    295      1.1  christos 		for x in `grep "^num.opcode" $state`; do
    296      1.1  christos 			nm=`echo $x | sed -e 's/=.*$//'`
    297      1.1  christos 			tp=`echo $nm | sed -e s/num.opcode.//`
    298      1.1  christos 			p_config "$nm" "$tp" "ABSOLUTE"
    299      1.1  christos 		done
    300      1.1  christos 		echo "graph_info queries by opcode in the query packet."
    301      1.1  christos 		;;
    302      1.1  christos 	by_rcode)
    303      1.1  christos 		echo "graph_title NSD answers by return code"
    304      1.1  christos 		echo "graph_args --base 1000 -l 0"
    305      1.1  christos 		echo "graph_vlabel answer packets / \${graph_period}"
    306      1.1  christos 		echo "graph_scale no"
    307      1.1  christos 		echo "graph_category DNS"
    308      1.1  christos 		for x in `grep "^num.rcode" $state`; do
    309      1.1  christos 			nm=`echo $x | sed -e 's/=.*$//'`
    310      1.1  christos 			tp=`echo $nm | sed -e s/num.rcode.//`
    311      1.1  christos 			p_config "$nm" "$tp" "ABSOLUTE"
    312      1.1  christos 		done
    313      1.1  christos 		echo "graph_info answers split out by return value."
    314      1.1  christos 		;;
    315      1.1  christos 	zones)
    316      1.1  christos 		echo "graph_title NSD number of zones"
    317      1.1  christos 		echo "graph_args --base 1000 -l 0"
    318      1.1  christos 		echo "graph_vlabel zone count"
    319      1.1  christos 		echo "graph_category DNS"
    320      1.1  christos 		p_config "zone.total" "total zones" "GAUGE"
    321  1.1.1.3  christos 		p_config "zone.primary" "primary zones" "GAUGE"
    322  1.1.1.3  christos 		p_config "zone.secondary" "secondary zones" "GAUGE"
    323      1.1  christos 		echo "graph_info number of zones served by NSD."
    324      1.1  christos 		;;
    325      1.1  christos 	esac
    326      1.1  christos 
    327      1.1  christos 	exit 0
    328      1.1  christos fi
    329      1.1  christos 
    330      1.1  christos # do the stats itself
    331      1.1  christos get_state
    332      1.1  christos 
    333      1.1  christos # get the time elapsed
    334      1.1  christos get_value "time.elapsed"
    335      1.1  christos if test $value = 0 || test $value = "0.000000"; then
    336      1.1  christos 	echo "error: time elapsed 0 or could not retrieve data"
    337      1.1  christos 	exit 1
    338      1.1  christos fi
    339      1.1  christos elapsed="$value"
    340      1.1  christos 
    341      1.1  christos # print value for $1
    342      1.1  christos print_value ( ) {
    343      1.1  christos 	mn=`echo $1 | sed $ABBREV | tr . _`
    344      1.1  christos 	get_value $1
    345      1.1  christos 	echo "$mn.value" $value
    346      1.1  christos }
    347      1.1  christos 
    348      1.1  christos # print value if line already found in $2
    349      1.1  christos print_value_line ( ) {
    350      1.1  christos 	mn=`echo $1 | sed $ABBREV | tr . _`
    351      1.1  christos 	value="`echo $2 | sed -e 's/^.*=//'`"
    352      1.1  christos 	echo "$mn.value" $value
    353      1.1  christos }
    354      1.1  christos 
    355      1.1  christos 
    356      1.1  christos case $id in
    357      1.1  christos hits)
    358      1.1  christos 	for x in server0.queries server1.queries server2.queries \
    359      1.1  christos 		server3.queries server4.queries server5.queries \
    360      1.1  christos 		server6.queries server7.queries server8.queries \
    361      1.1  christos 		server9.queries server10.queries server11.queries \
    362      1.1  christos 		server12.queries server13.queries server14.queries \
    363      1.1  christos 		server15.queries \
    364      1.1  christos 		num.queries num.udp num.udp6 num.tcp num.tcp6 \
    365      1.1  christos 		num.edns num.ednserr num.answer_wo_aa num.rxerr num.txerr \
    366  1.1.1.2  christos 		num.truncated num.raxfr num.rixfr num.dropped ; do
    367      1.1  christos 		if grep "^"$x"=" $state >/dev/null 2>&1; then
    368      1.1  christos 			print_value $x
    369      1.1  christos 		fi
    370      1.1  christos 	done
    371      1.1  christos 	;;
    372      1.1  christos memory)
    373      1.1  christos 	# get the total memory for NSD
    374      1.1  christos 	serverpid=`$ctrl -c $conf serverpid 2>&1`
    375      1.1  christos 	# small race condition, if reload happens between previous and next
    376      1.1  christos 	# lines, if so, detect by checking if we have a number as output.
    377      1.1  christos 	rssval=`ps -p $serverpid -o rss= 2>&1`
    378      1.1  christos 	vszval=`ps -p $serverpid -o vsz= 2>&1`
    379      1.1  christos 	if test "`expr $rssval + 1 - 1 2>&1`" -eq "$rssval" >/dev/null 2>&1; then
    380      1.1  christos 		rssval=`expr $rssval \* 1024` 
    381      1.1  christos 	else
    382      1.1  christos 		rssval=0
    383      1.1  christos 	fi
    384      1.1  christos 	if test "`expr $vszval + 1 - 1 2>&1`" -eq "$vszval" >/dev/null 2>&1; then
    385      1.1  christos 		vszval=`expr $vszval \* 1024` 
    386      1.1  christos 	else
    387      1.1  christos 		vszval=0
    388      1.1  christos 	fi
    389      1.1  christos 	echo "size_vsz.value" $vszval
    390      1.1  christos 	echo "size_rss.value" $rssval
    391      1.1  christos 	for x in size.db.mem size.xfrd.mem size.config.mem \
    392      1.1  christos 		size.db.disk size.config.disk; do
    393      1.1  christos 		print_value $x
    394      1.1  christos 	done
    395      1.1  christos 	;;
    396      1.1  christos by_type)
    397      1.1  christos 	for x in `grep "^num.type" $state`; do
    398      1.1  christos 		nm=`echo $x | sed -e 's/=.*$//'`
    399      1.1  christos 		print_value_line $nm $x
    400      1.1  christos 	done
    401      1.1  christos 	;;
    402      1.1  christos by_class)
    403      1.1  christos 	for x in `grep "^num.class" $state`; do
    404      1.1  christos 		nm=`echo $x | sed -e 's/=.*$//'`
    405      1.1  christos 		print_value_line $nm $x
    406      1.1  christos 	done
    407      1.1  christos 	;;
    408      1.1  christos by_opcode)
    409      1.1  christos 	for x in `grep "^num.opcode" $state`; do
    410      1.1  christos 		nm=`echo $x | sed -e 's/=.*$//'`
    411      1.1  christos 		print_value_line $nm $x
    412      1.1  christos 	done
    413      1.1  christos 	;;
    414      1.1  christos by_rcode)
    415      1.1  christos 	for x in `grep "^num.rcode" $state`; do
    416      1.1  christos 		nm=`echo $x | sed -e 's/=.*$//'`
    417      1.1  christos 		print_value_line $nm $x
    418      1.1  christos 	done
    419      1.1  christos 	;;
    420      1.1  christos zones)
    421  1.1.1.3  christos 	get_value "zone.primary"
    422      1.1  christos 	nummas="$value"
    423  1.1.1.3  christos 	get_value "zone.secondary"
    424      1.1  christos 	numsla="$value"
    425      1.1  christos 	echo "zone_total.value" `expr $nummas + $numsla`
    426  1.1.1.3  christos 	echo "zone_primary.value" "$nummas"
    427  1.1.1.3  christos 	echo "zone_secondary.value" "$numsla"
    428      1.1  christos esac
    429