Commit e4580cd9 authored by Ted Trask's avatar Ted Trask

First cut at ACF for iproute2-qos

parents
APP_NAME=iproute2-qos
PACKAGE=acf-$(APP_NAME)
VERSION=0.1.0
APP_DIST=\
qos* \
EXTRA_DIST=README Makefile config.mk
DISTFILES=$(APP_DIST) $(EXTRA_DIST)
TAR=tar
P=$(PACKAGE)-$(VERSION)
tarball=$(P).tar.bz2
install_dir=$(DESTDIR)/$(appdir)/$(APP_NAME)
all:
clean:
rm -rf $(tarball) $(P)
dist: $(tarball)
install:
mkdir -p "$(install_dir)"
cp -a $(APP_DIST) "$(install_dir)"
$(tarball): $(DISTFILES)
rm -rf $(P)
mkdir -p $(P)
cp -a $(DISTFILES) $(P)
$(TAR) -jcf $@ $(P)
rm -rf $(P)
# target that creates a tar package, unpacks is and install from package
dist-install: $(tarball)
$(TAR) -jxf $(tarball)
$(MAKE) -C $(P) install DESTDIR=$(DESTDIR)
rm -rf $(P)
include config.mk
.PHONY: all clean dist install dist-install
ACF for iproute2-qos
prefix=/usr
datadir=${prefix}/share
sysconfdir=${prefix}/etc
localstatedir=${prefix}/var
acfdir=${datadir}/acf
wwwdir=${acfdir}/www
cgibindir=${acfdir}/cgi-bin
appdir=${acfdir}/app
acflibdir=${acfdir}/lib
sessionsdir=${localstatedir}/lib/acf/sessions
<% local form, viewlibrary, page_info = ...
require("viewfunctions")
%>
<H1><%= html.html_escape(form.label) %></H1>
<%
local order = {"DEV", "DEV_RATE", "INGRESS_ALG", "IFB_DEV", "INGRESS_RATE", "EGRESS_ALG", "EGRESS_RATE"}
form.value.DEV.readonly = true
displayform(form, order, nil, page_info)
%>
module(..., package.seeall)
-- Load libraries
require("controllerfunctions")
default_action = "status"
function status(self)
return self.model.getstatus()
end
function startstop(self)
return controllerfunctions.handle_startstop(self, function(action) return self.model.startstop_service(self.clientdata.init, action) end, self.clientdata)
end
function details(self)
return self.model.listinterfacedetails(self)
end
function enable(self)
return self:redirect_to_referrer(self.model.enable(self.clientdata.interface))
end
function config(self)
return controllerfunctions.handle_form(self, function() return self.model.get_config(self.clientdata.DEV) end, self.model.update_config, self.clientdata, "Save", "Edit QOS Config", "Configuration Set")
end
function expert(self)
return controllerfunctions.handle_form(self, function() return self.model.get_filedetails(self.clientdata.interface) end, self.model.update_filedetails, self.clientdata, "Save", "Edit QOS Config", "Configuration Set")
end
<% local data, viewlibrary, page_info, session = ...
require("viewfunctions")
%>
<% displaycommandresults({"enable","config","expert","startstop"}, session) %>
<% viewlibrary.dispatch_component("status") %>
<H2><%= html.html_escape(data.label) %></H2>
<TABLE>
<TR style="background:#eee;font-weight:bold;">
<TD style="padding-right:20px;white-space:nowrap;text-align:left;" class="header">Action</TD>
<TD style="padding-right:20px;white-space:nowrap;text-align:left;" class="header">Interface</TD>
<TD style="padding-right:20px;white-space:nowrap;text-align:left;" class="header">Status</TD>
<TD style="white-space:nowrap;text-align:left;" class="header"></TD>
</TR>
<% for i,intf in ipairs(data.value) do %>
<TR>
<TD style="padding-right:20px;white-space:nowrap;">
<% if intf.enabled then %>
<% io.write(html.link{value = "config?DEV="..intf.interface.."&redir="..page_info.orig_action, label="Edit " }) %>
<% io.write(html.link{value = "expert?interface="..intf.interface.."&redir="..page_info.orig_action, label="Expert " }) %>
<% io.write(html.link{value = page_info.script .. "/alpine-baselayout/rc/edit?servicename="..intf.init.."&redir=".. page_info.orig_action, label="Autostart " }) %>
<% else %>
<% io.write(html.link{value = "enable?interface="..intf.interface.."&redir="..page_info.orig_action, label="Enable " }) %>
<% end %>
</TD>
<TD style="padding-right:20px;white-space:nowrap;"><%= html.html_escape(intf.interface) %></TD>
<TD style="white-space:nowrap;" width="90%"><%= html.html_escape(intf.status) %></TD>
<% if intf.enabled then %>
<TD><form action="<%= html.html_escape(page_info.script .. page_info.prefix .. page_info.controller .. "/startstop") %>" method="POST">
<input type="hidden" name="init" value="<%= intf.init %>">
<input class="submit" type="submit" name="action" value="Start">
<input class="submit" type="submit" name="action" value="Stop">
<input class="submit" type="submit" name="action" value="Restart">
</form></TD>
<% end %>
</TR>
<% end %>
</TABLE>
../filedetails-html.lsp
\ No newline at end of file
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
../status-html.lsp
\ No newline at end of file
#CAT GROUP/DESC TAB ACTION
Networking 15QOS Status details
USER=qos:status,qos:details,qos:startstop,qos:config
EXPERT=qos:enable,qos:expert
ADMIN=qos:status,qos:details,qos:startstop,qos:config,qos:enable,qos:expert
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment