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