Merge branch 'unstable' into stable
Some checks failed
gitea/ansible-roles/pipeline/tag There was a failure building this commit
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
gitea/ansible-roles/pipeline/head This commit looks good

This commit is contained in:
Jérémy Lecour 2025-01-03 15:07:51 +01:00 committed by Jérémy Lecour
commit f4ab4a5dc7
Signed by: jlecour
SSH key fingerprint: SHA256:h+5LgHRKwN9lS0SsdVR5yZPeFlJE4Mt+8UtL4CcP8dY
15 changed files with 1762 additions and 145 deletions

View file

@ -21,14 +21,27 @@ The **patch** part is incremented if multiple releases happen the same month
### Security
## [25.01] 2025-01-03
### Changed
* check_free_space: don't store outgoing emails (they can be quite big)
* lxc: replace `lxc_template_mirror` with `lxc_template_options`
* nagios-nrpe: extend check_nfsclient to accept cifs and smb2 stat type
* nagios-nrpe: use locally stored upstream files for monitoringctl
### Fixed
* lxc: install of Jessie and Stretch LXC container
## [24.12.2] 2024-12-31
### Changed
* monitoringctl: follow v0.1 branch by default
* munin: return 444 for our « bad bots » list
* nagios-nrpe: change NRPE check_https definition
* nagios-nrpe: check_nfsclient takes and needs now paths arguments
* munin: return 444 for our « bad bots » list
### Fixed
@ -198,6 +211,7 @@ The **patch** part is incremented if multiple releases happen the same month
* evolinux-users: improve SSH configuration
* evomaintenance: upstream release 24.05
* evomaintenance: move upstream files into upstream folder
* webapps/evoadmin-mail: package installed via public.evolix.org/evolix repo starting with Buster
### Fixed

View file

@ -146,7 +146,7 @@ then
-e "s/__URGENCYFROM__/$URGENCYFROM/" \
-e "s/__URGENCYTEL__/$URGENCYTEL/" \
$email_template | \
/usr/bin/mutt -H - $graph_list
/usr/bin/mutt -e 'unset record' -H - $graph_list
else
sed -e "s/__TO__/$EVOMAINTMAIL/" \
-e "s/__HOSTNAME__/$HOSTNAME/" \
@ -160,7 +160,7 @@ else
-e "s/__URGENCYFROM__/$URGENCYFROM/" \
-e "s/__URGENCYTEL__/$URGENCYTEL/" \
$email_template | \
/usr/bin/mutt -H - $graph_list
/usr/bin/mutt -e 'unset record' -H - $graph_list
fi
rm -f $PID_FILE

View file

@ -8,9 +8,8 @@ lxc_network_type: "none"
# Partition to bind mount into containers.
lxc_mount_part: "/home"
# Mirror URL (optionnal).
# For old Debian, use https://archive.debian.org/debian/
lxc_template_mirror: ""
# It's "template_options" used directly by community.general.lxc_container
lxc_template_options: ""
# List of LXC containers to create.
# Eg.:

View file

@ -7,7 +7,26 @@
register: container_exists
- ansible.builtin.set_fact:
lxc_template_mirror_option: "{{ '--mirror ' + lxc_template_mirror if lxc_template_mirror != '' else '' }}"
lxc_template_options: >-
--keyring=/etc/apt/trusted.gpg.d/freexian-archive-extended-lts.gpg
--mirror=https://elts.evolix.org/extended-lts
--security-mirror=http://archive.debian.org/debian-security
{{ lxc_template_options }}
when: release == "jessie"
- ansible.builtin.set_fact:
lxc_template_options: >-
--keyring=/usr/share/keyrings/debian-archive-removed-keys.gpg
--mirror=http://archive.debian.org/debian
--security-mirror=http://archive.debian.org/debian-security
{{ lxc_template_options }}
when: release == "stretch"
- name: "Install keyring for eLTS"
ansible.builtin.apt:
name:
- freexian-archive-keyring
when: release == 'jessie' or release == 'stretch' or release == 'buster'
- name: "Create container {{ name }}"
community.general.lxc_container:
@ -15,7 +34,7 @@
container_log: true
template: debian
state: stopped
template_options: "--arch amd64 --release {{ release }} {{ lxc_template_mirror_option }}"
template_options: "--arch amd64 --release {{ release }} {{ lxc_template_options }}"
when: container_exists.stdout_lines | length == 0
- name: "Disable network configuration inside container {{ name }}"

View file

@ -30,3 +30,7 @@ nagios_plugins_directory: "/usr/local/lib/nagios/plugins"
monitoringctl_wrapper_path: /usr/local/lib/monitoringctl/alerts_wrapper
monitoringctl_branch: v0.1
monitoringctl_lib_dir: /usr/local/lib/monitoringctl
monitoringctl_bin_dir: /usr/local/bin
monitoringctl_var_dir: /var/lib/monitoringctl

View file

