Користувальницькькі налаштування

Налаштування сайту


Сайдбар

Розділи

Загальний опис
Історія змін
Рекомендації до оновлення
Плани на майбутнє
Відомі проблеми
Онлайн демо
Допомога проекту
Люди
Трохи про безпеку

FAQ



Редагувати сайдбар

watchdog

Це стара версія документу!


Собака-наблюдака aka Watchdog

Собака-наблюдака предназначена для собаченья и наблюдаченья оперативного мониторинга окружающей действительности. Она предоставляет гибкий функционал для описания внештатных ситуаций практически любой странности а также для оповещения, в случае их возникновения. В том числе, при помощи отсылки SMS через поддерживаемые собакой-посылакой сервисы, по електронной почте, мессенджера Telegram а также может вызывать запуск какого-либо внешнего скрипта при описании события. Для включения собаки-наблюдаки требуется изменение опции WATCHDOG_ENABLED в alter.ini. Также для отсылки уведомлений, очевидно, в помощь собаке-наблюдаке потребуется Собака-посылака.

Возможные проверки

Типы задач Действие Возвращает
icmpping выполняется ICMP ping хоста указанного в параметре bool
tcpping выполняется попытка TCP соединения к хосту указанному в параметре в виде host:port bool
udpping выполняется попытка UDP соединения к хосту указанному в параметре в виде host:port bool
hopeping Пинг надежды. Трижды выполняется ICMP ping хоста указанного в параметре, в надежде на то, что хоть один вернется bool
script запуск shell-скрипта по пути указанному в параметре string
httpget получение данных из URL указанного в параметре string
getusertraff получение количества трафика пользователя с логином указанном в параметре int
fileexists проверка на существование файла по пути указанному в параметре bool
opentickets количество открытых тикетов хелпдеска. Требует указания рандомного параметра int
onepunch Производит запуск One-Punch скрипта по алиасу указанному в параметре. Результат ожидается в виде переменной $watchdogCallbackResult string
snmpwalk Выполняется snmpwalk по OID хоста указанных в параметре, в формате host:community:OID string
freediskspace Проверяет количество свободного места на разделе указанном в параметре. Возвращает цифру в Гб. float

Возможные операторы для проверки

Условия Значение Требует “Условие”?
=true Истинно
=false Ложно
== Равно +
!= Не равно +
> Больше +
< Меньше +
> = Больше или равно +
< = Меньше или равно +
empty Пустой результат
notempty Непустой результат
changed Изменилось
notchanged Не изменилось
like Содержит +
notlike Не содержит +
rised Увеличилось +-
decreased Уменьшилось +-

Действия выполняемые в результате прохождения условий

Действия Результат
log запись в системный лог
sms отсылка СМС уведомления, дополнительные номера можно указывать в формате {номер,номер}.
noprimary в случае указанных действий sms и указанных {доп. номерах} - основные номера игнорируются
email отсылка уведомления почтой
telegram отсылка уведомления Telegram, дополнительные чаты можно указывать в формате (чат1,чат2).
no_tg_primary в случае указанных действий telegram и указанных (доп. чатах) - основные чаты Telegram игнорируются
andresult в случае указанных действий sms или email к тексту сообщения будет добавлен текущий результат задачи
oldresult в случае указанных действий sms или email к тексту сообщения будет добавлен предыдущий результат задачи
script запуск скрипта или любого приложения, по пути указанному в виде [/полный/путь/к_скрипту]

Логика работы

Каждую задачу для Собаки-наблюдаки следует воспринимать как “случилось нечто” или “ой какое событие” которые случаются в случае возвращения “типом проверки” по “параметру” результата предусмотренного “оператором” с опциональным “условием”. Как пример, можно привести вот такую простенькую задачу:

Активный Имя Тип проверки Параметр Оператор Условие Действия
X Гугл не пингается icmpping 8.8.8.8 =false log,sms,email

При обнаружении того, что ping на адрес 8.8.8.8 вернул значение “ложно” собака-наблюдака заботливо отошлет вам СМС-ку, почту и запишет в лог уведомление о том, что “Гугл не пингается”.

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

Активный Имя Тип проверки Параметр Оператор Условие Действия
X Чего-то изменилось icmpping 192.168.0.22 changed log,sms,email,andresult

В принципе никто не запрещает нам делать и задачи такого плана:

Активный Имя Тип проверки Параметр Оператор Условие Действия
X В серверной пожар script /bin/gettemp > 22 log,sms,email, andresult

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

