From da7e25264c2dd9e5a109d547f0162913c9a85f62 Mon Sep 17 00:00:00 2001 From: Hannu Nyman Date: Wed, 15 Oct 2014 19:11:46 +0300 Subject: [PATCH] luci-app-sqm: Luci GUI for sqm-scripts QoS This is the Luci GUI for managing SQM QoS System. Signed-off-by: Hannu Nyman --- net/luci-app-sqm/Makefile | 66 +++++++ net/luci-app-sqm/files/sqm-cbi.lua | 217 ++++++++++++++++++++++ net/luci-app-sqm/files/sqm-controller.lua | 26 +++ net/luci-app-sqm/files/uci-defaults-sqm | 12 ++ 4 files changed, 321 insertions(+) create mode 100644 net/luci-app-sqm/Makefile create mode 100644 net/luci-app-sqm/files/sqm-cbi.lua create mode 100644 net/luci-app-sqm/files/sqm-controller.lua create mode 100644 net/luci-app-sqm/files/uci-defaults-sqm diff --git a/net/luci-app-sqm/Makefile b/net/luci-app-sqm/Makefile new file mode 100644 index 000000000..649424002 --- /dev/null +++ b/net/luci-app-sqm/Makefile @@ -0,0 +1,66 @@ +# +# Copyright (C) 2014 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-sqm +PKG_VERSION:=3 +PKG_RELEASE:=1 +LUCI_DIR:=/usr/lib/lua/luci + +include $(INCLUDE_DIR)/package.mk + +define Package/luci-app-sqm + SECTION:=luci + CATEGORY:=LuCI + TITLE:=SQM Scripts - LuCI interface + MAINTAINER:=Toke Høiland-Jørgensen + PKGARCH:=all + DEPENDS:= lua luci-base +sqm-scripts + SUBMENU:=3. Applications +endef + +define Package/luci-app-sqm/description + Control the simple_qos SQM script +endef + +define Build/Compile +endef + +define Build/Configure +endef + +define Package/luci-app-sqm/install + $(INSTALL_DIR) $(1)$(LUCI_DIR)/controller $(1)$(LUCI_DIR)/model/cbi + $(INSTALL_DATA) ./files/sqm-controller.lua $(1)$(LUCI_DIR)/controller/sqm.lua + $(INSTALL_DATA) ./files/sqm-cbi.lua $(1)$(LUCI_DIR)/model/cbi/sqm.lua + $(INSTALL_DIR) $(1)/etc/uci-defaults + $(INSTALL_BIN) ./files/uci-defaults-sqm $(1)/etc/uci-defaults/luci-sqm +endef + +define Package/luci-app-sqm/postinst +#!/bin/sh +which uci > /dev/null || exit 0 +uci -q get ucitrack.@sqm[0] > /dev/null || { + uci add ucitrack sqm > /dev/null + uci set ucitrack.@sqm[0].init=sqm + uci add_list ucitrack.@firewall[0].affects=sqm + uci commit +} +endef + +define Package/luci-app-sqm/postrm +#!/bin/sh +which uci > /dev/null || exit 0 +uci -q get ucitrack.@sqm[0] > /dev/null && { + uci delete ucitrack.@sqm[0] + uci del_list ucitrack.@firewall[0].affects=sqm + uci commit +} +endef + +$(eval $(call BuildPackage,luci-app-sqm)) diff --git a/net/luci-app-sqm/files/sqm-cbi.lua b/net/luci-app-sqm/files/sqm-cbi.lua new file mode 100644 index 000000000..e7d79df00 --- /dev/null +++ b/net/luci-app-sqm/files/sqm-cbi.lua @@ -0,0 +1,217 @@ +--[[ +LuCI - Lua Configuration Interface + +Copyright 2014 Steven Barth +Copyright 2014 Dave Taht + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +$Id$ +]]-- + +local wa = require "luci.tools.webadmin" +local fs = require "nixio.fs" +local net = require "luci.model.network".init() +local sys = require "luci.sys" +--local ifaces = net:get_interfaces() +local ifaces = sys.net:devices() +local path = "/usr/lib/sqm" + +m = Map("sqm", translate("Smart Queue Management"), + translate("With SQM you " .. + "can enable traffic shaping, better mixing (Fair Queueing)," .. + " active queue length management (AQM) " .. + " and prioritisation on one " .. + "network interface.")) + +s = m:section(TypedSection, "queue", translate("Queues")) +s:tab("tab_basic", translate("Basic Settings")) +s:tab("tab_qdisc", translate("Queue Discipline")) +s:tab("tab_linklayer", translate("Link Layer Adaptation")) +s.addremove = true -- set to true to allow adding SQM instances in the GUI +s.anonymous = true + +-- BASIC +e = s:taboption("tab_basic", Flag, "enabled", translate("Enable")) +e.rmempty = false + +n = s:taboption("tab_basic", ListValue, "interface", translate("Interface name")) +-- sm lifted from luci-app-wol, the original implementation failed to show pppoe-ge00 type interface names +for _, iface in ipairs(ifaces) do +-- if iface:is_up() then +-- n:value(iface:name()) +-- end + if iface ~= "lo" then + n:value(iface) + end +end +n.rmempty = false + + +dl = s:taboption("tab_basic", Value, "download", translate("Download speed (kbit/s) (ingress):")) +dl.datatype = "and(uinteger,min(0))" +dl.rmempty = false + +ul = s:taboption("tab_basic", Value, "upload", translate("Upload speed (kbit/s) (egress):")) +ul.datatype = "and(uinteger,min(0))" +ul.rmempty = false + +-- QDISC + +c = s:taboption("tab_qdisc", ListValue, "qdisc", translate("Queueing discipline")) +c:value("fq_codel", "fq_codel ("..translate("default")..")") +c:value("efq_codel") +c:value("nfq_codel") +c:value("sfq") +c:value("codel") +c:value("ns2_codel") +c:value("pie") +c:value("sfq") +c.default = "fq_codel" +c.rmempty = false + +local qos_desc = "" +sc = s:taboption("tab_qdisc", ListValue, "script", translate("Queue setup script")) +for file in fs.dir(path) do + if string.find(file, ".qos$") then + sc:value(file) + end + if string.find(file, ".qos.help$") then + fh = io.open(path .. "/" .. file, "r") + qos_desc = qos_desc .. "

