Ad Widget

Collapse

Помогите разобраться с LLD и itemprototype для случая внешней проверки

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • Nick2018
    Junior Member
    • Oct 2018
    • 7

    #1

    Помогите разобраться с LLD и itemprototype для случая внешней проверки

    Помогите пожалуйста разобраться, как настроить LLD для случая externalscripts.
    Узлы для мониторинга поддерживают интерфейс RedFish, но в Zabbix этот интерфейс не реализован, приходится делать внешнюю проверку.
    Я реализовал скрипт с использованием curl и jq утилит, который принимает в качестве аргумента ip-адрес хоста и делает https запрос с фиксированным логином и паролем на заданный узел, получает JSON объект и упрощает его до требуемого списка словарей в Zabbix.
    Поместил скрипт sensors.sh в каталог /usr/lib/zabbix/externalscripts

    Пока удалось добиться, получать JSON как текст в элемент данных.
    Для этого я создал шаблон, в нем создал элемент данных типа externalscripts
    Key = sensors.sh[{HOST.IP}]
    Тип данных = текст

    Протестировал - получил строку JSON следующего вида
    [
    {
    "{#SENSOR}": "Core_1_CPU1",
    "{#VALUE}": 39
    },
    {
    "{#SENSOR}": "Core_2_CPU1",
    "{#VALUE}": 39
    },
    {
    "{#SENSOR}": "Core_3_CPU1",
    "{#VALUE}": 39
    },
    {
    "{#SENSOR}": "Core_4_CPU1",
    "{#VALUE}": 39
    },
    {
    "{#SENSOR}": "Core_5_CPU1",
    "{#VALUE}": 40
    },
    {
    "{#SENSOR}": "Core_6_CPU1",
    "{#VALUE}": 39
    },
    {
    "{#SENSOR}": "DIMM_A1_CPU1",
    "{#VALUE}": 32
    },
    {
    "{#SENSOR}": "DTS_CPU1",
    "{#VALUE}": 41.079
    },
    {
    "{#SENSOR}": "Die_CPU1",
    "{#VALUE}": 41.094
    }
    ]

    Создал узел сети с IP адресом и связал его с шаблоном. В результате получаю в базу данных поток JSON-текстов. Но мне надо не это.
    Мне нужно, чтобы для каждого узла, для которого применен шаблон, при получении очередного JSON автоматически создавались или обновлялись элементы данных с именами из {#SENSOR} и значениями из {#VALUES} и накапливалась их история, а текстовый исходник JSON не сохранялся. Элементов данных много, в данном примере только часть из них, поэтому важно чтобы элементы данных создавались и обновлялись автоматически.

    Я попробовал реализовать это через LLD, прочитав естественно документацию по LLD, externalscripts и прочее, но ничего не получилось. В документации описываются только стандартные случаи и встроенные скрипты и макросы, а как быть в данном случае совершенно не понятно.
    Во всех попытках получал ошибки синтаксиса ключа. И вообще логика LLD осталась непонятной. В данном случае озадачивает форма ключа, ведь во всех местах - в элементе данных, в правиле обнаружения, в прототипе элемента данных, в основном и зависимом элементе, в общем везде, приходится дублировать вызов внешнего скрипта с параметром, т.к. другого вида ключа не просматривается. Это очень озадачивает. И ничего не получается.
  • Kos
    Senior Member
    Zabbix Certified SpecialistZabbix Certified Professional
    • Aug 2015
    • 3404

    #2
    Вот ссылка на относительно старый FAQ, с которой можно начать.
    С тех пор, правда, вышло несколько новых версий Zabbix, в каждой из которых добавлялись какие-то возможности.
    В принципе, содержащаяся в этом FAQ информация устарела не очень сильно (наверное, имеет смысл дополнить), могу кратко добавить актуальное:
    • сейчас JSON, используемый для LLD, не обязан начинаться именно с элемента "data": он может сразу содержать массив LLD-макросов и их значений;
    • при наличии на входе JSON-а, содержащего как статические данные (нужные для работы механизма LLD, т.е. для создания объектов - элементов данных и триггеров), так и динамические (т.е. значения элементов данных), можно принимать этот JSON в мастер-айтем (причём даже без хранения истории), а затем при помощи зависимых элементов данных и препроцессинга разбирать по частям: для правила LLD - оставлять только статическую часть и использовать её при создании элементов данных, а в прототипах элементов данных - выдёргивать нужные значения (из того же мастер-айтема).
    Кстати, сейчас HTTP-запрос можно делать с помощью HTTP-агента (на самом деле, этот запрос делается Zabbix-сервером либо Zabbix-прокси), а нужные преобразования производить при помощи предобработки (скажем, на JavaScript). Это сделает ваше решение более переносимым и простым в поддержке, т.к. избавит от внешних зависимостей (curl, qj, bash, сам файл со скриптом).

    Вне зависимости от того, каким образом получать данные, если мы уже имеем на входе такой JSON, как вы процитировали, я бы дальше делал так:
    • Немного модифицировал бы этот JSON, чтобы избежать в нём имён LLD-макросов напрямую. Т.е. он должен иметь такой вид:
    Code:
    [
    {
    "sensor": "Core_1_CPU1",
    "value": 39
    },
    {
    "sensor": "Core_2_CPU1",
    "value": 39
    },
    {
    "sensor": "Core_3_CPU1",
    "value": 39
    },
    {
    "sensor": "Core_4_CPU1",
    "value": 39
    },
    {
    "sensor": "Core_5_CPU1",
    "value": 40
    },
    {
    "sensor": "Core_6_CPU1",
    "value": 39
    },
    {
    "sensor": "DIMM_A1_CPU1",
    "value": 32
    },
    {
    "sensor": "DTS_CPU1",
    "value": 41.079
    },
    {
    "sensor": "Die_CPU1",
    "value": 41.094
    }
    ]​
    • Принимал бы этот JSON в отдельный мастер-айтем. Он должен иметь тип данных "текст", ключ - допустим, "sensors.get", а историю - для начала короткую (1 день), просто на время отладки (чтобы видеть, что за данные приходят и приходят ли вообще), а затем (когда всё уже работает как надо) - вообще без истории;
    • Правило LLD имело бы ключ, допустим, "sensors.discovery", тип - зависимый (Dependent), мастер-айтем - вышеупомянутый "sensors.get". В принципе, исходный мастер-айтем можно использовать как есть; но для эффективности я бы добавил два шага препроцессинга: первым шагом - убирал бы из JSON-а динамические данные, оставляя только то, что нужно для LLD, а вторым шагом - делая тротлинг. Дело в том, что работа механизма LLD - довольно затратная по ресурсам операция; при этом список объектов, которые надо мониторить, меняется относительно редко, но сами собираемые значения - часто. Поэтому тротлинг в данном случае вполне оправдан, но, чтобы LLD не дёргалось каждый раз по приходу новых значений, JSON для LLD должен содержать только статические макросы. Например, чтобы оставить только имя сенсора (элементы "sensor", которые потом будут преобразованы в LLD-макрос {#SENSOR}), можно использовать шаг препроцессинга с типом JavaScript и таким кодом скрипта:
    Code:
    //transform source string into JSON object
    var val_json=JSON.parse(value);
    //transform JSON back to string but filtering only with "sensor" members
    return JSON.stringify(val_json,['sensor']);
    ​
    В итоге исходный JSON примет такой вид:
    Code:
    [
    {"sensor": "Core_1_CPU1"},
    {"sensor": "Core_2_CPU1"},
    {"sensor": "Core_3_CPU1"},
    {"sensor": "Core_4_CPU1"},
    {"sensor": "Core_5_CPU1"},
    {"sensor": "Core_6_CPU1"},
    {"sensor": "DIMM_A1_CPU1"},
    {"sensor": "DTS_CPU1"},
    {"sensor": "Die_CPU1"}
    ]​
    • Уже теперь в настройках правила обнаружения на вкладке "LLD macros" указать, что LLD-макрос {#SENSOR}​ подставляется вместо элементов $.sensor.
    • В прототипе элемента данных указал бы ключ sensors.value["{#SENSOR}"], тип - тоже зависимый от того же мастер-айтема "sensors.get", а тип данных - Numeric (float). Для извлечения только нужных данных нужно добавить шаг препроцессинга "JSON Path" и указать выражение:
    Code:
    $[?(@.sensor=="{#SENSOR}")].value
    В итоге после первого обновления мастер-айтема должен отработать механизм LLD и создать все необходимые элементы данных, а уже после второго обновления (и далее на каждом последующем) - присвоить им значения.​
    Last edited by Kos; 06-09-2022, 11:10.

    Comment

    • Nick2018
      Junior Member
      • Oct 2018
      • 7

      #3
      Большое спасибо за развернутый ответ! Очень интересно. Сейчас попробую реализовать ваш вариант.​

      Comment

      • Nick2018
        Junior Member
        • Oct 2018
        • 7

        #4
        Что-то не получается запрос через HTTP-агента. Начальная задача - получить необработанное тело JSON в виде текста в исходном формате RedFish.

        Создал шаблон с элементом данных типа HTTP-агент.
        В качестве url указал /redfish/v1/Chassis/Vendor_CPUs/Thermal
        в качестве ключа указал произвольную строку redfish-v1-Chassis-Vendor_CPUs-Thermal
        Не стал заполнять поля SSL, т.к. нужно игнорировать ошибки сертификата.

        Подключил шаблон к узлу с адресом 192.168.0.220 и портом 443

        Я ожидаю, что Zabbix сформирует следующий запрос https://192.168.0.220/redfish/v1/Cha...r_CPUs/Thermal

        Данные не поступают, в тесте элемента данных для данного узла ошибка "Cannot perform request: URL using bad/illegal format or missing URL​", при этом адрес и порт не отображаются, как будто они не известны.

        Comment

        • Nick2018
          Junior Member
          • Oct 2018
          • 7

          #5
          Кстати, предыдущий вариант, через внешний скрипт, и шаблон с прототипом элементов данных у меня получился полностью, в том виде как я изначально планировал, и похоже на вашу рекомендацию. Но мне интересно реализовать ваш вариант, без внешнего скрипта, через HTTP запрос и с обработкой на JavaScript.

          Comment

          • Nick2018
            Junior Member
            • Oct 2018
            • 7

            #6
            Упс... Начальная задача решена. Оказывается, нужно было самостоятельно полностью формировать URL, вот так: https://{HOST.IP}/redfish/v1/Cha...r_CPUs/Thermal
            Копаем дальше

            Тело JSON получаю
            {
            "@odata.id": "/redfish/v1/Chassis/Vendor_CPUs/Thermal",
            "@odata.type": "#Thermal.v1_4_0.Thermal",
            "Fans": [],
            "Id": "Thermal",
            "Name": "Thermal",
            "Redundancy": [],
            "Temperatures": [
            {
            ...
            и т.д.

            , но в Предобработке не удается выделить поля

            JSONPath $.Name

            вообще на любые конструкции получаю аналогичную ошибку теста
            • cannot extract value from json by path "$.Name": cannot parse as a valid JSON object: invalid object format, expected opening character '{' or '[' at: '40'

            Попытался в предобработке выделить хоть что-то при помощи JavaScript

            var val_json=JSON.parse(value);
            return JSON.stringify(val_json,['Name']);​
            • SyntaxError: invalid token (line 2)
            или так

            var val_json=JSON.parse(value);
            return JSON.stringify(val_json,['Temperatures']);​


            получил ошибку
            • SyntaxError: invalid json (at offset 1) at [anon] (duktape.c:36562) internal at parse () native strict preventsyield at [anon] (function:1) preventsyield
            Last edited by Nick2018; 07-09-2022, 12:23.

            Comment

            • Kos
              Senior Member
              Zabbix Certified SpecialistZabbix Certified Professional
              • Aug 2015
              • 3404

              #7
              Я ожидаю, что Zabbix сформирует следующий запрос https://192.168.0.220/redfish/v1/Cha...r_CPUs/Thermal
              Не сформирует. И схему, и хост нужно указывать явно. Вместо имени хоста можно использовать системные макросы {HOST.*}, (например, {HOST.CONN}).
              Впрочем, с этим Вы уже, кажется, разобрались.
              Originally posted by Nick2018
              Копаем дальше

              Тело JSON получаю
              [...]
              Похоже, что вы получаете либо некорректный JSON, либо не только JSON.
              Например, анализируете весь HTTP-responce (не только его тело, но и HTTP-заголовки).
              Попробуйте для начала, выставив тип данных = "Текст", просто убрать препроцессинг совсем, а затем посмотреть по истории (Latest data) - что именно возвращается в качестве значения.
              Потом то, что вернулось, засунуть в какой-либо JSON парсер (примеры навскидку: раз, два, три), чтобы убедиться в его синтаксической корректности.
              И потом уже пытаться обрабатывать и выцеплять какие-то фрагменты через JSONPath.

              Comment


              • Nick2018
                Nick2018 commented
                Editing a comment
                Ошибка с JSONPath как-то сама собой исчезла, не знаю в чем была причина. Я несколько раз вносил изменения и один раз заметил, что ошибка связана со старым состоянием, которое уже давно изменено, какое-то внутреннее рассогласование, или я забыл сохранить изменение. В общем, с одного прекрасного момента JSONPath заработал в том виде, как я привел. А вот JavaScript пока так и не победил, выдает ошибку во второй строке. Но я решил задачу, без JavaScript, обошелся JSONPath и LLD макросами. Сейчас собираю данные из нескольких веток RedFish без внешнего скрипта (элементов данных немного 30 и до 50, узел отладочный). Исправить ошибку в JavaScript интересно просто для понимания. И мне не совсем понятно Ваше опасение или рекомендация разделить создание элементов данных по прототипам и получение данных. Я не знаю как это реализовано на внутреннем уровне Zabbix, но если бы я это писал, то реализовал бы через поиск по словарю ключ:значение, для которого обычно делается сначала поиск ключа(что очень быстро) и в случае его отсутствия создание нового ключа и элемента данных(одноразовая операция), а в случае наличия, сохранение данных. При таком алгоритме не имеет смысла разделять создание элементов данных и добавление новых данных, т.к. в случае наличия элемента данных попытки его пересоздания не происходит, а сразу добавляются данные, как мне кажется. У меня сейчас данные (30 элементов) обновляются с частотой 10 секунд и какой либо особой нагрузки на сервер(рабочая персоналка) я не заметил, хотя входной JSON не фильтрованный(много полей), создание элементов по прототипам не отделено от получения данных.

                Коротко, мое решение, с Вашей помощью:
                1. Создать основной элемент данных типа "HTTP-агент" для получения произвольного необработанного тела JSON, заполнить обязательные поля, URL, логин, пароль. В предобработке выделить из него нужный список, используя JSONPath $.Temperatures. Историю можно не хранить
                2. Создать правило обнаружения типа "зависимый элемент", ключ - произвольная уникальная строка "xxx_rule"(в сетевых запросах не участвует). Нужно только создать LLD макросы. В моем случае {#SENSOR} = $.MemberId, {#VALUE} = $.ReadingCelsius
                3. Внутри правила создать прототип элементов данных типа "вычисляемый". Имя: "Температура {#SENSOR}", уникальный ключ: xxx_rule[{$SENSOR}], формула {#VALUE}. Заметим, что ключ в сетевых запросах не участвует, а просто строится на основе готового значения макроса. Т.е. данные поступают "на лету", из JSON предобработки, в потоке, через правило обнаружения и макросы. Перебор списка автоматизирован в Zabbix.
                4. Присоединить шаблон к узлу сети, указать IP-адрес узла. Протестировать.

                Для каждого набора данных создать в том же шаблоне отдельные основные элементы, правила обнаружения и прототипы

                Всё.
                Last edited by Nick2018; 08-09-2022, 13:00.
            • Kos
              Senior Member
              Zabbix Certified SpecialistZabbix Certified Professional
              • Aug 2015
              • 3404

              #8
              Да, всё верно. Спасибо, что отписались о текущей ситуации. Единственная просьба - лучше это делать не в комментарии к чужой реплике, а своей отдельной репликой: так гораздо удобнее читать, к тому же комментарии не обновляют ветку обсуждения и не видно, что кто-то комментарий добавил, если только специально не зайти и не проверить.
              Я не знаю как это реализовано на внутреннем уровне Zabbix, но если бы я это писал, то...
              Я не знаю, как именно делал бы реализацию я, но в любом случае - мы вынуждены подстраиваться под то, как это реализовано разработчиками сейчас.
              А реализовано это, как я говорил, как разные процессы: мухи отдельно, а котлеты - отдельно процесс LLD занимается созданием нужных объектов в базе данных (айтемы/триггеры/графики/и т.п.), а сборщики данных разных видов собирают для существующих айтемов данные (для последующего анализа и сохранения в истории). И процесс LLD - таки да, "дорогостоящий" (по сравнению с другими процессами), и запускать его с интервалом 10 секунд нерационально. При небольшой нагрузке это не будет заметно, но зачем? Задача LLD - создавать нужные объекты в конфигурации, а список таких объектов меняется относительно редко (уж никак не каждые 10 секунд).
              3. Внутри правила создать прототип элементов данных типа "вычисляемый элемент".
              Почему "вычисляемый"? Я бы делал зависимый (от того же исходного элемента данных), с выдёргиванием нужного куска JSON-а через препроцессинг.
              Что касается именования, то есть полезный ресурс тут: насколько я помню, хорошим тоном считается ключ для мастер-айтема заканчивать суффиксом ".get", а ключ для правила LLD - суффиксом ".discovery".

              Comment

              • Nick2018
                Junior Member
                • Oct 2018
                • 7

                #9
                Задача решена
                "
                Вот, придумал задачу для JavaScript.

                В наборе данных по вентиляторам есть поле "единицы измерения". Я сделал для него такой LLD макрос в правиле обнаружения

                {#UNITS} = $.ReadingUnits

                возможны два значения - "RPM" и "Percent".

                нужно на лету заменять значения этого поля на "rpm" и "%" , соответственно​, чтобы подставить в прототип данных.

                Т.е. выполнять проверку и замену, что-то типа такого, синтаксис возможно ошибочный.

                return ($.ReadingUnits == "RPM")? "rpm" : $.ReadingUnits == "Percent")? "%" : $.ReadingUnits;

                Как это реализовать и куда подставить?
                "
                Задача решена путем "Замены" в предобработке основного элемента данных, там есть готовое решение.
                Last edited by Nick2018; 08-09-2022, 16:06.

                Comment

                Working...