diff --git a/check_mountpoints.sh b/check_mountpoints.sh index 2ef6c2b..2ac571b 100755 --- a/check_mountpoints.sh +++ b/check_mountpoints.sh @@ -25,6 +25,10 @@ # @version: 2.4 # @date: 2017-12-03 21:25:00 CEST # +# changes 2.5 +# - added performance data (for nagiosgraph or pnp4nagios) +# - changed parameter for writable to (uppercase) -W (to free -w for warning acconding dev guidelines https://nagios-plugins.org/doc/guidelines.html) +# - added -w and -c params for warning and critical thresholds # changes 2.4 # - add support for ext2 # changes 2.3 @@ -147,7 +151,7 @@ case $KERNEL in FSTAB=/etc/fstab MTAB=none GREP=grep - ;; + ;; *) FSF=3 MF=2 OF=4 @@ -161,7 +165,9 @@ esac # Time in seconds after which the check assumes that an NFS mount is staled, if # it does not respond. (default: 3) TIME_TILL_STALE=3 - +WARN_TIME=3 +CRIT_TIME=3 +CURRENT_STATE=$STATE_OK # -------------------------------------------------------------------- @@ -182,13 +188,15 @@ function usage() { echo " -M NUMBER Mount Field number in fstab (default: ${MF})" echo " -O NUMBER Option Field number in fstab (default: ${OF})" echo " -T SECONDS Responsetime at which an NFS is declared as staled (default: ${TIME_TILL_STALE})" + echo " -w SECONDS Warning threshold for responsetime of an NFS mount (default: ${WARN_TIME})" + echo " -c SECONDS Critical threshold for responsetime of an NFS mount (default: ${CRIT_TIME})" echo " -L Allow softlinks to be accepted instead of mount points" echo " -i Ignore fstab. Do not fail just because mount is not in fstab. (default: unset)" echo " -a Autoselect mounts from fstab (default: unset)" echo " -A Autoselect from fstab. Return OK if no mounts found. (default: unset)" - echo " -E PATH Use with -a or -A to exclude a path from fstab. Use '\|' between paths for multiple. (default: unset)" + echo " -E PATH Use with -a or -A to exclude a path from fstab. Use '\|' between paths for multiple. (default: unset)" echo " -o When autoselecting mounts from fstab, ignore mounts having noauto flag. (default: unset)" - echo " -w Writetest. Touch file \$mountpoint/.mount_test_from_\$(hostname) (default: unset)" + echo " -W Writetest. Touch file \$mountpoint/.mount_test_from_\$(hostname) (default: unset)" echo " -e ARGS Extra arguments for df (default: unset)" echo " MOUNTPOINTS list of mountpoints to check. Ignored when -a is given" } @@ -209,14 +217,39 @@ function print_help() { # Create a temporary mtab systems that don't have such a file # Format is dev mountpoint filesystem function make_mtab() { - mtab=$(mktemp) - mount > $mtab - sed -i '' 's/ on / /' $mtab - sed -i '' 's/ (/ /' $mtab - sed -i '' 's/,.*/ /' $mtab - echo $mtab + mtab=$(mktemp) + mount > $mtab + sed -i '' 's/ on / /' $mtab + sed -i '' 's/ (/ /' $mtab + sed -i '' 's/,.*/ /' $mtab + echo $mtab } +function measure_exectime() { + # check params + DFPID=$1 + POSTFIX=$2 + # compute relevant timestamps + start_at=$(date +%s.%N) + stale_at=$(bc <<< "$start_at + ${TIME_TILL_STALE}") + # loop until process is ended or stale-time is reached + while [ $(bc <<< "scale=2; ($(date +%s.%N) < $stale_at)") ]; do + if ps -p $DFPID > /dev/null ; then + sleep 0.01 + else + break + fi + done + # set endtime and compute duration of process + end_at=$(date +%s.%N) +# time_cost=$(bc <<< 'scale=3; x=(${end_at} - ${start_at})/1; if(x<1){"0"}; x') + time_cost=$(bc <<< "scale=3; (${end_at} - ${start_at})/1") + if [[ ${time_cost:0:1} == "." ]]; then time_cost="0"${time_cost}; fi # ensure leading zero! + warn_at=$(bc <<< "$start_at + ${WARN_TIME}") + crit_at=$(bc <<< "$start_at + ${CRIT_TIME}") + # set (global) performance data + PERFDATA="${PERFDATA}'${MP}${POSTFIX}'=${time_cost}s;${WARN_TIME};${CRIT_TIME};0;${TIME_TILL_STALE} " +} # -------------------------------------------------------------------- # startup checks @@ -232,7 +265,7 @@ do case "$1" in -a) AUTO=1; shift;; -A) AUTO=1; AUTOIGNORE=1; shift;; - -E) EXCLUDE=$2; shift 2;; + -E) EXCLUDE=$2; shift 2;; -o) NOAUTOIGNORE=1; shift;; --help) print_help; exit $STATE_OK;; -h) print_help; exit $STATE_OK;; @@ -241,63 +274,68 @@ do -N) FSF=$2; shift 2;; -M) MF=$2; shift 2;; -O) OF=$2; shift 2;; + -w) WARN_TIME=$2; shift 2;; + -c) CRIT_TIME=$2; shift 2;; -T) TIME_TILL_STALE=$2; shift 2;; -i) IGNOREFSTAB=1; shift;; - -w) WRITETEST=1; shift;; + -W) WRITETEST=1; shift;; -L) LINKOK=1; shift;; - -e) DFARGS=$2; shift 2;; + -e) DFARGS=$2; shift 2;; /*) MPS="${MPS} $1"; shift;; *) usage; exit $STATE_UNKNOWN;; esac done -# ZFS file system have no fstab. Make on +# adjust warn, crit and stale threshold if applicable +if [ ${WARN_TIME} -gt ${CRIT_TIME} ]; then WARN_TIME=${CRIT_TIME}; fi +if [ ${TIME_TILL_STALE} -lt ${CRIT_TIME} ]; then TILE_TILL_STALE=${CRIT_TIME}; fi +# ZFS file system have no fstab. Make on if [ -x '/sbin/zfs' ]; then - TMPTAB=$(mktemp) - cat ${FSTAB} > ${TMPTAB} - for DS in $(zfs list -H -o name); do - MP=$(zfs get -H mountpoint ${DS} |awk '{print $3}') - # mountpoint ~ "none|legacy|-" - if [ ! -d "$MP" ]; then - continue - fi - if [ $(zfs get -H canmount ${DS} |awk '{print $3}') == 'off' ]; then - continue - fi - case $KERNEL in - SunOS) - if [ $(zfs get -H zoned ${DS} |awk '{print $3}') == 'on' ]; then - continue - fi - ;; - FreeBSD) - if [ $(zfs get -H jailed ${DS} |awk '{print $3}') == 'on' ]; then - continue - fi - ;; - esac - RO=$(zfs get -H readonly ${DS} |awk '($3 == "on"){print "ro"}') - [ -z "$RO" ] && RO='rw' - echo -e "$DS\t$MP\tzfs\t$RO\t0\t0" >> ${TMPTAB} - done - FSTAB=${TMPTAB} + TMPTAB=$(mktemp) + cat ${FSTAB} > ${TMPTAB} + for DS in $(zfs list -H -o name); do + MP=$(zfs get -H mountpoint ${DS} |awk '{print $3}') + # mountpoint ~ "none|legacy|-" + if [ ! -d "$MP" ]; then + continue + fi + if [ $(zfs get -H canmount ${DS} |awk '{print $3}') == 'off' ]; then + continue + fi + case $KERNEL in + SunOS) + if [ $(zfs get -H zoned ${DS} |awk '{print $3}') == 'on' ]; then + continue + fi + ;; + FreeBSD) + if [ $(zfs get -H jailed ${DS} |awk '{print $3}') == 'on' ]; then + continue + fi + ;; + esac + RO=$(zfs get -H readonly ${DS} |awk '($3 == "on"){print "ro"}') + [ -z "$RO" ] && RO='rw' + echo -e "$DS\t$MP\tzfs\t$RO\t0\t0" >> ${TMPTAB} + done + FSTAB=${TMPTAB} fi if [ ${AUTO} -eq 1 ]; then if [ ${NOAUTOIGNORE} -eq 1 ]; then NOAUTOCOND='!index($'${OF}',"'${NOAUTOSTR}'")' fi - if [ "${EXCLUDE}" == "none" ]; then - MPS=`${GREP} -v '^#' ${FSTAB} | awk '{if ('${NOAUTOCOND}'&&($'${FSF}'=="ext2" || $'${FSF}'=="ext3" || $'${FSF}'=="xfs" || $'${FSF}'=="auto" || $'${FSF}'=="ext4" || $'${FSF}'=="nfs" || $'${FSF}'=="nfs4" || $'${FSF}'=="davfs" || $'${FSF}'=="cifs" || $'${FSF}'=="fuse" || $'${FSF}'=="glusterfs" || $'${FSF}'=="ocfs2" || $'${FSF}'=="lustre" || $'${FSF}'=="ufs" || $'${FSF}'=="zfs" || $'${FSF}'=="ceph" || $'${FSF}'=="btrfs" || $'${FSF}'=="yas3fs"))print $'${MF}'}' | sed -e 's/\/$//i' | tr '\n' ' '` - else - MPS=`${GREP} -v '^#' ${FSTAB} | ${GREP} -v ${EXCLUDE} | awk '{if ('${NOAUTOCOND}'&&($'${FSF}'=="ext2" || $'${FSF}'=="ext3" || $'${FSF}'=="xfs" || $'${FSF}'=="auto" || $'${FSF}'=="ext4" || $'${FSF}'=="nfs" || $'${FSF}'=="nfs4" || $'${FSF}'=="davfs" || $'${FSF}'=="cifs" || $'${FSF}'=="fuse" || $'${FSF}'=="glusterfs" || $'${FSF}'=="ocfs2" || $'${FSF}'=="lustre" || $'${FSF}'=="ufs" || $'${FSF}'=="zfs" || $'${FSF}'=="ceph" || $'${FSF}'=="btrfs" || $'${FSF}'=="yas3fs"))print $'${MF}'}' | sed -e 's/\/$//i' | tr '\n' ' '` - fi + if [ "${EXCLUDE}" == "none" ]; then + MPS=`${GREP} -v '^#' ${FSTAB} | awk '{if ('${NOAUTOCOND}'&&($'${FSF}'=="ext2" || $'${FSF}'=="ext3" || $'${FSF}'=="xfs" || $'${FSF}'=="auto" || $'${FSF}'=="ext4" || $'${FSF}'=="nfs" || $'${FSF}'=="nfs4" || $'${FSF}'=="davfs" || $'${FSF}'=="cifs" || $'${FSF}'=="fuse" || $'${FSF}'=="glusterfs" || $'${FSF}'=="ocfs2" || $'${FSF}'=="lustre" || $'${FSF}'=="ufs" || $'${FSF}'=="zfs" || $'${FSF}'=="ceph" || $'${FSF}'=="btrfs" || $'${FSF}'=="yas3fs"))print $'${MF}'}' | sed -e 's/\/$//i' | tr '\n' ' '` + else + MPS=`${GREP} -v '^#' ${FSTAB} | ${GREP} -v ${EXCLUDE} | awk '{if ('${NOAUTOCOND}'&&($'${FSF}'=="ext2" || $'${FSF}'=="ext3" || $'${FSF}'=="xfs" || $'${FSF}'=="auto" || $'${FSF}'=="ext4" || $'${FSF}'=="nfs" || $'${FSF}'=="nfs4" || $'${FSF}'=="davfs" || $'${FSF}'=="cifs" || $'${FSF}'=="fuse" || $'${FSF}'=="glusterfs" || $'${FSF}'=="ocfs2" || $'${FSF}'=="lustre" || $'${FSF}'=="ufs" || $'${FSF}'=="zfs" || $'${FSF}'=="ceph" || $'${FSF}'=="btrfs" || $'${FSF}'=="yas3fs"))print $'${MF}'}' | sed -e 's/\/$//i' | tr '\n' ' '` + fi fi if [ -z "${MPS}" ] && [ ${AUTOIGNORE} -eq 1 ] ; then - echo "OK: no external mounts were found in ${FSTAB}" - exit $STATE_OK + echo "OK: no external mounts were found in ${FSTAB}" + exit $STATE_OK elif [ -z "${MPS}" ]; then log "ERROR: no mountpoints given!" echo "ERROR: no mountpoints given!" @@ -309,10 +347,11 @@ if [ ! -f /proc/mounts -a "${MTAB}" == "/proc/mounts" ]; then log "CRIT: /proc wasn't mounted!" mount -t proc proc /proc ERR_MESG[${#ERR_MESG[*]}]="CRIT: mounted /proc $?" + CURRENT_STATE=$STATE_CRITICAL fi if [ "${MTAB}" == "none" ]; then - MTAB=$(make_mtab) + MTAB=$(make_mtab) fi if [ ! -e "${MTAB}" ]; then @@ -336,15 +375,17 @@ for MP in ${MPS} ; do if [ -z "$( "${GREP}" -v '^#' "${FSTAB}" | awk '$'${MF}' == "'${MP}'" {print $'${MF}'}' )" ]; then log "CRIT: ${MP} doesn't exist in /etc/fstab" ERR_MESG[${#ERR_MESG[*]}]="${MP} doesn't exist in fstab ${FSTAB}" + CURRENT_STATE=$STATE_CRITICAL fi fi ## check kernel mounts if [ -z "$( awk '$'${MF}' == "'${MP}'" {print $'${MF}'}' "${MTAB}" )" ]; then ## if a softlink is not an adequate replacement - if [ -z "$LINKOK" -o ! -L ${MP} ]; then - log "CRIT: ${MP} is not mounted" - ERR_MESG[${#ERR_MESG[*]}]="${MP} is not mounted" + if [ -z "$LINKOK" -o ! -L ${MP} ]; then + log "CRIT: ${MP} is not mounted" + ERR_MESG[${#ERR_MESG[*]}]="${MP} is not mounted" + CURRENT_STATE=$STATE_CRITICAL fi fi @@ -352,59 +393,66 @@ for MP in ${MPS} ; do df -k ${DFARGS} ${MP} &>/dev/null & DFPID=$! disown - for (( i=1 ; i<$TIME_TILL_STALE ; i++ )) ; do - if ps -p $DFPID > /dev/null ; then - sleep 1 - else - break + measure_exectime $DFPID + + if [ "$(bc <<< "$end_at > $stale_at")" == "1" ]; then + log "CRIT: ${MP} did not respond in $TIME_TILL_STALE sec. Seems to be stale." + ERR_MESG[${#ERR_MESG[*]}]="${MP} did not respond in $TIME_TILL_STALE sec. Seems to be stale." + CURRENT_STATE=${STATE_CRITICAL} + elif [ "$(bc <<< "$end_at > $crit_at")" == "1" ]; then + log "CRIT: ${MP} exceeded critical threshold ($CRIT_TIME sec.)" + ERR_MESG[${#ERR_MESG[*]}]="${MP} exceeded critical threshold ($CRIT_TIME sec.)" + CURRENT_STATE=${STATE_CRITICAL} + elif [ "$(bc <<< "$end_at > $warn_at")" == "1" ]; then + log "CRIT: ${MP} exceeded warning threshold ($WARN_TIME sec.)" + ERR_MESG[${#ERR_MESG[*]}]="${MP} exceeded warning threshold ($WARN_TIME sec.)" + if [ $CURRENT_STATE -lt $STATE_WARNING ]; then + CURRENT_STATE=${STATE_WARNING} fi - done + fi if ps -p $DFPID > /dev/null ; then $(kill -s SIGTERM $DFPID &>/dev/null) - ERR_MESG[${#ERR_MESG[*]}]="${MP} did not respond in $TIME_TILL_STALE sec. Seems to be stale." else ## if it not stales, check if it is a directory - ISRW=0 + ISRW=0 if [ ! -d ${MP} ]; then log "CRIT: ${MP} doesn't exist on filesystem" ERR_MESG[${#ERR_MESG[*]}]="${MP} doesn't exist on filesystem" + CURRENT_STATE=$STATE_CRITICAL ## if wanted, check if it is writable - elif [ ${WRITETEST} -eq 1 ]; then - ISRW=1 - ## in auto mode first check if it's readonly - elif [ ${WRITETEST} -eq 1 ] && [ ${AUTO} -eq 1 ]; then - ISRW=1 - for OPT in $(${GREP} -w ${MP} ${FSTAB} |awk '{print $4}'| sed -e 's/,/ /g'); do - if [ "$OPT" == 'ro' ]; then - ISRW=0 + elif [ ${WRITETEST} -eq 1 ]; then + ISRW=1 + ## in auto mode first check if it's readonly + elif [ ${WRITETEST} -eq 1 ] && [ ${AUTO} -eq 1 ]; then + ISRW=1 + for OPT in $(${GREP} -w ${MP} ${FSTAB} |awk '{print $4}'| sed -e 's/,/ /g'); do + if [ "$OPT" == 'ro' ]; then + ISRW=0 log "CRIT: ${TOUCHFILE} is not mounted as writable." ERR_MESG[${#ERR_MESG[*]}]="Could not write in ${MP} filesystem was mounted RO." - fi - done - fi - if [ ${ISRW} -eq 1 ]; then - TOUCHFILE=${MP}/.mount_test_from_$(hostname)_$(date +%Y-%m-%d--%H-%M-%S).$RANDOM.$$ - touch ${TOUCHFILE} &>/dev/null & - TOUCHPID=$! - for (( i=1 ; i<$TIME_TILL_STALE ; i++ )) ; do - if ps -p $TOUCHPID > /dev/null ; then - sleep 1 - else - break - fi - done - if ps -p $TOUCHPID > /dev/null ; then - $(kill -s SIGTERM $TOUCHPID &>/dev/null) - log "CRIT: ${TOUCHFILE} is not writable." - ERR_MESG[${#ERR_MESG[*]}]="Could not write in ${MP} in $TIME_TILL_STALE sec. Seems to be stale." - else - if [ ! -f ${TOUCHFILE} ]; then - log "CRIT: ${TOUCHFILE} is not writable." - ERR_MESG[${#ERR_MESG[*]}]="Could not write in ${MP}." - else - rm ${TOUCHFILE} &>/dev/null - fi - fi + CURRENT_STATE=$STATE_CRITICAL + fi + done + fi + if [ ${ISRW} -eq 1 ]; then + TOUCHFILE=${MP}/.mount_test_from_$(hostname)_$(date +%Y-%m-%d--%H-%M-%S).$RANDOM.$$ + touch ${TOUCHFILE} &>/dev/null & + TOUCHPID=$! + measure_exectime ${TOUCHPID} "(WRITE)" + if ps -p $TOUCHPID > /dev/null ; then + $(kill -s SIGTERM $TOUCHPID &>/dev/null) + log "CRIT: ${TOUCHFILE} is not writable." + ERR_MESG[${#ERR_MESG[*]}]="Could not write in ${MP} in $TIME_TILL_STALE sec. Seems to be stale." + CURRENT_STATE=$STATE_CRITICAL + else + if [ ! -f ${TOUCHFILE} ]; then + log "CRIT: ${TOUCHFILE} is not writable." + ERR_MESG[${#ERR_MESG[*]}]="Could not write in ${MP}." + CURRENT_STATE=$STATE_CRITICAL + else + rm ${TOUCHFILE} &>/dev/null + fi + fi fi fi @@ -418,14 +466,23 @@ if [[ "${FSTAB}" =~ "/tmp" ]]; then rm -f ${FSTAB} fi +# Write result (state and output) +declare -a STATESTR=("OK" "WARNING" "CRITICAL" "UNKNOWN") +echo -n "${STATESTR[$CURRENT_STATE]}: " + + if [ ${#ERR_MESG[*]} -ne 0 ]; then - echo -n "CRITICAL: " for element in "${ERR_MESG[@]}"; do echo -n ${element}" ; " done - echo - exit $STATE_CRITICAL +else + echo -n "all mounts were found (${MPS})" fi -echo "OK: all mounts were found (${MPS})" -exit $STATE_OK +echo "|${PERFDATA}" +exit $CURRENT_STATE +#!/usr/bin/env bash + +# -------------------------------------------------------------------- +# **** BEGIN LICENSE BLOCK ***** +