Отправка данных на сервер Zabbix или прокси

zabbix_utils позволяет отправлять значения элементов данных на элемент данных типа trapper на сервер Zabbix или прокси (аналогично Zabbix sender).

Вы можете отправить одно значение, несколько значений или даже на несколько кластеров Zabbix.

Данные можно отправлять в синхронном или асинхронном режиме:

  • В синхронном режиме ваш скрипт Python отправляет значения и ожидает ответа перед продолжением; это подходит для простых, последовательных и предсказуемых операций.
  • В асинхронном режиме скрипт отправляет значения, не ожидая каждого ответа, что позволяет выполнять другие операции параллельно; это эффективнее для медленных запросов или больших пакетов данных.

Примеры на этой странице сосредоточены на синхронном режиме, хотя асинхронный режим использует похожие подходы. Дополнительные примеры доступны в репозитории GitHub zabbix_utils.

Импорт

Чтобы использовать zabbix_utils для отправки значений элементов данных, импортируйте класс Sender в свой скрипт:

from zabbix_utils import Sender

Для отправки нескольких значений вы также можете импортировать класс ItemValue:

from zabbix_utils import Sender, ItemValue

Отправка одного значения

Чтобы отправить значение элемента данных:

  1. Создайте экземпляр Sender, указав IP-адрес и порт вашего сервера Zabbix или прокси.
  2. Вызовите метод send_value() у экземпляра Sender в следующем формате:
sender_instance.send_value('host', 'item.key', 'value', optional_timestamp, optional_nanoseconds)

Например, чтобы отправить 1 в элемент данных trapper service.status на узел сети Linux server:

sender = Sender(server='127.0.0.1', port=10051)
response = sender.send_value('Linux server', 'service.status', 1)
Использование IP-адреса, отличного от значения по умолчанию

Если сервер, на котором выполняется ваш скрипт, имеет несколько IP-адресов, вы можете указать source_ip, который Sender будет использовать при отправке значений на сервер Zabbix или прокси:

sender = Sender(
    server='127.0.0.1',
    port=10051,
    source_ip='10.10.7.1'
)
Использование тайм-аута

Вы можете задать timeout ответа для Sender, чтобы контролировать, как долго ваш скрипт должен ждать ответа от сервера Zabbix или прокси, прежде чем прекратить ожидание:

sender = Sender(
    server='127.0.0.1',
    port=10051,
    timeout=30
)
Использование файла конфигурации агента

Вы можете разрешить zabbix_utils считывать параметры Server или ServerActive из локального файла конфигурации Zabbix агента или агента 2. В таких случаях при создании экземпляра Sender не нужно указывать параметры подключения:

sender = Sender(
    use_config=True,
    config_path='/etc/zabbix/zabbix_agent2.conf'
)

Если ServerActive содержит один или несколько кластеров Zabbix с несколькими экземплярами сервера, Sender отправляет данные на первый доступный сервер в каждом кластере. Если ServerActive не задан, используется адрес из Server с портом по умолчанию (10051).

Использование шифрования

Sender не включает встроенную поддержку шифрования, но вы можете добавить ее, создав обертку с использованием сторонних библиотек:

def psk_wrapper(sock, tls):
    # ...
    # Реализация обертки TLS PSK для сокета
    # ...

sender = Sender(
    server='127.0.0.1',
    port=10051,
    socket_wrapper=psk_wrapper
)
Ответ для одного значения

Ответ, возвращаемый сервером или прокси Zabbix, обрабатывается библиотекой и возвращается как объект TrapperResponse:

print(response)
# {"processed": 1, "failed": 0, "total": 1, "time": "0.000123", "chunk": 1}

print(response.processed)
# 1

print(response.failed)
# 0

print(response.total)
# 1
Асинхронный режим

Асинхронный режим позволяет вашему Python-скрипту отправлять значения, не дожидаясь ответа от сервера Zabbix или прокси. Это может сделать ваш скрипт более эффективным, когда ему нужно отправлять много значений или когда на отправку некоторых значений уходит много времени.

При использовании асинхронного режима есть несколько важных отличий по сравнению с синхронным режимом:

  • Импортируйте модуль Python asyncio (сначала необходимо установить требуемые зависимости).
  • Импортируйте AsyncSender вместо Sender.
  • Пишите код внутри асинхронной функции async.
  • Используйте await при вызове метода send_value().

Например, чтобы отправить одно значение с использованием асинхронного режима:

