Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Common settings are to be modified in: `/etc/netdata/charts.d/sqm.conf`

### Values

- `sqm_ifc` - Modify to match the WAN interface where your SQM configuration is applied. [default: eth0]
- `sqm_ifc` - Modify to match the interface(s) where your SQM configuration is applied. Each interface names should be placed in quotes and separated by a space. e.g. for eth0 and eth1: `declare -a sqm_ifc=("eth0" "eth1")` [default: "eth0"]
- `sqm_priority` - Modify to change where the SQM chart appears in Netdata's web interface. [default: 90000]

## Screenshots
Expand Down
142 changes: 87 additions & 55 deletions sqm-chart/sqm.chart.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ sqm_update_every=

# global variables to store our collected data
# remember: they need to start with the module name sqm_
sqm_ifr="${sqm_ifc//[!0-9A-Za-z]/_}"

sqm_qdisc_bytes=0
sqm_qdisc_drops=0
sqm_qdisc_backlog=0

declare -a sqm_tns
# associative arrays
declare -A sqm_tns

# indexed arrays
declare -a sqm_tns_vals_traffic_kb
declare -a sqm_tns_vals_traffic_thres
declare -a sqm_tns_vals_latency_target
Expand All @@ -43,8 +44,9 @@ declare -a sqm_tns_vals_flows_unresponsive
# perform the query of tc to get qdisc output
sqm_query_tc() {
local jsn
local ifc="$1"

jsn=$(tc -s -j qdisc show dev $sqm_ifc) || return 1
jsn=$(tc -s -j qdisc show dev "$ifc") || return 1

# strip leading & trailing []
jsn="${jsn#[}" ; jsn="${jsn%]}"
Expand All @@ -62,7 +64,7 @@ sqm_set_overall() {
}

sqm_set_tins() {
local tin i
local tin i ifr

# empty all the arrays
sqm_tns_vals_traffic_kb=()
Expand All @@ -89,19 +91,22 @@ sqm_set_tins() {
# Discard the results from a stuck tin.
json_get_keys tins tins
json_select tins

ifr="${ifc//[!0-9A-Za-z]/_}"

i=0
for tin in $tins; do
json_select "$tin"
tn="${sqm_tns[i]}"
tn="${sqm_tns[$ifc]:$((i<<1)):2}"

json_get_vars threshold_rate sent_bytes sent_packets backlog_bytes target_us peak_delay_us avg_delay_us base_delay_us drops ecn_mark ack_drops sparse_flows bulk_flows unresponsive_flows

eval osp="\$osp${sqm_ifr}t${i}"
eval osp="\$osp${ifr}t${i}"
if [ "$osp" ] && [ "$osp" -eq "$sent_packets" ] ; then
peak_delay_us=0; avg_delay_us=0; base_delay_us=0
sparse_flows=0; bulk_flows=0; unresponsive_flows=0
else
eval "osp${sqm_ifr}t${i}=$sent_packets"
eval "osp${ifr}t${i}=$sent_packets"
fi

sqm_tns_vals_traffic_kb[i]=$sent_bytes
Expand All @@ -125,8 +130,11 @@ sqm_set_tins() {
}

sqm_create_overall() {
local ifc="$1"
local offset="$2"

cat << EOF
CHART "SQM.${sqm_ifc}_overview" 'overview' "SQM qdisc $sqm_ifc Overview" '' Qdisc '' line $((sqm_priority)) $sqm_update_every
CHART "SQM.${ifc}_overview" '' "SQM qdisc $ifc Overview" '' "${ifc} Qdisc" '' line $((sqm_priority + offset)) $sqm_update_every
DIMENSION 'bytes' 'Kb/s' incremental 1 125
DIMENSION 'backlog' 'Backlog/B' incremental 1 1
DIMENSION 'drops' 'Drops/s' incremental 1 1
Expand All @@ -135,21 +143,26 @@ EOF

sqm_create_tins() {
local tin i j
local ifc="$1"
local offset="$2"

# Options
json_select options
json_get_vars bandwidth diffserv
json_select ".."

case "$diffserv" in
besteffort)
sqm_tns[$ifc]="T0"
;;
diffserv3)
sqm_tns=("BK" "BE" "VI")
sqm_tns[$ifc]="BKBEVI"
;;
diffserv4)
sqm_tns=("BK" "BE" "VI" "VO")
sqm_tns[$ifc]="BKBEVIVO"
;;
*)
sqm_tns=("T0" "T1" "T2" "T3" "T4" "T5" "T6" "T7")
sqm_tns[$ifc]="T0T1T2T3T4T5T6T7"
;;
esac

Expand All @@ -162,24 +175,24 @@ sqm_create_tins() {
j=0
for tin in $tins; do
json_select "$tin"
tn="${sqm_tns[i]}"
tn="${sqm_tns[$ifc]:$((i<<1)):2}"

cat << EOF
CHART "SQM.${tn}_traffic" '' "CAKE $sqm_ifc $tn Traffic" 'Kb/s' $tn 'traffic' line $((sqm_priority + 1 + j)) $sqm_update_every
CHART "SQM.${ifc}_${tn}_traffic" '' "CAKE $ifc $tn Traffic" 'Kb/s' "${ifc} ${tn}" 'traffic' line $((sqm_priority + offset + 1 + j)) $sqm_update_every
DIMENSION 'bytes' 'Kb/s' incremental 1 125
DIMENSION 'thres' 'Thres' absolute 1 125
CHART "SQM.${tn}_latency" '' "CAKE $sqm_ifc $tn Latency" 'ms' $tn 'latency' line $((sqm_priority + 2 + j)) $sqm_update_every
CHART "SQM.${ifc}_${tn}_latency" '' "CAKE $ifc $tn Latency" 'ms' "${ifc} ${tn}" 'latency' line $((sqm_priority + offset + 2 + j)) $sqm_update_every
DIMENSION 'tg' 'Target' absolute 1 1000
DIMENSION 'pk' 'Peak' absolute 1 1000
DIMENSION 'av' 'Avg' absolute 1 1000
DIMENSION 'sp' 'Sparse' absolute 1 1000
CHART "SQM.${tn}_drops" '' "CAKE $sqm_ifc $tn Drops/s" 'Drops/s' $tn 'drops' line $((sqm_priority + 3 + j)) $sqm_update_every
CHART "SQM.${ifc}_${tn}_drops" '' "CAKE $ifc $tn Drops/s" 'Drops/s' "${ifc} ${tn}" 'drops' line $((sqm_priority + offset + 3 + j)) $sqm_update_every
DIMENSION 'ack' 'Ack' incremental 1 1
DIMENSION 'drops' 'Drops' incremental 1 1
DIMENSION 'ecn' 'Ecn' incremental 1 1
CHART "SQM.${tn}_backlog" '' "CAKE $sqm_ifc $tn Backlog" 'Bytes' $tn 'backlog' line $((sqm_priority + 4 + j)) $sqm_update_every
CHART "SQM.${ifc}_${tn}_backlog" '' "CAKE $ifc $tn Backlog" 'Bytes' "${ifc} ${tn}" 'backlog' line $((sqm_priority + offset + 4 + j)) $sqm_update_every
DIMENSION 'backlog' 'Backlog' absolute 1 1
CHART "SQM.${tn}_flows" '' "CAKE $sqm_ifc $tn Flow Counts" 'Flows' $tn 'flows' line $((sqm_priority + 5 + j)) $sqm_update_every
CHART "SQM.${ifc}_${tn}_flows" '' "CAKE $ifc $tn Flow Counts" 'Flows' "${ifc} ${tn}" 'flows' line $((sqm_priority + offset + 5 + j)) $sqm_update_every
DIMENSION 'sp' 'Sparse' absolute 1 1
DIMENSION 'bu' 'Bulk' absolute 1 1
DIMENSION 'un' 'Unresponsive' absolute 1 1
Expand All @@ -202,8 +215,9 @@ sqm_get() {
# 4. USE LOCAL VARIABLES (global variables may overlap with other modules)

local jsn
local ifc="$1"

jsn=$(sqm_query_tc) || return 1
jsn=$(sqm_query_tc "$ifc") || return 1

json_load "${jsn}"
json_get_var qdisc kind
Expand All @@ -217,7 +231,7 @@ sqm_get() {
sqm_set_overall
;;

*) echo "Unknown qdisc type '$qdisc' on interface '$sqm_ifc'" 1>&2
*) echo "Unknown qdisc type '$qdisc' on interface '$ifc'" 1>&2
return 1
;;
esac
Expand All @@ -236,37 +250,46 @@ sqm_check() {
# - 0 to enable the chart
# - 1 to disable the chart

# check that we can collect data
# check that we have the tc binary
require_cmd tc || return 1
sqm_query_tc &>/dev/null || return 1

# check that we can collect data for each interface
for ifc in "${sqm_ifc[@]}"; do
sqm_query_tc "$ifc" &>/dev/null || return 1
done

return 0
}

# _create is called once, to create the charts
sqm_create() {
local jsn

jsn=$(sqm_query_tc) || return 1

json_load "${jsn}"
json_get_var qdisc kind

case "$qdisc" in
cake)
sqm_create_overall
sqm_create_tins
;;
mq)
sqm_create_overall
local jsn ifc offset

# offset is the value for ensuring charts are incrementing for priority order
offset=0
for ifc in "${sqm_ifc[@]}"; do
jsn=$(sqm_query_tc "$ifc") || return 1

json_load "${jsn}"
json_get_var qdisc kind

case "$qdisc" in
cake)
sqm_create_overall "$ifc" "$offset"
sqm_create_tins "$ifc" "$offset"
;;
mq)
sqm_create_overall "$ifc" "$offset"
;;

