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