From 0d0882aea0e67faaef769c3517f17b1f650f0794 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Wed, 14 Aug 2019 18:17:10 +0200 Subject: [PATCH] luci-base: network.js: overhaul code - Rework internal state management - Implement new utility functions such as getL3Device() or getModemType() - Fix bugs in various functions Signed-off-by: Jo-Philipp Wich --- .../htdocs/luci-static/resources/network.js | 551 +++++++++--------- 1 file changed, 271 insertions(+), 280 deletions(-) diff --git a/modules/luci-base/htdocs/luci-static/resources/network.js b/modules/luci-base/htdocs/luci-static/resources/network.js index 487adabb4..d6a97408a 100644 --- a/modules/luci-base/htdocs/luci-static/resources/network.js +++ b/modules/luci-base/htdocs/luci-static/resources/network.js @@ -90,119 +90,98 @@ var callGetProtoHandlers = rpc.declare({ expect: { '': {} } }); -var _cache = {}, - _flush = true, +var _init = null, _state = null, - _protocols = {}; - -function getWifiState(flush) { - if (_cache.wifi == null || flush) - return callNetworkWirelessStatus().then(function(state) { - if (!L.isObject(state)) - throw !1; - return (_cache.wifi = state); - }).catch(function() { - return (_cache.wifi = {}); - }); - - return Promise.resolve(_cache.wifi); + _protocols = {}, + _protospecs = {}; + +function getWifiState(cache) { + return callNetworkWirelessStatus().then(function(state) { + if (!L.isObject(state)) + throw !1; + return state; + }).catch(function() { + return {}; + }); } -function getInterfaceState(flush) { - if (_cache.interfacedump == null || flush) - return callNetworkInterfaceStatus().then(function(state) { - if (!Array.isArray(state)) - throw !1; - return (_cache.interfacedump = state); - }).catch(function() { - return (_cache.interfacedump = []); - }); - - return Promise.resolve(_cache.interfacedump); +function getInterfaceState(cache) { + return callNetworkInterfaceStatus().then(function(state) { + if (!Array.isArray(state)) + throw !1; + return state; + }).catch(function() { + return []; + }); } -function getDeviceState(flush) { - if (_cache.devicedump == null || flush) - return callNetworkDeviceStatus().then(function(state) { - if (!L.isObject(state)) - throw !1; - return (_cache.devicedump = state); - }).catch(function() { - return (_cache.devicedump = {}); - }); - - return Promise.resolve(_cache.devicedump); +function getDeviceState(cache) { + return callNetworkDeviceStatus().then(function(state) { + if (!L.isObject(state)) + throw !1; + return state; + }).catch(function() { + return {}; + }); } -function getIfaddrState(flush) { - if (_cache.ifaddrs == null || flush) - return callLuciIfaddrs().then(function(addrs) { - if (!Array.isArray(addrs)) - throw !1; - return (_cache.ifaddrs = addrs); - }).catch(function() { - return (_cache.ifaddrs = []); - }); - - return Promise.resolve(_cache.ifaddrs); +function getIfaddrState(cache) { + return callLuciIfaddrs().then(function(addrs) { + if (!Array.isArray(addrs)) + throw !1; + return addrs; + }).catch(function() { + return []; + }); } -function getNetdevState(flush) { - if (_cache.devices == null || flush) - return callLuciNetdevs().then(function(state) { - if (!L.isObject(state)) - throw !1; - return (_cache.devices = state); - }).catch(function() { - return (_cache.devices = {}); - }); - - return Promise.resolve(_cache.devices); +function getNetdevState(cache) { + return callLuciNetdevs().then(function(state) { + if (!L.isObject(state)) + throw !1; + return state; + }).catch(function() { + return {}; + }); } -function getBoardState(flush) { - if (_cache.board == null || flush) - return callLuciBoardjson().then(function(state) { - if (!L.isObject(state)) - throw !1; - return (_cache.board = state); - }).catch(function() { - return (_cache.board = {}); - }); - - return Promise.resolve(_cache.board); +function getBoardState(cache) { + return callLuciBoardjson().then(function(state) { + if (!L.isObject(state)) + throw !1; + return state; + }).catch(function() { + return {}; + }); } -function getProtocolHandlers(flush) { - if (_cache.protocols == null || flush) - return callGetProtoHandlers().then(function(protos) { - if (!L.isObject(protos)) - throw !1; +function getProtocolHandlers(cache) { + return callGetProtoHandlers().then(function(protos) { + if (!L.isObject(protos)) + throw !1; - _cache.protocols = protos; + Object.assign(_protospecs, protos); - return Promise.all(Object.keys(protos).map(function(p) { - return Promise.resolve(L.require('protocol.%s'.format(p))).catch(function(err) { - if (L.isObject(err) && err.name != 'NetworkError') - L.error(err); - }); - })).then(function() { - return _cache.protocols; + return Promise.all(Object.keys(protos).map(function(p) { + return Promise.resolve(L.require('protocol.%s'.format(p))).catch(function(err) { + if (L.isObject(err) && err.name != 'NetworkError') + L.error(err); }); - }).catch(function() { - return (_cache.protocols = {}); + })).then(function() { + return protos; }); - - return Promise.resolve(_cache.protocols); + }).catch(function() { + return {}; + }); } function getWifiStateBySid(sid) { var s = uci.get('wireless', sid); if (s != null && s['.type'] == 'wifi-iface') { - for (var radioname in _cache.wifi) { - for (var i = 0; i < _cache.wifi[radioname].interfaces.length; i++) { - var netstate = _cache.wifi[radioname].interfaces[i]; + for (var radioname in _state.radios) { + for (var i = 0; i < _state.radios[radioname].interfaces.length; i++) { + var netstate = _state.radios[radioname].interfaces[i]; if (typeof(netstate.section) != 'string') continue; @@ -210,7 +189,7 @@ function getWifiStateBySid(sid) { var s2 = uci.get('wireless', netstate.section); if (s2 != null && s['.type'] == s2['.type'] && s['.name'] == s2['.name']) - return [ radioname, _cache.wifi[radioname], netstate ]; + return [ radioname, _state.radios[radioname], netstate ]; } } } @@ -219,15 +198,15 @@ function getWifiStateBySid(sid) { } function getWifiStateByIfname(ifname) { - for (var radioname in _cache.wifi) { - for (var i = 0; i < _cache.wifi[radioname].interfaces.length; i++) { - var netstate = _cache.wifi[radioname].interfaces[i]; + for (var radioname in _state.radios) { + for (var i = 0; i < _state.radios[radioname].interfaces.length; i++) { + var netstate = _state.radios[radioname].interfaces[i]; if (typeof(netstate.ifname) != 'string') continue; if (netstate.ifname == ifname) - return [ radioname, _cache.wifi[radioname], netstate ]; + return [ radioname, _state.radios[radioname], netstate ]; } } @@ -367,7 +346,7 @@ function appendValue(config, section, option, value) { rv = false; if (isArray == false) - values = String(values || '').split(/\s+/); + values = L.toArray(values); if (values.indexOf(value) == -1) { values.push(value); @@ -385,7 +364,7 @@ function removeValue(config, section, option, value) { rv = false; if (isArray == false) - values = String(values || '').split(/\s+/); + values = L.toArray(values); for (var i = values.length - 1; i >= 0; i--) { if (values[i] == value) { @@ -444,159 +423,160 @@ function maskToPrefix(mask, v6) { return bits; } -function initNetworkState() { - var flush = _flush; - - _flush = false; - - if (_state != null && !flush) - return Promise.resolve(_state); - - if (_cache.pendingInit != null) - return Promise.resolve(_cache.pendingInit); - - return (_cache.pendingInit = Promise.all([ - getInterfaceState(flush), getDeviceState(flush), getBoardState(flush), - getWifiState(flush), getIfaddrState(flush), getNetdevState(flush), getProtocolHandlers(flush), - uci.load('network'), uci.load('wireless'), uci.load('luci') - ]).finally(function() { - var ifaddrs = _cache.ifaddrs, - devices = _cache.devices, - board = _cache.board, - s = { isTunnel: {}, isBridge: {}, isSwitch: {}, isWifi: {}, interfaces: {}, bridges: {}, switches: {} }; - - for (var i = 0, a; (a = ifaddrs[i]) != null; i++) { - var name = a.name.replace(/:.+$/, ''); - - if (isVirtualIfname(name)) - s.isTunnel[name] = true; - - if (s.isTunnel[name] || !(isIgnoredIfname(name) || isVirtualIfname(name))) { - s.interfaces[name] = s.interfaces[name] || { - idx: a.ifindex || i, - name: name, - rawname: a.name, - flags: [], - ipaddrs: [], - ip6addrs: [] - }; - - if (a.family == 'packet') { - s.interfaces[name].flags = a.flags; - s.interfaces[name].stats = a.data; - s.interfaces[name].macaddr = a.addr; - } - else if (a.family == 'inet') { - s.interfaces[name].ipaddrs.push(a.addr + '/' + a.netmask); - } - else if (a.family == 'inet6') { - s.interfaces[name].ip6addrs.push(a.addr + '/' + a.netmask); +function initNetworkState(refresh) { + if (_state == null || refresh) { + _init = _init || Promise.all([ + getInterfaceState(), getDeviceState(), getBoardState(), + getWifiState(), getIfaddrState(), getNetdevState(), getProtocolHandlers(), + uci.load('network'), uci.load('wireless'), uci.load('luci') + ]).then(function(data) { + var board = data[2], ifaddrs = data[4], devices = data[5]; + var s = { + isTunnel: {}, isBridge: {}, isSwitch: {}, isWifi: {}, + ifaces: data[0], devices: data[1], radios: data[3], + netdevs: {}, bridges: {}, switches: {} + }; + + for (var i = 0, a; (a = ifaddrs[i]) != null; i++) { + var name = a.name.replace(/:.+$/, ''); + + if (isVirtualIfname(name)) + s.isTunnel[name] = true; + + if (s.isTunnel[name] || !(isIgnoredIfname(name) || isVirtualIfname(name))) { + s.netdevs[name] = s.netdevs[name] || { + idx: a.ifindex || i, + name: name, + rawname: a.name, + flags: [], + ipaddrs: [], + ip6addrs: [] + }; + + if (a.family == 'packet') { + s.netdevs[name].flags = a.flags; + s.netdevs[name].stats = a.data; + s.netdevs[name].macaddr = a.addr; + } + else if (a.family == 'inet') { + s.netdevs[name].ipaddrs.push(a.addr + '/' + a.netmask); + } + else if (a.family == 'inet6') { + s.netdevs[name].ip6addrs.push(a.addr + '/' + a.netmask); + } } } - } - for (var devname in devices) { - var dev = devices[devname]; + for (var devname in devices) { + var dev = devices[devname]; + + if (dev.bridge) { + var b = { + name: devname, + id: dev.id, + stp: dev.stp, + ifnames: [] + }; - if (dev.bridge) { - var b = { - name: devname, - id: dev.id, - stp: dev.stp, - ifnames: [] - }; + for (var i = 0; dev.ports && i < dev.ports.length; i++) { + var subdev = s.netdevs[dev.ports[i]]; - for (var i = 0; dev.ports && i < dev.ports.length; i++) { - var subdev = s.interfaces[dev.ports[i]]; + if (subdev == null) + continue; - if (subdev == null) - continue; + b.ifnames.push(subdev); + subdev.bridge = b; + } - b.ifnames.push(subdev); - subdev.bridge = b; + s.bridges[devname] = b; + s.isBridge[devname] = true; } - s.bridges[devname] = b; - } - - if (s.interfaces.hasOwnProperty(devname)) { - Object.assign(s.interfaces[devname], { - macaddr: dev.mac, - type: dev.type, - mtu: dev.mtu, - qlen: dev.qlen - }); + if (s.netdevs.hasOwnProperty(devname)) { + Object.assign(s.netdevs[devname], { + macaddr: dev.mac, + type: dev.type, + mtu: dev.mtu, + qlen: dev.qlen + }); + } } - } - if (L.isObject(board.switch)) { - for (var switchname in board.switch) { - var layout = board.switch[switchname], - netdevs = {}, - nports = {}, - ports = [], - pnum = null, - role = null; - - if (L.isObject(layout) && Array.isArray(layout.ports)) { - for (var i = 0, port; (port = layout.ports[i]) != null; i++) { - if (typeof(port) == 'object' && typeof(port.num) == 'number' && - (typeof(port.role) == 'string' || typeof(port.device) == 'string')) { - var spec = { - num: port.num, - role: port.role || 'cpu', - index: (port.index != null) ? port.index : port.num - }; - - if (port.device != null) { - spec.device = port.device; - spec.tagged = spec.need_tag; - netdevs[port.num] = port.device; + if (L.isObject(board.switch)) { + for (var switchname in board.switch) { + var layout = board.switch[switchname], + netdevs = {}, + nports = {}, + ports = [], + pnum = null, + role = null; + + if (L.isObject(layout) && Array.isArray(layout.ports)) { + for (var i = 0, port; (port = layout.ports[i]) != null; i++) { + if (typeof(port) == 'object' && typeof(port.num) == 'number' && + (typeof(port.role) == 'string' || typeof(port.device) == 'string')) { + var spec = { + num: port.num, + role: port.role || 'cpu', + index: (port.index != null) ? port.index : port.num + }; + + if (port.device != null) { + spec.device = port.device; + spec.tagged = spec.need_tag; + netdevs[port.num] = port.device; + } + + ports.push(spec); + + if (port.role != null) + nports[port.role] = (nports[port.role] || 0) + 1; } + } - ports.push(spec); + ports.sort(function(a, b) { + if (a.role != b.role) + return (a.role < b.role) ? -1 : 1; - if (port.role != null) - nports[port.role] = (nports[port.role] || 0) + 1; - } - } + return (a.index - b.index); + }); - ports.sort(function(a, b) { - if (a.role != b.role) - return (a.role < b.role) ? -1 : 1; + for (var i = 0, port; (port = ports[i]) != null; i++) { + if (port.role != role) { + role = port.role; + pnum = 1; + } - return (a.index - b.index); - }); + if (role == 'cpu') + port.label = 'CPU (%s)'.format(port.device); + else if (nports[role] > 1) + port.label = '%s %d'.format(role.toUpperCase(), pnum++); + else + port.label = role.toUpperCase(); - for (var i = 0, port; (port = ports[i]) != null; i++) { - if (port.role != role) { - role = port.role; - pnum = 1; + delete port.role; + delete port.index; } - if (role == 'cpu') - port.label = 'CPU (%s)'.format(port.device); - else if (nports[role] > 1) - port.label = '%s %d'.format(role.toUpperCase(), pnum++); - else - port.label = role.toUpperCase(); - - delete port.role; - delete port.index; + s.switches[switchname] = { + ports: ports, + netdevs: netdevs + }; } - - s.switches[switchname] = { - ports: ports, - netdevs: netdevs - }; } } - } - delete _cache.pendingInit; + if (L.isObject(board.dsl) && L.isObject(board.dsl.modem)) { + s.hasDSLModem = board.dsl.modem; + } - return (_state = s); - })); + _init = null; + + return (_state = s); + }); + } + + return (_state != null ? Promise.resolve(_state) : _init); } function ifnameOf(obj) { @@ -637,9 +617,8 @@ Network = L.Class.extend({ maskToPrefix: maskToPrefix, flushCache: function() { - return Promise.resolve(_state).then(function() { - _flush = true; - }); + initNetworkState(true); + return _init; }, getProtocol: function(protoname, netname) { @@ -660,7 +639,7 @@ Network = L.Class.extend({ }, registerProtocol: function(protoname, methods) { - var spec = L.isObject(_cache.protocols) ? _cache.protocols[protoname] : null; + var spec = L.isObject(_protospecs) ? _protospecs[protoname] : null; var proto = Protocol.extend(Object.assign({ getI18n: function() { return protoname; @@ -740,9 +719,9 @@ Network = L.Class.extend({ return this.instantiateNetwork(name); } else if (name != null) { - for (var i = 0; i < _cache.interfacedump.length; i++) - if (_cache.interfacedump[i].interface == name) - return this.instantiateNetwork(name, _cache.interfacedump[i].proto); + for (var i = 0; i < _state.ifaces.length; i++) + if (_state.ifaces[i].interface == name) + return this.instantiateNetwork(name, _state.ifaces[i].proto); } return null; @@ -757,10 +736,10 @@ Network = L.Class.extend({ for (var i = 0; i < uciInterfaces.length; i++) networks[uciInterfaces[i]['.name']] = this.instantiateNetwork(uciInterfaces[i]['.name']); - for (var i = 0; i < _cache.interfacedump.length; i++) - if (networks[_cache.interfacedump[i].interface] == null) - networks[_cache.interfacedump[i].interface] = - this.instantiateNetwork(_cache.interfacedump[i].interface, _cache.interfacedump[i].proto); + for (var i = 0; i < _state.ifaces.length; i++) + if (networks[_state.ifaces[i].interface] == null) + networks[_state.ifaces[i].interface] = + this.instantiateNetwork(_state.ifaces[i].interface, _state.ifaces[i].proto); var rv = []; @@ -874,7 +853,7 @@ Network = L.Class.extend({ if (name == null) return null; - if (_state.interfaces.hasOwnProperty(name) || isWifiIfname(name)) + if (_state.netdevs.hasOwnProperty(name) || isWifiIfname(name)) return this.instantiateDevice(name); var netid = getWifiNetidBySid(name); @@ -905,7 +884,7 @@ Network = L.Class.extend({ } } - for (var ifname in _state.interfaces) { + for (var ifname in _state.netdevs) { if (devices.hasOwnProperty(ifname)) continue; @@ -1096,7 +1075,7 @@ Network = L.Class.extend({ var radioname = existingDevice['.name'], netid = getWifiNetidBySid(sid) || []; - return this.instantiateWifiNetwork(sid, radioname, _cache.wifi[radioname], netid[0], null, { ifname: netid }); + return this.instantiateWifiNetwork(sid, radioname, _state.radios[radioname], netid[0], null, { ifname: netid }); }, this)); }, @@ -1116,20 +1095,24 @@ Network = L.Class.extend({ return initNetworkState().then(L.bind(function() { var rv = []; - for (var i = 0; i < _state.interfacedump.length; i++) { - if (!Array.isArray(_state.interfacedump[i].route)) + for (var i = 0; i < _state.ifaces.length; i++) { + if (!Array.isArray(_state.ifaces[i].route)) continue; - for (var j = 0; j < _state.interfacedump[i].route.length; j++) { - if (typeof(_state.interfacedump[i].route[j]) != 'object' || - typeof(_state.interfacedump[i].route[j].target) != 'string' || - typeof(_state.interfacedump[i].route[j].mask) != 'number') + for (var j = 0; j < _state.ifaces[i].route.length; j++) { + if (typeof(_state.ifaces[i].route[j]) != 'object' || + typeof(_state.ifaces[i].route[j].target) != 'string' || + typeof(_state.ifaces[i].route[j].mask) != 'number') continue; - if (_state.interfacedump[i].route[j].table) + if (_state.ifaces[i].route[j].table) continue; - rv.push(_state.interfacedump[i]); + if (_state.ifaces[i].route[j].target != addr || + _state.ifaces[i].route[j].mask != mask) + continue; + + rv.push(_state.ifaces[i]); } } @@ -1141,25 +1124,25 @@ Network = L.Class.extend({ return initNetworkState().then(L.bind(function() { var rv = []; - for (var i = 0; i < _state.interfacedump.length; i++) { - if (Array.isArray(_state.interfacedump[i]['ipv4-address'])) - for (var j = 0; j < _state.interfacedump[i]['ipv4-address'].length; j++) - if (typeof(_state.interfacedump[i]['ipv4-address'][j]) == 'object' && - _state.interfacedump[i]['ipv4-address'][j].address == addr) - return _state.interfacedump[i]; - - if (Array.isArray(_state.interfacedump[i]['ipv6-address'])) - for (var j = 0; j < _state.interfacedump[i]['ipv6-address'].length; j++) - if (typeof(_state.interfacedump[i]['ipv6-address'][j]) == 'object' && - _state.interfacedump[i]['ipv6-address'][j].address == addr) - return _state.interfacedump[i]; - - if (Array.isArray(_state.interfacedump[i]['ipv6-prefix-assignment'])) - for (var j = 0; j < _state.interfacedump[i]['ipv6-prefix-assignment'].length; j++) - if (typeof(_state.interfacedump[i]['ipv6-prefix-assignment'][j]) == 'object' && - typeof(_state.interfacedump[i]['ipv6-prefix-assignment'][j]['local-address']) == 'object' && - _state.interfacedump[i]['ipv6-prefix-assignment'][j]['local-address'].address == addr) - return _state.interfacedump[i]; + for (var i = 0; i < _state.ifaces.length; i++) { + if (Array.isArray(_state.ifaces[i]['ipv4-address'])) + for (var j = 0; j < _state.ifaces[i]['ipv4-address'].length; j++) + if (typeof(_state.ifaces[i]['ipv4-address'][j]) == 'object' && + _state.ifaces[i]['ipv4-address'][j].address == addr) + return _state.ifaces[i]; + + if (Array.isArray(_state.ifaces[i]['ipv6-address'])) + for (var j = 0; j < _state.ifaces[i]['ipv6-address'].length; j++) + if (typeof(_state.ifaces[i]['ipv6-address'][j]) == 'object' && + _state.ifaces[i]['ipv6-address'][j].address == addr) + return _state.ifaces[i]; + + if (Array.isArray(_state.ifaces[i]['ipv6-prefix-assignment'])) + for (var j = 0; j < _state.ifaces[i]['ipv6-prefix-assignment'].length; j++) + if (typeof(_state.ifaces[i]['ipv6-prefix-assignment'][j]) == 'object' && + typeof(_state.ifaces[i]['ipv6-prefix-assignment'][j]['local-address']) == 'object' && + _state.ifaces[i]['ipv6-prefix-assignment'][j]['local-address'].address == addr) + return _state.ifaces[i]; } return null; @@ -1218,6 +1201,12 @@ Network = L.Class.extend({ getIfnameOf: function(obj) { return ifnameOf(obj); + }, + + getDSLModemType: function() { + return initNetworkState().then(function() { + return _state.hasDSLModem ? _state.hasDSLModem.type : null; + }); } }); @@ -1236,11 +1225,11 @@ Protocol = L.Class.extend({ }, _ubus: function(field) { - for (var i = 0; i < _cache.interfacedump.length; i++) { - if (_cache.interfacedump[i].interface != this.sid) + for (var i = 0; i < _state.ifaces.length; i++) { + if (_state.ifaces[i].interface != this.sid) continue; - return (field != null ? _cache.interfacedump[i][field] : _cache.interfacedump[i]); + return (field != null ? _state.ifaces[i][field] : _state.ifaces[i]); } }, @@ -1538,11 +1527,6 @@ Protocol = L.Class.extend({ return new Device(ifname, this); } else { - var ifname = this._ubus('l3_device') || this._ubus('device'); - - if (ifname != null) - return L.network.instantiateDevice(ifname, this); - var ifnames = L.toArray(uci.get('network', this.sid, 'ifname')); for (var i = 0; i < ifnames.length; i++) { @@ -1561,6 +1545,11 @@ Protocol = L.Class.extend({ return (ifname != null ? L.network.instantiateDevice(ifname, this) : null); }, + getL3Device: function() { + var ifname = this._ubus('l3_device'); + return (ifname != null ? L.network.instantiateDevice(ifname, this) : null); + }, + getDevices: function() { var rv = []; @@ -1647,12 +1636,12 @@ Device = L.Class.extend({ } this.ifname = this.ifname || ifname; - this.dev = _state.interfaces[this.ifname]; + this.dev = _state.netdevs[this.ifname]; this.network = network; }, _ubus: function(field) { - var dump = _cache.devicedump[this.ifname] || {}; + var dump = _state.devices[this.ifname] || {}; return (field != null ? dump[field] : dump); }, @@ -1751,7 +1740,9 @@ Device = L.Class.extend({ return null; for (var i = 0; i < br.ifnames.length; i++) - rv.push(L.network.instantiateDevice(br.ifnames[i])); + rv.push(L.network.instantiateDevice(br.ifnames[i].name)); + + rv.sort(deviceSort); return rv; }, @@ -1882,8 +1873,8 @@ WifiDevice = L.Class.extend({ }, isUp: function() { - if (L.isObject(_cache.wifi[this.sid])) - return (_cache.wifi[this.sid].up == true); + if (L.isObject(_state.radios[this.sid])) + return (_state.radios[this.sid].up == true); return false; }, -- 2.25.1