local mymodule = {} -- Load libraries modelfunctions = require("modelfunctions") posix = require("posix") fs = require("acf.fs") format = require("acf.format") -- Set variables local packagename = "iproute2-qos" local initscript = "qos" local initfile = "/etc/init.d/"..initscript local conffile = "/etc/conf.d/qos" local conffilesample = "/etc/conf.d/qos.eth0.sample" local path = "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin " local ints -- ################################################################################ -- LOCAL FUNCTIONS local function list_interfaces(self) if not ints then local interfacescontroller = self:new("alpine-baselayout/interfaces") local interfaces = interfacescontroller.model.get_interfaces() interfacescontroller:destroy() ints = interfaces.value end return ints end local function validate_filename(filename) return string.find(filename, "^"..conffile.."%.%w+$") end -- modify /etc/network/interfaces with the up/down lines for ifb device local function modify_interfaces(self, DEV, IFB_DEV) local up = "ip link set ifb%d+ up" local down = "ip link set ifb%d+ down" local interfacescontroller = self:new("alpine-baselayout/interfaces") local interface = interfacescontroller.model.get_iface_by_name(interfacescontroller, {name=DEV}) interface.value.up.value = string.gsub(interface.value.up.value, up, "") interface.value.down.value = string.gsub(interface.value.down.value, down, "") if IFB_DEV ~= "" then interface.value.up.value = string.gsub(up, "ifb%%d%+", IFB_DEV) .. "\n" .. interface.value.up.value interface.value.down.value = string.gsub(down, "ifb%%d%+", IFB_DEV) .. "\n" .. interface.value.down.value end interfacescontroller.model.update_iface(interfacescontroller, interface) interfacescontroller:destroy() end -- ################################################################################ -- PUBLIC FUNCTIONS function mymodule.get_startstop(self, clientdata) return modelfunctions.get_startstop(clientdata.init) end function mymodule.startstop_service(self, startstop, action) return modelfunctions.startstop_service(startstop, action) end function mymodule.getstatus() return modelfunctions.getstatus(nil, packagename, "QOS Status") end function mymodule.listinterfacedetails(self) local result = {} local interface for i,int in ipairs(list_interfaces(self)) do if interface ~= int then local temp = {interface=int, init=initscript.."."..int, filename=conffile.."."..int, enabled=true} local status = modelfunctions.getenabled(temp.init) temp.status = status.value if status.errtxt then temp.enabled = false end result[#result+1] = temp end end table.sort(result, function(a,b) return a.interface < b.interface end) return cfe({ type="structure", value=result, label="QOS Interface List" }) end function mymodule.get_enable(self, clientdata) local retval = {} retval.interface = cfe({ value=clientdata.interface or "", label="Interface" }) return cfe({ type="group", value=retval, label="Enable QOS on Interface" }) end function mymodule.enable(self, int_enable) local interface = int_enable.value.interface.value local init = initfile.."."..interface int_enable.errtxt = "Failed to enable QOS" if not posix.stat(initfile) then int_enable.value.interface.errtxt = initfile.." does not exist" elseif posix.stat(init) then int_enable.value.interface.errtxt = init.." already exists" else posix.link(initfile, init, true) local conf = string.gsub(init, "init%.d", "conf.d") if not posix.stat(conf) then local filecontent = fs.read_file(conffilesample) or "" filecontent = format.update_ini_file(filecontent, "", "DEV", interface) if string.find(interface, "^ifb") then filecontent = format.update_ini_file(filecontent, "", "IFB_DEV", "") end fs.write_file(conf, filecontent) end local IFB_DEV = format.parse_ini_file(fs.read_file(conf) or "", "", "IFB_DEV") or "" modify_interfaces(self, interface, IFB_DEV) int_enable.errtxt = nil end return int_enable end function mymodule.get_filedetails(self, clientdata) return modelfunctions.getfiledetails(clientdata.filename, validate_filename) end function mymodule.update_filedetails(self, filedetails) return modelfunctions.setfiledetails(self, filedetails, validate_filename) end function mymodule.get_config(self, clientdata) local interface = clientdata.DEV local config = {} local ifbs = {""} if not string.find(interface, "^ifb") then for i,int in ipairs(list_interfaces(self)) do if string.find(int, "^ifb") then ifbs[#ifbs+1] = int end end end local configfile = conffile.."."..interface local configopts = format.parse_ini_file(fs.read_file(configfile) or "", "") or {} config.DEV = cfe({ value=configopts.DEV or interface, label="Network device", readonly=true, seq=1 }) config.DEV_RATE = cfe({ value=configopts.DEV_RATE or "", label="Device Rate", descr="Actual speed of physical device (units: mbps, mbit, kbit, kbps, bps)", seq=2 }) config.INGRESS_ALG = cfe({ type="select", value=configopts.INGRESS_ALG or "none", label="Ingress Algorithm", option={"police", "cpolice", "ifb", "none"}, seq=3 }) config.IFB_DEV = cfe({ type="select", value=configopts.IFB_DEV or "", label="Inbound IFB device", option=ifbs, seq=4 }) config.INGRESS_RATE = cfe({ value=configopts.INGRESS_RATE or "", label="Ingress rate", descr="(units: mbps, mbit, kbit, kbps, bps)", seq=5 }) config.EGRESS_ALG = cfe({ type="select", value=configopts.EGRESS_ALG or "none", label="Egress Algorithm", option={"htb", "hfsc", "prio", "none"}, seq=6 }) config.EGRESS_RATE = cfe({ value=configopts.EGRESS_RATE or "", label="Egress rate", descr="(units: mbps, mbit, kbit, kbps, bps)", seq=7 }) config.DEV.errtxt = "Invalid device" for i,int in ipairs(list_interfaces(self)) do if int == interface then config.DEV.errtxt = nil break end end return cfe({ type="group", value=config, label=interface.." QOS Config" }) end function mymodule.update_config(self, config) local success = false config.value.DEV.errtxt = "Invalid device" for i,int in ipairs(list_interfaces(self)) do if int == config.value.DEV.value then config.value.DEV.errtxt = nil success = true break end end success = modelfunctions.validateselect(config.value.INGRESS_ALG) and success success = modelfunctions.validateselect(config.value.IFB_DEV) and success success = modelfunctions.validateselect(config.value.EGRESS_ALG) and success local rates = { mbps=1, mbit=1, kbit=1, kbps=1, bps=1, [""]=1 } -- last entry allows for no units for i,rate in ipairs({"DEV_RATE", "INGRESS_RATE", "EGRESS_RATE"}) do if config.value[rate] and ( not string.find(config.value[rate].value, "^%s*%d+%s*%l*%s*$") or not rates[string.match(config.value[rate].value, "(%l*)")] ) then config.value[rate].errtxt = "Invalid rate" success = false end end -- ifb device should have nothing defined for ingress if string.find(config.value.DEV.value, "^ifb") then if config.value.IFB_DEV.value ~= "" then config.value.IFB_DEV.errtxt = "Must be undefined for ifb" success = false end if config.value.INGRESS_ALG.value ~= "none" then config.value.INGRESS_ALG.errtxt = "Must be 'none' for ifb" success = false end elseif config.value.INGRESS_ALG.value == "ifb" then if config.value.IFB_DEV.value == "" then config.value.IFB_DEV.errtxt = "Must define ifb device when using ifb ingress algorithm" success = false end elseif config.value.IFB_DEV.value ~= "" then config.value.IFB_DEV.errtxt = "Must be undefined if not using ifb ingress algorithm" success = false end if string.find(config.value.DEV.value, "^ifb") or config.value.INGRESS_ALG.value == "ifb" then if tonumber(string.match(config.value.INGRESS_RATE.value, "%d+") or "") ~= 0 then config.value.INGRESS_RATE.errtxt = "Must be 0 for ifb" success = false end end if success then local configfile = conffile.."."..config.value.DEV.value local contents = fs.read_file(configfile) contents = format.update_ini_file(contents, "", "DEV", config.value.DEV.value) contents = format.update_ini_file(contents, "", "DEV_RATE", config.value.DEV_RATE.value) contents = format.update_ini_file(contents, "", "INGRESS_ALG", config.value.INGRESS_ALG.value) contents = format.update_ini_file(contents, "", "IFB_DEV", config.value.IFB_DEV.value) contents = format.update_ini_file(contents, "", "INGRESS_RATE", config.value.INGRESS_RATE.value) contents = format.update_ini_file(contents, "", "EGRESS_ALG", config.value.EGRESS_ALG.value) contents = format.update_ini_file(contents, "", "EGRESS_RATE", config.value.EGRESS_RATE.value) fs.write_file(configfile, contents) -- need to set lines in /etc/network/interfaces modify_interfaces(self, config.value.DEV.value, config.value.IFB_DEV.value) else config.errtxt = "Failed to set config" end return config end return mymodule