Módulos carregáveis

Visão geral

Módulos carregáveis oferecem uma opção orientada ao desempenho para estender a funcionalidade do Zabbix.

Você pode estender a funcionalidade do Zabbix de várias maneiras, por exemplo, com parâmetros de usuário, verificações externas e itens do agent do Zabbix system.run. Eles funcionam muito bem, mas têm uma grande desvantagem, que é o fork(). O Zabbix precisa criar um novo processo toda vez que lida com uma métrica de usuário, o que não é bom para o desempenho. Normalmente não é um grande problema, no entanto, pode ser uma questão séria ao monitorar sistemas embarcados, ter um grande número de parâmetros monitorados ou scripts pesados com lógica complexa ou longo tempo de inicialização.

O suporte a módulos carregáveis oferece maneiras de estender o agent, server e proxy do Zabbix sem sacrificar o desempenho.

Um módulo carregável é basicamente uma biblioteca compartilhada usada pelo daemon do Zabbix e carregada na inicialização. A biblioteca deve conter certas funções, para que um processo do Zabbix possa detectar que o arquivo é realmente um módulo que pode carregar e trabalhar.

Módulos carregáveis têm vários benefícios. Ótimo desempenho e capacidade de implementar qualquer lógica são muito importantes, mas talvez a vantagem mais importante seja a capacidade de desenvolver, usar e compartilhar módulos do Zabbix. Isso contribui para uma manutenção sem problemas e ajuda a entregar novas funcionalidades de forma mais fácil e independente do código base principal do Zabbix.

O licenciamento do módulo e a distribuição em forma binária são regidos pela licença AGPL-3.0 (os módulos são vinculados ao Zabbix em tempo de execução e usam os cabeçalhos do Zabbix; todo o código do Zabbix é licenciado sob AGPL-3.0 desde o Zabbix 7.0). A compatibilidade binária não é garantida pelo Zabbix.

A estabilidade da API do módulo é garantida durante um ciclo de release LTS (Long Term Support) do Zabbix. A estabilidade da API do Zabbix não é garantida (tecnicamente é possível chamar funções internas do Zabbix a partir de um módulo, mas não há garantia de que tais módulos funcionarão).

API do módulo

Para que uma biblioteca compartilhada seja tratada como um módulo Zabbix, ela deve implementar e exportar várias funções. Atualmente, existem seis funções na API do módulo Zabbix, das quais apenas uma é obrigatória e as outras cinco são opcionais.

Interface obrigatória

A única função obrigatória é zbx_module_api_version():

int zbx_module_api_version(void);

Essa função deve retornar a versão da API implementada por este módulo e, para que o módulo seja carregado, essa versão deve corresponder à versão da API do módulo suportada pelo Zabbix. A versão da API do módulo suportada pelo Zabbix é ZBX_MODULE_API_VERSION. Portanto, essa função deve retornar essa constante. A constante antiga ZBX_MODULE_API_VERSION_ONE, usada para esse propósito, agora está definida como igual a ZBX_MODULE_API_VERSION para preservar a compatibilidade com o código-fonte, mas seu uso não é recomendado.

Interface opcional

As funções opcionais são zbx_module_init(), zbx_module_item_list(), zbx_module_item_timeout(), zbx_module_history_write_cbs() e zbx_module_uninit():

int zbx_module_init(void);

Esta função deve executar a inicialização necessária para o módulo (se houver). Se for bem-sucedida, deve retornar ZBX_MODULE_OK. Caso contrário, deve retornar ZBX_MODULE_FAIL. Neste último caso, o Zabbix não será iniciado.

ZBX_METRIC  *zbx_module_item_list(void);

Esta função deve retornar uma lista de items suportados pelo módulo. Cada item é definido em uma estrutura ZBX_METRIC, veja a seção abaixo para detalhes. A lista é encerrada por uma estrutura ZBX_METRIC com o campo "key" igual a NULL.

void    zbx_module_item_timeout(int timeout);

Se o módulo exportar zbx_module_item_list(), então esta função é usada pelo Zabbix para especificar as configurações de timeout no arquivo de configuração do Zabbix que as verificações de item implementadas pelo módulo devem obedecer. Aqui, o parâmetro "timeout" está em segundos.

