У нашей команды тестирования в тестовом стенде есть множество устройств на разных аппаратных платформах, работающих под управлением ОС “Нейтрино”. В том числе есть и редкие девайсы, которые бывают нужны не только тестировщикам, но и разработчикам. Последние регулярно обращались с просьбой одолжить ту или иную плату «на часок-другой» и ничего на ней временно не запускать.
Однако, и разработчиков много и тестировщиков тоже много, и за всеми такими просьбами уследить не удаётся: кто-то на удалёнке, кто-то неверно услышал чужие слова, кто-то забыл предупредить коллег в чате — и пошли коллизии. Да и просто неудобно, когда нужно перед началом работы с устройством листать переписку за весь день или бежать к руководителю. С ростом команд, разумеется, проблема масштабировалась.

Также мы подметили, что разработчики приходят с однотипными вопросами: «а какой там IP?», «как выглядит плата?», «подключена ли она к диагностике?» — хотя всё это было и есть во внутренней базе знаний. Отвечать надоедало.
В общем, у нас возникла необходимость в сервисе, который бы:
Показывал актуальную занятость тестовых устройств на момент обращения;
Давал разработчикам краткие сведения об устройствах;
Имел способ аутентификации во внутреннем контуре компании (LDAP);
Позволял синхронизировать и хранить запросы пользователей в базе данных;
Оповещал тестировщиков о состоянии стенда по удобному каналу связи;
Умел интегрироваться с ОС “Нейтрино”.
И делал бы это всё наглядно и просто.
Реализация
К моменту появления этой идеи мы в компании уже вовсю пользовались Grafana для мониторинга продуктовых метрик, а также runtime-метрик самих тестовых устройств. Так что в нашем отделе уже было достаточно много компетенций в вопросах настройки дэшбордов и виджетов Grafana, а также их связи с БД (мы используем PostgreSQL).
Дополнительно, у Grafana уже был настроен наш LDAP, так что вопрос аутентификации сотрудников в сервисе отпадал сам собой. Каждый сотрудник, заходя в дэшборд, видит его актуальное состояние на момент обращения - то, что нам и нужно! Потому было принято решение не изобретать собственный велосипед, а попробовать затюнить имеющееся решение под задачу.
Теперь перейдём к рассказу о том, как мы реализовывали остальные требования.
Отмечу, что мы используем Grafana 10.0.3 от июля 2023 года.
Интерфейс
Продемонстрирую, как выглядит наш дэшборд на примере одного устройства.
Начнём с левой части - это выбор устройства и его фотография. Наглядно отвечает на вопрос "А как устройство выглядит?" и даёт представление о том, какие девайсы нужной архитектуры у нас в наличии. Выбирая архитектуру в верхней строке, автоматически обновляется нижняя строка с перечнем устройств. Данные подтягиваются из статической БД в PostgreSQL, которую мы вручную правим при появлении новых девайсов в тестовом стенде.

В центре у нас основная информационная часть. Сразу бросается в глаза крупная цветная надпись "Устройство свободно" - текущий статус устройства. Над этой надписью расположились управляющие элементы - две кнопки ("Занять устройство" и "Освободить устройство") и поле для ввода произвольного информационного сообщения для руководителя команды тестирования.
Пару слов про кнопки. На удивление, в Grafana они недоступны "из коробки", поэтому нам пришлось установить в контейнер соответствующее расширение. Эти кнопки позволяют выполнять SQL-запросы на запись к БД. Запись производится от лица псевдопользователя, специально заведённого под задачи сервиса.
grafana-cli plugins install speakyourcode-button-panel sudo systemctl restart grafana-server
У этого расширения есть недостаток - кнопки никак не изменить по размеру. Потому они у нас несколько теряются в пустом пространстве.
Для поля ввода сообщения используется небольшой трюк. Сам по себе виджет - HTML Graphics. То есть это отрендеренная форма, в которую можно ввести текст. Вводимый текст автоматически экспортируется в глобальную переменную дэшборда, благодаря чему его могут считывать кнопки и передавать вместе с запросом в БД.

Внизу по центру дэшборда находится блок информации о текущем либо последнем пользователе устройства, с временным интервалом использования и сообщением (если его оставили). Для удобства реализована интеграция с API Битрикс24: достаём Битриксовый ID пользователя устройства и открываем новую вкладку Битрикса с диалогом.
Для настройки такого взаимодействия с Битриксом потребовалась установка расширения Infinity.
grafana-cli plugins install yesoreyeram-infinity-datasource sudo systemctl restart grafana-server
Этот виджет - тот же HTML Graphics, который внутри себя исполняет комбинированный (Mixed) запрос, внутри которого есть POST-запрос к Битриксу от псевдопользователя, у которого настроен хук на получение информации о пользователях.
# Поле URL: POST-запрос к API Битрикс24 https://**bitrix_address**/rest/**bitrix_uid**/***bitrix_webhook**/user.get # Вкладка URL Options - фильтрация по переменной дэшборда с почтой пользователя. {"filter":{"EMAIL":"${G_LAST_USER}%"}}
В этом же виджете есть и часто запрашиваемые сведения: IP-адреса самого устройства, блока PDU, камеры видеонаблюдения, и сведения для диагностики. Данные берутся из обычной БД.
Таким образом, алгоритм для разработчика при работе с устройством таков:
Выбрать архитектуру.
Выбрать интересующее устройство.
(опционально) Ввести сообщение для команды тестирования.
Занять устройство по кнопке. С этого момента никто другой им воспользоваться не может.
По окончании работ освободить устройство по соседней кнопке. Освободить устройство может только тот пользователь, который его занял. Ну и администратор, конечно же.

Как только устройство меняет свой статус занятости, его можно сразу пронаблюдать в правой части дэшборда, обновив страницу. В этой таблице видно текущую занятость всех устройств и можно, например, сразу занять еще одно устройство, перейдя по ссылке.
Регулировать, какие устройства доступны для определённых пользователей можно через штатный механизм команд (Teams).

Когда пользователь нажимает одну из двух кнопок, происходит отправка SQL-запроса к БД PostgreSQL, где добавляется новая строка. Все виджеты дэшборда тут же подхватывают обновлённое состояние устройств и показывают его пользователям.
Устройства могут быть заняты не только людьми, но и автоматизированным запуском сценариев тестирования через CI/CD (Jenkins). У нас тесты запускаются ежедневно в ночное время, в разных объёмах, и на это время крайне нежелательно стороннее вмешательство. Ежедневное тестирование, это, в первую очередь, контроль регрессий продукта, поэтому запуски задач Jenkins имеют приоритет над обычными пользователями. Если кто-то занял устройство, задействованное в ежедневном тестировании и не вернул его до того, как ушёл с работы, то ночью оно будет принудительно отобрано и перезагружено. Задачи Jenkins отправляют SQL-запрос к БД напрямую, минуя Grafana, чтобы не перегружать логику в скриптах виджетов Grafana, и гарантированно иметь приоритет над обычными пользователями. Руководитель команды тестирования имеет возможность вручную регулировать доступ к устройствам через прямой доступ к БД, а также через TUI-утилиту.

Подводя итог вышесказанному, схема работы сервиса выглядит так:

Это то, как сервис фактически работает сейчас и действительно всем помогает. Проанализировав БД состояний стендов, мы обнаружили, что, без учёта Jenkins и сотрудников нашего отдела, в компании сервисом воспользовались более 20 сотрудников более 800 раз за несколько месяцев. Огромное количество времени и энергии нашего отдела было сохранено!
А что дальше?
Однако, видно, что в текущей схеме не хватает самого главного: самих тестовых устройств! То есть весь этот сервис имеет смысл только до тех пор, пока им пользуются. Если кто-то из сотрудников, зная адрес и пароль от устройства, тайком подключится к нему напрямую по SSH и выполнит rm -rf в файлах своего коллеги, то никто об этом и не узнает. Поэтому схема должна выглядеть так (см. внизу справа):

