Scenario:
We have daily quota users as described here.
https://aacable.wordpress.com/2012/11/20/mikrotik-radius-manager-quota-base-service/
OP want to send alert when daily quota users crosses 70% of there allowed daily traffic quota. Since RM sends alert for TOTAL traffic only , not for daily, therefore I made following workaround.
The purpose of this script is to send SMS/Email alert to user who have consumed 70% of there daily allowed download/upload quota [value must be set in combined unit]. Once the user will use 70% of his allowed traffic, an SMS alert will be sent using local KANNEL SMS gateway and it will update flag in rm_users table which will prevent repetitive sms. only one sms alert will be sent in one day. once the date will be changed, the script will update the flags to 0, so that it will send sms alert again once the quota crosses the line again.
It may be scheduled to run after every 10 minutes or whatever the suitable interval according to your billing load.
Disclaimer:
Following is an LAB test version. It will generate many queries and may put burden on heavily production server. So make sure if you are using it, trim it and remove junk data before deploying in production.
Plus I know that its not an elegant way to perform this task. If it could be done via php/rm itself that would be best, but since RM is a protected system and we cannot modify it, therefore i was forced to take the ‘dirty workaround’ route to achieve the task. in production i will trim it to make sure it put minimum payload on the server. It took almost 3 days to make it work.
Copyright:
No part of this post is copied from any where. Its all made by myself. You are free to use/modify/share it as you like.
~ Syed Jahanzaib ~
#!/bin/bash #set -x TODAY=$(date +"%Y-%m-%d") TODAYTIME=$(date +"%Y-%m-%d %T") SQLUSER="root" SQLPASS="zaib1234" TMPUSERINFO="/tmp/username.txt" QUOTAPERCLIMIT="70" COMPANY="SYED JAHANZAIB" # Kannel SMS Gateway Details KHOST="your_kannel_host" KID="kannel" KPASS="kannel_password" > /tmp/username.txt > /tmp/tempuser.txt # Create QMAIL table if not exists QMAILCHECK=`mysql -u$SQLUSER -p$SQLPASS -e " SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'radius' AND TABLE_NAME = 'rm_users' AND COLUMN_NAME = 'qmail';"` if [ ! -z "$QMAILCHECK" ]; then echo "Step-1 Check QMAIL Column in rm_users ... QMAIL Column Found OK, proceeding further ..." else echo "QMAIL Column does NOT exists in rm_users table. it is required to prevent repeating email being sent to users, creating one NOW ..." mysql -u$SQLUSER -p$SQLPASS "use radius; ALTER TABLE rm_users ADD qmail TINYINT(1) NOT NULL;" mysql -u$SQLUSER -p$SQLPASS -e "use radius; ALTER TABLE rm_users ADD qmailtime DATETIME NOT NULL;" fi # Qurty Active Users list and store in it file mysql -u$SQLUSER -p$SQLPASS -e "use radius; SELECT SQL_CALC_FOUND_ROWS username, firstname, lastname, address, city, zip, country, state, phone, mobile, email, company, taxid, srvid, downlimit, uplimit, comblimit, expiration, uptimelimit, credits, comment, enableuser, staticipcpe, staticipcm, ipmodecpe, ipmodecm, srvname, limitdl, limitul, limitcomb, limitexpiration, limituptime, createdon, verifycode, verified, selfreg, acctype, maccm, LEFT(lastlogoff, 10) , IF (limitdl = 1, downlimit - COALESCE((SELECT SUM(acctoutputoctets) FROM radacct WHERE radacct.username = tmp.username) - (SELECT COALESCE(SUM(dlbytes), 0) FROM rm_radacct WHERE rm_radacct.username = tmp.username), 0), 0), IF (limitul = 1, uplimit - COALESCE((SELECT SUM(acctinputoctets) FROM radacct WHERE radacct.username = tmp.username) - (SELECT COALESCE(SUM(ulbytes), 0) FROM rm_radacct WHERE rm_radacct.username = tmp.username), 0), 0), IF (limitcomb =1, comblimit - COALESCE((SELECT SUM(acctinputoctets + acctoutputoctets) FROM radacct WHERE radacct.username = tmp.username) - (SELECT COALESCE(SUM(ulbytes + dlbytes), 0) FROM rm_radacct WHERE rm_radacct.username = tmp.username), 0), 0), IF (limituptime = 1, uptimelimit - COALESCE((SELECT SUM(acctsessiontime) FROM radacct WHERE radacct.username = tmp.username) - (SELECT COALESCE(SUM(acctsessiontime), 0) FROM rm_radacct WHERE rm_radacct.username = tmp.username), 0), 0) FROM ( SELECT username, firstname, lastname, address, city, zip, country, state, phone, mobile, email, company, taxid, rm_users.srvid, rm_users.downlimit, rm_users.uplimit, rm_users.comblimit, rm_users.expiration, rm_users.uptimelimit, credits, comment, enableuser, staticipcpe, staticipcm, ipmodecpe, ipmodecm, srvname, limitdl, limitul, limitcomb, limitexpiration, limituptime, createdon, verifycode, verified, selfreg, acctype, maccm, mac, groupid, contractid, contractvalid, rm_users.owner, srvtype, lastlogoff FROM rm_users JOIN rm_services USING (srvid) ORDER BY username ASC ) AS tmp WHERE 1 AND (tmp.acctype = '0' OR tmp.acctype = '2' OR tmp.acctype = '6' ) AND tmp.enableuser = 1 AND (limitdl = 0 OR IF (limitdl =1, downlimit - (SELECT COALESCE(SUM(acctoutputoctets), 0) FROM radacct WHERE radacct.username = tmp.username) - (SELECT COALESCE(SUM(dlbytes), 0) FROM rm_radacct WHERE rm_radacct.username = tmp.username) , 1) > 0) AND (limitul = 0 OR IF (limitul =1, uplimit - (SELECT COALESCE(SUM(acctinputoctets), 0) FROM radacct WHERE radacct.username = tmp.username) - (SELECT COALESCE(SUM(ulbytes ), 0) FROM rm_radacct WHERE rm_radacct.username = tmp.username) , 1) > 0) AND (limitcomb = 0 OR IF (limitcomb =1, comblimit - (SELECT COALESCE(SUM(acctinputoctets + acctoutputoctets), 0) FROM radacct WHERE radacct.username = tmp.username) + (SELECT COALESCE(SUM(ulbytes + dlbytes), 0) FROM rm_radacct WHERE rm_radacct.username = tmp.username) , 1) > 0) AND (limituptime = 0 OR IF (limituptime=1, uptimelimit - (SELECT COALESCE(SUM(acctsessiontime), 0) FROM radacct WHERE radacct.username = tmp.username) - (SELECT COALESCE(SUM(acctsessiontime), 0) FROM rm_radacct WHERE rm_radacct.username = tmp.username) , 1) > 0) AND (limitexpiration = 0 OR IF (limitexpiration=1, UNIX_TIMESTAMP(expiration) - UNIX_TIMESTAMP(NOW()), 1) > 0);" | awk '{print $1}' |awk 'NR > 1 { print }' > /tmp/tempuser.txt # REMOVE user which donot have any COMBLIMIT num=0 cat /tmp/tempuser.txt | while read users do num=$[$num+1] USERID=`echo $users | awk '{print $1}'` SRVID=`mysql -u$SQLUSER -p$SQLPASS -e "use radius; SELECT srvid FROM radius.rm_users WHERE rm_users.username = '$USERID';" |awk 'FNR == 2 {print $1}'` COMBLIMITCHECK=`mysql -u$SQLUSER -p$SQLPASS -e "use radius; SELECT limitcomb FROM rm_services WHERE srvid = '$SRVID';" |awk 'FNR == 2 {print $1}'` if [[ $COMBLIMITCHECK -eq "1" ]]; then echo "" > /dev/null #echo "$USERID have Quota limit = 1 , moving to correct file" echo "$USERID" >> /tmp/username.txt else echo "" > /dev/null #sed -i 's/\<$USERID\>//g' /tmp/username.txt fi done # Check if username.txt is empty , maybe no user is applicable to show or email have already been sent to them. so they will not appear, # Echo this info for admin info purposes. if [ -s /tmp/username.txt ]; then echo "" > /dev/null else echo "Maybe no user is applicable to show or email have already been sent to them. so they will not appear" fi # Apply Loop formula throught the rest of script / zaib num=0 cat /tmp/username.txt | while read users do num=$[$num+1] USERID=`echo $users | awk '{print $1}'` # Check if time is in between 00:00 till 00:10 , if YES, then maek qmail flag set to 0 so that email can be sent again. Clever😉 . ZAIB #CURHM=`date +%H:%M` #start="00:00" #end="00:10" #if [[ "$CURHM" > "$start" && "$CURHM" < "$end" ]]; then #echo "Time matches to reset FLAGS on qmail flag set to zero ...." #mysql -u$SQLUSER -p$SQLPASS -e "use radius; UPDATE rm_users SET qmail = 0 WHERE username = '$USERID';" #mysql -u$SQLUSER -p$SQLPASS -e "use radius; UPDATE rm_users SET qmailtime = '0000-00-00 00:00:00' WHERE username = '$USERID';" #fi TODAY=$(date +"%Y-%m-%d") TODAYTIME=$(date +"%Y-%m-%d %T") TOMORROW=`date --date='tomorrow' +%Y-%m-%d` # CHECK IF DATE IS CHANGED then CLEAR THE QMAIL FLAGS, otherwise ignore and continue LASTDEXEC=`cat /etc/lastupdate.txt` if [ "$TODAY" != "$LASTDEXEC" ]; then echo "ALERT: Date changed. clearing the flags .... " mysql -u$SQLUSER -p$SQLPASS -e "use radius; UPDATE rm_users SET qmail = 0 WHERE username = '$USERID';" mysql -u$SQLUSER -p$SQLPASS -e "use radius; UPDATE rm_users SET qmailtime = '0000-00-00 00:00:00' WHERE username = '$USERID';" fi #ZZZZZAIB QMAILTIME=`mysql -u$SQLUSER -p$SQLPASS -e "use radius; SELECT qmailtime FROM rm_users WHERE username = '$USERID';" |awk 'FNR == 2 {print $1,$2}'` #echo "$USERID vs $QMAILTIME vs $TODAY" #if [[ $QMAILTIME -eq $TODAY ]]; then #echo "SMS have already sent to $USERID for $TODAY !" #else #echo "" > /dev/null #fi SRVID=`mysql -u$SQLUSER -p$SQLPASS -e "use radius; SELECT srvid FROM radius.rm_users WHERE rm_users.username = '$USERID';" |awk 'FNR == 2 {print $1}'` SRVNAME=`mysql -u$SQLUSER -p$SQLPASS -e "use radius; SELECT srvname FROM radius.rm_services WHERE rm_services.srvid = '$SRVID';" |awk 'FNR == 2'` NEXTSRVID=`mysql -u$SQLUSER -p$SQLPASS -e "use radius; SELECT dailynextsrvid FROM radius.rm_services WHERE srvid = '$SRVID';" |awk 'FNR == 2 {print $1}'` NEXTSRVIDNAME=`mysql -u$SQLUSER -p$SQLPASS -e "use radius; SELECT srvname FROM radius.rm_services WHERE rm_services.srvid = '$NEXTSRVID';" |awk 'FNR == 2'` COMBQUOTA=`mysql -u$SQLUSER -p$SQLPASS -e "use radius; SELECT combquota FROM radius.rm_services WHERE srvid = '$SRVID';" |awk 'FNR == 2 {print $1}'` QMAIL=`mysql -u$SQLUSER -p$SQLPASS -e "use radius; SELECT qmail FROM radius.rm_users WHERE rm_users.username = '$USERID';" |awk 'FNR == 2 {print $1}'` EXPIRY=`mysql -u$SQLUSER -p$SQLPASS -e "use radius; SELECT expiration FROM radius.rm_users WHERE rm_users.username = '$USERID';" |awk 'FNR == 2 {print $1}'` # Query Today Download Dynamically TODAYDL=`mysql -u$SQLUSER -p$SQLPASS -e "use radius; SELECT SQL_CALC_FOUND_ROWS date, SUM(allbytesdl) - COALESCE(SUM(specbytesdl), 0), SUM(allbytesul) - COALESCE(SUM(specbytesul), 0), SUM(alltime) - COALESCE(SUM(spectime), 0) FROM ( SELECT LEFT(radacct.acctstarttime, 7) AS date, acctoutputoctets AS allbytesdl, SUM(dlbytes) AS specbytesdl, acctinputoctets AS allbytesul, SUM(ulbytes) AS specbytesul, radacct.acctsessiontime AS alltime, SUM(rm_radacct.acctsessiontime) AS spectime FROM radacct LEFT JOIN rm_radacct ON rm_radacct.radacctid = radacct.radacctid WHERE LEFT(radacct.acctstarttime, 4) LIKE '$1%' AND radacct.username LIKE '$USERID' AND radacct.acctstarttime > '$TODAY' AND radacct.acctstarttime < '$TOMORROW' AND FramedIPAddress LIKE '%' AND CallingStationId LIKE '%' GROUP BY radacct.radacctid ) AS tmp GROUP BY date LIMIT 0, 50;" |sed '1d' | awk '{ print $2 + $3 }'` # If user Download is Empty or Zero, set fake value of 111 so that percentage formula maynot make issues if [ ! -z "$TODAYDL" ]; then #TODAYDL="1000" echo "" else echo "" #No quota is used TODAY so using FAKE zero value so percentage value will not give errors." TODAYDL="111" fi # If downloaded data percentage is above then 70% then do action PERCENTUSED=$((100*$TODAYDL/$COMBQUOTA)) #if [[ $PERCENTUSED -gt 70 ]] if [ "$PERCENTUSED" -gt $QUOTAPERCLIMIT ] then echo " ----------------------------------------------- ID = $USERID QUOTA ALERT = $PERCENTUSED % SRVID = $SRVID NAME = $SRVNAME NEXT DAILY SERVICE = $NEXTSRVIDNAME TODAY DONWLOAD BYTES = $TODAYDL QUOTA LIMIT IN BYTES = $COMBQUOTA" echo "QUOTA ALLOWED = $(($COMBQUOTA / 1024 / 1024))" MB DLINMB=`echo "$TODAYDL/1024/1024" | bc` echo "Today Downloaded = $DLINMB MB" else # Otherwise just ECHO, do nothing echo " ----------------------------------------------- ID = $USERID QUOTA = OK, NOT USED / $PERCENTUSED % NAME = $SRVNAME Next Daily Service = $NEXTSRVIDNAME" if [ "$TODAYDL" -eq 111 ]; then echo "TODAYDL is empty so using fake value" fi #TODAYDL="1000" #echo "NEW VALUE is $TODAYDL" #else #TODAYDL="1000" #fi echo "TODAY DONWLOADED BYTES = $TODAYDL QUOTA LIMIT IN BYTES = $COMBQUOTA" echo "QUOTA ALLOWED = $(($COMBQUOTA / 1024 / 1024))" MB #echo "$TODAYDL/1024/1024" | bc fi # check if near quota users have already sent email, if fetched value is 1, then do nothing # else send email and update QMAIL flag in rm_users table ########## SENDING EMAIL if [[ $PERCENTUSED -gt $QUOTAPERCLIMIT && $QMAIL -eq 1 ]]; then echo "INFO: $USERID have consumed 70% or above quota and SMS have alreay been sent on $QMAILTIME -----------------------------------------------" fi if [[ $PERCENTUSED -gt $QUOTAPERCLIMIT && $QMAIL -eq 0 ]] then echo "Sending SMS Alert info to $USERID for Quota Alert ..." # Setting Variables for sending email and fetch other data DAILYLIMITINMB=`echo "$COMBQUOTA/1024/1024" | bc` MOBILE=`mysql -u$SQLUSER -p$SQLPASS -e "use radius; SELECT mobile FROM radius.rm_users WHERE rm_users.username = '$USERID';" |awk 'FNR == 2 {print $1}'` FIRSTNAME=`mysql -u$SQLUSER -p$SQLPASS -e "use radius; SELECT firstname FROM radius.rm_users WHERE rm_users.username = '$USERID';" |awk 'FNR == 2 {print $1}'` LASTNAME=`mysql -u$SQLUSER -p$SQLPASS -e "use radius; SELECT lastname FROM radius.rm_users WHERE rm_users.username = '$USERID';" |awk 'FNR == 2 {print $1}'` # Echo for Screen Print echo "Dear $FIRSTNAME $LASTNAME, Your internet account ID $USERID have consumed $QUOTAPERCLIMIT% of daily allowed quota that is $DAILYLIMITINMB MB. After this your speed will be reduced to $NEXTSRVIDNAME for current date. After current date change, You will be reverted back to $SRVNAME. Your account expiration date is $EXPIRY. Regard's $COMPANY" # Echo to save data inf ile which will be used later by KANNEL to send properly formatted message. echo "Dear $FIRSTNAME $LASTNAME, Your internet account ID $USERID have consumed $QUOTAPERCLIMIT% of daily allowed quota that is $DAILYLIMITINMB MB. After this your speed will be reduced to $NEXTSRVIDNAME for current date. After current date change, You will be reverted back to $SRVNAME. Your account expiration date is $EXPIRY. Regard's $COMPANY" > /tmp/$USERID.sms # Finally SENDING SMS using KANNEL SMS GATEWAY, you can use other functions as well : D ~ curl "http://$KHOST:13013/cgi-bin/sendsms?username=$KID&password=$KPASS&to=$MOBILE" -G --data-urlencode text@/tmp/$USERID.sms # Update mysql QMAIL flag so that system should not repeat sending emails # Make sure you run another script that should change the QMAIL flag to 0 after data cahnges mysql -u$SQLUSER -p$SQLPASS -e "use radius; UPDATE rm_users SET qmail = 1 WHERE username = '$USERID';" mysql -u$SQLUSER -p$SQLPASS -e "use radius; UPDATE rm_users SET qmailtime = '$TODAYTIME' WHERE username = '$USERID';" fi done # In the end UPDATE the last executed time that will be check on next script execution echo "$TODAY" > /etc/lastupdate.txt
Filed under: Linux Related, Radius Manager
