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.hstdint.h

了解这些信息后,构建模块所需的一切就都准备好了。
模块应包含 stdlib.hstdint.hmodule.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] 将返回 ABC
  • dummy.random[param1, param2] - 返回 param1-param2 范围内的一个随机数,例如,dummy.random[1,1000000]

限制

仅在 Unix 平台上实现了对可加载模块的支持。 这意味着它不适用于 Windows agent。

在某些情况下,模块可能需要从 zabbix_agentd.conf 读取与模块相关的配置参数。 目前不支持这一点。 如果您需要让模块使用某些配置参数,您可能需要实现对模块专用配置文件的解析。