@ -0,0 +1,133 @@
#!/usr/bin/env python3
#
# alerts_wrapper wraps an NRPE command and overrides its return code if alert is disabled by monitoringctl.
#
# Source:
# https://gitea.evolix.org/evolix/ansible-roles/src/branch/stable/nagios-nrpe/
#
lib_dir = '/usr/local/lib/monitoringctl'
prog_version = '0.1.2'
prog_name = 'monitoringctl'
import sys, os
import argparse
import subprocess
from datetime import datetime, timedelta
# Load common lib
if os.path.isdir(lib_dir) and os.path.isfile(lib_dir + '/common.py'):
sys.path.append(lib_dir)
import common as lib
else:
print('ERROR: missing {}/monitoringctl_common module.'.format(lib_dir), file=sys.stderr)
exit(2)
# Check /var directory
if not os.path.isdir(lib.var_dir):
lib.error('ERROR: missing ${var_dir} directory.'.format(lib.var_dir))
def show_help():
help_str = '''
alerts_wrapper wraps an NRPE command and overrides the return code.
Usage: alerts_wrapper --name <WRAPPER_NAME> -- <CHECK_COMMAND>
Usage: alerts_wrapper <WRAPPER_NAME> <CHECK_COMMAND> (deprecated)
Options
--name Wrapper name, it is very recommended to use the check name (like load, disk1).
Special name: 'all' is already hard-coded.
-h, --help Print this message and exit.
-V, --version Print version and exit.
'''
print(help_str)
def enable_wrapper(wrapper_name):
enable_command = '/usr/local/bin/monitoringctl enable {} --message \'Disable time expired.\''.format(wrapper_name)
if os.getuid() != 0:
enable_command = 'sudo ' + enable_command
subprocess.run(enable_command, shell=True)
def main(wrapper_name, check_command):
disable_file = lib.get_disable_file_path(wrapper_name)
is_disabled = lib.is_disabled_wrapper(wrapper_name)
if os.path.exists(disable_file) and not is_disabled:
enable_wrapper(wrapper_name)
if is_disabled:
check_command = 'timeout 8 ' + check_command
check_rc = 0
try:
stdout = subprocess.check_output(check_command, shell=True)
except subprocess.CalledProcessError as e:
check_rc = e.returncode
stdout = e.output
check_stdout = stdout.decode('utf8').strip() # strip() removes trailing \n
if is_disabled and check_rc == 124 and not check_stdout:
check_stdout = 'Check timeout (> 8 sec)'
if is_disabled:
# TODO: Pythonize these lib functions
enable_time = lib.get_enable_time(wrapper_name)
enable_delay = lib.calc_enable_delay(enable_time)
delay_str = lib.delay_to_string(enable_delay)
enable_delay_delta = timedelta(seconds=enable_delay)
enable_date = datetime.strftime(datetime.now() + enable_delay_delta, '%d %h %Y at %H:%M:%S')
disable_msg = lib.get_disable_message(wrapper_name)
if disable_msg:
disable_msg = '- {} '.format(disable_msg)
print('ALERT DISABLED until {} ({} left) {}- Check output: {}'.format(enable_date, delay_str, disable_msg, check_stdout))
else:
print(check_stdout)
if is_disabled:
if check_rc == 0:
exit(0) # Nagios OK
else:
exit(1) # Nagios WARNING
else:
exit(check_rc)
# Parse arguments
parser = argparse.ArgumentParser(
prog='alerts_wrapper',
description='alerts_wrapper wraps an NRPE command and overrides its return code if alert is disabled by monitoringctl.')
parser.add_argument('-V', '--version', action='store_true')
parser.add_argument('-n', '--name', required=True, help='Wrapper name. Using the check name (like "load", "disk1"…) is strongly advised. "all" is a special name already hard-coded.')
parser.add_argument('check_command', help='NRPE check command that will be run by the wrapper.')
# unknown_args is a workaround to get args starting with '--'
args, unknown_args = parser.parse_known_args()
if args.version:
lib.show_version(prog_name, prog_version)
exit(0)
wrapper_name = args.name
for i, arg in enumerate(unknown_args):
if '"' in arg:
unknown_args[i] = '\'{}\''.format(arg)
else:
unknown_args[i] = '"{}"'.format(arg)
check_command = [args.check_command] + unknown_args
check_command = ' '.join(check_command)
# Run program
main(wrapper_name, check_command)

View file

@ -0,0 +1,6 @@
#!/usr/bin/env bash
# Replaced by monitoringctl.
# check-local executable is kept for retrocompatibility purposes.
monitoringctl check "${1}"

View file

@ -0,0 +1,16 @@
#!/usr/bin/env bash
function _get_checks_names() {
grep --extended-regexp --no-filename --no-messages -R "command\[check_.*\]=" /etc/nagios/ | grep --invert-match --extended-regexp "^\s*#" | awk -F"[\\\[\\\]=]" '{sub("check_", "", $2); print $2}' | sort | uniq
}
# List of available checks
_check_local_dynamic_completion() {
local cur=${COMP_WORDS[COMP_CWORD]};
COMPREPLY=( $( compgen -W '$(_get_checks_names)' -- "${cur}" ) );
}
complete -F _check_local_dynamic_completion check-local

View file

