mirror of
https://github.com/netdata/netdata.git
synced 2025-04-02 20:48:06 +00:00
Easily disable alarms, by persisting the silencers configuration (#6360)
This PR was created to fix #3414, here I am completing the job initiated by Christopher, among the newest features that we are bring we have JSON inside the core - We are bringing to the core the capacity to work with JSON files, this is available either using the JSON-C library case it is present in the system or using JSMN library that was incorporated to our core. The preference is to have JSON-C, because it is a more complete library, but case the user does not have the library installed we are keeping the JSMN for we do not lose the feature. Health LIST - We are bringing more one command to the Health API, now with the LIST it is possible to get in JSON format the alarms active with Netdata. Health reorganized - Previously we had duplicated code in different files, this PR is fixing this (Thanks @cakrit !), the Health is now better organized. Removing memory leak - The first implementation of the json.c was creating SILENCERS without to link it in anywhere. Now it has been linked properly. Script updated - We are bringing some changes to the script that tests the Health. This PR also fixes the race condition created by the previous new position of the SILENCERS creation, I had to move it to daemon/main.c, because after various tests, it was confirmed that the error could happen in different parts of the code, case it was not initialized before the threads starts. Component Name health directory health-cmd Additional Information Fixes #6356 and #3414
This commit is contained in:
parent
037a212faa
commit
c56e086ba3
40 changed files with 1642 additions and 121 deletions
CMakeLists.txtMakefile.amconfigure.ac
daemon
health
libnetdata
tests/health_mgmtapi
expected_list
ALARM_CPU_IOWAIT-list.jsonALARM_CPU_USAGE-list.jsonCONTEXT_SYSTEM_CPU-list.jsonDISABLE-list.jsonDISABLE_ALL-list.jsonDISABLE_ALL_ERROR-list.jsonDISABLE_SYSTEM_LOAD-list.jsonFAMILIES_LOAD-list.jsonHOSTS-list.jsonRESET-list.jsonSILENCE-list.jsonSILENCE_2-list.jsonSILENCE_3-list.jsonSILENCE_ALARM_CPU_USAGE-list.jsonSILENCE_ALARM_CPU_USAGE_LOAD_TRIGGER-list.jsonSILENCE_ALL-list.json
health-cmdapi-test.sh.inweb/api
|
@ -130,6 +130,15 @@ set(NETDATA_COMMON_CFLAGS ${NETDATA_COMMON_CFLAGS} ${OPENSSL_CFLAGS_OTHER})
|
|||
set(NETDATA_COMMON_LIBRARIES ${NETDATA_COMMON_LIBRARIES} ${OPENSSL_LIBRARIES})
|
||||
set(NETDATA_COMMON_INCLUDE_DIRS ${NETDATA_COMMON_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIRS})
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# JSON-C used to health
|
||||
|
||||
pkg_check_modules(JSON REQUIRED json-c)
|
||||
set(NETDATA_COMMON_CFLAGS ${NETDATA_COMMON_CFLAGS} ${JSONC_CFLAGS_OTHER})
|
||||
set(NETDATA_COMMON_LIBRARIES ${NETDATA_COMMON_LIBRARIES} ${JSON_LIBRARIES})
|
||||
set(NETDATA_COMMON_INCLUDE_DIRS ${NETDATA_COMMON_INCLUDE_DIRS} ${JSON_INCLUDE_DIRS})
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Detect libcap
|
||||
|
||||
|
@ -305,6 +314,12 @@ set(LIBNETDATA_FILES
|
|||
libnetdata/threads/threads.h
|
||||
libnetdata/url/url.c
|
||||
libnetdata/url/url.h
|
||||
libnetdata/json/json.c
|
||||
libnetdata/json/json.h
|
||||
libnetdata/json/jsmn.c
|
||||
libnetdata/json/jsmn.h
|
||||
libnetdata/health/health.c
|
||||
libnetdata/health/health.h
|
||||
libnetdata/socket/security.c
|
||||
libnetdata/socket/security.h)
|
||||
|
||||
|
|
|
@ -161,6 +161,12 @@ LIBNETDATA_FILES = \
|
|||
libnetdata/threads/threads.h \
|
||||
libnetdata/url/url.c \
|
||||
libnetdata/url/url.h \
|
||||
libnetdata/json/json.c \
|
||||
libnetdata/json/json.h \
|
||||
libnetdata/json/jsmn.c \
|
||||
libnetdata/json/jsmn.h \
|
||||
libnetdata/health/health.c \
|
||||
libnetdata/health/health.h \
|
||||
$(NULL)
|
||||
|
||||
APPS_PLUGIN_FILES = \
|
||||
|
@ -512,6 +518,7 @@ NETDATA_COMMON_LIBS = \
|
|||
$(OPTIONAL_LZ4_LIBS) \
|
||||
$(OPTIONAL_JUDY_LIBS) \
|
||||
$(OPTIONAL_SSL_LIBS) \
|
||||
$(OPTIONAL_JSONC_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
sbin_PROGRAMS += netdata
|
||||
|
|
38
configure.ac
38
configure.ac
|
@ -148,6 +148,12 @@ AC_ARG_ENABLE(
|
|||
,
|
||||
[enable_dbengine="detect"]
|
||||
)
|
||||
AC_ARG_ENABLE(
|
||||
[jsonc],
|
||||
[AS_HELP_STRING([--enable-jsonc], [Enable JSON-C support @<:@default autodetect@:>@])],
|
||||
,
|
||||
[enable_jsonc="detect"]
|
||||
)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# netdata required checks
|
||||
|
@ -347,6 +353,20 @@ AC_CHECK_LIB(
|
|||
OPTIONAL_SSL_CFLAGS="${SSL_CFLAGS}"
|
||||
OPTIONAL_SSL_LIBS="${SSL_LIBS}"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# JSON-C library
|
||||
|
||||
PKG_CHECK_MODULES([JSON],[json-c],AC_CHECK_LIB(
|
||||
[json-c],
|
||||
[json_object_get_type],
|
||||
[JSONC_LIBS="-ljson-c"]),AC_CHECK_LIB(
|
||||
[json],
|
||||
[json_object_get_type],
|
||||
[JSONC_LIBS="-ljson"])
|
||||
)
|
||||
|
||||
OPTIONAL_JSONC_LIBS="${JSONC_LIBS}"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# DB engine and HTTPS
|
||||
test "${enable_dbengine}" = "yes" -a -z "${UV_LIBS}" && \
|
||||
|
@ -381,6 +401,21 @@ fi
|
|||
AC_MSG_RESULT([${enable_https}])
|
||||
AM_CONDITIONAL([ENABLE_HTTPS], [test "${enable_https}" = "yes"])
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# JSON-C
|
||||
test "${enable_jsonc}" = "yes" -a -z "${JSONC_LIBS}" && \
|
||||
AC_MSG_ERROR([JSON-C required but not found. Try installing 'libjson-c-dev' or 'json-c'.])
|
||||
|
||||
AC_MSG_CHECKING([if json-c should be used])
|
||||
if test "${enable_jsonc}" != "no" -a "${JSONC_LIBS}"; then
|
||||
enable_jsonc="yes"
|
||||
AC_DEFINE([ENABLE_JSONC], [1], [netdata json-c usability])
|
||||
else
|
||||
enable_jsonc="no"
|
||||
fi
|
||||
AC_MSG_RESULT([${enable_jsonc}])
|
||||
AM_CONDITIONAL([ENABLE_JSONC], [test "${enable_jsonc}" = "yes"])
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# compiler options
|
||||
|
||||
|
@ -975,6 +1010,7 @@ AC_SUBST([OPTIONAL_UV_LIBS])
|
|||
AC_SUBST([OPTIONAL_LZ4_LIBS])
|
||||
AC_SUBST([OPTIONAL_JUDY_LIBS])
|
||||
AC_SUBST([OPTIONAL_SSL_LIBS])
|
||||
AC_SUBST([OPTIONAL_JSONC_LIBS])
|
||||
AC_SUBST([OPTIONAL_NFACCT_CFLAGS])
|
||||
AC_SUBST([OPTIONAL_NFACCT_LIBS])
|
||||
AC_SUBST([OPTIONAL_ZLIB_CFLAGS])
|
||||
|
@ -1051,6 +1087,8 @@ AC_CONFIG_FILES([
|
|||
libnetdata/storage_number/Makefile
|
||||
libnetdata/threads/Makefile
|
||||
libnetdata/url/Makefile
|
||||
libnetdata/json/Makefile
|
||||
libnetdata/health/Makefile
|
||||
registry/Makefile
|
||||
streaming/Makefile
|
||||
system/Makefile
|
||||
|
|
|
@ -774,6 +774,12 @@ void send_statistics( const char *action, const char *action_result, const char
|
|||
freez(command_to_run);
|
||||
}
|
||||
|
||||
void set_silencers_filename() {
|
||||
char filename[FILENAME_MAX + 1];
|
||||
snprintfz(filename, FILENAME_MAX, "%s/health.silencers.json", netdata_configured_varlib_dir);
|
||||
silencers_filename = config_get(CONFIG_SECTION_HEALTH, "silencers file", filename);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int i;
|
||||
int config_loaded = 0;
|
||||
|
@ -1101,6 +1107,11 @@ int main(int argc, char **argv) {
|
|||
security_init();
|
||||
#endif
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// This is the safest place to start the SILENCERS structure
|
||||
set_silencers_filename();
|
||||
health_initialize_global_silencers();
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// setup process signals
|
||||
|
||||
|
|
|
@ -13,18 +13,69 @@ unsigned int default_health_enabled = 1;
|
|||
// ----------------------------------------------------------------------------
|
||||
// health initialization
|
||||
|
||||
/**
|
||||
* User Config directory
|
||||
*
|
||||
* Get the config directory for health and return it.
|
||||
*
|
||||
* @return a pointer to the user config directory
|
||||
*/
|
||||
inline char *health_user_config_dir(void) {
|
||||
char buffer[FILENAME_MAX + 1];
|
||||
snprintfz(buffer, FILENAME_MAX, "%s/health.d", netdata_configured_user_config_dir);
|
||||
return config_get(CONFIG_SECTION_HEALTH, "health configuration directory", buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stock Config Directory
|
||||
*
|
||||
* Get the Stock config directory and return it.
|
||||
*
|
||||
* @return a pointer to the stock config directory.
|
||||
*/
|
||||
inline char *health_stock_config_dir(void) {
|
||||
char buffer[FILENAME_MAX + 1];
|
||||
snprintfz(buffer, FILENAME_MAX, "%s/health.d", netdata_configured_stock_config_dir);
|
||||
return config_get(CONFIG_SECTION_HEALTH, "stock health configuration directory", buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Silencers init
|
||||
*
|
||||
* Function used to initialize the silencer structure.
|
||||
*/
|
||||
void health_silencers_init(void) {
|
||||
struct stat statbuf;
|
||||
if (!stat(silencers_filename,&statbuf)) {
|
||||
off_t length = statbuf.st_size;
|
||||
if (length && length < HEALTH_SILENCERS_MAX_FILE_LEN) {
|
||||
FILE *fd = fopen(silencers_filename, "r");
|
||||
if (fd) {
|
||||
char *str = mallocz((length+1)* sizeof(char));
|
||||
if(str) {
|
||||
fread(str, sizeof(char), length, fd);
|
||||
str[length] = 0x00;
|
||||
json_parse(str, NULL, health_silencers_json_read_callback);
|
||||
freez(str);
|
||||
info("Parsed health silencers file %s", silencers_filename);
|
||||
}
|
||||
fclose(fd);
|
||||
} else {
|
||||
error("Cannot open the file %s",silencers_filename);
|
||||
}
|
||||
} else {
|
||||
error("Health silencers file %s has the size %ld that is out of range[ 1 , %d ]. Aborting read.", silencers_filename, length, HEALTH_SILENCERS_MAX_FILE_LEN);
|
||||
}
|
||||
} else {
|
||||
error("Cannot open the file %s",silencers_filename);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Health Init
|
||||
*
|
||||
* Initialize the health thread.
|
||||
*/
|
||||
void health_init(void) {
|
||||
debug(D_HEALTH, "Health configuration initializing");
|
||||
|
||||
|
@ -32,11 +83,20 @@ void health_init(void) {
|
|||
debug(D_HEALTH, "Health is disabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
health_silencers_init();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// re-load health configuration
|
||||
|
||||
/**
|
||||
* Reload host
|
||||
*
|
||||
* Reload configuration for a specific host.
|
||||
*
|
||||
* @param host the structure of the host that the function will reload the configuration.
|
||||
*/
|
||||
void health_reload_host(RRDHOST *host) {
|
||||
if(unlikely(!host->health_enabled))
|
||||
return;
|
||||
|
@ -84,6 +144,11 @@ void health_reload_host(RRDHOST *host) {
|
|||
rrdhost_unlock(host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload
|
||||
*
|
||||
* Reload the host configuration for all hosts.
|
||||
*/
|
||||
void health_reload(void) {
|
||||
|
||||
rrd_rdlock();
|
||||
|
@ -430,6 +495,16 @@ SILENCE_TYPE check_silenced(RRDCALC *rc, char* host, SILENCERS *silencers) {
|
|||
return STYPE_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Disabled Silenced
|
||||
*
|
||||
* Update the variable rrdcalc_flags of the structure RRDCALC according with the values of the host structure
|
||||
*
|
||||
* @param host structure that contains information about the host monitored.
|
||||
* @param rc structure with information about the alarm
|
||||
*
|
||||
* @return It returns 1 case rrdcalc_flags is DISABLED or 0 otherwise
|
||||
*/
|
||||
int update_disabled_silenced(RRDHOST *host, RRDCALC *rc) {
|
||||
uint32_t rrdcalc_flags_old = rc->rrdcalc_flags;
|
||||
// Clear the flags
|
||||
|
@ -459,6 +534,15 @@ int update_disabled_silenced(RRDHOST *host, RRDCALC *rc) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Health Main
|
||||
*
|
||||
* The main thread of the health system. In this function all the alarms will be processed.
|
||||
*
|
||||
* @param ptr is a pointer to the netdata_static_thread structure.
|
||||
*
|
||||
* @return It always returns NULL
|
||||
*/
|
||||
void *health_main(void *ptr) {
|
||||
netdata_thread_cleanup_push(health_main_cleanup, ptr);
|
||||
|
||||
|
@ -469,12 +553,6 @@ void *health_main(void *ptr) {
|
|||
time_t hibernation_delay = config_get_number(CONFIG_SECTION_HEALTH, "postpone alarms during hibernation for seconds", 60);
|
||||
|
||||
unsigned int loop = 0;
|
||||
|
||||
silencers = mallocz(sizeof(SILENCERS));
|
||||
silencers->all_alarms=0;
|
||||
silencers->stype=STYPE_NONE;
|
||||
silencers->silencers=NULL;
|
||||
|
||||
while(!netdata_exit) {
|
||||
loop++;
|
||||
debug(D_HEALTH, "Health monitoring iteration no %u started", loop);
|
||||
|
|
|
@ -35,16 +35,7 @@ extern unsigned int default_health_enabled;
|
|||
#define HEALTH_LISTEN_BACKLOG 4096
|
||||
#endif
|
||||
|
||||
#define HEALTH_ALARM_KEY "alarm"
|
||||
#define HEALTH_TEMPLATE_KEY "template"
|
||||
#define HEALTH_ON_KEY "on"
|
||||
#define HEALTH_CONTEXT_KEY "context"
|
||||
#define HEALTH_CHART_KEY "chart"
|
||||
#define HEALTH_HOST_KEY "hosts"
|
||||
#define HEALTH_OS_KEY "os"
|
||||
#define HEALTH_FAMILIES_KEY "families"
|
||||
#define HEALTH_LOOKUP_KEY "lookup"
|
||||
#define HEALTH_CALC_KEY "calc"
|
||||
#define HEALTH_EVERY_KEY "every"
|
||||
#define HEALTH_GREEN_KEY "green"
|
||||
#define HEALTH_RED_KEY "red"
|
||||
|
@ -57,38 +48,9 @@ extern unsigned int default_health_enabled;
|
|||
#define HEALTH_DELAY_KEY "delay"
|
||||
#define HEALTH_OPTIONS_KEY "options"
|
||||
|
||||
typedef struct silencer {
|
||||
char *alarms;
|
||||
SIMPLE_PATTERN *alarms_pattern;
|
||||
#define HEALTH_SILENCERS_MAX_FILE_LEN 10000
|
||||
|
||||
char *hosts;
|
||||
SIMPLE_PATTERN *hosts_pattern;
|
||||
|
||||
char *contexts;
|
||||
SIMPLE_PATTERN *contexts_pattern;
|
||||
|
||||
char *charts;
|
||||
SIMPLE_PATTERN *charts_pattern;
|
||||
|
||||
char *families;
|
||||
SIMPLE_PATTERN *families_pattern;
|
||||
|
||||
struct silencer *next;
|
||||
} SILENCER;
|
||||
|
||||
typedef enum silence_type {
|
||||
STYPE_NONE,
|
||||
STYPE_DISABLE_ALARMS,
|
||||
STYPE_SILENCE_NOTIFICATIONS
|
||||
} SILENCE_TYPE;
|
||||
|
||||
typedef struct silencers {
|
||||
int all_alarms;
|
||||
SILENCE_TYPE stype;
|
||||
SILENCER *silencers;
|
||||
} SILENCERS;
|
||||
|
||||
SILENCERS *silencers;
|
||||
char *silencers_filename;
|
||||
|
||||
extern void health_init(void);
|
||||
extern void *health_main(void *ptr);
|
||||
|
|
|
@ -490,7 +490,7 @@ static int health_readfile(const char *filename, void *data) {
|
|||
if(append < HEALTH_CONF_MAX_LINE)
|
||||
continue;
|
||||
else {
|
||||
error("Health configuration has too long muli-line at line %zu of file '%s'.", line, filename);
|
||||
error("Health configuration has too long multi-line at line %zu of file '%s'.", line, filename);
|
||||
}
|
||||
}
|
||||
append = 0;
|
||||
|
|
|
@ -11,6 +11,8 @@ SUBDIRS = \
|
|||
config \
|
||||
dictionary \
|
||||
eval \
|
||||
json \
|
||||
health \
|
||||
locks \
|
||||
log \
|
||||
popen \
|
||||
|
|
8
libnetdata/health/Makefile.am
Normal file
8
libnetdata/health/Makefile.am
Normal file
|
@ -0,0 +1,8 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
AUTOMAKE_OPTIONS = subdir-objects
|
||||
MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
|
||||
|
||||
|
||||
dist_noinst_DATA = \
|
||||
$(NULL)
|
170
libnetdata/health/health.c
Normal file
170
libnetdata/health/health.c
Normal file
|
@ -0,0 +1,170 @@
|
|||
#include "health.h"
|
||||
|
||||
/**
|
||||
* Create Silencer
|
||||
*
|
||||
* Allocate a new silencer to Netdata.
|
||||
*
|
||||
* @return It returns the address off the silencer on success and NULL otherwise
|
||||
*/
|
||||
SILENCER *create_silencer(void) {
|
||||
SILENCER *t = callocz(1, sizeof(SILENCER));
|
||||
debug(D_HEALTH, "HEALTH command API: Created empty silencer");
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Health Silencers add
|
||||
*
|
||||
* Add more one silencer to the list of silenecers.
|
||||
*
|
||||
* @param silencer
|
||||
*/
|
||||
void health_silencers_add(SILENCER *silencer) {
|
||||
// Add the created instance to the linked list in silencers
|
||||
silencer->next = silencers->silencers;
|
||||
silencers->silencers = silencer;
|
||||
debug(D_HEALTH, "HEALTH command API: Added silencer %s:%s:%s:%s:%s", silencer->alarms,
|
||||
silencer->charts, silencer->contexts, silencer->hosts, silencer->families
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Silencers Add Parameter
|
||||
*
|
||||
* Create a new silencer and adjust the variables
|
||||
*
|
||||
* @param silencer a pointer to the silencer that will be adjusted
|
||||
* @param key the key value sent by client
|
||||
* @param value the value sent to the key
|
||||
*
|
||||
* @return It returns the silencer configured on success and NULL otherwise
|
||||
*/
|
||||
SILENCER *health_silencers_addparam(SILENCER *silencer, char *key, char *value) {
|
||||
static uint32_t
|
||||
hash_alarm = 0,
|
||||
hash_template = 0,
|
||||
hash_chart = 0,
|
||||
hash_context = 0,
|
||||
hash_host = 0,
|
||||
hash_families = 0;
|
||||
|
||||
if (unlikely(!hash_alarm)) {
|
||||
hash_alarm = simple_uhash(HEALTH_ALARM_KEY);
|
||||
hash_template = simple_uhash(HEALTH_TEMPLATE_KEY);
|
||||
hash_chart = simple_uhash(HEALTH_CHART_KEY);
|
||||
hash_context = simple_uhash(HEALTH_CONTEXT_KEY);
|
||||
hash_host = simple_uhash(HEALTH_HOST_KEY);
|
||||
hash_families = simple_uhash(HEALTH_FAMILIES_KEY);
|
||||
}
|
||||
|
||||
uint32_t hash = simple_uhash(key);
|
||||
if (unlikely(silencer == NULL)) {
|
||||
if (
|
||||
(hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) ||
|
||||
(hash == hash_template && !strcasecmp(key, HEALTH_TEMPLATE_KEY)) ||
|
||||
(hash == hash_chart && !strcasecmp(key, HEALTH_CHART_KEY)) ||
|
||||
(hash == hash_context && !strcasecmp(key, HEALTH_CONTEXT_KEY)) ||
|
||||
(hash == hash_host && !strcasecmp(key, HEALTH_HOST_KEY)) ||
|
||||
(hash == hash_families && !strcasecmp(key, HEALTH_FAMILIES_KEY))
|
||||
) {
|
||||
silencer = create_silencer();
|
||||
if(!silencer) {
|
||||
error("Cannot add a new silencer to Netdata");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) {
|
||||
silencer->alarms = strdupz(value);
|
||||
silencer->alarms_pattern = simple_pattern_create(silencer->alarms, NULL, SIMPLE_PATTERN_EXACT);
|
||||
} else if (hash == hash_chart && !strcasecmp(key, HEALTH_CHART_KEY)) {
|
||||
silencer->charts = strdupz(value);
|
||||
silencer->charts_pattern = simple_pattern_create(silencer->charts, NULL, SIMPLE_PATTERN_EXACT);
|
||||
} else if (hash == hash_context && !strcasecmp(key, HEALTH_CONTEXT_KEY)) {
|
||||
silencer->contexts = strdupz(value);
|
||||
silencer->contexts_pattern = simple_pattern_create(silencer->contexts, NULL, SIMPLE_PATTERN_EXACT);
|
||||
} else if (hash == hash_host && !strcasecmp(key, HEALTH_HOST_KEY)) {
|
||||
silencer->hosts = strdupz(value);
|
||||
silencer->hosts_pattern = simple_pattern_create(silencer->hosts, NULL, SIMPLE_PATTERN_EXACT);
|
||||
} else if (hash == hash_families && !strcasecmp(key, HEALTH_FAMILIES_KEY)) {
|
||||
silencer->families = strdupz(value);
|
||||
silencer->families_pattern = simple_pattern_create(silencer->families, NULL, SIMPLE_PATTERN_EXACT);
|
||||
}
|
||||
|
||||
return silencer;
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON Read Callback
|
||||
*
|
||||
* Callback called by netdata to create the silencer.
|
||||
*
|
||||
* @param e the main json structure
|
||||
*
|
||||
* @return It always return 0.
|
||||
*/
|
||||
int health_silencers_json_read_callback(JSON_ENTRY *e)
|
||||
{
|
||||
switch(e->type) {
|
||||
case JSON_OBJECT:
|
||||
#ifndef ENABLE_JSONC
|
||||
e->callback_function = health_silencers_json_read_callback;
|
||||
if(e->name && strcmp(e->name,"")) {
|
||||
// init silencer
|
||||
debug(D_HEALTH, "JSON: Got object with a name, initializing new silencer for %s",e->name);
|
||||
#endif
|
||||
e->callback_data = create_silencer();
|
||||
if(e->callback_data) {
|
||||
health_silencers_add(e->callback_data);
|
||||
}
|
||||
#ifndef ENABLE_JSONC
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case JSON_ARRAY:
|
||||
e->callback_function = health_silencers_json_read_callback;
|
||||
break;
|
||||
|
||||
case JSON_STRING:
|
||||
if(!strcmp(e->name,"type")) {
|
||||
debug(D_HEALTH, "JSON: Processing type=%s",e->data.string);
|
||||
if (!strcmp(e->data.string,"SILENCE")) silencers->stype = STYPE_SILENCE_NOTIFICATIONS;
|
||||
else if (!strcmp(e->data.string,"DISABLE")) silencers->stype = STYPE_DISABLE_ALARMS;
|
||||
} else {
|
||||
debug(D_HEALTH, "JSON: Adding %s=%s", e->name, e->data.string);
|
||||
health_silencers_addparam(e->callback_data, e->name, e->data.string);
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_BOOLEAN:
|
||||
debug(D_HEALTH, "JSON: Processing all_alarms");
|
||||
silencers->all_alarms=e->data.boolean?1:0;
|
||||
break;
|
||||
|
||||
case JSON_NUMBER:
|
||||
case JSON_NULL:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize Global Silencers
|
||||
*
|
||||
* Initialize the silencer for the whole netdata system.
|
||||
*
|
||||
* @return It returns 0 on success and -1 otherwise
|
||||
*/
|
||||
int health_initialize_global_silencers() {
|
||||
silencers = mallocz(sizeof(SILENCERS));
|
||||
silencers->all_alarms=0;
|
||||
silencers->stype=STYPE_NONE;
|
||||
silencers->silencers=NULL;
|
||||
|
||||
return 0;
|
||||
}
|
55
libnetdata/health/health.h
Normal file
55
libnetdata/health/health.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
#ifndef NETDATA_HEALTH_LIB
|
||||
# define NETDATA_HEALTH_LIB 1
|
||||
|
||||
# include "../libnetdata.h"
|
||||
|
||||
#define HEALTH_ALARM_KEY "alarm"
|
||||
#define HEALTH_TEMPLATE_KEY "template"
|
||||
#define HEALTH_CONTEXT_KEY "context"
|
||||
#define HEALTH_CHART_KEY "chart"
|
||||
#define HEALTH_HOST_KEY "hosts"
|
||||
#define HEALTH_OS_KEY "os"
|
||||
#define HEALTH_FAMILIES_KEY "families"
|
||||
#define HEALTH_LOOKUP_KEY "lookup"
|
||||
#define HEALTH_CALC_KEY "calc"
|
||||
|
||||
typedef struct silencer {
|
||||
char *alarms;
|
||||
SIMPLE_PATTERN *alarms_pattern;
|
||||
|
||||
char *hosts;
|
||||
SIMPLE_PATTERN *hosts_pattern;
|
||||
|
||||
char *contexts;
|
||||
SIMPLE_PATTERN *contexts_pattern;
|
||||
|
||||
char *charts;
|
||||
SIMPLE_PATTERN *charts_pattern;
|
||||
|
||||
char *families;
|
||||
SIMPLE_PATTERN *families_pattern;
|
||||
|
||||
struct silencer *next;
|
||||
} SILENCER;
|
||||
|
||||
typedef enum silence_type {
|
||||
STYPE_NONE,
|
||||
STYPE_DISABLE_ALARMS,
|
||||
STYPE_SILENCE_NOTIFICATIONS
|
||||
} SILENCE_TYPE;
|
||||
|
||||
typedef struct silencers {
|
||||
int all_alarms;
|
||||
SILENCE_TYPE stype;
|
||||
SILENCER *silencers;
|
||||
} SILENCERS;
|
||||
|
||||
SILENCERS *silencers;
|
||||
|
||||
extern SILENCER *create_silencer(void);
|
||||
extern int health_silencers_json_read_callback(JSON_ENTRY *e);
|
||||
extern void health_silencers_add(SILENCER *silencer);
|
||||
extern SILENCER * health_silencers_addparam(SILENCER *silencer, char *key, char *value);
|
||||
extern int health_initialize_global_silencers();
|
||||
|
||||
#endif
|
9
libnetdata/json/Makefile.am
Normal file
9
libnetdata/json/Makefile.am
Normal file
|
@ -0,0 +1,9 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
AUTOMAKE_OPTIONS = subdir-objects
|
||||
MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
|
||||
|
||||
|
||||
dist_noinst_DATA = \
|
||||
README.md \
|
||||
$(NULL)
|
5
libnetdata/json/README.md
Normal file
5
libnetdata/json/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# json
|
||||
|
||||
`json` contains a parser for json strings, based on `jsmn` (https://github.com/zserge/jsmn), but case you have installed the JSON-C library, the installation script will prefer it, you can also force its use with `--enable-jsonc` in the compilation time.
|
||||
|
||||
[]()
|
326
libnetdata/json/jsmn.c
Normal file
326
libnetdata/json/jsmn.c
Normal file
|
@ -0,0 +1,326 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "jsmn.h"
|
||||
|
||||
/**
|
||||
* Alloc token
|
||||
*
|
||||
* Allocates a fresh unused token from the token pull.
|
||||
*
|
||||
* @param parser the controller
|
||||
* @param tokens the tokens I am working
|
||||
* @param num_tokens the number total of tokens.
|
||||
*
|
||||
* @return it returns the next token to work.
|
||||
*/
|
||||
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
|
||||
jsmntok_t *tokens, size_t num_tokens) {
|
||||
jsmntok_t *tok;
|
||||
if (parser->toknext >= num_tokens) {
|
||||
return NULL;
|
||||
}
|
||||
tok = &tokens[parser->toknext++];
|
||||
tok->start = tok->end = -1;
|
||||
tok->size = 0;
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
tok->parent = -1;
|
||||
#endif
|
||||
return tok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill Token
|
||||
*
|
||||
* Fills token type and boundaries.
|
||||
*
|
||||
* @param token the structure to set the values
|
||||
* @param type is the token type
|
||||
* @param start is the first position of the value
|
||||
* @param end is the end of the value
|
||||
*/
|
||||
static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
|
||||
int start, int end) {
|
||||
token->type = type;
|
||||
token->start = start;
|
||||
token->end = end;
|
||||
token->size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse primitive
|
||||
*
|
||||
* Fills next available token with JSON primitive.
|
||||
*
|
||||
* @param parser is the control structure
|
||||
* @param js is the json string
|
||||
* @param type is the token type
|
||||
*/
|
||||
static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js,
|
||||
size_t len, jsmntok_t *tokens, size_t num_tokens) {
|
||||
jsmntok_t *token;
|
||||
int start;
|
||||
|
||||
start = parser->pos;
|
||||
|
||||
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
|
||||
switch (js[parser->pos]) {
|
||||
#ifndef JSMN_STRICT
|
||||
/* In strict mode primitive must be followed by "," or "}" or "]" */
|
||||
case ':':
|
||||
#endif
|
||||
case '\t' : case '\r' : case '\n' : case ' ' :
|
||||
case ',' : case ']' : case '}' :
|
||||
goto found;
|
||||
}
|
||||
if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
}
|
||||
#ifdef JSMN_STRICT
|
||||
/* In strict mode primitive must be followed by a comma/object/array */
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_PART;
|
||||
#endif
|
||||
|
||||
found:
|
||||
if (tokens == NULL) {
|
||||
parser->pos--;
|
||||
return 0;
|
||||
}
|
||||
token = jsmn_alloc_token(parser, tokens, num_tokens);
|
||||
if (token == NULL) {
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_NOMEM;
|
||||
}
|
||||
jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
token->parent = parser->toksuper;
|
||||
#endif
|
||||
parser->pos--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse string
|
||||
*
|
||||
* Fills next token with JSON string.
|
||||
*
|
||||
* @param parser is the control structure
|
||||
* @param js is the json string
|
||||
* @param len is the js length
|
||||
* @param tokens is structure with the tokens mapped.
|
||||
* @param num_tokens is the total number of tokens
|
||||
*
|
||||
* @return It returns 0 on success and another integer otherwise
|
||||
*/
|
||||
static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
|
||||
size_t len, jsmntok_t *tokens, size_t num_tokens) {
|
||||
jsmntok_t *token;
|
||||
|
||||
int start = parser->pos;
|
||||
|
||||
parser->pos++;
|
||||
|
||||
/* Skip starting quote */
|
||||
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
|
||||
char c = js[parser->pos];
|
||||
|
||||
/* Quote: end of string */
|
||||
if (c == '\"') {
|
||||
if (tokens == NULL) {
|
||||
return 0;
|
||||
}
|
||||
token = jsmn_alloc_token(parser, tokens, num_tokens);
|
||||
if (token == NULL) {
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_NOMEM;
|
||||
}
|
||||
jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
token->parent = parser->toksuper;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Backslash: Quoted symbol expected */
|
||||
if (c == '\\') {
|
||||
parser->pos++;
|
||||
switch (js[parser->pos]) {
|
||||
/* Allowed escaped symbols */
|
||||
case '\"': case '/' : case '\\' : case 'b' :
|
||||
case 'f' : case 'r' : case 'n' : case 't' :
|
||||
break;
|
||||
/* Allows escaped symbol \uXXXX */
|
||||
case 'u':
|
||||
parser->pos++;
|
||||
int i = 0;
|
||||
for(; i < 4 && js[parser->pos] != '\0'; i++) {
|
||||
/* If it isn't a hex character we have an error */
|
||||
if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
|
||||
(js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
|
||||
(js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
parser->pos++;
|
||||
}
|
||||
parser->pos--;
|
||||
break;
|
||||
/* Unexpected symbol */
|
||||
default:
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_PART;
|
||||
}
|
||||
|
||||
/**
|
||||
* JSMN Parse
|
||||
*
|
||||
* Parse JSON string and fill tokens.
|
||||
*
|
||||
* @param parser the auxiliar vector used to parser
|
||||
* @param js the string to parse
|
||||
* @param len the string length
|
||||
* @param tokens the place to map the tokens
|
||||
* @param num_tokens the number of tokens present in the tokens structure.
|
||||
*
|
||||
* @return It returns the number of tokens present in the string on success or a negative number otherwise
|
||||
*/
|
||||
jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
|
||||
jsmntok_t *tokens, unsigned int num_tokens) {
|
||||
jsmnerr_t r;
|
||||
int i;
|
||||
jsmntok_t *token;
|
||||
int count = 0;
|
||||
|
||||
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
|
||||
char c;
|
||||
jsmntype_t type;
|
||||
|
||||
c = js[parser->pos];
|
||||
switch (c) {
|
||||
case '{': case '[':
|
||||
count++;
|
||||
if (tokens == NULL) {
|
||||
break;
|
||||
}
|
||||
token = jsmn_alloc_token(parser, tokens, num_tokens);
|
||||
if (token == NULL)
|
||||
return JSMN_ERROR_NOMEM;
|
||||
if (parser->toksuper != -1) {
|
||||
tokens[parser->toksuper].size++;
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
token->parent = parser->toksuper;
|
||||
#endif
|
||||
}
|
||||
token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
|
||||
token->start = parser->pos;
|
||||
parser->toksuper = parser->toknext - 1;
|
||||
break;
|
||||
case '}': case ']':
|
||||
if (tokens == NULL)
|
||||
break;
|
||||
type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
if (parser->toknext < 1) {
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
token = &tokens[parser->toknext - 1];
|
||||
for (;;) {
|
||||
if (token->start != -1 && token->end == -1) {
|
||||
if (token->type != type) {
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
token->end = parser->pos + 1;
|
||||
parser->toksuper = token->parent;
|
||||
break;
|
||||
}
|
||||
if (token->parent == -1) {
|
||||
break;
|
||||
}
|
||||
token = &tokens[token->parent];
|
||||
}
|
||||
#else
|
||||
for (i = parser->toknext - 1; i >= 0; i--) {
|
||||
token = &tokens[i];
|
||||
if (token->start != -1 && token->end == -1) {
|
||||
if (token->type != type) {
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
parser->toksuper = -1;
|
||||
token->end = parser->pos + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Error if unmatched closing bracket */
|
||||
if (i == -1) return JSMN_ERROR_INVAL;
|
||||
for (; i >= 0; i--) {
|
||||
token = &tokens[i];
|
||||
if (token->start != -1 && token->end == -1) {
|
||||
parser->toksuper = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case '\"':
|
||||
r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
|
||||
if (r < 0) return r;
|
||||
count++;
|
||||
if (parser->toksuper != -1 && tokens != NULL)
|
||||
tokens[parser->toksuper].size++;
|
||||
break;
|
||||
case '\t' : case '\r' : case '\n' : case ':' : case ',': case ' ':
|
||||
break;
|
||||
#ifdef JSMN_STRICT
|
||||
/* In strict mode primitives are: numbers and booleans */
|
||||
case '-': case '0': case '1' : case '2': case '3' : case '4':
|
||||
case '5': case '6': case '7' : case '8': case '9':
|
||||
case 't': case 'f': case 'n' :
|
||||
#else
|
||||
/* In non-strict mode every unquoted value is a primitive */
|
||||
default:
|
||||
#endif
|
||||
r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
|
||||
if (r < 0) return r;
|
||||
count++;
|
||||
if (parser->toksuper != -1 && tokens != NULL)
|
||||
tokens[parser->toksuper].size++;
|
||||
break;
|
||||
|
||||
#ifdef JSMN_STRICT
|
||||
/* Unexpected char in strict mode */
|
||||
default:
|
||||
return JSMN_ERROR_INVAL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
for (i = parser->toknext - 1; i >= 0; i--) {
|
||||
/* Unmatched opened object or array */
|
||||
if (tokens[i].start != -1 && tokens[i].end == -1) {
|
||||
return JSMN_ERROR_PART;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* JSMN Init
|
||||
*
|
||||
* Creates a new parser based over a given buffer with an array of tokens
|
||||
* available.
|
||||
*
|
||||
* @param parser is the structure with values to reset
|
||||
*/
|
||||
void jsmn_init(jsmn_parser *parser) {
|
||||
parser->pos = 0;
|
||||
parser->toknext = 0;
|
||||
parser->toksuper = -1;
|
||||
}
|
75
libnetdata/json/jsmn.h
Normal file
75
libnetdata/json/jsmn.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
#ifndef __JSMN_H_
|
||||
#define __JSMN_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
/**
|
||||
* JSON type identifier. Basic types are:
|
||||
* o Object
|
||||
* o Array
|
||||
* o String
|
||||
* o Other primitive: number, boolean (true/false) or null
|
||||
*/
|
||||
typedef enum {
|
||||
JSMN_PRIMITIVE = 0,
|
||||
JSMN_OBJECT = 1,
|
||||
JSMN_ARRAY = 2,
|
||||
JSMN_STRING = 3
|
||||
} jsmntype_t;
|
||||
|
||||
typedef enum {
|
||||
/* Not enough tokens were provided */
|
||||
JSMN_ERROR_NOMEM = -1,
|
||||
/* Invalid character inside JSON string */
|
||||
JSMN_ERROR_INVAL = -2,
|
||||
/* The string is not a full JSON packet, more bytes expected */
|
||||
JSMN_ERROR_PART = -3,
|
||||
} jsmnerr_t;
|
||||
|
||||
/**
|
||||
* JSON token description.
|
||||
*
|
||||
* @param type type (object, array, string etc.)
|
||||
* @param start start position in JSON data string
|
||||
* @param end end position in JSON data string
|
||||
*/
|
||||
typedef struct {
|
||||
jsmntype_t type;
|
||||
int start;
|
||||
int end;
|
||||
int size;
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
int parent;
|
||||
#endif
|
||||
} jsmntok_t;
|
||||
|
||||
/**
|
||||
* JSON parser. Contains an array of token blocks available. Also stores
|
||||
* the string being parsed now and current position in that string
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned int pos; /* offset in the JSON string */
|
||||
unsigned int toknext; /* next token to allocate */
|
||||
int toksuper; /* superior token node, e.g parent object or array */
|
||||
} jsmn_parser;
|
||||
|
||||
/**
|
||||
* Create JSON parser over an array of tokens
|
||||
*/
|
||||
void jsmn_init(jsmn_parser *parser);
|
||||
|
||||
/**
|
||||
* Run JSON parser. It parses a JSON data string into and array of tokens, each describing
|
||||
* a single JSON object.
|
||||
*/
|
||||
jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
|
||||
jsmntok_t *tokens, unsigned int num_tokens);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __JSMN_H_ */
|
546
libnetdata/json/json.c
Normal file
546
libnetdata/json/json.c
Normal file
|
@ -0,0 +1,546 @@
|
|||
#include "jsmn.h"
|
||||
#include "../libnetdata.h"
|
||||
#include "json.h"
|
||||
#include "libnetdata/libnetdata.h"
|
||||
#include "../../health/health.h"
|
||||
|
||||
#define JSON_TOKENS 1024
|
||||
|
||||
int json_tokens = JSON_TOKENS;
|
||||
|
||||
/**
|
||||
* Json Tokenise
|
||||
*
|
||||
* Map the string given inside tokens.
|
||||
*
|
||||
* @param js is the string used to create the tokens
|
||||
* @param len is the string length
|
||||
* @param count the number of tokens present in the string
|
||||
*
|
||||
* @return it returns the json parsed in tokens
|
||||
*/
|
||||
#ifdef ENABLE_JSONC
|
||||
json_object *json_tokenise(char *js) {
|
||||
if(!js) {
|
||||
error("JSON: json string is empty.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
json_object *token = json_tokener_parse(js);
|
||||
if(!token) {
|
||||
error("JSON: Invalid json string.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
#else
|
||||
jsmntok_t *json_tokenise(char *js, size_t len, size_t *count)
|
||||
{
|
||||
int n = json_tokens;
|
||||
if(!js || !len) {
|
||||
error("JSON: json string is empty.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jsmn_parser parser;
|
||||
jsmn_init(&parser);
|
||||
|
||||
jsmntok_t *tokens = mallocz(sizeof(jsmntok_t) * n);
|
||||
if(!tokens) return NULL;
|
||||
|
||||
int ret = jsmn_parse(&parser, js, len, tokens, n);
|
||||
while (ret == JSMN_ERROR_NOMEM) {
|
||||
n *= 2;
|
||||
jsmntok_t *new = reallocz(tokens, sizeof(jsmntok_t) * n);
|
||||
if(!new) {
|
||||
freez(tokens);
|
||||
return NULL;
|
||||
}
|
||||
tokens = new;
|
||||
ret = jsmn_parse(&parser, js, len, tokens, n);
|
||||
}
|
||||
|
||||
if (ret == JSMN_ERROR_INVAL) {
|
||||
error("JSON: Invalid json string.");
|
||||
freez(tokens);
|
||||
return NULL;
|
||||
}
|
||||
else if (ret == JSMN_ERROR_PART) {
|
||||
error("JSON: Truncated JSON string.");
|
||||
freez(tokens);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(count) *count = (size_t)ret;
|
||||
|
||||
if(json_tokens < n) json_tokens = n;
|
||||
return tokens;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Callback Print
|
||||
*
|
||||
* Set callback print case necesary and wrinte an information inside a buffer to write in the log.
|
||||
*
|
||||
* @param e a pointer for a structure that has the complete information about json structure.
|
||||
*
|
||||
* @return It always return 0
|
||||
*/
|
||||
int json_callback_print(JSON_ENTRY *e)
|
||||
{
|
||||
BUFFER *wb=buffer_create(300);
|
||||
|
||||
buffer_sprintf(wb,"%s = ", e->name);
|
||||
char txt[50];
|
||||
switch(e->type) {
|
||||
case JSON_OBJECT:
|
||||
e->callback_function = json_callback_print;
|
||||
buffer_strcat(wb,"OBJECT");
|
||||
break;
|
||||
|
||||
case JSON_ARRAY:
|
||||
e->callback_function = json_callback_print;
|
||||
sprintf(txt,"ARRAY[%lu]", e->data.items);
|
||||
buffer_strcat(wb, txt);
|
||||
break;
|
||||
|
||||
case JSON_STRING:
|
||||
buffer_strcat(wb, e->data.string);
|
||||
break;
|
||||
|
||||
case JSON_NUMBER:
|
||||
sprintf(txt,"%Lf", e->data.number);
|
||||
buffer_strcat(wb,txt);
|
||||
|
||||
break;
|
||||
|
||||
case JSON_BOOLEAN:
|
||||
buffer_strcat(wb, e->data.boolean?"TRUE":"FALSE");
|
||||
break;
|
||||
|
||||
case JSON_NULL:
|
||||
buffer_strcat(wb,"NULL");
|
||||
break;
|
||||
}
|
||||
info("JSON: %s", buffer_tostring(wb));
|
||||
buffer_free(wb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* JSONC Set String
|
||||
*
|
||||
* Set the string value of the structure JSON_ENTRY.
|
||||
*
|
||||
* @param e the output structure
|
||||
*/
|
||||
static inline void json_jsonc_set_string(JSON_ENTRY *e,char *key,const char *value) {
|
||||
size_t length = strlen(key);
|
||||
e->type = JSON_STRING;
|
||||
memcpy(e->name,key,length);
|
||||
e->name[length] = 0x00;
|
||||
e->data.string = (char *) value;
|
||||
}
|
||||
|
||||
|
||||
#ifdef ENABLE_JSONC
|
||||
/**
|
||||
* JSONC set Boolean
|
||||
*
|
||||
* Set the boolean value of the structure JSON_ENTRY
|
||||
*
|
||||
* @param e the output structure
|
||||
* @param value the input value
|
||||
*/
|
||||
static inline void json_jsonc_set_boolean(JSON_ENTRY *e,int value) {
|
||||
e->type = JSON_BOOLEAN;
|
||||
e->data.boolean = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse Array
|
||||
*
|
||||
* Parse the array object.
|
||||
*
|
||||
* @param ptr the pointer for the object that we will parse.
|
||||
* @param callback_data additional data to be used together the callback function
|
||||
* @param callback_function function used to create a silencer.
|
||||
*/
|
||||
static inline void json_jsonc_parse_array(json_object *ptr, void *callback_data,int (*callback_function)(struct json_entry *)) {
|
||||
int end = json_object_array_length(ptr);
|
||||
JSON_ENTRY e;
|
||||
|
||||
if(end) {
|
||||
int i;
|
||||
i = 0;
|
||||
|
||||
enum json_type type;
|
||||
do {
|
||||
json_object *jvalue = json_object_array_get_idx(ptr, i);
|
||||
if(jvalue) {
|
||||
e.callback_data = callback_data;
|
||||
e.type = JSON_OBJECT;
|
||||
callback_function(&e);
|
||||
json_object_object_foreach(jvalue, key, val) {
|
||||
type = json_object_get_type(val);
|
||||
if (type == json_type_array) {
|
||||
e.type = JSON_ARRAY;
|
||||
json_jsonc_parse_array(val, callback_data, callback_function);
|
||||
} else if (type == json_type_object) {
|
||||
json_walk(val,callback_data,callback_function);
|
||||
} else if (type == json_type_string) {
|
||||
json_jsonc_set_string(&e,key,json_object_get_string(val));
|
||||
callback_function(&e);
|
||||
} else if (type == json_type_boolean) {
|
||||
json_jsonc_set_boolean(&e,json_object_get_boolean(val));
|
||||
callback_function(&e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} while (++i < end);
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
/**
|
||||
* Walk string
|
||||
*
|
||||
* Set JSON_ENTRY to string and map the values from jsmntok_t.
|
||||
*
|
||||
* @param js the original string
|
||||
* @param t the tokens
|
||||
* @param start the first position
|
||||
* @param e the output structure.
|
||||
*
|
||||
* @return It always return 1
|
||||
*/
|
||||
size_t json_walk_string(char *js, jsmntok_t *t, size_t start, JSON_ENTRY *e)
|
||||
{
|
||||
char old = js[t[start].end];
|
||||
js[t[start].end] = '\0';
|
||||
e->original_string = &js[t[start].start];
|
||||
|
||||
e->type = JSON_STRING;
|
||||
e->data.string = e->original_string;
|
||||
if(e->callback_function) e->callback_function(e);
|
||||
js[t[start].end] = old;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walk Primitive
|
||||
*
|
||||
* Define the data type of the string
|
||||
*
|
||||
* @param js the original string
|
||||
* @param t the tokens
|
||||
* @param start the first position
|
||||
* @param e the output structure.
|
||||
*
|
||||
* @return It always return 1
|
||||
*/
|
||||
size_t json_walk_primitive(char *js, jsmntok_t *t, size_t start, JSON_ENTRY *e)
|
||||
{
|
||||
char old = js[t[start].end];
|
||||
js[t[start].end] = '\0';
|
||||
e->original_string = &js[t[start].start];
|
||||
|
||||
switch(e->original_string[0]) {
|
||||
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
|
||||
case '8': case '9': case '-': case '.':
|
||||
e->type = JSON_NUMBER;
|
||||
e->data.number = strtold(e->original_string, NULL);
|
||||
break;
|
||||
|
||||
case 't': case 'T':
|
||||
e->type = JSON_BOOLEAN;
|
||||
e->data.boolean = 1;
|
||||
break;
|
||||
|
||||
case 'f': case 'F':
|
||||
e->type = JSON_BOOLEAN;
|
||||
e->data.boolean = 0;
|
||||
break;
|
||||
|
||||
case 'n': case 'N':
|
||||
default:
|
||||
e->type = JSON_NULL;
|
||||
break;
|
||||
}
|
||||
if(e->callback_function) e->callback_function(e);
|
||||
js[t[start].end] = old;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Array
|
||||
*
|
||||
* Measure the array length
|
||||
*
|
||||
* @param js the original string
|
||||
* @param t the tokens
|
||||
* @param nest the length of structure t
|
||||
* @param start the first position
|
||||
* @param e the output structure.
|
||||
*
|
||||
* @return It returns the array length
|
||||
*/
|
||||
size_t json_walk_array(char *js, jsmntok_t *t, size_t nest, size_t start, JSON_ENTRY *e)
|
||||
{
|
||||
JSON_ENTRY ne = {
|
||||
.name = "",
|
||||
.fullname = "",
|
||||
.callback_data = NULL,
|
||||
.callback_function = NULL
|
||||
};
|
||||
|
||||
char old = js[t[start].end];
|
||||
js[t[start].end] = '\0';
|
||||
ne.original_string = &js[t[start].start];
|
||||
|
||||
memcpy(&ne, e, sizeof(JSON_ENTRY));
|
||||
ne.type = JSON_ARRAY;
|
||||
ne.data.items = t[start].size;
|
||||
ne.callback_function = NULL;
|
||||
ne.name[0]='\0';
|
||||
ne.fullname[0]='\0';
|
||||
if(e->callback_function) e->callback_function(&ne);
|
||||
js[t[start].end] = old;
|
||||
|
||||
size_t i, init = start, size = t[start].size;
|
||||
|
||||
start++;
|
||||
for(i = 0; i < size ; i++) {
|
||||
ne.pos = i;
|
||||
if (!e->name || !e->fullname || strlen(e->name) > JSON_NAME_LEN - 24 || strlen(e->fullname) > JSON_FULLNAME_LEN -24) {
|
||||
info("JSON: JSON walk_array ignoring element with name:%s fullname:%s",e->name, e->fullname);
|
||||
continue;
|
||||
}
|
||||
sprintf(ne.name, "%s[%lu]", e->name, i);
|
||||
sprintf(ne.fullname, "%s[%lu]", e->fullname, i);
|
||||
|
||||
switch(t[start].type) {
|
||||
case JSMN_PRIMITIVE:
|
||||
start += json_walk_primitive(js, t, start, &ne);
|
||||
break;
|
||||
|
||||
case JSMN_OBJECT:
|
||||
start += json_walk_object(js, t, nest + 1, start, &ne);
|
||||
break;
|
||||
|
||||
case JSMN_ARRAY:
|
||||
start += json_walk_array(js, t, nest + 1, start, &ne);
|
||||
break;
|
||||
|
||||
case JSMN_STRING:
|
||||
start += json_walk_string(js, t, start, &ne);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return start - init;
|
||||
}
|
||||
|
||||
/**
|
||||
* Object
|
||||
*
|
||||
* Measure the Object length
|
||||
*
|
||||
* @param js the original string
|
||||
* @param t the tokens
|
||||
* @param nest the length of structure t
|
||||
* @param start the first position
|
||||
* @param e the output structure.
|
||||
*
|
||||
* @return It returns the Object length
|
||||
*/
|
||||
size_t json_walk_object(char *js, jsmntok_t *t, size_t nest, size_t start, JSON_ENTRY *e)
|
||||
{
|
||||
JSON_ENTRY ne = {
|
||||
.name = "",
|
||||
.fullname = "",
|
||||
.callback_data = NULL,
|
||||
.callback_function = NULL
|
||||
};
|
||||
|
||||
char old = js[t[start].end];
|
||||
js[t[start].end] = '\0';
|
||||
ne.original_string = &js[t[start].start];
|
||||
memcpy(&ne, e, sizeof(JSON_ENTRY));
|
||||
ne.type = JSON_OBJECT;
|
||||
ne.callback_function = NULL;
|
||||
if(e->callback_function) e->callback_function(&ne);
|
||||
js[t[start].end] = old;
|
||||
|
||||
int key = 1;
|
||||
size_t i, init = start, size = t[start].size;
|
||||
|
||||
start++;
|
||||
for(i = 0; i < size ; i++) {
|
||||
switch(t[start].type) {
|
||||
case JSMN_PRIMITIVE:
|
||||
start += json_walk_primitive(js, t, start, &ne);
|
||||
key = 1;
|
||||
break;
|
||||
|
||||
case JSMN_OBJECT:
|
||||
start += json_walk_object(js, t, nest + 1, start, &ne);
|
||||
key = 1;
|
||||
break;
|
||||
|
||||
case JSMN_ARRAY:
|
||||
start += json_walk_array(js, t, nest + 1, start, &ne);
|
||||
key = 1;
|
||||
break;
|
||||
|
||||
case JSMN_STRING:
|
||||
default:
|
||||
if(key) {
|
||||
int len = t[start].end - t[start].start;
|
||||
if (unlikely(len>JSON_NAME_LEN)) len=JSON_NAME_LEN;
|
||||
strncpy(ne.name, &js[t[start].start], len);
|
||||
ne.name[len] = '\0';
|
||||
len=strlen(e->fullname) + strlen(e->fullname[0]?".":"") + strlen(ne.name);
|
||||
char *c = mallocz((len+1)*sizeof(char));
|
||||
sprintf(c,"%s%s%s", e->fullname, e->fullname[0]?".":"", ne.name);
|
||||
if (unlikely(len>JSON_FULLNAME_LEN)) len=JSON_FULLNAME_LEN;
|
||||
strncpy(ne.fullname, c, len);
|
||||
freez(c);
|
||||
start++;
|
||||
key = 0;
|
||||
}
|
||||
else {
|
||||
start += json_walk_string(js, t, start, &ne);
|
||||
key = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return start - init;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Tree
|
||||
*
|
||||
* Call the correct walk function according its type.
|
||||
*
|
||||
* @param t the json object to work
|
||||
* @param callback_data additional data to be used together the callback function
|
||||
* @param callback_function function used to create a silencer.
|
||||
*
|
||||
* @return It always return 1
|
||||
*/
|
||||
#ifdef ENABLE_JSONC
|
||||
size_t json_walk(json_object *t, void *callback_data, int (*callback_function)(struct json_entry *)) {
|
||||
JSON_ENTRY e;
|
||||
|
||||
e.callback_data = callback_data;
|
||||
enum json_type type;
|
||||
json_object_object_foreach(t, key, val) {
|
||||
type = json_object_get_type(val);
|
||||
if (type == json_type_array) {
|
||||
e.type = JSON_ARRAY;
|
||||
json_jsonc_parse_array(val,NULL,health_silencers_json_read_callback);
|
||||
} else if (type == json_type_object) {
|
||||
e.type = JSON_OBJECT;
|
||||
} else if (type == json_type_string) {
|
||||
json_jsonc_set_string(&e,key,json_object_get_string(val));
|
||||
callback_function(&e);
|
||||
} else if (type == json_type_boolean) {
|
||||
json_jsonc_set_boolean(&e,json_object_get_boolean(val));
|
||||
callback_function(&e);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* Tree
|
||||
*
|
||||
* Call the correct walk function according its type.
|
||||
*
|
||||
* @param js the original string
|
||||
* @param t the tokens
|
||||
* @param callback_data additional data to be used together the callback function
|
||||
* @param callback_function function used to create a silencer.
|
||||
*
|
||||
* @return It always return 1
|
||||
*/
|
||||
size_t json_walk_tree(char *js, jsmntok_t *t, void *callback_data, int (*callback_function)(struct json_entry *))
|
||||
{
|
||||
JSON_ENTRY e = {
|
||||
.name = "",
|
||||
.fullname = "",
|
||||
.callback_data = callback_data,
|
||||
.callback_function = callback_function
|
||||
};
|
||||
|
||||
switch (t[0].type) {
|
||||
case JSMN_OBJECT:
|
||||
e.type = JSON_OBJECT;
|
||||
json_walk_object(js, t, 0, 0, &e);
|
||||
break;
|
||||
|
||||
case JSMN_ARRAY:
|
||||
e.type = JSON_ARRAY;
|
||||
json_walk_array(js, t, 0, 0, &e);
|
||||
break;
|
||||
|
||||
case JSMN_PRIMITIVE:
|
||||
case JSMN_STRING:
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* JSON Parse
|
||||
*
|
||||
* Parse the json message with the callback function
|
||||
*
|
||||
* @param js the string that the callback function will parse
|
||||
* @param callback_data additional data to be used together the callback function
|
||||
* @param callback_function function used to create a silencer.
|
||||
*
|
||||
* @return JSON_OK case everything happend as expected, JSON_CANNOT_PARSE case there were errors in the
|
||||
* parsing procces and JSON_CANNOT_DOWNLOAD case the string given(js) is NULL.
|
||||
*/
|
||||
int json_parse(char *js, void *callback_data, int (*callback_function)(JSON_ENTRY *))
|
||||
{
|
||||
if(js) {
|
||||
#ifdef ENABLE_JSONC
|
||||
json_object *tokens = json_tokenise(js);
|
||||
#else
|
||||
size_t count;
|
||||
jsmntok_t *tokens = json_tokenise(js, strlen(js), &count);
|
||||
#endif
|
||||
|
||||
if(tokens) {
|
||||
#ifdef ENABLE_JSONC
|
||||
json_walk(tokens, callback_data, callback_function);
|
||||
json_object_put(tokens);
|
||||
#else
|
||||
json_walk_tree(js, tokens, callback_data, callback_function);
|
||||
freez(tokens);
|
||||
#endif
|
||||
return JSON_OK;
|
||||
}
|
||||
|
||||
return JSON_CANNOT_PARSE;
|
||||
}
|
||||
|
||||
return JSON_CANNOT_DOWNLOAD;
|
||||
}
|
||||
|
||||
/*
|
||||
int json_test(char *str)
|
||||
{
|
||||
return json_parse(str, NULL, json_callback_print);
|
||||
}
|
||||
*/
|
72
libnetdata/json/json.h
Normal file
72
libnetdata/json/json.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
#ifndef CHECKIN_JSON_H
|
||||
#define CHECKIN_JSON_H 1
|
||||
|
||||
|
||||
#if ENABLE_JSONC
|
||||
# include <json-c/json.h>
|
||||
#endif
|
||||
|
||||
#include "jsmn.h"
|
||||
|
||||
//https://www.ibm.com/support/knowledgecenter/en/SS9H2Y_7.6.0/com.ibm.dp.doc/json_parserlimits.html
|
||||
#define JSON_NAME_LEN 256
|
||||
#define JSON_FULLNAME_LEN 1024
|
||||
|
||||
typedef enum {
|
||||
JSON_OBJECT = 0,
|
||||
JSON_ARRAY = 1,
|
||||
JSON_STRING = 2,
|
||||
JSON_NUMBER = 3,
|
||||
JSON_BOOLEAN = 4,
|
||||
JSON_NULL = 5,
|
||||
} JSON_ENTRY_TYPE;
|
||||
|
||||
typedef struct json_entry {
|
||||
JSON_ENTRY_TYPE type;
|
||||
char name[JSON_NAME_LEN + 1];
|
||||
char fullname[JSON_FULLNAME_LEN + 1];
|
||||
union {
|
||||
char *string; // type == JSON_STRING
|
||||
long double number; // type == JSON_NUMBER
|
||||
int boolean; // type == JSON_BOOLEAN
|
||||
size_t items; // type == JSON_ARRAY
|
||||
} data;
|
||||
size_t pos; // the position of this item in its parent
|
||||
|
||||
char *original_string;
|
||||
|
||||
void *callback_data;
|
||||
int (*callback_function)(struct json_entry *);
|
||||
} JSON_ENTRY;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// public functions
|
||||
|
||||
#define JSON_OK 0
|
||||
#define JSON_CANNOT_DOWNLOAD 1
|
||||
#define JSON_CANNOT_PARSE 2
|
||||
|
||||
int json_parse(char *js, void *callback_data, int (*callback_function)(JSON_ENTRY *));
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// private functions
|
||||
|
||||
#ifdef ENABLE_JSONC
|
||||
json_object *json_tokenise(char *js);
|
||||
size_t json_walk(json_object *t, void *callback_data, int (*callback_function)(struct json_entry *));
|
||||
#else
|
||||
jsmntok_t *json_tokenise(char *js, size_t len, size_t *count);
|
||||
size_t json_walk_tree(char *js, jsmntok_t *t, void *callback_data, int (*callback_function)(struct json_entry *));
|
||||
#endif
|
||||
|
||||
size_t json_walk_object(char *js, jsmntok_t *t, size_t nest, size_t start, JSON_ENTRY *e);
|
||||
size_t json_walk_array(char *js, jsmntok_t *t, size_t nest, size_t start, JSON_ENTRY *e);
|
||||
size_t json_walk_string(char *js, jsmntok_t *t, size_t start, JSON_ENTRY *e);
|
||||
size_t json_walk_primitive(char *js, jsmntok_t *t, size_t start, JSON_ENTRY *e);
|
||||
|
||||
int json_callback_print(JSON_ENTRY *e);
|
||||
|
||||
|
||||
|
||||
#endif
|
|
@ -310,5 +310,7 @@ extern char *netdata_configured_host_prefix;
|
|||
#include "statistical/statistical.h"
|
||||
#include "adaptive_resortable_list/adaptive_resortable_list.h"
|
||||
#include "url/url.h"
|
||||
#include "json/json.h"
|
||||
#include "health/health.h"
|
||||
|
||||
#endif // NETDATA_LIB_H
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{ "all": false, "type": "SILENCE", "silencers": [ { "alarm": "*10min_cpu_iowait" }, { "alarm": "*10min_cpu_usage *load_trigger" } ] }
|
|
@ -0,0 +1 @@
|
|||
{ "all": false, "type": "SILENCE", "silencers": [ { "alarm": "*10min_cpu_usage *load_trigger", "context": "system.cpu" }, { "alarm": "*10min_cpu_usage *load_trigger", "chart": "system.load" } ] }
|
|
@ -0,0 +1 @@
|
|||
{ "all": false, "type": "DISABLE", "silencers": [ { "context": "system.cpu" }, { "chart": "system.load" } ] }
|
1
tests/health_mgmtapi/expected_list/DISABLE-list.json
Normal file
1
tests/health_mgmtapi/expected_list/DISABLE-list.json
Normal file
|
@ -0,0 +1 @@
|
|||
{ "all": false, "type": "DISABLE", "silencers": [ { "alarm": "*10min_cpu_usage *load_trigger" } ] }
|
1
tests/health_mgmtapi/expected_list/DISABLE_ALL-list.json
Normal file
1
tests/health_mgmtapi/expected_list/DISABLE_ALL-list.json
Normal file
|
@ -0,0 +1 @@
|
|||
{ "all": true, "type": "DISABLE", "silencers": [] }
|
|
@ -0,0 +1 @@
|
|||
Auth Error
|
|
@ -0,0 +1 @@
|
|||
{ "all": false, "type": "DISABLE", "silencers": [ { "chart": "system.load" } ] }
|
|
@ -0,0 +1 @@
|
|||
{ "all": false, "type": "None", "silencers": [ { "families": "load" } ] }
|
1
tests/health_mgmtapi/expected_list/HOSTS-list.json
Normal file
1
tests/health_mgmtapi/expected_list/HOSTS-list.json
Normal file
|
@ -0,0 +1 @@
|
|||
{ "all": false, "type": "SILENCE", "silencers": [ { "hosts": "*" } ] }
|
1
tests/health_mgmtapi/expected_list/RESET-list.json
Normal file
1
tests/health_mgmtapi/expected_list/RESET-list.json
Normal file
|
@ -0,0 +1 @@
|
|||
{ "all": false, "type": "None", "silencers": [] }
|
1
tests/health_mgmtapi/expected_list/SILENCE-list.json
Normal file
1
tests/health_mgmtapi/expected_list/SILENCE-list.json
Normal file
|
@ -0,0 +1 @@
|
|||
{ "all": false, "type": "SILENCE", "silencers": [ { "alarm": "*10min_cpu_usage *load_trigger" } ] }
|
1
tests/health_mgmtapi/expected_list/SILENCE_2-list.json
Normal file
1
tests/health_mgmtapi/expected_list/SILENCE_2-list.json
Normal file
|
@ -0,0 +1 @@
|
|||
{ "all": false, "type": "SILENCE", "silencers": [ { "families": "load" } ] }
|
1
tests/health_mgmtapi/expected_list/SILENCE_3-list.json
Normal file
1
tests/health_mgmtapi/expected_list/SILENCE_3-list.json
Normal file
|
@ -0,0 +1 @@
|
|||
{ "all": false, "type": "SILENCE", "silencers": [] } WARNING: SILENCE or DISABLE command is ineffective without defining any alarm selectors.
|
|
@ -0,0 +1 @@
|
|||
{ "all": false, "type": "SILENCE", "silencers": [ { "alarm": "*10min_cpu_usage *load_trigger", "chart": "system.load" } ] }
|
|
@ -0,0 +1 @@
|
|||
{ "all": false, "type": "SILENCE", "silencers": [ { "alarm": "*10min_cpu_usage *load_trigger" } ] }
|
1
tests/health_mgmtapi/expected_list/SILENCE_ALL-list.json
Normal file
1
tests/health_mgmtapi/expected_list/SILENCE_ALL-list.json
Normal file
|
@ -0,0 +1 @@
|
|||
{ "all": true, "type": "SILENCE", "silencers": [] }
|
|
@ -27,7 +27,7 @@ check () {
|
|||
elif [ "${r}" != "${2}" ] ; then
|
||||
echo -e " ${GRAY}WARNING: 'Got ${r}'. Expected '${2}'"
|
||||
iter=$((iter+1))
|
||||
if [ $iter -lt 10 ] ; then
|
||||
if [ $iter -lt 10 ] ; then
|
||||
echo -e " ${GRAY}Repeating test "
|
||||
check "$1" "$2"
|
||||
else
|
||||
|
@ -53,6 +53,20 @@ cmd () {
|
|||
fi
|
||||
}
|
||||
|
||||
check_list() {
|
||||
RESPONSE=$(curl -s "http://$URL/api/v1/manage/health?cmd=LIST" -H "X-Auth-Token: $TOKEN" 2>&1)
|
||||
|
||||
NAME="$1-list.json"
|
||||
echo $RESPONSE > $NAME
|
||||
diff $NAME expected_list/$NAME 1>/dev/null 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "${GREEN}Success: The list command got the correct answer for $NAME!"
|
||||
else
|
||||
echo -e "${RED}ERROR: the files $NAME and expected_list/$NAME does not match."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
WHITE='\033[0;37m'
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
|
@ -90,11 +104,13 @@ err=0
|
|||
# Test default state
|
||||
cmd "cmd=RESET" "$HEALTH_CMDAPI_MSG_RESET"
|
||||
check "Default State" "False False False False False False"
|
||||
check_list "RESET"
|
||||
|
||||
# Test auth failure
|
||||
TOKEN="Wrong token"
|
||||
cmd "cmd=DISABLE ALL" "$HEALTH_CMDAPI_MSG_AUTHERROR"
|
||||
check "Default State" "False False False False False False"
|
||||
check_list "DISABLE_ALL_ERROR"
|
||||
|
||||
# Set correct token
|
||||
TOKEN="${CORRECT_TOKEN}"
|
||||
|
@ -102,87 +118,107 @@ err=0
|
|||
# Test disable
|
||||
cmd "cmd=DISABLE ALL" "$HEALTH_CMDAPI_MSG_DISABLEALL"
|
||||
check "All disabled" "True False True False True False"
|
||||
check_list "DISABLE_ALL"
|
||||
|
||||
# Reset
|
||||
cmd "cmd=RESET" "$HEALTH_CMDAPI_MSG_RESET"
|
||||
check "Default State" "False False False False False False"
|
||||
check_list "RESET"
|
||||
|
||||
# Test silence
|
||||
cmd "cmd=SILENCE ALL" "$HEALTH_CMDAPI_MSG_SILENCEALL"
|
||||
check "All silenced" "False True False True False True"
|
||||
check_list "SILENCE_ALL"
|
||||
|
||||
# Reset
|
||||
cmd "cmd=RESET" "$HEALTH_CMDAPI_MSG_RESET"
|
||||
check "Default State" "False False False False False False"
|
||||
check_list "RESET"
|
||||
|
||||
# Add silencer by name
|
||||
printf -v resp "$HEALTH_CMDAPI_MSG_SILENCE\n$HEALTH_CMDAPI_MSG_ADDED"
|
||||
cmd "cmd=SILENCE&alarm=*10min_cpu_usage *load_trigger" "${resp}"
|
||||
check "Silence notifications for alarm1 and load_trigger" "False True False False False True"
|
||||
check_list "SILENCE_ALARM_CPU_USAGE_LOAD_TRIGGER"
|
||||
|
||||
# Convert to disable health checks
|
||||
cmd "cmd=DISABLE" "$HEALTH_CMDAPI_MSG_DISABLE"
|
||||
check "Disable notifications for alarm1 and load_trigger" "True False False False True False"
|
||||
check_list "DISABLE"
|
||||
|
||||
# Convert back to silence notifications
|
||||
cmd "cmd=SILENCE" "$HEALTH_CMDAPI_MSG_SILENCE"
|
||||
check "Silence notifications for alarm1 and load_trigger" "False True False False False True"
|
||||
check_list "SILENCE"
|
||||
|
||||
# Add second silencer by name
|
||||
cmd "alarm=*10min_cpu_iowait" "$HEALTH_CMDAPI_MSG_ADDED"
|
||||
check "Silence notifications for alarm1,alarm2 and load_trigger" "False True False True False True"
|
||||
check_list "ALARM_CPU_IOWAIT"
|
||||
|
||||
# Reset
|
||||
cmd "cmd=RESET" "$HEALTH_CMDAPI_MSG_RESET"
|
||||
check_list "RESET"
|
||||
|
||||
# Add silencer by chart
|
||||
printf -v resp "$HEALTH_CMDAPI_MSG_DISABLE\n$HEALTH_CMDAPI_MSG_ADDED"
|
||||
cmd "cmd=DISABLE&chart=system.load" "${resp}"
|
||||
check "Default State" "False False False False True False"
|
||||
check_list "DISABLE_SYSTEM_LOAD"
|
||||
|
||||
# Add silencer by context
|
||||
cmd "context=system.cpu" "$HEALTH_CMDAPI_MSG_ADDED"
|
||||
check "Default State" "True False True False True False"
|
||||
check_list "CONTEXT_SYSTEM_CPU"
|
||||
|
||||
# Reset
|
||||
cmd "cmd=RESET" "$HEALTH_CMDAPI_MSG_RESET"
|
||||
check_list "RESET"
|
||||
|
||||
# Add second condition to a selector (AND)
|
||||
printf -v resp "$HEALTH_CMDAPI_MSG_SILENCE\n$HEALTH_CMDAPI_MSG_ADDED"
|
||||
cmd "cmd=SILENCE&alarm=*10min_cpu_usage *load_trigger&chart=system.load" "${resp}"
|
||||
check "Silence notifications load_trigger" "False False False False False True"
|
||||
check_list "SILENCE_ALARM_CPU_USAGE"
|
||||
|
||||
# Add second selector with two conditions
|
||||
cmd "alarm=*10min_cpu_usage *load_trigger&context=system.cpu" "$HEALTH_CMDAPI_MSG_ADDED"
|
||||
check "Silence notifications load_trigger" "False True False False False True"
|
||||
check_list "ALARM_CPU_USAGE"
|
||||
|
||||
# Reset
|
||||
cmd "cmd=RESET" "$HEALTH_CMDAPI_MSG_RESET"
|
||||
check_list "RESET"
|
||||
|
||||
# Add silencer without a command to disable or silence alarms
|
||||
printf -v resp "$HEALTH_CMDAPI_MSG_ADDED\n$HEALTH_CMDAPI_MSG_STYPEWARNING"
|
||||
cmd "families=load" "${resp}"
|
||||
check "Family selector with no command" "False False False False False False"
|
||||
check_list "FAMILIES_LOAD"
|
||||
|
||||
# Add silence command
|
||||
cmd "cmd=SILENCE" "$HEALTH_CMDAPI_MSG_SILENCE"
|
||||
check "Silence family load" "False False False False False True"
|
||||
check_list "SILENCE_2"
|
||||
|
||||
# Reset
|
||||
cmd "cmd=RESET" "$HEALTH_CMDAPI_MSG_RESET"
|
||||
check_list "RESET"
|
||||
|
||||
# Add command without silencers
|
||||
printf -v resp "$HEALTH_CMDAPI_MSG_SILENCE\n$HEALTH_CMDAPI_MSG_NOSELECTORWARNING"
|
||||
cmd "cmd=SILENCE" "${resp}"
|
||||
check "Command with no selector" "False False False False False False"
|
||||
check_list "SILENCE_3"
|
||||
|
||||
# Add hosts silencer
|
||||
cmd "hosts=*" "$HEALTH_CMDAPI_MSG_ADDED"
|
||||
check "Silence all hosts" "False True False True False True"
|
||||
check_list "HOSTS"
|
||||
|
||||
# Reset
|
||||
cmd "cmd=RESET" "$HEALTH_CMDAPI_MSG_RESET"
|
||||
|
||||
check_list "RESET"
|
||||
|
||||
if [ $err -gt 0 ] ; then
|
||||
echo "$err error(s) found"
|
||||
|
|
|
@ -45,6 +45,7 @@ The following will return an SVG badge of the alarm named `NAME`, attached to th
|
|||
## Health Management API
|
||||
|
||||
Netdata v1.12 and beyond provides a command API to control health checks and notifications at runtime. The feature is especially useful for maintenance periods, during which you receive meaningless alarms.
|
||||
From Netdata v1.16.0 and beyond, the configuration controlled via the API commands is [persisted across netdata restarts](#persistence).
|
||||
|
||||
Specifically, the API allows you to:
|
||||
- Disable health checks completely. Alarm conditions will not be evaluated at all and no entries will be added to the alarm log.
|
||||
|
@ -142,6 +143,43 @@ Example 2.2: Add one more selector, to also silence alarms for cpu1 and cpu2
|
|||
http://localhost/api/v1/manage/health?families=cpu1 cpu2
|
||||
```
|
||||
|
||||
### List silencers
|
||||
|
||||
The command `LIST` was added in netdata v1.16.0 and returns a JSON with the current status of the silencers.
|
||||
|
||||
```
|
||||
curl "http://myserver/api/v1/manage/health?cmd=LIST" -H "X-Auth-Token: Mytoken"
|
||||
```
|
||||
|
||||
As an example, the following response shows that we have two silencers configured, one for an alarm called `samplealarm` and one for alarms with context `random` on host `myhost`
|
||||
```
|
||||
json
|
||||
{
|
||||
"all": false,
|
||||
"type": "SILENCE",
|
||||
"silencers": [
|
||||
{
|
||||
"alarm": "samplealarm"
|
||||
},
|
||||
{
|
||||
"context": "random",
|
||||
"hosts": "myhost"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The response below shows that we have disabled all health checks.
|
||||
|
||||
```
|
||||
json
|
||||
{
|
||||
"all": true,
|
||||
"type": "DISABLE",
|
||||
"silencers": []
|
||||
}
|
||||
|
||||
|
||||
### Responses
|
||||
|
||||
- "Auth Error" : Token authentication failed
|
||||
|
@ -155,6 +193,17 @@ http://localhost/api/v1/manage/health?families=cpu1 cpu2
|
|||
- "WARNING: Added alarm selector to silence/disable alarms without a SILENCE or DISABLE command." : Added to the response if a selector is added without a selector-specific command.
|
||||
- "WARNING: SILENCE or DISABLE command is ineffective without defining any alarm selectors." : Added to the response if a selector-specific command is issued without a selector.
|
||||
|
||||
### Persistence
|
||||
|
||||
From netdata v1.16.0 and beyond, the silencers configuration is persisted to disk and loaded when netdata starts.
|
||||
The JSON string returned by the [LIST command](#list-silencers) is automatically saved to the `silencers file`, every time a command alters the silencers configuration.
|
||||
The file's location is configurable in `netdata.conf`. The default is shown below:
|
||||
|
||||
```
|
||||
[health]
|
||||
# silencers file = /var/lib/netdata/health.silencers.json
|
||||
```
|
||||
|
||||
### Further reading
|
||||
|
||||
The test script under [tests/health_mgmtapi](../../../tests/health_mgmtapi) contains a series of tests that you can either run or read through to understand the various calls and responses better.
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
//
|
||||
// Created by christopher on 11/12/18.
|
||||
// Created by Christopher on 11/12/18.
|
||||
//
|
||||
|
||||
#include "health_cmdapi.h"
|
||||
|
||||
|
||||
static SILENCER *create_silencer(void) {
|
||||
SILENCER *t = callocz(1, sizeof(SILENCER));
|
||||
debug(D_HEALTH, "HEALTH command API: Created empty silencer");
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free Silencers
|
||||
*
|
||||
* Clean the silencer structure
|
||||
*
|
||||
* @param t is the structure that will be cleaned.
|
||||
*/
|
||||
void free_silencers(SILENCER *t) {
|
||||
if (!t) return;
|
||||
if (t->next) free_silencers(t->next);
|
||||
|
@ -31,38 +30,104 @@ void free_silencers(SILENCER *t) {
|
|||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Silencers to JSON Entry
|
||||
*
|
||||
* Fill the buffer with the other values given.
|
||||
*
|
||||
* @param wb a pointer to the output buffer
|
||||
* @param var the json variable
|
||||
* @param val the json value
|
||||
* @param hasprev has it a previous value?
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
int health_silencers2json_entry(BUFFER *wb, char* var, char* val, int hasprev) {
|
||||
if (val) {
|
||||
buffer_sprintf(wb, "%s\n\t\t\t\"%s\": \"%s\"", (hasprev)?",":"", var, val);
|
||||
return 1;
|
||||
} else {
|
||||
return hasprev;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Silencer to JSON
|
||||
*
|
||||
* Write the silencer values using JSON format inside a buffer.
|
||||
*
|
||||
* @param wb is the buffer to write the silencers.
|
||||
*/
|
||||
void health_silencers2json(BUFFER *wb) {
|
||||
buffer_sprintf(wb, "{\n\t\"all\": %s,"
|
||||
"\n\t\"type\": \"%s\","
|
||||
"\n\t\"silencers\": [",
|
||||
(silencers->all_alarms)?"true":"false",
|
||||
(silencers->stype == STYPE_NONE)?"None":((silencers->stype == STYPE_DISABLE_ALARMS)?"DISABLE":"SILENCE"));
|
||||
|
||||
SILENCER *silencer;
|
||||
int i = 0, j = 0;
|
||||
for(silencer = silencers->silencers; silencer ; silencer = silencer->next) {
|
||||
if(likely(i)) buffer_strcat(wb, ",");
|
||||
buffer_strcat(wb, "\n\t\t{");
|
||||
j=health_silencers2json_entry(wb, HEALTH_ALARM_KEY, silencer->alarms, j);
|
||||
j=health_silencers2json_entry(wb, HEALTH_CHART_KEY, silencer->charts, j);
|
||||
j=health_silencers2json_entry(wb, HEALTH_CONTEXT_KEY, silencer->contexts, j);
|
||||
j=health_silencers2json_entry(wb, HEALTH_HOST_KEY, silencer->hosts, j);
|
||||
health_silencers2json_entry(wb, HEALTH_FAMILIES_KEY, silencer->families, j);
|
||||
j=0;
|
||||
buffer_strcat(wb, "\n\t\t}");
|
||||
i++;
|
||||
}
|
||||
if(likely(i)) buffer_strcat(wb, "\n\t");
|
||||
buffer_strcat(wb, "]\n}\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Silencer to FILE
|
||||
*
|
||||
* Write the sliencer buffer to a file.
|
||||
* @param wb
|
||||
*/
|
||||
void health_silencers2file(BUFFER *wb) {
|
||||
if (wb->len == 0) return;
|
||||
|
||||
FILE *fd = fopen(silencers_filename, "wb");
|
||||
if(fd) {
|
||||
size_t written = (size_t)fprintf(fd, "%s", wb->buffer) ;
|
||||
if (written == wb->len ) {
|
||||
info("Silencer changes written to %s", silencers_filename);
|
||||
}
|
||||
fclose(fd);
|
||||
return;
|
||||
}
|
||||
error("Silencer changes could not be written to %s. Error %s", silencers_filename, strerror(errno));
|
||||
}
|
||||
|
||||
/**
|
||||
* Request V1 MGMT Health
|
||||
*
|
||||
* Function called by api to management the health.
|
||||
*
|
||||
* @param host main structure with client information!
|
||||
* @param w is the structure with all information of the client request.
|
||||
* @param url is the url that netdata is working
|
||||
*
|
||||
* @return It returns 200 on success and another code otherwise.
|
||||
*/
|
||||
int web_client_api_request_v1_mgmt_health(RRDHOST *host, struct web_client *w, char *url) {
|
||||
int ret = 400;
|
||||
(void) host;
|
||||
|
||||
|
||||
|
||||
BUFFER *wb = w->response.data;
|
||||
buffer_flush(wb);
|
||||
wb->contenttype = CT_TEXT_PLAIN;
|
||||
|
||||
buffer_flush(w->response.data);
|
||||
|
||||
static uint32_t
|
||||
hash_alarm = 0,
|
||||
hash_template = 0,
|
||||
hash_chart = 0,
|
||||
hash_context = 0,
|
||||
hash_host = 0,
|
||||
hash_families = 0;
|
||||
|
||||
if (unlikely(!hash_alarm)) {
|
||||
hash_alarm = simple_uhash(HEALTH_ALARM_KEY);
|
||||
hash_template = simple_uhash(HEALTH_TEMPLATE_KEY);
|
||||
hash_chart = simple_uhash(HEALTH_CHART_KEY);
|
||||
hash_context = simple_uhash(HEALTH_CONTEXT_KEY);
|
||||
hash_host = simple_uhash(HEALTH_HOST_KEY);
|
||||
hash_families = simple_uhash(HEALTH_FAMILIES_KEY);
|
||||
}
|
||||
|
||||
//Local instance of the silencer
|
||||
SILENCER *silencer = NULL;
|
||||
int config_changed = 1;
|
||||
|
||||
if (!w->auth_bearer_token) {
|
||||
buffer_strcat(wb, HEALTH_CMDAPI_MSG_AUTHERROR);
|
||||
|
@ -105,50 +170,17 @@ int web_client_api_request_v1_mgmt_health(RRDHOST *host, struct web_client *w, c
|
|||
free_silencers(silencers->silencers);
|
||||
silencers->silencers = NULL;
|
||||
buffer_strcat(wb, HEALTH_CMDAPI_MSG_RESET);
|
||||
} else if (!strcmp(value, HEALTH_CMDAPI_CMD_LIST)) {
|
||||
w->response.data->contenttype = CT_APPLICATION_JSON;
|
||||
health_silencers2json(wb);
|
||||
config_changed=0;
|
||||
}
|
||||
} else {
|
||||
uint32_t hash = simple_uhash(key);
|
||||
if (unlikely(silencer == NULL)) {
|
||||
if (
|
||||
(hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) ||
|
||||
(hash == hash_template && !strcasecmp(key, HEALTH_TEMPLATE_KEY)) ||
|
||||
(hash == hash_chart && !strcasecmp(key, HEALTH_CHART_KEY)) ||
|
||||
(hash == hash_context && !strcasecmp(key, HEALTH_CONTEXT_KEY)) ||
|
||||
(hash == hash_host && !strcasecmp(key, HEALTH_HOST_KEY)) ||
|
||||
(hash == hash_families && !strcasecmp(key, HEALTH_FAMILIES_KEY))
|
||||
) {
|
||||
silencer = create_silencer();
|
||||
}
|
||||
}
|
||||
|
||||
if (hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) {
|
||||
silencer->alarms = strdupz(value);
|
||||
silencer->alarms_pattern = simple_pattern_create(silencer->alarms, NULL, SIMPLE_PATTERN_EXACT);
|
||||
} else if (hash == hash_chart && !strcasecmp(key, HEALTH_CHART_KEY)) {
|
||||
silencer->charts = strdupz(value);
|
||||
silencer->charts_pattern = simple_pattern_create(silencer->charts, NULL, SIMPLE_PATTERN_EXACT);
|
||||
} else if (hash == hash_context && !strcasecmp(key, HEALTH_CONTEXT_KEY)) {
|
||||
silencer->contexts = strdupz(value);
|
||||
silencer->contexts_pattern = simple_pattern_create(silencer->contexts, NULL, SIMPLE_PATTERN_EXACT);
|
||||
} else if (hash == hash_host && !strcasecmp(key, HEALTH_HOST_KEY)) {
|
||||
silencer->hosts = strdupz(value);
|
||||
silencer->hosts_pattern = simple_pattern_create(silencer->hosts, NULL, SIMPLE_PATTERN_EXACT);
|
||||
} else if (hash == hash_families && !strcasecmp(key, HEALTH_FAMILIES_KEY)) {
|
||||
silencer->families = strdupz(value);
|
||||
silencer->families_pattern = simple_pattern_create(silencer->families, NULL, SIMPLE_PATTERN_EXACT);
|
||||
} else {
|
||||
buffer_strcat(wb, HEALTH_CMDAPI_MSG_INVALID_KEY);
|
||||
}
|
||||
silencer = health_silencers_addparam(silencer, key, value);
|
||||
}
|
||||
|
||||
}
|
||||
if (likely(silencer)) {
|
||||
// Add the created instance to the linked list in silencers
|
||||
silencer->next = silencers->silencers;
|
||||
silencers->silencers = silencer;
|
||||
debug(D_HEALTH, "HEALTH command API: Added silencer %s:%s:%s:%s:%s", silencer->alarms,
|
||||
silencer->charts, silencer->contexts, silencer->hosts, silencer->families
|
||||
);
|
||||
health_silencers_add(silencer);
|
||||
buffer_strcat(wb, HEALTH_CMDAPI_MSG_ADDED);
|
||||
if (silencers->stype == STYPE_NONE) {
|
||||
buffer_strcat(wb, HEALTH_CMDAPI_MSG_STYPEWARNING);
|
||||
|
@ -162,5 +194,11 @@ int web_client_api_request_v1_mgmt_health(RRDHOST *host, struct web_client *w, c
|
|||
}
|
||||
w->response.data = wb;
|
||||
buffer_no_cacheable(w->response.data);
|
||||
if (ret == 200 && config_changed) {
|
||||
BUFFER *jsonb = buffer_create(200);
|
||||
health_silencers2json(jsonb);
|
||||
health_silencers2file(jsonb);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#define HEALTH_CMDAPI_CMD_SILENCE "SILENCE"
|
||||
#define HEALTH_CMDAPI_CMD_DISABLE "DISABLE"
|
||||
#define HEALTH_CMDAPI_CMD_RESET "RESET"
|
||||
#define HEALTH_CMDAPI_CMD_LIST "LIST"
|
||||
|
||||
#define HEALTH_CMDAPI_MSG_AUTHERROR "Auth Error\n"
|
||||
#define HEALTH_CMDAPI_MSG_SILENCEALL "All alarm notifications are silenced\n"
|
||||
|
@ -20,7 +21,6 @@
|
|||
#define HEALTH_CMDAPI_MSG_DISABLE "Health checks disabled for alarms matching the selectors\n"
|
||||
#define HEALTH_CMDAPI_MSG_SILENCE "Alarm notifications silenced for alarms matching the selectors\n"
|
||||
#define HEALTH_CMDAPI_MSG_ADDED "Alarm selector added\n"
|
||||
#define HEALTH_CMDAPI_MSG_INVALID_KEY "Invalid key. Ignoring it.\n"
|
||||
#define HEALTH_CMDAPI_MSG_STYPEWARNING "WARNING: Added alarm selector to silence/disable alarms without a SILENCE or DISABLE command.\n"
|
||||
#define HEALTH_CMDAPI_MSG_NOSELECTORWARNING "WARNING: SILENCE or DISABLE command is ineffective without defining any alarm selectors.\n"
|
||||
|
||||
|
|
|
@ -664,7 +664,7 @@
|
|||
{
|
||||
"name": "cmd",
|
||||
"in": "query",
|
||||
"description": "DISABLE ALL: No alarm criteria are evaluated, nothing is written in the alarm log. SILENCE ALL: No notifications are sent. RESET: Return to the default state. DISABLE/SILENCE: Set the mode to be used for the alarms matching the criteria of the alarm selectors.",
|
||||
"description": "DISABLE ALL: No alarm criteria are evaluated, nothing is written in the alarm log. SILENCE ALL: No notifications are sent. RESET: Return to the default state. DISABLE/SILENCE: Set the mode to be used for the alarms matching the criteria of the alarm selectors. LIST: Show active configuration.",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"enum": [
|
||||
|
@ -672,7 +672,8 @@
|
|||
"SILENCE ALL",
|
||||
"DISABLE",
|
||||
"SILENCE",
|
||||
"RESET"
|
||||
"RESET",
|
||||
"LIST"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -437,10 +437,10 @@ paths:
|
|||
parameters:
|
||||
- name: cmd
|
||||
in: query
|
||||
description: 'DISABLE ALL: No alarm criteria are evaluated, nothing is written in the alarm log. SILENCE ALL: No notifications are sent. RESET: Return to the default state. DISABLE/SILENCE: Set the mode to be used for the alarms matching the criteria of the alarm selectors.'
|
||||
description: 'DISABLE ALL: No alarm criteria are evaluated, nothing is written in the alarm log. SILENCE ALL: No notifications are sent. RESET: Return to the default state. DISABLE/SILENCE: Set the mode to be used for the alarms matching the criteria of the alarm selectors. LIST: Show active configuration.'
|
||||
required: false
|
||||
type: string
|
||||
enum: ['DISABLE ALL', 'SILENCE ALL', 'DISABLE', 'SILENCE', 'RESET']
|
||||
enum: ['DISABLE ALL', 'SILENCE ALL', 'DISABLE', 'SILENCE', 'RESET', 'LIST']
|
||||
- name: alarm
|
||||
in: query
|
||||
description: 'The expression provided will match both `alarm` and `template` names.'
|
||||
|
|
Loading…
Add table
Reference in a new issue