Простой мониторинг или 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
Панда/ автор статьи
Понравилась статья? Поделиться с друзьями:
Медвежья берлога
Комментарии: 2
  1. Avatar photo
    Alex P.

    Отличный мануал! Спасибо

  2. Вася

    Огонь, ровно то что нужно, спасибо!

Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: