Moduły ładowalne
Przegląd
Moduły ładowalne oferują rozwiązanie nastawione na wydajność do rozszerzania funkcjonalności Zabbix.
Funkcjonalność Zabbix można rozszerzać na wiele sposobów, na przykład za pomocą parametrów użytkownika, kontroli zewnętrznych oraz pozycji agenta Zabbix system.run. Rozwiązania te działają bardzo dobrze, ale mają jedną istotną wadę, a mianowicie fork(). Zabbix musi utworzyć nowy proces za każdym razem, gdy obsługuje metrykę użytkownika, co nie jest korzystne dla wydajności. Zwykle nie stanowi to dużego problemu, jednak może być poważnym wyzwaniem podczas monitorowania systemów wbudowanych, przy dużej liczbie monitorowanych parametrów lub w przypadku ciężkich skryptów ze złożoną logiką albo długim czasem uruchamiania.
Obsługa modułów ładowalnych umożliwia rozszerzanie agenta Zabbix, serwera i proxy bez poświęcania wydajności.
Moduł ładowalny jest zasadniczo biblioteką współdzieloną używaną przez demon Zabbix i ładowaną podczas uruchamiania. Biblioteka powinna zawierać określone funkcje, aby proces Zabbix mógł wykryć, że plik jest rzeczywiście modułem, który można załadować i z którym można pracować.
Moduły ładowalne mają szereg zalet. Bardzo ważne są wysoka wydajność i możliwość implementacji dowolnej logiki, ale być może najważniejszą zaletą jest możliwość tworzenia, używania i udostępniania modułów Zabbix. Przyczynia się to do bezproblemowego utrzymania i ułatwia dostarczanie nowej funkcjonalności w prostszy sposób oraz niezależnie od bazowego kodu Zabbix.
Licencjonowanie modułów i ich dystrybucja w postaci binarnej są regulowane licencją AGPL-3.0 (moduły są linkowane z Zabbix w czasie działania i używają nagłówków Zabbix; cały kod Zabbix jest objęty licencją AGPL-3.0 od wersji Zabbix 7.0). Zabbix nie gwarantuje zgodności binarnej.
Stabilność API modułów jest gwarantowana w trakcie jednego cyklu wydania Zabbix LTS (Long Term Support). Stabilność API Zabbix nie jest gwarantowana (technicznie możliwe jest wywoływanie wewnętrznych funkcji Zabbix z modułu, ale nie ma gwarancji, że takie moduły będą działać).
API modułu
Aby biblioteka współdzielona była traktowana jako moduł Zabbix, powinna implementować i eksportować kilka funkcji. Obecnie w API modułu Zabbix istnieje sześć funkcji, z których tylko jedna jest obowiązkowa, a pozostałe pięć jest opcjonalnych.
Obowiązkowy interfejs
Jedyną obowiązkową funkcją jest zbx_module_api_version():
int zbx_module_api_version(void);
Funkcja ta powinna zwracać wersję API zaimplementowaną przez ten moduł, a aby moduł mógł zostać załadowany, wersja ta musi być zgodna z wersją API modułu obsługiwaną przez Zabbix.
Wersja API modułu obsługiwana przez Zabbix to ZBX_MODULE_API_VERSION.
Dlatego ta funkcja powinna zwracać tę stałą.
Stara stała ZBX_MODULE_API_VERSION_ONE, używana w tym celu, jest obecnie zdefiniowana jako równa ZBX_MODULE_API_VERSION, aby zachować zgodność źródłową, jednak jej użycie nie jest zalecane.
Opcjonalny interfejs
Opcjonalne funkcje to zbx_module_init(), zbx_module_item_list(), zbx_module_item_timeout(), zbx_module_history_write_cbs() oraz zbx_module_uninit():
int zbx_module_init(void);
Ta funkcja powinna wykonać niezbędną inicjalizację modułu (jeśli jest wymagana).
W przypadku powodzenia powinna zwrócić ZBX_MODULE_OK.
W przeciwnym razie powinna zwrócić ZBX_MODULE_FAIL.
W tym drugim przypadku Zabbix nie zostanie uruchomiony.
ZBX_METRIC *zbx_module_item_list(void);
Ta funkcja powinna zwrócić listę pozycji obsługiwanych przez moduł.
Każda pozycja jest zdefiniowana w strukturze ZBX_METRIC; szczegóły znajdują się w sekcji poniżej.
Lista jest zakończona strukturą ZBX_METRIC z polem "key" ustawionym na NULL.
void zbx_module_item_timeout(int timeout);
Jeśli moduł eksportuje zbx_module_item_list(), to funkcja ta jest używana przez Zabbix do określenia ustawień limitu czasu w pliku konfiguracyjnym Zabbix, których powinny przestrzegać sprawdzenia pozycji zaimplementowane przez moduł.
Tutaj parametr "timeout" jest podawany w sekundach.
ZBX_HISTORY_WRITE_CBS zbx_module_history_write_cbs(void);
Ta funkcja powinna zwracać funkcje wywołania zwrotnego, których serwer Zabbix użyje do eksportu historii różnych typów danych.
Funkcje wywołania zwrotnego są udostępniane jako pola struktury ZBX_HISTORY_WRITE_CBS; pola mogą mieć wartość NULL, jeśli moduł nie jest zainteresowany historią określonego typu.
int zbx_module_uninit(void);
Ta funkcja powinna wykonać niezbędną deinicjalizację (jeśli jest wymagana), taką jak zwolnienie zaalokowanych zasobów, zamknięcie deskryptorów plików itp.
Wszystkie funkcje są wywoływane raz podczas uruchamiania Zabbix, gdy moduł jest ładowany, z wyjątkiem zbx_module_uninit(), która jest wywoływana raz podczas zamykania Zabbix, gdy moduł jest rozładowywany.
Definiowanie pozycji
Każda pozycja jest definiowana w strukturze ZBX_METRIC:
typedef struct
{
char *key;
unsigned flags;
int (*function)();
char *test_param;
}
ZBX_METRIC;
Tutaj key to klucz pozycji (np. "dummy.random"), flags to albo CF_HAVEPARAMS, albo 0 (w zależności od tego, czy pozycja przyjmuje parametry, czy nie), function to funkcja C implementująca pozycję (np. zbx_module_dummy_random), a test_param to lista parametrów używana, gdy agent Zabbix jest uruchamiany z flagą -p (np. "1,1000", może mieć wartość NULL).
Przykładowa definicja może wyglądać tak:
static ZBX_METRIC keys[] =
{
{ "dummy.random", CF_HAVEPARAMS, zbx_module_dummy_random, "1,1000" },
{ NULL }
}
Każda funkcja implementująca pozycję powinna przyjmować dwa parametry wskaźnikowe: pierwszy typu AGENT_REQUEST, a drugi typu AGENT_RESULT:
int zbx_module_dummy_random(AGENT_REQUEST *request, AGENT_RESULT *result)
{
...
SET_UI64_RESULT(result, from + rand() % (to - from + 1));
return SYSINFO_RET_OK;
}
Funkcje te powinny zwracać SYSINFO_RET_OK, jeśli wartość pozycji została pomyślnie pobrana.
W przeciwnym razie powinny zwracać SYSINFO_RET_FAIL.
Zobacz przykład modułu "dummy" poniżej, aby uzyskać szczegółowe informacje o tym, jak pobierać informacje z AGENT_REQUEST i jak ustawiać informacje w AGENT_RESULT.
Udostępnianie callbacków eksportu historii
Eksport historii za pomocą modułu nie jest już obsługiwany przez proxy Zabbix.
Moduł może określać funkcje do eksportu danych historii według typu: Numeric (float), Numeric (unsigned), Character, Text i Log:
typedef struct
{
void (*history_float_cb)(const ZBX_HISTORY_FLOAT *history, int history_num);
void (*history_integer_cb)(const ZBX_HISTORY_INTEGER *history, int history_num);
void (*history_string_cb)(const ZBX_HISTORY_STRING *history, int history_num);
void (*history_text_cb)(const ZBX_HISTORY_TEXT *history, int history_num);
void (*history_log_cb)(const ZBX_HISTORY_LOG *history, int history_num);
}
ZBX_HISTORY_WRITE_CBS;
Każda z nich powinna przyjmować jako argumenty tablicę history o history_num elementach.
W zależności od typu danych historii przeznaczonych do eksportu, history jest odpowiednio tablicą następujących struktur:
typedef struct
{
zbx_uint64_t itemid;
int clock;
int ns;
double value;
}
ZBX_HISTORY_FLOAT;
typedef struct
{
zbx_uint64_t itemid;
int clock;
int ns;
zbx_uint64_t value;
}
ZBX_HISTORY_INTEGER;
typedef struct
{
zbx_uint64_t itemid;
int clock;
int ns;
const char *value;
}
ZBX_HISTORY_STRING;
typedef struct
{
zbx_uint64_t itemid;
int clock;
int ns;
const char *value;
}
ZBX_HISTORY_TEXT;
typedef struct
{
zbx_uint64_t itemid;
int clock;
int ns;
const char *value;
const char *source;
int timestamp;
int logeventid;
int severity;
}
ZBX_HISTORY_LOG;
Callbacki będą używane przez procesy synchronizacji historii serwera Zabbix na końcu procedury synchronizacji historii, po zapisaniu danych do bazy danych Zabbix i zapisaniu ich w pamięci podręcznej wartości.
W przypadku wewnętrznego błędu w module eksportu historii zaleca się, aby moduł był napisany w taki sposób, by nie blokował całego monitoringu do czasu odzyskania sprawności, lecz zamiast tego odrzucał dane i pozwalał serwerowi Zabbix kontynuować działanie.
Budowanie modułów
Moduły są obecnie przeznaczone do budowania wewnątrz drzewa źródeł Zabbix, ponieważ API modułu zależy od niektórych struktur danych zdefiniowanych w nagłówkach Zabbix.
Najważniejszym nagłówkiem dla modułów ładowanych dynamicznie jest include/module.h, który definiuje te struktury danych.
Inne niezbędne nagłówki systemowe, które pomagają include/module.h działać poprawnie, to stdlib.h i stdint.h.
Mając te informacje na uwadze, wszystko jest gotowe do zbudowania modułu.
Moduł powinien zawierać stdlib.h, stdint.h i module.h, a skrypt budowania powinien upewnić się, że pliki te znajdują się w ścieżce dołączania.
Szczegóły można znaleźć w przykładzie modułu "dummy" poniżej.
Innym przydatnym nagłówkiem jest include/zbxcommon.h, który definiuje funkcję zabbix_log(), mogącą służyć do logowania i debugowania.
Parametry konfiguracji
agent, serwer i proxy obsługują dwa parametry służące do pracy z modułami:
LoadModulePath- pełna ścieżka do lokalizacji modułów ładowanychLoadModule- moduł(y) do załadowania podczas uruchamiania. Moduły muszą znajdować się w katalogu określonym przezLoadModulePathalbo ścieżka musi poprzedzać nazwę modułu. Jeśli poprzedzająca ścieżka jest bezwzględna (zaczyna się od/),LoadModulePathjest ignorowany. Dozwolone jest podanie wielu parametrów LoadModule.
Na przykład, aby rozszerzyć agent Zabbix, można dodać następujące parametry:
LoadModulePath=/usr/local/lib/zabbix/agent/
LoadModule=mariadb.so
LoadModule=apache.so
LoadModule=kernel.so
LoadModule=/usr/local/lib/zabbix/dummy.so
Po uruchomieniu agenta załaduje on moduły mariadb.so, apache.so i kernel.so z katalogu /usr/local/lib/zabbix/agent, natomiast dummy.so zostanie załadowany z /usr/local/lib/zabbix.
Agent nie uruchomi się, jeśli moduł jest brakujący, w przypadku nieprawidłowych uprawnień lub gdy biblioteka współdzielona nie jest modułem Zabbix.
Konfiguracja frontend
Moduły ładowalne są obsługiwane przez agent, serwer i proxy Zabbix. Dlatego typ pozycji w frontend Zabbix zależy od tego, gdzie moduł jest załadowany. Jeśli moduł jest załadowany do agenta, typ pozycji powinien być "Zabbix agent" lub "Zabbix agent (active)". Jeśli moduł jest załadowany do serwera lub proxy, typ pozycji powinien być "Simple check".
Eksport historii za pomocą modułów Zabbix nie wymaga żadnej konfiguracji frontend.
Jeśli moduł zostanie pomyślnie załadowany przez serwer i udostępnia funkcję zbx_module_history_write_cbs(), która zwraca co najmniej jedną nie-NULL funkcję zwrotną, eksport historii zostanie włączony automatycznie.
Moduł dummy
Zabbix zawiera przykładowy moduł napisany w języku C.
Moduł znajduje się w katalogu src/modules/dummy:
alex@alex:~trunk/src/modules/dummy$ ls -l
-rw-rw-r-- 1 alex alex 9019 Apr 24 17:54 dummy.c
-rw-rw-r-- 1 alex alex 67 Apr 24 17:54 Makefile
-rw-rw-r-- 1 alex alex 245 Apr 24 17:54 README
Moduł jest dobrze udokumentowany i może służyć jako szablon dla własnych modułów.
Po wykonaniu ./configure w katalogu głównym drzewa źródłowego Zabbix, zgodnie z opisem powyżej, wystarczy uruchomić make, aby zbudować dummy.so.
/*
** Zabbix
** Copyright (C) 2001-2020 Zabbix SIA
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdint.h>
#include "module.h"
/* zmienna przechowuje ustawienie limitu czasu dla przetwarzania pozycji */
static int item_timeout = 0;
/* moduł POWINIEN definiować funkcje wewnętrzne jako static i używać wzorca nazewnictwa innego niż wewnętrzne */
/* symbole Zabbix (zbx_*) oraz funkcje API modułów ładowanych (zbx_module_*) w celu uniknięcia konfliktów */
static int dummy_ping(AGENT_REQUEST *request, AGENT_RESULT *result);
static int dummy_echo(AGENT_REQUEST *request, AGENT_RESULT *result);
static int dummy_random(AGENT_REQUEST *request, AGENT_RESULT *result);
static ZBX_METRIC keys[] =
/* KEY FLAG FUNCTION TEST PARAMETERS */
{
{"dummy.ping", 0, dummy_ping, NULL},
{"dummy.echo", CF_HAVEPARAMS, dummy_echo, "a message"},
{"dummy.random", CF_HAVEPARAMS, dummy_random, "1,1000"},
{NULL}
};
/******************************************************************************
* *
* Function: zbx_module_api_version *
* *
* Purpose: returns version number of the module interface *
* *
* Return value: ZBX_MODULE_API_VERSION - version of module.h module is *
* compiled with, in order to load module successfully Zabbix *
* MUST be compiled with the same version of this header file *
* *
******************************************************************************/
int zbx_module_api_version(void)
{
return ZBX_MODULE_API_VERSION;
}
/******************************************************************************
* *
* Function: zbx_module_item_timeout *
* *
* Purpose: set timeout value for processing of items *
* *
* Parameters: timeout - timeout in seconds, 0 - no timeout set *
* *
******************************************************************************/
void zbx_module_item_timeout(int timeout)
{
item_timeout = timeout;
}
/******************************************************************************
* *
* Function: zbx_module_item_list *
* *
* Purpose: returns list of item keys supported by the module *
* *
* Return value: list of item keys *
* *
******************************************************************************/
ZBX_METRIC *zbx_module_item_list(void)
{
return keys;
}
static int dummy_ping(AGENT_REQUEST *request, AGENT_RESULT *result)
{
SET_UI64_RESULT(result, 1);
return SYSINFO_RET_OK;
}
static int dummy_echo(AGENT_REQUEST *request, AGENT_RESULT *result)
{
char *param;
if (1 != request->nparam)
{
/* ustaw opcjonalny komunikat o błędzie */
SET_MSG_RESULT(result, strdup("Invalid number of parameters."));
return SYSINFO_RET_FAIL;
}
param = get_rparam(request, 0);
SET_STR_RESULT(result, strdup(param));
return SYSINFO_RET_OK;
}
/******************************************************************************
* *
* Function: dummy_random *
* *
* Purpose: a main entry point for processing of an item *
* *
* Parameters: request - structure that contains item key and parameters *
* request->key - item key without parameters *
* request->nparam - number of parameters *
* request->params[N-1] - pointers to item key parameters *
* request->types[N-1] - item key parameters types: *
* REQUEST_PARAMETER_TYPE_UNDEFINED (key parameter is empty) *
* REQUEST_PARAMETER_TYPE_ARRAY (array) *
* REQUEST_PARAMETER_TYPE_STRING (quoted or unquoted string) *
* *
* result - structure that will contain result *
* *
* Return value: SYSINFO_RET_FAIL - function failed, item will be marked *
* as not supported by zabbix *
* SYSINFO_RET_OK - success *
* *
* Comment: get_rparam(request, N-1) can be used to get a pointer to the Nth *
* parameter starting from 0 (first parameter). Make sure it exists *
* by checking value of request->nparam. *
* In the same manner get_rparam_type(request, N-1) can be used to *
* get a parameter type. *
* *
******************************************************************************/
static int dummy_random(AGENT_REQUEST *request, AGENT_RESULT *result)
{
char *param1, *param2;
int from, to;
if (2 != request->nparam)
{
/* ustaw opcjonalny komunikat o błędzie */
SET_MSG_RESULT(result, strdup("Invalid number of parameters."));
return SYSINFO_RET_FAIL;
}
param1 = get_rparam(request, 0);
param2 = get_rparam(request, 1);
/* dla uproszczenia nie ma ścisłej walidacji parametrów i typów */
from = atoi(param1);
to = atoi(param2);
if (from > to)
{
SET_MSG_RESULT(result, strdup("Invalid range specified."));
return SYSINFO_RET_FAIL;
}
SET_UI64_RESULT(result, from + rand() % (to - from + 1));
return SYSINFO_RET_OK;
}
/******************************************************************************
* *
* Function: zbx_module_init *
* *
* Purpose: the function is called on agent startup *
* It should be used to call any initialization routines *
* *
* Return value: ZBX_MODULE_OK - success *
* ZBX_MODULE_FAIL - module initialization failed *
* *
* Comment: the module won't be loaded in case of ZBX_MODULE_FAIL *
* *
******************************************************************************/
int zbx_module_init(void)
{
/* inicjalizacja dla dummy.random */
srand(time(NULL));
return ZBX_MODULE_OK;
}
/******************************************************************************
* *
* Function: zbx_module_uninit *
* *
* Purpose: the function is called on agent shutdown *
* It should be used to cleanup used resources if there are any *
* *
* Return value: ZBX_MODULE_OK - success *
* ZBX_MODULE_FAIL - function failed *
* *
******************************************************************************/
int zbx_module_uninit(void)
{
return ZBX_MODULE_OK;
}
/******************************************************************************
* *
* Functions: dummy_history_float_cb *
* dummy_history_integer_cb *
* dummy_history_string_cb *
* dummy_history_text_cb *
* dummy_history_log_cb *
* *
* Purpose: callback functions for storing historical data of types float, *
* integer, string, text and log respectively in external storage *
* *
* Parameters: history - array of historical data *
* history_num - number of elements in history array *
* *
******************************************************************************/
static void dummy_history_float_cb(const ZBX_HISTORY_FLOAT *history, int history_num)
{
int i;
for (i = 0; i < history_num; i++)
{
/* wykonaj coś z history[i].itemid, history[i].clock, history[i].ns, history[i].value, ... */
}
}
static void dummy_history_integer_cb(const ZBX_HISTORY_INTEGER *history, int history_num)
{
int i;
for (i = 0; i < history_num; i++)
{
/* wykonaj coś z history[i].itemid, history[i].clock, history[i].ns, history[i].value, ... */
}
}
static void dummy_history_string_cb(const ZBX_HISTORY_STRING *history, int history_num)
{
int i;
for (i = 0; i < history_num; i++)
{
/* wykonaj coś z history[i].itemid, history[i].clock, history[i].ns, history[i].value, ... */
}
}
static void dummy_history_text_cb(const ZBX_HISTORY_TEXT *history, int history_num)
{
int i;
for (i = 0; i < history_num; i++)
{
/* wykonaj coś z history[i].itemid, history[i].clock, history[i].ns, history[i].value, ... */
}
}
static void dummy_history_log_cb(const ZBX_HISTORY_LOG *history, int history_num)
{
int i;
for (i = 0; i < history_num; i++)
{
/* wykonaj coś z history[i].itemid, history[i].clock, history[i].ns, history[i].value, ... */
}
}
/******************************************************************************
* *
* Function: zbx_module_history_write_cbs *
* *
* Purpose: returns a set of module functions Zabbix will call to export *
* different types of historical data *
* *
* Return value: structure with callback function pointers (can be NULL if *
* module is not interested in data of certain types) *
* *
******************************************************************************/
ZBX_HISTORY_WRITE_CBS zbx_module_history_write_cbs(void)
{
static ZBX_HISTORY_WRITE_CBS dummy_callbacks =
{
dummy_history_float_cb,
dummy_history_integer_cb,
dummy_history_string_cb,
dummy_history_text_cb,
dummy_history_log_cb,
};
return dummy_callbacks;
}
Moduł eksportuje trzy nowe pozycje:
dummy.ping- zawsze zwraca '1'dummy.echo[param1]- zwraca pierwszy parametr bez zmian, na przykładdummy.echo[ABC]zwróci ABCdummy.random[param1, param2]- zwraca losową liczbę z zakresu param1-param2, na przykładdummy.random[1,1000000]
Ograniczenia
Obsługa modułów ładowanych jest zaimplementowana tylko dla platformy Unix. Oznacza to, że nie działa to dla agentów Windows.
W niektórych przypadkach moduł może potrzebować odczytać parametry konfiguracyjne związane z modułem z pliku zabbix_agentd.conf.
Obecnie nie jest to obsługiwane.
Jeśli potrzebujesz, aby Twój moduł używał niektórych parametrów konfiguracyjnych, powinieneś prawdopodobnie zaimplementować analizę pliku konfiguracyjnego specyficznego dla modułu.