| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "commands.h" |
| 4 | #include "../stream-receiver-internals.h" |
| 5 | #include "../stream-sender-internals.h" |
| 6 | #include "plugins.d/pluginsd_internals.h" |
| 7 | |
| 8 | // the child disconnected from the parent, and it has to clear the parent's claim id |
| 9 | void stream_sender_clear_parent_claim_id(RRDHOST *host) { |
| 10 | if (!UUIDiszero(host->aclk.claim_id_of_parent)) { |
| 11 | nd_log(NDLS_DAEMON, NDLP_INFO, |
| 12 | "Host '%s' [PCLAIMID] cleared parent's claim id", |
| 13 | rrdhost_hostname(host)); |
| 14 | |
| 15 | host->aclk.claim_id_of_parent = UUID_ZERO; |
| 16 | } |
| 17 | } |
| 18 | |
| 19 | // the parent sends to the child its claim id, node id and cloud url |
| 20 | void stream_receiver_send_node_and_claim_id_to_child(RRDHOST *host) { |
| 21 | if(rrdhost_is_local(host) || UUIDiszero(host->node_id)) return; |
| 22 | |
| 23 | rrdhost_receiver_lock(host); |
| 24 | if(stream_has_capability(host->receiver, STREAM_CAP_NODE_ID)) { |
| 25 | char node_id_str[UUID_STR_LEN] = ""; |
| 26 | uuid_unparse_lower(host->node_id.uuid, node_id_str); |
| 27 | |
| 28 | CLAIM_ID claim_id = claim_id_get(); |
| 29 | |
| 30 | if((!claim_id_is_set(claim_id) || !aclk_online())) { |
| 31 | // the agent is not claimed or not connected, just use parent claim id |
| 32 | // to allow the connection flow. |
| 33 | // this may be zero and it is ok. |
| 34 | claim_id.uuid = host->aclk.claim_id_of_parent; |
| 35 | uuid_unparse_lower(claim_id.uuid.uuid, claim_id.str); |
| 36 | } |
| 37 | |
| 38 | char buf[4096]; |
| 39 | snprintfz(buf, sizeof(buf), |
| 40 | PLUGINSD_KEYWORD_NODE_ID " '%s' '%s' '%s'\n", |
| 41 | claim_id.str, node_id_str, cloud_config_url_get()); |
| 42 | |
| 43 | send_to_plugin(buf, __atomic_load_n(&host->receiver->thread.parser, __ATOMIC_RELAXED), STREAM_TRAFFIC_TYPE_METADATA); |
| 44 | } |
| 45 | rrdhost_receiver_unlock(host); |
| 46 | } |
| 47 | |
| 48 | // the sender of the child receives node id, claim id and cloud url from the receiver of the parent |
| 49 | void stream_sender_get_node_and_claim_id_from_parent(struct sender_state *s, const char *claim_id_str, const char *node_id_str, const char *url) { |
| 50 | |
| 51 | bool claimed = is_agent_claimed(); |
| 52 | bool update_node_id = false; |
| 53 | |
| 54 | // ---------------------------------------------------------------------------------------------------------------- |
| 55 | // validate the parameters |
| 56 | |
| 57 | ND_UUID claim_id; |
| 58 | if (uuid_parse(claim_id_str ? claim_id_str : "", claim_id.uuid) != 0) { |
| 59 | nd_log(NDLS_DAEMON, NDLP_ERR, |
| 60 | "STREAM SND '%s' [to %s] [PCLAIMID]: received invalid claim id '%s'", |
| 61 | rrdhost_hostname(s->host), s->remote_ip, |
| 62 | claim_id_str ? claim_id_str : "(unset)"); |
| 63 | return; |
| 64 | } |
| 65 | |
| 66 | if(UUIDiszero(claim_id)) { |
| 67 | nd_log(NDLS_DAEMON, NDLP_DEBUG, |
| 68 | "STREAM SND '%s' [to %s] [PCLAIMID]: received zero claim id '%s'", |
| 69 | rrdhost_hostname(s->host), s->remote_ip, |
| 70 | claim_id_str ? claim_id_str : "(unset)"); |
| 71 | return; |
| 72 | } |
| 73 | |
| 74 | ND_UUID node_id; |
| 75 | if(uuid_parse(node_id_str ? node_id_str : "", node_id.uuid) != 0) { |
| 76 | nd_log(NDLS_DAEMON, NDLP_ERR, |
| 77 | "STREAM SND '%s' [to %s] [PCLAIMID] received an invalid node id '%s'", |
| 78 | rrdhost_hostname(s->host), s->remote_ip, |
| 79 | node_id_str ? node_id_str : "(unset)"); |
| 80 | return; |
| 81 | } |
| 82 | |
| 83 | if(UUIDiszero(node_id)) { |
| 84 | nd_log(NDLS_DAEMON, NDLP_DEBUG, |
| 85 | "STREAM SND '%s' [to %s] [PCLAIMID]: received zero node id '%s'", |
| 86 | rrdhost_hostname(s->host), s->remote_ip, |
| 87 | node_id_str ? node_id_str : "(unset)"); |
| 88 | return; |
| 89 | } |
| 90 | |
| 91 | if(!url || !*url) { |
| 92 | nd_log(NDLS_DAEMON, NDLP_ERR, |
| 93 | "STREAM SND '%s' [to %s] [PCLAIMID] received an invalid cloud URL '%s'", |
| 94 | rrdhost_hostname(s->host), s->remote_ip, |
| 95 | url ? url : "(unset)"); |
| 96 | return; |
| 97 | } |
| 98 | |
| 99 | // ---------------------------------------------------------------------------------------------------------------- |
| 100 | // the parameters are ok |
| 101 | // apply the changes |
| 102 | |
| 103 | if (!UUIDeq(s->host->aclk.claim_id_of_parent, claim_id)) { |
| 104 | if(UUIDiszero(s->host->aclk.claim_id_of_parent)) |
| 105 | nd_log(NDLS_DAEMON, NDLP_INFO, |
| 106 | "STREAM SND '%s' [to %s] [PCLAIMID] set parent's claim id to %s (was empty)", |
| 107 | rrdhost_hostname(s->host), s->remote_ip, |
| 108 | claim_id_str ? claim_id_str : "(unset)"); |
| 109 | else |
| 110 | nd_log(NDLS_DAEMON, NDLP_INFO, |
| 111 | "STREAM SND '%s' [to %s] [PCLAIMID] changed parent's claim id to %s (was set)", |
| 112 | rrdhost_hostname(s->host), s->remote_ip, |
| 113 | claim_id_str ? claim_id_str : "(unset)"); |
| 114 | |
| 115 | s->host->aclk.claim_id_of_parent = claim_id; |
| 116 | } |
| 117 | |
| 118 | if(!UUIDiszero(s->host->node_id) && !UUIDeq(s->host->node_id, node_id)) { |
| 119 | if(claimed) { |
| 120 | update_node_id = false; |
| 121 | nd_log(NDLS_DAEMON, NDLP_WARNING, |
| 122 | "STREAM SND '%s' [to %s] [PCLAIMID] parent reports different node id '%s', but we are claimed. Ignoring it.", |
| 123 | rrdhost_hostname(s->host), s->remote_ip, |
| 124 | node_id_str ? node_id_str : "(unset)"); |
| 125 | } |
| 126 | else { |
| 127 | update_node_id = true; |
| 128 | nd_log(NDLS_DAEMON, NDLP_WARNING, |
| 129 | "STREAM SND '%s' [to %s] [PCLAIMID] changed node id to %s", |
| 130 | rrdhost_hostname(s->host), s->remote_ip, |
| 131 | node_id_str ? node_id_str : "(unset)"); |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | // There are some very strange corner cases here: |
| 136 | // |
| 137 | // - Agent is claimed but offline, and it receives node_id and cloud_url from a different Netdata Cloud. |
| 138 | // - Agent is configured to talk to an on-prem Netdata Cloud, it is offline, but the parent is connected |
| 139 | // to a different Netdata Cloud. |
| 140 | // |
| 141 | // The solution below, tries to get the agent online, using the latest information. |
| 142 | // So, if the agent is not claimed or not connected, we inherit whatever information sent from the parent, |
| 143 | // to allow the user to work with it. |
| 144 | |
| 145 | if(claimed && aclk_online()) |
| 146 | // we are directly claimed and connected, ignore node id and cloud url |
| 147 | return; |
| 148 | |
| 149 | bool node_id_updated = false; |
| 150 | if(UUIDiszero(s->host->node_id) || update_node_id) { |
| 151 | s->host->node_id = node_id; |
| 152 | node_id_updated = true; |
| 153 | } |
| 154 | |
| 155 | // we change the URL, to allow the agent dashboard to work with Netdata Cloud on-prem, if any. |
| 156 | if(node_id_updated) |
| 157 | cloud_config_url_set(url); |
| 158 | |
| 159 | // send it down the line (to children) |
| 160 | stream_receiver_send_node_and_claim_id_to_child(s->host); |
| 161 | |
| 162 | if(node_id_updated) |
| 163 | stream_path_node_id_updated(s->host); |
| 164 | } |