@cryptotaxi247 / netdata-1 / commits / 0b4f820e9

/api/v2/nodes and streaming function (#15168)

* dummy streaming function * expose global functions upstream * separate function for pushing global functions * add missing conditions * allow streaming function to run async * started internal API for functions * cache host retention and expose it to /api/v2/nodes * internal API for function table fields; more progress on streaming status * abstracted and unified rrdhost status * port old coverity warning fix - although it is not needed * add ML information to rrdhost status * add ML capability to streaming to signal the transmission of ML information; added ML information to host status * protect host->receiver * count metrics and instances per host * exposed all inbound and outbound streaming * fix for ML status and dependency of DATA_WITH_ML to INTERPOLATED, not IEEE754 * update ML dummy * added all fields * added streaming group by and cleaned up accepted values by cloud * removed type * Revert "removed type" This reverts commit faae4177e603d4f85b7433f33f92ef3ccd23976e. * added context to db summary * new /api/v2/nodes schema * added ML type * change default function charts * log to trace new capa * add more debug * removed debugging code * retry on receive interrupted read; respect sender reconnect delay in all cases * set disconnected host flag and manipulate localhost child count atomically, inside set/clear receiver * fix infinite loop * send_to_plugin() now has a spinlock to ensure that only 1 thread is writing to the plugin/child at the same time * global cloud_status() call * cloud should be a section, since it will contain error information * put cloud capabilities into cloud * aclk status in /api/v2 agents sections * keep aclk_connection_counter * updates on /api/v2/nodes * final /api/v2/nodes and addition of /api/v2/nodes_instances * parametrize all /api/v2/xxx output to control which info is outputed per endpoint * always accept nodes selector * st needs to be per instance, not per node * fix merging of contexts; fix cups plugin priorities * add after and before parameters to /api/v2/contexts/nodes/nodes_instances/q * give each libuv worker a unique id * aclk http_api_v2 version 4

Costa Tsaousis committed Jun 19, 2023 at 20:52 UTC 0b4f820e9d42d10f64c3305d9c084261bc9880cf
43 files changed +2801 -539
aclk/aclk.c
+125 -10
@@ -155,7 +155,7 @@ biofailed:
155 static int wait_till_cloud_enabled()
156 {
157 info("Waiting for Cloud to be enabled");
158 - while (!netdata_cloud_setting) {
158 + while (!netdata_cloud_enabled) {
159 sleep_usec(USEC_PER_SEC * 1);
160 if (!service_running(SERVICE_ACLK))
161 return 1;
@@ -489,6 +489,74 @@ static int aclk_get_transport_idx(aclk_env_t *env) {
489 }
490 #endif
491
492 +ACLK_STATUS aclk_status = ACLK_STATUS_INITIALIZING;
493 +
494 +const char *aclk_status_to_string(void) {
495 + switch(aclk_status) {
496 + case ACLK_STATUS_CONNECTED:
497 + return "connected";
498 +
499 + case ACLK_STATUS_INITIALIZING:
500 + return "initializing";
501 +
502 + case ACLK_STATUS_DISABLED:
503 + return "disabled";
504 +
505 + case ACLK_STATUS_NO_CLOUD_URL:
506 + return "no_cloud_url";
507 +
508 + case ACLK_STATUS_INVALID_CLOUD_URL:
509 + return "invalid_cloud_url";
510 +
511 + case ACLK_STATUS_NOT_CLAIMED:
512 + return "not_claimed";
513 +
514 + case ACLK_STATUS_ENV_ENDPOINT_UNREACHABLE:
515 + return "env_endpoint_unreachable";
516 +
517 + case ACLK_STATUS_ENV_RESPONSE_NOT_200:
518 + return "env_response_not_200";
519 +
520 + case ACLK_STATUS_ENV_RESPONSE_EMPTY:
521 + return "env_response_empty";
522 +
523 + case ACLK_STATUS_ENV_RESPONSE_NOT_JSON:
524 + return "env_response_not_json";
525 +
526 + case ACLK_STATUS_ENV_FAILED:
527 + return "env_failed";
528 +
529 + case ACLK_STATUS_BLOCKED:
530 + return "blocked";
531 +
532 + case ACLK_STATUS_NO_OLD_PROTOCOL:
533 + return "no_old_protocol";
534 +
535 + case ACLK_STATUS_NO_PROTOCOL_CAPABILITY:
536 + return "no_protocol_capability";
537 +
538 + case ACLK_STATUS_INVALID_ENV_AUTH_URL:
539 + return "invalid_env_auth_url";
540 +
541 + case ACLK_STATUS_INVALID_ENV_TRANSPORT_IDX:
542 + return "invalid_env_transport_idx";
543 +
544 + case ACLK_STATUS_INVALID_ENV_TRANSPORT_URL:
545 + return "invalid_env_transport_url";
546 +
547 + case ACLK_STATUS_INVALID_OTP:
548 + return "invalid_otp";
549 +
550 + case ACLK_STATUS_NO_LWT_TOPIC:
551 + return "no_lwt_topic";
552 +
553 + default:
554 + return "unknown";
555 + }
556 +}
557 +
558 +const char *aclk_cloud_base_url = NULL;
559 +
560 /* Attempts to make a connection to MQTT broker over WSS
561 * @param client instance of mqtt_wss_client
562 * @return 0 - Successful Connection,
@@ -513,18 +581,22 @@ static int aclk_attempt_to_connect(mqtt_wss_client client)
581 #endif
582
583 while (service_running(SERVICE_ACLK)) {
516 - char *cloud_base_url = appconfig_get(&cloud_config, CONFIG_SECTION_GLOBAL, "cloud base url", NULL);
517 - if (cloud_base_url == NULL) {
584 + aclk_cloud_base_url = appconfig_get(&cloud_config, CONFIG_SECTION_GLOBAL, "cloud base url", NULL);
585 + if (aclk_cloud_base_url == NULL) {
586 error_report("Do not move the cloud base url out of post_conf_load!!");
587 + aclk_status = ACLK_STATUS_NO_CLOUD_URL;
588 return -1;
589 }
590
522 - if (aclk_block_till_recon_allowed())
591 + if (aclk_block_till_recon_allowed()) {
592 + aclk_status = ACLK_STATUS_BLOCKED;
593 return 1;
594 + }
595
596 info("Attempting connection now");
597 memset(&base_url, 0, sizeof(url_t));
527 - if (url_parse(cloud_base_url, &base_url)) {
598 + if (url_parse(aclk_cloud_base_url, &base_url)) {
599 + aclk_status = ACLK_STATUS_INVALID_CLOUD_URL;
600 error_report("ACLK base URL configuration key could not be parsed. Will retry in %d seconds.", CLOUD_BASE_URL_READ_RETRY);
601 sleep(CLOUD_BASE_URL_READ_RETRY);
602 url_t_destroy(&base_url);
@@ -554,21 +626,57 @@ static int aclk_attempt_to_connect(mqtt_wss_client client)
626
627 ret = aclk_get_env(aclk_env, base_url.host, base_url.port);
628 url_t_destroy(&base_url);
557 - if (ret) {
558 - error_report("Failed to Get ACLK environment");
559 - // delay handled by aclk_block_till_recon_allowed
560 - continue;
629 + if(ret) switch(ret) {
630 + case 1:
631 + aclk_status = ACLK_STATUS_NOT_CLAIMED;
632 + error_report("Failed to Get ACLK environment (agent is not claimed)");
633 + // delay handled by aclk_block_till_recon_allowed
634 + continue;
635 +
636 + case 2:
637 + aclk_status = ACLK_STATUS_ENV_ENDPOINT_UNREACHABLE;
638 + error_report("Failed to Get ACLK environment (cannot contact ENV endpoint)");
639 + // delay handled by aclk_block_till_recon_allowed
640 + continue;
641 +
642 + case 3:
643 + aclk_status = ACLK_STATUS_ENV_RESPONSE_NOT_200;
644 + error_report("Failed to Get ACLK environment (ENV response code is not 200)");
645 + // delay handled by aclk_block_till_recon_allowed
646 + continue;
647 +
648 + case 4:
649 + aclk_status = ACLK_STATUS_ENV_RESPONSE_EMPTY;
650 + error_report("Failed to Get ACLK environment (ENV response is empty)");
651 + // delay handled by aclk_block_till_recon_allowed
652 + continue;
653 +
654 + case 5:
655 + aclk_status = ACLK_STATUS_ENV_RESPONSE_NOT_JSON;
656 + error_report("Failed to Get ACLK environment (ENV response is not JSON)");
657 + // delay handled by aclk_block_till_recon_allowed
658 + continue;
659 +
660 + default:
661 + aclk_status = ACLK_STATUS_ENV_FAILED;
662 + error_report("Failed to Get ACLK environment (unknown error)");
663 + // delay handled by aclk_block_till_recon_allowed
664 + continue;
665 }
666
563 - if (!service_running(SERVICE_ACLK))
667 + if (!service_running(SERVICE_ACLK)) {
668 + aclk_status = ACLK_STATUS_DISABLED;
669 return 1;
670 + }
671
672 if (aclk_env->encoding != ACLK_ENC_PROTO) {
673 + aclk_status = ACLK_STATUS_NO_OLD_PROTOCOL;
674 error_report("This agent can only use the new cloud protocol but cloud requested old one.");
675 continue;
676 }
677
678 if (!aclk_env_has_capa("proto")) {
679 + aclk_status = ACLK_STATUS_NO_PROTOCOL_CAPABILITY;
680 error_report("Can't use encoding=proto without at least \"proto\" capability.");
681 continue;
682 }
@@ -576,6 +684,7 @@ static int aclk_attempt_to_connect(mqtt_wss_client client)
684
685 memset(&auth_url, 0, sizeof(url_t));
686 if (url_parse(aclk_env->auth_endpoint, &auth_url)) {
687 + aclk_status = ACLK_STATUS_INVALID_ENV_AUTH_URL;
688 error_report("Parsing URL returned by env endpoint for authentication failed. \"%s\"", aclk_env->auth_endpoint);
689 url_t_destroy(&auth_url);
690 continue;
@@ -584,6 +693,7 @@ static int aclk_attempt_to_connect(mqtt_wss_client client)
693 ret = aclk_get_mqtt_otp(aclk_private_key, (char **)&mqtt_conn_params.clientid, (char **)&mqtt_conn_params.username, (char **)&mqtt_conn_params.password, &auth_url);
694 url_t_destroy(&auth_url);
695 if (ret) {
696 + aclk_status = ACLK_STATUS_INVALID_OTP;
697 error_report("Error passing Challenge/Response to get OTP");
698 continue;
699 }
@@ -593,6 +703,7 @@ static int aclk_attempt_to_connect(mqtt_wss_client client)
703 mqtt_conn_params.will_topic = aclk_get_topic(ACLK_TOPICID_AGENT_CONN);
704
705 if (!mqtt_conn_params.will_topic) {
706 + aclk_status = ACLK_STATUS_NO_LWT_TOPIC;
707 error_report("Couldn't get LWT topic. Will not send LWT.");
708 continue;
709 }
@@ -600,12 +711,14 @@ static int aclk_attempt_to_connect(mqtt_wss_client client)
711 // Do the MQTT connection
712 ret = aclk_get_transport_idx(aclk_env);
713 if (ret < 0) {
714 + aclk_status = ACLK_STATUS_INVALID_ENV_TRANSPORT_IDX;
715 error_report("Cloud /env endpoint didn't return any transport usable by this Agent.");
716 continue;
717 }
718
719 memset(&mqtt_url, 0, sizeof(url_t));
720 if (url_parse(aclk_env->transports[ret]->endpoint, &mqtt_url)){
721 + aclk_status = ACLK_STATUS_INVALID_ENV_TRANSPORT_URL;
722 error_report("Failed to parse target URL for /env trp idx %d \"%s\"", ret, aclk_env->transports[ret]->endpoint);
723 url_t_destroy(&mqtt_url);
724 continue;
@@ -638,6 +751,7 @@ static int aclk_attempt_to_connect(mqtt_wss_client client)
751 if (!ret) {
752 last_conn_time_mqtt = now_realtime_sec();
753 info("ACLK connection successfully established");
754 + aclk_status = ACLK_STATUS_CONNECTED;
755 log_access("ACLK CONNECTED");
756 mqtt_connected_actions(client);
757 return 0;
@@ -646,6 +760,7 @@ static int aclk_attempt_to_connect(mqtt_wss_client client)
760 error_report("Connect failed");
761 }
762
763 + aclk_status = ACLK_STATUS_DISABLED;
764 return 1;
765 }
766
aclk/aclk.h
+33
@@ -13,17 +13,50 @@
13 #define ACLK_PUBACKS_CONN_STABLE 3
14 #endif /* ENABLE_ACLK */
15
16 +typedef enum __attribute__((packed)) {
17 + ACLK_STATUS_CONNECTED = 0,
18 + ACLK_STATUS_INITIALIZING,
19 + ACLK_STATUS_DISABLED,
20 + ACLK_STATUS_NO_CLOUD_URL,
21 + ACLK_STATUS_INVALID_CLOUD_URL,
22 + ACLK_STATUS_NOT_CLAIMED,
23 + ACLK_STATUS_ENV_ENDPOINT_UNREACHABLE,
24 + ACLK_STATUS_ENV_RESPONSE_NOT_200,
25 + ACLK_STATUS_ENV_RESPONSE_EMPTY,
26 + ACLK_STATUS_ENV_RESPONSE_NOT_JSON,
27 + ACLK_STATUS_ENV_FAILED,
28 + ACLK_STATUS_BLOCKED,
29 + ACLK_STATUS_NO_OLD_PROTOCOL,
30 + ACLK_STATUS_NO_PROTOCOL_CAPABILITY,
31 + ACLK_STATUS_INVALID_ENV_AUTH_URL,
32 + ACLK_STATUS_INVALID_ENV_TRANSPORT_IDX,
33 + ACLK_STATUS_INVALID_ENV_TRANSPORT_URL,
34 + ACLK_STATUS_INVALID_OTP,
35 + ACLK_STATUS_NO_LWT_TOPIC,
36 +} ACLK_STATUS;
37 +
38 +extern ACLK_STATUS aclk_status;
39 +extern const char *aclk_cloud_base_url;
40 +const char *aclk_status_to_string(void);
41 +
42 extern int aclk_connected;
43 extern int aclk_ctx_based;
44 extern int aclk_disable_runtime;
45 extern int aclk_stats_enabled;
46 extern int aclk_kill_link;
47
48 +extern time_t last_conn_time_mqtt;
49 +extern time_t last_conn_time_appl;
50 +extern time_t last_disconnect_time;
51 +extern time_t next_connection_attempt;
52 +extern float last_backoff_value;
53 +
54 extern usec_t aclk_session_us;
55 extern time_t aclk_session_sec;
56
57 extern time_t aclk_block_until;
58
59 +extern int aclk_connection_counter;
60 extern int disconnect_req;
61
62 #ifdef ENABLE_ACLK
aclk/aclk_capas.c
+4 -7
@@ -13,7 +13,7 @@ const struct capability *aclk_get_agent_capas()
13 { .name = "mc", .version = 0, .enabled = 0 },
14 { .name = "ctx", .version = 1, .enabled = 1 },
15 { .name = "funcs", .version = 1, .enabled = 1 },
16 - { .name = "http_api_v2", .version = 3, .enabled = 1 },
16 + { .name = "http_api_v2", .version = 4, .enabled = 1 },
17 { .name = "health", .version = 1, .enabled = 0 },
18 { .name = "req_cancel", .version = 1, .enabled = 1 },
19 { .name = NULL, .version = 0, .enabled = 0 }
@@ -31,6 +31,8 @@ const struct capability *aclk_get_agent_capas()
31
32 struct capability *aclk_get_node_instance_capas(RRDHOST *host)
33 {
34 + bool functions = (host == localhost || (host->receiver && stream_has_capability(host->receiver, STREAM_CAP_FUNCTIONS)));
35 +
36 struct capability ni_caps[] = {
37 { .name = "proto", .version = 1, .enabled = 1 },
38 { .name = "ml", .version = ml_capable(), .enabled = ml_enabled(host) },
@@ -38,7 +40,7 @@ struct capability *aclk_get_node_instance_capas(RRDHOST *host)
40 .version = enable_metric_correlations ? metric_correlations_version : 0,
41 .enabled = enable_metric_correlations },
42 { .name = "ctx", .version = 1, .enabled = 1 },
41 - { .name = "funcs", .version = 0, .enabled = 0 },
43 + { .name = "funcs", .version = functions ? 1 : 0, .enabled = functions ? 1 : 0 },
44 { .name = "http_api_v2", .version = 3, .enabled = 1 },
45 { .name = "health", .version = 1, .enabled = host->health.health_enabled },
46 { .name = "req_cancel", .version = 1, .enabled = 1 },
@@ -48,10 +50,5 @@ struct capability *aclk_get_node_instance_capas(RRDHOST *host)
50 struct capability *ret = mallocz(sizeof(ni_caps));
51 memcpy(ret, ni_caps, sizeof(ni_caps));
52
51 - if (host == localhost || (host->receiver && stream_has_capability(host->receiver, STREAM_CAP_FUNCTIONS))) {
52 - ret[4].version = 1;
53 - ret[4].enabled = 1;
54 - }
55 -
53 return ret;
54 }
aclk/aclk_otp.c
+4 -4
@@ -846,7 +846,7 @@ int aclk_get_env(aclk_env_t *env, const char* aclk_hostname, int aclk_port) {
846 error("Error trying to contact env endpoint");
847 https_req_response_free(&resp);
848 buffer_free(buf);
849 - return 1;
849 + return 2;
850 }
851 if (resp.http_code != 200) {
852 error("The HTTP code not 200 OK (Got %d)", resp.http_code);
@@ -854,21 +854,21 @@ int aclk_get_env(aclk_env_t *env, const char* aclk_hostname, int aclk_port) {
854 aclk_parse_otp_error(resp.payload);
855 https_req_response_free(&resp);
856 buffer_free(buf);
857 - return 1;
857 + return 3;
858 }
859
860 if (!resp.payload || !resp.payload_size) {
861 error("Unexpected empty payload as response to /env call");
862 https_req_response_free(&resp);
863 buffer_free(buf);
864 - return 1;
864 + return 4;
865 }
866
867 if (parse_json_env(resp.payload, env)) {
868 error ("error parsing /env message");
869 https_req_response_free(&resp);
870 buffer_free(buf);
871 - return 1;
871 + return 5;
872 }
873
874 info("Getting Cloud /env successful");
claim/claim.c
+3 -3
@@ -49,7 +49,7 @@ extern struct registry registry;
49 /* rrd_init() and post_conf_load() must have been called before this function */
50 void claim_agent(char *claiming_arguments)
51 {
52 - if (!netdata_cloud_setting) {
52 + if (!netdata_cloud_enabled) {
53 error("Refusing to claim agent -> cloud functionality has been disabled");
54 return;
55 }
@@ -132,7 +132,7 @@ void load_claiming_state(void)
132 // --------------------------------------------------------------------
133 // Check if the cloud is enabled
134 #if defined( DISABLE_CLOUD ) || !defined( ENABLE_ACLK )
135 - netdata_cloud_setting = 0;
135 + netdata_cloud_enabled = false;
136 #else
137 uuid_t uuid;
138
@@ -181,7 +181,7 @@ void load_claiming_state(void)
181 freez(claimed_id);
182
183 info("File '%s' was found. Setting state to AGENT_CLAIMED.", filename);
184 - netdata_cloud_setting = appconfig_get_boolean(&cloud_config, CONFIG_SECTION_GLOBAL, "enabled", 1);
184 + netdata_cloud_enabled = appconfig_get_boolean(&cloud_config, CONFIG_SECTION_GLOBAL, "enabled", 1);
185 #endif
186 }
187
collectors/apps.plugin/apps_plugin.c
+275 -82
@@ -4356,32 +4356,6 @@ static void apps_plugin_function_processes_help(const char *transaction) {
4356 pluginsd_function_result_end_to_stdout();
4357 }
4358
4359 -#define add_table_field(wb, key, name, visible, type, visualization, transform, decimal_points, units, max, sort, sortable, sticky, unique_key, pointer_to, summary, range) do { \
4360 - buffer_json_member_add_object(wb, key); \
4361 - buffer_json_member_add_uint64(wb, "index", fields_added); \
4362 - buffer_json_member_add_boolean(wb, "unique_key", unique_key); \
4363 - buffer_json_member_add_string(wb, "name", name); \
4364 - buffer_json_member_add_boolean(wb, "visible", visible); \
4365 - buffer_json_member_add_string(wb, "type", type); \
4366 - buffer_json_member_add_string_or_omit(wb, "units", (char*)(units)); \
4367 - buffer_json_member_add_string(wb, "visualization", visualization); \
4368 - buffer_json_member_add_object(wb, "value_options"); \
4369 - buffer_json_member_add_string_or_omit(wb, "units", (char*)(units)); \
4370 - buffer_json_member_add_string(wb, "transform", transform); \
4371 - buffer_json_member_add_uint64(wb, "decimal_points", decimal_points); \
4372 - buffer_json_object_close(wb); \
4373 - if(!isnan((NETDATA_DOUBLE)(max))) \
4374 - buffer_json_member_add_double(wb, "max", (NETDATA_DOUBLE)(max)); \
4375 - buffer_json_member_add_string_or_omit(wb, "pointer_to", (char *)(pointer_to)); \
4376 - buffer_json_member_add_string(wb, "sort", sort); \
4377 - buffer_json_member_add_boolean(wb, "sortable", sortable); \
4378 - buffer_json_member_add_boolean(wb, "sticky", sticky); \
4379 - buffer_json_member_add_string(wb, "summary", summary); \
4380 - buffer_json_member_add_string(wb, "filter", (range)?"range":"multiselect"); \
4381 - buffer_json_object_close(wb); \
4382 - fields_added++; \
4383 -} while(0)
4384 -
4359 #define add_value_field_llu_with_max(wb, key, value) do { \
4360 unsigned long long _tmp = (value); \
4361 key ## _max = (rows == 0) ? (_tmp) : MAX(key ## _max, _tmp); \
@@ -4553,7 +4527,7 @@ static void apps_plugin_function_processes(const char *transaction, char *functi
4527
4528 rows++;
4529
4556 - buffer_json_add_array_item_array(wb);
4530 + buffer_json_add_array_item_array(wb); // for each pid
4531
4532 // IMPORTANT!
4533 // THE ORDER SHOULD BE THE SAME WITH THE FIELDS!
@@ -4649,94 +4623,299 @@ static void apps_plugin_function_processes(const char *transaction, char *functi
4623 add_value_field_llu_with_max(wb, Threads, p->num_threads);
4624 add_value_field_llu_with_max(wb, Uptime, p->uptime);
4625
4652 - buffer_json_array_close(wb);
4626 + buffer_json_array_close(wb); // for each pid
4627 }
4628
4655 - buffer_json_array_close(wb);
4629 + buffer_json_array_close(wb); // data
4630 buffer_json_member_add_object(wb, "columns");
4631
4632 {
4659 - int fields_added = 0;
4633 + int field_id = 0;
4634
4635 // IMPORTANT!
4636 // THE ORDER SHOULD BE THE SAME WITH THE VALUES!
4663 - add_table_field(wb, "PID", "Process ID", true, "integer", "value", "number", 0, NULL, NAN, "ascending", true, true, true, NULL, "count_unique", false);
4664 - add_table_field(wb, "Cmd", "Process Name", true, "string", "value", "none", 0, NULL, NAN, "ascending", true, true, false, NULL, "count_unique", false);
4637 + // wb, key, name, visible, type, visualization, transform, decimal_points, units, max, sort, sortable, sticky, unique_key, pointer_to, summary, range
4638 + buffer_rrdf_table_add_field(wb, field_id++, "PID", "Process ID", RRDF_FIELD_TYPE_INTEGER,
4639 + RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, 0, NULL, NAN,
4640 + RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
4641 + RRDF_FIELD_FILTER_MULTISELECT,
4642 + RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_STICKY |
4643 + RRDF_FIELD_OPTS_UNIQUE_KEY, NULL);
4644 +
4645 + buffer_rrdf_table_add_field(wb, field_id++, "Cmd", "Process Name", RRDF_FIELD_TYPE_STRING,
4646 + RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, 0, NULL, NAN,
4647 + RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
4648 + RRDF_FIELD_FILTER_MULTISELECT,
4649 + RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_STICKY, NULL);
4650
4651 #ifdef NETDATA_DEV_MODE
4667 - add_table_field(wb, "CmdLine", "Command Line", false, "detail-string:Cmd", "value", "none", 0, NULL, NAN, "ascending", true, false, false, NULL, "count_unique", false);
4652 + buffer_rrdf_table_add_field(wb, field_id++, "CmdLine", "Command Line", RRDF_FIELD_TYPE_DETAIL_STRING,
4653 + RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, 0,
4654 + NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
4655 + RRDF_FIELD_FILTER_MULTISELECT,
4656 + RRDF_FIELD_OPTS_NONE, NULL);
4657 #endif
4669 - add_table_field(wb, "PPID", "Parent Process ID", false, "integer", "value", "number", 0, NULL, NAN, "ascending", true, false, false, "PID", "count_unique", false);
4670 - add_table_field(wb, "Category", "Category (apps_groups.conf)", true, "string", "value", "none", 0, NULL, NAN, "ascending", true, true, false, NULL, "count_unique", false);
4671 - add_table_field(wb, "User", "User Owner", true, "string", "value", "none", 0, NULL, NAN, "ascending", true, false, false, NULL, "count_unique", false);
4672 - add_table_field(wb, "Uid", "User ID", false, "integer", "value", "number", 0, NULL, NAN, "ascending", true, false, false, NULL, "count_unique", false);
4673 - add_table_field(wb, "Group", "Group Owner", false, "string", "value", "none", 0, NULL, NAN, "ascending", true, false, false, NULL, "count_unique", false);
4674 - add_table_field(wb, "Gid", "Group ID", false, "integer", "value", "number", 0, NULL, NAN, "ascending", true, false, false, NULL, "count_unique", false);
4658 + buffer_rrdf_table_add_field(wb, field_id++, "PPID", "Parent Process ID", RRDF_FIELD_TYPE_INTEGER,
4659 + RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, 0, NULL,
4660 + NAN, RRDF_FIELD_SORT_ASCENDING, "PID", RRDF_FIELD_SUMMARY_COUNT,
4661 + RRDF_FIELD_FILTER_MULTISELECT,
4662 + RRDF_FIELD_OPTS_NONE, NULL);
4663 + buffer_rrdf_table_add_field(wb, field_id++, "Category", "Category (apps_groups.conf)", RRDF_FIELD_TYPE_STRING,
4664 + RRDF_FIELD_VISUAL_VALUE,
4665 + RRDF_FIELD_TRANSFORM_NONE,
4666 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
4667 + RRDF_FIELD_FILTER_MULTISELECT,
4668 + RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_STICKY, NULL);
4669 + buffer_rrdf_table_add_field(wb, field_id++, "User", "User Owner", RRDF_FIELD_TYPE_STRING,
4670 + RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, 0, NULL, NAN,
4671 + RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
4672 + RRDF_FIELD_FILTER_MULTISELECT,
4673 + RRDF_FIELD_OPTS_VISIBLE, NULL);
4674 + buffer_rrdf_table_add_field(wb, field_id++, "Uid", "User ID", RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE,
4675 + RRDF_FIELD_TRANSFORM_NUMBER, 0, NULL, NAN,
4676 + RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
4677 + RRDF_FIELD_FILTER_MULTISELECT,
4678 + RRDF_FIELD_OPTS_NONE, NULL);
4679 + buffer_rrdf_table_add_field(wb, field_id++, "Group", "Group Owner", RRDF_FIELD_TYPE_STRING,
4680 + RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, 0, NULL, NAN,
4681 + RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
4682 + RRDF_FIELD_FILTER_MULTISELECT,
4683 + RRDF_FIELD_OPTS_NONE, NULL);
4684 + buffer_rrdf_table_add_field(wb, field_id++, "Gid", "Group ID", RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE,
4685 + RRDF_FIELD_TRANSFORM_NUMBER, 0, NULL, NAN,
4686 + RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
4687 + RRDF_FIELD_FILTER_MULTISELECT,
4688 + RRDF_FIELD_OPTS_NONE, NULL);
4689
4690 // CPU utilization
4677 - add_table_field(wb, "CPU", "Total CPU Time (100% = 1 core)", true, "bar-with-integer", "bar", "number", 2, "%", CPU_max, "descending", true, false, false, NULL, "sum", true);
4678 - add_table_field(wb, "UserCPU", "User CPU time (100% = 1 core)", false, "bar-with-integer", "bar", "number", 2, "%", UserCPU_max, "descending", true, false, false, NULL, "sum", true);
4679 - add_table_field(wb, "SysCPU", "System CPU Time (100% = 1 core)", false, "bar-with-integer", "bar", "number", 2, "%", SysCPU_max, "descending", true, false, false, NULL, "sum", true);
4680 - add_table_field(wb, "GuestCPU", "Guest CPU Time (100% = 1 core)", false, "bar-with-integer", "bar", "number", 2, "%", GuestCPU_max, "descending", true, false, false, NULL, "sum", true);
4681 - add_table_field(wb, "CUserCPU", "Children User CPU Time (100% = 1 core)", false, "bar-with-integer", "bar", "number", 2, "%", CUserCPU_max, "descending", true, false, false, NULL, "sum", true);
4682 - add_table_field(wb, "CSysCPU", "Children System CPU Time (100% = 1 core)", false, "bar-with-integer", "bar", "number", 2, "%", CSysCPU_max, "descending", true, false, false, NULL, "sum", true);
4683 - add_table_field(wb, "CGuestCPU", "Children Guest CPU Time (100% = 1 core)", false, "bar-with-integer", "bar", "number", 2, "%", CGuestCPU_max, "descending", true, false, false, NULL, "sum", true);
4691 + buffer_rrdf_table_add_field(wb, field_id++, "CPU", "Total CPU Time (100% = 1 core)",
4692 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4693 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", CPU_max, RRDF_FIELD_SORT_DESCENDING, NULL,
4694 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4695 + RRDF_FIELD_OPTS_VISIBLE, NULL);
4696 + buffer_rrdf_table_add_field(wb, field_id++, "UserCPU", "User CPU time (100% = 1 core)",
4697 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4698 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", UserCPU_max,
4699 + RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4700 + RRDF_FIELD_OPTS_NONE, NULL);
4701 + buffer_rrdf_table_add_field(wb, field_id++, "SysCPU", "System CPU Time (100% = 1 core)",
4702 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4703 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", SysCPU_max,
4704 + RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4705 + RRDF_FIELD_OPTS_NONE, NULL);
4706 + buffer_rrdf_table_add_field(wb, field_id++, "GuestCPU", "Guest CPU Time (100% = 1 core)",
4707 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4708 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", GuestCPU_max,
4709 + RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4710 + RRDF_FIELD_OPTS_NONE, NULL);
4711 + buffer_rrdf_table_add_field(wb, field_id++, "CUserCPU", "Children User CPU Time (100% = 1 core)",
4712 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4713 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", CUserCPU_max, RRDF_FIELD_SORT_DESCENDING, NULL,
4714 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4715 + RRDF_FIELD_OPTS_NONE, NULL);
4716 + buffer_rrdf_table_add_field(wb, field_id++, "CSysCPU", "Children System CPU Time (100% = 1 core)",
4717 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4718 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", CSysCPU_max, RRDF_FIELD_SORT_DESCENDING, NULL,
4719 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4720 + RRDF_FIELD_OPTS_NONE, NULL);
4721 + buffer_rrdf_table_add_field(wb, field_id++, "CGuestCPU", "Children Guest CPU Time (100% = 1 core)",
4722 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4723 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", CGuestCPU_max, RRDF_FIELD_SORT_DESCENDING,
4724 + NULL,
4725 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, RRDF_FIELD_OPTS_NONE, NULL);
4726
4727 // CPU context switches
4686 - add_table_field(wb, "vCtxSwitch", "Voluntary Context Switches", false, "bar-with-integer", "bar", "number", 2, "switches/s", VoluntaryCtxtSwitches_max, "descending", true, false, false, NULL, "sum", true);
4687 - add_table_field(wb, "iCtxSwitch", "Involuntary Context Switches", false, "bar-with-integer", "bar", "number", 2, "switches/s", NonVoluntaryCtxtSwitches_max, "descending", true, false, false, NULL, "sum", true);
4728 + buffer_rrdf_table_add_field(wb, field_id++, "vCtxSwitch", "Voluntary Context Switches",
4729 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4730 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2, "switches/s",
4731 + VoluntaryCtxtSwitches_max, RRDF_FIELD_SORT_DESCENDING, NULL,
4732 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, RRDF_FIELD_OPTS_NONE, NULL);
4733 + buffer_rrdf_table_add_field(wb, field_id++, "iCtxSwitch", "Involuntary Context Switches",
4734 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4735 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2, "switches/s",
4736 + NonVoluntaryCtxtSwitches_max, RRDF_FIELD_SORT_DESCENDING, NULL,
4737 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, RRDF_FIELD_OPTS_NONE, NULL);
4738
4739 // memory
4690 - if(MemTotal)
4691 - add_table_field(wb, "Memory", "Memory Percentage", true, "bar-with-integer", "bar", "number", 2, "%", 100.0, "descending", true, false, false, NULL, "sum", true);
4692 -
4693 - add_table_field(wb, "Resident", "Resident Set Size", true, "bar-with-integer", "bar", "number", 2, "MiB", RSS_max, "descending", true, false, false, NULL, "sum", true);
4694 - add_table_field(wb, "Shared", "Shared Pages", true, "bar-with-integer", "bar", "number", 2, "MiB", Shared_max, "descending", true, false, false, NULL, "sum", true);
4695 - add_table_field(wb, "Virtual", "Virtual Memory Size", true, "bar-with-integer", "bar", "number", 2, "MiB", VMSize_max, "descending", true, false, false, NULL, "sum", true);
4696 - add_table_field(wb, "Swap", "Swap Memory", false, "bar-with-integer", "bar", "number", 2, "MiB", Swap_max, "descending", true, false, false, NULL, "sum", true);
4740 + if (MemTotal)
4741 + buffer_rrdf_table_add_field(wb, field_id++, "Memory", "Memory Percentage", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4742 + RRDF_FIELD_VISUAL_BAR,
4743 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", 100.0, RRDF_FIELD_SORT_DESCENDING, NULL,
4744 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4745 + RRDF_FIELD_OPTS_VISIBLE, NULL);
4746 +
4747 + buffer_rrdf_table_add_field(wb, field_id++, "Resident", "Resident Set Size", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4748 + RRDF_FIELD_VISUAL_BAR,
4749 + RRDF_FIELD_TRANSFORM_NUMBER,
4750 + 2, "MiB", RSS_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4751 + RRDF_FIELD_FILTER_RANGE,
4752 + RRDF_FIELD_OPTS_VISIBLE, NULL);
4753 + buffer_rrdf_table_add_field(wb, field_id++, "Shared", "Shared Pages", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4754 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2,
4755 + "MiB", Shared_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4756 + RRDF_FIELD_FILTER_RANGE,
4757 + RRDF_FIELD_OPTS_VISIBLE, NULL);
4758 + buffer_rrdf_table_add_field(wb, field_id++, "Virtual", "Virtual Memory Size", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4759 + RRDF_FIELD_VISUAL_BAR,
4760 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "MiB", VMSize_max, RRDF_FIELD_SORT_DESCENDING, NULL,
4761 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4762 + RRDF_FIELD_OPTS_VISIBLE, NULL);
4763 + buffer_rrdf_table_add_field(wb, field_id++, "Swap", "Swap Memory", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4764 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2,
4765 + "MiB",
4766 + Swap_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4767 + RRDF_FIELD_FILTER_RANGE,
4768 + RRDF_FIELD_OPTS_NONE, NULL);
4769
4770 // Physical I/O
4699 - add_table_field(wb, "PReads", "Physical I/O Reads", true, "bar-with-integer", "bar", "number", 2, "KiB/s", PReads_max, "descending", true, false, false, NULL, "sum", true);
4700 - add_table_field(wb, "PWrites", "Physical I/O Writes", true, "bar-with-integer", "bar", "number", 2, "KiB/s", PWrites_max, "descending", true, false, false, NULL, "sum", true);
4771 + buffer_rrdf_table_add_field(wb, field_id++, "PReads", "Physical I/O Reads", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4772 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
4773 + 2, "KiB/s", PReads_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4774 + RRDF_FIELD_FILTER_RANGE,
4775 + RRDF_FIELD_OPTS_VISIBLE, NULL);
4776 + buffer_rrdf_table_add_field(wb, field_id++, "PWrites", "Physical I/O Writes", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4777 + RRDF_FIELD_VISUAL_BAR,
4778 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "KiB/s", PWrites_max, RRDF_FIELD_SORT_DESCENDING,
4779 + NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4780 + RRDF_FIELD_OPTS_VISIBLE, NULL);
4781
4782 // Logical I/O
4783 #ifndef __FreeBSD__
4704 - add_table_field(wb, "LReads", "Logical I/O Reads", true, "bar-with-integer", "bar", "number", 2, "KiB/s", LReads_max, "descending", true, false, false, NULL, "sum", true);
4705 - add_table_field(wb, "LWrites", "Logical I/O Writes", true, "bar-with-integer", "bar", "number", 2, "KiB/s", LWrites_max, "descending", true, false, false, NULL, "sum", true);
4784 + buffer_rrdf_table_add_field(wb, field_id++, "LReads", "Logical I/O Reads", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4785 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
4786 + 2, "KiB/s", LReads_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4787 + RRDF_FIELD_FILTER_RANGE,
4788 + RRDF_FIELD_OPTS_VISIBLE, NULL);
4789 + buffer_rrdf_table_add_field(wb, field_id++, "LWrites", "Logical I/O Writes", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4790 + RRDF_FIELD_VISUAL_BAR,
4791 + RRDF_FIELD_TRANSFORM_NUMBER,
4792 + 2, "KiB/s", LWrites_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4793 + RRDF_FIELD_FILTER_RANGE,
4794 + RRDF_FIELD_OPTS_VISIBLE, NULL);
4795 #endif
4796
4797 // I/O calls
4709 - add_table_field(wb, "RCalls", "I/O Read Calls", true, "bar-with-integer", "bar", "number", 2, "calls/s", RCalls_max, "descending", true, false, false, NULL, "sum", true);
4710 - add_table_field(wb, "WCalls", "I/O Write Calls", true, "bar-with-integer", "bar", "number", 2, "calls/s", WCalls_max, "descending", true, false, false, NULL, "sum", true);
4798 + buffer_rrdf_table_add_field(wb, field_id++, "RCalls", "I/O Read Calls", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4799 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2,
4800 + "calls/s", RCalls_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4801 + RRDF_FIELD_FILTER_RANGE,
4802 + RRDF_FIELD_OPTS_VISIBLE, NULL);
4803 + buffer_rrdf_table_add_field(wb, field_id++, "WCalls", "I/O Write Calls", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4804 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2,
4805 + "calls/s", WCalls_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4806 + RRDF_FIELD_FILTER_RANGE,
4807 + RRDF_FIELD_OPTS_VISIBLE, NULL);
4808
4809 // minor page faults
4713 - add_table_field(wb, "MinFlt", "Minor Page Faults/s", false, "bar-with-integer", "bar", "number", 2, "pgflts/s", MinFlt_max, "descending", true, false, false, NULL, "sum", true);
4714 - add_table_field(wb, "CMinFlt", "Children Minor Page Faults/s", false, "bar-with-integer", "bar", "number", 2, "pgflts/s", CMinFlt_max, "descending", true, false, false, NULL, "sum", true);
4715 - add_table_field(wb, "TMinFlt", "Total Minor Page Faults/s", false, "bar-with-integer", "bar", "number", 2, "pgflts/s", TMinFlt_max, "descending", true, false, false, NULL, "sum", true);
4810 + buffer_rrdf_table_add_field(wb, field_id++, "MinFlt", "Minor Page Faults/s", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4811 + RRDF_FIELD_VISUAL_BAR,
4812 + RRDF_FIELD_TRANSFORM_NUMBER,
4813 + 2, "pgflts/s", MinFlt_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4814 + RRDF_FIELD_FILTER_RANGE,
4815 + RRDF_FIELD_OPTS_NONE, NULL);
4816 + buffer_rrdf_table_add_field(wb, field_id++, "CMinFlt", "Children Minor Page Faults/s",
4817 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4818 + RRDF_FIELD_VISUAL_BAR,
4819 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "pgflts/s", CMinFlt_max, RRDF_FIELD_SORT_DESCENDING,
4820 + NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4821 + RRDF_FIELD_OPTS_NONE, NULL);
4822 + buffer_rrdf_table_add_field(wb, field_id++, "TMinFlt", "Total Minor Page Faults/s",
4823 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4824 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "pgflts/s", TMinFlt_max, RRDF_FIELD_SORT_DESCENDING,
4825 + NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4826 + RRDF_FIELD_OPTS_NONE, NULL);
4827
4828 // major page faults
4718 - add_table_field(wb, "MajFlt", "Major Page Faults/s", false, "bar-with-integer", "bar", "number", 2, "pgflts/s", MajFlt_max, "descending", true, false, false, NULL, "sum", true);
4719 - add_table_field(wb, "CMajFlt", "Children Major Page Faults/s", false, "bar-with-integer", "bar", "number", 2, "pgflts/s", CMajFlt_max, "descending", true, false, false, NULL, "sum", true);
4720 - add_table_field(wb, "TMajFlt", "Total Major Page Faults/s", true, "bar-with-integer", "bar", "number", 2, "pgflts/s", TMajFlt_max, "descending", true, false, false, NULL, "sum", true);
4829 + buffer_rrdf_table_add_field(wb, field_id++, "MajFlt", "Major Page Faults/s", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4830 + RRDF_FIELD_VISUAL_BAR,
4831 + RRDF_FIELD_TRANSFORM_NUMBER,
4832 + 2, "pgflts/s", MajFlt_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4833 + RRDF_FIELD_FILTER_RANGE,
4834 + RRDF_FIELD_OPTS_NONE, NULL);
4835 + buffer_rrdf_table_add_field(wb, field_id++, "CMajFlt", "Children Major Page Faults/s",
4836 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4837 + RRDF_FIELD_VISUAL_BAR,
4838 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "pgflts/s", CMajFlt_max, RRDF_FIELD_SORT_DESCENDING,
4839 + NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4840 + RRDF_FIELD_OPTS_NONE, NULL);
4841 + buffer_rrdf_table_add_field(wb, field_id++, "TMajFlt", "Total Major Page Faults/s",
4842 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4843 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "pgflts/s", TMajFlt_max, RRDF_FIELD_SORT_DESCENDING,
4844 + NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4845 + RRDF_FIELD_OPTS_VISIBLE, NULL);
4846
4847 // open file descriptors
4723 - add_table_field(wb, "FDs", "All Open File Descriptors", true, "bar-with-integer", "bar", "number", 0, "fds", FDs_max, "descending", true, false, false, NULL, "sum", true);
4724 - add_table_field(wb, "Files", "Open Files", true, "bar-with-integer", "bar", "number", 0, "fds", Files_max, "descending", true, false, false, NULL, "sum", true);
4725 - add_table_field(wb, "Pipes", "Open Pipes", true, "bar-with-integer", "bar", "number", 0, "fds", Pipes_max, "descending", true, false, false, NULL, "sum", true);
4726 - add_table_field(wb, "Sockets", "Open Sockets", true, "bar-with-integer", "bar", "number", 0, "fds", Sockets_max, "descending", true, false, false, NULL, "sum", true);
4727 - add_table_field(wb, "iNotiFDs", "Open iNotify Descriptors", false, "bar-with-integer", "bar", "number", 0, "fds", iNotiFDs_max, "descending", true, false, false, NULL, "sum", true);
4728 - add_table_field(wb, "EventFDs", "Open Event Descriptors", false, "bar-with-integer", "bar", "number", 0, "fds", EventFDs_max, "descending", true, false, false, NULL, "sum", true);
4729 - add_table_field(wb, "TimerFDs", "Open Timer Descriptors", false, "bar-with-integer", "bar", "number", 0, "fds", TimerFDs_max, "descending", true, false, false, NULL, "sum", true);
4730 - add_table_field(wb, "SigFDs", "Open Signal Descriptors", false, "bar-with-integer", "bar", "number", 0, "fds", SigFDs_max, "descending", true, false, false, NULL, "sum", true);
4731 - add_table_field(wb, "EvPollFDs", "Open Event Poll Descriptors", false, "bar-with-integer", "bar", "number", 0, "fds", EvPollFDs_max, "descending", true, false, false, NULL, "sum", true);
4732 - add_table_field(wb, "OtherFDs", "Other Open Descriptors", false, "bar-with-integer", "bar", "number", 0, "fds", OtherFDs_max, "descending", true, false, false, NULL, "sum", true);
4848 + buffer_rrdf_table_add_field(wb, field_id++, "FDs", "All Open File Descriptors",
4849 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4850 + RRDF_FIELD_TRANSFORM_NUMBER, 0, "fds", FDs_max, RRDF_FIELD_SORT_DESCENDING, NULL,
4851 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4852 + RRDF_FIELD_OPTS_VISIBLE, NULL);
4853 + buffer_rrdf_table_add_field(wb, field_id++, "Files", "Open Files", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4854 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 0,
4855 + "fds",
4856 + Files_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4857 + RRDF_FIELD_FILTER_RANGE,
4858 + RRDF_FIELD_OPTS_VISIBLE, NULL);
4859 + buffer_rrdf_table_add_field(wb, field_id++, "Pipes", "Open Pipes", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4860 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 0,
4861 + "fds",
4862 + Pipes_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4863 + RRDF_FIELD_FILTER_RANGE,
4864 + RRDF_FIELD_OPTS_VISIBLE, NULL);
4865 + buffer_rrdf_table_add_field(wb, field_id++, "Sockets", "Open Sockets", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4866 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 0,
4867 + "fds", Sockets_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4868 + RRDF_FIELD_FILTER_RANGE,
4869 + RRDF_FIELD_OPTS_VISIBLE, NULL);
4870 + buffer_rrdf_table_add_field(wb, field_id++, "iNotiFDs", "Open iNotify Descriptors",
4871 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4872 + RRDF_FIELD_TRANSFORM_NUMBER, 0, "fds", iNotiFDs_max, RRDF_FIELD_SORT_DESCENDING,
4873 + NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4874 + RRDF_FIELD_OPTS_NONE, NULL);
4875 + buffer_rrdf_table_add_field(wb, field_id++, "EventFDs", "Open Event Descriptors",
4876 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4877 + RRDF_FIELD_TRANSFORM_NUMBER, 0, "fds", EventFDs_max, RRDF_FIELD_SORT_DESCENDING,
4878 + NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4879 + RRDF_FIELD_OPTS_NONE, NULL);
4880 + buffer_rrdf_table_add_field(wb, field_id++, "TimerFDs", "Open Timer Descriptors",
4881 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4882 + RRDF_FIELD_TRANSFORM_NUMBER, 0, "fds", TimerFDs_max, RRDF_FIELD_SORT_DESCENDING,
4883 + NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4884 + RRDF_FIELD_OPTS_NONE, NULL);
4885 + buffer_rrdf_table_add_field(wb, field_id++, "SigFDs", "Open Signal Descriptors",
4886 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4887 + RRDF_FIELD_TRANSFORM_NUMBER, 0, "fds", SigFDs_max, RRDF_FIELD_SORT_DESCENDING, NULL,
4888 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4889 + RRDF_FIELD_OPTS_NONE, NULL);
4890 + buffer_rrdf_table_add_field(wb, field_id++, "EvPollFDs", "Open Event Poll Descriptors",
4891 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4892 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 0, "fds", EvPollFDs_max,
4893 + RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4894 + RRDF_FIELD_OPTS_NONE, NULL);
4895 + buffer_rrdf_table_add_field(wb, field_id++, "OtherFDs", "Other Open Descriptors",
4896 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4897 + RRDF_FIELD_TRANSFORM_NUMBER, 0, "fds", OtherFDs_max, RRDF_FIELD_SORT_DESCENDING,
4898 + NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4899 + RRDF_FIELD_OPTS_NONE, NULL);
4900
4901 // processes, threads, uptime
4735 - add_table_field(wb, "Processes", "Processes", true, "bar-with-integer", "bar", "number", 0, "processes", Processes_max, "descending", true, false, false, NULL, "sum", true);
4736 - add_table_field(wb, "Threads", "Threads", true, "bar-with-integer", "bar", "number", 0, "threads", Threads_max, "descending", true, false, false, NULL, "sum", true);
4737 - add_table_field(wb, "Uptime", "Uptime in seconds", true, "duration", "bar", "duration", 2, "seconds", Uptime_max, "descending", true, false, false, NULL, "max", true);
4738 - }
4739 - buffer_json_object_close(wb);
4902 + buffer_rrdf_table_add_field(wb, field_id++, "Processes", "Processes", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4903 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 0,
4904 + "processes", Processes_max, RRDF_FIELD_SORT_DESCENDING, NULL,
4905 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4906 + RRDF_FIELD_OPTS_VISIBLE, NULL);
4907 + buffer_rrdf_table_add_field(wb, field_id++, "Threads", "Threads", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4908 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 0,
4909 + "threads", Threads_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4910 + RRDF_FIELD_FILTER_RANGE,
4911 + RRDF_FIELD_OPTS_VISIBLE, NULL);
4912 + buffer_rrdf_table_add_field(wb, field_id++, "Uptime", "Uptime in seconds", RRDF_FIELD_TYPE_DURATION,
4913 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_DURATION, 2,
4914 + "seconds", Uptime_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_MAX,
4915 + RRDF_FIELD_FILTER_RANGE,
4916 + RRDF_FIELD_OPTS_VISIBLE, NULL);
4917 + }
4918 + buffer_json_object_close(wb); // columns
4919
4920 buffer_json_member_add_string(wb, "default_sort_column", "CPU");
4921
@@ -4953,6 +5132,20 @@ static void apps_plugin_function_processes(const char *transaction, char *functi
5132 }
5133 buffer_json_object_close(wb); // charts
5134
5135 + buffer_json_member_add_array(wb, "default_charts");
5136 + {
5137 + buffer_json_add_array_item_array(wb);
5138 + buffer_json_add_array_item_string(wb, "CPU");
5139 + buffer_json_add_array_item_string(wb, "Category");
5140 + buffer_json_array_close(wb);
5141 +
5142 + buffer_json_add_array_item_array(wb);
5143 + buffer_json_add_array_item_string(wb, "Memory");
5144 + buffer_json_add_array_item_string(wb, "Category");
5145 + buffer_json_array_close(wb);
5146 + }
5147 + buffer_json_array_close(wb);
5148 +
5149 buffer_json_member_add_object(wb, "group_by");
5150 {
5151 // group by PID
collectors/cups.plugin/cups_plugin.c
+35 -33
@@ -17,7 +17,7 @@
17 static int debug = 0;
18
19 static int netdata_update_every = 1;
20 -static int netdata_priority = 100004;
20 +static uint32_t netdata_priority = 100004;
21
22 http_t *http; // connection to the cups daemon
23
@@ -25,7 +25,9 @@ http_t *http; // connection to the cups daemon
25 * Used to aggregate job metrics for a destination (and all destinations).
26 */
27 struct job_metrics {
28 - int is_collected; // flag if this was collected in the current cycle
28 + uint32_t id;
29 +
30 + bool is_collected; // flag if this was collected in the current cycle
31
32 int num_pending;
33 int num_processing;
@@ -140,7 +142,7 @@ getIntegerOption(
142 static int reset_job_metrics(const DICTIONARY_ITEM *item __maybe_unused, void *entry, void *data __maybe_unused) {
143 struct job_metrics *jm = (struct job_metrics *)entry;
144
143 - jm->is_collected = 0;
145 + jm->is_collected = false;
146 jm->num_held = 0;
147 jm->num_pending = 0;
148 jm->num_processing = 0;
@@ -151,28 +153,34 @@ static int reset_job_metrics(const DICTIONARY_ITEM *item __maybe_unused, void *e
153 return 0;
154 }
155
156 +void send_job_charts_definitions_to_netdata(const char *name, uint32_t job_id, bool obsolete) {
157 + printf("CHART cups.job_num_%s '' 'Active jobs of %s' jobs '%s' cups.destination_job_num stacked %u %i %s\n",
158 + name, name, name, netdata_priority + job_id, netdata_update_every, obsolete?"obsolete":"");
159 + printf("DIMENSION pending '' absolute 1 1\n");
160 + printf("DIMENSION held '' absolute 1 1\n");
161 + printf("DIMENSION processing '' absolute 1 1\n");
162 +
163 + printf("CHART cups.job_size_%s '' 'Active jobs size of %s' KB '%s' cups.destination_job_size stacked %u %i %s\n",
164 + name, name, name, netdata_priority + 1 + job_id, netdata_update_every, obsolete?"obsolete":"");
165 + printf("DIMENSION pending '' absolute 1 1\n");
166 + printf("DIMENSION held '' absolute 1 1\n");
167 + printf("DIMENSION processing '' absolute 1 1\n");
168 +}
169 +
170 struct job_metrics *get_job_metrics(char *dest) {
171 struct job_metrics *jm = dictionary_get(dict_dest_job_metrics, dest);
172
173 if (unlikely(!jm)) {
158 - struct job_metrics new_job_metrics;
159 - reset_job_metrics(NULL, &new_job_metrics, NULL);
174 + static uint32_t job_id = 0;
175 + struct job_metrics new_job_metrics = { .id = ++job_id };
176 jm = dictionary_set(dict_dest_job_metrics, dest, &new_job_metrics, sizeof(struct job_metrics));
161 -
162 - printf("CHART cups.job_num_%s '' 'Active jobs of %s' jobs '%s' cups.destination_job_num stacked %i %i\n", dest, dest, dest, netdata_priority++, netdata_update_every);
163 - printf("DIMENSION pending '' absolute 1 1\n");
164 - printf("DIMENSION held '' absolute 1 1\n");
165 - printf("DIMENSION processing '' absolute 1 1\n");
166 -
167 - printf("CHART cups.job_size_%s '' 'Active jobs size of %s' KB '%s' cups.destination_job_size stacked %i %i\n", dest, dest, dest, netdata_priority++, netdata_update_every);
168 - printf("DIMENSION pending '' absolute 1 1\n");
169 - printf("DIMENSION held '' absolute 1 1\n");
170 - printf("DIMENSION processing '' absolute 1 1\n");
177 + send_job_charts_definitions_to_netdata(dest, jm->id, false);
178 };
179 +
180 return jm;
181 }
182
175 -int collect_job_metrics(const DICTIONARY_ITEM *item, void *entry, void *data __maybe_unused) {
183 +int send_job_metrics_to_netdata(const DICTIONARY_ITEM *item, void *entry, void *data __maybe_unused) {
184 const char *name = dictionary_acquired_item_name(item);
185
186 struct job_metrics *jm = (struct job_metrics *)entry;
@@ -192,16 +200,12 @@ int collect_job_metrics(const DICTIONARY_ITEM *item, void *entry, void *data __m
200 "SET processing = %d\n"
201 "END\n",
202 name, jm->size_pending, jm->size_held, jm->size_processing);
195 - } else {
196 - printf("CHART cups.job_num_%s '' 'Active jobs of %s' jobs '%s' cups.destination_job_num stacked 1 %i 'obsolete'\n", name, name, name, netdata_update_every);
197 - printf("DIMENSION pending '' absolute 1 1\n");
198 - printf("DIMENSION held '' absolute 1 1\n");
199 - printf("DIMENSION processing '' absolute 1 1\n");
200 -
201 - printf("CHART cups.job_size_%s '' 'Active jobs size of %s' KB '%s' cups.destination_job_size stacked 1 %i 'obsolete'\n", name, name, name, netdata_update_every);
202 - printf("DIMENSION pending '' absolute 1 1\n");
203 - printf("DIMENSION held '' absolute 1 1\n");
204 - printf("DIMENSION processing '' absolute 1 1\n");
203 + }
204 + else {
205 + // mark it obsolete
206 + send_job_charts_definitions_to_netdata(name, jm->id, true);
207 +
208 + // delete it
209 dictionary_del(dict_dest_job_metrics, name);
210 }
211
@@ -255,14 +259,11 @@ int main(int argc, char **argv) {
259
260 heartbeat_t hb;
261 heartbeat_init(&hb);
258 - for (iteration = 0; 1; iteration++)
259 - {
262 + for (iteration = 0; 1; iteration++) {
263 heartbeat_next(&hb, step);
264
265 if (unlikely(netdata_exit))
263 - {
266 break;
265 - }
267
268 reset_metrics();
269
@@ -329,7 +330,7 @@ int main(int argc, char **argv) {
330 * This is needed to report also destinations with zero active jobs.
331 */
332 struct job_metrics *jm = get_job_metrics(curr_dest->name);
332 - jm->is_collected = 1;
333 + jm->is_collected = true;
334 }
335 cupsFreeDests(num_dest_total, dests);
336
@@ -341,7 +342,7 @@ int main(int argc, char **argv) {
342 int i;
343 for (i = num_jobs, curr_job = jobs; i > 0; i--, curr_job++) {
344 struct job_metrics *jm = get_job_metrics(curr_job->dest);
344 - jm->is_collected = 1;
345 + jm->is_collected = true;
346
347 switch (curr_job->state) {
348 case IPP_JOB_PENDING:
@@ -369,7 +370,8 @@ int main(int argc, char **argv) {
370 }
371 cupsFreeJobs(num_jobs, jobs);
372
372 - dictionary_walkthrough_write(dict_dest_job_metrics, collect_job_metrics, NULL);
373 + dictionary_walkthrough_write(dict_dest_job_metrics, send_job_metrics_to_netdata, NULL);
374 + dictionary_garbage_collect(dict_dest_job_metrics);
375
376 static int cups_printer_by_option_created = 0;
377 if (unlikely(!cups_printer_by_option_created))
collectors/plugins.d/pluginsd_parser.c
+26 -9
@@ -4,51 +4,66 @@
4
5 #define LOG_FUNCTIONS false
6
7 -static int send_to_plugin(const char *txt, void *data) {
7 +static ssize_t send_to_plugin(const char *txt, void *data) {
8 PARSER *parser = data;
9
10 if(!txt || !*txt)
11 return 0;
12
13 + errno = 0;
14 + netdata_spinlock_lock(&parser->writer.spinlock);
15 + ssize_t bytes = -1;
16 +
17 #ifdef ENABLE_HTTPS
18 NETDATA_SSL *ssl = parser->ssl_output;
19 if(ssl) {
20 +
21 if(SSL_connection(ssl))
17 - return (int)netdata_ssl_write(ssl, (void *)txt, strlen(txt));
22 + bytes = netdata_ssl_write(ssl, (void *) txt, strlen(txt));
23
19 - error("PLUGINSD: cannot send command (SSL)");
20 - return -1;
24 + else
25 + error("PLUGINSD: cannot send command (SSL)");
26 +
27 + netdata_spinlock_unlock(&parser->writer.spinlock);
28 + return bytes;
29 }
30 #endif
31
32 if(parser->fp_output) {
25 - int bytes = fprintf(parser->fp_output, "%s", txt);
33 +
34 + bytes = fprintf(parser->fp_output, "%s", txt);
35 if(bytes <= 0) {
36 error("PLUGINSD: cannot send command (FILE)");
28 - return -2;
37 + bytes = -2;
38 }
30 - fflush(parser->fp_output);
39 + else
40 + fflush(parser->fp_output);
41 +
42 + netdata_spinlock_unlock(&parser->writer.spinlock);
43 return bytes;
44 }
45
46 if(parser->fd != -1) {
35 - size_t bytes = 0;
36 - size_t total = strlen(txt);
47 + bytes = 0;
48 + ssize_t total = (ssize_t)strlen(txt);
49 ssize_t sent;
50
51 do {
52 sent = write(parser->fd, &txt[bytes], total - bytes);
53 if(sent <= 0) {
54 error("PLUGINSD: cannot send command (fd)");
55 + netdata_spinlock_unlock(&parser->writer.spinlock);
56 return -3;
57 }
58 bytes += sent;
59 }
60 while(bytes < total);
61
62 + netdata_spinlock_unlock(&parser->writer.spinlock);
63 return (int)bytes;
64 }
65
66 + netdata_spinlock_unlock(&parser->writer.spinlock);
67 error("PLUGINSD: cannot send command (no output socket/pipe/file given to plugins.d parser)");
68 return -4;
69 }
@@ -402,6 +417,8 @@ static PARSER_RC pluginsd_host_define_end(char **words __maybe_unused, size_t nu
417 false
418 );
419
420 + rrdhost_option_set(host, RRDHOST_OPTION_VIRTUAL_HOST);
421 +
422 if(host->rrdlabels) {
423 rrdlabels_migrate_to_these(host->rrdlabels, u->host_define.rrdlabels);
424 }
daemon/commands.c
+1 -1
@@ -213,7 +213,7 @@ static cmd_status_t cmd_reload_claiming_state_execute(char *args, char **message
213 info("COMMAND: Reloading Agent Claiming configuration.");
214 load_claiming_state();
215 registry_update_cloud_base_url();
216 - rrdpush_claimed_id(localhost);
216 + rrdpush_send_claimed_id(localhost);
217 error_log_limit_reset();
218 return CMD_STATUS_SUCCESS;
219 }
daemon/common.c
+90 -2
@@ -16,8 +16,14 @@ char *netdata_configured_host_prefix = NULL;
16 char *netdata_configured_timezone = NULL;
17 char *netdata_configured_abbrev_timezone = NULL;
18 int32_t netdata_configured_utc_offset = 0;
19 -int netdata_ready;
20 -int netdata_cloud_setting;
19 +
20 +bool netdata_ready = false;
21 +
22 +#if defined( DISABLE_CLOUD ) || !defined( ENABLE_ACLK )
23 +bool netdata_cloud_enabled = false;
24 +#else
25 +bool netdata_cloud_enabled = true;
26 +#endif
27
28 long get_netdata_cpus(void) {
29 static long processors = 0;
@@ -54,3 +60,85 @@ long get_netdata_cpus(void) {
60
61 return processors;
62 }
63 +
64 +const char *cloud_status_to_string(CLOUD_STATUS status) {
65 + switch(status) {
66 + default:
67 + case CLOUD_STATUS_DISABLED:
68 + return "disabled";
69 +
70 + case CLOUD_STATUS_BANNED:
71 + return "banned";
72 +
73 + case CLOUD_STATUS_OFFLINE:
74 + return "offline";
75 +
76 + case CLOUD_STATUS_ONLINE:
77 + return "online";
78 + }
79 +}
80 +
81 +CLOUD_STATUS cloud_status(void) {
82 +#ifdef ENABLE_ACLK
83 + if(aclk_disable_runtime)
84 + return CLOUD_STATUS_BANNED;
85 +
86 + if(aclk_connected)
87 + return CLOUD_STATUS_ONLINE;
88 +
89 + if(netdata_cloud_enabled)
90 + return CLOUD_STATUS_OFFLINE;
91 +
92 + return CLOUD_STATUS_DISABLED;
93 +#else
94 + return CLOUD_STATUS_DISABLED;
95 +#endif
96 +}
97 +
98 +time_t cloud_last_change(void) {
99 +#ifdef ENABLE_ACLK
100 + time_t ret = MAX(last_conn_time_mqtt, last_disconnect_time);
101 + if(!ret) ret = netdata_start_time;
102 + return ret;
103 +#else
104 + return netdata_start_time;
105 +#endif
106 +}
107 +
108 +time_t cloud_next_connection_attempt(void) {
109 +#ifdef ENABLE_ACLK
110 + return next_connection_attempt;
111 +#else
112 + return 0;
113 +#endif
114 +}
115 +
116 +size_t cloud_connection_id(void) {
117 +#ifdef ENABLE_ACLK
118 + return aclk_connection_counter;
119 +#else
120 + return 0;
121 +#endif
122 +}
123 +
124 +const char *cloud_offline_reason() {
125 +#ifdef ENABLE_ACLK
126 + if(!netdata_cloud_enabled)
127 + return "disabled";
128 +
129 + if(aclk_disable_runtime)
130 + return "banned";
131 +
132 + return aclk_status_to_string();
133 +#else
134 + return "disabled";
135 +#endif
136 +}
137 +
138 +const char *cloud_base_url() {
139 +#ifdef ENABLE_ACLK
140 + return aclk_cloud_base_url;
141 +#else
142 + return NULL;
143 +#endif
144 +}
daemon/common.h
+19 -2
@@ -109,9 +109,26 @@ extern int32_t netdata_configured_utc_offset;
109 extern int netdata_zero_metrics_enabled;
110 extern int netdata_anonymous_statistics_enabled;
111
112 -extern int netdata_ready;
113 -extern int netdata_cloud_setting;
112 +extern bool netdata_ready;
113 +extern bool netdata_cloud_enabled;
114 +
115 +extern time_t netdata_start_time;
116
117 long get_netdata_cpus(void);
118
119 +typedef enum __attribute__((packed)) {
120 + CLOUD_STATUS_DISABLED = 0,
121 + CLOUD_STATUS_BANNED,
122 + CLOUD_STATUS_OFFLINE,
123 + CLOUD_STATUS_ONLINE,
124 +} CLOUD_STATUS;
125 +
126 +const char *cloud_status_to_string(CLOUD_STATUS status);
127 +CLOUD_STATUS cloud_status(void);
128 +time_t cloud_last_change(void);
129 +time_t cloud_next_connection_attempt(void);
130 +size_t cloud_connection_id(void);
131 +const char *cloud_offline_reason(void);
132 +const char *cloud_base_url(void);
133 +
134 #endif /* NETDATA_COMMON_H */
daemon/event_loop.c
+6 -1
@@ -56,5 +56,10 @@ void register_libuv_worker_jobs() {
56 // netdatacli
57 worker_register_job_name(UV_EVENT_SCHEDULE_CMD, "schedule command");
58
59 - uv_thread_set_name_np(pthread_self(), "LIBUV_WORKER");
59 + static int workers = 0;
60 + int worker_id = __atomic_add_fetch(&workers, 1, __ATOMIC_RELAXED);
61 +
62 + char buf[NETDATA_THREAD_TAG_MAX + 1];
63 + snprintfz(buf, NETDATA_THREAD_TAG_MAX, "UV_WORKER[%d]", worker_id);
64 + uv_thread_set_name_np(pthread_self(), buf);
65 }
daemon/global_statistics.c
+4 -4
@@ -231,10 +231,10 @@ static void global_statistics_charts(void) {
231 static collected_number compression_ratio = -1,
232 average_response_time = -1;
233
234 - static time_t netdata_start_time = 0;
235 - if (!netdata_start_time)
236 - netdata_start_time = now_boottime_sec();
237 - time_t netdata_uptime = now_boottime_sec() - netdata_start_time;
234 + static time_t netdata_boottime_time = 0;
235 + if (!netdata_boottime_time)
236 + netdata_boottime_time = now_boottime_sec();
237 + time_t netdata_uptime = now_boottime_sec() - netdata_boottime_time;
238
239 struct global_statistics gs;
240 struct rusage me;
daemon/main.c
+12 -7
@@ -14,7 +14,7 @@ int netdata_anonymous_statistics_enabled;
14
15 int libuv_worker_threads = MIN_LIBUV_WORKER_THREADS;
16 bool ieee754_doubles = false;
17 -
17 +time_t netdata_start_time = 0;
18 struct netdata_static_thread *static_threads;
19
20 struct config netdata_config = {
@@ -446,8 +446,11 @@ void netdata_cleanup_and_exit(int ret) {
446 for (size_t tier = 0; tier < storage_tiers; tier++)
447 running += rrdeng_collectors_running(multidb_ctx[tier]);
448
449 - if(running)
450 - sleep_usec(100 * USEC_PER_MS);
449 + if(running) {
450 + error_limit_static_thread_var(erl, 1, 100 * USEC_PER_MS);
451 + error_limit(&erl, "waiting for %zu collectors to finish", running);
452 + // sleep_usec(100 * USEC_PER_MS);
453 + }
454 }
455
456 delta_shutdown_time("wait for dbengine main cache to finish flushing");
@@ -1313,9 +1316,9 @@ void post_conf_load(char **user)
1316 // --------------------------------------------------------------------
1317 // Check if the cloud is enabled
1318 #if defined( DISABLE_CLOUD ) || !defined( ENABLE_ACLK )
1316 - netdata_cloud_setting = 0;
1319 + netdata_cloud_enabled = false;
1320 #else
1318 - netdata_cloud_setting = appconfig_get_boolean(&cloud_config, CONFIG_SECTION_GLOBAL, "enabled", 1);
1321 + netdata_cloud_enabled = appconfig_get_boolean(&cloud_config, CONFIG_SECTION_GLOBAL, "enabled", 1);
1322 #endif
1323 // This must be set before any point in the code that accesses it. Do not move it from this function.
1324 appconfig_get(&cloud_config, CONFIG_SECTION_GLOBAL, "cloud base url", DEFAULT_CLOUD_BASE_URL);
@@ -1342,6 +1345,8 @@ void replication_initialize(void);
1345 int main(int argc, char **argv) {
1346 // initialize the system clocks
1347 clocks_init();
1348 + netdata_start_time = now_realtime_sec();
1349 +
1350 usec_t started_ut = now_monotonic_usec();
1351 usec_t last_ut = started_ut;
1352 const char *prev_msg = NULL;
@@ -1357,7 +1362,7 @@ int main(int argc, char **argv) {
1362
1363 static_threads = static_threads_get();
1364
1360 - netdata_ready=0;
1365 + netdata_ready = false;
1366 // set the name for logging
1367 program_name = "netdata";
1368
@@ -2117,7 +2122,7 @@ int main(int argc, char **argv) {
2122
2123 usec_t ready_ut = now_monotonic_usec();
2124 info("NETDATA STARTUP: completed in %llu ms. Enjoy real-time performance monitoring!", (ready_ut - started_ut) / USEC_PER_MS);
2120 - netdata_ready = 1;
2125 + netdata_ready = true;
2126
2127 send_statistics("START", "-", "-");
2128 if (crash_detected)
database/contexts/api_v2.c
+444 -98
@@ -88,6 +88,8 @@ static inline bool full_text_search_char(FTS_INDEX *fts, SIMPLE_PATTERN *q, char
88 }
89
90 struct rrdcontext_to_json_v2_data {
91 + time_t now;
92 +
93 BUFFER *wb;
94 struct api_v2_contexts_request *request;
95 DICTIONARY *ctx;
@@ -113,6 +115,13 @@ struct rrdcontext_to_json_v2_data {
115 FTS_INDEX fts;
116 } q;
117
118 + struct {
119 + bool enabled;
120 + bool relative;
121 + time_t after;
122 + time_t before;
123 + } window;
124 +
125 struct query_timings timings;
126 };
127
@@ -132,6 +141,9 @@ static FTS_MATCH rrdcontext_to_json_v2_full_text_search(struct rrdcontext_to_jso
141 dfe_start_read(rc->rrdinstances, ri) {
142 if(matched) break;
143
144 + if(ctl->window.enabled && !query_matches_retention(ctl->window.after, ctl->window.before, ri->first_time_s, ri->last_time_s, 0))
145 + continue;
146 +
147 if(unlikely(full_text_search_string(&ctl->q.fts, q, ri->id)) ||
148 (ri->name != ri->id && full_text_search_string(&ctl->q.fts, q, ri->name))) {
149 matched = FTS_MATCHED_INSTANCE;
@@ -140,6 +152,9 @@ static FTS_MATCH rrdcontext_to_json_v2_full_text_search(struct rrdcontext_to_jso
152
153 RRDMETRIC *rm;
154 dfe_start_read(ri->rrdmetrics, rm) {
155 + if(ctl->window.enabled && !query_matches_retention(ctl->window.after, ctl->window.before, rm->first_time_s, rm->last_time_s, 0))
156 + continue;
157 +
158 if(unlikely(full_text_search_string(&ctl->q.fts, q, rm->id)) ||
159 (rm->name != rm->id && full_text_search_string(&ctl->q.fts, q, rm->name))) {
160 matched = FTS_MATCHED_DIMENSION;
@@ -185,16 +200,19 @@ static ssize_t rrdcontext_to_json_v2_add_context(void *data, RRDCONTEXT_ACQUIRED
200
201 RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
202
203 + if(ctl->window.enabled && !query_matches_retention(ctl->window.after, ctl->window.before, rc->first_time_s, rc->last_time_s, 0))
204 + return 0; // continue to next context
205 +
206 FTS_MATCH match = ctl->q.host_match;
207 if((ctl->options & CONTEXTS_V2_SEARCH) && ctl->q.pattern) {
208 match = rrdcontext_to_json_v2_full_text_search(ctl, rc, ctl->q.pattern);
209
210 if(match == FTS_MATCHED_NONE)
193 - return 0;
211 + return 0; // continue to next context
212 }
213
214 struct rrdcontext_to_json_v2_entry t = {
197 - .count = 0,
215 + .count = 1,
216 .id = rc->id,
217 .family = string_dup(rc->family),
218 .priority = rc->priority,
@@ -202,65 +220,197 @@ static ssize_t rrdcontext_to_json_v2_add_context(void *data, RRDCONTEXT_ACQUIRED
220 .last_time_s = rc->last_time_s,
221 .flags = rc->flags,
222 .match = match,
205 - }, *z = dictionary_set(ctl->ctx, string2str(rc->id), &t, sizeof(t));
206 -
207 - if(!z->count) {
208 - // we just added this
209 - z->count = 1;
210 - }
211 - else {
212 - // it is already in there
213 - z->count++;
214 - z->flags |= rc->flags;
215 -
216 - if(z->priority > rc->priority)
217 - z->priority = rc->priority;
223 + };
224
219 - if(z->first_time_s > rc->first_time_s)
220 - z->first_time_s = rc->first_time_s;
225 + dictionary_set(ctl->ctx, string2str(rc->id), &t, sizeof(t));
226
222 - if(z->last_time_s < rc->last_time_s)
223 - z->last_time_s = rc->last_time_s;
227 + return 1;
228 +}
229
225 - if(z->family != rc->family) {
226 - z->family = string_2way_merge(z->family, rc->family);
227 - }
230 +void buffer_json_agent_status_id(BUFFER *wb, size_t ai, usec_t duration_ut) {
231 + buffer_json_member_add_object(wb, "st");
232 + {
233 + buffer_json_member_add_uint64(wb, "ai", ai);
234 + buffer_json_member_add_uint64(wb, "code", 200);
235 + buffer_json_member_add_string(wb, "msg", "");
236 + if (duration_ut)
237 + buffer_json_member_add_double(wb, "ms", (NETDATA_DOUBLE) duration_ut / 1000.0);
238 }
229 -
230 - return 1;
239 + buffer_json_object_close(wb);
240 }
241
233 -void buffer_json_node_add_v2(BUFFER *wb, RRDHOST *host, size_t ni, usec_t duration_ut) {
242 +void buffer_json_node_add_v2(BUFFER *wb, RRDHOST *host, size_t ni, usec_t duration_ut, bool status) {
243 buffer_json_member_add_string(wb, "mg", host->machine_guid);
244 +
245 if(host->node_id)
246 buffer_json_member_add_uuid(wb, "nd", host->node_id);
247 buffer_json_member_add_string(wb, "nm", rrdhost_hostname(host));
248 buffer_json_member_add_uint64(wb, "ni", ni);
239 - buffer_json_member_add_object(wb, "st");
240 - buffer_json_member_add_uint64(wb, "ai", 0);
241 - buffer_json_member_add_uint64(wb, "code", 200);
242 - buffer_json_member_add_string(wb, "msg", "");
243 - if(duration_ut)
244 - buffer_json_member_add_double(wb, "ms", (NETDATA_DOUBLE)duration_ut / 1000.0);
245 - buffer_json_object_close(wb);
249 +
250 + if(status)
251 + buffer_json_agent_status_id(wb, 0, duration_ut);
252 +}
253 +
254 +static void rrdhost_receiver_to_json(BUFFER *wb, RRDHOST_STATUS *s, const char *key) {
255 + buffer_json_member_add_object(wb, key);
256 + {
257 + buffer_json_member_add_uint64(wb, "id", s->ingest.id);
258 + buffer_json_member_add_uint64(wb, "hops", s->ingest.hops);
259 + buffer_json_member_add_string(wb, "type", rrdhost_ingest_type_to_string(s->ingest.type));
260 + buffer_json_member_add_string(wb, "status", rrdhost_ingest_status_to_string(s->ingest.status));
261 + buffer_json_member_add_time_t(wb, "since", s->ingest.since);
262 + buffer_json_member_add_time_t(wb, "age", s->now - s->ingest.since);
263 +
264 + if(s->ingest.type == RRDHOST_INGEST_TYPE_CHILD) {
265 + if(s->ingest.status == RRDHOST_INGEST_STATUS_OFFLINE)
266 + buffer_json_member_add_string(wb, "reason", s->ingest.reason);
267 +
268 + if(s->ingest.status == RRDHOST_INGEST_STATUS_REPLICATING) {
269 + buffer_json_member_add_object(wb, "replication");
270 + {
271 + buffer_json_member_add_boolean(wb, "in_progress", s->ingest.replication.in_progress);
272 + buffer_json_member_add_double(wb, "completion", s->ingest.replication.completion);
273 + buffer_json_member_add_uint64(wb, "instances", s->ingest.replication.instances);
274 + }
275 + buffer_json_object_close(wb); // replication
276 + }
277 +
278 + if(s->ingest.status == RRDHOST_INGEST_STATUS_REPLICATING || s->ingest.status == RRDHOST_INGEST_STATUS_ONLINE) {
279 + buffer_json_member_add_object(wb, "source");
280 + {
281 + char buf[1024 + 1];
282 + snprintfz(buf, 1024, "[%s]:%d%s", s->ingest.peers.local.ip, s->ingest.peers.local.port, s->ingest.ssl ? ":SSL" : "");
283 + buffer_json_member_add_string(wb, "local", buf);
284 +
285 + snprintfz(buf, 1024, "[%s]:%d%s", s->ingest.peers.peer.ip, s->ingest.peers.peer.port, s->ingest.ssl ? ":SSL" : "");
286 + buffer_json_member_add_string(wb, "remote", buf);
287 +
288 + stream_capabilities_to_json_array(wb, s->ingest.capabilities, "capabilities");
289 + }
290 + buffer_json_object_close(wb); // source
291 + }
292 + }
293 + }
294 + buffer_json_object_close(wb); // collection
295 +}
296 +
297 +static void rrdhost_sender_to_json(BUFFER *wb, RRDHOST_STATUS *s, const char *key) {
298 + if(s->stream.status == RRDHOST_STREAM_STATUS_DISABLED)
299 + return;
300 +
301 + buffer_json_member_add_object(wb, key);
302 +
303 + buffer_json_member_add_uint64(wb, "id", s->stream.id);
304 + buffer_json_member_add_uint64(wb, "hops", s->stream.hops);
305 + buffer_json_member_add_string(wb, "status", rrdhost_streaming_status_to_string(s->stream.status));
306 + buffer_json_member_add_time_t(wb, "since", s->stream.since);
307 + buffer_json_member_add_time_t(wb, "age", s->now - s->stream.since);
308 +
309 + if(s->stream.status == RRDHOST_STREAM_STATUS_OFFLINE)
310 + buffer_json_member_add_string(wb, "reason", s->stream.reason);
311 +
312 + if(s->stream.status == RRDHOST_STREAM_STATUS_REPLICATING || s->stream.status == RRDHOST_STREAM_STATUS_ONLINE) {
313 +
314 + if(s->stream.status == RRDHOST_STREAM_STATUS_REPLICATING) {
315 + buffer_json_member_add_object(wb, "replication");
316 + {
317 + buffer_json_member_add_boolean(wb, "in_progress", s->stream.replication.in_progress);
318 + buffer_json_member_add_double(wb, "completion", s->stream.replication.completion);
319 + buffer_json_member_add_uint64(wb, "instances", s->stream.replication.instances);
320 + }
321 + buffer_json_object_close(wb);
322 + }
323 +
324 + buffer_json_member_add_object(wb, "destination");
325 + {
326 + char buf[1024 + 1];
327 + snprintfz(buf, 1024, "[%s]:%d%s", s->stream.peers.local.ip, s->stream.peers.local.port, s->stream.ssl ? ":SSL" : "");
328 + buffer_json_member_add_string(wb, "local", buf);
329 +
330 + snprintfz(buf, 1024, "[%s]:%d%s", s->stream.peers.peer.ip, s->stream.peers.peer.port, s->stream.ssl ? ":SSL" : "");
331 + buffer_json_member_add_string(wb, "remote", buf);
332 +
333 + stream_capabilities_to_json_array(wb, s->stream.capabilities, "capabilities");
334 +
335 + buffer_json_member_add_object(wb, "traffic");
336 + {
337 + buffer_json_member_add_boolean(wb, "compression", s->stream.compression);
338 + buffer_json_member_add_uint64(wb, "data", s->stream.sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_DATA]);
339 + buffer_json_member_add_uint64(wb, "metadata", s->stream.sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_METADATA]);
340 + buffer_json_member_add_uint64(wb, "functions", s->stream.sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_FUNCTIONS]);
341 + buffer_json_member_add_uint64(wb, "replication", s->stream.sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_REPLICATION]);
342 + }
343 + buffer_json_object_close(wb); // traffic
344 +
345 + buffer_json_member_add_array(wb, "candidates");
346 + struct rrdpush_destinations *d;
347 + for (d = s->host->destinations; d; d = d->next) {
348 + buffer_json_add_array_item_object(wb);
349 + {
350 +
351 + if (d->ssl) {
352 + snprintfz(buf, 1024, "%s:SSL", string2str(d->destination));
353 + buffer_json_member_add_string(wb, "destination", buf);
354 + }
355 + else
356 + buffer_json_member_add_string(wb, "destination", string2str(d->destination));
357 +
358 + buffer_json_member_add_time_t(wb, "last_check", d->last_attempt);
359 + buffer_json_member_add_time_t(wb, "age", s->now - d->last_attempt);
360 + buffer_json_member_add_string(wb, "last_error", d->last_error);
361 + buffer_json_member_add_string(wb, "last_handshake",
362 + stream_handshake_error_to_string(d->last_handshake));
363 + buffer_json_member_add_time_t(wb, "next_check", d->postpone_reconnection_until);
364 + buffer_json_member_add_time_t(wb, "next_in",
365 + (d->postpone_reconnection_until > s->now) ?
366 + d->postpone_reconnection_until - s->now : 0);
367 + }
368 + buffer_json_object_close(wb); // each candidate
369 + }
370 + buffer_json_array_close(wb); // candidates
371 + }
372 + buffer_json_object_close(wb); // destination
373 + }
374 + buffer_json_object_close(wb); // streaming
375 +}
376 +
377 +static void agent_capabilities_to_json(BUFFER *wb, RRDHOST *host, const char *key) {
378 + buffer_json_member_add_array(wb, key);
379 +
380 + struct capability *capas = aclk_get_node_instance_capas(host);
381 + for(struct capability *capa = capas; capa->name ;capa++) {
382 + buffer_json_add_array_item_object(wb);
383 + {
384 + buffer_json_member_add_string(wb, "name", capa->name);
385 + buffer_json_member_add_uint64(wb, "version", capa->version);
386 + buffer_json_member_add_boolean(wb, "enabled", capa->enabled);
387 + }
388 + buffer_json_object_close(wb);
389 + }
390 + buffer_json_array_close(wb);
391 + freez(capas);
392 }
393
394 static ssize_t rrdcontext_to_json_v2_add_host(void *data, RRDHOST *host, bool queryable_host) {
395 if(!queryable_host || !host->rrdctx.contexts)
396 // the host matches the 'scope_host' but does not match the 'host' patterns
397 // or the host does not have any contexts
252 - return 0;
398 + return 0; // continue to next host
399
400 struct rrdcontext_to_json_v2_data *ctl = data;
401 BUFFER *wb = ctl->wb;
402
403 + if(ctl->window.enabled && !rrdhost_matches_window(host, ctl->window.after, ctl->window.before, ctl->now))
404 + // the host does not have data in the requested window
405 + return 0; // continue to next host
406 +
407 if(ctl->request->timeout_ms && now_monotonic_usec() > ctl->timings.received_ut + ctl->request->timeout_ms * USEC_PER_MS)
408 // timed out
259 - return -2;
409 + return -2; // stop the query
410
411 if(ctl->request->interrupt_callback && ctl->request->interrupt_callback(ctl->request->interrupt_callback_data))
412 // interrupted
263 - return -1;
413 + return -1; // stop the query
414
415 bool host_matched = (ctl->options & CONTEXTS_V2_NODES);
416 bool do_contexts = (ctl->options & (CONTEXTS_V2_CONTEXTS | CONTEXTS_V2_SEARCH));
@@ -293,87 +443,157 @@ static ssize_t rrdcontext_to_json_v2_add_host(void *data, RRDHOST *host, bool qu
443 // restore it
444 ctl->q.pattern = old_q;
445
296 - if(added == -1)
297 - return -1;
446 + if(unlikely(added < 0))
447 + return -1; // stop the query
448
449 if(added)
450 host_matched = true;
451 }
452
303 - if(host_matched && (ctl->options & (CONTEXTS_V2_NODES | CONTEXTS_V2_NODES_DETAILED | CONTEXTS_V2_DEBUG))) {
304 - buffer_json_add_array_item_object(wb);
305 - buffer_json_node_add_v2(wb, host, ctl->nodes.ni++, 0);
306 -
307 - if(ctl->options & CONTEXTS_V2_NODES_DETAILED) {
308 - buffer_json_member_add_string(wb, "version", rrdhost_program_version(host));
309 - buffer_json_member_add_uint64(wb, "hops", host->system_info ? host->system_info->hops : (host == localhost) ? 0 : 1);
310 - buffer_json_member_add_string(wb, "state", (host == localhost || !rrdhost_flag_check(host, RRDHOST_FLAG_ORPHAN)) ? "reachable" : "stale");
311 - buffer_json_member_add_boolean(wb, "isDeleted", false);
312 -
313 - buffer_json_member_add_array(wb, "services");
314 - buffer_json_array_close(wb);
453 + if(!host_matched)
454 + return 0;
455
316 - buffer_json_member_add_array(wb, "nodeInstanceCapabilities");
456 + if(ctl->options & CONTEXTS_V2_NODES) {
457 + buffer_json_add_array_item_object(wb); // this node
458 + buffer_json_node_add_v2(wb, host, ctl->nodes.ni++, 0,
459 + (ctl->options & CONTEXTS_V2_AGENTS) && !(ctl->options & CONTEXTS_V2_NODES_INSTANCES));
460 +
461 + if(ctl->options & (CONTEXTS_V2_NODES_INFO)) {
462 + buffer_json_member_add_string(wb, "v", rrdhost_program_version(host));
463 +
464 + host_labels2json(host, wb, "labels");
465 +
466 + if (host->system_info) {
467 + buffer_json_member_add_object(wb, "hw");
468 + {
469 + buffer_json_member_add_string_or_empty(wb, "architecture", host->system_info->architecture);
470 + buffer_json_member_add_string_or_empty(wb, "cpuFrequency", host->system_info->host_cpu_freq);
471 + buffer_json_member_add_string_or_empty(wb, "cpus", host->system_info->host_cores);
472 + buffer_json_member_add_string_or_empty(wb, "memory", host->system_info->host_ram_total);
473 + buffer_json_member_add_string_or_empty(wb, "diskSpace", host->system_info->host_disk_space);
474 + buffer_json_member_add_string_or_empty(wb, "virtualization", host->system_info->virtualization);
475 + buffer_json_member_add_string_or_empty(wb, "container", host->system_info->container);
476 + }
477 + buffer_json_object_close(wb);
478
318 - struct capability *capas = aclk_get_node_instance_capas(host);
319 - struct capability *capa = capas;
320 - while(capa->name != NULL) {
321 - buffer_json_add_array_item_object(wb);
322 - buffer_json_member_add_string(wb, "name", capa->name);
323 - buffer_json_member_add_uint64(wb, "version", capa->version);
324 - buffer_json_member_add_boolean(wb, "enabled", capa->enabled);
479 + buffer_json_member_add_object(wb, "os");
480 + {
481 + buffer_json_member_add_string_or_empty(wb, "id", host->system_info->host_os_id);
482 + buffer_json_member_add_string_or_empty(wb, "nm", host->system_info->host_os_name);
483 + buffer_json_member_add_string_or_empty(wb, "v", host->system_info->host_os_version);
484 + buffer_json_member_add_object(wb, "kernel");
485 + buffer_json_member_add_string_or_empty(wb, "nm", host->system_info->kernel_name);
486 + buffer_json_member_add_string_or_empty(wb, "v", host->system_info->kernel_version);
487 + buffer_json_object_close(wb);
488 + }
489 buffer_json_object_close(wb);
326 - capa++;
490 }
328 - buffer_json_array_close(wb);
329 - freez(capas);
491
331 - web_client_api_request_v1_info_summary_alarm_statuses(host, wb, "alarmCounters");
492 + // created - the node is created but never connected to cloud
493 + // unreachable - not currently connected
494 + // stale - connected but not having live data
495 + // reachable - connected with live data
496 + // pruned - not connected for some time and has been removed
497 + buffer_json_member_add_string(wb, "state", rrdhost_state_cloud_emulation(host)?"reachable":"stale");
498
333 - host_labels2json(host, wb, "hostLabels");
499 + agent_capabilities_to_json(wb, host, "capabilities");
500 + }
501
335 - buffer_json_member_add_object(wb, "mlInfo");
336 - buffer_json_member_add_boolean(wb, "mlCapable", ml_capable(host));
337 - buffer_json_member_add_boolean(wb, "mlEnabled", ml_enabled(host));
338 - buffer_json_object_close(wb);
502 + if(ctl->options & (CONTEXTS_V2_NODES_INSTANCES)) {
503 + buffer_json_member_add_array(wb, "instances");
504 + buffer_json_add_array_item_object(wb); // this instance
505 + {
506 + buffer_json_agent_status_id(wb, 0, 0);
507 +
508 + time_t now = now_realtime_sec();
509 + RRDHOST_STATUS s;
510 + rrdhost_status(host, now, &s);
511 + buffer_json_member_add_object(wb, "db");
512 + {
513 + buffer_json_member_add_string(wb, "status", rrdhost_db_status_to_string(s.db.status));
514 + buffer_json_member_add_string(wb, "liveness", rrdhost_db_liveness_to_string(s.db.liveness));
515 + buffer_json_member_add_string(wb, "mode", rrd_memory_mode_name(s.db.mode));
516 + buffer_json_member_add_time_t(wb, "first_time", s.db.first_time_s);
517 + buffer_json_member_add_time_t(wb, "last_time", s.db.last_time_s);
518 + buffer_json_member_add_uint64(wb, "metrics", s.db.metrics);
519 + buffer_json_member_add_uint64(wb, "instances", s.db.instances);
520 + buffer_json_member_add_uint64(wb, "contexts", s.db.contexts);
521 + }
522 + buffer_json_object_close(wb);
523
340 - if(host->system_info) {
341 - buffer_json_member_add_string_or_empty(wb, "architecture", host->system_info->architecture);
342 - buffer_json_member_add_string_or_empty(wb, "kernelName", host->system_info->kernel_name);
343 - buffer_json_member_add_string_or_empty(wb, "kernelVersion", host->system_info->kernel_version);
344 - buffer_json_member_add_string_or_empty(wb, "cpuFrequency", host->system_info->host_cpu_freq);
345 - buffer_json_member_add_string_or_empty(wb, "cpus", host->system_info->host_cores);
346 - buffer_json_member_add_string_or_empty(wb, "memory", host->system_info->host_ram_total);
347 - buffer_json_member_add_string_or_empty(wb, "diskSpace", host->system_info->host_disk_space);
348 - buffer_json_member_add_string_or_empty(wb, "container", host->system_info->container);
349 - buffer_json_member_add_string_or_empty(wb, "virtualization", host->system_info->virtualization);
350 - buffer_json_member_add_string_or_empty(wb, "os", host->system_info->host_os_id);
351 - buffer_json_member_add_string_or_empty(wb, "osName", host->system_info->host_os_name);
352 - buffer_json_member_add_string_or_empty(wb, "osVersion", host->system_info->host_os_version);
353 - }
524 + rrdhost_receiver_to_json(wb, &s, "ingest");
525 + rrdhost_sender_to_json(wb, &s, "stream");
526 +
527 + buffer_json_member_add_object(wb, "ml");
528 + buffer_json_member_add_string(wb, "status", rrdhost_ml_status_to_string(s.ml.status));
529 + buffer_json_member_add_string(wb, "type", rrdhost_ml_type_to_string(s.ml.type));
530 + if (s.ml.status == RRDHOST_ML_STATUS_RUNNING) {
531 + buffer_json_member_add_object(wb, "metrics");
532 + {
533 + buffer_json_member_add_uint64(wb, "anomalous", s.ml.metrics.anomalous);
534 + buffer_json_member_add_uint64(wb, "normal", s.ml.metrics.normal);
535 + buffer_json_member_add_uint64(wb, "trained", s.ml.metrics.trained);
536 + buffer_json_member_add_uint64(wb, "pending", s.ml.metrics.pending);
537 + buffer_json_member_add_uint64(wb, "silenced", s.ml.metrics.silenced);
538 + }
539 + buffer_json_object_close(wb); // metrics
540 + }
541 + buffer_json_object_close(wb); // ml
542 +
543 + buffer_json_member_add_object(wb, "health");
544 + {
545 + buffer_json_member_add_string(wb, "status", rrdhost_health_status_to_string(s.health.status));
546 + if (s.health.status == RRDHOST_HEALTH_STATUS_RUNNING) {
547 + buffer_json_member_add_object(wb, "alerts");
548 + {
549 + buffer_json_member_add_uint64(wb, "critical", s.health.alerts.critical);
550 + buffer_json_member_add_uint64(wb, "warning", s.health.alerts.warning);
551 + buffer_json_member_add_uint64(wb, "clear", s.health.alerts.clear);
552 + buffer_json_member_add_uint64(wb, "undefined", s.health.alerts.undefined);
553 + buffer_json_member_add_uint64(wb, "uninitialized", s.health.alerts.uninitialized);
554 + }
555 + buffer_json_object_close(wb); // alerts
556 + }
557 + }
558 + buffer_json_object_close(wb); // health
559
355 - time_t now = now_realtime_sec();
356 - buffer_json_member_add_object(wb, "status");
357 - {
358 - rrdhost_receiver_to_json(wb, host, "collection", now);
359 - rrdhost_sender_to_json(wb, host, "streaming", now);
560 + host_functions2json(host, wb); // functions
561 + agent_capabilities_to_json(wb, host, "capabilities");
562 }
361 - buffer_json_object_close(wb); // status
563 + buffer_json_object_close(wb); // this instance
564 + buffer_json_array_close(wb); // instances
565 }
363 -
364 - buffer_json_object_close(wb);
566 + buffer_json_object_close(wb); // this node
567 }
568
367 - return host_matched ? 1 : 0;
569 + return 1;
570 }
571
572 static void buffer_json_contexts_v2_options_to_array(BUFFER *wb, CONTEXTS_V2_OPTIONS options) {
573 if(options & CONTEXTS_V2_DEBUG)
574 buffer_json_add_array_item_string(wb, "debug");
575
374 - if(options & (CONTEXTS_V2_NODES | CONTEXTS_V2_NODES_DETAILED))
576 + if(options & CONTEXTS_V2_MINIFY)
577 + buffer_json_add_array_item_string(wb, "minify");
578 +
579 + if(options & CONTEXTS_V2_VERSIONS)
580 + buffer_json_add_array_item_string(wb, "versions");
581 +
582 + if(options & CONTEXTS_V2_AGENTS)
583 + buffer_json_add_array_item_string(wb, "agents");
584 +
585 + if(options & CONTEXTS_V2_AGENTS_INFO)
586 + buffer_json_add_array_item_string(wb, "agents-info");
587 +
588 + if(options & CONTEXTS_V2_NODES)
589 buffer_json_add_array_item_string(wb, "nodes");
590
591 + if(options & CONTEXTS_V2_NODES_INFO)
592 + buffer_json_add_array_item_string(wb, "nodes-info");
593 +
594 + if(options & CONTEXTS_V2_NODES_INSTANCES)
595 + buffer_json_add_array_item_string(wb, "nodes-instances");
596 +
597 if(options & CONTEXTS_V2_CONTEXTS)
598 buffer_json_add_array_item_string(wb, "contexts");
599
@@ -396,7 +616,7 @@ void buffer_json_query_timings(BUFFER *wb, const char *key, struct query_timings
616 buffer_json_object_close(wb);
617 }
618
399 -void buffer_json_agents_array_v2(BUFFER *wb, struct query_timings *timings, time_t now_s) {
619 +void buffer_json_agents_array_v2(BUFFER *wb, struct query_timings *timings, time_t now_s, bool info) {
620 if(!now_s)
621 now_s = now_realtime_sec();
622
@@ -408,6 +628,56 @@ void buffer_json_agents_array_v2(BUFFER *wb, struct query_timings *timings, time
628 buffer_json_member_add_time_t(wb, "now", now_s);
629 buffer_json_member_add_uint64(wb, "ai", 0);
630
631 + if(info) {
632 + buffer_json_member_add_string(wb, "v", string2str(localhost->program_version));
633 +
634 + buffer_json_member_add_object(wb, "cloud");
635 + {
636 + size_t id = cloud_connection_id();
637 + CLOUD_STATUS status = cloud_status();
638 + time_t last_change = cloud_last_change();
639 + time_t next_connect = cloud_next_connection_attempt();
640 + buffer_json_member_add_uint64(wb, "id", id);
641 + buffer_json_member_add_string(wb, "status", cloud_status_to_string(status));
642 + buffer_json_member_add_time_t(wb, "since", last_change);
643 + buffer_json_member_add_time_t(wb, "age", now_s - last_change);
644 +
645 + if (status != CLOUD_STATUS_ONLINE)
646 + buffer_json_member_add_string(wb, "reason", cloud_offline_reason());
647 +
648 + if (status == CLOUD_STATUS_OFFLINE && next_connect > now_s) {
649 + buffer_json_member_add_time_t(wb, "next_check", next_connect);
650 + buffer_json_member_add_time_t(wb, "next_in", next_connect - now_s);
651 + }
652 +
653 + if (status != CLOUD_STATUS_DISABLED && cloud_base_url())
654 + buffer_json_member_add_string(wb, "url", cloud_base_url());
655 + }
656 + buffer_json_object_close(wb); // cloud
657 +
658 + buffer_json_member_add_array(wb, "db_size");
659 + for (size_t tier = 0; tier < storage_tiers; tier++) {
660 + STORAGE_ENGINE *eng = localhost->db[tier].eng;
661 + if (!eng) continue;
662 +
663 + size_t max = storage_engine_disk_space_max(eng->backend, localhost->db[tier].instance);
664 + size_t used = storage_engine_disk_space_used(eng->backend, localhost->db[tier].instance);
665 + NETDATA_DOUBLE percent;
666 + if (used && max)
667 + percent = (NETDATA_DOUBLE) used * 100.0 / (NETDATA_DOUBLE) max;
668 + else
669 + percent = 0.0;
670 +
671 + buffer_json_add_array_item_object(wb);
672 + buffer_json_member_add_uint64(wb, "tier", tier);
673 + buffer_json_member_add_uint64(wb, "disk_used", used);
674 + buffer_json_member_add_uint64(wb, "disk_max", max);
675 + buffer_json_member_add_double(wb, "disk_percent", percent);
676 + buffer_json_object_close(wb);
677 + }
678 + buffer_json_array_close(wb); // db_size
679 + }
680 +
681 if(timings)
682 buffer_json_query_timings(wb, "timings", timings);
683
@@ -416,6 +686,9 @@ void buffer_json_agents_array_v2(BUFFER *wb, struct query_timings *timings, time
686 }
687
688 void buffer_json_cloud_timings(BUFFER *wb, const char *key, struct query_timings *timings) {
689 + if(!timings->finished_ut)
690 + timings->finished_ut = now_monotonic_usec();
691 +
692 buffer_json_member_add_object(wb, key);
693 buffer_json_member_add_double(wb, "routing_ms", 0.0);
694 buffer_json_member_add_double(wb, "node_max_ms", 0.0);
@@ -423,6 +696,48 @@ void buffer_json_cloud_timings(BUFFER *wb, const char *key, struct query_timings
696 buffer_json_object_close(wb);
697 }
698
699 +bool contexts_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data __maybe_unused) {
700 + struct rrdcontext_to_json_v2_entry *o = old_value;
701 + struct rrdcontext_to_json_v2_entry *n = new_value;
702 +
703 + o->count++;
704 +
705 + if(o->family != n->family) {
706 + STRING *m = string_2way_merge(o->family, n->family);
707 + string_freez(o->family);
708 + o->family = m;
709 + }
710 +
711 + if(o->priority != n->priority) {
712 + if((o->flags & RRD_FLAG_COLLECTED) && !(n->flags & RRD_FLAG_COLLECTED))
713 + // keep o
714 + ;
715 + else if(!(o->flags & RRD_FLAG_COLLECTED) && (n->flags & RRD_FLAG_COLLECTED))
716 + // keep n
717 + o->priority = n->priority;
718 + else
719 + // keep the min
720 + o->priority = MIN(o->priority, n->priority);
721 + }
722 +
723 + if(o->first_time_s && n->first_time_s)
724 + o->first_time_s = MIN(o->first_time_s, n->first_time_s);
725 + else if(!o->first_time_s)
726 + o->first_time_s = n->first_time_s;
727 +
728 + if(o->last_time_s && n->last_time_s)
729 + o->last_time_s = MAX(o->last_time_s, n->last_time_s);
730 + else if(!o->last_time_s)
731 + o->last_time_s = n->last_time_s;
732 +
733 + o->flags |= n->flags;
734 + o->match = MIN(o->match, n->match);
735 +
736 + string_freez(n->family);
737 +
738 + return true;
739 +}
740 +
741 void contexts_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
742 struct rrdcontext_to_json_v2_entry *z = value;
743 string_freez(z->family);
@@ -434,6 +749,12 @@ int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTE
749 if(options & CONTEXTS_V2_SEARCH)
750 options |= CONTEXTS_V2_CONTEXTS;
751
752 + if(options & (CONTEXTS_V2_NODES_INFO | CONTEXTS_V2_NODES_INSTANCES))
753 + options |= CONTEXTS_V2_NODES;
754 +
755 + if(options & (CONTEXTS_V2_AGENTS_INFO))
756 + options |= CONTEXTS_V2_AGENTS;
757 +
758 struct rrdcontext_to_json_v2_data ctl = {
759 .wb = wb,
760 .request = req,
@@ -445,6 +766,12 @@ int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTE
766 .contexts.pattern = string_to_simple_pattern(req->contexts),
767 .contexts.scope_pattern = string_to_simple_pattern(req->scope_contexts),
768 .q.pattern = string_to_simple_pattern_nocase(req->q),
769 + .window = {
770 + .enabled = false,
771 + .relative = false,
772 + .after = req->after,
773 + .before = req->before,
774 + },
775 .timings = {
776 .received_ut = now_monotonic_usec(),
777 }
@@ -455,11 +782,20 @@ int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTE
782 DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL,
783 sizeof(struct rrdcontext_to_json_v2_entry));
784
785 + dictionary_register_conflict_callback(ctl.ctx, contexts_conflict_callback, NULL);
786 dictionary_register_delete_callback(ctl.ctx, contexts_delete_callback, NULL);
787 }
788
461 - time_t now_s = now_realtime_sec();
462 - buffer_json_initialize(wb, "\"", "\"", 0, true, false);
789 + if(req->after || req->before) {
790 + ctl.window.relative = rrdr_relative_window_to_absolute(&ctl.window.after, &ctl.window.before, &ctl.now);
791 + ctl.window.enabled = true;
792 + }
793 + else
794 + ctl.now = now_realtime_sec();
795 +
796 + buffer_json_initialize(wb, "\"", "\"", 0,
797 + true, (options & CONTEXTS_V2_MINIFY) && !(options & CONTEXTS_V2_DEBUG));
798 +
799 buffer_json_member_add_uint64(wb, "api", 2);
800
801 if(options & CONTEXTS_V2_DEBUG) {
@@ -475,7 +811,12 @@ int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTE
811 buffer_json_member_add_string(wb, "contexts", req->contexts);
812 buffer_json_object_close(wb);
813
814 + buffer_json_member_add_object(wb, "filters");
815 buffer_json_member_add_string(wb, "q", req->q);
816 + buffer_json_member_add_time_t(wb, "after", req->after);
817 + buffer_json_member_add_time_t(wb, "before", req->before);
818 + buffer_json_object_close(wb);
819 +
820 buffer_json_member_add_array(wb, "options");
821 buffer_json_contexts_v2_options_to_array(wb, options);
822 buffer_json_array_close(wb);
@@ -483,7 +824,7 @@ int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTE
824 buffer_json_object_close(wb);
825 }
826
486 - if(options & (CONTEXTS_V2_NODES | CONTEXTS_V2_NODES_DETAILED | CONTEXTS_V2_DEBUG))
827 + if(options & CONTEXTS_V2_NODES)
828 buffer_json_member_add_array(wb, "nodes");
829
830 ssize_t ret = query_scope_foreach_host(ctl.nodes.scope_pattern, ctl.nodes.pattern,
@@ -504,11 +845,10 @@ int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTE
845 goto cleanup;
846 }
847
507 - if(options & (CONTEXTS_V2_NODES | CONTEXTS_V2_NODES_DETAILED | CONTEXTS_V2_DEBUG))
848 + if(options & CONTEXTS_V2_NODES)
849 buffer_json_array_close(wb);
850
851 ctl.timings.executed_ut = now_monotonic_usec();
511 - version_hashes_api_v2(wb, &ctl.versions);
852
853 if(options & CONTEXTS_V2_CONTEXTS) {
854 buffer_json_member_add_object(wb, "contexts");
@@ -521,7 +861,7 @@ int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTE
861 buffer_json_member_add_string(wb, "family", string2str(z->family));
862 buffer_json_member_add_uint64(wb, "priority", z->priority);
863 buffer_json_member_add_time_t(wb, "first_entry", z->first_time_s);
524 - buffer_json_member_add_time_t(wb, "last_entry", collected ? now_s : z->last_time_s);
864 + buffer_json_member_add_time_t(wb, "last_entry", collected ? ctl.now : z->last_time_s);
865 buffer_json_member_add_boolean(wb, "live", collected);
866 if (options & CONTEXTS_V2_SEARCH)
867 buffer_json_member_add_string(wb, "match", fts_match_to_string(z->match));
@@ -540,8 +880,14 @@ int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTE
880 buffer_json_object_close(wb);
881 }
882
543 - buffer_json_agents_array_v2(wb, &ctl.timings, now_s);
883 + if(options & (CONTEXTS_V2_VERSIONS))
884 + version_hashes_api_v2(wb, &ctl.versions);
885 +
886 + if(options & CONTEXTS_V2_AGENTS)
887 + buffer_json_agents_array_v2(wb, &ctl.timings, ctl.now, options & (CONTEXTS_V2_AGENTS_INFO));
888 +
889 buffer_json_cloud_timings(wb, "timings", &ctl.timings);
890 +
891 buffer_json_finalize(wb);
892
893 cleanup:
database/contexts/internal.h
+4
@@ -275,6 +275,10 @@ typedef struct rrdcontext {
275 size_t dispatches; // the number of times this has been dispatched to hub
276 } queue;
277
278 + struct {
279 + uint32_t metrics; // the number of metrics in this context
280 + } stats;
281 +
282 netdata_mutex_t mutex;
283 } RRDCONTEXT;
284
database/contexts/rrdcontext.h
+14 -7
@@ -467,6 +467,8 @@ struct api_v2_contexts_request {
467 char *contexts;
468 char *q;
469
470 + time_t after;
471 + time_t before;
472 time_t timeout_ms;
473
474 qt_interrupt_callback_t interrupt_callback;
@@ -474,18 +476,23 @@ struct api_v2_contexts_request {
476 };
477
478 typedef enum __attribute__ ((__packed__)) {
477 - CONTEXTS_V2_DEBUG = (1 << 0),
478 - CONTEXTS_V2_SEARCH = (1 << 1),
479 - CONTEXTS_V2_NODES = (1 << 2),
480 - CONTEXTS_V2_NODES_DETAILED = (1 << 3),
481 - CONTEXTS_V2_CONTEXTS = (1 << 4),
479 + CONTEXTS_V2_DEBUG = (1 << 0),
480 + CONTEXTS_V2_MINIFY = (1 << 1),
481 + CONTEXTS_V2_SEARCH = (1 << 2),
482 + CONTEXTS_V2_NODES = (1 << 3),
483 + CONTEXTS_V2_NODES_INFO = (1 << 4),
484 + CONTEXTS_V2_NODES_INSTANCES = (1 << 5),
485 + CONTEXTS_V2_CONTEXTS = (1 << 6),
486 + CONTEXTS_V2_AGENTS = (1 << 7),
487 + CONTEXTS_V2_AGENTS_INFO = (1 << 8),
488 + CONTEXTS_V2_VERSIONS = (1 << 9),
489 } CONTEXTS_V2_OPTIONS;
490
491 int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTEXTS_V2_OPTIONS options);
492
493 RRDCONTEXT_TO_JSON_OPTIONS rrdcontext_to_json_parse_options(char *o);
487 -void buffer_json_agents_array_v2(BUFFER *wb, struct query_timings *timings, time_t now_s);
488 -void buffer_json_node_add_v2(BUFFER *wb, RRDHOST *host, size_t ni, usec_t duration_ut);
494 +void buffer_json_agents_array_v2(BUFFER *wb, struct query_timings *timings, time_t now_s, bool info);
495 +void buffer_json_node_add_v2(BUFFER *wb, RRDHOST *host, size_t ni, usec_t duration_ut, bool status);
496 void buffer_json_query_timings(BUFFER *wb, const char *key, struct query_timings *timings);
497 void buffer_json_cloud_timings(BUFFER *wb, const char *key, struct query_timings *timings);
498
database/contexts/worker.c
+54 -3
@@ -167,6 +167,27 @@ uint64_t rrdcontext_version_hash_with_callback(
167 // ----------------------------------------------------------------------------
168 // retention recalculation
169
170 +static void rrdhost_update_cached_retention(RRDHOST *host, time_t first_time_s, time_t last_time_s, bool global) {
171 + if(unlikely(!host))
172 + return;
173 +
174 + netdata_spinlock_lock(&host->retention.spinlock);
175 +
176 + if(global) {
177 + host->retention.first_time_s = first_time_s;
178 + host->retention.last_time_s = last_time_s;
179 + }
180 + else {
181 + if(!host->retention.first_time_s || first_time_s < host->retention.first_time_s)
182 + host->retention.first_time_s = first_time_s;
183 +
184 + if(!host->retention.last_time_s || last_time_s > host->retention.last_time_s)
185 + host->retention.last_time_s = last_time_s;
186 + }
187 +
188 + netdata_spinlock_unlock(&host->retention.spinlock);
189 +}
190 +
191 void rrdcontext_recalculate_context_retention(RRDCONTEXT *rc, RRD_FLAGS reason, bool worker_jobs) {
192 rrdcontext_post_process_updates(rc, true, reason, worker_jobs);
193 }
@@ -174,11 +195,22 @@ void rrdcontext_recalculate_context_retention(RRDCONTEXT *rc, RRD_FLAGS reason,
195 void rrdcontext_recalculate_host_retention(RRDHOST *host, RRD_FLAGS reason, bool worker_jobs) {
196 if(unlikely(!host || !host->rrdctx.contexts)) return;
197
198 + time_t first_time_s = 0;
199 + time_t last_time_s = 0;
200 +
201 RRDCONTEXT *rc;
202 dfe_start_read(host->rrdctx.contexts, rc) {
179 - rrdcontext_recalculate_context_retention(rc, reason, worker_jobs);
180 - }
203 + rrdcontext_recalculate_context_retention(rc, reason, worker_jobs);
204 +
205 + if(!first_time_s || rc->first_time_s < first_time_s)
206 + first_time_s = rc->first_time_s;
207 +
208 + if(!last_time_s || rc->last_time_s > last_time_s)
209 + last_time_s = rc->last_time_s;
210 + }
211 dfe_done(rc);
212 +
213 + rrdhost_update_cached_retention(host, first_time_s, last_time_s, true);
214 }
215
216 static void rrdcontext_recalculate_retention_all_hosts(void) {
@@ -554,7 +586,7 @@ static void rrdcontext_post_process_updates(RRDCONTEXT *rc, bool force, RRD_FLAG
586 size_t min_priority_not_collected = LONG_MAX;
587 size_t min_priority = LONG_MAX;
588 time_t min_first_time_t = LONG_MAX, max_last_time_t = 0;
557 - size_t instances_active = 0, instances_deleted = 0;
589 + size_t instances_active = 0, instances_deleted = 0, metrics = 0;
590 bool live_retention = true, currently_collected = false, hidden = true;
591 if(dictionary_entries(rc->rrdinstances) > 0) {
592 RRDINSTANCE *ri;
@@ -587,6 +619,7 @@ static void rrdcontext_post_process_updates(RRDCONTEXT *rc, bool force, RRD_FLAG
619 string2str(rc->units), string2str(ri->units));
620
621 instances_active++;
622 + metrics += dictionary_entries(ri->rrdmetrics);
623
624 if (ri->priority >= RRDCONTEXT_MINIMUM_ALLOWED_PRIORITY) {
625 if(rrd_flag_check(ri, RRD_FLAG_COLLECTED)) {
@@ -607,6 +640,8 @@ static void rrdcontext_post_process_updates(RRDCONTEXT *rc, bool force, RRD_FLAG
640 }
641 dfe_done(ri);
642
643 + rc->stats.metrics = metrics;
644 +
645 if(min_priority_collected != LONG_MAX)
646 // use the collected priority
647 min_priority = min_priority_collected;
@@ -872,9 +907,14 @@ static bool check_if_cloud_version_changed_unsafe(RRDCONTEXT *rc, bool sending _
907 sending ? (rc->queue.scheduled_dispatch_ut - rc->queue.queued_ut) / USEC_PER_MS : 0
908 );
909
910 + rrdhost_update_cached_retention(rc->rrdhost, rc->first_time_s, rc->last_time_s, false);
911 +
912 return true;
913 }
914
915 + if(!(flags & RRD_FLAG_COLLECTED))
916 + rrdhost_update_cached_retention(rc->rrdhost, rc->first_time_s, rc->last_time_s, false);
917 +
918 return false;
919 }
920
@@ -1082,6 +1122,17 @@ void *rrdcontext_main(void *ptr) {
1122
1123 if (host->rrdctx.contexts)
1124 dictionary_garbage_collect(host->rrdctx.contexts);
1125 +
1126 + // calculate the number of metrics and instances in the host
1127 + RRDCONTEXT *rc;
1128 + uint32_t metrics = 0, instances = 0;
1129 + dfe_start_read(host->rrdctx.contexts, rc) {
1130 + metrics += rc->stats.metrics;
1131 + instances += dictionary_entries(rc->rrdinstances);
1132 + }
1133 + dfe_done(rc);
1134 + host->rrdctx.metrics = metrics;
1135 + host->rrdctx.instances = instances;
1136 }
1137 dfe_done(host);
1138
database/engine/rrdengineapi.c
+10
@@ -1002,6 +1002,16 @@ bool rrdeng_metric_retention_by_uuid(STORAGE_INSTANCE *db_instance, uuid_t *dim_
1002 return true;
1003 }
1004
1005 +size_t rrdeng_disk_space_max(STORAGE_INSTANCE *db_instance) {
1006 + struct rrdengine_instance *ctx = (struct rrdengine_instance *)db_instance;
1007 + return ctx->config.max_disk_space;
1008 +}
1009 +
1010 +size_t rrdeng_disk_space_used(STORAGE_INSTANCE *db_instance) {
1011 + struct rrdengine_instance *ctx = (struct rrdengine_instance *)db_instance;
1012 + return __atomic_load_n(&ctx->atomic.current_disk_space, __ATOMIC_RELAXED);
1013 +}
1014 +
1015 /*
1016 * Gathers Database Engine statistics.
1017 * Careful when modifying this function.
database/engine/rrdengineapi.h
+3
@@ -222,4 +222,7 @@ RRDENG_SIZE_STATS rrdeng_size_statistics(struct rrdengine_instance *ctx);
222 size_t rrdeng_collectors_running(struct rrdengine_instance *ctx);
223 bool rrdeng_is_legacy(STORAGE_INSTANCE *db_instance);
224
225 +size_t rrdeng_disk_space_max(STORAGE_INSTANCE *db_instance);
226 +size_t rrdeng_disk_space_used(STORAGE_INSTANCE *db_instance);
227 +
228 #endif /* NETDATA_RRDENGINEAPI_H */
database/rrd.h
+63 -1
@@ -7,6 +7,8 @@
7 extern "C" {
8 #endif
9
10 +#include "libnetdata/libnetdata.h"
11 +
12 // non-existing structs instead of voids
13 // to enable type checking at compile time
14 typedef struct storage_instance STORAGE_INSTANCE;
@@ -95,6 +97,14 @@ extern RRD_MEMORY_MODE default_rrd_memory_mode;
97 const char *rrd_memory_mode_name(RRD_MEMORY_MODE id);
98 RRD_MEMORY_MODE rrd_memory_mode_id(const char *name);
99
100 +struct ml_metrics_statistics {
101 + size_t anomalous;
102 + size_t normal;
103 + size_t trained;
104 + size_t pending;
105 + size_t silenced;
106 +};
107 +
108 #include "daemon/common.h"
109 #include "web/api/queries/query.h"
110 #include "web/api/queries/rrdr.h"
@@ -301,7 +311,7 @@ void rrdlabels_copy(DICTIONARY *dst, DICTIONARY *src);
311 void reload_host_labels(void);
312 void rrdset_update_rrdlabels(RRDSET *st, DICTIONARY *new_rrdlabels);
313 void rrdset_save_rrdlabels_to_sql(RRDSET *st);
304 -void rrdhost_set_is_parent_label(int count);
314 +void rrdhost_set_is_parent_label(void);
315 int rrdlabels_unittest(void);
316
317 // unfortunately this break when defined in exporting_engine.h
@@ -487,6 +497,27 @@ static inline void storage_engine_store_metric(
497 count, anomaly_count, flags);
498 }
499
500 +size_t rrdeng_disk_space_max(STORAGE_INSTANCE *db_instance);
501 +static inline size_t storage_engine_disk_space_max(STORAGE_ENGINE_BACKEND backend __maybe_unused, STORAGE_INSTANCE *db_instance) {
502 +#ifdef ENABLE_DBENGINE
503 + if(likely(backend == STORAGE_ENGINE_BACKEND_DBENGINE))
504 + return rrdeng_disk_space_max(db_instance);
505 +#endif
506 +
507 + return 0;
508 +}
509 +
510 +size_t rrdeng_disk_space_used(STORAGE_INSTANCE *db_instance);
511 +static inline size_t storage_engine_disk_space_used(STORAGE_ENGINE_BACKEND backend __maybe_unused, STORAGE_INSTANCE *db_instance) {
512 +#ifdef ENABLE_DBENGINE
513 + if(likely(backend == STORAGE_ENGINE_BACKEND_DBENGINE))
514 + return rrdeng_disk_space_used(db_instance);
515 +#endif
516 +
517 + // TODO - calculate the total host disk space for memory mode save and map
518 + return 0;
519 +}
520 +
521 void rrdeng_store_metric_flush_current_page(STORAGE_COLLECT_HANDLE *collection_handle);
522 void rrddim_store_metric_flush(STORAGE_COLLECT_HANDLE *collection_handle);
523 static inline void storage_engine_store_flush(STORAGE_COLLECT_HANDLE *collection_handle) {
@@ -924,6 +955,7 @@ typedef enum __attribute__ ((__packed__)) rrdhost_flags {
955 // ACLK
956 RRDHOST_FLAG_ACLK_STREAM_CONTEXTS = (1 << 21), // when set, we should send ACLK stream context updates
957 RRDHOST_FLAG_ACLK_STREAM_ALERTS = (1 << 22), // set when the receiver part is disconnected
958 +
959 // Metadata
960 RRDHOST_FLAG_METADATA_UPDATE = (1 << 23), // metadata needs to be stored in the database
961 RRDHOST_FLAG_METADATA_LABELS = (1 << 24), // metadata needs to be stored in the database
@@ -959,6 +991,8 @@ typedef enum __attribute__ ((__packed__)) {
991 RRDHOST_OPTION_DELETE_ORPHAN_HOST = (1 << 4), // delete the entire host when orphan
992
993 RRDHOST_OPTION_REPLICATION = (1 << 5), // when set, we support replication for this host
994 +
995 + RRDHOST_OPTION_VIRTUAL_HOST = (1 << 6), // when set, this host is a virtual one
996 } RRDHOST_OPTIONS;
997
998 #define rrdhost_option_check(host, flag) ((host)->options & (flag))
@@ -1163,6 +1197,9 @@ struct rrdhost {
1197 size_t rrdpush_sender_replicating_charts; // the number of charts currently being replicated to a parent
1198 void *aclk_sync_host_config;
1199
1200 + uint32_t rrdpush_receiver_connection_counter; // the number of times this receiver has connected
1201 + uint32_t rrdpush_sender_connection_counter; // the number of times this sender has connected
1202 +
1203 // ------------------------------------------------------------------------
1204 // streaming of data from remote hosts - rrdpush receiver
1205
@@ -1225,8 +1262,16 @@ struct rrdhost {
1262 DICTIONARY *contexts;
1263 DICTIONARY *hub_queue;
1264 DICTIONARY *pp_queue;
1265 + uint32_t metrics;
1266 + uint32_t instances;
1267 } rrdctx;
1268
1269 + struct {
1270 + SPINLOCK spinlock;
1271 + time_t first_time_s;
1272 + time_t last_time_s;
1273 + } retention;
1274 +
1275 uuid_t host_uuid; // Global GUID for this host
1276 uuid_t *node_id; // Cloud node_id
1277
@@ -1260,6 +1305,9 @@ extern RRDHOST *localhost;
1305 #define rrdhost_sender_replicating_charts_minus_one(host) (__atomic_sub_fetch(&((host)->rrdpush_sender_replicating_charts), 1, __ATOMIC_RELAXED))
1306 #define rrdhost_sender_replicating_charts_zero(host) (__atomic_store_n(&((host)->rrdpush_sender_replicating_charts), 0, __ATOMIC_RELAXED))
1307
1308 +#define rrdhost_is_online(host) ((host) == localhost || !(rrdhost_flag_check(host, RRDHOST_FLAG_ORPHAN | RRDHOST_FLAG_RRDPUSH_RECEIVER_DISCONNECTED) || rrdhost_option_check(host, RRDHOST_OPTION_VIRTUAL_HOST)))
1309 +bool rrdhost_matches_window(RRDHOST *host, time_t after, time_t before, time_t now);
1310 +
1311 extern DICTIONARY *rrdhost_root_index;
1312 size_t rrdhost_hosts_available(void);
1313
@@ -1522,6 +1570,20 @@ void set_host_properties(
1570 size_t get_tier_grouping(size_t tier);
1571 void store_metric_collection_completed(void);
1572
1573 +static inline void rrdhost_retention(RRDHOST *host, time_t now, bool online, time_t *from, time_t *to) {
1574 + time_t first_time_s = 0, last_time_s = 0;
1575 + netdata_spinlock_lock(&host->retention.spinlock);
1576 + first_time_s = host->retention.first_time_s;
1577 + last_time_s = host->retention.last_time_s;
1578 + netdata_spinlock_unlock(&host->retention.spinlock);
1579 +
1580 + if(from)
1581 + *from = first_time_s;
1582 +
1583 + if(to)
1584 + *to = online ? now : last_time_s;
1585 +}
1586 +
1587 // ----------------------------------------------------------------------------
1588 // RRD DB engine declarations
1589
database/rrdfunctions.c
+643
@@ -480,6 +480,22 @@ void rrd_functions_expose_rrdpush(RRDSET *st, BUFFER *wb) {
480 dfe_done(tmp);
481 }
482
483 +void rrd_functions_expose_global_rrdpush(RRDHOST *host, BUFFER *wb) {
484 + struct rrd_collector_function *tmp;
485 + dfe_start_read(host->functions, tmp) {
486 + if(!(tmp->options & RRD_FUNCTION_GLOBAL))
487 + continue;
488 +
489 + buffer_sprintf(wb
490 + , PLUGINSD_KEYWORD_FUNCTION " GLOBAL \"%s\" %d \"%s\"\n"
491 + , tmp_dfe.name
492 + , tmp->timeout
493 + , string2str(tmp->help)
494 + );
495 + }
496 + dfe_done(tmp);
497 +}
498 +
499 struct rrd_function_call_wait {
500 bool free_with_signal;
501 bool data_are_ready;
@@ -774,3 +790,630 @@ void chart_functions_to_dict(DICTIONARY *rrdset_functions_view, DICTIONARY *dst)
790 }
791 dfe_done(t);
792 }
793 +
794 +int rrdhost_function_streaming(BUFFER *wb, int timeout __maybe_unused, const char *function __maybe_unused,
795 + void *collector_data __maybe_unused,
796 + function_data_ready_callback callback __maybe_unused, void *callback_data __maybe_unused) {
797 + time_t now = now_realtime_sec();
798 +
799 + buffer_flush(wb);
800 + wb->content_type = CT_APPLICATION_JSON;
801 + buffer_json_initialize(wb, "\"", "\"", 0, true, false);
802 +
803 + buffer_json_member_add_string(wb, "hostname", rrdhost_hostname(localhost));
804 + buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
805 + buffer_json_member_add_string(wb, "type", "table");
806 + buffer_json_member_add_time_t(wb, "update_every", 1);
807 + buffer_json_member_add_string(wb, "help", RRDFUNCTIONS_STREAMING_HELP);
808 + buffer_json_member_add_array(wb, "data");
809 +
810 + size_t max_sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_MAX];
811 + size_t max_db_metrics = 0, max_db_instances = 0, max_db_contexts = 0;
812 + size_t max_collection_replication_instances = 0, max_streaming_replication_instances = 0;
813 + size_t max_ml_anomalous = 0, max_ml_normal = 0, max_ml_trained = 0, max_ml_pending = 0, max_ml_silenced = 0;
814 + {
815 + RRDHOST *host;
816 + dfe_start_read(rrdhost_root_index, host) {
817 + RRDHOST_STATUS s;
818 + rrdhost_status(host, now, &s);
819 + buffer_json_add_array_item_array(wb);
820 +
821 + if(s.db.metrics > max_db_metrics)
822 + max_db_metrics = s.db.metrics;
823 +
824 + if(s.db.instances > max_db_instances)
825 + max_db_instances = s.db.instances;
826 +
827 + if(s.db.contexts > max_db_contexts)
828 + max_db_contexts = s.db.contexts;
829 +
830 + if(s.ingest.replication.instances > max_collection_replication_instances)
831 + max_collection_replication_instances = s.ingest.replication.instances;
832 +
833 + if(s.stream.replication.instances > max_streaming_replication_instances)
834 + max_streaming_replication_instances = s.stream.replication.instances;
835 +
836 + for(int i = 0; i < STREAM_TRAFFIC_TYPE_MAX ;i++) {
837 + if (s.stream.sent_bytes_on_this_connection_per_type[i] >
838 + max_sent_bytes_on_this_connection_per_type[i])
839 + max_sent_bytes_on_this_connection_per_type[i] =
840 + s.stream.sent_bytes_on_this_connection_per_type[i];
841 + }
842 +
843 + // retention
844 + buffer_json_add_array_item_string(wb, rrdhost_hostname(s.host)); // Node
845 + buffer_json_add_array_item_uint64(wb, s.db.first_time_s * 1000); // dbFrom
846 + buffer_json_add_array_item_uint64(wb, s.db.last_time_s * 1000); // dbTo
847 +
848 + if(s.db.first_time_s && s.db.last_time_s && s.db.last_time_s > s.db.first_time_s)
849 + buffer_json_add_array_item_uint64(wb, s.db.last_time_s - s.db.first_time_s); // dbDuration
850 + else
851 + buffer_json_add_array_item_string(wb, NULL); // dbDuration
852 +
853 + buffer_json_add_array_item_uint64(wb, s.db.metrics); // dbMetrics
854 + buffer_json_add_array_item_uint64(wb, s.db.instances); // dbInstances
855 + buffer_json_add_array_item_uint64(wb, s.db.contexts); // dbContexts
856 +
857 + // statuses
858 + buffer_json_add_array_item_string(wb, rrdhost_ingest_status_to_string(s.ingest.status)); // InStatus
859 + buffer_json_add_array_item_string(wb, rrdhost_streaming_status_to_string(s.stream.status)); // OutStatus
860 + buffer_json_add_array_item_string(wb, rrdhost_ml_status_to_string(s.ml.status)); // MLStatus
861 +
862 + // collection
863 + if(s.ingest.since) {
864 + buffer_json_add_array_item_uint64(wb, s.ingest.since * 1000); // InSince
865 + buffer_json_add_array_item_time_t(wb, s.now - s.ingest.since); // InAge
866 + }
867 + else {
868 + buffer_json_add_array_item_string(wb, NULL); // InSince
869 + buffer_json_add_array_item_string(wb, NULL); // InAge
870 + }
871 + buffer_json_add_array_item_string(wb, s.ingest.reason); // InReason
872 + buffer_json_add_array_item_uint64(wb, s.ingest.hops); // InHops
873 + buffer_json_add_array_item_double(wb, s.ingest.replication.completion); // InReplCompletion
874 + buffer_json_add_array_item_uint64(wb, s.ingest.replication.instances); // InReplInstances
875 + buffer_json_add_array_item_string(wb, s.ingest.peers.local.ip); // InLocalIP
876 + buffer_json_add_array_item_uint64(wb, s.ingest.peers.local.port); // InLocalPort
877 + buffer_json_add_array_item_string(wb, s.ingest.peers.peer.ip); // InRemoteIP
878 + buffer_json_add_array_item_uint64(wb, s.ingest.peers.peer.port); // InRemotePort
879 + buffer_json_add_array_item_string(wb, s.ingest.ssl ? "SSL" : "PLAIN"); // InSSL
880 + stream_capabilities_to_json_array(wb, s.ingest.capabilities, NULL); // InCapabilities
881 +
882 + // streaming
883 + if(s.stream.since) {
884 + buffer_json_add_array_item_uint64(wb, s.stream.since * 1000); // OutSince
885 + buffer_json_add_array_item_time_t(wb, s.now - s.stream.since); // OutAge
886 + }
887 + else {
888 + buffer_json_add_array_item_string(wb, NULL); // OutSince
889 + buffer_json_add_array_item_string(wb, NULL); // OutAge
890 + }
891 + buffer_json_add_array_item_string(wb, s.stream.reason); // OutReason
892 + buffer_json_add_array_item_uint64(wb, s.stream.hops); // OutHops
893 + buffer_json_add_array_item_double(wb, s.stream.replication.completion); // OutReplCompletion
894 + buffer_json_add_array_item_uint64(wb, s.stream.replication.instances); // OutReplInstances
895 + buffer_json_add_array_item_string(wb, s.stream.peers.local.ip); // OutLocalIP
896 + buffer_json_add_array_item_uint64(wb, s.stream.peers.local.port); // OutLocalPort
897 + buffer_json_add_array_item_string(wb, s.stream.peers.peer.ip); // OutRemoteIP
898 + buffer_json_add_array_item_uint64(wb, s.stream.peers.peer.port); // OutRemotePort
899 + buffer_json_add_array_item_string(wb, s.stream.ssl ? "SSL" : "PLAIN"); // OutSSL
900 + buffer_json_add_array_item_string(wb, s.stream.compression ? "COMPRESSED" : "UNCOMPRESSED"); // OutCompression
901 + stream_capabilities_to_json_array(wb, s.stream.capabilities, NULL); // OutCapabilities
902 + buffer_json_add_array_item_uint64(wb, s.stream.sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_DATA]);
903 + buffer_json_add_array_item_uint64(wb, s.stream.sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_METADATA]);
904 + buffer_json_add_array_item_uint64(wb, s.stream.sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_REPLICATION]);
905 + buffer_json_add_array_item_uint64(wb, s.stream.sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_FUNCTIONS]);
906 +
907 + buffer_json_add_array_item_array(wb); // OutAttemptHandshake
908 + time_t last_attempt = 0;
909 + for(struct rrdpush_destinations *d = host->destinations; d ; d = d->next) {
910 + if(d->last_attempt > last_attempt)
911 + last_attempt = d->last_attempt;
912 +
913 + buffer_json_add_array_item_string(wb, stream_handshake_error_to_string(d->last_handshake));
914 + }
915 + buffer_json_array_close(wb); // // OutAttemptHandshake
916 +
917 + if(!last_attempt) {
918 + buffer_json_add_array_item_string(wb, NULL); // OutAttemptSince
919 + buffer_json_add_array_item_string(wb, NULL); // OutAttemptAge
920 + }
921 + else {
922 + buffer_json_add_array_item_uint64(wb, last_attempt * 1000); // OutAttemptSince
923 + buffer_json_add_array_item_time_t(wb, s.now - last_attempt); // OutAttemptAge
924 + }
925 +
926 + // ML
927 + if(s.ml.status == RRDHOST_ML_STATUS_RUNNING) {
928 + buffer_json_add_array_item_uint64(wb, s.ml.metrics.anomalous); // MlAnomalous
929 + buffer_json_add_array_item_uint64(wb, s.ml.metrics.normal); // MlNormal
930 + buffer_json_add_array_item_uint64(wb, s.ml.metrics.trained); // MlTrained
931 + buffer_json_add_array_item_uint64(wb, s.ml.metrics.pending); // MlPending
932 + buffer_json_add_array_item_uint64(wb, s.ml.metrics.silenced); // MlSilenced
933 +
934 + if(s.ml.metrics.anomalous > max_ml_anomalous)
935 + max_ml_anomalous = s.ml.metrics.anomalous;
936 +
937 + if(s.ml.metrics.normal > max_ml_normal)
938 + max_ml_normal = s.ml.metrics.normal;
939 +
940 + if(s.ml.metrics.trained > max_ml_trained)
941 + max_ml_trained = s.ml.metrics.trained;
942 +
943 + if(s.ml.metrics.pending > max_ml_pending)
944 + max_ml_pending = s.ml.metrics.pending;
945 +
946 + if(s.ml.metrics.silenced > max_ml_silenced)
947 + max_ml_silenced = s.ml.metrics.silenced;
948 +
949 + }
950 + else {
951 + buffer_json_add_array_item_string(wb, NULL); // MlAnomalous
952 + buffer_json_add_array_item_string(wb, NULL); // MlNormal
953 + buffer_json_add_array_item_string(wb, NULL); // MlTrained
954 + buffer_json_add_array_item_string(wb, NULL); // MlPending
955 + buffer_json_add_array_item_string(wb, NULL); // MlSilenced
956 + }
957 +
958 + // close
959 + buffer_json_array_close(wb);
960 + }
961 + dfe_done(host);
962 + }
963 + buffer_json_array_close(wb); // data
964 + buffer_json_member_add_object(wb, "columns");
965 + {
966 + size_t field_id = 0;
967 +
968 + // Node
969 + buffer_rrdf_table_add_field(wb, field_id++, "Node", "Node's Hostname",
970 + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
971 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
972 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
973 + RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY | RRDF_FIELD_OPTS_STICKY,
974 + NULL);
975 +
976 + buffer_rrdf_table_add_field(wb, field_id++, "dbFrom", "DB Data Retention From",
977 + RRDF_FIELD_TYPE_TIMESTAMP, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DATETIME,
978 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
979 + RRDF_FIELD_SUMMARY_MIN, RRDF_FIELD_FILTER_RANGE,
980 + RRDF_FIELD_OPTS_VISIBLE, NULL);
981 +
982 + buffer_rrdf_table_add_field(wb, field_id++, "dbTo", "DB Data Retention To",
983 + RRDF_FIELD_TYPE_TIMESTAMP, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DATETIME,
984 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
985 + RRDF_FIELD_SUMMARY_MAX, RRDF_FIELD_FILTER_RANGE,
986 + RRDF_FIELD_OPTS_VISIBLE, NULL);
987 +
988 + buffer_rrdf_table_add_field(wb, field_id++, "dbDuration", "DB Data Retention Duration",
989 + RRDF_FIELD_TYPE_DURATION, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DURATION,
990 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
991 + RRDF_FIELD_SUMMARY_MAX, RRDF_FIELD_FILTER_RANGE,
992 + RRDF_FIELD_OPTS_NONE, NULL);
993 +
994 + buffer_rrdf_table_add_field(wb, field_id++, "dbMetrics", "Time-series Metrics in the DB",
995 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
996 + 0, NULL, max_db_metrics, RRDF_FIELD_SORT_DESCENDING, NULL,
997 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
998 + RRDF_FIELD_OPTS_VISIBLE, NULL);
999 +
1000 + buffer_rrdf_table_add_field(wb, field_id++, "dbInstances", "Instances in the DB",
1001 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1002 + 0, NULL, max_db_instances, RRDF_FIELD_SORT_DESCENDING, NULL,
1003 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1004 + RRDF_FIELD_OPTS_VISIBLE, NULL);
1005 +
1006 + buffer_rrdf_table_add_field(wb, field_id++, "dbContexts", "Contexts in the DB",
1007 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1008 + 0, NULL, max_db_contexts, RRDF_FIELD_SORT_DESCENDING, NULL,
1009 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1010 + RRDF_FIELD_OPTS_VISIBLE, NULL);
1011 +
1012 + // --- statuses ---
1013 +
1014 + buffer_rrdf_table_add_field(wb, field_id++, "InStatus", "Data Collection Online Status",
1015 + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1016 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1017 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1018 + RRDF_FIELD_OPTS_VISIBLE, NULL);
1019 +
1020 +
1021 + buffer_rrdf_table_add_field(wb, field_id++, "OutStatus", "Streaming Online Status",
1022 + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1023 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1024 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1025 + RRDF_FIELD_OPTS_VISIBLE, NULL);
1026 +
1027 + buffer_rrdf_table_add_field(wb, field_id++, "MlStatus", "ML Status",
1028 + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1029 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1030 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1031 + RRDF_FIELD_OPTS_VISIBLE, NULL);
1032 +
1033 + // --- collection ---
1034 +
1035 + buffer_rrdf_table_add_field(wb, field_id++, "InSince", "Last Data Collection Status Change",
1036 + RRDF_FIELD_TYPE_TIMESTAMP, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DATETIME,
1037 + 0, NULL, NAN, RRDF_FIELD_SORT_DESCENDING, NULL,
1038 + RRDF_FIELD_SUMMARY_MIN, RRDF_FIELD_FILTER_RANGE,
1039 + RRDF_FIELD_OPTS_NONE, NULL);
1040 +
1041 + buffer_rrdf_table_add_field(wb, field_id++, "InAge", "Last Data Collection Online Status Change Age",
1042 + RRDF_FIELD_TYPE_DURATION, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DURATION,
1043 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1044 + RRDF_FIELD_SUMMARY_MAX, RRDF_FIELD_FILTER_RANGE,
1045 + RRDF_FIELD_OPTS_VISIBLE, NULL);
1046 +
1047 + buffer_rrdf_table_add_field(wb, field_id++, "InReason", "Data Collection Online Status Reason",
1048 + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1049 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1050 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1051 + RRDF_FIELD_OPTS_VISIBLE, NULL);
1052 +
1053 + buffer_rrdf_table_add_field(wb, field_id++, "InHops", "Data Collection Distance Hops from Origin Node",
1054 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1055 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1056 + RRDF_FIELD_SUMMARY_MIN, RRDF_FIELD_FILTER_RANGE,
1057 + RRDF_FIELD_OPTS_VISIBLE, NULL);
1058 +
1059 + buffer_rrdf_table_add_field(wb, field_id++, "InReplCompletion", "Inbound Replication Completion",
1060 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
1061 + 1, "%", 100.0, RRDF_FIELD_SORT_DESCENDING, NULL,
1062 + RRDF_FIELD_SUMMARY_MIN, RRDF_FIELD_FILTER_RANGE,
1063 + RRDF_FIELD_OPTS_VISIBLE, NULL);
1064 +
1065 + buffer_rrdf_table_add_field(wb, field_id++, "InReplInstances", "Inbound Replicating Instances",
1066 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1067 + 0, "instances", max_collection_replication_instances, RRDF_FIELD_SORT_DESCENDING,
1068 + NULL,
1069 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1070 + RRDF_FIELD_OPTS_NONE, NULL);
1071 +
1072 + buffer_rrdf_table_add_field(wb, field_id++, "InLocalIP", "Inbound Local IP",
1073 + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1074 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1075 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1076 + RRDF_FIELD_OPTS_NONE, NULL);
1077 +
1078 + buffer_rrdf_table_add_field(wb, field_id++, "InLocalPort", "Inbound Local Port",
1079 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1080 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1081 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_RANGE,
1082 + RRDF_FIELD_OPTS_NONE, NULL);
1083 +
1084 + buffer_rrdf_table_add_field(wb, field_id++, "InRemoteIP", "Inbound Remote IP",
1085 + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1086 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1087 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1088 + RRDF_FIELD_OPTS_NONE, NULL);
1089 +
1090 + buffer_rrdf_table_add_field(wb, field_id++, "InRemotePort", "Inbound Remote Port",
1091 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1092 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1093 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_RANGE,
1094 + RRDF_FIELD_OPTS_NONE, NULL);
1095 +
1096 + buffer_rrdf_table_add_field(wb, field_id++, "InSSL", "Inbound SSL Connection",
1097 + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1098 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1099 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1100 + RRDF_FIELD_OPTS_NONE, NULL);
1101 +
1102 + buffer_rrdf_table_add_field(wb, field_id++, "InCapabilities", "Inbound Connection Capabilities",
1103 + RRDF_FIELD_TYPE_ARRAY, RRDF_FIELD_VISUAL_PILL, RRDF_FIELD_TRANSFORM_NONE,
1104 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1105 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1106 + RRDF_FIELD_OPTS_NONE, NULL);
1107 +
1108 + // --- streaming ---
1109 +
1110 + buffer_rrdf_table_add_field(wb, field_id++, "OutSince", "Last Streaming Status Change",
1111 + RRDF_FIELD_TYPE_TIMESTAMP, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DATETIME,
1112 + 0, NULL, NAN, RRDF_FIELD_SORT_DESCENDING, NULL,
1113 + RRDF_FIELD_SUMMARY_MAX, RRDF_FIELD_FILTER_RANGE,
1114 + RRDF_FIELD_OPTS_NONE, NULL);
1115 +
1116 + buffer_rrdf_table_add_field(wb, field_id++, "OutAge", "Last Streaming Status Change Age",
1117 + RRDF_FIELD_TYPE_DURATION, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DURATION,
1118 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1119 + RRDF_FIELD_SUMMARY_MIN, RRDF_FIELD_FILTER_RANGE,
1120 + RRDF_FIELD_OPTS_VISIBLE, NULL);
1121 +
1122 + buffer_rrdf_table_add_field(wb, field_id++, "OutReason", "Streaming Status Reason",
1123 + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1124 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1125 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1126 + RRDF_FIELD_OPTS_VISIBLE, NULL);
1127 +
1128 + buffer_rrdf_table_add_field(wb, field_id++, "OutHops", "Streaming Distance Hops from Origin Node",
1129 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1130 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1131 + RRDF_FIELD_SUMMARY_MIN, RRDF_FIELD_FILTER_RANGE,
1132 + RRDF_FIELD_OPTS_VISIBLE, NULL);
1133 +
1134 + buffer_rrdf_table_add_field(wb, field_id++, "OutReplCompletion", "Outbound Replication Completion",
1135 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
1136 + 1, "%", 100.0, RRDF_FIELD_SORT_DESCENDING, NULL,
1137 + RRDF_FIELD_SUMMARY_MIN, RRDF_FIELD_FILTER_RANGE,
1138 + RRDF_FIELD_OPTS_VISIBLE, NULL);
1139 +
1140 + buffer_rrdf_table_add_field(wb, field_id++, "OutReplInstances", "Outbound Replicating Instances",
1141 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1142 + 0, "instances", max_streaming_replication_instances, RRDF_FIELD_SORT_DESCENDING,
1143 + NULL,
1144 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1145 + RRDF_FIELD_OPTS_NONE, NULL);
1146 +
1147 + buffer_rrdf_table_add_field(wb, field_id++, "OutLocalIP", "Outbound Local IP",
1148 + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1149 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1150 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1151 + RRDF_FIELD_OPTS_NONE, NULL);
1152 +
1153 + buffer_rrdf_table_add_field(wb, field_id++, "OutLocalPort", "Outbound Local Port",
1154 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1155 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1156 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_RANGE,
1157 + RRDF_FIELD_OPTS_NONE, NULL);
1158 +
1159 + buffer_rrdf_table_add_field(wb, field_id++, "OutRemoteIP", "Outbound Remote IP",
1160 + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1161 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1162 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1163 + RRDF_FIELD_OPTS_NONE, NULL);
1164 +
1165 + buffer_rrdf_table_add_field(wb, field_id++, "OutRemotePort", "Outbound Remote Port",
1166 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1167 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1168 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_RANGE,
1169 + RRDF_FIELD_OPTS_NONE, NULL);
1170 +
1171 + buffer_rrdf_table_add_field(wb, field_id++, "OutSSL", "Outbound SSL Connection",
1172 + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1173 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1174 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1175 + RRDF_FIELD_OPTS_NONE, NULL);
1176 +
1177 + buffer_rrdf_table_add_field(wb, field_id++, "OutCompression", "Outbound Compressed Connection",
1178 + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1179 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1180 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1181 + RRDF_FIELD_OPTS_NONE, NULL);
1182 +
1183 + buffer_rrdf_table_add_field(wb, field_id++, "OutCapabilities", "Outbound Connection Capabilities",
1184 + RRDF_FIELD_TYPE_ARRAY, RRDF_FIELD_VISUAL_PILL, RRDF_FIELD_TRANSFORM_NONE,
1185 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1186 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1187 + RRDF_FIELD_OPTS_NONE, NULL);
1188 +
1189 + buffer_rrdf_table_add_field(wb, field_id++, "OutTrafficData", "Outbound Metric Data Traffic",
1190 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1191 + 0, "bytes", max_sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_DATA],
1192 + RRDF_FIELD_SORT_DESCENDING, NULL,
1193 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1194 + RRDF_FIELD_OPTS_NONE, NULL);
1195 +
1196 + buffer_rrdf_table_add_field(wb, field_id++, "OutTrafficMetadata", "Outbound Metric Metadata Traffic",
1197 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1198 + 0, "bytes",
1199 + max_sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_METADATA],
1200 + RRDF_FIELD_SORT_DESCENDING, NULL,
1201 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1202 + RRDF_FIELD_OPTS_NONE, NULL);
1203 +
1204 + buffer_rrdf_table_add_field(wb, field_id++, "OutTrafficReplication", "Outbound Metric Replication Traffic",
1205 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1206 + 0, "bytes",
1207 + max_sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_REPLICATION],
1208 + RRDF_FIELD_SORT_DESCENDING, NULL,
1209 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1210 + RRDF_FIELD_OPTS_NONE, NULL);
1211 +
1212 + buffer_rrdf_table_add_field(wb, field_id++, "OutTrafficFunctions", "Outbound Metric Functions Traffic",
1213 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1214 + 0, "bytes",
1215 + max_sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_FUNCTIONS],
1216 + RRDF_FIELD_SORT_DESCENDING, NULL,
1217 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1218 + RRDF_FIELD_OPTS_NONE, NULL);
1219 +
1220 + buffer_rrdf_table_add_field(wb, field_id++, "OutAttemptHandshake",
1221 + "Outbound Connection Attempt Handshake Status",
1222 + RRDF_FIELD_TYPE_ARRAY, RRDF_FIELD_VISUAL_PILL, RRDF_FIELD_TRANSFORM_NONE,
1223 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1224 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1225 + RRDF_FIELD_OPTS_NONE, NULL);
1226 +
1227 + buffer_rrdf_table_add_field(wb, field_id++, "OutAttemptSince",
1228 + "Last Outbound Connection Attempt Status Change Time",
1229 + RRDF_FIELD_TYPE_TIMESTAMP, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DATETIME,
1230 + 0, NULL, NAN, RRDF_FIELD_SORT_DESCENDING, NULL,
1231 + RRDF_FIELD_SUMMARY_MAX, RRDF_FIELD_FILTER_RANGE,
1232 + RRDF_FIELD_OPTS_NONE, NULL);
1233 +
1234 + buffer_rrdf_table_add_field(wb, field_id++, "OutAttemptAge",
1235 + "Last Outbound Connection Attempt Status Change Age",
1236 + RRDF_FIELD_TYPE_DURATION, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DURATION,
1237 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1238 + RRDF_FIELD_SUMMARY_MIN, RRDF_FIELD_FILTER_RANGE,
1239 + RRDF_FIELD_OPTS_VISIBLE, NULL);
1240 +
1241 + // --- ML ---
1242 +
1243 + buffer_rrdf_table_add_field(wb, field_id++, "MlAnomalous", "Number of Anomalous Metrics",
1244 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1245 + 0, "metrics",
1246 + max_ml_anomalous,
1247 + RRDF_FIELD_SORT_DESCENDING, NULL,
1248 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1249 + RRDF_FIELD_OPTS_NONE, NULL);
1250 +
1251 + buffer_rrdf_table_add_field(wb, field_id++, "MlNormal", "Number of Not Anomalous Metrics",
1252 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1253 + 0, "metrics",
1254 + max_ml_normal,
1255 + RRDF_FIELD_SORT_DESCENDING, NULL,
1256 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1257 + RRDF_FIELD_OPTS_NONE, NULL);
1258 +
1259 + buffer_rrdf_table_add_field(wb, field_id++, "MlTrained", "Number of Trained Metrics",
1260 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1261 + 0, "metrics",
1262 + max_ml_trained,
1263 + RRDF_FIELD_SORT_DESCENDING, NULL,
1264 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1265 + RRDF_FIELD_OPTS_NONE, NULL);
1266 +
1267 + buffer_rrdf_table_add_field(wb, field_id++, "MlPending", "Number of Pending Metrics",
1268 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1269 + 0, "metrics",
1270 + max_ml_pending,
1271 + RRDF_FIELD_SORT_DESCENDING, NULL,
1272 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1273 + RRDF_FIELD_OPTS_NONE, NULL);
1274 +
1275 + buffer_rrdf_table_add_field(wb, field_id++, "MlSilenced", "Number of Silenced Metrics",
1276 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1277 + 0, "metrics",
1278 + max_ml_silenced,
1279 + RRDF_FIELD_SORT_DESCENDING, NULL,
1280 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1281 + RRDF_FIELD_OPTS_NONE, NULL);
1282 + }
1283 + buffer_json_object_close(wb); // columns
1284 + buffer_json_member_add_string(wb, "default_sort_column", "Node");
1285 + buffer_json_member_add_object(wb, "charts");
1286 + {
1287 + // Data Collection Age chart
1288 + buffer_json_member_add_object(wb, "InAge");
1289 + {
1290 + buffer_json_member_add_string(wb, "name", "Data Collection Age");
1291 + buffer_json_member_add_string(wb, "type", "stacked-bar");
1292 + buffer_json_member_add_array(wb, "columns");
1293 + {
1294 + buffer_json_add_array_item_string(wb, "InAge");
1295 + }
1296 + buffer_json_array_close(wb);
1297 + }
1298 + buffer_json_object_close(wb);
1299 +
1300 + // Streaming Age chart
1301 + buffer_json_member_add_object(wb, "OutAge");
1302 + {
1303 + buffer_json_member_add_string(wb, "name", "Streaming Age");
1304 + buffer_json_member_add_string(wb, "type", "stacked-bar");
1305 + buffer_json_member_add_array(wb, "columns");
1306 + {
1307 + buffer_json_add_array_item_string(wb, "OutAge");
1308 + }
1309 + buffer_json_array_close(wb);
1310 + }
1311 + buffer_json_object_close(wb);
1312 +
1313 + // DB Duration
1314 + buffer_json_member_add_object(wb, "dbDuration");
1315 + {
1316 + buffer_json_member_add_string(wb, "name", "Retention Duration");
1317 + buffer_json_member_add_string(wb, "type", "stacked-bar");
1318 + buffer_json_member_add_array(wb, "columns");
1319 + {
1320 + buffer_json_add_array_item_string(wb, "dbDuration");
1321 + }
1322 + buffer_json_array_close(wb);
1323 + }
1324 + buffer_json_object_close(wb);
1325 + }
1326 + buffer_json_object_close(wb); // charts
1327 +
1328 + buffer_json_member_add_array(wb, "default_charts");
1329 + {
1330 + buffer_json_add_array_item_array(wb);
1331 + buffer_json_add_array_item_string(wb, "InAge");
1332 + buffer_json_add_array_item_string(wb, "Node");
1333 + buffer_json_array_close(wb);
1334 +
1335 + buffer_json_add_array_item_array(wb);
1336 + buffer_json_add_array_item_string(wb, "OutAge");
1337 + buffer_json_add_array_item_string(wb, "Node");
1338 + buffer_json_array_close(wb);
1339 + }
1340 + buffer_json_array_close(wb);
1341 +
1342 + buffer_json_member_add_object(wb, "group_by");
1343 + {
1344 + buffer_json_member_add_object(wb, "Node");
1345 + {
1346 + buffer_json_member_add_string(wb, "name", "Node");
1347 + buffer_json_member_add_array(wb, "columns");
1348 + {
1349 + buffer_json_add_array_item_string(wb, "Node");
1350 + }
1351 + buffer_json_array_close(wb);
1352 + }
1353 + buffer_json_object_close(wb);
1354 +
1355 + buffer_json_member_add_object(wb, "InStatus");
1356 + {
1357 + buffer_json_member_add_string(wb, "name", "Nodes by Collection Status");
1358 + buffer_json_member_add_array(wb, "columns");
1359 + {
1360 + buffer_json_add_array_item_string(wb, "InStatus");
1361 + }
1362 + buffer_json_array_close(wb);
1363 + }
1364 + buffer_json_object_close(wb);
1365 +
1366 + buffer_json_member_add_object(wb, "OutStatus");
1367 + {
1368 + buffer_json_member_add_string(wb, "name", "Nodes by Streaming Status");
1369 + buffer_json_member_add_array(wb, "columns");
1370 + {
1371 + buffer_json_add_array_item_string(wb, "OutStatus");
1372 + }
1373 + buffer_json_array_close(wb);
1374 + }
1375 + buffer_json_object_close(wb);
1376 +
1377 + buffer_json_member_add_object(wb, "MlStatus");
1378 + {
1379 + buffer_json_member_add_string(wb, "name", "Nodes by ML Status");
1380 + buffer_json_member_add_array(wb, "columns");
1381 + {
1382 + buffer_json_add_array_item_string(wb, "MlStatus");
1383 + }
1384 + buffer_json_array_close(wb);
1385 + }
1386 + buffer_json_object_close(wb);
1387 +
1388 + buffer_json_member_add_object(wb, "InRemoteIP");
1389 + {
1390 + buffer_json_member_add_string(wb, "name", "Nodes by Inbound IP");
1391 + buffer_json_member_add_array(wb, "columns");
1392 + {
1393 + buffer_json_add_array_item_string(wb, "InRemoteIP");
1394 + }
1395 + buffer_json_array_close(wb);
1396 + }
1397 + buffer_json_object_close(wb);
1398 +
1399 + buffer_json_member_add_object(wb, "OutRemoteIP");
1400 + {
1401 + buffer_json_member_add_string(wb, "name", "Nodes by Outbound IP");
1402 + buffer_json_member_add_array(wb, "columns");
1403 + {
1404 + buffer_json_add_array_item_string(wb, "OutRemoteIP");
1405 + }
1406 + buffer_json_array_close(wb);
1407 + }
1408 + buffer_json_object_close(wb);
1409 + }
1410 + buffer_json_object_close(wb); // group_by
1411 +
1412 + buffer_json_member_add_time_t(wb, "expires", now_realtime_sec() + 1);
1413 + buffer_json_finalize(wb);
1414 +
1415 + if(callback)
1416 + callback(wb, HTTP_RESP_OK, callback_data);
1417 +
1418 + return HTTP_RESP_OK;
1419 +}
database/rrdfunctions.h
+6
@@ -23,6 +23,7 @@ typedef void (*rrd_call_function_async_callback)(BUFFER *wb, int code, void *cal
23 int rrd_call_function_async(RRDHOST *host, BUFFER *wb, int timeout, const char *name, rrd_call_function_async_callback, void *callback_data);
24
25 void rrd_functions_expose_rrdpush(RRDSET *st, BUFFER *wb);
26 +void rrd_functions_expose_global_rrdpush(RRDHOST *host, BUFFER *wb);
27
28 void chart_functions2json(RRDSET *st, BUFFER *wb, int tabs, const char *kq, const char *sq);
29 void chart_functions_to_dict(DICTIONARY *rrdset_functions_view, DICTIONARY *dst);
@@ -32,4 +33,9 @@ uint8_t functions_format_to_content_type(const char *format);
33 const char *functions_content_type_to_format(HTTP_CONTENT_TYPE content_type);
34 int rrd_call_function_error(BUFFER *wb, const char *msg, int code);
35
36 +int rrdhost_function_streaming(BUFFER *wb, int timeout, const char *function, void *collector_data,
37 + function_data_ready_callback callback, void *callback_data);
38 +
39 +#define RRDFUNCTIONS_STREAMING_HELP "Streaming status for parents and children."
40 +
41 #endif // NETDATA_RRDFUNCTIONS_H
database/rrdhost.c
+252 -4
@@ -42,6 +42,7 @@ bool is_storage_engine_shared(STORAGE_INSTANCE *engine __maybe_unused) {
42 }
43
44 RRDHOST *find_host_by_node_id(char *node_id) {
45 +
46 uuid_t node_uuid;
47 if (unlikely(!node_id || uuid_parse(node_id, node_uuid)))
48 return NULL;
@@ -1035,6 +1036,13 @@ int rrd_init(char *hostname, struct rrdhost_system_info *system_info, bool unitt
1036 return 1;
1037 }
1038
1039 + // we register this only on localhost
1040 + // for the other nodes, the origin server should register it
1041 + rrd_collector_started(); // this creates a collector that runs for as long as netdata runs
1042 + rrd_collector_add_function(localhost, NULL, "streaming", 10,
1043 + RRDFUNCTIONS_STREAMING_HELP, true,
1044 + rrdhost_function_streaming, NULL);
1045 +
1046 if (likely(system_info)) {
1047 migrate_localhost(&localhost->host_uuid);
1048 sql_aclk_sync_init();
@@ -1094,7 +1102,7 @@ static void rrdhost_streaming_sender_structures_init(RRDHOST *host)
1102
1103 host->sender->host = host;
1104 host->sender->buffer = cbuffer_new(CBUFFER_INITIAL_SIZE, 1024 * 1024, &netdata_buffers_statistics.cbuffers_streaming);
1097 - host->sender->capabilities = stream_our_capabilities();
1105 + host->sender->capabilities = stream_our_capabilities(host, true);
1106
1107 host->sender->rrdpush_sender_pipe[PIPE_READ] = -1;
1108 host->sender->rrdpush_sender_pipe[PIPE_WRITE] = -1;
@@ -1378,15 +1386,16 @@ static void rrdhost_load_auto_labels(void) {
1386 rrdlabels_add(labels, "_streams_to", localhost->rrdpush_send_destination, RRDLABEL_SRC_AUTO);
1387 }
1388
1381 -void rrdhost_set_is_parent_label(int count) {
1382 - DICTIONARY *labels = localhost->rrdlabels;
1389 +void rrdhost_set_is_parent_label(void) {
1390 + int count = __atomic_load_n(&localhost->connected_children_count, __ATOMIC_RELAXED);
1391
1392 if (count == 0 || count == 1) {
1393 + DICTIONARY *labels = localhost->rrdlabels;
1394 rrdlabels_add(labels, "_is_parent", (count) ? "true" : "false", RRDLABEL_SRC_AUTO);
1395
1396 //queue a node info
1397 #ifdef ENABLE_ACLK
1389 - if (netdata_cloud_setting) {
1398 + if (netdata_cloud_enabled) {
1399 aclk_queue_node_info(localhost, false);
1400 }
1401 #endif
@@ -1687,3 +1696,242 @@ int rrdhost_set_system_info_variable(struct rrdhost_system_info *system_info, ch
1696
1697 return res;
1698 }
1699 +
1700 +static NETDATA_DOUBLE rrdhost_sender_replication_completion_unsafe(RRDHOST *host, time_t now, size_t *instances) {
1701 + size_t charts = rrdhost_sender_replicating_charts(host);
1702 + NETDATA_DOUBLE completion;
1703 + if(!charts || !host->sender || !host->sender->replication.oldest_request_after_t)
1704 + completion = 100.0;
1705 + else if(!host->sender->replication.latest_completed_before_t || host->sender->replication.latest_completed_before_t < host->sender->replication.oldest_request_after_t)
1706 + completion = 0.0;
1707 + else {
1708 + time_t total = now - host->sender->replication.oldest_request_after_t;
1709 + time_t current = host->sender->replication.latest_completed_before_t - host->sender->replication.oldest_request_after_t;
1710 + completion = (NETDATA_DOUBLE) current * 100.0 / (NETDATA_DOUBLE) total;
1711 + }
1712 +
1713 + *instances = charts;
1714 +
1715 + return completion;
1716 +}
1717 +
1718 +bool rrdhost_matches_window(RRDHOST *host, time_t after, time_t before, time_t now) {
1719 + time_t first_time_s, last_time_s;
1720 + rrdhost_retention(host, now, rrdhost_is_online(host), &first_time_s, &last_time_s);
1721 + return query_matches_retention(after, before, first_time_s, last_time_s, 0);
1722 +}
1723 +
1724 +bool rrdhost_state_cloud_emulation(RRDHOST *host) {
1725 + return rrdhost_is_online(host);
1726 +}
1727 +
1728 +void rrdhost_status(RRDHOST *host, time_t now, RRDHOST_STATUS *s) {
1729 + memset(s, 0, sizeof(*s));
1730 +
1731 + s->host = host;
1732 + s->now = now;
1733 +
1734 + RRDHOST_FLAGS flags = __atomic_load_n(&host->flags, __ATOMIC_RELAXED);
1735 +
1736 + // --- db ---
1737 +
1738 + bool online = rrdhost_is_online(host);
1739 +
1740 + rrdhost_retention(host, now, online, &s->db.first_time_s, &s->db.last_time_s);
1741 + s->db.metrics = host->rrdctx.metrics;
1742 + s->db.instances = host->rrdctx.instances;
1743 + s->db.contexts = dictionary_entries(host->rrdctx.contexts);
1744 + if(!s->db.first_time_s || !s->db.last_time_s || !s->db.metrics || !s->db.instances || !s->db.contexts ||
1745 + (flags & (RRDHOST_FLAG_PENDING_CONTEXT_LOAD|RRDHOST_FLAG_CONTEXT_LOAD_IN_PROGRESS)))
1746 + s->db.status = RRDHOST_DB_STATUS_INITIALIZING;
1747 + else
1748 + s->db.status = RRDHOST_DB_STATUS_QUERYABLE;
1749 +
1750 + s->db.mode = host->rrd_memory_mode;
1751 +
1752 + // --- ingest ---
1753 +
1754 + s->ingest.since = MAX(host->child_connect_time, host->child_disconnected_time);
1755 + s->ingest.reason = (online) ? "" : host->rrdpush_last_receiver_exit_reason;
1756 +
1757 + netdata_mutex_lock(&host->receiver_lock);
1758 + s->ingest.hops = (host->system_info ? host->system_info->hops : (host == localhost) ? 0 : 1);
1759 + bool has_receiver = false;
1760 + if (host->receiver) {
1761 + has_receiver = true;
1762 + s->ingest.replication.instances = rrdhost_receiver_replicating_charts(host);
1763 + s->ingest.replication.completion = host->rrdpush_receiver_replication_percent;
1764 + s->ingest.replication.in_progress = s->ingest.replication.instances > 0;
1765 +
1766 + s->ingest.capabilities = host->receiver->capabilities;
1767 + s->ingest.peers = socket_peers(host->receiver->fd);
1768 +#ifdef ENABLE_HTTPS
1769 + s->ingest.ssl = SSL_connection(&host->receiver->ssl);
1770 +#endif
1771 + }
1772 + netdata_mutex_unlock(&host->receiver_lock);
1773 +
1774 + if (online) {
1775 + if(s->db.status == RRDHOST_DB_STATUS_INITIALIZING)
1776 + s->ingest.status = RRDHOST_INGEST_STATUS_INITIALIZING;
1777 +
1778 + else if (host == localhost || rrdhost_option_check(host, RRDHOST_OPTION_VIRTUAL_HOST)) {
1779 + s->ingest.status = RRDHOST_INGEST_STATUS_ONLINE;
1780 + s->ingest.since = netdata_start_time;
1781 + }
1782 +
1783 + else if (s->ingest.replication.in_progress)
1784 + s->ingest.status = RRDHOST_INGEST_STATUS_REPLICATING;
1785 +
1786 + else
1787 + s->ingest.status = RRDHOST_INGEST_STATUS_ONLINE;
1788 + }
1789 + else {
1790 + if (!s->ingest.since) {
1791 + s->ingest.status = RRDHOST_INGEST_STATUS_ARCHIVED;
1792 + s->ingest.since = s->db.last_time_s;
1793 + }
1794 +
1795 + else
1796 + s->ingest.status = RRDHOST_INGEST_STATUS_OFFLINE;
1797 + }
1798 +
1799 + if(host == localhost)
1800 + s->ingest.type = RRDHOST_INGEST_TYPE_LOCALHOST;
1801 + else if(has_receiver || rrdhost_flag_set(host, RRDHOST_FLAG_RRDPUSH_RECEIVER_DISCONNECTED))
1802 + s->ingest.type = RRDHOST_INGEST_TYPE_CHILD;
1803 + else if(rrdhost_option_check(host, RRDHOST_OPTION_VIRTUAL_HOST))
1804 + s->ingest.type = RRDHOST_INGEST_TYPE_VIRTUAL;
1805 + else
1806 + s->ingest.type = RRDHOST_INGEST_TYPE_ARCHIVED;
1807 +
1808 + s->ingest.id = host->rrdpush_receiver_connection_counter;
1809 +
1810 + if(!s->ingest.since)
1811 + s->ingest.since = netdata_start_time;
1812 +
1813 + if(!s->ingest.reason)
1814 + s->ingest.reason = "";
1815 +
1816 + if(s->ingest.status == RRDHOST_INGEST_STATUS_ONLINE)
1817 + s->db.liveness = RRDHOST_DB_LIVENESS_LIVE;
1818 + else
1819 + s->db.liveness = RRDHOST_DB_LIVENESS_STALE;
1820 +
1821 + // --- stream ---
1822 +
1823 + if (!host->sender) {
1824 + s->stream.status = RRDHOST_STREAM_STATUS_DISABLED;
1825 + s->stream.hops = s->ingest.hops + 1;
1826 + }
1827 + else {
1828 + netdata_mutex_lock(&host->sender->mutex);
1829 +
1830 + s->stream.since = host->sender->last_state_since_t;
1831 + s->stream.peers = socket_peers(host->sender->rrdpush_sender_socket);
1832 + s->stream.ssl = SSL_connection(&host->sender->ssl);
1833 +
1834 + memcpy(s->stream.sent_bytes_on_this_connection_per_type,
1835 + host->sender->sent_bytes_on_this_connection_per_type,
1836 + MIN(sizeof(s->stream.sent_bytes_on_this_connection_per_type),
1837 + sizeof(host->sender->sent_bytes_on_this_connection_per_type)));
1838 +
1839 + if (rrdhost_flag_check(host, RRDHOST_FLAG_RRDPUSH_SENDER_CONNECTED)) {
1840 + s->stream.hops = host->sender->hops;
1841 + s->stream.reason = "";
1842 + s->stream.capabilities = host->sender->capabilities;
1843 +
1844 + s->stream.replication.completion = rrdhost_sender_replication_completion_unsafe(host, now, &s->stream.replication.instances);
1845 + s->stream.replication.in_progress = s->stream.replication.instances > 0;
1846 +
1847 + if(s->stream.replication.in_progress)
1848 + s->stream.status = RRDHOST_STREAM_STATUS_REPLICATING;
1849 + else
1850 + s->stream.status = RRDHOST_STREAM_STATUS_ONLINE;
1851 +
1852 +#ifdef ENABLE_COMPRESSION
1853 + s->stream.compression = (stream_has_capability(host->sender, STREAM_CAP_COMPRESSION) && host->sender->compressor);
1854 +#endif
1855 + }
1856 + else {
1857 + s->stream.status = RRDHOST_STREAM_STATUS_OFFLINE;
1858 + s->stream.hops = s->ingest.hops + 1;
1859 + s->stream.reason = host->sender->exit.reason;
1860 + }
1861 +
1862 + netdata_mutex_unlock(&host->sender->mutex);
1863 + }
1864 +
1865 + s->stream.id = host->rrdpush_sender_connection_counter;
1866 +
1867 + if(!s->stream.since)
1868 + s->stream.since = netdata_start_time;
1869 +
1870 + if(!s->stream.reason)
1871 + s->stream.reason = "";
1872 +
1873 + // --- ml ---
1874 +
1875 + if(ml_host_get_host_status(host, &s->ml.metrics)) {
1876 + s->ml.type = RRDHOST_ML_TYPE_SELF;
1877 +
1878 + if(s->ingest.status == RRDHOST_INGEST_STATUS_OFFLINE || s->ingest.status == RRDHOST_INGEST_STATUS_ARCHIVED)
1879 + s->ml.status = RRDHOST_ML_STATUS_OFFLINE;
1880 + else
1881 + s->ml.status = RRDHOST_ML_STATUS_RUNNING;
1882 + }
1883 + else if(stream_has_capability(&s->ingest, STREAM_CAP_DATA_WITH_ML)) {
1884 + s->ml.type = RRDHOST_ML_TYPE_RECEIVED;
1885 + s->ml.status = RRDHOST_ML_STATUS_RUNNING;
1886 + }
1887 + else {
1888 + // does not receive ML, does not run ML
1889 + s->ml.type = RRDHOST_ML_TYPE_DISABLED;
1890 + s->ml.status = RRDHOST_ML_STATUS_DISABLED;
1891 + }
1892 +
1893 + // --- health ---
1894 +
1895 + if(host->health.health_enabled) {
1896 + if(flags & RRDHOST_FLAG_PENDING_HEALTH_INITIALIZATION)
1897 + s->health.status = RRDHOST_HEALTH_STATUS_INITIALIZING;
1898 + else {
1899 + s->health.status = RRDHOST_HEALTH_STATUS_RUNNING;
1900 +
1901 + RRDCALC *rc;
1902 + foreach_rrdcalc_in_rrdhost_read(host, rc) {
1903 + if (unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec))
1904 + continue;
1905 +
1906 + switch (rc->status) {
1907 + default:
1908 + case RRDCALC_STATUS_REMOVED:
1909 + break;
1910 +
1911 + case RRDCALC_STATUS_CLEAR:
1912 + s->health.alerts.clear++;
1913 + break;
1914 +
1915 + case RRDCALC_STATUS_WARNING:
1916 + s->health.alerts.warning++;
1917 + break;
1918 +
1919 + case RRDCALC_STATUS_CRITICAL:
1920 + s->health.alerts.critical++;
1921 + break;
1922 +
1923 + case RRDCALC_STATUS_UNDEFINED:
1924 + s->health.alerts.undefined++;
1925 + break;
1926 +
1927 + case RRDCALC_STATUS_UNINITIALIZED:
1928 + s->health.alerts.uninitialized++;
1929 + break;
1930 + }
1931 + }
1932 + foreach_rrdcalc_in_rrdhost_done(rc);
1933 + }
1934 + }
1935 + else
1936 + s->health.status = RRDHOST_HEALTH_STATUS_DISABLED;
1937 +}
database/sqlite/sqlite_health.c
+1 -1
@@ -363,7 +363,7 @@ void sql_health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae)
363 else {
364 sql_health_alarm_log_insert(host, ae);
365 #ifdef ENABLE_ACLK
366 - if (netdata_cloud_setting) {
366 + if (netdata_cloud_enabled) {
367 sql_queue_alarm_to_aclk(host, ae, 0);
368 }
369 #endif
health/health.c
+4 -4
@@ -349,7 +349,7 @@ static void health_reload_host(RRDHOST *host) {
349 rrdset_foreach_done(st);
350
351 #ifdef ENABLE_ACLK
352 - if (netdata_cloud_setting) {
352 + if (netdata_cloud_enabled) {
353 struct aclk_sync_host_config *wc = (struct aclk_sync_host_config *)host->aclk_sync_host_config;
354 if (likely(wc)) {
355 wc->alert_queue_removed = SEND_REMOVED_AFTER_HEALTH_LOOPS;
@@ -905,7 +905,7 @@ static int update_disabled_silenced(RRDHOST *host, RRDCALC *rc) {
905
906 static void sql_health_postpone_queue_removed(RRDHOST *host __maybe_unused) {
907 #ifdef ENABLE_ACLK
908 - if (netdata_cloud_setting) {
908 + if (netdata_cloud_enabled) {
909 struct aclk_sync_host_config *wc = (struct aclk_sync_host_config *)host->aclk_sync_host_config;
910 if (unlikely(!wc)) {
911 return;
@@ -1131,7 +1131,7 @@ void *health_main(void *ptr) {
1131 rc->value = NAN;
1132
1133 #ifdef ENABLE_ACLK
1134 - if (netdata_cloud_setting)
1134 + if (netdata_cloud_enabled)
1135 sql_queue_alarm_to_aclk(host, ae, 1);
1136 #endif
1137 }
@@ -1503,7 +1503,7 @@ void *health_main(void *ptr) {
1503 break;
1504 }
1505 #ifdef ENABLE_ACLK
1506 - if (netdata_cloud_setting) {
1506 + if (netdata_cloud_enabled) {
1507 struct aclk_sync_host_config *wc = (struct aclk_sync_host_config *)host->aclk_sync_host_config;
1508 if (unlikely(!wc)) {
1509 continue;
libnetdata/buffer/buffer.h
+195
@@ -921,4 +921,199 @@ static inline void buffer_json_array_close(BUFFER *wb) {
921 _buffer_json_depth_pop(wb);
922 }
923
924 +typedef enum __attribute__((packed)) {
925 + RRDF_FIELD_OPTS_NONE = 0,
926 + RRDF_FIELD_OPTS_UNIQUE_KEY = (1 << 0), // the field is the unique key of the row
927 + RRDF_FIELD_OPTS_VISIBLE = (1 << 1), // the field should be visible by default
928 + RRDF_FIELD_OPTS_STICKY = (1 << 2), // the field should be sticky
929 +} RRDF_FIELD_OPTIONS;
930 +
931 +typedef enum __attribute__((packed)) {
932 + RRDF_FIELD_TYPE_INTEGER,
933 + RRDF_FIELD_TYPE_STRING,
934 + RRDF_FIELD_TYPE_DETAIL_STRING,
935 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
936 + RRDF_FIELD_TYPE_DURATION,
937 + RRDF_FIELD_TYPE_TIMESTAMP,
938 + RRDF_FIELD_TYPE_ARRAY,
939 +} RRDF_FIELD_TYPE;
940 +
941 +static inline const char *rrdf_field_type_to_string(RRDF_FIELD_TYPE type) {
942 + switch(type) {
943 + default:
944 + case RRDF_FIELD_TYPE_INTEGER:
945 + return "integer";
946 +
947 + case RRDF_FIELD_TYPE_STRING:
948 + return "string";
949 +
950 + case RRDF_FIELD_TYPE_DETAIL_STRING:
951 + return "detail-string";
952 +
953 + case RRDF_FIELD_TYPE_BAR_WITH_INTEGER:
954 + return "bar-with-integer";
955 +
956 + case RRDF_FIELD_TYPE_DURATION:
957 + return "duration";
958 +
959 + case RRDF_FIELD_TYPE_TIMESTAMP:
960 + return "timestamp";
961 +
962 + case RRDF_FIELD_TYPE_ARRAY:
963 + return "array";
964 + }
965 +}
966 +
967 +typedef enum __attribute__((packed)) {
968 + RRDF_FIELD_VISUAL_VALUE, // show the value, possibly applying a transformation
969 + RRDF_FIELD_VISUAL_BAR, // show the value and a bar, respecting the max field to fill the bar at 100%
970 + RRDF_FIELD_VISUAL_PILL, // array of values (transformation is respected)
971 +} RRDF_FIELD_VISUAL;
972 +
973 +static inline const char *rrdf_field_visual_to_string(RRDF_FIELD_VISUAL visual) {
974 + switch(visual) {
975 + default:
976 + case RRDF_FIELD_VISUAL_VALUE:
977 + return "value";
978 +
979 + case RRDF_FIELD_VISUAL_BAR:
980 + return "bar";
981 +
982 + case RRDF_FIELD_VISUAL_PILL:
983 + return "pill";
984 + }
985 +}
986 +
987 +typedef enum __attribute__((packed)) {
988 + RRDF_FIELD_TRANSFORM_NONE, // show the value as-is
989 + RRDF_FIELD_TRANSFORM_NUMBER, // show the value repsecting the decimal_points
990 + RRDF_FIELD_TRANSFORM_DURATION, // transform as duration in second to a human readable duration
991 + RRDF_FIELD_TRANSFORM_DATETIME, // UNIX epoch timestamp in ms
992 +} RRDF_FIELD_TRANSFORM;
993 +
994 +static inline const char *rrdf_field_transform_to_string(RRDF_FIELD_TRANSFORM transform) {
995 + switch(transform) {
996 + default:
997 + case RRDF_FIELD_TRANSFORM_NONE:
998 + return "none";
999 +
1000 + case RRDF_FIELD_TRANSFORM_NUMBER:
1001 + return "number";
1002 +
1003 + case RRDF_FIELD_TRANSFORM_DURATION:
1004 + return "duration";
1005 +
1006 + case RRDF_FIELD_TRANSFORM_DATETIME:
1007 + return "datetime";
1008 + }
1009 +}
1010 +
1011 +typedef enum __attribute__((packed)) {
1012 + RRDF_FIELD_SORT_ASCENDING = (1 << 0),
1013 + RRDF_FIELD_SORT_DESCENDING = (1 << 1),
1014 +
1015 + RRDF_FIELD_SORT_FIXED = (1 << 7),
1016 +} RRDF_FIELD_SORT;
1017 +
1018 +static inline const char *rrdf_field_sort_to_string(RRDF_FIELD_SORT sort) {
1019 + if(sort & RRDF_FIELD_SORT_DESCENDING)
1020 + return "descending";
1021 +
1022 + else
1023 + return "ascending";
1024 +}
1025 +
1026 +typedef enum __attribute__((packed)) {
1027 + RRDF_FIELD_SUMMARY_UNIQUECOUNT, // Finds the number of unique values of a group of rows
1028 + RRDF_FIELD_SUMMARY_SUM, // Sums the values of a group of rows
1029 + RRDF_FIELD_SUMMARY_MIN, // Finds the minimum value of a group of rows
1030 + RRDF_FIELD_SUMMARY_MAX, // Finds the maximum value of a group of rows
1031 + // RRDF_FIELD_SUMMARY_EXTENT, // Finds the minimum and maximum values of a group of rows
1032 + RRDF_FIELD_SUMMARY_MEAN, // Finds the mean/average value of a group of rows
1033 + RRDF_FIELD_SUMMARY_MEDIAN, // Finds the median value of a group of rows
1034 + // RRDF_FIELD_SUMMARY_UNIQUE, // Finds the unique values of a group of rows
1035 + RRDF_FIELD_SUMMARY_COUNT, // Calculates the number of rows in a group
1036 +} RRDF_FIELD_SUMMARY;
1037 +
1038 +static inline const char *rrdf_field_summary_to_string(RRDF_FIELD_SUMMARY summary) {
1039 + switch(summary) {
1040 + default:
1041 + case RRDF_FIELD_SUMMARY_COUNT:
1042 + return "count";
1043 +
1044 + case RRDF_FIELD_SUMMARY_UNIQUECOUNT:
1045 + return "uniqueCount";
1046 +
1047 + case RRDF_FIELD_SUMMARY_SUM:
1048 + return "sum";
1049 +
1050 + case RRDF_FIELD_SUMMARY_MIN:
1051 + return "min";
1052 +
1053 + case RRDF_FIELD_SUMMARY_MEAN:
1054 + return "mean";
1055 +
1056 + case RRDF_FIELD_SUMMARY_MEDIAN:
1057 + return "median";
1058 +
1059 + case RRDF_FIELD_SUMMARY_MAX:
1060 + return "max";
1061 + }
1062 +}
1063 +
1064 +typedef enum __attribute__((packed)) {
1065 + RRDF_FIELD_FILTER_RANGE,
1066 + RRDF_FIELD_FILTER_MULTISELECT,
1067 +} RRDF_FIELD_FILTER;
1068 +
1069 +static inline const char *rrdf_field_filter_to_string(RRDF_FIELD_FILTER filter) {
1070 + switch(filter) {
1071 + default:
1072 + case RRDF_FIELD_FILTER_RANGE:
1073 + return "range";
1074 +
1075 + case RRDF_FIELD_FILTER_MULTISELECT:
1076 + return "multiselect";
1077 + }
1078 +}
1079 +
1080 +static inline void
1081 +buffer_rrdf_table_add_field(BUFFER *wb, size_t field_id, const char *key, const char *name, RRDF_FIELD_TYPE type,
1082 + RRDF_FIELD_VISUAL visual, RRDF_FIELD_TRANSFORM transform, size_t decimal_points,
1083 + const char *units, NETDATA_DOUBLE max, RRDF_FIELD_SORT sort, const char *pointer_to,
1084 + RRDF_FIELD_SUMMARY summary, RRDF_FIELD_FILTER filter, RRDF_FIELD_OPTIONS options,
1085 + const char *default_value) {
1086 +
1087 + buffer_json_member_add_object(wb, key);
1088 + {
1089 + buffer_json_member_add_uint64(wb, "index", field_id);
1090 + buffer_json_member_add_boolean(wb, "unique_key", options & RRDF_FIELD_OPTS_UNIQUE_KEY);
1091 + buffer_json_member_add_string(wb, "name", name);
1092 + buffer_json_member_add_boolean(wb, "visible", options & RRDF_FIELD_OPTS_VISIBLE);
1093 + buffer_json_member_add_string(wb, "type", rrdf_field_type_to_string(type));
1094 + buffer_json_member_add_string_or_omit(wb, "units", units);
1095 + buffer_json_member_add_string(wb, "visualization", rrdf_field_visual_to_string(visual));
1096 +
1097 + buffer_json_member_add_object(wb, "value_options");
1098 + {
1099 + buffer_json_member_add_string_or_omit(wb, "units", units);
1100 + buffer_json_member_add_string(wb, "transform", rrdf_field_transform_to_string(transform));
1101 + buffer_json_member_add_uint64(wb, "decimal_points", decimal_points);
1102 + buffer_json_member_add_string(wb, "default_value", default_value);
1103 + }
1104 + buffer_json_object_close(wb);
1105 +
1106 + if (!isnan((NETDATA_DOUBLE) (max)))
1107 + buffer_json_member_add_double(wb, "max", (NETDATA_DOUBLE) (max));
1108 +
1109 + buffer_json_member_add_string_or_omit(wb, "pointer_to", pointer_to);
1110 + buffer_json_member_add_string(wb, "sort", rrdf_field_sort_to_string(sort));
1111 + buffer_json_member_add_boolean(wb, "sortable", !(sort & RRDF_FIELD_SORT_FIXED));
1112 + buffer_json_member_add_boolean(wb, "sticky", options & RRDF_FIELD_OPTS_STICKY);
1113 + buffer_json_member_add_string(wb, "summary", rrdf_field_summary_to_string(summary));
1114 + buffer_json_member_add_string(wb, "filter", rrdf_field_filter_to_string(filter));
1115 + }
1116 + buffer_json_object_close(wb);
1117 +}
1118 +
1119 #endif /* NETDATA_WEB_BUFFER_H */
libnetdata/parser/parser.c
+1
@@ -46,6 +46,7 @@ PARSER *parser_init(void *user, FILE *fp_input, FILE *fp_output, int fd,
46 parser->flags = flags;
47 parser->worker_job_next_id = WORKER_PARSER_FIRST_JOB;
48
49 + netdata_spinlock_init(&parser->writer.spinlock);
50 return parser;
51 }
52
libnetdata/parser/parser.h
+4
@@ -65,6 +65,10 @@ typedef struct parser {
65 DICTIONARY *functions;
66 usec_t smaller_timeout;
67 } inflight;
68 +
69 + struct {
70 + SPINLOCK spinlock;
71 + } writer;
72 } PARSER;
73
74 PARSER *parser_init(void *user, FILE *fp_input, FILE *fp_output, int fd, PARSER_INPUT_TYPE flags, void *ssl);
libnetdata/socket/socket.c
+2 -2
@@ -15,10 +15,10 @@ SOCKET_PEERS socket_peers(int sock_fd) {
15 SOCKET_PEERS peers;
16
17 if(sock_fd < 0) {
18 - strncpyz(peers.peer.ip, "unknown", sizeof(peers.peer.ip) - 1);
18 + strncpyz(peers.peer.ip, "not connected", sizeof(peers.peer.ip) - 1);
19 peers.peer.port = 0;
20
21 - strncpyz(peers.local.ip, "unknown", sizeof(peers.local.ip) - 1);
21 + strncpyz(peers.local.ip, "not connected", sizeof(peers.local.ip) - 1);
22 peers.local.port = 0;
23
24 return peers;
ml/ml-dummy.c
+9
@@ -109,4 +109,13 @@ void ml_update_global_statistics_charts(uint64_t models_consulted) {
109 UNUSED(models_consulted);
110 }
111
112 +bool ml_host_get_host_status(RRDHOST *rh, struct ml_metrics_statistics *mlm) {
113 + memset(mlm, 0, sizeof(*mlm));
114 + return false;
115 +}
116 +
117 +bool ml_host_running(RRDHOST *rh) {
118 + return false;
119 +}
120 +
121 #endif
ml/ml.cc
+28
@@ -1356,6 +1356,34 @@ void ml_host_get_detection_info(RRDHOST *rh, BUFFER *wb)
1356 netdata_mutex_unlock(&host->mutex);
1357 }
1358
1359 +bool ml_host_get_host_status(RRDHOST *rh, struct ml_metrics_statistics *mlm) {
1360 + ml_host_t *host = (ml_host_t *) rh->ml_host;
1361 + if (!host) {
1362 + memset(mlm, 0, sizeof(*mlm));
1363 + return false;
1364 + }
1365 +
1366 + netdata_mutex_lock(&host->mutex);
1367 +
1368 + mlm->anomalous = host->mls.num_anomalous_dimensions;
1369 + mlm->normal = host->mls.num_normal_dimensions;
1370 + mlm->trained = host->mls.num_training_status_trained + host->mls.num_training_status_pending_with_model;
1371 + mlm->pending = host->mls.num_training_status_untrained + host->mls.num_training_status_pending_without_model;
1372 + mlm->silenced = host->mls.num_training_status_silenced;
1373 +
1374 + netdata_mutex_unlock(&host->mutex);
1375 +
1376 + return true;
1377 +}
1378 +
1379 +bool ml_host_running(RRDHOST *rh) {
1380 + ml_host_t *host = (ml_host_t *) rh->ml_host;
1381 + if(!host)
1382 + return false;
1383 +
1384 + return true;
1385 +}
1386 +
1387 void ml_host_get_models(RRDHOST *rh, BUFFER *wb)
1388 {
1389 UNUSED(rh);
ml/ml.h
+3
@@ -43,6 +43,9 @@ int ml_dimension_load_models(RRDDIM *rd);
43
44 void ml_update_global_statistics_charts(uint64_t models_consulted);
45
46 +bool ml_host_get_host_status(RRDHOST *rh, struct ml_metrics_statistics *mlm);
47 +bool ml_host_running(RRDHOST *rh);
48 +
49 #ifdef __cplusplus
50 };
51 #endif
streaming/receiver.c
+25 -72
@@ -93,7 +93,7 @@ PARSER_RC streaming_claimed_id(char **words, size_t num_words, void *user)
93
94 rrdhost_flag_set(host, RRDHOST_FLAG_METADATA_CLAIMID |RRDHOST_FLAG_METADATA_UPDATE);
95
96 - rrdpush_claimed_id(host);
96 + rrdpush_send_claimed_id(host);
97
98 return PARSER_RC_OK;
99 }
@@ -104,17 +104,23 @@ static int read_stream(struct receiver_state *r, char* buffer, size_t size) {
104 return 0;
105 }
106
107 + int tries = 100;
108 ssize_t bytes_read;
109
110 + do {
111 + errno = 0;
112 +
113 #ifdef ENABLE_HTTPS
110 - if (SSL_connection(&r->ssl))
111 - bytes_read = netdata_ssl_read(&r->ssl, buffer, size);
112 - else
113 - bytes_read = read(r->fd, buffer, size);
114 + if (SSL_connection(&r->ssl))
115 + bytes_read = netdata_ssl_read(&r->ssl, buffer, size);
116 + else
117 + bytes_read = read(r->fd, buffer, size);
118 #else
115 - bytes_read = read(r->fd, buffer, size);
119 + bytes_read = read(r->fd, buffer, size);
120 #endif
121
122 + } while(bytes_read < 0 && errno == EINTR && tries--);
123 +
124 if((bytes_read == 0 || bytes_read == -1) && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS)) {
125 error("STREAM: %s(): timeout while waiting for data on socket!", __FUNCTION__);
126 bytes_read = -3;
@@ -423,67 +429,18 @@ static void rrdpush_receiver_replication_reset(RRDHOST *host) {
429 rrdhost_receiver_replicating_charts_zero(host);
430 }
431
426 -void rrdhost_receiver_to_json(BUFFER *wb, RRDHOST *host, const char *key, time_t now __maybe_unused) {
427 - size_t receiver_hops = host->system_info ? host->system_info->hops : (host == localhost) ? 0 : 1;
428 -
429 - netdata_mutex_lock(&host->receiver_lock);
430 -
431 - buffer_json_member_add_object(wb, key);
432 - buffer_json_member_add_uint64(wb, "hops", receiver_hops);
433 -
434 - bool online = host == localhost || !rrdhost_flag_check(host, RRDHOST_FLAG_ORPHAN | RRDHOST_FLAG_RRDPUSH_RECEIVER_DISCONNECTED);
435 - buffer_json_member_add_boolean(wb, "online", online);
436 -
437 - if(host->child_connect_time || host->child_disconnected_time) {
438 - time_t since = MAX(host->child_connect_time, host->child_disconnected_time);
439 - buffer_json_member_add_time_t(wb, "since", since);
440 - buffer_json_member_add_time_t(wb, "age", now - since);
441 - }
442 -
443 - if(!online && host->rrdpush_last_receiver_exit_reason)
444 - buffer_json_member_add_string(wb, "reason", host->rrdpush_last_receiver_exit_reason);
445 -
446 - if(host != localhost && host->receiver) {
447 - buffer_json_member_add_object(wb, "replication");
448 - {
449 - size_t instances = rrdhost_receiver_replicating_charts(host);
450 - buffer_json_member_add_boolean(wb, "in_progress", instances);
451 - buffer_json_member_add_double(wb, "completion", host->rrdpush_receiver_replication_percent);
452 - buffer_json_member_add_uint64(wb, "instances", instances);
453 - }
454 - buffer_json_object_close(wb); // replication
455 -
456 - buffer_json_member_add_object(wb, "source");
457 - {
458 -
459 - char buf[1024 + 1];
460 - SOCKET_PEERS peers = socket_peers(host->receiver->fd);
461 - bool ssl = SSL_connection(&host->receiver->ssl);
462 -
463 - snprintfz(buf, 1024, "[%s]:%d%s", peers.local.ip, peers.local.port, ssl ? ":SSL" : "");
464 - buffer_json_member_add_string(wb, "local", buf);
465 -
466 - snprintfz(buf, 1024, "[%s]:%d%s", peers.peer.ip, peers.peer.port, ssl ? ":SSL" : "");
467 - buffer_json_member_add_string(wb, "remote", buf);
468 -
469 - stream_capabilities_to_json_array(wb, host->receiver->capabilities, "capabilities");
470 - }
471 - buffer_json_object_close(wb); // source
472 - }
473 - buffer_json_object_close(wb); // collection
474 -
475 - netdata_mutex_unlock(&host->receiver_lock);
476 -}
477 -
432 static bool rrdhost_set_receiver(RRDHOST *host, struct receiver_state *rpt) {
433 bool signal_rrdcontext = false;
434 bool set_this = false;
435
436 netdata_mutex_lock(&host->receiver_lock);
437
484 - if (!host->receiver || host->receiver == rpt) {
438 + if (!host->receiver) {
439 rrdhost_flag_clear(host, RRDHOST_FLAG_ORPHAN);
440
441 + host->rrdpush_receiver_connection_counter++;
442 + __atomic_add_fetch(&localhost->connected_children_count, 1, __ATOMIC_RELAXED);
443 +
444 host->receiver = rpt;
445 rpt->host = host;
446
@@ -534,6 +491,9 @@ static void rrdhost_clear_receiver(struct receiver_state *rpt) {
491
492 // Make sure that we detach this thread and don't kill a freshly arriving receiver
493 if(host->receiver == rpt) {
494 + __atomic_sub_fetch(&localhost->connected_children_count, 1, __ATOMIC_RELAXED);
495 + rrdhost_flag_set(rpt->host, RRDHOST_FLAG_RRDPUSH_RECEIVER_DISCONNECTED);
496 +
497 host->trigger_chart_obsoletion_check = 0;
498 host->child_connect_time = 0;
499 host->child_disconnected_time = now_realtime_sec();
@@ -633,11 +593,6 @@ void rrdpush_receive_log_status(struct receiver_state *rpt, const char *msg, con
593
594 }
595
636 -static void rrdhost_reset_destinations(RRDHOST *host) {
637 - for (struct rrdpush_destinations *d = host->destinations; d; d = d->next)
638 - d->postpone_reconnection_until = 0;
639 -}
640 -
596 static void rrdpush_receive(struct receiver_state *rpt)
597 {
598 rpt->config.mode = default_rrd_memory_mode;
@@ -867,14 +822,14 @@ static void rrdpush_receive(struct receiver_state *rpt)
822 #ifdef ENABLE_ACLK
823 // in case we have cloud connection we inform cloud
824 // new child connected
870 - if (netdata_cloud_setting)
825 + if (netdata_cloud_enabled)
826 aclk_host_state_update(rpt->host, 1);
827 #endif
828
874 - rrdhost_set_is_parent_label(++localhost->connected_children_count);
829 + rrdhost_set_is_parent_label();
830
831 // let it reconnect to parent immediately
877 - rrdhost_reset_destinations(rpt->host);
832 + rrdpush_reset_destinations_postpone_time(rpt->host);
833
834 size_t count = streaming_parser(rpt, &cd, rpt->fd,
835 #ifdef ENABLE_HTTPS
@@ -884,8 +839,6 @@ static void rrdpush_receive(struct receiver_state *rpt)
839 #endif
840 );
841
887 - rrdhost_flag_set(rpt->host, RRDHOST_FLAG_RRDPUSH_RECEIVER_DISCONNECTED);
888 -
842 if(!rpt->exit.reason)
843 rpt->exit.reason = "PARSER EXIT";
844
@@ -898,12 +851,10 @@ static void rrdpush_receive(struct receiver_state *rpt)
851 #ifdef ENABLE_ACLK
852 // in case we have cloud connection we inform cloud
853 // a child disconnected
901 - if (netdata_cloud_setting)
854 + if (netdata_cloud_enabled)
855 aclk_host_state_update(rpt->host, 0);
856 #endif
857
905 - rrdhost_set_is_parent_label(--localhost->connected_children_count);
906 -
858 cleanup:
859 ;
860 }
@@ -921,6 +872,8 @@ static void rrdpush_receiver_thread_cleanup(void *ptr) {
872 , gettid());
873
874 receiver_state_free(rpt);
875 +
876 + rrdhost_set_is_parent_label();
877 }
878
879 void *rrdpush_receiver_thread(void *ptr) {
streaming/replication.c
+2 -2
@@ -797,9 +797,9 @@ static bool send_replay_chart_cmd(struct replication_request_details *r, const c
797 rrdset_id(st), r->wanted.start_streaming ? "true" : "false",
798 (unsigned long long)r->wanted.after, (unsigned long long)r->wanted.before);
799
800 - int ret = r->caller.callback(buffer, r->caller.data);
800 + ssize_t ret = r->caller.callback(buffer, r->caller.data);
801 if (ret < 0) {
802 - error("REPLAY ERROR: 'host:%s/chart:%s' failed to send replication request to child (error %d)",
802 + error("REPLAY ERROR: 'host:%s/chart:%s' failed to send replication request to child (error %zd)",
803 rrdhost_hostname(r->host), rrdset_id(r->st), ret);
804 return false;
805 }
streaming/replication.h
+1 -1
@@ -17,7 +17,7 @@ struct replication_query_statistics replication_get_query_statistics(void);
17
18 bool replicate_chart_response(RRDHOST *rh, RRDSET *rs, bool start_streaming, time_t after, time_t before);
19
20 -typedef int (*send_command)(const char *txt, void *data);
20 +typedef ssize_t (*send_command)(const char *txt, void *data);
21
22 bool replicate_chart_request(send_command callback, void *callback_data,
23 RRDHOST *rh, RRDSET *rs,
streaming/rrdpush.c
+68 -24
@@ -67,20 +67,37 @@ static void load_stream_conf() {
67 freez(filename);
68 }
69
70 -STREAM_CAPABILITIES stream_our_capabilities() {
71 - return STREAM_CAP_V1 |
72 - STREAM_CAP_V2 |
73 - STREAM_CAP_VN |
74 - STREAM_CAP_VCAPS |
75 - STREAM_CAP_HLABELS |
76 - STREAM_CAP_CLAIM |
77 - STREAM_CAP_CLABELS |
78 - STREAM_CAP_FUNCTIONS |
79 - STREAM_CAP_REPLICATION |
80 - STREAM_CAP_BINARY |
81 - STREAM_CAP_INTERPOLATED |
82 - STREAM_HAS_COMPRESSION |
70 +STREAM_CAPABILITIES stream_our_capabilities(RRDHOST *host, bool sender) {
71 +
72 + // we can have DATA_WITH_ML when INTERPOLATED is available
73 + bool ml_capability = true;
74 +
75 + if(host && sender) {
76 + // we have DATA_WITH_ML capability
77 + // we should remove the DATA_WITH_ML capability if our database does not have anomaly info
78 + // this can happen under these conditions: 1. we don't run ML, and 2. we don't receive ML
79 + netdata_mutex_lock(&host->receiver_lock);
80 +
81 + if(!ml_host_running(host) && !stream_has_capability(host->receiver, STREAM_CAP_DATA_WITH_ML))
82 + ml_capability = false;
83 +
84 + netdata_mutex_unlock(&host->receiver_lock);
85 + }
86 +
87 + return STREAM_CAP_V1 |
88 + STREAM_CAP_V2 |
89 + STREAM_CAP_VN |
90 + STREAM_CAP_VCAPS |
91 + STREAM_CAP_HLABELS |
92 + STREAM_CAP_CLAIM |
93 + STREAM_CAP_CLABELS |
94 + STREAM_CAP_FUNCTIONS |
95 + STREAM_CAP_REPLICATION |
96 + STREAM_CAP_BINARY |
97 + STREAM_CAP_INTERPOLATED |
98 + STREAM_HAS_COMPRESSION |
99 (ieee754_doubles ? STREAM_CAP_IEEE754 : 0) |
100 + (ml_capability ? STREAM_CAP_DATA_WITH_ML : 0) |
101 0;
102 }
103
@@ -504,6 +521,7 @@ static int send_labels_callback(const char *name, const char *value, RRDLABEL_SR
521 buffer_sprintf(wb, "LABEL \"%s\" = %d \"%s\"\n", name, ls, value);
522 return 1;
523 }
524 +
525 void rrdpush_send_host_labels(RRDHOST *host) {
526 if(unlikely(!rrdhost_can_send_definitions_to_parent(host)
527 || !stream_has_capability(host->sender, STREAM_CAP_HLABELS)))
@@ -519,8 +537,23 @@ void rrdpush_send_host_labels(RRDHOST *host) {
537 sender_thread_buffer_free();
538 }
539
522 -void rrdpush_claimed_id(RRDHOST *host)
523 -{
540 +void rrdpush_send_global_functions(RRDHOST *host) {
541 + if(!stream_has_capability(host->sender, STREAM_CAP_FUNCTIONS))
542 + return;
543 +
544 + if(unlikely(!rrdhost_can_send_definitions_to_parent(host)))
545 + return;
546 +
547 + BUFFER *wb = sender_start(host->sender);
548 +
549 + rrd_functions_expose_global_rrdpush(host, wb);
550 +
551 + sender_commit(host->sender, wb, STREAM_TRAFFIC_TYPE_METADATA);
552 +
553 + sender_thread_buffer_free();
554 +}
555 +
556 +void rrdpush_send_claimed_id(RRDHOST *host) {
557 if(!stream_has_capability(host->sender, STREAM_CAP_CLAIM))
558 return;
559
@@ -823,7 +856,7 @@ int rrdpush_receiver_thread_spawn(struct web_client *w, char *decoded_query_stri
856 rpt->tags = strdupz(value);
857
858 else if(!strcmp(name, "ver") && (rpt->capabilities & STREAM_CAP_INVALID))
826 - rpt->capabilities = convert_stream_version_to_capabilities(strtoul(value, NULL, 0));
859 + rpt->capabilities = convert_stream_version_to_capabilities(strtoul(value, NULL, 0), NULL, false);
860
861 else {
862 // An old Netdata child does not have a compatible streaming protocol, map to something sane.
@@ -846,7 +879,7 @@ int rrdpush_receiver_thread_spawn(struct web_client *w, char *decoded_query_stri
879 name = "NETDATA_HOST_OS_DETECTION";
880
881 else if(!strcmp(name, "NETDATA_PROTOCOL_VERSION") && (rpt->capabilities & STREAM_CAP_INVALID))
849 - rpt->capabilities = convert_stream_version_to_capabilities(1);
882 + rpt->capabilities = convert_stream_version_to_capabilities(1, NULL, false);
883
884 if (unlikely(rrdhost_set_system_info_variable(rpt->system_info, name, value))) {
885 info("STREAM '%s' [receive from [%s]:%s]: "
@@ -860,7 +893,7 @@ int rrdpush_receiver_thread_spawn(struct web_client *w, char *decoded_query_stri
893
894 if (rpt->capabilities & STREAM_CAP_INVALID)
895 // no version is supplied, assume version 0;
863 - rpt->capabilities = convert_stream_version_to_capabilities(0);
896 + rpt->capabilities = convert_stream_version_to_capabilities(0, NULL, false);
897
898 // find the program name and version
899 if(w->user_agent && w->user_agent[0]) {
@@ -1177,9 +1210,10 @@ int rrdpush_receiver_thread_spawn(struct web_client *w, char *decoded_query_stri
1210 }
1211
1212 void rrdpush_reset_destinations_postpone_time(RRDHOST *host) {
1180 - struct rrdpush_destinations *d;
1181 - for (d = host->destinations; d; d = d->next)
1182 - d->postpone_reconnection_until = 0;
1213 + uint32_t wait = (host->sender) ? host->sender->reconnect_delay : 5;
1214 + time_t now = now_realtime_sec();
1215 + for (struct rrdpush_destinations *d = host->destinations; d; d = d->next)
1216 + d->postpone_reconnection_until = now + wait;
1217 }
1218
1219 static struct {
@@ -1232,6 +1266,7 @@ static struct {
1266 { STREAM_CAP_BINARY, "BINARY" },
1267 { STREAM_CAP_INTERPOLATED, "INTERPOLATED" },
1268 { STREAM_CAP_IEEE754, "IEEE754" },
1269 + { STREAM_CAP_DATA_WITH_ML, "ML" },
1270 { 0 , NULL },
1271 };
1272
@@ -1245,7 +1280,10 @@ static void stream_capabilities_to_string(BUFFER *wb, STREAM_CAPABILITIES caps)
1280 }
1281
1282 void stream_capabilities_to_json_array(BUFFER *wb, STREAM_CAPABILITIES caps, const char *key) {
1248 - buffer_json_member_add_array(wb, key);
1283 + if(key)
1284 + buffer_json_member_add_array(wb, key);
1285 + else
1286 + buffer_json_add_array_item_array(wb);
1287
1288 for(size_t i = 0; capability_names[i].str ; i++) {
1289 if(caps & capability_names[i].cap)
@@ -1275,7 +1313,7 @@ void log_sender_capabilities(struct sender_state *s) {
1313 buffer_free(wb);
1314 }
1315
1278 -STREAM_CAPABILITIES convert_stream_version_to_capabilities(int32_t version) {
1316 +STREAM_CAPABILITIES convert_stream_version_to_capabilities(int32_t version, RRDHOST *host, bool sender) {
1317 STREAM_CAPABILITIES caps = 0;
1318
1319 if(version <= 1) caps = STREAM_CAP_V1;
@@ -1294,7 +1332,13 @@ STREAM_CAPABILITIES convert_stream_version_to_capabilities(int32_t version) {
1332 if(caps & STREAM_CAP_V2)
1333 caps &= ~(STREAM_CAP_V1);
1334
1297 - return caps & stream_our_capabilities();
1335 + STREAM_CAPABILITIES common_caps = caps & stream_our_capabilities(host, sender);
1336 +
1337 + if(!(common_caps & STREAM_CAP_INTERPOLATED))
1338 + // DATA WITH ML requires INTERPOLATED
1339 + common_caps &= ~STREAM_CAP_DATA_WITH_ML;
1340 +
1341 + return common_caps;
1342 }
1343
1344 int32_t stream_capabilities_to_vn(uint32_t caps) {
streaming/rrdpush.h
+292 -21
@@ -43,6 +43,7 @@ typedef enum {
43 STREAM_CAP_BINARY = (1 << 13), // streaming supports binary data
44 STREAM_CAP_INTERPOLATED = (1 << 14), // streaming supports interpolated streaming of values
45 STREAM_CAP_IEEE754 = (1 << 15), // streaming supports binary/hex transfer of double values
46 + STREAM_CAP_DATA_WITH_ML = (1 << 16), // streaming supports transferring anomaly bit
47
48 STREAM_CAP_INVALID = (1 << 30), // used as an invalid value for capabilities when this is set
49 // this must be signed int, so don't use the last bit
@@ -55,7 +56,7 @@ typedef enum {
56 #define STREAM_HAS_COMPRESSION 0
57 #endif // ENABLE_COMPRESSION
58
58 -STREAM_CAPABILITIES stream_our_capabilities();
59 +STREAM_CAPABILITIES stream_our_capabilities(RRDHOST *host, bool sender);
60
61 #define stream_has_capability(rpt, capability) ((rpt) && ((rpt)->capabilities & (capability)) == (capability))
62
@@ -97,18 +98,6 @@ typedef enum {
98 } STREAM_HANDSHAKE;
99
100
100 -// ----------------------------------------------------------------------------
101 -
102 -typedef enum __attribute__((packed)) {
103 - STREAM_TRAFFIC_TYPE_REPLICATION,
104 - STREAM_TRAFFIC_TYPE_FUNCTIONS,
105 - STREAM_TRAFFIC_TYPE_METADATA,
106 - STREAM_TRAFFIC_TYPE_DATA,
107 -
108 - // terminator
109 - STREAM_TRAFFIC_TYPE_MAX,
110 -} STREAM_TRAFFIC_TYPE;
111 -
101 // ----------------------------------------------------------------------------
102
103 typedef struct {
@@ -145,9 +134,19 @@ struct decompressor_state {
134 #endif
135
136 // Thread-local storage
148 - // Metric transmission: collector threads asynchronously fill the buffer, sender thread uses it.
137 +// Metric transmission: collector threads asynchronously fill the buffer, sender thread uses it.
138
150 -typedef enum {
139 +typedef enum __attribute__((packed)) {
140 + STREAM_TRAFFIC_TYPE_REPLICATION = 0,
141 + STREAM_TRAFFIC_TYPE_FUNCTIONS,
142 + STREAM_TRAFFIC_TYPE_METADATA,
143 + STREAM_TRAFFIC_TYPE_DATA,
144 +
145 + // terminator
146 + STREAM_TRAFFIC_TYPE_MAX,
147 +} STREAM_TRAFFIC_TYPE;
148 +
149 +typedef enum __attribute__((packed)) {
150 SENDER_FLAG_OVERFLOW = (1 << 0), // The buffer has been overflown
151 SENDER_FLAG_COMPRESSION = (1 << 1), // The stream needs to have and has compression
152 } SENDER_FLAGS;
@@ -158,7 +157,7 @@ struct sender_state {
157 SENDER_FLAGS flags;
158 int timeout;
159 int default_port;
161 - usec_t reconnect_delay;
160 + uint32_t reconnect_delay;
161 char connected_to[CONNECTED_TO_SIZE + 1]; // We don't know which proxy we connect to, passed back from socket.c
162 size_t begin;
163 size_t reconnects_counter;
@@ -242,6 +241,31 @@ struct sender_state {
241 #define rrdpush_sender_pending_replication_requests_minus_one(sender) __atomic_sub_fetch(&((sender)->replication.atomic.pending_requests), 1, __ATOMIC_RELAXED)
242 #define rrdpush_sender_pending_replication_requests_zero(sender) __atomic_store_n(&((sender)->replication.atomic.pending_requests), 0, __ATOMIC_RELAXED)
243
244 +/*
245 +typedef enum {
246 + STREAM_NODE_INSTANCE_FEATURE_CLOUD_ONLINE = (1 << 0),
247 + STREAM_NODE_INSTANCE_FEATURE_VIRTUAL_HOST = (1 << 1),
248 + STREAM_NODE_INSTANCE_FEATURE_HEALTH_ENABLED = (1 << 2),
249 + STREAM_NODE_INSTANCE_FEATURE_ML_SELF = (1 << 3),
250 + STREAM_NODE_INSTANCE_FEATURE_ML_RECEIVED = (1 << 4),
251 + STREAM_NODE_INSTANCE_FEATURE_SSL = (1 << 5),
252 +} STREAM_NODE_INSTANCE_FEATURES;
253 +
254 +typedef struct stream_node_instance {
255 + uuid_t uuid;
256 + STRING *agent;
257 + STREAM_NODE_INSTANCE_FEATURES features;
258 + uint32_t hops;
259 +
260 + // receiver information on that agent
261 + int32_t capabilities;
262 + uint32_t local_port;
263 + uint32_t remote_port;
264 + STRING *local_ip;
265 + STRING *remote_ip;
266 +} STREAM_NODE_INSTANCE;
267 +*/
268 +
269 struct receiver_state {
270 RRDHOST *host;
271 pid_t tid;
@@ -298,6 +322,13 @@ struct receiver_state {
322 #endif
323
324 time_t replication_first_time_t;
325 +
326 +/*
327 + struct {
328 + uint32_t count;
329 + STREAM_NODE_INSTANCE *array;
330 + } instances;
331 +*/
332 };
333
334 struct rrdpush_destinations {
@@ -352,7 +383,8 @@ void rrddim_push_metrics_v2(RRDSET_STREAM_BUFFER *rsb, RRDDIM *rd, usec_t point_
383 bool rrdset_push_chart_definition_now(RRDSET *st);
384 void *rrdpush_sender_thread(void *ptr);
385 void rrdpush_send_host_labels(RRDHOST *host);
355 -void rrdpush_claimed_id(RRDHOST *host);
386 +void rrdpush_send_claimed_id(RRDHOST *host);
387 +void rrdpush_send_global_functions(RRDHOST *host);
388
389 #define THREAD_TAG_STREAM_RECEIVER "RCVR" // "[host]" is appended
390 #define THREAD_TAG_STREAM_SENDER "SNDR" // "[host]" is appended
@@ -383,7 +415,7 @@ void stream_capabilities_to_json_array(BUFFER *wb, STREAM_CAPABILITIES caps, con
415 void rrdpush_receive_log_status(struct receiver_state *rpt, const char *msg, const char *status);
416 void log_receiver_capabilities(struct receiver_state *rpt);
417 void log_sender_capabilities(struct sender_state *s);
386 -STREAM_CAPABILITIES convert_stream_version_to_capabilities(int32_t version);
418 +STREAM_CAPABILITIES convert_stream_version_to_capabilities(int32_t version, RRDHOST *host, bool sender);
419 int32_t stream_capabilities_to_vn(uint32_t caps);
420
421 void receiver_state_free(struct receiver_state *rpt);
@@ -391,9 +423,248 @@ bool stop_streaming_receiver(RRDHOST *host, const char *reason);
423
424 void sender_thread_buffer_free(void);
425
394 -void rrdhost_receiver_to_json(BUFFER *wb, RRDHOST *host, const char *key, time_t now __maybe_unused);
395 -void rrdhost_sender_to_json(BUFFER *wb, RRDHOST *host, const char *key, time_t now __maybe_unused);
396 -
426 #include "replication.h"
427
428 +typedef enum __attribute__((packed)) {
429 + RRDHOST_DB_STATUS_INITIALIZING = 0,
430 + RRDHOST_DB_STATUS_QUERYABLE,
431 +} RRDHOST_DB_STATUS;
432 +
433 +static inline const char *rrdhost_db_status_to_string(RRDHOST_DB_STATUS status) {
434 + switch(status) {
435 + default:
436 + case RRDHOST_DB_STATUS_INITIALIZING:
437 + return "initializing";
438 +
439 + case RRDHOST_DB_STATUS_QUERYABLE:
440 + return "online";
441 + }
442 +}
443 +
444 +typedef enum __attribute__((packed)) {
445 + RRDHOST_DB_LIVENESS_STALE = 0,
446 + RRDHOST_DB_LIVENESS_LIVE,
447 +} RRDHOST_DB_LIVENESS;
448 +
449 +static inline const char *rrdhost_db_liveness_to_string(RRDHOST_DB_LIVENESS status) {
450 + switch(status) {
451 + default:
452 + case RRDHOST_DB_LIVENESS_STALE:
453 + return "stale";
454 +
455 + case RRDHOST_DB_LIVENESS_LIVE:
456 + return "live";
457 + }
458 +}
459 +
460 +typedef enum __attribute__((packed)) {
461 + RRDHOST_INGEST_STATUS_ARCHIVED = 0,
462 + RRDHOST_INGEST_STATUS_INITIALIZING,
463 + RRDHOST_INGEST_STATUS_REPLICATING,
464 + RRDHOST_INGEST_STATUS_ONLINE,
465 + RRDHOST_INGEST_STATUS_OFFLINE,
466 +} RRDHOST_INGEST_STATUS;
467 +
468 +static inline const char *rrdhost_ingest_status_to_string(RRDHOST_INGEST_STATUS status) {
469 + switch(status) {
470 + case RRDHOST_INGEST_STATUS_ARCHIVED:
471 + return "archived";
472 +
473 + case RRDHOST_INGEST_STATUS_INITIALIZING:
474 + return "initializing";
475 +
476 + case RRDHOST_INGEST_STATUS_REPLICATING:
477 + return "replicating";
478 +
479 + case RRDHOST_INGEST_STATUS_ONLINE:
480 + return "online";
481 +
482 + default:
483 + case RRDHOST_INGEST_STATUS_OFFLINE:
484 + return "offline";
485 + }
486 +}
487 +
488 +typedef enum __attribute__((packed)) {
489 + RRDHOST_INGEST_TYPE_LOCALHOST = 0,
490 + RRDHOST_INGEST_TYPE_VIRTUAL,
491 + RRDHOST_INGEST_TYPE_CHILD,
492 + RRDHOST_INGEST_TYPE_ARCHIVED,
493 +} RRDHOST_INGEST_TYPE;
494 +
495 +static inline const char *rrdhost_ingest_type_to_string(RRDHOST_INGEST_TYPE type) {
496 + switch(type) {
497 + case RRDHOST_INGEST_TYPE_LOCALHOST:
498 + return "localhost";
499 +
500 + case RRDHOST_INGEST_TYPE_VIRTUAL:
501 + return "virtual";
502 +
503 + case RRDHOST_INGEST_TYPE_CHILD:
504 + return "child";
505 +
506 + default:
507 + case RRDHOST_INGEST_TYPE_ARCHIVED:
508 + return "archived";
509 + }
510 +}
511 +
512 +typedef enum __attribute__((packed)) {
513 + RRDHOST_STREAM_STATUS_DISABLED = 0,
514 + RRDHOST_STREAM_STATUS_REPLICATING,
515 + RRDHOST_STREAM_STATUS_ONLINE,
516 + RRDHOST_STREAM_STATUS_OFFLINE,
517 +} RRDHOST_STREAMING_STATUS;
518 +
519 +static inline const char *rrdhost_streaming_status_to_string(RRDHOST_STREAMING_STATUS status) {
520 + switch(status) {
521 + case RRDHOST_STREAM_STATUS_DISABLED:
522 + return "disabled";
523 +
524 + case RRDHOST_STREAM_STATUS_REPLICATING:
525 + return "replicating";
526 +
527 + case RRDHOST_STREAM_STATUS_ONLINE:
528 + return "online";
529 +
530 + default:
531 + case RRDHOST_STREAM_STATUS_OFFLINE:
532 + return "offline";
533 + }
534 +}
535 +
536 +typedef enum __attribute__((packed)) {
537 + RRDHOST_ML_STATUS_DISABLED = 0,
538 + RRDHOST_ML_STATUS_OFFLINE,
539 + RRDHOST_ML_STATUS_RUNNING,
540 +} RRDHOST_ML_STATUS;
541 +
542 +static inline const char *rrdhost_ml_status_to_string(RRDHOST_ML_STATUS status) {
543 + switch(status) {
544 + case RRDHOST_ML_STATUS_RUNNING:
545 + return "online";
546 +
547 + case RRDHOST_ML_STATUS_OFFLINE:
548 + return "offline";
549 +
550 + default:
551 + case RRDHOST_ML_STATUS_DISABLED:
552 + return "disabled";
553 + }
554 +}
555 +
556 +typedef enum __attribute__((packed)) {
557 + RRDHOST_ML_TYPE_DISABLED = 0,
558 + RRDHOST_ML_TYPE_SELF,
559 + RRDHOST_ML_TYPE_RECEIVED,
560 +} RRDHOST_ML_TYPE;
561 +
562 +static inline const char *rrdhost_ml_type_to_string(RRDHOST_ML_TYPE type) {
563 + switch(type) {
564 + case RRDHOST_ML_TYPE_SELF:
565 + return "self";
566 +
567 + case RRDHOST_ML_TYPE_RECEIVED:
568 + return "received";
569 +
570 + default:
571 + case RRDHOST_ML_TYPE_DISABLED:
572 + return "disabled";
573 + }
574 +}
575 +
576 +typedef enum __attribute__((packed)) {
577 + RRDHOST_HEALTH_STATUS_DISABLED = 0,
578 + RRDHOST_HEALTH_STATUS_INITIALIZING,
579 + RRDHOST_HEALTH_STATUS_RUNNING,
580 +} RRDHOST_HEALTH_STATUS;
581 +
582 +static inline const char *rrdhost_health_status_to_string(RRDHOST_HEALTH_STATUS status) {
583 + switch(status) {
584 + default:
585 + case RRDHOST_HEALTH_STATUS_DISABLED:
586 + return "disabled";
587 +
588 + case RRDHOST_HEALTH_STATUS_INITIALIZING:
589 + return "initializing";
590 +
591 + case RRDHOST_HEALTH_STATUS_RUNNING:
592 + return "online";
593 + }
594 +}
595 +
596 +typedef struct rrdhost_status {
597 + RRDHOST *host;
598 + time_t now;
599 +
600 + struct {
601 + RRDHOST_DB_STATUS status;
602 + RRDHOST_DB_LIVENESS liveness;
603 + RRD_MEMORY_MODE mode;
604 + time_t first_time_s;
605 + time_t last_time_s;
606 + size_t metrics;
607 + size_t instances;
608 + size_t contexts;
609 + } db;
610 +
611 + struct {
612 + RRDHOST_ML_STATUS status;
613 + RRDHOST_ML_TYPE type;
614 + struct ml_metrics_statistics metrics;
615 + } ml;
616 +
617 + struct {
618 + size_t hops;
619 + RRDHOST_INGEST_TYPE type;
620 + RRDHOST_INGEST_STATUS status;
621 + SOCKET_PEERS peers;
622 + bool ssl;
623 + STREAM_CAPABILITIES capabilities;
624 + uint32_t id;
625 + time_t since;
626 + const char *reason;
627 +
628 + struct {
629 + bool in_progress;
630 + NETDATA_DOUBLE completion;
631 + size_t instances;
632 + } replication;
633 + } ingest;
634 +
635 + struct {
636 + size_t hops;
637 + RRDHOST_STREAMING_STATUS status;
638 + SOCKET_PEERS peers;
639 + bool ssl;
640 + bool compression;
641 + STREAM_CAPABILITIES capabilities;
642 + uint32_t id;
643 + time_t since;
644 + const char *reason;
645 +
646 + struct {
647 + bool in_progress;
648 + NETDATA_DOUBLE completion;
649 + size_t instances;
650 + } replication;
651 +
652 + size_t sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_MAX];
653 + } stream;
654 +
655 + struct {
656 + RRDHOST_HEALTH_STATUS status;
657 + struct {
658 + uint32_t undefined;
659 + uint32_t uninitialized;
660 + uint32_t clear;
661 + uint32_t warning;
662 + uint32_t critical;
663 + } alerts;
664 + } health;
665 +} RRDHOST_STATUS;
666 +
667 +void rrdhost_status(RRDHOST *host, time_t now, RRDHOST_STATUS *s);
668 +bool rrdhost_state_cloud_emulation(RRDHOST *host);
669 +
670 #endif //NETDATA_RRDPUSH_H
streaming/sender.c
+10 -120
@@ -490,18 +490,19 @@ static inline bool rrdpush_sender_validate_response(RRDHOST *host, struct sender
490 break;
491 }
492 }
493 - const char *error = stream_responses[i].error;
494 - int worker_job_id = stream_responses[i].worker_job_id;
495 - time_t delay = stream_responses[i].postpone_reconnect_seconds;
493
494 if(version >= STREAM_HANDSHAKE_OK_V1) {
495 host->destination->last_error = NULL;
496 host->destination->last_handshake = version;
500 - host->destination->postpone_reconnection_until = 0;
501 - s->capabilities = convert_stream_version_to_capabilities(version);
497 + host->destination->postpone_reconnection_until = now_realtime_sec() + s->reconnect_delay;
498 + s->capabilities = convert_stream_version_to_capabilities(version, host, true);
499 return true;
500 }
501
502 + const char *error = stream_responses[i].error;
503 + int worker_job_id = stream_responses[i].worker_job_id;
504 + time_t delay = stream_responses[i].postpone_reconnect_seconds;
505 +
506 worker_is_busy(worker_job_id);
507 rrdpush_sender_thread_close_socket(host);
508 host->destination->last_error = error;
@@ -591,7 +592,7 @@ static bool rrdpush_sender_thread_connect_to_parent(RRDHOST *host, int default_p
592 // info("STREAM %s [send to %s]: initializing communication...", rrdhost_hostname(host), s->connected_to);
593
594 // reset our capabilities to default
594 - s->capabilities = stream_our_capabilities();
595 + s->capabilities = stream_our_capabilities(host, true);
596
597 #ifdef ENABLE_COMPRESSION
598 // If we don't want compression, remove it from our capabilities
@@ -1098,119 +1099,6 @@ void rrdpush_signal_sender_to_wake_up(struct sender_state *s) {
1099 }
1100 }
1101
1101 -static NETDATA_DOUBLE rrdhost_sender_replication_completion(RRDHOST *host, time_t now, size_t *instances) {
1102 - size_t charts = rrdhost_sender_replicating_charts(host);
1103 - NETDATA_DOUBLE completion;
1104 - if(!charts || !host->sender || !host->sender->replication.oldest_request_after_t)
1105 - completion = 100.0;
1106 - else if(!host->sender->replication.latest_completed_before_t || host->sender->replication.latest_completed_before_t < host->sender->replication.oldest_request_after_t)
1107 - completion = 0.0;
1108 - else {
1109 - time_t total = now - host->sender->replication.oldest_request_after_t;
1110 - time_t current = host->sender->replication.latest_completed_before_t - host->sender->replication.oldest_request_after_t;
1111 - completion = (NETDATA_DOUBLE) current * 100.0 / (NETDATA_DOUBLE) total;
1112 - }
1113 -
1114 - *instances = charts;
1115 -
1116 - return completion;
1117 -}
1118 -
1119 -void rrdhost_sender_to_json(BUFFER *wb, RRDHOST *host, const char *key, time_t now __maybe_unused) {
1120 - bool online = rrdhost_flag_check(host, RRDHOST_FLAG_RRDPUSH_SENDER_CONNECTED);
1121 - buffer_json_member_add_object(wb, key);
1122 -
1123 - if(host->sender)
1124 - buffer_json_member_add_uint64(wb, "hops", host->sender->hops);
1125 -
1126 - buffer_json_member_add_boolean(wb, "online", online);
1127 -
1128 - if(host->sender && host->sender->last_state_since_t) {
1129 - buffer_json_member_add_time_t(wb, "since", host->sender->last_state_since_t);
1130 - buffer_json_member_add_time_t(wb, "age", now - host->sender->last_state_since_t);
1131 - }
1132 -
1133 - if(!online && host->sender && host->sender->exit.reason)
1134 - buffer_json_member_add_string(wb, "reason", host->sender->exit.reason);
1135 -
1136 - buffer_json_member_add_object(wb, "replication");
1137 - {
1138 - size_t instances;
1139 - NETDATA_DOUBLE completion = rrdhost_sender_replication_completion(host, now, &instances);
1140 - buffer_json_member_add_boolean(wb, "in_progress", instances);
1141 - buffer_json_member_add_double(wb, "completion", completion);
1142 - buffer_json_member_add_uint64(wb, "instances", instances);
1143 - }
1144 - buffer_json_object_close(wb);
1145 -
1146 - if(host->sender) {
1147 - netdata_mutex_lock(&host->sender->mutex);
1148 -
1149 - buffer_json_member_add_object(wb, "destination");
1150 - {
1151 - char buf[1024 + 1];
1152 - if(online && host->sender->rrdpush_sender_socket != -1) {
1153 - SOCKET_PEERS peers = socket_peers(host->sender->rrdpush_sender_socket);
1154 - bool ssl = SSL_connection(&host->sender->ssl);
1155 -
1156 - snprintfz(buf, 1024, "[%s]:%d%s", peers.local.ip, peers.local.port, ssl ? ":SSL" : "");
1157 - buffer_json_member_add_string(wb, "local", buf);
1158 -
1159 - snprintfz(buf, 1024, "[%s]:%d%s", peers.peer.ip, peers.peer.port, ssl ? ":SSL" : "");
1160 - buffer_json_member_add_string(wb, "remote", buf);
1161 -
1162 - stream_capabilities_to_json_array(wb, host->sender->capabilities, "capabilities");
1163 -
1164 - buffer_json_member_add_object(wb, "traffic");
1165 - {
1166 - bool compression = false;
1167 -#ifdef ENABLE_COMPRESSION
1168 - compression = (stream_has_capability(host->sender, STREAM_CAP_COMPRESSION) && host->sender->compressor);
1169 -#endif
1170 - buffer_json_member_add_boolean(wb, "compression", compression);
1171 - buffer_json_member_add_uint64(wb, "data", host->sender->sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_DATA]);
1172 - buffer_json_member_add_uint64(wb, "metadata", host->sender->sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_METADATA]);
1173 - buffer_json_member_add_uint64(wb, "functions", host->sender->sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_FUNCTIONS]);
1174 - buffer_json_member_add_uint64(wb, "replication", host->sender->sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_REPLICATION]);
1175 - }
1176 - buffer_json_object_close(wb); // traffic
1177 - }
1178 -
1179 - buffer_json_member_add_array(wb, "candidates");
1180 - struct rrdpush_destinations *d;
1181 - for (d = host->destinations; d; d = d->next) {
1182 - buffer_json_add_array_item_object(wb);
1183 - {
1184 -
1185 - if (d->ssl) {
1186 - snprintfz(buf, 1024, "%s:SSL", string2str(d->destination));
1187 - buffer_json_member_add_string(wb, "destination", buf);
1188 - }
1189 - else
1190 - buffer_json_member_add_string(wb, "destination", string2str(d->destination));
1191 -
1192 - buffer_json_member_add_time_t(wb, "last_check", d->last_attempt);
1193 - buffer_json_member_add_time_t(wb, "age", now - d->last_attempt);
1194 - buffer_json_member_add_string(wb, "last_error", d->last_error);
1195 - buffer_json_member_add_string(wb, "last_handshake",
1196 - stream_handshake_error_to_string(d->last_handshake));
1197 - buffer_json_member_add_time_t(wb, "next_check", d->postpone_reconnection_until);
1198 - buffer_json_member_add_time_t(wb, "next_in",
1199 - (d->postpone_reconnection_until > now) ?
1200 - d->postpone_reconnection_until - now : 0);
1201 - }
1202 - buffer_json_object_close(wb); // each candidate
1203 - }
1204 - buffer_json_array_close(wb); // candidates
1205 - }
1206 - buffer_json_object_close(wb); // destination
1207 -
1208 - netdata_mutex_unlock(&host->sender->mutex);
1209 - }
1210 -
1211 - buffer_json_object_close(wb); // streaming
1212 -}
1213 -
1102 static bool rrdhost_set_sender(RRDHOST *host) {
1103 if(unlikely(!host->sender)) return false;
1104
@@ -1219,6 +1107,7 @@ static bool rrdhost_set_sender(RRDHOST *host) {
1107 if(!host->sender->tid) {
1108 rrdhost_flag_clear(host, RRDHOST_FLAG_RRDPUSH_SENDER_CONNECTED | RRDHOST_FLAG_RRDPUSH_SENDER_READY_4_METRICS);
1109 rrdhost_flag_set(host, RRDHOST_FLAG_RRDPUSH_SENDER_SPAWN);
1110 + host->rrdpush_sender_connection_counter++;
1111 host->sender->tid = gettid();
1112 host->sender->last_state_since_t = now_realtime_sec();
1113 host->sender->exit.reason = NULL;
@@ -1435,8 +1324,9 @@ void *rrdpush_sender_thread(void *ptr) {
1324 break;
1325
1326 now_s = s->last_traffic_seen_t = now_monotonic_sec();
1438 - rrdpush_claimed_id(s->host);
1327 + rrdpush_send_claimed_id(s->host);
1328 rrdpush_send_host_labels(s->host);
1329 + rrdpush_send_global_functions(s->host);
1330 s->replication.oldest_request_after_t = 0;
1331
1332 rrdhost_flag_set(s->host, RRDHOST_FLAG_RRDPUSH_SENDER_READY_4_METRICS);
web/api/formatters/json_wrapper.c
+3 -2
@@ -250,7 +250,7 @@ static void query_target_summary_nodes_v2(BUFFER *wb, QUERY_TARGET *qt, const ch
250 QUERY_NODE *qn = query_node(qt, c);
251 RRDHOST *host = qn->rrdhost;
252 buffer_json_add_array_item_object(wb);
253 - buffer_json_node_add_v2(wb, host, qn->slot, qn->duration_ut);
253 + buffer_json_node_add_v2(wb, host, qn->slot, qn->duration_ut, true);
254 query_target_instance_counts(wb, &qn->instances);
255 query_target_metric_counts(wb, &qn->metrics);
256 query_target_alerts_counts(wb, &qn->alerts, NULL, false);
@@ -1268,6 +1268,7 @@ static void query_target_detailed_objects_tree(BUFFER *wb, RRDR *r, RRDR_OPTIONS
1268
1269 void version_hashes_api_v2(BUFFER *wb, struct query_versions *versions) {
1270 buffer_json_member_add_object(wb, "versions");
1271 + buffer_json_member_add_uint64(wb, "routing_hard_hash", 1);
1272 buffer_json_member_add_uint64(wb, "nodes_hard_hash", dictionary_version(rrdhost_root_index));
1273 buffer_json_member_add_uint64(wb, "contexts_hard_hash", versions->contexts_hard_hash);
1274 buffer_json_member_add_uint64(wb, "contexts_soft_hash", versions->contexts_soft_hash);
@@ -1569,7 +1570,7 @@ void rrdr_json_wrapper_end2(RRDR *r, BUFFER *wb) {
1570 }
1571 buffer_json_object_close(wb); // view
1572
1572 - buffer_json_agents_array_v2(wb, &r->internal.qt->timings, 0);
1573 + buffer_json_agents_array_v2(wb, &r->internal.qt->timings, 0, false);
1574 buffer_json_cloud_timings(wb, "timings", &r->internal.qt->timings);
1575 buffer_json_finalize(wb);
1576 }
web/api/queries/weights.c
+3 -3
@@ -874,7 +874,7 @@ static size_t registered_results_to_json_multinode_no_group_by(
874 continue;
875
876 buffer_json_add_array_item_object(wb);
877 - buffer_json_node_add_v2(wb, dun->host, dun->i, dun->duration_ut);
877 + buffer_json_node_add_v2(wb, dun->host, dun->i, dun->duration_ut, true);
878 buffer_json_object_close(wb);
879 }
880 dfe_done(dun);
@@ -936,7 +936,7 @@ static size_t registered_results_to_json_multinode_no_group_by(
936
937 buffer_json_object_close(wb); //dictionaries
938
939 - buffer_json_agents_array_v2(wb, &qwd->timings, 0);
939 + buffer_json_agents_array_v2(wb, &qwd->timings, 0, false);
940 buffer_json_member_add_uint64(wb, "correlated_dimensions", total_dimensions);
941 buffer_json_member_add_uint64(wb, "total_dimensions_count", examined_dimensions);
942 buffer_json_finalize(wb);
@@ -1067,7 +1067,7 @@ static size_t registered_results_to_json_multinode_group_by(
1067 dfe_done(aw);
1068 buffer_json_array_close(wb); // result
1069
1070 - buffer_json_agents_array_v2(wb, &qwd->timings, 0);
1070 + buffer_json_agents_array_v2(wb, &qwd->timings, 0, false);
1071 buffer_json_member_add_uint64(wb, "correlated_dimensions", total_dimensions);
1072 buffer_json_member_add_uint64(wb, "total_dimensions_count", examined_dimensions);
1073 buffer_json_finalize(wb);
web/api/web_api_v1.c
+1 -1
@@ -233,7 +233,7 @@ void web_client_api_request_v1_data_options_to_string(char *buf, size_t size, RR
233 *write = *end = '\0';
234 }
235
236 -inline DATASOURCE_FORMAT web_client_api_request_v1_data_format(char *name) {
236 +inline uint32_t web_client_api_request_v1_data_format(char *name) {
237 uint32_t hash = simple_hash(name);
238 int i;
239
web/api/web_api_v2.c
+19 -8
@@ -18,30 +18,40 @@ static int web_client_api_request_v2_contexts_internal(RRDHOST *host __maybe_unu
18 // they are not null and not empty
19
20 if(!strcmp(name, "scope_nodes")) req.scope_nodes = value;
21 - else if((options & (CONTEXTS_V2_NODES | CONTEXTS_V2_CONTEXTS)) && !strcmp(name, "nodes")) req.nodes = value;
22 - else if((options & CONTEXTS_V2_CONTEXTS) && !strcmp(name, "scope_contexts")) req.scope_contexts = value;
23 - else if((options & CONTEXTS_V2_CONTEXTS) && !strcmp(name, "contexts")) req.contexts = value;
21 + else if(!strcmp(name, "nodes")) req.nodes = value;
22 + else if((options & (CONTEXTS_V2_CONTEXTS | CONTEXTS_V2_SEARCH)) && !strcmp(name, "scope_contexts")) req.scope_contexts = value;
23 + else if((options & (CONTEXTS_V2_CONTEXTS | CONTEXTS_V2_SEARCH)) && !strcmp(name, "contexts")) req.contexts = value;
24 else if((options & CONTEXTS_V2_SEARCH) && !strcmp(name, "q")) req.q = value;
25 + else if(!strcmp(name, "options")) {
26 + if(strstr(value, "minify"))
27 + options |= CONTEXTS_V2_MINIFY;
28 + else if(strstr(value, "debug"))
29 + options |= CONTEXTS_V2_DEBUG;
30 + }
31 + else if(!strcmp(name, "after")) req.after = str2l(value);
32 + else if(!strcmp(name, "before")) req.before = str2l(value);
33 else if(!strcmp(name, "timeout")) req.timeout_ms = str2l(value);
34 }
35
28 - options |= CONTEXTS_V2_DEBUG;
29 -
36 buffer_flush(w->response.data);
37 buffer_no_cacheable(w->response.data);
38 return rrdcontext_to_json_v2(w->response.data, &req, options);
39 }
40
41 static int web_client_api_request_v2_q(RRDHOST *host __maybe_unused, struct web_client *w, char *url) {
36 - return web_client_api_request_v2_contexts_internal(host, w, url, CONTEXTS_V2_SEARCH | CONTEXTS_V2_CONTEXTS | CONTEXTS_V2_NODES);
42 + return web_client_api_request_v2_contexts_internal(host, w, url, CONTEXTS_V2_SEARCH | CONTEXTS_V2_CONTEXTS | CONTEXTS_V2_NODES | CONTEXTS_V2_AGENTS | CONTEXTS_V2_VERSIONS);
43 }
44
45 static int web_client_api_request_v2_contexts(RRDHOST *host __maybe_unused, struct web_client *w, char *url) {
40 - return web_client_api_request_v2_contexts_internal(host, w, url, CONTEXTS_V2_CONTEXTS);
46 + return web_client_api_request_v2_contexts_internal(host, w, url, CONTEXTS_V2_CONTEXTS | CONTEXTS_V2_NODES | CONTEXTS_V2_AGENTS | CONTEXTS_V2_VERSIONS);
47 }
48
49 static int web_client_api_request_v2_nodes(RRDHOST *host __maybe_unused, struct web_client *w, char *url) {
44 - return web_client_api_request_v2_contexts_internal(host, w, url, CONTEXTS_V2_NODES | CONTEXTS_V2_NODES_DETAILED);
50 + return web_client_api_request_v2_contexts_internal(host, w, url, CONTEXTS_V2_NODES | CONTEXTS_V2_NODES_INFO);
51 +}
52 +
53 +static int web_client_api_request_v2_nodes_instances(RRDHOST *host __maybe_unused, struct web_client *w, char *url) {
54 + return web_client_api_request_v2_contexts_internal(host, w, url, CONTEXTS_V2_NODES | CONTEXTS_V2_NODES_INFO | CONTEXTS_V2_NODES_INSTANCES | CONTEXTS_V2_AGENTS | CONTEXTS_V2_AGENTS_INFO | CONTEXTS_V2_VERSIONS);
55 }
56
57 static int web_client_api_request_v2_weights(RRDHOST *host __maybe_unused, struct web_client *w, char *url) {
@@ -348,6 +358,7 @@ static int web_client_api_request_v2_webrtc(RRDHOST *host __maybe_unused, struct
358 static struct web_api_command api_commands_v2[] = {
359 {"data", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_data},
360 {"nodes", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_nodes},
361 + {"nodes_instances", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_nodes_instances},
362 {"contexts", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_contexts},
363 {"weights", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_weights},
364 {"q", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_q},