пятница, 30 октября 2015 г.

Пишем свои плагины

Попробуем разобраться как можно написать свой плагин для icinga2 (конечно же его можно будет использовать и для всех нагиосоподобных). Сперва стоит остановиться на вопросе: для чего вообще писать свои плагины, когда есть и некий набор, и также на просторах интернетов существует куча разнообразных написанных кем то плагинов.
Ответов на этот вопрос может быть несколько, от попробовать разобраться как это всё работает до тонкого тюнинга проверок и создания очень нестандартных проверок.

Я использую самописные плагины по следующим причинам:
- возможность логирования не только конечных данных, но и промежуточных, что возможно потребуется при разборе полётов некой ситуации;
- русификация текста сообщений выдаваемых плагином, т.к. не всегда персонал, получающий сообщения об изменении статуса объектов мониторинга, умеет читать буржуйские словечки, а некоторые лучше бы и не пытались читать;
- использование протокола SNMP для снятия данных, т.к. в реальной жизни существует много ограничений, например, бывают случаи, что не на все сервера можно ставить стороннее ПО, а SNMP есть почти везде и всегда это штатное исполнение;
- и, пожалуй, самая основная причины -- отсутствие нужных плагинов.

#!/bin/bash
# Супер плагин
# v.01a
/usr/bin/echo "Всё хорошо, прекрасная маркиза"
exit 0


Вот так выглядит вполне рабочий плагин. Система мониторинга может с ним работать, т.к. присутствует выводимый текст и код завершения.

Примерно становится понятно, что реальный плагин будет содержать в себе некие действия для определения параметров объекта мониторинга и вычисления его статуса, от которого будет зависеть код выполнения плагина.

Рассмотрим пример плагина, в котором определяется состояние сервиса на линуксовом сервере по протоколу SNMP.

#!/bin/bash
# Снимаем данные с серверов с ОС Linux по SNMP
# v.01
PATH_CONF="/usr/local/libexec/icinga2/my_libexec"
. $PATH_CONF/_exit_codes
. $PATH_CONF/_commands
. $PATH_CONF/_functions
. $PATH_CONF/_conf_def
case "$FUNCTION" in
        "proc"          )       ID_RAW=`$snmpwalkcmd -Oqs -c blablabla -v 1 $HOSTNAME prNames 2>>$LOG_FILE`
                                if [ $? = 0 ]
                                        then
                                                ID=`$echocmd "$ID_RAW" | $grepcmd "$ARG1" | $awkcmd '{print $1}' | $trcmd -d 'prNames.'`
                                                if [ -z $ID ]
                                                        then
                                                                MSG="ID процесса не определён"
                                                        else
                                                                CHECK_ERROR=`$snmpwalkcmd -Oqv -c
blablabla -v 1 $HOSTNAME prErrorFlag.$ID 2>>$LOG_FILE`
                                                                if [ $? = 0 ]
                                                                        then
                                                                                case "$CHECK_ERROR" in
                                                                                        "noError"       )       MSG="В работе"
                                                                                                                EXIT_CODE=$OK ;;
                                                                                        "error"         )       MSG="Служба остановлена"
                                                                                                                EXIT_CODE=$CRITICAL ;;
                                                                                        *               )       MSG="НЕИЗВЕСТНО"
                                                                                                                EXIT_CODE=$UNKNOWN ;;
                                                                                esac
                                                                        else
                                                                                MSG="$MSG_TIMEOUT"
                                                                fi
                                                fi
                                        else
                                                MSG="$MSG_TIMEOUT"
                                fi
                                $printfcmd "%b" "$DATE\nService: $ARG1\nID=$ID\nCHECK_ERROR: $CHECK_ERROR\n$MSG\n\n" >> $LOG_FILE

                        ;;
 ...
         *               )       exit $UNKNOWN
                                 $echocmd "Функция не определена" ;;
esac

$echocmd $MSG
exit $EXIT_CODE


где:
# cat _exit_codes
OK=0
WARNING=1
CRITICAL=2
UNKNOWN=3


# cat _conf_def
HOSTNAME=$1
FUNCTION=$2
ARG1=$3
ARG2=$4

......
LOG_FILE="/var/log/icinga2/snmp_logs/$HOSTNAME.$FUNCTION"
EXIT_CODE=$CRITICAL
MSG_TIMEOUT="Узел $HOSTNAME не ответил за максимально допустимое время"

.....

# cat _commands
echocmd="/usr/bin/echo"
awkcmd="/usr/bin/awk"
cutcmd="/usr/bin/cut"
bccmd="/usr/bin/bc"
grepcmd="/bin/grep"
snmpwalkcmd="/usr/bin/snmpwalk"
trcmd="/usr/bin/tr"
printfcmd="/usr/bin/printf"
wccmd="/usr/bin/wc"
smbclientcmd="/usr/bin/smbclient"
lscmd="/usr/bin/ls"

....

В первом файле (_exit_codes) определены коды выходов, чтобы удобнее и понятнее было их использовать. Во втором файле (_conf_def) аргументы передаваемые плагину, а в третьем (_commands) определены пути к исполняемым файлам, удобно при переносе на другую систему.

Запустить и проверить работоспособность плагина можно так:
$ ./check_snmp_linux server.local proc service_name
В работе
$ echo $?
0


Также к выводимому тексту можно добавить через трубу "|" дополнительные данные, так называемые "Performance Data", которые можно будет использовать для построения графиков числовых состояний объектов мониторинга, например:
$ ./check_snmp_linux server.local disk /dev/sda7 90 95
Используется 500.3 Гбайт (80%) из 656.6 Гбайт|disk=80%;9
0;95;0;100


или так:
$ ./check_snmp_linux server.local load 70 70 70 90 90 90
Средняя загрузка: 0.00, 0.00, 0.00, | 1min=0.00;70;90;0;100 5min=0.00;70;90;0;100 15min=0.00;70;90;0;100


С Performance Data попробуем разобраться попозже.

Итак, написать свой плагин не так уж и сложно, делать это можно на любом языке, никаких ограничений нет. Может быть использована любая цепочка обработки и переброски данных.
Например, есть группа серверов которая по мультикасту обменивается о своих ролях, нам необходимо знать текущие роли и во время узнавать о смене ролей.
Для этого нам нужно подписаться на мультикаст, можно использовать Twisted.
Пакеты с информацией о ролях серверов получили, теперь определим значения ролей, используя любой сетвой анализатор, для нашей цели может подойти NGREP, вывод которого можно разобрать своим парсером.
Осталось подсунуть конечные данные системе мониторинга, для этого существует множество вариантов от взятия из файла текущей роли до передачи через пассивные проверки, также предыдущий пункт мог быть выполнен как активная проверка (плагин для icinga2).
Также можно отправить дополнительное уведомление для не очень квалифицированного персонала, в котором будет подробное описание произошедшего, сделать это можно как и через систему мониторинга, так и без её использования.