summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTed Trask <ttrask01@yahoo.com>2009-09-17 16:18:32 +0000
committerTed Trask <ttrask01@yahoo.com>2009-09-17 16:18:32 +0000
commite4580cd98e5e42612dfa49ccfa2bb79fc47cfab5 (patch)
tree8db120c961cbde070b672c51c2a2446e3e7b6a39
downloadacf-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--Makefile44
-rw-r--r--README1
-rw-r--r--config.mk10
-rw-r--r--qos-config-html.lsp10
-rw-r--r--qos-controller.lua30
-rw-r--r--qos-details-html.lsp40
l---------qos-expert-html.lsp1
-rw-r--r--qos-model.lua218
l---------qos-status-html.lsp1
-rw-r--r--qos.menu2
-rw-r--r--qos.roles3
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
diff --git a/README b/README
new file mode 100644
index 0000000..5abe662
--- /dev/null
+++ b/README
@@ -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