Как писал систему оповещения для 500 Linux-машин.

Меня зовут Павел, я сисадмин на промышленном предприятии. Под управлением — около 500 рабочих станций на ALT Linux. Это статья о том, как я написал маленький инструмент, который сэкономил мне нервы, и почему это вообще понадобилось.

Спойлер: потому что на Linux нет нормального способа крикнуть всем сразу «ребята, сохраните работу, я сейчас всё перезагружу».

Контекст

ALT Linux Workstation в смешанной среде — часть парка Windows, часть Linux, все в одном домене AD. Управление через Ansible.

Примерно через полгода после перехода я понял, что у меня нет ни одного нормального способа сказать людям что-то срочное. Ну, типа «через десять минут буду ребутать сервер с шарами » или «буду накатывать CVE где требуется обновление ядра».

На Windows это решается одной командой. msg * "Внимание" — и у всех залогиненных пользователей выскакивает окошко. Примитивно, но работает.

На Linux такого нет. Вообще.

Есть wall — пишет текст в терминал. Но у наших бухгалтеров терминал не открыт понятное дело. Есть notify-send — но он локальный, работает только на той машине, где запущен, и только если правильно настроен D-Bus. Есть корпоративный Telegram — но туда люди заходят когда хотят, а не когда надо мне, да и не работает должным образом, сами знаете почему.

В итоге я ходил по кабинетам. Буквально. Четыре корпуса, третий этаж, пятый этаж, потом снова первый. Пока дойдёшь до всех — уже что-нибудь да случится.

Как я искал готовое решение и не нашёл

Первым делом погуглил. Нашёл:

— Несколько вопросов на ServerFault 2013 года с ответами «используйте wall» — Один заброшенный репозиторий на Python 2, последний коммит в 2016-м — Коммерческое решение с кнопкой «оставить заявку» и ценником «по запросу» (это всегда означает дорого) — Совет использовать Zabbix для оповещений (серьёзно, Zabbix?)

Ничего из этого не подходило. Либо устарело, либо не работает на наших ОС, либо избыточно сложно для задачи «покажи людям текст на экране».

Тогда я сел и написал сам. Думал — на выходные. Вышло немного дольше.

Что получилось

Называется Notify. Принцип простой: на каждой рабочей станции крутится маленький Python-агент. Он запускается вместе с X11-сеансом пользователя и тихо ждёт. Когда мне надо что-то отправить — я запускаю Ansible-плейбук, он обходит все машины и стучится к каждому агенту. Агент показывает окно.

Два режима отображения — popup и fullscreen.

Popup — небольшое окно, появляется в углу экрана и само закрывается через несколько секунд. Три цвета в зависимости от важности: INFO, WARNING, CRITICAL. Подходит для плановых вещей — напомнить про обновление системы, предупредить о перезагрузке сервера такого же 1с и остальные рутинные задачи.

Fullscreen — окно на весь экран, поверх всего. Не закроется пока пользователь не нажмёт кнопку. Это уже не «напоминалка» — это требование подтвердить что человек прочитал. Мы используем такой режим для тренировок по ГО и ЧС: сообщение должно дойти до каждого, и каждый должен его осознанно закрыть.

Интерфейс написан на PyQt5. Это принципиальное решение — я изначально пробовал zenity и yad, но они выглядят по-разному на разных рабочих столах, иногда вообще не установлены, и вся эта история с D-Bus и окружением превращается в ад. Qt рисует окно сам, нативно, в сеансе пользователя. Просто работает.

Где я провозился дольше всего

Вот здесь будет честная часть, потому что «написал за выходные» — это неправда.

Иконка в трее не появлялась на MATE. ALT Linux по умолчанию ставит MATE. Qt пытался подхватить системную тему, которой не существовало, и молча не показывал иконку. Решение — добавить QT_QPA_PLATFORMTHEME="" в /etc/profile.d/. Нигде это не задокументировано нормально, нашёл случайно.

Qt падал при попытке показать окно из потока. Первая версия создавала объект Notifier до того, как стартовал QApplication. Qt не кидал exception — просто зависал. Несколько часов отладки на ровном месте.

Fullscreen-режим блокировал весь агент. QDialog.exec_() создаёт свой event loop. Пока пользователь не закрыл полноэкранное окно — агент не принимал новых сообщений. Переписал на show() с асинхронной очередью.

Машины засыпали ночью, агент умирал. Вечером рабочая станция уходила в suspend, утром пользователь выходил из него — агент уже не отвечал. Два фикса: systemctl mask sleep.target (машина перестаёт засыпать) и wrapper-скрипт с while true вокруг запуска агента (если упал — сам перезапустится).

Автозапуск в доменной среде. ~/.config/autostart/ — это личная папка пользователя, у каждого своя. Мне нужен был глобальный автозапуск для всех. Решение — /etc/xdg/autostart/ плюс wrapper-скрипт, который ждёт появления X11 через xprop -root, прежде чем запустить агент. Этот паттерн я подсмотрел у Яндекс Браузера — он делает то же самое при установке на ALT Linux.

Каждая из этих проблем сама по себе небольшая. Вместе они превратили «выходной проект» в несколько месяцев итераций.

Как это выглядит в работе

Мне надо предупредить всех о плановом обслуживании. Запускаю:

Запускаю плейбук Ansible — и через несколько секунд у всех на экране появляется попап сообщение. "Плановые работы сегодня в 17:00. Завершите задачи."

Развернуть агент на новые машины тоже через Ansible — один плейбук устанавливает зависимости, кладёт .desktop файл в /etc/xdg/autostart/ и прописывает переменные окружения. После следующего входа пользователя агент уже работает.

Зачем я это выложил

Честно — хотел посмотреть, столкнётся ли кто-то ещё с той же проблемой.

Переход на Linux в российских компаниях идёт массово. Сисадмины, которые раньше жили в уютном мире Windows-инструментов, теперь обнаруживают, что на Linux много базовых вещей либо не существует, либо существует в виде «ну напишите скрипт». Задача оповещения сотрудников — одна из таких.

Я написал решение для своей инфраструктуры. Оно работает в продакшне на ALT Linux, проверено на Astra Linux и Red OS. Может, кому-то пригодится.

Стек достаточно простой для продакшена и дабы не было проблем с заносом в периметр, специально обходился стандартными средствами, версия от python3 и выше и qt5

Исходники на GitHub: github.com/PapaBorscht/Notify

Там же — примеры плейбуков Ansible, пример Bash скрипта с вызовом окна, Puppet-манифест и инструкция по установке. Если у вас другой дистрибутив и что-то не работает — пишите в issues, разберёмся.

Работает пока на X11, есть маленькие проблемки и нюансики с wayland. Кстати, то же работает, только попап окно центрируется само.

Всем добра, надеюсь кому нибудь будет полезно и поможет в не легком нашем деле админов)

Popup уведомление
Popup уведомление
Fullscreen окно
Fullscreen окно
1