" .. file:gsub(".help$", "") .. ":
" .. fh:read("*a") .. "

" + end +end +sc.default = "simple.qos" +sc.rmempty = false +sc.description = qos_desc + +ad = s:taboption("tab_qdisc", Flag, "qdisc_advanced", translate("Show Advanced Configuration")) +ad.default = false +ad.rmempty = true + +squash_dscp = s:taboption("tab_qdisc", ListValue, "squash_dscp", translate("Squash DSCP on inbound packets (ingress):")) +squash_dscp:value("1", "SQUASH") +squash_dscp:value("0", "DO NOT SQUASH") +squash_dscp.default = "1" +squash_dscp.rmempty = true +squash_dscp:depends("qdisc_advanced", "1") + +squash_ingress = s:taboption("tab_qdisc", ListValue, "squash_ingress", translate("Ignore DSCP on ingress:")) +squash_ingress:value("1", "Ignore") +squash_ingress:value("0", "Allow") +squash_ingress.default = "1" +squash_ingress.rmempty = true +squash_ingress:depends("qdisc_advanced", "1") + +iecn = s:taboption("tab_qdisc", ListValue, "ingress_ecn", translate("Explicit congestion notification (ECN) status on inbound packets (ingress):")) +iecn:value("ECN", "ECN ("..translate("default")..")") +iecn:value("NOECN") +iecn.default = "ECN" +iecn.rmempty = true +iecn:depends("qdisc_advanced", "1") + +eecn = s:taboption("tab_qdisc", ListValue, "egress_ecn", translate("Explicit congestion notification (ECN) status on outbound packets (egress).")) +eecn:value("NOECN", "NOECN ("..translate("default")..")") +eecn:value("ECN") +eecn.default = "NOECN" +eecn.rmempty = true +eecn:depends("qdisc_advanced", "1") + +ad2 = s:taboption("tab_qdisc", Flag, "qdisc_really_really_advanced", translate("Show Dangerous Configuration")) +ad2.default = false +ad2.rmempty = true +ad2:depends("qdisc_advanced", "1") + +ilim = s:taboption("tab_qdisc", Value, "ilimit", translate("Hard limit on ingress queues; leave empty for default.")) +-- ilim.default = 1000 +ilim.isnumber = true +ilim.datatype = "and(uinteger,min(0))" +ilim.rmempty = true +ilim:depends("qdisc_really_really_advanced", "1") + +elim = s:taboption("tab_qdisc", Value, "elimit", translate("Hard limit on egress queues; leave empty for default.")) +-- elim.default = 1000 +elim.datatype = "and(uinteger,min(0))" +elim.rmempty = true +elim:depends("qdisc_really_really_advanced", "1") + + +itarg = s:taboption("tab_qdisc", Value, "itarget", translate("Latency target for ingress, e.g 5ms [units: s, ms, or us]; leave empty for default, or auto for automatic selection.")) +itarg.datatype = "string" +itarg.rmempty = true +itarg:depends("qdisc_really_really_advanced", "1") + +etarg = s:taboption("tab_qdisc", Value, "etarget", translate("Latency target for egress, e.g. 5ms [units: s, ms, or us]; leave empty for default, or auto for automatic selection.")) +etarg.datatype = "string" +etarg.rmempty = true +etarg:depends("qdisc_really_really_advanced", "1") + + + +iqdisc_opts = s:taboption("tab_qdisc", Value, "iqdisc_opts", translate("Advanced option string to pass to the ingress queueing disciplines; no error checking, use very carefully.")) +iqdisc_opts.rmempty = true +iqdisc_opts:depends("qdisc_really_really_advanced", "1") + +eqdisc_opts = s:taboption("tab_qdisc", Value, "eqdisc_opts", translate("Advanced option string to pass to the egress queueing disciplines; no error checking, use very carefully.")) +eqdisc_opts.rmempty = true +eqdisc_opts:depends("qdisc_really_really_advanced", "1") + +-- LINKLAYER +ll = s:taboption("tab_linklayer", ListValue, "linklayer", translate("Which link layer to account for:")) +ll:value("none", "none ("..translate("default")..")") +ll:value("ethernet", "Ethernet with overhead: select for e.g. VDSL2.") +ll:value("atm", "ATM: select for e.g. ADSL1, ADSL2, ADSL2+.") +-- ll:value("adsl") -- reduce the options +ll.default = "none" + +po = s:taboption("tab_linklayer", Value, "overhead", translate("Per Packet Overhead (byte):")) +po.datatype = "and(integer,min(-1500))" +po.default = 0 +po.isnumber = true +po.rmempty = true +po:depends("linklayer", "ethernet") +-- po:depends("linklayer", "adsl") +po:depends("linklayer", "atm") + + +adll = s:taboption("tab_linklayer", Flag, "linklayer_advanced", translate("Show Advanced Linklayer Options, (only needed if MTU > 1500)")) +adll.rmempty = true +adll:depends("linklayer", "ethernet") +-- adll:depends("linklayer", "adsl") +adll:depends("linklayer", "atm") + +smtu = s:taboption("tab_linklayer", Value, "tcMTU", translate("Maximal Size for size and rate calculations, tcMTU (byte); needs to be >= interface MTU + overhead:")) +smtu.datatype = "and(uinteger,min(0))" +smtu.default = 2047 +smtu.isnumber = true +smtu.rmempty = true +smtu:depends("linklayer_advanced", "1") + +stsize = s:taboption("tab_linklayer", Value, "tcTSIZE", translate("Number of entries in size/rate tables, TSIZE; for ATM choose TSIZE = (tcMTU + 1) / 16:")) +stsize.datatype = "and(uinteger,min(0))" +stsize.default = 128 +stsize.isnumber = true +stsize.rmempty = true +stsize:depends("linklayer_advanced", "1") + +smpu = s:taboption("tab_linklayer", Value, "tcMPU", translate("Minimal packet size, MPU (byte); needs to be > 0 for ethernet size tables:")) +smpu.datatype = "and(uinteger,min(0))" +smpu.default = 0 +smpu.isnumber = true +smpu.rmempty = true +smpu:depends("linklayer_advanced", "1") + +lla = s:taboption("tab_linklayer", ListValue, "linklayer_adaptation_mechanism", translate("Which linklayer adaptation mechanism to use; for testing only")) +lla:value("htb_private") +lla:value("tc_stab", "tc_stab ("..translate("default")..")") +lla.default = "tc_stab" +lla.rmempty = true +lla:depends("linklayer_advanced", "1") + +-- PRORITIES? + +return m diff --git a/net/luci-app-sqm/files/sqm-controller.lua b/net/luci-app-sqm/files/sqm-controller.lua new file mode 100644 index 000000000..d36f5fd67 --- /dev/null +++ b/net/luci-app-sqm/files/sqm-controller.lua @@ -0,0 +1,26 @@ +--[[ +LuCI - Lua Configuration Interface + +Copyright 2008 Steven Barth + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +$Id$ +]]-- + +module("luci.controller.sqm", package.seeall) + +function index() + if not nixio.fs.access("/etc/config/sqm") then + return + end + + local page + + page = entry({"admin", "network", "sqm"}, cbi("sqm"), _("SQM QoS")) + page.dependent = true +end diff --git a/net/luci-app-sqm/files/uci-defaults-sqm b/net/luci-app-sqm/files/uci-defaults-sqm new file mode 100644 index 000000000..cd9e7b2b1 --- /dev/null +++ b/net/luci-app-sqm/files/uci-defaults-sqm @@ -0,0 +1,12 @@ +#!/bin/sh + +uci -q batch <<-EOF >/dev/null + delete ucitrack.@sqm[-1] + add ucitrack sqm + set ucitrack.@sqm[-1].init=sqm + add_list ucitrack.@firewall[0].affects=sqm + commit ucitrack +EOF + +rm -f /tmp/luci-indexcache +exit 0