Los módulos cargables ofrecen una opción orientada al rendimiento para ampliar la funcionalidad de Zabbix.
Puede ampliar la funcionalidad de Zabbix de muchas maneras, por ejemplo, con parámetros de usuario, comprobaciones externas y elementos del agent de Zabbix system.run. Estos funcionan muy bien, pero tienen un gran inconveniente, a saber, fork(). Zabbix tiene que bifurcar un nuevo proceso cada vez que maneja una métrica de usuario, lo cual no es bueno para el rendimiento. Normalmente no es un gran problema, sin embargo, podría ser un problema grave al monitorizar sistemas embebidos, tener un gran número de parámetros monitorizados o scripts pesados con lógica compleja o un largo tiempo de inicio.
El soporte de módulos cargables ofrece formas de ampliar el agent, el server y el proxy de Zabbix sin sacrificar el rendimiento.
Un módulo cargable es básicamente una biblioteca compartida utilizada por el demonio de Zabbix y cargada al inicio. La biblioteca debe contener ciertas funciones, de modo que un proceso de Zabbix pueda detectar que el archivo es realmente un módulo que puede cargar y con el que puede trabajar.
Los módulos cargables tienen una serie de ventajas. Un gran rendimiento y la capacidad de implementar cualquier lógica son muy importantes, pero quizás la ventaja más importante es la capacidad de desarrollar, utilizar y compartir módulos de Zabbix. Contribuye a un mantenimiento sin problemas y ayuda a ofrecer nuevas funcionalidades más fácilmente e independientemente de la base de código principal de Zabbix.
La licencia de los módulos y su distribución en forma binaria se rigen por la licencia AGPL-3.0 (los módulos se enlazan con Zabbix en tiempo de ejecución y utilizan cabeceras de Zabbix; todo el código de Zabbix está licenciado bajo AGPL-3.0 desde Zabbix 7.0). Zabbix no garantiza la compatibilidad binaria.
La estabilidad de la API de los módulos está garantizada durante un ciclo de lanzamiento de Zabbix LTS (Long Term Support). No se garantiza la estabilidad de la API de Zabbix (técnicamente es posible llamar a funciones internas de Zabbix desde un módulo, pero no hay garantía de que dichos módulos funcionen).
Para que una biblioteca compartida sea tratada como un módulo de Zabbix, debe implementar y exportar varias funciones. Actualmente hay seis funciones en la API de módulos de Zabbix, de las cuales sólo una es obligatoria y las otras cinco son opcionales.
La única función obligatoria es zbx_module_api_version():
Esta función debe devolver la versión de la API implementada por este módulo y, para que el módulo se cargue, esta versión debe coincidir con la versión de la API de módulo soportada por Zabbix. La versión de la API de módulo soportada por Zabbix es ZBX_MODULE_API_VERSION. Por lo tanto, esta función debe devolver esta constante. La antigua constante ZBX_MODULE_API_VERSION_ONE utilizada para este propósito ahora está definida como igual a ZBX_MODULE_API_VERSION para preservar la compatibilidad con el código fuente, pero no se recomienda su uso.
Las funciones opcionales son zbx_module_init(), zbx_module_item_list(), zbx_module_item_timeout(), zbx_module_history_write_cbs() y zbx_module_uninit():
Esta función debe realizar la inicialización necesaria para el módulo (si la hay). Si tiene éxito, debe devolver ZBX_MODULE_OK. De lo contrario, debe devolver ZBX_MODULE_FAIL. En este último caso, Zabbix no se iniciará.
Esta función debe devolver una lista de items soportados por el módulo. Cada item se define en una estructura ZBX_METRIC, consulte la sección siguiente para más detalles. La lista termina con una estructura ZBX_METRIC con el campo "key" en NULL.
Si el módulo exporta zbx_module_item_list() entonces esta función es utilizada por Zabbix para especificar la configuración de timeout en el archivo de configuración de Zabbix que las comprobaciones de items implementadas por el módulo deben respetar. Aquí, el parámetro "timeout" está en segundos.
Esta función debe devolver funciones de callback que el server de Zabbix utilizará para exportar el historial de diferentes tipos de datos. Las funciones de callback se proporcionan como campos de la estructura ZBX_HISTORY_WRITE_CBS, los campos pueden ser NULL si el módulo no está interesado en el historial de cierto tipo.
Esta función debe realizar la desinicialización necesaria (si la hay), como liberar recursos asignados, cerrar descriptores de archivos, etc.
Todas las funciones se llaman una vez al iniciar Zabbix cuando se carga el módulo, con la excepción de zbx_module_uninit(), que se llama una vez al apagar Zabbix cuando se descarga el módulo.
Cada item se define en una estructura ZBX_METRIC:
Aquí, key es la clave del item (por ejemplo, "dummy.random"), flags es CF_HAVEPARAMS o 0 (dependiendo de si el item acepta parámetros o no), function es una función en C que implementa el item (por ejemplo, "zbx_module_dummy_random"), y test_param es la lista de parámetros que se utilizará cuando el agent de Zabbix se inicie con el parámetro "-p" (por ejemplo, "1,1000", puede ser NULL). Una definición de ejemplo podría verse así:
static ZBX_METRIC keys[] =
{
{ "dummy.random", CF_HAVEPARAMS, zbx_module_dummy_random, "1,1000" },
{ NULL }
}Cada función que implementa un item debe aceptar dos parámetros puntero, el primero de tipo AGENT_REQUEST y el segundo de tipo 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;
}Estas funciones deben devolver SYSINFO_RET_OK, si el valor del item se obtuvo correctamente. De lo contrario, deben devolver SYSINFO_RET_FAIL. Consulte el ejemplo de módulo "dummy" a continuación para obtener detalles sobre cómo obtener información de AGENT_REQUEST y cómo establecer información en AGENT_RESULT.
La exportación de historial a través del módulo ya no es compatible con el proxy de Zabbix.
El módulo puede especificar funciones para exportar datos de historial por tipo: Numérico (flotante), Numérico (sin signo), Carácter, Texto y 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;Cada una de ellas debe tomar como argumentos un array "history" de "history_num" elementos. Dependiendo del tipo de datos de historial a exportar, "history" es un array de las siguientes estructuras, respectivamente:
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;Los callbacks serán utilizados por los procesos history syncer del servidor Zabbix al final del procedimiento de sincronización de historial, después de que los datos se escriban en la base de datos de Zabbix y se guarden en la caché de valores.
En caso de error interno en el módulo de exportación de historial, se recomienda que el módulo esté escrito de tal manera que no bloquee toda la monitorización hasta que se recupere, sino que descarte los datos y permita que el servidor Zabbix continúe funcionando.
Actualmente, los módulos deben compilarse dentro del árbol de fuentes de Zabbix, porque la API del módulo depende de algunas estructuras de datos que se definen en los encabezados de Zabbix.
El encabezado más importante para los módulos cargables es include/module.h, que define estas estructuras de datos. Otros encabezados de sistema necesarios que ayudan a que include/module.h funcione correctamente son stdlib.h y stdint.h.
Teniendo en cuenta esta información, todo está listo para compilar el módulo. El módulo debe incluir stdlib.h, stdint.h y module.h, y el script de compilación debe asegurarse de que estos archivos estén en la ruta de inclusión. Consulte el ejemplo de módulo "dummy" a continuación para más detalles.
Otro encabezado útil es include/zbxcommon.h, que define la función zabbix_log(), que puede utilizarse para fines de registro y depuración.
El agent, server y proxy de Zabbix admiten dos parámetros para trabajar con módulos:
Por ejemplo, para extender el agent de Zabbix podríamos añadir los siguientes parámetros:
LoadModulePath=/usr/local/lib/zabbix/agent/
LoadModule=mariadb.so
LoadModule=apache.so
LoadModule=kernel.so
LoadModule=/usr/local/lib/zabbix/dummy.soAl iniciar el agent, cargará los módulos mariadb.so, apache.so y kernel.so desde el directorio /usr/local/lib/zabbix/agent, mientras que dummy.so se cargará desde /usr/local/lib/zabbix. El agent no podrá iniciarse si falta un módulo, en caso de permisos incorrectos o si una biblioteca compartida no es un módulo de Zabbix.
Los módulos cargables son compatibles con el agent, server y proxy de Zabbix. Por lo tanto, el tipo de item en el frontend de Zabbix depende de dónde se cargue el módulo. Si el módulo se carga en el agent, entonces el tipo de item debe ser "Zabbix agent" o "Zabbix agent (active)". Si el módulo se carga en el server o proxy, entonces el tipo de item debe ser "Simple check".
La exportación de histórico a través de módulos de Zabbix no necesita ninguna configuración en el frontend. Si el módulo se carga correctamente por el server y proporciona la función zbx_module_history_write_cbs() que devuelve al menos una función de callback distinta de NULL, entonces la exportación de histórico se habilitará automáticamente.
Zabbix incluye un módulo de ejemplo escrito en lenguaje C. El módulo se encuentra en 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 READMEEl módulo está bien documentado y puede utilizarse como plantilla para sus propios módulos.
Después de ejecutar ./configure en la raíz del árbol de fuentes de Zabbix como se describe arriba, simplemente ejecute make para compilar 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"
/* la variable mantiene la configuración de timeout para el procesamiento de items */
static int item_timeout = 0;
/* el módulo DEBE definir funciones internas como static y usar un patrón de nombres diferente a los símbolos internos de Zabbix (zbx_*) y a las funciones API de módulos cargables (zbx_module_*) para evitar conflictos */
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}
};
/******************************************************************************
* *
* Función: zbx_module_api_version *
* *
* Propósito: devuelve el número de versión de la interfaz del módulo *
* *
* Valor de retorno: ZBX_MODULE_API_VERSION - versión de module.h con la que *
* se compila el módulo, para cargar el módulo correctamente *
* Zabbix DEBE compilarse con la misma versión de este *
* archivo de cabecera *
* *
******************************************************************************/
int zbx_module_api_version(void)
{
return ZBX_MODULE_API_VERSION;
}
/******************************************************************************
* *
* Función: zbx_module_item_timeout *
* *
* Propósito: establecer el valor de timeout para el procesamiento de items *
* *
* Parámetros: timeout - timeout en segundos, 0 - no se establece timeout *
* *
******************************************************************************/
void zbx_module_item_timeout(int timeout)
{
item_timeout = timeout;
}
/******************************************************************************
* *
* Función: zbx_module_item_list *
* *
* Propósito: devuelve la lista de claves de items soportadas por el módulo *
* *
* Valor de retorno: lista de claves de items *
* *
******************************************************************************/
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)
{
/* establecer mensaje de error opcional */
SET_MSG_RESULT(result, strdup("Número de parámetros inválido."));
return SYSINFO_RET_FAIL;
}
param = get_rparam(request, 0);
SET_STR_RESULT(result, strdup(param));
return SYSINFO_RET_OK;
}
/******************************************************************************
* *
* Función: dummy_random *
* *
* Propósito: punto de entrada principal para el procesamiento de un item *
* *
* Parámetros: request - estructura que contiene la clave del item y *
* parámetros *
* request->key - clave del item sin parámetros *
* request->nparam - número de parámetros *
* request->params[N-1] - punteros a los parámetros de la clave *
* request->types[N-1] - tipos de parámetros de la clave: *
* REQUEST_PARAMETER_TYPE_UNDEFINED (parámetro vacío) *
* REQUEST_PARAMETER_TYPE_ARRAY (array) *
* REQUEST_PARAMETER_TYPE_STRING (cadena entrecomillada o no)*
* *
* result - estructura que contendrá el resultado *
* *
* Valor de retorno: SYSINFO_RET_FAIL - la función falló, el item se marcará *
* como no soportado por zabbix *
* SYSINFO_RET_OK - éxito *
* *
* Comentario: get_rparam(request, N-1) puede usarse para obtener un puntero *
* al N-ésimo parámetro comenzando desde 0 (primer parámetro). *
* Asegúrese de que existe comprobando el valor de request->nparam*
* De la misma manera, get_rparam_type(request, N-1) puede usarse *
* para obtener el tipo de parámetro. *
* *
******************************************************************************/
static int dummy_random(AGENT_REQUEST *request, AGENT_RESULT *result)
{
char *param1, *param2;
int from, to;
if (2 != request->nparam)
{
/* establecer mensaje de error opcional */
SET_MSG_RESULT(result, strdup("Número de parámetros inválido."));
return SYSINFO_RET_FAIL;
}
param1 = get_rparam(request, 0);
param2 = get_rparam(request, 1);
/* no hay validación estricta de parámetros y tipos por simplicidad */
from = atoi(param1);
to = atoi(param2);
if (from > to)
{
SET_MSG_RESULT(result, strdup("Rango especificado inválido."));
return SYSINFO_RET_FAIL;
}
SET_UI64_RESULT(result, from + rand() % (to - from + 1));
return SYSINFO_RET_OK;
}
/******************************************************************************
* *
* Función: zbx_module_init *
* *
* Propósito: la función se llama al iniciar el agent *
* Debe usarse para llamar a cualquier rutina de inicialización *
* *
* Valor de retorno: ZBX_MODULE_OK - éxito *
* ZBX_MODULE_FAIL - fallo en la inicialización del módulo *
* *
* Comentario: el módulo no se cargará en caso de ZBX_MODULE_FAIL *
* *
******************************************************************************/
int zbx_module_init(void)
{
/* inicialización para dummy.random */
srand(time(NULL));
return ZBX_MODULE_OK;
}
/******************************************************************************
* *
* Función: zbx_module_uninit *
* *
* Propósito: la función se llama al apagar el agent *
* Debe usarse para limpiar recursos usados si los hay *
* *
* Valor de retorno: ZBX_MODULE_OK - éxito *
* ZBX_MODULE_FAIL - la función falló *
* *
******************************************************************************/
int zbx_module_uninit(void)
{
return ZBX_MODULE_OK;
}
/******************************************************************************
* *
* Funciones: dummy_history_float_cb *
* dummy_history_integer_cb *
* dummy_history_string_cb *
* dummy_history_text_cb *
* dummy_history_log_cb *
* *
* Propósito: funciones de callback para almacenar datos históricos de tipos *
* float, integer, string, text y log respectivamente en un *
* almacenamiento externo *
* *
* Parámetros: history - array de datos históricos *
* history_num - número de elementos en el array history *
* *
******************************************************************************/
static void dummy_history_float_cb(const ZBX_HISTORY_FLOAT *history, int history_num)
{
int i;
for (i = 0; i < history_num; i++)
{
/* hacer algo con 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++)
{
/* hacer algo con 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++)
{
/* hacer algo con 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++)
{
/* hacer algo con 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++)
{
/* hacer algo con history[i].itemid, history[i].clock, history[i].ns, history[i].value, ... */
}
}
/******************************************************************************
* *
* Función: zbx_module_history_write_cbs *
* *
* Propósito: devuelve un conjunto de funciones del módulo que Zabbix llamará *
* para exportar diferentes tipos de datos históricos *
* *
* Valor de retorno: estructura con punteros a funciones de callback (puede *
* ser NULL si el módulo no está interesado en datos de *
* ciertos tipos) *
* *
******************************************************************************/
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;
}El módulo exporta tres nuevos items:
dummy.ping - siempre devuelve '1'dummy.echo[param1] - devuelve el primer parámetro tal cual, por ejemplo, dummy.echo[ABC] devolverá ABCdummy.random[param1, param2] - devuelve un número aleatorio dentro del rango param1-param2, por ejemplo, dummy.random[1,1000000]El soporte de módulos cargables está implementado solo para la plataforma Unix. Esto significa que no funciona para los agentes de Windows.
En algunos casos, un módulo puede necesitar leer parámetros de configuración relacionados con el módulo desde zabbix_agentd.conf. Actualmente no está soportado. Si necesita que su módulo utilice algunos parámetros de configuración, probablemente debería implementar el análisis de un archivo de configuración específico del módulo.