summaryrefslogtreecommitdiffhomepage
path: root/applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr
diff options
context:
space:
mode:
authorAyushman Tripathi <ayushmantripathi7724@gmail.com>2023-06-26 01:23:48 +0530
committerAyushman Tripathi <ayushmantripathi7724@gmail.com>2023-07-20 20:05:50 +0530
commitebd09332b4330001ecd3a86d1b379e0afc3bafee (patch)
tree39e973d153f0081800822e6a4a325f71d4441a00 /applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr
parentfd5440a7be5506fa39c3993f10f754dedb5e8888 (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')
-rw-r--r--applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/error_olsr.js16
-rw-r--r--applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/hna.js271
-rw-r--r--applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/interfaces.js186
-rw-r--r--applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/mid.js171
-rw-r--r--applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/neighbors.js579
-rw-r--r--applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/routes.js330
-rw-r--r--applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/smartgw.js283
-rw-r--r--applications/luci-app-olsr/htdocs/luci-static/resources/view/olsr/status-olsr/topology.js218
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,
+});