*) echo "Unknown qdisc type '$qdisc' on interface '$ifc'" 1>&2
return 1
;;
esac

*) echo "Unknown qdisc type '$qdisc' on interface '$sqm_ifc'" 1>&2
return 1
;;
esac

json_cleanup
json_cleanup
offset=$((i+50))
done

return 0
}
Expand All @@ -276,45 +299,54 @@ sqm_update() {
# the first argument to this function is the microseconds since last update
# pass this parameter to the BEGIN statement (see bellow).

sqm_get || return 1
local ifc i

# write the result of the work.
cat << VALUESOF
BEGIN "SQM.${sqm_ifc}_overview" $1
for ifc in "${sqm_ifc[@]}"; do
sqm_get "$ifc" || return 1

# write the result of the work.
cat << VALUESOF
BEGIN "SQM.${ifc}_overview" $1
SET 'bytes' = $sqm_qdisc_bytes
SET 'backlog' = $sqm_qdisc_drops
SET 'drops' = $sqm_qdisc_backlog
END
VALUESOF

i=0
for tn in "${sqm_tns[@]}"; do
cat << VALUESOF
BEGIN "SQM.${tn}_traffic" $1
# get the number of tins as length of the tin
# string in sqm_tns for the given interface / 2
# since each tin is represented by a two char id
num_tins=$((${#sqm_tns[$ifc]} / 2))

for ((i=0; i<num_tins; i++)); do
tn="${sqm_tns[$ifc]:$((i<<1)):2}"

cat << VALUESOF
BEGIN "SQM.${ifc}_${tn}_traffic" $1
SET 'bytes' = ${sqm_tns_vals_traffic_kb[i]}
SET 'thres' = ${sqm_tns_vals_traffic_thres[i]}
END
BEGIN "SQM.${tn}_latency" $1
BEGIN "SQM.${ifc}_${tn}_latency" $1
SET 'tg' = ${sqm_tns_vals_latency_target[i]}
SET 'pk' = ${sqm_tns_vals_latency_peak[i]}
SET 'av' = ${sqm_tns_vals_latency_average[i]}
SET 'sp' = ${sqm_tns_vals_latency_sparse[i]}
END
BEGIN "SQM.${tn}_drops" $1
BEGIN "SQM.${ifc}_${tn}_drops" $1
SET 'ack' = ${sqm_tns_vals_drops_backlog_acks[i]}
SET 'drops' = ${sqm_tns_vals_drops_backlog_drops[i]}
SET 'ecn' = ${sqm_tns_vals_drops_backlog_ecn[i]}
END
BEGIN "SQM.${tn}_backlog" $1
BEGIN "SQM.${ifc}_${tn}_backlog" $1
SET 'backlog' = ${sqm_tns_vals_drops_backlog_backlog[i]}
END
BEGIN "SQM.${tn}_flows" $1
BEGIN "SQM.${ifc}_${tn}_flows" $1
SET 'sp' = ${sqm_tns_vals_flows_sparse[i]}
SET 'bu' = ${sqm_tns_vals_flows_bulk[i]}
SET 'un' = ${sqm_tns_vals_flows_unresponsive[i]}
END
VALUESOF
i=$((i+1))
done
done

return 0
Expand Down
4 changes: 2 additions & 2 deletions sqm-chart/sqm.conf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# the SQM interface to monitor
sqm_ifc=eth0
# the SQM interface(s) to monitor
declare -a sqm_ifc=("eth0")

# the priority is used to sort the charts on the dashboard
# 1 = the first chart
Expand Down