diff options
59 files changed, 1599 insertions, 1657 deletions
diff --git a/applications/luci-app-adblock/Makefile b/applications/luci-app-adblock/Makefile index 2de2ed8627..c51b9d8eda 100644 --- a/applications/luci-app-adblock/Makefile +++ b/applications/luci-app-adblock/Makefile @@ -4,7 +4,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=LuCI support for Adblock -LUCI_DEPENDS:=+adblock +luci-lib-jsonc +LUCI_DEPENDS:=+luci-compat +adblock +luci-lib-jsonc LUCI_PKGARCH:=all include ../../luci.mk diff --git a/applications/luci-app-ahcp/Makefile b/applications/luci-app-ahcp/Makefile index bb4d4158d0..10859345e6 100644 --- a/applications/luci-app-ahcp/Makefile +++ b/applications/luci-app-ahcp/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=LuCI Support for AHCPd -LUCI_DEPENDS:=+ahcpd +LUCI_DEPENDS:=+luci-compat +ahcpd include ../../luci.mk diff --git a/applications/luci-app-banip/Makefile b/applications/luci-app-banip/Makefile index 1936bc36b2..39712708be 100644 --- a/applications/luci-app-banip/Makefile +++ b/applications/luci-app-banip/Makefile @@ -4,7 +4,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=LuCI support for banIP -LUCI_DEPENDS:=+banip +luci-lib-jsonc +LUCI_DEPENDS:=+luci-compat +banip +luci-lib-jsonc LUCI_PKGARCH:=all include ../../luci.mk diff --git a/applications/luci-app-bcp38/Makefile b/applications/luci-app-bcp38/Makefile index 9ab5a6701d..a8a6e7402e 100644 --- a/applications/luci-app-bcp38/Makefile +++ b/applications/luci-app-bcp38/Makefile @@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=BCP38 LuCI interface -LUCI_DEPENDS:=+luci-mod-admin-full +bcp38 +LUCI_DEPENDS:=+luci-compat +luci-mod-admin-full +bcp38 PKG_MAINTAINER:=Toke Høiland-Jørgensen <toke@toke.dk> PKG_LICENSE:=Apache-2.0 diff --git a/applications/luci-app-dcwapd/Makefile b/applications/luci-app-dcwapd/Makefile index 3396bcd296..3def090a76 100644 --- a/applications/luci-app-dcwapd/Makefile +++ b/applications/luci-app-dcwapd/Makefile @@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=Dual Channel Wi-Fi AP Daemon configuration module -LUCI_DEPENDS:=+dcwapd +LUCI_DEPENDS:=+luci-compat +dcwapd include ../../luci.mk diff --git a/applications/luci-app-minidlna/Makefile b/applications/luci-app-minidlna/Makefile index 4790aa32ce..3d3ec4d52e 100644 --- a/applications/luci-app-minidlna/Makefile +++ b/applications/luci-app-minidlna/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=LuCI Support for miniDLNA -LUCI_DEPENDS:=+minidlna +LUCI_DEPENDS:=+luci-compat +minidlna include ../../luci.mk diff --git a/applications/luci-app-mwan3/Makefile b/applications/luci-app-mwan3/Makefile index c719cb134c..50f87cd846 100644 --- a/applications/luci-app-mwan3/Makefile +++ b/applications/luci-app-mwan3/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=LuCI support for the MWAN3 multiwan hotplug script -LUCI_DEPENDS:=+mwan3 +libuci-lua +luci-mod-admin-full +luci-app-firewall +luci-lib-nixio +LUCI_DEPENDS:=+luci-compat +mwan3 +libuci-lua +luci-mod-admin-full +luci-app-firewall +luci-lib-nixio LUCI_PKGARCH:=all PKG_LICENSE:=GPLv2 diff --git a/applications/luci-app-nlbwmon/Makefile b/applications/luci-app-nlbwmon/Makefile index a00177f2ca..c4b83e807c 100644 --- a/applications/luci-app-nlbwmon/Makefile +++ b/applications/luci-app-nlbwmon/Makefile @@ -1,7 +1,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=Netlink based bandwidth accounting -LUCI_DEPENDS:=+nlbwmon +LUCI_DEPENDS:=+luci-compat +nlbwmon include ../../luci.mk diff --git a/applications/luci-app-ocserv/Makefile b/applications/luci-app-ocserv/Makefile index 3fbf4d2c1b..2f2ea788eb 100644 --- a/applications/luci-app-ocserv/Makefile +++ b/applications/luci-app-ocserv/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=LuCI Support for OpenConnect VPN -LUCI_DEPENDS:=+ocserv +certtool +LUCI_DEPENDS:=+luci-compat +ocserv +certtool include ../../luci.mk diff --git a/applications/luci-app-olsr/Makefile b/applications/luci-app-olsr/Makefile index 6ce1ac2556..b6432e6acb 100644 --- a/applications/luci-app-olsr/Makefile +++ b/applications/luci-app-olsr/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=OLSR configuration and status module -LUCI_DEPENDS:=+olsrd +olsrd-mod-jsoninfo +luci-lib-json +LUCI_DEPENDS:=+luci-compat +olsrd +olsrd-mod-jsoninfo +luci-lib-json include ../../luci.mk diff --git a/applications/luci-app-p910nd/Makefile b/applications/luci-app-p910nd/Makefile index adb35bd47f..8c73f372f4 100644 --- a/applications/luci-app-p910nd/Makefile +++ b/applications/luci-app-p910nd/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=p910nd - Printer server module -LUCI_DEPENDS:=+p910nd +LUCI_DEPENDS:=+luci-compat +p910nd include ../../luci.mk diff --git a/applications/luci-app-rp-pppoe-server/Makefile b/applications/luci-app-rp-pppoe-server/Makefile index aa3ae538cd..b4edfc9499 100644 --- a/applications/luci-app-rp-pppoe-server/Makefile +++ b/applications/luci-app-rp-pppoe-server/Makefile @@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=Roaring Penguin PPPoE Server -LUCI_DEPENDS:=+rp-pppoe-server +LUCI_DEPENDS:=+luci-compat +rp-pppoe-server include ../../luci.mk diff --git a/applications/luci-app-shadowsocks-libev/Makefile b/applications/luci-app-shadowsocks-libev/Makefile index d0923e07a4..72195a7110 100644 --- a/applications/luci-app-shadowsocks-libev/Makefile +++ b/applications/luci-app-shadowsocks-libev/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=LuCI Support for shadowsocks-libev -LUCI_DEPENDS:= +LUCI_DEPENDS:=+luci-compat PKG_LICENSE:=Apache-2.0 diff --git a/applications/luci-app-statistics/Makefile b/applications/luci-app-statistics/Makefile index b552400fed..b38146c2c2 100644 --- a/applications/luci-app-statistics/Makefile +++ b/applications/luci-app-statistics/Makefile @@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=LuCI Statistics Application LUCI_DEPENDS:= \ - +luci-lib-iptparser \ + +luci-compat +luci-lib-iptparser \ +collectd +rrdtool1 +collectd-mod-rrdtool +collectd-mod-iwinfo \ +collectd-mod-cpu +collectd-mod-memory \ +collectd-mod-interface +collectd-mod-load +collectd-mod-network diff --git a/applications/luci-app-travelmate/Makefile b/applications/luci-app-travelmate/Makefile index 58d9bf37d4..4115dadbef 100644 --- a/applications/luci-app-travelmate/Makefile +++ b/applications/luci-app-travelmate/Makefile @@ -5,7 +5,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=LuCI support for Travelmate -LUCI_DEPENDS:=+travelmate +luci-lib-jsonc +LUCI_DEPENDS:=+luci-compat +travelmate +luci-lib-jsonc LUCI_PKGARCH:=all include ../../luci.mk diff --git a/applications/luci-app-unbound/Makefile b/applications/luci-app-unbound/Makefile index 6b43adab3f..9dd01ffed6 100644 --- a/applications/luci-app-unbound/Makefile +++ b/applications/luci-app-unbound/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=Unbound Recursive DNS Resolver Configuration -LUCI_DEPENDS:=+unbound-daemon +LUCI_DEPENDS:=+luci-compat +unbound-daemon include ../../luci.mk diff --git a/applications/luci-app-vnstat/Makefile b/applications/luci-app-vnstat/Makefile index 6d98610d6d..c2e930632e 100644 --- a/applications/luci-app-vnstat/Makefile +++ b/applications/luci-app-vnstat/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=LuCI Support for VnStat -LUCI_DEPENDS:=+vnstat +vnstati +LUCI_DEPENDS:=+luci-compat +vnstat +vnstati include ../../luci.mk diff --git a/libs/rpcd-mod-luci/Makefile b/libs/rpcd-mod-luci/Makefile index 65a436b3e6..01b581d240 100644 --- a/libs/rpcd-mod-luci/Makefile +++ b/libs/rpcd-mod-luci/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=rpcd-mod-luci -PKG_VERSION:=20191031 +PKG_VERSION:=20191102 PKG_MAINTAINER:=Jo-Philipp Wich <jo@mein.io> PKG_LICENSE:=Apache-2.0 diff --git a/libs/rpcd-mod-luci/src/luci.c b/libs/rpcd-mod-luci/src/luci.c index 04d3a15b7b..91f6798d79 100644 --- a/libs/rpcd-mod-luci/src/luci.c +++ b/libs/rpcd-mod-luci/src/luci.c @@ -359,7 +359,7 @@ find_leasefile(struct uci_context *uci, const char *section) ptr.option = "leasefile"; ptr.o = NULL; - if (uci_lookup_ptr(uci, &ptr, NULL, true)) + if (uci_lookup_ptr(uci, &ptr, NULL, true) || ptr.o == NULL) continue; if (ptr.o->type != UCI_TYPE_STRING) @@ -435,9 +435,6 @@ lease_next(void) ea = ether_aton(p); - if (!ea) - continue; - p = strtok(NULL, " \t\n"); if (p && inet_pton(AF_INET6, p, &e.addr.in6)) @@ -447,6 +444,9 @@ lease_next(void) else continue; + if (!ea && e.af != AF_INET6) + continue; + e.hostname = strtok(NULL, " \t\n"); e.duid = strtok(NULL, " \t\n"); @@ -459,7 +459,11 @@ lease_next(void) if (!strcmp(e.duid, "*")) e.duid = NULL; - e.mac = *ea; + if (!ea && e.duid) + ea = duid2ea(e.duid); + + if (ea) + e.mac = *ea; return &e; } @@ -1254,7 +1258,7 @@ rpc_luci_get_host_hints_uci(struct reply_context *rctx) ptr.option = "ip"; ptr.o = NULL; - if (uci_lookup_ptr(uci, &ptr, NULL, true)) + if (uci_lookup_ptr(uci, &ptr, NULL, true) || ptr.o == NULL) continue; if (ptr.o->type != UCI_TYPE_STRING) @@ -1266,7 +1270,7 @@ rpc_luci_get_host_hints_uci(struct reply_context *rctx) ptr.option = "name"; ptr.o = NULL; - if (!uci_lookup_ptr(uci, &ptr, NULL, true) && + if (!uci_lookup_ptr(uci, &ptr, NULL, true) && ptr.o != NULL && ptr.o->type == UCI_TYPE_STRING) n = ptr.o->v.string; else @@ -1275,7 +1279,7 @@ rpc_luci_get_host_hints_uci(struct reply_context *rctx) ptr.option = "mac"; ptr.o = NULL; - if (uci_lookup_ptr(uci, &ptr, NULL, true)) + if (uci_lookup_ptr(uci, &ptr, NULL, true) || ptr.o == NULL) continue; if (ptr.o->type == UCI_TYPE_STRING) { diff --git a/modules/luci-base/htdocs/luci-static/resources/form.js b/modules/luci-base/htdocs/luci-static/resources/form.js index f4cce17fe1..7de5e0f76b 100644 --- a/modules/luci-base/htdocs/luci-static/resources/form.js +++ b/modules/luci-base/htdocs/luci-static/resources/form.js @@ -1390,9 +1390,9 @@ var CBIGridSection = CBITableSection.extend({ CBIAbstractSection.prototype.tab.call(this, name, title, description); }, - handleAdd: function(ev) { + handleAdd: function(ev, name) { var config_name = this.uciconfig || this.map.config, - section_id = this.map.data.add(config_name, this.sectiontype); + section_id = this.map.data.add(config_name, this.sectiontype, name); this.addedSection = section_id; return this.renderMoreOptionsModal(section_id); diff --git a/modules/luci-base/luasrc/sys.lua b/modules/luci-base/luasrc/sys.lua index f82cd78cb7..c8ad5393e0 100644 --- a/modules/luci-base/luasrc/sys.lua +++ b/modules/luci-base/luasrc/sys.lua @@ -7,7 +7,6 @@ local table = require "table" local nixio = require "nixio" local fs = require "nixio.fs" local uci = require "luci.model.uci" -local ntm = require "luci.model.network" local luci = {} luci.util = require "luci.util" @@ -537,6 +536,8 @@ end wifi = {} function wifi.getiwinfo(ifname) + local ntm = require "luci.model.network" + ntm.init() local wnet = ntm:get_wifinet(ifname) diff --git a/modules/luci-base/luasrc/view/cbi/firewall_zoneforwards.htm b/modules/luci-base/luasrc/view/cbi/firewall_zoneforwards.htm deleted file mode 100644 index dc251dbd94..0000000000 --- a/modules/luci-base/luasrc/view/cbi/firewall_zoneforwards.htm +++ /dev/null @@ -1,73 +0,0 @@ -<%+cbi/valueheader%> - -<%- - local utl = require "luci.util" - local fwm = require "luci.model.firewall".init() - local nwm = require "luci.model.network".init() - - local zone, fwd, fz - local value = self:formvalue(section) - if not value or value == "-" then - value = self:cfgvalue(section) or self.default - end - - local def = fwm:get_defaults() - local zone = fwm:get_zone(value) - local empty = true - - local function render_zone(zone) --%> - <label class="zonebadge" style="background-color:<%=zone:get_color()%>"> - <strong><%=zone:name()%></strong> - <div class="cbi-tooltip"> - <%- - local zempty = true - for _, net in ipairs(zone:get_networks()) do - net = nwm:get_network(net) - if net then - zempty = false - -%> - <span class="ifacebadge<% if net:name() == self.network then %> ifacebadge-active<% end %>"><%=net:name()%>:  - <% - local nempty = true - for _, iface in ipairs(net:is_bridge() and net:get_interfaces() or { net:get_interface() }) do - nempty = false - %> - <img<%=attr("title", iface:get_i18n())%> src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" /> - <% end %> - <% if nempty then %><em><%:(empty)%></em><% end %> - </span> - <%- end end -%> - <% if zempty then %><span class="ifacebadge"><em><%:(empty)%></em></span><% end %> - </div> - </label> -<%- - end --%> - -<% if zone then %> -<div class="zone-forwards"> - <div class="zone-src"> - <%=render_zone(zone)%> - </div> - <span>⇒</span> - <div class="zone-dest"> - <% - for _, fwd in ipairs(zone:get_forwardings_by("src")) do - fz = fwd:dest_zone() - if fz then - empty = false - render_zone(fz) - end - end - if empty then - %> - <label class="zonebadge zonebadge-empty"> - <strong><%=def:forward():upper()%></strong> - </label> - <% end %> - </div> -</div> -<% end %> - -<%+cbi/valuefooter%> diff --git a/modules/luci-base/root/usr/libexec/rpcd/luci b/modules/luci-base/root/usr/libexec/rpcd/luci index 2601512da6..10c1691b96 100755 --- a/modules/luci-base/root/usr/libexec/rpcd/luci +++ b/modules/luci-base/root/usr/libexec/rpcd/luci @@ -516,6 +516,72 @@ local methods = { return { error = err } end end + }, + + getRealtimeStats = { + args = { mode = "interface", device = "eth0" }, + call = function(args) + local util = require "luci.util" + + local flags + if args.mode == "interface" then + flags = "-i %s" % util.shellquote(args.device) + elseif args.mode == "wireless" then + flags = "-r %s" % util.shellquote(args.device) + elseif args.mode == "conntrack" then + flags = "-c" + elseif args.mode == "load" then + flags = "-l" + else + return { error = "Invalid mode" } + end + + local fd, err = io.popen("luci-bwc %s" % flags, "r") + if fd then + local parse = json.new() + local done + + parse:parse("[") + + while true do + local ln = fd:read("*l") + if not ln then + break + end + + done, err = parse:parse((ln:gsub("%d+", "%1.0"))) + + if done then + err = "Unexpected JSON data" + end + + if err then + break + end + end + + fd:close() + + done, err = parse:parse("]") + + if err then + return { error = err } + elseif not done then + return { error = "Incomplete JSON data" } + else + return { result = parse:get() } + end + else + return { error = err } + end + end + }, + + getConntrackList = { + call = function() + local sys = require "luci.sys" + return { result = sys.net.conntrack() } + end } } diff --git a/modules/luci-base/root/usr/share/rpcd/acl.d/luci-base.json b/modules/luci-base/root/usr/share/rpcd/acl.d/luci-base.json index a09c6b4245..540fc72c10 100644 --- a/modules/luci-base/root/usr/share/rpcd/acl.d/luci-base.json +++ b/modules/luci-base/root/usr/share/rpcd/acl.d/luci-base.json @@ -43,9 +43,10 @@ "ubus": { "file": [ "list", "read", "stat" ], "iwinfo": [ "assoclist", "freqlist", "txpowerlist", "countrylist" ], - "luci": [ "getDUIDHints", "getInitList", "getLocaltime", "getTimezones", "getLEDs", "getUSBDevices", "getSwconfigFeatures", "getSwconfigPortState", "getBlockDevices", "getMountPoints" ], + "luci": [ "getConntrackList", "getDUIDHints", "getInitList", "getLocaltime", "getRealtimeStats", "getTimezones", "getLEDs", "getUSBDevices", "getSwconfigFeatures", "getSwconfigPortState", "getBlockDevices", "getMountPoints" ], "luci-rpc": [ "getBoardJSON", "getDHCPLeases", "getDSLStatus", "getHostHints", "getNetworkDevices", "getWirelessDevices" ], "network.interface": [ "dump" ], + "network.rrdns": [ "lookup" ], "network": [ "get_proto_handlers" ], "system": [ "board", "info", "validate_firmware_image" ], "uci": [ "changes", "get" ] @@ -62,6 +63,7 @@ "/etc/sysupgrade.conf": [ "write" ], "/sbin/block": [ "exec" ], "/sbin/firstboot": [ "exec" ], + "/sbin/ifdown": [ "exec" ], "/sbin/ifup": [ "exec" ], "/sbin/reboot": [ "exec" ], "/sbin/sysupgrade": [ "exec" ], diff --git a/modules/luci-compat/Makefile b/modules/luci-compat/Makefile new file mode 100644 index 0000000000..d73ca070a1 --- /dev/null +++ b/modules/luci-compat/Makefile @@ -0,0 +1,19 @@ +# +# Copyright (C) 2019 Jo-Philipp Wich <jo@mein.io> +# +# This is free software, licensed under the Apache License, Version 2.0 . +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-compat + +LUCI_TYPE:=mod +LUCI_BASENAME:=compat + +LUCI_TITLE:=LuCI compatibility libraries +LUCI_DEPENDS:=+luci-base + +include ../../luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/modules/luci-base/luasrc/model/network.lua b/modules/luci-compat/luasrc/model/network.lua index a36a23f321..a36a23f321 100644 --- a/modules/luci-base/luasrc/model/network.lua +++ b/modules/luci-compat/luasrc/model/network.lua diff --git a/protocols/luci-proto-3g/luasrc/model/network/proto_3g.lua b/modules/luci-compat/luasrc/model/network/proto_3g.lua index 60d8e2ebae..60d8e2ebae 100644 --- a/protocols/luci-proto-3g/luasrc/model/network/proto_3g.lua +++ b/modules/luci-compat/luasrc/model/network/proto_3g.lua diff --git a/protocols/luci-proto-ipv6/luasrc/model/network/proto_4x6.lua b/modules/luci-compat/luasrc/model/network/proto_4x6.lua index 0b329d8a92..0b329d8a92 100644 --- a/protocols/luci-proto-ipv6/luasrc/model/network/proto_4x6.lua +++ b/modules/luci-compat/luasrc/model/network/proto_4x6.lua diff --git a/protocols/luci-proto-ipv6/luasrc/model/network/proto_6x4.lua b/modules/luci-compat/luasrc/model/network/proto_6x4.lua index 2fd0b11957..2fd0b11957 100644 --- a/protocols/luci-proto-ipv6/luasrc/model/network/proto_6x4.lua +++ b/modules/luci-compat/luasrc/model/network/proto_6x4.lua diff --git a/protocols/luci-proto-ipv6/luasrc/model/network/proto_dhcpv6.lua b/modules/luci-compat/luasrc/model/network/proto_dhcpv6.lua index 0b45dad050..0b45dad050 100644 --- a/protocols/luci-proto-ipv6/luasrc/model/network/proto_dhcpv6.lua +++ b/modules/luci-compat/luasrc/model/network/proto_dhcpv6.lua diff --git a/protocols/luci-proto-hnet/luasrc/model/network/proto_hnet.lua b/modules/luci-compat/luasrc/model/network/proto_hnet.lua index f525061be4..f525061be4 100644 --- a/protocols/luci-proto-hnet/luasrc/model/network/proto_hnet.lua +++ b/modules/luci-compat/luasrc/model/network/proto_hnet.lua diff --git a/protocols/luci-proto-ipip/luasrc/model/network/proto_ipip.lua b/modules/luci-compat/luasrc/model/network/proto_ipip.lua index 04d2e78b09..04d2e78b09 100644 --- a/protocols/luci-proto-ipip/luasrc/model/network/proto_ipip.lua +++ b/modules/luci-compat/luasrc/model/network/proto_ipip.lua diff --git a/protocols/luci-proto-modemmanager/luasrc/model/network/proto_modemmanager.lua b/modules/luci-compat/luasrc/model/network/proto_modemmanager.lua index 3ad15dfd22..3ad15dfd22 100644 --- a/protocols/luci-proto-modemmanager/luasrc/model/network/proto_modemmanager.lua +++ b/modules/luci-compat/luasrc/model/network/proto_modemmanager.lua diff --git a/protocols/luci-proto-ncm/luasrc/model/network/proto_ncm.lua b/modules/luci-compat/luasrc/model/network/proto_ncm.lua index 49abe23472..49abe23472 100644 --- a/protocols/luci-proto-ncm/luasrc/model/network/proto_ncm.lua +++ b/modules/luci-compat/luasrc/model/network/proto_ncm.lua diff --git a/protocols/luci-proto-openconnect/luasrc/model/network/proto_openconnect.lua b/modules/luci-compat/luasrc/model/network/proto_openconnect.lua index 0944c7fe6a..0944c7fe6a 100644 --- a/protocols/luci-proto-openconnect/luasrc/model/network/proto_openconnect.lua +++ b/modules/luci-compat/luasrc/model/network/proto_openconnect.lua diff --git a/protocols/luci-proto-ppp/luasrc/model/network/proto_ppp.lua b/modules/luci-compat/luasrc/model/network/proto_ppp.lua index f87b30fcc1..f87b30fcc1 100644 --- a/protocols/luci-proto-ppp/luasrc/model/network/proto_ppp.lua +++ b/modules/luci-compat/luasrc/model/network/proto_ppp.lua diff --git a/protocols/luci-proto-pppossh/luasrc/model/network/proto_pppossh.lua b/modules/luci-compat/luasrc/model/network/proto_pppossh.lua index a0e2a510c9..a0e2a510c9 100644 --- a/protocols/luci-proto-pppossh/luasrc/model/network/proto_pppossh.lua +++ b/modules/luci-compat/luasrc/model/network/proto_pppossh.lua diff --git a/protocols/luci-proto-qmi/luasrc/model/network/proto_qmi.lua b/modules/luci-compat/luasrc/model/network/proto_qmi.lua index c414378d80..c414378d80 100644 --- a/protocols/luci-proto-qmi/luasrc/model/network/proto_qmi.lua +++ b/modules/luci-compat/luasrc/model/network/proto_qmi.lua diff --git a/protocols/luci-proto-relay/luasrc/model/network/proto_relay.lua b/modules/luci-compat/luasrc/model/network/proto_relay.lua index 3b811d44d1..3b811d44d1 100644 --- a/protocols/luci-proto-relay/luasrc/model/network/proto_relay.lua +++ b/modules/luci-compat/luasrc/model/network/proto_relay.lua diff --git a/protocols/luci-proto-vpnc/luasrc/model/network/proto_vpnc.lua b/modules/luci-compat/luasrc/model/network/proto_vpnc.lua index 6c3136e384..6c3136e384 100644 --- a/protocols/luci-proto-vpnc/luasrc/model/network/proto_vpnc.lua +++ b/modules/luci-compat/luasrc/model/network/proto_vpnc.lua diff --git a/protocols/luci-proto-wireguard/luasrc/model/network/proto_wireguard.lua b/modules/luci-compat/luasrc/model/network/proto_wireguard.lua index d6937618a7..d6937618a7 100644 --- a/protocols/luci-proto-wireguard/luasrc/model/network/proto_wireguard.lua +++ b/modules/luci-compat/luasrc/model/network/proto_wireguard.lua diff --git a/modules/luci-base/luasrc/view/cbi/firewall_zonelist.htm b/modules/luci-compat/luasrc/view/cbi/firewall_zonelist.htm index 7ecec10a8f..7ecec10a8f 100644 --- a/modules/luci-base/luasrc/view/cbi/firewall_zonelist.htm +++ b/modules/luci-compat/luasrc/view/cbi/firewall_zonelist.htm diff --git a/modules/luci-base/luasrc/view/cbi/network_ifacelist.htm b/modules/luci-compat/luasrc/view/cbi/network_ifacelist.htm index f23e51d18d..f23e51d18d 100644 --- a/modules/luci-base/luasrc/view/cbi/network_ifacelist.htm +++ b/modules/luci-compat/luasrc/view/cbi/network_ifacelist.htm diff --git a/modules/luci-base/luasrc/view/cbi/network_netinfo.htm b/modules/luci-compat/luasrc/view/cbi/network_netinfo.htm index 4fd84112a4..4fd84112a4 100644 --- a/modules/luci-base/luasrc/view/cbi/network_netinfo.htm +++ b/modules/luci-compat/luasrc/view/cbi/network_netinfo.htm diff --git a/modules/luci-base/luasrc/view/cbi/network_netlist.htm b/modules/luci-compat/luasrc/view/cbi/network_netlist.htm index 3ee4274a33..3ee4274a33 100644 --- a/modules/luci-base/luasrc/view/cbi/network_netlist.htm +++ b/modules/luci-compat/luasrc/view/cbi/network_netlist.htm diff --git a/modules/luci-mod-network/htdocs/luci-static/resources/view/network/interfaces.js b/modules/luci-mod-network/htdocs/luci-static/resources/view/network/interfaces.js index 60d61adfec..12d28808fb 100644 --- a/modules/luci-mod-network/htdocs/luci-static/resources/view/network/interfaces.js +++ b/modules/luci-mod-network/htdocs/luci-static/resources/view/network/interfaces.js @@ -158,9 +158,50 @@ function iface_updown(up, id, ev, force) { btns[0].disabled = true; btns[1].disabled = true; - dsc.setAttribute(up ? 'reconnect' : 'disconnect', force ? 'force' : ''); - L.dom.content(dsc, E('em', - up ? _('Interface is reconnecting...') : _('Interface is shutting down...'))); + if (!up) { + L.Request.get(L.url('admin/network/remote_addr')).then(function(res) { + var info = res.json(); + + if (L.isObject(info) && + Array.isArray(info.inbound_interfaces) && + info.inbound_interfaces.filter(function(i) { return i == id })[0]) { + + L.ui.showModal(_('Confirm disconnect'), [ + E('p', _('You appear to be currently connected to the device via the "%h" interface. Do you really want to shut down the interface?').format(id)), + E('div', { 'class': 'right' }, [ + E('button', { + 'class': 'cbi-button cbi-button-neutral', + 'click': function(ev) { + btns[1].classList.remove('spinning'); + btns[1].disabled = false; + btns[0].disabled = false; + + L.ui.hideModal(); + } + }, _('Cancel')), + ' ', + E('button', { + 'class': 'cbi-button cbi-button-negative important', + 'click': function(ev) { + dsc.setAttribute('disconnect', ''); + L.dom.content(dsc, E('em', _('Interface is shutting down...'))); + + L.ui.hideModal(); + } + }, _('Disconnect')) + ]) + ]); + } + else { + dsc.setAttribute('disconnect', ''); + L.dom.content(dsc, E('em', _('Interface is shutting down...'))); + } + }); + } + else { + dsc.setAttribute(up ? 'reconnect' : 'disconnect', force ? 'force' : ''); + L.dom.content(dsc, E('em', up ? _('Interface is reconnecting...') : _('Interface is shutting down...'))); + } } function get_netmask(s, use_cfgvalue) { @@ -939,34 +980,11 @@ return L.view.extend({ L.ui.addNotification(null, E('p', e.message)); })); } - else if (dsc.getAttribute('disconnect') == '' || dsc.getAttribute('disconnect') == 'force') { - var force = dsc.getAttribute('disconnect'); + else if (dsc.getAttribute('disconnect') == '') { dsc.setAttribute('disconnect', '1'); - tasks.push(L.Request.post( - L.url('admin/network/iface_down', section_ids[i], force), - 'token=' + L.env.token, - { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } } - ).then(L.bind(function(ifname, res) { - if (res.status == 409) { - L.ui.showModal(_('Confirm disconnect'), [ - E('p', _('You appear to be currently connected to the device via the "%h" interface. Do you really want to shut down the interface?').format(ifname)), - E('div', { 'class': 'right' }, [ - E('button', { - 'class': 'cbi-button cbi-button-neutral', - 'click': L.ui.hideModal - }, _('Cancel')), - ' ', - E('button', { - 'class': 'cbi-button cbi-button-negative important', - 'click': function(ev) { - iface_updown(false, ifname, ev, true); - L.ui.hideModal(); - } - }, _('Disconnect')) - ]) - ]); - } - }, this, section_ids[i]), function() {})); + tasks.push(fs.exec('/sbin/ifdown', [section_ids[i]]).catch(function(e) { + L.ui.addNotification(null, E('p', e.message)); + })); } else if (dsc.getAttribute('reconnect') == '1') { dsc.removeAttribute('reconnect'); diff --git a/modules/luci-mod-network/luasrc/controller/admin/network.lua b/modules/luci-mod-network/luasrc/controller/admin/network.lua index 9f6a568b07..bd00235faa 100644 --- a/modules/luci-mod-network/luasrc/controller/admin/network.lua +++ b/modules/luci-mod-network/luasrc/controller/admin/network.lua @@ -15,7 +15,7 @@ function index() page.uci_depends = { wireless = { ["@wifi-device[0]"] = "wifi-device" } } page.leaf = true - page = entry({"admin", "network", "iface_down"}, post("iface_down"), nil) + page = entry({"admin", "network", "remote_addr"}, call("remote_addr"), nil) page.leaf = true page = entry({"admin", "network", "network"}, view("network/interfaces"), _("Interfaces"), 10) @@ -70,51 +70,58 @@ local function addr2dev(addr, src) return route and route.dev end -function iface_down(iface, force) - local netmd = require "luci.model.network".init() - local peer = luci.http.getenv("REMOTE_ADDR") - local serv = luci.http.getenv("SERVER_ADDR") - - if force ~= "force" and serv and peer then - local dev = addr2dev(peer, serv) - if dev then - local nets = netmd:get_networks() - local outnet = nil - local _, net, ai - - for _, net in ipairs(nets) do - if net:contains_interface(dev) then - outnet = net - break +function remote_addr() + local uci = require "luci.model.uci" + local peer = luci.http.getenv("REMOTE_ADDR") + local serv = luci.http.getenv("SERVER_ADDR") + local device = addr2dev(peer, serv) + local ifaces = luci.util.ubus("network.interface", "dump") + local indevs = {} + local inifs = {} + + local result = { + remote_addr = peer, + server_addr = serv, + inbound_devices = {}, + inbound_interfaces = {} + } + + if type(ifaces) == "table" and type(ifaces.interface) == "table" then + for _, iface in ipairs(ifaces.interface) do + if type(iface) == "table" then + if iface.device == device or iface.l3_device == device then + inifs[iface.interface] = true + indevs[device] = true end - end - - if outnet:name() == iface then - luci.http.status(409, "Is inbound interface") - return - end - local peeraddr = outnet:get("peeraddr") - for _, ai in ipairs(peeraddr and nixio.getaddrinfo(peeraddr) or {}) do - local peerdev = addr2dev(ai.address) - for _, net in ipairs(peerdev and nets or {}) do - if net:contains_interface(peerdev) and net:name() == iface then - luci.http.status(409, "Is inbound interface") - return + local peeraddr = uci:get("network", iface.interface, "peeraddr") + for _, ai in ipairs(peeraddr and nixio.getaddrinfo(peeraddr) or {}) do + local peerdev = addr2dev(ai.address) + if peerdev then + for _, iface in ipairs(ifaces.interface) do + if type(iface) == "table" and + (iface.device == peerdev or iface.l3_device == peerdev) + then + inifs[iface.interface] = true + indevs[peerdev] = true + end + end end end end end end - if netmd:get_network(iface) then - luci.sys.call("env -i /sbin/ifdown %s >/dev/null 2>/dev/null" - % luci.util.shellquote(iface)) - luci.http.status(200, "Shut down") - return + for k in pairs(inifs) do + result.inbound_interfaces[#result.inbound_interfaces + 1] = k + end + + for k in pairs(indevs) do + result.inbound_devices[#result.inbound_devices + 1] = k end - luci.http.status(404, "No such interface") + luci.http.prepare_content("application/json") + luci.http.write_json(result) end function diag_command(cmd, addr) diff --git a/modules/luci-mod-status/htdocs/luci-static/resources/view/status/bandwidth.js b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/bandwidth.js new file mode 100644 index 0000000000..0565490b9d --- /dev/null +++ b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/bandwidth.js @@ -0,0 +1,311 @@ +'use strict'; +'require rpc'; +'require network'; + +var callLuciRealtimeStats = rpc.declare({ + object: 'luci', + method: 'getRealtimeStats', + params: [ 'mode', 'device' ], + expect: { result: [] } +}); + +var graphPolls = [], + pollInterval = 3; + +Math.log2 = Math.log2 || function(x) { return Math.log(x) * Math.LOG2E; }; + +function rate(n, br) { + n = (n || 0).toFixed(2); + return [ '%1024.2mbit/s'.format(n * 8), br ? E('br') : ' ', '(%1024.2mB/s)'.format(n) ] +} + +return L.view.extend({ + load: function() { + return Promise.all([ + this.loadSVG(L.resource('bandwidth.svg')), + network.getDevices() + ]); + }, + + updateGraph: function(ifname, svg, lines, cb) { + var G = svg.firstElementChild; + + var view = document.querySelector('#view'); + + var width = view.offsetWidth - 2; + var height = 300 - 2; + var step = 5; + + var data_wanted = Math.floor(width / step); + + var data_values = [], + line_elements = []; + + for (var i = 0; i < lines.length; i++) + if (lines[i] != null) + data_values.push([]); + + var info = { + line_current: [], + line_average: [], + line_peak: [] + }; + + /* prefill datasets */ + for (var i = 0; i < data_values.length; i++) + for (var j = 0; j < data_wanted; j++) + data_values[i][j] = 0; + + /* plot horizontal time interval lines */ + for (var i = width % (step * 60); i < width; i += step * 60) { + var line = document.createElementNS('http://www.w3.org/2000/svg', 'line'); + line.setAttribute('x1', i); + line.setAttribute('y1', 0); + line.setAttribute('x2', i); + line.setAttribute('y2', '100%'); + line.setAttribute('style', 'stroke:black;stroke-width:0.1'); + + var text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); + text.setAttribute('x', i + 5); + text.setAttribute('y', 15); + text.setAttribute('style', 'fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000'); + text.appendChild(document.createTextNode(Math.round((width - i) / step / 60) + 'm')); + + G.appendChild(line); + G.appendChild(text); + } + + info.interval = pollInterval; + info.timeframe = data_wanted / 60; + + graphPolls.push({ + ifname: ifname, + svg: svg, + lines: lines, + cb: cb, + info: info, + width: width, + height: height, + step: step, + values: data_values, + timestamp: 0, + fill: 1 + }); + }, + + pollData: function() { + L.Poll.add(L.bind(function() { + var tasks = []; + + for (var i = 0; i < graphPolls.length; i++) { + var ctx = graphPolls[i]; + tasks.push(L.resolveDefault(callLuciRealtimeStats('interface', ctx.ifname), [])); + } + + return Promise.all(tasks).then(L.bind(function(datasets) { + for (var gi = 0; gi < graphPolls.length; gi++) { + var ctx = graphPolls[gi], + data = datasets[gi], + values = ctx.values, + lines = ctx.lines, + info = ctx.info; + + var data_scale = 0; + var data_wanted = Math.floor(ctx.width / ctx.step); + var last_timestamp = NaN; + + for (var i = 0, di = 0; di < lines.length; di++) { + if (lines[di] == null) + continue; + + var multiply = (lines[di].multiply != null) ? lines[di].multiply : 1, + offset = (lines[di].offset != null) ? lines[di].offset : 0; + + for (var j = ctx.timestamp ? 0 : 1; j < data.length; j++) { + /* skip overlapping entries */ + if (data[j][0] <= ctx.timestamp) + continue; + + if (i == 0) { + ctx.fill++; + last_timestamp = data[j][0]; + } + + if (lines[di].counter) { + /* normalize difference against time interval */ + if (j > 0) { + var time_delta = data[j][0] - data[j - 1][0]; + if (time_delta) { + info.line_current[i] = (data[j][di + 1] * multiply - data[j - 1][di + 1] * multiply) / time_delta; + info.line_current[i] -= Math.min(info.line_current[i], offset); + values[i].push(info.line_current[i]); + } + } + } + else { + info.line_current[i] = data[j][di + 1] * multiply; + info.line_current[i] -= Math.min(info.line_current[i], offset); + values[i].push(info.line_current[i]); + } + } + + i++; + } + + /* cut off outdated entries */ + ctx.fill = Math.min(ctx.fill, data_wanted); + + for (var i = 0; i < values.length; i++) { + var len = values[i].length; + values[i] = values[i].slice(len - data_wanted, len); + + /* find peaks, averages */ + info.line_peak[i] = NaN; + info.line_average[i] = 0; + + for (var j = 0; j < values[i].length; j++) { + info.line_peak[i] = isNaN(info.line_peak[i]) ? values[i][j] : Math.max(info.line_peak[i], values[i][j]); + info.line_average[i] += values[i][j]; + } + + info.line_average[i] = info.line_average[i] / ctx.fill; + } + + info.peak = Math.max.apply(Math, info.line_peak); + + /* remember current timestamp, calculate horizontal scale */ + if (!isNaN(last_timestamp)) + ctx.timestamp = last_timestamp; + + var size = Math.floor(Math.log2(info.peak)), + div = Math.pow(2, size - (size % 10)), + mult = info.peak / div, + mult = (mult < 5) ? 2 : ((mult < 50) ? 10 : ((mult < 500) ? 100 : 1000)); + + info.peak = info.peak + (mult * div) - (info.peak % (mult * div)); + + data_scale = ctx.height / info.peak; + + /* plot data */ + for (var i = 0, di = 0; di < lines.length; di++) { + if (lines[di] == null) + continue; + + var el = ctx.svg.firstElementChild.getElementById(lines[di].line), + pt = '0,' + ctx.height, + y = 0; + + if (!el) + continue; + + for (var j = 0; j < values[i].length; j++) { + var x = j * ctx.step; + + y = ctx.height - Math.floor(values[i][j] * data_scale); + //y -= Math.floor(y % (1 / data_scale)); + + pt += ' ' + x + ',' + y; + } + + pt += ' ' + ctx.width + ',' + y + ' ' + ctx.width + ',' + ctx.height; + + el.setAttribute('points', pt); + + i++; + } + + info.label_25 = 0.25 * info.peak; + info.label_50 = 0.50 * info.peak; + info.label_75 = 0.75 * info.peak; + + if (typeof(ctx.cb) == 'function') + ctx.cb(ctx.svg, info); + } + }, this)); + }, this), pollInterval); + }, + + loadSVG: function(src) { + return L.Request.get(src).then(function(response) { + if (!response.ok) + throw new Error(response.statusText); + + return E('div', { + 'style': 'width:100%;height:300px;border:1px solid #000;background:#fff' + }, response.text()); + }); + }, + + render: function(data) { + var svg = data[0], + devs = data[1]; + + var v = E('div', {}, E('div')); + + for (var i = 0; i < devs.length; i++) { + var ifname = devs[i].getName(); + + if (!ifname) + continue; + + var csvg = svg.cloneNode(true); + + v.firstElementChild.appendChild(E('div', { 'data-tab': ifname, 'data-tab-title': ifname }, [ + csvg, + E('div', { 'class': 'right' }, E('small', { 'id': 'scale' }, '-')), + E('br'), + + E('div', { 'class': 'table', 'style': 'width:100%;table-layout:fixed' }, [ + E('div', { 'class': 'tr' }, [ + E('div', { 'class': 'td right top' }, E('strong', { 'style': 'border-bottom:2px solid blue' }, [ _('Inbound:') ])), + E('div', { 'class': 'td', 'id': 'rx_bw_cur' }, rate(0, true)), + + E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Average:') ])), + E('div', { 'class': 'td', 'id': 'rx_bw_avg' }, rate(0, true)), + + E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Peak:') ])), + E('div', { 'class': 'td', 'id': 'rx_bw_peak' }, rate(0, true)) + ]), + E('div', { 'class': 'tr' }, [ + E('div', { 'class': 'td right top' }, E('strong', { 'style': 'border-bottom:2px solid green' }, [ _('Outbound:') ])), + E('div', { 'class': 'td', 'id': 'tx_bw_cur' }, rate(0, true)), + + E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Average:') ])), + E('div', { 'class': 'td', 'id': 'tx_bw_avg' }, rate(0, true)), + + E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Peak:') ])), + E('div', { 'class': 'td', 'id': 'tx_bw_peak' }, rate(0, true)) + ]) + ]) + ])); + + this.updateGraph(ifname, csvg, [ { line: 'rx', counter: true }, null, { line: 'tx', counter: true } ], function(svg, info) { + var G = svg.firstElementChild, tab = svg.parentNode; + + G.getElementById('label_25').firstChild.data = rate(info.label_25).join(''); + G.getElementById('label_50').firstChild.data = rate(info.label_50).join(''); + G.getElementById('label_75').firstChild.data = rate(info.label_75).join(''); + + tab.querySelector('#scale').firstChild.data = _('(%d minute window, %d second interval)').format(info.timeframe, info.interval); + + L.dom.content(tab.querySelector('#rx_bw_cur'), rate(info.line_current[0], true)); + L.dom.content(tab.querySelector('#rx_bw_avg'), rate(info.line_average[0], true)); + L.dom.content(tab.querySelector('#rx_bw_peak'), rate(info.line_peak[0], true)); + + L.dom.content(tab.querySelector('#tx_bw_cur'), rate(info.line_current[1], true)); + L.dom.content(tab.querySelector('#tx_bw_avg'), rate(info.line_average[1], true)); + L.dom.content(tab.querySelector('#tx_bw_peak'), rate(info.line_peak[1], true)); + }); + } + + L.ui.tabs.initTabGroup(v.firstElementChild.childNodes); + + this.pollData(); + + return v; + }, + + handleSaveApply: null, + handleSave: null, + handleReset: null +}); diff --git a/modules/luci-mod-status/htdocs/luci-static/resources/view/status/connections.js b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/connections.js new file mode 100644 index 0000000000..96dee1db77 --- /dev/null +++ b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/connections.js @@ -0,0 +1,423 @@ +'use strict'; +'require rpc'; + +var callLuciRealtimeStats = rpc.declare({ + object: 'luci', + method: 'getRealtimeStats', + params: [ 'mode', 'device' ], + expect: { result: [] } +}); + +var callLuciConntrackList = rpc.declare({ + object: 'luci', + method: 'getConntrackList', + expect: { result: [] } +}); + +var callNetworkRrdnsLookup = rpc.declare({ + object: 'network.rrdns', + method: 'lookup', + params: [ 'addrs', 'timeout', 'limit' ], + expect: { '': {} } +}); + +var graphPolls = [], + pollInterval = 3, + dns_cache = {}, + enableLookups = false; + +var recheck_lookup_queue = {}; + +Math.log2 = Math.log2 || function(x) { return Math.log(x) * Math.LOG2E; }; + +return L.view.extend({ + load: function() { + return Promise.all([ + this.loadSVG(L.resource('connections.svg')) + ]); + }, + + updateGraph: function(svg, lines, cb) { + var G = svg.firstElementChild; + + var view = document.querySelector('#view'); + + var width = view.offsetWidth - 2; + var height = 300 - 2; + var step = 5; + + var data_wanted = Math.floor(width / step); + + var data_values = [], + line_elements = []; + + for (var i = 0; i < lines.length; i++) + if (lines[i] != null) + data_values.push([]); + + var info = { + line_current: [], + line_average: [], + line_peak: [] + }; + + /* prefill datasets */ + for (var i = 0; i < data_values.length; i++) + for (var j = 0; j < data_wanted; j++) + data_values[i][j] = 0; + + /* plot horizontal time interval lines */ + for (var i = width % (step * 60); i < width; i += step * 60) { + var line = document.createElementNS('http://www.w3.org/2000/svg', 'line'); + line.setAttribute('x1', i); + line.setAttribute('y1', 0); + line.setAttribute('x2', i); + line.setAttribute('y2', '100%'); + line.setAttribute('style', 'stroke:black;stroke-width:0.1'); + + var text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); + text.setAttribute('x', i + 5); + text.setAttribute('y', 15); + text.setAttribute('style', 'fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000'); + text.appendChild(document.createTextNode(Math.round((width - i) / step / 60) + 'm')); + + G.appendChild(line); + G.appendChild(text); + } + + info.interval = pollInterval; + info.timeframe = data_wanted / 60; + + graphPolls.push({ + svg: svg, + lines: lines, + cb: cb, + info: info, + width: width, + height: height, + step: step, + values: data_values, + timestamp: 0, + fill: 1 + }); + }, + + updateConntrack: function(conn) { + var lookup_queue = [ ]; + var rows = []; + + conn.sort(function(a, b) { + return b.bytes - a.bytes; + }); + + for (var i = 0; i < conn.length; i++) + { + var c = conn[i]; + + if ((c.src == '127.0.0.1' && c.dst == '127.0.0.1') || + (c.src == '::1' && c.dst == '::1')) + continue; + + if (!dns_cache[c.src] && lookup_queue.indexOf(c.src) == -1) + lookup_queue.push(c.src); + + if (!dns_cache[c.dst] && lookup_queue.indexOf(c.dst) == -1) + lookup_queue.push(c.dst); + + var src = dns_cache[c.src] || (c.layer3 == 'ipv6' ? '[' + c.src + ']' : c.src); + var dst = dns_cache[c.dst] || (c.layer3 == 'ipv6' ? '[' + c.dst + ']' : c.dst); + + rows.push([ + c.layer3.toUpperCase(), + c.layer4.toUpperCase(), + c.hasOwnProperty('sport') ? (src + ':' + c.sport) : src, + c.hasOwnProperty('dport') ? (dst + ':' + c.dport) : dst, + '%1024.2mB (%d %s)'.format(c.bytes, c.packets, _('Pkts.')) + ]); + } + + cbi_update_table('#connections', rows, E('em', _('No information available'))); + + if (enableLookups && lookup_queue.length > 0) { + var reduced_lookup_queue = lookup_queue; + + if (lookup_queue.length > 100) + reduced_lookup_queue = lookup_queue.slice(0, 100); + + callNetworkRrdnsLookup(reduced_lookup_queue, 5000, 1000).then(function(replies) { + for (var index in reduced_lookup_queue) { + var address = reduced_lookup_queue[index]; + + if (!address) + continue; + + if (replies[address]) { + dns_cache[address] = replies[address]; + lookup_queue.splice(reduced_lookup_queue.indexOf(address), 1); + continue; + } + + if (recheck_lookup_queue[address] > 2) { + dns_cache[address] = (address.match(/:/)) ? '[' + address + ']' : address; + lookup_queue.splice(index, 1); + } + else { + recheck_lookup_queue[address] = (recheck_lookup_queue[address] || 0) + 1; + } + } + + var btn = document.querySelector('.btn.toggle-lookups'); + if (btn) { + btn.firstChild.data = enableLookups ? _('Disable DNS lookups') : _('Enable DNS lookups'); + btn.classList.remove('spinning'); + btn.disabled = false; + } + }); + } + }, + + pollData: function() { + L.Poll.add(L.bind(function() { + var tasks = [ + L.resolveDefault(callLuciConntrackList(), []) + ]; + + for (var i = 0; i < graphPolls.length; i++) { + var ctx = graphPolls[i]; + tasks.push(L.resolveDefault(callLuciRealtimeStats('conntrack'), [])); + } + + return Promise.all(tasks).then(L.bind(function(datasets) { + this.updateConntrack(datasets[0]); + + for (var gi = 0; gi < graphPolls.length; gi++) { + var ctx = graphPolls[gi], + data = datasets[gi + 1], + values = ctx.values, + lines = ctx.lines, + info = ctx.info; + + var data_scale = 0; + var data_wanted = Math.floor(ctx.width / ctx.step); + var last_timestamp = NaN; + + for (var i = 0, di = 0; di < lines.length; di++) { + if (lines[di] == null) + continue; + + var multiply = (lines[di].multiply != null) ? lines[di].multiply : 1, + offset = (lines[di].offset != null) ? lines[di].offset : 0; + + for (var j = ctx.timestamp ? 0 : 1; j < data.length; j++) { + /* skip overlapping entries */ + if (data[j][0] <= ctx.timestamp) + continue; + + if (i == 0) { + ctx.fill++; + last_timestamp = data[j][0]; + } + + info.line_current[i] = data[j][di + 1] * multiply; + info.line_current[i] -= Math.min(info.line_current[i], offset); + values[i].push(info.line_current[i]); + } + + i++; + } + + /* cut off outdated entries */ + ctx.fill = Math.min(ctx.fill, data_wanted); + + for (var i = 0; i < values.length; i++) { + var len = values[i].length; + values[i] = values[i].slice(len - data_wanted, len); + + /* find peaks, averages */ + info.line_peak[i] = NaN; + info.line_average[i] = 0; + + for (var j = 0; j < values[i].length; j++) { + info.line_peak[i] = isNaN(info.line_peak[i]) ? values[i][j] : Math.max(info.line_peak[i], values[i][j]); + info.line_average[i] += values[i][j]; + } + + info.line_average[i] = info.line_average[i] / ctx.fill; + } + + info.peak = Math.max.apply(Math, info.line_peak); + + /* remember current timestamp, calculate horizontal scale */ + if (!isNaN(last_timestamp)) + ctx.timestamp = last_timestamp; + + var size = Math.floor(Math.log2(info.peak)), + div = Math.pow(2, size - (size % 10)), + mult = info.peak / div, + mult = (mult < 5) ? 2 : ((mult < 50) ? 10 : ((mult < 500) ? 100 : 1000)); + + info.peak = info.peak + (mult * div) - (info.peak % (mult * div)); + + data_scale = ctx.height / info.peak; + + /* plot data */ + for (var i = 0, di = 0; di < lines.length; di++) { + if (lines[di] == null) + continue; + + var el = ctx.svg.firstElementChild.getElementById(lines[di].line), + pt = '0,' + ctx.height, + y = 0; + + if (!el) + continue; + + for (var j = 0; j < values[i].length; j++) { + var x = j * ctx.step; + + y = ctx.height - Math.floor(values[i][j] * data_scale); + //y -= Math.floor(y % (1 / data_scale)); + + pt += ' ' + x + ',' + y; + } + + pt += ' ' + ctx.width + ',' + y + ' ' + ctx.width + ',' + ctx.height; + + el.setAttribute('points', pt); + + i++; + } + + info.label_25 = 0.25 * info.peak; + info.label_50 = 0.50 * info.peak; + info.label_75 = 0.75 * info.peak; + + if (typeof(ctx.cb) == 'function') + ctx.cb(ctx.svg, info); + } + }, this)); + }, this), pollInterval); + }, + + loadSVG: function(src) { + return L.Request.get(src).then(function(response) { + if (!response.ok) + throw new Error(response.statusText); + + return E('div', { + 'style': 'width:100%;height:300px;border:1px solid #000;background:#fff' + }, response.text()); + }); + }, + + render: function(data) { + var svg = data[0]; + + var v = E([], [ + svg, + E('div', { 'class': 'right' }, E('small', { 'id': 'scale' }, '-')), + E('br'), + + E('div', { 'class': 'table', 'style': 'width:100%;table-layout:fixed' }, [ + E('div', { 'class': 'tr' }, [ + E('div', { 'class': 'td right top' }, E('strong', { 'style': 'border-bottom:2px solid blue' }, [ _('UDP:') ])), + E('div', { 'class': 'td', 'id': 'lb_udp_cur' }, [ '0' ]), + + E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Average:') ])), + E('div', { 'class': 'td', 'id': 'lb_udp_avg' }, [ '0' ]), + + E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Peak:') ])), + E('div', { 'class': 'td', 'id': 'lb_udp_peak' }, [ '0' ]) + ]), + E('div', { 'class': 'tr' }, [ + E('div', { 'class': 'td right top' }, E('strong', { 'style': 'border-bottom:2px solid green' }, [ _('TCP:') ])), + E('div', { 'class': 'td', 'id': 'lb_tcp_cur' }, [ '0' ]), + + E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Average:') ])), + E('div', { 'class': 'td', 'id': 'lb_tcp_avg' }, [ '0' ]), + + E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Peak:') ])), + E('div', { 'class': 'td', 'id': 'lb_tcp_peak' }, [ '0' ]) + ]), + E('div', { 'class': 'tr' }, [ + E('div', { 'class': 'td right top' }, E('strong', { 'style': 'border-bottom:2px solid red' }, [ _('Other:') ])), + E('div', { 'class': 'td', 'id': 'lb_otr_cur' }, [ '0' ]), + + E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Average:') ])), + E('div', { 'class': 'td', 'id': 'lb_otr_avg' }, [ '0' ]), + + E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Peak:') ])), + E('div', { 'class': 'td', 'id': 'lb_otr_peak' }, [ '0' ]) + ]) + ]), + + E('div', { 'class': 'right' }, [ + E('button', { + 'class': 'btn toggle-lookups', + 'click': function(ev) { + if (!enableLookups) { + ev.currentTarget.classList.add('spinning'); + ev.currentTarget.disabled = true; + enableLookups = true; + } + else { + ev.currentTarget.firstChild.data = _('Enable DNS lookups'); + enableLookups = false; + } + + this.blur(); + } + }, [ enableLookups ? _('Disable DNS lookups') : _('Enable DNS lookups') ]) + ]), + + E('br'), + + E('div', { 'class': 'cbi-section-node' }, [ + E('div', { 'class': 'table', 'id': 'connections' }, [ + E('div', { 'class': 'tr table-titles' }, [ + E('div', { 'class': 'th col-2 hide-xs' }, [ _('Network') ]), + E('div', { 'class': 'th col-2' }, [ _('Protocol') ]), + E('div', { 'class': 'th col-7' }, [ _('Source') ]), + E('div', { 'class': 'th col-7' }, [ _('Destination') ]), + E('div', { 'class': 'th col-4' }, [ _('Transfer') ]) + ]), + E('div', { 'class': 'tr placeholder' }, [ + E('div', { 'class': 'td' }, [ + E('em', {}, [ _('Collecting data...') ]) + ]) + ]) + ]) + ]) + ]); + + this.updateGraph(svg, [ { line: 'udp' }, { line: 'tcp' }, { line: 'other' } ], function(svg, info) { + var G = svg.firstElementChild, tab = svg.parentNode; + + G.getElementById('label_25').firstChild.data = '%d'.format(info.label_25); + G.getElementById('label_50').firstChild.data = '%d'.format(info.label_50); + G.getElementById('label_75').firstChild.data = '%d'.format(info.label_75); + + tab.querySelector('#scale').firstChild.data = _('(%d minute window, %d second interval)').format(info.timeframe, info.interval); + + tab.querySelector('#lb_udp_cur').firstChild.data = '%d'.format(info.line_current[0]); + tab.querySelector('#lb_udp_avg').firstChild.data = '%d'.format(info.line_average[0]); + tab.querySelector('#lb_udp_peak').firstChild.data = '%d'.format(info.line_peak[0]); + + tab.querySelector('#lb_tcp_cur').firstChild.data = '%d'.format(info.line_current[1]); + tab.querySelector('#lb_tcp_avg').firstChild.data = '%d'.format(info.line_average[1]); + tab.querySelector('#lb_tcp_peak').firstChild.data = '%d'.format(info.line_peak[1]); + + tab.querySelector('#lb_otr_cur').firstChild.data = '%d'.format(info.line_current[2]); + tab.querySelector('#lb_otr_avg').firstChild.data = '%d'.format(info.line_average[2]); + tab.querySelector('#lb_otr_peak').firstChild.data = '%d'.format(info.line_peak[2]); + }); + + this.pollData(); + + return v; + }, + + handleSaveApply: null, + handleSave: null, + handleReset: null +}); diff --git a/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/20_memory.js b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/20_memory.js index 9baf0420d7..2e84774346 100644 --- a/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/20_memory.js +++ b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/20_memory.js @@ -31,11 +31,14 @@ return L.Class.extend({ swap = L.isObject(systeminfo.swap) ? systeminfo.swap : {}; var fields = [ - _('Total Available'), (mem.total && mem.free && mem.buffered) ? mem.free + mem.buffered : null, + _('Total Available'), (mem.available) ? mem.available : (mem.total && mem.free && mem.buffered) ? mem.free + mem.buffered : null, _('Free'), (mem.total && mem.free) ? mem.free : null, _('Buffered'), (mem.total && mem.buffered) ? mem.buffered : null ]; + if (mem.cached) + fields.push(_('Cached'), mem.cached); + if (swap.total > 0) fields.push(_('Swap free'), swap.free); diff --git a/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/60_wifi.js b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/60_wifi.js index 08a2f5de1c..8242711b6a 100644 --- a/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/60_wifi.js +++ b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/60_wifi.js @@ -3,10 +3,9 @@ 'require network'; function renderbox(radio) { - var net0 = radio.networks[0], - chan = net0 ? net0.getChannel() : null, - freq = net0 ? net0.getFrequency() : null, - rate = net0 ? net0.getBitRate() : null, + var chan = null, + freq = null, + rate = null, badges = []; for (var i = 0; i < radio.networks.length; i++) { @@ -42,6 +41,10 @@ function renderbox(radio) { badge.lastElementChild.style.textOverflow = 'ellipsis'; badges.push(badge); + + chan = (chan != null) ? chan : net.getChannel(); + freq = (freq != null) ? freq : net.getFrequency(); + rate = (rate != null) ? rate : net.getBitRate(); } return E('div', { class: 'ifacebox' }, [ diff --git a/modules/luci-mod-status/htdocs/luci-static/resources/view/status/load.js b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/load.js new file mode 100644 index 0000000000..a1ed43478d --- /dev/null +++ b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/load.js @@ -0,0 +1,290 @@ +'use strict'; +'require rpc'; + +var callLuciRealtimeStats = rpc.declare({ + object: 'luci', + method: 'getRealtimeStats', + params: [ 'mode', 'device' ], + expect: { result: [] } +}); + +var graphPolls = [], + pollInterval = 3; + +Math.log2 = Math.log2 || function(x) { return Math.log(x) * Math.LOG2E; }; + +return L.view.extend({ + load: function() { + return Promise.all([ + this.loadSVG(L.resource('load.svg')) + ]); + }, + + updateGraph: function(svg, lines, cb) { + var G = svg.firstElementChild; + + var view = document.querySelector('#view'); + + var width = view.offsetWidth - 2; + var height = 300 - 2; + var step = 5; + + var data_wanted = Math.floor(width / step); + + var data_values = [], + line_elements = []; + + for (var i = 0; i < lines.length; i++) + if (lines[i] != null) + data_values.push([]); + + var info = { + line_current: [], + line_average: [], + line_peak: [] + }; + + /* prefill datasets */ + for (var i = 0; i < data_values.length; i++) + for (var j = 0; j < data_wanted; j++) + data_values[i][j] = 0; + + /* plot horizontal time interval lines */ + for (var i = width % (step * 60); i < width; i += step * 60) { + var line = document.createElementNS('http://www.w3.org/2000/svg', 'line'); + line.setAttribute('x1', i); + line.setAttribute('y1', 0); + line.setAttribute('x2', i); + line.setAttribute('y2', '100%'); + line.setAttribute('style', 'stroke:black;stroke-width:0.1'); + + var text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); + text.setAttribute('x', i + 5); + text.setAttribute('y', 15); + text.setAttribute('style', 'fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000'); + text.appendChild(document.createTextNode(Math.round((width - i) / step / 60) + 'm')); + + G.appendChild(line); + G.appendChild(text); + } + + info.interval = pollInterval; + info.timeframe = data_wanted / 60; + + graphPolls.push({ + svg: svg, + lines: lines, + cb: cb, + info: info, + width: width, + height: height, + step: step, + values: data_values, + timestamp: 0, + fill: 1 + }); + }, + + pollData: function() { + L.Poll.add(L.bind(function() { + var tasks = []; + + for (var i = 0; i < graphPolls.length; i++) { + var ctx = graphPolls[i]; + tasks.push(L.resolveDefault(callLuciRealtimeStats('load'), [])); + } + + return Promise.all(tasks).then(L.bind(function(datasets) { + for (var gi = 0; gi < graphPolls.length; gi++) { + var ctx = graphPolls[gi], + data = datasets[gi], + values = ctx.values, + lines = ctx.lines, + info = ctx.info; + + var data_scale = 0; + var data_wanted = Math.floor(ctx.width / ctx.step); + var last_timestamp = NaN; + + for (var i = 0, di = 0; di < lines.length; di++) { + if (lines[di] == null) + continue; + + var multiply = (lines[di].multiply != null) ? lines[di].multiply : 1, + offset = (lines[di].offset != null) ? lines[di].offset : 0; + + for (var j = ctx.timestamp ? 0 : 1; j < data.length; j++) { + /* skip overlapping entries */ + if (data[j][0] <= ctx.timestamp) + continue; + + if (i == 0) { + ctx.fill++; + last_timestamp = data[j][0]; + } + + info.line_current[i] = data[j][di + 1] * multiply; + info.line_current[i] -= Math.min(info.line_current[i], offset); + values[i].push(info.line_current[i]); + } + + i++; + } + + /* cut off outdated entries */ + ctx.fill = Math.min(ctx.fill, data_wanted); + + for (var i = 0; i < values.length; i++) { + var len = values[i].length; + values[i] = values[i].slice(len - data_wanted, len); + + /* find peaks, averages */ + info.line_peak[i] = NaN; + info.line_average[i] = 0; + + for (var j = 0; j < values[i].length; j++) { + info.line_peak[i] = isNaN(info.line_peak[i]) ? values[i][j] : Math.max(info.line_peak[i], values[i][j]); + info.line_average[i] += values[i][j]; + } + + info.line_average[i] = info.line_average[i] / ctx.fill; + } + + info.peak = Math.max.apply(Math, info.line_peak); + + /* remember current timestamp, calculate horizontal scale */ + if (!isNaN(last_timestamp)) + ctx.timestamp = last_timestamp; + + var size = Math.floor(Math.log2(info.peak)), + div = Math.pow(2, size - (size % 10)), + mult = info.peak / div, + mult = (mult < 5) ? 2 : ((mult < 50) ? 10 : ((mult < 500) ? 100 : 1000)); + + info.peak = info.peak + (mult * div) - (info.peak % (mult * div)); + + data_scale = ctx.height / info.peak; + + /* plot data */ + for (var i = 0, di = 0; di < lines.length; di++) { + if (lines[di] == null) + continue; + + var el = ctx.svg.firstElementChild.getElementById(lines[di].line), + pt = '0,' + ctx.height, + y = 0; + + if (!el) + continue; + + for (var j = 0; j < values[i].length; j++) { + var x = j * ctx.step; + + y = ctx.height - Math.floor(values[i][j] * data_scale); + //y -= Math.floor(y % (1 / data_scale)); + + pt += ' ' + x + ',' + y; + } + + pt += ' ' + ctx.width + ',' + y + ' ' + ctx.width + ',' + ctx.height; + + el.setAttribute('points', pt); + + i++; + } + + info.label_25 = 0.25 * info.peak; + info.label_50 = 0.50 * info.peak; + info.label_75 = 0.75 * info.peak; + + if (typeof(ctx.cb) == 'function') + ctx.cb(ctx.svg, info); + } + }, this)); + }, this), pollInterval); + }, + + loadSVG: function(src) { + return L.Request.get(src).then(function(response) { + if (!response.ok) + throw new Error(response.statusText); + + return E('div', { + 'style': 'width:100%;height:300px;border:1px solid #000;background:#fff' + }, response.text()); + }); + }, + + render: function(data) { + var svg = data[0]; + + var v = E([], [ + svg, + E('div', { 'class': 'right' }, E('small', { 'id': 'scale' }, '-')), + E('br'), + + E('div', { 'class': 'table', 'style': 'width:100%;table-layout:fixed' }, [ + E('div', { 'class': 'tr' }, [ + E('div', { 'class': 'td right top' }, E('strong', { 'style': 'border-bottom:2px solid #f00' }, [ _('1 Minute Load:') ])), + E('div', { 'class': 'td', 'id': 'lb_load01_cur' }, [ '0.00' ]), + + E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Average:') ])), + E('div', { 'class': 'td', 'id': 'lb_load01_avg' }, [ '0.00' ]), + + E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Peak:') ])), + E('div', { 'class': 'td', 'id': 'lb_load01_peak' }, [ '0.00' ]) + ]), + E('div', { 'class': 'tr' }, [ + E('div', { 'class': 'td right top' }, E('strong', { 'style': 'border-bottom:2px solid #f60' }, [ _('5 Minute Load:') ])), + E('div', { 'class': 'td', 'id': 'lb_load05_cur' }, [ '0.00' ]), + + E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Average:') ])), + E('div', { 'class': 'td', 'id': 'lb_load05_avg' }, [ '0.00' ]), + + E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Peak:') ])), + E('div', { 'class': 'td', 'id': 'lb_load05_peak' }, [ '0.00' ]) + ]), + E('div', { 'class': 'tr' }, [ + E('div', { 'class': 'td right top' }, E('strong', { 'style': 'border-bottom:2px solid #fa0' }, [ _('15 Minute Load:') ])), + E('div', { 'class': 'td', 'id': 'lb_load15_cur' }, [ '0.00' ]), + + E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Average:') ])), + E('div', { 'class': 'td', 'id': 'lb_load15_avg' }, [ '0.00' ]), + + E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Peak:') ])), + E('div', { 'class': 'td', 'id': 'lb_load15_peak' }, [ '0.00' ]) + ]) + ]) + ]); + + this.updateGraph(svg, [ { line: 'load01' }, { line: 'load05' }, { line: 'load15' } ], function(svg, info) { + var G = svg.firstElementChild, tab = svg.parentNode; + + G.getElementById('label_25').firstChild.data = '%.2f'.format(info.label_25 / 100); + G.getElementById('label_50').firstChild.data = '%.2f'.format(info.label_50 / 100); + G.getElementById('label_75').firstChild.data = '%.2f'.format(info.label_75 / 100); + + tab.querySelector('#scale').firstChild.data = _('(%d minute window, %d second interval)').format(info.timeframe, info.interval); + + tab.querySelector('#lb_load01_cur').firstChild.data = '%.2f'.format(info.line_current[0] / 100); + tab.querySelector('#lb_load01_avg').firstChild.data = '%.2f'.format(info.line_average[0] / 100); + tab.querySelector('#lb_load01_peak').firstChild.data = '%.2f'.format(info.line_peak[0] / 100); + + tab.querySelector('#lb_load05_cur').firstChild.data = '%.2f'.format(info.line_current[1] / 100); + tab.querySelector('#lb_load05_avg').firstChild.data = '%.2f'.format(info.line_average[1] / 100); + tab.querySelector('#lb_load05_peak').firstChild.data = '%.2f'.format(info.line_peak[1] / 100); + + tab.querySelector('#lb_load15_cur').firstChild.data = '%.2f'.format(info.line_current[2] / 100); + tab.querySelector('#lb_load15_avg').firstChild.data = '%.2f'.format(info.line_average[2] / 100); + tab.querySelector('#lb_load15_peak').firstChild.data = '%.2f'.format(info.line_peak[2] / 100); + }); + + this.pollData(); + + return v; + }, + + handleSaveApply: null, + handleSave: null, + handleReset: null +}); diff --git a/modules/luci-mod-status/htdocs/luci-static/resources/view/status/wireless.js b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/wireless.js new file mode 100644 index 0000000000..45dd54a883 --- /dev/null +++ b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/wireless.js @@ -0,0 +1,339 @@ +'use strict'; +'require rpc'; +'require network'; + +var callLuciRealtimeStats = rpc.declare({ + object: 'luci', + method: 'getRealtimeStats', + params: [ 'mode', 'device' ], + expect: { result: [] } +}); + +var graphPolls = [], + pollInterval = 3; + +Math.log2 = Math.log2 || function(x) { return Math.log(x) * Math.LOG2E; }; + +return L.view.extend({ + load: function() { + return Promise.all([ + this.loadSVG(L.resource('wireless.svg')), + this.loadSVG(L.resource('wifirate.svg')), + network.getWifiDevices().then(function(radios) { + var tasks = [], all_networks = []; + + for (var i = 0; i < radios.length; i++) + tasks.push(radios[i].getWifiNetworks().then(function(networks) { + all_networks.push.apply(all_networks, networks); + })); + + return Promise.all(tasks).then(function() { + return all_networks; + }); + }) + ]); + }, + + updateGraph: function(ifname, svg, lines, cb) { + var G = svg.firstElementChild; + + var view = document.querySelector('#view'); + + var width = view.offsetWidth - 2; + var height = 300 - 2; + var step = 5; + + var data_wanted = Math.floor(width / step); + + var data_values = [], + line_elements = []; + + for (var i = 0; i < lines.length; i++) + if (lines[i] != null) + data_values.push([]); + + var info = { + line_current: [], + line_average: [], + line_peak: [] + }; + + /* prefill datasets */ + for (var i = 0; i < data_values.length; i++) + for (var j = 0; j < data_wanted; j++) + data_values[i][j] = 0; + + /* plot horizontal time interval lines */ + for (var i = width % (step * 60); i < width; i += step * 60) { + var line = document.createElementNS('http://www.w3.org/2000/svg', 'line'); + line.setAttribute('x1', i); + line.setAttribute('y1', 0); + line.setAttribute('x2', i); + line.setAttribute('y2', '100%'); + line.setAttribute('style', 'stroke:black;stroke-width:0.1'); + + var text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); + text.setAttribute('x', i + 5); + text.setAttribute('y', 15); + text.setAttribute('style', 'fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000'); + text.appendChild(document.createTextNode(Math.round((width - i) / step / 60) + 'm')); + + G.appendChild(line); + G.appendChild(text); + } + + info.interval = pollInterval; + info.timeframe = data_wanted / 60; + + graphPolls.push({ + ifname: ifname, + svg: svg, + lines: lines, + cb: cb, + info: info, + width: width, + height: height, + step: step, + values: data_values, + timestamp: 0, + fill: 1 + }); + }, + + pollData: function() { + L.Poll.add(L.bind(function() { + var tasks = []; + + for (var i = 0; i < graphPolls.length; i++) { + var ctx = graphPolls[i]; + tasks.push(L.resolveDefault(callLuciRealtimeStats('wireless', ctx.ifname), [])); + } + + return Promise.all(tasks).then(L.bind(function(datasets) { + for (var gi = 0; gi < graphPolls.length; gi++) { + var ctx = graphPolls[gi], + data = datasets[gi], + values = ctx.values, + lines = ctx.lines, + info = ctx.info; + + var data_scale = 0; + var data_wanted = Math.floor(ctx.width / ctx.step); + var last_timestamp = NaN; + + for (var i = 0, di = 0; di < lines.length; di++) { + if (lines[di] == null) + continue; + + var multiply = (lines[di].multiply != null) ? lines[di].multiply : 1, + offset = (lines[di].offset != null) ? lines[di].offset : 0; + + for (var j = ctx.timestamp ? 0 : 1; j < data.length; j++) { + /* skip overlapping entries */ + if (data[j][0] <= ctx.timestamp) + continue; + + if (i == 0) { + ctx.fill++; + last_timestamp = data[j][0]; + } + + info.line_current[i] = data[j][di + 1] * multiply; + info.line_current[i] -= Math.min(info.line_current[i], offset); + values[i].push(info.line_current[i]); + } + + i++; + } + + /* cut off outdated entries */ + ctx.fill = Math.min(ctx.fill, data_wanted); + + for (var i = 0; i < values.length; i++) { + var len = values[i].length; + values[i] = values[i].slice(len - data_wanted, len); + + /* find peaks, averages */ + info.line_peak[i] = NaN; + info.line_average[i] = 0; + + for (var j = 0; j < values[i].length; j++) { + info.line_peak[i] = isNaN(info.line_peak[i]) ? values[i][j] : Math.max(info.line_peak[i], values[i][j]); + info.line_average[i] += values[i][j]; + } + + info.line_average[i] = info.line_average[i] / ctx.fill; + } + + info.peak = Math.max.apply(Math, info.line_peak); + + /* remember current timestamp, calculate horizontal scale */ + if (!isNaN(last_timestamp)) + ctx.timestamp = last_timestamp; + + var size = Math.floor(Math.log2(info.peak)), + div = Math.pow(2, size - (size % 10)), + mult = info.peak / div, + mult = (mult < 5) ? 2 : ((mult < 50) ? 10 : ((mult < 500) ? 100 : 1000)); + + info.peak = info.peak + (mult * div) - (info.peak % (mult * div)); + + data_scale = ctx.height / info.peak; + + /* plot data */ + for (var i = 0, di = 0; di < lines.length; di++) { + if (lines[di] == null) + continue; + + var el = ctx.svg.firstElementChild.getElementById(lines[di].line), + pt = '0,' + ctx.height, + y = 0; + + if (!el) + continue; + + for (var j = 0; j < values[i].length; j++) { + var x = j * ctx.step; + + y = ctx.height - Math.floor(values[i][j] * data_scale); + //y -= Math.floor(y % (1 / data_scale)); + + pt += ' ' + x + ',' + y; + } + + pt += ' ' + ctx.width + ',' + y + ' ' + ctx.width + ',' + ctx.height; + + el.setAttribute('points', pt); + + i++; + } + + info.label_25 = 0.25 * info.peak; + info.label_50 = 0.50 * info.peak; + info.label_75 = 0.75 * info.peak; + + if (typeof(ctx.cb) == 'function') + ctx.cb(ctx.svg, info); + } + }, this)); + }, this), pollInterval); + }, + + loadSVG: function(src) { + return L.Request.get(src).then(function(response) { + if (!response.ok) + throw new Error(response.statusText); + + return E('div', { + 'style': 'width:100%;height:300px;border:1px solid #000;background:#fff' + }, response.text()); + }); + }, + + render: function(data) { + var svg1 = data[0], + svg2 = data[1], + wifidevs = data[2]; + + var v = E('div', {}, E('div')); + + for (var i = 0; i < wifidevs.length; i++) { + var ifname = wifidevs[i].getIfname(); + + if (!ifname) + continue; + + var csvg1 = svg1.cloneNode(true), + csvg2 = svg2.cloneNode(true); + + v.firstElementChild.appendChild(E('div', { 'data-tab': ifname, 'data-tab-title': ifname }, [ + csvg1, + E('div', { 'class': 'right' }, E('small', { 'id': 'scale' }, '-')), + E('br'), + + E('div', { 'class': 'table', 'style': 'width:100%;table-layout:fixed' }, [ + E('div', { 'class': 'tr' }, [ + E('div', { 'class': 'td right top' }, E('strong', { 'style': 'border-bottom:2px solid blue' }, [ _('Signal:') ])), + E('div', { 'class': 'td', 'id': 'rssi_bw_cur' }, [ '0 ' + _('dBm') ]), + + E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Average:') ])), + E('div', { 'class': 'td', 'id': 'rssi_bw_avg' }, [ '0 ' + _('dBm') ]), + + E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Peak:') ])), + E('div', { 'class': 'td', 'id': 'rssi_bw_peak' }, [ '0 ' + _('dBm') ]) + ]), + E('div', { 'class': 'tr' }, [ + E('div', { 'class': 'td right top' }, E('strong', { 'style': 'border-bottom:2px solid red' }, [ _('Noise:') ])), + E('div', { 'class': 'td', 'id': 'noise_bw_cur' }, [ '0 ' + _('dBm') ]), + + E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Average:') ])), + E('div', { 'class': 'td', 'id': 'noise_bw_avg' }, [ '0 ' + _('dBm') ]), + + E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Peak:') ])), + E('div', { 'class': 'td', 'id': 'noise_bw_peak' }, [ '0 ' + _('dBm') ]) + ]) + ]), + E('br'), + + csvg2, + E('div', { 'class': 'right' }, E('small', { 'id': 'scale2' }, '-')), + E('br'), + + E('div', { 'class': 'table', 'style': 'width:100%;table-layout:fixed' }, [ + E('div', { 'class': 'tr' }, [ + E('div', { 'class': 'td right top' }, E('strong', { 'style': 'border-bottom:2px solid green' }, [ _('Phy Rate:') ])), + E('div', { 'class': 'td', 'id': 'rate_bw_cur' }, [ '0 MBit/s' ]), + + E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Average:') ])), + E('div', { 'class': 'td', 'id': 'rate_bw_avg' }, [ '0 MBit/s' ]), + + E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Peak:') ])), + E('div', { 'class': 'td', 'id': 'rate_bw_peak' }, [ '0 MBit/s' ]) + ]) + ]) + ])); + + this.updateGraph(ifname, csvg1, [ null, { line: 'rssi', offset: 155 }, { line: 'noise', offset: 155 } ], function(svg, info) { + var G = svg.firstElementChild, tab = svg.parentNode; + + G.getElementById('label_25').firstChild.data = '%d %s'.format(info.label_25 - 100, _('dBm')); + G.getElementById('label_50').firstChild.data = '%d %s'.format(info.label_50 - 100, _('dBm')); + G.getElementById('label_75').firstChild.data = '%d %s'.format(info.label_75 - 100, _('dBm')); + + tab.querySelector('#scale').firstChild.data = _('(%d minute window, %d second interval)').format(info.timeframe, info.interval); + + tab.querySelector('#rssi_bw_cur').firstChild.data = '%d %s'.format(info.line_current[0] - 100, _('dBm')); + tab.querySelector('#rssi_bw_avg').firstChild.data = '%d %s'.format(info.line_average[0] - 100, _('dBm')); + tab.querySelector('#rssi_bw_peak').firstChild.data = '%d %s'.format(info.line_peak[0] - 100, _('dBm')); + + tab.querySelector('#noise_bw_cur').firstChild.data = '%d %s'.format(info.line_current[1] - 100, _('dBm')); + tab.querySelector('#noise_bw_avg').firstChild.data = '%d %s'.format(info.line_average[1] - 100, _('dBm')); + tab.querySelector('#noise_bw_peak').firstChild.data = '%d %s'.format(info.line_peak[1] - 100, _('dBm')); + }); + + this.updateGraph(ifname, csvg2, [ { line: 'rate', multiply: 0.001 } ], function(svg, info) { + var G = svg.firstElementChild, tab = svg.parentNode; + + G.getElementById('label_25').firstChild.data = '%.2f %s'.format(info.label_25, _('MBit/s')); + G.getElementById('label_50').firstChild.data = '%.2f %s'.format(info.label_50, _('MBit/s')); + G.getElementById('label_75').firstChild.data = '%.2f %s'.format(info.label_75, _('MBit/s')); + + tab.querySelector('#scale2').firstChild.data = _('(%d minute window, %d second interval)').format(info.timeframe, info.interval); + + tab.querySelector('#rate_bw_cur').firstChild.data = '%d %s'.format(info.line_current[0], _('Mbit/s')); + tab.querySelector('#rate_bw_avg').firstChild.data = '%d %s'.format(info.line_average[0], _('Mbit/s')); + tab.querySelector('#rate_bw_peak').firstChild.data = '%d %s'.format(info.line_peak[0], _('Mbit/s')); + }); + } + + L.ui.tabs.initTabGroup(v.firstElementChild.childNodes); + + this.pollData(); + + return v; + }, + + handleSaveApply: null, + handleSave: null, + handleReset: null +}); diff --git a/modules/luci-mod-status/luasrc/controller/admin/status.lua b/modules/luci-mod-status/luasrc/controller/admin/status.lua index d09cb6e2f7..e888ccf097 100644 --- a/modules/luci-mod-status/luasrc/controller/admin/status.lua +++ b/modules/luci-mod-status/luasrc/controller/admin/status.lua @@ -20,22 +20,10 @@ function index() entry({"admin", "status", "realtime"}, alias("admin", "status", "realtime", "load"), _("Realtime Graphs"), 7) - entry({"admin", "status", "realtime", "load"}, template("admin_status/load"), _("Load"), 1).leaf = true - entry({"admin", "status", "realtime", "load_status"}, call("action_load")).leaf = true - - entry({"admin", "status", "realtime", "bandwidth"}, template("admin_status/bandwidth"), _("Traffic"), 2).leaf = true - entry({"admin", "status", "realtime", "bandwidth_status"}, call("action_bandwidth")).leaf = true - - page = entry({"admin", "status", "realtime", "wireless"}, template("admin_status/wireless"), _("Wireless"), 3) - page.uci_depends = { wireless = true } - page.leaf = true - - page = entry({"admin", "status", "realtime", "wireless_status"}, call("action_wireless")) - page.uci_depends = { wireless = true } - page.leaf = true - - entry({"admin", "status", "realtime", "connections"}, template("admin_status/connections"), _("Connections"), 4).leaf = true - entry({"admin", "status", "realtime", "connections_status"}, call("action_connections")).leaf = true + entry({"admin", "status", "realtime", "load"}, view("status/load"), _("Load"), 1) + entry({"admin", "status", "realtime", "bandwidth"}, view("status/bandwidth"), _("Traffic"), 2) + entry({"admin", "status", "realtime", "wireless"}, view("status/wireless"), _("Wireless"), 3).uci_depends = { wireless = true } + entry({"admin", "status", "realtime", "connections"}, view("status/connections"), _("Connections"), 4) entry({"admin", "status", "nameinfo"}, call("action_nameinfo")).leaf = true end @@ -84,97 +72,3 @@ function action_iptables() luci.http.redirect(luci.dispatcher.build_url("admin/status/iptables")) end - -function action_bandwidth(iface) - luci.http.prepare_content("application/json") - - local bwc = io.popen("luci-bwc -i %s 2>/dev/null" - % luci.util.shellquote(iface)) - - if bwc then - luci.http.write("[") - - while true do - local ln = bwc:read("*l") - if not ln then break end - luci.http.write(ln) - end - - luci.http.write("]") - bwc:close() - end -end - -function action_wireless(iface) - luci.http.prepare_content("application/json") - - local bwc = io.popen("luci-bwc -r %s 2>/dev/null" - % luci.util.shellquote(iface)) - - if bwc then - luci.http.write("[") - - while true do - local ln = bwc:read("*l") - if not ln then break end - luci.http.write(ln) - end - - luci.http.write("]") - bwc:close() - end -end - -function action_load() - luci.http.prepare_content("application/json") - - local bwc = io.popen("luci-bwc -l 2>/dev/null") - if bwc then - luci.http.write("[") - - while true do - local ln = bwc:read("*l") - if not ln then break end - luci.http.write(ln) - end - - luci.http.write("]") - bwc:close() - end -end - -function action_connections() - local sys = require "luci.sys" - - luci.http.prepare_content("application/json") - - luci.http.write('{ "connections": ') - luci.http.write_json(sys.net.conntrack()) - - local bwc = io.popen("luci-bwc -c 2>/dev/null") - if bwc then - luci.http.write(', "statistics": [') - - while true do - local ln = bwc:read("*l") - if not ln then break end - luci.http.write(ln) - end - - luci.http.write("]") - bwc:close() - end - - luci.http.write(" }") -end - -function action_nameinfo(...) - local util = require "luci.util" - - luci.http.prepare_content("application/json") - luci.http.write_json(util.ubus("network.rrdns", "lookup", { - addrs = { ... }, - timeout = 5000, - limit = 1000 - }) or { }) -end diff --git a/modules/luci-mod-status/luasrc/view/admin_status/bandwidth.htm b/modules/luci-mod-status/luasrc/view/admin_status/bandwidth.htm deleted file mode 100644 index 5cc661ad17..0000000000 --- a/modules/luci-mod-status/luasrc/view/admin_status/bandwidth.htm +++ /dev/null @@ -1,308 +0,0 @@ -<%# - Copyright 2010-2018 Jo-Philipp Wich <jo@mein.io> - Licensed to the public under the Apache License 2.0. --%> - -<%- - local ntm = require "luci.model.network".init() - - local dev - local devices = { } - for _, dev in luci.util.vspairs(luci.sys.net.devices()) do - if dev ~= "lo" and not ntm:ignore_interface(dev) then - devices[#devices+1] = dev - end - end - - local curdev = luci.http.formvalue("dev") or devices[1] --%> - -<%+header%> - -<script type="text/javascript">//<![CDATA[ - var bwxhr = new XHR(); - - var G; - var TIME = 0; - var RXB = 1; - var RXP = 2; - var TXB = 3; - var TXP = 4; - - var width = 760; - var height = 300; - var step = 5; - - var data_wanted = Math.floor(width / step); - var data_fill = 1; - var data_stamp = 0; - - var data_rx = [ ]; - var data_tx = [ ]; - - var line_rx; - var line_tx; - - var label_25; - var label_50; - var label_75; - - var label_rx_cur; - var label_rx_avg; - var label_rx_peak; - - var label_tx_cur; - var label_tx_avg; - var label_tx_peak; - - var label_scale; - - - Math.log2 = Math.log2 || function(x) { return Math.log(x) * Math.LOG2E; }; - - function bandwidth_label(bytes, br) - { - var uby = '<%:kB/s%>'; - var kby = (bytes / 1024); - - if (kby >= 1024) - { - uby = '<%:MB/s%>'; - kby = kby / 1024; - } - - var ubi = '<%:kbit/s%>'; - var kbi = (bytes * 8 / 1024); - - if (kbi >= 1024) - { - ubi = '<%:Mbit/s%>'; - kbi = kbi / 1024; - } - - return String.format("%f %s%s(%f %s)", - kbi.toFixed(2), ubi, - br ? '<br />' : ' ', - kby.toFixed(2), uby - ); - } - - /* wait for SVG */ - window.setTimeout( - function() { - var svg = document.getElementById('bwsvg'); - - try { - G = svg.getSVGDocument - ? svg.getSVGDocument() : svg.contentDocument; - } - catch(e) { - G = document.embeds['bwsvg'].getSVGDocument(); - } - - if (!G) - { - window.setTimeout(arguments.callee, 1000); - } - else - { - /* find sizes */ - width = svg.offsetWidth - 2; - height = svg.offsetHeight - 2; - data_wanted = Math.ceil(width / step); - - /* prefill datasets */ - for (var i = 0; i < data_wanted; i++) - { - data_rx[i] = 0; - data_tx[i] = 0; - } - - /* find svg elements */ - line_rx = G.getElementById('rx'); - line_tx = G.getElementById('tx'); - - label_25 = G.getElementById('label_25'); - label_50 = G.getElementById('label_50'); - label_75 = G.getElementById('label_75'); - - label_rx_cur = document.getElementById('rx_bw_cur'); - label_rx_avg = document.getElementById('rx_bw_avg'); - label_rx_peak = document.getElementById('rx_bw_peak'); - - label_tx_cur = document.getElementById('tx_bw_cur'); - label_tx_avg = document.getElementById('tx_bw_avg'); - label_tx_peak = document.getElementById('tx_bw_peak'); - - label_scale = document.getElementById('scale'); - - - /* plot horizontal time interval lines */ - for (var i = width % (step * 60); i < width; i += step * 60) - { - var line = G.createElementNS('http://www.w3.org/2000/svg', 'line'); - line.setAttribute('x1', i); - line.setAttribute('y1', 0); - line.setAttribute('x2', i); - line.setAttribute('y2', '100%'); - line.setAttribute('style', 'stroke:black;stroke-width:0.1'); - - var text = G.createElementNS('http://www.w3.org/2000/svg', 'text'); - text.setAttribute('x', i + 5); - text.setAttribute('y', 15); - text.setAttribute('style', 'fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000'); - text.appendChild(G.createTextNode(Math.round((width - i) / step / 60) + 'm')); - - label_25.parentNode.appendChild(line); - label_25.parentNode.appendChild(text); - } - - label_scale.innerHTML = String.format('<%:(%d minute window, %d second interval)%>', data_wanted / 60, 3); - - /* render datasets, start update interval */ - XHR.poll(3, '<%=build_url("admin/status/realtime/bandwidth_status", curdev)%>', null, - function(x, data) - { - var data_max = 0; - var data_scale = 0; - - var data_rx_avg = 0; - var data_tx_avg = 0; - - var data_rx_peak = 0; - var data_tx_peak = 0; - - for (var i = data_stamp ? 0 : 1; i < data.length; i++) - { - /* skip overlapping entries */ - if (data[i][TIME] <= data_stamp) - continue; - - data_fill++; - - /* normalize difference against time interval */ - if (i > 0) - { - var time_delta = data[i][TIME] - data[i-1][TIME]; - if (time_delta) - { - data_rx.push((data[i][RXB] - data[i-1][RXB]) / time_delta); - data_tx.push((data[i][TXB] - data[i-1][TXB]) / time_delta); - } - } - } - - /* cut off outdated entries */ - data_rx = data_rx.slice(data_rx.length - data_wanted, data_rx.length); - data_tx = data_tx.slice(data_tx.length - data_wanted, data_tx.length); - data_fill = Math.min(data_fill, data_wanted); - - /* find peak */ - for (var i = 0; i < data_rx.length; i++) - { - data_max = Math.max(data_max, data_rx[i]); - data_max = Math.max(data_max, data_tx[i]); - - data_rx_peak = Math.max(data_rx_peak, data_rx[i]); - data_tx_peak = Math.max(data_tx_peak, data_tx[i]); - - data_rx_avg += data_rx[i]; - data_tx_avg += data_tx[i]; - } - - data_rx_avg = (data_rx_avg / data_fill); - data_tx_avg = (data_tx_avg / data_fill); - - var size = Math.floor(Math.log2(data_max)), - div = Math.pow(2, size - (size % 10)), - mult = data_max / div, - mult = (mult < 5) ? 2 : ((mult < 50) ? 10 : ((mult < 500) ? 100 : 1000)); - - data_max = data_max + (mult * div) - (data_max % (mult * div)); - - /* remember current timestamp, calculate horizontal scale */ - data_stamp = data[data.length-1][TIME]; - data_scale = height / data_max; - - /* plot data */ - var pt_rx = '0,' + height; - var pt_tx = '0,' + height; - - var y_rx = 0; - var y_tx = 0; - - for (var i = 0; i < data_rx.length; i++) - { - var x = i * step; - - y_rx = height - Math.floor(data_rx[i] * data_scale); - y_tx = height - Math.floor(data_tx[i] * data_scale); - - pt_rx += ' ' + x + ',' + y_rx; - pt_tx += ' ' + x + ',' + y_tx; - } - - pt_rx += ' ' + width + ',' + y_rx + ' ' + width + ',' + height; - pt_tx += ' ' + width + ',' + y_tx + ' ' + width + ',' + height; - - - line_rx.setAttribute('points', pt_rx); - line_tx.setAttribute('points', pt_tx); - - label_25.firstChild.data = bandwidth_label(0.25 * data_max); - label_50.firstChild.data = bandwidth_label(0.50 * data_max); - label_75.firstChild.data = bandwidth_label(0.75 * data_max); - - label_rx_cur.innerHTML = bandwidth_label(data_rx[data_rx.length-1], true); - label_tx_cur.innerHTML = bandwidth_label(data_tx[data_tx.length-1], true); - - label_rx_avg.innerHTML = bandwidth_label(data_rx_avg, true); - label_tx_avg.innerHTML = bandwidth_label(data_tx_avg, true); - - label_rx_peak.innerHTML = bandwidth_label(data_rx_peak, true); - label_tx_peak.innerHTML = bandwidth_label(data_tx_peak, true); - } - ); - - XHR.run(); - } - }, 1000 - ); -//]]></script> - -<h2 name="content"><%:Realtime Traffic%></h2> - -<ul class="cbi-tabmenu"> - <% for _, dev in ipairs(devices) do %> - <li class="cbi-tab<%= dev == curdev and "" or "-disabled" %>"><a href="?dev=<%=pcdata(dev)%>"><%=pcdata(dev)%></a></li> - <% end %> -</ul> - -<embed id="bwsvg" style="width:100%; height:300px; border:1px solid #000000; background-color:#FFFFFF" src="<%=resource%>/bandwidth.svg" /> -<div style="text-align:right"><small id="scale">-</small></div> -<br /> - -<div class="table" style="width:100%; table-layout:fixed" cellspacing="5"> - <div class="tr"> - <div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid blue"><%:Inbound:%></strong></div> - <div class="td" id="rx_bw_cur">0 <%:kbit/s%><br />(0 <%:kB/s%>)</div> - - <div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div> - <div class="td" id="rx_bw_avg">0 <%:kbit/s%><br />(0 <%:kB/s%>)</div> - - <div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div> - <div class="td" id="rx_bw_peak">0 <%:kbit/s%><br />(0 <%:kB/s%>)</div> - </div> - <div class="tr"> - <div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid green"><%:Outbound:%></strong></div> - <div class="td" id="tx_bw_cur">0 <%:kbit/s%><br />(0 <%:kB/s%>)</div> - - <div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div> - <div class="td" id="tx_bw_avg">0 <%:kbit/s%><br />(0 <%:kB/s%>)</div> - - <div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div> - <div class="td" id="tx_bw_peak">0 <%:kbit/s%><br />(0 <%:kB/s%>)</div> - </div> -</div> - -<%+footer%> diff --git a/modules/luci-mod-status/luasrc/view/admin_status/connections.htm b/modules/luci-mod-status/luasrc/view/admin_status/connections.htm deleted file mode 100644 index 37debcde66..0000000000 --- a/modules/luci-mod-status/luasrc/view/admin_status/connections.htm +++ /dev/null @@ -1,405 +0,0 @@ -<%# - Copyright 2010-2018 Jo-Philipp Wich <jo@mein.io> - Licensed to the public under the Apache License 2.0. --%> - -<%+header%> - -<script type="text/javascript">//<![CDATA[ - var bwxhr = new XHR(); - - var G; - var TIME = 0; - var UDP = 1; - var TCP = 2; - var OTHER = 3; - - var width = 760; - var height = 300; - var step = 5; - - var data_wanted = Math.floor(width / step); - var data_fill = 1; - var data_stamp = 0; - - var data_udp = [ ]; - var data_tcp = [ ]; - var data_otr = [ ]; - - var line_udp; - var line_tcp; - - var label_25; - var label_50; - var label_75; - - var label_udp_cur; - var label_udp_avg; - var label_udp_peak; - - var label_tcp_cur; - var label_tcp_avg; - var label_tcp_peak; - - var label_otr_cur; - var label_otr_avg; - var label_otr_peak; - - var label_scale; - - var conn_table; - - var dns_cache = { }; - - - /* wait for SVG */ - window.setTimeout( - function() { - var svg = document.getElementById('bwsvg'); - - try { - G = svg.getSVGDocument - ? svg.getSVGDocument() : svg.contentDocument; - } - catch(e) { - G = document.embeds['bwsvg'].getSVGDocument(); - } - - if (!G) - { - window.setTimeout(arguments.callee, 1000); - } - else - { - /* find sizes */ - width = svg.offsetWidth - 2; - height = svg.offsetHeight - 2; - data_wanted = Math.ceil(width / step); - - /* prefill datasets */ - for (var i = 0; i < data_wanted; i++) - { - data_udp[i] = 0; - data_tcp[i] = 0; - data_otr[i] = 0; - } - - /* find svg elements */ - line_udp = G.getElementById('udp'); - line_tcp = G.getElementById('tcp'); - line_otr = G.getElementById('other'); - - label_25 = G.getElementById('label_25'); - label_50 = G.getElementById('label_50'); - label_75 = G.getElementById('label_75'); - - label_udp_cur = document.getElementById('lb_udp_cur'); - label_udp_avg = document.getElementById('lb_udp_avg'); - label_udp_peak = document.getElementById('lb_udp_peak'); - - label_tcp_cur = document.getElementById('lb_tcp_cur'); - label_tcp_avg = document.getElementById('lb_tcp_avg'); - label_tcp_peak = document.getElementById('lb_tcp_peak'); - - label_otr_cur = document.getElementById('lb_otr_cur'); - label_otr_avg = document.getElementById('lb_otr_avg'); - label_otr_peak = document.getElementById('lb_otr_peak'); - - label_scale = document.getElementById('scale'); - - conn_table = document.getElementById('connections'); - - - /* plot horizontal time interval lines */ - for (var i = width % (step * 60); i < width; i += step * 60) - { - var line = G.createElementNS('http://www.w3.org/2000/svg', 'line'); - line.setAttribute('x1', i); - line.setAttribute('y1', 0); - line.setAttribute('x2', i); - line.setAttribute('y2', '100%'); - line.setAttribute('style', 'stroke:black;stroke-width:0.1'); - - var text = G.createElementNS('http://www.w3.org/2000/svg', 'text'); - text.setAttribute('x', i + 5); - text.setAttribute('y', 15); - text.setAttribute('style', 'fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000'); - text.appendChild(G.createTextNode(Math.round((width - i) / step / 60) + 'm')); - - label_25.parentNode.appendChild(line); - label_25.parentNode.appendChild(text); - } - - label_scale.innerHTML = String.format('<%:(%d minute window, %d second interval)%>', data_wanted / 60, 3); - - var recheck_lookup_queue = {}; - - /* render datasets, start update interval */ - XHR.poll(3, '<%=build_url("admin/status/realtime/connections_status")%>', null, - function(x, json) - { - - if (!json.connections) - return; - - var conn = json.connections; - - var lookup_queue = [ ]; - var rows = []; - - conn.sort(function(a, b) { - return b.bytes - a.bytes; - }); - - for (var i = 0; i < conn.length; i++) - { - var c = conn[i]; - - if ((c.src == '127.0.0.1' && c.dst == '127.0.0.1') || - (c.src == '::1' && c.dst == '::1')) - continue; - - if (!dns_cache[c.src] && lookup_queue.indexOf(c.src) == -1) - lookup_queue.push(c.src); - - if (!dns_cache[c.dst] && lookup_queue.indexOf(c.dst) == -1) - lookup_queue.push(c.dst); - - var src = dns_cache[c.src] || (c.layer3 == 'ipv6' ? '[' + c.src + ']' : c.src); - var dst = dns_cache[c.dst] || (c.layer3 == 'ipv6' ? '[' + c.dst + ']' : c.dst); - - rows.push([ - c.layer3.toUpperCase(), - c.layer4.toUpperCase(), - c.hasOwnProperty('sport') ? (src + ':' + c.sport) : src, - c.hasOwnProperty('dport') ? (dst + ':' + c.dport) : dst, - '%1024.2mB (%d <%:Pkts.%>)'.format(c.bytes, c.packets) - ]); - } - - cbi_update_table(conn_table, rows, '<em><%:No information available%></em>'); - - if (lookup_queue.length > 0) { - var reduced_lookup_queue = lookup_queue; - - if (lookup_queue.length > 100) - reduced_lookup_queue = lookup_queue.slice(0, 100); - - XHR.get('<%=build_url("admin/status/nameinfo")%>/' + reduced_lookup_queue.join('/'), null, - function(x, json) { - if (!json) - return; - - for (var index in reduced_lookup_queue) { - var address = reduced_lookup_queue[index]; - - if (!address) - continue; - - if (json[address]) { - dns_cache[address] = json[address]; - lookup_queue.splice(reduced_lookup_queue.indexOf(address),1); - continue; - } - - if(recheck_lookup_queue[address] > 2) { - dns_cache[address] = (address.match(/:/)) ? '[' + address + ']' : address; - lookup_queue.splice(index,1); - } else { - recheck_lookup_queue[address] != null ? recheck_lookup_queue[address]++ : recheck_lookup_queue[address] = 0; - } - } - } - ); - } - - - var data = json.statistics; - - var data_max = 0; - var data_scale = 0; - - var data_udp_avg = 0; - var data_tcp_avg = 0; - var data_otr_avg = 0; - - var data_udp_peak = 0; - var data_tcp_peak = 0; - var data_otr_peak = 0; - - for (var i = data_stamp ? 0 : 1; i < data.length; i++) - { - /* skip overlapping entries */ - if (data[i][TIME] <= data_stamp) - continue; - - data_fill++; - - data_udp.push(data[i][UDP]); - data_tcp.push(data[i][TCP]); - data_otr.push(data[i][OTHER]); - } - - /* cut off outdated entries */ - data_fill = Math.min(data_fill, data_wanted); - data_udp = data_udp.slice(data_udp.length - data_wanted, data_udp.length); - data_tcp = data_tcp.slice(data_tcp.length - data_wanted, data_tcp.length); - data_otr = data_otr.slice(data_otr.length - data_wanted, data_otr.length); - - /* find peak */ - for (var i = 0; i < data_udp.length; i++) - { - data_max = Math.max(data_max, data_udp[i]); - data_max = Math.max(data_max, data_tcp[i]); - data_max = Math.max(data_max, data_otr[i]); - - data_udp_peak = Math.max(data_udp_peak, data_udp[i]); - data_tcp_peak = Math.max(data_tcp_peak, data_tcp[i]); - data_otr_peak = Math.max(data_otr_peak, data_otr[i]); - - data_udp_avg += data_udp[i]; - data_tcp_avg += data_tcp[i]; - data_otr_avg += data_otr[i]; - } - - data_udp_avg = data_udp_avg / data_fill; - data_tcp_avg = data_tcp_avg / data_fill; - data_otr_avg = data_otr_avg / data_fill; - - /* remember current timestamp, calculate horizontal scale */ - data_stamp = data[data.length-1][TIME]; - data_scale = height / (data_max * 1.1); - - - /* plot data */ - var pt_udp = '0,' + height; - var pt_tcp = '0,' + height; - var pt_otr = '0,' + height; - - var y_udp = 0; - var y_tcp = 0; - var y_otr = 0; - - for (var i = 0; i < data_udp.length; i++) - { - var x = i * step; - - y_udp = height - Math.floor(data_udp[i] * data_scale); - y_tcp = height - Math.floor(data_tcp[i] * data_scale); - y_otr = height - Math.floor(data_otr[i] * data_scale); - - pt_udp += ' ' + x + ',' + y_udp; - pt_tcp += ' ' + x + ',' + y_tcp; - pt_otr += ' ' + x + ',' + y_otr; - } - - pt_udp += ' ' + width + ',' + y_udp + ' ' + width + ',' + height; - pt_tcp += ' ' + width + ',' + y_tcp + ' ' + width + ',' + height; - pt_otr += ' ' + width + ',' + y_otr + ' ' + width + ',' + height; - - - var order = [ - [ line_udp, data_udp[data_udp.length-1] ], - [ line_tcp, data_tcp[data_tcp.length-1] ], - [ line_otr, data_otr[data_otr.length-1] ] - ]; - - order.sort(function(a, b) { return b[1] - a[1] }); - - for (var i = 0; i < order.length; i++) - order[i][0].parentNode.appendChild(order[i][0]); - - - line_udp.setAttribute('points', pt_udp); - line_tcp.setAttribute('points', pt_tcp); - line_otr.setAttribute('points', pt_otr); - - label_25.firstChild.data = Math.floor(1.1 * 0.25 * data_max); - label_50.firstChild.data = Math.floor(1.1 * 0.50 * data_max); - label_75.firstChild.data = Math.floor(1.1 * 0.75 * data_max); - - label_udp_cur.innerHTML = Math.floor(data_udp[data_udp.length-1]); - label_tcp_cur.innerHTML = Math.floor(data_tcp[data_tcp.length-1]); - label_otr_cur.innerHTML = Math.floor(data_otr[data_otr.length-1]); - - label_udp_avg.innerHTML = Math.floor(data_udp_avg); - label_tcp_avg.innerHTML = Math.floor(data_tcp_avg); - label_otr_avg.innerHTML = Math.floor(data_otr_avg); - - label_udp_peak.innerHTML = Math.floor(data_udp_peak); - label_tcp_peak.innerHTML = Math.floor(data_tcp_peak); - label_otr_peak.innerHTML = Math.floor(data_otr_peak); - } - ); - - XHR.run(); - } - }, 1000 - ); -//]]></script> - -<h2 name="content"><%:Realtime Connections%></h2> - -<div class="cbi-map-descr"><%:This page gives an overview over currently active network connections.%></div> - -<fieldset class="cbi-section" id="cbi-table-table"> - <legend><%:Active Connections%></legend> - - <embed id="bwsvg" style="width:100%; height:300px; border:1px solid #000000; background-color:#FFFFFF" src="<%=resource%>/connections.svg" /> - <div style="text-align:right"><small id="scale">-</small></div> - <br /> - - <div class="table"> - <div class="tr"> - <div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid blue"><%:UDP:%></strong></div> - <div class="td" id="lb_udp_cur">0</div> - - <div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div> - <div class="td" id="lb_udp_avg">0</div> - - <div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div> - <div class="td" id="lb_udp_peak">0</div> - </div> - <div class="tr"> - <div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid green"><%:TCP:%></strong></div> - <div class="td" id="lb_tcp_cur">0</div> - - <div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div> - <div class="td" id="lb_tcp_avg">0</div> - - <div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div> - <div class="td" id="lb_tcp_peak">0</div> - </div> - <div class="tr"> - <div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid red"><%:Other:%></strong></div> - <div class="td" id="lb_otr_cur">0</div> - - <div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div> - <div class="td" id="lb_otr_avg">0</div> - - <div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div> - <div class="td" id="lb_otr_peak">0</div> - </div> - </div> - <br /> - - <div class="cbi-section-node"> - <div class="table" id="connections"> - <div class="tr table-titles"> - <div class="th col-2 hide-xs"><%:Network%></div> - <div class="th col-2"><%:Protocol%></div> - <div class="th col-7"><%:Source%></div> - <div class="th col-7"><%:Destination%></div> - <div class="th col-4"><%:Transfer%></div> - </div> - - <div class="tr placeholder"> - <div class="td"> - <em><%:Collecting data...%></em> - </div> - </div> - </div> - </div> -</fieldset> - -<%+footer%> diff --git a/modules/luci-mod-status/luasrc/view/admin_status/load.htm b/modules/luci-mod-status/luasrc/view/admin_status/load.htm deleted file mode 100644 index d31d340621..0000000000 --- a/modules/luci-mod-status/luasrc/view/admin_status/load.htm +++ /dev/null @@ -1,283 +0,0 @@ -<%# - Copyright 2010-2018 Jo-Philipp Wich <jo@mein.io> - Licensed to the public under the Apache License 2.0. --%> - -<%+header%> - -<script type="text/javascript">//<![CDATA[ - var bwxhr = new XHR(); - - var G; - var TIME = 0; - var L01 = 1; - var L05 = 2; - var L15 = 3; - - var width = 760; - var height = 300; - var step = 5; - - var data_wanted = Math.floor(width / step); - var data_fill = 1; - var data_stamp = 0; - - var data_01 = [ ]; - var data_05 = [ ]; - var data_15 = [ ]; - - var line_01; - var line_05; - var line_15; - - var label_25; - var label_050; - var label_75; - - var label_01_cur; - var label_01_avg; - var label_01_peak; - - var label_05_cur; - var label_05_avg; - var label_05_peak; - - var label_15_cur; - var label_15_avg; - var label_15_peak; - - var label_scale; - - - /* wait for SVG */ - window.setTimeout( - function() { - var svg = document.getElementById('bwsvg'); - - try { - G = svg.getSVGDocument - ? svg.getSVGDocument() : svg.contentDocument; - } - catch(e) { - G = document.embeds['bwsvg'].getSVGDocument(); - } - - if (!G) - { - window.setTimeout(arguments.callee, 1000); - } - else - { - /* find sizes */ - width = svg.offsetWidth - 2; - height = svg.offsetHeight - 2; - data_wanted = Math.ceil(width / step); - - /* prefill datasets */ - for (var i = 0; i < data_wanted; i++) - { - data_01[i] = 0; - data_05[i] = 0; - data_15[i] = 0; - } - - /* find svg elements */ - line_01 = G.getElementById('load01'); - line_05 = G.getElementById('load05'); - line_15 = G.getElementById('load15'); - - label_25 = G.getElementById('label_25'); - label_50 = G.getElementById('label_50'); - label_75 = G.getElementById('label_75'); - - label_01_cur = document.getElementById('lb_load01_cur'); - label_01_avg = document.getElementById('lb_load01_avg'); - label_01_peak = document.getElementById('lb_load01_peak'); - - label_05_cur = document.getElementById('lb_load05_cur'); - label_05_avg = document.getElementById('lb_load05_avg'); - label_05_peak = document.getElementById('lb_load05_peak'); - - label_15_cur = document.getElementById('lb_load15_cur'); - label_15_avg = document.getElementById('lb_load15_avg'); - label_15_peak = document.getElementById('lb_load15_peak'); - - label_scale = document.getElementById('scale'); - - - /* plot horizontal time interval lines */ - for (var i = width % (step * 60); i < width; i += step * 60) - { - var line = G.createElementNS('http://www.w3.org/2000/svg', 'line'); - line.setAttribute('x1', i); - line.setAttribute('y1', 0); - line.setAttribute('x2', i); - line.setAttribute('y2', '100%'); - line.setAttribute('style', 'stroke:black;stroke-width:0.1'); - - var text = G.createElementNS('http://www.w3.org/2000/svg', 'text'); - text.setAttribute('x', i + 5); - text.setAttribute('y', 15); - text.setAttribute('style', 'fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000'); - text.appendChild(G.createTextNode(Math.round((width - i) / step / 60) + 'm')); - - label_25.parentNode.appendChild(line); - label_25.parentNode.appendChild(text); - } - - label_scale.innerHTML = String.format('<%:(%d minute window, %d second interval)%>', data_wanted / 60, 3); - - /* render datasets, start update interval */ - XHR.poll(3, '<%=build_url("admin/status/realtime/load_status")%>', null, - function(x, data) - { - var data_max = 0; - var data_scale = 0; - - var data_01_avg = 0; - var data_05_avg = 0; - var data_15_avg = 0; - - var data_01_peak = 0; - var data_05_peak = 0; - var data_15_peak = 0; - - for (var i = data_stamp ? 0 : 1; i < data.length; i++) - { - /* skip overlapping entries */ - if (data[i][TIME] <= data_stamp) - continue; - - data_fill++; - - data_01.push(data[i][L01]); - data_05.push(data[i][L05]); - data_15.push(data[i][L15]); - } - - /* cut off outdated entries */ - data_fill = Math.min(data_fill, data_wanted); - data_01 = data_01.slice(data_01.length - data_wanted, data_01.length); - data_05 = data_05.slice(data_05.length - data_wanted, data_05.length); - data_15 = data_15.slice(data_15.length - data_wanted, data_15.length); - - /* find peak */ - for (var i = 0; i < data_01.length; i++) - { - data_max = Math.max(data_max, data_01[i]); - data_max = Math.max(data_max, data_05[i]); - data_max = Math.max(data_max, data_15[i]); - - data_01_peak = Math.max(data_01_peak, data_01[i]); - data_05_peak = Math.max(data_05_peak, data_05[i]); - data_15_peak = Math.max(data_15_peak, data_15[i]); - - data_01_avg += data_01[i]; - data_05_avg += data_05[i]; - data_15_avg += data_15[i]; - } - - data_01_avg = data_01_avg / data_fill; - data_05_avg = data_05_avg / data_fill; - data_15_avg = data_15_avg / data_fill; - - /* remember current timestamp, calculate horizontal scale */ - data_stamp = data[data.length-1][TIME]; - data_scale = height / (data_max * 1.1); - - - /* plot data */ - var pt_01 = '0,' + height; - var pt_05 = '0,' + height; - var pt_15 = '0,' + height; - - var y_01 = 0; - var y_05 = 0; - var y_15 = 0; - - for (var i = 0; i < data_01.length; i++) - { - var x = i * step; - - y_01 = height - Math.floor(data_01[i] * data_scale); - y_05 = height - Math.floor(data_05[i] * data_scale); - y_15 = height - Math.floor(data_15[i] * data_scale); - - pt_01 += ' ' + x + ',' + y_01; - pt_05 += ' ' + x + ',' + y_05; - pt_15 += ' ' + x + ',' + y_15; - } - - pt_01 += ' ' + width + ',' + y_01 + ' ' + width + ',' + height; - pt_05 += ' ' + width + ',' + y_05 + ' ' + width + ',' + height; - pt_15 += ' ' + width + ',' + y_15 + ' ' + width + ',' + height; - - - line_01.setAttribute('points', pt_01); - line_05.setAttribute('points', pt_05); - line_15.setAttribute('points', pt_15); - - label_25.firstChild.data = (1.1 * 0.25 * data_max / 100).toFixed(2); - label_50.firstChild.data = (1.1 * 0.50 * data_max / 100).toFixed(2); - label_75.firstChild.data = (1.1 * 0.75 * data_max / 100).toFixed(2); - - label_01_cur.innerHTML = (data_01[data_01.length-1] / 100).toFixed(2); - label_05_cur.innerHTML = (data_05[data_05.length-1] / 100).toFixed(2); - label_15_cur.innerHTML = (data_15[data_15.length-1] / 100).toFixed(2); - - label_01_avg.innerHTML = (data_01_avg / 100).toFixed(2); - label_05_avg.innerHTML = (data_05_avg / 100).toFixed(2); - label_15_avg.innerHTML = (data_15_avg / 100).toFixed(2); - - label_01_peak.innerHTML = (data_01_peak / 100).toFixed(2); - label_05_peak.innerHTML = (data_05_peak / 100).toFixed(2); - label_15_peak.innerHTML = (data_15_peak / 100).toFixed(2); - } - ); - - XHR.run(); - } - }, 1000 - ); -//]]></script> - -<h2 name="content"><%:Realtime Load%></h2> - -<embed id="bwsvg" style="width:100%; height:300px; border:1px solid #000000; background-color:#FFFFFF" src="<%=resource%>/load.svg" /> -<div style="text-align:right"><small id="scale">-</small></div> -<br /> - -<div class="table" style="width:100%; table-layout:fixed" cellspacing="5"> - <div class="tr"> - <div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid #ff0000; white-space:nowrap"><%:1 Minute Load:%></strong></div> - <div class="td" id="lb_load01_cur">0</div> - - <div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div> - <div class="td" id="lb_load01_avg">0</div> - - <div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div> - <div class="td" id="lb_load01_peak">0</div> - </div> - <div class="tr"> - <div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid #ff6600; white-space:nowrap"><%:5 Minute Load:%></strong></div> - <div class="td" id="lb_load05_cur">0</div> - - <div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div> - <div class="td" id="lb_load05_avg">0</div> - - <div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div> - <div class="td" id="lb_load05_peak">0</div> - </div> - <div class="tr"> - <div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid #ffaa00; white-space:nowrap"><%:15 Minute Load:%></strong></div> - <div class="td" id="lb_load15_cur">0</div> - - <div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div> - <div class="td" id="lb_load15_avg">0</div> - - <div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div> - <div class="td" id="lb_load15_peak">0</div> - </div> -</div> - -<%+footer%> diff --git a/modules/luci-mod-status/luasrc/view/admin_status/wireless.htm b/modules/luci-mod-status/luasrc/view/admin_status/wireless.htm deleted file mode 100644 index 5ac2eb462d..0000000000 --- a/modules/luci-mod-status/luasrc/view/admin_status/wireless.htm +++ /dev/null @@ -1,370 +0,0 @@ -<%# - Copyright 2011-2018 Jo-Philipp Wich <jo@mein.io> - Licensed to the public under the Apache License 2.0. --%> - -<%- - local ntm = require "luci.model.network".init() - - local dev - local devices = { } - for _, dev in luci.util.vspairs(luci.sys.net.devices()) do - if dev:match("^wlan%d") or dev:match("^ath%d") or dev:match("^wl%d") then - devices[#devices+1] = dev - end - end - - local curdev = luci.http.formvalue("dev") or devices[1] --%> - -<%+header%> - -<script type="text/javascript">//<![CDATA[ - var bwxhr = new XHR(); - - var G, G2; - var TIME = 0; - var RATE = 1; - var RSSI = 2; - var NOISE = 3; - - var width = 760; - var height = 300; - var step = 5; - - var data_wanted = Math.floor(width / step); - var data_fill = 1; - var data_stamp = 0; - - var data_rssi = [ ]; - var data_noise = [ ]; - var data_rate = [ ]; - - var line_rssi; - var line_noise; - var line_rate; - - var label_25, label_25_2; - var label_50, label_50_2; - var label_75, label_75_2; - - var label_rssi_cur; - var label_rssi_avg; - var label_rssi_peak; - - var label_noise_cur; - var label_noise_avg; - var label_noise_peak; - - var label_rate_cur; - var label_rate_avg; - var label_rate_peak; - - var label_scale; - var label_scale_2; - - - /* wait for SVG */ - window.setTimeout( - function() { - var svg = document.getElementById('iwsvg'); - var svg2 = document.getElementById('iwsvg2'); - - try { - G = svg.getSVGDocument - ? svg.getSVGDocument() : svg.contentDocument; - G2 = svg2.getSVGDocument - ? svg2.getSVGDocument() : svg2.contentDocument; - } - catch(e) { - G = document.embeds['iwsvg'].getSVGDocument(); - G2 = document.embeds['iwsvg2'].getSVGDocument(); - } - - if (!G || !G2) - { - window.setTimeout(arguments.callee, 1000); - } - else - { - /* find sizes */ - width = svg.offsetWidth - 2; - height = svg.offsetHeight - 2; - data_wanted = Math.ceil(width / step); - - /* prefill datasets */ - for (var i = 0; i < data_wanted; i++) - { - data_rssi[i] = 0; - data_noise[i] = 0; - data_rate[i] = 0; - } - - /* find svg elements */ - line_rssi = G.getElementById('rssi'); - line_noise = G.getElementById('noise'); - line_rate = G2.getElementById('rate'); - - label_25 = G.getElementById('label_25'); - label_50 = G.getElementById('label_50'); - label_75 = G.getElementById('label_75'); - label_25_2 = G2.getElementById('label_25'); - label_50_2 = G2.getElementById('label_50'); - label_75_2 = G2.getElementById('label_75'); - - label_rssi_cur = document.getElementById('rssi_bw_cur'); - label_rssi_avg = document.getElementById('rssi_bw_avg'); - label_rssi_peak = document.getElementById('rssi_bw_peak'); - - label_noise_cur = document.getElementById('noise_bw_cur'); - label_noise_avg = document.getElementById('noise_bw_avg'); - label_noise_peak = document.getElementById('noise_bw_peak'); - - label_rate_cur = document.getElementById('rate_bw_cur'); - label_rate_avg = document.getElementById('rate_bw_avg'); - label_rate_peak = document.getElementById('rate_bw_peak'); - - label_scale = document.getElementById('scale'); - label_scale_2 = document.getElementById('scale2'); - - - /* plot horizontal time interval lines */ - for (var i = width % (step * 60); i < width; i += step * 60) - { - var line = G.createElementNS('http://www.w3.org/2000/svg', 'line'); - line.setAttribute('x1', i); - line.setAttribute('y1', 0); - line.setAttribute('x2', i); - line.setAttribute('y2', '100%'); - line.setAttribute('style', 'stroke:black;stroke-width:0.1'); - - var text = G.createElementNS('http://www.w3.org/2000/svg', 'text'); - text.setAttribute('x', i + 5); - text.setAttribute('y', 15); - text.setAttribute('style', 'fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000'); - text.appendChild(G.createTextNode(Math.round((width - i) / step / 60) + 'm')); - - label_25.parentNode.appendChild(line); - label_25.parentNode.appendChild(text); - - - var line2 = G2.createElementNS('http://www.w3.org/2000/svg', 'line'); - line2.setAttribute('x1', i); - line2.setAttribute('y1', 0); - line2.setAttribute('x2', i); - line2.setAttribute('y2', '100%'); - line2.setAttribute('style', 'stroke:black;stroke-width:0.1'); - - var text2 = G2.createElementNS('http://www.w3.org/2000/svg', 'text'); - text2.setAttribute('x', i + 5); - text2.setAttribute('y', 15); - text2.setAttribute('style', 'fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000'); - text2.appendChild(G.createTextNode(Math.round((width - i) / step / 60) + 'm')); - - label_25_2.parentNode.appendChild(line2); - label_25_2.parentNode.appendChild(text2); - } - - label_scale.innerHTML = String.format('<%:(%d minute window, %d second interval)%>', data_wanted / 60, 3); - label_scale_2.innerHTML = String.format('<%:(%d minute window, %d second interval)%>', data_wanted / 60, 3); - - /* render datasets, start update interval */ - XHR.poll(3, '<%=build_url("admin/status/realtime/wireless_status", curdev)%>', null, - function(x, data) - { - var noise_floor = 255; - var rate_floor = 60000; - - for (var i = 0; i < data.length; i++) { - noise_floor = Math.min(noise_floor, data[i][NOISE]); - rate_floor = Math.min(rate_floor, data[i][RATE]); - } - - noise_floor -= 5; - - var data_max = 0; - var data_scale = 0; - var data_max_2 = 0; - var data_scale_2 = 0; - - var data_rssi_avg = 0; - var data_noise_avg = 0; - var data_rate_avg = 0; - - var data_rssi_peak = 0; - var data_noise_peak = 0; - var data_rate_peak = 0; - - for (var i = data_stamp ? 0 : 1; i < data.length; i++) - { - /* skip overlapping entries */ - if (data[i][TIME] <= data_stamp) - continue; - - data_fill++; - - data_rssi.push(data[i][RSSI] - noise_floor); - data_noise.push(data[i][NOISE] - noise_floor); - data_rate.push(Math.floor(data[i][RATE] / 1000)); - } - - /* cut off outdated entries */ - data_fill = Math.min(data_fill, data_wanted); - data_rssi = data_rssi.slice(data_rssi.length - data_wanted, data_rssi.length); - data_noise = data_noise.slice(data_noise.length - data_wanted, data_noise.length); - data_rate = data_rate.slice(data_rate.length - data_wanted, data_rate.length); - - /* find peak */ - for (var i = 0; i < data_rssi.length; i++) - { - data_max = Math.max(data_max, data_rssi[i]); - data_max_2 = Math.max(data_max_2, data_rate[i]); - - data_rssi_peak = Math.max(data_rssi_peak, data_rssi[i]); - data_noise_peak = Math.max(data_noise_peak, data_noise[i]); - data_rate_peak = Math.max(data_rate_peak, data_rate[i]); - - data_rssi_avg += data_rssi[i]; - data_noise_avg += data_noise[i]; - data_rate_avg += data_rate[i]; - } - - data_rssi_avg = data_rssi_avg / data_fill; - data_noise_avg = data_noise_avg / data_fill; - data_rate_avg = data_rate_avg / data_fill; - - /* remember current timestamp, calculate horizontal scale */ - data_stamp = data[data.length-1][TIME]; - data_scale = (height / (data_max * 1.1)).toFixed(1); - data_scale_2 = (height / (data_max_2 * 1.1)).toFixed(1); - - /* plot data */ - var pt_rssi = '0,' + height; - var pt_noise = '0,' + height; - var pt_rate = '0,' + height; - - var y_rssi = 0; - var y_noise = 0; - var y_rate = 0; - - for (var i = 0; i < data_rssi.length; i++) - { - var x = i * step; - - y_rssi = height - Math.floor(data_rssi[i] * data_scale); - y_noise = height - Math.floor(data_noise[i] * data_scale); - y_rate = height - Math.floor(data_rate[i] * data_scale_2); - - y_rssi -= Math.floor(y_rssi % (1/data_scale)); - y_noise -= Math.floor(y_noise % (1/data_scale)); - - pt_rssi += ' ' + x + ',' + y_rssi; - pt_noise += ' ' + x + ',' + y_noise; - pt_rate += ' ' + x + ',' + y_rate; - } - - pt_rssi += ' ' + width + ',' + y_rssi + ' ' + width + ',' + height; - pt_noise += ' ' + width + ',' + y_noise + ' ' + width + ',' + height; - pt_rate += ' ' + width + ',' + y_rate + ' ' + width + ',' + height; - - line_rssi.setAttribute('points', pt_rssi); - line_noise.setAttribute('points', pt_noise); - line_rate.setAttribute('points', pt_rate); - - function wireless_label(dbm, noise) - { - if (noise) - return String.format("%d <%:dBm%> (SNR %d <%:dB%>)", noise_floor + dbm - 255, dbm - noise); - else - return String.format("%d <%:dBm%>", noise_floor + dbm - 255); - } - - function rate_label(mbit) - { - return String.format("%d <%:Mbit/s%>", mbit); - } - - label_25.firstChild.data = wireless_label(1.1 * 0.25 * data_max); - label_50.firstChild.data = wireless_label(1.1 * 0.50 * data_max); - label_75.firstChild.data = wireless_label(1.1 * 0.75 * data_max); - - label_25_2.firstChild.data = rate_label(1.1 * 0.25 * data_max_2); - label_50_2.firstChild.data = rate_label(1.1 * 0.50 * data_max_2); - label_75_2.firstChild.data = rate_label(1.1 * 0.75 * data_max_2); - - label_rssi_cur.innerHTML = wireless_label(data_rssi[data_rssi.length-1], data_noise[data_noise.length-1]).nobr(); - label_noise_cur.innerHTML = wireless_label(data_noise[data_noise.length-1]).nobr(); - - label_rssi_avg.innerHTML = wireless_label(data_rssi_avg, data_noise_avg).nobr(); - label_noise_avg.innerHTML = wireless_label(data_noise_avg).nobr(); - - label_rssi_peak.innerHTML = wireless_label(data_rssi_peak, data_noise_peak).nobr(); - label_noise_peak.innerHTML = wireless_label(data_noise_peak).nobr(); - - label_rate_cur.innerHTML = rate_label(data_rate[data_rate.length-1]); - label_rate_avg.innerHTML = rate_label(data_rate_avg); - label_rate_peak.innerHTML = rate_label(data_rate_peak); - } - ); - - XHR.run(); - } - }, 1000 - ); -//]]></script> - -<h2 name="content"><%:Realtime Wireless%></h2> - -<ul class="cbi-tabmenu"> - <% for _, dev in ipairs(devices) do %> - <li class="cbi-tab<%= dev == curdev and "" or "-disabled" %>"><a href="?dev=<%=pcdata(dev)%>"><%=pcdata(dev)%></a></li> - <% end %> -</ul> - -<embed id="iwsvg" style="width:100%; height:300px; border:1px solid #000000; background-color:#FFFFFF" src="<%=resource%>/wireless.svg" /> -<div style="text-align:right"><small id="scale">-</small></div> -<br /> - -<div class="table" style="width:100%; table-layout:fixed" cellspacing="5"> - <div class="tr"> - <div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid blue"><%:Signal:%></strong></div> - <div class="td" id="rssi_bw_cur">0 <%:dBm%></div> - - <div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div> - <div class="td" id="rssi_bw_avg">0 <%:dBm%></div> - - <div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div> - <div class="td" id="rssi_bw_peak">0 <%:dBm%></div> - </div> - <div class="tr"> - <div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid red"><%:Noise:%></strong></div> - <div class="td" id="noise_bw_cur">0 <%:dBm%></div> - - <div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div> - <div class="td" id="noise_bw_avg">0 <%:dBm%></div> - - <div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div> - <div class="td" id="noise_bw_peak">0 <%:dBm%></div> - </div> -</div> - -<br /> - -<embed id="iwsvg2" style="width:100%; height:300px; border:1px solid #000000; background-color:#FFFFFF" src="<%=resource%>/wifirate.svg" /> -<div style="text-align:right"><small id="scale2">-</small></div> -<br /> - -<div class="table" style="width:100%; table-layout:fixed" cellspacing="5"> - <div class="tr"> - <div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid green"><%:Phy Rate:%></strong></div> - <div class="td" id="rate_bw_cur">0 MBit/s</div> - - <div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div> - <div class="td" id="rate_bw_avg">0 MBit/s</div> - - <div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div> - <div class="td" id="rate_bw_peak">0 MBit/s</div> - </div> -</div> - -<%+footer%> diff --git a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/sshkeys.js b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/sshkeys.js index 05d41b0dde..8b9673ae11 100644 --- a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/sshkeys.js +++ b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/sshkeys.js @@ -1,5 +1,6 @@ 'use strict'; 'require fs'; +'require ui'; var SSHPubkeyDecoder = L.Class.singleton({ lengthDecode: function(s, off) @@ -119,8 +120,8 @@ function renderKeys(keys) { function saveKeys(keys) { return fs.write('/etc/dropbear/authorized_keys', keys.join('\n') + '\n', 384 /* 0600 */) .then(renderKeys.bind(this, keys)) - .catch(function(e) { L.ui.addNotification(null, E('p', e.message)) }) - .finally(L.ui.hideModal); + .catch(function(e) { ui.addNotification(null, E('p', e.message)) }) + .finally(ui.hideModal); } function addKey(ev) { @@ -138,13 +139,13 @@ function addKey(ev) { }); if (keys.indexOf(key) !== -1) { - L.ui.showModal(_('Add key'), [ + ui.showModal(_('Add key'), [ E('div', { class: 'alert-message warning' }, _('The given SSH public key has already been added.')), E('div', { class: 'right' }, E('div', { class: 'btn', click: L.hideModal }, _('Close'))) ]); } else if (!pubkey) { - L.ui.showModal(_('Add key'), [ + ui.showModal(_('Add key'), [ E('div', { class: 'alert-message warning' }, _('The given SSH public key is invalid. Please supply proper public RSA or ECDSA keys.')), E('div', { class: 'right' }, E('div', { class: 'btn', click: L.hideModal }, _('Close'))) ]); @@ -178,7 +179,7 @@ function removeKey(ev) { E('div', { class: 'right' }, [ E('div', { class: 'btn', click: L.hideModal }, _('Cancel')), ' ', - E('div', { class: 'btn danger', click: L.ui.createHandlerFn(this, saveKeys, keys) }, _('Delete key')), + E('div', { class: 'btn danger', click: ui.createHandlerFn(this, saveKeys, keys) }, _('Delete key')), ]) ]); } @@ -232,7 +233,7 @@ return L.view.extend({ }), E('button', { 'class': 'cbi-button', - 'click': L.ui.createHandlerFn(this, addKey) + 'click': ui.createHandlerFn(this, addKey) }, _('Add key')) ]) ]); @@ -242,7 +243,7 @@ return L.view.extend({ if (pubkey) list.insertBefore(E('div', { class: 'item', - click: L.ui.createHandlerFn(this, removeKey), + click: ui.createHandlerFn(this, removeKey), 'data-key': key }, [ E('strong', pubkey.comment || _('Unnamed key')), E('br'), |