@ -0,0 +1,746 @@
#!/usr/bin/env bash
#set -x
readonly VERSION="0.1.2"
function show_help() {
cat <<EOF
${bold}monitoringctl${no_bold} version ${VERSION}.
${bold}monitoringctl${no_bold} gives some control over NRPE checks and alerts.
Usage: ${bold}monitoringctl${no_bold} [OPTIONS] ACTION ARGUMENTS
${bold}ACTIONS${no_bold}
${bold}help${no_bold}
Print this message and exit.
${bold}version${no_bold}
Print version number and exit.
${bold}list${no_bold}
List the checks defined in NRPE configuration.
${bold}status [CHECK_NAME]${no_bold}
Print whether alerts are enabled or not (silenced).
If alerts are disabled (silenced), show disable message and time left before automatic re-enabling.
${bold}check [-b|--bypass-nrpe] [CHECK_NAME]${no_bold}
Ask CHECK_NAME to NRPE with an HTTP request.
If CHECK_NAME is not provided, verify all checks.
Also, print which command NRPE has supposedly run (from its configuration).
-b, --bypass-nrpe Execute directly command from NRPE configuration,
as user nagios, without passing the request to NRPE.
${bold}disable [CHECK_NAME] [-d|--during DURATION] [-m|--message 'DISABLE MESSAGE']${no_bold}
Disable (silence) CHECK_NAME alerts for DURATION and write DISABLE MESSAGE into the log.
Checks output is still printed, so alerts history won't be lost.
If CHECK_NAME is not provided, disable all alerts.
-d, --during DURATION See section DURATION.
-m, --message 'DISABLE MESSAGE' See section MESSAGE.
-n, --non-interactive Do not ask confirmation (--message is mandatory then).
${bold}enable [CHECK_NAME] [-m|--message 'ENABLE MESSAGE']${no_bold}
Re-enable CHECK_NAME.
If CHECK_NAME is not provided, re-enable all alerts.
-m, --message 'ENABLE MESSAGE' See section MESSAGE.
${bold}show CHECK_NAME${no_bold}
Show NPRE command(s) configured for CHECK_NAME
${bold}MESSAGE${no_bold}
Message that will be written in log and in check output when disabled.
It is mandatory, but in interactive shells it can be omitted. In this case it is asked interactively.
Warning: In non-interactive shells (scripts, crons…) or with option --non-interactive,
this option is mandatory.
${bold}DURATION${no_bold}
Time (string) during which alerts will be disabled (optional, default: "1h").
${bold}Format${no_bold}
You can use 'd' (day), 'h' (hour) and 'm' (minute) , or a combination of them, to specify a duration.
Examples: '2d', '1h', '10m', '1h10' ('m' is guessed).
${bold}OTHER NOTES${no_bold}
For actions disable, enable and status, CHECK_NAME is actually the --name option passed to alerts_wrapper, and not the NRPE check name. Both check name and alerts_wrapper --name option should be equal in NRPE configuration to avoid confusion.
Log path: ${log_file}
EOF
}
function list_checks() {
checks="$(get_checks_names)"
for check in $checks; do
echo "${check}"
done
}
function check() {
# $1: check name or empty
readonly check_nrpe_bin="/usr/lib/nagios/plugins/check_nrpe"
if [ ! -f "${check_nrpe_bin}" ]; then
>&2 echo "${check_nrpe_bin} is missing, please install nagios-nrpe-plugin package."
exit 1
fi
conf_lines="$(get_nrpe_conf "${nrpe_conf_path}")"
server_address=$(echo "$conf_lines" | grep "server_address" | tail -n1 | cut -d'=' -f2)
if [ -z "${server_address}" ]; then server_address="127.0.0.1"; fi
server_port=$(echo "$conf_lines" | grep "server_port" | tail -n1 | cut -d'=' -f2)
if [ -z "${server_port}" ]; then server_port="5666"; fi
if [ -z "${1}" ]; then
# Array header for multi-checks
checks="$(get_checks_names)"
header="Check\tStatus\tOutput (truncated)"
underline="-----\t------\t------------------"
str_out="\n${header}\n${underline}\n"
else
checks="${1}"
fi
warn_options_not_ended_properly "${checks}"
for check in $checks; do
printf "\033[KChecking %s…\r" "${check}"
err_msg=""
check_commands="$(get_check_commands "${check}")"
if [ -n "${check_commands}" ]; then
check_command="$(echo "${check_commands}" | tail -n1)"
if [ -n "${1}" ]; then
printf "\033[K" # erase tmp line "Checking check_toto…"
echo "Command played by NRPE:"
echo -e " ${orange}${check_command}${nocolor}"
if is_wrapped "${check}"; then
if [[ "${check_command}" == *" -- "* ]]; then
check_command_no_wrapper="$(echo "${check_command}" | awk -F' -- ' '{print $2}')"
echo "Command without 'alerts_wrapper':"
echo -e " ${orange}${check_command_no_wrapper}${nocolor}"
fi
fi
fi
fi
if [ "${bypass_nrpe}" = "False" ]; then
request_command="${check_nrpe_bin} -H ${server_address} -p ${server_port} -b 127.0.0.1 -c check_${check} 2&>1"
else
if [ -n "${check_command}" ]; then
request_command="sudo -u nagios -- ${check_command}"
else
if [ -z "${1}" ]; then
err_msg="Check command not found in NRPE configuration."
else
err_msg="Error: no command found in NRPE configuration for check '${check}'. Aborted."
fi
fi
fi
if [ -z "${err_msg}" ]; then
check_output="$(${request_command})"
rc="$?"
if [ -z "${1}" ]; then
check_output="$(echo "${check_output}" | sed 's/|/\n/g' | head -n1)"
if [ "${#check_output}" -gt 60 ]; then
check_output="$(echo "${check_output}" | cut -c-80) [...]"
fi
fi
else
check_output="${err_msg}"
rc="3"
fi
case "${rc}" in
0)
rc_str="OK"
color="${green}"
;;
1)
rc_str="Warning"
color="${orange}"
;;
2)
rc_str="Critical"
color="${red}"
;;
3)
rc_str="Unknown"
color="${purple}"
;;
*)
rc_str="Unknown"
color="${purple}"
esac
if [ -z "${1}" ]; then
str_out="${str_out}${color}${check}\t${rc_str}${nocolor}\t${check_output}\n"
fi
done
if [ -z "${1}" ]; then
echo -e "${str_out}" | column -t -s $'\t'
else
printf "\033[K\n" # erase tmp line « Checking check_toto…»
if [ "${bypass_nrpe}" = "False" ]; then
echo -e "NRPE service output (on ${server_address}:${server_port}):\n"
else
echo -e "Direct check output (bypassing NRPE):\n"
fi
echo -e "${color}${check_output}${nocolor}\n" | sed 's/|/\n/g'
exit "${rc}"
fi
}
function disable_alert() {
# $1: wrapper name, $2: duration_sec, $3: disable message
now_secs=$(date +"%s")
disable_until_secs=$(( now_secs + ${2} ))
disable_file_path="$(get_disable_file_path "${1}")"
echo "${disable_until_secs}" > "${disable_file_path}"
echo "$(get_real_user): \"${3}\"" >> "${disable_file_path}"
chmod 0644 "${disable_file_path}"
log "${1} alerts disabled by user $(get_real_user)"
log "Disable message: ${3}"
}
function get_real_user() {
real_user="$(logname)"
if [ -z "${real_user}" ]; then
if [ -n "${SUDO_USER}" ]; then
real_user="${SUDO_USER}"
else
real_user="unknown"
fi
fi
echo "${real_user}"
}
function enable_alert() {
# $1: wrapper name, $2: enable message
disable_file_path="$(get_disable_file_path "${1}")"
if [ -e "${disable_file_path}" ]; then
rm "${disable_file_path}"
fi
log "${1} alerts enabled by user $(get_real_user)"
log "Enable message: ${2}"
}
function disable_alerts() {
# $1: check name
# $2: disable message
if [ -z "${1}" ]; then
checks="$(get_checks_names)"
else
checks="${1}"
fi
warn_not_wrapped "${checks}"
warn_options_not_ended_properly "${checks}"
warn_wrapper_names "${checks}"
if [ -z "${2}" ]; then
if [ "${is_interactive}" = "False" ]; then
error "Error: disable message option is mandatory in non-interactive shell."
fi
echo -n "> Please provide a disable message (for logging and check output): "
read -r message
echo ''
if [ -z "${message}" ]; then
error "${red}Error:${nocolor} disable message is mandatory."
fi
else
message="${2}"
fi
default_msg=""
if [ "${default_duration}" = "True" ]; then
default_msg=" (use --during to change default time)"
fi
if [ -z "${1}" ]; then
check_txt="All checks"
else
check_txt="Check ${1}"
fi
echo_box "${check_txt} will be disabled for ${duration}${default_msg}."
cat <<EOF
Additional information:
* Alerts history is kept in our monitoring system.
* To see when the will be re-enabled, execute 'monitoringctl status ${1}'.
* To re-enable alert(s) before ${duration}, execute as root or with sudo: 'monitoringctl enable ${1}'.
EOF
wrapper=""
if [ -n "${1}" ]; then
if is_check "${1}"; then
wrapper="$(get_check_wrapper_name "${1}")"
else
wrapper="${1}"
fi
checks="$(get_wrapper_checks "${wrapper}")"
n_checks="$(echo "${checks}" | wc -w)"
if [ "${n_checks}" -gt 1 ]; then
>&2 echo -e "${orange}Warning:${nocolor} because they have the same configuration, disabling ${1} will disable: ${checks}.\n"
log "Warning: disabling ${1} will disable ${checks} (which have the same wrapper name)."
fi
fi
if [ "${is_interactive}" = "True" ]; then
echo -n "> Confirm (y/N)? "
read -r answer
if [ "${answer}" != "Y" ] && [ "${answer}" != "y" ]; then
echo -e "${orange}Canceled.${nocolor}" && exit 0
fi
fi
duration_sec=$(time_to_seconds "${duration}")
if [ -z "${wrapper}" ]; then
for wrapper_name in $(get_wrappers_names); do
disable_alert "${wrapper_name}" "${duration_sec}" "${message}"
done
else
disable_alert "${wrapper}" "${duration_sec}" "${message}"
fi
if [ -n "${1}" ]; then
if [ "${n_checks}" -eq 1 ]; then
echo -e "${orange}Check ${1} alerts are now disabled for ${duration}.${nocolor}"
else
echo -e "${orange}Alerts are now disabled for ${duration} for checks: ${checks}.${nocolor}"
fi
else
echo -e "${orange}All alerts are now disabled for ${duration}.${nocolor}"
fi
}
function enable_alerts() {
# $1: check name, $2: enable message
if [ -z "${2}" ]; then
if [ "${is_interactive}" = "False" ]; then
error "Error: disable message option is mandatory in non-interactive shell."
fi
echo -n "> Please provide an enable message (for logging): "
read -r message
echo ''
if [ -z "${message}" ]; then
error "${red}Error:${nocolor} disable message is mandatory."
fi
else
message="${2}"
fi
wrapper=""
if [ -n "${1}" ]; then
if is_check "${1}"; then
wrapper="$(get_check_wrapper_name "${1}")"
else
wrapper="${1}"
fi
checks="$(get_wrapper_checks "${wrapper}")"
n_checks="$(echo "${checks}" | wc -w)"
if [ "${n_checks}" -gt 1 ]; then
>&2 echo -e "${orange}Warning:${nocolor} because they have the same configuration, enabling ${1} will enable: ${checks}.\n"
log "Warning: check ${1} will enable ${checks} (which have the same wrapper name)."
fi
fi
if [ -z "${wrapper}" ]; then
for wrapper_name in $(get_wrappers_names); do
enable_alert "${wrapper_name}" "${message}"
done
else
enable_alert "${wrapper}" "${message}"
fi
if [ -n "${1}" ]; then
if [ "${n_checks}" -eq 1 ]; then
echo -e "${green}Check ${1} alerts are now enabled.${nocolor}"
else
echo -e "${green}Alerts are now enabled for checks: ${checks}.${nocolor}"
fi
else
echo -e "${green}All alerts are now enabled.${nocolor}"
fi
}
# Show NRPE command(s) configured for a check
function show_check_commands() {
# $1: check name
check_commands=$(get_check_commands "${1}")
if [ -z "${check_commands}" ]; then
usage_error "Error: no command found in NRPE configuration for check '${1}."
fi
warn_options_not_ended_properly "${1}"
n_commands="$(echo "${check_commands}" | wc -l)"
if [ "${n_commands}" -ne 1 ]; then
echo "Available commands (in config order, the last one overwrites the others):"
echo " $check_commands"
fi
check_command=$(echo "${check_commands}" | tail -n1)
echo "Command used by NRPE:"
echo " ${check_command}"
if [[ "${check_command}" == *" -- "* ]]; then
check_command_no_wrapper="$(echo "${check_command}" | awk -F' -- ' '{print $2}')"
echo
echo "Command without 'alerts_wrapper':"
echo -e " ${check_command_no_wrapper}"
fi
}
# Print a warning if some wrappers have the same name
# or if a name is different from the check.
function warn_wrapper_names() {
#$1: checks to verify
warned="False"
for check in ${1}; do
wrapper_name="$(get_check_wrapper_name "${check}")"
if [ -n "${wrapper_name}" ] && [ "${wrapper_name}" != "${check}" ]; then
>&2 echo -e "${orange}Warning:${nocolor} ${check} check has wrapper name ${wrapper_name}."
warned="True"
fi
done
if [ "${warned}" = "True" ]; then
>&2 echo -e "${orange}It is recommanded to name the wrappers the same as the checks.${nocolor}\n"
fi
}
# Print a warning if the wrapper does not end its options with -- (to not interpret the wrapped NRPE command options)
function warn_options_not_ended_properly() {
#$1: checks to verify
wrappers_without_ddash='' # double dash --
for check in ${1}; do
if is_wrapped "${check}"; then
check_command=$(get_check_commands "${check}" | tail -n1)
if [[ ! ${check_command} == *" -- "* ]]; then
wrappers_without_ddash="${wrappers_without_ddash} ${check}"
fi
fi
done
if [ -n "${wrappers_without_ddash}" ]; then
>&2 echo -e "${orange}Some wrappers do not end their options with '--' before the NRPE command:${wrappers_without_ddash}"
>&2 echo -e "Use of '--' is strongly recommended to separate 'alerts_wrapper' and the wrapped command in NRPE configuration.${nocolor}\n"
fi
}
# Print a warning if some checks are not wrapped
function warn_not_wrapped() {
#$1: checks to verify
unwrappeds="$(not_wrapped_checks)"
unwrapped_checks="$(comm -12 <(echo "${1}") <(echo "${unwrappeds}"))"
if [ -n "${unwrapped_checks}" ]; then
n_checks="$(echo "${1}" | wc -w)"
n_unwrapped="$(echo "${unwrapped_checks}" | wc -w)"
if [ "${n_unwrapped}" == "${n_checks}" ]; then
if [ "${n_unwrapped}" -eq 1 ]; then
error "${red}Error:${nocolor} ${1} check is not wrapped, it cannot be disabled."
else
error "${red}Error:${nocolor} these checks are not wrapped, they cannot be disabled: $(echo "${unwrapped_checks}" | xargs)"
fi
else
if [ "${n_unwrapped}" -eq 1 ]; then
>&2 echo -e "${orange}Warning:${nocolor} ${unwrapped_checks} check is not wrapped, it will not be disabled."
else
>&2 echo -e -n "${orange}Warning:${nocolor} some checks are not configured, they will not be disabled: $(echo "${unwrapped_checks}" | xargs)\n\n"
fi
fi
log "Warning: some checks have no alerts_wrapper, they will not be disabled: $(echo "${unwrapped_checks}" | xargs)"
fi
}
# Echo a message in a box
function echo_box() {
# $1: message
msg_len="${#1}"
line="$(printf '─%.0s' $(eval "echo {1.."${msg_len}"}"))"
cat <<EOF
┌${line}┐
│${1}│
└${line}┘
EOF
}
# Echo which checks are enabled or disabled and time left
function alerts_status() {
# $1: check name or empty
if [ -z "${1}" ]; then
checks="$(get_checks_names)"
else
checks="${1}"
fi
warn_options_not_ended_properly "${checks}"
warn_wrapper_names "${checks}"
header="Check\tStatus\tRe-enable time\tDisable message"
underline="-----\t------\t--------------\t---------------"
str_out="${header}\n${underline}\n"
for check in $checks; do
enable_str=""
status_str="Enabled"
disable_msg=""
if ! is_wrapped "${check}"; then
status_str="Not configured"
else
is_disabled="$(is_disabled_check "${check}")"
wrapper_name="$(get_check_wrapper_name "${check}")"
if [ "${is_disabled}" = "True" ]; then
status_str="Disabled"
enable_time="$(get_enable_time "${wrapper_name}")"
enable_delay="$(enable_delay "${enable_time}")"
delay_str="$(delay_to_string "${enable_delay}")"
enable_date="$(date --date "+${enable_delay} seconds" "+%d %h %Y at %H:%M:%S")"
enable_str="${enable_date} (${delay_str} left)"
disable_msg="$(get_disable_message "${wrapper_name}")"
fi
fi
case "${status_str}" in
"Enabled")
color="${green}"
;;
"Disabled")
color="${orange}"
;;
*)
color="${red}"
esac
str_out="${str_out}${color}${check}\t${status_str}${nocolor}\t${enable_str}\t${disable_msg}\n"
done
echo -e "${str_out}" | column -t -s $'\t'
}
### MAIN #########################################
red=''
green=''
orange=''
purple=''
nocolor=''
bold=''
no_bold=''
# Is interactive shell ?
if [ -t 0 ] && [ -t 1 ]; then
is_interactive="True"
red="\e[0;31m"
green="\e[0;32m"
orange="\e[0;33m"
purple="\e[0;35m"
nocolor="\e[0m"
bold="$(tput bold)"
no_bold="$(tput sgr0)"
else
is_interactive="False"
fi
# Load common functions and vars
readonly lib_dir="/usr/local/lib/monitoringctl"
if [ -r "${lib_dir}/common" ]; then
# shellcheck source=monitoringctl_common
source "${lib_dir}/common"
else
>&2 echo "Error: missing ${lib_dir}/common file."
exit 1
fi
if [[ ! "${PATH}" =~ /usr/local/bin ]]; then
PATH="/usr/local/bin:${PATH}"
fi
# Must be root
if [ "$(id -u)" -ne 0 ]; then
>&2 echo "You need to be root (or use sudo) to run ${0}!"
exit 1
fi
# No argument
if [ "$#" = "0" ]; then
show_help
exit 1
fi
# Default arguments and options
action=""
message=""
duration="${default_disabled_time}"
bypass_nrpe="False"
default_duration="True"
# Parse arguments and options
while :; do
case "${1}" in
-h|-\?|--help)
show_help
exit 0;;
-V|--version)
show_version
exit 0;;
-b|--bypass-nrpe)
bypass_nrpe="True"
shift;;
-n|--non-interactive)
is_interactive="False"
shift;;
-d|--during)
if [ "${default_duration}" = "False" ]; then
usage_error "Option --during: defined multiple times."
fi
if [ "$#" -lt 2 ]; then
usage_error "Option --during: missing value."
fi
if filter_duration "${2}"; then
duration="${2}"
else
usage_error "Option --during: \"${2}\" is not a valid duration."
fi
default_duration="False"
shift; shift;;
-m|--message)
if [ "$#" -lt 2 ]; then
usage_error "Option --message: missing message string."
fi
message="${2}"
shift; shift;;
status|check|enable|disable|show|list|help|version)
action="${1}"
shift;;
*)
if [ -z "${1}" ]; then
break
else
arg="${1}"
fi
# Add some flexibility with - and _ in check name
if ! is_check "${arg}"; then
if [[ "${1}" == *"-"* ]]; then
arg_underscore="$(echo "${arg}" | tr '-' '_')"
if is_check "${arg_underscore}"; then
>&2 echo -e "${orange}Warning: '${arg}' real name is '${arg_underscore}'!${nocolor}\n"
arg="${arg_underscore}"
fi
fi
if [[ "${1}" == *"_"* ]]; then
arg_dash="$(echo "${arg}" | tr '_' '-')"
if is_check "${arg_dash}"; then
>&2 echo -e "${orange}Warning: '${arg}' real name is '${arg_dash}'!${nocolor}\n"
arg="${arg_dash}"
fi
fi
fi
case "${action}" in
status|check)
if is_check "${arg}"; then
check_name="${arg}"
else
usage_error "Action ${action}: unknown check '${arg}'."
fi
;;
show)
if is_check "${arg}"; then
check_name="${arg}"
else
usage_error "Action ${action}: unknown check '${arg}'."
fi
;;
enable|disable)
if is_wrapper "${arg}" || is_check "${arg}"; then
check_name="${arg}"
else
# We use the word "check" for the end user,
# but this is actually "unknown wrapper"
usage_error "Action ${action}: unknown check '${arg}'."
fi
;;
help)
show_help
exit 0;;
version)
show_version
exit 0;;
*)
usage_error "Missing or invalid ACTION argument."
;;
esac
shift;;
esac
done
if [ "$#" -gt 0 ]; then
usage_error "Too many arguments."
fi
case "${action}" in
show)
if [ -z "${check_name}" ]; then
usage_error "Action ${action}: missing CHECK_NAME argument."
fi
;;
esac
if [ ! "${action}" = "disable" ]; then
if [ "${default_duration}" = "False" ]; then
usage_error "Action ${action}: there is no --during option."
fi
fi
case "${action}" in
list)
list_checks
;;
status)
alerts_status "${check_name}"
;;
check)
check "${check_name}"
;;
show)
show_check_commands "${check_name}"
;;
enable)
enable_alerts "${check_name}" "${message}"
;;
disable)
disable_alerts "${check_name}" "${message}"
;;
esac