Если творчески подойти к парсингу вывода внешнего ПО - можно мониторить много интересных вещей без дописывания внешних скриптов:

Активный Имя Тип проверки Параметр Оператор Условие Действия
X DNS сломался script nslookup ya.ru | tail -n 2 like find log,sms

Также мы можем очень просто и элегантно контролировать запущенность важных сервисов типа stargazer создав задачу такого плана:

Активный Имя Тип проверки Параметр Оператор Условие Действия
X Stargazer упал script /bin/ps aux | /usr/bin/grep stg notlike stargazer log,sms

Хотя ту же самую по сути задачу мы можем оформить как

Активный Имя Тип проверки Параметр Оператор Условие Действия
X Stargazer упал fileexists /var/run/stargazer.pid =false log,sms

Чтобы не сосредотачиваться на тривиальных вещах, мы можем предусмотреть ситуацию, когда у нас есть очень важный клиент с логином vipclient на прекращение трафика от которого нам следует таки отреагировать. Описать такое мы можем в виде:

Активный Имя Тип проверки Параметр Оператор Условие Действия
X Важный клиент подох getusertraff vipclient notchanged log,sms,email

Окей, а если мы хотим также эту же СМС-ку послать скажем администратору этого же важного клиента, чтобы он точно знал, что он подох?

Активный Имя Тип проверки Параметр Оператор Условие Действия
X Важный клиент подох getusertraff vipclient notchanged log,sms,email {+380509999999}

а если мы хотим послать СМС только на дополнительный номер? Да, без проблем:

log,sms,email {+380509999999} noprimary

а если мы хотим послать алерт только в дополнительный/е чат/ы Telegram? Да, так же без проблем:

log,telegram,email (ChatID1,ChatID2,ChatID3) no_tg_primary

а можно миксовать СМС и Telegram? Да вообще без проблем:

log,telegram,sms,email (ChatID1,ChatID2,ChatID3) no_tg_primary {+380509999999} noprimary

Хотя опять же никто не запрещает нам, скажем мониторить этого клиента скажем внешним скриптом запрашивающим по SNMP состояние порта или счетчик трафика на свиче, куда он воткнут, либо банально его дергать по какому-то TCP порту. Думаю вышеприведенных примеров достаточно для получения представления о гибкости собаки-наблюдаки и возможностях построения оповещений по практически любым внештатным ситуациям. Обработка задач собаки происходит при соответствующем вызове RemoteAPI. Рекомендуемым интервалом является 10 минут.

А еще мы, можем реагировать не только на изменения текущих значений относительно каких-то порогов, а также и относительно предыдущих значений полученных собакой. Резкость этих изменений мы можем опционально указывать в условии. А можем и не указывать. Тогда мы будем реагировать вообще на все изменения этих циферок в сторону увеличения или уменьшения. Например мы можем реагировать на увеличение роста ошибок на каком-то интерфейсе

Активный Имя Тип проверки Параметр Оператор Условие Действия
X Ошибки полезли onepunch uplinkerrors rised log,sms,telegram

Ну либо какой-то уровень роста ошибок мы считаем допустимым, и например, устанавливаем порог в 100 ошибок за 10 минут

Активный Имя Тип проверки Параметр Оператор Условие Действия
X Ошибки полезли резво onepunch uplinkerrors rised 100 log,sms,telegram

Также мы можем таким способом отлавливать либо резкие всплески либо падения утилизации, например все того же канала. Типа, мы считаем, что если утилизация канала за последние 10 минут выросла на восемь гиг.. типа что-то пошло не так

Активный Имя Тип проверки Параметр Оператор Условие Действия
X Канал разогнался как-то onepunch uplinktraffic rised 8000 log,sms,telegram

Ну или наоборот резкие падения относительно предыдущих значений (типа утилизация аплинка провалилась на 20 гиг от последнего запуска собаки)

Активный Имя Тип проверки Параметр Оператор Условие Действия
X Трафик рухнул куда-то onepunch uplinktraffic decreased 20000 log,sms,telegram

А еще мы можем очень просто проверять рабочесть сервисов которые должны слушать соединения на какие-то TCP или UDP порты, типа так

Активный Имя Тип проверки Параметр Оператор Условие Действия
X http на хосте tcpping 192.168.42.18:80 changed log,sms,telegram andresult
Активный Имя Тип проверки Параметр Оператор Условие Действия
X https на хосте tcpping 192.168.42.18:443 changed log,sms,telegram andresult
Активный Имя Тип проверки Параметр Оператор Условие Действия
X syslogd на хосте udpping 192.168.42.18:514 changed log,sms,telegram andresult