ZBX_HISTORY_WRITE_CBS   zbx_module_history_write_cbs(void);

Esta função deve retornar as funções de callback que o Zabbix server usará para exportar o histórico de diferentes tipos de dados. As funções de callback são fornecidas como campos da estrutura ZBX_HISTORY_WRITE_CBS; os campos podem ser NULL se o módulo não tiver interesse no histórico de um determinado tipo.

int zbx_module_uninit(void);

Esta função deve executar a desinicialização necessária (se houver), como liberar recursos alocados, fechar descritores de arquivo etc.

Todas as funções são chamadas uma vez na inicialização do Zabbix quando o módulo é carregado, com exceção de zbx_module_uninit(), que é chamada uma vez no encerramento do Zabbix quando o módulo é descarregado.

Definindo items

Cada item é definido em uma estrutura ZBX_METRIC:

typedef struct
{
    char        *key;
    unsigned    flags;
    int     (*function)();
    char        *test_param;
}
ZBX_METRIC;

Aqui, key é a chave do item (por exemplo, "dummy.random"), flags é CF_HAVEPARAMS ou 0 (dependendo de o item aceitar parâmetros ou não), function é uma função C que implementa o item (por exemplo, zbx_module_dummy_random), e test_param é a lista de parâmetros a ser usada quando o agent Zabbix é iniciado com a flag -p (por exemplo, "1,1000", pode ser NULL). Uma definição de exemplo pode ser assim:

static ZBX_METRIC keys[] =
{
    { "dummy.random", CF_HAVEPARAMS, zbx_module_dummy_random, "1,1000" },
    { NULL }
}

Cada função que implementa um item deve aceitar dois parâmetros ponteiro, o primeiro do tipo AGENT_REQUEST e o segundo do 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;
}

Essas funções devem retornar SYSINFO_RET_OK se o valor do item tiver sido obtido com sucesso. Caso contrário, devem retornar SYSINFO_RET_FAIL. Consulte o exemplo do módulo "dummy" abaixo para obter detalhes sobre como obter informações de AGENT_REQUEST e como definir informações em AGENT_RESULT.

Fornecendo callbacks de exportação de histórico

A exportação de histórico via módulo não é mais suportada pelo Zabbix proxy.

O módulo pode especificar funções para exportar dados de histórico por tipo: Numeric (float), Numeric (unsigned), Character, Text e 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 uma delas deve receber o array history com history_num elementos como argumento. Dependendo do tipo de dado de histórico a ser exportado, history é um array das seguintes estruturas, 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;

Os callbacks serão usados pelos processos history syncer do Zabbix server no final do procedimento de sincronização de histórico, após os dados serem gravados no banco de dados do Zabbix e salvos no cache de valores.

Em caso de erro interno no módulo de exportação de histórico, recomenda-se que o módulo seja escrito de forma que não bloqueie todo o monitoramento até se recuperar, mas descarte os dados e permita que o Zabbix server continue em execução.

Compilando módulos

Os módulos atualmente devem ser compilados dentro da árvore de código-fonte do Zabbix, porque a API do módulo depende de algumas estruturas de dados que são definidas nos cabeçalhos do Zabbix.

O cabeçalho mais importante para módulos carregáveis é include/module.h, que define essas estruturas de dados. Outros cabeçalhos de sistema necessários que ajudam include/module.h a funcionar corretamente são stdlib.h e stdint.h.

Com essas informações em mente, tudo está pronto para que o módulo seja compilado. O módulo deve incluir stdlib.h, stdint.h e module.h, e o script de compilação deve garantir que esses arquivos estejam no caminho de inclusão. Veja o exemplo do módulo "dummy" abaixo para detalhes.

Outro cabeçalho útil é include/zbxcommon.h, que define a função zabbix_log(), que pode ser usada para fins de registro e depuração.

Parâmetros de configuração

O Zabbix agent, server e proxy oferecem suporte a dois parâmetros para lidar com módulos:

  • LoadModulePath - caminho completo para o local dos módulos carregáveis
  • LoadModule - módulo(s) a serem carregados na inicialização. Os módulos devem estar localizados em um diretório especificado por LoadModulePath ou o caminho deve preceder o nome do módulo. Se o caminho precedente for absoluto (começa com /), então LoadModulePath é ignorado. É permitido incluir vários parâmetros LoadModule.

