mirror of
https://github.com/netdata/netdata.git
synced 2025-04-27 06:10:43 +00:00
Kubernetes labels (#10107)
Co-authored-by: Markos Fountoulakis <markos.fountoulakis.senior@gmail.com> Co-authored-by: Vladimir Kobal <vlad@prokk.net>
This commit is contained in:
parent
7bfa8c8eba
commit
0f8175dd30
38 changed files with 1233 additions and 449 deletions
CMakeLists.txtMakefile.am
collectors
daemon
database
exporting
graphite
json
opentsdb
prometheus
tests
health
libnetdata
streaming
web/api
|
@ -590,6 +590,7 @@ set(RRD_PLUGIN_FILES
|
|||
database/rrddimvar.h
|
||||
database/rrdfamily.c
|
||||
database/rrdhost.c
|
||||
database/rrdlabels.c
|
||||
database/rrd.c
|
||||
database/rrd.h
|
||||
database/rrdset.c
|
||||
|
@ -1332,12 +1333,29 @@ endif()
|
|||
target_link_libraries(valid_urls_testdriver libnetdata ${NETDATA_COMMON_LIBRARIES} ${CMOCKA_LIBRARIES})
|
||||
# add_test(NAME test_valid_urls COMMAND valid_urls_testdriver)
|
||||
|
||||
set(CGROUPS_TEST_FILES
|
||||
collectors/cgroups.plugin/tests/test_cgroups_plugin.c
|
||||
collectors/cgroups.plugin/tests/test_cgroups_plugin.h
|
||||
collectors/cgroups.plugin/tests/test_doubles.c
|
||||
database/rrdlabels.c
|
||||
database/rrd.h
|
||||
)
|
||||
add_executable(cgroups_testdriver ${CGROUPS_TEST_FILES} ${CGROUPS_PLUGIN_FILES})
|
||||
target_link_options(
|
||||
cgroups_testdriver
|
||||
PRIVATE
|
||||
-Wl,--wrap=add_label_to_list
|
||||
)
|
||||
target_link_libraries(cgroups_testdriver libnetdata ${NETDATA_COMMON_LIBRARIES} ${CMOCKA_LIBRARIES})
|
||||
add_test(NAME test_cgroups COMMAND cgroups_testdriver)
|
||||
|
||||
set_target_properties(
|
||||
str2ld_testdriver
|
||||
storage_number_testdriver
|
||||
exporting_engine_testdriver
|
||||
web_api_testdriver
|
||||
valid_urls_testdriver
|
||||
cgroups_testdriver
|
||||
PROPERTIES RUNTIME_OUTPUT_DIRECTORY tests
|
||||
)
|
||||
|
||||
|
|
17
Makefile.am
17
Makefile.am
|
@ -366,6 +366,7 @@ RRD_PLUGIN_FILES = \
|
|||
database/rrddimvar.h \
|
||||
database/rrdfamily.c \
|
||||
database/rrdhost.c \
|
||||
database/rrdlabels.c \
|
||||
database/rrd.c \
|
||||
database/rrd.h \
|
||||
database/rrdset.c \
|
||||
|
@ -854,6 +855,7 @@ if ENABLE_UNITTESTS
|
|||
exporting/tests/exporting_engine_testdriver \
|
||||
web/api/tests/web_api_testdriver \
|
||||
web/api/tests/valid_urls_testdriver \
|
||||
collectors/cgroups_plugin/tests/cgroups_testdriver \
|
||||
$(NULL)
|
||||
|
||||
TESTS = $(check_PROGRAMS)
|
||||
|
@ -1006,4 +1008,19 @@ if ENABLE_BACKEND_MONGODB
|
|||
-Wl,--wrap=mongoc_collection_insert_many \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
collectors_cgroups_plugin_tests_cgroups_testdriver_SOURCES = \
|
||||
collectors/cgroups.plugin/tests/test_cgroups_plugin.c \
|
||||
collectors/cgroups.plugin/tests/test_cgroups_plugin.h \
|
||||
collectors/cgroups.plugin/tests/test_doubles.c \
|
||||
$(CGROUPS_PLUGIN_FILES) \
|
||||
database/rrdlabels.c \
|
||||
database/rrd.h \
|
||||
$(LIBNETDATA_FILES) \
|
||||
$(NULL)
|
||||
collectors_cgroups_plugin_tests_cgroups_testdriver_LDADD = $(NETDATA_COMMON_LIBS) $(TEST_LIBS)
|
||||
collectors_cgroups_plugin_tests_cgroups_testdriver_LDFLAGS = \
|
||||
-Wl,--wrap=add_label_to_list \
|
||||
$(NULL)
|
||||
|
||||
endif
|
||||
|
|
|
@ -17,63 +17,63 @@ export LC_ALL=C
|
|||
PROGRAM_NAME="$(basename "${0}")"
|
||||
|
||||
logdate() {
|
||||
date "+%Y-%m-%d %H:%M:%S"
|
||||
date "+%Y-%m-%d %H:%M:%S"
|
||||
}
|
||||
|
||||
log() {
|
||||
local status="${1}"
|
||||
shift
|
||||
local status="${1}"
|
||||
shift
|
||||
|
||||
echo >&2 "$(logdate): ${PROGRAM_NAME}: ${status}: ${*}"
|
||||
echo >&2 "$(logdate): ${PROGRAM_NAME}: ${status}: ${*}"
|
||||
|
||||
}
|
||||
|
||||
warning() {
|
||||
log WARNING "${@}"
|
||||
log WARNING "${@}"
|
||||
}
|
||||
|
||||
error() {
|
||||
log ERROR "${@}"
|
||||
log ERROR "${@}"
|
||||
}
|
||||
|
||||
info() {
|
||||
log INFO "${@}"
|
||||
log INFO "${@}"
|
||||
}
|
||||
|
||||
fatal() {
|
||||
log FATAL "${@}"
|
||||
exit 1
|
||||
log FATAL "${@}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
function docker_like_get_name_command() {
|
||||
local command="${1}"
|
||||
local id="${2}"
|
||||
info "Running command: ${command} ps --filter=id=\"${id}\" --format=\"{{.Names}}\""
|
||||
NAME="$(${command} ps --filter=id="${id}" --format="{{.Names}}")"
|
||||
return 0
|
||||
local command="${1}"
|
||||
local id="${2}"
|
||||
info "Running command: ${command} ps --filter=id=\"${id}\" --format=\"{{.Names}}\""
|
||||
NAME="$(${command} ps --filter=id="${id}" --format="{{.Names}}")"
|
||||
return 0
|
||||
}
|
||||
|
||||
function docker_like_get_name_api() {
|
||||
local host_var="${1}"
|
||||
local host="${!host_var}"
|
||||
local path="/containers/${2}/json"
|
||||
if [ -z "${host}" ]; then
|
||||
warning "No ${host_var} is set"
|
||||
return 1
|
||||
fi
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
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"
|
||||
return 1
|
||||
fi
|
||||
if [ -S "${host}" ]; then
|
||||
info "Running API command: curl --unix-socket \"${host}\" http://localhost${path}"
|
||||
JSON=$(curl -sS --unix-socket "${host}" "http://localhost${path}")
|
||||
else
|
||||
info "Running API command: curl \"${host}${path}\""
|
||||
JSON=$(curl -sS "${host}${path}")
|
||||
fi
|
||||
NAME=$(echo "${JSON}" | jq -r .Name,.Config.Hostname | grep -v null | head -n1 | sed 's|^/||')
|
||||
return 0
|
||||
local host_var="${1}"
|
||||
local host="${!host_var}"
|
||||
local path="/containers/${2}/json"
|
||||
if [ -z "${host}" ]; then
|
||||
warning "No ${host_var} is set"
|
||||
return 1
|
||||
fi
|
||||
if ! command -v jq > /dev/null 2>&1; then
|
||||
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"
|
||||
return 1
|
||||
fi
|
||||
if [ -S "${host}" ]; then
|
||||
info "Running API command: curl --unix-socket \"${host}\" http://localhost${path}"
|
||||
JSON=$(curl -sS --unix-socket "${host}" "http://localhost${path}")
|
||||
else
|
||||
info "Running API command: curl \"${host}${path}\""
|
||||
JSON=$(curl -sS "${host}${path}")
|
||||
fi
|
||||
NAME=$(echo "${JSON}" | jq -r .Name,.Config.Hostname | grep -v null | head -n1 | sed 's|^/||')
|
||||
return 0
|
||||
}
|
||||
|
||||
# get_lbl_val returns the value for the label with the given name.
|
||||
|
@ -99,6 +99,21 @@ function get_lbl_val() {
|
|||
return 1
|
||||
}
|
||||
|
||||
function add_lbl_prefix() {
|
||||
local orig_labels prefix
|
||||
orig_labels="${1}"
|
||||
prefix="${2}"
|
||||
|
||||
IFS=, read -ra labels <<< "$orig_labels"
|
||||
|
||||
local new_labels
|
||||
for l in "${labels[@]}"; do
|
||||
new_labels+="${prefix}${l},"
|
||||
done
|
||||
|
||||
echo "${new_labels:0:-1}" # trim last ','
|
||||
}
|
||||
|
||||
# k8s_get_kubepod_name resolves */kubepods/* cgroup name.
|
||||
# pod level cgroup name format: 'pod_<namespace>_<pod_name>'
|
||||
# container level cgroup name format: 'cntr_<namespace>_<pod_name>_<container_name>'
|
||||
|
@ -122,11 +137,11 @@ function k8s_get_kubepod_name() {
|
|||
#
|
||||
# NOTE: cgroups plugin uses '_' to join dir names, so it is <parent>_<child>_<child>_...
|
||||
|
||||
local funcname="${FUNCNAME[0]}"
|
||||
local fn="${FUNCNAME[0]}"
|
||||
local id="${1}"
|
||||
|
||||
if [[ ! $id =~ ^kubepods ]]; then
|
||||
warning "${funcname}: '${id}' is not kubepod cgroup."
|
||||
warning "${fn}: '${id}' is not kubepod cgroup."
|
||||
return 1
|
||||
fi
|
||||
|
||||
|
@ -160,72 +175,121 @@ function k8s_get_kubepod_name() {
|
|||
fi
|
||||
|
||||
if [ -z "$pod_uid" ] && [ -z "$cntr_id" ]; then
|
||||
warning "${funcname}: can't extract pod_uid or container_id from the cgroup '$id'."
|
||||
warning "${fn}: can't extract pod_uid or container_id from the cgroup '$id'."
|
||||
return 1
|
||||
fi
|
||||
|
||||
[ -n "$pod_uid" ] && info "${funcname}: cgroup '$id' is a pod(uid:$pod_uid)"
|
||||
[ -n "$cntr_id" ] && info "${funcname}: cgroup '$id' is a container(id:$cntr_id)"
|
||||
[ -n "$pod_uid" ] && info "${fn}: cgroup '$id' is a pod(uid:$pod_uid)"
|
||||
[ -n "$cntr_id" ] && info "${fn}: cgroup '$id' is a container(id:$cntr_id)"
|
||||
|
||||
if ! command -v jq > /dev/null 2>&1; then
|
||||
warning "${funcname}: 'jq' command not available."
|
||||
warning "${fn}: 'jq' command not available."
|
||||
return 1
|
||||
fi
|
||||
|
||||
local kube_system_ns
|
||||
local tmp_kube_system_ns_file="${TMPDIR:-"/tmp/"}netdata-cgroups-kube-system-ns"
|
||||
[ -f "$tmp_kube_system_ns_file" ] && kube_system_ns=$(cat "$tmp_kube_system_ns_file" 2> /dev/null)
|
||||
|
||||
local pods
|
||||
if [ -n "${KUBERNETES_SERVICE_HOST}" ] && [ -n "${KUBERNETES_PORT_443_TCP_PORT}" ]; then
|
||||
local token header url
|
||||
local token header host url
|
||||
token="$(< /var/run/secrets/kubernetes.io/serviceaccount/token)"
|
||||
header="Authorization: Bearer $token"
|
||||
url="https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT/api/v1/pods"
|
||||
host="$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT"
|
||||
|
||||
if [ -z "$kube_system_ns" ]; then
|
||||
url="https://$host/api/v1/namespaces/kube-system"
|
||||
# FIX: check HTTP response code
|
||||
if ! kube_system_ns=$(curl -sSk -H "$header" "$url" 2>&1); then
|
||||
warning "${fn}: error on curl '${url}': ${kube_system_ns}."
|
||||
else
|
||||
echo "$kube_system_ns" > "$tmp_kube_system_ns_file" 2> /dev/null
|
||||
fi
|
||||
fi
|
||||
|
||||
url="https://$host/api/v1/pods"
|
||||
[ -n "$MY_NODE_NAME" ] && url+="?fieldSelector=spec.nodeName==$MY_NODE_NAME"
|
||||
# FIX: check HTTP response code
|
||||
if ! pods=$(curl -sSk -H "$header" "$url" 2>&1); then
|
||||
warning "${funcname}: error on curl '${url}': ${pods}."
|
||||
warning "${fn}: error on curl '${url}': ${pods}."
|
||||
return 1
|
||||
fi
|
||||
elif ps -C kubelet > /dev/null 2>&1 && command -v kubectl > /dev/null 2>&1; then
|
||||
if [ -z "$kube_system_ns" ]; then
|
||||
if ! kube_system_ns=$(kubectl get namespaces kube-system -o json 2>&1); then
|
||||
warning "${fn}: error on 'kubectl': ${kube_system_ns}."
|
||||
else
|
||||
echo "$kube_system_ns" > "$tmp_kube_system_ns_file" 2> /dev/null
|
||||
fi
|
||||
fi
|
||||
|
||||
[[ -z ${KUBE_CONFIG+x} ]] && KUBE_CONFIG="/etc/kubernetes/admin.conf"
|
||||
if ! pods=$(kubectl --kubeconfig="$KUBE_CONFIG" get pods --all-namespaces -o json 2>&1); then
|
||||
warning "${funcname}: error on 'kubectl': ${pods}."
|
||||
warning "${fn}: error on 'kubectl': ${pods}."
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
warning "${funcname}: not inside the k8s cluster and 'kubectl' command not available."
|
||||
warning "${fn}: not inside the k8s cluster and 'kubectl' command not available."
|
||||
return 1
|
||||
fi
|
||||
|
||||
local kube_system_uid
|
||||
if [ -n "$kube_system_ns" ] && ! kube_system_uid=$(jq -r '.metadata.uid' <<< "$kube_system_ns" 2>&1); then
|
||||
warning "${fn}: error on 'jq' parse kube_system_ns: ${kube_system_uid}."
|
||||
fi
|
||||
|
||||
local jq_filter
|
||||
# 'namespace="<NAMESPACE>",pod_name="<NAME>",pod_uid="<UID>",container_name="<NAME>",container_ID="<ID>"'
|
||||
# wrong field value is 'null'
|
||||
jq_filter+='.items[] | '
|
||||
jq_filter+='"namespace=\"\(.metadata.namespace)\",pod_name=\"\(.metadata.name)\",pod_uid=\"\(.metadata.uid)\"" + '
|
||||
jq_filter+='(.status.containerStatuses[]? | '
|
||||
jq_filter+='",container_name=\"\(.name)\",container_id=\"\(.containerID)\""'
|
||||
jq_filter+=') | '
|
||||
jq_filter+='sub("docker://";"")'
|
||||
jq_filter+='.items[] | "'
|
||||
jq_filter+='namespace=\"\(.metadata.namespace)\",'
|
||||
jq_filter+='pod_name=\"\(.metadata.name)\",'
|
||||
jq_filter+='pod_uid=\"\(.metadata.uid)\",'
|
||||
#jq_filter+='\(.metadata.labels | to_entries | map("pod_label_"+.key+"=\""+.value+"\"") | join(",") | if length > 0 then .+"," else . end)'
|
||||
jq_filter+='\((.metadata.ownerReferences[]? | select(.controller==true) | "controller_kind=\""+.kind+"\",controller_name=\""+.name+"\",") // "")'
|
||||
jq_filter+='node_name=\"\(.spec.nodeName)\",'
|
||||
jq_filter+='" + '
|
||||
jq_filter+='(.status.containerStatuses[]? | "'
|
||||
jq_filter+='container_name=\"\(.name)\",'
|
||||
jq_filter+='container_id=\"\(.containerID)\"'
|
||||
jq_filter+='") | '
|
||||
jq_filter+='sub("docker://";"")' # containerID: docker://a346da9bc0e3eaba6b295f64ac16e02f2190db2cef570835706a9e7a36e2c722
|
||||
|
||||
local containers
|
||||
if ! containers=$(jq -r "${jq_filter}" <<< "$pods" 2>&1); then
|
||||
warning "${funcname}: error on 'jq' parse: ${containers}."
|
||||
warning "${fn}: error on 'jq' parse pods: ${containers}."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# available labels:
|
||||
# namespace, pod_name, pod_uid, container_name, container_id
|
||||
# namespace, pod_name, pod_uid, container_name, container_id, node_name
|
||||
local labels
|
||||
if [ -n "$cntr_id" ]; then
|
||||
if labels=$(grep "$cntr_id" <<< "$containers" 2> /dev/null); then
|
||||
name="cntr_$(get_lbl_val "$labels" namespace)_$(get_lbl_val "$labels" pod_name)_$(get_lbl_val "$labels" container_name)"
|
||||
labels+=',kind="container"'
|
||||
[ -n "$kube_system_uid" ] && [ "$kube_system_uid" != "null" ] && labels+=",cluster_id=\"$kube_system_uid\""
|
||||
name="cntr"
|
||||
name+="_$(get_lbl_val "$labels" namespace)"
|
||||
name+="_$(get_lbl_val "$labels" pod_name)"
|
||||
name+="_$(get_lbl_val "$labels" container_name)"
|
||||
labels=$(add_lbl_prefix "$labels" "k8s_")
|
||||
name+=" $labels"
|
||||
fi
|
||||
elif [ -n "$pod_uid" ]; then
|
||||
if labels=$(grep "$pod_uid" -m 1 <<< "$containers" 2> /dev/null); then
|
||||
labels="${labels%%,cont*}"
|
||||
name="pod_$(get_lbl_val "$labels" namespace)_$(get_lbl_val "$labels" pod_name)"
|
||||
labels="${labels%%,container_*}"
|
||||
labels+=',kind="pod"'
|
||||
[ -n "$kube_system_uid" ] && [ "$kube_system_uid" != "null" ] && labels+=",cluster_id=\"$kube_system_uid\""
|
||||
name="pod"
|
||||
name+="_$(get_lbl_val "$labels" namespace)"
|
||||
name+="_$(get_lbl_val "$labels" pod_name)"
|
||||
labels=$(add_lbl_prefix "$labels" "k8s_")
|
||||
name+=" $labels"
|
||||
fi
|
||||
fi
|
||||
|
||||
# jq filter nonexistent field and nonexistent label value is 'null'
|
||||
if [[ $name =~ _null(_|$) ]]; then
|
||||
warning "${funcname}: invalid name: $name (cgroup '$id')"
|
||||
warning "${fn}: invalid name: $name (cgroup '$id')"
|
||||
name=""
|
||||
fi
|
||||
|
||||
|
@ -235,69 +299,77 @@ function k8s_get_kubepod_name() {
|
|||
}
|
||||
|
||||
function k8s_get_name() {
|
||||
local funcname="${FUNCNAME[0]}"
|
||||
local fn="${FUNCNAME[0]}"
|
||||
local id="${1}"
|
||||
|
||||
NAME=$(k8s_get_kubepod_name "$id")
|
||||
|
||||
if [ -z "${NAME}" ]; then
|
||||
warning "${funcname}: cannot find the name of cgroup with id '${id}'. Setting name to ${id} and disabling it."
|
||||
warning "${fn}: cannot find the name of cgroup with id '${id}'. Setting name to ${id} and disabling it."
|
||||
NAME="${id}"
|
||||
NAME_NOT_FOUND=3
|
||||
else
|
||||
NAME="k8s_${NAME}"
|
||||
info "${funcname}: cgroup '${id}' has chart name '${NAME}'"
|
||||
|
||||
local name labels
|
||||
name=${NAME%% *}
|
||||
labels=${NAME#* }
|
||||
if [ "$name" != "$labels" ]; then
|
||||
info "${fn}: cgroup '${id}' has chart name '${name}', labels '${labels}"
|
||||
else
|
||||
info "${fn}: cgroup '${id}' has chart name '${NAME}'"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function docker_get_name() {
|
||||
local id="${1}"
|
||||
if hash docker 2>/dev/null; then
|
||||
docker_like_get_name_command docker "${id}"
|
||||
else
|
||||
docker_like_get_name_api DOCKER_HOST "${id}" || docker_like_get_name_command podman "${id}"
|
||||
fi
|
||||
if [ -z "${NAME}" ]; then
|
||||
warning "cannot find the name of docker container '${id}'"
|
||||
NAME_NOT_FOUND=2
|
||||
NAME="${id:0:12}"
|
||||
else
|
||||
info "docker container '${id}' is named '${NAME}'"
|
||||
fi
|
||||
local id="${1}"
|
||||
if hash docker 2> /dev/null; then
|
||||
docker_like_get_name_command docker "${id}"
|
||||
else
|
||||
docker_like_get_name_api DOCKER_HOST "${id}" || docker_like_get_name_command podman "${id}"
|
||||
fi
|
||||
if [ -z "${NAME}" ]; then
|
||||
warning "cannot find the name of docker container '${id}'"
|
||||
NAME_NOT_FOUND=2
|
||||
NAME="${id:0:12}"
|
||||
else
|
||||
info "docker container '${id}' is named '${NAME}'"
|
||||
fi
|
||||
}
|
||||
|
||||
function docker_validate_id() {
|
||||
local id="${1}"
|
||||
if [ -n "${id}" ] && { [ ${#id} -eq 64 ] || [ ${#id} -eq 12 ]; }; then
|
||||
docker_get_name "${id}"
|
||||
else
|
||||
error "a docker id cannot be extracted from docker cgroup '${CGROUP}'."
|
||||
fi
|
||||
local id="${1}"
|
||||
if [ -n "${id}" ] && { [ ${#id} -eq 64 ] || [ ${#id} -eq 12 ]; }; then
|
||||
docker_get_name "${id}"
|
||||
else
|
||||
error "a docker id cannot be extracted from docker cgroup '${CGROUP}'."
|
||||
fi
|
||||
}
|
||||
|
||||
function podman_get_name() {
|
||||
local id="${1}"
|
||||
local id="${1}"
|
||||
|
||||
# for Podman, prefer using the API if we can, as netdata will not normally have access
|
||||
# to other users' containers, so they will not be visible when running `podman ps`
|
||||
docker_like_get_name_api PODMAN_HOST "${id}" || docker_like_get_name_command podman "${id}"
|
||||
# for Podman, prefer using the API if we can, as netdata will not normally have access
|
||||
# to other users' containers, so they will not be visible when running `podman ps`
|
||||
docker_like_get_name_api PODMAN_HOST "${id}" || docker_like_get_name_command podman "${id}"
|
||||
|
||||
if [ -z "${NAME}" ]; then
|
||||
warning "cannot find the name of podman container '${id}'"
|
||||
NAME_NOT_FOUND=2
|
||||
NAME="${id:0:12}"
|
||||
else
|
||||
info "podman container '${id}' is named '${NAME}'"
|
||||
fi
|
||||
if [ -z "${NAME}" ]; then
|
||||
warning "cannot find the name of podman container '${id}'"
|
||||
NAME_NOT_FOUND=2
|
||||
NAME="${id:0:12}"
|
||||
else
|
||||
info "podman container '${id}' is named '${NAME}'"
|
||||
fi
|
||||
}
|
||||
|
||||
function podman_validate_id() {
|
||||
local id="${1}"
|
||||
if [ -n "${id}" ] && [ ${#id} -eq 64 ]; then
|
||||
podman_get_name "${id}"
|
||||
else
|
||||
error "a podman id cannot be extracted from docker cgroup '${CGROUP}'."
|
||||
fi
|
||||
local id="${1}"
|
||||
if [ -n "${id}" ] && [ ${#id} -eq 64 ]; then
|
||||
podman_get_name "${id}"
|
||||
else
|
||||
error "a podman id cannot be extracted from docker cgroup '${CGROUP}'."
|
||||
fi
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
@ -314,86 +386,85 @@ NAME=
|
|||
# -----------------------------------------------------------------------------
|
||||
|
||||
if [ -z "${CGROUP}" ]; then
|
||||
fatal "called without a cgroup name. Nothing to do."
|
||||
fatal "called without a cgroup name. Nothing to do."
|
||||
fi
|
||||
|
||||
for CONFIG in "${NETDATA_USER_CONFIG_DIR}/cgroups-names.conf" "${NETDATA_STOCK_CONFIG_DIR}/cgroups-names.conf"; do
|
||||
if [ -f "${CONFIG}" ]; then
|
||||
NAME="$(grep "^${CGROUP} " "${CONFIG}" | sed 's/[[:space:]]\+/ /g' | cut -d ' ' -f 2)"
|
||||
if [ -z "${NAME}" ]; then
|
||||
info "cannot find cgroup '${CGROUP}' in '${CONFIG}'."
|
||||
else
|
||||
break
|
||||
fi
|
||||
#else
|
||||
# info "configuration file '${CONFIG}' is not available."
|
||||
fi
|
||||
if [ -f "${CONFIG}" ]; then
|
||||
NAME="$(grep "^${CGROUP} " "${CONFIG}" | sed 's/[[:space:]]\+/ /g' | cut -d ' ' -f 2)"
|
||||
if [ -z "${NAME}" ]; then
|
||||
info "cannot find cgroup '${CGROUP}' in '${CONFIG}'."
|
||||
else
|
||||
break
|
||||
fi
|
||||
#else
|
||||
# info "configuration file '${CONFIG}' is not available."
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "${NAME}" ]; then
|
||||
if [[ ${CGROUP} =~ ^.*kubepods.* ]]; then
|
||||
k8s_get_name "${CGROUP}"
|
||||
fi
|
||||
if [[ ${CGROUP} =~ ^.*kubepods.* ]]; then
|
||||
k8s_get_name "${CGROUP}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "${NAME}" ]; then
|
||||
if [[ ${CGROUP} =~ ^.*docker[-_/\.][a-fA-F0-9]+[-_\.]?.*$ ]]; then
|
||||
# docker containers
|
||||
#shellcheck disable=SC1117
|
||||
DOCKERID="$(echo "${CGROUP}" | sed "s|^.*docker[-_/]\([a-fA-F0-9]\+\)[-_\.]\?.*$|\1|")"
|
||||
docker_validate_id "${DOCKERID}"
|
||||
elif [[ ${CGROUP} =~ ^.*ecs[-_/\.][a-fA-F0-9]+[-_\.]?.*$ ]]; then
|
||||
# ECS
|
||||
#shellcheck disable=SC1117
|
||||
DOCKERID="$(echo "${CGROUP}" | sed "s|^.*ecs[-_/].*[-_/]\([a-fA-F0-9]\+\)[-_\.]\?.*$|\1|")"
|
||||
docker_validate_id "${DOCKERID}"
|
||||
elif [[ ${CGROUP} =~ ^.*libpod-[a-fA-F0-9]+.*$ ]]; then
|
||||
# Podman
|
||||
PODMANID="$(echo "${CGROUP}" | sed "s|^.*libpod-\([a-fA-F0-9]\+\).*$|\1|")"
|
||||
podman_validate_id "${PODMANID}"
|
||||
if [[ ${CGROUP} =~ ^.*docker[-_/\.][a-fA-F0-9]+[-_\.]?.*$ ]]; then
|
||||
# docker containers
|
||||
#shellcheck disable=SC1117
|
||||
DOCKERID="$(echo "${CGROUP}" | sed "s|^.*docker[-_/]\([a-fA-F0-9]\+\)[-_\.]\?.*$|\1|")"
|
||||
docker_validate_id "${DOCKERID}"
|
||||
elif [[ ${CGROUP} =~ ^.*ecs[-_/\.][a-fA-F0-9]+[-_\.]?.*$ ]]; then
|
||||
# ECS
|
||||
#shellcheck disable=SC1117
|
||||
DOCKERID="$(echo "${CGROUP}" | sed "s|^.*ecs[-_/].*[-_/]\([a-fA-F0-9]\+\)[-_\.]\?.*$|\1|")"
|
||||
docker_validate_id "${DOCKERID}"
|
||||
elif [[ ${CGROUP} =~ ^.*libpod-[a-fA-F0-9]+.*$ ]]; then
|
||||
# Podman
|
||||
PODMANID="$(echo "${CGROUP}" | sed "s|^.*libpod-\([a-fA-F0-9]\+\).*$|\1|")"
|
||||
podman_validate_id "${PODMANID}"
|
||||
|
||||
elif [[ ${CGROUP} =~ machine.slice[_/].*\.service ]]; then
|
||||
# systemd-nspawn
|
||||
NAME="$(echo "${CGROUP}" | sed 's/.*machine.slice[_\/]\(.*\)\.service/\1/g')"
|
||||
elif [[ ${CGROUP} =~ machine.slice[_/].*\.service ]]; then
|
||||
# systemd-nspawn
|
||||
NAME="$(echo "${CGROUP}" | sed 's/.*machine.slice[_\/]\(.*\)\.service/\1/g')"
|
||||
|
||||
elif [[ ${CGROUP} =~ machine.slice_machine.*-qemu ]]; then
|
||||
# libvirtd / qemu virtual machines
|
||||
# NAME="$(echo ${CGROUP} | sed 's/machine.slice_machine.*-qemu//; s/\/x2d//; s/\/x2d/\-/g; s/\.scope//g')"
|
||||
NAME="qemu_$(echo "${CGROUP}" | sed 's/machine.slice_machine.*-qemu//; s/\/x2d[[:digit:]]*//; s/\/x2d//g; s/\.scope//g')"
|
||||
elif [[ ${CGROUP} =~ machine.slice_machine.*-qemu ]]; then
|
||||
# libvirtd / qemu virtual machines
|
||||
# NAME="$(echo ${CGROUP} | sed 's/machine.slice_machine.*-qemu//; s/\/x2d//; s/\/x2d/\-/g; s/\.scope//g')"
|
||||
NAME="qemu_$(echo "${CGROUP}" | sed 's/machine.slice_machine.*-qemu//; s/\/x2d[[:digit:]]*//; s/\/x2d//g; s/\.scope//g')"
|
||||
|
||||
elif [[ ${CGROUP} =~ machine_.*\.libvirt-qemu ]]; then
|
||||
# libvirtd / qemu virtual machines
|
||||
NAME="qemu_$(echo "${CGROUP}" | sed 's/^machine_//; s/\.libvirt-qemu$//; s/-/_/;')"
|
||||
elif [[ ${CGROUP} =~ machine_.*\.libvirt-qemu ]]; then
|
||||
# libvirtd / qemu virtual machines
|
||||
NAME="qemu_$(echo "${CGROUP}" | sed 's/^machine_//; s/\.libvirt-qemu$//; s/-/_/;')"
|
||||
|
||||
elif [[ ${CGROUP} =~ qemu.slice_([0-9]+).scope && -d /etc/pve ]]; then
|
||||
# Proxmox VMs
|
||||
elif [[ ${CGROUP} =~ qemu.slice_([0-9]+).scope && -d /etc/pve ]]; then
|
||||
# Proxmox VMs
|
||||
|
||||
FILENAME="/etc/pve/qemu-server/${BASH_REMATCH[1]}.conf"
|
||||
if [[ -f $FILENAME && -r $FILENAME ]]; then
|
||||
NAME="qemu_$(grep -e '^name: ' "/etc/pve/qemu-server/${BASH_REMATCH[1]}.conf" | head -1 | sed -rn 's|\s*name\s*:\s*(.*)?$|\1|p')"
|
||||
else
|
||||
error "proxmox config file missing ${FILENAME} or netdata does not have read access. Please ensure netdata is a member of www-data group."
|
||||
fi
|
||||
elif [[ ${CGROUP} =~ lxc_([0-9]+) && -d /etc/pve ]]; then
|
||||
# Proxmox Containers (LXC)
|
||||
FILENAME="/etc/pve/qemu-server/${BASH_REMATCH[1]}.conf"
|
||||
if [[ -f $FILENAME && -r $FILENAME ]]; then
|
||||
NAME="qemu_$(grep -e '^name: ' "/etc/pve/qemu-server/${BASH_REMATCH[1]}.conf" | head -1 | sed -rn 's|\s*name\s*:\s*(.*)?$|\1|p')"
|
||||
else
|
||||
error "proxmox config file missing ${FILENAME} or netdata does not have read access. Please ensure netdata is a member of www-data group."
|
||||
fi
|
||||
elif [[ ${CGROUP} =~ lxc_([0-9]+) && -d /etc/pve ]]; then
|
||||
# Proxmox Containers (LXC)
|
||||
|
||||
FILENAME="/etc/pve/lxc/${BASH_REMATCH[1]}.conf"
|
||||
if [[ -f ${FILENAME} && -r ${FILENAME} ]]; then
|
||||
NAME=$(grep -e '^hostname: ' "/etc/pve/lxc/${BASH_REMATCH[1]}.conf" | head -1 | sed -rn 's|\s*hostname\s*:\s*(.*)?$|\1|p')
|
||||
else
|
||||
error "proxmox config file missing ${FILENAME} or netdata does not have read access. Please ensure netdata is a member of www-data group."
|
||||
fi
|
||||
elif [[ ${CGROUP} =~ lxc.payload.* ]]; then
|
||||
# LXC 4.0
|
||||
NAME="$(echo "${CGROUP}" | sed 's/lxc\.payload\.\(.*\)/\1/g')"
|
||||
fi
|
||||
FILENAME="/etc/pve/lxc/${BASH_REMATCH[1]}.conf"
|
||||
if [[ -f ${FILENAME} && -r ${FILENAME} ]]; then
|
||||
NAME=$(grep -e '^hostname: ' "/etc/pve/lxc/${BASH_REMATCH[1]}.conf" | head -1 | sed -rn 's|\s*hostname\s*:\s*(.*)?$|\1|p')
|
||||
else
|
||||
error "proxmox config file missing ${FILENAME} or netdata does not have read access. Please ensure netdata is a member of www-data group."
|
||||
fi
|
||||
elif [[ ${CGROUP} =~ lxc.payload.* ]]; then
|
||||
# LXC 4.0
|
||||
NAME="$(echo "${CGROUP}" | sed 's/lxc\.payload\.\(.*\)/\1/g')"
|
||||
fi
|
||||
|
||||
[ -z "${NAME}" ] && NAME="${CGROUP}"
|
||||
[ ${#NAME} -gt 100 ] && NAME="${NAME:0:100}"
|
||||
[ -z "${NAME}" ] && NAME="${CGROUP}"
|
||||
[ ${#NAME} -gt 100 ] && NAME="${NAME:0:100}"
|
||||
fi
|
||||
|
||||
info "cgroup '${CGROUP}' is called '${NAME}'"
|
||||
echo "${NAME}"
|
||||
|
||||
exit ${NAME_NOT_FOUND}
|
||||
|
||||
|
|
|
@ -600,6 +600,8 @@ struct cgroup {
|
|||
|
||||
char *chart_title;
|
||||
|
||||
struct label *chart_labels;
|
||||
|
||||
struct cpuacct_stat cpuacct_stat;
|
||||
struct cpuacct_usage cpuacct_usage;
|
||||
|
||||
|
@ -1226,7 +1228,7 @@ static inline void read_cgroup_network_interfaces(struct cgroup *cg) {
|
|||
info("CGROUP: cgroup '%s' has network interface '%s' as '%s'", cg->id, i->host_device, i->container_device);
|
||||
|
||||
// register a device rename to proc_net_dev.c
|
||||
netdev_rename_device_add(i->host_device, i->container_device, cg->chart_id);
|
||||
netdev_rename_device_add(i->host_device, i->container_device, cg->chart_id, cg->chart_labels);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1275,6 +1277,35 @@ static inline char *cgroup_chart_id_strdupz(const char *s) {
|
|||
return r;
|
||||
}
|
||||
|
||||
char *parse_k8s_data(struct label **labels, char *data)
|
||||
{
|
||||
char *name = mystrsep(&data, " ");
|
||||
|
||||
if (!data) {
|
||||
return name;
|
||||
}
|
||||
|
||||
while (data) {
|
||||
char *key = mystrsep(&data, "=");
|
||||
|
||||
char *value;
|
||||
if (data && *data == ',') {
|
||||
value = "";
|
||||
*data++ = '\0';
|
||||
} else {
|
||||
value = mystrsep(&data, ",");
|
||||
}
|
||||
value = strip_double_quotes(value, 1);
|
||||
|
||||
if (!key || *key == '\0' || !value || *value == '\0')
|
||||
continue;
|
||||
|
||||
*labels = add_label_to_list(*labels, key, value, LABEL_SOURCE_KUBERNETES);
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
static inline void cgroup_get_chart_name(struct cgroup *cg) {
|
||||
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);
|
||||
|
||||
|
@ -1305,12 +1336,19 @@ static inline void cgroup_get_chart_name(struct cgroup *cg) {
|
|||
cg->enabled = 0;
|
||||
}
|
||||
|
||||
if(likely(cg->pending_renames < 2)) {
|
||||
if (likely(cg->pending_renames < 2)) {
|
||||
char *name = s;
|
||||
|
||||
if (!strncmp(s, "k8s_", 4)) {
|
||||
free_label_list(cg->chart_labels);
|
||||
name = parse_k8s_data(&cg->chart_labels, s);
|
||||
}
|
||||
|
||||
freez(cg->chart_title);
|
||||
cg->chart_title = cgroup_title_strdupz(s);
|
||||
cg->chart_title = cgroup_title_strdupz(name);
|
||||
|
||||
freez(cg->chart_id);
|
||||
cg->chart_id = cgroup_chart_id_strdupz(s);
|
||||
cg->chart_id = cgroup_chart_id_strdupz(name);
|
||||
cg->hash_chart = simple_hash(cg->chart_id);
|
||||
}
|
||||
}
|
||||
|
@ -1508,6 +1546,8 @@ static inline void cgroup_free(struct cgroup *cg) {
|
|||
freez(cg->chart_id);
|
||||
freez(cg->chart_title);
|
||||
|
||||
free_label_list(cg->chart_labels);
|
||||
|
||||
freez(cg);
|
||||
|
||||
cgroup_root_count--;
|
||||
|
@ -3022,6 +3062,9 @@ void update_cgroup_charts(int update_every) {
|
|||
, update_every
|
||||
, RRDSET_TYPE_STACKED
|
||||
);
|
||||
|
||||
rrdset_update_labels(cg->st_cpu, cg->chart_labels);
|
||||
|
||||
if(!(cg->options & CGROUP_OPTIONS_IS_UNIFIED)) {
|
||||
rrddim_add(cg->st_cpu, "user", NULL, 100, system_hz, RRD_ALGORITHM_INCREMENTAL);
|
||||
rrddim_add(cg->st_cpu, "system", NULL, 100, system_hz, RRD_ALGORITHM_INCREMENTAL);
|
||||
|
@ -3093,6 +3136,8 @@ void update_cgroup_charts(int update_every) {
|
|||
, RRDSET_TYPE_LINE
|
||||
);
|
||||
|
||||
rrdset_update_labels(cg->st_cpu_limit, cg->chart_labels);
|
||||
|
||||
if(!(cg->options & CGROUP_OPTIONS_IS_UNIFIED))
|
||||
rrddim_add(cg->st_cpu_limit, "used", NULL, 1, system_hz, RRD_ALGORITHM_ABSOLUTE);
|
||||
else
|
||||
|
@ -3146,6 +3191,8 @@ void update_cgroup_charts(int update_every) {
|
|||
, RRDSET_TYPE_STACKED
|
||||
);
|
||||
|
||||
rrdset_update_labels(cg->st_cpu_per_core, cg->chart_labels);
|
||||
|
||||
for(i = 0; i < cg->cpuacct_usage.cpus; i++) {
|
||||
snprintfz(id, RRD_ID_LENGTH_MAX, "cpu%u", i);
|
||||
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) {
|
|||
, update_every
|
||||
, RRDSET_TYPE_STACKED
|
||||
);
|
||||
|
||||
rrdset_update_labels(cg->st_mem, cg->chart_labels);
|
||||
|
||||
if(!(cg->options & CGROUP_OPTIONS_IS_UNIFIED)) {
|
||||
rrddim_add(cg->st_mem, "cache", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
|
||||
rrddim_add(cg->st_mem, "rss", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
|
||||
|
@ -3237,6 +3287,8 @@ void update_cgroup_charts(int update_every) {
|
|||
, RRDSET_TYPE_AREA
|
||||
);
|
||||
|
||||
rrdset_update_labels(cg->st_writeback, cg->chart_labels);
|
||||
|
||||
if(cg->memory.detailed_has_dirty)
|
||||
rrddim_add(cg->st_writeback, "dirty", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
|
||||
|
||||
|
@ -3270,6 +3322,8 @@ void update_cgroup_charts(int update_every) {
|
|||
, RRDSET_TYPE_LINE
|
||||
);
|
||||
|
||||
rrdset_update_labels(cg->st_mem_activity, cg->chart_labels);
|
||||
|
||||
rrddim_add(cg->st_mem_activity, "pgpgin", "in", system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
|
||||
rrddim_add(cg->st_mem_activity, "pgpgout", "out", -system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
|
||||
}
|
||||
|
@ -3299,6 +3353,8 @@ void update_cgroup_charts(int update_every) {
|
|||
, RRDSET_TYPE_LINE
|
||||
);
|
||||
|
||||
rrdset_update_labels(cg->st_pgfaults, cg->chart_labels);
|
||||
|
||||
rrddim_add(cg->st_pgfaults, "pgfault", NULL, system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
|
||||
rrddim_add(cg->st_pgfaults, "pgmajfault", "swap", -system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
|
||||
}
|
||||
|
@ -3329,6 +3385,8 @@ void update_cgroup_charts(int update_every) {
|
|||
, RRDSET_TYPE_STACKED
|
||||
);
|
||||
|
||||
rrdset_update_labels(cg->st_mem_usage, cg->chart_labels);
|
||||
|
||||
rrddim_add(cg->st_mem_usage, "ram", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
|
||||
rrddim_add(cg->st_mem_usage, "swap", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
|
||||
}
|
||||
|
@ -3390,6 +3448,8 @@ void update_cgroup_charts(int update_every) {
|
|||
, RRDSET_TYPE_STACKED
|
||||
);
|
||||
|
||||
rrdset_update_labels(cg->st_mem_usage_limit, cg->chart_labels);
|
||||
|
||||
rrddim_add(cg->st_mem_usage_limit, "available", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
|
||||
rrddim_add(cg->st_mem_usage_limit, "used", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
|
||||
}
|
||||
|
@ -3431,6 +3491,8 @@ void update_cgroup_charts(int update_every) {
|
|||
, update_every
|
||||
, RRDSET_TYPE_LINE
|
||||
);
|
||||
|
||||
rrdset_update_labels(cg->st_mem_failcnt, cg->chart_labels);
|
||||
|
||||
rrddim_add(cg->st_mem_failcnt, "failures", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
|
||||
}
|
||||
|
@ -3460,6 +3522,8 @@ void update_cgroup_charts(int update_every) {
|
|||
, RRDSET_TYPE_AREA
|
||||
);
|
||||
|
||||
rrdset_update_labels(cg->st_io, cg->chart_labels);
|
||||
|
||||
rrddim_add(cg->st_io, "read", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
|
||||
rrddim_add(cg->st_io, "write", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL);
|
||||
}
|
||||
|
@ -3490,6 +3554,8 @@ void update_cgroup_charts(int update_every) {
|
|||
, RRDSET_TYPE_LINE
|
||||
);
|
||||
|
||||
rrdset_update_labels(cg->st_serviced_ops, cg->chart_labels);
|
||||
|
||||
rrddim_add(cg->st_serviced_ops, "read", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
|
||||
rrddim_add(cg->st_serviced_ops, "write", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
|
||||
}
|
||||
|
@ -3519,6 +3585,8 @@ void update_cgroup_charts(int update_every) {
|
|||
, update_every
|
||||
, RRDSET_TYPE_AREA
|
||||
);
|
||||
|
||||
rrdset_update_labels(cg->st_throttle_io, cg->chart_labels);
|
||||
|
||||
rrddim_add(cg->st_throttle_io, "read", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
|
||||
rrddim_add(cg->st_throttle_io, "write", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL);
|
||||
|
@ -3549,6 +3617,8 @@ void update_cgroup_charts(int update_every) {
|
|||
, update_every
|
||||
, RRDSET_TYPE_LINE
|
||||
);
|
||||
|
||||
rrdset_update_labels(cg->st_throttle_serviced_ops, cg->chart_labels);
|
||||
|
||||
rrddim_add(cg->st_throttle_serviced_ops, "read", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
|
||||
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) {
|
|||
, update_every
|
||||
, RRDSET_TYPE_LINE
|
||||
);
|
||||
|
||||
rrdset_update_labels(cg->st_queued_ops, cg->chart_labels);
|
||||
|
||||
rrddim_add(cg->st_queued_ops, "read", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
|
||||
rrddim_add(cg->st_queued_ops, "write", NULL, -1, 1, RRD_ALGORITHM_ABSOLUTE);
|
||||
|
@ -3609,6 +3681,8 @@ void update_cgroup_charts(int update_every) {
|
|||
, update_every
|
||||
, RRDSET_TYPE_LINE
|
||||
);
|
||||
|
||||
rrdset_update_labels(cg->st_merged_ops, cg->chart_labels);
|
||||
|
||||
rrddim_add(cg->st_merged_ops, "read", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
|
||||
rrddim_add(cg->st_merged_ops, "write", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL);
|
||||
|
@ -3639,8 +3713,11 @@ void update_cgroup_charts(int update_every) {
|
|||
, PLUGIN_CGROUPS_NAME
|
||||
, PLUGIN_CGROUPS_MODULE_CGROUPS_NAME
|
||||
, cgroup_containers_chart_priority + 2200
|
||||
, update_every,
|
||||
RRDSET_TYPE_LINE);
|
||||
, update_every
|
||||
, RRDSET_TYPE_LINE
|
||||
);
|
||||
|
||||
rrdset_update_labels(chart = res->some.st, cg->chart_labels);
|
||||
|
||||
res->some.rd10 = rrddim_add(chart, "some 10", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE);
|
||||
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) {
|
|||
, PLUGIN_CGROUPS_NAME
|
||||
, PLUGIN_CGROUPS_MODULE_CGROUPS_NAME
|
||||
, cgroup_containers_chart_priority + 2300
|
||||
, update_every,
|
||||
RRDSET_TYPE_LINE);
|
||||
, update_every
|
||||
, RRDSET_TYPE_LINE
|
||||
);
|
||||
|
||||
rrdset_update_labels(chart = res->some.st, cg->chart_labels);
|
||||
|
||||
res->some.rd10 = rrddim_add(chart, "some 10", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE);
|
||||
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) {
|
|||
, PLUGIN_CGROUPS_NAME
|
||||
, PLUGIN_CGROUPS_MODULE_CGROUPS_NAME
|
||||
, cgroup_containers_chart_priority + 2350
|
||||
, update_every,
|
||||
RRDSET_TYPE_LINE);
|
||||
, update_every
|
||||
, RRDSET_TYPE_LINE
|
||||
);
|
||||
|
||||
rrdset_update_labels(chart = res->full.st, cg->chart_labels);
|
||||
|
||||
res->full.rd10 = rrddim_add(chart, "full 10", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE);
|
||||
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) {
|
|||
, PLUGIN_CGROUPS_NAME
|
||||
, PLUGIN_CGROUPS_MODULE_CGROUPS_NAME
|
||||
, cgroup_containers_chart_priority + 2400
|
||||
, update_every,
|
||||
RRDSET_TYPE_LINE);
|
||||
, update_every
|
||||
, RRDSET_TYPE_LINE
|
||||
);
|
||||
|
||||
rrdset_update_labels(chart = res->some.st, cg->chart_labels);
|
||||
|
||||
res->some.rd10 = rrddim_add(chart, "some 10", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE);
|
||||
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) {
|
|||
, PLUGIN_CGROUPS_NAME
|
||||
, PLUGIN_CGROUPS_MODULE_CGROUPS_NAME
|
||||
, cgroup_containers_chart_priority + 2450
|
||||
, update_every,
|
||||
RRDSET_TYPE_LINE);
|
||||
, update_every
|
||||
, RRDSET_TYPE_LINE
|
||||
);
|
||||
|
||||
rrdset_update_labels(chart = res->full.st, cg->chart_labels);
|
||||
|
||||
res->full.rd10 = rrddim_add(chart, "full 10", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE);
|
||||
res->full.rd60 = rrddim_add(chart, "full 60", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE);
|
||||
|
|
|
@ -28,4 +28,6 @@ extern void *cgroups_main(void *ptr);
|
|||
|
||||
#endif // (TARGET_OS == OS_LINUX)
|
||||
|
||||
extern char *parse_k8s_data(struct label **labels, char *data);
|
||||
|
||||
#endif //NETDATA_SYS_FS_CGROUP_H
|
||||
|
|
110
collectors/cgroups.plugin/tests/test_cgroups_plugin.c
Normal file
110
collectors/cgroups.plugin/tests/test_cgroups_plugin.c
Normal file
|
@ -0,0 +1,110 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "test_cgroups_plugin.h"
|
||||
#include "libnetdata/required_dummies.h"
|
||||
|
||||
RRDHOST *localhost;
|
||||
int netdata_zero_metrics_enabled = 1;
|
||||
struct config netdata_config;
|
||||
char *netdata_configured_primary_plugins_dir = NULL;
|
||||
|
||||
static void test_parse_k8s_data(void **state)
|
||||
{
|
||||
UNUSED(state);
|
||||
|
||||
struct label *labels = (struct label *)0xff;
|
||||
|
||||
struct k8s_test_data {
|
||||
char *data;
|
||||
char *name;
|
||||
char *key[3];
|
||||
char *value[3];
|
||||
};
|
||||
|
||||
struct k8s_test_data test_data[] = {
|
||||
// One label
|
||||
{ .data = "name label1=\"value1\"",
|
||||
.name = "name",
|
||||
.key[0] = "label1", .value[0] = "value1" },
|
||||
|
||||
// Three labels
|
||||
{ .data = "name label1=\"value1\",label2=\"value2\",label3=\"value3\"",
|
||||
.name = "name",
|
||||
.key[0] = "label1", .value[0] = "value1",
|
||||
.key[1] = "label2", .value[1] = "value2",
|
||||
.key[2] = "label3", .value[2] = "value3" },
|
||||
|
||||
// Comma at the end of the data string
|
||||
{ .data = "name label1=\"value1\",",
|
||||
.name = "name",
|
||||
.key[0] = "label1", .value[0] = "value1" },
|
||||
|
||||
// Equals sign in the value
|
||||
{ .data = "name label1=\"value=1\"",
|
||||
.name = "name",
|
||||
.key[0] = "label1", .value[0] = "value=1" },
|
||||
|
||||
// Double quotation mark in the value
|
||||
{ .data = "name label1=\"value\"1\"",
|
||||
.name = "name",
|
||||
.key[0] = "label1", .value[0] = "value" },
|
||||
|
||||
// Escaped double quotation mark in the value
|
||||
{ .data = "name label1=\"value\\\"1\"",
|
||||
.name = "name",
|
||||
.key[0] = "label1", .value[0] = "value\\\"1" },
|
||||
|
||||
// Equals sign in the key
|
||||
{ .data = "name label=1=\"value1\"",
|
||||
.name = "name",
|
||||
.key[0] = "label", .value[0] = "1=\"value1\"" },
|
||||
|
||||
// Skipped value
|
||||
{ .data = "name label1=,label2=\"value2\"",
|
||||
.name = "name",
|
||||
.key[0] = "label2", .value[0] = "value2" },
|
||||
|
||||
// A pair of equals signs
|
||||
{ .data = "name= =",
|
||||
.name = "name=" },
|
||||
|
||||
// A pair of commas
|
||||
{ .data = "name, ,",
|
||||
.name = "name," },
|
||||
|
||||
{ .data = NULL }
|
||||
};
|
||||
|
||||
for (int i = 0; test_data[i].data != NULL; i++) {
|
||||
char *data = strdup(test_data[i].data);
|
||||
|
||||
for (int l = 0; l < 3 && test_data[i].key[l] != NULL; l++) {
|
||||
char *key = test_data[i].key[l];
|
||||
char *value = test_data[i].value[l];
|
||||
|
||||
expect_function_call(__wrap_add_label_to_list);
|
||||
expect_value(__wrap_add_label_to_list, l, 0xff);
|
||||
expect_string(__wrap_add_label_to_list, key, key);
|
||||
expect_string(__wrap_add_label_to_list, value, value);
|
||||
expect_value(__wrap_add_label_to_list, label_source, LABEL_SOURCE_KUBERNETES);
|
||||
}
|
||||
|
||||
char *name = parse_k8s_data(&labels, data);
|
||||
|
||||
assert_string_equal(name, test_data[i].name);
|
||||
assert_ptr_equal(labels, 0xff);
|
||||
|
||||
free(data);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test(test_parse_k8s_data),
|
||||
};
|
||||
|
||||
int test_res = cmocka_run_group_tests_name("test_parse_k8s_data", tests, NULL, NULL);
|
||||
|
||||
return test_res;
|
||||
}
|
16
collectors/cgroups.plugin/tests/test_cgroups_plugin.h
Normal file
16
collectors/cgroups.plugin/tests/test_cgroups_plugin.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef TEST_CGROUPS_PLUGIN_H
|
||||
#define TEST_CGROUPS_PLUGIN_H 1
|
||||
|
||||
#include "libnetdata/libnetdata.h"
|
||||
|
||||
#include "../sys_fs_cgroup.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdint.h>
|
||||
#include <cmocka.h>
|
||||
|
||||
#endif /* TEST_CGROUPS_PLUGIN_H */
|
162
collectors/cgroups.plugin/tests/test_doubles.c
Normal file
162
collectors/cgroups.plugin/tests/test_doubles.c
Normal file
|
@ -0,0 +1,162 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "test_cgroups_plugin.h"
|
||||
|
||||
void rrdset_is_obsolete(RRDSET *st)
|
||||
{
|
||||
UNUSED(st);
|
||||
}
|
||||
|
||||
void rrdset_isnot_obsolete(RRDSET *st)
|
||||
{
|
||||
UNUSED(st);
|
||||
}
|
||||
|
||||
struct mountinfo *mountinfo_read(int do_statvfs)
|
||||
{
|
||||
UNUSED(do_statvfs);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct mountinfo *
|
||||
mountinfo_find_by_filesystem_mount_source(struct mountinfo *root, const char *filesystem, const char *mount_source)
|
||||
{
|
||||
UNUSED(root);
|
||||
UNUSED(filesystem);
|
||||
UNUSED(mount_source);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct mountinfo *
|
||||
mountinfo_find_by_filesystem_super_option(struct mountinfo *root, const char *filesystem, const char *super_options)
|
||||
{
|
||||
UNUSED(root);
|
||||
UNUSED(filesystem);
|
||||
UNUSED(super_options);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void mountinfo_free_all(struct mountinfo *mi)
|
||||
{
|
||||
UNUSED(mi);
|
||||
}
|
||||
|
||||
struct label *__wrap_add_label_to_list(struct label *l, char *key, char *value, LABEL_SOURCE label_source)
|
||||
{
|
||||
function_called();
|
||||
check_expected_ptr(l);
|
||||
check_expected_ptr(key);
|
||||
check_expected_ptr(value);
|
||||
check_expected(label_source);
|
||||
return l;
|
||||
}
|
||||
|
||||
void rrdset_update_labels(RRDSET *st, struct label *labels)
|
||||
{
|
||||
UNUSED(st);
|
||||
UNUSED(labels);
|
||||
}
|
||||
|
||||
RRDSET *rrdset_create_custom(
|
||||
RRDHOST *host, const char *type, const char *id, const char *name, const char *family, const char *context,
|
||||
const char *title, const char *units, const char *plugin, const char *module, long priority, int update_every,
|
||||
RRDSET_TYPE chart_type, RRD_MEMORY_MODE memory_mode, long history_entries)
|
||||
{
|
||||
UNUSED(host);
|
||||
UNUSED(type);
|
||||
UNUSED(id);
|
||||
UNUSED(name);
|
||||
UNUSED(family);
|
||||
UNUSED(context);
|
||||
UNUSED(title);
|
||||
UNUSED(units);
|
||||
UNUSED(plugin);
|
||||
UNUSED(module);
|
||||
UNUSED(priority);
|
||||
UNUSED(update_every);
|
||||
UNUSED(chart_type);
|
||||
UNUSED(memory_mode);
|
||||
UNUSED(history_entries);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RRDDIM *rrddim_add_custom(
|
||||
RRDSET *st, const char *id, const char *name, collected_number multiplier, collected_number divisor,
|
||||
RRD_ALGORITHM algorithm, RRD_MEMORY_MODE memory_mode)
|
||||
{
|
||||
UNUSED(st);
|
||||
UNUSED(id);
|
||||
UNUSED(name);
|
||||
UNUSED(multiplier);
|
||||
UNUSED(divisor);
|
||||
UNUSED(algorithm);
|
||||
UNUSED(memory_mode);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
collected_number rrddim_set(RRDSET *st, const char *id, collected_number value)
|
||||
{
|
||||
UNUSED(st);
|
||||
UNUSED(id);
|
||||
UNUSED(value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value)
|
||||
{
|
||||
UNUSED(st);
|
||||
UNUSED(rd);
|
||||
UNUSED(value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
RRDSETVAR *rrdsetvar_custom_chart_variable_create(RRDSET *st, const char *name)
|
||||
{
|
||||
UNUSED(st);
|
||||
UNUSED(name);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void rrdsetvar_custom_chart_variable_set(RRDSETVAR *rs, calculated_number value)
|
||||
{
|
||||
UNUSED(rs);
|
||||
UNUSED(value);
|
||||
}
|
||||
|
||||
void rrdset_next_usec(RRDSET *st, usec_t microseconds)
|
||||
{
|
||||
UNUSED(st);
|
||||
UNUSED(microseconds);
|
||||
}
|
||||
|
||||
void rrdset_done(RRDSET *st)
|
||||
{
|
||||
UNUSED(st);
|
||||
}
|
||||
|
||||
void update_pressure_chart(struct pressure_chart *chart)
|
||||
{
|
||||
UNUSED(chart);
|
||||
}
|
||||
|
||||
void netdev_rename_device_add(
|
||||
const char *host_device, const char *container_device, const char *container_name, struct label *labels)
|
||||
{
|
||||
UNUSED(host_device);
|
||||
UNUSED(container_device);
|
||||
UNUSED(container_name);
|
||||
UNUSED(labels);
|
||||
}
|
||||
|
||||
void netdev_rename_device_del(const char *host_device)
|
||||
{
|
||||
UNUSED(host_device);
|
||||
}
|
|
@ -157,11 +157,11 @@ PARSER_RC pluginsd_overwrite_action(void *user, RRDHOST *host, struct label *new
|
|||
{
|
||||
UNUSED(user);
|
||||
|
||||
if (!host->labels) {
|
||||
host->labels = new_labels;
|
||||
if (!host->labels.head) {
|
||||
host->labels.head = new_labels;
|
||||
} else {
|
||||
rrdhost_rdlock(host);
|
||||
replace_label_list(host, new_labels);
|
||||
replace_label_list(&host->labels, new_labels);
|
||||
rrdhost_unlock(host);
|
||||
}
|
||||
return PARSER_RC_OK;
|
||||
|
|
|
@ -65,7 +65,8 @@ extern int get_numa_node_count(void);
|
|||
extern unsigned long long tcpext_TCPSynRetrans;
|
||||
|
||||
// netdev renames
|
||||
extern void netdev_rename_device_add(const char *host_device, const char *container_device, const char *container_name);
|
||||
extern void netdev_rename_device_add(
|
||||
const char *host_device, const char *container_device, const char *container_name, struct label *labels);
|
||||
extern void netdev_rename_device_del(const char *host_device);
|
||||
|
||||
#include "proc_self_mountinfo.h"
|
||||
|
|
|
@ -60,6 +60,8 @@ static struct netdev {
|
|||
|
||||
const char *chart_family;
|
||||
|
||||
struct label *chart_labels;
|
||||
|
||||
int flipped;
|
||||
unsigned long priority;
|
||||
|
||||
|
@ -192,6 +194,7 @@ static void netdev_free_chart_strings(struct netdev *d) {
|
|||
static void netdev_free(struct netdev *d) {
|
||||
netdev_charts_release(d);
|
||||
netdev_free_chart_strings(d);
|
||||
free_label_list(d->chart_labels);
|
||||
|
||||
freez((void *)d->name);
|
||||
freez((void *)d->filename_speed);
|
||||
|
@ -211,6 +214,8 @@ static struct netdev_rename {
|
|||
const char *container_device;
|
||||
const char *container_name;
|
||||
|
||||
struct label *chart_labels;
|
||||
|
||||
int processed;
|
||||
|
||||
struct netdev_rename *next;
|
||||
|
@ -230,7 +235,9 @@ static struct netdev_rename *netdev_rename_find(const char *host_device, uint32_
|
|||
}
|
||||
|
||||
// other threads can call this function to register a rename to a netdev
|
||||
void netdev_rename_device_add(const char *host_device, const char *container_device, const char *container_name) {
|
||||
void netdev_rename_device_add(
|
||||
const char *host_device, const char *container_device, const char *container_name, struct label *labels)
|
||||
{
|
||||
netdata_mutex_lock(&netdev_rename_mutex);
|
||||
|
||||
uint32_t hash = simple_hash(host_device);
|
||||
|
@ -240,6 +247,7 @@ void netdev_rename_device_add(const char *host_device, const char *container_dev
|
|||
r->host_device = strdupz(host_device);
|
||||
r->container_device = strdupz(container_device);
|
||||
r->container_name = strdupz(container_name);
|
||||
update_label_list(&r->chart_labels, labels);
|
||||
r->hash = hash;
|
||||
r->next = netdev_rename_root;
|
||||
r->processed = 0;
|
||||
|
@ -254,6 +262,9 @@ void netdev_rename_device_add(const char *host_device, const char *container_dev
|
|||
|
||||
r->container_device = strdupz(container_device);
|
||||
r->container_name = strdupz(container_name);
|
||||
|
||||
update_label_list(&r->chart_labels, labels);
|
||||
|
||||
r->processed = 0;
|
||||
netdev_pending_renames++;
|
||||
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) {
|
|||
freez((void *) r->host_device);
|
||||
freez((void *) r->container_name);
|
||||
freez((void *) r->container_device);
|
||||
free_label_list(r->chart_labels);
|
||||
freez((void *) r);
|
||||
break;
|
||||
}
|
||||
|
@ -334,6 +346,8 @@ static inline void netdev_rename_cgroup(struct netdev *d, struct netdev_rename *
|
|||
snprintfz(buffer, RRD_ID_LENGTH_MAX, "net %s", r->container_device);
|
||||
d->chart_family = strdupz(buffer);
|
||||
|
||||
update_label_list(&d->chart_labels, r->chart_labels);
|
||||
|
||||
d->priority = NETDATA_CHART_PRIO_CGROUP_NET_IFACE;
|
||||
d->flipped = 1;
|
||||
}
|
||||
|
@ -677,6 +691,8 @@ int do_proc_net_dev(int update_every, usec_t dt) {
|
|||
, RRDSET_TYPE_AREA
|
||||
);
|
||||
|
||||
rrdset_update_labels(d->st_bandwidth, d->chart_labels);
|
||||
|
||||
d->rd_rbytes = rrddim_add(d->st_bandwidth, "received", NULL, 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL);
|
||||
d->rd_tbytes = rrddim_add(d->st_bandwidth, "sent", NULL, -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL);
|
||||
|
||||
|
@ -820,6 +836,8 @@ int do_proc_net_dev(int update_every, usec_t dt) {
|
|||
|
||||
rrdset_flag_set(d->st_packets, RRDSET_FLAG_DETAIL);
|
||||
|
||||
rrdset_update_labels(d->st_packets, d->chart_labels);
|
||||
|
||||
d->rd_rpackets = rrddim_add(d->st_packets, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
|
||||
d->rd_tpackets = rrddim_add(d->st_packets, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
|
||||
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) {
|
|||
|
||||
rrdset_flag_set(d->st_errors, RRDSET_FLAG_DETAIL);
|
||||
|
||||
rrdset_update_labels(d->st_errors, d->chart_labels);
|
||||
|
||||
d->rd_rerrors = rrddim_add(d->st_errors, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
|
||||
d->rd_terrors = rrddim_add(d->st_errors, "outbound", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
|
||||
|
||||
|
@ -910,6 +930,8 @@ int do_proc_net_dev(int update_every, usec_t dt) {
|
|||
|
||||
rrdset_flag_set(d->st_drops, RRDSET_FLAG_DETAIL);
|
||||
|
||||
rrdset_update_labels(d->st_drops, d->chart_labels);
|
||||
|
||||
d->rd_rdrops = rrddim_add(d->st_drops, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
|
||||
d->rd_tdrops = rrddim_add(d->st_drops, "outbound", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
|
||||
|
||||
|
@ -954,6 +976,8 @@ int do_proc_net_dev(int update_every, usec_t dt) {
|
|||
|
||||
rrdset_flag_set(d->st_fifo, RRDSET_FLAG_DETAIL);
|
||||
|
||||
rrdset_update_labels(d->st_fifo, d->chart_labels);
|
||||
|
||||
d->rd_rfifo = rrddim_add(d->st_fifo, "receive", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
|
||||
d->rd_tfifo = rrddim_add(d->st_fifo, "transmit", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
|
||||
|
||||
|
@ -998,6 +1022,8 @@ int do_proc_net_dev(int update_every, usec_t dt) {
|
|||
|
||||
rrdset_flag_set(d->st_compressed, RRDSET_FLAG_DETAIL);
|
||||
|
||||
rrdset_update_labels(d->st_compressed, d->chart_labels);
|
||||
|
||||
d->rd_rcompressed = rrddim_add(d->st_compressed, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
|
||||
d->rd_tcompressed = rrddim_add(d->st_compressed, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
|
||||
|
||||
|
@ -1042,6 +1068,8 @@ int do_proc_net_dev(int update_every, usec_t dt) {
|
|||
|
||||
rrdset_flag_set(d->st_events, RRDSET_FLAG_DETAIL);
|
||||
|
||||
rrdset_update_labels(d->st_events, d->chart_labels);
|
||||
|
||||
d->rd_rframe = rrddim_add(d->st_events, "frames", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
|
||||
d->rd_tcollisions = rrddim_add(d->st_events, "collisions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
|
||||
d->rd_tcarrier = rrddim_add(d->st_events, "carrier", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
|
||||
|
|
|
@ -216,13 +216,13 @@ static cmd_status_t cmd_reload_labels_execute(char *args, char **message)
|
|||
BUFFER *wb = buffer_create(10);
|
||||
|
||||
rrdhost_rdlock(localhost);
|
||||
netdata_rwlock_rdlock(&localhost->labels_rwlock);
|
||||
struct label *l=localhost->labels;
|
||||
netdata_rwlock_rdlock(&localhost->labels.labels_rwlock);
|
||||
struct label *l = localhost->labels.head;
|
||||
while (l != NULL) {
|
||||
buffer_sprintf(wb,"Label [source id=%s]: \"%s\" -> \"%s\"\n", translate_label_source(l->label_source), l->key, l->value);
|
||||
l = l->next;
|
||||
}
|
||||
netdata_rwlock_unlock(&localhost->labels_rwlock);
|
||||
netdata_rwlock_unlock(&localhost->labels.labels_rwlock);
|
||||
rrdhost_unlock(localhost);
|
||||
|
||||
(*message)=strdupz(buffer_tostring(wb));
|
||||
|
|
|
@ -19,6 +19,7 @@ typedef struct context_param CONTEXT_PARAM;
|
|||
struct rrddim_volatile;
|
||||
struct rrdset_volatile;
|
||||
struct context_param;
|
||||
struct label;
|
||||
#ifdef ENABLE_DBENGINE
|
||||
struct rrdeng_page_descr;
|
||||
struct rrdengine_instance;
|
||||
|
@ -185,12 +186,45 @@ struct label {
|
|||
struct label *next;
|
||||
};
|
||||
|
||||
struct label_index {
|
||||
struct label *head; // Label list
|
||||
netdata_rwlock_t labels_rwlock; // lock for the label list
|
||||
uint32_t labels_flag; // Flags for labels
|
||||
};
|
||||
|
||||
typedef enum strip_quotes {
|
||||
DO_NOT_STRIP_QUOTES,
|
||||
STRIP_QUOTES
|
||||
} STRIP_QUOTES_OPTION;
|
||||
|
||||
typedef enum skip_escaped_characters {
|
||||
DO_NOT_SKIP_ESCAPED_CHARACTERS,
|
||||
SKIP_ESCAPED_CHARACTERS
|
||||
} SKIP_ESCAPED_CHARACTERS_OPTION;
|
||||
|
||||
char *translate_label_source(LABEL_SOURCE l);
|
||||
struct label *create_label(char *key, char *value, LABEL_SOURCE label_source);
|
||||
struct label *add_label_to_list(struct label *l, char *key, char *value, LABEL_SOURCE label_source);
|
||||
extern void replace_label_list(RRDHOST *host, struct label *new_labels);
|
||||
extern void free_host_labels(struct label *labels);
|
||||
void reload_host_labels();
|
||||
extern struct label *add_label_to_list(struct label *l, char *key, char *value, LABEL_SOURCE label_source);
|
||||
extern void update_label_list(struct label **labels, struct label *new_labels);
|
||||
extern void replace_label_list(struct label_index *labels, struct label *new_labels);
|
||||
extern int is_valid_label_value(char *value);
|
||||
extern int is_valid_label_key(char *key);
|
||||
extern void free_label_list(struct label *labels);
|
||||
extern struct label *label_list_lookup_key(struct label *head, char *key, uint32_t key_hash);
|
||||
extern int label_list_contains_key(struct label *head, char *key, uint32_t key_hash);
|
||||
extern int label_list_contains(struct label *head, struct label *check);
|
||||
extern struct label *merge_label_lists(struct label *lo_pri, struct label *hi_pri);
|
||||
extern void strip_last_symbol(
|
||||
char *str,
|
||||
char symbol,
|
||||
SKIP_ESCAPED_CHARACTERS_OPTION skip_escaped_characters);
|
||||
extern char *strip_double_quotes(char *str, SKIP_ESCAPED_CHARACTERS_OPTION skip_escaped_characters);
|
||||
void reload_host_labels(void);
|
||||
extern void rrdset_add_label_to_new_list(RRDSET *st, char *key, char *value, LABEL_SOURCE source);
|
||||
extern void rrdset_finalize_labels(RRDSET *st);
|
||||
extern void rrdset_update_labels(RRDSET *st, struct label *labels);
|
||||
extern int rrdset_contains_label_key(RRDSET *st, char *key, uint32_t key_hash);
|
||||
extern struct label *rrdset_lookup_label_key(RRDSET *st, char *key, uint32_t key_hash);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// RRD DIMENSION - this is a metric
|
||||
|
@ -376,6 +410,8 @@ struct rrdset_volatile {
|
|||
char *old_title;
|
||||
char *old_family;
|
||||
char *old_context;
|
||||
struct label *new_labels;
|
||||
struct label_index labels;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -801,9 +837,7 @@ struct rrdhost {
|
|||
|
||||
// ------------------------------------------------------------------------
|
||||
// Support for host-level labels
|
||||
struct label *labels;
|
||||
netdata_rwlock_t labels_rwlock; // lock for the label list
|
||||
uint32_t labels_flag; //Flags for labels
|
||||
struct label_index labels;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// indexes
|
||||
|
|
|
@ -651,7 +651,7 @@ static void rrdcalc_labels_unlink_alarm_loop(RRDHOST *host, RRDCALC *alarms) {
|
|||
}
|
||||
|
||||
char cmp[CONFIG_FILE_LINE_MAX+1];
|
||||
struct label *move = host->labels;
|
||||
struct label *move = host->labels.head;
|
||||
while(move) {
|
||||
snprintf(cmp, CONFIG_FILE_LINE_MAX, "%s=%s", move->key, move->value);
|
||||
if (simple_pattern_matches(rc->splabels, move->key) ||
|
||||
|
@ -682,12 +682,12 @@ static void rrdcalc_labels_unlink_alarm_loop(RRDHOST *host, RRDCALC *alarms) {
|
|||
|
||||
void rrdcalc_labels_unlink_alarm_from_host(RRDHOST *host) {
|
||||
rrdhost_check_rdlock(host);
|
||||
netdata_rwlock_rdlock(&host->labels_rwlock);
|
||||
netdata_rwlock_rdlock(&host->labels.labels_rwlock);
|
||||
|
||||
rrdcalc_labels_unlink_alarm_loop(host, host->alarms);
|
||||
rrdcalc_labels_unlink_alarm_loop(host, host->alarms_with_foreach);
|
||||
|
||||
netdata_rwlock_unlock(&host->labels_rwlock);
|
||||
netdata_rwlock_unlock(&host->labels.labels_rwlock);
|
||||
}
|
||||
|
||||
void rrdcalc_labels_unlink() {
|
||||
|
@ -698,7 +698,7 @@ void rrdcalc_labels_unlink() {
|
|||
if (unlikely(!host->health_enabled))
|
||||
continue;
|
||||
|
||||
if (host->labels) {
|
||||
if (host->labels.head) {
|
||||
rrdhost_wrlock(host);
|
||||
|
||||
rrdcalc_labels_unlink_alarm_from_host(host);
|
||||
|
|
|
@ -10,13 +10,13 @@ static int rrdcalctemplate_is_there_label_restriction(RRDCALCTEMPLATE *rt, RRDH
|
|||
return 0;
|
||||
|
||||
errno = 0;
|
||||
struct label *move = host->labels;
|
||||
struct label *move = host->labels.head;
|
||||
char cmp[CONFIG_FILE_LINE_MAX+1];
|
||||
|
||||
int ret;
|
||||
if(move) {
|
||||
rrdhost_check_rdlock(host);
|
||||
netdata_rwlock_rdlock(&host->labels_rwlock);
|
||||
netdata_rwlock_rdlock(&host->labels.labels_rwlock);
|
||||
while(move) {
|
||||
snprintfz(cmp, CONFIG_FILE_LINE_MAX, "%s=%s", move->key, move->value);
|
||||
if (simple_pattern_matches(rt->splabels, move->key) ||
|
||||
|
@ -25,7 +25,7 @@ static int rrdcalctemplate_is_there_label_restriction(RRDCALCTEMPLATE *rt, RRDH
|
|||
}
|
||||
move = move->next;
|
||||
}
|
||||
netdata_rwlock_unlock(&host->labels_rwlock);
|
||||
netdata_rwlock_unlock(&host->labels.labels_rwlock);
|
||||
|
||||
if(!move) {
|
||||
error("Health template '%s' cannot be applied, because the host %s does not have the label(s) '%s'",
|
||||
|
|
|
@ -187,7 +187,7 @@ RRDHOST *rrdhost_create(const char *hostname,
|
|||
#endif
|
||||
|
||||
netdata_rwlock_init(&host->rrdhost_rwlock);
|
||||
netdata_rwlock_init(&host->labels_rwlock);
|
||||
netdata_rwlock_init(&host->labels.labels_rwlock);
|
||||
|
||||
netdata_mutex_init(&host->aclk_state_lock);
|
||||
|
||||
|
@ -873,7 +873,7 @@ void rrdhost_free(RRDHOST *host) {
|
|||
pthread_mutex_destroy(&host->aclk_state_lock);
|
||||
freez(host->aclk_state.claimed_id);
|
||||
freez((void *)host->tags);
|
||||
free_host_labels(host->labels);
|
||||
free_label_list(host->labels.head);
|
||||
freez((void *)host->os);
|
||||
freez((void *)host->timezone);
|
||||
freez(host->program_version);
|
||||
|
@ -890,7 +890,7 @@ void rrdhost_free(RRDHOST *host) {
|
|||
freez(host->registry_hostname);
|
||||
simple_pattern_free(host->rrdpush_send_charts_matching);
|
||||
rrdhost_unlock(host);
|
||||
netdata_rwlock_destroy(&host->labels_rwlock);
|
||||
netdata_rwlock_destroy(&host->labels.labels_rwlock);
|
||||
netdata_rwlock_destroy(&host->health_log.alarm_log_rwlock);
|
||||
netdata_rwlock_destroy(&host->rrdhost_rwlock);
|
||||
|
||||
|
@ -930,55 +930,7 @@ void rrdhost_save_charts(RRDHOST *host) {
|
|||
rrdhost_unlock(host);
|
||||
}
|
||||
|
||||
static int is_valid_label_value(char *value) {
|
||||
while(*value) {
|
||||
if(*value == '"' || *value == '\'' || *value == '*' || *value == '!') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
value++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int is_valid_label_key(char *key) {
|
||||
//Prometheus exporter
|
||||
if(!strcmp(key, "chart") || !strcmp(key, "family") || !strcmp(key, "dimension"))
|
||||
return 0;
|
||||
|
||||
//Netdata and Prometheus internal
|
||||
if (*key == '_')
|
||||
return 0;
|
||||
|
||||
while(*key) {
|
||||
if(!(isdigit(*key) || isalpha(*key) || *key == '.' || *key == '_' || *key == '-'))
|
||||
return 0;
|
||||
|
||||
key++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *translate_label_source(LABEL_SOURCE l) {
|
||||
switch (l) {
|
||||
case LABEL_SOURCE_AUTO:
|
||||
return "AUTO";
|
||||
case LABEL_SOURCE_NETDATA_CONF:
|
||||
return "NETDATA.CONF";
|
||||
case LABEL_SOURCE_DOCKER :
|
||||
return "DOCKER";
|
||||
case LABEL_SOURCE_ENVIRONMENT :
|
||||
return "ENVIRONMENT";
|
||||
case LABEL_SOURCE_KUBERNETES :
|
||||
return "KUBERNETES";
|
||||
default:
|
||||
return "Invalid label source";
|
||||
}
|
||||
}
|
||||
|
||||
struct label *load_auto_labels()
|
||||
static struct label *rrdhost_load_auto_labels(void)
|
||||
{
|
||||
struct label *label_list = NULL;
|
||||
|
||||
|
@ -1040,11 +992,13 @@ struct label *load_auto_labels()
|
|||
return label_list;
|
||||
}
|
||||
|
||||
static inline int is_valid_label_config_option(char *name, char *value) {
|
||||
return (is_valid_label_key(name) && is_valid_label_value(value) && strcmp(name, "from environment") && strcmp(name, "from kubernetes pods") );
|
||||
}
|
||||
static inline int rrdhost_is_valid_label_config_option(char *name, char *value)
|
||||
{
|
||||
return (is_valid_label_key(name) && is_valid_label_value(value) && strcmp(name, "from environment") &&
|
||||
strcmp(name, "from kubernetes pods"));
|
||||
}
|
||||
|
||||
struct label *load_config_labels()
|
||||
static struct label *rrdhost_load_config_labels()
|
||||
{
|
||||
int status = config_load(NULL, 1, CONFIG_SECTION_HOST_LABEL);
|
||||
if(!status) {
|
||||
|
@ -1058,7 +1012,7 @@ struct label *load_config_labels()
|
|||
config_section_wrlock(co);
|
||||
struct config_option *cv;
|
||||
for(cv = co->values; cv ; cv = cv->next) {
|
||||
if( is_valid_label_config_option(cv->name, cv->value)) {
|
||||
if(rrdhost_is_valid_label_config_option(cv->name, cv->value)) {
|
||||
l = add_label_to_list(l, cv->name, cv->value, LABEL_SOURCE_NETDATA_CONF);
|
||||
cv->flags |= CONFIG_VALUE_USED;
|
||||
} else {
|
||||
|
@ -1072,45 +1026,6 @@ struct label *load_config_labels()
|
|||
return l;
|
||||
}
|
||||
|
||||
typedef enum strip_quotes {
|
||||
DO_NOT_STRIP_QUOTES,
|
||||
STRIP_QUOTES
|
||||
} STRIP_QUOTES_OPTION;
|
||||
|
||||
typedef enum skip_escaped_characters {
|
||||
DO_NOT_SKIP_ESCAPED_CHARACTERS,
|
||||
SKIP_ESCAPED_CHARACTERS
|
||||
} SKIP_ESCAPED_CHARACTERS_OPTION;
|
||||
|
||||
static inline void strip_last_symbol(
|
||||
char *str,
|
||||
char symbol,
|
||||
SKIP_ESCAPED_CHARACTERS_OPTION skip_escaped_characters)
|
||||
{
|
||||
char *end = str;
|
||||
|
||||
while (*end && *end != symbol) {
|
||||
if (unlikely(skip_escaped_characters && *end == '\\')) {
|
||||
end++;
|
||||
if (unlikely(!*end))
|
||||
break;
|
||||
}
|
||||
end++;
|
||||
}
|
||||
if (likely(*end == symbol))
|
||||
*end = '\0';
|
||||
}
|
||||
|
||||
static inline char *strip_double_quotes(char *str, SKIP_ESCAPED_CHARACTERS_OPTION skip_escaped_characters)
|
||||
{
|
||||
if (*str == '"') {
|
||||
str++;
|
||||
strip_last_symbol(str, '"', skip_escaped_characters);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
struct label *parse_simple_tags(
|
||||
struct label *label_list,
|
||||
const char *tags,
|
||||
|
@ -1200,7 +1115,7 @@ struct label *parse_json_tags(struct label *label_list, const char *tags)
|
|||
return label_list;
|
||||
}
|
||||
|
||||
struct label *load_labels_from_tags()
|
||||
static struct label *rrdhost_load_labels_from_tags(void)
|
||||
{
|
||||
if (!localhost->tags)
|
||||
return NULL;
|
||||
|
@ -1244,7 +1159,7 @@ struct label *load_labels_from_tags()
|
|||
return label_list;
|
||||
}
|
||||
|
||||
struct label *load_kubernetes_labels()
|
||||
static struct label *rrdhost_load_kubernetes_labels(void)
|
||||
{
|
||||
struct label *l=NULL;
|
||||
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()
|
|||
return l;
|
||||
}
|
||||
|
||||
struct label *create_label(char *key, char *value, LABEL_SOURCE label_source)
|
||||
void reload_host_labels(void)
|
||||
{
|
||||
size_t key_len = strlen(key), value_len = strlen(value);
|
||||
size_t n = sizeof(struct label) + key_len + 1 + value_len + 1;
|
||||
struct label *result = callocz(1,n);
|
||||
if (result != NULL) {
|
||||
char *c = (char *)result;
|
||||
c += sizeof(struct label);
|
||||
strcpy(c, key);
|
||||
result->key = c;
|
||||
c += key_len + 1;
|
||||
strcpy(c, value);
|
||||
result->value = c;
|
||||
result->label_source = label_source;
|
||||
result->key_hash = simple_hash(result->key);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void free_host_labels(struct label *labels)
|
||||
{
|
||||
while (labels != NULL)
|
||||
{
|
||||
struct label *current = labels;
|
||||
labels = labels->next;
|
||||
freez(current);
|
||||
}
|
||||
}
|
||||
|
||||
void replace_label_list(RRDHOST *host, struct label *new_labels)
|
||||
{
|
||||
rrdhost_check_rdlock(host);
|
||||
netdata_rwlock_wrlock(&host->labels_rwlock);
|
||||
struct label *old_labels = host->labels;
|
||||
host->labels = new_labels;
|
||||
netdata_rwlock_unlock(&host->labels_rwlock);
|
||||
|
||||
free_host_labels(old_labels);
|
||||
}
|
||||
|
||||
struct label *add_label_to_list(struct label *l, char *key, char *value, LABEL_SOURCE label_source)
|
||||
{
|
||||
struct label *lab = create_label(key, value, label_source);
|
||||
lab->next = l;
|
||||
return lab;
|
||||
}
|
||||
|
||||
int label_list_contains(struct label *head, struct label *check)
|
||||
{
|
||||
while (head != NULL)
|
||||
{
|
||||
if (head->key_hash == check->key_hash && !strcmp(head->key, check->key))
|
||||
return 1;
|
||||
head = head->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create a list with entries from both lists.
|
||||
If any entry in the low priority list is masked by an entry in the high priorty list then delete it.
|
||||
*/
|
||||
struct label *merge_label_lists(struct label *lo_pri, struct label *hi_pri)
|
||||
{
|
||||
struct label *result = hi_pri;
|
||||
while (lo_pri != NULL)
|
||||
{
|
||||
struct label *current = lo_pri;
|
||||
lo_pri = lo_pri->next;
|
||||
if (!label_list_contains(result, current)) {
|
||||
current->next = result;
|
||||
result = current;
|
||||
}
|
||||
else
|
||||
freez(current);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void reload_host_labels()
|
||||
{
|
||||
struct label *from_auto = load_auto_labels();
|
||||
struct label *from_k8s = load_kubernetes_labels();
|
||||
struct label *from_config = load_config_labels();
|
||||
struct label *from_tags = load_labels_from_tags();
|
||||
struct label *from_auto = rrdhost_load_auto_labels();
|
||||
struct label *from_k8s = rrdhost_load_kubernetes_labels();
|
||||
struct label *from_config = rrdhost_load_config_labels();
|
||||
struct label *from_tags = rrdhost_load_labels_from_tags();
|
||||
|
||||
struct label *new_labels = merge_label_lists(from_auto, from_k8s);
|
||||
new_labels = merge_label_lists(new_labels, from_tags);
|
||||
new_labels = merge_label_lists(new_labels, from_config);
|
||||
|
||||
rrdhost_rdlock(localhost);
|
||||
replace_label_list(localhost, new_labels);
|
||||
replace_label_list(&localhost->labels, new_labels);
|
||||
|
||||
health_label_log_save(localhost);
|
||||
rrdhost_unlock(localhost);
|
||||
|
||||
/* TODO-GAPS - fix this so that it looks properly at the state and version of the sender
|
||||
if(localhost->rrdpush_send_enabled && localhost->rrdpush_sender_buffer){
|
||||
localhost->labels_flag |= LABEL_FLAG_UPDATE_STREAM;
|
||||
localhost->labels.labels_flag |= LABEL_FLAG_UPDATE_STREAM;
|
||||
rrdpush_send_labels(localhost);
|
||||
}
|
||||
*/
|
||||
|
|
181
database/rrdlabels.c
Normal file
181
database/rrdlabels.c
Normal file
|
@ -0,0 +1,181 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#define NETDATA_RRD_INTERNALS
|
||||
#include "rrd.h"
|
||||
|
||||
char *translate_label_source(LABEL_SOURCE l) {
|
||||
switch (l) {
|
||||
case LABEL_SOURCE_AUTO:
|
||||
return "AUTO";
|
||||
case LABEL_SOURCE_NETDATA_CONF:
|
||||
return "NETDATA.CONF";
|
||||
case LABEL_SOURCE_DOCKER :
|
||||
return "DOCKER";
|
||||
case LABEL_SOURCE_ENVIRONMENT :
|
||||
return "ENVIRONMENT";
|
||||
case LABEL_SOURCE_KUBERNETES :
|
||||
return "KUBERNETES";
|
||||
default:
|
||||
return "Invalid label source";
|
||||
}
|
||||
}
|
||||
|
||||
int is_valid_label_value(char *value) {
|
||||
while(*value) {
|
||||
if(*value == '"' || *value == '\'' || *value == '*' || *value == '!') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
value++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int is_valid_label_key(char *key) {
|
||||
//Prometheus exporter
|
||||
if(!strcmp(key, "chart") || !strcmp(key, "family") || !strcmp(key, "dimension"))
|
||||
return 0;
|
||||
|
||||
//Netdata and Prometheus internal
|
||||
if (*key == '_')
|
||||
return 0;
|
||||
|
||||
while(*key) {
|
||||
if(!(isdigit(*key) || isalpha(*key) || *key == '.' || *key == '_' || *key == '-'))
|
||||
return 0;
|
||||
|
||||
key++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void strip_last_symbol(
|
||||
char *str,
|
||||
char symbol,
|
||||
SKIP_ESCAPED_CHARACTERS_OPTION skip_escaped_characters)
|
||||
{
|
||||
char *end = str;
|
||||
|
||||
while (*end && *end != symbol) {
|
||||
if (unlikely(skip_escaped_characters && *end == '\\')) {
|
||||
end++;
|
||||
if (unlikely(!*end))
|
||||
break;
|
||||
}
|
||||
end++;
|
||||
}
|
||||
if (likely(*end == symbol))
|
||||
*end = '\0';
|
||||
}
|
||||
|
||||
char *strip_double_quotes(char *str, SKIP_ESCAPED_CHARACTERS_OPTION skip_escaped_characters)
|
||||
{
|
||||
if (*str == '"') {
|
||||
str++;
|
||||
strip_last_symbol(str, '"', skip_escaped_characters);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
struct label *create_label(char *key, char *value, LABEL_SOURCE label_source)
|
||||
{
|
||||
size_t key_len = strlen(key), value_len = strlen(value);
|
||||
size_t n = sizeof(struct label) + key_len + 1 + value_len + 1;
|
||||
struct label *result = callocz(1,n);
|
||||
if (result != NULL) {
|
||||
char *c = (char *)result;
|
||||
c += sizeof(struct label);
|
||||
strcpy(c, key);
|
||||
result->key = c;
|
||||
c += key_len + 1;
|
||||
strcpy(c, value);
|
||||
result->value = c;
|
||||
result->label_source = label_source;
|
||||
result->key_hash = simple_hash(result->key);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void free_label_list(struct label *labels)
|
||||
{
|
||||
while (labels != NULL)
|
||||
{
|
||||
struct label *current = labels;
|
||||
labels = labels->next;
|
||||
freez(current);
|
||||
}
|
||||
}
|
||||
|
||||
void replace_label_list(struct label_index *labels, struct label *new_labels)
|
||||
{
|
||||
netdata_rwlock_wrlock(&labels->labels_rwlock);
|
||||
struct label *old_labels = labels->head;
|
||||
labels->head = new_labels;
|
||||
netdata_rwlock_unlock(&labels->labels_rwlock);
|
||||
|
||||
free_label_list(old_labels);
|
||||
}
|
||||
|
||||
struct label *add_label_to_list(struct label *l, char *key, char *value, LABEL_SOURCE label_source)
|
||||
{
|
||||
struct label *lab = create_label(key, value, label_source);
|
||||
lab->next = l;
|
||||
return lab;
|
||||
}
|
||||
|
||||
void update_label_list(struct label **labels, struct label *new_labels)
|
||||
{
|
||||
free_label_list(*labels);
|
||||
*labels = NULL;
|
||||
|
||||
while (new_labels != NULL)
|
||||
{
|
||||
*labels = add_label_to_list(*labels, new_labels->key, new_labels->value, new_labels->label_source);
|
||||
new_labels = new_labels->next;
|
||||
}
|
||||
}
|
||||
|
||||
struct label *label_list_lookup_key(struct label *head, char *key, uint32_t key_hash)
|
||||
{
|
||||
while (head != NULL)
|
||||
{
|
||||
if (head->key_hash == key_hash && !strcmp(head->key, key))
|
||||
return head;
|
||||
head = head->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int label_list_contains_key(struct label *head, char *key, uint32_t key_hash)
|
||||
{
|
||||
return (label_list_lookup_key(head, key, key_hash) != NULL);
|
||||
}
|
||||
|
||||
int label_list_contains(struct label *head, struct label *check)
|
||||
{
|
||||
return label_list_contains_key(head, check->key, check->key_hash);
|
||||
}
|
||||
|
||||
/* Create a list with entries from both lists.
|
||||
If any entry in the low priority list is masked by an entry in the high priority list then delete it.
|
||||
*/
|
||||
struct label *merge_label_lists(struct label *lo_pri, struct label *hi_pri)
|
||||
{
|
||||
struct label *result = hi_pri;
|
||||
while (lo_pri != NULL)
|
||||
{
|
||||
struct label *current = lo_pri;
|
||||
lo_pri = lo_pri->next;
|
||||
if (!label_list_contains(result, current)) {
|
||||
current->next = result;
|
||||
result = current;
|
||||
}
|
||||
else
|
||||
freez(current);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
@ -330,13 +330,22 @@ void rrdset_free(RRDSET *st) {
|
|||
|
||||
rrdset_index_del_name(host, st);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// remove it from the configuration
|
||||
|
||||
appconfig_section_destroy_non_loaded(&netdata_config, st->config_section);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// free its children structures
|
||||
|
||||
freez(st->exporting_flags);
|
||||
|
||||
while(st->variables) rrdsetvar_free(st->variables);
|
||||
while(st->alarms) rrdsetcalc_unlink(st->alarms);
|
||||
// while(st->alarms) rrdsetcalc_unlink(st->alarms);
|
||||
/* We must free all connected alarms here in case this has been an ephemeral chart whose alarm was
|
||||
* created by a template. This leads to an effective memory leak, which cannot be detected since the
|
||||
* alarms will still be connected to the host, and freed during shutdown. */
|
||||
while(st->alarms) rrdcalc_unlink_and_free(st->rrdhost, st->alarms);
|
||||
while(st->dimensions) rrddim_free(st, st->dimensions);
|
||||
|
||||
rrdfamily_free(host, st->rrdfamily);
|
||||
|
@ -366,6 +375,7 @@ void rrdset_free(RRDSET *st) {
|
|||
// free it
|
||||
|
||||
netdata_rwlock_destroy(&st->rrdset_rwlock);
|
||||
netdata_rwlock_destroy(&st->state->labels.labels_rwlock);
|
||||
|
||||
// free directly allocated members
|
||||
freez(st->config_section);
|
||||
|
@ -374,6 +384,7 @@ void rrdset_free(RRDSET *st) {
|
|||
freez(st->state->old_title);
|
||||
freez(st->state->old_family);
|
||||
freez(st->state->old_context);
|
||||
free_label_list(st->state->labels.head);
|
||||
freez(st->state);
|
||||
|
||||
switch(st->rrd_memory_mode) {
|
||||
|
@ -835,7 +846,7 @@ RRDSET *rrdset_create_custom(
|
|||
st->chart_type = rrdset_type_id(config_get(st->config_section, "chart type", rrdset_type_name(chart_type)));
|
||||
st->type = config_get(st->config_section, "type", type);
|
||||
|
||||
st->state = mallocz(sizeof(*st->state));
|
||||
st->state = callocz(1, sizeof(*st->state));
|
||||
st->family = config_get(st->config_section, "family", family?family:st->type);
|
||||
st->state->old_family = strdupz(st->family);
|
||||
json_fix_string(st->family);
|
||||
|
@ -887,6 +898,7 @@ RRDSET *rrdset_create_custom(
|
|||
avl_init_lock(&st->rrdvar_root_index, rrdvar_compare);
|
||||
|
||||
netdata_rwlock_init(&st->rrdset_rwlock);
|
||||
netdata_rwlock_init(&st->state->labels.labels_rwlock);
|
||||
|
||||
if(name && *name && rrdset_set_name(st, name))
|
||||
// we did set the name
|
||||
|
@ -1903,3 +1915,58 @@ after_second_database_work:
|
|||
|
||||
netdata_thread_enable_cancelability();
|
||||
}
|
||||
|
||||
void rrdset_add_label_to_new_list(RRDSET *st, char *key, char *value, LABEL_SOURCE source)
|
||||
{
|
||||
st->state->new_labels = add_label_to_list(st->state->new_labels, key, value, source);
|
||||
}
|
||||
|
||||
void rrdset_finalize_labels(RRDSET *st)
|
||||
{
|
||||
struct label *new_labels = st->state->new_labels;
|
||||
struct label_index *labels = &st->state->labels;
|
||||
|
||||
if (!labels->head) {
|
||||
labels->head = new_labels;
|
||||
} else {
|
||||
replace_label_list(labels, new_labels);
|
||||
}
|
||||
st->state->new_labels = NULL;
|
||||
}
|
||||
|
||||
void rrdset_update_labels(RRDSET *st, struct label *labels)
|
||||
{
|
||||
if (!labels)
|
||||
return;
|
||||
|
||||
update_label_list(&st->state->new_labels, labels);
|
||||
rrdset_finalize_labels(st);
|
||||
}
|
||||
|
||||
int rrdset_contains_label_key(RRDSET *st, char *key, uint32_t key_hash)
|
||||
{
|
||||
struct label_index *labels = &st->state->labels;
|
||||
int ret;
|
||||
|
||||
if (!labels->head)
|
||||
return 0;
|
||||
|
||||
netdata_rwlock_rdlock(&labels->labels_rwlock);
|
||||
ret = label_list_contains_key(labels->head, key, key_hash);
|
||||
netdata_rwlock_unlock(&labels->labels_rwlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct label *rrdset_lookup_label_key(RRDSET *st, char *key, uint32_t key_hash)
|
||||
{
|
||||
struct label_index *labels = &st->state->labels;
|
||||
struct label *ret = NULL;
|
||||
|
||||
if (labels->head) {
|
||||
netdata_rwlock_rdlock(&labels->labels_rwlock);
|
||||
ret = label_list_lookup_key(labels->head, key, key_hash);
|
||||
netdata_rwlock_unlock(&labels->labels_rwlock);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -100,8 +100,8 @@ int format_host_labels_graphite_plaintext(struct instance *instance, RRDHOST *ho
|
|||
return 0;
|
||||
|
||||
rrdhost_check_rdlock(host);
|
||||
netdata_rwlock_rdlock(&host->labels_rwlock);
|
||||
for (struct label *label = host->labels; label; label = label->next) {
|
||||
netdata_rwlock_rdlock(&host->labels.labels_rwlock);
|
||||
for (struct label *label = host->labels.head; label; label = label->next) {
|
||||
if (!should_send_label(instance, label))
|
||||
continue;
|
||||
|
||||
|
@ -113,7 +113,7 @@ int format_host_labels_graphite_plaintext(struct instance *instance, RRDHOST *ho
|
|||
buffer_sprintf(instance->labels, "%s=%s", label->key, value);
|
||||
}
|
||||
}
|
||||
netdata_rwlock_unlock(&host->labels_rwlock);
|
||||
netdata_rwlock_unlock(&host->labels.labels_rwlock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -125,8 +125,8 @@ int format_host_labels_json_plaintext(struct instance *instance, RRDHOST *host)
|
|||
|
||||
int count = 0;
|
||||
rrdhost_check_rdlock(host);
|
||||
netdata_rwlock_rdlock(&host->labels_rwlock);
|
||||
for (struct label *label = host->labels; label; label = label->next) {
|
||||
netdata_rwlock_rdlock(&host->labels.labels_rwlock);
|
||||
for (struct label *label = host->labels.head; label; label = label->next) {
|
||||
if (!should_send_label(instance, label))
|
||||
continue;
|
||||
|
||||
|
@ -138,7 +138,7 @@ int format_host_labels_json_plaintext(struct instance *instance, RRDHOST *host)
|
|||
|
||||
count++;
|
||||
}
|
||||
netdata_rwlock_unlock(&host->labels_rwlock);
|
||||
netdata_rwlock_unlock(&host->labels.labels_rwlock);
|
||||
|
||||
buffer_strcat(instance->labels, "},");
|
||||
|
||||
|
|
|
@ -153,8 +153,8 @@ int format_host_labels_opentsdb_telnet(struct instance *instance, RRDHOST *host)
|
|||
return 0;
|
||||
|
||||
rrdhost_check_rdlock(localhost);
|
||||
netdata_rwlock_rdlock(&host->labels_rwlock);
|
||||
for (struct label *label = host->labels; label; label = label->next) {
|
||||
netdata_rwlock_rdlock(&host->labels.labels_rwlock);
|
||||
for (struct label *label = host->labels.head; label; label = label->next) {
|
||||
if (!should_send_label(instance, label))
|
||||
continue;
|
||||
|
||||
|
@ -164,7 +164,7 @@ int format_host_labels_opentsdb_telnet(struct instance *instance, RRDHOST *host)
|
|||
if (*value)
|
||||
buffer_sprintf(instance->labels, " %s=%s", label->key, value);
|
||||
}
|
||||
netdata_rwlock_unlock(&host->labels_rwlock);
|
||||
netdata_rwlock_unlock(&host->labels.labels_rwlock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -294,8 +294,8 @@ int format_host_labels_opentsdb_http(struct instance *instance, RRDHOST *host)
|
|||
return 0;
|
||||
|
||||
rrdhost_check_rdlock(host);
|
||||
netdata_rwlock_rdlock(&host->labels_rwlock);
|
||||
for (struct label *label = host->labels; label; label = label->next) {
|
||||
netdata_rwlock_rdlock(&host->labels.labels_rwlock);
|
||||
for (struct label *label = host->labels.head; label; label = label->next) {
|
||||
if (!should_send_label(instance, label))
|
||||
continue;
|
||||
|
||||
|
@ -310,7 +310,7 @@ int format_host_labels_opentsdb_http(struct instance *instance, RRDHOST *host)
|
|||
buffer_sprintf(instance->labels, "\"%s\":\"%s\"", label->key, value);
|
||||
}
|
||||
}
|
||||
netdata_rwlock_unlock(&host->labels_rwlock);
|
||||
netdata_rwlock_unlock(&host->labels.labels_rwlock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -283,8 +283,8 @@ void format_host_labels_prometheus(struct instance *instance, RRDHOST *host)
|
|||
|
||||
int count = 0;
|
||||
rrdhost_check_rdlock(host);
|
||||
netdata_rwlock_rdlock(&host->labels_rwlock);
|
||||
for (struct label *label = host->labels; label; label = label->next) {
|
||||
netdata_rwlock_rdlock(&host->labels.labels_rwlock);
|
||||
for (struct label *label = host->labels.head; label; label = label->next) {
|
||||
if (!should_send_label(instance, label))
|
||||
continue;
|
||||
|
||||
|
@ -301,7 +301,7 @@ void format_host_labels_prometheus(struct instance *instance, RRDHOST *host)
|
|||
count++;
|
||||
}
|
||||
}
|
||||
netdata_rwlock_unlock(&host->labels_rwlock);
|
||||
netdata_rwlock_unlock(&host->labels.labels_rwlock);
|
||||
}
|
||||
|
||||
struct host_variables_callback_options {
|
||||
|
|
|
@ -156,8 +156,8 @@ int format_host_prometheus_remote_write(struct instance *instance, RRDHOST *host
|
|||
|
||||
if (unlikely(sending_labels_configured(instance))) {
|
||||
rrdhost_check_rdlock(host);
|
||||
netdata_rwlock_rdlock(&host->labels_rwlock);
|
||||
for (struct label *label = host->labels; label; label = label->next) {
|
||||
netdata_rwlock_rdlock(&host->labels.labels_rwlock);
|
||||
for (struct label *label = host->labels.head; label; label = label->next) {
|
||||
if (!should_send_label(instance, label))
|
||||
continue;
|
||||
|
||||
|
@ -169,7 +169,7 @@ int format_host_prometheus_remote_write(struct instance *instance, RRDHOST *host
|
|||
|
||||
add_label(connector_specific_data->write_request, key, value);
|
||||
}
|
||||
netdata_rwlock_unlock(&host->labels_rwlock);
|
||||
netdata_rwlock_unlock(&host->labels.labels_rwlock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -43,13 +43,13 @@ int setup_rrdhost()
|
|||
label->key = strdupz("key1");
|
||||
label->value = strdupz("value1");
|
||||
label->label_source = LABEL_SOURCE_NETDATA_CONF;
|
||||
localhost->labels = label;
|
||||
localhost->labels.head = label;
|
||||
|
||||
label = calloc(1, sizeof(struct label));
|
||||
label->key = strdupz("key2");
|
||||
label->value = strdupz("value2");
|
||||
label->label_source = LABEL_SOURCE_AUTO;
|
||||
localhost->labels->next = label;
|
||||
localhost->labels.head->next = label;
|
||||
|
||||
localhost->rrdset_root = calloc(1, sizeof(RRDSET));
|
||||
RRDSET *st = localhost->rrdset_root;
|
||||
|
@ -93,12 +93,12 @@ int teardown_rrdhost()
|
|||
free((void *)st->name);
|
||||
free(st);
|
||||
|
||||
free(localhost->labels->next->key);
|
||||
free(localhost->labels->next->value);
|
||||
free(localhost->labels->next);
|
||||
free(localhost->labels->key);
|
||||
free(localhost->labels->value);
|
||||
free(localhost->labels);
|
||||
free(localhost->labels.head->next->key);
|
||||
free(localhost->labels.head->next->value);
|
||||
free(localhost->labels.head->next);
|
||||
free(localhost->labels.head->key);
|
||||
free(localhost->labels.head->value);
|
||||
free(localhost->labels.head);
|
||||
|
||||
free((void *)localhost->tags);
|
||||
free(localhost);
|
||||
|
|
|
@ -73,13 +73,13 @@ inline void health_label_log_save(RRDHOST *host) {
|
|||
if(likely(host->health_log_fp)) {
|
||||
BUFFER *wb = buffer_create(1024);
|
||||
rrdhost_check_rdlock(host);
|
||||
netdata_rwlock_rdlock(&host->labels_rwlock);
|
||||
struct label *l=localhost->labels;
|
||||
netdata_rwlock_rdlock(&host->labels.labels_rwlock);
|
||||
struct label *l=localhost->labels.head;
|
||||
while (l != NULL) {
|
||||
buffer_sprintf(wb,"%s=%s\t ", l->key, l->value);
|
||||
l = l->next;
|
||||
}
|
||||
netdata_rwlock_unlock(&host->labels_rwlock);
|
||||
netdata_rwlock_unlock(&host->labels.labels_rwlock);
|
||||
|
||||
char *write = (char *) buffer_tostring(wb) ;
|
||||
|
||||
|
|
|
@ -367,6 +367,22 @@ void avl_init_lock(avl_tree_lock *tree, int (*compar)(void * /*a*/, void * /*b*/
|
|||
#endif /* AVL_WITHOUT_PTHREADS */
|
||||
}
|
||||
|
||||
void avl_destroy_lock(avl_tree_lock *tree) {
|
||||
#ifndef AVL_WITHOUT_PTHREADS
|
||||
int lock;
|
||||
|
||||
#ifdef AVL_LOCK_WITH_MUTEX
|
||||
lock = pthread_mutex_destroy(&tree->mutex);
|
||||
#else
|
||||
lock = pthread_rwlock_destroy(&tree->rwlock);
|
||||
#endif
|
||||
|
||||
if(lock != 0)
|
||||
fatal("Failed to destroy AVL mutex/rwlock, error: %d", lock);
|
||||
|
||||
#endif /* AVL_WITHOUT_PTHREADS */
|
||||
}
|
||||
|
||||
avl *avl_search_lock(avl_tree_lock *tree, avl *item) {
|
||||
avl_read_lock(tree);
|
||||
avl *ret = avl_search(&tree->avl_tree, item);
|
||||
|
|
|
@ -82,6 +82,9 @@ avl *avl_search(avl_tree_type *tree, avl *item);
|
|||
void avl_init_lock(avl_tree_lock *tree, int (*compar)(void *a, void *b));
|
||||
void avl_init(avl_tree_type *tree, int (*compar)(void *a, void *b));
|
||||
|
||||
/* Destroy the avl_tree_lock locks
|
||||
*/
|
||||
void avl_destroy_lock(avl_tree_lock *tree);
|
||||
|
||||
int avl_traverse_lock(avl_tree_lock *tree, int (*callback)(void *entry, void *data), void *data);
|
||||
int avl_traverse(avl_tree_type *tree, int (*callback)(void *entry, void *data), void *data);
|
||||
|
|
|
@ -189,6 +189,49 @@ static inline struct section *appconfig_section_create(struct config *root, cons
|
|||
return co;
|
||||
}
|
||||
|
||||
void appconfig_section_destroy_non_loaded(struct config *root, const char *section)
|
||||
{
|
||||
struct section *co;
|
||||
struct config_option *cv, *cv_next;
|
||||
|
||||
debug(D_CONFIG, "Destroying section '%s'.", section);
|
||||
|
||||
co = appconfig_section_find(root, section);
|
||||
if(!co) {
|
||||
error("Could not destroy section '%s'. Not found.", section);
|
||||
return;
|
||||
}
|
||||
|
||||
config_section_wrlock(co);
|
||||
for(cv = co->values; cv ; cv = cv->next) {
|
||||
if (cv->flags & CONFIG_VALUE_LOADED) {
|
||||
/* Do not destroy values that were loaded from the configuration files. */
|
||||
config_section_unlock(co);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for(cv = co->values ; cv ; cv = cv_next) {
|
||||
cv_next = cv->next;
|
||||
if(unlikely(!appconfig_option_index_del(co, cv)))
|
||||
error("Cannot remove config option '%s' from section '%s'.", cv->name, co->name);
|
||||
freez(cv->value);
|
||||
freez(cv->name);
|
||||
freez(cv);
|
||||
}
|
||||
co->values = NULL;
|
||||
config_section_unlock(co);
|
||||
|
||||
if (unlikely(!appconfig_index_del(root, co))) {
|
||||
error("Cannot remove section '%s' from config.", section);
|
||||
return;
|
||||
}
|
||||
|
||||
avl_destroy_lock(&co->values_index);
|
||||
freez(co->name);
|
||||
pthread_mutex_destroy(&co->mutex);
|
||||
freez(co);
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// config name-value methods
|
||||
|
|
|
@ -182,6 +182,8 @@ extern void appconfig_generate(struct config *root, BUFFER *wb, int only_changed
|
|||
|
||||
extern int appconfig_section_compare(void *a, void *b);
|
||||
|
||||
extern void appconfig_section_destroy_non_loaded(struct config *root, const char *section);
|
||||
|
||||
extern int config_parse_duration(const char* string, int* result);
|
||||
|
||||
extern struct section *appconfig_get_section(struct config *root, const char *name);
|
||||
|
|
|
@ -421,7 +421,7 @@ static int rrdpush_receive(struct receiver_state *rpt)
|
|||
*/
|
||||
|
||||
// rpt->host->connected_senders++;
|
||||
rpt->host->labels_flag = (rpt->stream_version > 0)?LABEL_FLAG_UPDATE_STREAM:LABEL_FLAG_STOP_STREAM;
|
||||
rpt->host->labels.labels_flag = (rpt->stream_version > 0)?LABEL_FLAG_UPDATE_STREAM:LABEL_FLAG_STOP_STREAM;
|
||||
|
||||
if(health_enabled != CONFIG_BOOLEAN_NO) {
|
||||
if(alarms_delay > 0) {
|
||||
|
|
|
@ -334,35 +334,35 @@ void rrdset_done_push(RRDSET *st) {
|
|||
|
||||
// labels
|
||||
void rrdpush_send_labels(RRDHOST *host) {
|
||||
if (!host->labels || !(host->labels_flag & LABEL_FLAG_UPDATE_STREAM) || (host->labels_flag & LABEL_FLAG_STOP_STREAM))
|
||||
if (!host->labels.head || !(host->labels.labels_flag & LABEL_FLAG_UPDATE_STREAM) || (host->labels.labels_flag & LABEL_FLAG_STOP_STREAM))
|
||||
return;
|
||||
|
||||
sender_start(host->sender);
|
||||
rrdhost_rdlock(host);
|
||||
netdata_rwlock_rdlock(&host->labels_rwlock);
|
||||
netdata_rwlock_rdlock(&host->labels.labels_rwlock);
|
||||
|
||||
struct label *labels = host->labels;
|
||||
while(labels) {
|
||||
struct label *label_i = host->labels.head;
|
||||
while(label_i) {
|
||||
buffer_sprintf(host->sender->build
|
||||
, "LABEL \"%s\" = %d %s\n"
|
||||
, labels->key
|
||||
, (int)labels->label_source
|
||||
, labels->value);
|
||||
, label_i->key
|
||||
, (int)label_i->label_source
|
||||
, label_i->value);
|
||||
|
||||
labels = labels->next;
|
||||
label_i = label_i->next;
|
||||
}
|
||||
|
||||
buffer_sprintf(host->sender->build
|
||||
, "OVERWRITE %s\n", "labels");
|
||||
|
||||
netdata_rwlock_unlock(&host->labels_rwlock);
|
||||
netdata_rwlock_unlock(&host->labels.labels_rwlock);
|
||||
rrdhost_unlock(host);
|
||||
sender_commit(host->sender);
|
||||
|
||||
if(host->rrdpush_sender_pipe[PIPE_WRITE] != -1 && write(host->rrdpush_sender_pipe[PIPE_WRITE], " ", 1) == -1)
|
||||
error("STREAM %s [send]: cannot write to internal pipe", host->hostname);
|
||||
|
||||
host->labels_flag &= ~LABEL_FLAG_UPDATE_STREAM;
|
||||
host->labels.labels_flag &= ~LABEL_FLAG_UPDATE_STREAM;
|
||||
}
|
||||
|
||||
void rrdpush_claimed_id(RRDHOST *host)
|
||||
|
|
|
@ -116,8 +116,8 @@ static inline void rrdpush_sender_thread_data_flush(RRDHOST *host) {
|
|||
}
|
||||
|
||||
static inline void rrdpush_set_flags_to_newest_stream(RRDHOST *host) {
|
||||
host->labels_flag |= LABEL_FLAG_UPDATE_STREAM;
|
||||
host->labels_flag &= ~LABEL_FLAG_STOP_STREAM;
|
||||
host->labels.labels_flag |= LABEL_FLAG_UPDATE_STREAM;
|
||||
host->labels.labels_flag &= ~LABEL_FLAG_STOP_STREAM;
|
||||
}
|
||||
|
||||
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
|
|||
answer = memcmp(http, START_STREAMING_PROMPT, strlen(START_STREAMING_PROMPT));
|
||||
if(!answer) {
|
||||
version = 0;
|
||||
host->labels_flag |= LABEL_FLAG_STOP_STREAM;
|
||||
host->labels_flag &= ~LABEL_FLAG_UPDATE_STREAM;
|
||||
host->labels.labels_flag |= LABEL_FLAG_STOP_STREAM;
|
||||
host->labels.labels_flag &= ~LABEL_FLAG_UPDATE_STREAM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include "json_wrapper.h"
|
||||
|
||||
void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, int string_value, RRDDIM *temp_rd) {
|
||||
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) {
|
||||
rrdset_check_rdlock(r->st);
|
||||
|
||||
long rows = rrdr_rows(r);
|
||||
|
@ -86,12 +86,12 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS
|
|||
buffer_strcat(wb, "no data");
|
||||
buffer_strcat(wb, sq);
|
||||
}
|
||||
buffer_strcat(wb, "],\n");
|
||||
|
||||
// Composite charts
|
||||
if (temp_rd) {
|
||||
buffer_sprintf(
|
||||
wb,
|
||||
"],\n"
|
||||
" %schart_ids%s: [",
|
||||
kq, kq);
|
||||
|
||||
|
@ -114,11 +114,46 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS
|
|||
buffer_strcat(wb, "no data");
|
||||
buffer_strcat(wb, sq);
|
||||
}
|
||||
buffer_strcat(wb, "],\n");
|
||||
if (chart_label_key) {
|
||||
uint32_t key_hash = simple_hash(chart_label_key);
|
||||
struct label *current_label;
|
||||
|
||||
buffer_sprintf(
|
||||
wb,
|
||||
" %schart_labels%s: { %s%s%s : [",
|
||||
kq, kq, kq, chart_label_key, kq);
|
||||
|
||||
for (c = 0, i = 0, rd = temp_rd; rd && c < r->d; c++, rd = rd->next) {
|
||||
if (unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN))
|
||||
continue;
|
||||
if (unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO)))
|
||||
continue;
|
||||
|
||||
if (i)
|
||||
buffer_strcat(wb, ", ");
|
||||
|
||||
current_label = rrdset_lookup_label_key(rd->rrdset, chart_label_key, key_hash);
|
||||
if (current_label) {
|
||||
buffer_strcat(wb, sq);
|
||||
buffer_strcat(wb, current_label->value);
|
||||
buffer_strcat(wb, sq);
|
||||
} else
|
||||
buffer_strcat(wb, "null");
|
||||
i++;
|
||||
}
|
||||
if (!i) {
|
||||
rows = 0;
|
||||
buffer_strcat(wb, sq);
|
||||
buffer_strcat(wb, "no data");
|
||||
buffer_strcat(wb, sq);
|
||||
}
|
||||
buffer_strcat(wb, "] },\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
buffer_sprintf(wb, "],\n"
|
||||
" %slatest_values%s: ["
|
||||
buffer_sprintf(wb, " %slatest_values%s: ["
|
||||
, kq, kq);
|
||||
|
||||
for(c = 0, i = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#include "rrd2json.h"
|
||||
|
||||
extern void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, int string_value, RRDDIM *temp_rd);
|
||||
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);
|
||||
extern void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value);
|
||||
|
||||
#endif //NETDATA_API_FORMATTER_JSON_WRAPPER_H
|
||||
|
|
|
@ -188,6 +188,7 @@ int rrdset2anything_api_v1(
|
|||
, uint32_t options
|
||||
, time_t *latest_timestamp
|
||||
, struct context_param *context_param_list
|
||||
, char *chart_label_key
|
||||
) {
|
||||
time_t last_accessed_time = now_realtime_sec();
|
||||
st->last_accessed_time = last_accessed_time;
|
||||
|
@ -212,7 +213,7 @@ int rrdset2anything_api_v1(
|
|||
case DATASOURCE_SSV:
|
||||
if(options & RRDR_OPTION_JSON_WRAP) {
|
||||
wb->contenttype = CT_APPLICATION_JSON;
|
||||
rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd);
|
||||
rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd, chart_label_key);
|
||||
rrdr2ssv(r, wb, options, "", " ", "");
|
||||
rrdr_json_wrapper_end(r, wb, format, options, 1);
|
||||
}
|
||||
|
@ -225,7 +226,7 @@ int rrdset2anything_api_v1(
|
|||
case DATASOURCE_SSV_COMMA:
|
||||
if(options & RRDR_OPTION_JSON_WRAP) {
|
||||
wb->contenttype = CT_APPLICATION_JSON;
|
||||
rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd);
|
||||
rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd, chart_label_key);
|
||||
rrdr2ssv(r, wb, options, "", ",", "");
|
||||
rrdr_json_wrapper_end(r, wb, format, options, 1);
|
||||
}
|
||||
|
@ -238,7 +239,7 @@ int rrdset2anything_api_v1(
|
|||
case DATASOURCE_JS_ARRAY:
|
||||
if(options & RRDR_OPTION_JSON_WRAP) {
|
||||
wb->contenttype = CT_APPLICATION_JSON;
|
||||
rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd);
|
||||
rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd, chart_label_key);
|
||||
rrdr2ssv(r, wb, options, "[", ",", "]");
|
||||
rrdr_json_wrapper_end(r, wb, format, options, 0);
|
||||
}
|
||||
|
@ -251,7 +252,7 @@ int rrdset2anything_api_v1(
|
|||
case DATASOURCE_CSV:
|
||||
if(options & RRDR_OPTION_JSON_WRAP) {
|
||||
wb->contenttype = CT_APPLICATION_JSON;
|
||||
rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd);
|
||||
rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd, chart_label_key);
|
||||
rrdr2csv(r, wb, format, options, "", ",", "\\n", "", temp_rd);
|
||||
rrdr_json_wrapper_end(r, wb, format, options, 1);
|
||||
}
|
||||
|
@ -264,7 +265,7 @@ int rrdset2anything_api_v1(
|
|||
case DATASOURCE_CSV_MARKDOWN:
|
||||
if(options & RRDR_OPTION_JSON_WRAP) {
|
||||
wb->contenttype = CT_APPLICATION_JSON;
|
||||
rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd);
|
||||
rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd, chart_label_key);
|
||||
rrdr2csv(r, wb, format, options, "", "|", "\\n", "", temp_rd);
|
||||
rrdr_json_wrapper_end(r, wb, format, options, 1);
|
||||
}
|
||||
|
@ -277,7 +278,7 @@ int rrdset2anything_api_v1(
|
|||
case DATASOURCE_CSV_JSON_ARRAY:
|
||||
wb->contenttype = CT_APPLICATION_JSON;
|
||||
if(options & RRDR_OPTION_JSON_WRAP) {
|
||||
rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd);
|
||||
rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd, chart_label_key);
|
||||
buffer_strcat(wb, "[\n");
|
||||
rrdr2csv(r, wb, format, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n", temp_rd);
|
||||
buffer_strcat(wb, "\n]");
|
||||
|
@ -294,7 +295,7 @@ int rrdset2anything_api_v1(
|
|||
case DATASOURCE_TSV:
|
||||
if(options & RRDR_OPTION_JSON_WRAP) {
|
||||
wb->contenttype = CT_APPLICATION_JSON;
|
||||
rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd);
|
||||
rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd, chart_label_key);
|
||||
rrdr2csv(r, wb, format, options, "", "\t", "\\n", "", temp_rd);
|
||||
rrdr_json_wrapper_end(r, wb, format, options, 1);
|
||||
}
|
||||
|
@ -307,7 +308,7 @@ int rrdset2anything_api_v1(
|
|||
case DATASOURCE_HTML:
|
||||
if(options & RRDR_OPTION_JSON_WRAP) {
|
||||
wb->contenttype = CT_APPLICATION_JSON;
|
||||
rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd);
|
||||
rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd, chart_label_key);
|
||||
buffer_strcat(wb, "<html>\\n<center>\\n<table border=\\\"0\\\" cellpadding=\\\"5\\\" cellspacing=\\\"5\\\">\\n");
|
||||
rrdr2csv(r, wb, format, options, "<tr><td>", "</td><td>", "</td></tr>\\n", "", temp_rd);
|
||||
buffer_strcat(wb, "</table>\\n</center>\\n</html>\\n");
|
||||
|
@ -325,7 +326,7 @@ int rrdset2anything_api_v1(
|
|||
wb->contenttype = CT_APPLICATION_X_JAVASCRIPT;
|
||||
|
||||
if(options & RRDR_OPTION_JSON_WRAP)
|
||||
rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd);
|
||||
rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd, chart_label_key);
|
||||
|
||||
rrdr2json(r, wb, options, 1, temp_rd);
|
||||
|
||||
|
@ -337,7 +338,7 @@ int rrdset2anything_api_v1(
|
|||
wb->contenttype = CT_APPLICATION_JSON;
|
||||
|
||||
if(options & RRDR_OPTION_JSON_WRAP)
|
||||
rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd);
|
||||
rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd, chart_label_key);
|
||||
|
||||
rrdr2json(r, wb, options, 1, temp_rd);
|
||||
|
||||
|
@ -348,7 +349,7 @@ int rrdset2anything_api_v1(
|
|||
case DATASOURCE_JSONP:
|
||||
wb->contenttype = CT_APPLICATION_X_JAVASCRIPT;
|
||||
if(options & RRDR_OPTION_JSON_WRAP)
|
||||
rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd);
|
||||
rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd, chart_label_key);
|
||||
|
||||
rrdr2json(r, wb, options, 0, temp_rd);
|
||||
|
||||
|
@ -361,7 +362,7 @@ int rrdset2anything_api_v1(
|
|||
wb->contenttype = CT_APPLICATION_JSON;
|
||||
|
||||
if(options & RRDR_OPTION_JSON_WRAP)
|
||||
rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd);
|
||||
rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd, chart_label_key);
|
||||
|
||||
rrdr2json(r, wb, options, 0, temp_rd);
|
||||
|
||||
|
|
|
@ -66,6 +66,7 @@ extern int rrdset2anything_api_v1(
|
|||
, uint32_t options
|
||||
, time_t *latest_timestamp
|
||||
, struct context_param *context_param_list
|
||||
, char *chart_label_key
|
||||
);
|
||||
|
||||
extern int rrdset2value_api_v1(
|
||||
|
|
|
@ -2,6 +2,36 @@
|
|||
|
||||
#include "rrdset2json.h"
|
||||
|
||||
void chart_labels2json(RRDSET *st, BUFFER *wb, size_t indentation)
|
||||
{
|
||||
char tabs[11];
|
||||
struct label_index *labels = &st->state->labels;
|
||||
|
||||
if (indentation > 10)
|
||||
indentation = 10;
|
||||
|
||||
tabs[0] = '\0';
|
||||
while (indentation) {
|
||||
strcat(tabs, "\t");
|
||||
indentation--;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
netdata_rwlock_rdlock(&labels->labels_rwlock);
|
||||
for (struct label *label = labels->head; label; label = label->next) {
|
||||
if(count > 0) buffer_strcat(wb, ",\n");
|
||||
buffer_strcat(wb, tabs);
|
||||
|
||||
char value[CONFIG_MAX_VALUE * 2 + 1];
|
||||
sanitize_json_string(value, label->value, CONFIG_MAX_VALUE * 2);
|
||||
buffer_sprintf(wb, "\"%s\": \"%s\"", label->key, value);
|
||||
|
||||
count++;
|
||||
}
|
||||
buffer_strcat(wb, "\n");
|
||||
netdata_rwlock_unlock(&labels->labels_rwlock);
|
||||
}
|
||||
|
||||
// generate JSON for the /api/v1/chart API call
|
||||
|
||||
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
|
|||
"\n\t\t\t}"
|
||||
);
|
||||
}
|
||||
buffer_strcat(wb, ",\n\t\t\t\"chart_labels\": {\n");
|
||||
chart_labels2json(st, wb, 2);
|
||||
buffer_strcat(wb, "\t\t\t}\n");
|
||||
|
||||
|
||||
buffer_sprintf(wb,
|
||||
"\n\t\t}"
|
||||
|
|
|
@ -402,7 +402,8 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
|
|||
, *after_str = NULL
|
||||
, *group_time_str = NULL
|
||||
, *points_str = NULL
|
||||
, *context = NULL;
|
||||
, *context = NULL
|
||||
, *chart_label_key = NULL;
|
||||
|
||||
int group = RRDR_GROUPING_AVERAGE;
|
||||
uint32_t format = DATASOURCE_JSON;
|
||||
|
@ -422,6 +423,7 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
|
|||
// they are not null and not empty
|
||||
|
||||
if(!strcmp(name, "context")) context = value;
|
||||
else if(!strcmp(name, "chart_label_key")) chart_label_key = value;
|
||||
else if(!strcmp(name, "chart")) chart = value;
|
||||
else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
|
||||
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
|
|||
if (context && !chart) {
|
||||
RRDSET *st1;
|
||||
uint32_t context_hash = simple_hash(context);
|
||||
uint32_t key_hash;
|
||||
|
||||
if (chart_label_key)
|
||||
key_hash = simple_hash(chart_label_key);
|
||||
|
||||
rrdhost_rdlock(host);
|
||||
rrdset_foreach_read(st1, host) {
|
||||
if (st1->hash_context == context_hash && !strcmp(st1->context, context))
|
||||
if (st1->hash_context == context_hash && !strcmp(st1->context, context) &&
|
||||
(!chart_label_key || rrdset_contains_label_key(st1, chart_label_key, key_hash)))
|
||||
build_context_param_list(&context_param_list, st1);
|
||||
}
|
||||
rrdhost_unlock(host);
|
||||
|
@ -518,8 +526,16 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
|
|||
|
||||
if (!st && !context_param_list) {
|
||||
if (context && !chart) {
|
||||
buffer_strcat(w->response.data, "Context is not found: ");
|
||||
buffer_strcat_htmlescape(w->response.data, context);
|
||||
if (!chart_label_key) {
|
||||
buffer_strcat(w->response.data, "Context is not found: ");
|
||||
buffer_strcat_htmlescape(w->response.data, context);
|
||||
} else {
|
||||
buffer_strcat(w->response.data, "Context: ");
|
||||
buffer_strcat_htmlescape(w->response.data, context);
|
||||
buffer_strcat(w->response.data, " or chart label key: ");
|
||||
buffer_strcat_htmlescape(w->response.data, chart_label_key);
|
||||
buffer_strcat(w->response.data, " not found");
|
||||
}
|
||||
}
|
||||
else {
|
||||
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
|
|||
}
|
||||
|
||||
ret = rrdset2anything_api_v1(st, w->response.data, dimensions, format, points, after, before, group, group_time
|
||||
, options, &last_timestamp_in_data, context_param_list);
|
||||
, options, &last_timestamp_in_data, context_param_list, chart_label_key);
|
||||
|
||||
free_context_param_list(&context_param_list);
|
||||
|
||||
|
@ -869,8 +885,8 @@ inline void host_labels2json(RRDHOST *host, BUFFER *wb, size_t indentation) {
|
|||
|
||||
int count = 0;
|
||||
rrdhost_rdlock(host);
|
||||
netdata_rwlock_rdlock(&host->labels_rwlock);
|
||||
for (struct label *label = host->labels; label; label = label->next) {
|
||||
netdata_rwlock_rdlock(&host->labels.labels_rwlock);
|
||||
for (struct label *label = host->labels.head; label; label = label->next) {
|
||||
if(count > 0) buffer_strcat(wb, ",\n");
|
||||
buffer_strcat(wb, tabs);
|
||||
|
||||
|
@ -881,7 +897,7 @@ inline void host_labels2json(RRDHOST *host, BUFFER *wb, size_t indentation) {
|
|||
count++;
|
||||
}
|
||||
buffer_strcat(wb, "\n");
|
||||
netdata_rwlock_unlock(&host->labels_rwlock);
|
||||
netdata_rwlock_unlock(&host->labels.labels_rwlock);
|
||||
rrdhost_unlock(host);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue