Привет, Хабр! В данной статье мы рассмотрим процесс настройки мониторинга массивов семейства HP EVA (Enterprise Virtual Array) с помощью Open Source продукта Zabbix, объясним, как получать и обрабатывать данные с массива, покажем, с какими проблемами можно столкнуться при настройке, а также расскажем о двух вариантах реализации системы мониторинга.

Изучаем пациента

Итак, перед нами была поставлена задача — настроить мониторинг массива HP EVA 6000-й серии, конец жизненного цикла (EOL) которых наступил еще в 2014–2015 гг., но они до сих пор нередко встречаются у наших заказчиков. При беглом взгляде на документацию к этому аппарату мы увидели пункт о настройке SNMP и подумали: «Ну, тут работы максимум на полчаса: проставить пару галочек и докинуть шаблон». Ничто не предвещало сложностей — берем шаблон для HP-массивов, коих у нас накопилось немало, или вооружаемся мибами и делаем свой.

Попросили коллег проверить, включен ли доступ по SNMP, и создать какое-нибудь community для мониторинга. Ребята отвечают, что не нашли в CV (инструмент для управления массивом) или веб-интерфейсе никаких пунктов, связанных с SNMP.

Но как же так? Мы же видели! А, постойте...

 Рисунок 1. Пример настройки SNMP-трапов из документации HP (Источник: «Инфосистемы Джет»).
Рисунок 1. Пример настройки SNMP-трапов из документации HP (Источник: «Инфосистемы Джет»).

Перечитываем документацию, и действительно: «There are two ways in which HP SIM can receive events: SNMP and WBEM». HP SIM (Systems Insight Manager) — это ПО, предназначенное для управления и отслеживания состояния оборудования HP. Некая центральная консоль с кучей возможностей и, конечно же, отдельной платной лицензией. Отправлять трапы на сторонний сервер СХД не умеет, и подключиться к ней по WBEM/SNMP тоже нельзя.

Ищем подход

Поняв, что никаких привычных интерфейсов для взаимодействия с массивом нет, мы стали искать информацию по какому-нибудь API или SDK. И такой вариант нашелся — HP Storage System Scripting Utility (SSSU). Это утилита для автоматизации работы с СХД, которая существует под Windows и Linux. Ее можно скачать с официального сайта в составе утилиты Command View или найти в виде отдельного бинарного файла. Судя по отзывам на форуме вендора, между версией SSSU и версией прошивки массива есть зависимость, но найти какую-то внятную матрицу совместимости не удалось.

Также поиск натолкнул нас на статьи по настройке мониторинга этого семейства массивов в Nagios, и даже был какой-то пакет VBA-скриптов и простенький шаблон для Zabbix. Их авторы тоже использовали SSSU для сбора информации и, похоже, это единственный вариант.

Изучаем интерфейс

Способ коммуникации найден, теперь нужно попробовать связаться с железом и что-нибудь у него «спросить». Изначально мы решили использовать Linux-версию SSSU, так как это дает нам следующие преимущества:

  • можно использовать «внешние проверки» и запускать утилиту прямо из Zabbix-сервера;

  • не нужно хранить в мониторинге или обновлять в локальных конфигах информацию о том, куда инсталлирован SSUU/CV;

  • можно применять богатый инструментарий для обработки вывода.

Только вот найти эту «Linux-версию» оказалось непросто, сказался возраст утилиты и ее отсутствие в виде отдельного пакета на CDN вендора. Каким-то чудом удалось отыскать древний пост на вендорском форуме, который вел на FTP-сервер с заветным файлом.

4 версия и 6 версия.

Листинг 1. Информация о найденном бинаре.

user@server hptools]# file sssu    

sssu: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, for GNU/Linux 2.2.5, not stripped

Смотрим в документации, как работать с этой утилитой, и там все неплохо. SSSU может запускаться без параметров и работать в интерактивном режиме, или можно передать необходимую последовательность команд и получить вывод. Также она может отдавать данные в виде XML-документов, а структурированный вывод данных — это именно то, что мы искали.

Чтобы начать работать, мы должны подключиться к CommandView — для этого заранее следует создать там учетную запись. Подключиться можно, задав все параметры в файле sssu.cfg (он должен находиться в той же директории, что и SSSU):

Листинг 2. Пример конфигурационного файла SSSU.

Section login 
{ 
SET OPTIONS CV_PORT=2372 
SELECT MANAGER localhost username=admin password=secretPassword 
SELECT SYSTEM you_system_name
} 
Section Options 
{ 
SET OPTIONS NOCOMMAND_DELAY 
SET OPTIONS EXIT_ON_ERROR 
SET OPTIONS DISPLAY_STATUS 
}

По сути, это просто набор команд, который будет выполняться при запуске утилиты. Либо можно передать эти же команды в виде параметров:

