Wysyłanie danych do serwera lub proxy Zabbix

zabbix_utils umożliwia wysyłanie wartości pozycji do pozycji trapper na serwer lub proxy Zabbix (podobnie jak Zabbix sender).

Możesz wysłać pojedynczą wartość, wiele wartości, a nawet kierować dane do wielu klastrów Zabbix.

Dane mogą być wysyłane w trybie synchronicznym lub asynchronicznym:

  • W trybie synchronicznym skrypt Python wysyła wartości i czeka na odpowiedź przed kontynuowaniem działania; jest to odpowiednie rozwiązanie dla prostych, sekwencyjnych i przewidywalnych operacji.
  • W trybie asynchronicznym skrypt wysyła wartości bez oczekiwania na każdą odpowiedź, umożliwiając równoległe wykonywanie innych operacji; jest to bardziej efektywne w przypadku wolnych żądań lub dużych partii danych.

Przykłady na tej stronie koncentrują się na trybie synchronicznym, chociaż tryb asynchroniczny opiera się na podobnych wzorcach. Dodatkowe przykłady są dostępne w repozytorium GitHub zabbix_utils.

Import

Aby używać zabbix_utils do wysyłania wartości pozycji, zaimportuj klasę Sender w swoim skrypcie:

from zabbix_utils import Sender

Aby wysyłać wiele wartości, możesz również zaimportować klasę ItemValue:

from zabbix_utils import Sender, ItemValue

Wyślij pojedynczą wartość

Aby wysłać wartość pozycji:

  1. Utwórz instancję Sender, podając adres IP i port swojego serwera lub proxy Zabbix.
  2. Wywołaj metodę send_value() na instancji Sender, używając następującego formatu:
sender_instance.send_value('host', 'item.key', 'value', optional_timestamp, optional_nanoseconds)

Na przykład, aby wysłać 1 do pozycji trapper service.status na hoście Linux server:

sender = Sender(server='127.0.0.1', port=10051)
response = sender.send_value('Linux server', 'service.status', 1)
Używanie niestandardowego adresu IP

Jeśli serwer uruchamiający skrypt ma wiele adresów IP, możesz określić source_ip, którego Sender ma używać podczas wysyłania wartości do serwera Zabbix lub proxy:

sender = Sender(
    server='127.0.0.1',
    port=10051,
    source_ip='10.10.7.1'
)
Używanie timeout

Możesz ustawić timeout odpowiedzi dla Sender, aby kontrolować, jak długo skrypt powinien czekać na odpowiedź z serwera Zabbix lub proxy, zanim zrezygnuje:

sender = Sender(
    server='127.0.0.1',
    port=10051,
    timeout=30
)
Używanie pliku konfiguracyjnego agent

Możesz pozwolić, aby zabbix_utils odczytywał parametry Server lub ServerActive z lokalnego pliku konfiguracyjnego Zabbix agent lub agent 2. W takich przypadkach nie musisz określać parametrów połączenia podczas tworzenia instancji Sender:

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

Jeśli ServerActive zawiera jeden lub więcej klastrów Zabbix z wieloma instancjami serwer, Sender wysyła dane do pierwszego dostępnego serwer w każdym klastrze. Jeśli ServerActive nie jest ustawiony, używany jest adres z Server z domyślnym portem (10051).

Używanie szyfrowania

Sender nie zawiera wbudowanej obsługi szyfrowania, ale można ją zapewnić, tworząc opakowanie przy użyciu bibliotek zewnętrznych:

def psk_wrapper(sock, tls):
    # ...
    # Implementacja opakowania TLS PSK dla gniazda
    # ...

sender = Sender(
    server='127.0.0.1',
    port=10051,
    socket_wrapper=psk_wrapper
)
Odpowiedź dla pojedynczej wartości

Odpowiedź zwrócona przez serwer Zabbix lub proxy jest przetwarzana przez bibliotekę i zwracana jako obiekt 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
Tryb asynchroniczny

Tryb asynchroniczny pozwala skryptowi Python wysyłać wartości bez oczekiwania na odpowiedź z serwera Zabbix lub proxy. Może to zwiększyć wydajność skryptu, gdy musi on wysłać wiele wartości albo gdy wysłanie niektórych wartości zajmuje dużo czasu.

Podczas korzystania z trybu asynchronicznego występuje kilka istotnych różnic w porównaniu z trybem synchronicznym:

  • Zaimportuj moduł asyncio języka Python (najpierw należy zainstalować wymagane zależności).
  • Zaimportuj AsyncSender zamiast Sender.
  • Napisz kod wewnątrz funkcji async.
  • Użyj await podczas wywoływania metody send_value().

Na przykład, aby wysłać pojedynczą wartość w trybie asynchronicznym:

# 1. Import asyncio for asynchronous mode, and AsyncSender from zabbix_utils:
import asyncio
from zabbix_utils import AsyncSender

# 2. Define the main async function where all data sending operations (must await) will be executed:
async def main():
    sender = AsyncSender(server='127.0.0.1', port=10051)
    response = await sender.send_value('Linux server', 'service.status', 1)

    # 3. Print the response returned by Zabbix server or proxy:
    print(response)

# 4. Run the async main() function using asyncio's event loop:
asyncio.run(main())

Wysyłanie wielu wartości

Aby wysłać wiele wartości:

  1. Przygotuj tablicę obiektów ItemValue, z których każdy używa tego samego formatu co metoda send_value().
  2. Utwórz instancję Sender, podając adres IP i port serwera lub proxy Zabbix.
  3. Wywołaj metodę send() (zamiast send_value()) na instancji Sender, przekazując tablicę obiektów z wartościami do wysłania.

Na przykład, aby wysłać pięć wartości do różnych hostów:

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)
Używanie niestandardowego rozmiaru fragmentu

Jeśli chcesz wysłać więcej wartości, niż pozycja trapper może przyjąć w pojedynczym żądaniu, możesz podzielić je na fragmenty.

Domyślnie rozmiar fragmentu wynosi 250 wartości. Możesz go zmienić, ustawiając parametr chunk_size podczas tworzenia instancji Sender.

Na przykład, aby wysłać pięć wartości w trzech fragmentach (2-2-1), ustaw parametr chunk_size na 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)
Wysyłanie wartości do wielu klastrów Zabbix

Aby wysyłać wartości do wielu klastrów Zabbix:

  1. Przygotuj tablicę klastrów Zabbix. Jeśli klaster ma wiele węzłów, wartość zostanie wysłana do pierwszego dostępnego węzła każdego klastra.
  2. Utwórz obiekt Sender, podając tablicę klastrów Zabbix.
  3. Wywołaj metodę send_value() na instancji Sender, używając tego samego formatu jak w metodzie send_value().

Na przykład, aby wysłać wartość do pierwszego dostępnego węzła w każdym klastrze:

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)
Odpowiedź dla wielu wartości

Domyślnie Sender zwraca zagregowany wynik wysyłania wartości dla wszystkich hostów lub klastrów:

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

Jeśli potrzebujesz bardziej szczegółowych informacji, możesz sprawdzić wyniki dla każdego klastra i każdego fragmentu za pomocą atrybutu response.details:

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

if response.failed == 0:
    print(f"Wartość została wysłana pomyślnie w {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"Przetworzono {resp.processed} z {resp.total} na {node.address}:{node.port}")
            # Przetworzono 1 z 1 na 127.0.0.1:10051
            # Przetworzono 1 z 1 na zabbix.example.local:10051