@cryptotaxi247 / netdata-1 / commits / 0f8175dd3

Kubernetes labels (#10107)

Co-authored-by: Markos Fountoulakis <markos.fountoulakis.senior@gmail.com> Co-authored-by: Vladimir Kobal <vlad@prokk.net>

Ilya Mashchenko committed Dec 14, 2020 at 17:27 UTC 0f8175dd3060691394e263cdab01c8f940b1b5d3
38 files changed +1242 -458
CMakeLists.txt
+18
@@ -590,6 +590,7 @@ set(RRD_PLUGIN_FILES
590 database/rrddimvar.h
591 database/rrdfamily.c
592 database/rrdhost.c
593 + database/rrdlabels.c
594 database/rrd.c
595 database/rrd.h
596 database/rrdset.c
@@ -1332,12 +1333,29 @@ endif()
1333 target_link_libraries(valid_urls_testdriver libnetdata ${NETDATA_COMMON_LIBRARIES} ${CMOCKA_LIBRARIES})
1334 # add_test(NAME test_valid_urls COMMAND valid_urls_testdriver)
1335
1336 + set(CGROUPS_TEST_FILES
1337 + collectors/cgroups.plugin/tests/test_cgroups_plugin.c
1338 + collectors/cgroups.plugin/tests/test_cgroups_plugin.h
1339 + collectors/cgroups.plugin/tests/test_doubles.c
1340 + database/rrdlabels.c
1341 + database/rrd.h
1342 + )
1343 + add_executable(cgroups_testdriver ${CGROUPS_TEST_FILES} ${CGROUPS_PLUGIN_FILES})
1344 + target_link_options(
1345 + cgroups_testdriver
1346 + PRIVATE
1347 + -Wl,--wrap=add_label_to_list
1348 + )
1349 + target_link_libraries(cgroups_testdriver libnetdata ${NETDATA_COMMON_LIBRARIES} ${CMOCKA_LIBRARIES})
1350 + add_test(NAME test_cgroups COMMAND cgroups_testdriver)
1351 +
1352 set_target_properties(
1353 str2ld_testdriver
1354 storage_number_testdriver
1355 exporting_engine_testdriver
1356 web_api_testdriver
1357 valid_urls_testdriver
1358 + cgroups_testdriver
1359 PROPERTIES RUNTIME_OUTPUT_DIRECTORY tests
1360 )
1361
Makefile.am
+17
@@ -366,6 +366,7 @@ RRD_PLUGIN_FILES = \
366 database/rrddimvar.h \
367 database/rrdfamily.c \
368 database/rrdhost.c \
369 + database/rrdlabels.c \
370 database/rrd.c \
371 database/rrd.h \
372 database/rrdset.c \
@@ -854,6 +855,7 @@ if ENABLE_UNITTESTS
855 exporting/tests/exporting_engine_testdriver \
856 web/api/tests/web_api_testdriver \
857 web/api/tests/valid_urls_testdriver \
858 + collectors/cgroups_plugin/tests/cgroups_testdriver \
859 $(NULL)
860
861 TESTS = $(check_PROGRAMS)
@@ -1006,4 +1008,19 @@ if ENABLE_BACKEND_MONGODB
1008 -Wl,--wrap=mongoc_collection_insert_many \
1009 $(NULL)
1010 endif
1011 +
1012 + collectors_cgroups_plugin_tests_cgroups_testdriver_SOURCES = \
1013 + collectors/cgroups.plugin/tests/test_cgroups_plugin.c \
1014 + collectors/cgroups.plugin/tests/test_cgroups_plugin.h \
1015 + collectors/cgroups.plugin/tests/test_doubles.c \
1016 + $(CGROUPS_PLUGIN_FILES) \
1017 + database/rrdlabels.c \
1018 + database/rrd.h \
1019 + $(LIBNETDATA_FILES) \
1020 + $(NULL)
1021 + collectors_cgroups_plugin_tests_cgroups_testdriver_LDADD = $(NETDATA_COMMON_LIBS) $(TEST_LIBS)
1022 + collectors_cgroups_plugin_tests_cgroups_testdriver_LDFLAGS = \
1023 + -Wl,--wrap=add_label_to_list \
1024 + $(NULL)
1025 +
1026 endif
collectors/cgroups.plugin/cgroup-name.sh.in
+239 -168
@@ -17,63 +17,63 @@ export LC_ALL=C
17 PROGRAM_NAME="$(basename "${0}")"
18
19 logdate() {
20 - date "+%Y-%m-%d %H:%M:%S"
20 + date "+%Y-%m-%d %H:%M:%S"
21 }
22
23 log() {
24 - local status="${1}"
25 - shift
24 + local status="${1}"
25 + shift
26
27 - echo >&2 "$(logdate): ${PROGRAM_NAME}: ${status}: ${*}"
27 + echo >&2 "$(logdate): ${PROGRAM_NAME}: ${status}: ${*}"
28
29 }
30
31 warning() {
32 - log WARNING "${@}"
32 + log WARNING "${@}"
33 }
34
35 error() {
36 - log ERROR "${@}"
36 + log ERROR "${@}"
37 }
38
39 info() {
40 - log INFO "${@}"
40 + log INFO "${@}"
41 }
42
43 fatal() {
44 - log FATAL "${@}"
45 - exit 1
44 + log FATAL "${@}"
45 + exit 1
46 }
47
48 function docker_like_get_name_command() {
49 - local command="${1}"
50 - local id="${2}"
51 - info "Running command: ${command} ps --filter=id=\"${id}\" --format=\"{{.Names}}\""
52 - NAME="$(${command} ps --filter=id="${id}" --format="{{.Names}}")"
53 - return 0
49 + local command="${1}"
50 + local id="${2}"
51 + info "Running command: ${command} ps --filter=id=\"${id}\" --format=\"{{.Names}}\""
52 + NAME="$(${command} ps --filter=id="${id}" --format="{{.Names}}")"
53 + return 0
54 }
55
56 function docker_like_get_name_api() {
57 - local host_var="${1}"
58 - local host="${!host_var}"
59 - local path="/containers/${2}/json"
60 - if [ -z "${host}" ]; then
61 - warning "No ${host_var} is set"
62 - return 1
63 - fi
64 - if ! command -v jq >/dev/null 2>&1; then
65 - warning "Can't find jq command line tool. jq is required for netdata to retrieve container name using ${host} API, falling back to docker ps"
66 - return 1
67 - fi
68 - if [ -S "${host}" ]; then
69 - info "Running API command: curl --unix-socket \"${host}\" http://localhost${path}"
70 - JSON=$(curl -sS --unix-socket "${host}" "http://localhost${path}")
71 - else
72 - info "Running API command: curl \"${host}${path}\""
73 - JSON=$(curl -sS "${host}${path}")
74 - fi
75 - NAME=$(echo "${JSON}" | jq -r .Name,.Config.Hostname | grep -v null | head -n1 | sed 's|^/||')
76 - return 0
57 + local host_var="${1}"
58 + local host="${!host_var}"
59 + local path="/containers/${2}/json"
60 + if [ -z "${host}" ]; then
61 + warning "No ${host_var} is set"
62 + return 1
63 + fi
64 + if ! command -v jq > /dev/null 2>&1; then
65 + warning "Can't find jq command line tool. jq is required for netdata to retrieve container name using ${host} API, falling back to docker ps"
66 + return 1
67 + fi
68 + if [ -S "${host}" ]; then
69 + info "Running API command: curl --unix-socket \"${host}\" http://localhost${path}"
70 + JSON=$(curl -sS --unix-socket "${host}" "http://localhost${path}")
71 + else
72 + info "Running API command: curl \"${host}${path}\""
73 + JSON=$(curl -sS "${host}${path}")
74 + fi
75 + NAME=$(echo "${JSON}" | jq -r .Name,.Config.Hostname | grep -v null | head -n1 | sed 's|^/||')
76 + return 0
77 }
78
79 # get_lbl_val returns the value for the label with the given name.
@@ -99,6 +99,21 @@ function get_lbl_val() {
99 return 1
100 }
101
102 +function add_lbl_prefix() {
103 + local orig_labels prefix
104 + orig_labels="${1}"
105 + prefix="${2}"
106 +
107 + IFS=, read -ra labels <<< "$orig_labels"
108 +
109 + local new_labels
110 + for l in "${labels[@]}"; do
111 + new_labels+="${prefix}${l},"
112 + done
113 +
114 + echo "${new_labels:0:-1}" # trim last ','
115 +}
116 +
117 # k8s_get_kubepod_name resolves */kubepods/* cgroup name.
118 # pod level cgroup name format: 'pod_<namespace>_<pod_name>'
119 # container level cgroup name format: 'cntr_<namespace>_<pod_name>_<container_name>'
@@ -122,11 +137,11 @@ function k8s_get_kubepod_name() {
137 #
138 # NOTE: cgroups plugin uses '_' to join dir names, so it is <parent>_<child>_<child>_...
139
125 - local funcname="${FUNCNAME[0]}"
140 + local fn="${FUNCNAME[0]}"
141 local id="${1}"
142
143 if [[ ! $id =~ ^kubepods ]]; then
129 - warning "${funcname}: '${id}' is not kubepod cgroup."
144 + warning "${fn}: '${id}' is not kubepod cgroup."
145 return 1
146 fi
147
@@ -160,72 +175,121 @@ function k8s_get_kubepod_name() {
175 fi
176
177 if [ -z "$pod_uid" ] && [ -z "$cntr_id" ]; then
163 - warning "${funcname}: can't extract pod_uid or container_id from the cgroup '$id'."
178 + warning "${fn}: can't extract pod_uid or container_id from the cgroup '$id'."
179 return 1
180 fi
181
167 - [ -n "$pod_uid" ] && info "${funcname}: cgroup '$id' is a pod(uid:$pod_uid)"
168 - [ -n "$cntr_id" ] && info "${funcname}: cgroup '$id' is a container(id:$cntr_id)"
182 + [ -n "$pod_uid" ] && info "${fn}: cgroup '$id' is a pod(uid:$pod_uid)"
183 + [ -n "$cntr_id" ] && info "${fn}: cgroup '$id' is a container(id:$cntr_id)"
184
185 if ! command -v jq > /dev/null 2>&1; then
171 - warning "${funcname}: 'jq' command not available."
186 + warning "${fn}: 'jq' command not available."
187 return 1
188 fi
189
190 + local kube_system_ns
191 + local tmp_kube_system_ns_file="${TMPDIR:-"/tmp/"}netdata-cgroups-kube-system-ns"
192 + [ -f "$tmp_kube_system_ns_file" ] && kube_system_ns=$(cat "$tmp_kube_system_ns_file" 2> /dev/null)
193 +
194 local pods
195 if [ -n "${KUBERNETES_SERVICE_HOST}" ] && [ -n "${KUBERNETES_PORT_443_TCP_PORT}" ]; then
177 - local token header url
196 + local token header host url
197 token="$(< /var/run/secrets/kubernetes.io/serviceaccount/token)"
198 header="Authorization: Bearer $token"
180 - url="https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT/api/v1/pods"
199 + host="$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT"
200 +
201 + if [ -z "$kube_system_ns" ]; then
202 + url="https://$host/api/v1/namespaces/kube-system"
203 + # FIX: check HTTP response code
204 + if ! kube_system_ns=$(curl -sSk -H "$header" "$url" 2>&1); then
205 + warning "${fn}: error on curl '${url}': ${kube_system_ns}."
206 + else
207 + echo "$kube_system_ns" > "$tmp_kube_system_ns_file" 2> /dev/null
208 + fi
209 + fi
210 +
211 + url="https://$host/api/v1/pods"
212 + [ -n "$MY_NODE_NAME" ] && url+="?fieldSelector=spec.nodeName==$MY_NODE_NAME"
213 + # FIX: check HTTP response code
214 if ! pods=$(curl -sSk -H "$header" "$url" 2>&1); then
182 - warning "${funcname}: error on curl '${url}': ${pods}."
215 + warning "${fn}: error on curl '${url}': ${pods}."
216 return 1
217 fi
218 elif ps -C kubelet > /dev/null 2>&1 && command -v kubectl > /dev/null 2>&1; then
219 + if [ -z "$kube_system_ns" ]; then
220 + if ! kube_system_ns=$(kubectl get namespaces kube-system -o json 2>&1); then
221 + warning "${fn}: error on 'kubectl': ${kube_system_ns}."
222 + else
223 + echo "$kube_system_ns" > "$tmp_kube_system_ns_file" 2> /dev/null
224 + fi
225 + fi
226 +
227 [[ -z ${KUBE_CONFIG+x} ]] && KUBE_CONFIG="/etc/kubernetes/admin.conf"
228 if ! pods=$(kubectl --kubeconfig="$KUBE_CONFIG" get pods --all-namespaces -o json 2>&1); then
188 - warning "${funcname}: error on 'kubectl': ${pods}."
229 + warning "${fn}: error on 'kubectl': ${pods}."
230 return 1
231 fi
232 else
192 - warning "${funcname}: not inside the k8s cluster and 'kubectl' command not available."
233 + warning "${fn}: not inside the k8s cluster and 'kubectl' command not available."
234 return 1
235 fi
236
237 + local kube_system_uid
238 + if [ -n "$kube_system_ns" ] && ! kube_system_uid=$(jq -r '.metadata.uid' <<< "$kube_system_ns" 2>&1); then
239 + warning "${fn}: error on 'jq' parse kube_system_ns: ${kube_system_uid}."
240 + fi
241 +
242 local jq_filter
197 - # 'namespace="<NAMESPACE>",pod_name="<NAME>",pod_uid="<UID>",container_name="<NAME>",container_ID="<ID>"'
198 - # wrong field value is 'null'
199 - jq_filter+='.items[] | '
200 - jq_filter+='"namespace=\"\(.metadata.namespace)\",pod_name=\"\(.metadata.name)\",pod_uid=\"\(.metadata.uid)\"" + '
201 - jq_filter+='(.status.containerStatuses[]? | '
202 - jq_filter+='",container_name=\"\(.name)\",container_id=\"\(.containerID)\""'
203 - jq_filter+=') | '
204 - jq_filter+='sub("docker://";"")'
243 + jq_filter+='.items[] | "'
244 + jq_filter+='namespace=\"\(.metadata.namespace)\",'
245 + jq_filter+='pod_name=\"\(.metadata.name)\",'
246 + jq_filter+='pod_uid=\"\(.metadata.uid)\",'
247 + #jq_filter+='\(.metadata.labels | to_entries | map("pod_label_"+.key+"=\""+.value+"\"") | join(",") | if length > 0 then .+"," else . end)'
248 + jq_filter+='\((.metadata.ownerReferences[]? | select(.controller==true) | "controller_kind=\""+.kind+"\",controller_name=\""+.name+"\",") // "")'
249 + jq_filter+='node_name=\"\(.spec.nodeName)\",'
250 + jq_filter+='" + '
251 + jq_filter+='(.status.containerStatuses[]? | "'
252 + jq_filter+='container_name=\"\(.name)\",'
253 + jq_filter+='container_id=\"\(.containerID)\"'
254 + jq_filter+='") | '
255 + jq_filter+='sub("docker://";"")' # containerID: docker://a346da9bc0e3eaba6b295f64ac16e02f2190db2cef570835706a9e7a36e2c722
256
257 local containers
258 if ! containers=$(jq -r "${jq_filter}" <<< "$pods" 2>&1); then
208 - warning "${funcname}: error on 'jq' parse: ${containers}."
259 + warning "${fn}: error on 'jq' parse pods: ${containers}."
260 return 1
261 fi
262
263 # available labels:
213 - # namespace, pod_name, pod_uid, container_name, container_id
264 + # namespace, pod_name, pod_uid, container_name, container_id, node_name
265 local labels
266 if [ -n "$cntr_id" ]; then
267 if labels=$(grep "$cntr_id" <<< "$containers" 2> /dev/null); then
217 - name="cntr_$(get_lbl_val "$labels" namespace)_$(get_lbl_val "$labels" pod_name)_$(get_lbl_val "$labels" container_name)"
268 + labels+=',kind="container"'
269 + [ -n "$kube_system_uid" ] && [ "$kube_system_uid" != "null" ] && labels+=",cluster_id=\"$kube_system_uid\""
270 + name="cntr"
271 + name+="_$(get_lbl_val "$labels" namespace)"
272 + name+="_$(get_lbl_val "$labels" pod_name)"
273 + name+="_$(get_lbl_val "$labels" container_name)"
274 + labels=$(add_lbl_prefix "$labels" "k8s_")
275 + name+=" $labels"
276 fi
277 elif [ -n "$pod_uid" ]; then
278 if labels=$(grep "$pod_uid" -m 1 <<< "$containers" 2> /dev/null); then
221 - labels="${labels%%,cont*}"
222 - name="pod_$(get_lbl_val "$labels" namespace)_$(get_lbl_val "$labels" pod_name)"
279 + labels="${labels%%,container_*}"
280 + labels+=',kind="pod"'
281 + [ -n "$kube_system_uid" ] && [ "$kube_system_uid" != "null" ] && labels+=",cluster_id=\"$kube_system_uid\""
282 + name="pod"
283 + name+="_$(get_lbl_val "$labels" namespace)"
284 + name+="_$(get_lbl_val "$labels" pod_name)"
285 + labels=$(add_lbl_prefix "$labels" "k8s_")
286 + name+=" $labels"
287 fi
288 fi
289
290 # jq filter nonexistent field and nonexistent label value is 'null'
291 if [[ $name =~ _null(_|$) ]]; then
228 - warning "${funcname}: invalid name: $name (cgroup '$id')"
292 + warning "${fn}: invalid name: $name (cgroup '$id')"
293 name=""
294 fi
295
@@ -235,69 +299,77 @@ function k8s_get_kubepod_name() {
299 }
300
301 function k8s_get_name() {
238 - local funcname="${FUNCNAME[0]}"
302 + local fn="${FUNCNAME[0]}"
303 local id="${1}"
304
305 NAME=$(k8s_get_kubepod_name "$id")
306
307 if [ -z "${NAME}" ]; then
244 - warning "${funcname}: cannot find the name of cgroup with id '${id}'. Setting name to ${id} and disabling it."
308 + warning "${fn}: cannot find the name of cgroup with id '${id}'. Setting name to ${id} and disabling it."
309 NAME="${id}"
310 NAME_NOT_FOUND=3
311 else
312 NAME="k8s_${NAME}"
249 - info "${funcname}: cgroup '${id}' has chart name '${NAME}'"
313 +
314 + local name labels
315 + name=${NAME%% *}
316 + labels=${NAME#* }
317 + if [ "$name" != "$labels" ]; then
318 + info "${fn}: cgroup '${id}' has chart name '${name}', labels '${labels}"
319 + else
320 + info "${fn}: cgroup '${id}' has chart name '${NAME}'"
321 + fi
322 fi
323 }
324
325 function docker_get_name() {
254 - local id="${1}"
255 - if hash docker 2>/dev/null; then
256 - docker_like_get_name_command docker "${id}"
257 - else
258 - docker_like_get_name_api DOCKER_HOST "${id}" || docker_like_get_name_command podman "${id}"
259 - fi
260 - if [ -z "${NAME}" ]; then
261 - warning "cannot find the name of docker container '${id}'"
262 - NAME_NOT_FOUND=2
263 - NAME="${id:0:12}"
264 - else
265 - info "docker container '${id}' is named '${NAME}'"
266 - fi
326 + local id="${1}"
327 + if hash docker 2> /dev/null; then
328 + docker_like_get_name_command docker "${id}"
329 + else
330 + docker_like_get_name_api DOCKER_HOST "${id}" || docker_like_get_name_command podman "${id}"
331 + fi
332 + if [ -z "${NAME}" ]; then
333 + warning "cannot find the name of docker container '${id}'"
334 + NAME_NOT_FOUND=2
335 + NAME="${id:0:12}"
336 + else
337 + info "docker container '${id}' is named '${NAME}'"
338 + fi
339 }
340
341 function docker_validate_id() {
270 - local id="${1}"
271 - if [ -n "${id}" ] && { [ ${#id} -eq 64 ] || [ ${#id} -eq 12 ]; }; then
272 - docker_get_name "${id}"
273 - else
274 - error "a docker id cannot be extracted from docker cgroup '${CGROUP}'."
275 - fi
342 + local id="${1}"
343 + if [ -n "${id}" ] && { [ ${#id} -eq 64 ] || [ ${#id} -eq 12 ]; }; then
344 + docker_get_name "${id}"
345 + else
346 + error "a docker id cannot be extracted from docker cgroup '${CGROUP}'."
347 + fi
348 }
349
350 function podman_get_name() {
279 - local id="${1}"
280 -
281 - # for Podman, prefer using the API if we can, as netdata will not normally have access
282 - # to other users' containers, so they will not be visible when running `podman ps`
283 - docker_like_get_name_api PODMAN_HOST "${id}" || docker_like_get_name_command podman "${id}"
284 -
285 - if [ -z "${NAME}" ]; then
286 - warning "cannot find the name of podman container '${id}'"
287 - NAME_NOT_FOUND=2
288 - NAME="${id:0:12}"
289 - else
290 - info "podman container '${id}' is named '${NAME}'"
291 - fi
351 + local id="${1}"
352 +
353 + # for Podman, prefer using the API if we can, as netdata will not normally have access
354 + # to other users' containers, so they will not be visible when running `podman ps`
355 + docker_like_get_name_api PODMAN_HOST "${id}" || docker_like_get_name_command podman "${id}"
356 +
357 + if [ -z "${NAME}" ]; then
358 + warning "cannot find the name of podman container '${id}'"
359 + NAME_NOT_FOUND=2
360 + NAME="${id:0:12}"
361 + else
362 + info "podman container '${id}' is named '${NAME}'"
363 + fi
364 }
365
366 function podman_validate_id() {
295 - local id="${1}"
296 - if [ -n "${id}" ] && [ ${#id} -eq 64 ]; then
297 - podman_get_name "${id}"
298 - else
299 - error "a podman id cannot be extracted from docker cgroup '${CGROUP}'."
300 - fi
367 + local id="${1}"
368 + if [ -n "${id}" ] && [ ${#id} -eq 64 ]; then
369 + podman_get_name "${id}"
370 + else
371 + error "a podman id cannot be extracted from docker cgroup '${CGROUP}'."
372 + fi
373 }
374
375 # -----------------------------------------------------------------------------
@@ -314,86 +386,85 @@ NAME=
386 # -----------------------------------------------------------------------------
387
388 if [ -z "${CGROUP}" ]; then
317 - fatal "called without a cgroup name. Nothing to do."
389 + fatal "called without a cgroup name. Nothing to do."
390 fi
391
392 for CONFIG in "${NETDATA_USER_CONFIG_DIR}/cgroups-names.conf" "${NETDATA_STOCK_CONFIG_DIR}/cgroups-names.conf"; do
321 - if [ -f "${CONFIG}" ]; then
322 - NAME="$(grep "^${CGROUP} " "${CONFIG}" | sed 's/[[:space:]]\+/ /g' | cut -d ' ' -f 2)"
323 - if [ -z "${NAME}" ]; then
324 - info "cannot find cgroup '${CGROUP}' in '${CONFIG}'."
325 - else
326 - break
327 - fi
328 - #else
329 - # info "configuration file '${CONFIG}' is not available."
330 - fi
393 + if [ -f "${CONFIG}" ]; then
394 + NAME="$(grep "^${CGROUP} " "${CONFIG}" | sed 's/[[:space:]]\+/ /g' | cut -d ' ' -f 2)"
395 + if [ -z "${NAME}" ]; then
396 + info "cannot find cgroup '${CGROUP}' in '${CONFIG}'."
397 + else
398 + break
399 + fi
400 + #else
401 + # info "configuration file '${CONFIG}' is not available."
402 + fi
403 done
404
405 if [ -z "${NAME}" ]; then
334 - if [[ ${CGROUP} =~ ^.*kubepods.* ]]; then
335 - k8s_get_name "${CGROUP}"
336 - fi
406 + if [[ ${CGROUP} =~ ^.*kubepods.* ]]; then
407 + k8s_get_name "${CGROUP}"
408 + fi
409 fi
410
411 if [ -z "${NAME}" ]; then
340 - if [[ ${CGROUP} =~ ^.*docker[-_/\.][a-fA-F0-9]+[-_\.]?.*$ ]]; then
341 - # docker containers
342 - #shellcheck disable=SC1117
343 - DOCKERID="$(echo "${CGROUP}" | sed "s|^.*docker[-_/]\([a-fA-F0-9]\+\)[-_\.]\?.*$|\1|")"
344 - docker_validate_id "${DOCKERID}"
345 - elif [[ ${CGROUP} =~ ^.*ecs[-_/\.][a-fA-F0-9]+[-_\.]?.*$ ]]; then
346 - # ECS
347 - #shellcheck disable=SC1117
348 - DOCKERID="$(echo "${CGROUP}" | sed "s|^.*ecs[-_/].*[-_/]\([a-fA-F0-9]\+\)[-_\.]\?.*$|\1|")"
349 - docker_validate_id "${DOCKERID}"
350 - elif [[ ${CGROUP} =~ ^.*libpod-[a-fA-F0-9]+.*$ ]]; then
351 - # Podman
352 - PODMANID="$(echo "${CGROUP}" | sed "s|^.*libpod-\([a-fA-F0-9]\+\).*$|\1|")"
353 - podman_validate_id "${PODMANID}"
354 -
355 - elif [[ ${CGROUP} =~ machine.slice[_/].*\.service ]]; then
356 - # systemd-nspawn
357 - NAME="$(echo "${CGROUP}" | sed 's/.*machine.slice[_\/]\(.*\)\.service/\1/g')"
358 -
359 - elif [[ ${CGROUP} =~ machine.slice_machine.*-qemu ]]; then
360 - # libvirtd / qemu virtual machines
361 - # NAME="$(echo ${CGROUP} | sed 's/machine.slice_machine.*-qemu//; s/\/x2d//; s/\/x2d/\-/g; s/\.scope//g')"
362 - NAME="qemu_$(echo "${CGROUP}" | sed 's/machine.slice_machine.*-qemu//; s/\/x2d[[:digit:]]*//; s/\/x2d//g; s/\.scope//g')"
363 -
364 - elif [[ ${CGROUP} =~ machine_.*\.libvirt-qemu ]]; then
365 - # libvirtd / qemu virtual machines
366 - NAME="qemu_$(echo "${CGROUP}" | sed 's/^machine_//; s/\.libvirt-qemu$//; s/-/_/;')"
367 -
368 - elif [[ ${CGROUP} =~ qemu.slice_([0-9]+).scope && -d /etc/pve ]]; then
369 - # Proxmox VMs
370 -
371 - FILENAME="/etc/pve/qemu-server/${BASH_REMATCH[1]}.conf"
372 - if [[ -f $FILENAME && -r $FILENAME ]]; then
373 - NAME="qemu_$(grep -e '^name: ' "/etc/pve/qemu-server/${BASH_REMATCH[1]}.conf" | head -1 | sed -rn 's|\s*name\s*:\s*(.*)?$|\1|p')"
374 - else
375 - error "proxmox config file missing ${FILENAME} or netdata does not have read access. Please ensure netdata is a member of www-data group."
376 - fi
377 - elif [[ ${CGROUP} =~ lxc_([0-9]+) && -d /etc/pve ]]; then
378 - # Proxmox Containers (LXC)
379 -
380 - FILENAME="/etc/pve/lxc/${BASH_REMATCH[1]}.conf"
381 - if [[ -f ${FILENAME} && -r ${FILENAME} ]]; then
382 - NAME=$(grep -e '^hostname: ' "/etc/pve/lxc/${BASH_REMATCH[1]}.conf" | head -1 | sed -rn 's|\s*hostname\s*:\s*(.*)?$|\1|p')
383 - else
384 - error "proxmox config file missing ${FILENAME} or netdata does not have read access. Please ensure netdata is a member of www-data group."
385 - fi
386 - elif [[ ${CGROUP} =~ lxc.payload.* ]]; then
387 - # LXC 4.0
388 - NAME="$(echo "${CGROUP}" | sed 's/lxc\.payload\.\(.*\)/\1/g')"
389 - fi
390 -
391 - [ -z "${NAME}" ] && NAME="${CGROUP}"
392 - [ ${#NAME} -gt 100 ] && NAME="${NAME:0:100}"
412 + if [[ ${CGROUP} =~ ^.*docker[-_/\.][a-fA-F0-9]+[-_\.]?.*$ ]]; then
413 + # docker containers
414 + #shellcheck disable=SC1117
415 + DOCKERID="$(echo "${CGROUP}" | sed "s|^.*docker[-_/]\([a-fA-F0-9]\+\)[-_\.]\?.*$|\1|")"
416 + docker_validate_id "${DOCKERID}"
417 + elif [[ ${CGROUP} =~ ^.*ecs[-_/\.][a-fA-F0-9]+[-_\.]?.*$ ]]; then
418 + # ECS
419 + #shellcheck disable=SC1117
420 + DOCKERID="$(echo "${CGROUP}" | sed "s|^.*ecs[-_/].*[-_/]\([a-fA-F0-9]\+\)[-_\.]\?.*$|\1|")"
421 + docker_validate_id "${DOCKERID}"
422 + elif [[ ${CGROUP} =~ ^.*libpod-[a-fA-F0-9]+.*$ ]]; then
423 + # Podman
424 + PODMANID="$(echo "${CGROUP}" | sed "s|^.*libpod-\([a-fA-F0-9]\+\).*$|\1|")"
425 + podman_validate_id "${PODMANID}"
426 +
427 + elif [[ ${CGROUP} =~ machine.slice[_/].*\.service ]]; then
428 + # systemd-nspawn
429 + NAME="$(echo "${CGROUP}" | sed 's/.*machine.slice[_\/]\(.*\)\.service/\1/g')"
430 +
431 + elif [[ ${CGROUP} =~ machine.slice_machine.*-qemu ]]; then
432 + # libvirtd / qemu virtual machines
433 + # NAME="$(echo ${CGROUP} | sed 's/machine.slice_machine.*-qemu//; s/\/x2d//; s/\/x2d/\-/g; s/\.scope//g')"
434 + NAME="qemu_$(echo "${CGROUP}" | sed 's/machine.slice_machine.*-qemu//; s/\/x2d[[:digit:]]*//; s/\/x2d//g; s/\.scope//g')"
435 +
436 + elif [[ ${CGROUP} =~ machine_.*\.libvirt-qemu ]]; then
437 + # libvirtd / qemu virtual machines
438 + NAME="qemu_$(echo "${CGROUP}" | sed 's/^machine_//; s/\.libvirt-qemu$//; s/-/_/;')"
439 +
440 + elif [[ ${CGROUP} =~ qemu.slice_([0-9]+).scope && -d /etc/pve ]]; then
441 + # Proxmox VMs
442 +
443 + FILENAME="/etc/pve/qemu-server/${BASH_REMATCH[1]}.conf"
444 + if [[ -f $FILENAME && -r $FILENAME ]]; then
445 + NAME="qemu_$(grep -e '^name: ' "/etc/pve/qemu-server/${BASH_REMATCH[1]}.conf" | head -1 | sed -rn 's|\s*name\s*:\s*(.*)?$|\1|p')"
446 + else
447 + error "proxmox config file missing ${FILENAME} or netdata does not have read access. Please ensure netdata is a member of www-data group."
448 + fi
449 + elif [[ ${CGROUP} =~ lxc_([0-9]+) && -d /etc/pve ]]; then
450 + # Proxmox Containers (LXC)
451 +
452 + FILENAME="/etc/pve/lxc/${BASH_REMATCH[1]}.conf"
453 + if [[ -f ${FILENAME} && -r ${FILENAME} ]]; then
454 + NAME=$(grep -e '^hostname: ' "/etc/pve/lxc/${BASH_REMATCH[1]}.conf" | head -1 | sed -rn 's|\s*hostname\s*:\s*(.*)?$|\1|p')
455 + else
456 + error "proxmox config file missing ${FILENAME} or netdata does not have read access. Please ensure netdata is a member of www-data group."
457 + fi
458 + elif [[ ${CGROUP} =~ lxc.payload.* ]]; then
459 + # LXC 4.0
460 + NAME="$(echo "${CGROUP}" | sed 's/lxc\.payload\.\(.*\)/\1/g')"
461 + fi
462 +
463 + [ -z "${NAME}" ] && NAME="${CGROUP}"
464 + [ ${#NAME} -gt 100 ] && NAME="${NAME:0:100}"
465 fi
466
467 info "cgroup '${CGROUP}' is called '${NAME}'"
468 echo "${NAME}"
469
470 exit ${NAME_NOT_FOUND}
399 -
collectors/cgroups.plugin/sys_fs_cgroup.c
+103 -14
@@ -600,6 +600,8 @@ struct cgroup {
600
601 char *chart_title;
602
603 + struct label *chart_labels;
604 +
605 struct cpuacct_stat cpuacct_stat;
606 struct cpuacct_usage cpuacct_usage;
607
@@ -1226,7 +1228,7 @@ static inline void read_cgroup_network_interfaces(struct cgroup *cg) {
1228 info("CGROUP: cgroup '%s' has network interface '%s' as '%s'", cg->id, i->host_device, i->container_device);
1229
1230 // register a device rename to proc_net_dev.c
1229 - netdev_rename_device_add(i->host_device, i->container_device, cg->chart_id);
1231 + netdev_rename_device_add(i->host_device, i->container_device, cg->chart_id, cg->chart_labels);
1232 }
1233 }
1234
@@ -1275,6 +1277,35 @@ static inline char *cgroup_chart_id_strdupz(const char *s) {
1277 return r;
1278 }
1279
1280 +char *parse_k8s_data(struct label **labels, char *data)
1281 +{
1282 + char *name = mystrsep(&data, " ");
1283 +
1284 + if (!data) {
1285 + return name;
1286 + }
1287 +
1288 + while (data) {
1289 + char *key = mystrsep(&data, "=");
1290 +
1291 + char *value;
1292 + if (data && *data == ',') {
1293 + value = "";
1294 + *data++ = '\0';
1295 + } else {
1296 + value = mystrsep(&data, ",");
1297 + }
1298 + value = strip_double_quotes(value, 1);
1299 +
1300 + if (!key || *key == '\0' || !value || *value == '\0')
1301 + continue;
1302 +
1303 + *labels = add_label_to_list(*labels, key, value, LABEL_SOURCE_KUBERNETES);
1304 + }
1305 +
1306 + return name;
1307 +}
1308 +
1309 static inline void cgroup_get_chart_name(struct cgroup *cg) {
1310 debug(D_CGROUP, "looking for the name of cgroup '%s' with chart id '%s' and title '%s'", cg->id, cg->chart_id, cg->chart_title);
1311
@@ -1305,12 +1336,19 @@ static inline void cgroup_get_chart_name(struct cgroup *cg) {
1336 cg->enabled = 0;
1337 }
1338
1308 - if(likely(cg->pending_renames < 2)) {
1339 + if (likely(cg->pending_renames < 2)) {
1340 + char *name = s;
1341 +
1342 + if (!strncmp(s, "k8s_", 4)) {
1343 + free_label_list(cg->chart_labels);
1344 + name = parse_k8s_data(&cg->chart_labels, s);
1345 + }
1346 +
1347 freez(cg->chart_title);
1310 - cg->chart_title = cgroup_title_strdupz(s);
1348 + cg->chart_title = cgroup_title_strdupz(name);
1349
1350 freez(cg->chart_id);
1313 - cg->chart_id = cgroup_chart_id_strdupz(s);
1351 + cg->chart_id = cgroup_chart_id_strdupz(name);
1352 cg->hash_chart = simple_hash(cg->chart_id);
1353 }
1354 }
@@ -1508,6 +1546,8 @@ static inline void cgroup_free(struct cgroup *cg) {
1546 freez(cg->chart_id);
1547 freez(cg->chart_title);
1548
1549 + free_label_list(cg->chart_labels);
1550 +
1551 freez(cg);
1552
1553 cgroup_root_count--;
@@ -3022,6 +3062,9 @@ void update_cgroup_charts(int update_every) {
3062 , update_every
3063 , RRDSET_TYPE_STACKED
3064 );
3065 +
3066 + rrdset_update_labels(cg->st_cpu, cg->chart_labels);
3067 +
3068 if(!(cg->options & CGROUP_OPTIONS_IS_UNIFIED)) {
3069 rrddim_add(cg->st_cpu, "user", NULL, 100, system_hz, RRD_ALGORITHM_INCREMENTAL);
3070 rrddim_add(cg->st_cpu, "system", NULL, 100, system_hz, RRD_ALGORITHM_INCREMENTAL);
@@ -3093,6 +3136,8 @@ void update_cgroup_charts(int update_every) {
3136 , RRDSET_TYPE_LINE
3137 );
3138
3139 + rrdset_update_labels(cg->st_cpu_limit, cg->chart_labels);
3140 +
3141 if(!(cg->options & CGROUP_OPTIONS_IS_UNIFIED))
3142 rrddim_add(cg->st_cpu_limit, "used", NULL, 1, system_hz, RRD_ALGORITHM_ABSOLUTE);
3143 else
@@ -3146,6 +3191,8 @@ void update_cgroup_charts(int update_every) {
3191 , RRDSET_TYPE_STACKED
3192 );
3193
3194 + rrdset_update_labels(cg->st_cpu_per_core, cg->chart_labels);
3195 +
3196 for(i = 0; i < cg->cpuacct_usage.cpus; i++) {
3197 snprintfz(id, RRD_ID_LENGTH_MAX, "cpu%u", i);
3198 rrddim_add(cg->st_cpu_per_core, id, NULL, 100, 1000000000, RRD_ALGORITHM_INCREMENTAL);
@@ -3179,6 +3226,9 @@ void update_cgroup_charts(int update_every) {
3226 , update_every
3227 , RRDSET_TYPE_STACKED
3228 );
3229 +
3230 + rrdset_update_labels(cg->st_mem, cg->chart_labels);
3231 +
3232 if(!(cg->options & CGROUP_OPTIONS_IS_UNIFIED)) {
3233 rrddim_add(cg->st_mem, "cache", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
3234 rrddim_add(cg->st_mem, "rss", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
@@ -3237,6 +3287,8 @@ void update_cgroup_charts(int update_every) {
3287 , RRDSET_TYPE_AREA
3288 );
3289
3290 + rrdset_update_labels(cg->st_writeback, cg->chart_labels);
3291 +
3292 if(cg->memory.detailed_has_dirty)
3293 rrddim_add(cg->st_writeback, "dirty", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
3294
@@ -3270,6 +3322,8 @@ void update_cgroup_charts(int update_every) {
3322 , RRDSET_TYPE_LINE
3323 );
3324
3325 + rrdset_update_labels(cg->st_mem_activity, cg->chart_labels);
3326 +
3327 rrddim_add(cg->st_mem_activity, "pgpgin", "in", system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
3328 rrddim_add(cg->st_mem_activity, "pgpgout", "out", -system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
3329 }
@@ -3299,6 +3353,8 @@ void update_cgroup_charts(int update_every) {
3353 , RRDSET_TYPE_LINE
3354 );
3355
3356 + rrdset_update_labels(cg->st_pgfaults, cg->chart_labels);
3357 +
3358 rrddim_add(cg->st_pgfaults, "pgfault", NULL, system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
3359 rrddim_add(cg->st_pgfaults, "pgmajfault", "swap", -system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
3360 }
@@ -3329,6 +3385,8 @@ void update_cgroup_charts(int update_every) {
3385 , RRDSET_TYPE_STACKED
3386 );
3387
3388 + rrdset_update_labels(cg->st_mem_usage, cg->chart_labels);
3389 +
3390 rrddim_add(cg->st_mem_usage, "ram", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
3391 rrddim_add(cg->st_mem_usage, "swap", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
3392 }
@@ -3390,6 +3448,8 @@ void update_cgroup_charts(int update_every) {
3448 , RRDSET_TYPE_STACKED
3449 );
3450
3451 + rrdset_update_labels(cg->st_mem_usage_limit, cg->chart_labels);
3452 +
3453 rrddim_add(cg->st_mem_usage_limit, "available", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
3454 rrddim_add(cg->st_mem_usage_limit, "used", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
3455 }
@@ -3431,6 +3491,8 @@ void update_cgroup_charts(int update_every) {
3491 , update_every
3492 , RRDSET_TYPE_LINE
3493 );
3494 +
3495 + rrdset_update_labels(cg->st_mem_failcnt, cg->chart_labels);
3496
3497 rrddim_add(cg->st_mem_failcnt, "failures", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
3498 }
@@ -3460,6 +3522,8 @@ void update_cgroup_charts(int update_every) {
3522 , RRDSET_TYPE_AREA
3523 );
3524
3525 + rrdset_update_labels(cg->st_io, cg->chart_labels);
3526 +
3527 rrddim_add(cg->st_io, "read", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
3528 rrddim_add(cg->st_io, "write", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL);
3529 }
@@ -3490,6 +3554,8 @@ void update_cgroup_charts(int update_every) {
3554 , RRDSET_TYPE_LINE
3555 );
3556
3557 + rrdset_update_labels(cg->st_serviced_ops, cg->chart_labels);
3558 +
3559 rrddim_add(cg->st_serviced_ops, "read", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
3560 rrddim_add(cg->st_serviced_ops, "write", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
3561 }
@@ -3519,6 +3585,8 @@ void update_cgroup_charts(int update_every) {
3585 , update_every
3586 , RRDSET_TYPE_AREA
3587 );
3588 +
3589 + rrdset_update_labels(cg->st_throttle_io, cg->chart_labels);
3590
3591 rrddim_add(cg->st_throttle_io, "read", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
3592 rrddim_add(cg->st_throttle_io, "write", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL);
@@ -3549,6 +3617,8 @@ void update_cgroup_charts(int update_every) {
3617 , update_every
3618 , RRDSET_TYPE_LINE
3619 );
3620 +
3621 + rrdset_update_labels(cg->st_throttle_serviced_ops, cg->chart_labels);
3622
3623 rrddim_add(cg->st_throttle_serviced_ops, "read", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
3624 rrddim_add(cg->st_throttle_serviced_ops, "write", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
@@ -3579,6 +3649,8 @@ void update_cgroup_charts(int update_every) {
3649 , update_every
3650 , RRDSET_TYPE_LINE
3651 );
3652 +
3653 + rrdset_update_labels(cg->st_queued_ops, cg->chart_labels);
3654
3655 rrddim_add(cg->st_queued_ops, "read", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
3656 rrddim_add(cg->st_queued_ops, "write", NULL, -1, 1, RRD_ALGORITHM_ABSOLUTE);
@@ -3609,6 +3681,8 @@ void update_cgroup_charts(int update_every) {
3681 , update_every
3682 , RRDSET_TYPE_LINE
3683 );
3684 +
3685 + rrdset_update_labels(cg->st_merged_ops, cg->chart_labels);
3686
3687 rrddim_add(cg->st_merged_ops, "read", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
3688 rrddim_add(cg->st_merged_ops, "write", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL);
@@ -3639,8 +3713,11 @@ void update_cgroup_charts(int update_every) {
3713 , PLUGIN_CGROUPS_NAME
3714 , PLUGIN_CGROUPS_MODULE_CGROUPS_NAME
3715 , cgroup_containers_chart_priority + 2200
3642 - , update_every,
3643 - RRDSET_TYPE_LINE);
3716 + , update_every
3717 + , RRDSET_TYPE_LINE
3718 + );
3719 +
3720 + rrdset_update_labels(chart = res->some.st, cg->chart_labels);
3721
3722 res->some.rd10 = rrddim_add(chart, "some 10", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE);
3723 res->some.rd60 = rrddim_add(chart, "some 60", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE);
@@ -3669,8 +3746,11 @@ void update_cgroup_charts(int update_every) {
3746 , PLUGIN_CGROUPS_NAME
3747 , PLUGIN_CGROUPS_MODULE_CGROUPS_NAME
3748 , cgroup_containers_chart_priority + 2300
3672 - , update_every,
3673 - RRDSET_TYPE_LINE);
3749 + , update_every
3750 + , RRDSET_TYPE_LINE
3751 + );
3752 +
3753 + rrdset_update_labels(chart = res->some.st, cg->chart_labels);
3754
3755 res->some.rd10 = rrddim_add(chart, "some 10", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE);
3756 res->some.rd60 = rrddim_add(chart, "some 60", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE);
@@ -3698,8 +3778,11 @@ void update_cgroup_charts(int update_every) {
3778 , PLUGIN_CGROUPS_NAME
3779 , PLUGIN_CGROUPS_MODULE_CGROUPS_NAME
3780 , cgroup_containers_chart_priority + 2350
3701 - , update_every,
3702 - RRDSET_TYPE_LINE);
3781 + , update_every
3782 + , RRDSET_TYPE_LINE
3783 + );
3784 +
3785 + rrdset_update_labels(chart = res->full.st, cg->chart_labels);
3786
3787 res->full.rd10 = rrddim_add(chart, "full 10", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE);
3788 res->full.rd60 = rrddim_add(chart, "full 60", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE);
@@ -3728,8 +3811,11 @@ void update_cgroup_charts(int update_every) {
3811 , PLUGIN_CGROUPS_NAME
3812 , PLUGIN_CGROUPS_MODULE_CGROUPS_NAME
3813 , cgroup_containers_chart_priority + 2400
3731 - , update_every,
3732 - RRDSET_TYPE_LINE);
3814 + , update_every
3815 + , RRDSET_TYPE_LINE
3816 + );
3817 +
3818 + rrdset_update_labels(chart = res->some.st, cg->chart_labels);
3819
3820 res->some.rd10 = rrddim_add(chart, "some 10", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE);
3821 res->some.rd60 = rrddim_add(chart, "some 60", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE);
@@ -3757,8 +3843,11 @@ void update_cgroup_charts(int update_every) {
3843 , PLUGIN_CGROUPS_NAME
3844 , PLUGIN_CGROUPS_MODULE_CGROUPS_NAME
3845 , cgroup_containers_chart_priority + 2450
3760 - , update_every,
3761 - RRDSET_TYPE_LINE);
3846 + , update_every
3847 + , RRDSET_TYPE_LINE
3848 + );
3849 +
3850 + rrdset_update_labels(chart = res->full.st, cg->chart_labels);
3851
3852 res->full.rd10 = rrddim_add(chart, "full 10", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE);
3853 res->full.rd60 = rrddim_add(chart, "full 60", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE);
collectors/cgroups.plugin/sys_fs_cgroup.h
+2
@@ -28,4 +28,6 @@ extern void *cgroups_main(void *ptr);
28
29 #endif // (TARGET_OS == OS_LINUX)
30
31 +extern char *parse_k8s_data(struct label **labels, char *data);
32 +
33 #endif //NETDATA_SYS_FS_CGROUP_H
collectors/cgroups.plugin/tests/test_cgroups_plugin.c new
+110
@@ -0,0 +1,110 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "test_cgroups_plugin.h"
4 +#include "libnetdata/required_dummies.h"
5 +
6 +RRDHOST *localhost;
7 +int netdata_zero_metrics_enabled = 1;
8 +struct config netdata_config;
9 +char *netdata_configured_primary_plugins_dir = NULL;
10 +
11 +static void test_parse_k8s_data(void **state)
12 +{
13 + UNUSED(state);
14 +
15 + struct label *labels = (struct label *)0xff;
16 +
17 + struct k8s_test_data {
18 + char *data;
19 + char *name;
20 + char *key[3];
21 + char *value[3];
22 + };
23 +
24 + struct k8s_test_data test_data[] = {
25 + // One label
26 + { .data = "name label1=\"value1\"",
27 + .name = "name",
28 + .key[0] = "label1", .value[0] = "value1" },
29 +
30 + // Three labels
31 + { .data = "name label1=\"value1\",label2=\"value2\",label3=\"value3\"",
32 + .name = "name",
33 + .key[0] = "label1", .value[0] = "value1",
34 + .key[1] = "label2", .value[1] = "value2",
35 + .key[2] = "label3", .value[2] = "value3" },
36 +
37 + // Comma at the end of the data string
38 + { .data = "name label1=\"value1\",",
39 + .name = "name",
40 + .key[0] = "label1", .value[0] = "value1" },
41 +
42 + // Equals sign in the value
43 + { .data = "name label1=\"value=1\"",
44 + .name = "name",
45 + .key[0] = "label1", .value[0] = "value=1" },
46 +
47 + // Double quotation mark in the value
48 + { .data = "name label1=\"value\"1\"",
49 + .name = "name",
50 + .key[0] = "label1", .value[0] = "value" },
51 +
52 + // Escaped double quotation mark in the value
53 + { .data = "name label1=\"value\\\"1\"",
54 + .name = "name",
55 + .key[0] = "label1", .value[0] = "value\\\"1" },
56 +
57 + // Equals sign in the key
58 + { .data = "name label=1=\"value1\"",
59 + .name = "name",
60 + .key[0] = "label", .value[0] = "1=\"value1\"" },
61 +
62 + // Skipped value
63 + { .data = "name label1=,label2=\"value2\"",
64 + .name = "name",
65 + .key[0] = "label2", .value[0] = "value2" },
66 +
67 + // A pair of equals signs
68 + { .data = "name= =",
69 + .name = "name=" },
70 +
71 + // A pair of commas
72 + { .data = "name, ,",
73 + .name = "name," },
74 +
75 + { .data = NULL }
76 + };
77 +
78 + for (int i = 0; test_data[i].data != NULL; i++) {
79 + char *data = strdup(test_data[i].data);
80 +
81 + for (int l = 0; l < 3 && test_data[i].key[l] != NULL; l++) {
82 + char *key = test_data[i].key[l];
83 + char *value = test_data[i].value[l];
84 +
85 + expect_function_call(__wrap_add_label_to_list);
86 + expect_value(__wrap_add_label_to_list, l, 0xff);
87 + expect_string(__wrap_add_label_to_list, key, key);
88 + expect_string(__wrap_add_label_to_list, value, value);
89 + expect_value(__wrap_add_label_to_list, label_source, LABEL_SOURCE_KUBERNETES);
90 + }
91 +
92 + char *name = parse_k8s_data(&labels, data);
93 +
94 + assert_string_equal(name, test_data[i].name);
95 + assert_ptr_equal(labels, 0xff);
96 +
97 + free(data);
98 + }
99 +}
100 +
101 +int main(void)
102 +{
103 + const struct CMUnitTest tests[] = {
104 + cmocka_unit_test(test_parse_k8s_data),
105 + };
106 +
107 + int test_res = cmocka_run_group_tests_name("test_parse_k8s_data", tests, NULL, NULL);
108 +
109 + return test_res;
110 +}
collectors/cgroups.plugin/tests/test_cgroups_plugin.h new
+16
@@ -0,0 +1,16 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef TEST_CGROUPS_PLUGIN_H
4 +#define TEST_CGROUPS_PLUGIN_H 1
5 +
6 +#include "libnetdata/libnetdata.h"
7 +
8 +#include "../sys_fs_cgroup.h"
9 +
10 +#include <stdarg.h>
11 +#include <stddef.h>
12 +#include <setjmp.h>
13 +#include <stdint.h>
14 +#include <cmocka.h>
15 +
16 +#endif /* TEST_CGROUPS_PLUGIN_H */
collectors/cgroups.plugin/tests/test_doubles.c new
+162
@@ -0,0 +1,162 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "test_cgroups_plugin.h"
4 +
5 +void rrdset_is_obsolete(RRDSET *st)
6 +{
7 + UNUSED(st);
8 +}
9 +
10 +void rrdset_isnot_obsolete(RRDSET *st)
11 +{
12 + UNUSED(st);
13 +}
14 +
15 +struct mountinfo *mountinfo_read(int do_statvfs)
16 +{
17 + UNUSED(do_statvfs);
18 +
19 + return NULL;
20 +}
21 +
22 +struct mountinfo *
23 +mountinfo_find_by_filesystem_mount_source(struct mountinfo *root, const char *filesystem, const char *mount_source)
24 +{
25 + UNUSED(root);
26 + UNUSED(filesystem);
27 + UNUSED(mount_source);
28 +
29 + return NULL;
30 +}
31 +
32 +struct mountinfo *
33 +mountinfo_find_by_filesystem_super_option(struct mountinfo *root, const char *filesystem, const char *super_options)
34 +{
35 + UNUSED(root);
36 + UNUSED(filesystem);
37 + UNUSED(super_options);
38 +
39 + return NULL;
40 +}
41 +
42 +void mountinfo_free_all(struct mountinfo *mi)
43 +{
44 + UNUSED(mi);
45 +}
46 +
47 +struct label *__wrap_add_label_to_list(struct label *l, char *key, char *value, LABEL_SOURCE label_source)
48 +{
49 + function_called();
50 + check_expected_ptr(l);
51 + check_expected_ptr(key);
52 + check_expected_ptr(value);
53 + check_expected(label_source);
54 + return l;
55 +}
56 +
57 +void rrdset_update_labels(RRDSET *st, struct label *labels)
58 +{
59 + UNUSED(st);
60 + UNUSED(labels);
61 +}
62 +
63 +RRDSET *rrdset_create_custom(
64 + RRDHOST *host, const char *type, const char *id, const char *name, const char *family, const char *context,
65 + const char *title, const char *units, const char *plugin, const char *module, long priority, int update_every,
66 + RRDSET_TYPE chart_type, RRD_MEMORY_MODE memory_mode, long history_entries)
67 +{
68 + UNUSED(host);
69 + UNUSED(type);
70 + UNUSED(id);
71 + UNUSED(name);
72 + UNUSED(family);
73 + UNUSED(context);
74 + UNUSED(title);
75 + UNUSED(units);
76 + UNUSED(plugin);
77 + UNUSED(module);
78 + UNUSED(priority);
79 + UNUSED(update_every);
80 + UNUSED(chart_type);
81 + UNUSED(memory_mode);
82 + UNUSED(history_entries);
83 +
84 + return NULL;
85 +}
86 +
87 +RRDDIM *rrddim_add_custom(
88 + RRDSET *st, const char *id, const char *name, collected_number multiplier, collected_number divisor,
89 + RRD_ALGORITHM algorithm, RRD_MEMORY_MODE memory_mode)
90 +{
91 + UNUSED(st);
92 + UNUSED(id);
93 + UNUSED(name);
94 + UNUSED(multiplier);
95 + UNUSED(divisor);
96 + UNUSED(algorithm);
97 + UNUSED(memory_mode);
98 +
99 + return NULL;
100 +}
101 +
102 +collected_number rrddim_set(RRDSET *st, const char *id, collected_number value)
103 +{
104 + UNUSED(st);
105 + UNUSED(id);
106 + UNUSED(value);
107 +
108 + return 0;
109 +}
110 +
111 +collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value)
112 +{
113 + UNUSED(st);
114 + UNUSED(rd);
115 + UNUSED(value);
116 +
117 + return 0;
118 +}
119 +
120 +RRDSETVAR *rrdsetvar_custom_chart_variable_create(RRDSET *st, const char *name)
121 +{
122 + UNUSED(st);
123 + UNUSED(name);
124 +
125 + return NULL;
126 +}
127 +
128 +void rrdsetvar_custom_chart_variable_set(RRDSETVAR *rs, calculated_number value)
129 +{
130 + UNUSED(rs);
131 + UNUSED(value);
132 +}
133 +
134 +void rrdset_next_usec(RRDSET *st, usec_t microseconds)
135 +{
136 + UNUSED(st);
137 + UNUSED(microseconds);
138 +}
139 +
140 +void rrdset_done(RRDSET *st)
141 +{
142 + UNUSED(st);
143 +}
144 +
145 +void update_pressure_chart(struct pressure_chart *chart)
146 +{
147 + UNUSED(chart);
148 +}
149 +
150 +void netdev_rename_device_add(
151 + const char *host_device, const char *container_device, const char *container_name, struct label *labels)
152 +{
153 + UNUSED(host_device);
154 + UNUSED(container_device);
155 + UNUSED(container_name);
156 + UNUSED(labels);
157 +}
158 +
159 +void netdev_rename_device_del(const char *host_device)
160 +{
161 + UNUSED(host_device);
162 +}
collectors/plugins.d/pluginsd_parser.c
+3 -3
@@ -157,11 +157,11 @@ PARSER_RC pluginsd_overwrite_action(void *user, RRDHOST *host, struct label *new
157 {
158 UNUSED(user);
159
160 - if (!host->labels) {
161 - host->labels = new_labels;
160 + if (!host->labels.head) {
161 + host->labels.head = new_labels;
162 } else {
163 rrdhost_rdlock(host);
164 - replace_label_list(host, new_labels);
164 + replace_label_list(&host->labels, new_labels);
165 rrdhost_unlock(host);
166 }
167 return PARSER_RC_OK;
collectors/proc.plugin/plugin_proc.h
+2 -1
@@ -65,7 +65,8 @@ extern int get_numa_node_count(void);
65 extern unsigned long long tcpext_TCPSynRetrans;
66
67 // netdev renames
68 -extern void netdev_rename_device_add(const char *host_device, const char *container_device, const char *container_name);
68 +extern void netdev_rename_device_add(
69 + const char *host_device, const char *container_device, const char *container_name, struct label *labels);
70 extern void netdev_rename_device_del(const char *host_device);
71
72 #include "proc_self_mountinfo.h"
collectors/proc.plugin/proc_net_dev.c
+29 -1
@@ -60,6 +60,8 @@ static struct netdev {
60
61 const char *chart_family;
62
63 + struct label *chart_labels;
64 +
65 int flipped;
66 unsigned long priority;
67
@@ -192,6 +194,7 @@ static void netdev_free_chart_strings(struct netdev *d) {
194 static void netdev_free(struct netdev *d) {
195 netdev_charts_release(d);
196 netdev_free_chart_strings(d);
197 + free_label_list(d->chart_labels);
198
199 freez((void *)d->name);
200 freez((void *)d->filename_speed);
@@ -211,6 +214,8 @@ static struct netdev_rename {
214 const char *container_device;
215 const char *container_name;
216
217 + struct label *chart_labels;
218 +
219 int processed;
220
221 struct netdev_rename *next;
@@ -230,7 +235,9 @@ static struct netdev_rename *netdev_rename_find(const char *host_device, uint32_
235 }
236
237 // other threads can call this function to register a rename to a netdev
233 -void netdev_rename_device_add(const char *host_device, const char *container_device, const char *container_name) {
238 +void netdev_rename_device_add(
239 + const char *host_device, const char *container_device, const char *container_name, struct label *labels)
240 +{
241 netdata_mutex_lock(&netdev_rename_mutex);
242
243 uint32_t hash = simple_hash(host_device);
@@ -240,6 +247,7 @@ void netdev_rename_device_add(const char *host_device, const char *container_dev
247 r->host_device = strdupz(host_device);
248 r->container_device = strdupz(container_device);
249 r->container_name = strdupz(container_name);
250 + update_label_list(&r->chart_labels, labels);
251 r->hash = hash;
252 r->next = netdev_rename_root;
253 r->processed = 0;
@@ -254,6 +262,9 @@ void netdev_rename_device_add(const char *host_device, const char *container_dev
262
263 r->container_device = strdupz(container_device);
264 r->container_name = strdupz(container_name);
265 +
266 + update_label_list(&r->chart_labels, labels);
267 +
268 r->processed = 0;
269 netdev_pending_renames++;
270 info("CGROUP: altered network interface rename for '%s' as '%s' under '%s'", r->host_device, r->container_device, r->container_name);
@@ -285,6 +296,7 @@ void netdev_rename_device_del(const char *host_device) {
296 freez((void *) r->host_device);
297 freez((void *) r->container_name);
298 freez((void *) r->container_device);
299 + free_label_list(r->chart_labels);
300 freez((void *) r);
301 break;
302 }
@@ -334,6 +346,8 @@ static inline void netdev_rename_cgroup(struct netdev *d, struct netdev_rename *
346 snprintfz(buffer, RRD_ID_LENGTH_MAX, "net %s", r->container_device);
347 d->chart_family = strdupz(buffer);
348
349 + update_label_list(&d->chart_labels, r->chart_labels);
350 +
351 d->priority = NETDATA_CHART_PRIO_CGROUP_NET_IFACE;
352 d->flipped = 1;
353 }
@@ -677,6 +691,8 @@ int do_proc_net_dev(int update_every, usec_t dt) {
691 , RRDSET_TYPE_AREA
692 );
693
694 + rrdset_update_labels(d->st_bandwidth, d->chart_labels);
695 +
696 d->rd_rbytes = rrddim_add(d->st_bandwidth, "received", NULL, 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL);
697 d->rd_tbytes = rrddim_add(d->st_bandwidth, "sent", NULL, -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL);
698
@@ -820,6 +836,8 @@ int do_proc_net_dev(int update_every, usec_t dt) {
836
837 rrdset_flag_set(d->st_packets, RRDSET_FLAG_DETAIL);
838
839 + rrdset_update_labels(d->st_packets, d->chart_labels);
840 +
841 d->rd_rpackets = rrddim_add(d->st_packets, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
842 d->rd_tpackets = rrddim_add(d->st_packets, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
843 d->rd_rmulticast = rrddim_add(d->st_packets, "multicast", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
@@ -866,6 +884,8 @@ int do_proc_net_dev(int update_every, usec_t dt) {
884
885 rrdset_flag_set(d->st_errors, RRDSET_FLAG_DETAIL);
886
887 + rrdset_update_labels(d->st_errors, d->chart_labels);
888 +
889 d->rd_rerrors = rrddim_add(d->st_errors, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
890 d->rd_terrors = rrddim_add(d->st_errors, "outbound", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
891
@@ -910,6 +930,8 @@ int do_proc_net_dev(int update_every, usec_t dt) {
930
931 rrdset_flag_set(d->st_drops, RRDSET_FLAG_DETAIL);
932
933 + rrdset_update_labels(d->st_drops, d->chart_labels);
934 +
935 d->rd_rdrops = rrddim_add(d->st_drops, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
936 d->rd_tdrops = rrddim_add(d->st_drops, "outbound", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
937
@@ -954,6 +976,8 @@ int do_proc_net_dev(int update_every, usec_t dt) {
976
977 rrdset_flag_set(d->st_fifo, RRDSET_FLAG_DETAIL);
978
979 + rrdset_update_labels(d->st_fifo, d->chart_labels);
980 +
981 d->rd_rfifo = rrddim_add(d->st_fifo, "receive", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
982 d->rd_tfifo = rrddim_add(d->st_fifo, "transmit", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
983
@@ -998,6 +1022,8 @@ int do_proc_net_dev(int update_every, usec_t dt) {
1022
1023 rrdset_flag_set(d->st_compressed, RRDSET_FLAG_DETAIL);
1024
1025 + rrdset_update_labels(d->st_compressed, d->chart_labels);
1026 +
1027 d->rd_rcompressed = rrddim_add(d->st_compressed, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1028 d->rd_tcompressed = rrddim_add(d->st_compressed, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
1029
@@ -1042,6 +1068,8 @@ int do_proc_net_dev(int update_every, usec_t dt) {
1068
1069 rrdset_flag_set(d->st_events, RRDSET_FLAG_DETAIL);
1070
1071 + rrdset_update_labels(d->st_events, d->chart_labels);
1072 +
1073 d->rd_rframe = rrddim_add(d->st_events, "frames", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1074 d->rd_tcollisions = rrddim_add(d->st_events, "collisions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
1075 d->rd_tcarrier = rrddim_add(d->st_events, "carrier", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
daemon/commands.c
+3 -3
@@ -216,13 +216,13 @@ static cmd_status_t cmd_reload_labels_execute(char *args, char **message)
216 BUFFER *wb = buffer_create(10);
217
218 rrdhost_rdlock(localhost);
219 - netdata_rwlock_rdlock(&localhost->labels_rwlock);
220 - struct label *l=localhost->labels;
219 + netdata_rwlock_rdlock(&localhost->labels.labels_rwlock);
220 + struct label *l = localhost->labels.head;
221 while (l != NULL) {
222 buffer_sprintf(wb,"Label [source id=%s]: \"%s\" -> \"%s\"\n", translate_label_source(l->label_source), l->key, l->value);
223 l = l->next;
224 }
225 - netdata_rwlock_unlock(&localhost->labels_rwlock);
225 + netdata_rwlock_unlock(&localhost->labels.labels_rwlock);
226 rrdhost_unlock(localhost);
227
228 (*message)=strdupz(buffer_tostring(wb));
database/rrd.h
+41 -7
@@ -19,6 +19,7 @@ typedef struct context_param CONTEXT_PARAM;
19 struct rrddim_volatile;
20 struct rrdset_volatile;
21 struct context_param;
22 +struct label;
23 #ifdef ENABLE_DBENGINE
24 struct rrdeng_page_descr;
25 struct rrdengine_instance;
@@ -185,12 +186,45 @@ struct label {
186 struct label *next;
187 };
188
189 +struct label_index {
190 + struct label *head; // Label list
191 + netdata_rwlock_t labels_rwlock; // lock for the label list
192 + uint32_t labels_flag; // Flags for labels
193 +};
194 +
195 +typedef enum strip_quotes {
196 + DO_NOT_STRIP_QUOTES,
197 + STRIP_QUOTES
198 +} STRIP_QUOTES_OPTION;
199 +
200 +typedef enum skip_escaped_characters {
201 + DO_NOT_SKIP_ESCAPED_CHARACTERS,
202 + SKIP_ESCAPED_CHARACTERS
203 +} SKIP_ESCAPED_CHARACTERS_OPTION;
204 +
205 char *translate_label_source(LABEL_SOURCE l);
206 struct label *create_label(char *key, char *value, LABEL_SOURCE label_source);
190 -struct label *add_label_to_list(struct label *l, char *key, char *value, LABEL_SOURCE label_source);
191 -extern void replace_label_list(RRDHOST *host, struct label *new_labels);
192 -extern void free_host_labels(struct label *labels);
193 -void reload_host_labels();
207 +extern struct label *add_label_to_list(struct label *l, char *key, char *value, LABEL_SOURCE label_source);
208 +extern void update_label_list(struct label **labels, struct label *new_labels);
209 +extern void replace_label_list(struct label_index *labels, struct label *new_labels);
210 +extern int is_valid_label_value(char *value);
211 +extern int is_valid_label_key(char *key);
212 +extern void free_label_list(struct label *labels);
213 +extern struct label *label_list_lookup_key(struct label *head, char *key, uint32_t key_hash);
214 +extern int label_list_contains_key(struct label *head, char *key, uint32_t key_hash);
215 +extern int label_list_contains(struct label *head, struct label *check);
216 +extern struct label *merge_label_lists(struct label *lo_pri, struct label *hi_pri);
217 +extern void strip_last_symbol(
218 + char *str,
219 + char symbol,
220 + SKIP_ESCAPED_CHARACTERS_OPTION skip_escaped_characters);
221 +extern char *strip_double_quotes(char *str, SKIP_ESCAPED_CHARACTERS_OPTION skip_escaped_characters);
222 +void reload_host_labels(void);
223 +extern void rrdset_add_label_to_new_list(RRDSET *st, char *key, char *value, LABEL_SOURCE source);
224 +extern void rrdset_finalize_labels(RRDSET *st);
225 +extern void rrdset_update_labels(RRDSET *st, struct label *labels);
226 +extern int rrdset_contains_label_key(RRDSET *st, char *key, uint32_t key_hash);
227 +extern struct label *rrdset_lookup_label_key(RRDSET *st, char *key, uint32_t key_hash);
228
229 // ----------------------------------------------------------------------------
230 // RRD DIMENSION - this is a metric
@@ -376,6 +410,8 @@ struct rrdset_volatile {
410 char *old_title;
411 char *old_family;
412 char *old_context;
413 + struct label *new_labels;
414 + struct label_index labels;
415 };
416
417 // ----------------------------------------------------------------------------
@@ -801,9 +837,7 @@ struct rrdhost {
837
838 // ------------------------------------------------------------------------
839 // Support for host-level labels
804 - struct label *labels;
805 - netdata_rwlock_t labels_rwlock; // lock for the label list
806 - uint32_t labels_flag; //Flags for labels
840 + struct label_index labels;
841
842 // ------------------------------------------------------------------------
843 // indexes
database/rrdcalc.c
+4 -4
@@ -651,7 +651,7 @@ static void rrdcalc_labels_unlink_alarm_loop(RRDHOST *host, RRDCALC *alarms) {
651 }
652
653 char cmp[CONFIG_FILE_LINE_MAX+1];
654 - struct label *move = host->labels;
654 + struct label *move = host->labels.head;
655 while(move) {
656 snprintf(cmp, CONFIG_FILE_LINE_MAX, "%s=%s", move->key, move->value);
657 if (simple_pattern_matches(rc->splabels, move->key) ||
@@ -682,12 +682,12 @@ static void rrdcalc_labels_unlink_alarm_loop(RRDHOST *host, RRDCALC *alarms) {
682
683 void rrdcalc_labels_unlink_alarm_from_host(RRDHOST *host) {
684 rrdhost_check_rdlock(host);
685 - netdata_rwlock_rdlock(&host->labels_rwlock);
685 + netdata_rwlock_rdlock(&host->labels.labels_rwlock);
686
687 rrdcalc_labels_unlink_alarm_loop(host, host->alarms);
688 rrdcalc_labels_unlink_alarm_loop(host, host->alarms_with_foreach);
689
690 - netdata_rwlock_unlock(&host->labels_rwlock);
690 + netdata_rwlock_unlock(&host->labels.labels_rwlock);
691 }
692
693 void rrdcalc_labels_unlink() {
@@ -698,7 +698,7 @@ void rrdcalc_labels_unlink() {
698 if (unlikely(!host->health_enabled))
699 continue;
700
701 - if (host->labels) {
701 + if (host->labels.head) {
702 rrdhost_wrlock(host);
703
704 rrdcalc_labels_unlink_alarm_from_host(host);
database/rrdcalctemplate.c
+3 -3
@@ -10,13 +10,13 @@ static int rrdcalctemplate_is_there_label_restriction(RRDCALCTEMPLATE *rt, RRDH
10 return 0;
11
12 errno = 0;
13 - struct label *move = host->labels;
13 + struct label *move = host->labels.head;
14 char cmp[CONFIG_FILE_LINE_MAX+1];
15
16 int ret;
17 if(move) {
18 rrdhost_check_rdlock(host);
19 - netdata_rwlock_rdlock(&host->labels_rwlock);
19 + netdata_rwlock_rdlock(&host->labels.labels_rwlock);
20 while(move) {
21 snprintfz(cmp, CONFIG_FILE_LINE_MAX, "%s=%s", move->key, move->value);
22 if (simple_pattern_matches(rt->splabels, move->key) ||
@@ -25,7 +25,7 @@ static int rrdcalctemplate_is_there_label_restriction(RRDCALCTEMPLATE *rt, RRDH
25 }
26 move = move->next;
27 }
28 - netdata_rwlock_unlock(&host->labels_rwlock);
28 + netdata_rwlock_unlock(&host->labels.labels_rwlock);
29
30 if(!move) {
31 error("Health template '%s' cannot be applied, because the host %s does not have the label(s) '%s'",
database/rrdhost.c
+20 -183
@@ -187,7 +187,7 @@ RRDHOST *rrdhost_create(const char *hostname,
187 #endif
188
189 netdata_rwlock_init(&host->rrdhost_rwlock);
190 - netdata_rwlock_init(&host->labels_rwlock);
190 + netdata_rwlock_init(&host->labels.labels_rwlock);
191
192 netdata_mutex_init(&host->aclk_state_lock);
193
@@ -873,7 +873,7 @@ void rrdhost_free(RRDHOST *host) {
873 pthread_mutex_destroy(&host->aclk_state_lock);
874 freez(host->aclk_state.claimed_id);
875 freez((void *)host->tags);
876 - free_host_labels(host->labels);
876 + free_label_list(host->labels.head);
877 freez((void *)host->os);
878 freez((void *)host->timezone);
879 freez(host->program_version);
@@ -890,7 +890,7 @@ void rrdhost_free(RRDHOST *host) {
890 freez(host->registry_hostname);
891 simple_pattern_free(host->rrdpush_send_charts_matching);
892 rrdhost_unlock(host);
893 - netdata_rwlock_destroy(&host->labels_rwlock);
893 + netdata_rwlock_destroy(&host->labels.labels_rwlock);
894 netdata_rwlock_destroy(&host->health_log.alarm_log_rwlock);
895 netdata_rwlock_destroy(&host->rrdhost_rwlock);
896
@@ -930,55 +930,7 @@ void rrdhost_save_charts(RRDHOST *host) {
930 rrdhost_unlock(host);
931 }
932
933 -static int is_valid_label_value(char *value) {
934 - while(*value) {
935 - if(*value == '"' || *value == '\'' || *value == '*' || *value == '!') {
936 - return 0;
937 - }
938 -
939 - value++;
940 - }
941 -
942 - return 1;
943 -}
944 -
945 -static int is_valid_label_key(char *key) {
946 - //Prometheus exporter
947 - if(!strcmp(key, "chart") || !strcmp(key, "family") || !strcmp(key, "dimension"))
948 - return 0;
949 -
950 - //Netdata and Prometheus internal
951 - if (*key == '_')
952 - return 0;
953 -
954 - while(*key) {
955 - if(!(isdigit(*key) || isalpha(*key) || *key == '.' || *key == '_' || *key == '-'))
956 - return 0;
957 -
958 - key++;
959 - }
960 -
961 - return 1;
962 -}
963 -
964 -char *translate_label_source(LABEL_SOURCE l) {
965 - switch (l) {
966 - case LABEL_SOURCE_AUTO:
967 - return "AUTO";
968 - case LABEL_SOURCE_NETDATA_CONF:
969 - return "NETDATA.CONF";
970 - case LABEL_SOURCE_DOCKER :
971 - return "DOCKER";
972 - case LABEL_SOURCE_ENVIRONMENT :
973 - return "ENVIRONMENT";
974 - case LABEL_SOURCE_KUBERNETES :
975 - return "KUBERNETES";
976 - default:
977 - return "Invalid label source";
978 - }
979 -}
980 -
981 -struct label *load_auto_labels()
933 +static struct label *rrdhost_load_auto_labels(void)
934 {
935 struct label *label_list = NULL;
936
@@ -1040,11 +992,13 @@ struct label *load_auto_labels()
992 return label_list;
993 }
994
1043 -static inline int is_valid_label_config_option(char *name, char *value) {
1044 - return (is_valid_label_key(name) && is_valid_label_value(value) && strcmp(name, "from environment") && strcmp(name, "from kubernetes pods") );
1045 - }
995 +static inline int rrdhost_is_valid_label_config_option(char *name, char *value)
996 +{
997 + return (is_valid_label_key(name) && is_valid_label_value(value) && strcmp(name, "from environment") &&
998 + strcmp(name, "from kubernetes pods"));
999 +}
1000
1047 -struct label *load_config_labels()
1001 +static struct label *rrdhost_load_config_labels()
1002 {
1003 int status = config_load(NULL, 1, CONFIG_SECTION_HOST_LABEL);
1004 if(!status) {
@@ -1058,7 +1012,7 @@ struct label *load_config_labels()
1012 config_section_wrlock(co);
1013 struct config_option *cv;
1014 for(cv = co->values; cv ; cv = cv->next) {
1061 - if( is_valid_label_config_option(cv->name, cv->value)) {
1015 + if(rrdhost_is_valid_label_config_option(cv->name, cv->value)) {
1016 l = add_label_to_list(l, cv->name, cv->value, LABEL_SOURCE_NETDATA_CONF);
1017 cv->flags |= CONFIG_VALUE_USED;
1018 } else {
@@ -1072,45 +1026,6 @@ struct label *load_config_labels()
1026 return l;
1027 }
1028
1075 -typedef enum strip_quotes {
1076 - DO_NOT_STRIP_QUOTES,
1077 - STRIP_QUOTES
1078 -} STRIP_QUOTES_OPTION;
1079 -
1080 -typedef enum skip_escaped_characters {
1081 - DO_NOT_SKIP_ESCAPED_CHARACTERS,
1082 - SKIP_ESCAPED_CHARACTERS
1083 -} SKIP_ESCAPED_CHARACTERS_OPTION;
1084 -
1085 -static inline void strip_last_symbol(
1086 - char *str,
1087 - char symbol,
1088 - SKIP_ESCAPED_CHARACTERS_OPTION skip_escaped_characters)
1089 -{
1090 - char *end = str;
1091 -
1092 - while (*end && *end != symbol) {
1093 - if (unlikely(skip_escaped_characters && *end == '\\')) {
1094 - end++;
1095 - if (unlikely(!*end))
1096 - break;
1097 - }
1098 - end++;
1099 - }
1100 - if (likely(*end == symbol))
1101 - *end = '\0';
1102 -}
1103 -
1104 -static inline char *strip_double_quotes(char *str, SKIP_ESCAPED_CHARACTERS_OPTION skip_escaped_characters)
1105 -{
1106 - if (*str == '"') {
1107 - str++;
1108 - strip_last_symbol(str, '"', skip_escaped_characters);
1109 - }
1110 -
1111 - return str;
1112 -}
1113 -
1029 struct label *parse_simple_tags(
1030 struct label *label_list,
1031 const char *tags,
@@ -1200,7 +1115,7 @@ struct label *parse_json_tags(struct label *label_list, const char *tags)
1115 return label_list;
1116 }
1117
1203 -struct label *load_labels_from_tags()
1118 +static struct label *rrdhost_load_labels_from_tags(void)
1119 {
1120 if (!localhost->tags)
1121 return NULL;
@@ -1244,7 +1159,7 @@ struct label *load_labels_from_tags()
1159 return label_list;
1160 }
1161
1247 -struct label *load_kubernetes_labels()
1162 +static struct label *rrdhost_load_kubernetes_labels(void)
1163 {
1164 struct label *l=NULL;
1165 char *label_script = mallocz(sizeof(char) * (strlen(netdata_configured_primary_plugins_dir) + strlen("get-kubernetes-labels.sh") + 2));
@@ -1302,104 +1217,26 @@ struct label *load_kubernetes_labels()
1217 return l;
1218 }
1219
1305 -struct label *create_label(char *key, char *value, LABEL_SOURCE label_source)
1306 -{
1307 - size_t key_len = strlen(key), value_len = strlen(value);
1308 - size_t n = sizeof(struct label) + key_len + 1 + value_len + 1;
1309 - struct label *result = callocz(1,n);
1310 - if (result != NULL) {
1311 - char *c = (char *)result;
1312 - c += sizeof(struct label);
1313 - strcpy(c, key);
1314 - result->key = c;
1315 - c += key_len + 1;
1316 - strcpy(c, value);
1317 - result->value = c;
1318 - result->label_source = label_source;
1319 - result->key_hash = simple_hash(result->key);
1320 - }
1321 - return result;
1322 -}
1323 -
1324 -void free_host_labels(struct label *labels)
1325 -{
1326 - while (labels != NULL)
1327 - {
1328 - struct label *current = labels;
1329 - labels = labels->next;
1330 - freez(current);
1331 - }
1332 -}
1333 -
1334 -void replace_label_list(RRDHOST *host, struct label *new_labels)
1335 -{
1336 - rrdhost_check_rdlock(host);
1337 - netdata_rwlock_wrlock(&host->labels_rwlock);
1338 - struct label *old_labels = host->labels;
1339 - host->labels = new_labels;
1340 - netdata_rwlock_unlock(&host->labels_rwlock);
1341 -
1342 - free_host_labels(old_labels);
1343 -}
1344 -
1345 -struct label *add_label_to_list(struct label *l, char *key, char *value, LABEL_SOURCE label_source)
1346 -{
1347 - struct label *lab = create_label(key, value, label_source);
1348 - lab->next = l;
1349 - return lab;
1350 -}
1351 -
1352 -int label_list_contains(struct label *head, struct label *check)
1353 -{
1354 - while (head != NULL)
1355 - {
1356 - if (head->key_hash == check->key_hash && !strcmp(head->key, check->key))
1357 - return 1;
1358 - head = head->next;
1359 - }
1360 - return 0;
1361 -}
1362 -
1363 -/* Create a list with entries from both lists.
1364 - If any entry in the low priority list is masked by an entry in the high priorty list then delete it.
1365 -*/
1366 -struct label *merge_label_lists(struct label *lo_pri, struct label *hi_pri)
1367 -{
1368 - struct label *result = hi_pri;
1369 - while (lo_pri != NULL)
1370 - {
1371 - struct label *current = lo_pri;
1372 - lo_pri = lo_pri->next;
1373 - if (!label_list_contains(result, current)) {
1374 - current->next = result;
1375 - result = current;
1376 - }
1377 - else
1378 - freez(current);
1379 - }
1380 - return result;
1381 -}
1382 -
1383 -void reload_host_labels()
1220 +void reload_host_labels(void)
1221 {
1385 - struct label *from_auto = load_auto_labels();
1386 - struct label *from_k8s = load_kubernetes_labels();
1387 - struct label *from_config = load_config_labels();
1388 - struct label *from_tags = load_labels_from_tags();
1222 + struct label *from_auto = rrdhost_load_auto_labels();
1223 + struct label *from_k8s = rrdhost_load_kubernetes_labels();
1224 + struct label *from_config = rrdhost_load_config_labels();
1225 + struct label *from_tags = rrdhost_load_labels_from_tags();
1226
1227 struct label *new_labels = merge_label_lists(from_auto, from_k8s);
1228 new_labels = merge_label_lists(new_labels, from_tags);
1229 new_labels = merge_label_lists(new_labels, from_config);
1230
1231 rrdhost_rdlock(localhost);
1395 - replace_label_list(localhost, new_labels);
1232 + replace_label_list(&localhost->labels, new_labels);
1233
1234 health_label_log_save(localhost);
1235 rrdhost_unlock(localhost);
1236
1237 /* TODO-GAPS - fix this so that it looks properly at the state and version of the sender
1238 if(localhost->rrdpush_send_enabled && localhost->rrdpush_sender_buffer){
1402 - localhost->labels_flag |= LABEL_FLAG_UPDATE_STREAM;
1239 + localhost->labels.labels_flag |= LABEL_FLAG_UPDATE_STREAM;
1240 rrdpush_send_labels(localhost);
1241 }
1242 */
database/rrdlabels.c new
+181
@@ -0,0 +1,181 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#define NETDATA_RRD_INTERNALS
4 +#include "rrd.h"
5 +
6 +char *translate_label_source(LABEL_SOURCE l) {
7 + switch (l) {
8 + case LABEL_SOURCE_AUTO:
9 + return "AUTO";
10 + case LABEL_SOURCE_NETDATA_CONF:
11 + return "NETDATA.CONF";
12 + case LABEL_SOURCE_DOCKER :
13 + return "DOCKER";
14 + case LABEL_SOURCE_ENVIRONMENT :
15 + return "ENVIRONMENT";
16 + case LABEL_SOURCE_KUBERNETES :
17 + return "KUBERNETES";
18 + default:
19 + return "Invalid label source";
20 + }
21 +}
22 +
23 +int is_valid_label_value(char *value) {
24 + while(*value) {
25 + if(*value == '"' || *value == '\'' || *value == '*' || *value == '!') {
26 + return 0;
27 + }
28 +
29 + value++;
30 + }
31 +
32 + return 1;
33 +}
34 +
35 +int is_valid_label_key(char *key) {
36 + //Prometheus exporter
37 + if(!strcmp(key, "chart") || !strcmp(key, "family") || !strcmp(key, "dimension"))
38 + return 0;
39 +
40 + //Netdata and Prometheus internal
41 + if (*key == '_')
42 + return 0;
43 +
44 + while(*key) {
45 + if(!(isdigit(*key) || isalpha(*key) || *key == '.' || *key == '_' || *key == '-'))
46 + return 0;
47 +
48 + key++;
49 + }
50 +
51 + return 1;
52 +}
53 +
54 +void strip_last_symbol(
55 + char *str,
56 + char symbol,
57 + SKIP_ESCAPED_CHARACTERS_OPTION skip_escaped_characters)
58 +{
59 + char *end = str;
60 +
61 + while (*end && *end != symbol) {
62 + if (unlikely(skip_escaped_characters && *end == '\\')) {
63 + end++;
64 + if (unlikely(!*end))
65 + break;
66 + }
67 + end++;
68 + }
69 + if (likely(*end == symbol))
70 + *end = '\0';
71 +}
72 +
73 +char *strip_double_quotes(char *str, SKIP_ESCAPED_CHARACTERS_OPTION skip_escaped_characters)
74 +{
75 + if (*str == '"') {
76 + str++;
77 + strip_last_symbol(str, '"', skip_escaped_characters);
78 + }
79 +
80 + return str;
81 +}
82 +
83 +struct label *create_label(char *key, char *value, LABEL_SOURCE label_source)
84 +{
85 + size_t key_len = strlen(key), value_len = strlen(value);
86 + size_t n = sizeof(struct label) + key_len + 1 + value_len + 1;
87 + struct label *result = callocz(1,n);
88 + if (result != NULL) {
89 + char *c = (char *)result;
90 + c += sizeof(struct label);
91 + strcpy(c, key);
92 + result->key = c;
93 + c += key_len + 1;
94 + strcpy(c, value);
95 + result->value = c;
96 + result->label_source = label_source;
97 + result->key_hash = simple_hash(result->key);
98 + }
99 + return result;
100 +}
101 +
102 +void free_label_list(struct label *labels)
103 +{
104 + while (labels != NULL)
105 + {
106 + struct label *current = labels;
107 + labels = labels->next;
108 + freez(current);
109 + }
110 +}
111 +
112 +void replace_label_list(struct label_index *labels, struct label *new_labels)
113 +{
114 + netdata_rwlock_wrlock(&labels->labels_rwlock);
115 + struct label *old_labels = labels->head;
116 + labels->head = new_labels;
117 + netdata_rwlock_unlock(&labels->labels_rwlock);
118 +
119 + free_label_list(old_labels);
120 +}
121 +
122 +struct label *add_label_to_list(struct label *l, char *key, char *value, LABEL_SOURCE label_source)
123 +{
124 + struct label *lab = create_label(key, value, label_source);
125 + lab->next = l;
126 + return lab;
127 +}
128 +
129 +void update_label_list(struct label **labels, struct label *new_labels)
130 +{
131 + free_label_list(*labels);
132 + *labels = NULL;
133 +
134 + while (new_labels != NULL)
135 + {
136 + *labels = add_label_to_list(*labels, new_labels->key, new_labels->value, new_labels->label_source);
137 + new_labels = new_labels->next;
138 + }
139 +}
140 +
141 +struct label *label_list_lookup_key(struct label *head, char *key, uint32_t key_hash)
142 +{
143 + while (head != NULL)
144 + {
145 + if (head->key_hash == key_hash && !strcmp(head->key, key))
146 + return head;
147 + head = head->next;
148 + }
149 + return NULL;
150 +}
151 +
152 +int label_list_contains_key(struct label *head, char *key, uint32_t key_hash)
153 +{
154 + return (label_list_lookup_key(head, key, key_hash) != NULL);
155 +}
156 +
157 +int label_list_contains(struct label *head, struct label *check)
158 +{
159 + return label_list_contains_key(head, check->key, check->key_hash);
160 +}
161 +
162 +/* Create a list with entries from both lists.
163 + If any entry in the low priority list is masked by an entry in the high priority list then delete it.
164 +*/
165 +struct label *merge_label_lists(struct label *lo_pri, struct label *hi_pri)
166 +{
167 + struct label *result = hi_pri;
168 + while (lo_pri != NULL)
169 + {
170 + struct label *current = lo_pri;
171 + lo_pri = lo_pri->next;
172 + if (!label_list_contains(result, current)) {
173 + current->next = result;
174 + result = current;
175 + }
176 + else
177 + freez(current);
178 + }
179 + return result;
180 +}
181 +
database/rrdset.c
+69 -2
@@ -330,13 +330,22 @@ void rrdset_free(RRDSET *st) {
330
331 rrdset_index_del_name(host, st);
332
333 + // ------------------------------------------------------------------------
334 + // remove it from the configuration
335 +
336 + appconfig_section_destroy_non_loaded(&netdata_config, st->config_section);
337 +
338 // ------------------------------------------------------------------------
339 // free its children structures
340
341 freez(st->exporting_flags);
342
343 while(st->variables) rrdsetvar_free(st->variables);
339 - while(st->alarms) rrdsetcalc_unlink(st->alarms);
344 +// while(st->alarms) rrdsetcalc_unlink(st->alarms);
345 + /* We must free all connected alarms here in case this has been an ephemeral chart whose alarm was
346 + * created by a template. This leads to an effective memory leak, which cannot be detected since the
347 + * alarms will still be connected to the host, and freed during shutdown. */
348 + while(st->alarms) rrdcalc_unlink_and_free(st->rrdhost, st->alarms);
349 while(st->dimensions) rrddim_free(st, st->dimensions);
350
351 rrdfamily_free(host, st->rrdfamily);
@@ -366,6 +375,7 @@ void rrdset_free(RRDSET *st) {
375 // free it
376
377 netdata_rwlock_destroy(&st->rrdset_rwlock);
378 + netdata_rwlock_destroy(&st->state->labels.labels_rwlock);
379
380 // free directly allocated members
381 freez(st->config_section);
@@ -374,6 +384,7 @@ void rrdset_free(RRDSET *st) {
384 freez(st->state->old_title);
385 freez(st->state->old_family);
386 freez(st->state->old_context);
387 + free_label_list(st->state->labels.head);
388 freez(st->state);
389
390 switch(st->rrd_memory_mode) {
@@ -835,7 +846,7 @@ RRDSET *rrdset_create_custom(
846 st->chart_type = rrdset_type_id(config_get(st->config_section, "chart type", rrdset_type_name(chart_type)));
847 st->type = config_get(st->config_section, "type", type);
848
838 - st->state = mallocz(sizeof(*st->state));
849 + st->state = callocz(1, sizeof(*st->state));
850 st->family = config_get(st->config_section, "family", family?family:st->type);
851 st->state->old_family = strdupz(st->family);
852 json_fix_string(st->family);
@@ -887,6 +898,7 @@ RRDSET *rrdset_create_custom(
898 avl_init_lock(&st->rrdvar_root_index, rrdvar_compare);
899
900 netdata_rwlock_init(&st->rrdset_rwlock);
901 + netdata_rwlock_init(&st->state->labels.labels_rwlock);
902
903 if(name && *name && rrdset_set_name(st, name))
904 // we did set the name
@@ -1903,3 +1915,58 @@ after_second_database_work:
1915
1916 netdata_thread_enable_cancelability();
1917 }
1918 +
1919 +void rrdset_add_label_to_new_list(RRDSET *st, char *key, char *value, LABEL_SOURCE source)
1920 +{
1921 + st->state->new_labels = add_label_to_list(st->state->new_labels, key, value, source);
1922 +}
1923 +
1924 +void rrdset_finalize_labels(RRDSET *st)
1925 +{
1926 + struct label *new_labels = st->state->new_labels;
1927 + struct label_index *labels = &st->state->labels;
1928 +
1929 + if (!labels->head) {
1930 + labels->head = new_labels;
1931 + } else {
1932 + replace_label_list(labels, new_labels);
1933 + }
1934 + st->state->new_labels = NULL;
1935 +}
1936 +
1937 +void rrdset_update_labels(RRDSET *st, struct label *labels)
1938 +{
1939 + if (!labels)
1940 + return;
1941 +
1942 + update_label_list(&st->state->new_labels, labels);
1943 + rrdset_finalize_labels(st);
1944 +}
1945 +
1946 +int rrdset_contains_label_key(RRDSET *st, char *key, uint32_t key_hash)
1947 +{
1948 + struct label_index *labels = &st->state->labels;
1949 + int ret;
1950 +
1951 + if (!labels->head)
1952 + return 0;
1953 +
1954 + netdata_rwlock_rdlock(&labels->labels_rwlock);
1955 + ret = label_list_contains_key(labels->head, key, key_hash);
1956 + netdata_rwlock_unlock(&labels->labels_rwlock);
1957 +
1958 + return ret;
1959 +}
1960 +
1961 +struct label *rrdset_lookup_label_key(RRDSET *st, char *key, uint32_t key_hash)
1962 +{
1963 + struct label_index *labels = &st->state->labels;
1964 + struct label *ret = NULL;
1965 +
1966 + if (labels->head) {
1967 + netdata_rwlock_rdlock(&labels->labels_rwlock);
1968 + ret = label_list_lookup_key(labels->head, key, key_hash);
1969 + netdata_rwlock_unlock(&labels->labels_rwlock);
1970 + }
1971 + return ret;
1972 +}
exporting/graphite/graphite.c
+3 -3
@@ -100,8 +100,8 @@ int format_host_labels_graphite_plaintext(struct instance *instance, RRDHOST *ho
100 return 0;
101
102 rrdhost_check_rdlock(host);
103 - netdata_rwlock_rdlock(&host->labels_rwlock);
104 - for (struct label *label = host->labels; label; label = label->next) {
103 + netdata_rwlock_rdlock(&host->labels.labels_rwlock);
104 + for (struct label *label = host->labels.head; label; label = label->next) {
105 if (!should_send_label(instance, label))
106 continue;
107
@@ -113,7 +113,7 @@ int format_host_labels_graphite_plaintext(struct instance *instance, RRDHOST *ho
113 buffer_sprintf(instance->labels, "%s=%s", label->key, value);
114 }
115 }
116 - netdata_rwlock_unlock(&host->labels_rwlock);
116 + netdata_rwlock_unlock(&host->labels.labels_rwlock);
117
118 return 0;
119 }
exporting/json/json.c
+3 -3
@@ -125,8 +125,8 @@ int format_host_labels_json_plaintext(struct instance *instance, RRDHOST *host)
125
126 int count = 0;
127 rrdhost_check_rdlock(host);
128 - netdata_rwlock_rdlock(&host->labels_rwlock);
129 - for (struct label *label = host->labels; label; label = label->next) {
128 + netdata_rwlock_rdlock(&host->labels.labels_rwlock);
129 + for (struct label *label = host->labels.head; label; label = label->next) {
130 if (!should_send_label(instance, label))
131 continue;
132
@@ -138,7 +138,7 @@ int format_host_labels_json_plaintext(struct instance *instance, RRDHOST *host)
138
139 count++;
140 }
141 - netdata_rwlock_unlock(&host->labels_rwlock);
141 + netdata_rwlock_unlock(&host->labels.labels_rwlock);
142
143 buffer_strcat(instance->labels, "},");
144
exporting/opentsdb/opentsdb.c
+6 -6
@@ -153,8 +153,8 @@ int format_host_labels_opentsdb_telnet(struct instance *instance, RRDHOST *host)
153 return 0;
154
155 rrdhost_check_rdlock(localhost);
156 - netdata_rwlock_rdlock(&host->labels_rwlock);
157 - for (struct label *label = host->labels; label; label = label->next) {
156 + netdata_rwlock_rdlock(&host->labels.labels_rwlock);
157 + for (struct label *label = host->labels.head; label; label = label->next) {
158 if (!should_send_label(instance, label))
159 continue;
160
@@ -164,7 +164,7 @@ int format_host_labels_opentsdb_telnet(struct instance *instance, RRDHOST *host)
164 if (*value)
165 buffer_sprintf(instance->labels, " %s=%s", label->key, value);
166 }
167 - netdata_rwlock_unlock(&host->labels_rwlock);
167 + netdata_rwlock_unlock(&host->labels.labels_rwlock);
168
169 return 0;
170 }
@@ -294,8 +294,8 @@ int format_host_labels_opentsdb_http(struct instance *instance, RRDHOST *host)
294 return 0;
295
296 rrdhost_check_rdlock(host);
297 - netdata_rwlock_rdlock(&host->labels_rwlock);
298 - for (struct label *label = host->labels; label; label = label->next) {
297 + netdata_rwlock_rdlock(&host->labels.labels_rwlock);
298 + for (struct label *label = host->labels.head; label; label = label->next) {
299 if (!should_send_label(instance, label))
300 continue;
301
@@ -310,7 +310,7 @@ int format_host_labels_opentsdb_http(struct instance *instance, RRDHOST *host)
310 buffer_sprintf(instance->labels, "\"%s\":\"%s\"", label->key, value);
311 }
312 }
313 - netdata_rwlock_unlock(&host->labels_rwlock);
313 + netdata_rwlock_unlock(&host->labels.labels_rwlock);
314
315 return 0;
316 }
exporting/prometheus/prometheus.c
+3 -3
@@ -283,8 +283,8 @@ void format_host_labels_prometheus(struct instance *instance, RRDHOST *host)
283
284 int count = 0;
285 rrdhost_check_rdlock(host);
286 - netdata_rwlock_rdlock(&host->labels_rwlock);
287 - for (struct label *label = host->labels; label; label = label->next) {
286 + netdata_rwlock_rdlock(&host->labels.labels_rwlock);
287 + for (struct label *label = host->labels.head; label; label = label->next) {
288 if (!should_send_label(instance, label))
289 continue;
290
@@ -301,7 +301,7 @@ void format_host_labels_prometheus(struct instance *instance, RRDHOST *host)
301 count++;
302 }
303 }
304 - netdata_rwlock_unlock(&host->labels_rwlock);
304 + netdata_rwlock_unlock(&host->labels.labels_rwlock);
305 }
306
307 struct host_variables_callback_options {
exporting/prometheus/remote_write/remote_write.c
+3 -3
@@ -156,8 +156,8 @@ int format_host_prometheus_remote_write(struct instance *instance, RRDHOST *host
156
157 if (unlikely(sending_labels_configured(instance))) {
158 rrdhost_check_rdlock(host);
159 - netdata_rwlock_rdlock(&host->labels_rwlock);
160 - for (struct label *label = host->labels; label; label = label->next) {
159 + netdata_rwlock_rdlock(&host->labels.labels_rwlock);
160 + for (struct label *label = host->labels.head; label; label = label->next) {
161 if (!should_send_label(instance, label))
162 continue;
163
@@ -169,7 +169,7 @@ int format_host_prometheus_remote_write(struct instance *instance, RRDHOST *host
169
170 add_label(connector_specific_data->write_request, key, value);
171 }
172 - netdata_rwlock_unlock(&host->labels_rwlock);
172 + netdata_rwlock_unlock(&host->labels.labels_rwlock);
173 }
174
175 return 0;
exporting/tests/exporting_fixtures.c
+8 -8
@@ -43,13 +43,13 @@ int setup_rrdhost()
43 label->key = strdupz("key1");
44 label->value = strdupz("value1");
45 label->label_source = LABEL_SOURCE_NETDATA_CONF;
46 - localhost->labels = label;
46 + localhost->labels.head = label;
47
48 label = calloc(1, sizeof(struct label));
49 label->key = strdupz("key2");
50 label->value = strdupz("value2");
51 label->label_source = LABEL_SOURCE_AUTO;
52 - localhost->labels->next = label;
52 + localhost->labels.head->next = label;
53
54 localhost->rrdset_root = calloc(1, sizeof(RRDSET));
55 RRDSET *st = localhost->rrdset_root;
@@ -93,12 +93,12 @@ int teardown_rrdhost()
93 free((void *)st->name);
94 free(st);
95
96 - free(localhost->labels->next->key);
97 - free(localhost->labels->next->value);
98 - free(localhost->labels->next);
99 - free(localhost->labels->key);
100 - free(localhost->labels->value);
101 - free(localhost->labels);
96 + free(localhost->labels.head->next->key);
97 + free(localhost->labels.head->next->value);
98 + free(localhost->labels.head->next);
99 + free(localhost->labels.head->key);
100 + free(localhost->labels.head->value);
101 + free(localhost->labels.head);
102
103 free((void *)localhost->tags);
104 free(localhost);
health/health_log.c
+3 -3
@@ -73,13 +73,13 @@ inline void health_label_log_save(RRDHOST *host) {
73 if(likely(host->health_log_fp)) {
74 BUFFER *wb = buffer_create(1024);
75 rrdhost_check_rdlock(host);
76 - netdata_rwlock_rdlock(&host->labels_rwlock);
77 - struct label *l=localhost->labels;
76 + netdata_rwlock_rdlock(&host->labels.labels_rwlock);
77 + struct label *l=localhost->labels.head;
78 while (l != NULL) {
79 buffer_sprintf(wb,"%s=%s\t ", l->key, l->value);
80 l = l->next;
81 }
82 - netdata_rwlock_unlock(&host->labels_rwlock);
82 + netdata_rwlock_unlock(&host->labels.labels_rwlock);
83
84 char *write = (char *) buffer_tostring(wb) ;
85
libnetdata/avl/avl.c
+16
@@ -367,6 +367,22 @@ void avl_init_lock(avl_tree_lock *tree, int (*compar)(void * /*a*/, void * /*b*/
367 #endif /* AVL_WITHOUT_PTHREADS */
368 }
369
370 +void avl_destroy_lock(avl_tree_lock *tree) {
371 +#ifndef AVL_WITHOUT_PTHREADS
372 + int lock;
373 +
374 +#ifdef AVL_LOCK_WITH_MUTEX
375 + lock = pthread_mutex_destroy(&tree->mutex);
376 +#else
377 + lock = pthread_rwlock_destroy(&tree->rwlock);
378 +#endif
379 +
380 + if(lock != 0)
381 + fatal("Failed to destroy AVL mutex/rwlock, error: %d", lock);
382 +
383 +#endif /* AVL_WITHOUT_PTHREADS */
384 +}
385 +
386 avl *avl_search_lock(avl_tree_lock *tree, avl *item) {
387 avl_read_lock(tree);
388 avl *ret = avl_search(&tree->avl_tree, item);
libnetdata/avl/avl.h
+3
@@ -82,6 +82,9 @@ avl *avl_search(avl_tree_type *tree, avl *item);
82 void avl_init_lock(avl_tree_lock *tree, int (*compar)(void *a, void *b));
83 void avl_init(avl_tree_type *tree, int (*compar)(void *a, void *b));
84
85 +/* Destroy the avl_tree_lock locks
86 + */
87 +void avl_destroy_lock(avl_tree_lock *tree);
88
89 int avl_traverse_lock(avl_tree_lock *tree, int (*callback)(void *entry, void *data), void *data);
90 int avl_traverse(avl_tree_type *tree, int (*callback)(void *entry, void *data), void *data);
libnetdata/config/appconfig.c
+43
@@ -189,6 +189,49 @@ static inline struct section *appconfig_section_create(struct config *root, cons
189 return co;
190 }
191
192 +void appconfig_section_destroy_non_loaded(struct config *root, const char *section)
193 +{
194 + struct section *co;
195 + struct config_option *cv, *cv_next;
196 +
197 + debug(D_CONFIG, "Destroying section '%s'.", section);
198 +
199 + co = appconfig_section_find(root, section);
200 + if(!co) {
201 + error("Could not destroy section '%s'. Not found.", section);
202 + return;
203 + }
204 +
205 + config_section_wrlock(co);
206 + for(cv = co->values; cv ; cv = cv->next) {
207 + if (cv->flags & CONFIG_VALUE_LOADED) {
208 + /* Do not destroy values that were loaded from the configuration files. */
209 + config_section_unlock(co);
210 + return;
211 + }
212 + }
213 + for(cv = co->values ; cv ; cv = cv_next) {
214 + cv_next = cv->next;
215 + if(unlikely(!appconfig_option_index_del(co, cv)))
216 + error("Cannot remove config option '%s' from section '%s'.", cv->name, co->name);
217 + freez(cv->value);
218 + freez(cv->name);
219 + freez(cv);
220 + }
221 + co->values = NULL;
222 + config_section_unlock(co);
223 +
224 + if (unlikely(!appconfig_index_del(root, co))) {
225 + error("Cannot remove section '%s' from config.", section);
226 + return;
227 + }
228 +
229 + avl_destroy_lock(&co->values_index);
230 + freez(co->name);
231 + pthread_mutex_destroy(&co->mutex);
232 + freez(co);
233 +}
234 +
235
236 // ----------------------------------------------------------------------------
237 // config name-value methods
libnetdata/config/appconfig.h
+2
@@ -182,6 +182,8 @@ extern void appconfig_generate(struct config *root, BUFFER *wb, int only_changed
182
183 extern int appconfig_section_compare(void *a, void *b);
184
185 +extern void appconfig_section_destroy_non_loaded(struct config *root, const char *section);
186 +
187 extern int config_parse_duration(const char* string, int* result);
188
189 extern struct section *appconfig_get_section(struct config *root, const char *name);
streaming/receiver.c
+1 -1
@@ -421,7 +421,7 @@ static int rrdpush_receive(struct receiver_state *rpt)
421 */
422
423 // rpt->host->connected_senders++;
424 - rpt->host->labels_flag = (rpt->stream_version > 0)?LABEL_FLAG_UPDATE_STREAM:LABEL_FLAG_STOP_STREAM;
424 + rpt->host->labels.labels_flag = (rpt->stream_version > 0)?LABEL_FLAG_UPDATE_STREAM:LABEL_FLAG_STOP_STREAM;
425
426 if(health_enabled != CONFIG_BOOLEAN_NO) {
427 if(alarms_delay > 0) {
streaming/rrdpush.c
+10 -10
@@ -334,35 +334,35 @@ void rrdset_done_push(RRDSET *st) {
334
335 // labels
336 void rrdpush_send_labels(RRDHOST *host) {
337 - if (!host->labels || !(host->labels_flag & LABEL_FLAG_UPDATE_STREAM) || (host->labels_flag & LABEL_FLAG_STOP_STREAM))
337 + if (!host->labels.head || !(host->labels.labels_flag & LABEL_FLAG_UPDATE_STREAM) || (host->labels.labels_flag & LABEL_FLAG_STOP_STREAM))
338 return;
339
340 sender_start(host->sender);
341 rrdhost_rdlock(host);
342 - netdata_rwlock_rdlock(&host->labels_rwlock);
342 + netdata_rwlock_rdlock(&host->labels.labels_rwlock);
343
344 - struct label *labels = host->labels;
345 - while(labels) {
344 + struct label *label_i = host->labels.head;
345 + while(label_i) {
346 buffer_sprintf(host->sender->build
347 , "LABEL \"%s\" = %d %s\n"
348 - , labels->key
349 - , (int)labels->label_source
350 - , labels->value);
348 + , label_i->key
349 + , (int)label_i->label_source
350 + , label_i->value);
351
352 - labels = labels->next;
352 + label_i = label_i->next;
353 }
354
355 buffer_sprintf(host->sender->build
356 , "OVERWRITE %s\n", "labels");
357
358 - netdata_rwlock_unlock(&host->labels_rwlock);
358 + netdata_rwlock_unlock(&host->labels.labels_rwlock);
359 rrdhost_unlock(host);
360 sender_commit(host->sender);
361
362 if(host->rrdpush_sender_pipe[PIPE_WRITE] != -1 && write(host->rrdpush_sender_pipe[PIPE_WRITE], " ", 1) == -1)
363 error("STREAM %s [send]: cannot write to internal pipe", host->hostname);
364
365 - host->labels_flag &= ~LABEL_FLAG_UPDATE_STREAM;
365 + host->labels.labels_flag &= ~LABEL_FLAG_UPDATE_STREAM;
366 }
367
368 void rrdpush_claimed_id(RRDHOST *host)
streaming/sender.c
+4 -4
@@ -116,8 +116,8 @@ static inline void rrdpush_sender_thread_data_flush(RRDHOST *host) {
116 }
117
118 static inline void rrdpush_set_flags_to_newest_stream(RRDHOST *host) {
119 - host->labels_flag |= LABEL_FLAG_UPDATE_STREAM;
120 - host->labels_flag &= ~LABEL_FLAG_STOP_STREAM;
119 + host->labels.labels_flag |= LABEL_FLAG_UPDATE_STREAM;
120 + host->labels.labels_flag &= ~LABEL_FLAG_STOP_STREAM;
121 }
122
123 void rrdpush_encode_variable(stream_encoded_t *se, RRDHOST *host)
@@ -354,8 +354,8 @@ static int rrdpush_sender_thread_connect_to_parent(RRDHOST *host, int default_po
354 answer = memcmp(http, START_STREAMING_PROMPT, strlen(START_STREAMING_PROMPT));
355 if(!answer) {
356 version = 0;
357 - host->labels_flag |= LABEL_FLAG_STOP_STREAM;
358 - host->labels_flag &= ~LABEL_FLAG_UPDATE_STREAM;
357 + host->labels.labels_flag |= LABEL_FLAG_STOP_STREAM;
358 + host->labels.labels_flag &= ~LABEL_FLAG_UPDATE_STREAM;
359 }
360 }
361 }
web/api/formatters/json_wrapper.c
+39 -4
@@ -2,7 +2,7 @@
2
3 #include "json_wrapper.h"
4
5 -void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, int string_value, RRDDIM *temp_rd) {
5 +void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, int string_value, RRDDIM *temp_rd, char *chart_label_key) {
6 rrdset_check_rdlock(r->st);
7
8 long rows = rrdr_rows(r);
@@ -86,12 +86,12 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS
86 buffer_strcat(wb, "no data");
87 buffer_strcat(wb, sq);
88 }
89 + buffer_strcat(wb, "],\n");
90
91 // Composite charts
92 if (temp_rd) {
93 buffer_sprintf(
94 wb,
94 - "],\n"
95 " %schart_ids%s: [",
96 kq, kq);
97
@@ -114,11 +114,46 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS
114 buffer_strcat(wb, "no data");
115 buffer_strcat(wb, sq);
116 }
117 + buffer_strcat(wb, "],\n");
118 + if (chart_label_key) {
119 + uint32_t key_hash = simple_hash(chart_label_key);
120 + struct label *current_label;
121 +
122 + buffer_sprintf(
123 + wb,
124 + " %schart_labels%s: { %s%s%s : [",
125 + kq, kq, kq, chart_label_key, kq);
126 +
127 + for (c = 0, i = 0, rd = temp_rd; rd && c < r->d; c++, rd = rd->next) {
128 + if (unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN))
129 + continue;
130 + if (unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO)))
131 + continue;
132 +
133 + if (i)
134 + buffer_strcat(wb, ", ");
135 +
136 + current_label = rrdset_lookup_label_key(rd->rrdset, chart_label_key, key_hash);
137 + if (current_label) {
138 + buffer_strcat(wb, sq);
139 + buffer_strcat(wb, current_label->value);
140 + buffer_strcat(wb, sq);
141 + } else
142 + buffer_strcat(wb, "null");
143 + i++;
144 + }
145 + if (!i) {
146 + rows = 0;
147 + buffer_strcat(wb, sq);
148 + buffer_strcat(wb, "no data");
149 + buffer_strcat(wb, sq);
150 + }
151 + buffer_strcat(wb, "] },\n");
152 + }
153 }
154
155
120 - buffer_sprintf(wb, "],\n"
121 - " %slatest_values%s: ["
156 + buffer_sprintf(wb, " %slatest_values%s: ["
157 , kq, kq);
158
159 for(c = 0, i = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
web/api/formatters/json_wrapper.h
+1 -1
@@ -5,7 +5,7 @@
5
6 #include "rrd2json.h"
7
8 -extern void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, int string_value, RRDDIM *temp_rd);
8 +extern void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, int string_value, RRDDIM *temp_rd, char *chart_key);
9 extern void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value);
10
11 #endif //NETDATA_API_FORMATTER_JSON_WRAPPER_H
web/api/formatters/rrd2json.c
+13 -12
@@ -188,6 +188,7 @@ int rrdset2anything_api_v1(
188 , uint32_t options
189 , time_t *latest_timestamp
190 , struct context_param *context_param_list
191 + , char *chart_label_key
192 ) {
193 time_t last_accessed_time = now_realtime_sec();
194 st->last_accessed_time = last_accessed_time;
@@ -212,7 +213,7 @@ int rrdset2anything_api_v1(
213 case DATASOURCE_SSV:
214 if(options & RRDR_OPTION_JSON_WRAP) {
215 wb->contenttype = CT_APPLICATION_JSON;
215 - rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd);
216 + rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd, chart_label_key);
217 rrdr2ssv(r, wb, options, "", " ", "");
218 rrdr_json_wrapper_end(r, wb, format, options, 1);
219 }
@@ -225,7 +226,7 @@ int rrdset2anything_api_v1(
226 case DATASOURCE_SSV_COMMA:
227 if(options & RRDR_OPTION_JSON_WRAP) {
228 wb->contenttype = CT_APPLICATION_JSON;
228 - rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd);
229 + rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd, chart_label_key);
230 rrdr2ssv(r, wb, options, "", ",", "");
231 rrdr_json_wrapper_end(r, wb, format, options, 1);
232 }
@@ -238,7 +239,7 @@ int rrdset2anything_api_v1(
239 case DATASOURCE_JS_ARRAY:
240 if(options & RRDR_OPTION_JSON_WRAP) {
241 wb->contenttype = CT_APPLICATION_JSON;
241 - rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd);
242 + rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd, chart_label_key);
243 rrdr2ssv(r, wb, options, "[", ",", "]");
244 rrdr_json_wrapper_end(r, wb, format, options, 0);
245 }
@@ -251,7 +252,7 @@ int rrdset2anything_api_v1(
252 case DATASOURCE_CSV:
253 if(options & RRDR_OPTION_JSON_WRAP) {
254 wb->contenttype = CT_APPLICATION_JSON;
254 - rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd);
255 + rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd, chart_label_key);
256 rrdr2csv(r, wb, format, options, "", ",", "\\n", "", temp_rd);
257 rrdr_json_wrapper_end(r, wb, format, options, 1);
258 }
@@ -264,7 +265,7 @@ int rrdset2anything_api_v1(
265 case DATASOURCE_CSV_MARKDOWN:
266 if(options & RRDR_OPTION_JSON_WRAP) {
267 wb->contenttype = CT_APPLICATION_JSON;
267 - rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd);
268 + rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd, chart_label_key);
269 rrdr2csv(r, wb, format, options, "", "|", "\\n", "", temp_rd);
270 rrdr_json_wrapper_end(r, wb, format, options, 1);
271 }
@@ -277,7 +278,7 @@ int rrdset2anything_api_v1(
278 case DATASOURCE_CSV_JSON_ARRAY:
279 wb->contenttype = CT_APPLICATION_JSON;
280 if(options & RRDR_OPTION_JSON_WRAP) {
280 - rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd);
281 + rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd, chart_label_key);
282 buffer_strcat(wb, "[\n");
283 rrdr2csv(r, wb, format, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n", temp_rd);
284 buffer_strcat(wb, "\n]");
@@ -294,7 +295,7 @@ int rrdset2anything_api_v1(
295 case DATASOURCE_TSV:
296 if(options & RRDR_OPTION_JSON_WRAP) {
297 wb->contenttype = CT_APPLICATION_JSON;
297 - rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd);
298 + rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd, chart_label_key);
299 rrdr2csv(r, wb, format, options, "", "\t", "\\n", "", temp_rd);
300 rrdr_json_wrapper_end(r, wb, format, options, 1);
301 }
@@ -307,7 +308,7 @@ int rrdset2anything_api_v1(
308 case DATASOURCE_HTML:
309 if(options & RRDR_OPTION_JSON_WRAP) {
310 wb->contenttype = CT_APPLICATION_JSON;
310 - rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd);
311 + rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd, chart_label_key);
312 buffer_strcat(wb, "<html>\\n<center>\\n<table border=\\\"0\\\" cellpadding=\\\"5\\\" cellspacing=\\\"5\\\">\\n");
313 rrdr2csv(r, wb, format, options, "<tr><td>", "</td><td>", "</td></tr>\\n", "", temp_rd);
314 buffer_strcat(wb, "</table>\\n</center>\\n</html>\\n");
@@ -325,7 +326,7 @@ int rrdset2anything_api_v1(
326 wb->contenttype = CT_APPLICATION_X_JAVASCRIPT;
327
328 if(options & RRDR_OPTION_JSON_WRAP)
328 - rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd);
329 + rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd, chart_label_key);
330
331 rrdr2json(r, wb, options, 1, temp_rd);
332
@@ -337,7 +338,7 @@ int rrdset2anything_api_v1(
338 wb->contenttype = CT_APPLICATION_JSON;
339
340 if(options & RRDR_OPTION_JSON_WRAP)
340 - rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd);
341 + rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd, chart_label_key);
342
343 rrdr2json(r, wb, options, 1, temp_rd);
344
@@ -348,7 +349,7 @@ int rrdset2anything_api_v1(
349 case DATASOURCE_JSONP:
350 wb->contenttype = CT_APPLICATION_X_JAVASCRIPT;
351 if(options & RRDR_OPTION_JSON_WRAP)
351 - rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd);
352 + rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd, chart_label_key);
353
354 rrdr2json(r, wb, options, 0, temp_rd);
355
@@ -361,7 +362,7 @@ int rrdset2anything_api_v1(
362 wb->contenttype = CT_APPLICATION_JSON;
363
364 if(options & RRDR_OPTION_JSON_WRAP)
364 - rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd);
365 + rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd, chart_label_key);
366
367 rrdr2json(r, wb, options, 0, temp_rd);
368
web/api/formatters/rrd2json.h
+1
@@ -66,6 +66,7 @@ extern int rrdset2anything_api_v1(
66 , uint32_t options
67 , time_t *latest_timestamp
68 , struct context_param *context_param_list
69 + , char *chart_label_key
70 );
71
72 extern int rrdset2value_api_v1(
web/api/formatters/rrdset2json.c
+34
@@ -2,6 +2,36 @@
2
3 #include "rrdset2json.h"
4
5 +void chart_labels2json(RRDSET *st, BUFFER *wb, size_t indentation)
6 +{
7 + char tabs[11];
8 + struct label_index *labels = &st->state->labels;
9 +
10 + if (indentation > 10)
11 + indentation = 10;
12 +
13 + tabs[0] = '\0';
14 + while (indentation) {
15 + strcat(tabs, "\t");
16 + indentation--;
17 + }
18 +
19 + int count = 0;
20 + netdata_rwlock_rdlock(&labels->labels_rwlock);
21 + for (struct label *label = labels->head; label; label = label->next) {
22 + if(count > 0) buffer_strcat(wb, ",\n");
23 + buffer_strcat(wb, tabs);
24 +
25 + char value[CONFIG_MAX_VALUE * 2 + 1];
26 + sanitize_json_string(value, label->value, CONFIG_MAX_VALUE * 2);
27 + buffer_sprintf(wb, "\"%s\": \"%s\"", label->key, value);
28 +
29 + count++;
30 + }
31 + buffer_strcat(wb, "\n");
32 + netdata_rwlock_unlock(&labels->labels_rwlock);
33 +}
34 +
35 // generate JSON for the /api/v1/chart API call
36
37 void rrdset2json(RRDSET *st, BUFFER *wb, size_t *dimensions_count, size_t *memory_used, int skip_volatile) {
@@ -118,6 +148,10 @@ void rrdset2json(RRDSET *st, BUFFER *wb, size_t *dimensions_count, size_t *memor
148 "\n\t\t\t}"
149 );
150 }
151 + buffer_strcat(wb, ",\n\t\t\t\"chart_labels\": {\n");
152 + chart_labels2json(st, wb, 2);
153 + buffer_strcat(wb, "\t\t\t}\n");
154 +
155
156 buffer_sprintf(wb,
157 "\n\t\t}"
web/api/web_api_v1.c
+24 -8
@@ -402,7 +402,8 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
402 , *after_str = NULL
403 , *group_time_str = NULL
404 , *points_str = NULL
405 - , *context = NULL;
405 + , *context = NULL
406 + , *chart_label_key = NULL;
407
408 int group = RRDR_GROUPING_AVERAGE;
409 uint32_t format = DATASOURCE_JSON;
@@ -422,6 +423,7 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
423 // they are not null and not empty
424
425 if(!strcmp(name, "context")) context = value;
426 + else if(!strcmp(name, "chart_label_key")) chart_label_key = value;
427 else if(!strcmp(name, "chart")) chart = value;
428 else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
429 if(!dimensions) dimensions = buffer_create(100);
@@ -499,9 +501,15 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
501 if (context && !chart) {
502 RRDSET *st1;
503 uint32_t context_hash = simple_hash(context);
504 + uint32_t key_hash;
505 +
506 + if (chart_label_key)
507 + key_hash = simple_hash(chart_label_key);
508 +
509 rrdhost_rdlock(host);
510 rrdset_foreach_read(st1, host) {
504 - if (st1->hash_context == context_hash && !strcmp(st1->context, context))
511 + if (st1->hash_context == context_hash && !strcmp(st1->context, context) &&
512 + (!chart_label_key || rrdset_contains_label_key(st1, chart_label_key, key_hash)))
513 build_context_param_list(&context_param_list, st1);
514 }
515 rrdhost_unlock(host);
@@ -518,8 +526,16 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
526
527 if (!st && !context_param_list) {
528 if (context && !chart) {
521 - buffer_strcat(w->response.data, "Context is not found: ");
522 - buffer_strcat_htmlescape(w->response.data, context);
529 + if (!chart_label_key) {
530 + buffer_strcat(w->response.data, "Context is not found: ");
531 + buffer_strcat_htmlescape(w->response.data, context);
532 + } else {
533 + buffer_strcat(w->response.data, "Context: ");
534 + buffer_strcat_htmlescape(w->response.data, context);
535 + buffer_strcat(w->response.data, " or chart label key: ");
536 + buffer_strcat_htmlescape(w->response.data, chart_label_key);
537 + buffer_strcat(w->response.data, " not found");
538 + }
539 }
540 else {
541 buffer_strcat(w->response.data, "Chart is not found: ");
@@ -572,7 +588,7 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
588 }
589
590 ret = rrdset2anything_api_v1(st, w->response.data, dimensions, format, points, after, before, group, group_time
575 - , options, &last_timestamp_in_data, context_param_list);
591 + , options, &last_timestamp_in_data, context_param_list, chart_label_key);
592
593 free_context_param_list(&context_param_list);
594
@@ -869,8 +885,8 @@ inline void host_labels2json(RRDHOST *host, BUFFER *wb, size_t indentation) {
885
886 int count = 0;
887 rrdhost_rdlock(host);
872 - netdata_rwlock_rdlock(&host->labels_rwlock);
873 - for (struct label *label = host->labels; label; label = label->next) {
888 + netdata_rwlock_rdlock(&host->labels.labels_rwlock);
889 + for (struct label *label = host->labels.head; label; label = label->next) {
890 if(count > 0) buffer_strcat(wb, ",\n");
891 buffer_strcat(wb, tabs);
892
@@ -881,7 +897,7 @@ inline void host_labels2json(RRDHOST *host, BUFFER *wb, size_t indentation) {
897 count++;
898 }
899 buffer_strcat(wb, "\n");
884 - netdata_rwlock_unlock(&host->labels_rwlock);
900 + netdata_rwlock_unlock(&host->labels.labels_rwlock);
901 rrdhost_unlock(host);
902 }
903