===== Скрипты для системы мониторинга Nagios =====
==== Отправка СМС ====
В скрипте для отправки сообщений используется программа gnokii. С небольшими правками скрипта можно переделать на использование gammu. Описаний настройки gnokii/gammu не привожу, они выходят за рамки описания скрипта.
Для диагностических целей скрипт пишет свои действия в лог-файл /var/log/nagios4/sendsms.log
"set -o pipefail" в данном скрипте необязателен, но я почти всегда задаю опцию pipefail, которая указывает оболочке, что код завершения конвейера будет совпадать с первым ненулевым кодом завершения одной из команд конвейера или же нулю в случае, если все команды завершились корректно
Если отправка оповещений делается нескольким адресатам, то параллельное обращение к модему/телефону приведет к тому, что какие-то смс не будут доставлены. Чтобы этого избежать я делаю отправку в цикле, пока она не будет успешной (код возврата 0). Чтоб в случае постоянной ошибки скрипт не ушел в бесконечный цикл ограничиваю число попыток отправки (100 попыток думаю достаточно)
"LANG=ru_RU.UTF-8" перед выполнением программы gnokii нужно, чтоб кириллический текст в смс нормально отображался.
#!/bin/bash
set -o pipefail
echo "$(date +%Y-%m-%dT%H:%M:%S) Send SMS to $2: $1"
echo "$(date +%Y-%m-%dT%H:%M:%S) Send SMS to $2: $1">>/var/log/nagios4/sendsms.log
retcode=1
try=1
while [[ $retcode -ne 0 |]]
do
echo "Try$try: LANG=ru_RU.UTF-8 echo \"$1\" | /usr/bin/gnokii --sendsms \"$2\" --smsc \"+79289900028\"">>/var/log/nagios4/sendsms.log 2>&1
LANG=ru_RU.UTF-8 echo "$1" | /usr/bin/gnokii --sendsms "$2" --smsc "+79289900028"
retcode=$?
try=$((try++))
if [[ $try -ge 100 |]]; then echo "Max try 100 exceeded"; break; fi
sleep 1
done
echo "$(date +%Y-%m-%dT%H:%M:%S) Send SMS to $2: $1 RetCode:$retcode">>/var/log/nagios4/sendsms.log 2>&1
echo "$(date +%Y-%m-%dT%H:%M:%S) Send SMS to $2: $1 RetCode:$retcode"
exit $retcode
==== Отправка оповещений в Telegram ====
Для работы скрипта в нем нужно задать правильные параметры телеграм-канала: TOKEN и CHAT_ID
#!/bin/bash
set -o pipefail
TOKEN="SpecifyYourTelegramToken"
CHAT_ID="SpecifyChatId"
APIURL="https://api.telegram.org/bot${TOKEN}/sendMessage"
if [[ -z "$1$2" ]]; then echo "Missing arguments" >&2; exit 2; fi
if [[ -z "$2" ]]
then
SUBJECT=""
MESSAGE="$1"
else
SUBJECT="$1"
MESSAGE="$2"
fi
curlres=$(curl -s --header 'Content-Type: application/json' --request 'POST' --data "{\"chat_id\":\"${CHAT_ID}\",\"text\":\"${SUBJECT}\n${MESSAGE}\"}" "${APIURL}")
curlerr="$?"
if [[ $curlerr -ne 0 ]]; then echo "Curl error:$curlerr" >&2; exit 2;fi
if [[ "$(echo "$curlres"jq ".ok")" != "true" ]]
then
echo "api.telegram error" >&2
echo "cmd=curl -s --header 'Content-Type: application/json' --request 'POST' --data \"{\"chat_id\":\"${CHAT_ID}\",\"text\":\"${SUBJECT}\n${MESSAGE}\"}\" \"${APIURL}\"" >&2
echo "ressult=$curlres" >&2
fi
exit 0
==== Проверка загрузки CPU ====
Для проверки загрузки процессора используется программа mpstat (входит в состав пакета sysstat)
#!/bin/bash
E_OK=0;E_WARN=1;E_CRIT=2;E_UNKNOWN=3 #Коды возврата Nagios
WARN=20; CRIT=10 #Задаем пороги предупреждений и критического состояния по-умолчанию.
# Считываем пороги, если они заданы в командной строке
if [[ -n "$1" ]]; then WARN="$1";fi
if [[ -n "$2" ]]; then CRIT="$2";fi
res=$E_OK; res_txt="OK"
read check_time CPU usr nice sys iowait irq soft steal guest gnice idle <<<$(S_TIME_FORMAT=ISO mpstat --dec=0 1 1|tail -n2|head -n1)
if [[ $idle -le $WARN ]]; then res=$E_WARN; res_txt="WARN"; fi
if [[ $idle -le $CRIT ]]; then res=$E_CRIT; res_txt="CRIT"; fi
echo -n "$res_txt: Idle=$idle% usr=$usr% nice=$nice% sys=$sys% iowait=$iowait% irq=$irq% soft=$soft% steal=$steal% guest=$guest% gnice=$gnice%"
echo "|Idle=$idle%;$WARN;$CRIT;; usr=$usr%;;;; nice=$nice%;;;; sys=$sys%;;;; iowait=$iowait%;;;; irq=$irq%;;;; soft=$soft%;;;; steal=$steal%;;;; guest=$guest%;;;; gnice=$gnice%;;;;"
exit $res
==== Проверка состояния сетевого интерфейса ====
#!/bin/sh
# Exit-Codes:
STATE_OK=0;STATE_WARNING=1;STATE_CRITICAL=2;STATE_UNKNOWN=3
usage()
{
cat <
==== Пинг большими нефрагментированными пакетами ====
Зачем? Ну например для проверки линков с включенным JumboFrame.
#!/bin/sh
E_OK=0;E_WARN=1;E_CRIT=2;E_UNKNOWN=3
#Defaults
WARN=100
CRIT=200
WARNPCT=10
CRITPCT=20
PKTCNT=5
PKTSIZE=6050
res=$E_OK;txt="OK"
usage()
{
cat <= $WARN"|bc) -ne 0 ] && [ $res -lt $E_WARN ]
then
res=$E_WARN;txt="WARNING"
if [ $(echo "$TIME >= $CRIT"|bc) -ne 0 ] && [ $res -lt $E_CRIT ]
then
res=$E_CRIT;txt="CRITICAL"
fi
fi
echo "$txt: Host $CHECKHOST - $PCT% packet loss (warn:$WARNPCT,crit:$CRITPCT), AVG rtt $TIME (warn:$WARN,crit:$CRIT)|PacketLoss=$PCT%;$WARNPCT;$CRITPCT;0;100 RTT=${TIME}ms;$WARN;$CRIT;;"
exit $res
==== Сравнение локального файла с файлом на удаленной системе ====
Область применения - например проверка идентичности конфиг-файла на двух серверах.
Сравнение делается с помощью подключения по ssh. Для корректной работы скрипта требуется настроить ключи ssh для пользователя из под которого будет работать скрипт.
#!/bin/bash
E_OK=0;E_WARNING=1;E_CRITICAL=2;E_UNKNOWN=3
usage()
{
cat <
Модифицированная версия. Может сравнивать несколько файлов заданных шаблоном (напр. /etc/application/*.conf)
#!/bin/bash
STATE_OK=0;STATE_WARNING=1;STATE_CRITICAL=2;STATE_UNKNOWN=3
usage()
{
cat << EOF
Usage: $0 remotehost filename
This script checks differecies of local and remote files
EOF
}
if [[ -z "$1" ]]; then usage; exit $STATE_UNKNOWN; else serv="$1"; fi
if [[ -z "$2" ]]; then usage; exit $STATE_UNKNOWN; else fname="$2"; fi
difs=""
ndifs=0
for f in $fname
do
dif="$(ssh "$serv" "cat $f"|diff - $f)"
if [[ $? -ne 0 ]]; then.
ndifs=$(($ndifs + 1 ))
difs="$difs$(echo $f):$dif"
fi
done
if [[ $ndifs -eq 0 ]]
then
echo "OK: File $fname. Local and on host $serv has no differencies"; exit $STATE_OK
else
echo "CRITICAL: File $fname. Local and on host $serv has differencies"; echo "$difs"; exit $STATE_CRITICAL
fi
==== Проверка DNS-записей на соответствие заданным ====
В nagios-plugins есть [[https://nagios-plugins.org/doc/man/check_dns.html|check_dns]], который позволяет (кроме другого функционала) проверить соответствуют ли записи о домене на днс-сервере желаемым. Однако check_dns не дружит с интернациональными доменами, даже если указать домен в punycode. Поэтому может потребоваться скрипт ниже.
Пример запуска скрипта
check_dns_recs.sh domain.com 1.1.1.1,2.2.2.2 8.8.8.8
В этом примере проверяется соответствуют ли записи домена domain.com на сервере 8.8.8.8 списку 1.1.1.1, 2.2.2.2
Адреса в списке можно указывать разделяя их между собой запятыми. Скрипт проверяет только А-записи (может позже это исправлю).
В основном действия в скрипте несложные. Немного описать имеет смысл следующую строку:
host -t $TYPE $DOMAIN $SERVER|grep "$DOMAIN has address "|grep -oE '[^ ]+$'|sort|tr '\n' ','|sed s/,$//
* grep -oE '[^ ]+$' - эта команда удаляет из строк все кроме последнего слова, а в последнем слове содержатся требуемые адреса домена. Если разделителем слов будет не только пробел, но и другие разделители, то может понадобиться преобразовать регулярное выражение [^ ] к виду [^[:space:]]
* sort - если не отсортировать вывод, то порядок запиесей может меняться и при сравнении один и тот же список адресов не совпадет с заданным шаблоном.
* tr '\n' ','|sed s%%/%%,$%%//%% - зменяем все переводы строк запятыми (объединяем строки в одну с разделителем запятая) и удаляем лишнюю запятую в конце строки.
#!/bin/bash
E_OK=0;E_WARN=1;E_CRIT=2;E_UNKNOWN=3 #Коды возврата Nagios
set -o pipefail
#Defaults
TYPE="A"
usage()
{
cat <
==== Проверка является ли заданный сервер кластера PgPool-II primary-хостом ====
check_pgpool_node_is_primary.sh pg_master_host
#!/bin/bash
E_OK=0;E_WARN=1;E_CRIT=2;E_UNKNOWN=3
if [[ -z "$1" ]]; then echo "Usage: $(basename $0) PostgresMasterHostname"; exit $E_UNKNOWN; fi
mastername="$1"
HOST="localhost"
#HOST="/var/run/postgresql"
res=$E_OK; res_txt="OK"; res_txt2="Host $mastername is Master"
strings="$(echo "node_id hname status role last_status_change")"
while IFS='|' read -r node_id hname port status lb_weight role select_cnt load_balance_node replication_delay replication_state replication_sync_state last_status_change
do
strings="$strings\n$(echo "$node_id $hname $status $role $last_status_change")"
if [[ "$hname" == "$mastername" && "$role" != "primary" ]]
then
res=$E_CRIT; res_txt="CRIT"; res_txt2="Host $hname is not Master"
fi
if [[ "$hname" != "$mastername" && "$role" == "primary" ]]
then
if [[ $res -lt $E_WARN ]]; then res=$E_WARN; res_txt="CRIT"; res_txt2="Host $hname is Master (myst be a slave)"; fi
fi
done < <(psql -tA -h localhost -c "SHOW pool_nodes;" postgres postgres)
echo "$res_txt: $res_txt2"
echo -e "$strings"
exit $res
==== Проверка состояния streaming-репликации PostgreSQL-серверов через PgPool-II====
Скрипт на пгпуле определяет который из серверов является мастером. Затем на мастере проверяется статистика по репликации.
Если позиции (LSN) в журналах различаются (не все отправлено на слейв, не все записано, не все подтверждено), то будет выдано предупреждение
Если состояние репликации будет не streaming, то будет выдано критическое состояние
В графики из получаемых данных ничего не запишешь, но все-таки я вывожу в них код завершения нагиос-скрипта. Это будет полезно для статистики. Коды возврата: E_OK=0;E_WARN=1;E_CRIT=2;E_UNKNOWN=3
#!/bin/bash
E_OK=0;E_WARN=1;E_CRIT=2;E_UNKNOWN=3
#HOST="localhost"
HOST="/var/run/postgresql"
while IFS='|' read -r node_id hname port status lb_weight role select_cnt load_balance_node replication_delay replication_state replication_sync_state last_status_change
do
if [[ "$role" == "primary" ]]; then master=$hname; break; fi
done < <(psql -tA -h localhost -c "SHOW pool_nodes;" postgres postgres)
res=$E_OK; res_txt="OK"; res_txt2=""
IFS='|' read -r client_addr state sent_lsn write_lsn flush_lsn replay_lsn \
<<< $(echo $(psql -tA -h $master -c 'select client_addr, state, sent_lsn, write_lsn,flush_lsn, replay_lsn from pg_stat_replication;' postgres postgres))
lsn_equal=1
for i in "$write_lsn" "$flush_lsn" "$replay_lsn"
do
if [[ "$sent_lsn" != "$i" ]]; then lsn_equal=0;break; fi
done
if [[ $lsn_equal -ne 1 ]]; then res=$E_WARN; res_txt="WARN"; res_txt2="LSNs is not equal"; fi
if [[ "$state" != "streaming" ]]; then res=$E_CRIT; res_txt="CRIT"; res_txt2="state != streaming"; fi
echo -n "$res_txt: Master is $master. $res_txt2 "
echo "client_addr:$client_addr state:$state sent_lsn:$sent_lsn write_lsn:$write_lsn flush_lsn:$flush_lsn replay_lsn:$replay_lsn|result=$res"
exit $res
{{tag>[shell bash nagios]}}