Skip to content
Snippets Groups Projects
check_haproxy_status 7.19 KiB
#!/bin/bash
# ======================================================================
#
# NAGIOS CLIENT CHECK :: haproxy status
# show current requests to frontend and backend
#
# ----------------------------------------------------------------------
# settings values are parsed by a given section.
# if it fails it scans other sections .. finally default and global
# see getCfgvar
# ----------------------------------------------------------------------
# 2020-04-27  v1.0  <axel.hahn@iml.unibe.ch>
# 2020-04-28  v1.1  <axel.hahn@iml.unibe.ch>  added status and max connections for frontend and backend
# 2020-04-29  v1.2  <axel.hahn@iml.unibe.ch>  parse ini section by given section
# 2020-06-03  v1.3  <axel.hahn@iml.unibe.ch>  added ping to each backend server
# 2020-12-03  v1.4  <axel.hahn@iml.unibe.ch>  added support of multiple front- and backends
# 2021-12-14  v1.5  <axel.hahn@iml.unibe.ch>  use updated haproxy paser in sourced file
# 2022-04-01  v1.6  <axel.hahn@iml.unibe.ch>  use wget default params; shell fixes
# 2022-10-21  v1.7  <axel.hahn@unibe.ch>      remove grep: warning: stray \ before white space
# 2023-07-28  v1.8  <axel.hahn@unibe.ch>      add help page
# ======================================================================

. $(dirname $0)/inc_pluginfunctions

export self_APPVERSION=1.8

. $(dirname $0)/inc_haproxy_cfg.sh

tmpfile=/tmp/check_haproxy_status_$$
tmpfile2=/tmp/check_haproxy_status2_$$
tmpfileping=/tmp/check_haproxy_status3_$$

# ----------------------------------------------------------------------
# functions
# ----------------------------------------------------------------------

# find a column number of a given field name in haproxy csv
function getColnumber(){
  cat $tmpfile | head -1 | sed 's#,#\n#g' | grep -n "^$1$" | cut -f 1 -d ':'
}

# get stats data from column [name]
function getColumn(){
  typeset -i _iNumber=$1
  local _filter=$2
  test -z "$_filter" && _filter='.*'
  grep "$_filter" $tmpfile | grep -v "stats_frontend" | cut -f 2,$_iNumber -d ','
}

# get sum of all stats data from column [name]
function getColSum(){
  getColumn $1 $2 | rev | cut -f 1 -d "," | rev | while read myvalue
  do
    echo +$myvalue
  done
}

function checkStatus(){
  local _value=$1
  local _must=$2
  echo $_value | grep "$_must" >/dev/null
  if [ $? -eq 0 ]; then
    echo $_value | grep -v "$_must" | grep "." >/dev/null && echo ERROR
  else
    echo ERROR
  fi
}

# show help
function showHelp(){
    local _self; _self=$(basename $0)
cat <<EOF
$( ph.showImlHelpHeader )

Check HA Proxy statistics on frontend and backend.
It shows if the status is OPEN or UP and show counts of current connectons.

The plugin reads $HAPROXYcfgfile to detect the required statistics url and get
csv data.
It requires wget to handle the http request.

Non OK values occur:
  UNKNOWN - if $HAPROXYcfgfile cannot be read
          - haproxy is not in http mode
          - the url wasn't detected
          - the detected status url doesn't send a response
  ERROR - a frontend status is not OPEN
        - a backend status is not UP

The plugin sends performance data.

SYNTAX:
$_self [-h]

OPTIONS:

    -h or --help   show this help.

EXAMPLE:

    $_self

EOF
}

# ----------------------------------------------------------------------
# MAIN
# ----------------------------------------------------------------------

# --- check param -h
case "$1" in
    "--help"|"-h")
        showHelp
        exit 0
        ;;
    *)
esac

ph.require wget

if [ ! -f $HAPROXYcfgfile ]; then
  ph.abort "UNKNOWN: config file does not exist: $HAPROXYcfgfile"
fi

if ! cat $HAPROXYcfgfile >/dev/null; then
  ph.abort "UNKNOWN: unable to read ha proxy config $HAPROXYcfgfile ... $(ls -l $HAPROXYcfgfile)"
fi

if ! cat $HAPROXYcfgfile | grep " mode .*http" >/dev/null; then
  ph.abort "UNKNOWN: haproxy is not in http mode"
