===== Скрипты для системы мониторинга 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]}}