update 29_ethinfo.js from imm
This commit is contained in:
parent
6d5942a5ec
commit
9dcb411881
@ -1,60 +1,351 @@
|
||||
'use strict';
|
||||
'require baseclass';
|
||||
'require fs';
|
||||
'require ui';
|
||||
'require uci';
|
||||
'require rpc';
|
||||
'require network';
|
||||
'require firewall';
|
||||
|
||||
var callLuciETHInfo = rpc.declare({
|
||||
object: 'luci',
|
||||
method: 'getETHInfo',
|
||||
var callNetworkStatus = rpc.declare({
|
||||
object: 'network.device',
|
||||
method: 'status',
|
||||
expect: { '': {} }
|
||||
});
|
||||
|
||||
return L.Class.extend({
|
||||
|
||||
function isString(v)
|
||||
{
|
||||
return typeof(v) === 'string' && v !== '';
|
||||
}
|
||||
|
||||
function resolveVLANChain(ifname, bridges, mapping)
|
||||
{
|
||||
while (!mapping[ifname]) {
|
||||
var m = ifname.match(/^(.+)\.([^.]+)$/);
|
||||
|
||||
if (!m)
|
||||
break;
|
||||
|
||||
if (bridges[m[1]]) {
|
||||
if (bridges[m[1]].vlan_filtering)
|
||||
mapping[ifname] = bridges[m[1]].vlans[m[2]];
|
||||
else
|
||||
mapping[ifname] = bridges[m[1]].ports;
|
||||
}
|
||||
else if (/^[0-9]{1,4}$/.test(m[2]) && m[2] <= 4095) {
|
||||
mapping[ifname] = [ m[1] ];
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
ifname = m[1];
|
||||
}
|
||||
}
|
||||
|
||||
function buildVLANMappings(mapping)
|
||||
{
|
||||
var bridge_vlans = uci.sections('network', 'bridge-vlan'),
|
||||
vlan_devices = uci.sections('network', 'device'),
|
||||
interfaces = uci.sections('network', 'interface'),
|
||||
bridges = {};
|
||||
|
||||
/* find bridge VLANs */
|
||||
for (var i = 0, s; (s = bridge_vlans[i]) != null; i++) {
|
||||
if (!isString(s.device) || !/^[0-9]{1,4}$/.test(s.vlan) || +s.vlan > 4095)
|
||||
continue;
|
||||
|
||||
var aliases = L.toArray(s.alias),
|
||||
ports = L.toArray(s.ports),
|
||||
br = bridges[s.device] = (bridges[s.device] || { ports: [], vlans: {}, vlan_filtering: true });
|
||||
|
||||
br.vlans[s.vlan] = [];
|
||||
|
||||
for (var j = 0; j < ports.length; j++) {
|
||||
var port = ports[j].replace(/:[ut*]+$/, '');
|
||||
|
||||
if (br.ports.indexOf(port) === -1)
|
||||
br.ports.push(port);
|
||||
|
||||
br.vlans[s.vlan].push(port);
|
||||
}
|
||||
|
||||
for (var j = 0; j < aliases.length; j++)
|
||||
if (aliases[j] != s.vlan)
|
||||
br.vlans[aliases[j]] = br.vlans[s.vlan];
|
||||
}
|
||||
|
||||
/* find bridges, VLAN devices */
|
||||
for (var i = 0, s; (s = vlan_devices[i]) != null; i++) {
|
||||
if (s.type == 'bridge') {
|
||||
if (!isString(s.name))
|
||||
continue;
|
||||
|
||||
var ports = L.toArray(s.ports),
|
||||
br = bridges[s.name] || (bridges[s.name] = { ports: [], vlans: {}, vlan_filtering: false });
|
||||
|
||||
if (s.vlan_filtering == '0')
|
||||
br.vlan_filtering = false;
|
||||
else if (s.vlan_filtering == '1')
|
||||
br.vlan_filtering = true;
|
||||
|
||||
for (var j = 0; j < ports.length; j++)
|
||||
if (br.ports.indexOf(ports[j]) === -1)
|
||||
br.ports.push(ports[j]);
|
||||
|
||||
mapping[s.name] = br.ports;
|
||||
}
|
||||
else if (s.type == '8021q' || s.type == '8021ad') {
|
||||
if (!isString(s.name) || !isString(s.vid) || !isString(s.ifname))
|
||||
continue;
|
||||
|
||||
/* parent device is a bridge */
|
||||
if (bridges[s.ifname]) {
|
||||
/* parent bridge is VLAN enabled, device refers to VLAN ports */
|
||||
if (bridges[s.ifname].vlan_filtering)
|
||||
mapping[s.name] = bridges[s.ifname].vlans[s.vid];
|
||||
|
||||
/* parent bridge is not VLAN enabled, device refers to all bridge ports */
|
||||
else
|
||||
mapping[s.name] = bridges[s.ifname].ports;
|
||||
}
|
||||
|
||||
/* parent is a simple netdev */
|
||||
else {
|
||||
mapping[s.name] = [ s.ifname ];
|
||||
}
|
||||
|
||||
resolveVLANChain(s.ifname, bridges, mapping);
|
||||
}
|
||||
}
|
||||
|
||||
/* resolve VLAN tagged interfaces in bridge ports */
|
||||
for (var brname in bridges) {
|
||||
for (var i = 0; i < bridges[brname].ports.length; i++)
|
||||
resolveVLANChain(bridges[brname].ports[i], bridges, mapping);
|
||||
|
||||
for (var vid in bridges[brname].vlans)
|
||||
for (var i = 0; i < bridges[brname].vlans[vid].length; i++)
|
||||
resolveVLANChain(bridges[brname].vlans[vid][i], bridges, mapping);
|
||||
}
|
||||
|
||||
/* find implicit VLAN devices */
|
||||
for (var i = 0, s; (s = interfaces[i]) != null; i++) {
|
||||
if (!isString(s.device))
|
||||
continue;
|
||||
|
||||
resolveVLANChain(s.device, bridges, mapping);
|
||||
}
|
||||
}
|
||||
|
||||
function resolveVLANPorts(ifname, mapping)
|
||||
{
|
||||
var ports = [];
|
||||
|
||||
if (mapping[ifname])
|
||||
for (var i = 0; i < mapping[ifname].length; i++)
|
||||
ports.push.apply(ports, resolveVLANPorts(mapping[ifname][i], mapping));
|
||||
else
|
||||
ports.push(ifname);
|
||||
|
||||
return ports.sort(L.naturalCompare);
|
||||
}
|
||||
|
||||
function buildInterfaceMapping(zones, networks) {
|
||||
var vlanmap = {},
|
||||
portmap = {},
|
||||
netmap = {};
|
||||
|
||||
buildVLANMappings(vlanmap);
|
||||
|
||||
for (var i = 0; i < networks.length; i++) {
|
||||
var l3dev = networks[i].getDevice();
|
||||
|
||||
if (!l3dev)
|
||||
continue;
|
||||
|
||||
var ports = resolveVLANPorts(l3dev.getName(), vlanmap);
|
||||
|
||||
for (var j = 0; j < ports.length; j++) {
|
||||
portmap[ports[j]] = portmap[ports[j]] || { networks: [], zones: [] };
|
||||
portmap[ports[j]].networks.push(networks[i]);
|
||||
}
|
||||
|
||||
netmap[networks[i].getName()] = networks[i];
|
||||
}
|
||||
|
||||
for (var i = 0; i < zones.length; i++) {
|
||||
var networknames = zones[i].getNetworks();
|
||||
|
||||
for (var j = 0; j < networknames.length; j++) {
|
||||
if (!netmap[networknames[j]])
|
||||
continue;
|
||||
|
||||
var l3dev = netmap[networknames[j]].getDevice();
|
||||
|
||||
if (!l3dev)
|
||||
continue;
|
||||
|
||||
var ports = resolveVLANPorts(l3dev.getName(), vlanmap);
|
||||
|
||||
for (var k = 0; k < ports.length; k++) {
|
||||
portmap[ports[k]] = portmap[ports[k]] || { networks: [], zones: [] };
|
||||
|
||||
if (portmap[ports[k]].zones.indexOf(zones[i]) === -1)
|
||||
portmap[ports[k]].zones.push(zones[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return portmap;
|
||||
}
|
||||
|
||||
function formatSpeed(speed, duplex) {
|
||||
if (speed && duplex) {
|
||||
var d = (duplex == 'half') ? '\u202f(H)' : '',
|
||||
e = E('span', { 'title': _('Speed: %d Mbit/s, Duplex: %s').format(speed, duplex) });
|
||||
|
||||
switch (speed) {
|
||||
case 10: e.innerText = '10\u202fM' + d; break;
|
||||
case 100: e.innerText = '100\u202fM' + d; break;
|
||||
case 1000: e.innerText = '1\u202fGbE' + d; break;
|
||||
case 2500: e.innerText = '2.5\u202fGbE'; break;
|
||||
case 5000: e.innerText = '5\u202fGbE'; break;
|
||||
case 10000: e.innerText = '10\u202fGbE'; break;
|
||||
case 25000: e.innerText = '25\u202fGbE'; break;
|
||||
case 40000: e.innerText = '40\u202fGbE'; break;
|
||||
default: e.innerText = '%d\u202fMbE%s'.format(speed, d);
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
return _('no link');
|
||||
}
|
||||
|
||||
function formatStats(portdev) {
|
||||
var stats = portdev._devstate('stats');
|
||||
|
||||
return ui.itemlist(E('span'), [
|
||||
_('Received bytes'), '%1024mB'.format(stats.rx_bytes),
|
||||
_('Received packets'), '%1000mPkts.'.format(stats.rx_packets),
|
||||
_('Received multicast'), '%1000mPkts.'.format(stats.multicast),
|
||||
_('Receive errors'), '%1000mPkts.'.format(stats.rx_errors),
|
||||
_('Receive dropped'), '%1000mPkts.'.format(stats.rx_dropped),
|
||||
|
||||
_('Transmitted bytes'), '%1024mB'.format(stats.tx_bytes),
|
||||
_('Transmitted packets'), '%1000mPkts.'.format(stats.tx_packets),
|
||||
_('Transmit errors'), '%1000mPkts.'.format(stats.tx_errors),
|
||||
_('Transmit dropped'), '%1000mPkts.'.format(stats.tx_dropped),
|
||||
|
||||
_('Collisions seen'), stats.collisions
|
||||
]);
|
||||
}
|
||||
|
||||
function renderNetworkBadge(network, zonename) {
|
||||
var l3dev = network.getDevice();
|
||||
var span = E('span', { 'class': 'ifacebadge', 'style': 'margin:.125em 0' }, [
|
||||
E('span', {
|
||||
'class': 'zonebadge',
|
||||
'title': zonename ? _('Part of zone %q').format(zonename) : _('No zone assigned'),
|
||||
'style': firewall.getZoneColorStyle(zonename)
|
||||
}, '\u202f'),
|
||||
'\u202f', network.getName(), ': '
|
||||
]);
|
||||
|
||||
if (l3dev)
|
||||
span.appendChild(E('img', {
|
||||
'title': l3dev.getI18n(),
|
||||
'src': L.resource('icons/%s%s.png'.format(l3dev.getType(), l3dev.isUp() ? '' : '_disabled'))
|
||||
}));
|
||||
else
|
||||
span.appendChild(E('em', _('(no interfaces attached)')));
|
||||
|
||||
return span;
|
||||
}
|
||||
|
||||
function renderNetworksTooltip(pmap) {
|
||||
var res = [ null ],
|
||||
zmap = {};
|
||||
|
||||
for (var i = 0; pmap && i < pmap.zones.length; i++) {
|
||||
var networknames = pmap.zones[i].getNetworks();
|
||||
|
||||
for (var k = 0; k < networknames.length; k++)
|
||||
zmap[networknames[k]] = pmap.zones[i].getName();
|
||||
}
|
||||
|
||||
for (var i = 0; pmap && i < pmap.networks.length; i++)
|
||||
res.push(E('br'), renderNetworkBadge(pmap.networks[i], zmap[pmap.networks[i].getName()]));
|
||||
|
||||
if (res.length > 1)
|
||||
res[0] = N_((res.length - 1) / 2, 'Part of network:', 'Part of networks:');
|
||||
else
|
||||
res[0] = _('Port is not part of any network');
|
||||
|
||||
return E([], res);
|
||||
}
|
||||
|
||||
return baseclass.extend({
|
||||
title: _('Ethernet Information'),
|
||||
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
L.resolveDefault(callLuciETHInfo(), {})
|
||||
L.resolveDefault(callNetworkStatus(), {}),
|
||||
firewall.getZones(),
|
||||
network.getNetworks(),
|
||||
uci.load('network')
|
||||
]);
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
var ethinfo = Array.isArray(data[0].ethinfo) ? data[0].ethinfo : [];
|
||||
var known_ports = [],
|
||||
port_map = buildInterfaceMapping(data[1], data[2]);
|
||||
|
||||
var table = E('table', { 'class': 'table' }, [
|
||||
E('tr', { 'class': 'tr table-titles' }, [
|
||||
E('th', { 'class': 'th' }, _('Ethernet Name')),
|
||||
E('th', { 'class': 'th' }, _('Link Status')),
|
||||
E('th', { 'class': 'th' }, _('Speed')),
|
||||
E('th', { 'class': 'th' }, _('Duplex'))
|
||||
])
|
||||
]);
|
||||
Object.keys(data[0]).forEach((k) => {
|
||||
if (['dsa', 'ethernet'].includes(data[0][k].devtype) && data[0][k]['link-advertising'].length)
|
||||
known_ports.push({
|
||||
device: k,
|
||||
netdev: network.instantiateDevice(k)
|
||||
});
|
||||
});
|
||||
|
||||
cbi_update_table(table, ethinfo.map(function(info) {
|
||||
var exp1;
|
||||
var exp2;
|
||||
known_ports.sort(function(a, b) {
|
||||
return L.naturalCompare(a.device, b.device);
|
||||
});
|
||||
|
||||
if (info.status == "yes")
|
||||
exp1 = _('Link Up');
|
||||
else if (info.status == "no")
|
||||
exp1 = _('Link Down');
|
||||
return E('div', { 'style': 'display:grid;grid-template-columns:repeat(auto-fit, minmax(100px, 1fr));margin-bottom:1em;align-items:center;justify-items:center;text-align:center' }, known_ports.map(function(port) {
|
||||
var speed = port.netdev.getSpeed(),
|
||||
duplex = port.netdev.getDuplex(),
|
||||
pmap = port_map[port.netdev.getName()],
|
||||
pzones = (pmap && pmap.zones.length) ? pmap.zones.sort(function(a, b) { return L.naturalCompare(a.getName(), b.getName()) }) : [ null ];
|
||||
|
||||
if (info.duplex == "Full")
|
||||
exp2 = _('Full Duplex');
|
||||
else if (info.duplex == "Half")
|
||||
exp2 = _('Half Duplex');
|
||||
else
|
||||
exp2 = _('-');
|
||||
|
||||
return [
|
||||
info.name,
|
||||
exp1,
|
||||
info.speed,
|
||||
exp2
|
||||
];
|
||||
return E('div', { 'class': 'ifacebox', 'style': 'margin:.25em;width:100px' }, [
|
||||
E('div', { 'class': 'ifacebox-head', 'style': 'font-weight:bold' }, [ port.netdev.getName() ]),
|
||||
E('div', { 'class': 'ifacebox-body' }, [
|
||||
E('img', { 'src': L.resource('icons/port_%s.png').format((speed && duplex) ? 'up' : 'down') }),
|
||||
E('br'),
|
||||
formatSpeed(speed, duplex)
|
||||
]),
|
||||
E('div', { 'class': 'ifacebox-head cbi-tooltip-container', 'style': 'display:flex' }, [
|
||||
E([], pzones.map(function(zone) {
|
||||
return E('div', {
|
||||
'class': 'zonebadge',
|
||||
'style': 'cursor:help;flex:1;height:3px;' + firewall.getZoneColorStyle(zone)
|
||||
});
|
||||
})),
|
||||
E('span', { 'class': 'cbi-tooltip left' }, [ renderNetworksTooltip(pmap) ])
|
||||
]),
|
||||
E('div', { 'class': 'ifacebox-body' }, [
|
||||
E('div', { 'class': 'cbi-tooltip-container', 'style': 'text-align:left;font-size:80%' }, [
|
||||
'\u25b2\u202f%1024.1mB'.format(port.netdev.getTXBytes()),
|
||||
E('br'),
|
||||
'\u25bc\u202f%1024.1mB'.format(port.netdev.getRXBytes()),
|
||||
E('span', { 'class': 'cbi-tooltip' }, formatStats(port.netdev))
|
||||
]),
|
||||
])
|
||||
]);
|
||||
}));
|
||||
|
||||
return E([
|
||||
table
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
@ -3,7 +3,8 @@
|
||||
"description": "Grant access to autocore",
|
||||
"read": {
|
||||
"ubus": {
|
||||
"luci": [ "getCPUInfo", "getETHInfo", "getTempInfo", "getCPUBench", "getCPUUsage" ]
|
||||
"luci": [ "getCPUInfo", "getTempInfo", "getCPUBench", "getCPUUsage" ],
|
||||
"network.device": [ "status" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user