#!/bin/bash : << =cut =head1 NAME zfs_arcstats - Munin multi-graph plugin to monitor ZFS ARC statistics These functions are implemented: size : to monitor ARC size activity : to monitor ARC activities actlist : to monitor ARC activities by cache list (MFU/MRU) actdata : to monitor ARC activities by data type (Demand/Prefetch) hitratio : to monitor ARC hit ratio Tested with Solaris 10 and 11, OpenIndiana Hipster, FreeBSD 11, CentOS 7 This plugin is inspired by arcstat.pl [https://github.com/mharsch/arcstat] =head1 CONFIGURATION Make symlink: cd /path/to/munin/etc/plugins ln -s /path/to/munin/lib/plugins/zfs_arcstats . For FreeBSD, it should be necessary to change shebang /bin/bash -> /usr/local/bin/bash =head1 ENVIRONMENT VARIABLES None =head1 AUTHOR K.Cima https://github.com/shakemid =head1 LICENSE GPLv2 =head1 Magic markers #%# family=contrib #%# capabilities=autoconf =cut # Include plugin.sh . "${MUNIN_LIBDIR:-}/plugins/plugin.sh" is_multigraph "$@" # Shell options set -o nounset # Set global variables plugin_name=zfs_arcstats functions='size activity actlist actdata hitratio' # Functions get_osname() { local osname osver osname=$( uname -s ) osver=$( uname -v ) case $osname in SunOS) case $osver in illumos*) osname=illumos ;; esac ;; esac echo "$osname" } preconfig() { local func=$1 # data_attr format: field type draw label # label can contain white-spaces. case $func in size) global_attr=" graph_title ZFS ARC - Size graph_category fs graph_args --base 1024 --lower-limit 0 graph_vlabel Bytes graph_info ZFS ARC - Size " case $osname in SunOS) # For Solaris 10,11 data_attr=" data_size GAUGE AREASTACK Data size prefetch_meta_size GAUGE AREASTACK Prefetch meta size buf_size GAUGE AREASTACK Buf size other_size GAUGE AREASTACK Other size " ;; *) # For illumos, FreeBSD, Linux (OpenZFS) data_attr=" data_size GAUGE AREASTACK Data size metadata_size GAUGE AREASTACK Metadata size hdr_size GAUGE AREASTACK Hdr size other_size GAUGE AREASTACK Other size mru_size GAUGE LINE MRU size mfu_size GAUGE LINE MFU size " ;; esac data_attr=" $data_attr size GAUGE LINE ARC size c GAUGE LINE Target size p GAUGE LINE Target MRU size " ;; activity) global_attr=" graph_title ZFS ARC - Activities graph_category fs graph_args --base 1000 --lower-limit 0 graph_vlabel misses (-) / hits (+) per second graph_info ZFS ARC - Activities hits.negative misses l2_hits.negative l2_misses " data_attr=" misses DERIVE LINE dummy hits DERIVE LINE ARC l2_misses DERIVE LINE dummy l2_hits DERIVE LINE L2ARC " ;; actlist) global_attr=" graph_title ZFS ARC - Activities by cache list graph_category fs graph_args --base 1000 --lower-limit 0 graph_vlabel ghost hits (-) / hits (+) per second graph_info ZFS ARC - Activities by cache list mfu_hits.negative mfu_ghost_hits mru_hits.negative mru_ghost_hits " data_attr=" mfu_ghost_hits DERIVE LINE dummy mfu_hits DERIVE LINE MFU mru_ghost_hits DERIVE LINE dummy mru_hits DERIVE LINE MRU " ;; actdata) global_attr=" graph_title ZFS ARC - Activities by data type graph_category fs graph_args --base 1000 --lower-limit 0 graph_vlabel misses (-) / hits (+) per second graph_info ZFS ARC - Activities by data type demand_data_hits.negative demand_data_misses demand_metadata_hits.negative demand_metadata_misses prefetch_data_hits.negative prefetch_data_misses prefetch_metadata_hits.negative prefetch_metadata_misses " data_attr=" demand_data_misses DERIVE LINE dummy demand_data_hits DERIVE LINE D data demand_metadata_misses DERIVE LINE dummy demand_metadata_hits DERIVE LINE D meta prefetch_data_misses DERIVE LINE dummy prefetch_data_hits DERIVE LINE P data prefetch_metadata_misses DERIVE LINE dummy prefetch_metadata_hits DERIVE LINE P meta " ;; hitratio) global_attr=" graph_title ZFS ARC - Hit ratio graph_category fs graph_args --base 1000 --lower-limit 0 --upper-limit 100 --rigid graph_vlabel % hits graph_info ZFS ARC - Hit ratio - The graph shows cache hit ratio between munin-update intervals (usually 5 minutes). hitratio.cdef hits,DUP,misses,+,/,100,* l2_hitratio.cdef l2_hits,DUP,l2_misses,+,/,100,* demand_data_hitratio.cdef demand_data_hits,DUP,demand_data_misses,+,/,100,* demand_metadata_hitratio.cdef demand_metadata_hits,DUP,demand_metadata_misses,+,/,100,* prefetch_data_hitratio.cdef prefetch_data_hits,DUP,prefetch_data_misses,+,/,100,* prefetch_metadata_hitratio.cdef prefetch_metadata_hits,DUP,prefetch_metadata_misses,+,/,100,* " data_attr=" hits DERIVE LINE dummy misses DERIVE LINE dummy l2_hits DERIVE LINE dummy l2_misses DERIVE LINE dummy demand_data_hits DERIVE LINE dummy demand_data_misses DERIVE LINE dummy demand_metadata_hits DERIVE LINE dummy demand_metadata_misses DERIVE LINE dummy prefetch_data_hits DERIVE LINE dummy prefetch_data_misses DERIVE LINE dummy prefetch_metadata_hits DERIVE LINE dummy prefetch_metadata_misses DERIVE LINE dummy hitratio GAUGE LINE2 ARC hits l2_hitratio GAUGE LINE L2ARC hits demand_data_hitratio GAUGE LINE Demand data hits demand_metadata_hitratio GAUGE LINE Demand metadata hits prefetch_data_hitratio GAUGE LINE Prefetch data hits prefetch_metadata_hitratio GAUGE LINE Prefetch metadata hits " ;; *) echo "Unknown function: $func" exit 1 ;; esac } do_config() { local func=$1 local label_max_length=45 local field type draw label preconfig "$func" echo "multigraph ${plugin_name}_${func}" # print global attributes echo "$global_attr" | sed -e 's/^ *//' -e '/^$/d' # print data source attributes echo "$data_attr" | while read -r field type draw label do [ -z "$field" ] && continue echo "${field}.type ${type}" echo "${field}.draw ${draw}" echo "${field}.label ${label:0:${label_max_length}}" if [ "$type" = 'DERIVE' ]; then echo "${field}.min 0" fi if [ "$label" = 'dummy' ]; then echo "${field}.graph no" fi done echo } get_stats() { local arcstats stat value case $osname in SunOS|illumos) arcstats=$( kstat -p 'zfs:0:arcstats' | sed -e 's/:/ /g' | awk '{ print $4,$5 }' ) # kstat output example: # $ kstat -p zfs:0:arcstats # zfs:0:arcstats:c 4135233544 # ... ;; *BSD) arcstats=$( /sbin/sysctl -a | sed -n -e 's/^kstat\.zfs\.misc\.arcstats\.//p' | awk -F: '{ print $1,$2 }' ) # sysctl output example: # $ sysctl -a # ... # kstat.zfs.misc.arcstats.c: 632540160 # ... ;; Linux) arcstats=$( sed '1,2d' /proc/spl/kstat/zfs/arcstats | awk '{ print $1,$3 }' ) # proc file output example: # $ cat /proc/spl/kstat/zfs/arcstats # ... # name type data # hits 4 62 # ... ;; *) echo "Unsupported OS: $osname" exit 1 esac while read -r stat value do printf -v "arcstats_${stat}" "%s" "$value" # printf -v means indirect variable assignment (similar to eval) done <<< "$arcstats" } do_fetch() { local func=$1 local field type draw label value ref preconfig "$func" echo "multigraph ${plugin_name}_${func}" echo "$data_attr" | while read -r field type draw label do [ -z "$field" ] && continue ref="arcstats_${field}" value=${!ref:-0} # ${!varname} means indirect evaluation (similar to eval) echo "${field}.value ${value}" done echo } autoconf() { if [ -x /sbin/zfs ]; then echo yes else echo "no (ZFS looks unavailable)" fi } config() { local func for func in $functions do do_config "$func" done } fetch() { local func get_stats for func in $functions do do_fetch "$func" done } # Main osname=$( get_osname ) case ${1:-} in autoconf) autoconf ;; config) config [ "${MUNIN_CAP_DIRTYCONFIG:-}" = "1" ] && fetch ;; *) fetch ;; esac exit 0