diff options
author | Ayushman Tripathi <ayushmantripathi7724@gmail.com> | 2023-06-26 01:23:48 +0530 |
---|---|---|
committer | Ayushman Tripathi <ayushmantripathi7724@gmail.com> | 2023-07-20 20:05:50 +0530 |
commit | ebd09332b4330001ecd3a86d1b379e0afc3bafee (patch) | |
tree | 39e973d153f0081800822e6a4a325f71d4441a00 /applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr | |
parent | fd5440a7be5506fa39c3993f10f754dedb5e8888 (diff) |
luci-app-olsr: migrate to js
Signed-off-by: Ayushman Tripathi <ayushmantripathi7724@gmail.com>
luci-app-olsr: migrate to js fix XSS vulnerability
Signed-off-by: Ayushman Tripathi <ayushmantripathi7724@gmail.com>
luci-app-olsr: migrate to js
luci-app-olsr: migrate to js fix minor bugs
Signed-off-by: Ayushman Tripathi <ayushmantripathi7724@gmail.com>
luci-app-olsr: migrate to js
luci-app-olsr: migrate to js fix plugins bugs
Signed-off-by: Ayushman Tripathi <ayushmantripathi7724@gmail.com>
luci-app-olsr: migrate to js
luci-app-olsr: migrate to js fix interfaces bugs
Signed-off-by: Ayushman Tripathi <ayushmantripathi7724@gmail.com>
luci-app-olsr: migrate to js
luci-app-olsr: migrate to js fix interface & snr bugs
Signed-off-by: Ayushman Tripathi <ayushmantripathi7724@gmail.com>
luci-app-olsr: migrate to js
luci-app-olsr: migrate to js fix hostname
Signed-off-by: Ayushman Tripathi <ayushmantripathi7724@gmail.com>
luci-app-olsr: migrate to js
luci-app-olsr: migrate to js fix typo
Signed-off-by: Ayushman Tripathi <ayushmantripathi7724@gmail.com>
luci-app-olsr: migrate to js
luci-app-olsr: migrate to js fix missing files, use rpc for hostnames, remove luci-compat
Signed-off-by: Ayushman Tripathi <ayushmantripathi7724@gmail.com>
luci-app-olsr: migrate to js
luci-app-olsr: migrate to js fix menu order
Signed-off-by: Ayushman Tripathi <ayushmantripathi7724@gmail.com>
luci-app-olsr: migrate to js
Diffstat (limited to 'applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr')
8 files changed, 2054 insertions, 0 deletions
diff --git a/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/error_olsr.js b/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/error_olsr.js new file mode 100644 index 0000000000..2db2270781 --- /dev/null +++ b/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/error_olsr.js @@ -0,0 +1,16 @@ +'use strict'; +'require view'; +'require rpc'; +'require ui'; +return view.extend({ + render: function () { + return E('div', {}, [ + E('h2', { 'name': 'content' }, _('OLSR Daemon')), + E('p', { 'class': 'error' }, _('Unable to connect to the OLSR daemon!')), + E('p', {}, [_('Make sure that OLSRd is running, the "jsoninfo" plugin is loaded, configured on port 9090, and accepts connections from "127.0.0.1".')]), + ]); + }, + handleSaveApply: null, + handleSave: null, + handleReset: null, +}); diff --git a/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/hna.js b/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/hna.js new file mode 100644 index 0000000000..8b84e7a4a4 --- /dev/null +++ b/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/hna.js @@ -0,0 +1,271 @@ +'use strict'; +'require uci'; +'require view'; +'require poll'; +'require network'; +'require rpc'; +'require ui'; + +return view.extend({ + callGetJsonStatus: rpc.declare({ + object: 'olsrinfo', + method: 'getjsondata', + params: ['otable', 'v4_port', 'v6_port'], + }), + + callGetHosts: rpc.declare({ + object: 'olsrinfo', + method: 'hosts', + }), + + fetch_jsoninfo: function (otable) { + var jsonreq4 = ''; + var jsonreq6 = ''; + var v4_port = parseInt(uci.get('olsrd', 'olsrd_jsoninfo', 'port') || '') || 9090; + var v6_port = parseInt(uci.get('olsrd6', 'olsrd_jsoninfo', 'port') || '') || 9090; + var json; + var self = this; + return new Promise(function (resolve, reject) { + L.resolveDefault(self.callGetJsonStatus(otable, v4_port, v6_port), {}) + .then(function (res) { + json = res; + + jsonreq4 = JSON.parse(json.jsonreq4); + jsonreq6 = json.jsonreq6 !== '' ? JSON.parse(json.jsonreq6) : []; + + var jsondata4 = {}; + var jsondata6 = {}; + var data4 = []; + var data6 = []; + var has_v4 = false; + var has_v6 = false; + + if (jsonreq4 === '' && jsonreq6 === '') { + window.location.href = 'error_olsr'; + reject([null, 0, 0, true]); + return; + } + + if (jsonreq4 !== '') { + has_v4 = true; + jsondata4 = jsonreq4 || {}; + if (otable === 'status') { + data4 = jsondata4; + } else { + data4 = jsondata4[otable] || []; + } + + for (var i = 0; i < data4.length; i++) { + data4[i]['proto'] = '4'; + } + } + + if (jsonreq6 !== '') { + has_v6 = true; + jsondata6 = jsonreq6 || {}; + if (otable === 'status') { + data6 = jsondata6; + } else { + data6 = jsondata6[otable] || []; + } + + for (var j = 0; j < data6.length; j++) { + data6[j]['proto'] = '6'; + } + } + + for (var k = 0; k < data6.length; k++) { + data4.push(data6[k]); + } + + resolve([data4, has_v4, has_v6, false]); + }) + .catch(function (err) { + console.error(err); + reject([null, 0, 0, true]); + }); + }); + }, + action_hna: function () { + var self = this; + return new Promise(function (resolve, reject) { + self + .fetch_jsoninfo('hna') + .then(function ([data, has_v4, has_v6, error]) { + if (error) { + reject(error); + } + + var resolveVal = uci.get('luci_olsr', 'general', 'resolve'); + + function compare(a, b) { + if (a.proto === b.proto) { + return a.genmask < b.genmask; + } else { + return a.proto < b.proto; + } + } + var modifiedData; + self + .callGetHosts() + .then(function (res) { + function matchHostnames(ip) { + var lines = res.hosts.split('\n'); + for (var i = 0; i < lines.length; i++) { + var ipandhostname = lines[i].trim().split(/\s+/); + if (ipandhostname[0] === ip) { + return ipandhostname[1]; + } + } + return null; + } + modifiedData = data.map(function (v) { + if (resolveVal === '1') { + var hostname = matchHostnames(v.gateway); + if (hostname) { + v.hostname = hostname; + } + } + if (v.validityTime) { + v.validityTime = parseInt((v.validityTime / 1000).toFixed(0)); + } + return v; + }); + + modifiedData.sort(compare); + + var result = { hna: modifiedData, has_v4: has_v4, has_v6: has_v6 }; + resolve(result); + }) + .catch(function (err) { + modifiedData = data; + console.error(err); + }); + }) + .catch(function (err) { + reject(err); + }); + }); + }, + + load: function () { + var self = this; + poll.add(function () { + self.render(); + }, 5); + return Promise.all([uci.load('olsrd'), uci.load('luci_olsr')]); + }, + render: function () { + var hna_res; + var has_v4; + var has_v6; + var self = this; + return this.action_hna() + .then(function (result) { + hna_res = result.hna; + has_v4 = result.has_v4; + has_v6 = result.has_v6; + var table = E('div', { 'class': 'table cbi-section-table', 'id': 'olsrd_hna' }, [ + E('div', { 'class': 'tr cbi-section-table-titles' }, [ + E('div', { 'class': 'th cbi-section-table-cell' }, _('Announced network')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('OLSR gateway')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('Validity Time')), + ]), + ]); + + var i = 1; + + var rv = []; + for (var k = 0; k < hna_res.length; k++) { + var entry = hna_res[k]; + rv.push({ + proto: entry.proto, + destination: entry.destination, + genmask: entry.genmask, + gateway: entry.gateway, + hostname: entry.hostname, + validityTime: entry.validityTime, + }); + } + + var info = rv; + + var hnadiv = document.getElementById('olsrd_hna'); + if (hnadiv) { + var s = + '<div class="tr cbi-section-table-titles">' + + '<div class="th cbi-section-table-cell">Announced network</div>' + + '<div class="th cbi-section-table-cell">OLSR gateway</div>' + + '<div class="th cbi-section-table-cell">Validity Time</div>' + + '</div>'; + + for (var idx = 0; idx < info.length; idx++) { + var hna = info[idx]; + var linkgw = ''; + s += '<div class="tr cbi-section-table-row cbi-rowstyle-' + (1 + (idx % 2)) + ' proto-' + hna.proto + '">'; + + if (hna.proto === '6') { + linkgw = '<a href="http://[' + hna.gateway + ']/cgi-bin-status.html">' + hna.gateway + '</a>'; + } else { + linkgw = '<a href="http://' + hna.gateway + '/cgi-bin-status.html">' + hna.gateway + '</a>'; + } + + var validity = hna.validityTime !== undefined ? hna.validityTime + 's' : '-'; + var hostname = hna.hostname !== null ? ' / <a href="http://%q/cgi-bin-status.html">%h</a>'.format(hna.hostname, hna.hostname) : ''; + + s += + '<div class="td cbi-section-table-cell left">' + + (hna.destination + '/' + hna.genmask) + + '</div>' + + '<div class="td cbi-section-table-cell left">' + + (linkgw + hostname) + + '</div>' + + '<div class="td cbi-section-table-cell left">' + + validity + + '</div>' + + '</div>'; + } + + hnadiv.innerHTML = s; + } + + var i = 1; + + for (var k = 0; k < hna_res.length; k++) { + var route = hna_res[k]; + + var tr = E('div', { 'class': 'tr cbi-section-table-row cbi-rowstyle-' + i + ' proto-' + route.proto }, [ + E('div', { 'class': 'td cbi-section-table-cell left' }, route.destination + '/' + route.genmask), + E('div', { 'class': 'td cbi-section-table-cell left' }, [ + route.proto === '6' ? E('a', { 'href': 'http://[' + route.gateway + ']/cgi-bin-status.html' }, route.gateway) : E('a', { 'href': 'http://' + route.gateway + '/cgi-bin-status.html' }, route.gateway), + route.hostname ? E('span', {}, [' / ', E('a', { 'href': 'http://%q/cgi-bin-status.html'.format(route.hostname) }, '%h'.format(route.hostname))]) : '', + ]), + E('div', { 'class': 'td cbi-section-table-cell left' }, route.validityTime ? route.validityTime + 's' : '-'), + ]); + + table.appendChild(tr); + i = (i % 2) + 1; + } + + var fieldset = E('fieldset', { 'class': 'cbi-section' }, [E('legend', {}, _('Overview of currently active OLSR host net announcements')), table]); + + var h2 = E('h2', { 'name': 'content' }, _('Active host net announcements')); + var divToggleButtons = E('div', { 'id': 'togglebuttons' }); + var statusOlsrCommonJs = null; + + if (has_v4 && has_v6) { + statusOlsrCommonJs = E('script', { 'type': 'text/javascript', 'src': L.resource('common/common_js.js') }); + } + + var result = E([], {}, [h2, divToggleButtons, fieldset, statusOlsrCommonJs]); + + return result; + }) + .catch(function (error) { + console.error(error); + }); + }, + handleSaveApply: null, + handleSave: null, + handleReset: null, +}); diff --git a/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/interfaces.js b/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/interfaces.js new file mode 100644 index 0000000000..fe0ac6a238 --- /dev/null +++ b/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/interfaces.js @@ -0,0 +1,186 @@ +'use strict'; +'require uci'; +'require view'; +'require rpc'; +'require ui'; +'require network'; + +return view.extend({ + callGetJsonStatus: rpc.declare({ + object: 'olsrinfo', + method: 'getjsondata', + params: ['otable', 'v4_port', 'v6_port'], + }), + + fetch_jsoninfo: function (otable) { + var jsonreq4 = ''; + var jsonreq6 = ''; + var v4_port = parseInt(uci.get('olsrd', 'olsrd_jsoninfo', 'port') || '') || 9090; + var v6_port = parseInt(uci.get('olsrd6', 'olsrd_jsoninfo', 'port') || '') || 9090; + var json; + var self = this; + return new Promise(function (resolve, reject) { + L.resolveDefault(self.callGetJsonStatus(otable, v4_port, v6_port), {}) + .then(function (res) { + json = res; + + jsonreq4 = JSON.parse(json.jsonreq4); + jsonreq6 = json.jsonreq6 !== '' ? JSON.parse(json.jsonreq6) : []; + + var jsondata4 = {}; + var jsondata6 = {}; + var data4 = []; + var data6 = []; + var has_v4 = false; + var has_v6 = false; + + if (jsonreq4 === '' && jsonreq6 === '') { + window.location.href = 'error_olsr'; + reject([null, 0, 0, true]); + return; + } + + if (jsonreq4 !== '') { + has_v4 = true; + jsondata4 = jsonreq4 || {}; + if (otable === 'status') { + data4 = jsondata4; + } else { + data4 = jsondata4[otable] || []; + } + + for (var i = 0; i < data4.length; i++) { + data4[i]['proto'] = '4'; + } + } + + if (jsonreq6 !== '') { + has_v6 = true; + jsondata6 = jsonreq6 || {}; + if (otable === 'status') { + data6 = jsondata6; + } else { + data6 = jsondata6[otable] || []; + } + + for (var j = 0; j < data6.length; j++) { + data6[j]['proto'] = '6'; + } + } + + for (var k = 0; k < data6.length; k++) { + data4.push(data6[k]); + } + + resolve([data4, has_v4, has_v6, false]); + }) + .catch(function (err) { + console.error(err); + reject([null, 0, 0, true]); + }); + }); + }, + + action_interfaces: async function () { + try { + const [data, has_v4, has_v6, error] = await this.fetch_jsoninfo('interfaces'); + + if (error) { + throw error; + } + + function compare(a, b) { + return a.proto < b.proto; + } + + const modifiedData = await Promise.all( + data.map(async function (v) { + const interfac = await network.getStatusByAddress(v.olsrInterface.ipAddress); + if (interfac) { + v.interface = interfac; + } + return v; + }) + ); + + modifiedData.sort(compare); + + const result = { + iface: modifiedData, + has_v4: has_v4, + has_v6: has_v6, + }; + + return result; + } catch (err) { + throw err; + } + }, + + load: function () { + return Promise.all([uci.load('olsrd'), uci.load('luci_olsr')]); + }, + render: function () { + var iface_res; + var has_v4; + var has_v6; + var self = this; + return this.action_interfaces() + .then(function (result) { + iface_res = result.iface; + has_v4 = result.has_v4; + has_v6 = result.has_v6; + var table = E('div', { 'class': 'table cbi-section-table' }, [ + E('div', { 'class': 'tr' }, [ + E('div', { 'class': 'th cbi-section-table-cell' }, _('Interface')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('Device')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('State')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('MTU')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('WLAN')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('Source address')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('Netmask')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('Broadcast address')), + ]), + ]); + var i = 1; + + for (var k = 0; k < iface_res.length; k++) { + var iface = iface_res[k]; + + var tr = E('div', { 'class': 'tr cbi-section-table-row cbi-rowstyle-' + i + ' proto-' + iface.proto }, [ + E('div', { 'class': 'td cbi-section-table-cell left' }, iface?.interface?.interface ?? '?'), + E('div', { 'class': 'td cbi-section-table-cell left' }, iface.name), + E('div', { 'class': 'td cbi-section-table-cell left' }, iface.olsrInterface.up ? _('up') : _('down')), + E('div', { 'class': 'td cbi-section-table-cell left' }, iface.olsrInterface.mtu), + E('div', { 'class': 'td cbi-section-table-cell left' }, iface.olsrInterface.wireless ? _('yes') : _('no')), + E('div', { 'class': 'td cbi-section-table-cell left' }, iface.olsrInterface.ipAddress), + E('div', { 'class': 'td cbi-section-table-cell left' }, iface.olsrInterface.ipv4Address !== '0.0.0.0' ? iface.olsrInterface.ipv4Netmask : ''), + E('div', { 'class': 'td cbi-section-table-cell left' }, iface.olsrInterface.ipv4Address !== '0.0.0.0' ? iface.olsrInterface.ipv4Broadcast : iface.olsrInterface.ipv6Multicast), + ]); + + table.appendChild(tr); + i = (i % 2) + 1; + } + + var fieldset = E('fieldset', { 'class': 'cbi-section' }, [E('legend', {}, _('Overview of interfaces where OLSR is running')), table]); + + var h2 = E('h2', { 'name': 'content' }, _('Interfaces')); + var divToggleButtons = E('div', { 'id': 'togglebuttons' }); + var statusOlsrCommonJs = null; + + if (has_v4 && has_v6) { + statusOlsrCommonJs = E('script', { 'type': 'text/javascript', 'src': L.resource('common/common_js.js') }); + } + + var result = E([], {}, [h2, divToggleButtons, fieldset, statusOlsrCommonJs]); + + return result; + }) + .catch(function (error) { + console.error(error); + }); + }, + handleSaveApply: null, + handleSave: null, + handleReset: null, +}); diff --git a/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/mid.js b/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/mid.js new file mode 100644 index 0000000000..828974a844 --- /dev/null +++ b/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/mid.js @@ -0,0 +1,171 @@ +'use strict'; +'require uci'; +'require view'; +'require rpc'; +'require ui'; + +return view.extend({ + callGetJsonStatus: rpc.declare({ + object: 'olsrinfo', + method: 'getjsondata', + params: ['otable', 'v4_port', 'v6_port'], + }), + + fetch_jsoninfo: function (otable) { + var jsonreq4 = ''; + var jsonreq6 = ''; + var v4_port = parseInt(uci.get('olsrd', 'olsrd_jsoninfo', 'port') || '') || 9090; + var v6_port = parseInt(uci.get('olsrd6', 'olsrd_jsoninfo', 'port') || '') || 9090; + var json; + var self = this; + return new Promise(function (resolve, reject) { + L.resolveDefault(self.callGetJsonStatus(otable, v4_port, v6_port), {}) + .then(function (res) { + json = res; + + jsonreq4 = JSON.parse(json.jsonreq4); + jsonreq6 = json.jsonreq6 !== '' ? JSON.parse(json.jsonreq6) : []; + var jsondata4 = {}; + var jsondata6 = {}; + var data4 = []; + var data6 = []; + var has_v4 = false; + var has_v6 = false; + + if (jsonreq4 === '' && jsonreq6 === '') { + window.location.href = 'error_olsr'; + reject([null, 0, 0, true]); + return; + } + + if (jsonreq4 !== '') { + has_v4 = true; + jsondata4 = jsonreq4 || {}; + if (otable === 'status') { + data4 = jsondata4; + } else { + data4 = jsondata4[otable] || []; + } + + for (var i = 0; i < data4.length; i++) { + data4[i]['proto'] = '4'; + } + } + + if (jsonreq6 !== '') { + has_v6 = true; + jsondata6 = jsonreq6 || {}; + if (otable === 'status') { + data6 = jsondata6; + } else { + data6 = jsondata6[otable] || []; + } + + for (var j = 0; j < data6.length; j++) { + data6[j]['proto'] = '6'; + } + } + + for (var k = 0; k < data6.length; k++) { + data4.push(data6[k]); + } + + resolve([data4, has_v4, has_v6, false]); + }) + .catch(function (err) { + console.error(err); + reject([null, 0, 0, true]); + }); + }); + }, + action_mid: function () { + var self = this; + return new Promise(function (resolve, reject) { + self + .fetch_jsoninfo('mid') + .then(function ([data, has_v4, has_v6, error]) { + if (error) { + reject(error); + } + + function compare(a, b) { + if (a.proto === b.proto) { + return a.main.ipAddress < b.main.ipAddress; + } else { + return a.proto < b.proto; + } + } + + data.sort(compare); + + var result = { mids: data, has_v4: has_v4, has_v6: has_v6 }; + resolve(result); + }) + .catch(function (err) { + reject(err); + }); + }); + }, + + render: function () { + var mids_res; + var has_v4; + var has_v6; + + return this.action_mid() + .then(function (result) { + mids_res = result.mids; + has_v4 = result.has_v4; + has_v6 = result.has_v6; + + var table = E('div', { 'class': 'table cbi-section-table' }, [ + E('div', { 'class': 'tr cbi-section-table-titles' }, [E('div', { 'class': 'th cbi-section-table-cell' }, _('OLSR node')), E('div', { class: 'th cbi-section-table-cell' }, _('Secondary OLSR interfaces'))]), + ]); + + var i = 1; + + for (var k = 0; k < mids_res.length; k++) { + var mid = mids_res[k]; + var aliases = ''; + for (var j = 0; j < mid.aliases.length; j++) { + var v = mid.aliases[j]; + var sep = aliases === '' ? '' : ', '; + aliases = v.ipAddress + sep + aliases; + } + + var host = mid.main.ipAddress; + if (mid.proto === '6') { + host = '[' + mid.main.ipAddress + ']'; + } + + var tr = E('div', { 'class': 'tr cbi-section-table-row cbi-rowstyle-' + i + ' proto-' + mid.proto }, [ + E('div', { 'class': 'td cbi-section-table-cell left' }, [E('a', { 'href': 'http://' + host + '/cgi-bin-status.html' }, mid.main.ipAddress)]), + E('div', { 'class': 'td cbi-section-table-cell left' }, aliases), + ]); + + table.appendChild(tr); + i = (i % 2) + 1; + } + + var fieldset = E('fieldset', { 'class': 'cbi-section' }, [E('legend', {}, _('Overview of known multiple interface announcements')), table]); + + var h2 = E('h2', { 'name': 'content' }, _('Active MID announcements')); + var divToggleButtons = E('div', { 'id': 'togglebuttons' }); + var statusOlsrCommonJs = null; + + if (has_v4 && has_v6) { + statusOlsrCommonJs = E('script', { 'type': 'text/javascript', 'src': L.resource('common/common_js.js') }); + } + + var result = E([], {}, [h2, divToggleButtons, fieldset, statusOlsrCommonJs]); + + return result; + }) + .catch(function (error) { + console.error(error); + }); + }, + handleSaveApply: null, + handleSave: null, + handleReset: null, +}); diff --git a/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/neighbors.js b/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/neighbors.js new file mode 100644 index 0000000000..bc14915814 --- /dev/null +++ b/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/neighbors.js @@ -0,0 +1,579 @@ +'use strict'; +'require uci'; +'require view'; +'require poll'; +'require rpc'; +'require ui'; +'require network'; + +function etx_color(etx) { + let color = '#bb3333'; + if (etx === 0) { + color = '#bb3333'; + } else if (etx < 2) { + color = '#00cc00'; + } else if (etx < 4) { + color = '#ffcb05'; + } else if (etx < 10) { + color = '#ff6600'; + } + return color; +} + +function snr_colors(snr) { + let color = '#bb3333'; + if (snr === 0) { + color = '#bb3333'; + } else if (snr > 30) { + color = '#00cc00'; + } else if (snr > 20) { + color = '#ffcb05'; + } else if (snr > 5) { + color = '#ff6600'; + } + return color; +} + +return view.extend({ + callGetJsonStatus: rpc.declare({ + object: 'olsrinfo', + method: 'getjsondata', + params: ['otable', 'v4_port', 'v6_port'], + }), + + callGetHosts: rpc.declare({ + object: 'olsrinfo', + method: 'hosts', + }), + + fetch_jsoninfo: function (otable) { + var jsonreq4 = ''; + var jsonreq6 = ''; + var v4_port = parseInt(uci.get('olsrd', 'olsrd_jsoninfo', 'port') || '') || 9090; + var v6_port = parseInt(uci.get('olsrd6', 'olsrd_jsoninfo', 'port') || '') || 9090; + var json; + var self = this; + return new Promise(function (resolve, reject) { + L.resolveDefault(self.callGetJsonStatus(otable, v4_port, v6_port), {}) + .then(function (res) { + json = res; + + jsonreq4 = JSON.parse(json.jsonreq4); + jsonreq6 = json.jsonreq6 !== '' ? JSON.parse(json.jsonreq6) : []; + var jsondata4 = {}; + var jsondata6 = {}; + var data4 = []; + var data6 = []; + var has_v4 = false; + var has_v6 = false; + + if (jsonreq4 === '' && jsonreq6 === '') { + window.location.href = 'error_olsr'; + reject([null, 0, 0, true]); + return; + } + + if (jsonreq4 !== '') { + has_v4 = true; + jsondata4 = jsonreq4 || {}; + if (otable === 'status') { + data4 = jsondata4; + } else { + data4 = jsondata4[otable] || []; + } + + for (var i = 0; i < data4.length; i++) { + data4[i]['proto'] = '4'; + } + } + + if (jsonreq6 !== '') { + has_v6 = true; + jsondata6 = jsonreq6 || {}; + if (otable === 'status') { + data6 = jsondata6; + } else { + data6 = jsondata6[otable] || []; + } + + for (var j = 0; j < data6.length; j++) { + data6[j]['proto'] = '6'; + } + } + + for (var k = 0; k < data6.length; k++) { + data4.push(data6[k]); + } + + resolve([data4, has_v4, has_v6, false]); + }) + .catch(function (err) { + console.error(err); + reject([null, 0, 0, true]); + }); + }); + }, + + action_neigh: async function () { + try { + const [data, has_v4, has_v6, error] = await this.fetch_jsoninfo('links'); + + if (error) { + throw error; + } + + function compare(a, b) { + if (a.proto === b.proto) { + return a.linkCost < b.linkCost; + } else { + return a.proto < b.proto; + } + } + + var assoclist = []; + var resolveVal = uci.get('luci_olsr', 'general', 'resolve'); + var devices; + var defaultgw; + + devices = await network.getWifiDevices(); + var rts = await network.getWANNetworks(); + + rts.forEach(function (rt) { + defaultgw = rt.getGatewayAddr() || '0.0.0.0'; + }); + + var networkPromises = devices.map(async function (dev) { + var networks = await dev.getWifiNetworks(); + + var promiseArr = networks.map(async function (net) { + var radio = await net.getDevice(); + var [ifname, devnetwork, device, list] = await Promise.all([net.getIfname(), net.getNetworkNames(), radio ? radio.getName() : null, net.getAssocList()]); + + assoclist.push({ + ifname: ifname, + network: devnetwork[0], + device: device, + list: list, + }); + }); + + await Promise.all(promiseArr); + }); + + await Promise.all(networkPromises); + var res = ''; + var self = this; + await (async function() { + try { + res = await self.callGetHosts(); + } + catch (e) { + console.error(e); + } + })(); + + function matchHostnames(ip) { + var lines = res.hosts.split('\n'); + for (var i = 0; i < lines.length; i++) { + var ipandhostname = lines[i].trim().split(/\s+/); + if (ipandhostname[0] === ip) { + return ipandhostname[1]; + } + } + return null; + } + var modifiedData = await Promise.all( + data.map(async function (v) { + var snr = 0; + var signal = 0; + var noise = 0; + var mac = ''; + var ip; + var neihgt = []; + + if (resolveVal === '1') { + var hostname = matchHostnames(v.remoteIP); + if (hostname) { + v.hostname = hostname; + } + } + var hosthints = await network.getHostHints(); + var interfac = await network.getStatusByAddress(v.localIP); + var lmac = await hosthints.getMACAddrByIPAddr(v.localIP); + var rmac = await hosthints.getMACAddrByIPAddr(v.remoteIP); + + for (let i = 0; i < assoclist.length; i++) { + var val = assoclist[i]; + if (val.network === interfac.interface && val.list) { + for (var assocmac in val.list) { + var assot = val.list[assocmac]; + if (rmac == assot.mac) { + signal = parseInt(assot.signal); + noise = parseInt(assot.noise); + snr = noise * -1 - signal * -1; + } + } + } + } + + if (interfac) { + v.interface = interfac; + } + v.snr = snr || null; + v.signal = signal || null; + v.noise = noise || null; + if (rmac) { + v.remoteMAC = rmac; + } + if (lmac) { + v.localMAC = lmac; + } + + if (defaultgw === v.remoteIP) { + v.defaultgw = 1; + } + return v; + }) + ); + + modifiedData.sort(compare); + + var result = { links: modifiedData, has_v4: has_v4, has_v6: has_v6 }; + return result; + } catch (err) { + console.error(err); + throw err; + } + }, + + load: function () { + var self = this; + poll.add(function () { + self.render(); + }, 5); + return Promise.all([uci.load('olsrd'), uci.load('luci_olsr')]); + }, + render: function () { + var neigh_res; + var has_v4; + var has_v6; + var self = this; + + return this.action_neigh() + .then(function (result) { + neigh_res = result.links; + has_v4 = result.has_v4; + has_v6 = result.has_v6; + + var table = E('div', { 'class': 'table cbi-section-table', 'id': 'olsr_neigh_table' }, [ + E('div', { 'class': 'tr cbi-section-table-cell' }, [ + E('div', { 'class': 'th cbi-section-table-cell' }, _('Neighbour IP')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('Hostname')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('Interface')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('Local interface IP')), + E('div', { 'class': 'th cbi-section-table-cell' }, 'LQ'), + E('div', { 'class': 'th cbi-section-table-cell' }, 'NLQ'), + E('div', { 'class': 'th cbi-section-table-cell' }, 'ETX'), + E('div', { 'class': 'th cbi-section-table-cell' }, 'SNR'), + ]), + ]); + + var rv = []; + for (var k = 0; k < neigh_res.length; k++) { + var link = neigh_res[k]; + link.linkCost = parseInt(link.linkCost) || 0; + if (link.linkCost === 4194304) { + link.linkCost = 0; + } + var color = etx_color(link.linkCost); + var snr_color = snr_colors(link.snr); + var defaultgw_color = ''; + if (link.defaultgw === 1) { + defaultgw_color = '#ffff99'; + } + + rv.push({ + rip: link.remoteIP, + hn: link.hostname, + lip: link.localIP, + ifn: link.interface, + lq: link.linkQuality.toFixed(3), + nlq: link.neighborLinkQuality.toFixed(3), + cost: link.linkCost.toFixed(3), + snr: link.snr, + signal: link.signal, + noise: link.noise, + color: color, + snr_color: snr_color, + dfgcolor: defaultgw_color, + proto: link.proto, + }); + } + + var nt = document.getElementById('olsr_neigh_table'); + if (nt) { + var s = + '<div class="tr cbi-section-table-cell">' + + '<div class="th cbi-section-table-cell">Neighbour IP</div>' + + '<div class="th cbi-section-table-cell">Hostname</div>' + + '<div class="th cbi-section-table-cell">Interface</div>' + + '<div class="th cbi-section-table-cell">Local interface IP</div>' + + '<div class="th cbi-section-table-cell">LQ</div>' + + '<div class="th cbi-section-table-cell">NLQ</div>' + + '<div class="th cbi-section-table-cell">ETX</div>' + + '<div class="th cbi-section-table-cell">SNR</div>' + + '</div>'; + + for (var idx = 0; idx < rv.length; idx++) { + var neigh = rv[idx]; + + if (neigh.proto == '6') { + s += + '<div class="tr cbi-section-table-row cbi-rowstyle-' + + (1 + (idx % 2)) + + ' proto-' + + neigh.proto + + '">' + + '<div class="td cbi-section-table-cell left" style="background-color:' + + neigh.dfgcolor + + '"><a href="http://[' + + neigh.rip + + ']/cgi-bin-status.html">' + + neigh.rip + + '</a></div>'; + } else { + s += + '<div class="tr cbi-section-table-row cbi-rowstyle-' + + (1 + (idx % 2)) + + ' proto-' + + neigh.proto + + '">' + + '<div class="td cbi-section-table-cell left" style="background-color:' + + neigh.dfgcolor + + '"><a href="http://' + + neigh.rip + + '/cgi-bin-status.html">' + + neigh.rip + + '</a></div>'; + } + if (neigh.hn) { + s += '<div class="td cbi-section-table-cell left" style="background-color:' + neigh.dfgcolor + '"><a href="http://' + neigh.hn + '/cgi-bin-status.html">' + neigh.hn + '</a></div>'; + } else { + s += '<div class="td cbi-section-table-cell left" style="background-color:' + neigh.dfgcolor + '">?</div>'; + } + s += + '<div class="td cbi-section-table-cell left" style="background-color:' + + neigh.dfgcolor + + '">' + + (neigh?.ifn?.interface ?? '?') + + '</div>' + + '<div class="td cbi-section-table-cell left" style="background-color:' + + neigh.dfgcolor + + '">' + + neigh.lip + + '</div>' + + '<div class="td cbi-section-table-cell left" style="background-color:' + + neigh.dfgcolor + + '">' + + neigh.lq + + '</div>' + + '<div class="td cbi-section-table-cell left" style="background-color:' + + neigh.dfgcolor + + '">' + + neigh.nlq + + '</div>' + + '<div class="td cbi-section-table-cell left" style="background-color:' + + neigh.color + + '">' + + neigh.cost + + '</div>' + + '<div class="td cbi-section-table-cell left" style="background-color:' + + neigh.snr_color + + '" title="Signal: ' + + neigh.signal + + ' Noise: ' + + neigh.noise + + '">' + + (neigh.snr || '?') + + '</div>' + + '</div>'; + } + + nt.innerHTML = s; + } + + var i = 1; + + for (var k = 0; k < neigh_res.length; k++) { + var link = neigh_res[k]; + link.linkCost = parseInt(link.linkCost) || 0; + if (link.linkCost === 4194304) { + link.linkCost = 0; + } + + color = etx_color(link.linkCost); + snr_color = snr_colors(link.snr); + + if (link.snr === 0) { + link.snr = '?'; + } + + var defaultgw_color = ''; + if (link.defaultgw === 1) { + defaultgw_color = '#ffff99'; + } + + var tr = E( + 'div', + { + 'class': 'tr cbi-section-table-row cbi-rowstyle-' + i + ' proto-' + link.proto, + }, + [ + link.proto === '6' + ? E( + 'div', + { + 'class': 'td cbi-section-table-cell left', + 'style': 'background-color:' + defaultgw_color, + }, + [ + E( + 'a', + { + 'href': 'http://[' + link.remoteIP + ']/cgi-bin-status.html', + }, + link.remoteIP + ), + ] + ) + : E( + 'div', + { + 'class': 'td cbi-section-table-cell left', + 'style': 'background-color:' + defaultgw_color, + }, + [ + E( + 'a', + { + 'href': 'http://' + link.remoteIP + '/cgi-bin-status.html', + }, + link.remoteIP + ), + ] + ), + E( + 'div', + { + 'class': 'td cbi-section-table-cell left', + 'style': 'background-color:' + defaultgw_color, + }, + [E('a', { 'href': 'http://%q/cgi-bin-status.html'.format(link.hostname) }, '%h'.format(link.hostname))] + ), + E( + 'div', + { + 'class': 'td cbi-section-table-cell left', + 'style': 'background-color:' + defaultgw_color, + }, + link?.interface?.interface ?? '?' + ), + E( + 'div', + { + 'class': 'td cbi-section-table-cell left', + 'style': 'background-color:' + defaultgw_color, + }, + link.localIP + ), + E( + 'div', + { + 'class': 'td cbi-section-table-cell left', + 'style': 'background-color:' + defaultgw_color, + }, + [E('div', {}, link.linkQuality.toFixed(3))] + ), + E( + 'div', + { + 'class': 'td cbi-section-table-cell left', + 'style': 'background-color:' + defaultgw_color, + }, + [E('div', {}, link.neighborLinkQuality.toFixed(3))] + ), + E( + 'div', + { + 'class': 'td cbi-section-table-cell left', + 'style': 'background-color:' + color, + }, + [E('div', {}, link.linkCost.toFixed(3))] + ), + E( + 'div', + { + 'class': 'td cbi-section-table-cell left', + 'style': 'background-color:' + snr_color, + 'title': 'Signal: ' + link.signal + ' Noise: ' + link.noise, + }, + link.snr + ), + ] + ); + + table.appendChild(tr); + i = (i % 2) + 1; + } + + var fieldset = E('fieldset', { 'class': 'cbi-section' }, [E('legend', {}, _('Overview of currently established OLSR connections')), table]); + + var h2 = E('h2', { 'name': 'content' }, _('OLSR connections')); + var divToggleButtons = E('div', { 'id': 'togglebuttons' }); + var statusOlsrLegend = E('div', {}, [ + E('h3', {}, [_('Legend') + ':']), + E('ul', {}, [ + E('li', {}, [E('strong', {}, [_('LQ: ')]), _('Success rate of packages received from the neighbour')]), + E('li', {}, [E('strong', {}, [_('NLQ: ')]), _('Success rate of packages sent to the neighbour')]), + E('li', {}, [E('strong', {}, [_('ETX: ')]), _('Expected retransmission count')]), + E('li', { 'style': 'list-style: none' }, [ + E('ul', {}, [ + E('li', {}, [E('strong', { 'style': 'color:#00cc00' }, [_('Green')]), ':', _('Very good (ETX < 2)')]), + E('li', {}, [E('strong', { 'style': 'color:#ffcb05' }, [_('Yellow')]), ':', _('Good (2 < ETX < 4)')]), + E('li', {}, [E('strong', { 'style': 'color:#ff6600' }, [_('Orange')]), ':', _('Still usable (4 < ETX < 10)')]), + E('li', {}, [E('strong', { 'style': 'color:#bb3333' }, [_('Red')]), ':', _('Bad (ETX > 10)')]), + ]), + ]), + E('li', {}, [E('strong', {}, [_('SNR: ')]), _('Signal Noise Ratio in dB')]), + E('li', { 'style': 'list-style: none' }, [ + E('ul', {}, [ + E('li', {}, [E('strong', { 'style': 'color:#00cc00' }, [_('Green')]), ':', _('Very good (SNR > 30)')]), + E('li', {}, [E('strong', { 'style': 'color:#ffcb05' }, [_('Yellow')]), ':', _('Good (30 > SNR > 20)')]), + E('li', {}, [E('strong', { 'style': 'color:#ff6600' }, [_('Orange')]), ':', _('Still usable (20 > SNR > 5)')]), + E('li', {}, [E('strong', { 'style': 'color:#bb3333' }, [_('Red')]), ':', _('Bad (SNR < 5)')]), + ]), + ]), + ]), + ]); + + var statusOlsrCommonJs = null; + + if (has_v4 && has_v6) { + statusOlsrCommonJs = E('script', { + type: 'text/javascript', + src: L.resource('common/common_js.js'), + }); + } + + var result = E([], {}, [h2, divToggleButtons, fieldset, statusOlsrLegend, statusOlsrCommonJs]); + + return result; + }) + .catch(function (error) { + console.error(error); + }); + }, + handleSaveApply: null, + handleSave: null, + handleReset: null, +}); diff --git a/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/routes.js b/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/routes.js new file mode 100644 index 0000000000..a202da1412 --- /dev/null +++ b/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/routes.js @@ -0,0 +1,330 @@ +'use strict'; +'require uci'; +'require view'; +'require poll'; +'require network'; +'require rpc'; +'require ui'; + +function etx_color(etx) { + let color = '#bb3333'; + if (etx === 0) { + color = '#bb3333'; + } else if (etx < 2) { + color = '#00cc00'; + } else if (etx < 4) { + color = '#ffcb05'; + } else if (etx < 10) { + color = '#ff6600'; + } + return color; +} + +return view.extend({ + callGetJsonStatus: rpc.declare({ + object: 'olsrinfo', + method: 'getjsondata', + params: ['otable', 'v4_port', 'v6_port'], + }), + + callGetHosts: rpc.declare({ + object: 'olsrinfo', + method: 'hosts', + }), + + fetch_jsoninfo: function (otable) { + var jsonreq4 = ''; + var jsonreq6 = ''; + var v4_port = parseInt(uci.get('olsrd', 'olsrd_jsoninfo', 'port') || '') || 9090; + var v6_port = parseInt(uci.get('olsrd6', 'olsrd_jsoninfo', 'port') || '') || 9090; + var json; + var self = this; + return new Promise(function (resolve, reject) { + L.resolveDefault(self.callGetJsonStatus(otable, v4_port, v6_port), {}) + .then(function (res) { + json = res; + + jsonreq4 = JSON.parse(json.jsonreq4); + jsonreq6 = json.jsonreq6 !== '' ? JSON.parse(json.jsonreq6) : []; + var jsondata4 = {}; + var jsondata6 = {}; + var data4 = []; + var data6 = []; + var has_v4 = false; + var has_v6 = false; + + if (jsonreq4 === '' && jsonreq6 === '') { + window.location.href = 'error_olsr'; + reject([null, 0, 0, true]); + return; + } + + if (jsonreq4 !== '') { + has_v4 = true; + jsondata4 = jsonreq4 || {}; + if (otable === 'status') { + data4 = jsondata4; + } else { + data4 = jsondata4[otable] || []; + } + + for (var i = 0; i < data4.length; i++) { + data4[i]['proto'] = '4'; + } + } + + if (jsonreq6 !== '') { + has_v6 = true; + jsondata6 = jsonreq6 || {}; + if (otable === 'status') { + data6 = jsondata6; + } else { + data6 = jsondata6[otable] || []; + } + + for (var j = 0; j < data6.length; j++) { + data6[j]['proto'] = '6'; + } + } + + for (var k = 0; k < data6.length; k++) { + data4.push(data6[k]); + } + + resolve([data4, has_v4, has_v6, false]); + }) + .catch(function (err) { + console.error(err); + reject([null, 0, 0, true]); + }); + }); + }, + action_routes: function () { + var self = this; + return new Promise(function (resolve, reject) { + self + .fetch_jsoninfo('routes') + .then(function ([data, has_v4, has_v6, error]) { + if (error) { + reject(error); + } + + var resolveVal = uci.get('luci_olsr', 'general', 'resolve'); + + function compare(a, b) { + if (a.proto === b.proto) { + return a.rtpMetricCost < b.rtpMetricCost; + } else { + return a.proto < b.proto; + } + } + var modifiedData; + self + .callGetHosts() + .then(function (res) { + function matchHostnames(ip) { + var lines = res.hosts.split('\n'); + for (var i = 0; i < lines.length; i++) { + var ipandhostname = lines[i].trim().split(/\s+/); + if (ipandhostname[0] === ip) { + return ipandhostname[1]; + } + } + return null; + } + modifiedData = data.map(function (v) { + if (resolveVal === '1') { + var hostname = matchHostnames(v.gateway); + if (hostname) { + v.hostname = hostname; + } + } + return v; + }); + + modifiedData.sort(compare); + + var result = { routes: modifiedData, has_v4: has_v4, has_v6: has_v6 }; + resolve(result); + }) + .catch(function (err) { + modifiedData = data; + console.error(err); + }); + }) + .catch(function (err) { + reject(err); + }); + }); + }, + load: function () { + var self = this; + poll.add(function () { + self.render(); + }, 5); + return Promise.all([uci.load('olsrd'), uci.load('luci_olsr')]); + }, + render: function () { + var routes_res; + var has_v4; + var has_v6; + var self = this; + return this.action_routes() + .then(function (result) { + routes_res = result.routes; + has_v4 = result.has_v4; + has_v6 = result.has_v6; + var table = E('div', { 'class': 'table cbi-section-table', 'id': 'olsrd_routes' }, [ + E('div', { 'class': 'tr cbi-section-table-cell' }, [ + E('div', { 'class': 'th cbi-section-table-cell' }, _('Announced network')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('OLSR gateway')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('Interface')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('Metric')), + E('div', { 'class': 'th cbi-section-table-cell' }, 'ETX'), + ]), + ]); + var rv = []; + for (var k = 0; k < routes_res.length; k++) { + var route = routes_res[k]; + var ETX = (parseFloat(route.etx) || 0).toFixed(3); + rv.push({ + hostname: route.hostname, + dest: route.destination, + genmask: route.genmask, + gw: route.gateway, + interface: route.networkInterface, + metric: route.metric, + etx: ETX, + color: etx_color(parseFloat(ETX)), + }); + } + + var rt = document.getElementById('olsrd_routes'); + if (rt) { + var s = + '<div class="tr cbi-section-table-cell">' + + '<div class="th cbi-section-table-cell">Announced network</div>' + + '<div class="th cbi-section-table-cell">OLSR gateway</div>' + + '<div class="th cbi-section-table-cell">Interface</div>' + + '<div class="th cbi-section-table-cell">Metric</div>' + + '<div class="th cbi-section-table-cell">ETX</div>' + + '</div>'; + + for (var idx = 0; idx < rv.length; idx++) { + var route = rv[idx]; + + s += + '<div class="tr cbi-section-table-row cbi-rowstyle-' + + (1 + (idx % 2)) + + ' proto-' + + route.proto + + '">' + + '<div class="td cbi-section-table-cell left">' + + route.dest + + '/' + + route.genmask + + '</div>' + + '<div class="td cbi-section-table-cell left">' + + '<a href="http://' + + route.gw + + '/cgi-bin-status.html">' + + route.gw + + '</a>'; + + if (route.hostname) { + if (route.proto == '6') { + s += ' / <a href="http://[%q]/cgi-bin-status.html">%h</a>'.format(route.hostname, route.hostname || '') + } else { + s += ' / <a href="http://%q/cgi-bin-status.html">%h</a>'.format(route.hostname, route.hostname || ''); + } + } + + s += + '</div>' + + '<div class="td cbi-section-table-cell left">' + + route.interface + + '</div>' + + '<div class="td cbi-section-table-cell left">' + + route.metric + + '</div>' + + '<div class="td cbi-section-table-cell left" style="background-color:' + + route.color + + '">' + + (route.etx || '?') + + '</div>' + + '</div>'; + } + + rt.innerHTML = s; + } + + var i = 1; + + for (var k = 0; k < routes_res.length; k++) { + var route = routes_res[k]; + var ETX = parseInt(route.etx) || 0; + var color = etx_color(ETX); + + var tr = E('div', { 'class': 'tr cbi-section-table-row cbi-rowstyle-' + i + ' proto-' + route.proto }, [ + E('div', { 'class': 'td cbi-section-table-cell left' }, route.destination + '/' + route.genmask), + E('div', { 'class': 'td cbi-section-table-cell left' }, [ + route.proto === '6' ? E('a', { 'href': 'http://[' + route.gateway + ']/cgi-bin-status.html' }, route.gateway) : E('a', { 'href': 'http://' + route.gateway + '/cgi-bin-status.html' }, route.gateway), + route.hostname ? E('span', {}, [' / ', E('a', { 'href': 'http://%q/cgi-bin-status.html'.format(route.hostname) }, '%h'.format(route.hostname))]) : '', + ]), + E('div', { 'class': 'td cbi-section-table-cell left' }, route.networkInterface), + E('div', { 'class': 'td cbi-section-table-cell left' }, route.metric), + E('div', { 'class': 'td cbi-section-table-cell left', 'style': 'background-color:' + color }, [ETX.toFixed(3)]), + ]); + + table.appendChild(tr); + i = (i % 2) + 1; + } + + var fieldset = E('fieldset', { 'class': 'cbi-section' }, [E('legend', {}, _('Overview of currently known routes to other OLSR nodes')), table]); + + var h2 = E('h2', { 'name': 'content' }, _('Known OLSR routes')); + var divToggleButtons = E('div', { 'id': 'togglebuttons' }); + var statusOlsrLegend = E('div', {}, [ + E('h3', {}, [_('Legend') + ':']), + E('ul', {}, [ + E('li', {}, [E('strong', {}, [_('LQ: ')]), _('Success rate of packages received from the neighbour')]), + E('li', {}, [E('strong', {}, [_('NLQ: ')]), _('Success rate of packages sent to the neighbour')]), + E('li', {}, [E('strong', {}, [_('ETX: ')]), _('Expected retransmission count')]), + E('li', { 'style': 'list-style: none' }, [ + E('ul', {}, [ + E('li', {}, [E('strong', { 'style': 'color:#00cc00' }, [_('Green')]), ':', _('Very good (ETX < 2)')]), + E('li', {}, [E('strong', { 'style': 'color:#ffcb05' }, [_('Yellow')]), ':', _('Good (2 < ETX < 4)')]), + E('li', {}, [E('strong', { 'style': 'color:#ff6600' }, [_('Orange')]), ':', _('Still usable (4 < ETX < 10)')]), + E('li', {}, [E('strong', { 'style': 'color:#bb3333' }, [_('Red')]), ':', _('Bad (ETX > 10)')]), + ]), + ]), + E('li', {}, [E('strong', {}, [_('SNR: ')]), _('Signal Noise Ratio in dB')]), + E('li', { 'style': 'list-style: none' }, [ + E('ul', {}, [ + E('li', {}, [E('strong', { 'style': 'color:#00cc00' }, [_('Green')]), ':', _('Very good (SNR > 30)')]), + E('li', {}, [E('strong', { 'style': 'color:#ffcb05' }, [_('Yellow')]), ':', _('Good (30 > SNR > 20)')]), + E('li', {}, [E('strong', { 'style': 'color:#ff6600' }, [_('Orange')]), ':', _('Still usable (20 > SNR > 5)')]), + E('li', {}, [E('strong', { 'style': 'color:#bb3333' }, [_('Red')]), ':', _('Bad (SNR < 5)')]), + ]), + ]), + ]), + ]); + + var statusOlsrCommonJs = null; + + if (has_v4 && has_v6) { + statusOlsrCommonJs = E('script', { 'type': 'text/javascript', 'src': L.resource('common/common_js.js') }); + } + + var result = E([], {}, [h2, divToggleButtons, fieldset, statusOlsrLegend, statusOlsrCommonJs]); + + return result; + }) + .catch(function (error) { + console.error(error); + }); + }, + handleSaveApply: null, + handleSave: null, + handleReset: null, +}); diff --git a/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/smartgw.js b/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/smartgw.js new file mode 100644 index 0000000000..3b6ca662f9 --- /dev/null +++ b/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/smartgw.js @@ -0,0 +1,283 @@ +'use strict'; +'require uci'; +'require view'; +'require poll'; +'require rpc'; +'require ui'; + +return view.extend({ + callGetJsonStatus: rpc.declare({ + object: 'olsrinfo', + method: 'getjsondata', + params: ['otable', 'v4_port', 'v6_port'], + }), + + fetch_jsoninfo: function (otable) { + var jsonreq4 = ''; + var jsonreq6 = ''; + var v4_port = parseInt(uci.get('olsrd', 'olsrd_jsoninfo', 'port') || '') || 9090; + var v6_port = parseInt(uci.get('olsrd6', 'olsrd_jsoninfo', 'port') || '') || 9090; + var json; + var self = this; + return new Promise(function (resolve, reject) { + L.resolveDefault(self.callGetJsonStatus(otable, v4_port, v6_port), {}) + .then(function (res) { + json = res; + + jsonreq4 = JSON.parse(json.jsonreq4); + jsonreq6 = json.jsonreq6 !== '' ? JSON.parse(json.jsonreq6) : []; + var jsondata4 = {}; + var jsondata6 = {}; + var data4 = []; + var data6 = []; + var has_v4 = false; + var has_v6 = false; + + if (jsonreq4 === '' && jsonreq6 === '') { + window.location.href = 'error_olsr'; + reject([null, 0, 0, true]); + return; + } + + if (jsonreq4 !== '') { + has_v4 = true; + jsondata4 = jsonreq4 || {}; + if (otable === 'status') { + data4 = jsondata4; + } else { + data4 = jsondata4[otable] || []; + } + + for (var i = 0; i < data4.length; i++) { + data4[i]['proto'] = '4'; + } + } + + if (jsonreq6 !== '') { + has_v6 = true; + jsondata6 = jsonreq6 || {}; + if (otable === 'status') { + data6 = jsondata6; + } else { + data6 = jsondata6[otable] || []; + } + + for (var j = 0; j < data6.length; j++) { + data6[j]['proto'] = '6'; + } + } + + for (var k = 0; k < data6.length; k++) { + data4.push(data6[k]); + } + + resolve([data4, has_v4, has_v6, false]); + }) + .catch(function (err) { + console.error(err); + reject([null, 0, 0, true]); + }); + }); + }, + action_smartgw: function () { + var self = this; + return new Promise(function (resolve, reject) { + self + .fetch_jsoninfo('gateways') + .then(function ([data, has_v4, has_v6, error]) { + if (error) { + reject(error); + } + + function compare(a, b) { + if (a.proto === b.proto) { + return a.cost < b.cost; + } else { + return a.proto < b.proto; + } + } + + data.ipv4.sort(compare); + data.ipv6.sort(compare); + + var result = { gws: data, has_v4: has_v4, has_v6: has_v6 }; + resolve(result); + }) + .catch(function (err) { + reject(err); + }); + }); + }, + load: function () { + var self = this; + poll.add(function () { + self.render(); + }, 5); + return Promise.all([uci.load('olsrd'), uci.load('luci_olsr')]); + }, + render: function () { + var gws_res; + var has_v4; + var has_v6; + var self = this; + return this.action_smartgw() + .then(function (result) { + gws_res = result.gws; + has_v4 = result.has_v4; + has_v6 = result.has_v6; + var fieldset = E('fieldset', { 'class': 'cbi-section' }, [ + E('legend', {}, _('Overview of smart gateways in this network')), + E('div', { 'class': 'table cbi-section-table', 'id': 'olsrd_smartgw' }, [ + E('div', { 'class': 'tr cbi-section-table-titles' }, [ + E('div', { 'class': 'th cbi-section-table-cell' }, _('Gateway')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('Selected')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('ETX')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('Hops')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('Uplink')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('Downlink')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('IPv4')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('IPv6')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('Prefix')), + ]), + ]), + ]); + var has_smartgw; + uci.sections('olsrd', 'olsrd', function (s) { + if (s.SmartGateway && s.SmartGateway === 'yes') { + has_smartgw = true; + } + }); + + var rv = []; + for (var k = 0; k < gws_res.ipv4.length; k++) { + var gw = gws_res.ipv4[k]; + gw.cost = parseFloat(gw.cost) / 1024 || 0; + if (gw.cost >= 100) { + gw.cost = 0; + } + + rv.push({ + proto: gw.IPv4 ? '4' : '6', + originator: gw.originator, + selected: gw.selected ? luci.i18n.translate('yes') : luci.i18n.translate('no'), + cost: gw.cost > 0 ? gw.cost.toFixed(3) : luci.i18n.translate('infinite'), + hops: gw.hops, + uplink: gw.uplink, + downlink: gw.downlink, + v4: gw.IPv4 ? luci.i18n.translate('yes') : luci.i18n.translate('no'), + v6: gw.IPv6 ? luci.i18n.translate('yes') : luci.i18n.translate('no'), + prefix: gw.prefix, + }); + } + + var smartgwdiv = document.getElementById('olsrd_smartgw'); + if (smartgwdiv) { + var s = + '<div class="tr cbi-section-table-titles">' + + '<div class="th cbi-section-table-cell">Gateway</div>' + + '<div class="th cbi-section-table-cell">Selected</div>' + + '<div class="th cbi-section-table-cell">ETX></div>' + + '<div class="th cbi-section-table-cell">Hops></div>' + + '<div class="th cbi-section-table-cell">Uplink</div>' + + '<div class="th cbi-section-table-cell">Downlink</div>' + + '<div class="th cbi-section-table-cell">IPv4</div>' + + '<div class="th cbi-section-table-cell">IPv6</div>' + + '<div class="th cbi-section-table-cell">Prefix</div>' + + '</div>'; + + for (var idx = 0; idx < rv.length; idx++) { + var smartgw = rv[idx]; + var linkgw; + s += '<div class="tr cbi-section-table-row cbi-rowstyle-' + (1 + (idx % 2)) + ' proto-' + smartgw.proto + '">'; + + if (smartgw.proto == '6') { + linkgw = '<a href="http://[' + smartgw.originator + ']/cgi-bin-status.html">' + smartgw.originator + '</a>'; + } else { + linkgw = '<a href="http://' + smartgw.originator + '/cgi-bin-status.html">' + smartgw.originator + '</a>'; + } + + s += + '<div class="td cbi-section-table-cell left">' + + linkgw + + '</div>' + + '<div class="td cbi-section-table-cell left">' + + smartgw.selected + + '</div>' + + '<div class="td cbi-section-table-cell left">' + + smartgw.cost + + '</div>' + + '<div class="td cbi-section-table-cell left">' + + smartgw.hops + + '</div>' + + '<div class="td cbi-section-table-cell left">' + + smartgw.uplink + + '</div>' + + '<div class="td cbi-section-table-cell left">' + + smartgw.downlink + + '</div>' + + '<div class="td cbi-section-table-cell left">' + + smartgw.v4 + + '</div>' + + '<div class="td cbi-section-table-cell left">' + + smartgw.v6 + + '</div>' + + '<div class="td cbi-section-table-cell left">' + + smartgw.prefix + + '</div>'; + + s += '</div>'; + } + smartgwdiv.innerHTML = s; + } + + var i = 1; + + if (has_smartgw) { + for (var k = 0; k < gws_res.ipv4.length; k++) { + var gw = gws_res.ipv4[k]; + gw.cost = parseInt(gw.cost) / 1024 || 0; + if (gw.cost >= 100) { + gw.cost = 0; + } + + var tr = E('div', { 'class': 'tr cbi-section-table-row cbi-rowstyle-' + i + ' proto-' + gw.proto }, [ + gw.proto === '6' + ? E('div', { 'class': 'td cbi-section-table-cell left' }, [E('a', { 'href': 'http://[' + gw.originator + ']/cgi-bin-status.html' }, gw.originator)]) + : E('div', { 'class': 'td cbi-section-table-cell left' }, [E('a', { 'href': 'http://' + gw.originator + '/cgi-bin-status.html' }, gw.originator)]), + E('div', { 'class': 'td cbi-section-table-cell left' }, [gw.selected ? luci.i18n.translate('yes') : luci.i18n.translate('no')]), + E('div', { 'class': 'td cbi-section-table-cell left' }, [gw.cost > 0 ? string.format('%.3f', gw.cost) : luci.i18n.translate('infinite')]), + E('div', { 'class': 'td cbi-section-table-cell left' }, gw.hops), + E('div', { 'class': 'td cbi-section-table-cell left' }, gw.uplink), + E('div', { 'class': 'td cbi-section-table-cell left' }, gw.downlink), + E('div', { 'class': 'td cbi-section-table-cell left' }, gw.IPv4 ? luci.i18n.translate('yes') : luci.i18n.translate('no')), + E('div', { 'class': 'td cbi-section-table-cell left' }, gw.IPv6 ? luci.i18n.translate('yes') : luci.i18n.translate('no')), + E('div', { 'class': 'td cbi-section-table-cell left' }, gw.prefix), + ]); + + fieldset.appendChild(tr); + i = (i % 2) + 1; + } + + var h2 = E('h2', { 'name': 'content' }, _('SmartGW announcements')); + var divToggleButtons = E('div', { 'id': 'togglebuttons' }); + var statusOlsrCommonJs = null; + + if (has_v4 && has_v6) { + statusOlsrCommonJs = E('script', { 'type': 'text/javascript', 'src': L.resource('common/common_js.js') }); + } + + var result = E([], {}, [h2, divToggleButtons, fieldset, statusOlsrCommonJs]); + + return result; + } else { + return E('h2', {}, _('SmartGateway is not configured on this system')); + } + }) + .catch(function (error) { + console.error(error); + }); + }, + handleSaveApply: null, + handleSave: null, + handleReset: null +}); diff --git a/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/topology.js b/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/topology.js new file mode 100644 index 0000000000..6a179e848b --- /dev/null +++ b/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/topology.js @@ -0,0 +1,218 @@ +'use strict'; +'require uci'; +'require view'; +'require poll'; +'require rpc'; +'require ui'; + +function etx_color(etx) { + let color = '#bb3333'; + if (etx === 0) { + color = '#bb3333'; + } else if (etx < 2) { + color = '#00cc00'; + } else if (etx < 4) { + color = '#ffcb05'; + } else if (etx < 10) { + color = '#ff6600'; + } + return color; +} + +return view.extend({ + callGetJsonStatus: rpc.declare({ + object: 'olsrinfo', + method: 'getjsondata', + params: ['otable', 'v4_port', 'v6_port'], + }), + + fetch_jsoninfo: function (otable) { + var jsonreq4 = ''; + var jsonreq6 = ''; + var v4_port = parseInt(uci.get('olsrd', 'olsrd_jsoninfo', 'port') || '') || 9090; + var v6_port = parseInt(uci.get('olsrd6', 'olsrd_jsoninfo', 'port') || '') || 9090; + var json; + var self = this; + return new Promise(function (resolve, reject) { + L.resolveDefault(self.callGetJsonStatus(otable, v4_port, v6_port), {}) + .then(function (res) { + json = res; + + jsonreq4 = JSON.parse(json.jsonreq4); + jsonreq6 = json.jsonreq6 !== '' ? JSON.parse(json.jsonreq6) : []; + var jsondata4 = {}; + var jsondata6 = {}; + var data4 = []; + var data6 = []; + var has_v4 = false; + var has_v6 = false; + + if (jsonreq4 === '' && jsonreq6 === '') { + window.location.href = 'error_olsr'; + reject([null, 0, 0, true]); + return; + } + + if (jsonreq4 !== '') { + has_v4 = true; + jsondata4 = jsonreq4 || {}; + if (otable === 'status') { + data4 = jsondata4; + } else { + data4 = jsondata4[otable] || []; + } + + for (var i = 0; i < data4.length; i++) { + data4[i]['proto'] = '4'; + } + } + + if (jsonreq6 !== '') { + has_v6 = true; + jsondata6 = jsonreq6 || {}; + if (otable === 'status') { + data6 = jsondata6; + } else { + data6 = jsondata6[otable] || []; + } + + for (var j = 0; j < data6.length; j++) { + data6[j]['proto'] = '6'; + } + } + + for (var k = 0; k < data6.length; k++) { + data4.push(data6[k]); + } + + resolve([data4, has_v4, has_v6, false]); + }) + .catch(function (err) { + console.error(err); + reject([null, 0, 0, true]); + }); + }); + }, + action_topology: function () { + var self = this; + return new Promise(function (resolve, reject) { + self + .fetch_jsoninfo('topology') + .then(function ([data, has_v4, has_v6, error]) { + if (error) { + reject(error); + } + + function compare(a, b) { + if (a.proto === b.proto) { + return a.tcEdgeCost < b.tcEdgeCost; + } else { + return a.proto < b.proto; + } + } + + data.sort(compare); + + var result = { routes: data, has_v4: has_v4, has_v6: has_v6 }; + resolve(result); + }) + .catch(function (err) { + reject(err); + }); + }); + }, + load: function () { + return Promise.all([uci.load('olsrd'), uci.load('luci_olsr')]); + }, + render: function () { + var routes_res; + var has_v4; + var has_v6; + + return this.action_topology() + .then(function (result) { + routes_res = result.routes; + has_v4 = result.has_v4; + has_v6 = result.has_v6; + var table = E('div', { 'class': 'table cbi-section-table' }, [ + E('div', { 'class': 'tr cbi-section-table-titles' }, [ + E('div', { 'class': 'th cbi-section-table-cell' }, _('OLSR node')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('Last hop')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('LQ')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('NLQ')), + E('div', { 'class': 'th cbi-section-table-cell' }, _('ETX')), + ]), + ]); + var i = 1; + + for (var k = 0; k < routes_res.length; k++) { + var route = routes_res[k]; + var cost = (parseInt(route.tcEdgeCost) || 0).toFixed(3); + var color = etx_color(parseInt(cost)); + var lq = (parseInt(route.linkQuality) || 0).toFixed(3); + var nlq = (parseInt(route.neighborLinkQuality) || 0).toFixed(3); + + var tr = E('div', { 'class': 'tr cbi-section-table-row cbi-rowstyle-' + i + ' proto-' + route.proto }, [ + route.proto === '6' + ? E('div', { 'class': 'td cbi-section-table-cell left' }, [E('a', { 'href': 'http://[' + route.destinationIP + ']/cgi-bin-status.html' }, route.destinationIP)]) + : E('div', { 'class': 'td cbi-section-table-cell left' }, [E('a', { 'href': 'http://' + route.destinationIP + '/cgi-bin-status.html' }, route.destinationIP)]), + route.proto === '6' + ? E('div', { 'class': 'td cbi-section-table-cell left' }, [E('a', { 'href': 'http://[' + route.lastHopIP + ']/cgi-bin-status.html' }, route.lastHopIP)]) + : E('div', { 'class': 'td cbi-section-table-cell left' }, [E('a', { 'href': 'http://' + route.lastHopIP + '/cgi-bin-status.html' }, route.lastHopIP)]), + E('div', { 'class': 'td cbi-section-table-cell left' }, lq), + E('div', { 'class': 'td cbi-section-table-cell left' }, nlq), + E('div', { 'class': 'td cbi-section-table-cell left', 'style': 'background-color:' + color }, cost), + ]); + + table.appendChild(tr); + i = (i % 2) + 1; + } + + var fieldset = E('fieldset', { 'class': 'cbi-section' }, [E('legend', {}, _('Overview of currently known OLSR nodes')), table]); + + var h2 = E('h2', { 'name': 'content' }, _('Active OLSR nodes')); + var divToggleButtons = E('div', { 'id': 'togglebuttons' }); + var statusOlsrLegend = E('div', {}, [ + E('h3', {}, [_('Legend') + ':']), + E('ul', {}, [ + E('li', {}, [E('strong', {}, [_('LQ: ')]), _('Success rate of packages received from the neighbour')]), + E('li', {}, [E('strong', {}, [_('NLQ: ')]), _('Success rate of packages sent to the neighbour')]), + E('li', {}, [E('strong', {}, [_('ETX: ')]), _('Expected retransmission count')]), + E('li', { style: 'list-style: none' }, [ + E('ul', {}, [ + E('li', {}, [E('strong', { 'style': 'color:#00cc00' }, [_('Green')]), ':', _('Very good (ETX < 2)')]), + E('li', {}, [E('strong', { 'style': 'color:#ffcb05' }, [_('Yellow')]), ':', _('Good (2 < ETX < 4)')]), + E('li', {}, [E('strong', { 'style': 'color:#ff6600' }, [_('Orange')]), ':', _('Still usable (4 < ETX < 10)')]), + E('li', {}, [E('strong', { 'style': 'color:#bb3333' }, [_('Red')]), ':', _('Bad (ETX > 10)')]), + ]), + ]), + E('li', {}, [E('strong', {}, [_('SNR: ')]), _('Signal Noise Ratio in dB')]), + E('li', { 'style': 'list-style: none' }, [ + E('ul', {}, [ + E('li', {}, [E('strong', { 'style': 'color:#00cc00' }, [_('Green')]), ':', _('Very good (SNR > 30)')]), + E('li', {}, [E('strong', { 'style': 'color:#ffcb05' }, [_('Yellow')]), ':', _('Good (30 > SNR > 20)')]), + E('li', {}, [E('strong', { 'style': 'color:#ff6600' }, [_('Orange')]), ':', _('Still usable (20 > SNR > 5)')]), + E('li', {}, [E('strong', { 'style': 'color:#bb3333' }, [_('Red')]), ':', _('Bad (SNR < 5)')]), + ]), + ]), + ]), + ]); + + var statusOlsrCommonJs = null; + + if (has_v4 && has_v6) { + statusOlsrCommonJs = E('script', { 'type': 'text/javascript', 'src': L.resource('common/common_js.js') }); + } + + var result = E([], {}, [h2, divToggleButtons, fieldset, statusOlsrLegend, statusOlsrCommonJs]); + + return result; + }) + .catch(function (error) { + console.error(error); + }); + }, + handleSaveApply: null, + handleSave: null, + handleReset: null, +}); |