]> arthur.barton.de Git - netdata.git/blobdiff - plugins.d/tc-qos-helper.sh
ab-debian 0.20170327.01-0ab1, upstream v1.6.0-42-gaa6b96fc
[netdata.git] / plugins.d / tc-qos-helper.sh
index 7b4739815481d2097bb1573500b8f92e8757a338..074fece9a5e3d571618a69a0c3942ac7b11cd0ea 100755 (executable)
-#!/bin/bash
+#!/usr/bin/env bash
+
+# netdata
+# real-time performance and health monitoring, done right!
+# (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+# GPL v3+
+#
+# This script is a helper to allow netdata collect tc data.
+# tc output parsing has been implemented in C, inside netdata
+# This script allows setting names to dimensions.
 
 export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/sbin"
+export LC_ALL=C
+
+
+# -----------------------------------------------------------------------------
+# find /var/run/fireqos
+
+# the default
+fireqos_run_dir="/var/run/fireqos"
+
+function realdir {
+    local r="$1"
+    local t=$(readlink "$r")
 
-# default time function
-now_ms=
-current_time_ms() {
-       now_ms="$(date +'%s')000"
+    while [ "$t" ]
+        do
+        r=$(cd $(dirname "$r") && cd $(dirname "$t") && pwd -P)/$(basename "$t")
+        t=$(readlink "$r")
+    done
+
+    dirname "$r"
 }
 
+if [ ! -d "${fireqos_run_dir}" ]
+    then
+
+    # the fireqos executable - we will use it to find its config
+    fireqos="$(which fireqos 2>/dev/null || command -v fireqos 2>/dev/null)"
+
+    if [ ! -z "${fireqos}" ]
+        then
+
+        fireqos_exec_dir="$(realdir ${fireqos})"
+
+        if [ ! -z "${fireqos_exec_dir}" -a "${fireqos_exec_dir}" != "." -a -f "${fireqos_exec_dir}/install.config" ]
+            then
+
+            LOCALSTATEDIR=
+            source "${fireqos_exec_dir}/install.config"
+
+            if [ -d "${LOCALSTATEDIR}/run/fireqos" ]
+                then
+                fireqos_run_dir="${LOCALSTATEDIR}/run/fireqos"
+            fi
+        fi
+    fi
+fi
+
+# -----------------------------------------------------------------------------
+# logging functions
+
+PROGRAM_FILE="$0"
+PROGRAM_NAME="$(basename $0)"
+PROGRAM_NAME="${PROGRAM_NAME/.plugin}"
+
+logdate() {
+    date "+%Y-%m-%d %H:%M:%S"
+}
+
+log() {
+    local status="${1}"
+    shift
+
+    echo >&2 "$(logdate): ${PROGRAM_NAME}: ${status}: ${*}"
+
+}
+
+warning() {
+    log WARNING "${@}"
+}
+
+error() {
+    log ERROR "${@}"
+}
+
+info() {
+    log INFO "${@}"
+}
+
+fatal() {
+    log FATAL "${@}"
+    exit 1
+}
+
+debug=0
+debug() {
+    [ $debug -eq 1 ] && log DEBUG "${@}"
+}
+
+
+# -----------------------------------------------------------------------------
+
+plugins_dir="${NETDATA_PLUGINS_DIR}"
+[ -z "$plugins_dir" ] && plugins_dir="$( dirname $PROGRAM_FILE )"
+
+config_dir=${NETDATA_CONFIG_DIR-/etc/netdata}
+tc="$(which tc 2>/dev/null || command -v tc 2>/dev/null)"
+
+
+# -----------------------------------------------------------------------------
+# user configuration
+
+# time in seconds to refresh QoS class/qdisc names
+qos_get_class_names_every=120
+
+# time in seconds to exit - netdata will restart the script
+qos_exit_every=3600
+
+# what to use? classes or qdiscs?
+tc_show="qdisc" # can also be "class"
+
+
+# -----------------------------------------------------------------------------
+# check if we have a valid number for interval
+
+t=${1}
+update_every=$((t))
+[ $((update_every)) -lt 1 ] && update_every=${NETDATA_UPDATE_EVERY}
+[ $((update_every)) -lt 1 ] && update_every=1
+
+
+# -----------------------------------------------------------------------------
+# allow the user to override our defaults
+
+if [ -f "${config_dir}/tc-qos-helper.conf" ]
+    then
+    source "${config_dir}/tc-qos-helper.conf"
+fi
+
+case "${tc_show}" in
+    qdisc|class)
+        ;;
+
+    *)
+        error "tc_show variable can be either 'qdisc' or 'class' but is set to '${tc_show}'. Assuming it is 'qdisc'."
+        tc_show="qdisc"
+        ;;
+esac
+
+
+# -----------------------------------------------------------------------------
 # default sleep function
+
 LOOPSLEEPMS_LASTWORK=0
 loopsleepms() {
-       [ "$1" = "tellwork" ] && shift
-       sleep $1
+    sleep $1
 }
 
 # if found and included, this file overwrites loopsleepms()
 # with a high resolution timer function for precise looping.
-. "$NETDATA_PLUGINS_DIR/loopsleepms.sh.inc"
+. "${plugins_dir}/loopsleepms.sh.inc"
 
-# check if we have a valid number for interval
-t=$1
-sleep_time=$((t))
-[ $((sleep_time)) -lt 1 ] && $NETDATA_UPDATE_EVERY
-[ $((sleep_time)) -lt 1 ] && sleep_time=1
-
-tc_cmd="$(which tc)"
-if [ -z "$tc_cmd" ]
-       then
-       echo >&2 "tc: Cannot find a 'tc' command in this system."
-       exit 1
+
+# -----------------------------------------------------------------------------
+# final checks we can run
+
+if [ -z "${tc}" -o ! -x "${tc}" ]
+    then
+    fatal "cannot find command 'tc' in this system."
 fi
 