View file

@ -0,0 +1,294 @@
#!/usr/bin/env bash
# Location of disable files
readonly var_dir="/var/lib/monitoringctl"
readonly log_file="/var/log/monitoringctl.log"
readonly nrpe_conf_path="/etc/nagios/nrpe.cfg"
debian_major_version="$(cut -d "." -f 1 < /etc/debian_version)"
readonly debian_major_version
# If no time limit is provided in CLI or found in file, this value is used
readonly default_disabled_time="1h"
_nrpe_conf_lines='' # populated at the end of the file
function error() {
# $1: error message
>&2 echo -e "${1}"
exit 1
}
function usage_error() {
# $1: error message
>&2 echo "${1}"
>&2 echo "Execute \"${PROGNAME} --help\" for information on usage."
exit 1
}
function log() {
# $1: message
echo "$(now_iso) - ${PROGNAME}: ${1}" >> "${log_file}"
}
function show_version() {
cat <<END
${PROGNAME} version ${VERSION}.
Copyright 2018-2024 Evolix <info@evolix.fr>,
Jérémy Lecour <jlecour@evolix.fr>
and others.
${PROGNAME} comes with ABSOLUTELY NO WARRANTY.This is free software,
and you are welcome to redistribute it under certain conditions.
See the GNU General Public License v3.0 for details.
END
}
# Fail if argument does not respect format: XwXdXhXmXs, XhX, XmX
function filter_duration() {
# $1: duration in format specified above
_time_regex="^([0-9]+d)?(([0-9]+h(([0-9]+m?)|([0-9]+m([0-9]+s?)?))?)|(([0-9]+m([0-9]+s?)?)?))?$"
if [[ "${1}" =~ ${_time_regex} ]]; then
return 0
fi
return 1
}
# Convert human writable duration into seconds
function time_to_seconds() {
# $1: formated time string
if echo "${1}" | grep -E -q '^([0-9]+[wdhms])+$'; then
echo "${1}" | sed 's/w/ * 604800 + /g; s/d/ * 86400 + /g; s/h/ * 3600 + /g; s/m/ * 60 + /g; s/s/ + /g; s/+ $//' | xargs expr
elif echo "${1}" | grep -E -q '^([0-9]+h[0-9]+$)'; then
echo "${1}" | sed 's/h/ * 3600 + /g; s/$/ * 60/' | xargs expr
elif echo "${1}" | grep -E -q '^([0-9]+m[0-9]+$)'; then
echo "${1}" | sed 's/m/ * 60 + /g' | xargs expr
else
error "Invalid duration: '${1}'."
fi
}
# Print re-enable time in secs
function get_enable_time() {
# $1: wrapper name
_disable_file_path="$(get_disable_file_path "${1}")"
if [ ! -e "${_disable_file_path}" ]; then
return
fi
_enable_secs="$(grep -v -E "^\s*#" "${_disable_file_path}" | sed '/^$/d' | head -n1 | awk '/^[0-9]+$/ {print $1}')"
# If file is empty, use file last change date plus default disabled time
if [ -z "${_enable_secs}" ]; then
_file_last_change_secs="$(stat -c %Z "${_disable_file_path}")"
_default_disabled_time_secs="$(time_to_seconds "${default_disabled_time}")"
_enable_secs="$(( _file_last_change_secs + _default_disabled_time_secs ))"
fi
echo "${_enable_secs}"
}
# Print disable message
function get_disable_message() {
# $1: wrapper name
_disable_file_path="$(get_disable_file_path "${1}")"
if [ ! -e "${_disable_file_path}" ]; then
return
fi
_disable_msg="$(sed '/^$/d' "${_disable_file_path}" | tail -n+2 | tr '\n' ' ' | awk '{$1=$1;print}')"
echo "${_disable_msg}"
}
function now_secs() {
date +"%s"
}
function now_iso() {
date --iso-8601=seconds
}
# Print delay before re-enable in secs
function enable_delay() {
# $1: re-enable time in secs
echo $(( ${1} - $(now_secs) ))
}
# Converts delay (in seconds) into human readable duration
function delay_to_string() {
# $1: delay in secs
_delay_days="$(( ${1} /86400 ))"
if [ "${_delay_days}" -eq 0 ]; then _delay_days=""
else _delay_days="${_delay_days}d"; fi
_delay_hours="$(( (${1} %86400) /3600 ))"
if [ "${_delay_hours}" -eq 0 ]; then _delay_hours=""
else _delay_hours="${_delay_hours}h"; fi
_delay_minutes="$(( ((${1} %86400) %3600) /60 ))"
if [ "${_delay_minutes}" -eq 0 ]; then _delay_minutes=""
else _delay_minutes="${_delay_minutes}m"; fi
_delay_seconds="$(( ((${1} %86400) %3600) %60 ))"
if [ "${_delay_seconds}" -eq 0 ]; then _delay_seconds=""
else _delay_seconds="${_delay_seconds}s"; fi
echo "${_delay_days}${_delay_hours}${_delay_minutes}${_delay_seconds}"
}
function is_disabled_check() {
# $1: check name
_wrapper="$(get_check_wrapper_name "${1}")"
is_disabled_wrapper "${_wrapper}"
}
function is_disabled_wrapper() {
# $1: wrapper name
_wrapper="${1}"
_disable_file_path="$(get_disable_file_path "${_wrapper}")"
if [ -e "${_disable_file_path}" ]; then
_enable_time="$(get_enable_time "${_wrapper}")"
_enable_delay="$(enable_delay "${_enable_time}")"
if [ "${_enable_delay}" -le "0" ]; then
echo "False"
else
echo "True"
fi
else
echo False
fi
}
function get_disable_file_path() {
# $1: wrapper name
echo "${var_dir}/${1}_alerts_disabled"
}
### Nagios configuration functions ####################
# Print NRPE configuration, with includes, without comments
# and in the same order than NRPE does (taking account that
# order changes from Deb10)
function get_nrpe_conf() {
echo "${_nrpe_conf_lines}"
}
# Private function to recursively get NRPE conf from file
function _get_conf_from_file() {
# $1: NRPE conf file (.cfg)
if [ ! -f "${1}" ]; then return; fi
_conf_lines=$(grep -E -R -v --no-filename "^\s*(#.*|)$" "${1}")
while read -r _line; do
if [[ "${_line}" =~ .*'include='.* ]]; then
_conf_file=$(echo "${_line}" | cut -d= -f2)
_get_conf_from_file "${_conf_file}"
elif [[ "${_line}" =~ .*'include_dir='.* ]]; then
_conf_dir=$(echo "${_line}" | cut -d= -f2)
_get_conf_from_dir "${_conf_dir}"
elif [[ "${_line}" =~ .*'check_hda1'.* ]]; then
continue # Ludo dirty hack to avoid modifying /etc/nrpe/nrpe.cfg
else
echo "${_line}"
fi
done <<< "${_conf_lines}"
}
# Private function to recursively get NRPE conf from directory
function _get_conf_from_dir() {
# $1: NRPE conf dir
if [ ! -d "${1}" ]; then return; fi
if [ "${debian_major_version}" -ge 10 ]; then
# From Deb10, NRPE use scandir() with alphasort() function
_sort_command="sort"
else
# Before Deb10, NRPE use loaddir(), like find utility
_sort_command="cat -"
fi
# Add conf files in dir to be processed recursively
for _file in $(find "${1}" -maxdepth 1 -name "*.cfg" 2> /dev/null | ${_sort_command}); do
if [ -f "${_file}" ]; then
_get_conf_from_file "${_file}"
elif [ -d "${_file}" ]; then
_get_conf_from_dir "${_file}"
fi
done
}
# Print the checks that are configured in NRPE
function get_checks_names() {
echo "${_nrpe_conf_lines}" | grep -E "command\[check_.*\]=" | awk -F"[\\\[\\\]=]" '{sub("check_", "", $2); print $2}' | sort | uniq
}
# Print the commands defined for check $1 in NRPE configuration
function get_check_commands() {
# $1: check name
echo "${_nrpe_conf_lines}" | grep -E "command\[check_${1}\]" | cut -d'=' -f2-
}
# Print the checks that have no alerts_wrapper in NRPE configuration
function not_wrapped_checks() {
for _check in $(get_checks_names); do
if ! is_wrapped "${_check}"; then
echo "${_check}"
fi
done
}
# Fail if check is not wrapped
function is_wrapped() {
# $1: check name
_cmd=$(get_check_commands "${1}" | tail -n1)
if echo "${_cmd}" | grep --quiet --no-messages alerts_wrapper; then
return 0
fi
return 1
}
# Print the names that are defined in the wrappers of the checks
function get_wrappers_names() {
echo "${_nrpe_conf_lines}" | grep -s "alerts_wrapper" | awk '{ for (i=1 ; i<=NF; i++) { if ($i ~ /^(-n|--name)$/) { print $(i+1); break } } }' | tr ',' '\n' | sort | uniq
}
# Print the wrapper name of the check
function get_check_wrapper_name() {
# $1: check name
_cmd=$(get_check_commands "${1}" | tail -n1)
if echo "${_cmd}" | grep --quiet --no-messages alerts_wrapper; then
echo "${_cmd}" | awk '/--name/ {match($0, /--name\s*([a-zA-Z0-9_\-]*)\s*/, m); print m[1]}'
fi
}
function is_check() {
# $1: check name
_checks="$(get_checks_names)"
if echo "${_checks}" | grep --quiet -E "^${1}$"; then
return 0
fi
return 1
}
function is_wrapper() {
# $1: wrapper name
_wrappers="$(get_wrappers_names)"
if echo "${_wrappers}" | grep --quiet -E "^${1}$"; then
return 0
fi
return 1
}
# Print the checks that name this wrapper
function get_wrapper_checks() {
# $1: wrapper name
echo "${_nrpe_conf_lines}" | grep -E "command\[check_.*\]=" | grep -E "\-\-name\s+${1}\s+" | awk -F"[\\\[\\\]=]" '{sub("check_", "", $2); print $2}' | sort | uniq | xargs
}
# Load NRPE configuration
_nrpe_conf_lines="$(_get_conf_from_file "${nrpe_conf_path}")"

