#!/bin/bash # ====================================================================== # # NAGIOS CLIENT CHECK :: FUNCTIONS # # ---------------------------------------------------------------------- # # Functions # execIfReady COMMAND [SLEEPTIME] [MAXTRIES] # execute a command max MAXTRIES times until it returns # exitcode 0 (or reaches limit) # ph.getOS get operating system as lowercase - centos|debian|ubuntu|... # ph.getOSMajor get OS Major version as integer, i.e. 7 on a CentOS7 # ph.getValueWithParam VALUE PARAMNAME "$@" # return default value or its override from command line # ph.hasParamoption PARAMNAME "$@" # check if a letter was used as command line option # ph.toUnit VALUE TO-UNIT # convert value i.e. "12M" into other unit i.e. "K" # ph.setStatus VALUE set a status # ph.setStatusByLimit VALUE WARNLIMIT CRITLIMIT # ph.status [TEXT] show status as Text # # ph.abort [TEXT] shows error message and exit with status unknown # ph.exit exit plugin (with set statuscode) # # ---------------------------------------------------------------------- # 2016-09-23 added getOS # 2019-10-29 added setExitcode # 2020-03-05 v1.2 <axel.hahn@iml.unibe.ch> switch to ph.* helper functions # 2020-09-01 v1.3 <axel.hahn@iml.unibe.ch> added ph.hasParamoption # 2022-08-31 v1.4 <axel.hahn@iml.unibe.ch> shellfix corrections # 2022-10-25 v1.5 <axel.hahn@iml.unibe.ch> handle empty value in ph.perfadd # 2023-01-30 v1.6 <axel.hahn@unibe.ch> check performance params 5+6 and show a warning if missing # 2023-02-16 v1.7 <axel.hahn@unibe.ch> adding a generic min and max value did not really help # 2024-04-24 v1.8 <axel.hahn@unibe.ch> fix unit conversion # ====================================================================== # nagios exit code values typeset -i ph_cfg__EXIT_OK=0 typeset -i ph_cfg__EXIT_WARNING=1 typeset -i ph_cfg__EXIT_CRITICAL=2 typeset -i ph_cfg__EXIT_UNKNOWN=3 declare ph_perfdatafile= # declare -a ph_perfdata typeset -i ph_perfcounter=0 # save command line params ph_cmd__params="$@" # abort a check and exit with status "unknown" function ph.abort(){ ph.setStatus "unknown" echo "$*" ph.exit } # check required binaries in the path # param(s) string name of binary to check with "which" command function ph.require(){ which $* >/dev/null if [ $? -ne 0 ]; then ph.setStatus "unknown" ph.status "$0 requires the following tools to run: $*" ph.exit fi } # ---------------------------------------------------------------------- # exit a check plugin function ph.exit(){ # echo ______________________________________________________________________ # echo "DEBUG: $0 $* -- leaving with _rc = $ph_cfg__EXIT_CODE -- Status $ph_cfg__EXIT_STATUS" ph.perfshow exit $ph_cfg__EXIT_CODE } # ---------------------------------------------------------------------- # detect LINUX DISTRO as lowercase # returns one of centos|debian|ubuntu|... function ph.getOS(){ local distro= if [ -z $distro ]; then # centos7, debian, manjaro, ubuntu distro=$( grep "^ID=" /etc/*-release | cut -f 2 -d "=" ) fi if [ -z $distro ]; then # debian6,7, ubuntu 10,12 .. maybe unneeded. distro=$( head -1 /etc/issue | grep "^[a-zA-Z]" | cut -f 1 -d " " ) fi # sanitize: lowercase, remove " distro=$( echo $distro | tr -d '"' | tr [:upper:] [:lower:] ) if [ -z $distro ]; then ph.abort "UNKNOWN: distro was not detected." fi echo "$distro" } # get OS MajorRelease # returns an integer, i.e. 7 on CentOS7 function ph.getOSMajor(){ local _version= _version=$( grep -E "^(VERSION_ID|DISTRIB_RELEASE)=" /etc/*-release | head -1 | cut -f 2 -d "=" | sed 's#"##g' | cut -f 1 -d "." ) if [ -z "$_version" ]; then _version="?" exit 1 fi echo "$_version" } # helper to use the default _value or override it with a found param # getValueWithParam [default] [parameter] "$@" function ph.getValueWithParam(){ local _value=$1 local _sParam=$2 local _opt shift 2 # trick I: allows ${_sParam} in case .. esac section shopt -s extglob # trick II: allows usage of getopts multiple times OPTIND=1 # trick III: changing getops params with a single param do not work ... # while getopts ":$_sParam:" opt; do while getopts ":a:b:c:d:e:f:g:h:i:j:k:l:m:n:o:p:q:r:s:t:u:v:w:x:y:z:" _opt; do # echo "DEBUG: testing ${opt} ..." case "${_opt}" in $_sParam) _value=$OPTARG ;; esac done echo "$_value" } # check if a letter was used as command line option and return as 0 (=no) or 1 (=yes) # ph.hasParamoption "h" "$@" # param string parameter(letter) to test # param string "$@" function ph.hasParamoption(){ local _sParam=$1 local _opt shift 1 # trick I: allows ${_sParam} in case .. esac section shopt -s extglob # trick II: allows usage of getopts multiple times OPTIND=1 # trick III: changing getops params with a single param do not work ... # while getopts ":$_sParam:" opt; do while getopts "abcdefghijklmnopqrstuvwxyz" _opt; do # echo "DEBUG: testing $_sParam in ${_opt} ..." case "${_opt}" in "$_sParam") echo "1" return 1 ;; esac done echo "0" return 0 } # set an exitcode for the plugin # # example: # in your plugin set an exitcode: # ph.setStatus "ok" # # param integer|string 0..3 or ok|warning|critical|unknown function ph.setStatus(){ case $1 in $ph_cfg__EXIT_OK|"ok"): ph_cfg__EXIT_CODE=$ph_cfg__EXIT_OK ph_cfg__EXIT_STATUS="OK" ;; $ph_cfg__EXIT_WARNING|"warning"): ph_cfg__EXIT_CODE=$ph_cfg__EXIT_WARNING ph_cfg__EXIT_STATUS="WARNING" ;; $ph_cfg__EXIT_CRITICAL|"critical"): ph_cfg__EXIT_CODE=$ph_cfg__EXIT_CRITICAL ph_cfg__EXIT_STATUS="CRITICAL" ;; $ph_cfg__EXIT_UNKNOWN|"unknown"): ph_cfg__EXIT_CODE=$ph_cfg__EXIT_UNKNOWN ph_cfg__EXIT_STATUS="UNKNOWN" ;; *) ph.abort "ERROR: wrong usage ... Status code [$1] is unknown ... please fix the plugin $0." esac } # set exit status by given _value and limits for warning and critical # it works in both directions with limits on lower end and higher end to # The mode is detected by given warning and critical limit # param integer _value of a test # param integer warning level # param integer critical level function ph.setStatusByLimit(){ typeset -i local _value=$1 typeset -i local _iWarnLimit=$2 typeset -i local _iCriticalLimit=$3 if [ $_iWarnLimit -gt $_iCriticalLimit ]; then _iWarnLimit=-$_iWarnLimit _iCriticalLimit=-$_iCriticalLimit _value=-$_value fi if [ $_value -lt $_iWarnLimit ]; then ph.setStatus "ok" else if [ $_value -ge $_iCriticalLimit ]; then ph.setStatus "critical" else ph.setStatus "warning" fi fi } # show status as OK|WARNING|CRITICAL|UNKNOWN # if you add params it will be shown behind with added carriage return # without additional params there is NO carriage return # # Example # ph.status # echo what was tested here # is the same like # ph.status "what was tested here" # function ph.status(){ echo -n "${ph_cfg__EXIT_STATUS}" test $# -gt 0 && echo ": $*" } # ---------------------------------------------------------------------- # # convert binary units # # ---------------------------------------------------------------------- # helper function for ph.toUnit: get scaling factor # example: 12M returns 2^30 from ending "M" # param value with ending scale [none]=1 K=Kilo M=Mega G=Giga function ph._getExp(){ local _unit _unit=$( echo $1 | sed "s#[0-9]##g" ) test -z "$_unit" && echo 1 # binary test "$_unit" = "K" && echo 2^10 test "$_unit" = "M" && echo 2^20 test "$_unit" = "G" && echo 2^30 test "$_unit" = "T" && echo 2^40 test "$_unit" = "P" && echo 2^50 # phyiscal # test "$_unit" = "m" && echo 10^-3 # test "$_unit" = "c" && echo 10^-2 # test "$_unit" = "d" && echo 10^-1 # test "$_unit" = "k" && echo 10^3 # test "$_unit" = "M" && echo 10^6 # test "$_unit" = "G" && echo 10^9 # echo "ERROR: unsupported value: $_value" # exit 1 } # convert a given binary value into another unit # example $( ph.toUnit 12M K ) converts 12 M(ega) into K(ilo) # --> return value will be 12288 (without K as suffix) # # param string value with optional single letter for unit, i.e. 12M # param char target unit function ph.toUnit(){ local _value=$1 local _unit=$2 local _multiply=`ph._getExp $_value` local _divisor=`ph._getExp $_unit` # echo "DEBUG ... $_divisor .. $_multiply" echo "$(echo $_value | tr -d "[:alpha:]" )*${_multiply}/$_divisor" | bc } # ---------------------------------------------------------------------- # # performance data # # ---------------------------------------------------------------------- # ---------------------------------------------------------------------- # PRIVATE FUNCTIONS # ---------------------------------------------------------------------- # get age of a file in sec # param string filename to test # function ph.getFileAge(){ echo $(($(date +%s) - $(date +%s -r "$1"))) } # get file for storage of last value # global string dir_data custom path; default is /tmp/icinga_counter/ # param string varName variable name of a value to store function ph._getStorefile(){ local varName=$1 local mydir="/tmp/icinga_counter" test -n "$dir_data" && mydir="${dir_data}/_counter" local _basename _basename=$(basename $0) test -d "${mydir}" || mkdir -p "${mydir}" echo "${mydir}/${_basename}${varName}.lastvalue" } # save a value - needed for gdAddDeltaData # param string varName variable name of a value to store # param * value value as float/ integer function ph._savecounter() { local varName=$1 local value=$2 local sStoreFile=$(ph._getStorefile "${varName}") #echo "DEBUG: `date +%s`:${value} \> ${sStoreFile}" # echo "`date +%s`:${value}" > "${sStoreFile}" echo ${value} > "${sStoreFile}" } # get age of last storage of a value # used in perfdeltaspeed # param string varName variable name of a value to store function ph._getageoflastvalue() { local varName=$1 local sStoreFile=$(ph._getStorefile "${varName}") ph.getFileAge "${sStoreFile}" # local ilast=`cat "${sStoreFile}" 2>/dev/null | cut -f 1 -d ":" ` # local inow=`date +%s` # echo $(( ${inow}-${ilast}+1 )) } # get the last value # used in perfdeltaspeed # param string varName variable name of a value to store function ph._readlastvalue(){ local varName=$1 local sStoreFile=$(ph._getStorefile "${varName}") # cat "${sStoreFile}" 2>/dev/null | cut -f 2 -d ":" cat "${sStoreFile}" 2>/dev/null } # init usage for performance data # it creates a filename based on the started check script function ph._perfinit(){ local _basename _basename=$(basename $0) ph_perfdatafile="/tmp/perfdata_$(echo $_basename | sed "s#[^a-z0-9\-]#_#g")" rm -f "${ph_perfdatafile}" 2>/dev/null } # generate label for performance data value function ph._getperflabel(){ echo "$1" | tr [:upper:] [:lower:] | sed "s#[^a-z0-9\-]##g" } # get speed of change of a counter value # param1: string variable name # param2: integer value function ph.perfdeltaspeed(){ local varName=$1 local value=$2 local deltaUnit=$3 local isFloat=$4 local bcParam= typeset -i deltaFactor=1 test "$deltaUnit" = "s" && deltaFactor=1 test "$deltaUnit" = "sec" && deltaFactor=1 test "$deltaUnit" = "m" && deltaFactor=60 test "$deltaUnit" = "min" && deltaFactor=60 test "$isFloat" = "float" && bcParam="-l" # get last value local lastvalue=$(ph._readlastvalue "${varName}") if [ "$lastvalue" = "" ]; then # retvalue="[no last value]" retvalue="" else local f=1 if [ $value -lt 0 ]; then f=-1 fi if [ $(( $lastvalue * $f )) -gt $(( $value * $f )) ]; then # retvalue="[reset data]" retvalue="" else local iage=$(ph._getageoflastvalue "${varName}") test $iage = 0 && iage=1 local delta=$(echo "${value}-$lastvalue" | bc $bcParam ) local deltaspeed=$(echo "${delta}*${deltaFactor}/(${iage})" | bc $bcParam) retvalue=$deltaspeed fi fi ph._savecounter "${varName}" "${value}" #echo DEBUG sData="${sData} '${varName}'=${retvalue}" # sGDData="${sGDData} '${varName}'=${retvalue}" echo "${retvalue}" # DEBUG - wird in Nagios sichtbar # gdAddLabel "... ${varName} absolute: ${value} -> delta: ${delta} -> derive: ${retvalue} ($iage sec)" } # add performance data # # OUTPUT-SYNTAX # 'label'=value[UOM];[warn];[crit];[min];[max] # procs=401;200;300;0; # # example # ph.perfadd function ph.perfadd(){ if [ -z "$ph_perfdatafile" ]; then ph._perfinit fi local _label=$(ph._getperflabel "$1") local _value=$2 local _w=$3 local _c=$4 local _min=$5 local _max=$6 test -z "$_value" && _value=0 # adding a generic min and max value did not really help # test -z "$_min" && echo "WARNING from ph.perfadd: missing param 5 for minimum - setting it to 0" # test -z "$_min" && _min=0 # test -z "$_max" && echo "WARNING from ph.perfadd: missing param 6 for maxiumun - setting it to 100" # test -z "$_max" && _max=100 # ... so we add them if tey exist only local _minmax= test -n "$_min" && _minmax+=";$_min" test -n "$_max" && _minmax+=";$_max" # echo "${_label}=${_value};${_w};${_c};${_min};${_max}" >>"${ph_perfdatafile}" echo "${_label}=${_value};${_w};${_c}${_minmax}" >>"${ph_perfdatafile}" } # output of performance data # remark: this function is called in ph.exit too function ph.perfshow(){ if [ ! -z "$ph_perfdatafile" ]; then if [ -f "$ph_perfdatafile" ]; then echo -n " |" cat "${ph_perfdatafile}" | tr "\n" " " rm -f "${ph_perfdatafile}" fi fi } # ---------------------------------------------------------------------- # execute a command and repeat it N times before aborting # param string command line to execute # param integer sleeptime in sec before repeating again; default: 5 # param integer max number of tries; default: 3 function ph.execIfReady(){ local _cmd2run=$1 local _iSleeptime=${2:-5} local _iMaxTry=${3:-3} typeset -i local _iCount=0 typeset -i local _rc=1 local tmpfile=/tmp/execIfRead_out_$$ while [ $_rc -ne 0 ]; do eval "${_cmd2run}" >$tmpfile 2>&1 _rc=$? if [ $_rc -ne 0 ]; then _iCount=$_iCount+1 if [ $_iCount -ge $_iMaxTry ]; then rm -f $tmpfile ph.setStatus "unknown" ( ph.status "Aborting because command failed $_iMaxTry times: [$_cmd2run] - see $0" ) > /dev/tty rm -f $tmpfile ph.exit fi sleep $_iSleeptime fi done cat $tmpfile rm -f $tmpfile } # ---------------------------------------------------------------------- # init ph.setStatus "ok" # ----------------------------------------------------------------------