Поддержка нескольких типов хранилищ в сервере Zabbix 3.4

В доработанном веб-интерфейсе Zabbix реализована возможность индивидуальной настройких типа хранилища для каждой из таблиц истории. Теперь необходимо реализовать возможность настройки хранилища для каждой из таблиц на стороне сервера Zabbix. Я решил прибегнуть к следующей схеме: в файле конфигурации я добавил по одной опции для каждой из таблиц истории, в каждой опции указывается через запятую тип хранилища и URL, по которому оно доступно. Если для опции не указано значение, то используется хранилище по умолчанию - база данных SQL.

В файле конфигурации сервера Zabbix это может выглядеть следующим образом:

HistoryStorage=clickhouse,http://zabbix:zabbix@localhost:8123/?database=zabbix
HistoryUintStorage=clickhouse,http://zabbix:zabbix@localhost:8123/?database=zabbix
HistoryStrStorage=elastic,http://hostnameelastic:9200
HistoryTextStorage=elastic,http://hostnameelastic:9200
HistoryLogStorage=elastic,http://hostnameelastic:9200

Готовую заплатку с реализацией раздельного выбора типа хранилища для каждой из таблиц истории можно найти по ссылке zabbix3_4_12_server_storage_per_table.patch.

Ниже в логическом порядке описываются изменения, вносимые этой заплаткой.

Кстати, заплатки описываются так подробно по двум обстоятельствам:

1. Доработка файла конфигурации

Задекларируем наши намерения, доработав соответствующим образом пример файла конфигурации conf/zabbix_server.conf:

Index: zabbix-3.4.12-1+buster/conf/zabbix_server.conf
===================================================================
--- zabbix-3.4.12-1+buster.orig/conf/zabbix_server.conf
+++ zabbix-3.4.12-1+buster/conf/zabbix_server.conf
@@ -133,19 +133,40 @@ DBUser=zabbix
 # Default (for MySQL):
 # DBPort=3306
 
-### Option: HistoryStorageURL
-#      History storage HTTP[S] URL.
+### Option: HistoryStorage
+#      Storage type and HTTP[S] URL for double type values history.
 #
 # Mandatory: no
 # Default:
-# HistoryStorageURL=
+# HistoryStorage=
 
-### Option: HistoryStorageTypes
-#      Comma separated list of value types to be sent to the history storage.
+### Option: HistoryUintStorage
+#      Storage type and HTTP[S] URL for unsigned integer type values history.
 #
 # Mandatory: no
 # Default:
-# HistoryStorageTypes=uint,dbl,str,log,text
+# HistoryUintStorage=
+
+### Option: HistoryStrStorage
+#      Storage type and HTTP[S] URL for string type values history.
+#
+# Mandatory: no
+# Default:
+# HistoryStrStorage=
+
+### Option: HistoryTextStorage
+#      Storage type and HTTP[S] URL for text type values history.
+#
+# Mandatory: no
+# Default:
+# HistoryTextStorage=
+
+### Option: HistoryLogStorage
+#      Storage type and HTTP[S] URL for log type values history.
+#
+# Mandatory: no
+# Default:
+# HistoryLogStorage=
 
 ############ ADVANCED PARAMETERS ################

2. Доработка сервера Zabbix и Zabbix-прокси

Теперь удалим поддержку чтения опций конфигурации HistoryStorageUrl и HistoryStorageOpts из исходного текста сервера Zabbix в файле src/zabbix_server/server.c и добавим в него поддержку чтения новых опций конфигурации HistoryStorage, HistoryUintStorage, HistoryStrStorage, HistoryTextStorage, HistoryLogStorage:

Index: zabbix-3.4.12-1+buster/src/zabbix_server/server.c
===================================================================
--- zabbix-3.4.12-1+buster.orig/src/zabbix_server/server.c
+++ zabbix-3.4.12-1+buster/src/zabbix_server/server.c
@@ -258,8 +258,11 @@ char       *CONFIG_TLS_PSK_FILE            = NULL;
 #endif
 
 char   *CONFIG_SOCKET_PATH             = NULL;
-char   *CONFIG_HISTORY_STORAGE_URL     = NULL;
-char   *CONFIG_HISTORY_STORAGE_OPTS    = NULL;
+char   *CONFIG_HISTORY_STORAGE         = NULL;
+char   *CONFIG_HISTORY_UINT_STORAGE    = NULL;
+char   *CONFIG_HISTORY_STR_STORAGE     = NULL;
+char   *CONFIG_HISTORY_TEXT_STORAGE    = NULL;
+char   *CONFIG_HISTORY_LOG_STORAGE     = NULL;
 
 int    get_process_info_by_thread(int local_server_num, unsigned char *local_process_type, int *local_process_num);
 
