Enviar dados para o Zabbix server ou proxy

zabbix_utils permite enviar valores de item para um item trapper no Zabbix server ou proxy (de forma semelhante ao Zabbix sender).

Você pode enviar um único valor, múltiplos valores ou até mesmo direcionar múltiplos clusters Zabbix.

Os dados podem ser enviados em modo síncrono ou assíncrono:

  • No modo síncrono, seu script Python envia valores e aguarda uma resposta antes de continuar; isso é adequado para operações simples, sequenciais e previsíveis.
  • No modo assíncrono, o script envia valores sem esperar por cada resposta, permitindo que outras operações prossigam em paralelo; isso é mais eficiente para solicitações lentas ou grandes lotes de dados.

Os exemplos nesta página focam no modo síncrono, embora o modo assíncrono siga padrões semelhantes. Exemplos adicionais estão disponíveis no repositório GitHub do zabbix_utils.

Importação

Para usar o zabbix_utils para enviar valores de item, importe a classe Sender em seu script:

from zabbix_utils import Sender

Para enviar vários valores, você também pode importar a classe ItemValue:

from zabbix_utils import Sender, ItemValue

Enviar valor único

Para enviar o valor de um item:

  1. Crie uma instância de Sender, especificando o endereço IP e a porta do seu Zabbix server ou proxy.
  2. Chame o método send_value() na instância de Sender usando o seguinte formato:
sender_instance.send_value('host', 'item.key', 'value', optional_timestamp, optional_nanoseconds)

Por exemplo, para enviar 1 para o item trapper service.status no host Linux server:

sender = Sender(server='127.0.0.1', port=10051)
response = sender.send_value('Linux server', 'service.status', 1)
Usando IP não padrão

Se o servidor que executa seu script tiver vários endereços IP, você pode especificar um source_ip para o Sender usar ao enviar valores para o server ou proxy do Zabbix:

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

Você pode definir um timeout de resposta para o Sender para controlar quanto tempo seu script deve aguardar por uma resposta do Zabbix server ou proxy antes de desistir:

sender = Sender(
    server='127.0.0.1',
    port=10051,
    timeout=30
)
Usando o arquivo de configuração do agent

Você pode permitir que o zabbix_utils leia os parâmetros Server ou ServerActive de um arquivo de configuração local do Zabbix agent ou agent 2. Nesses casos, você não precisa especificar os parâmetros de conexão ao criar uma instância do Sender:

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

Se ServerActive contiver um ou mais clusters Zabbix com múltiplas instâncias de server, o Sender enviará dados para o primeiro server disponível em cada cluster. Se ServerActive não estiver definido, o endereço de Server com a porta padrão (10051) será usado.

Usando criptografia

O Sender não inclui suporte de criptografia embutido, mas você pode fornecê-lo criando um wrapper usando bibliotecas de terceiros:

def psk_wrapper(sock, tls):
    # ...
    # Implementação do wrapper TLS PSK para o socket
    # ...

sender = Sender(
    server='127.0.0.1',
    port=10051,
    socket_wrapper=psk_wrapper
)
Resposta para valor único

A resposta retornada pelo Zabbix server ou proxy é processada pela biblioteca e retornada como um 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 assíncrono

O modo assíncrono permite que seu script Python envie valores sem esperar por uma resposta do Zabbix server ou proxy. Isso pode tornar seu script mais eficiente quando ele precisa enviar muitos valores ou quando alguns valores demoram muito para serem enviados.

Ao usar o modo assíncrono, há algumas diferenças importantes em relação ao modo síncrono:

  • Importe o módulo asyncio do Python (você deve primeiro instalar as dependências necessárias).
  • Importe AsyncSender em vez de Sender.
  • Escreva seu código dentro de uma função async.
  • Use await ao chamar o método send_value().

Por exemplo, para enviar um único valor usando o modo assíncrono:

# 1. Importe asyncio para o modo assíncrono e AsyncSender de zabbix_utils:
import asyncio
from zabbix_utils import AsyncSender

# 2. Defina a função principal async onde todas as operações de envio de dados (devem aguardar) serão executadas:
async def main():
    sender = AsyncSender(server='127.0.0.1', port=10051)
    response = await sender.send_value('Linux server', 'service.status', 1)

    # 3. Imprima a resposta retornada pelo Zabbix server ou proxy:
    print(response)

# 4. Execute a função async main() usando o event loop do asyncio:
asyncio.run(main())

Enviar múltiplos valores

Para enviar múltiplos valores:

  1. Prepare um array de objetos ItemValue, cada um usando o mesmo formato do método send_value().
  2. Crie uma instância de Sender, especificando o endereço IP e a porta do seu Zabbix server ou proxy.
  3. Chame o método send() (em vez de send_value()) na instância de Sender, especificando o array de objetos com os valores a serem enviados.

Por exemplo, para enviar cinco valores para 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)
Usando tamanho de bloco personalizado

Se você precisar enviar mais valores do que um item trapper pode aceitar em uma única solicitação, poderá dividi-los em blocos.

Por padrão, o tamanho do bloco é de 250 valores. Você pode alterá-lo definindo o parâmetro chunk_size ao criar uma instância de Sender.

Por exemplo, para enviar cinco valores em três blocos (2-2-1), defina o parâmetro chunk_size como 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 para vários clusters Zabbix

Para enviar valores para vários clusters Zabbix:

  1. Prepare um array de clusters Zabbix. Se um cluster tiver vários nós, o valor será enviado para o primeiro nó disponível de cada cluster.
  2. Crie um Sender, especificando seu array de clusters Zabbix.
  3. Chame o método send_value() na instância do Sender usando o mesmo formato do método send_value().

Por exemplo, para enviar um valor para o primeiro nó disponível em cada cluster:

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)
Resposta para múltiplos valores

Por padrão, o Sender retorna um resultado agregado do envio de valores em todos os hosts ou clusters:

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

Se você precisar de informações mais detalhadas, pode inspecionar os resultados de cada cluster e de cada chunk usando o atributo response.details:

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

if response.failed == 0:
    print(f"Value sent successfully in {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"Processed {resp.processed} of {resp.total} at {node.address}:{node.port}")
            # Processed 1 of 1 at 127.0.0.1:10051
            # Processed 1 of 1 at zabbix.example.local:10051