From 1e7b62e20d29ea382c35a23b78f12ad434d501b1 Mon Sep 17 00:00:00 2001 From: "Hahn Axel (hahn)" <axel.hahn@iml.unibe.ch> Date: Thu, 10 Sep 2020 18:39:09 +0200 Subject: [PATCH] add mysqlserver checks (work in progress so far) --- check_mysqlserver | 300 +++++++++++++++++++++++++++++++++++++++++ inc_pluginfunctions | 99 +++++++++----- inc_pluginfunctions.md | 15 +++ 3 files changed, 378 insertions(+), 36 deletions(-) create mode 100755 check_mysqlserver diff --git a/check_mysqlserver b/check_mysqlserver new file mode 100755 index 0000000..45e7d65 --- /dev/null +++ b/check_mysqlserver @@ -0,0 +1,300 @@ +#!/bin/bash +# ====================================================================== +# +# !!! WORK IN PROGRESS !!! DO NOT USE YET !!! +# +# Check MYSQL / MARIADB SERVER +# +# requirements: +# - mysql client +# +# installation: +# - execute check_mysqlserver -i +# +# ---------------------------------------------------------------------- +# 2020-08-xx v0.0 <axel.hahn@iml.unibe.ch> +# ====================================================================== + + +. `dirname $0`/inc_pluginfunctions +_version="0.1" + +# --- set HOME +HOME=/etc/icinga2-passive-client +# cd $( dirname $0) +# cd .. +# HOME=$( pwd ); export HOME +# cd - >/dev/null + +# --- other vars... +cfgfile=$HOME/.my.cnf +myuser=icingamonitor +datafile=/tmp/mysqlvars.out + +# new line +NL=" +" + +out= + +# ---------------------------------------------------------------------- +# FUNCTIONS +# ---------------------------------------------------------------------- + +# uninstall database user +function _uninstall(){ + echo UNINSTALL ... + mysql -e "DROP user $myuser@localhost ;" +} + +# (re)install database user for monitoring +function _install(){ + echo INSTALLING ... + local pwlength=64 + + echo "- check mysql connection..." + mysql -e ";" + if [ $? -ne 0 ]; then + echo "ERROR: mysql connect without password failed." + exit 1 + fi + + echo "- creating mysql user $myuser@localhost with random password ($pwlength chars)..." + mypw=$( head /dev/urandom | tr -dc A-Za-z0-9 | head -c $pwlength ) + + mysql -e "CREATE USER $myuser@localhost IDENTIFIED BY '$mypw';" + if [ $? -ne 0 ]; then + echo "ERROR: mysql command to create user failed." + exit 1 + fi + echo "- grant SELECT on mysql tables ..." + mysql -e "GRANT SELECT ON mysql.* TO $myuser@localhost;" + if [ $? -ne 0 ]; then + echo "ERROR: mysql command to grant permissions failed." + exit 1 + fi + + echo "- flush privileges ..." + mysql -e "FLUSH PRIVILEGES;" + + echo "- creating config file $cfgfile ... " + cat >$cfgfile <<EOF +# +# generated on `date` +# +[client] +user=$myuser +host=localhost +password=$mypw + +EOF + ls -l $cfgfile + if [ $? -ne 0 ]; then + echo "ERROR: creation of config file failed." + exit 1 + fi +} + +function _usage(){ + cat <<EOH +______________________________________________________________________ + +CHECK MYSQL SERVER :: v${_version} + +(c) Institute for Medical Education - Univerity of Bern +Licence: GNU GPL 3 +______________________________________________________________________ + +USAGE: + `basename $0` [OPTIONS] -m METHOD + +OPTIONS: + -h this help + -i install monitoring user (must be executed as root) + -u uninstall monitoring user (must be executed as root) + +PARAMETERS: + -m method; valid methods are: + connections current/ max connections + connects connects per min and aborted connections/ clients + commands current running statements insert, select, ... + +EXAMPLES: + `basename $0` -i + `basename $0` -m commands + +EOH +} + +function _mysqlreadvars(){ + mysql -e "SHOW GLOBAL VARIABLES ;" --skip-column-names >$datafile + mysql -e "SHOW STATUS ;" --skip-column-names >>$datafile +} + +function _mysqlgetvar() { + local sVarname=$1 + grep "^$sVarname[^_a-z]" ${datafile} | awk '{ print $2 }' +} +# get a value from mysql status output +# param string variable name +# param string prefix to remove from beginning of variable +# param (set) flag if it is a delta value +function _mysqlrendervar() { + local sVarname=$1 + local sRemove=$2 + local sDeltaUnit=$3 + local sFloat=$4 + + # local iValue=`grep "^$sVarname[^_a-z]" ${datafile} | awk '{ print $2 }'` + local iValue=`_mysqlgetvar "$sVarname"` + if [ "$iValue" = "" ]; then + ph.abort "no value for ${sVarname}" + fi + + # get label for perfdata + local sLabel=`echo ${sVarname} | sed "s#^${sRemove}##g"` + + if [ ! -z $sDeltaUnit ]; then + local iSpeed=` ph.perfdeltaspeed "mysql-${sVarname}" ${iValue} $sDeltaUnit $sFloat` + out="${out}${sLabel}: ${iValue} ... delta = ${iSpeed} per $sDeltaUnit${NL}" + ph.perfadd "${sLabel}" "${iSpeed}" + else + out="${out}${sLabel}: ${iValue}${NL}" + ph.perfadd "${sLabel}" "${iValue}" + fi + +} + +function _mysqlrenderdelta() { + local deltaUnit=$3 + test -z $deltaUnit && deltaUnit="sec" + _mysqlrendervar "$1" "$2" $deltaUnit $4 +} +# ---------------------------------------------------------------------- +# MAIN +# ---------------------------------------------------------------------- + +bOptInstall=` ph.hasParamoption "i" "$@"` +bOptUninstall=` ph.hasParamoption "u" "$@"` +bOptHelp=` ph.hasParamoption "h" "$@"` + +if [ $bOptHelp -eq 1 -o $# -lt 1 ]; then + _usage + exit 0 +fi + +# --- check required tools +ph.reqire mysql + +# --- install +if [ $bOptInstall -eq 1 -a "$( whoami )" = "root" ]; then + if [ -f $cfgfile ]; then + ph.status "SKIP installation. config file already exists: $cfgfile." + ph.exit + fi + HOME=/root + export HOME + + _uninstall + _install + + ph.status "Installation was done" + ph.exit +fi + +# --- uninstall +if [ $bOptUninstall -eq 1 -a "$( whoami )" = "root" ]; then + HOME=/root + + _uninstall + rm -f $cfgfile + + ph.status "Uninstalled." + ph.exit +fi + + +# --- check installation +grep $myuser $cfgfile >/dev/null 2>/dev/null +if [ $? -ne 0 ]; then + ph.abort "MYSQL access not possible yet. You need to install the monitoring user first: as root execute `basename $0` -i" +fi + +# ---------------------------------------------------------------------- + +# --- get data +_mysqlreadvars +if [ $? -ne 0 ]; then + rm -f $datafile + ph.abort "MYSQL access not possible. Maybe Mysql is not running??" +fi + +# --- set optional limits +# typeset -i iWarnLimit=` ph.getValueWithParam 0 w "$@"` +# typeset -i iCriticalLimit=` ph.getValueWithParam 0 c "$@"` + +sMode=`ph.getValueWithParam '' m "$@"` + +case "${sMode}" in + + "connections") + descr="current/ max connections" + + _mysqlrendervar max_connections + _mysqlrendervar Max_used_connections + _mysqlrendervar Threads_connected + ;; + + "connects") + descr="connects per min and aborted connections/ clients" + _mysqlrenderdelta Connections "" min float + _mysqlrenderdelta Aborted_clients "" min float + _mysqlrenderdelta Aborted_connects "" min float + ;; + + "commands") + descr="currently executed commands" + _mysqlrendervar Com_delete "Com_" + _mysqlrendervar Com_insert "Com_" + _mysqlrendervar Com_replace "Com_" + _mysqlrendervar Com_select "Com_" + _mysqlrendervar Com_update "Com_" + ;; + + "qcache-blocks") + descr="query cache blocks" + _mysqlgetvar "have_query_cache" | grep "YES" >/dev/null || ph.abort "Query cache is not active" + _mysqlrendervar Qcache_total_blocks "Qcache_" + _mysqlrendervar Qcache_free_blocks "Qcache_" + ;; + + "qcache-queries") + descr="query cache data" + _mysqlgetvar "have_query_cache" | grep "YES" >/dev/null || ph.abort "Query cache is not active" + _mysqlrendervar Qcache_queries_in_cache "Qcache_" + _mysqlrenderdelta Qcache_not_cached "Qcache_" min float + _mysqlrenderdelta Qcache_lowmem_prunes "Qcache_" min float + ;; + "qcache-hits") + descr="query cache hits" + _mysqlgetvar "have_query_cache" | grep "YES" >/dev/null || ph.abort "Query cache is not active" + _mysqlrendervar Qcache_hits "Qcache_" + _mysqlrendervar Qcache_inserts "Qcache_" + _mysqlrendervar Qcache_not_cached "Qcache_" + + ;; + + *) + echo ERRROR: [${sMode}] is an INVALID mode + _usage + ph.abort + +esac + +ph.status "Mysql $sMode :: $descr" +echo "$out" + +rm -f $datafile +ph.exit + +# ---------------------------------------------------------------------- \ No newline at end of file diff --git a/inc_pluginfunctions b/inc_pluginfunctions index 35d6321..36cf81e 100644 --- a/inc_pluginfunctions +++ b/inc_pluginfunctions @@ -6,20 +6,29 @@ # ---------------------------------------------------------------------- # # Functions -# ph.abort [TEXT] shows error message and exit with status unknown -# ph.exit exit plugin (with set statuscode) +# 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 "$@" +# 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 # ====================================================================== @@ -30,10 +39,11 @@ typeset -i ph_cfg__EXIT_CRITICAL=2 typeset -i ph_cfg__EXIT_UNKNOWN=3 declare ph_perfdatafile= -declare -a ph_perfdata +# declare -a ph_perfdata typeset -i ph_perfcounter=0 - +# save command line params +ph_cmd__params="$@" # abort a check and exit with status "unknown" @@ -129,6 +139,37 @@ function ph.getValueWithParam(){ 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: @@ -325,7 +366,20 @@ function ph._getperflabel(){ function ph.perfdeltaspeed(){ local varName=$1 local value=$2 + local deltaUnit=$3 + local isFloat=$4 + + local bcParam= + typeset -i newvalue=0 + 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}"` @@ -344,9 +398,9 @@ function ph.perfdeltaspeed(){ local iage=`ph._getageoflastvalue "${varName}"` test $iage = 0 && iage=1 - local delta=$(( ${value}-$lastvalue )) - local newvalue=$(( ${delta}/(${iage}) )) - retvalue=$newvalue + local delta=`echo "${value}-$lastvalue" | bc $bcParam ` + local deltaspeed=`echo "${delta}*${deltaFactor}/(${iage})" | bc $bcParam` + retvalue=$deltaspeed fi fi ph._savecounter "${varName}" "${value}" @@ -359,32 +413,6 @@ function ph.perfdeltaspeed(){ # gdAddLabel "... ${varName} absolute: ${value} -> delta: ${delta} -> derive: ${retvalue} ($iage sec)" } -# W.I.P. -# add performance data with total counters and store change speed of it -# i.e. network byte or package counter in /proc/net/dev -# -# OUTPUT-SYNTAX -# 'label'=value[UOM];[warn];[crit];[min];[max] -# procs=401;200;300;0; -# -# example -# ph.perfadd -function ph.perfaddtotal(){ - - local _label=`ph._getperflabel "$1"` - - local _value=$2 - - - local _w=$3 - local _c=$4 - local _min=$5 - local _max=$6 - - ph.perfadd - echo "${_label}=${_value};${_w};${_c};${_min};${_max}" >>${ph_perfdatafile} -} - # add performance data # # OUTPUT-SYNTAX @@ -430,7 +458,7 @@ function ph.perfshow(){ # param string command line to execute # param integer sleeptime in sec before repeating again; default: 5 # param integer max number of tries; default: 3 -ph.execIfReady(){ +function ph.execIfReady(){ local _cmd2run=$1 local _iSleeptime=${2:-5} local _iMaxTry=${3:-3} @@ -464,6 +492,5 @@ ph.execIfReady(){ # init ph.setStatus "ok" - # ---------------------------------------------------------------------- diff --git a/inc_pluginfunctions.md b/inc_pluginfunctions.md index 8a66525..e489a03 100644 --- a/inc_pluginfunctions.md +++ b/inc_pluginfunctions.md @@ -48,6 +48,21 @@ Example: This will set variable iWarnLimit based on CLI parameter -w [value] ... if it does not exist it gets the default 75. +**ph.hasParamoption** PARAMNAME "$@" + +check if a letter was used as command line option and return as 0 (=no) or 1 (=yes) + +Example: + +```bash +bOptHelp=`ph.hasParamoption "h" "$@"` + +if [ $bOptHelp -eq 1 -o $# -lt 1 ]; then + _usage + exit 0 +fi +``` + **ph.setStatus** [STATUS] Set a status with keyword ok, warning, critical, unknown -- GitLab