Por exemplo, para estender o Zabbix agent, poderíamos adicionar os seguintes parâmetros:

LoadModulePath=/usr/local/lib/zabbix/agent/
LoadModule=mariadb.so
LoadModule=apache.so
LoadModule=kernel.so
LoadModule=/usr/local/lib/zabbix/dummy.so

Ao iniciar o agent, ele carregará os módulos mariadb.so, apache.so e kernel.so do diretório /usr/local/lib/zabbix/agent, enquanto dummy.so será carregado de /usr/local/lib/zabbix. O agent não conseguirá iniciar se um módulo estiver ausente, em caso de permissões incorretas ou se uma biblioteca compartilhada não for um módulo do Zabbix.

Configuração do frontend

Módulos carregáveis são suportados pelo Zabbix agent, server e proxy. Portanto, o tipo de item no frontend do Zabbix depende de onde o módulo é carregado. Se o módulo for carregado no agent, então o tipo de item deve ser "Zabbix agent" ou "Zabbix agent (active)". Se o módulo for carregado no server ou proxy, então o tipo de item deve ser "Simple check".

A exportação de histórico por meio de módulos do Zabbix não requer nenhuma configuração do frontend. Se o módulo for carregado com sucesso pelo server e fornecer a função zbx_module_history_write_cbs(), que retorna pelo menos uma função de callback não NULL, então a exportação de histórico será habilitada automaticamente.

Módulo dummy

O Zabbix inclui um módulo de exemplo escrito em linguagem C. O módulo está localizado em 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

O módulo está bem documentado e pode ser usado como template para seus próprios módulos.

Depois que ./configure tiver sido executado na raiz da árvore de código-fonte do Zabbix, conforme descrito acima, basta executar 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"

/* a variável mantém a configuração de timeout para o processamento de item */
static int  item_timeout = 0;

/* o módulo DEVE definir funções internas como static e usar um padrão de nomenclatura diferente dos símbolos internos */
/* do Zabbix (zbx_*) e das funções da API de módulos carregáveis (zbx_module_*) para evitar conflitos               */
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: retorna o número da versão da interface do módulo                *
 *                                                                            *
 * Return value: ZBX_MODULE_API_VERSION - versão de module.h com a qual o    *
 *               módulo foi compilado; para carregar o módulo com sucesso, o  *
 *               Zabbix DEVE ser compilado com a mesma versão deste arquivo   *
 *               de cabeçalho                                                 *
 *                                                                            *
 ******************************************************************************/
int zbx_module_api_version(void)
{
    return ZBX_MODULE_API_VERSION;
}

/******************************************************************************
 *                                                                            *
 * Function: zbx_module_item_timeout                                          *
 *                                                                            *
 * Purpose: define o valor de timeout para o processamento de items           *
 *                                                                            *
 * Parameters: timeout - timeout em segundos, 0 - nenhum timeout definido     *
 *                                                                            *
 ******************************************************************************/
void    zbx_module_item_timeout(int timeout)
{
    item_timeout = timeout;
}

