]> arthur.barton.de Git - netdata.git/blob - plugins.d/tc-qos-helper.sh
6dd9bfb59b29da4b3e5e70c167547b99d2eed265
[netdata.git] / plugins.d / tc-qos-helper.sh
1 #!/usr/bin/env bash
2
3 # netdata
4 # real-time performance and health monitoring, done right!
5 # (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
6 # GPL v3+
7 #
8 # This script is a helper to allow netdata collect tc data.
9 # tc output parsing has been implemented in C, inside netdata
10 # This script allows setting names to dimensions.
11
12 export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/sbin"
13 export LC_ALL=C
14
15 PROGRAM_FILE="$0"
16 PROGRAM_NAME="$(basename $0)"
17 PROGRAM_NAME="${PROGRAM_NAME/.plugin}"
18
19 # -----------------------------------------------------------------------------
20
21 logdate() {
22     date "+%Y-%m-%d %H:%M:%S"
23 }
24
25 log() {
26     local status="${1}"
27     shift
28
29     echo >&2 "$(logdate): ${PROGRAM_NAME}: ${status}: ${*}"
30
31 }
32
33 warning() {
34     log WARNING "${@}"
35 }
36
37 error() {
38     log ERROR "${@}"
39 }
40
41 info() {
42     log INFO "${@}"
43 }
44
45 fatal() {
46     log FATAL "${@}"
47     exit 1
48 }
49
50 debug=0
51 debug() {
52     [ $debug -eq 1 ] && log DEBUG "${@}"
53 }
54
55 # -----------------------------------------------------------------------------
56
57 plugins_dir="${NETDATA_PLUGINS_DIR}"
58 [ -z "$plugins_dir" ] && plugins_dir="$( dirname $PROGRAM_FILE )"
59
60 config_dir=${NETDATA_CONFIG_DIR-/etc/netdata}
61 tc="$(which tc 2>/dev/null || command -v tc 2>/dev/null)"
62 fireqos_run_dir="/var/run/fireqos"
63 qos_get_class_names_every=120
64 qos_exit_every=3600
65
66 tc_show="qdisc" # can also be "class"
67
68 # check if we have a valid number for interval
69 t=${1}
70 update_every=$((t))
71 [ $((update_every)) -lt 1 ] && update_every=${NETDATA_UPDATE_EVERY}
72 [ $((update_every)) -lt 1 ] && update_every=1
73
74 # allow the user to override our defaults
75 if [ -f "${config_dir}/tc-qos-helper.conf" ]
76     then
77     source "${config_dir}/tc-qos-helper.conf"
78 fi
79
80 case "${tc_show}" in
81     qdisc|class)
82         ;;
83
84     *)
85         error "tc_show variable can be either 'qdisc' or 'class' but is set to '${tc_show}'. Assuming it is 'qdisc'."
86         tc_show="qdisc"
87         ;;
88 esac
89
90 # default sleep function
91 LOOPSLEEPMS_LASTWORK=0
92 loopsleepms() {
93     sleep $1
94 }
95
96 # if found and included, this file overwrites loopsleepms()
97 # with a high resolution timer function for precise looping.
98 . "${plugins_dir}/loopsleepms.sh.inc"
99
100 if [ -z "${tc}" -o ! -x "${tc}" ]
101     then
102     fatal "cannot find command 'tc' in this system."
103 fi
104
105 tc_devices=
106 fix_names=
107
108 setclassname() {
109     if [ "${tc_show}" = "qdisc" ]
110         then
111         echo "SETCLASSNAME $4 $2"
112     else
113         echo "SETCLASSNAME $3 $2"
114     fi
115 }
116
117 show_tc_cls() {
118     [ "${tc_show}" = "qdisc" ] && return 1
119
120     local x="${1}"
121
122     if [ -f /etc/iproute2/tc_cls ]
123     then
124         local classid name rest
125         while read classid name rest
126         do
127             [ -z "${classid}" -o -z "${name}" -o "${classid}" = "#" -o "${name}" = "#" -o "${classid:0:1}" = "#" -o "${name:0:1}" = "#" ] && continue
128             setclassname "" "${name}" "${classid}"
129         done </etc/iproute2/tc_cls
130         return 0
131     fi
132     return 1
133 }
134
135 show_fireqos_names() {
136     local x="${1}" name n interface_dev interface_classes interface_classes_monitor
137
138     if [ -f "${fireqos_run_dir}/ifaces/${x}" ]
139     then
140         name="$(<"${fireqos_run_dir}/ifaces/${x}")"
141         echo "SETDEVICENAME ${name}"
142
143         interface_dev=
144         interface_classes=
145         interface_classes_monitor=
146         source "${fireqos_run_dir}/${name}.conf"
147         for n in ${interface_classes_monitor}
148         do
149             setclassname ${n//|/ }
150         done
151         [ ! -z "${interface_dev}" ] && echo "SETDEVICEGROUP ${interface_dev}"
152
153         return 0
154     fi
155
156     return 1
157 }
158
159 show_tc() {
160     local x="${1}"
161
162     echo "BEGIN ${x}"
163
164     # netdata can parse the output of tc
165     ${tc} -s ${tc_show} show dev ${x}
166
167     # check FireQOS names for classes
168     if [ ! -z "${fix_names}" ]
169     then
170         show_fireqos_names "${x}" || show_tc_cls "${x}"
171     fi
172
173     echo "END ${x}"
174 }
175
176 find_tc_devices() {
177     local count=0 devs= dev rest l
178
179     # find all the devices in the system
180     # without forking
181     while IFS=":| " read dev rest
182     do
183         count=$((count + 1))
184         [ ${count} -le 2 ] && continue
185         devs="${devs} ${dev}"
186     done </proc/net/dev
187
188     # from all the devices find the ones
189     # that have QoS defined
190     # unfortunately, one fork per device cannot be avoided
191     tc_devices=
192     for dev in ${devs}
193     do
194         l="$(${tc} class show dev ${dev} 2>/dev/null)"
195         [ ! -z "${l}" ] && tc_devices="${tc_devices} ${dev}"
196     done
197 }
198
199 # update devices and class names
200 # once every 2 minutes
201 names_every=$((qos_get_class_names_every / update_every))
202
203 # exit this script every hour
204 # it will be restarted automatically
205 exit_after=$((qos_exit_every / update_every))
206
207 c=0
208 gc=0
209 while [ 1 ]
210 do
211     fix_names=
212     c=$((c + 1))
213     gc=$((gc + 1))
214
215     if [ ${c} -le 1 -o ${c} -ge ${names_every} ]
216     then
217         c=1
218         fix_names="YES"
219         find_tc_devices
220     fi
221
222     for d in ${tc_devices}
223     do
224         show_tc ${d}
225     done
226
227     echo "WORKTIME ${LOOPSLEEPMS_LASTWORK}"
228
229     loopsleepms ${update_every}
230
231     [ ${gc} -gt ${exit_after} ] && exit 0
232 done