Листинг 3. Пример запуска SSSU в не интерактивном режиме.

[user@server hptools] ./sssu 'SELECT MANAGER 10.10.11.12 username=admin password=secretPassword'

После авторизации нужно указать, к какому массиву мы будем подключаться. Полный список систем можно посмотреть командой «LS SYSTEM», а, чтобы выбрать конкретную, использовать «SELECT SYSTEM system_name».

После авторизации можно передавать команды. Нас интересуют следующие:

  • LS CONTROLLER,

  • LS DISK,

  • LS DISK_GROUP,

  • LS DISKSHELF,

  • LS VDISK.

Выполняя команды «LS *» без аргументов, мы будет получать список выбранных компонентов. Также можно передать ряд параметров:

  • LS <ITEM_TYPE> item_name — вывод данных по конкретному объекту;

  • LS <ITEM_TYPE> item_name XML — вывод информации по объекту в формате XML;

  • LS <ITEM_TYPE> FULL XML — вывод информации по всем объектам выбранного компонента в формате XML.

Рисунок 2. Пример вывода команд с разными аргументами (Источник: «Инфосистемы Джет»).
Рисунок 2. Пример вывода команд с разными аргументами (Источник: «Инфосистемы Джет»).

Итоговый набор команд для получения списка контроллеров в не интерактивном режиме будет выглядеть так:

Листинг 4. Пример полной команды для получения данных

[user@server hptools] ./sssu 'SELECT MANAGER 10.10.11.12 username=admin password=secretPassword' 'SELECT SYSTEM sys1' 'LS CONTROLLER FULL XML'

Приступаем к реализации

Изначально у нас было три варианта решения задачи: модуль для Zabbix-сервера, сторонний скрипт для парсинга данных и возможность сделать все встроенными средствами Zabbix. От идеи с модулем пришлось отказаться, т. к. это заняло бы неоправданно много времени на реверс-инжиниринг утилиты или ее протокола (все общение, кстати, зашифровано, а найти исходный код не удалось).

Всё свое ношу с собой

Рассмотрим сначала вариант, где мы решили обойтись только средствами Zabbix. На старте мы имеем:

  • SSSU, который отдает нам XML;

  • автоматическое обнаружение элементов данных;

  • различные средства предобработки данных в Zabbix, включая XPath, JSONPath;

  • возможность писать свои скрипты для обработки полученных данных.

В целом всего этого должно хватить с головой, а наша первоначальная задача — получить список компонентов и преобразовать их в формат, понятный механизму LLD Zabbix. Начинаем с опроса контроллеров и смотрим на вывод команды «LS CONTROLLER». Полный вывод мы специально опускаем, т. к. он не принципиален, важна только структура.

Рисунок 3. Структура возвращаемых данных (Источник: «Инфосистемы Джет»).
Рисунок 3. Структура возвращаемых данных (Источник: «Инфосистемы Джет»).

Сразу видим, что, кроме данных, выводятся все выполненные команды, да и XML у нас не цельный, а представляет собой блоки с описанием каждого объекта. Это мы поправим предобработкой. Сейчас нам интересна вложенность элементов внутри object. Внутри есть объекты sensors и hostports, которые содержат в себе информацию о состоянии и нужны нам. Кроме как через эту команду, получить данные нельзя, да и еще количество элементов может меняться в зависимости от типа контроллера.

Тут мы или просто нарезаем в шаблоне энное число айтемов и триггеров, которые будут вытаскивать нужные значения из определенных нами тегов XML, или делаем правило автоматического обнаружения. Первый вариант непрактичный и будет требовать ручного вмешательства, зато простой. Мы решили остановиться на втором.

Для начала создаем метрику, которая будет хранить всю информацию по контроллерам. Для этого создаем элемент типа «Внешняя проверка» (бинарь SSSU, должен находиться в директории /usr/lib/zabbix/externalscripts) и в качестве ключа указываем следующее значение:

Листинг 5. Пример ключа для сбора данных

sssu["SELECT MANAGER {$CV.IP} username={$CV.USERNAME} password={$CV.PASSWORD}", "SELECT SYSTEM {$CV.SYSTEM}", "LS CONTROLLER FULL XML"]

Далее нам нужно из полученного вывода вытащить блоки с XML, объединить их в единый контейнер и преобразовать все это «добро» в JSON. JSON-документ нам нужен для того, чтобы потом было проще достать из него необходимые данные и удобнее формировать массив с именами компонентов в правилах обнаружения. Создаем новый шаг предобработки типа «JavaScript» и используем следующий код:

Листинг 6. Правило препроцессинга для трансформации XML в JSON

const regex = /(<\w+>.*<\/\w+>|<\w+>|<\/\w+>)/gm;
var xml_data = '<controllers>\n' + value.match(regex).join('\n') + '</controllers>';
 
return XML.toJson(xml_data);

С помощью регулярного выражения выбираем все теги и информацию, заключенную между ними (тегов с цифрами в выводе SSSU не встречается), далее помещаем полученные объекты в один родительский контейнер controllers и выполняем преобразование в JSON.

Рисунок 4. Результат работы полученного шага (Источник: «Инфосистемы Джет»).
Рисунок 4. Результат работы полученного шага (Источник: «Инфосистемы Джет»).

Теперь создаем правила обнаружения, где в качестве типа будет выступать «зависимый элемент», а с помощью предобработки мы сформируем из мастера метрики список элементов (датчиков, хостпортов, девайспортов, данных по самим контроллерам).

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

Рисунок 5. Создаем элемент, зависящий от элемента, хранящего полный вывод в JSON-формате (Источник: «Инфосистемы Джет»).
Рисунок 5. Создаем элемент, зависящий от элемента, хранящего полный вывод в JSON-формате (Источник: «Инфосистемы Джет»).

Листинг 7. Формирование списка контроллеров и их датчиков

var controllers_obj = JSON.parse(value);
var data = [];
 
for (c_id in controllers_obj.controllers.object) {
        for ( s_id in controllers_obj.controllers.object[c_id].sensors.sensor ) {
            data.push('{ "{#CONTROLLER_NAME}": "' + controllers_obj.controllers.object[c_id].controllername + 
                    '", "{#SENSOR_NAME}": "' + controllers_obj.controllers.object[c_id].sensors.sensor[s_id].name + '" }')
        }
}
 
return '{ "data": [' + data.join(',') + ']}';

Тут все просто: проходимся по JSON’у и достаем из него список контроллеров и список связанных с ними датчиков. После этого формируем JSON в формате, понятном правилу обнаружения, чтобы автоматически создавались/удалялись элементы данных под каждый обнаруженный элемент СХД.

Осталось создать прототип метрики (он также будет зависеть от нашего здоровенного JSON’a) и с помощью функции JSONPath доставать из него данные.

Рисунок 6. Прототип элемента данных (Источник: «Инфосистемы Джет»).
Рисунок 6. Прототип элемента данных (Источник: «Инфосистемы Джет»).

В списке обработчиков выбираем функцию JSONPath и вводим следующий фильтр:

$.controllers.object[?(@.controllername=='{#CONTROLLER_NAME}')].sensors.sensor[?(@.name=='{#SENSOR_NAME}')]["operationalstate"]

