Enviar datos al servidor o proxy de Zabbix

zabbix_utils le permite enviar valores de item a un item trapper en el servidor o proxy de Zabbix (de manera similar a Zabbix sender).

Puede enviar un solo valor, múltiples valores o incluso dirigirse a varios clústeres de Zabbix.

Los datos se pueden enviar en modo síncrono o asíncrono:

  • En modo síncrono, su script de Python envía valores y espera una respuesta antes de continuar; esto es adecuado para operaciones simples, secuenciales y predecibles.
  • En modo asíncrono, el script envía valores sin esperar cada respuesta, permitiendo que otras operaciones continúen en paralelo; esto es más eficiente para solicitudes lentas o grandes lotes de datos.

Los ejemplos de esta página se centran en el modo síncrono, aunque el modo asíncrono sigue patrones similares. Hay ejemplos adicionales disponibles en el repositorio de GitHub zabbix_utils.

Importar

Para utilizar zabbix_utils para enviar valores de item, importe la clase Sender en su script:

from zabbix_utils import Sender

Para enviar múltiples valores, también puede importar la clase ItemValue:

from zabbix_utils import Sender, ItemValue

Enviar un único valor

Para enviar el valor de un item:

  1. Cree una instancia de Sender, especificando la dirección IP y el puerto de su Zabbix server o proxy.
  2. Llame al método send_value() en la instancia de Sender utilizando el siguiente formato:
sender_instance.send_value('host', 'item.key', 'value', optional_timestamp, optional_nanoseconds)

Por ejemplo, para enviar 1 al item trapper service.status en el host Linux server:

sender = Sender(server='127.0.0.1', port=10051)
response = sender.send_value('Linux server', 'service.status', 1)
Usando una IP no predeterminada

Si el servidor que ejecuta su script tiene varias direcciones IP, puede especificar una source_ip para que el Sender la utilice al enviar valores al servidor o proxy de Zabbix:

sender = Sender(
    server='127.0.0.1',
    port=10051,
    source_ip='10.10.7.1'
)
Usando timeout

Puede establecer un timeout de respuesta para el Sender para controlar cuánto tiempo debe esperar su script una respuesta del server o proxy de Zabbix antes de rendirse:

sender = Sender(
    server='127.0.0.1',
    port=10051,
    timeout=30
)
Usar el archivo de configuración del agent

Puede hacer que zabbix_utils lea los parámetros Server o ServerActive de un archivo de configuración local de Zabbix agent o agent 2. En tales casos, no es necesario especificar los parámetros de conexión al crear una instancia de Sender:

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

Si ServerActive contiene uno o más clústeres de Zabbix con varias instancias de server, Sender envía datos al primer server disponible en cada clúster. Si ServerActive no está configurado, se utiliza la dirección de Server con el puerto por defecto (10051).

Usando cifrado

El Sender no incluye soporte de cifrado incorporado, pero puede proporcionarlo creando un wrapper usando librerías de terceros:

def psk_wrapper(sock, tls):
    # ...
    # Implementación del wrapper TLS PSK para el socket
    # ...

sender = Sender(
    server='127.0.0.1',
    port=10051,
    socket_wrapper=psk_wrapper
)
Respuesta para un solo valor

La respuesta devuelta por el server o proxy de Zabbix es procesada por la biblioteca y devuelta como un objeto 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
Modo asíncrono

El modo asíncrono permite que tu script de Python envíe valores sin esperar una respuesta del servidor o proxy de Zabbix. Esto puede hacer que tu script sea más eficiente cuando necesita enviar muchos valores o cuando algunos valores tardan mucho en enviarse.

Al utilizar el modo asíncrono, hay algunas diferencias importantes en comparación con el modo síncrono:

  • Importa el módulo asyncio de Python (primero debes instalar las dependencias requeridas).
  • Importa AsyncSender en lugar de Sender.
  • Escribe tu código dentro de una función async.
  • Usa await al llamar al método send_value().

Por ejemplo, para enviar un solo valor utilizando el modo asíncrono:

# 1. Importa asyncio para el modo asíncrono y AsyncSender desde zabbix_utils:
import asyncio
from zabbix_utils import AsyncSender

# 2. Define la función principal async donde se ejecutarán todas las operaciones de envío de datos (deben usar await):
async def main():
    sender = AsyncSender(server='127.0.0.1', port=10051)
    response = await sender.send_value('Linux server', 'service.status', 1)

    # 3. Imprime la respuesta devuelta por el servidor o proxy de Zabbix:
    print(response)

# 4. Ejecuta la función async main() usando el bucle de eventos de asyncio:
asyncio.run(main())

Enviar múltiples valores

Para enviar múltiples valores:

  1. Prepare un array de objetos ItemValue, cada uno usando el mismo formato que el método send_value().
  2. Cree una instancia de Sender, especificando la dirección IP y el puerto de su Zabbix server o proxy.
  3. Llame al método send() (en lugar de send_value()) en la instancia de Sender, especificando el array de objetos con los valores a enviar.

Por ejemplo, para enviar cinco valores a diferentes hosts:

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)
Usar un tamaño de fragmento personalizado

Si necesita enviar más valores de los que un item trapper puede aceptar en una sola solicitud, puede dividirlos en fragmentos.

Por defecto, el tamaño del fragmento es de 250 valores. Puede cambiarlo estableciendo el parámetro chunk_size al crear una instancia de Sender.

Por ejemplo, para enviar cinco valores en tres fragmentos (2-2-1), establezca el parámetro chunk_size en 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)
Enviar valores a varios clústeres de Zabbix

Para enviar valores a varios clústeres de Zabbix:

  1. Prepare una matriz de clústeres de Zabbix. Si un clúster tiene varios nodos, el valor se enviará al primer nodo disponible de cada clúster.
  2. Cree un Sender, especificando su matriz de clústeres de Zabbix.
  3. Llame al método send_value() en la instancia de Sender utilizando el mismo formato que el método send_value().

Por ejemplo, para enviar un valor al primer nodo disponible en cada clúster:

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)
Respuesta para múltiples valores

Por defecto, Sender devuelve un resultado agregado del envío de valores a todos los hosts o clústeres:

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

Si necesita información más detallada, puede inspeccionar los resultados para cada clúster y cada fragmento utilizando el atributo response.details:

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

if response.failed == 0:
    print(f"Valor enviado correctamente en {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"Procesados {resp.processed} de {resp.total} en {node.address}:{node.port}")
            # Procesados 1 de 1 en 127.0.0.1:10051
            # Procesados 1 de 1 en zabbix.example.local:10051