#!/bin/bash # ====================================================================== # # Icinga/ Nagios Check # COUCHDB # # ---------------------------------------------------------------------- # # REQUIREMENTS: # - curl # # SYNTAX: # - check_couchdb [-h] [-c CFGFILE] -m MODE # # ---------------------------------------------------------------------- # 2023-08-28 v0.1 <axel.hahn@unibe.ch> first lines # 2023-08-28 v0.2 <axel.hahn@unibe.ch> first check "up" # 2023-08-28 v0.3 <axel.hahn@unibe.ch> add check "replication" # 2023-08-28 v0.4 <axel.hahn@unibe.ch> add check "pending" # 2023-08-28 v0.5 <axel.hahn@unibe.ch> add checks "open_databases" + "open_os_files" # 2023-08-28 v0.6 <axel.hahn@unibe.ch> add check "httpd_methods" # 2023-08-29 v0.7 <axel.hahn@unibe.ch> add check "httpd" # 2023-08-29 v0.8 <axel.hahn@unibe.ch> add check "httpd_status_codes" # ====================================================================== . $(dirname $0)/inc_pluginfunctions export self_APPVERSION=0.8 cfgfile=/etc/icingaclient/.couchdb export RESPONSE # ---------------------------------------------------------------------- # FUNCTIONS # ---------------------------------------------------------------------- # show help function showHelp(){ local _self; _self=$(basename $0) cat <<EOF $( ph.showImlHelpHeader ) Show couchdb status. SYNTAX: $_self [-h] [-t FILE] -m MODE OPTIONS: -h or --help show this help. -c CFGFILE set a custom config file default: /etc/icingaclient/.couchdb -m MODE test a value; for debugging purposes the full json response will be shown MODE is one of httpd Show counters for http request types httpd_methods Show counters for http request methods httpd_status_codes Show counters per http status code open_databases show number of open databases open_os_files show number of file descriptors CouchDB has open replication show last replication status pending show count of pending updates for nodes, dbs and users replication show last replication status up show general couchdb health status EXAMPLE: $_self -m up Check if couchdb is up and running $_self -m httpd_methods Show counters and change rate per sec of GET, POST, and other methods $_self -c /opt/couchdb/myconfig.sh -m up Source another config to define COUCH_URL EOF } # get couchdb status by given url and a filter # The check aborts here if no data were found. # Response is written into global var RESPONSE # # param string url to request; the part behind couchdb base url # param string string to search for in the content function abortOnWrongResponse(){ _path="$1" _filter="$2" RESPONSE=$( curl -s "${COUCH_URL}${_path}" ) if ! grep "$_filter" <<< "$RESPONSE" >/dev/null ; then echo "ERROR: Wrong response from $_path - it does not contain $_filter" curl -si "${COUCH_URL}${_path}" ph.abort fi } # ---------------------------------------------------------------------- # MAIN # ---------------------------------------------------------------------- # --- check param -h case "$1" in "--help"|"-h") showHelp exit 0 ;; *) esac ph.require jq sMode=$(ph.getValueWithParam '' "m" "$@") cfgfile=$(ph.getValueWithParam "${cfgfile}" "c" "$@") if [ ! -f "$cfgfile" ]; then echo "ERROR: Config file [${cfgfile}] does not exist." ph.abort fi . "$cfgfile" || exit 1 if [ -z "$COUCH_URL" ]; then echo "ERROR: I have no couchdb url + authentication yet." echo "set 'export COUCH_URL=http://USER:PW@localhost:5984' in $cfgfile" ph.abort fi # ---------------------------------------------------------------------- case "${sMode}" in # ............................................................ "httpd") REQ=/_node/_local/_stats/couchdb/httpd abortOnWrongResponse "${REQ}" '"value":' typeset -i _iValue typeset -i _iDelta _status=$( jq 'with_entries(.value |= .value)' <<< "${RESPONSE}" | grep '^ "' | grep -v '\{' | sed 's#[", ]##g' ) # this returns: # aborted_requests:0 # bulk_requests:0 # requests:185531 # ... ph.status "Couchdb :: Http request methods" printf "%30s %10s %10s\n" "Property" "Counter" "Delta" | tr ' ' '_' for myVar in $( grep "^[a-z\_]" <<< "$_status" | cut -f 1 -d ':' ) do _iValue=$( grep "^${myVar}:" <<< "$_status" | cut -f 2 -d ':' ) _label="couchdb-hp-${myVar//_/-}" # underscrore to minus _iDelta=$( ph.perfdeltaspeed "${_label}" $_iValue ) printf "%30s %10s %10s per sec\n" "$myVar" $_iValue $_iDelta ph.perfadd "${myVar}" "$_iDelta" "" "" done echo ;; # ............................................................ "httpd_methods") REQ=/_node/_local/_stats/couchdb/httpd_request_methods abortOnWrongResponse "${REQ}" '"value":' typeset -i _iValue typeset -i _iDelta _status=$( jq 'with_entries(.value |= .value)' <<< "${RESPONSE}" | sed 's#[", ]##g' | grep '^[A-Z]' ) # this returns: # COPY:0 # DELETE:1 # GET:188183 # HEAD:0 # ... ph.status "Couchdb :: Http request methods" printf "%10s %10s %10s\n" "Method" "Counter" "Delta" | tr ' ' '_' for myVar in $( grep "^[A-Z]" <<< "$_status" | cut -f 1 -d ':' ) do _iValue=$( grep "^$myVar:" <<< "$_status" | cut -f 2 -d ':' ) _label="couchdb-hm-${myVar//_/-}" # underscrore to minus _iDelta=$( ph.perfdeltaspeed "${_label}" $_iValue) printf "%10s %10s %10s per sec\n" "$myVar" $_iValue $_iDelta ph.perfadd "${myVar}" "$_iDelta" "" "" done # echo "${_status}" | jq ;; # ............................................................ "httpd_status_codes") REQ=/_node/_local/_stats/couchdb/httpd_status_codes abortOnWrongResponse "${REQ}" '"value":' typeset -i _iValue typeset -i _iDelta _status=$( jq 'with_entries(.value |= .value)' <<< "${RESPONSE}" | sed 's#[", ]##g' | grep '^[1-9]' ) # this returns: # 200:199460 # 201:0 # 202:0 # 204:0 # ... ph.status "Couchdb :: Http status codes" printf "%10s %10s %10s\n" "Status" "Counter" "Delta" | tr ' ' '_' for myVar in $( grep "^[1-9]" <<< "$_status" | cut -f 1 -d ':' ) do _iValue=$( grep "^$myVar:" <<< "$_status" | cut -f 2 -d ':' ) _label="couchdb-hs-${myVar//_/-}" # underscrore to minus _iDelta=$( ph.perfdeltaspeed "${_label}" $_iValue) printf "%10s %10s %10s per sec\n" "$myVar" $_iValue $_iDelta ph.perfadd "http${myVar}" "$_iDelta" "" "" done ;; # ............................................................ "open_databases"|"open_os_files") REQ=/_node/_local/_stats/couchdb/${sMode} abortOnWrongResponse "${REQ}" '"value":' typeset -i _iValue # descr=$( jq '.desc' <<< "${RESPONSE}" | tr -d '"') _iValue=$( jq '.value' <<< "${RESPONSE}" ) ph.perfadd "${sMode}" "$_iValue" "" "" ph.status "Couchdb :: ${sMode} = $_iValue" # echo "$descr" # echo "Reponse: of ${REQ}"; # echo "${RESPONSE}" | jq ;; # ............................................................ "pending") REQ=/_up abortOnWrongResponse "${REQ}" '"status":"' _status=$( jq '.seeds[] | .pending_updates' <<< "${RESPONSE}" | grep -v "null" | tr -d '"') typeset -i _iSumme typeset -i _iTotal _iTotal=0 for myvar in _nodes _dbs _users do _iSumme=0 for myvalue in $( grep "$myvar" <<< "$_status" | cut -f 2 -d ':' | tr -d ',') do _iSumme+=$myvalue test "$myvalue" -gt 0 && ph.setStatus warning done ph.perfadd "$myvar" "$_iSumme" "" "" _iTotal+=$_iSumme done ph.status "Couchdb :: pending updates: $_iTotal (values below 'pending_updates' in ${REQ})" echo "Reponse: of ${REQ}"; echo "${RESPONSE}" | jq ;; # ............................................................ "replication") REQ=/_up abortOnWrongResponse "${REQ}" '"status":"' _status=$( jq '.seeds[] | .last_replication_status' <<< "${RESPONSE}" | grep -v "null" | tr -d '"') # there can be multiple sections "seeds" and multiple line responses. # remove all lines with "ok" and check if there is any "bad content" left _nonok=$( echo "$_status" | grep -v "ok" ) if [ -n "$_nonok" ]; then ph.setStatus critical fi ph.status "Couchdb :: replication (values 'last_replication_status' in ${REQ} are '$_status')" echo "Reponse: of ${REQ}"; echo "${RESPONSE}" | jq ;; # ............................................................ "up") REQ=/_up abortOnWrongResponse "${REQ}" '"status":"' _status=$( jq '.status' <<< "${RESPONSE}" | tr -d '"') if ! echo "$_status" | grep "ok" >/dev/null; then ph.setStatus critical fi ph.status "Couchdb :: health status" echo "Reponse of ${REQ}: "; echo "${RESPONSE}" | jq ;; # ............................................................ *) echo "ERRROR: [${sMode}] is an INVALID mode" showHelp ph.abort esac ph.exit # ----------------------------------------------------------------------