Мониторим ONU/ONT Huawei в телеграмм боте
Предисловие
Всем привет. Я работаю сетевым инженером в интернет провайдере. Как и многие сетевики, в последнее время, я увлёкся автоматизацией рутины. Первым кандидатом на автоматизацию стал, сбор информации с абонентских оптических терминалов (далее ОНУ).
ОНУ это — Optical Network Unit (ONU)/Optical Network Terminal (ONT)
Это такая штука, которая стоит у многих дома перед роутером :-)
Ну, а подключаются они все в OLT (OLT — терминал оптической линии)
Первая реализация скрипта была «для себя», и консольной, и при запросе от операторов тех поддержки, я мог быстро узнать нужную информацию по SNMP, запустив скрипт. Далее, что бы исключить себя, из цепочки сбора данных, начал думать, как реализовать интерфейс между скриптом и операторами. Самый простой вариант оказался — телеграмм бот. По сути, бот здесь это приём 4-х команд от оператора, далее всю работу делает Python скрипт, и выводит результат.
Почему не подходит стандартный мониторинг? Потому, что мониторинг по сути и не нужен. Данные с ОНУ нужны в реальном времени, в момент возникновения проблемы. Я слабо представляю себе мониторинг 30 000 ОНУ в Заббиксе, со всеми уровнями сигналов, аптаймами, состоянием портов. А так же читаемость таких данных и поиск. Ещё возникает проблема того, что ОНУ могут переезжать с спорта на порт, или с ОЛТа на ОЛТ. Да и задача в том, что когда звонит абонент, оператор ещё не дослушав жалобы, уже видит состояние его оборудования, а что было с его оборудованием месяц назад или вчера, или через 5 минут когда Заббикс сделает запрос, совсем не важно.
Я не буду углубляться в программирование, кода получилось много для одного поста, а по сложности, скрипт довольно таки простой. Опишу здесь саму концепцию, а кому интересен код, оставлю ссылку на Github.
Бот довольно таки узкоспециализированный, но может кому будет интересно, или у кого то есть решения по проще. Мне самому больше интересно, посмотреть комментарии, кто и как решил такую же задачу. Вряд ли я единственный, кому лень ходить на ОЛТы по SSH, и смотреть, что происходит с абонентским терминалом, каждый раз когда звонит абонент:)
Функционал
И так, стандартная ситуация, звонит абон, оператор первым делом выясняет какие лампочки горят на оборудовании, и благодаря боту уже сам знает какие:)
Вбиваем мак адрес, если Epon, либо серийный номер если Gpon, в телегу. И бот выдаёт текущее состояние ОНУ.
Здесь мы видим, на каком ОЛТе расположен абонент, порт, а так же конкретно в примере, видим, что у абона LAN порт находится в состоянии DOWN. Уже что-то, есть от чего отталкиваться при разговоре. Так же видим когда терминал был включен, когда выключался, почему, ну и уровни сигналов.
Или такой вариант:
Сразу видно в какую сторону копать.
Бывает, что нужен уровень всего pon дерева. Для этого, мы перед маком/серийником вставляем команду /tree.
И видим уровень всех ОНУ на порту.
Если же, операторы видят, что прилетел триггер с Заббикса, что весь порт не в сети. То причину, тоже можно посмотреть в боте (Правда нужно заранее, в биллинге посмотреть мак или серийник, который может быть на этом порту. Обычно это не проблема).
Скорей всего здесь нет света.
Так же видно, сколько свободного места на порту.
Что под капотом?
Как бот узнаёт все эти данные? Тут всё просто — SNMP. Алгоритм скрипта простой как угол дома:
Есть IP ОЛТа, есть OID, есть snmpwalk, получаем данные, парсим, и красиво выводим :-)
Да, скрипт запускает в линуксовой консоли команду snmpwalk, перехватывает вывод, и парсит регуляркой, либо сплитом, и по последнему значению.
if "gpon" in self.pon_type:
ponstateoid = "1.3.6.1.4.1.2011.6.128.1.1.2.46.1.15"
cmd = f"snmpwalk -c {self.snmp_com} -v2c {self.olt_ip} {ponstateoid}.{self.portoid}.{self.onuid}"
cmd_to_subprocess = cmd.split()
process = subprocess.Popen(cmd_to_subprocess, stdout=subprocess.PIPE)
data = process.communicate(timeout=5)
data2 = data[-2].decode('utf-8')
onu_state = data2.split()
onu_state_out = onu_state[-1]
От нас требуется список всех OID с нужными для нас значениями. И вызовом их, когда нам нужно.
И тут возникает вопрос, откуда их брать.
Для этого у ботаесть своя база, это файлик sqlite. В нём хранятся все мак адреса и серийники ОНУ, а так же OIDы портов и IP адреса, крч вся нужная информация для формирования запроса snmpwalk, а так же вывода нужной информации оператору.
Изначально список ОЛТов был — список:-) Я руками в коде добавлял ip адреса, но это не удобно и не масштабируемо. Пару месяцев до этого, я начал внедрять NetBox, и он как раз подходит, для хранения всей нужной информации.
В боте есть команда /oltsupdate. При её выполнении, скрипт идёт в NetBox, тащит через API список ОЛТов (узнаёт о том какие девайсы нужны, по тегу).
По тегу бот так же узнаёт какая это технология, Gpon или Epon. Это важно, т.к. у них разные OIDы. Далее по списку он опрашивает ОЛТы, получая с них список зарегистрированных ОНУ. В него входят, OID порта на котором находится ОНУ, а так же мак или серийник. Всё это записывается в базу. При каждом опросе база «обнуляется», и данные в неё записываются заново. Никакой истории не хранится. Вся информация свежая.
Вот так это выглядит. Список найденных в НетБоксе ОЛТов, а так же после опроса скрипт проверяет базу, на наличие одинаковых маков и серийников. Базу нужно периодически обновлять. Как часто — зависит от количества новых подключений. Если новые ОНУ в сети не появляются, то и базу обновлять не нужно. Обычно её обновляют раз в несколько дней, и этого хватает.
Далее уже обычными SQL запросами мы ищем в базе нужную нам ОНУ, берём все нужные данные, формируем команду snmpwalk, и результат выдаём оператору.
Заключение
Код скрипта получился довольно таки большой по объёму, и в рамках поста его не перескажешь. Поэтому код я здесь не привожу. Если кого заинтересовал код, вот ссылка на Гитхаб. Там я постарался дать понятные комментарии.
Область применения бота довольно таки узкая, готовых решений для такой задачи я не нашёл, поэтому запилил своё. Но вряд ли мой провайдер единственный кто использует pon в сети:) Поэтому интересно мнение других сетевиков, кто и как решает задачу — «пореже заходить в консоль» и что бы операторы лишний раз не дёргали.