module(..., package.seeall) -- Load libraries require("modelfunctions") require("fs") require("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() if not ints then local interfacescontroller = APP: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(DEV, IFB_DEV) local up = "ip link set ifb%d+ up" local down = "ip link set ifb%d+ down" local interfacescontroller = APP:new("alpine-baselayout/interfaces") local interface = interfacescontroller.model.get_iface_by_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(interface) interfacescontroller:destroy() end -- ################################################################################ -- PUBLIC FUNCTIONS function startstop_service(processname, action) return modelfunctions.startstop_service(processname, action) end function getstatus() local status = {} local value, errtxt = processinfo.package_version(packagename) status.version = cfe({ label="Program version", value=value, errtxt=errtxt, name=packagename }) return cfe({ type="group", value=status, label="QOS Status" }) end function listinterfacedetails() local result = {} local interface for i,int in ipairs(list_interfaces()) do if interface ~= int then local temp = {interface=int, init=initscript.."."..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 enable(interface) local retval = cfe({ label="Enable QOS Result" }) local init = initfile.."."..interface if not posix.stat(initfile) then retval.errtxt = initfile.." does not exist" elseif posix.stat(init) then retval.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 "") or "" modify_interfaces(interface, IFB_DEV) retval.value = "Enabled QOS on Interface" end return retval end function get_filedetails(interface) return modelfunctions.getfiledetails(conffile.."."..interface, validate_filename) end function update_filedetails(filedetails) return modelfunctions.setfiledetails(filedetails, validate_filename) end function get_config(interface) local config = {} local ifbs = {""} if not string.find(interface, "^ifb") then for i,int in ipairs(list_interfaces()) 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" }) config.DEV_RATE = cfe({ value=configopts.DEV_RATE or "", label="Device Rate", descr="Actual speed of physical device (units: mbps, mbit, kbit, kbps, bps)"}) config.INGRESS_ALG = cfe({ type="select", value=configopts.INGRESS_ALG or "none", label="Ingress Algorithm", option={"police", "cpolice", "ifb", "none"} }) config.IFB_DEV = cfe({ type="select", value=configopts.IFB_DEV or "", label="Inbound IFB device", option=ifbs }) config.INGRESS_RATE = cfe({ value=configopts.INGRESS_RATE or "", label="Ingress rate", descr="(units: mbps, mbit, kbit, kbps, bps)"}) config.EGRESS_ALG = cfe({ type="select", value=configopts.EGRESS_ALG or "none", label="Egress Algorithm", option={"htb", "hfsc", "prio", "none"} }) config.EGRESS_RATE = cfe({ value=configopts.EGRESS_RATE or "", label="Egress rate", descr="(units: mbps, mbit, kbit, kbps, bps)"}) config.DEV.errtxt = "Invalid device" for i,int in ipairs(list_interfaces()) do if int == interface then config.DEV.errtxt = nil break end end return cfe({ type="group", value=config, label=interface.." QOS Config" }) end function update_config(config) local success = false config.value.DEV.errtxt = "Invalid device" for i,int in ipairs(list_interfaces()) 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(config.value.DEV.value, config.value.IFB_DEV.value) else config.errtxt = "Failed to set config" end return config end