diff --git a/package/emortal/autocore/files/generic/21_ethinfo.js b/package/emortal/autocore/files/generic/21_ethinfo.js index 449b8638962..d66b44a4b46 100644 --- a/package/emortal/autocore/files/generic/21_ethinfo.js +++ b/package/emortal/autocore/files/generic/21_ethinfo.js @@ -1,53 +1,54 @@ 'use strict'; 'require baseclass'; +'require fs'; 'require rpc'; 'require network'; -var callSwconfigFeatures = rpc.declare({ - object: 'luci', - method: 'getSwconfigFeatures', - params: ['switch'], - expect: { '': {} } -}); - -var callSwconfigPortState = rpc.declare({ +let callSwconfigPortState = rpc.declare({ object: 'luci', method: 'getSwconfigPortState', params: ['switch'], expect: { result: [] } }); -var callLuciBoardJSON = rpc.declare({ +let callLuciBoardJSON = rpc.declare({ object: 'luci-rpc', method: 'getBoardJSON', expect: { '': {} } }); -var callLuciNetworkDevices = rpc.declare({ +let callLuciNetworkDevices = rpc.declare({ object: 'luci-rpc', method: 'getNetworkDevices', expect: { '': {} } }); -var isDSA = false; - -const ethStyle = { - box: 'max-width: 100px;', - head: ` - border-radius: 7px 7px 0 0; - text-align: center; - font-weight: bold;`, - body: ` - border: 1px solid lightgrey; - border-radius: 0 0 7px 7px; - display: flex; flex-direction: column; - align-items: center; justify-content: center;`, - icon: 'margin: 5px; width: 40px;', - speed: 'font-size: 0.8rem; font-weight: bold;', - traffic: ` - border-top: 1px solid lightgrey; - font-size: 0.8rem;` -}; +function getSwitchPortFlow() { + const portFlow = []; + fs.exec('/sbin/swconfig', ['dev', 'switch0', 'show']).then((res) => { + const lines = res.stdout.trim().split(/\n/); + let portNum; + for (let line of lines) { + let match = line.match(/^Port\s+(\d+):$/); + if (match != null) { + portNum = Number(match[1]); + portFlow[portNum] = {}; + continue; + } + match = line.match(/^TxByte\s*:\s*(\d+)$/); + if (match != null) { + portFlow[portNum].rxflow = Number(match[1]); + continue; + } + match = line.match(/^RxByte\s*:\s*(\d+)$/); + if (match != null) { + portFlow[portNum].txflow = Number(match[1]); + continue; + } + } + }); + return portFlow; +} function formatSpeed(speed) { if (speed <= 0) return '-'; @@ -57,123 +58,150 @@ function formatSpeed(speed) { } function getPortColor(carrier, duplex) { - if (!carrier) return 'background-color: whitesmoke;'; - if (duplex === 'full' || duplex === true) - return 'background-color: greenyellow;'; - return 'background-color: darkorange'; + if (!carrier) return 'Gainsboro;'; + if (duplex === 'full' || duplex === true) return 'greenyellow;'; + return 'darkorange'; } function getPortIcon(carrier) { return L.resource(`icons/port_${carrier ? 'up' : 'down'}.png`); } -function portDom(link, duplex, label, speed, tx_bytes, rx_bytes) { - const portIcon = getPortIcon(link); - const portColor = getPortColor(link, duplex); - - return E('div', { style: ethStyle.box }, [ - E('div', { style: ethStyle.head + portColor }, label), - E('div', { style: ethStyle.body }, [ - E('img', { style: ethStyle.icon, src: portIcon }), - E('div', { style: ethStyle.speed }, formatSpeed(speed)), - E('div', { style: ethStyle.traffic }, [ - '\u25b2\u202f%1024.1mB'.format(tx_bytes), - E('br'), - '\u25bc\u202f%1024.1mB'.format(rx_bytes) - ]) - ]) - ]); -} - -return baseclass.extend({ - title: _('Ethernet Information'), - - load: function () { - return network.getSwitchTopologies().then(function (topologies) { - if (Object.keys(topologies).length === 0) { - isDSA = true; - return Promise.all([ - L.resolveDefault(callLuciBoardJSON(), {}), - L.resolveDefault(callLuciNetworkDevices(), {}) - ]); - } - - callSwconfigPortState('switch0').then((ports) => { - topologies.switch0.portstate = ports; +function getPorts(board, netdevs, switches, portflow) { + const ports = []; + + if (Object.keys(switches).length === 0) { + const network = board.network; + const ifnames = [network?.wan?.device].concat(network?.lan?.ports); + for (const ifname of ifnames) { + if (ifname in netdevs === false) continue; + const dev = netdevs[ifname]; + ports.push({ + ifname: dev.name, + carrier: dev.link.carrier, + duplex: dev.link.duplex, + speed: dev.link.speed, + txflow: dev.stats.tx_bytes, + rxflow: dev.stats.rx_bytes }); - return Promise.all([ - topologies, - L.resolveDefault(callLuciBoardJSON(), {}), - L.resolveDefault(callLuciNetworkDevices(), {}) - ]); - }); - }, + } + return ports; + } - render_gsw: function (data) { - const topologies = data[0]; - const board = data[1]; - const netdevs = data[2]; - - let stats; - let foundWAN = false; - const ethPorts = []; - const switch0 = topologies.switch0; - for (const port of switch0.ports) { - const label = port.label.toUpperCase(); - const { link, duplex, speed } = switch0.portstate[port.num]; - const txrx = { tx_bytes: 0, rx_bytes: 0 }; - - if (label.startsWith('WAN')) { - foundWAN = true; - stats = netdevs[board.network.wan.device].stats; - const { tx_bytes, rx_bytes } = stats; - ethPorts.unshift( - portDom(link, duplex, 'WAN', speed, tx_bytes, rx_bytes) - ); - } else if (label.startsWith('LAN')) { - stats = netdevs['br-lan'].stats; - const { tx_bytes, rx_bytes } = link ? stats : txrx; - ethPorts.push(portDom(link, duplex, label, speed, tx_bytes, rx_bytes)); + let wanInSwitch; + const switch0 = switches['switch0']; + const lan = netdevs['br-lan']; + const wan = netdevs[board.network.wan.device]; + for (const port of switch0.ports) { + const label = port.label.toUpperCase(); + const portstate = switch0.portstate[port.num]; + portstate.ifname = label; + portstate.carrier = portstate.link; + if (portflow[port.num]) { + portstate.txflow = portflow[port.num].txflow; + portstate.rxflow = portflow[port.num].rxflow; + } + if (label.startsWith('WAN')) { + wanInSwitch = true; + if (!portstate.rxflow && wan) { + portstate.txflow = wan.stats.tx_bytes; + portstate.rxflow = wan.stats.rx_bytes; } + ports.unshift(portstate); + } else if (label.startsWith('LAN')) { + if (!portstate.rxflow && lan) { + portstate.txflow = lan.stats.tx_bytes; + portstate.rxflow = lan.stats.rx_bytes; + } + ports.push(portstate); } + } + if (wanInSwitch) return ports; + + if (wan) { + ports.unshift({ + ifname: 'WAN', + carrier: wan.link.carrier, + duplex: wan.link.duplex, + speed: wan.link.speed, + txflow: wan.stats.tx_bytes, + rxflow: wan.stats.rx_bytes + }); + } + return ports; +} - if (foundWAN) return ethPorts; - - const wan = netdevs[board.network.wan.device]; - const { speed, duplex, carrier } = wan.link; - const { tx_bytes, rx_bytes } = wan.stats; - ethPorts.unshift( - portDom(carrier, duplex, 'WAN', speed, tx_bytes, rx_bytes) +function renderPorts(data) { + const css = { + grids: ` + display: grid; grid-gap: 5px 10px; + grid-template-columns: repeat(auto-fit, minmax(80px, 1fr)); + margin-bottom: 1em; + `, + head: ` + color: Black; + text-align: center; + font-weight: bold; + border-radius: 7px 7px 0 0; + `, + body: ` + border: 1px solid lightgrey; + border-radius: 0 0 7px 7px; + display: flex; flex-direction: column; + align-items: center; justify-content: center;`, + icon: 'margin: 5px; width: 32px;', + speed: 'font-size: 0.8rem; font-weight: bold;', + flow: ` + border-top: 1px solid lightgrey; + font-size: 0.8rem;` + }; + + const ports = []; + getPorts(...data).forEach((port) => { + const { carrier, duplex } = port; + const ifname = port.ifname.replace(' ', ''); + const color = `background-color: ${getPortColor(carrier, duplex)};`; + ports.push( + E('div', {}, [ + E('div', { style: css.head + color }, ifname), + E('div', { style: css.body }, [ + E('img', { style: css.icon, src: getPortIcon(carrier) }), + E('div', { style: css.speed }, formatSpeed(port.speed)), + E('div', { style: css.flow }, [ + '\u25b2\u202f%1024.1mB'.format(carrier ? port.txflow : 0), + E('br'), + '\u25bc\u202f%1024.1mB'.format(carrier ? port.rxflow : 0) + ]) + ]) + ]) ); - return ethPorts; - }, + }); - render_dsa: function (data) { - const board = data[0]; - const netdevs = data[1]; - - const ethPorts = []; - const wan = board.network.wan.device; - let devices = `${wan},lan0,lan1,lan2,lan3,lan4,lan5,lan6`; - devices = devices.split(','); - for (const device of devices) { - if (device in netdevs === false) continue; - const dev = netdevs[device]; - const label = dev.name; - const { speed, duplex, carrier } = dev.link; - const { tx_bytes, rx_bytes } = dev.stats; - ethPorts.push(portDom(carrier, duplex, label, speed, tx_bytes, rx_bytes)); - } + return E('div', { style: css.grids }, ports); +} - return ethPorts; +return baseclass.extend({ + title: _('Ethernet Information'), + + load: function () { + return Promise.all([ + L.resolveDefault(callLuciBoardJSON(), {}), + L.resolveDefault(callLuciNetworkDevices(), {}), + network.getSwitchTopologies().then((topologies) => { + if (Object.keys(topologies).length === 0) return {}; + callSwconfigPortState('switch0').then((portstate) => { + topologies['switch0'].portstate = portstate; + }); + return topologies; + }), + network.getSwitchTopologies().then((topologies) => { + if (Object.keys(topologies).length === 0) return []; + return getSwitchPortFlow(); + }) + ]); }, render: function (data) { - const ethPorts = isDSA ? this.render_dsa(data) : this.render_gsw(data); - const gridStyle = ` - display: grid; grid-gap: 5px 5px; - grid-template-columns: repeat(auto-fit, minmax(70px, 1fr)); - margin-bottom: 1em`; - return E('div', { style: gridStyle }, ethPorts); + return renderPorts(data); } });