View file

@ -0,0 +1,350 @@
#!/usr/bin/env python3
#
# Common functions for monitoringctl and alerts_wrapper
#
# Source:
# https://gitea.evolix.org/evolix/ansible-roles/src/branch/stable/nagios-nrpe/
import sys, os
from datetime import datetime, timezone
import re
import subprocess
# Location of disable files
var_dir = '/var/lib/monitoringctl'
log_file = '/var/log/monitoringctl.log'
nrpe_conf_path = '/etc/nagios/nrpe.cfg'
# If no time limit is provided in CLI or found in file, this value is used
default_disabled_time = '1h'
_nrpe_conf_lines = '' # populated at the end of the file
def error(err_msg):
print(err_msg, file=sys.stderr)
exit(1)
def usage_error(err_msg):
print(err_msg, file=sys.stderr)
usage_msg='Execute "{} --help" for information on usage.'.format(prog_name)
print(usage_msg, file=sys.stderr)
exit(1)
def log(log_msg):
now = now_iso()
with open(log_file, 'a', encoding='utf-8') as file:
line = '{} - {}: {}'.format(now, prog_name, log_msg)
file.write(line)
def show_version(prog_name, prog_version):
msg = '''
{1} version {2}.
Copyright 2018-2024 Evolix <info@evolix.fr>,
Jérémy Lecour <jlecour@evolix.fr>
and others.
{1} comes with ABSOLUTELY NO WARRANTY.This is free software,
and you are welcome to redistribute it under certain conditions.
See the GNU General Public License v3.0 for details.
'''.format(prog_name, prog_version)
print(msg)
# Return Debian major version number : 10, 11, 12…
def debian_major_version():
if not os.path.exists('/etc/debian_version'):
error('OS is not Debian (/etc/debian_version missing).')
version = open('/etc/debian_version', 'r', encoding='utf-8').read().strip() # -> major.minor
return int(version.split('.')[0])
debian_major_version = debian_major_version()
# Return False if duration does not follow format: XwXdXhXmXs, XhX, XmX
def filter_duration(duration):
time_regex='^([0-9]+w)?([0-9]+d)?(([0-9]+h(([0-9]+m?)|([0-9]+m([0-9]+s?)?))?)|(([0-9]+m([0-9]+s?)?)?))?$'
pattern = re.compile(time_regex)
match = pattern.fullmatch(duration)
return match != None
# Convert human writable duration into seconds
def time_to_seconds(duration):
regex=(r'((?P<weeks>\d+)w)?'
r'((?P<days>\d+)d)?'
r'((?P<hours>\d+)h)?'
r'((?P<minutes>\d+)(m?$|m))?'
r'((?P<seconds>\d+)(s?$|s))?'
)
pattern = re.compile(regex, re.IGNORECASE)
m = pattern.match(duration)
if not m:
error('Invalid duration: "{}".'.format(duration))
duration_dict = {}
for s in ['weeks', 'days', 'hours', 'minutes', 'seconds']:
if not m[s]:
duration_dict[s] = 0
else:
duration_dict[s] = int(m[s])
duration_secs = duration_dict['weeks'] * 604800 + duration_dict['days'] * 86400 + duration_dict['hours'] * 3600 + duration_dict['minutes'] * 60 + duration_dict['seconds']
return duration_secs
# Print re-enable time in secs
def get_enable_time(wrapper_name):
enable_secs = now_secs()
disable_file_path = get_disable_file_path(wrapper_name)
if not os.path.exists(disable_file_path):
return enable_secs
with open(disable_file_path, 'r', encoding='utf-8') as file:
pattern = re.compile('^[0-9]+$')
for line in file:
match = pattern.fullmatch(line.strip())
if match:
enable_secs = int(line.strip())
break
# If disable_file_path is empty, use last change date plus default disabled time
if not enable_secs:
file_last_change_secs = int(os.stat(disable_file_path).st_ctime) # stat -c %Z
default_disabled_time_secs = time_to_seconds(default_disabled_time)
enable_secs = file_last_change_secs + default_disabled_time_secs
return enable_secs
# Return disable message
def get_disable_message(wrapper_name):
disable_file_path = get_disable_file_path(wrapper_name)
if not os.path.exists(disable_file_path):
return
lines = ''
with open(disable_file_path, 'r', encoding='utf-8') as file:
lines = file.readlines()
if len(lines) > 1:
# Remove first line which contains re-enable time
# The next lines as the disable message
lines = lines[1:]
return ' '.join(lines).replace('\n', '')
def now_secs():
now_secs = round(datetime.now().timestamp())
return now_secs
def now_iso():
dt = datetime.now(timezone.utc).astimezone()
dt = dt.replace(microsecond=0)
return dt.isoformat()
# Return delay before re-enable in secs
def calc_enable_delay(reenable_time): # in secs
return int(reenable_time - now_secs())
# Convert delay (in seconds) into human readable duration
def delay_to_string(delay):
delay_days = '{}d'.format(delay // 86400)
if delay_days == '0d': delay_days = ''
delay_hours = '{}h'.format((delay % 86400) // 3600)
if delay_hours == '0h' and not delay_days: delay_hours = ''
delay_minutes = '{}m'.format(((delay % 86400) % 3600) // 60)
if delay_minutes == '0m' and not delay_hours: delay_minutes = ''
delay_seconds = '{}s'.format(((delay % 86400) % 3600) % 60)
if delay_seconds == '0s' and not delay_minutes: delay_seconds = ''
return '{}{}{}{}'.format(delay_days, delay_hours, delay_minutes, delay_seconds)
def is_disabled_check(check_name):
wrapper = get_check_wrapper_name(check_name)
return is_disabled_wrapper(wrapper)
def is_disabled_wrapper(wrapper_name):
disable_file_path = get_disable_file_path(wrapper_name)
if os.path.exists(disable_file_path):
enable_time = get_enable_time(wrapper_name)
enable_delay = calc_enable_delay(enable_time)
is_disabled = enable_delay > 0
return is_disabled
else:
return False
def get_disable_file_path(wrapper_name):
return '{}/{}_alerts_disabled'.format(var_dir, wrapper_name)
### Nagios configuration functions ####################
# Return NRPE configuration, with includes, without comments
# and in the same order than NRPE does (taking account that
# order changes from Deb10)
def get_nrpe_conf():
return _nrpe_conf_lines
# Private function to recursively get NRPE conf from file
def _get_conf_from_file(file_path):
if not os.path.exists(file_path) or not os.path.isfile(file_path):
return
conf_lines = []
with open(file_path, 'r', encoding='utf-8') as file:
for line in file:
line = line.split('#')[0].strip()
if line:
if 'include=' in line:
conf_file = line.split('=')[1]
include = _get_conf_from_file(conf_file)
conf_lines.extend(include)
elif 'include_dir=' in line:
conf_dir = line.split('=')[1]
include = _get_conf_from_dir(conf_dir)
conf_lines.extend(include)
elif 'check_hda1' in line:
continue # Ludo dirty hack to avoid modifying /etc/nrpe/nrpe.cfg
else:
conf_lines.append(line)
return conf_lines
# Private function to recursively get NRPE conf from directory
def _get_conf_from_dir(dir_path):
if not os.path.exists(dir_path) or not os.path.isdir(dir_path):
return
# Get dir content in the right order (depending on debian_major_version).
# From Deb10, NRPE uses scandir() with alphasort() function, so we use 'sort' to reproduce it.
# Before Deb10, NRPE used loaddir(), so we keep 'find' output order because it also uses loaddir().
command = 'find "{}" -maxdepth 1 -name "*.cfg" 2> /dev/null'.format(dir_path)
if debian_major_version >= 10:
command += ' | sort'
proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
stdout, stderr = proc.communicate()
dir_content = stdout.decode('utf8').split('\n')
# Process recursively dir_path content
conf_lines = []
for path in dir_content:
if os.path.isfile(path):
include = _get_conf_from_file(path)
conf_lines.extend(include)
elif os.path.isdir(path):
include = _get_conf_from_dir(path)
conf_lines.extend(include)
return conf_lines
# Return the checks that are configured in NRPE
def get_checks_names():
pattern = re.compile('command\[check_([0-9a-zA-Z_\-]*)\]=')
check_names = []
for line in _nrpe_conf_lines:
match = re.search(pattern, line)
if match and len(match.groups()) == 1 and match.group(1) not in check_names:
check_names.append(match.group(1))
return check_names
# Return all the commands defined for check_name in NRPE configuration
def get_check_commands(check_name):
pattern = re.compile('command\[check_{}\]=(.*)'.format(check_name))
commands = []
for line in _nrpe_conf_lines:
match = re.search(pattern, line)
if match and len(match.groups()) == 1:
commands.append(match.group(1))
return commands
# Return the checks that have no alerts_wrapper in NRPE configuration
def not_wrapped_checks():
not_wrapped = []
for check in get_checks_names():
if not is_wrapped(check) and check not in not_wrapped:
not_wrapped.append(check)
return not_wrapped
# Return True if check is wrapped
def is_wrapped(check_name):
commands = get_check_commands(check_name)
if not commands:
return False
command = commands[-1]
return 'alerts_wrapper' in command
# Private function to extract the name of the wrapper from a NRPE config line
def _get_wrapper_name_from_line(line):
if 'alerts_wrapper' in line:
words = line.split(' ')
while '' in words:
words.remove('')
for i in range(len(words)):
if words[i] in ['--name', '-n']:
return words[i+1]
# Return the names that are defined in the wrappers of the checks
def get_wrappers_names():
wrapper_names = []
for line in _nrpe_conf_lines:
wrapper_name = _get_wrapper_name_from_line(line)
if wrapper_name:
wrapper_names.append(wrapper_name)
return wrapper_names
# Return the wrapper name of the check
def get_check_wrapper_name(check_name):
commands = get_check_commands(check_name)
if not commands:
return
return _get_wrapper_name_from_line(commands[-1])
def is_check(check_name):
checks = get_checks_names()
return check_name in checks
def is_wrapper(wrapper_name):
wrappers