File: //lib64/nagios/plugins/check_mysql_connections
#!/usr/bin/python
#
# Nagios plugin for MySQL tests, v0.2
# compatible with Python2.5+
# 10-06-2023 lukasz@worldhost.group
# 15-08-2023 - get thresholds from args
# 21-08-2023 - display failed tests in the alert
#
# Alerting conditions:
# 1. CRITICAL: when total amount of commands with 'Execute'/'Query' is > '--max-qe-processes'
# 2. WARNING: when 'Open_table_definitions' >= '@@table_definition_cache'
# 3. CRITICAL: when there are any queries in 'query cache lock' state
# 4. UNKNOWN: when MySQL command fails (ie db server is stopped)
###
import argparse
import subprocess
import sys
# reuse this function for all MySQL commands to make the code slightly shorter
def run_mysql_command(cmd):
mysql_output = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
cmd_return = mysql_output.wait()
if cmd_return != 0:
print("MySQL command ended with non-zero status (" + str(cmd_return) + ")")
sys.exit(3)
stdout, _ = mysql_output.communicate()
try:
mysql_result = stdout.decode('utf-8').split('\n')
except UnicodeDecodeError:
pass
return mysql_result
# return amount of MySQL commands with 'Execute' and 'Query' types only
def num_procs():
cmd_num_procs = "sudo /usr/bin/mysql -e \"show processlist;\""
cmd_output = run_mysql_command(cmd_num_procs)
num_queries=0
for cur_line in cmd_output:
if '\tQuery\t' in cur_line or '\tExecute\t' in cur_line:
num_queries+=1
return(num_queries)
# return value of MySQL variable 'Open_table_definitions'
def open_tables_vs_table_def():
cmd_table_defs = "sudo /usr/bin/mysql -e \"show global status where Variable_name = 'Open_table_definitions';\""
cmd_tabledef_cache = "sudo /usr/bin/mysql -e 'select @@table_definition_cache;'"
cmd_output1 = run_mysql_command(cmd_table_defs)
cmd_output2 = run_mysql_command(cmd_tabledef_cache)
global table_definition_cache
for string in cmd_output1:
if 'Open_table_definitions' in string:
global open_table_definitions
open_table_definitions = string.split('\t')[1]
break
table_definition_cache = cmd_output2[1]
if result_num_procs < args.trigger_above_numprocs:
return 0
else:
if int(open_table_definitions) >= int(table_definition_cache):
return 1
else:
return 0
# check for any state with 'Waiting for query cache lock'
def query_cache_lock():
cmd_check_cache_lock = "sudo /usr/bin/mysql -e \"show processlist;\""
cmd_output = run_mysql_command(cmd_check_cache_lock)
global num_query_cache_lock
num_query_cache_lock=0
for cur_line in cmd_output:
if 'Waiting for query cache lock' in cur_line:
num_query_cache_lock+=1
def status_info():
sys.stdout.write("(Query/Execute processes: " + str(result_num_procs) + "; ")
sys.stdout.write("Open_table_definitions / @@table_definiton_cache: " + str(open_table_definitions) + "/" + str(table_definition_cache) + "; ")
sys.stdout.write("queries with cache lock: " + str(num_query_cache_lock) + ")\n")
## main
parser = argparse.ArgumentParser(description="Check MySQL connections Icinga plugin")
parser.add_argument("--max-qe-processes", type=int, required=True, dest="max_qe_procs", help="defines maximum allowed query/execute processes, set return level to CRITICAL if the result is above this threshold")
parser.add_argument("--trigger-open-table-check-above-numprocs", type=int, dest="trigger_above_numprocs", required=True, help="enable open_table_definitons check against table_definition_cache above this threshold")
parser.add_argument("--max-query-cache-lock", type=int, required=True, dest="max_query_cache_lock", help="set return level to CRITICAL if the amount of queries with cache lock is above this threshold")
args = parser.parse_args()
result_num_procs = num_procs()
result_open_tables_vs_table_def = open_tables_vs_table_def()
result_query_cache_lock = query_cache_lock()
ret=0
if result_num_procs > args.max_qe_procs:
ret+=1
if result_open_tables_vs_table_def == 1:
ret+=2
if num_query_cache_lock > args.max_query_cache_lock:
ret+=4
# when alerting, print all failed tests
if ret == 0:
sys.stdout.write("OK ")
status_info()
sys.exit(0)
elif ret == 1:
sys.stdout.write(str(result_num_procs) + " command(s) in Execute/Query state, above " + str(args.max_qe_procs) + " ")
status_info()
sys.exit(1)
elif ret == 2:
sys.stdout.write("open_table_definitions >= @@table_definition_cache ")
status_info()
sys.exit(2)
elif ret == 3:
sys.stdout.write("CRITICAL1: " + str(result_num_procs) + " command(s) in Execute/Query state, above " + str(args.max_qe_procs) + " :: CRITICAL2: open_table_definitions >= @@table_definition_cache ")
status_info()
sys.exit(2)
elif ret == 4:
sys.stdout.write(str(num_query_cache_lock) + " command(s) with query_cache_lock, max " + str(args.max_query_cache_lock) + " allowed ")
status_info()
sys.exit(2)
elif ret == 5:
sys.stdout.write("WARNING: " + str(result_num_procs) + " command(s) in Execute/Query state, above " + str(args.max_qe_procs) + " :: CRITICAL: " + str(num_query_cache_lock) + " command(s) with query_cache_lock, max " + str(args.max_query_cache_lock) + " allowed ")
status_info()
sys.exit(2)
elif ret == 6:
sys.stdout.write("CRITICAL1: open_table_definitions >= @@table_definition_cache " + ":: CRITICAL2: " + str(num_query_cache_lock) + " command(s) with query_cache_lock, max " + str(args.max_query_cache_lock) + " allowed ")
status_info()
sys.exit(2)
elif ret == 7:
sys.stdout.write("WARNING: " + str(result_num_procs) + " command(s) in Execute/Query state, above " + str(args.max_qe_procs) + " :: CRITICAL1: open_table_definitions >= @@table_definition_cache " + ":: CRITICAL2: " + str(num_query_cache_lock) + " command(s) with query_cache_lock, max " + str(args.max_query_cache_lock) + " allowed ")
status_info()
sys.exit(2)