{"id":222,"date":"2023-07-12T10:43:10","date_gmt":"2023-07-12T02:43:10","guid":{"rendered":"https:\/\/22222.v6.army:\/?p=222"},"modified":"2023-07-12T11:25:38","modified_gmt":"2023-07-12T03:25:38","slug":"luci-introduce-ethernet-port-status-view","status":"publish","type":"post","link":"https:\/\/ch.cc.ua\/?p=222","title":{"rendered":"luci  &#8211;introduce ethernet port status view"},"content":{"rendered":"\n<audio src=\"https:\/\/ch.cc.ua\/wp-content\/uploads\/2023\/07\/Sophia-\u7c73\u5c0f\u6002kelly-\u300aLet-Me-Down-Slowly\u300b.mp3\" controls loop autoplay><\/audio>\n\n\n\n<p>luci-mod-status: introduce ethernet port status view<\/p>\n\n\n\n<p class=\"has-pale-pink-color has-text-color has-medium-font-size\"><strong>1.&nbsp;modules\/luci-mod-status\/htdocs\/luci-static\/resources\/view\/status\/include\/29_ports.js<\/strong><\/p>\n\n\n\n<p>&#8216;use strict&#8217;;<\/p>\n\n\n\n<p>&#8216;require baseclass&#8217;;<\/p>\n\n\n\n<p>&#8216;require fs&#8217;;<\/p>\n\n\n\n<p>&#8216;require ui&#8217;;<\/p>\n\n\n\n<p>&#8216;require uci&#8217;;<\/p>\n\n\n\n<p>&#8216;require network&#8217;;<\/p>\n\n\n\n<p>&#8216;require firewall&#8217;;<\/p>\n\n\n\n<p>function isString(v)<\/p>\n\n\n\n<p>{<\/p>\n\n\n\n<p>return typeof(v) === &#8216;string&#8217; &amp;&amp; v !== &#8221;;<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>function resolveVLANChain(ifname, bridges, mapping)<\/p>\n\n\n\n<p>{<\/p>\n\n\n\n<p>while (!mapping[ifname]) {<\/p>\n\n\n\n<p>var m = ifname.match(\/^(.+)\\.([^.]+)$\/);<\/p>\n\n\n\n<p>if (!m)<\/p>\n\n\n\n<p>break;<\/p>\n\n\n\n<p>if (bridges[m[1]]) {<\/p>\n\n\n\n<p>if (bridges[m[1]].vlan_filtering)<\/p>\n\n\n\n<p>mapping[ifname] = bridges[m[1]].vlans[m[2]];<\/p>\n\n\n\n<p>else<\/p>\n\n\n\n<p>mapping[ifname] = bridges[m[1]].ports;<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>else if (\/^[0-9]{1,4}$\/.test(m[2]) &amp;&amp; m[2] &lt;= 4095) {<\/p>\n\n\n\n<p>mapping[ifname] = [ m[1] ];<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>else {<\/p>\n\n\n\n<p>break;<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>ifname = m[1];<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>function buildVLANMappings(mapping)<\/p>\n\n\n\n<p>{<\/p>\n\n\n\n<p>var bridge_vlans = uci.sections(&#8216;network&#8217;, &#8216;bridge-vlan&#8217;),<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp; vlan_devices = uci.sections(&#8216;network&#8217;, &#8216;device&#8217;),<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp; interfaces = uci.sections(&#8216;network&#8217;, &#8216;interface&#8217;),<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp; bridges = {};<\/p>\n\n\n\n<p>\/* find bridge VLANs *\/<\/p>\n\n\n\n<p>for (var i = 0, s; (s = bridge_vlans[i]) != null; i++) {<\/p>\n\n\n\n<p>if (!isString(s.device) || !\/^[0-9]{1,4}$\/.test(s.vlan) || +s.vlan &gt; 4095)<\/p>\n\n\n\n<p>continue;<\/p>\n\n\n\n<p>var aliases = L.toArray(s.alias),<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp; ports = L.toArray(s.ports),<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp; br = bridges[s.device] = (bridges[s.device] || { ports: [], vlans: {}, vlan_filtering: true });<\/p>\n\n\n\n<p>br.vlans[s.vlan] = [];<\/p>\n\n\n\n<p>for (var j = 0; j &lt; ports.length; j++) {<\/p>\n\n\n\n<p>var port = ports[j].replace(\/:[ut*]+$\/, &#8221;);<\/p>\n\n\n\n<p>if (br.ports.indexOf(port) === -1)<\/p>\n\n\n\n<p>br.ports.push(port);<\/p>\n\n\n\n<p>br.vlans[s.vlan].push(port);<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>for (var j = 0; j &lt; aliases.length; j++)<\/p>\n\n\n\n<p>if (aliases[j] != s.vlan)<\/p>\n\n\n\n<p>br.vlans[aliases[j]] = br.vlans[s.vlan];<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>\/* find bridges, VLAN devices *\/<\/p>\n\n\n\n<p>for (var i = 0, s; (s = vlan_devices[i]) != null; i++) {<\/p>\n\n\n\n<p>if (s.type == &#8216;bridge&#8217;) {<\/p>\n\n\n\n<p>if (!isString(s.name))<\/p>\n\n\n\n<p>continue;<\/p>\n\n\n\n<p>var ports = L.toArray(s.ports),<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp; br = bridges[s.name] || (bridges[s.name] = { ports: [], vlans: {}, vlan_filtering: false });<\/p>\n\n\n\n<p>if (s.vlan_filtering == &#8216;0&#8217;)<\/p>\n\n\n\n<p>br.vlan_filtering = false;<\/p>\n\n\n\n<p>else if (s.vlan_filtering == &#8216;1&#8217;)<\/p>\n\n\n\n<p>br.vlan_filtering = true;<\/p>\n\n\n\n<p>for (var j = 0; j &lt; ports.length; j++)<\/p>\n\n\n\n<p>if (br.ports.indexOf(ports[j]) === -1)<\/p>\n\n\n\n<p>br.ports.push(ports[j]);<\/p>\n\n\n\n<p>mapping[s.name] = br.ports;<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>else if (s.type == &#8216;8021q&#8217; || s.type == &#8216;8021ad&#8217;) {<\/p>\n\n\n\n<p>if (!isString(s.name) || !isString(s.vid) || !isString(s.ifname))<\/p>\n\n\n\n<p>continue;<\/p>\n\n\n\n<p>\/* parent device is a bridge *\/<\/p>\n\n\n\n<p>if (bridges[s.ifname]) {<\/p>\n\n\n\n<p>\/* parent bridge is VLAN enabled, device refers to VLAN ports *\/<\/p>\n\n\n\n<p>if (bridges[s.ifname].vlan_filtering)<\/p>\n\n\n\n<p>mapping[s.name] = bridges[s.ifname].vlans[s.vid];<\/p>\n\n\n\n<p>\/* parent bridge is not VLAN enabled, device refers to all bridge ports *\/<\/p>\n\n\n\n<p>else<\/p>\n\n\n\n<p>mapping[s.name] = bridges[s.ifname].ports;<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>\/* parent is a simple netdev *\/<\/p>\n\n\n\n<p>else {<\/p>\n\n\n\n<p>mapping[s.name] = [ s.ifname ];<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>resolveVLANChain(s.ifname, bridges, mapping);<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>\/* resolve VLAN tagged interfaces in bridge ports *\/<\/p>\n\n\n\n<p>for (var brname in bridges) {<\/p>\n\n\n\n<p>for (var i = 0; i &lt; bridges[brname].ports.length; i++)<\/p>\n\n\n\n<p>resolveVLANChain(bridges[brname].ports[i], bridges, mapping);<\/p>\n\n\n\n<p>for (var vid in bridges[brname].vlans)<\/p>\n\n\n\n<p>for (var i = 0; i &lt; bridges[brname].vlans[vid].length; i++)<\/p>\n\n\n\n<p>resolveVLANChain(bridges[brname].vlans[vid][i], bridges, mapping);<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>\/* find implicit VLAN devices *\/<\/p>\n\n\n\n<p>for (var i = 0, s; (s = interfaces[i]) != null; i++) {<\/p>\n\n\n\n<p>if (!isString(s.device))<\/p>\n\n\n\n<p>continue;<\/p>\n\n\n\n<p>resolveVLANChain(s.device, bridges, mapping);<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>function resolveVLANPorts(ifname, mapping)<\/p>\n\n\n\n<p>{<\/p>\n\n\n\n<p>var ports = [];<\/p>\n\n\n\n<p>if (mapping[ifname])<\/p>\n\n\n\n<p>for (var i = 0; i &lt; mapping[ifname].length; i++)<\/p>\n\n\n\n<p>ports.push.apply(ports, resolveVLANPorts(mapping[ifname][i], mapping));<\/p>\n\n\n\n<p>else<\/p>\n\n\n\n<p>ports.push(ifname);<\/p>\n\n\n\n<p>return ports.sort(L.naturalCompare);<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>function buildInterfaceMapping(zones, networks) {<\/p>\n\n\n\n<p>var vlanmap = {},<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp; portmap = {},<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp; netmap = {};<\/p>\n\n\n\n<p>buildVLANMappings(vlanmap);<\/p>\n\n\n\n<p>for (var i = 0; i &lt; networks.length; i++) {<\/p>\n\n\n\n<p>var l3dev = networks[i].getDevice();<\/p>\n\n\n\n<p>if (!l3dev)<\/p>\n\n\n\n<p>continue;<\/p>\n\n\n\n<p>var ports = resolveVLANPorts(l3dev.getName(), vlanmap);<\/p>\n\n\n\n<p>for (var j = 0; j &lt; ports.length; j++) {<\/p>\n\n\n\n<p>portmap[ports[j]] = portmap[ports[j]] || { networks: [], zones: [] };<\/p>\n\n\n\n<p>portmap[ports[j]].networks.push(networks[i]);<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>netmap[networks[i].getName()] = networks[i];<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>for (var i = 0; i &lt; zones.length; i++) {<\/p>\n\n\n\n<p>var networknames = zones[i].getNetworks();<\/p>\n\n\n\n<p>for (var j = 0; j &lt; networknames.length; j++) {<\/p>\n\n\n\n<p>if (!netmap[networknames[j]])<\/p>\n\n\n\n<p>continue;<\/p>\n\n\n\n<p>var l3dev = netmap[networknames[j]].getDevice();<\/p>\n\n\n\n<p>if (!l3dev)<\/p>\n\n\n\n<p>continue;<\/p>\n\n\n\n<p>var ports = resolveVLANPorts(l3dev.getName(), vlanmap);<\/p>\n\n\n\n<p>for (var k = 0; k &lt; ports.length; k++) {<\/p>\n\n\n\n<p>portmap[ports[k]] = portmap[ports[k]] || { networks: [], zones: [] };<\/p>\n\n\n\n<p>if (portmap[ports[k]].zones.indexOf(zones[i]) === -1)<\/p>\n\n\n\n<p>portmap[ports[k]].zones.push(zones[i]);<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>return portmap;<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>function formatSpeed(speed, duplex) {<\/p>\n\n\n\n<p>if (speed &amp;&amp; duplex) {<\/p>\n\n\n\n<p>var d = (duplex == &#8216;half&#8217;) ? &#8216;\\u202f(H)&#8217; : &#8221;,<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp; e = E(&#8216;span&#8217;, { &#8216;title&#8217;: _(&#8216;Speed: %d Mibit\/s, Duplex: %s&#8217;).format(speed, duplex) });<\/p>\n\n\n\n<p>switch (speed) {<\/p>\n\n\n\n<p>case 10:&nbsp;&nbsp;&nbsp; e.innerText = &#8217;10\\u202fM&#8217; + d;&nbsp; break;<\/p>\n\n\n\n<p>case 100:&nbsp;&nbsp; e.innerText = &#8216;100\\u202fM&#8217; + d; break;<\/p>\n\n\n\n<p>case 1000:&nbsp; e.innerText = &#8216;1\\u202fGbE&#8217; + d; break;<\/p>\n\n\n\n<p>case 2500:&nbsp; e.innerText = &#8216;2.5\\u202fGbE&#8217;;&nbsp;&nbsp; break;<\/p>\n\n\n\n<p>case 5000:&nbsp; e.innerText = &#8216;5\\u202fGbE&#8217;;&nbsp;&nbsp;&nbsp;&nbsp; break;<\/p>\n\n\n\n<p>case 10000: e.innerText = &#8217;10\\u202fGbE&#8217;;&nbsp;&nbsp;&nbsp; break;<\/p>\n\n\n\n<p>case 25000: e.innerText = &#8217;25\\u202fGbE&#8217;;&nbsp;&nbsp;&nbsp; break;<\/p>\n\n\n\n<p>case 40000: e.innerText = &#8217;40\\u202fGbE&#8217;;&nbsp;&nbsp;&nbsp; break;<\/p>\n\n\n\n<p>default:&nbsp;&nbsp;&nbsp; e.innerText = &#8216;%d\\u202fMbE%s&#8217;.format(speed, d);<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>return e;<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>return _(&#8216;no link&#8217;);<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>function formatStats(portdev) {<\/p>\n\n\n\n<p>var stats = portdev._devstate(&#8216;stats&#8217;);<\/p>\n\n\n\n<p>return ui.itemlist(E(&#8216;span&#8217;), [<\/p>\n\n\n\n<p>_(&#8216;Received bytes&#8217;), &#8216;%1024mB&#8217;.format(stats.rx_bytes),<\/p>\n\n\n\n<p>_(&#8216;Received packets&#8217;), &#8216;%1000mPkts.&#8217;.format(stats.rx_packets),<\/p>\n\n\n\n<p>_(&#8216;Received multicast&#8217;), &#8216;%1000mPkts.&#8217;.format(stats.multicast),<\/p>\n\n\n\n<p>_(&#8216;Receive errors&#8217;), &#8216;%1000mPkts.&#8217;.format(stats.rx_errors),<\/p>\n\n\n\n<p>_(&#8216;Receive dropped&#8217;), &#8216;%1000mPkts.&#8217;.format(stats.rx_dropped),<\/p>\n\n\n\n<p>_(&#8216;Transmitted bytes&#8217;), &#8216;%1024mB&#8217;.format(stats.tx_bytes),<\/p>\n\n\n\n<p>_(&#8216;Transmitted packets&#8217;), &#8216;%1000mPkts.&#8217;.format(stats.tx_packets),<\/p>\n\n\n\n<p>_(&#8216;Transmit errors&#8217;), &#8216;%1000mPkts.&#8217;.format(stats.tx_errors),<\/p>\n\n\n\n<p>_(&#8216;Transmit dropped&#8217;), &#8216;%1000mPkts.&#8217;.format(stats.tx_dropped),<\/p>\n\n\n\n<p>_(&#8216;Collisions seen&#8217;), stats.collisions<\/p>\n\n\n\n<p>]);<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>function renderNetworkBadge(network, zonename) {<\/p>\n\n\n\n<p>var l3dev = network.getDevice();<\/p>\n\n\n\n<p>var span = E(&#8216;span&#8217;, { &#8216;class&#8217;: &#8216;ifacebadge&#8217;, &#8216;style&#8217;: &#8216;margin:.125em 0&#8217; }, [<\/p>\n\n\n\n<p>E(&#8216;span&#8217;, {<\/p>\n\n\n\n<p>&#8216;class&#8217;: &#8216;zonebadge&#8217;,<\/p>\n\n\n\n<p>&#8216;title&#8217;: zonename ? _(&#8216;Part of zone %q&#8217;).format(zonename) : _(&#8216;No zone assigned&#8217;),<\/p>\n\n\n\n<p>&#8216;style&#8217;: firewall.getZoneColorStyle(zonename)<\/p>\n\n\n\n<p>}, &#8216;\\u202f&#8217;),<\/p>\n\n\n\n<p>&#8216;\\u202f&#8217;, network.getName(), &#8216;: &#8216;<\/p>\n\n\n\n<p>]);<\/p>\n\n\n\n<p>if (l3dev)<\/p>\n\n\n\n<p>span.appendChild(E(&#8216;img&#8217;, {<\/p>\n\n\n\n<p>&#8216;title&#8217;: l3dev.getI18n(),<\/p>\n\n\n\n<p>&#8216;src&#8217;: L.resource(&#8216;icons\/%s%s.png&#8217;.format(l3dev.getType(), l3dev.isUp() ? &#8221; : &#8216;_disabled&#8217;))<\/p>\n\n\n\n<p>}));<\/p>\n\n\n\n<p>else<\/p>\n\n\n\n<p>span.appendChild(E(&#8217;em&#8217;, _(&#8216;(no interfaces attached)&#8217;)));<\/p>\n\n\n\n<p>return span;<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>function renderNetworksTooltip(pmap) {<\/p>\n\n\n\n<p>var res = [ null ],<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp; zmap = {};<\/p>\n\n\n\n<p>for (var i = 0; pmap &amp;&amp; i &lt; pmap.zones.length; i++) {<\/p>\n\n\n\n<p>var networknames = pmap.zones[i].getNetworks();<\/p>\n\n\n\n<p>for (var k = 0; k &lt; networknames.length; k++)<\/p>\n\n\n\n<p>zmap[networknames[k]] = pmap.zones[i].getName();<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>for (var i = 0; pmap &amp;&amp; i &lt; pmap.networks.length; i++)<\/p>\n\n\n\n<p>res.push(E(&#8216;br&#8217;), renderNetworkBadge(pmap.networks[i], zmap[pmap.networks[i].getName()]));<\/p>\n\n\n\n<p>if (res.length &gt; 1)<\/p>\n\n\n\n<p>res[0] = N_((res.length &#8211; 1) \/ 2, &#8216;Part of network:&#8217;, &#8216;Part of networks:&#8217;);<\/p>\n\n\n\n<p>else<\/p>\n\n\n\n<p>res[0] = _(&#8216;Port is not part of any network&#8217;);<\/p>\n\n\n\n<p>return E([], res);<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>return baseclass.extend({<\/p>\n\n\n\n<p>title: _(&#8216;Port status&#8217;),<\/p>\n\n\n\n<p>load: function() {<\/p>\n\n\n\n<p>return Promise.all([<\/p>\n\n\n\n<p>L.resolveDefault(fs.read(&#8216;\/etc\/board.json&#8217;), &#8216;{}&#8217;),<\/p>\n\n\n\n<p>firewall.getZones(),<\/p>\n\n\n\n<p>network.getNetworks(),<\/p>\n\n\n\n<p>uci.load(&#8216;network&#8217;)<\/p>\n\n\n\n<p>]);<\/p>\n\n\n\n<p>},<\/p>\n\n\n\n<p>render: function(data) {<\/p>\n\n\n\n<p>var board = JSON.parse(data[0]),<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp; known_ports = [],<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp; port_map = buildInterfaceMapping(data[1], data[2]);<\/p>\n\n\n\n<p>if (L.isObject(board) &amp;&amp; L.isObject(board.network)) {<\/p>\n\n\n\n<p>for (var k = &#8216;lan&#8217;; k != null; k = (k == &#8216;lan&#8217;) ? &#8216;wan&#8217; : null) {<\/p>\n\n\n\n<p>if (!L.isObject(board.network[k]))<\/p>\n\n\n\n<p>continue;<\/p>\n\n\n\n<p>if (Array.isArray(board.network[k].ports))<\/p>\n\n\n\n<p>for (let i = 0; i &lt; board.network[k].ports.length; i++)<\/p>\n\n\n\n<p>known_ports.push({<\/p>\n\n\n\n<p>role: k,<\/p>\n\n\n\n<p>device: board.network[k].ports[i],<\/p>\n\n\n\n<p>netdev: network.instantiateDevice(board.network[k].ports[i])<\/p>\n\n\n\n<p>});<\/p>\n\n\n\n<p>else if (typeof(board.network[k].device) == &#8216;string&#8217;)<\/p>\n\n\n\n<p>known_ports.push({<\/p>\n\n\n\n<p>role: k,<\/p>\n\n\n\n<p>device: board.network[k].device,<\/p>\n\n\n\n<p>netdev: network.instantiateDevice(board.network[k].device)<\/p>\n\n\n\n<p>});<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>known_ports.sort(function(a, b) {<\/p>\n\n\n\n<p>return L.naturalCompare(a.device, b.device);<\/p>\n\n\n\n<p>});<\/p>\n\n\n\n<p>return E(&#8216;div&#8217;, { &#8216;style&#8217;: &#8216;display:grid;grid-template-columns:repeat(auto-fit, minmax(70px, 1fr));margin-bottom:1em&#8217; }, known_ports.map(function(port) {<\/p>\n\n\n\n<p>var speed = port.netdev.getSpeed(),<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp; duplex = port.netdev.getDuplex(),<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp; pmap = port_map[port.netdev.getName()],<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp; pzones = (pmap &amp;&amp; pmap.zones.length) ? pmap.zones.sort(function(a, b) { return L.naturalCompare(a.getName(), b.getName()) }) : [ null ];<\/p>\n\n\n\n<p>return E(&#8216;div&#8217;, { &#8216;class&#8217;: &#8216;ifacebox&#8217;, &#8216;style&#8217;: &#8216;margin:.25em;min-width:70px;max-width:100px&#8217; }, [<\/p>\n\n\n\n<p>E(&#8216;div&#8217;, { &#8216;class&#8217;: &#8216;ifacebox-head&#8217;, &#8216;style&#8217;: &#8216;font-weight:bold&#8217; }, [ port.netdev.getName() ]),<\/p>\n\n\n\n<p>E(&#8216;div&#8217;, { &#8216;class&#8217;: &#8216;ifacebox-body&#8217; }, [<\/p>\n\n\n\n<p>E(&#8216;img&#8217;, { &#8216;src&#8217;: L.resource(&#8216;icons\/port_%s.png&#8217;).format((speed &amp;&amp; duplex) ? &#8216;up&#8217; : &#8216;down&#8217;) }),<\/p>\n\n\n\n<p>E(&#8216;br&#8217;),<\/p>\n\n\n\n<p>formatSpeed(speed, duplex)<\/p>\n\n\n\n<p>]),<\/p>\n\n\n\n<p>E(&#8216;div&#8217;, { &#8216;class&#8217;: &#8216;ifacebox-head cbi-tooltip-container&#8217;, &#8216;style&#8217;: &#8216;display:flex&#8217; }, [<\/p>\n\n\n\n<p>E([], pzones.map(function(zone) {<\/p>\n\n\n\n<p>return E(&#8216;div&#8217;, {<\/p>\n\n\n\n<p>&#8216;class&#8217;: &#8216;zonebadge&#8217;,<\/p>\n\n\n\n<p>&#8216;style&#8217;: &#8216;cursor:help;flex:1;height:3px;&#8217; + firewall.getZoneColorStyle(zone)<\/p>\n\n\n\n<p>});<\/p>\n\n\n\n<p>})),<\/p>\n\n\n\n<p>E(&#8216;span&#8217;, { &#8216;class&#8217;: &#8216;cbi-tooltip left&#8217; }, [ renderNetworksTooltip(pmap) ])<\/p>\n\n\n\n<p>]),<\/p>\n\n\n\n<p>E(&#8216;div&#8217;, { &#8216;class&#8217;: &#8216;ifacebox-body&#8217; }, [<\/p>\n\n\n\n<p>E(&#8216;div&#8217;, { &#8216;class&#8217;: &#8216;cbi-tooltip-container&#8217;, &#8216;style&#8217;: &#8216;text-align:left;font-size:80%&#8217; }, [<\/p>\n\n\n\n<p>&#8216;\\u25b2\\u202f%1024.1mB&#8217;.format(port.netdev.getTXBytes()),<\/p>\n\n\n\n<p>E(&#8216;br&#8217;),<\/p>\n\n\n\n<p>&#8216;\\u25bc\\u202f%1024.1mB&#8217;.format(port.netdev.getRXBytes()),<\/p>\n\n\n\n<p>E(&#8216;span&#8217;, { &#8216;class&#8217;: &#8216;cbi-tooltip&#8217; }, formatStats(port.netdev))<\/p>\n\n\n\n<p>]),<\/p>\n\n\n\n<p>])<\/p>\n\n\n\n<p>]);<\/p>\n\n\n\n<p>}));<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>});<\/p>\n\n\n\n<p class=\"has-pale-pink-color has-text-color has-medium-font-size\"><strong>2.modules\/luci-mod-status\/root\/usr\/share\/rpcd\/acl.d\/luci-mod-status-index.json<\/strong><\/p>\n\n\n\n<p>{<\/p>\n\n\n\n<p>&#8220;luci-mod-status-index&#8221;: {<\/p>\n\n\n\n<p>&#8220;description&#8221;: &#8220;Grant access to main status display&#8221;,<\/p>\n\n\n\n<p>&#8220;read&#8221;: {<\/p>\n\n\n\n<p>&#8220;file&#8221;: {<\/p>\n\n\n\n<p>&#8220;\/etc\/board.json&#8221;: [ &#8220;read&#8221; ],<\/p>\n\n\n\n<p>&#8220;\/proc\/sys\/net\/netfilter\/nf_conntrack_count&#8221;: [ &#8220;read&#8221; ],<\/p>\n\n\n\n<p>&#8220;\/proc\/sys\/net\/netfilter\/nf_conntrack_max&#8221;: [ &#8220;read&#8221; ],<\/p>\n\n\n\n<p>&#8220;\/usr\/lib\/lua\/luci\/version.lua&#8221;: [ &#8220;read&#8221; ],<\/p>\n<div class=\"clearfix\"><\/div>","protected":false},"excerpt":{"rendered":"<p>luci-mod-status: introduce ethernet port status view 1. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":223,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-222","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-3"],"_links":{"self":[{"href":"https:\/\/ch.cc.ua\/index.php?rest_route=\/wp\/v2\/posts\/222","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ch.cc.ua\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ch.cc.ua\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ch.cc.ua\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ch.cc.ua\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=222"}],"version-history":[{"count":4,"href":"https:\/\/ch.cc.ua\/index.php?rest_route=\/wp\/v2\/posts\/222\/revisions"}],"predecessor-version":[{"id":237,"href":"https:\/\/ch.cc.ua\/index.php?rest_route=\/wp\/v2\/posts\/222\/revisions\/237"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ch.cc.ua\/index.php?rest_route=\/wp\/v2\/media\/223"}],"wp:attachment":[{"href":"https:\/\/ch.cc.ua\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=222"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ch.cc.ua\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=222"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ch.cc.ua\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=222"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}