diff options
author | Ted Trask <ttrask01@yahoo.com> | 2009-09-17 16:18:32 +0000 |
---|---|---|
committer | Ted Trask <ttrask01@yahoo.com> | 2009-09-17 16:18:32 +0000 |
commit | e4580cd98e5e42612dfa49ccfa2bb79fc47cfab5 (patch) | |
tree | 8db120c961cbde070b672c51c2a2446e3e7b6a39 | |
download | acf-iproute2-qos-0.1.0.tar.bz2 acf-iproute2-qos-0.1.0.tar.xz |
First cut at ACF for iproute2-qosv0.1.0
-rw-r--r-- | Makefile | 44 | ||||
-rw-r--r-- | README | 1 | ||||
-rw-r--r-- | config.mk | 10 | ||||
-rw-r--r-- | qos-config-html.lsp | 10 | ||||
-rw-r--r-- | qos-controller.lua | 30 | ||||
-rw-r--r-- | qos-details-html.lsp | 40 | ||||
l--------- | qos-expert-html.lsp | 1 | ||||
-rw-r--r-- | qos-model.lua | 218 | ||||
l--------- | qos-status-html.lsp | 1 | ||||
-rw-r--r-- | qos.menu | 2 | ||||
-rw-r--r-- | qos.roles | 3 |
11 files changed, 360 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9016b4a --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ +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 @@ -0,0 +1 @@ +ACF for iproute2-qos diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..45f4d21 --- /dev/null +++ b/config.mk @@ -0,0 +1,10 @@ +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 diff --git a/qos-config-html.lsp b/qos-config-html.lsp new file mode 100644 index 0000000..31c965a --- /dev/null +++ b/qos-config-html.lsp @@ -0,0 +1,10 @@ +<% 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) +%> diff --git a/qos-controller.lua b/qos-controller.lua new file mode 100644 index 0000000..c3358e8 --- /dev/null +++ b/qos-controller.lua @@ -0,0 +1,30 @@ +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 diff --git a/qos-details-html.lsp b/qos-details-html.lsp new file mode 100644 index 0000000..2409387 --- /dev/null +++ b/qos-details-html.lsp @@ -0,0 +1,40 @@ +<% 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> diff --git a/qos-expert-html.lsp b/qos-expert-html.lsp new file mode 120000 index 0000000..15b1930 --- /dev/null +++ b/qos-expert-html.lsp @@ -0,0 +1 @@ +../filedetails-html.lsp
\ No newline at end of file diff --git a/qos-model.lua b/qos-model.lua new file mode 100644 index 0000000..93eed04 --- /dev/null +++ b/qos-model.lua @@ -0,0 +1,218 @@ +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 diff --git a/qos-status-html.lsp b/qos-status-html.lsp new file mode 120000 index 0000000..b2f8480 --- /dev/null +++ b/qos-status-html.lsp @@ -0,0 +1 @@ +../status-html.lsp
\ No newline at end of file diff --git a/qos.menu b/qos.menu new file mode 100644 index 0000000..36ddc33 --- /dev/null +++ b/qos.menu @@ -0,0 +1,2 @@ +#CAT GROUP/DESC TAB ACTION +Networking 15QOS Status details diff --git a/qos.roles b/qos.roles new file mode 100644 index 0000000..be14e6e --- /dev/null +++ b/qos.roles @@ -0,0 +1,3 @@ +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 |