diff options
50 files changed, 3141 insertions, 21 deletions
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f13a0bdba5..20c6f7b8c7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -57,7 +57,7 @@ jobs: - name: Configure run: | autoreconf -ivf - ./configure + ./configure --disable-ml # XXX: Work-around for bug with libbson-1.0 in Ubuntu 18.04 # See: https://bugs.launchpad.net/ubuntu/+source/libmongoc/+bug/1790771 # https://jira.mongodb.org/browse/CDRIVER-2818 diff --git a/.gitmodules b/.gitmodules index 83ef65e8d1..0f01d3d680 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,12 @@ [submodule "aclk/aclk-schemas"] path = aclk/aclk-schemas url = https://github.com/netdata/aclk-schemas.git +[submodule "ml/kmeans/dlib"] + path = ml/kmeans/dlib + url = https://github.com/davisking/dlib.git + shallow = true + ignore = dirty +[submodule "ml/json"] + path = ml/json + url = https://github.com/nlohmann/json.git + shallow = true @@ -17,6 +17,8 @@ path_classifiers: - collectors/node.d.plugin/node_modules/extend.js - collectors/node.d.plugin/node_modules/net-snmp.js - collectors/node.d.plugin/node_modules/pixl-xml.js + - ml/kmeans/dlib/ + - ml/json/ - web/gui/lib/ - web/gui/src/ - web/gui/css/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 873ad9a07f..aa49294e85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -945,6 +945,11 @@ set(DAEMON_FILES daemon/unit_test.h ) +set(ML_FILES + ml/ml.h + ml/ml-dummy.c +) + set(NETDATA_FILES collectors/all.h ${DAEMON_FILES} @@ -954,6 +959,7 @@ set(NETDATA_FILES ${CHECKS_PLUGIN_FILES} ${HEALTH_PLUGIN_FILES} ${IDLEJITTER_PLUGIN_FILES} + ${ML_FILES} ${PLUGINSD_PLUGIN_FILES} ${RRD_PLUGIN_FILES} ${REGISTRY_PLUGIN_FILES} diff --git a/Makefile.am b/Makefile.am index 508ddf381c..0cc3111aa3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -39,6 +39,7 @@ EXTRA_DIST = \ build/m4/ax_c_mallopt.m4 \ build/m4/tcmalloc.m4 \ build/m4/ax_c__generic.m4 \ + ml/kmeans/dlib \ README.md \ LICENSE \ REDISTRIBUTED.md \ @@ -117,6 +118,7 @@ SUBDIRS += \ claim \ parser \ spawn \ + ml \ $(NULL) if ENABLE_ACLK @@ -233,6 +235,44 @@ HEALTH_PLUGIN_FILES = \ health/health_log.c \ $(NULL) +ML_FILES = \ + ml/ml.h \ + ml/ml-dummy.c \ + $(NULL) + +if ENABLE_ML +ML_FILES += \ + ml/BitBufferCounter.h \ + ml/BitBufferCounter.cc \ + ml/BitRateWindow.h \ + ml/BitRateWindow.cc \ + ml/Config.h \ + ml/Config.cc \ + ml/Database.h \ + ml/Database.cc \ + ml/Dimension.cc \ + ml/Dimension.h \ + ml/Host.h \ + ml/Host.cc \ + ml/Query.h \ + ml/kmeans/KMeans.h \ + ml/kmeans/KMeans.cc \ + ml/kmeans/SamplesBuffer.h \ + ml/kmeans/SamplesBuffer.cc \ + ml/kmeans/dlib/dlib/all/source.cpp \ + ml/json/single_include/nlohmann/json.hpp \ + ml/ml.cc \ + ml/ml-private.h \ + $(NULL) +endif + +if ENABLE_ML_TESTS +ML_TESTS_FILES = \ + ml/kmeans/Tests.cc \ + ml/Tests.cc \ + $(NULL) +endif + IDLEJITTER_PLUGIN_FILES = \ collectors/idlejitter.plugin/plugin_idlejitter.c \ collectors/idlejitter.plugin/plugin_idlejitter.h \ @@ -863,6 +903,8 @@ NETDATA_FILES = \ $(EXPORTING_ENGINE_FILES) \ $(CHECKS_PLUGIN_FILES) \ $(HEALTH_PLUGIN_FILES) \ + $(ML_FILES) \ + $(ML_TESTS_FILES) \ $(IDLEJITTER_PLUGIN_FILES) \ $(PLUGINSD_PLUGIN_FILES) \ $(REGISTRY_PLUGIN_FILES) \ @@ -944,6 +986,11 @@ if ACLK_NG $(NULL) endif +if ENABLE_ML_TESTS + netdata_LDADD += $(OPTIONAL_ML_TESTS_LIBS) \ + $(NULL) +endif + if ACLK_LEGACY netdata_LDADD += \ $(abs_top_srcdir)/externaldeps/mosquitto/libmosquitto.a \ diff --git a/aclk/aclk_util.h b/aclk/aclk_util.h index fed1f7c200..750463b1c3 100644 --- a/aclk/aclk_util.h +++ b/aclk/aclk_util.h @@ -5,12 +5,6 @@ #include "libnetdata/libnetdata.h" #include "mqtt_wss_client.h" -// CentOS 7 has older version that doesn't define this -// same goes for MacOS -#ifndef UUID_STR_LEN -#define UUID_STR_LEN 37 -#endif - // Helper stuff which should not have any further inside ACLK dependency // and are supposed not to be needed outside of ACLK diff --git a/configure.ac b/configure.ac index 809192e870..4b42d8481b 100644 --- a/configure.ac +++ b/configure.ac @@ -184,6 +184,18 @@ AC_ARG_WITH( [with_bundled_protobuf="$withval"], [with_bundled_protobuf="detect"] ) +AC_ARG_ENABLE( + [ml], + [AS_HELP_STRING([--enable-ml], [Enable anomaly detection @<:@default autodetect@:>@])], + , + [enable_ml="detect"] +) +AC_ARG_ENABLE( + [ml_tests], + [AS_HELP_STRING([--enable-ml-tests], [Enable anomaly detection tests @<:@no@:>@])], + [enable_ml_tests="yes"], + [enable_ml_tests="no"] +) # ----------------------------------------------------------------------------- # Enforce building with C99, bail early if we can't. @@ -1190,6 +1202,90 @@ AC_MSG_RESULT([${enable_plugin_perf}]) AM_CONDITIONAL([ENABLE_PLUGIN_PERF], [test "${enable_plugin_perf}" = "yes"]) # ----------------------------------------------------------------------------- +# gtest/gmock + +AC_MSG_CHECKING([if gtest and gmock can be found]) + +PKG_CHECK_MODULES([GTEST], [gtest], [have_gtest=yes], [have_gtest=no]) +PKG_CHECK_MODULES([GMOCK], [gmock], [have_gmock=yes], [have_gmock=no]) + +if test "${have_gtest}" = "yes" -a "${have_gmock}" = "yes"; then + OPTIONAL_GTEST_CFLAGS="${GTEST_CFLAGS} ${GMOCK_CFLAGS}" + OPTIONAL_GTEST_LIBS="${GTEST_LIBS} ${GMOCK_LIBS}" + have_gtest="yes" +else + have_gtest="no" +fi + +# ----------------------------------------------------------------------------- +# ml - anomaly detection + +# Check if uuid is availabe. Fail if ML was explicitly requested. +if test "${enable_ml}" = "yes" -a "${have_uuid}" != "yes"; then + AC_MSG_ERROR([You have explicitly requested --enable-ml functionality but libuuid can not be found."]) +fi + +# Check if submodules have not been fetched. Fail if ML was explicitly requested. +AC_MSG_CHECKING([if git submodules are present for machine learning functionality]) +if test -f "ml/kmeans/dlib/dlib/all/source.cpp" -a -f "ml/json/single_include/nlohmann/json.hpp"; then + AC_MSG_RESULT([yes]) + have_ml_submodules="yes" +else + AC_MSG_RESULT([no]) + have_ml_submodules="no" +fi + +if test "${enable_ml}" = "yes" -a "${have_ml_submodules}" = "no"; then + AC_MSG_ERROR([You have explicitly requested --enable-ml functionality but it cannot be built because the required git submodules are missing.]) +fi + +# Check if C++ toolchain does not support C++11. Fail if ML was explicitly requested. +AC_LANG_PUSH([C++]) +AX_CHECK_COMPILE_FLAG([-std=c++11], [have_cxx11=yes], [have_cxx11=no]) +AC_LANG_POP([C++]) + +# PPC64LE needs -std=gnu++11 in order to build dlib. However, the rest of +# the agent's components use and have been tested only with -std=c++11. +# Skip ML compilation on that CPU until we reorganize and test the C++ flags. +if test "${host_cpu}" = "powerpc64le"; then + have_cxx11="no" +fi + +if test "${enable_ml}" = "yes" -a "${have_cxx11}" = "no"; then + AC_MSG_ERROR([You have explicitly requested --enable-ml functionality but it cannot be built without a C++11 toolchain.]) +else + CXX11FLAG="$CXX11FLAG -std=c++11" +fi + +# Decide if we should build ML +if test "${enable_ml}" != "no" -a "${have_ml_submodules}" = "yes" -a "${have_cxx11}" = "yes" -a "${have_uuid}" = "yes"; then + build_ml="yes" +else + build_ml="no" +fi + +AM_CONDITIONAL([ENABLE_ML], [test "${build_ml}" = "yes"]) +if test "${build_ml}" = "yes"; then + AC_DEFINE([ENABLE_ML], [1], [anomaly detection usability]) + OPTIONAL_ML_CFLAGS="-DDLIB_NO_GUI_SUPPORT -I \$(abs_top_srcdir)/ml/kmeans/dlib" + OPTIONAL_ML_LIBS="" +fi + +# Decide if we should build ML tests. +if test "${build_ml}" = "yes" -a "${enable_ml_tests}" = "yes" -a "${have_gtest}" = "yes"; then + build_ml_tests="yes" +else + build_ml_tests="no" +fi + +AM_CONDITIONAL([ENABLE_ML_TESTS], [test "${build_ml_tests}" = "yes"]) +if test "${build_ml_tests}" = "yes"; then + AC_DEFINE([ENABLE_ML_TESTS], [1], [anomaly detection tests]) + OPTIONAL_ML_TESTS_CFLAGS="${OPTIONAL_GTEST_CFLAGS}" + OPTIONAL_ML_TESTS_LIBS="${OPTIONAL_GTEST_LIBS}" +fi + +# ----------------------------------------------------------------------------- # ebpf.plugin if test "${build_target}" = "linux" -a "${enable_ebpf}" != "no"; then @@ -1557,7 +1653,8 @@ AC_MSG_RESULT([${enable_lto}]) AM_CONDITIONAL([ENABLE_CXX_LINKER], [test "${enable_backend_kinesis}" = "yes" \ -o "${enable_exporting_pubsub}" = "yes" \ -o "${enable_backend_prometheus_remote_write}" = "yes" \ - -o "${new_cloud_protocol}" = "yes"]) + -o "${new_cloud_protocol}" = "yes" \ + -o "${build_ml}" = "yes"]) AC_DEFINE_UNQUOTED([NETDATA_USER], ["${with_user}"], [use this user to drop privileged]) @@ -1589,7 +1686,7 @@ CFLAGS="${CFLAGS} ${OPTIONAL_PROTOBUF_CFLAGS} ${OPTIONAL_MATH_CFLAGS} ${OPTIONAL ${OPTIONAL_LIBCAP_CFLAGS} ${OPTIONAL_IPMIMONITORING_CFLAGS} ${OPTIONAL_CUPS_CFLAGS} ${OPTIONAL_XENSTAT_FLAGS} \ ${OPTIONAL_KINESIS_CFLAGS} ${OPTIONAL_PUBSUB_CFLAGS} ${OPTIONAL_PROMETHEUS_REMOTE_WRITE_CFLAGS} \ ${OPTIONAL_MONGOC_CFLAGS} ${LWS_CFLAGS} ${OPTIONAL_JSONC_STATIC_CFLAGS} ${OPTIONAL_BPF_CFLAGS} ${OPTIONAL_JUDY_CFLAGS} \ - ${OPTIONAL_ACLK_NG_CFLAGS}" + ${OPTIONAL_ACLK_NG_CFLAGS} ${OPTIONAL_ML_CFLAGS} ${OPTIONAL_ML_TESTS_CFLAGS}" CXXFLAGS="${CFLAGS} ${CXX11FLAG}" @@ -1642,6 +1739,12 @@ AC_SUBST([OPTIONAL_LWS_LIBS]) AC_SUBST([OPTIONAL_ACLK_NG_CFLAGS]) AC_SUBST([OPTIONAL_PROTOBUF_CFLAGS]) AC_SUBST([OPTIONAL_PROTOBUF_LIBS]) +AC_SUBST([OPTIONAL_GTEST_CFLAGS]) +AC_SUBST([OPTIONAL_GTEST_LIBS]) +AC_SUBST([OPTIONAL_ML_CFLAGS]) +AC_SUBST([OPTIONAL_ML_LIBS]) +AC_SUBST([OPTIONAL_ML_TESTS_CFLAGS]) +AC_SUBST([OPTIONAL_ML_TESTS_LIBS]) # ----------------------------------------------------------------------------- # Check if cmocka is available - needed for unit testing @@ -1731,6 +1834,8 @@ AC_CONFIG_FILES([ exporting/tests/Makefile health/Makefile health/notifications/Makefile + ml/Makefile + ml/kmeans/Makefile libnetdata/Makefile libnetdata/tests/Makefile libnetdata/adaptive_resortable_list/Makefile diff --git a/daemon/buildinfo.c b/daemon/buildinfo.c index 7477e3790b..3974074645 100644 --- a/daemon/buildinfo.c +++ b/daemon/buildinfo.c @@ -37,6 +37,12 @@ #define FEAT_NATIVE_HTTPS 0 #endif +#ifdef ENABLE_ML +#define FEAT_ML 1 +#else +#define FEAT_ML 0 +#endif + // Optional libraries #ifdef ENABLE_JSONC @@ -224,6 +230,7 @@ void print_build_info(void) { printf(" ACLK-NG New Cloud Protocol: %s\n", FEAT_YES_NO(NEW_CLOUD_PROTO)); printf(" ACLK Legacy: %s\n", FEAT_YES_NO(FEAT_ACLK_LEGACY)); printf(" TLS Host Verification: %s\n", FEAT_YES_NO(FEAT_TLS_HOST_VERIFY)); + printf(" Machine Learning: %s\n", FEAT_YES_NO(FEAT_ML)); printf("Libraries:\n"); printf(" jemalloc: %s\n", FEAT_YES_NO(FEAT_JEMALLOC)); @@ -282,7 +289,8 @@ void print_build_info_json(void) { printf(" \"aclk-ng-new-cloud-proto\": %s,\n", FEAT_JSON_BOOL(NEW_CLOUD_PROTO)); printf(" \"aclk-legacy\": %s,\n", FEAT_JSON_BOOL(FEAT_ACLK_LEGACY)); - printf(" \"tls-host-verify\": %s\n", FEAT_JSON_BOOL(FEAT_TLS_HOST_VERIFY)); + printf(" \"tls-host-verify\": %s,\n", FEAT_JSON_BOOL(FEAT_TLS_HOST_VERIFY)); + printf(" \"machine-learning\": %s\n", FEAT_JSON_BOOL(FEAT_ML)); printf(" },\n"); printf(" \"libs\": {\n"); @@ -337,6 +345,7 @@ void analytics_build_info(BUFFER *b) { if(NEW_CLOUD_PROTO) buffer_strcat (b, "|New Cloud Protocol Support"); if(FEAT_ACLK_LEGACY) buffer_strcat (b, "|ACLK Legacy"); if(FEAT_TLS_HOST_VERIFY) buffer_strcat (b, "|TLS Host Verification"); + if(FEAT_ML) buffer_strcat (b, "|Machine Learning"); if(FEAT_JEMALLOC) buffer_strcat (b, "|jemalloc"); if(FEAT_JSONC) buffer_strcat (b, "|JSON-C"); diff --git a/daemon/common.h b/daemon/common.h index 4c0677d96d..c892dbdb16 100644 --- a/daemon/common.h +++ b/daemon/common.h @@ -44,6 +44,9 @@ // health monitoring and alarm notifications #include "health/health.h" +// anomaly detection +#include "ml/ml.h" + // the netdata registry // the registry is actually an API feature #include "registry/registry.h" diff --git a/daemon/main.c b/daemon/main.c index 4bd434637e..0826e09d4f 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -826,6 +826,11 @@ int main(int argc, char **argv) { fprintf(stderr, "\n\nALL TESTS PASSED\n\n"); return 0; } +#ifdef ENABLE_ML_TESTS + else if(strcmp(optarg, "mltest") == 0) { + return test_ml(argc, argv); + } +#endif #ifdef ENABLE_DBENGINE else if(strncmp(optarg, createdataset_string, strlen(createdataset_string)) == 0) { optarg += strlen(createdataset_string); @@ -1145,6 +1150,10 @@ int main(int argc, char **argv) { health_initialize_global_silencers(); // -------------------------------------------------------------------- + // Initialize ML configuration + ml_init(); + + // -------------------------------------------------------------------- // setup process signals // block signals while initializing threads. diff --git a/database/engine/rrdenginelib.h b/database/engine/rrdenginelib.h index ebab93c8fe..8b6751f005 100644 --- a/database/engine/rrdenginelib.h +++ b/database/engine/rrdenginelib.h @@ -3,6 +3,8 @@ #ifndef NETDATA_RRDENGINELIB_H #define NETDATA_RRDENGINELIB_H +#include "libnetdata/libnetdata.h" + /* Forward declarations */ struct rrdeng_page_descr; struct rrdengine_instance; @@ -12,10 +14,6 @@ struct rrdengine_instance; #define BITS_PER_ULONG (sizeof(unsigned long) * 8) -#ifndef UUID_STR_LEN -#define UUID_STR_LEN (37) -#endif - /* Taken from linux kernel */ #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) @@ -141,4 +139,4 @@ extern char *get_rrdeng_statistics(struct rrdengine_instance *ctx, char *str, si extern int compute_multidb_diskspace(); extern int is_legacy_child(const char *machine_guid); -#endif /* NETDATA_RRDENGINELIB_H */
\ No newline at end of file +#endif /* NETDATA_RRDENGINELIB_H */ diff --git a/database/rrd.h b/database/rrd.h index 95c49eb21e..62edec423f 100644 --- a/database/rrd.h +++ b/database/rrd.h @@ -15,6 +15,9 @@ typedef struct rrdcalctemplate RRDCALCTEMPLATE; typedef struct alarm_entry ALARM_ENTRY; typedef struct context_param CONTEXT_PARAM; +typedef void *ml_host_t; +typedef void *ml_dimension_t; + // forward declarations struct rrddim_volatile; struct rrdset_volatile; @@ -421,6 +424,8 @@ struct rrddim_volatile { // get the timestamp of the first entry of this metric time_t (*oldest_time)(RRDDIM *rd); } query_ops; + + ml_dimension_t ml_dimension; }; // ---------------------------------------------------------------------------- @@ -877,6 +882,10 @@ struct rrdhost { netdata_rwlock_t rrdhost_rwlock; // lock for this RRDHOST (protects rrdset_root linked list) // ------------------------------------------------------------------------ + // ML handle + ml_host_t ml_host; + + // ------------------------------------------------------------------------ // Support for host-level labels struct label_index labels; diff --git a/database/rrddim.c b/database/rrddim.c index 5479ade7aa..78885df3dc 100644 --- a/database/rrddim.c +++ b/database/rrddim.c @@ -386,7 +386,7 @@ RRDDIM *rrddim_add_custom(RRDSET *st, const char *id, const char *name, collecte rd->last_collected_time.tv_sec = 0; rd->last_collected_time.tv_usec = 0; rd->rrdset = st; - rd->state = mallocz(sizeof(*rd->state)); + rd->state = callocz(1, sizeof(*rd->state)); #ifdef ENABLE_ACLK rd->state->aclk_live_status = -1; #endif @@ -454,6 +454,8 @@ RRDDIM *rrddim_add_custom(RRDSET *st, const char *id, const char *name, collecte calc_link_to_rrddim(rd); + ml_new_dimension(rd); + rrdset_unlock(st); #ifdef ENABLE_ACLK rrdset_flag_clear(st, RRDSET_FLAG_ACLK); @@ -466,6 +468,8 @@ RRDDIM *rrddim_add_custom(RRDSET *st, const char *id, const char *name, collecte void rrddim_free_custom(RRDSET *st, RRDDIM *rd, int db_rotated) { + ml_delete_dimension(rd); + #ifndef ENABLE_ACLK UNUSED(db_rotated); #endif diff --git a/database/rrdhost.c b/database/rrdhost.c index abb14beaf4..b28db1e3e4 100644 --- a/database/rrdhost.c +++ b/database/rrdhost.c @@ -382,6 +382,8 @@ RRDHOST *rrdhost_create(const char *hostname, else localhost = host; } + ml_new_host(host); + info("Host '%s' (at registry as '%s') with guid '%s' initialized" ", os '%s'" ", timezone '%s'" @@ -906,6 +908,8 @@ void rrdhost_free(RRDHOST *host) { rrdeng_exit(host->rrdeng_ctx); #endif + ml_delete_host(host); + // ------------------------------------------------------------------------ // remove it from the indexes diff --git a/database/rrdset.c b/database/rrdset.c index 282b0e5ba1..c0ac3be7b8 100644 --- a/database/rrdset.c +++ b/database/rrdset.c @@ -1237,13 +1237,22 @@ static inline size_t rrdset_done_interpolate( } if(unlikely(!store_this_entry)) { + (void) ml_is_anomalous(rd, 0, false); + rd->state->collect_ops.store_metric(rd, next_store_ut, SN_EMPTY_SLOT); // rd->values[current_entry] = SN_EMPTY_SLOT; continue; } if(likely(rd->updated && rd->collections_counter > 1 && iterations < st->gap_when_lost_iterations_above)) { - rd->state->collect_ops.store_metric(rd, next_store_ut, pack_storage_number(new_value, storage_flags)); + uint32_t dim_storage_flags = storage_flags; + + if (ml_is_anomalous(rd, new_value, true)) { + // clear anomaly bit: 0 -> is anomalous, 1 -> not anomalous + dim_storage_flags &= ~ ((uint32_t) SN_ANOMALY_BIT); + } + + rd->state->collect_ops.store_metric(rd, next_store_ut, pack_storage_number(new_value, dim_storage_flags)); // rd->values[current_entry] = pack_storage_number(new_value, storage_flags ); rd->last_stored_value = new_value; @@ -1255,9 +1264,9 @@ static inline size_t rrdset_done_interpolate( , unpack_storage_number(rd->values[current_entry]), new_value ); #endif - } else { + (void) ml_is_anomalous(rd, 0, false); #ifdef NETDATA_INTERNAL_CHECKS rrdset_debug(st, "%s: STORE[%ld] = NON EXISTING " diff --git a/libnetdata/config/appconfig.c b/libnetdata/config/appconfig.c index 90fa6ab852..37e9e76886 100644 --- a/libnetdata/config/appconfig.c +++ b/libnetdata/config/appconfig.c @@ -796,6 +796,7 @@ void appconfig_generate(struct config *root, BUFFER *wb, int only_changed) || !strcmp(co->name, CONFIG_SECTION_BACKEND) || !strcmp(co->name, CONFIG_SECTION_STREAM) || !strcmp(co->name, CONFIG_SECTION_HOST_LABEL) + || !strcmp(co->name, CONFIG_SECTION_ML) ) pri = 0; else if(!strncmp(co->name, "plugin:", 7)) pri = 1; diff --git a/libnetdata/config/appconfig.h b/libnetdata/config/appconfig.h index 246d1d5b92..bfc927353a 100644 --- a/libnetdata/config/appconfig.h +++ b/libnetdata/config/appconfig.h @@ -91,6 +91,7 @@ #define CONFIG_SECTION_HEALTH "health" #define CONFIG_SECTION_BACKEND "backend" #define CONFIG_SECTION_STREAM "stream" +#define CONFIG_SECTION_ML "ml" #define CONFIG_SECTION_EXPORTING "exporting:global" #define CONFIG_SECTION_PROMETHEUS "prometheus:exporter" #define CONFIG_SECTION_HOST_LABEL "host labels" diff --git a/libnetdata/libnetdata.h b/libnetdata/libnetdata.h index 77a1bbe7fd..b49ab21a08 100644 --- a/libnetdata/libnetdata.h +++ b/libnetdata/libnetdata.h @@ -53,6 +53,7 @@ extern "C" { #include <pthread.h> #include <errno.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <stdarg.h> @@ -90,6 +91,12 @@ extern "C" { #include <uv.h> #include <assert.h> +// CentOS 7 has older version that doesn't define this +// same goes for MacOS +#ifndef UUID_STR_LEN +#define UUID_STR_LEN (37) +#endif + |