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

Тестирование CLI-приложений без костылей: единый фреймворк вместо десятка утилит
с помощью нейросети
Готовые утилиты в области систем хранения данных зачастую не обеспечивают полного покрытия тестовых сценариев или ориентированы только на специфические задачи. Проверить массив из десятков или сотен дисков, учесть разные конфигурации железа и операционных систем, автоматизировать все до одного клика — такие задачи стандартные инструменты просто не решают. Перед инженерами встает выбор: продолжать вручную собирать «конструкторы» из разрозненных утилит или создать собственный инструмент. Так у нас появился кастомный фреймворк для тестирования CLI-приложений, который позволяет проводить различные виды тестирования, обеспечивает работу с оборудованием и предоставляет понятную отчетность в одном решении.
Артём Хюппенен, инженер по тестированию в YADRO, поделился техническими деталями фреймворка и примерами из практики: как выбирали технологии, что оказалось удачным и как теперь любой член команды может быстро автоматизировать тесты для сложных CLI-приложений. В конце статьи — ссылка на репозиторий, чтобы посмотреть архитектуру на практике.
Повторим основы
В основе любой СХД лежат накопители: жесткие диски, CD или SSD. Для того чтобы обеспечить скорость и надежность, их объединяют в RAID-массивы — логические устройства, построенные на основе технологии RAID. Если коротко, RAID — это технология, позволяющая объединить физические диски в одно логическое устройство и организовать хранение данных на нем так, чтобы достичь определенного баланса между скоростью, надежностью и эффективностью использования места. Что такое T-RAID и как он устроен в линейке СХД TATLIN мы уже писали.
Существует множество уровней RAID, но для примера мы возьмем такие:
- — RAID 0 — объединяет диски в один большой. Это дает высокую скорость, потому что данные пишутся и читаются параллельно, но надежности тут нет совсем: сломается один диск — потеряются все данные.
- — RAID 1 — дублирует данные на два диска. Если один выйдет из строя, второй продолжит работу, но при этом половина дискового пространства уходит под копию.
- — RAID 5 и выше — более сложные схемы, где диски обмениваются информацией о соседях. Если один диск ломается, данные можно восстановить. Скорость при этом остается высокой, потому что запись идет на несколько накопителей одновременно.
RAID — основа нашей работы. Тестируя СХД, мы проверяем не только сохранность данных, но и реакцию системы на сбои: если один диск выходит из строя — данные остаются целыми и доступны для чтения. Если падает производительность — нужно искать причину.
Главная проблема: большинство доступных инструментов для тестирования ориентированы на узкие задачи и не подходят для проведения комплексных нагрузочных сценариев — с моделированием отказов, нестандартных ситуаций и других стресс-кейсов. Именно это стало отправной точкой для идеи создать собственный фреймворк.
Конструктор из утилит: рабочий, но неудобный путь
Когда мы только начали думать о том, как тестировать наши СХД, первое, что пришло в голову, — взять готовые утилиты с рынка. Их немало, и многие из них знакомы даже людям, которые далеки от тестирования.
CrystalDiskMark — простая утилита с понятным интерфейсом: запустил, нажал кнопку и сразу видишь красивые цифры скорости чтения и записи. Удобно и наглядно, но хорошо работает для одного-двух дисков на домашнем ПК. Когда же речь идет о массиве из десятков накопителей и требуется автоматизация тестов, возможностей CrystalDiskMark конечно же не хватает.
Когда речь идет о большой системе с десятками накопителей, где нужно измерить производительность целиком, возможностей CrystalDiskMark уже недостаточно.

Есть и более «серьезные» варианты — CLI-утилиты вроде FIO или VDBench. Они работают в терминале, поддерживают множество параметров и позволяют тонко настраивать нагрузочные тесты.

Я уверен, что многие инженеры, которые хоть раз тестировали диски в Linux, с FIO сталкивались. Мы его тоже используем, и он прекрасно подходит для генерации нагрузки.
Еще один важный инструмент в нашем стеке — pgbench. Эта утилита создает нагрузку на PostgreSQL-базы данных, развернутые на тестируемых СХД, что позволяет оценить влияние дисковой подсистемы на работу реальных приложений. Ее сильные стороны — простота моделирования типичных сценариев — например, банковских транзакций и гибкость в настройке интенсивности запросов. Благодаря этому pgbench помогает выявить «узкие места» при интеграции СХД с enterprise-приложениями.

