0
0
Fork 0
mirror of https://github.com/netdata/netdata.git synced 2025-04-27 06:10:43 +00:00

Kubernetes labels ()

Co-authored-by: Markos Fountoulakis <markos.fountoulakis.senior@gmail.com>
Co-authored-by: Vladimir Kobal <vlad@prokk.net>
This commit is contained in:
Ilya Mashchenko 2020-12-14 17:27:55 +03:00 committed by GitHub
parent 7bfa8c8eba
commit 0f8175dd30
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 1233 additions and 449 deletions

View file

@ -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
)

View file

@ -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

View file

@ -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}

View file

@ -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);

View file

@ -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

View 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;
}

View 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 */

View 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);
}

View file

@ -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;

View file

@ -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"

View file

@ -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);

View file

@ -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));

View file

@ -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

View file

@ -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);

View file

@ -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'",

View file

@ -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
View 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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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, "},");

View file

@ -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;
}

View file

@ -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 {

View file

@ -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;

View file

@ -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);

View file

@ -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) ;

View file

@ -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);

View file

@ -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);

View file

@ -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

View file

@ -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);

View file

@ -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) {

View file

@ -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)

View file

@ -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;
}
}
}

View file

@ -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) {

View file

@ -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

View file

@ -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);

View file

@ -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(

View file

@ -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}"

View file

@ -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);
}