-devices=
+tc_devices=
 fix_names=
 
+# -----------------------------------------------------------------------------
+
 setclassname() {
-       echo "SETCLASSNAME $3 $2"
+    if [ "${tc_show}" = "qdisc" ]
+        then
+        echo "SETCLASSNAME $4 $2"
+    else
+        echo "SETCLASSNAME $3 $2"
+    fi
+}
+
+show_tc_cls() {
+    [ "${tc_show}" = "qdisc" ] && return 1
+
+    local x="${1}"
+
+    if [ -f /etc/iproute2/tc_cls ]
+    then
+        local classid name rest
+        while read classid name rest
+        do
+            [ -z "${classid}" -o -z "${name}" -o "${classid}" = "#" -o "${name}" = "#" -o "${classid:0:1}" = "#" -o "${name:0:1}" = "#" ] && continue
+            setclassname "" "${name}" "${classid}"
+        done </etc/iproute2/tc_cls
+        return 0
+    fi
+    return 1
+}
+
+show_fireqos_names() {
+    local x="${1}" name n interface_dev interface_classes interface_classes_monitor
+
+    if [ -f "${fireqos_run_dir}/ifaces/${x}" ]
+    then
+        name="$(<"${fireqos_run_dir}/ifaces/${x}")"
+        echo "SETDEVICENAME ${name}"
+
+        interface_dev=
+        interface_classes=
+        interface_classes_monitor=
+        source "${fireqos_run_dir}/${name}.conf"
+        for n in ${interface_classes_monitor}
+        do
+            setclassname ${n//|/ }
+        done
+        [ ! -z "${interface_dev}" ] && echo "SETDEVICEGROUP ${interface_dev}"
+
+        return 0
+    fi
+
+    return 1
 }
 
 show_tc() {
-       local x="$1"
-
-       echo "BEGIN $x"
-       $tc_cmd -s class show dev $x
-
-       # check FireQOS names for classes
-       if [ ! -z "$fix_names" -a -f /var/run/fireqos/ifaces/$x ]
-       then
-               name="$(cat /var/run/fireqos/ifaces/$x)"
-               echo "SETDEVICENAME $name"
-
-               interface_classes=
-               interface_classes_monitor=
-               . /var/run/fireqos/$name.conf
-               for n in $interface_classes_monitor
-               do
-                       setclassname $(echo $n | tr '|' ' ')
-               done
-               echo "SETDEVICEGROUP $interface_dev"
-       fi
-       echo "END $x"
+    local x="${1}"
+
+    echo "BEGIN ${x}"
+
+    # netdata can parse the output of tc
+    ${tc} -s ${tc_show} show dev ${x}
+
+    # check FireQOS names for classes
+    if [ ! -z "${fix_names}" ]
+    then
+        show_fireqos_names "${x}" || show_tc_cls "${x}"
+    fi
+
+    echo "END ${x}"
 }
 
-all_devices() {
-       cat /proc/net/dev | grep ":" | cut -d ':' -f 1 | while read dev
-       do
-               l=$($tc_cmd class show dev $dev | wc -l)
-               [ $l -ne 0 ] && echo $dev
-       done
+find_tc_devices() {
+    local count=0 devs= dev rest l
+
+    # find all the devices in the system
+    # without forking
+    while IFS=":| " read dev rest
+    do
+        count=$((count + 1))
+        [ ${count} -le 2 ] && continue
+        devs="${devs} ${dev}"
+    done </proc/net/dev
+
+    # from all the devices find the ones
+    # that have QoS defined
+    # unfortunately, one fork per device cannot be avoided
+    tc_devices=
+    for dev in ${devs}
+    do
+        l="$(${tc} class show dev ${dev} 2>/dev/null)"
+        [ ! -z "${l}" ] && tc_devices="${tc_devices} ${dev}"
+    done
 }
 
 # update devices and class names
 # once every 2 minutes
-names_every=$((120 / sleep_time))
+names_every=$((qos_get_class_names_every / update_every))
 
 # exit this script every hour
 # it will be restarted automatically
-exit_after=$((3600 / sleep_time))
+exit_after=$((qos_exit_every / update_every))
 
 c=0
 gc=0
 while [ 1 ]
 do
-       fix_names=
-       c=$((c + 1))
-       gc=$((gc + 1))
+    fix_names=
+    c=$((c + 1))
+    gc=$((gc + 1))
 
-       if [ $c -le 1 -o $c -ge $names_every ]
-       then
-               c=1
-               fix_names="YES"
-               devices="$( all_devices )"
-       fi
+    if [ ${c} -le 1 -o ${c} -ge ${names_every} ]
+    then
+        c=1
+        fix_names="YES"
+        find_tc_devices
+    fi
 
-       for d in $devices
-       do
-               show_tc $d
-       done
+    for d in ${tc_devices}
+    do
+        show_tc ${d}
+    done
 
-       echo "WORKTIME $LOOPSLEEPMS_LASTWORK"
+    echo "WORKTIME ${LOOPSLEEPMS_LASTWORK}"
 
-       loopsleepms $sleep_time
+    loopsleepms ${update_every}
 
-       [ $gc -gt $exit_after ] && exit 0
+    [ ${gc} -gt ${exit_after} ] && exit 0
 done