Однако у метода есть ограничение: тестирование ведется на уровне СУБД, а не на уровне блоков, поэтому результаты зависят не только от скорости дисков, но и от конфигурации самой базы данных. Для анализа «голой» производительности накопителей без накладных расходов СУБД этот инструмент не подходит.
Но у всех этих инструментов есть одно общее ограничение: они не решают всю задачу целиком. В итоге мы вручную комбинировали несколько утилит, писали к ним обвязку, подгоняли под конкретный случай — и каждый раз заново собирали «конструктор» из команд. Это работало, но занимало массу времени и сил.
Почему готовых инструментов было недостаточно
Решение создать свой инструмент возникло из потребности. У нас специфические сценарии тестирования, которые не покрывал ни один готовый инструмент. Например, у разных заказчиков — разные конфигурации СХД: разное «железо», разные Linux дистрибутивы и версии ядер. Иногда система даже ведет себя по-разному в зависимости от типа дисков или конкретных драйверов. В таких условиях нужно, чтобы инструмент тестирования мог быстро подстраиваться под любую среду.
Вторая проблема — интеграция с оборудованием. CLI-утилиты вроде FIO и VDBench генерируют нагрузку на уровне блочных операций, но не предоставляют встроенных средств для мониторинга или управления СХД — например, проверки состояния дисков. Это вынуждает вручную «склеивать» тестовую нагрузку с внешними утилитами — запускать параллельные процессы, синхронизировать логи и интерпретировать данные из разных источников. Это не только неудобно, но и увеличивает риск ошибок: легко запутаться, где закончился один инструмент и начался другой.
Третья задача — отчетность. Для инженеров лог с кучей цифр — это нормально. Но для менеджера или заказчика нужен отчет с графиками, понятными выводами и итоговыми оценками. Готовые утилиты этим не занимались: максимум, что они делают, — выводят результаты в консоль или сохраняют их в файл.
Еще одна особенность — глубина логирования. Иногда баг прячется настолько глубоко, что нужно смотреть не только результат теста, но и весь путь его выполнения: команды, параметры, промежуточные ответы системы. Обычные инструменты дают либо слишком много «мусора» в логах, либо слишком мало деталей.
И финальный аккорд — автоматизация. Мы хотели, чтобы в идеале запуск всех регрессионных тестов требовал одного нажатия кнопки.
В итоге мы пришли к выводу: нам нужен единый инструмент, который:
- — Умеет взаимодействовать с CLI-приложениями и самим оборудованием.
- — Может управлять нагрузочными тестами и проверками целостности данных.
- — Поддерживает гибкую настройку под разные конфигурации.
- — Генерирует понятные и детализированные отчеты.
- — Легко интегрируется в наши процессы автоматизации.
Так и родилась идея кастомного фреймворка, который объединит все это и станет универсальной платформой для тестирования наших систем хранения данных.
3 PM: Python, Paramiko, PyTest и Mapper
Осознав, что готового решения нет, мы сосредоточились на выборе инструментов, которые подходят команде и позволяют быстро развивать проект. Мы выбрали Python и вот почему:
- — Низкий порог входа — даже новичку проще освоить базовые конструкции Python.
- — Популярность среди тестировщиков — язык уже стал «дефолтным» для автоматизации тестов, вокруг него большое сообщество, множество библиотек и плагинов.
Для удаленного управления тестируемыми системами мы использовали Paramiko — проверенную библиотеку для работы с SSH в Python. Поскольку большинство СХД управляются через терминал, этот инструмент оказался идеальным: он позволил реализовать удаленное выполнение команд и получать результаты прямо в рамках фреймворка.
Для организации и запуска тестов — PyTest. Он прост в использовании, при этом позволяет строить сложную архитектуру тестов: параметризацию, фикстуры, хуки. Огромный плюс в том, что для PyTest уже есть тысячи плагинов, которые можно интегрировать без изобретения велосипеда.
В основе фреймворка — mapper, абстракция над CLI-командами, которая предоставляет естественный Python-интерфейс для работы с CLI операциями СХД. Вместо ручного конструирования терминальных команд мы оперируем методами, структурно повторяющими синтаксис CLI, но с преимуществами языка программирования: типизацией, валидацией параметров и интеграцией в кодовую базу.
Например, вместо вызова в терминале:
С помощью mapper эта же операция в автотестах будет выглядеть так:
Ключевая идея: mapper не «упрощает» CLI-команды, а зеркалит их логику в виде читаемого и поддерживаемого кода. Это позволяет инженерам писать тесты на Python, сохраняя эмулирование привычного «терминального ввода команд».
Что еще должно быть во фреймворке
Есть еще несколько моментов, которые мы предусмотрели в фреймворке.
Система логирования с понятными и структурированными записями
Для тестировщика логи — это основной инструмент: чем они понятнее и детальнее, тем проще выявлять ошибки и разбираться в том, что происходит в системе. Мы выделили два главных принципа:
- — Понятность и структурированность. Логи должны быть написаны простым, читаемым языком. Важно, чтобы в них можно было быстро разобраться, а не сталкиваться с «полотном текста», как в Java-трейсах.
- — Достаточность без избыточности. В логах должна оставаться только полезная информация. Лишний «мусор» лучше фильтровать, чтобы не перегружать отчет и ускорить диагностику проблем.
Интерфейс, отвечающий за удаленное подключение
Мы работаем не с локальными машинами, а с полноценными системами хранения данных, которые размещены где-то в серверных. Чтобы взаимодействовать с ними, нужен инструмент, позволяющий подключаться к удаленной машине по ее IP-адресу. И с этой задачей помогает библиотека Paramiko, в которой уже реализованы все необходимые нам методы. Нам только нужно на ее основе реализовать свой класс-интерфейс для взаимодействия с удаленными нодами.
Основная задача интерфейса — уметь подключаться к удаленной машине по SSH и выполнять команды. Для этого достаточно задать базовые параметры: IP-адрес, логин, пароль и, при необходимости, порт. Главным элементом здесь является функция exec (сокращение от execute). Она подключается к удаленному серверу, выполняет переданную команду и возвращает результат: успешное выполнение, ошибки, а также дополнительную информацию, с которой можно работать дальше. Как выглядит реализация:
Фактически Paramiko уже реализует метод exec_command, поэтому писать свою реализацию с нуля не нужно. Все, что требуется, — передать команду в метод: он отправит ее на сервер, выполнит, вернет вывод и код завершения. В Linux код 0 означает успешное выполнение, 1 — ошибку (аналогично тому, как в API принято 200 и 500). Для целей логирования это особенно важно: можно зафиксировать саму команду, ее вывод, время выполнения и результат. Например:
Всего несколько строк кода позволяют покрыть до 80% задач логирования и взаимодействия с системой:
Посмотрим на результаты. В первой строке лога фиксируется IP-адрес машины и сама выполненная команда.
Во второй — результат ее выполнения и время работы. Далее выводится полный результат выполнения (output), аналогичный тому, что мы получили бы, запустив команду вручную в терминале. Например, при вводе ls -l в каталоге root мы увидим список файлов, тот же самый список появится и в логах.
Так, буквально восемь строк кода решают большую часть задач по логированию. И все это благодаря одной библиотеке, которая уже реализовала за нас почти весь необходимый функционал.
Mapper для работы с командами Linux терминала
Он позволяет выполнять самые разные действия: создать каталог, перезагрузить систему, вывести содержимое логов или, например, использовать стандартные команды вроде cat или ls:
В Linux-mapper можно собрать любой набор команд, которые применяются в терминале при тестировании приложения или при работе со сторонними утилитами.
Mapper для работы с разрабатываемым приложением
Кроме работы с терминальными командами, во фреймворке нужен интерфейс для взаимодействия с самим приложением, которое разрабатывают наши инженеры и которое мы тестируем. CLI-интерфейс для управления СХД, реализован в виде утилиты eracli. Он позволяет инженерам управлять RAID-массивами, системными настройками
В коде у метода есть подметоды — create, destroy, show и другие:
Благодаря такому подходу формируется удобный и понятный интерфейс: есть родительский класс с методом raid, а у него — набор подметодов для управления рейдами:
На этом возможности фреймворка не ограничиваются. В него можно добавить любые дополнительные утилиты, которые необходимы для тестирования. Это могут быть как сторонние библиотеки, так и отдельные приложения для нагрузочного тестирования — например, FIO или VDBench, которые позволяют создавать нагрузку на систему и проверять целостность данных.
По сути, в фреймворк можно включить все, что нужно для работы, превратив его в максимально удобный и универсальный инструмент. Такой подход позволяет тестировать любое CLI подобное приложение.
Пишем тесты
После завершения базовой архитектуры фреймворка мы задумались, как упростить процесс написания тестов. Важно было, чтобы с этой задачей справлялись не только разработчики, но и ручные тестировщики без опыта программирования.
Простой тест-кейс
Для начала набросаем тест-кейс «на бумаге»:
- Создать рейд массив.
- Проверить, что массив создался и отображается в системе.
- Выполнить штатную перезагрузку сервера.
- После перезагрузки проверить, проверить что массив не удалился и отображается в системе.
Во фреймворке это превращается в довольно понятный любому тестировщику код:
Для тестировщика это читается почти как обычная инструкция: «создай RAID и проверь, что он есть».
Идем дальше: действия с системой
Теперь представим, что мы хотим проверить, что RAID остается в рабочем состоянии после перезагрузки системы. В CLI это целая цепочка команд. В нашем коде:
Метод node.reboot () под капотом подключается к нужной машине по SSH, отправляет команду на перезагрузку и ждет, пока система снова будет доступна. Это и есть mapper для работы с командами Linux-терминала — фактически интерфейс нашей машины:
Например, мы можем удаленно вызвать у нее метод reboot, дождаться полной загрузки системы и затем убедиться, что все прошло успешно. В частности, проверяем, что после перезагрузки RAID-массив сохранился и не разрушился.
Автоматизация тестов
Автоматизацию тестов можно значительно упростить и сделать элегантнее с помощью Pytest. Рассмотрим один пример. Допустим, мы хотим проверить создание RAID-массивов разных уровней — не только нулевого или пятого, но и других. Если писать такие тесты без особенностей Pytest, получится много однотипного кода, что противоречит хорошему стилю программирования. Нам нужно избежать дублирования, и здесь помогает параметризация.
Для этого используется классический декоратор @pytest.mark.parametrize. В нем мы передаем список параметров, которые будут подставляться в тест. В нашем случае это уровни RAID: 0, 1, 5, 50, N+M. Каждый запуск теста берет очередное значение из списка и выполняет проверку:
Так один тест автоматически прогоняется несколько раз с разными параметрами, и код остается компактным.
Вместо заключения
Подводя итог, можно выделить несколько ключевых результатов, которых удалось достичь благодаря такому подходу к созданию собственного инструмента для тестирования.
Мы обеспечили поддержку многих популярных Linux-дистрибутивов и основных версий ядер — 4, 5 и 6 серии. Это делает фреймворк гибким и применимым в разных окружениях.
Самое важное: мы добились возможности проводить полное регрессионное тестирование буквально нажатием одной кнопки. Для специалистов по тестированию веб-приложений это звучит привычно, но в мире «железа» — огромный шаг вперед. Ведь нужно проверить не только запись и чтение данных, но и их целостность в разных условиях: при сбоях, отказах компонентов, а то и при падении целой серверной стойки. Автоматизировать такие сценарии и свести их к одному клику — задача нетривиальная, и именно это стало нашей серьезной победой.
У такого собственного инструмента, конечно, есть и плюсы, и минусы.

Код фреймворка доступен на GitHub. Любой желающий может посмотреть, как он устроен, и при необходимости адаптировать под свои задачи. Изначально проект появился как решение конкретной проблемы, но сам подход подойдет каждому, кто сталкивается с тестированием сложных систем через CLI.
Для меня этот фреймворк стал наглядным доказательством: команда инженеров-тестировщиков способна не только «проверять чужой код», но и разрабатывать полноценные инженерные решения, которые меняют сам подход к работе.