Доброго времени суток.
Уже довольно давно я пользуюсь связкой 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
Отличный мануал! Спасибо
Огонь, ровно то что нужно, спасибо!