From 0f8175dd3060691394e263cdab01c8f940b1b5d3 Mon Sep 17 00:00:00 2001 From: Ilya Mashchenko Date: Mon, 14 Dec 2020 17:27:55 +0300 Subject: Kubernetes labels (#10107) Co-authored-by: Markos Fountoulakis Co-authored-by: Vladimir Kobal --- collectors/cgroups.plugin/cgroup-name.sh.in | 407 ++++++++++++--------- collectors/cgroups.plugin/sys_fs_cgroup.c | 117 +++++- collectors/cgroups.plugin/sys_fs_cgroup.h | 2 + .../cgroups.plugin/tests/test_cgroups_plugin.c | 110 ++++++ .../cgroups.plugin/tests/test_cgroups_plugin.h | 16 + collectors/cgroups.plugin/tests/test_doubles.c | 162 ++++++++ collectors/plugins.d/pluginsd_parser.c | 6 +- collectors/proc.plugin/plugin_proc.h | 3 +- collectors/proc.plugin/proc_net_dev.c | 30 +- 9 files changed, 666 insertions(+), 187 deletions(-) create mode 100644 collectors/cgroups.plugin/tests/test_cgroups_plugin.c create mode 100644 collectors/cgroups.plugin/tests/test_cgroups_plugin.h create mode 100644 collectors/cgroups.plugin/tests/test_doubles.c (limited to 'collectors') diff --git a/collectors/cgroups.plugin/cgroup-name.sh.in b/collectors/cgroups.plugin/cgroup-name.sh.in index cd1b8a5880..19fbf3989d 100755 --- a/collectors/cgroups.plugin/cgroup-name.sh.in +++ b/collectors/cgroups.plugin/cgroup-name.sh.in @@ -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__' # container level cgroup name format: 'cntr___' @@ -122,11 +137,11 @@ function k8s_get_kubepod_name() { # # NOTE: cgroups plugin uses '_' to join dir names, so it is ___... - 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="",pod_name="",pod_uid="",container_name="",container_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}" - - # 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 + 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}" + + 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}" - - 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_.*\.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 - - 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 - - [ -z "${NAME}" ] && NAME="${CGROUP}" - [ ${#NAME} -gt 100 ] && NAME="${NAME:0:100}" + 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_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} =~ 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/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}" fi info "cgroup '${CGROUP}' is called '${NAME}'" echo "${NAME}" exit ${NAME_NOT_FOUND} - diff --git a/collectors/cgroups.plugin/sys_fs_cgroup.c b/collectors/cgroups.plugin/sys_fs_cgroup.c index 75df8c6382..499124cb46 100644 --- a/collectors/cgroups.plugin/sys_fs_cgroup.c +++ b/collectors/cgroups.plugin/sys_fs_cgroup.c @@ -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); diff --git a/collectors/cgroups.plugin/sys_fs_cgroup.h b/collectors/cgroups.plugin/sys_fs_cgroup.h index 09ce5e3fb3..155330ff1b 100644 --- a/collectors/cgroups.plugin/sys_fs_cgroup.h +++ b/collectors/cgroups.plugin/sys_fs_cgroup.h @@ -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 diff --git a/collectors/cgroups.plugin/tests/test_cgroups_plugin.c b/collectors/cgroups.plugin/tests/test_cgroups_plugin.c new file mode 100644 index 0000000000..be8ea2c488 --- /dev/null +++ b/collectors/cgroups.plugin/tests/test_cgroups_plugin.c @@ -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; +} diff --git a/collectors/cgroups.plugin/tests/test_cgroups_plugin.h b/collectors/cgroups.plugin/tests/test_cgroups_plugin.h new file mode 100644 index 0000000000..3d68e92304 --- /dev/null +++ b/collectors/cgroups.plugin/tests/test_cgroups_plugin.h @@ -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 +#include +#include +#include +#include + +#endif /* TEST_CGROUPS_PLUGIN_H */ diff --git a/collectors/cgroups.plugin/tests/test_doubles.c b/collectors/cgroups.plugin/tests/test_doubles.c new file mode 100644 index 0000000000..5572fb2f50 --- /dev/null +++ b/collectors/cgroups.plugin/tests/test_doubles.c @@ -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); +} diff --git a/collectors/plugins.d/pluginsd_parser.c b/collectors/plugins.d/pluginsd_parser.c index aee8d33434..4a97c5535a 100644 --- a/collectors/plugins.d/pluginsd_parser.c +++ b/collectors/plugins.d/pluginsd_parser.c @@ -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; diff --git a/collectors/proc.plugin/plugin_proc.h b/collectors/proc.plugin/plugin_proc.h index 50ffb184ed..108c026ab2 100644 --- a/collectors/proc.plugin/plugin_proc.h +++ b/collectors/proc.plugin/plugin_proc.h @@ -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" diff --git a/collectors/proc.plugin/proc_net_dev.c b/collectors/proc.plugin/proc_net_dev.c index 9bfb0597b8..a90e3c3ee3 100644 --- a/collectors/proc.plugin/proc_net_dev.c +++ b/collectors/proc.plugin/proc_net_dev.c @@ -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); -- cgit v1.2.3