# 1. Импортируйте asyncio для асинхронного режима и AsyncSender из zabbix_utils:
import asyncio
from zabbix_utils import AsyncSender

# 2. Определите основную асинхронную функцию, в которой будут выполняться все операции отправки данных (для них нужно использовать await):
async def main():
    sender = AsyncSender(server='127.0.0.1', port=10051)
    response = await sender.send_value('Linux server', 'service.status', 1)

    # 3. Выведите ответ, возвращенный сервером Zabbix или прокси:
    print(response)

# 4. Запустите асинхронную функцию main() с помощью цикла событий asyncio:
asyncio.run(main())

Отправка нескольких значений

Чтобы отправить несколько значений:

  1. Подготовьте массив объектов ItemValue, каждый из которых использует тот же формат, что и метод send_value().
  2. Создайте экземпляр Sender, указав IP-адрес и порт вашего сервера Zabbix или прокси.
  3. Вызовите метод send() (вместо send_value()) для экземпляра Sender, указав массив объектов со значениями для отправки.

Например, чтобы отправить пять значений на разные узлы сети:

items = [
    ItemValue('server-de', 'service.status', 'up', 1770887205, 100),
    ItemValue('server-fr', 'service.status', 'up', 1770887205, 100),
    ItemValue('server-uk', 'service.status', 'up', 1770887205, 100),
    ItemValue('server-nl', 'service.status', 'up', 1770887205, 100),
    ItemValue('server-pl', 'service.status', 'up', 1770887205, 100),
]

sender = Sender(server='127.0.0.1', port=10051)
response = sender.send(items)
Использование пользовательского размера фрагмента

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

По умолчанию размер фрагмента составляет 250 значений. Вы можете изменить его, задав параметр chunk_size при создании экземпляра Sender.

Например, чтобы отправить пять значений в трех фрагментах (2-2-1), задайте параметр chunk_size равным 2:

items = [
    ItemValue('server-de', 'service.status', 'up'),
    ItemValue('server-fr', 'service.status', 'up'),
    ItemValue('server-uk', 'service.status', 'up'),
    ItemValue('server-nl', 'service.status', 'up'),
    ItemValue('server-pl', 'service.status', 'up'),
]

sender = Sender(server='127.0.0.1', port=10051, chunk_size=2)
response = sender.send(items)
Отправка значений в несколько кластеров Zabbix

Чтобы отправить значения в несколько кластеров Zabbix:

  1. Подготовьте массив кластеров Zabbix. Если у кластера несколько узлов, значение будет отправлено на первый доступный узел каждого кластера.
  2. Создайте Sender, указав ваш массив кластеров Zabbix.
  3. Вызовите метод send_value() для экземпляра Sender, используя тот же формат, что и метод send_value().

Например, чтобы отправить значение на первый доступный узел в каждом кластере:

zabbix_clusters = [
    ['zabbix.cluster1.node1', 'zabbix.cluster1.node2:10051'],
    ['zabbix.cluster2.node1:10051', 'zabbix.cluster2.node2', 'zabbix.cluster2.node3']
]

sender = Sender(clusters=zabbix_clusters)
response = sender.send_value('Linux server', 'service.status', 1)
Ответ для нескольких значений

По умолчанию Sender возвращает агрегированный результат отправки значений по всем узлам сети или кластерам:

print(response)
# {"processed": 2, "failed": 0, "total": 2, "time": "0.000108", "chunk": 2}

Если вам нужна более подробная информация, вы можете просмотреть результаты для каждого кластера и каждого фрагмента с помощью атрибута response.details:

print(response)
# {"processed": 2, "failed": 0, "total": 2, "time": "0.000108", "chunk": 2}

if response.failed == 0:
    print(f"Значение успешно отправлено за {response.time}")
else:
    print(response.details)
    # {
    #     127.0.0.1:10051: [
    #         {
    #             "processed": 1,
    #             "failed": 0,
    #             "total": 1,
    #             "time": "0.000051",
    #             "chunk": 1
    #         }
    #     ],
    #     zabbix.example.local:10051: [
    #         {
    #             "processed": 1,
    #             "failed": 0,
    #             "total": 1,
    #             "time": "0.000057",
    #             "chunk": 1
    #         }
    #     ]
    # }

    for node, chunks in response.details.items():
        for resp in chunks:
            print(f"Обработано {resp.processed} из {resp.total} на {node.address}:{node.port}")
            # Обработано 1 из 1 на 127.0.0.1:10051
            # Обработано 1 из 1 на zabbix.example.local:10051