Revert "Easily disable alarms, by persisting the silencers configuration (#6274)"

This reverts commit 60a73e90de.

Emergency rollback of potential culprit as per issue #6356
Will be re-merging the change after investigation
This commit is contained in:
Pavlos Emm. Katsoulakis 2019-06-28 08:27:57 +02:00
parent 5d5266dc1a
commit 171d8f5d01
40 changed files with 126 additions and 1638 deletions

View File

@ -130,14 +130,6 @@ 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
@ -313,15 +305,8 @@ set(LIBNETDATA_FILES
libnetdata/threads/threads.h
libnetdata/url/url.c
libnetdata/url/url.h
libnetdata/health/health.c
libnetdata/health/health.h
libnetdata/socket/security.c
libnetdata/socket/security.h
libnetdata/json/json.c
libnetdata/json/json.h
libnetdata/json/jsmn.c
libnetdata/json/jsmn.h
)
libnetdata/socket/security.h)
add_library(libnetdata OBJECT ${LIBNETDATA_FILES})

View File

@ -159,14 +159,8 @@ LIBNETDATA_FILES = \
libnetdata/storage_number/storage_number.h \
libnetdata/threads/threads.c \
libnetdata/threads/threads.h \
libnetdata/url/url.c \
libnetdata/url/url.h \
libnetdata/json/jsmn.h \
libnetdata/json/jsmn.c \
libnetdata/json/json.h \
libnetdata/json/json.c \
libnetdata/health/health.c \
libnetdata/health/health.h \
libnetdata/url/url.c \
libnetdata/url/url.h \
$(NULL)
APPS_PLUGIN_FILES = \
@ -512,12 +506,12 @@ endif
NETDATA_COMMON_LIBS = \
$(OPTIONAL_MATH_LIBS) \
$(OPTIONAL_ZLIB_LIBS) \
$(OPTIONAL_SSL_LIBS) \
$(OPTIONAL_UUID_LIBS) \
$(OPTIONAL_UV_LIBS) \
$(OPTIONAL_LZ4_LIBS) \
$(OPTIONAL_JUDY_LIBS) \
$(OPTIONAL_SSL_LIBS) \
$(OPTIONAL_JSONC_LIBS) \
$(NULL)
sbin_PROGRAMS += netdata

View File

@ -142,12 +142,6 @@ AC_ARG_ENABLE(
,
[enable_https="detect"]
)
AC_ARG_ENABLE(
[jsonc],
[AS_HELP_STRING([--enable-jsonc], [Enable JSON-C support @<:@default autodetect@:>@])],
,
[enable_jsonc="detect"]
)
AC_ARG_ENABLE(
[dbengine],
[AS_HELP_STRING([--disable-dbengine], [disable netdata dbengine @<:@default autodetect@:>@])],
@ -353,20 +347,6 @@ 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}" && \
@ -401,22 +381,6 @@ 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
@ -1011,7 +975,6 @@ 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])
@ -1078,8 +1041,6 @@ AC_CONFIG_FILES([
libnetdata/config/Makefile
libnetdata/dictionary/Makefile
libnetdata/eval/Makefile
libnetdata/json/Makefile
libnetdata/health/Makefile
libnetdata/locks/Makefile
libnetdata/log/Makefile
libnetdata/popen/Makefile

View File

@ -1,6 +1,5 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include <libnetdata/json/json.h>
#include "health.h"
struct health_cmdapi_thread_status {
@ -14,79 +13,18 @@ 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) {
silencers = mallocz(sizeof(SILENCERS));
silencers->all_alarms=0;
silencers->stype=STYPE_NONE;
silencers->silencers=NULL;
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);
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");
@ -94,20 +32,11 @@ 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;
@ -155,11 +84,6 @@ void health_reload_host(RRDHOST *host) {
rrdhost_unlock(host);
}
/**
* Reload
*
* Reload the host configuration for all hosts.
*/
void health_reload(void) {
rrd_rdlock();
@ -501,16 +425,6 @@ 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
@ -540,15 +454,6 @@ 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);
@ -560,6 +465,11 @@ void *health_main(void *ptr) {
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);
@ -613,7 +523,6 @@ void *health_main(void *ptr) {
// the first loop is to lookup values from the db
for (rc = host->alarms; rc; rc = rc->next) {
//case it is disabled I will get the next rc
if (update_disabled_silenced(host, rc))
continue;

View File

@ -35,9 +35,16 @@ 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"
@ -50,9 +57,38 @@ extern unsigned int default_health_enabled;
#define HEALTH_DELAY_KEY "delay"
#define HEALTH_OPTIONS_KEY "options"
#define HEALTH_SILENCERS_MAX_FILE_LEN 10000
typedef struct silencer {
char *alarms;
SIMPLE_PATTERN *alarms_pattern;
char *silencers_filename;
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 void health_init(void);
extern void *health_main(void *ptr);

View File

@ -481,7 +481,7 @@ static int health_readfile(const char *filename, void *data) {
if(append < HEALTH_CONF_MAX_LINE)
continue;
else {
error("Health configuration has too long multi-line at line %zu of file '%s'.", line, filename);
error("Health configuration has too long muli-line at line %zu of file '%s'.", line, filename);
}
}
append = 0;

View File

@ -21,8 +21,6 @@ SUBDIRS = \
storage_number \
threads \
url \
json \
health \
$(NULL)
dist_noinst_DATA = \

View File

@ -1,8 +0,0 @@
# SPDX-License-Identifier: GPL-3.0-or-later
AUTOMAKE_OPTIONS = subdir-objects
MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
dist_noinst_DATA = \
$(NULL)

View File

@ -1,154 +0,0 @@
#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;
}

View File

@ -1,54 +0,0 @@
#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);
#endif

View File

@ -1,9 +0,0 @@
# SPDX-License-Identifier: GPL-3.0-or-later
AUTOMAKE_OPTIONS = subdir-objects
MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
dist_noinst_DATA = \
README.md \
$(NULL)

View File

@ -1,5 +0,0 @@
# 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.
[![analytics](https://www.google-analytics.com/collect?v=1&aip=1&t=pageview&_s=1&ds=github&dr=https%3A%2F%2Fgithub.com%2Fnetdata%2Fnetdata&dl=https%3A%2F%2Fmy-netdata.io%2Fgithub%2Flibnetdata%2Fjson%2FREADME&_u=MAC~&cid=5792dfd7-8dc4-476b-af31-da2fdb9f93d2&tid=UA-64295674-3)]()

View File

@ -1,326 +0,0 @@
#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;
}

View File

@ -1,75 +0,0 @@
#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_ */

View File

@ -1,546 +0,0 @@
#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);
}
*/

View File

@ -1,72 +0,0 @@
#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

View File

@ -310,7 +310,5 @@ 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

View File

@ -175,7 +175,6 @@ USAGE: ${PROGRAM} [options]
--libs-are-really-here If you get errors about missing zlib or libuuid but you know it is available, you might
have a broken pkg-config. Use this option to proceed without checking pkg-config.
--disable-telemetry Use this flag to opt-out from our anonymous telemetry progam.
--enable-json Enable to use JSON-C library instead the internal Netdata library.
Netdata will by default be compiled with gcc optimization -O2
If you need to pass different CFLAGS, use something like this:
@ -223,7 +222,6 @@ while [ -n "${1}" ]; do
"--disable-x86-sse") NETDATA_CONFIGURE_OPTIONS="${NETDATA_CONFIGURE_OPTIONS//--disable-x86-sse/} --disable-x86-sse";;
"--disable-telemetry") NETDATA_DISABLE_TELEMETRY=1;;
"--disable-go") NETDATA_DISABLE_GO=1;;
"--enable-jsonc") NETDATA_CONFIGURE_OPTIONS="${NETDATA_CONFIGURE_OPTIONS//--enable-jsonc/} --enable-jsonc";;
"--install")
NETDATA_PREFIX="${2}/netdata"
shift 1

View File

@ -1 +0,0 @@
{ "all": false, "type": "SILENCE", "silencers": [ { "alarm": "*10min_cpu_iowait" }, { "alarm": "*10min_cpu_usage *load_trigger" } ] }

View File

@ -1 +0,0 @@
{ "all": false, "type": "SILENCE", "silencers": [ { "alarm": "*10min_cpu_usage *load_trigger", "context": "system.cpu" }, { "alarm": "*10min_cpu_usage *load_trigger", "chart": "system.load" } ] }

View File

@ -1 +0,0 @@
{ "all": false, "type": "DISABLE", "silencers": [ { "context": "system.cpu" }, { "chart": "system.load" } ] }

View File

@ -1 +0,0 @@
{ "all": false, "type": "DISABLE", "silencers": [ { "alarm": "*10min_cpu_usage *load_trigger" } ] }

View File

@ -1 +0,0 @@
{ "all": true, "type": "DISABLE", "silencers": [] }

View File

@ -1 +0,0 @@
{ "all": false, "type": "DISABLE", "silencers": [ { "chart": "system.load" } ] }

View File

@ -1 +0,0 @@
{ "all": false, "type": "None", "silencers": [ { "families": "load" } ] }