А еще мы можем получать и проверять любые данные, из любого OID при помощи snmpwalk:

Активный Имя Тип проверки Параметр Оператор Условие Действия
X Версия OS snmpwalk 192.168.42.18:changeme:.1.3.6.1.2.1.1.1.0 changed log,sms,telegram andresult

Или банально нотифицировать себя о том, что в корневом разделе заканчивается место

Активный Имя Тип проверки Параметр Оператор Условие Действия
X Место в корне заканчивается freediskspace / < 100 log,sms,telegram andresult

Отсылка SMS

Не требует включенного модуля рассылки TurboSMS. Весь функционал отправки реализован посредством подсистемы "собака-посылака". Номера для отсылки разделяются запятыми и указываются в международном формате (например +380509999999). Их, как основных так и дополнительных может быть сколько угодно.

Отсылка Telegram

Осуществляется тоже при проходе “собаки-посылаки”. Как настроить своего бота для рассылки, можно почитать тут.

Примеры скриптов

Как уже говорилось выше, при помощи собаки-наблюдаки, возможно заскриптовать и контролировать вообще все что угодно - это ограничено только вашей фантазией и радиусом кривизны рук. Мы рекомендуем хранить кастомные скрипты такого плана в content/documents/myscripts/ - таким образом они будут нормально переживать обновления. Вот несколько, простых примеров:

Получение трафика с порта свитча

switch_traffic
#!/usr/local/bin/php
<?php
 
//config section
$port='24';
$oid='.1.3.6.1.2.1.31.1.1.1.6';
$ip='192.168.0.15';
$community='yourcommunity';
//end of config
 
$cmd='/usr/local/bin/snmpwalk -v2c -On  -c '.$community.' '.$ip.' '.$oid.'.'.$port;
$raw=shell_exec($cmd);
$newTime=time();
if (!empty($raw)) {
$raw=explode('Counter64:',$raw);
$raw=trim($raw[1]);
if (!empty($raw)) {
        $cacheName=dirname(__FILE__).'/octets_'.$ip.'_'.$port;
        if (file_exists($cacheName)) {
                $oldTime=filemtime($cacheName);
                $oldOctets=file_get_contents($cacheName);
                $traffDiff=$raw-$oldOctets;
                $timeDiff=$newTime-$oldTime;
                if ($timeDiff!=0) {
                $speed=($traffDiff*8*100)/($timeDiff*10000);
                print(round($speed/10000));
                } else {
                print('-1');
                }
            file_put_contents($cacheName,$raw);
        } else {
                file_put_contents($cacheName,$raw);
                print('0');
        }
}
 
}

Снятие температуры с eping3

eping_temp
#!/usr/local/bin/php
<?php
 
$ip='192.168.0.89';
$community='yourcommunity';
$oid='1.3.6.1.4.1.35160.1.16.1.13.1';
$result=0;
 
 
$command='/usr/local/bin/snmpwalk -On -r 1 -t 1 -v2c -c '.$community.' '.$ip.' '.$oid;
$resultRaw=shell_exec($command);
        if (!empty($resultRaw)) {
                $result=explode('INTEGER:',$resultRaw);
                $result=(isset($result[1])) ? $result[1] : 0;
                $result=$result/10;
        }
 
print($result);

Наличие электропитания с eping3

eping_power
#!/usr/local/bin/php
<?php
 
$ip='192.168.0.89';
$community='yourcommunity';
$oid='1.3.6.1.4.1.35160.1.26.0';
$result=0;
 
 
$command='/usr/local/bin/snmpwalk -On -r 1 -t 1 -v2c -c '.$community.' '.$ip.' '.$oid;
$resultRaw=shell_exec($command);
        if (!empty($resultRaw)) {
                $result=explode('INTEGER:',$resultRaw);
                $result=(isset($result[1])) ? $result[1] : 0;
                $result=($result==1) ? 'OK' : 'FAILED!';
        }
 
print($result);

Количество свободного места в /var/

var_stat
#!/usr/local/bin/php
<?php
$result=disk_free_space("/var/");
$result=$result/1024/1024/1024;
$result=round($result,2);
 
print($result);
?>

Джиттер до гугла

check_icmp_google
#!/bin/sh
ping -c 1 8.8.8.8 | head -n 2 | tail -n 1 | awk -F "=" '{print $4}' | awk -F " " '{print $1}

Хешрейт на ethermine.org

ethereum_hashrate
#!/usr/local/bin/php
<?php
 
