Простой мониторинг или NetWatch для FreeBSD/Linux (bash)

Доброго времени суток.
Уже довольно давно я пользуюсь связкой Mikrotik (Netwatch) + Telegram для контроля за состоянием разных железок в своей сети и своевременном уведомлении об изменении этого состояния.

Говоря простым языком — Mikrotik пингует указанные хосты и сообщает в Telegram, когда хост перестал отвечать, или наоборот снова вернулся в строй.
Подробнее о том, что это и как настраивается можно посмотреть в статье Отправка уведомлений в Telegram с mikrotik

Работает эта связка весьма неплохо. Задачу свою выполняет в полной мере, но ввиду технических ограничений моего микротика, мне этот вариант перестал подходить.

Много проверяемых хостов очень сильно грузят процессор, особенно при старте роутера, когда все они начинают проверяться одновременно. А точнее — начинают одновременно отправляться уведомления о текущем состоянии.

А так как используется слабый hAP Lite из серии SMIPS, да еще и нагруженный другими задачами — периодически он не успевает отработать и уведомление просто «исчезает».

Так, в очередной раз пропустив сообщение о «упавшей» железке, я задумался — а можно ли перенести мониторинг куда то еще. Например на сервер под FreeBSD. Тем более он у меня есть и нагрузки на нем не много.

Естественно я подумал о готовых решениях вроде Zabbix — но честно говоря мне кроме уведомлений от него ничего не нужно, а ставить целый программный комплекс ради такого пустяка, как то не правильно.

Как я своему серверу в глаза посмотрю после этого?)

К тому же использовать готовое решение — не так интересно.
Потому было принято решение сделать свой NetWatch, как говорится «с блэкджеком и плюхами».

Постановка задачи

1) Нужно пинговать хост
— если хост доступен — отправить уведомление @hostname is UP
— если не доступен — отправить уведомление @hostname is DOWN
2) Нужно учесть тот факт, что хосты будут проверяться с определенной периодичностью —
то есть нужно как то запомнить его последнее состояние и принимать решение в зависимости от него.

Иначе говоря:
— если хост был UP — и сейчас он доступен — уведомлять не нужно
— если хост был DOWN — и сейчас он не доступен — уведомлять тоже не нужно
3) Нужно реагировать на настоящие проблемы, а не на каждую случайную потерю — а значит нужно иметь возможность откорректировать чувствительность пингов.
Т.е. если хост
— пингуется сразу — состояние UP
— не отвечает на 2-3 запроса, но на 4й отвечает — состояние UP
— не отвечает на 5 и более запросов — состояние DOWN
4) Так как проверяемых хостов довольно много нужно добавлять и удалять их отдельно в каком то файле. Для удобства пользования этим файлом нужно учесть наличие в нем комментариев и пустых строк
5) Текст уведомления будет всегда одинаковый (хоть и 2 варианта), поэтому нужно сделать шаблон

С задачей определились.

Приступим к реализации

В качестве интерпретатора будем использовать bash

!/bin/bash

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

Это поможет избежать проблем при запуске скрипта через cron

ping=/sbin/ping
read=/usr/bin/read
wget=/usr/local/bin/wget
grep=/usr/bin/grep

Объявим переменные для файлов, с которыми будем работать

Так не нужно будет в коде писать длинные пути.

### Файл со списком хостов
### Может содержать комментарии (#) и пустые строки
hosts="/usr/home/@username@/scripts/netwatch/list"

### Временный файл с очищенными от мусора данными. Без пустых и закомментированных строк
list="/usr/home/@username@/scripts/netwatch/list-clean"

### Директория куда будут складываться логфайлы с последним статусом каждого хоста
log="/usr/home/@username@/scripts/netwatch/log/"

Зададим параметры для уведомлений в Telegram

Почитать о том, как создать бота для уведомлений, узнать его идентификатор, id чата и т.д. можно все в той же статье
Отправка уведомлений в Telegram с mikrotik

### Ссылка для отправки сообщения боту
url="https://api.telegram.org/botXXXXXXXXX:AAHzIzSpz3LYouCmf-nzXd_rY2uEuuCkuxc/sendMessage?chat_id=IDIDIDID&text="

### Для удобства - перед текстом приходит дата и время изменения статуса
time=$(date "+%d/%m/%Y, %H:%M:%S - ")

### Текст сообщения для разных случаев
##	UP - хост вышел на связь
##	DOWN - хост не отвечает
##	ADD - хост добавлен в мониторинг (по умолчанию выключено)
textUP=" is UP"
textDOWN=" is DOWN"
textADD=" added to NetWatch"

Зададим параметры для пинга

Timeout — время ожидания ответа в секундах
(если равно 2 — то через 2 секунды без ответа запрос считается не отвеченным)
Count — количество запросов к хосту

TIMEOUT=5
COUNT=3

С переменными разобрались.

Перейдем к исполняемой части.

Прежде чем начнем работу с данными нам нужно очистить файл от лишнего «мусора» в виде комментариев и пустых строк.
Для этого запишем отфильтрованные строки в отдельный временный файл, с которым и будет работать цикл

cat $hosts | $grep "^[^#*/;]" > $list

Так как операции над данными будут происходить внутри цикла приведу для понимания общую картину в упрощенном виде

while read -r name ip; do
		####
		действия
		####
done < $file

Т.е. внутри цикла каждая строка из файла $file разделяется на слова и каждое помещается в свою переменную:
$name — первое слово в строке
$ip — второе слово в строке

Надеюсь общий смысл поняли — рассмотрим подробнее

while read -r name ip; do
### задаем имя для логфайла, в который будем писать статус хоста/
### состоит из пути файла и имени хоста
        file=$log$name.log  

### Если логфайла с таким именем не существует - создаем его, записывая внутрь статус DOWN
        if [ ! -f $file ]; then
                echo "DOWN" > $file;

### При желании здесь можно сообщать в телеграм о добавлении нового хоста
### По умолчанию закомментированно, чтобы не слать слишком много сообщений при первом добавлении хостов
#               $wget -q -O /dev/null "$url$time$name$textADD" 
        fi

### Читаем статус из логфайла
        read status < $file

### Пингуем хост с заданными ранее параметрами и присваиваем переменной p значение в зависимости от результата
### 1 - хост доступен
### 0 - хост не отвечает
        $ping -oc $COUNT -t $TIMEOUT $ip > /dev/null && p=1 || p=0

### Если статус хоста не изменился с прошлой проверки - переходим к следующему
        if [ $status == "DOWN" ] && [ $p -eq 0 ]; then
                continue;
        else
                if [ $status == "UP" ] && [ $p -eq 1 ]; then
                        continue;
                fi
        fi

### Если статус хоста изменился с прошлой проверки - пишем новый статус в логфайл и отправляем уведомление
        if [ $status == "UP" ] && [ $p -eq 0 ]; then
                $echo DOWN > $file;
                $wget -q -O /dev/null "$url$time$name$textDOWN";
        else
                if [ $status != "UP" ] && [ $p -eq 1 ]; then
                $echo UP > $file;
                $wget -q -O /dev/null "$url$time$name$textUP";
                fi
        fi

### Конец цикла (чтения из файла)
done < $list

Удаляем ненужный теперь временный файл с отфильтрованными данными

rm $list

Вот и весь скрипт.
Осталось добавить его в Cron, указать необходимую частоту запуска и радоваться жизни

### NetWatch
*/1 * * * * root /usr/local/bin/bash /usr/home/@username@/scripts/netwatch/netwatch > /dev/null 2>&1

Ниже представляю полный текст скрипта

#!/bin/bash

### System Soft
echo=/bin/echo
ping=/sbin/ping
read=/usr/bin/read
wget=/usr/local/bin/wget
grep=/usr/bin/grep

### Files
hosts="/usr/home/@username@/scripts/netwatch/list"
list="/usr/home/@username@/scripts/netwatch/list-clean"
log="/usr/home/@username@/scripts/netwatch/log/"

### Telegram
url="https://api.telegram.org/botXXXXXXXXX:AAHzIzSpz3LYouCmf-nzXd_rY2uEuuCkuxc/sendMessage?chat_id=IDIDIDID&text="
time=$(date "+%d/%m/%Y, %H:%M:%S - ")
textUP=" is UP"
textDOWN=" is DOWN"
textADD=" added to NetWatch"

### Ping
TIMEOUT=5 #timeout if unreachable (sec)
COUNT=3 #count of pings

### Clean comments and spaces
cat $hosts | $grep "^[^#*/;]" > $list

### Read from file
while read -r name ip; do
        file=$log$name.log
### Create logfile if not exist with default DOWN status
        if [ ! -f $file ]; then
                echo "DOWN" > $file;
#               $wget -q -O /dev/null "$url$time$name$textADD"
        fi
### Read status from logfile
        read status < $file
### Ping host
        $ping -oc $COUNT -t $TIMEOUT $ip > /dev/null && p=1 || p=0

### If ping status do not change - go to next host
        if [ $status == "DOWN" ] && [ $p -eq 0 ]; then
                continue;
        else
                if [ $status == "UP" ] && [ $p -eq 1 ]; then
                        continue;
                fi
        fi

### If ping status change - edit logfile and send message
        if [ $status == "UP" ] && [ $p -eq 0 ]; then
                $echo DOWN > $file;
                $wget -q -O /dev/null "$url$time$name$textDOWN";
        else
                if [ $status != "UP" ] && [ $p -eq 1 ]; then
                $echo UP > $file;
                $wget -q -O /dev/null "$url$time$name$textUP";
                fi
        fi

### End of While (read from file)
done < $list
###delete temporary clean list file
rm $list

Также для полного понимания картины — вот вам пример содержимого файла list

#servers
hostname1			10.0.0.1
hostname2			10.0.0.2
#hostname3			10.0.0.3

#switches
hostname4			10.0.0.4
hostname5			10.0.0.5
#hostname6			10.0.0.6

4 комментария к “Простой мониторинг или NetWatch для FreeBSD/Linux (bash)”

  1. Евгений
    все отлично, а для Zabbix как такое прикрутить только через ssh на микрот что бы пинг мониторил
    1. Добрый день. Перефразируйте задачу, совершенно не понятно что именно вы Zabbix хотите.

Оставьте комментарий

Ваш адрес email не будет опубликован.