1 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.
Wymagany interfejs
Jedyną obowiązkową funkcją jest zbx_module_api_version():
int zbx_module_api_version(void);
Ta funkcja 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 funkcja ta powinna zwracać tę stałą. Stara stała ZBX_MODULE_API_VERSION_ONE, używana wcześniej do tego celu, jest obecnie zdefiniowana jako równa ZBX_MODULE_API_VERSION w celu zachowania zgodności ze źródłami, jednak jej użycie nie jest zalecane.
Interfejs opcjonalny
Funkcjami opcjonalnymi są 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 ostatnim przypadku Zabbix nie uruchomi się.
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, w której pole "key" ma wartość NULL.
void zbx_module_item_timeout(int timeout);
Jeśli moduł eksportuje zbx_module_item_list(), to ta funkcja jest używana przez Zabbix do określenia ustawień timeout z pliku konfiguracyjnego Zabbix, których powinny przestrzegać kontrole pozycji zaimplementowane przez moduł. Tutaj parametr "timeout" jest podawany w sekundach.
ZBX_HISTORY_WRITE_CBS zbx_module_history_write_cbs(void);
Ta funkcja powinna zwrócić funkcje callback, których Zabbix server będzie używać do eksportowania historii różnych typów danych. Funkcje callback 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 zwalnianie przydzielonych zasobów, zamykanie deskryptorów plików itp.
Wszystkie funkcje są wywoływane jednokrotnie podczas uruchamiania Zabbix, gdy moduł jest ładowany, z wyjątkiem zbx_module_uninit(), która jest wywoływana jednokrotnie podczas zamykania Zabbix, gdy moduł jest wył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), 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ć następująco:
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. Szczegółowe informacje o tym, jak pobierać informacje z AGENT_REQUEST i jak ustawiać informacje w AGENT_RESULT, można znaleźć w przykładzie modułu "dummy" poniżej.
Udostępnianie callbacków eksportu historii
Eksport historii przez moduł nie jest już obsługiwany przez Zabbix proxy.
Moduł może określać funkcje do eksportu danych historii według typu: numeryczne (float), numeryczne (unsigned), znakowe, tekstowe 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" złożoną z "history_num" elementów. W zależności od typu eksportowanych danych historii, "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 Zabbix serwer na końcu procedury synchronizacji historii, po zapisaniu danych do bazy danych Zabbix i zachowaniu ich w pamięci podręcznej wartości.
W przypadku błędu wewnętrznego 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ł Zabbix serwer kontynuować działanie.
Budowanie modułów
Moduły są obecnie przeznaczone do budowania wewnątrz drzewa źródeł Zabbix, ponieważ API modułów zależy od niektórych struktur danych zdefiniowanych w nagłówkach Zabbix.
Najważniejszym nagłówkiem dla ładowalnych modułów 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 dołączać stdlib.h, stdint.h i module.h, a skrypt budowania powinien zapewnić, ż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(), której można używać do celów rejestrowania i debugowania.
Parametry konfiguracji
Zabbix agent, serwer i proxy obsługują dwa parametry do pracy z modułami:
- LoadModulePath – pełna ścieżka do lokalizacji ładowalnych modułów
- LoadModule – moduł(y) do załadowania podczas uruchamiania. Moduły muszą znajdować się w katalogu określonym przez LoadModulePath lub ścieżka musi poprzedzać nazwę modułu. Jeśli poprzedzająca ścieżka jest bezwzględna (zaczyna się od '/'), wtedy LoadModulePath jest ignorowany. Dozwolone jest uwzględnienie wielu parametrów LoadModule.
Na przykład, aby rozszerzyć Zabbix agent, możemy 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
Podczas uruchamiania agent załaduje 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ł będzie brakował, w przypadku nieprawidłowych uprawnień lub jeśli biblioteka współdzielona nie jest modułem Zabbix.
Konfiguracja frontendu
Ładowalne moduły są obsługiwane przez Zabbix agent, serwer i proxy. Dlatego typ pozycji w Zabbix frontend zależy od miejsca załadowania modułu. Jeśli moduł jest załadowany do agent, typ pozycji powinien być ustawiony na „Zabbix agent” lub „Zabbix agent (active)”. Jeśli moduł jest załadowany do serwera lub proxy, typ pozycji powinien być ustawiony na „Simple check”.
Eksport historii za pośrednictwem modułów Zabbix nie wymaga żadnej konfiguracji frontendu. 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ą funkcję callback różną od NULL, 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 być używany jako szablon dla własnych modułów.
Po uruchomieniu ./configure w katalogu głównym drzewa źródeł 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"
/* the variable keeps timeout setting for item processing */
static int item_timeout = 0;
/* module SHOULD define internal functions as static and use a naming pattern different from Zabbix internal */
/* symbols (zbx_*) and loadable module API functions (zbx_module_*) to avoid conflicts */
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)
{
/* set optional error message */
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)
{
/* set optional error message */
SET_MSG_RESULT(result, strdup("Invalid number of parameters."));
return SYSINFO_RET_FAIL;
}
param1 = get_rparam(request, 0);
param2 = get_rparam(request, 1);
/* there is no strict validation of parameters and types for simplicity sake */
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)
{
/* initialization for 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++)
{
/* do something with 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++)
{
/* do something with 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++)
{
/* do something with 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++)
{
/* do something with 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++)
{
/* do something with 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 liczbę losową z zakresu param1-param2, na przykładdummy.random[1,1000000]
Ograniczenia
Obsługa modułów ładowalnych jest zaimplementowana wyłącznie dla platformy Unix. Oznacza to, że nie działa ona dla agentów Windows.
W niektórych przypadkach moduł może wymagać odczytu parametrów konfiguracyjnych związanych z modułem z pliku zabbix_agentd.conf. Obecnie nie jest to obsługiwane. Jeśli potrzebujesz, aby Twój moduł korzystał z parametrów konfiguracyjnych, prawdopodobnie powinieneś zaimplementować analizę pliku konfiguracyjnego specyficznego dla modułu.