Skip to content
Snippets Groups Projects
status-page.html 8.86 KiB
Newer Older
	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 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

		wireless.foreach_radio(uci, function(radio)
			local channel = iwinfo.nl80211.channel(wireless.find_phy(radio))
			if channel then
					name = radio['.name'],
					channel = channel,
		table.sort(ret, function(a, b)
			return a.name < b.name
		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 .. '/phy80211') ~= nil
	local uconn = ubus.connect()
	if not uconn then
		error('failed to connect to ubus')
	end

	local interfaces = get_interfaces(uconn)

	ubus.close(uconn)
	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])
	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

		<meta name="viewport" content="width=device-width,initial-scale=1">
		<meta name="robots" content="noindex,nofollow">

		<title><%| nodeinfo.hostname %> - <%:Status%></title>

		<link rel="stylesheet" href="/static/status-page.css" type="text/css">
	<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%><br>
							<% if nodeinfo.network.mesh_vpn.bandwidth_limit.egress then -%>
<%| formatBits(nodeinfo.network.mesh_vpn.bandwidth_limit.egress*1000) %>/s <%:upstream%>
					<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>
					<% if nodeinfo.system.role then -%>
						<dt><%:Role%></dt><dd><%| site_i18n._translate('gluon-web-node-role:role:' .. nodeinfo.system.role) %></dd>
					<% 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>
					<tbody>
						<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') %> (TQ: <%= statistics('gateway_tq', 'tq') %>)<br><%= statistics('gateway_nexthop', 'neighbour') %></td></tr>
				</table>

				<h3><%:Clients%></h3>
				<table>
					<tbody>
						<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>
					</tbody>
				<% if radios[1] then -%>
					<h3><%:Radios%></h3>
						<tbody>
							<% for _, radio in ipairs(radios) do -%>
								<tr>
									<th><%| radio.name %></th>
									<td><%| translatef('Channel %u', radio.channel) %></td>
								</tr>
					<tbody>
						<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>
					</tbody>
				</table>

				<div id="mesh-vpn" style="display: none">
					<h3><%:Mesh VPN%></h3>
					<table>
						<tbody id="mesh-vpn-peers"></tbody>
				<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">
							<tbody>
								<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>
							</tbody>
						</table>
					</div>
				<%
						end
					end
				%>
			</div>
		</div>
		<script src="/static/status-page.js"></script>
	</body>
</html>