gluon/package/gluon-status-page/files/lib/gluon/status-page/view/status-page.html
Matthias Schiffer ae1023ac3a gluon-status-page: use UCI + iwinfo to get channel information
The network.wireless status ubus call only returns the configured
channel from UCI, breaking the status page in outdoor mode, where the
configuration contains 'auto' instead of a number.

Fixes: 0d3fa6b59b ("gluon-status-page: use ubus to get radio channels")
Closes #2336

(cherry picked from commit 201e1597b1)
2022-02-03 17:08:07 +01:00

284 lines
8.7 KiB
HTML

<%-
local iwinfo = require 'iwinfo'
local ubus = require 'ubus'
local unistd = require 'posix.unistd'
local util = require 'gluon.util'
local wireless = require 'gluon.wireless'
local uci = require('simple-uci').cursor()
local translations = {}
local site_i18n = i18n 'gluon-site'
local function _(v)
translations[v] = translate(v)
end
-- i18n strings for JavaScript
_('.') -- decimal point
_('connected')
_('not connected')
_('1 day')
_('%s days')
_('%s used')
_('%s packets/s')
local function get_mesh()
local f = loadfile('/lib/gluon/status-page/mesh.lua')
if f then
return f()
end
return {}
end
local mesh = get_mesh()
local function get_interfaces(uconn)
local interfaces = util.get_mesh_devices(uconn)
table.sort(interfaces)
return interfaces
end
local function get_radios()
local ret = {}
wireless.foreach_radio(uci, function(radio)
local channel = iwinfo.nl80211.channel(wireless.find_phy(radio))
if channel then
table.insert(ret, {
name = radio['.name'],
channel = channel,
})
end
end)
table.sort(ret, function(a, b)
return a.name < b.name
end)
return ret
end
local function is_wireless(iface)
while true do
local pattern = '/sys/class/net/' .. iface .. '/lower_*'
local lower = util.glob(pattern)[1]
if not lower then break end
iface = lower:sub(pattern:len())
end
return unistd.access('/sys/class/net/' .. iface .. '/wireless') ~= nil
end
local uconn = ubus.connect()
if not uconn then
error('failed to connect to ubus')
end
local interfaces = get_interfaces(uconn)
ubus.close(uconn)
local radios = get_radios()
local function sorted(t)
t = {unpack(t)}
table.sort(t)
return t
end
local function enabled(v)
return v and translate('enabled') or translate('disabled')
end
local function formatBits(bits)
local units = {[0]='', 'k', 'M', 'G'}
local unit = 0
for i = 1, #units do
if math.abs(bits) < 1000 then
break
end
unit = i
bits = bits / 1000
end
return string.format('%g %sbit', bits, units[unit])
end
local function statistics(key, format)
return string.format('<span data-statistics="%s" data-format="%s"></span>', pcdata(key), pcdata(format or 'id'))
end
local function statisticsTraffic(key)
return string.format('%s<br />%s<br />%s',
statistics(key .. '/packets', 'packetsDiff'),
statistics(key .. '/bytes', 'bytesDiff'),
statistics(key .. '/bytes', 'bytes')
)
end
http:prepare_content("application/xhtml+xml")
-%>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no" />
<title><%| nodeinfo.hostname %> - <%:Status%></title>
<link rel="stylesheet" href="/static/status-page.css" type="text/css" />
</head>
<body data-node-address="<%| http:getenv('SERVER_ADDR') %>"<%=
attr('data-translations', translations) ..
attr('data-node-location', nodeinfo.location) ..
attr('data-mesh-provider', mesh.provider)
%>>
<header>
<h1><%| nodeinfo.hostname %></h1>
</header>
<div class="container">
<div class="frame">
<h2><%:Overview%></h2>
<dl>
<dt><%:Node name%></dt><dd><%| nodeinfo.hostname %></dd>
<% if nodeinfo.owner and nodeinfo.owner.contact then -%>
<dt><%:Contact%></dt><dd><%| nodeinfo.owner.contact %></dd>
<%- end %>
<% if nodeinfo.location then -%>
<dt><%:Location%></dt>
<dd><a href="geo:<%| nodeinfo.location.latitude %>,<%| nodeinfo.location.longitude %>">
<%| nodeinfo.location.latitude %>, <%| nodeinfo.location.longitude %>
</a></dd>
<%- end %>
<dt><%:Model%></dt><dd><%| nodeinfo.hardware.model %></dd>
<dt><%:Primary MAC address%></dt><dd><%| nodeinfo.network.mac %></dd>
<dt><%:IP address%></dt><dd><%= pcdata(table.concat(sorted(nodeinfo.network.addresses), '\n')):gsub('\n', '<br />') %></dd>
<dt><%:Firmware%></dt><dd><%| nodeinfo.software.firmware.release %></dd>
<% if nodeinfo.network.mesh_vpn then -%>
<dt><%:Mesh VPN%></dt>
<dd>
<%| enabled(nodeinfo.network.mesh_vpn.enabled) %>
<% if nodeinfo.network.mesh_vpn.provider then -%>
<br /><%| nodeinfo.network.mesh_vpn.provider %>
<%- end %>
</dd>
<% if nodeinfo.network.mesh_vpn.bandwidth_limit.enabled then -%>
<dt><%:Bandwidth limit%></dt>
<dd>
<% if nodeinfo.network.mesh_vpn.bandwidth_limit.ingress then -%>
<%| formatBits(nodeinfo.network.mesh_vpn.bandwidth_limit.ingress*1000) %>/s <%:downstream%>
<%- end %>
<% if nodeinfo.network.mesh_vpn.bandwidth_limit.egress then -%>
<%| formatBits(nodeinfo.network.mesh_vpn.bandwidth_limit.egress*1000) %>/s <%:upstream%><br />
<%- end %>
</dd>
<%- end %>
<%- end %>
<dt><%:Site%></dt><dd><%| site.site_name() %></dd>
<% if nodeinfo.system.domain_code then -%>
<dt><%:Domain%></dt>
<dd>
<%| site.domain_names[nodeinfo.system.domain_code]() %>
<% if nodeinfo.system.domain_code ~= nodeinfo.system.primary_domain_code then %>
(<%| site.domain_names[nodeinfo.system.primary_domain_code]() %>)
<%- end %>
</dd>
<%- end %>
<% if nodeinfo.system.role then -%>
<dt><%:Role%></dt><dd><%| site_i18n._translate('gluon-web-node-role:role:' .. nodeinfo.system.role) %></dd>
<%- end %>
<% if nodeinfo.software.autoupdater then -%>
<dt><%:Automatic updates%></dt><dd><%| enabled(nodeinfo.software.autoupdater.enabled) %><%|
nodeinfo.software.autoupdater.enabled and
string.format(' (%s)', nodeinfo.software.autoupdater.branch)
%></dd>
<%- end %>
<% if nodeinfo.software.babeld or nodeinfo.software['batman-adv'] then -%>
<dt><%:Mesh protocol%></dt>
<% if nodeinfo.software.babeld then -%>
<dd>babeld <%| nodeinfo.software.babeld.version %></dd>
<%- end %>
<% if nodeinfo.software['batman-adv'] then -%>
<dd>batman-adv <%| nodeinfo.software['batman-adv'].version %> (compat<%| nodeinfo.software['batman-adv'].compat %>)</dd>
<%- end %>
<%- end %>
</dl>
</div>
<div class="frame">
<h2><%:Monitoring%></h2>
<table>
<tr><th><%:Uptime%></th><td><%= statistics('uptime', 'time') %></td></tr>
<tr><th><%:Load average%></th><td><%= statistics('loadavg', 'decimal') %></td></tr>
<tr><th><%:RAM%></th><td><%= statistics('memory', 'memory') %></td></tr>
<tr><th><%:Filesystem%></th><td><%= statistics('rootfs_usage', 'percent') %></td></tr>
<tr><th><%:Gateway%></th><td><%= statistics('gateway') %><br /><%= statistics('gateway_nexthop', 'neighbour') %></td></tr>
</table>
<h3><%:Clients%></h3>
<table>
<tr><th><%:Total%></th><td><%= statistics('clients/total') %></td></tr>
<tr><th><%:Wireless 2.4 GHz%></th><td><%= statistics('clients/wifi24') %></td></tr>
<tr><th><%:Wireless 5 GHz%></th><td><%= statistics('clients/wifi5') %></td></tr>
</table>
<% if radios[1] then -%>
<h3><%:Radios%></h3>
<table>
<% for _, radio in ipairs(radios) do -%>
<tr>
<th><%| radio.name %></th>
<td><%| translatef('Channel %u', radio.channel) %></td>
</tr>
<%- end %>
</table>
<%- end %>
<h3><%:Traffic%></h3>
<table>
<tr><th><%:Transmitted%></th><td><%= statisticsTraffic('traffic/tx') %></td></tr>
<tr><th><%:Received%></th><td><%= statisticsTraffic('traffic/rx') %></td></tr>
<tr><th><%:Forwarded%></th><td><%= statisticsTraffic('traffic/forward') %></td></tr>
</table>
<div id="mesh-vpn" style="display: none">
<h3><%:Mesh VPN%></h3>
<table id="mesh-vpn-peers">
</table>
</div>
</div>
<div class="frame frame-wide">
<h2><%:Neighbors%></h2>
<%
for _, iface in ipairs(interfaces) do
local wireless = is_wireless(iface)
local address = util.readfile('/sys/class/net/' .. iface .. '/address')
if address then
%>
<h3><%| iface %></h3>
<div data-interface="<%| iface %>" data-interface-address="<%| util.trim(address) %>"<%= attr('data-interface-wireless', wireless) %>>
<table class="datatable">
<tr>
<th><%:Node%></th>
<% for i, v in ipairs(mesh.attrs or {}) do %>
<th<%= attr('class', 'row-' .. v[1] ) .. attr('data-key', v[1]) .. attr('data-suffix', v[3]) %>><%| v[2] %></th>
<% end %>
<% if wireless then %>
<th class="row-signal">dBm</th>
<th class="row-distance"><%:Distance%></th>
<th class="row-inactive"><%:Last seen%></th>
<% end %>
</tr>
</table>
</div>
<%
end
end
%>
</div>
</div>
<script src="/static/status-page.js"></script>
</body>
</html>