123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315 |
- #!/usr/bin/env bash
- # netdata
- # real-time performance and health monitoring, done right!
- # (C) 2017 Costa Tsaousis <costa@tsaousis.gr>
- # SPDX-License-Identifier: GPL-3.0-or-later
- #
- # 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
- # -----------------------------------------------------------------------------
- # 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 "${@}"
- }
- # -----------------------------------------------------------------------------
- # find /var/run/fireqos
- # the default
- fireqos_run_dir="/var/run/fireqos"
- function realdir {
- local r="$1"
- local t=$(readlink "$r")
- 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"
- else
- warning "FireQoS is installed as '${fireqos}', its installation config at '${fireqos_exec_dir}/install.config' specifies local state data at '${LOCALSTATEDIR}/run/fireqos', but this directory is not found or is not readable (check the permissions of its parents)."
- fi
- else
- warning "Although FireQoS is installed on this system as '${fireqos}', I cannot find/read its installation configuration at '${fireqos_exec_dir}/install.config'."
- fi
- else
- warning "FireQoS is not installed on this system. Use FireQoS to apply traffic QoS and expose the class names to netdata. Check https://github.com/netdata/netdata/tree/master/collectors/tc.plugin#tcplugin"
- fi
- fi
- # -----------------------------------------------------------------------------
- [ -z "${NETDATA_PLUGINS_DIR}" ] && NETDATA_PLUGINS_DIR="$(dirname "${0}")"
- [ -z "${NETDATA_USER_CONFIG_DIR}" ] && NETDATA_USER_CONFIG_DIR="@configdir_POST@"
- [ -z "${NETDATA_STOCK_CONFIG_DIR}" ] && NETDATA_STOCK_CONFIG_DIR="@libconfigdir_POST@"
- plugins_dir="${NETDATA_PLUGINS_DIR}"
- 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
- for CONFIG in "${NETDATA_STOCK_CONFIG_DIR}/tc-qos-helper.conf" "${NETDATA_USER_CONFIG_DIR}/tc-qos-helper.conf"
- do
- if [ -f "${CONFIG}" ]
- then
- info "Loading config file '${CONFIG}'..."
- source "${CONFIG}"
- [ $? -ne 0 ] && error "Failed to load config file '${CONFIG}'."
- else
- warning "Cannot find file '${CONFIG}'."
- fi
- done
- 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() {
- sleep $1
- }
- # if found and included, this file overwrites loopsleepms()
- # with a high resolution timer function for precise looping.
- . "${plugins_dir}/loopsleepms.sh.inc"
- # -----------------------------------------------------------------------------
- # final checks we can run
- if [ -z "${tc}" -o ! -x "${tc}" ]
- then
- fatal "cannot find command 'tc' in this system."
- fi
- tc_devices=
- fix_names=
- # -----------------------------------------------------------------------------
- setclassname() {
- 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}"
- # 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}"
- }
- 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=$((qos_get_class_names_every / update_every))
- # exit this script every hour
- # it will be restarted automatically
- exit_after=$((qos_exit_every / update_every))
- c=0
- gc=0
- while [ 1 ]
- do
- fix_names=
- c=$((c + 1))
- gc=$((gc + 1))
- if [ ${c} -le 1 -o ${c} -ge ${names_every} ]
- then
- c=1
- fix_names="YES"
- find_tc_devices
- fi
- for d in ${tc_devices}
- do
- show_tc ${d}
- done
- echo "WORKTIME ${LOOPSLEEPMS_LASTWORK}"
- loopsleepms ${update_every}
- [ ${gc} -gt ${exit_after} ] && exit 0
- done
|