/******************************************************************************
 *                                                                            *
 * Function: zbx_module_item_list                                             *
 *                                                                            *
 * Purpose: retorna a lista de chaves de item suportadas pelo módulo         *
 *                                                                            *
 * Return value: lista de chaves de item                                      *
 *                                                                            *
 ******************************************************************************/
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)
    {
        /* define mensagem de erro opcional */
        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: ponto de entrada principal para o processamento de um item        *
 *                                                                            *
 * Parameters: request - estrutura que contém a chave do item e parâmetros    *
 *              request->key - chave do item sem parâmetros                   *
 *              request->nparam - número de parâmetros                        *
 *              request->params[N-1] - ponteiros para os parâmetros da chave  *
 *              request->types[N-1] - tipos dos parâmetros da chave:          *
 *                  REQUEST_PARAMETER_TYPE_UNDEFINED (parâmetro da chave vazio) *
 *                  REQUEST_PARAMETER_TYPE_ARRAY (array)                      *
 *                  REQUEST_PARAMETER_TYPE_STRING (string entre aspas ou sem aspas) *
 *                                                                            *
 *             result - estrutura que conterá o resultado                     *
 *                                                                            *
 * Return value: SYSINFO_RET_FAIL - a função falhou, o item será marcado      *
 *                                 como não suportado pelo zabbix             *
 *               SYSINFO_RET_OK - sucesso                                     *
 *                                                                            *
 * Comment: get_rparam(request, N-1) pode ser usado para obter um ponteiro    *
 *          para o N-ésimo parâmetro, começando em 0 (primeiro parâmetro).    *
 *          Certifique-se de que ele exista verificando o valor de request->nparam.
 *          Da mesma forma, get_rparam_type(request, N-1) pode ser usado para  *
 *          obter o tipo de um parâmetro.                                     *
 *                                                                            *
 ******************************************************************************/
static int  dummy_random(AGENT_REQUEST *request, AGENT_RESULT *result)
{
    char    *param1, *param2;
    int from, to;

    if (2 != request->nparam)
    {
        /* define mensagem de erro opcional */
        SET_MSG_RESULT(result, strdup("Invalid number of parameters."));
        return SYSINFO_RET_FAIL;
    }

    param1 = get_rparam(request, 0);
    param2 = get_rparam(request, 1);

    /* não há validação rigorosa de parâmetros e tipos por simplicidade */
    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: a função é chamada na inicialização do agent                    *
 *          Ela deve ser usada para chamar quaisquer rotinas de inicialização *
 *                                                                            *
 * Return value: ZBX_MODULE_OK - sucesso                                      *
 *               ZBX_MODULE_FAIL - inicialização do módulo falhou             *
 *                                                                            *
 * Comment: o módulo não será carregado em caso de ZBX_MODULE_FAIL            *
 *                                                                            *
 ******************************************************************************/
int zbx_module_init(void)
{
    /* inicialização para dummy.random */
    srand(time(NULL));

    return ZBX_MODULE_OK;
}

/******************************************************************************
 *                                                                            *
 * Function: zbx_module_uninit                                                *
 *                                                                            *
 * Purpose: a função é chamada no encerramento do agent                     *
 *          Ela deve ser usada para limpar recursos utilizados, se houver     *
 *                                                                            *
 * Return value: ZBX_MODULE_OK - sucesso                                      *
 *               ZBX_MODULE_FAIL - função falhou                            *
 *                                                                            *
 ******************************************************************************/
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: funções de callback para armazenar dados históricos dos tipos    *
 *          float, integer, string, text e log, respectivamente, em          *
 *          armazenamento externo                                            *
 *                                                                            *
 * Parameters: history     - array de dados históricos                       *
 *             history_num - número de elementos no 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++)
    {
        /* faça algo com 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++)
    {
        /* faça algo com 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++)
    {
        /* faça algo com 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++)
    {
        /* faça algo com 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++)
    {
        /* faça algo com history[i].itemid, history[i].clock, history[i].ns, history[i].value, ... */
    }
}

/******************************************************************************
 *                                                                            *
 * Function: zbx_module_history_write_cbs                                     *
 *                                                                            *
 * Purpose: retorna um conjunto de funções do módulo que o Zabbix chamará    *
 *          para exportar diferentes tipos de dados históricos                *
 *                                                                            *
 * Return value: estrutura com ponteiros para funções de callback (pode ser   *
 *               NULL se o módulo não tiver interesse em determinados tipos   *
 *               de dados)                                                    *
 *                                                                            *
 ******************************************************************************/
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;
}

O módulo exporta três novos items:

  • dummy.ping - sempre retorna '1'
  • dummy.echo[param1] - retorna o primeiro parâmetro exatamente como foi recebido; por exemplo, dummy.echo[ABC] retornará ABC
  • dummy.random[param1, param2] - retorna um número aleatório dentro do intervalo de param1-param2; por exemplo, dummy.random[1,1000000]

Limitações

O suporte a módulos carregáveis é implementado apenas para a plataforma Unix. Isso significa que ele não funciona para agents do Windows.

Em alguns casos, um módulo pode precisar ler parâmetros de configuração relacionados ao módulo a partir de zabbix_agentd.conf. Isso não é suportado atualmente. Se você precisar que seu módulo use alguns parâmetros de configuração, provavelmente deverá implementar a análise de um arquivo de configuração específico do módulo.