$wallet='72F7B047C192178B1cef591Bc7e960aAD333B822';
$workersStatsUrl = 'https://api.ethermine.org/miner/:'.$wallet.'/workers';
$jsonRaw = file_get_contents($workersStatsUrl);
$reportedHashrate = 0;
 
if (!empty($jsonRaw)) {
    $workersStats = json_decode($jsonRaw, true);
    if (!empty($workersStats)) {
        foreach ($workersStats['data'] as $io => $each) {
                $reportedHashrate+=$each['reportedHashrate'];
        }
    }
}
 
print(round($reportedHashrate/1000000,2));

PPS на интерфейсе

stat_pps
#!/bin/sh
/usr/bin/netstat -w 1 -I bridge0  -q 1 | /usr/bin/tail -n 1 | /usr/bin/awk {'print $1'}

Утилизация CPU Linux хоста

lin_cpustats
#!/usr/local/bin/php
<?php
 
/**
CPU Statistics
 
Load
1 minute Load: .1.3.6.1.4.1.2021.10.1.3.1
5 minute Load: .1.3.6.1.4.1.2021.10.1.3.2
15 minute Load: .1.3.6.1.4.1.2021.10.1.3.3
 
CPU
percentage of user CPU time: .1.3.6.1.4.1.2021.11.9.0
raw user cpu time: .1.3.6.1.4.1.2021.11.50.0
percentages of system CPU time: .1.3.6.1.4.1.2021.11.10.0
raw system cpu time: .1.3.6.1.4.1.2021.11.52.0
percentages of idle CPU time: .1.3.6.1.4.1.2021.11.11.0
raw idle cpu time: .1.3.6.1.4.1.2021.11.53.0
raw nice cpu time: .1.3.6.1.4.1.2021.11.51.0
 
*/
$oid='.1.3.6.1.4.1.2021.10.1.3.3';
$ip='192.168.0.70';
$community='yourcommunity';
//end of config
 
$cmd='/usr/local/bin/snmpwalk -v2c -On  -c '.$community.' '.$ip.' '.$oid;
$raw=shell_exec($cmd);
$newTime=time();
if (!empty($raw)) {
$raw=explode('STRING:',$raw);
$raw=trim($raw[1]);
if (!empty($raw)) {
	print($raw);
} else {
	print('FAIL');
}
 
}

Использование One-Punch скриптов

Еще более предпочтительным, является использование One-Punch скриптов вместо просто скриптов лежащих где-то на вашей ФС. Используя их, вы получаете сразу два главных преимущества:

  1. Они храняться в вашей БД и переезжают всегда вместе с ней
  2. Они выполняются внутри Ubilling и имеют прямой доступ ко всему его функционалу

Как это работает?

Допустим берем и создаем One-Punch скрипт следующего вида:

$watchdogCallbackResult='sometest data';

Вот как-то так

И допустим мы хотим контролировать не изменятся ли данные возвращаемые этим скриптом (с чего-бы это? ;)

Надеюсь очевидно, что собака-наблюдака будет воспринимать как результат выполнения только данные находящиеся в переменной $watchdogCallbackResult?
Также есть еще одно небольшое ограничение относящееся не только к данным возвращаемым One-Punch скриптами, а также и к таким типам проверок как script и httpget. Ограничение заключается в том, что операторы changed и notchanged не работают адекватно если объем возвращаемых этими проверками данных составляет более 255 байт. Поэтому если вы собираетесь использовать эти операторы для контроля измений в данных возвращаемых вышеуказанными типами проверок, вам следует это учитывать при разработке ваших скриптов. Для проверок типа like или скажем notempty это не важно.

Трафик с порта

Вот например то же самое снятие данных о трафике с порта, но в виде One-Punch скрипта:

//config section
$port='8';
$oid='.1.3.6.1.2.1.31.1.1.1.6';
$ip='192.168.18.234';
$community='changeme';
//end of config
 
$cmd='/usr/local/bin/snmpwalk -v2c -On  -c '.$community.' '.$ip.' '.$oid.'.'.$port;
$raw=shell_exec($cmd);
$newTime=time();
if (!empty($raw)) {
$raw=explode('Counter64:',$raw);
$raw=trim($raw[1]);
if (!empty($raw)) {
	$cacheName='content/documents/myscripts/octets_'.$ip.'_'.$port;
	if (file_exists($cacheName)) {
		$oldTime=filemtime($cacheName);
		$oldOctets=file_get_contents($cacheName);
		$traffDiff=$raw-$oldOctets;
		$timeDiff=$newTime-$oldTime;
		if ($timeDiff!=0) {
		$speed=($traffDiff*8*100)/($timeDiff*10000);
		$watchdogCallbackResult=round($speed/10000);
		} else {
		$watchdogCallbackResult='-1';
		}
	    file_put_contents($cacheName,$raw);
	} else {
		file_put_contents($cacheName,$raw);
		$watchdogCallbackResult='0';
	}
}
 
}