View File

@ -1 +0,0 @@
{ "all": false, "type": "SILENCE", "silencers": [ { "hosts": "*" } ] }

View File

@ -1 +0,0 @@
{ "all": false, "type": "None", "silencers": [] }

View File

@ -1 +0,0 @@
{ "all": false, "type": "SILENCE", "silencers": [ { "alarm": "*10min_cpu_usage *load_trigger" } ] }

View File

@ -1 +0,0 @@
{ "all": false, "type": "SILENCE", "silencers": [ { "families": "load" } ] }

View File

@ -1 +0,0 @@
{ "all": false, "type": "SILENCE", "silencers": [] } WARNING: SILENCE or DISABLE command is ineffective without defining any alarm selectors.

View File

@ -1 +0,0 @@
{ "all": false, "type": "SILENCE", "silencers": [ { "alarm": "*10min_cpu_usage *load_trigger", "chart": "system.load" } ] }

View File

@ -1 +0,0 @@
{ "all": false, "type": "SILENCE", "silencers": [ { "alarm": "*10min_cpu_usage *load_trigger" } ] }

View File

@ -1 +0,0 @@
{ "all": true, "type": "SILENCE", "silencers": [] }

View File

@ -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,20 +53,6 @@ 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'
@ -104,13 +90,11 @@ 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}"
@ -118,107 +102,87 @@ 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"

View File

@ -45,7 +45,6 @@ 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.
@ -143,43 +142,6 @@ 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
@ -189,21 +151,10 @@ json
- "Health checks disabled for alarms matching the selectors" : Added to the response for a cmd=DISABLE
- "Alarm notifications silenced for alarms matching the selectors" : Added to the response for a cmd=SILENCE
- "Alarm selector added" : Added to the response when a new selector is added
- "Invalid key. Ignoring it." : Wrong name of a parameter. Added to the response and ignored.
- "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.13.1 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.

View File

@ -1,16 +1,17 @@
//
// Created by Christopher on 11/12/18.
// Created by christopher on 11/12/18.
//
#include "health_cmdapi.h"
/**
* Free Silencers
*
* Clean the silencer structure
*
* @param t is the structure that will be cleaned.
*/
static SILENCER *create_silencer(void) {
SILENCER *t = callocz(1, sizeof(SILENCER));
debug(D_HEALTH, "HEALTH command API: Created empty silencer");
return t;
}
void free_silencers(SILENCER *t) {
if (!t) return;
if (t->next) free_silencers(t->next);
@ -30,104 +31,38 @@ 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);
//Local instance of the silencer
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);
}
SILENCER *silencer = NULL;
int config_changed = 1;
if (!w->auth_bearer_token) {
buffer_strcat(wb, HEALTH_CMDAPI_MSG_AUTHERROR);
@ -150,7 +85,6 @@ int web_client_api_request_v1_mgmt_health(RRDHOST *host, struct web_client *w, c
// name and value are now the parameters
if (!strcmp(key, "cmd")) {
//In this "if" we are working with the global silencers.
if (!strcmp(value, HEALTH_CMDAPI_CMD_SILENCEALL)) {
silencers->all_alarms = 1;
silencers->stype = STYPE_SILENCE_NOTIFICATIONS;
@ -171,19 +105,50 @@ 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 {
//In this else we work with local silencer
silencer = health_silencers_addparam(silencer,key,value);
}
}
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);
}
}
}
if (likely(silencer)) {
health_silencers_add(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
);
buffer_strcat(wb, HEALTH_CMDAPI_MSG_ADDED);
if (silencers->stype == STYPE_NONE) {
buffer_strcat(wb, HEALTH_CMDAPI_MSG_STYPEWARNING);
@ -197,10 +162,5 @@ 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;
}

View File

@ -12,7 +12,6 @@
#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"
@ -21,6 +20,7 @@
#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"

View File

@ -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. LIST: Show active configuration.",
"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.",
"required": false,
"type": "string",
"enum": [
@ -672,8 +672,7 @@
"SILENCE ALL",
"DISABLE",
"SILENCE",
"RESET",
"LIST"
"RESET"
]
},
{

View File

@ -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. LIST: Show active configuration.'
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.'
required: false
type: string
enum: ['DISABLE ALL', 'SILENCE ALL', 'DISABLE', 'SILENCE', 'RESET', 'LIST']
enum: ['DISABLE ALL', 'SILENCE ALL', 'DISABLE', 'SILENCE', 'RESET']
- name: alarm
in: query
description: 'The expression provided will match both `alarm` and `template` names.'