Он позволит достать значение сенсора с именем из макроса {#SENSOR_NAME} для контроллера {#CONTROLLER_NAME}. Добавим еще один шаг — TRIM с параметром «[]. Поскольку мы ищем данные среди массива элементов, JSONPath также вернет нам статус в виде массива из одного элемента, а с помощью трима мы обрежем обрамляющие значение символы.

Рисунок 7. Предобработка прототипа данных (Источник: «Инфосистемы Джет»).
Рисунок 7. Предобработка прототипа данных (Источник: «Инфосистемы Джет»).

Данные возвращаются в виде литер good/warning/bad, поэтому создаем новый триггер на основе функции find и проверяем, что там обнаружил мониторинг.

Рисунок 8. Пример обнаруженных устройств (Источник: «Инфосистемы Джет»).
Рисунок 8. Пример обнаруженных устройств (Источник: «Инфосистемы Джет»).

Аналогичным образом создаем правила обнаружения для хост/девайс-портов и метрик самих контроллеров. Меняться будут только пути до элементов JSON-объекта в самих правилах и получаемых элементах данных. При такой реализации все завязано на главную метрику, поэтому частота опроса для всех потомков будет одинакова. Также следует добавить триггер, проверяющий наличие данных в мастер-метрике. Схема получается следующая:

Рисунок 9. Поток данных в системе мониторинга (Источник: «Инфосистемы Джет»).
Рисунок 9. Поток данных в системе мониторинга (Источник: «Инфосистемы Джет»).

Немного будет отличаться картина с мониторингом дисков, т. к. тут вступает в силу одно неприятное ограничение Zabbix. Метрики типа text/log могут сохранить максимум 64 Кб данных, а всё, что больше этого значения, будет обрезаться перед сохранением в БД. Поэтому трюк с созданием одной мастер-метрики, собирающей данные по всем дискам за один раз, не прокатит.

Но обойти это ограничение можно, если мы будет получать в правиле обнаружения только имена дисков, и потом уже в прототипе данных создавать мастер айтем, который будет хранить данные по конкретному диску. К минусам такого решения можно отнести:

  • нельзя забрать данные за один раз, на больших инсталляциях СХД возможны проблемы с опросом;

  • теряется возможность динамически обнаруживать вложенные объекты, т. к. мы не можем создавать правила обнаружения внутри других правил или обращаться к прототипам других правил.

Рисунок 10. Обнаружение дисков (Источник: «Инфосистемы Джет»).
Рисунок 10. Обнаружение дисков (Источник: «Инфосистемы Джет»).

Свой обработчик

Итак, в прошлой главе мы разобрались, что можно сделать из буханки хлеба троллейбус обойтись «малой кровью» и решить проблему встроенными инструментами. Но нам не давали покоя проблемы, с которыми мы столкнулись при опросе дисков. Также, если у нас большое число подобных массивов, то все эти правила предобработки могут оказывать влияние на производительность мониторинга в целом. Последней проблемой является высокая связанность метрик между собой, что усложняет поддержку решения. Тогда мы задумались над реализацией собственного демона (программы в Unix/Linux-системе, которая запускается и работает в фоновом режиме), который возьмет на себя функции сбора, обработки и отправки в мониторинг полученных данных.

Собирать данные из СХД мы научились, а отправлять их в мониторинг будем с помощью Zabbix-траппера. Для тех, кто не знаком с этим термином: Zabbix поддерживает pull-модель сбора данных, т. е. мы можем из своих скриптов/продуктов отправлять данные напрямую в сервер мониторинга. Серверу, кроме самих данных, нужно передать ключ метрики (элемент должен иметь тип «Zabbix-траппер») и имя узла сети. Главное в этом случае — придерживаться описанного разработчиками формата сообщения либо использовать утилиту Zabbix_sender.

Вот примерная схема, где из конфиг-файла будет вычитываться список массивов с информацией по подключению, далее под каждый массив создаваться пара потоков, внутри которых в бесконечном цикле выполняться функция-обработчик. В функции-обработчике будет устанавливаться тайм-аут для управления частотой опроса, который также находится в конфиге. Дополнительно будет запущен поток HeatlhCheck, периодически отправляющий в мониторинг данные о состоянии демона. При возникновении ошибок или исключений трейсы и логи о событии также будут отправлены в систему мониторинга, что позволит упростить диагностику.

Решение мы реализовали на Python с использованием библиотек PyZabbix и lxml. Кстати, в отличие от ограниченного функционала JS в Zabbix мы можем работать с XML-файлами напрямую, без конвертации их в JSON и без использования JSONPath. Просто будем обходить XML-дерево и читать из его компонентов значения.

Рисунок 11. Схема демона обработчика данных (Источник: «Инфосистемы Джет»).
Рисунок 11. Схема демона обработчика данных (Источник: «Инфосистемы Джет»).

Потоки, обозначенные на схеме как «LLD Thread», занимаются обнаружением компонентов массива. Обработчик внутри выполняет команды SSSU с флагом FULL XML и, обходя полученное дерево, извлекает из него имена компонентов, которые потом формируют JSON-объект, передаваемый мониторингу. Мониторинг, в свою очередь, создает по этим спискам все необходимые метрики с типом «Zabbix-траппер» и триггеры, которые будут наполняться данными.

За этот процесс отвечает вторая группа потоков «Data Thread», которая также получает вывод SSSU, парсит его, подготавливает список метрик, заполняет его данными и отправляет в мониторинг.

Для получения данных реализована функция get_xml, которая, как и в случае с Zabbix, вытаскивает из вывода SSSU теги и оборачивает их в один XML-контейнер.

Рисунок 12. Функция получения данных SSSU (Источник: «Инфосистемы Джет»).
Рисунок 12. Функция получения данных SSSU (Источник: «Инфосистемы Джет»).

На данном этапе может возникнуть вопрос: а как мы связываем метрики в Zabbix и в нашем скрипте? Ответ прост: с помощью ключей элементов данных. Правила обнаружения создают под каждый компонент СХД набор элементов данных с одинаковыми ключами, у которых отличаются только аргументы.

Рисунок 13. Пример создания элементов через внешний скрипт (Источник: «Инфосистемы Джет»).
Рисунок 13. Пример создания элементов через внешний скрипт (Источник: «Инфосистемы Джет»).

На стороне скрипта мы просто подставляем имя компонента в ключ и данные. Если указанной метрики нет в мониторинге (например, добавили диск, а он еще не успел обнаружиться), то он просто отбросит это значение.

Создаем Docker-контейнер для нашего скрипта и темплейт под опрашиваемые метрики, нарезаем необходимые графики, триггеры, макросы для пороговых значений и любуемся результатом.

Заключение

Мы верим, что этот гайд будет полезен опытным пользователям, а новички смогут оценить, каким функционалом по обработке данных располагает Zabbix. Хоть в данном случае мы все же пришли к решению на основе самописного модуля, возможности Zabbix по предобработке данных с помощью JavaScript также позволили достигнуть поставленной задачи, пусть и не самым элегантным способом.

Jet Service Team,
Инфосистемы Джет

Комментарии (0)