Во-первых, на схеме появляются главные герои - сами устройства тестового стенда, во-вторых, доступ к ним выдаётся только на выделенную сессию посредством выдачи одноразового пароля.
То, что будет описано в статье далее - это наши попытки привести схему к такому виду и попутные размышления на тему того, как это реализовать правильно. Поэтому читателю стоит иметь в виду, что во внутренний продакшен полноценно это еще не запущено, хотя отдельные фрагменты уже внедрены. Всё-таки есть риски парализовать всю работу с устройствами.
Генерация сессионных паролей
При каждой транзакции в БД (создании новой строки) генерируется одноразовый пароль на время использования устройства. Это реализовано через триггеры PostgreSQL. Одноразовый пароль должен соответствовать политике пользовательских паролей ОС “Нейтрино”.
CREATE OR REPLACE FUNCTION generate_temp_password() RETURNS text AS $$ DECLARE password text := ''; chars text := 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!"#$()*+,-./'; BEGIN FOR i IN 1..12 LOOP password := password || SUBSTRING(chars, ceil(random() * LENGTH(chars))::integer, 1); END LOOP; RETURN password; END; $$ LANGUAGE plpgsql;
Создание триггерной функции для заполнения поля
CREATE OR REPLACE FUNCTION set_device_password() RETURNS TRIGGER AS $$ BEGIN NEW.temp_password := generate_temp_password(); RETURN NEW; END; $$ LANGUAGE plpgsql;
Привязка триггера к таблице
CREATE TRIGGER trg_set_device_password BEFORE INSERT ON devices FOR EACH ROW EXECUTE FUNCTION set_device_password();
Передача пароля на устройство
На целевом устройстве под управлением ОС “Нейтрино” должен работать прослушиватель БД. Реализован он может быть, например, с помощью библиотеки libpq, уже имеющейся в составе дистрибутива. При появлении новой записи прослушиватель должен отфильтровывать, относится ли запись к устройству, на котором он работает.
Затем, прослушиватель должен штатным образом изменить текущий пароль в системе на выданный одноразовый. Важный нюанс с точки зрения безопасности это то, как прослушивателем будет получен пароль - необходим анализ на предмет возможности обфускации или шифрования, чтобы не передавать его в открытом виде. Отдельно стоит отметить, что для псевдопользователя Jenkins должен всегда генерироваться один и тот же пароль, чтобы каждый раз не переписывать Credentials.
Передача пароля пользователю
Параллельно с передачей пароля на устройство необходимо передать сгенерированный пароль пользователю, забронировавшему себе стенд. Для этого, на первый взгляд, отлично подходит штатная поддержка Alerting в самой Grafana.

Для того, чтобы настроить SMTP, необходимо в конфигурационном файле etc/grafana/grafana.ini указать:
[smtp] enabled = true host = your.corporate.mail:777 user = grafana password = ********* from_address = grafana@corporate.ru from_name = Grafana
После настройки алертов, когда залогиненный пользователь нажимает на кнопку дэшборда, ему отправляется письмо на почту, содержащее одноразовый пароль. Вроде всё красиво, но на практике не всё так гладко: оказалось, штатные оповещения работают непредсказуемо. После первичной настройки уведомления исправно приходят на почту. Затем, спустя пару недель, мы заметили, что часть уведомлений пропадает, и поначалу списали это на проблемы почтового сервера. Но затем стали появляться запоздалые уведомления: в конце каждого письма стала добавляться строка, сообщающая о том, что событие произошло раньше, чем было доставлено письмо...

Сначала секунды, затем минуты, затем часы, и мы дошли до задержки уведомлений в 80 часов, и затем уведомления мы отключили, и на этом реализацию передачи паролей временно заморозили. Поиск решений в Интернете, запросы к LLM, правки конфигураций к решению так и не привели. Нужен будет еще один заход. Возможно, эта проблема решится банальным обновлением Grafana до более свежей версии.
Вместо заключения
Надеюсь, что вам было интересно почитать о таком нестандартном использовании Grafana!
Если вы пытались реализовать что-то подобное, буду рад, если поделитесь в комментариях своим опытом или предложениями по улучшению сабжа статьи!
Подписывайтесь на наш канал, чтобы быть в курсе свежих новостей