@@ -438,9 +441,6 @@ static void zbx_set_defaults(void)
 
        if (NULL == CONFIG_SSL_KEY_LOCATION)
                CONFIG_SSL_KEY_LOCATION = zbx_strdup(CONFIG_SSL_KEY_LOCATION, DATADIR "/zabbix/ssl/keys");
-
-       if (NULL == CONFIG_HISTORY_STORAGE_OPTS)
-               CONFIG_HISTORY_STORAGE_OPTS = zbx_strdup(CONFIG_HISTORY_STORAGE_OPTS, "uint,dbl,str,log,text");
 #endif
 
 #ifdef HAVE_SQLITE3
@@ -499,8 +499,11 @@ static void        zbx_validate_config(ZBX_TASK
        err |= (FAIL == check_cfg_feature_str("SSLCALocation", CONFIG_SSL_CA_LOCATION, "cURL library"));
        err |= (FAIL == check_cfg_feature_str("SSLCertLocation", CONFIG_SSL_CERT_LOCATION, "cURL library"));
        err |= (FAIL == check_cfg_feature_str("SSLKeyLocation", CONFIG_SSL_KEY_LOCATION, "cURL library"));
-       err |= (FAIL == check_cfg_feature_str("HistoryStorageURL", CONFIG_HISTORY_STORAGE_URL, "cURL library"));
-       err |= (FAIL == check_cfg_feature_str("HistoryStorageTypes", CONFIG_HISTORY_STORAGE_OPTS, "cURL library"));
+       err |= (FAIL == check_cfg_feature_str("HistoryStorage", CONFIG_HISTORY_STORAGE, "cURL library"));
+       err |= (FAIL == check_cfg_feature_str("HistoryUintStorage", CONFIG_HISTORY_UINT_STORAGE, "cURL library"));
+       err |= (FAIL == check_cfg_feature_str("HistoryStrStorage", CONFIG_HISTORY_STR_STORAGE, "cURL library"));
+       err |= (FAIL == check_cfg_feature_str("HistoryTextStorage", CONFIG_HISTORY_TEXT_STORAGE, "cURL library"));
+       err |= (FAIL == check_cfg_feature_str("HistoryLogStorage", CONFIG_HISTORY_LOG_STORAGE, "cURL library"));
 #endif
 
 #if !defined(HAVE_LIBXML2) || !defined(HAVE_LIBCURL)
@@ -696,9 +699,15 @@ static void        zbx_load_config(ZBX_TASK_EX
                        PARM_OPT,       1,                      100},
                {"StartPreprocessors",          &CONFIG_PREPROCESSOR_FORKS,             TYPE_INT,
                        PARM_OPT,       1,                      1000},
-               {"HistoryStorageURL",           &CONFIG_HISTORY_STORAGE_URL,            TYPE_STRING,
+               {"HistoryStorage",              &CONFIG_HISTORY_STORAGE,                TYPE_STRING,
+                       PARM_OPT,       0,                      0},
+               {"HistoryUintStorage",          &CONFIG_HISTORY_UINT_STORAGE,           TYPE_STRING,
+                       PARM_OPT,       0,                      0},
+               {"HistoryStrStorage",           &CONFIG_HISTORY_STR_STORAGE,            TYPE_STRING,
+                       PARM_OPT,       0,                      0},
+               {"HistoryTextStorage",          &CONFIG_HISTORY_TEXT_STORAGE,           TYPE_STRING,
                        PARM_OPT,       0,                      0},
-               {"HistoryStorageTypes",         &CONFIG_HISTORY_STORAGE_OPTS,           TYPE_STRING_LIST,
+               {"HistoryLogStorage",           &CONFIG_HISTORY_LOG_STORAGE,            TYPE_STRING,
                        PARM_OPT,       0,                      0},
                {NULL}
        };

Поскольку Zabbix-прокси реализован на основе сервера Zabbix, аналогичные фиктивные изменения нужно внести в исходный код Zabbix-прокси в файле src/zabbix_proxy/proxy.c:

Index: zabbix-3.4.12-1+buster/src/zabbix_proxy/proxy.c
===================================================================
--- zabbix-3.4.12-1+buster.orig/src/zabbix_proxy/proxy.c
+++ zabbix-3.4.12-1+buster/src/zabbix_proxy/proxy.c
@@ -251,8 +251,11 @@ char       *CONFIG_TLS_PSK_FILE            = NULL;
 
 char   *CONFIG_SOCKET_PATH             = NULL;
 
-char   *CONFIG_HISTORY_STORAGE_URL     = NULL;
-char   *CONFIG_HISTORY_STORAGE_OPTS    = NULL;
+char   *CONFIG_HISTORY_STORAGE         = NULL;
+char   *CONFIG_HISTORY_UINT_STORAGE    = NULL;
+char   *CONFIG_HISTORY_STR_STORAGE     = NULL;
+char   *CONFIG_HISTORY_TEXT_STORAGE    = NULL;
+char   *CONFIG_HISTORY_LOG_STORAGE     = NULL;
 
 int    get_process_info_by_thread(int local_server_num, unsigned char *local_process_type, int *local_process_num);

3. Доработка основы библиотеки zbxhistory

Суть вносимых изменений особенно наглядно можно увидеть в следующей заплатке для файла src/libs/zbxhistory/history.c. Вместо использования фиксированного URL для всех таблиц из переменной CONFIG_HISTORY_STORAGE_URL и переменной CONFIG_HISTORY_STORAGE_OPTS, которая предписывает использовать этот URL для таблиц указанных в ней типов, мы определяем тип хранилища для каждой из таблиц и вызываем функцию инициализации соответствующего модуля, передавая ей URL для доступа к этой конкретной таблице:

Index: zabbix-3.4.12-1+buster/src/libs/zbxhistory/history.c
===================================================================
--- zabbix-3.4.12-1+buster.orig/src/libs/zbxhistory/history.c
+++ zabbix-3.4.12-1+buster/src/libs/zbxhistory/history.c
@@ -27,8 +27,11 @@
 
 ZBX_VECTOR_IMPL(history_record, zbx_history_record_t);
 
-extern char    *CONFIG_HISTORY_STORAGE_URL;
-extern char    *CONFIG_HISTORY_STORAGE_OPTS;
+extern char    *CONFIG_HISTORY_STORAGE;
+extern char    *CONFIG_HISTORY_UINT_STORAGE;
+extern char    *CONFIG_HISTORY_STR_STORAGE;
+extern char    *CONFIG_HISTORY_TEXT_STORAGE;
+extern char    *CONFIG_HISTORY_LOG_STORAGE;
 
 zbx_history_iface_t    history_ifaces[ITEM_VALUE_TYPE_MAX];
 
@@ -46,16 +49,23 @@ zbx_history_iface_t history_ifaces[ITEM_
 int    zbx_history_init(char **error)
 {
        int             i, ret;
-       /* TODO: support per value type specific configuration */
-
-       const char      *opts[] = {"dbl", "str", "log", "uint", "text"};
+       char            *elastic_url;
+       const char      *opts[] = {
+                               CONFIG_HISTORY_STORAGE,
+                               CONFIG_HISTORY_STR_STORAGE,
+                               CONFIG_HISTORY_LOG_STORAGE,
+                               CONFIG_HISTORY_UINT_STORAGE,
+                               CONFIG_HISTORY_TEXT_STORAGE
+                       };
 
        for (i = 0; i < ITEM_VALUE_TYPE_MAX; i++)
        {
-               if (NULL == CONFIG_HISTORY_STORAGE_URL || NULL == strstr(CONFIG_HISTORY_STORAGE_OPTS, opts[i]))
-                       ret = zbx_history_sql_init(&history_ifaces[i], i, error);
+               if (elastic_url = zbx_strstartswith(opts[i], "elastic,"))
+                       ret = zbx_history_elastic_init(&history_ifaces[i], i, elastic_url, error);
+               /*else if (clickhouse_url = zbx_strstartswith(opts[i], "clickhouse,"))
+                       ret = zbx_history_clickhouse_init(&history_ifaces[i], i, clickhouse_url, error);*/
                else
-                       ret = zbx_history_elastic_init(&history_ifaces[i], i, error);
+                       ret = zbx_history_sql_init(&history_ifaces[i], i, error);
 
                if (FAIL == ret)
                        return FAIL;

В доработанном коде функции zbx_history_init добавлен закомментированный участок, обозначающий будущую поддержку хранилища ClickHouse.

Из кода удалён комментарий о необходимости реализовать раздельную возможность настройки типов хранилищ для каждой из таблиц, потому что именно это мы сейчас и делаем.

В коде используется новая функция zbx_startswith для проверки, что строка начинается с указанного префикса.

При доработке изменилась сигнатура функции zbx_history_elastic_init, теперь ей передаётся дополнительный аргумент - elastic_url.

Остановимся поподробнее на двух последних обстоятельствах.

4. Новая функция zbx_startswith

Во-первых, добавим объявление новой функции zbx_startswith в файл include/common.h и её реализацию в файл src/libs/zbxcommon/str.c:

Index: zabbix-3.4.12-1+buster/include/common.h
===================================================================
--- zabbix-3.4.12-1+buster.orig/include/common.h
+++ zabbix-3.4.12-1+buster/include/common.h
@@ -1114,6 +1114,7 @@ char      *zbx_time2str(time_t time);
 #define ZBX_NULL2STR(str)      (NULL != str ? str : "(null)")
 #define ZBX_NULL2EMPTY_STR(str)        (NULL != (str) ? (str) : "")
 
+char    *zbx_strstartswith(const char *str, const char *prefix);
 char   *zbx_strcasestr(const char *haystack, const char *needle);
 int    cmp_key_id(const char *key_1, const char *key_2);
 int    zbx_strncasecmp(const char *s1, const char *s2, size_t n);
Index: zabbix-3.4.12-1+buster/src/libs/zbxcommon/str.c
===================================================================
--- zabbix-3.4.12-1+buster.orig/src/libs/zbxcommon/str.c
+++ zabbix-3.4.12-1+buster/src/libs/zbxcommon/str.c
@@ -1827,6 +1827,38 @@ char     *zbx_time2str(time_t time)
        return buffer;
 }
 
+/******************************************************************************
+ *                                                                            *
+ * Function: zbx_startswith                                                   *
+ *                                                                            *
+ * Purpose: compare start of string str with string prefix                    *
+ *                                                                            *
+ * Parameters: str - [IN] null terminated source string                       *
+ *             prefix - [IN] null terminated prefix string                    *
+ *                                                                            *
+ * Return value: pointer to rest of str or NULL, if prefix not found          *
+ *                                                                            *
+ * Author: Vladimir Stupin                                                    *
+ *                                                                            *
+ ******************************************************************************/
+char   *zbx_strstartswith(const char *str, const char *prefix)
+{
+       if (NULL == prefix)
+               return (char *)str;
+
+       if (NULL == str)
+               return NULL;
+
+       while ('\0' != *prefix)
+       {
+               if ('\0' == *str || *str != *prefix)
+                       return NULL;
+               str++;
+               prefix++;
+       }
+       return (char *)str;
+}
+
 int    zbx_strncasecmp(const char *s1, const char *s2, size_t n)
 {
        if (NULL == s1 && NULL == s2)

5. Доработка поддержки Elasticsearch

Во-вторых, изменим объявление функции zbx_history_elastic_init в заголовочном файле src/libs/zbxhistory/history.h:

Index: zabbix-3.4.12-1+buster/src/libs/zbxhistory/history.h
===================================================================
--- zabbix-3.4.12-1+buster.orig/src/libs/zbxhistory/history.h
+++ zabbix-3.4.12-1+buster/src/libs/zbxhistory/history.h
@@ -47,6 +47,6 @@ struct zbx_history_iface
 int    zbx_history_sql_init(zbx_history_iface_t *hist, unsigned char value_type, char **error);
 
 /* elastic hist */
-int    zbx_history_elastic_init(zbx_history_iface_t *hist, unsigned char value_type, char **error);
+int    zbx_history_elastic_init(zbx_history_iface_t *hist, unsigned char value_type, const char *url, char **error);
 
 #endif

Осталось лишь соответствующим образом изменить реализацию этой функции в файле src/libs/zbxhistory/history_elastic.c:

Index: zabbix-3.4.12-1+buster/src/libs/zbxhistory/history_elastic.c
===================================================================
--- zabbix-3.4.12-1+buster.orig/src/libs/zbxhistory/history_elastic.c
+++ zabbix-3.4.12-1+buster/src/libs/zbxhistory/history_elastic.c
@@ -37,8 +37,6 @@
 
 const char     *value_type_str[] = {"dbl", "str", "log", "uint", "text"};
 
-extern char    *CONFIG_HISTORY_STORAGE_URL;
-
 typedef struct
 {
        char    *base_url;
@@ -912,7 +910,7 @@ static void elastic_flush(zbx_history_if
  *               FAIL    - otherwise                                                *
  *                                                                                  *
  ************************************************************************************/
-int    zbx_history_elastic_init(zbx_history_iface_t *hist, unsigned char value_type, char **error)
+int    zbx_history_elastic_init(zbx_history_iface_t *hist, unsigned char value_type, const char *url, char **error)
 {
        zbx_elastic_data_t      *data;
 
@@ -924,7 +922,7 @@ int zbx_history_elastic_init(zbx_history
 
        data = zbx_malloc(NULL, sizeof(zbx_elastic_data_t));
        memset(data, 0, sizeof(zbx_elastic_data_t));
-       data->base_url = zbx_strdup(NULL, CONFIG_HISTORY_STORAGE_URL);
+       data->base_url = zbx_strdup(NULL, url);
        zbx_rtrim(data->base_url, "/");
        data->buf = NULL;
        data->post_url = NULL;

Написать автору