1 可加载模块
概述
可加载模块为扩展 Zabbix 功能提供了一种注重性能的方案。
您可以通过多种方式扩展 Zabbix 功能,例如使用用户参数、外部检查以及 system.run Zabbix agent 监控项。 这些方式都非常有效,但有一个主要缺点,即 fork()。 Zabbix 每次处理用户指标时都必须 fork 一个新进程,这对性能不利。 通常这不是什么大问题,但在监控嵌入式系统、监控参数数量庞大,或者使用具有复杂逻辑或较长启动时间的重量级脚本时,这可能会成为一个严重问题。
支持可加载模块后,可以在不牺牲性能的情况下扩展 Zabbix agent、服务器和 proxy。
可加载模块本质上是由 Zabbix 守护进程使用并在启动时加载的共享库。 该库应包含特定函数,以便 Zabbix 进程能够识别该文件确实是它可以加载并使用的模块。
可加载模块具有多项优势。 卓越的性能以及实现任意逻辑的能力都非常重要,但也许最重要的优势是能够开发、使用和共享 Zabbix 模块。 这有助于实现无障碍维护,并帮助更轻松地交付新功能,而且无需依赖 Zabbix 核心代码库。
模块许可和二进制形式分发受 AGPL-3.0 许可证约束(模块在运行时与 Zabbix 链接,并使用 Zabbix 头文件;自 Zabbix 7.0 起,整个 Zabbix 代码均采用 AGPL-3.0 许可证)。 Zabbix 不保证二进制兼容性。
模块 API 的稳定性在一个 Zabbix LTS(长期支持)发布周期内得到保证。 Zabbix API 的稳定性不作保证(从技术上讲,模块可以调用 Zabbix 内部函数,但不能保证此类模块能够正常工作)。
模块 API
为了使共享库被视为 Zabbix 模块,它应实现并导出若干函数。
当前 Zabbix 模块 API 中有六个函数,其中只有一个是必需的,另外五个是可选的。
必需接口
唯一必需的函数是 zbx_module_api_version():
int zbx_module_api_version(void);
此函数应返回该模块实现的 API 版本,并且为了使模块能够被加载,该版本必须与 Zabbix 支持的模块 API 版本匹配。 Zabbix 支持的模块 API 版本为 ZBX_MODULE_API_VERSION。 因此,此函数应返回该常量。 此前用于此目的的旧常量 ZBX_MODULE_API_VERSION_ONE 现在被定义为等同于 ZBX_MODULE_API_VERSION,以保持源代码兼容性,但不建议使用它。
可选接口
可选函数包括 zbx_module_init()、zbx_module_item_list()、zbx_module_item_timeout()、zbx_module_history_write_cbs() 和 zbx_module_uninit():
int zbx_module_init(void);
此函数应执行模块所需的初始化操作(如果有)。 如果成功,应返回 ZBX_MODULE_OK。 否则,应返回 ZBX_MODULE_FAIL。 在后一种情况下,Zabbix 将不会启动。
ZBX_METRIC *zbx_module_item_list(void);
此函数应返回模块支持的监控项列表。 每个监控项都在一个 ZBX_METRIC 结构中定义,详情请参见下文。 该列表以一个 "key" 字段为 NULL 的 ZBX_METRIC 结构结束。
void zbx_module_item_timeout(int timeout);
如果模块导出了 zbx_module_item_list(),则 Zabbix 会使用此函数来指定 Zabbix 配置文件中的超时设置,模块实现的监控项检查应遵循该设置。 这里,"timeout" 参数的单位为秒。
ZBX_HISTORY_WRITE_CBS zbx_module_history_write_cbs(void);
此函数应返回回调函数,Zabbix 服务器将使用这些回调函数导出不同数据类型的历史数据。 回调函数作为 ZBX_HISTORY_WRITE_CBS 结构的字段提供;如果模块对某种类型的历史数据不感兴趣,相应字段可以为 NULL。
int zbx_module_uninit(void);
此函数应执行必要的反初始化操作(如果有),例如释放已分配的资源、关闭文件描述符等。
除 zbx_module_uninit() 外,所有函数都会在模块加载时于 Zabbix 启动期间调用一次;zbx_module_uninit() 则会在模块卸载时于 Zabbix 关闭期间调用一次。
定义监控项
每个监控项都在一个 ZBX_METRIC 结构中定义:
typedef struct
{
char *key;
unsigned flags;
int (*function)();
char *test_param;
}
ZBX_METRIC;
这里,key 是监控项键值(例如 "dummy.random"),flags 为 CF_HAVEPARAMS 或 0(二者取决于该监控项是否接受参数),function 是实现该监控项的 C 函数(例如 "zbx\_module\_dummy\_random"),而 test_param 是当 Zabbix agent 使用 "-p" 标志启动时要使用的参数列表(例如 "1,1000",也可以为 NULL)。
一个定义示例如下:
static ZBX_METRIC keys[] =
{
{ "dummy.random", CF_HAVEPARAMS, zbx_module_dummy_random, "1,1000" },
{ NULL }
}
每个实现监控项的函数都应接受两个指针参数,第一个参数类型为 AGENT_REQUEST,第二个参数类型为 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;
}
如果成功获取到监控项值,这些函数应返回 SYSINFO_RET_OK。
否则,应返回 SYSINFO_RET_FAIL。
有关如何从 AGENT_REQUEST 获取信息以及如何在 AGENT_RESULT 中设置信息的详细说明,请参见下面的 "dummy" 模块示例。
提供历史数据导出回调
Zabbix proxy 不再支持通过模块导出历史数据。
模块可以指定按类型导出历史数据的函数:数值型(浮点)、数值型(无符号)、字符型、文本型和日志型:
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;
其中每个函数都应接收包含 "history_num" 个元素的 "history" 数组作为参数。 根据要导出的历史数据类型不同,"history" 分别是以下结构的数组:
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;
这些回调将在历史同步过程结束时,由 Zabbix 服务器的历史同步器进程使用,此时数据已写入 Zabbix 数据库并保存到值缓存中。
如果历史数据导出模块发生内部错误,建议将模块编写为在恢复之前不会阻塞整个监控流程,而是丢弃数据,并允许 Zabbix 服务器继续运行。
构建模块
目前,模块应在 Zabbix 源代码树内构建,因为模块 API 依赖于一些在 Zabbix 头文件中定义的数据结构。
对于可加载模块来说,最重要的头文件是 include/module.h,它定义了这些数据结构。
其他为使 include/module.h 正常工作所需的系统头文件包括 stdlib.h 和 stdint.h。
了解这些信息后,构建模块所需的一切就都准备好了。
模块应包含 stdlib.h、stdint.h 和 module.h,并且构建脚本应确保这些文件位于 include 路径中。
详情请参见下面的 “dummy” 示例模块。
另一个有用的头文件是 include/zbxcommon.h,它定义了 zabbix_log() 函数,可用于日志记录和调试。
配置参数
Zabbix agent、服务器和 proxy 支持两个参数来处理模块:
- LoadModulePath – 可加载模块所在位置的完整路径
- LoadModule – 启动时要加载的模块。 模块必须位于 LoadModulePath 指定的目录中,或者路径必须写在模块名称之前。 如果前置路径是绝对路径(以 '/' 开头),则会忽略 LoadModulePath。 允许包含多个 LoadModule 参数。
例如,要扩展 Zabbix agent,我们可以添加以下参数:
LoadModulePath=/usr/local/lib/zabbix/agent/
LoadModule=mariadb.so
LoadModule=apache.so
LoadModule=kernel.so
LoadModule=/usr/local/lib/zabbix/dummy.so
agent 启动时,将从 /usr/local/lib/zabbix/agent 目录加载 mariadb.so、apache.so 和 kernel.so 模块,而 dummy.so 将从 /usr/local/lib/zabbix 加载。 如果模块缺失、权限不正确,或者共享库不是 Zabbix 模块,agent 将无法启动。
前端配置
Zabbix agent、服务器和 proxy 支持可加载模块。 因此,Zabbix 前端中的监控项类型取决于模块加载的位置。 如果模块加载到 agent 中,则监控项类型应为“Zabbix agent”或“Zabbix agent(主动)”。 如果模块加载到服务器或 proxy 中,则监控项类型应为“简单检查”。
通过 Zabbix 模块导出历史数据不需要任何前端配置。 如果模块已被服务器成功加载,并提供 zbx_module_history_write_cbs() 函数,且该函数返回至少一个非 NULL 的回调函数,则历史数据导出将自动启用。
Dummy 模块
Zabbix 包含一个使用 C 语言编写的示例模块。 该模块位于 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
该模块有完善的文档说明,可作为您自定义模块的模板使用。
如上所述,在 Zabbix 源代码树根目录中运行 ./configure 后,只需执行 make 即可构建 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;
}
该模块导出了三个新的监控项:
dummy.ping- 始终返回“1”dummy.echo[param1]- 按原样返回第一个参数,例如,dummy.echo[ABC]将返回 ABCdummy.random[param1, param2]- 返回 param1-param2 范围内的一个随机数,例如,dummy.random[1,1000000]
限制
仅在 Unix 平台上实现了对可加载模块的支持。 这意味着它不适用于 Windows agent。
在某些情况下,模块可能需要从 zabbix_agentd.conf 读取与模块相关的配置参数。 目前不支持这一点。 如果您需要让模块使用某些配置参数,您可能需要实现对模块专用配置文件的解析。