]> arthur.barton.de Git - netdata.git/blob - 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
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
16 # -----------------------------------------------------------------------------
17 # find /var/run/fireqos
18
19 # the default
20 fireqos_run_dir="/var/run/fireqos"
21
22 function realdir {
23     local r="$1"
24     local t=$(readlink "$r")
25
26     while [ "$t" ]
27         do
28         r=$(cd $(dirname "$r") && cd $(dirname "$t") && pwd -P)/$(basename "$t")
29         t=$(readlink "$r")
30     done
31
32     dirname "$r"
33 }
34
35 if [ ! -d "${fireqos_run_dir}" ]
36     then
37
38     # the fireqos executable - we will use it to find its config
39     fireqos="$(which fireqos 2>/dev/null || command -v fireqos 2>/dev/null)"
40
41     if [ ! -z "${fireqos}" ]
42         then
43
44         fireqos_exec_dir="$(realdir ${fireqos})"
45
46         if [ ! -z "${fireqos_exec_dir}" -a "${fireqos_exec_dir}" != "." -a -f "${fireqos_exec_dir}/install.config" ]
47             then
48
49             LOCALSTATEDIR=
50             source "${fireqos_exec_dir}/install.config"
51
52             if [ -d "${LOCALSTATEDIR}/run/fireqos" ]
53                 then
54                 fireqos_run_dir="${LOCALSTATEDIR}/run/fireqos"
55             fi
56         fi
57     fi
58 fi
59
60 # -----------------------------------------------------------------------------
61 # logging functions
62
63 PROGRAM_FILE="$0"
64 PROGRAM_NAME="$(basename $0)"
65 PROGRAM_NAME="${PROGRAM_NAME/.plugin}"
66
67 logdate() {
68     date "+%Y-%m-%d %H:%M:%S"
69 }
70
71 log() {
72     local status="${1}"
73     shift
74
75     echo >&2 "$(logdate): ${PROGRAM_NAME}: ${status}: ${*}"
76
77 }
78
79 warning() {
80     log WARNING "${@}"
81 }
82
83 error() {
84     log ERROR "${@}"
85 }
86
87 info() {
88     log INFO "${@}"
89 }
90
91 fatal() {
92     log FATAL "${@}"
93     exit 1
94 }
95
96 debug=0
97 debug() {
98     [ $debug -eq 1 ] && log DEBUG "${@}"
99 }
100
101
102 # -----------------------------------------------------------------------------
103
104 plugins_dir="${NETDATA_PLUGINS_DIR}"
105 [ -z "$plugins_dir" ] && plugins_dir="$( dirname $PROGRAM_FILE )"
106
107 config_dir=${NETDATA_CONFIG_DIR-/etc/netdata}
108 tc="$(which tc 2>/dev/null || command -v tc 2>/dev/null)"
109
110
111 # -----------------------------------------------------------------------------
112 # user configuration
113
114 # time in seconds to refresh QoS class/qdisc names
115 qos_get_class_names_every=120
116
117 # time in seconds to exit - netdata will restart the script
118 qos_exit_every=3600
119
120 # what to use? classes or qdiscs?
121 tc_show="qdisc" # can also be "class"
122
123
124 # -----------------------------------------------------------------------------
125 # check if we have a valid number for interval
126
127 t=${1}
128 update_every=$((t))
129 [ $((update_every)) -lt 1 ] && update_every=${NETDATA_UPDATE_EVERY}
130 [ $((update_every)) -lt 1 ] && update_every=1
131
132
133 # -----------------------------------------------------------------------------
134 # allow the user to override our defaults
135
136 if [ -f "${config_dir}/tc-qos-helper.conf" ]
137     then
138     source "${config_dir}/tc-qos-helper.conf"
139 fi
140
141 case "${tc_show}" in
142     qdisc|class)
143         ;;
144
145     *)
146         error "tc_show variable can be either 'qdisc' or 'class' but is set to '${tc_show}'. Assuming it is 'qdisc'."
147         tc_show="qdisc"
148         ;;
149 esac
150
151
152 # -----------------------------------------------------------------------------
153 # default sleep function
154
155 LOOPSLEEPMS_LASTWORK=0
156 loopsleepms() {
157     sleep $1
158 }
159
160 # if found and included, this file overwrites loopsleepms()
161 # with a high resolution timer function for precise looping.
162 . "${plugins_dir}/loopsleepms.sh.inc"
163
164
165 # -----------------------------------------------------------------------------
166 # final checks we can run
167
168 if [ -z "${tc}" -o ! -x "${tc}" ]
169     then
170     fatal "cannot find command 'tc' in this system."
171 fi
172
173 tc_devices=
174 fix_names=
175
176 # -----------------------------------------------------------------------------
177
178 setclassname() {
179     if [ "${tc_show}" = "qdisc" ]
180         then
181         echo "SETCLASSNAME $4 $2"
182     else
183         echo "SETCLASSNAME $3 $2"
184     fi
185 }
186
187 show_tc_cls() {
188     [ "${tc_show}" = "qdisc" ] && return 1
189
190     local x="${1}"
191
192     if [ -f /etc/iproute2/tc_cls ]
193     then
194         local classid name rest
195         while read classid name rest
196         do
197             [ -z "${classid}" -o -z "${name}" -o "${classid}" = "#" -o "${name}" = "#" -o "${classid:0:1}" = "#" -o "${name:0:1}" = "#" ] && continue
198             setclassname "" "${name}" "${classid}"
199         done </etc/iproute2/tc_cls
200         return 0
201     fi
202     return 1
203 }
204
205 show_fireqos_names() {
206     local x="${1}" name n interface_dev interface_classes interface_classes_monitor
207
208     if [ -f "${fireqos_run_dir}/ifaces/${x}" ]
209     then
210         name="$(<"${fireqos_run_dir}/ifaces/${x}")"
211         echo "SETDEVICENAME ${name}"
212
213         interface_dev=
214         interface_classes=
215         interface_classes_monitor=
216         source "${fireqos_run_dir}/${name}.conf"
217         for n in ${interface_classes_monitor}
218         do
219             setclassname ${n//|/ }
220         done
221         [ ! -z "${interface_dev}" ] && echo "SETDEVICEGROUP ${interface_dev}"
222
223         return 0
224     fi
225
226     return 1
227 }
228
229 show_tc() {
230     local x="${1}"
231
232     echo "BEGIN ${x}"
233
234     # netdata can parse the output of tc
235     ${tc} -s ${tc_show} show dev ${x}
236
237     # check FireQOS names for classes
238     if [ ! -z "${fix_names}" ]
239     then
240         show_fireqos_names "${x}" || show_tc_cls "${x}"
241     fi
242
243     echo "END ${x}"
244 }
245
246 find_tc_devices() {
247     local count=0 devs= dev rest l
248
249     # find all the devices in the system
250     # without forking
251     while IFS=":| " read dev rest
252     do
253         count=$((count + 1))
254         [ ${count} -le 2 ] && continue
255         devs="${devs} ${dev}"
256     done </proc/net/dev
257
258     # from all the devices find the ones
259     # that have QoS defined
260     # unfortunately, one fork per device cannot be avoided
261     tc_devices=
262     for dev in ${devs}
263     do
264         l="$(${tc} class show dev ${dev} 2>/dev/null)"
265         [ ! -z "${l}" ] && tc_devices="${tc_devices} ${dev}"
266     done
267 }
268
269 # update devices and class names
270 # once every 2 minutes
271 names_every=$((qos_get_class_names_every / update_every))
272
273 # exit this script every hour
274 # it will be restarted automatically
275 exit_after=$((qos_exit_every / update_every))
276
277 c=0
278 gc=0
279 while [ 1 ]
280 do
281     fix_names=
282     c=$((c + 1))
283     gc=$((gc + 1))
284
285     if [ ${c} -le 1 -o ${c} -ge ${names_every} ]
286     then
287         c=1
288         fix_names="YES"
289         find_tc_devices
290     fi
291
292     for d in ${tc_devices}
293     do
294         show_tc ${d}
295     done
296
297     echo "WORKTIME ${LOOPSLEEPMS_LASTWORK}"
298
299     loopsleepms ${update_every}
300
301     [ ${gc} -gt ${exit_after} ] && exit 0
302 done