After a lot of trial and error, i managed to create a (several actually) QoS script in which the throttling is volume based (GB).
The idea of the below is to configure your QoS settings via a txt file in which there are 2 levels QoS (LevelA and LevelB).
So you must create several simple queues (your master level queue and the child queue). Make sure at the total tab you select the total queue type as default.
The script will search for the comment of each queue (masterlevel, levelA, levelB) and it will throttle them according to their consumption.
So lets say that you have a 3TB pool. So the daily allowance is 100gb (3tb/30days)
At the first day of the month the throttling will happen quicker (because the allowance left will be little.) At lets say 15th of the month you will have 1.5GB allowance so you can perform more traffic till the QoS "kicks" in.
The QoS has 2 stages of throttling. The stage 1 where the throttling is smaller. The stage2 where the throttling is greater.
And that can be set via the QoSsettings.txt file at e.g config directory of files.
Also make sure to create the empty txt files at data/onbootconsumedbytes.txt and at data/monthlyconsumedbytes.txt
That will get you a more balanced QoS (according to volume per day of the month).
QoS sample file The levels are in GB of the data poll left for each queue (parent, levelA,levelB)
Be careful with the \n and \n\r (carriage return) of windows txt editors.
Code: Select all
levelAlow=20
levelAhigh=50
levelBlow=50
levelBhigh=80
levelparentlow=10
levelparenthigh=20
dailyallowance=100
levelAstage0=1G/1G
levelAstage1=200M/200M
levelAstage2=50M/50M
levelBstage0=1G/1G
levelBstage1=200M/200M
levelBstage2=50M/50M
levelparentstage0=1G/1G
levelparentstage1=200M/200M
levelparentstage2=50M/50M
Get config variables from file (it will be used as function for the other scripts so save it at scripts with name getvariablevalue)
Code: Select all
:local fileContents [/file get $2 contents]
# set a search variable that contains the variable name for the value to be extracted
:local variableName $1
# find the character offset for that variable name and add the length of the variable plus one for the = character, offset now points to character value
:local start [:find $fileContents $variableName ]
:set start ($start + [:len $variableName] +1)
# find the character offset for the next newline as that indicates the variable value end
:local end [:find $fileContents "\n" $start]
# extract that substring
:local value [:pick $fileContents $start $end]
:return $value
Script for scheduler to save the consumption for e.g per minute (+show the consumption at system note. Delete the last section if you like)
Code: Select all
# Variables
:local onbootbytes data/onbootconsumedbytes.txt
:local monthlybytes data/monthlyconsumedbytes.txt
:local getvariablevalue [:parse [/system script get getvariablevalue source]]
:local filevariables "configuration/QoSsettings.txt"
:local levelAhigh [$getvariablevalue levelAhigh $filevariables]
:local levelBhigh [$getvariablevalue levelBhigh $filevariables]
:local levelparenthigh [$getvariablevalue levelparenthigh $filevariables]
:local dailyallowance [$getvariablevalue dailyallowance $filevariables]
:set dailyallowance ($dailyallowance * 1073741824)
/queue simple
:foreach i in=[find] do={
:local sqName [get $i comment];
:local sqLevel [:find $sqName "masterlevel"];
:if ($sqLevel >= 0) do={
:local sqTotalBytes [get $i total-bytes];
:local storedconsumption [/file get $onbootbytes contents]
:local newconsumption ($sqTotalBytes - $storedconsumption)
:local monthlyconsumption [/file get $monthlybytes contents]
:local newmonthlyconsumption ($monthlyconsumption + $newconsumption)
/file set $onbootbytes contents=$sqTotalBytes
:delay 2
/file set $monthlybytes contents=$newmonthlyconsumption
#Get monthly comsumption and date of month.
:local datetime [/system clock get date];
:local date [:pick [$datetime] 8 10];
:local bandwidthallowance (($date * $dailyallowance) - $newmonthlyconsumption)
#Show the consumption at login and at terminal
/system note set note "Monthly Consumption is $($newmonthlyconsumption / 1073741824) GB\nConsumption since boot is $($sqTotalBytes / 1073741824) GB\nAllowance left before QoS kicks in is $(($bandwidthallowance / 1073741824) - $levelparenthigh) GB\nAllowance for Alevel left before QoS kicks in is $(($bandwidthallowance / 1073741824) - $levelAhigh) GB\nAllowance for Blevel left before QoS kicks in is $(($bandwidthallowance / 1073741824) - $levelBhigh) GB"
}
}
Throttling check (per minute for example via scheduler)
Code: Select all
#Import custom commands
:local getvariablevalue [:parse [/system script get getvariablevalue source]]
#Variables file location
:local filevariables "configuration/QoSsettings.txt"
# Level limits per gigabyte volume
:local levelAlow [$getvariablevalue levelAlow $filevariables]
:local levelAhigh [$getvariablevalue levelAhigh $filevariables]
:local levelBlow [$getvariablevalue levelBlow $filevariables]
:local levelBhigh [$getvariablevalue levelBhigh $filevariables]
:local levelparentlow [$getvariablevalue levelparentlow $filevariables]
:local levelparenthigh [$getvariablevalue levelparenthigh $filevariables]
# Bandwidth limits per level (upload/download)
:local levelAstage0 [$getvariablevalue levelAstage0 $filevariables]
:local levelAstage1 [$getvariablevalue levelAstage1 $filevariables]
:local levelAstage2 [$getvariablevalue levelAstage2 $filevariables]
:local levelBstage0 [$getvariablevalue levelBstage0 $filevariables]
:local levelBstage1 [$getvariablevalue levelBstage1 $filevariables]
:local levelBstage2 [$getvariablevalue levelBstage2 $filevariables]
:local levelparentstage0 [$getvariablevalue levelparentstage0 $filevariables]
:local levelparentstage1 [$getvariablevalue levelparentstage1 $filevariables]
:local levelparentstage2 [$getvariablevalue levelparentstage2 $filevariables]
:local monthlybytes data/monthlyconsumedbytes.txt
#Get daily allowance
:local dailyallowance [$getvariablevalue dailyallowance $filevariables]
:set dailyallowance ($dailyallowance * 1073741824)
#Get monthly comsumption and date of month.
:local datetime [/system clock get date];
:local date [put [:pick [$datetime] 8 10]];
:local storedconsumption [/file get $monthlybytes contents]
:local bandwidthallowance (($date * $dailyallowance) - $storedconsumption)
# Helper function to set the appropriate max-limit
:local setMaxLimit do={
:local queueId $1
:local currentStage $2
:local stage0 $3
:local stage1 $4
:local stage2 $5
:local low $6
:local high $7
:local bandwidthallowance $8
/queue simple
:if ($bandwidthallowance > ($low * 1073741824) && $bandwidthallowance < ($high * 1073741824) && $currentStage != $stage1) do={
set $queueId max-limit=$stage1;
} else={
:if ($bandwidthallowance <= ($low * 1073741824) && $currentStage != $stage2) do={
set $queueId max-limit=$stage2;
} else={
:if ($currentStage != $stage0 && $bandwidthallowance > ($high * 1073741824)) do={
set $queueId max-limit=$stage0;
}
}
}
}
#End of Helper function
# Process each simple queue
/queue simple
:foreach i in=[find] do={
:local sqName [get $i comment];
# Check for level A
:local sqLevel [:find $sqName "levelA"]
:if ($sqLevel >= 0) do={
:local currentStage [get $i max-limit];
$setMaxLimit $i $currentStage $levelAstage0 $levelAstage1 $levelAstage2 $levelAlow $levelAhigh $bandwidthallowance
} else={
# Check for level B
:local sqLevel [:find $sqName "levelB"];
:if ($sqLevel >= 0) do={
:local currentStage [get $i max-limit];
$setMaxLimit $i $currentStage $levelBstage0 $levelBstage1 $levelBstage2 $levelBlow $levelBhigh $bandwidthallowance
} else={
# Check for parent level
:local sqLevel [:find $sqName "masterlevel"];
:if ($sqLevel >= 0) do={
:local currentStage [get $i max-limit];
$setMaxLimit $i $currentStage $levelparentstage0 $levelparentstage1 $levelparentstage2 $levelparentlow $levelparenthigh $bandwidthallowance
}
}
}
}
Also there are 2 schedulers that must be created as well
on start scheduler:
Code: Select all
/file set data/onbootconsumedbytes.txt contents=0
daily scheduler to reset the monthly consumption at 01 day of the month:
Code: Select all
:local datetime [/system clock get date];
:local date [:pick [$datetime] 8 10];
:if ($date = "01") do={
/file set data/monthlyconsumedbytes.txt contents=0
}
So if you recognize parts of your code, please do mention it in this thread of course!
And if you have ideas on how to make it better, please do share!
Hope it will help some folks out there!