Свободное место на диске

$result=disk_free_space("/");
$result=$result/1024/1024/1024;
$watchdogCallbackResult=round($result,2);

Load Average удаленного Linux хоста

$oid = '.1.3.6.1.4.1.2021.10.1.3.3';
$ip = '192.168.0.70';
$community = 'yoursnmpcommunity';
 
 
$cmd = '/usr/local/bin/snmpwalk -v2c -On  -c ' . $community . ' ' . $ip . ' ' . $oid;
$raw = shell_exec($cmd);
$watchdogCallbackResult = '';
if (!empty($raw)) {
    $raw = explode('STRING:', $raw);
    $raw = trim($raw[1]);
    if (!empty($raw)) {
        $watchdogCallbackResult .= $raw;
    }
}

Мониторинг isc-dhcpd

$command='ps aux | grep dhcpd | grep -v grep';
$result=shell_exec($command);
if (!empty($result)) {
  $watchdogCallbackResult='запущено';
} else {
  $watchdogCallbackResult='впав нахрін';
}

Мониторинг TrassirServer

        $watchdogCallbackResult = '';
        $dvrs = new NyanORM('visor_dvrs');
        $dvrs->where('type', '=', 'trassir');
        $allDvrs = $dvrs->getAll();
 
        if (!empty($allDvrs)) {
            foreach ($allDvrs as $io => $each) {
                $dvrName = $each['name'];
                $trassir = new TrassirServer($each['ip'], $each['login'], $each['password'], $each['apikey'], $each['port']);
                $health = $trassir->getHealth();
                if (!$health['disks'] OR ! $health['database'] OR ! $health['network']) {
                    $watchdogCallbackResult .= $dvrName . ' - FAILS ';
                }
            }
        }

Мониторинг температуры на Equicom PING3

    $ip = '192.168.0.89';
    $community = 'yourcommunity';
    $oid = '1.3.6.1.4.1.35160.1.16.1.13.1';
    $correction = 0;
 
 
    $snmp = new SNMPHelper();
    $resultRaw = $snmp->walk($ip, $community, $oid, false);
    $watchdogCallbackResult = 0;
    if (!empty($resultRaw)) {
        $watchdogCallbackResult = zb_SanitizeSNMPValue($resultRaw) / 10;
        $watchdogCallbackResult = $watchdogCallbackResult + $correction;
    }

Мониторинг наличия питания на Equicom PING3

    $ip = '192.168.0.89';
    $community = 'yourcommunity';
    $oid = '1.3.6.1.4.1.35160.1.26.0';
    $result = 0;
 
 
    $snmp = new SNMPHelper();
    $resultRaw = $snmp->walk($ip, $community, $oid, false);
    $watchdogCallbackResult = 0;
    if (!empty($resultRaw)) {
        $watchdogCallbackResult = zb_SanitizeSNMPValue($resultRaw);
        $watchdogCallbackResult = ($watchdogCallbackResult == 1) ? 'OK' : 'FAILED!';
    }

Мониторинг температур на OLT-ах

    $criticalTemp = 70;
 
    $watchdogCallbackResult = '';
    $tempPath = OLTAttractor::TEMPERATURE_PATH;
    $tempExt = OLTAttractor::TEMPERATURE_EXT;
 
    $switchesDb = new nya_switches();
    $switchesDb->where('desc', 'LIKE', '%OLT%');
    $allOlt = $switchesDb->getAll();
 
    foreach ($allOlt as $io => $eachOltData) {
        $tempData = $tempPath . $eachOltData['id'] .'_'. $tempExt;
        if (file_exists($tempData)) {
            $oltTemp = file_get_contents($tempData);
            if ($oltTemp >= $criticalTemp) {
                $watchdogCallbackResult .= $eachOltData['location'] . ' - ' . $oltTemp . ' °C ';
            }
        }
    }
 
    if (empty($watchdogCallbackResult)) {
        $watchdogCallbackResult='Всі температури OLT в порядку';
    }
watchdog.1664019551.txt.gz · Востаннє змінено: 2022/09/24 14:39 повз 127.0.0.1