fi


# ----------------------------------------------------------------------
# build url
# ----------------------------------------------------------------------

url=$( getStatusUri )
if [ -z "$url" ]; then
  ph.abort "UNKNOWN: Unable to detect url for status page."
fi

url="${url};csv;norefresh"

# remove password for showing url in output
urlmasked=$( echo $url | sed "s#\(://\)\(.*@\)#\1#g" )

# ----------------------------------------------------------------------
# check output
# ----------------------------------------------------------------------

# --- get status page
wget -T 5 -t 1 --no-check-certificate -O $tmpfile $url 2>/dev/null
if [ $? -ne 0 ]; then
  rm -f $tmpfile
  ph.abort "UNKNOWN: url $urlmasked did not respond. $(wget -T 5 -t 1 --no-check-certificate -O - -S $url)"
fi


colLimit=$(getColnumber "slim")
colCurrentConnections=$(getColnumber "scur")
colStatus=$(getColnumber "status")

statusFront=$(getColumn $colStatus ",FRONTEND," | cut -f 2 -d "," | tr "\n" " ")
statusBack=$( getColumn $colStatus ",BACKEND,"  | cut -f 2 -d "," | tr "\n" " ")

typeset -i iMaxConnFront=$(getColSum $colLimit ",FRONTEND,")
typeset -i iMaxConnBack=$( getColSum $colLimit ",BACKEND,")

typeset -i iFrontend=$(getColSum $colCurrentConnections ",FRONTEND,")
typeset -i iFrontendFree=$iMaxConnFront-$iFrontend


typeset -i iBackend=$( getColSum $colCurrentConnections ",BACKEND," )
typeset -i iBackendFree=$iMaxConnBack-$iBackend

statusExt=

if [ "$( checkStatus ""$statusFront"" 'OPEN' )" = "ERROR" ]; then
  ph.setStatus "critical"
  statusExt="${statusExt}ERROR: a frontend status is not OPEN\n"
fi

if [ "$( checkStatus ""$statusBack"" 'UP' )" = "ERROR" ]; then
  ph.setStatus "critical"
  statusExt="${statusExt}ERROR: a backend status is not UP\n"
fi


ph.status "FRONT: $statusFront - ${iFrontend} (max: ${iMaxConnFront}) .. BACK: $statusBack - $iBackend (max: ${iMaxConnBack})"
test -z "$statusExt" || echo "${statusExt}"
# echo; echo DEBUG: ; cat $tmpfile

ph.perfadd "frontend"      "${iFrontend}"
ph.perfadd "frontend-max"  "${iMaxConnFront}"
ph.perfadd "frontend-free" "${iFrontendFree}"

ph.perfadd "backend"       "${iBackend}"
ph.perfadd "backend-max"   "${iMaxConnBack}"
ph.perfadd "backend-free"  "${iBackendFree}"

# ----------------------------------------------------------------------
# show server connections and summary (FRONTEND/ BACKEND)
# ----------------------------------------------------------------------

getColumn $colCurrentConnections | sed -n "2,$ p" >$tmpfile2
while read line
do
  srv=$(echo $line  | cut -f 1 -d ",")
  val=$(echo $line  | cut -f 2 -d ",")
  # label=$(echo $srv | tr [:upper:] [:lower:])

  # echo "${srv} - ${val}"
  echo -n "${srv} - ${val} "

  # v1.3: if it is a servername then ping to it
  # echo ${srv} | grep "^[a-z0-9\-\.]" >/dev/null
  # if [ $? -eq 0 ]; then
  #         ping -c 1 ${srv} >$tmpfileping
  #         grep "\ 0%\ packet\ loss" $tmpfileping >/dev/null
  #         if [ $? -eq 0 ]; then
  #           echo "- Ping OK"
  #         else
  #           echo "- Ping FAILED"
  #           cat $tmpfileping
  #           test "`ph.status`" = "OK" && ph.setStatus "warning"
  #         fi
  #         rm -f $tmpfileping
  # else
  #         echo
  # fi

  # performance data: send summary only
  # echo ${srv} | egrep "(FRONTEND|BACKEND)" >/dev/null && ph.perfadd "${label}" "${val}" 

done < $tmpfile2

rm -f $tmpfile $tmpfile2 2>/dev/null
ph.exit

# ----------------------------------------------------------------------