routes.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. 'use strict';
  2. 'require uci';
  3. 'require view';
  4. 'require poll';
  5. 'require network';
  6. 'require rpc';
  7. 'require ui';
  8. function etx_color(etx) {
  9. let color = '#bb3333';
  10. if (etx === 0) {
  11. color = '#bb3333';
  12. } else if (etx < 2) {
  13. color = '#00cc00';
  14. } else if (etx < 4) {
  15. color = '#ffcb05';
  16. } else if (etx < 10) {
  17. color = '#ff6600';
  18. }
  19. return color;
  20. }
  21. return view.extend({
  22. callGetJsonStatus: rpc.declare({
  23. object: 'olsrinfo',
  24. method: 'getjsondata',
  25. params: ['otable', 'v4_port', 'v6_port'],
  26. }),
  27. callGetHosts: rpc.declare({
  28. object: 'olsrinfo',
  29. method: 'hosts',
  30. }),
  31. fetch_jsoninfo: function (otable) {
  32. var jsonreq4 = '';
  33. var jsonreq6 = '';
  34. var v4_port = parseInt(uci.get('olsrd', 'olsrd_jsoninfo', 'port') || '') || 9090;
  35. var v6_port = parseInt(uci.get('olsrd6', 'olsrd_jsoninfo', 'port') || '') || 9090;
  36. var json;
  37. var self = this;
  38. return new Promise(function (resolve, reject) {
  39. L.resolveDefault(self.callGetJsonStatus(otable, v4_port, v6_port), {})
  40. .then(function (res) {
  41. json = res;
  42. jsonreq4 = JSON.parse(json.jsonreq4);
  43. jsonreq6 = json.jsonreq6 !== '' ? JSON.parse(json.jsonreq6) : [];
  44. var jsondata4 = {};
  45. var jsondata6 = {};
  46. var data4 = [];
  47. var data6 = [];
  48. var has_v4 = false;
  49. var has_v6 = false;
  50. if (jsonreq4 === '' && jsonreq6 === '') {
  51. window.location.href = 'error_olsr';
  52. reject([null, 0, 0, true]);
  53. return;
  54. }
  55. if (jsonreq4 !== '') {
  56. has_v4 = true;
  57. jsondata4 = jsonreq4 || {};
  58. if (otable === 'status') {
  59. data4 = jsondata4;
  60. } else {
  61. data4 = jsondata4[otable] || [];
  62. }
  63. for (var i = 0; i < data4.length; i++) {
  64. data4[i]['proto'] = '4';
  65. }
  66. }
  67. if (jsonreq6 !== '') {
  68. has_v6 = true;
  69. jsondata6 = jsonreq6 || {};
  70. if (otable === 'status') {
  71. data6 = jsondata6;
  72. } else {
  73. data6 = jsondata6[otable] || [];
  74. }
  75. for (var j = 0; j < data6.length; j++) {
  76. data6[j]['proto'] = '6';
  77. }
  78. }
  79. for (var k = 0; k < data6.length; k++) {
  80. data4.push(data6[k]);
  81. }
  82. resolve([data4, has_v4, has_v6, false]);
  83. })
  84. .catch(function (err) {
  85. console.error(err);
  86. reject([null, 0, 0, true]);
  87. });
  88. });
  89. },
  90. action_routes: function () {
  91. var self = this;
  92. return new Promise(function (resolve, reject) {
  93. self
  94. .fetch_jsoninfo('routes')
  95. .then(function ([data, has_v4, has_v6, error]) {
  96. if (error) {
  97. reject(error);
  98. }
  99. var resolveVal = uci.get('luci_olsr', 'general', 'resolve');
  100. function compare(a, b) {
  101. if (a.proto === b.proto) {
  102. return a.rtpMetricCost < b.rtpMetricCost;
  103. } else {
  104. return a.proto < b.proto;
  105. }
  106. }
  107. var modifiedData;
  108. self
  109. .callGetHosts()
  110. .then(function (res) {
  111. function matchHostnames(ip) {
  112. var lines = res.hosts.split('\n');
  113. for (var i = 0; i < lines.length; i++) {
  114. var ipandhostname = lines[i].trim().split(/\s+/);
  115. if (ipandhostname[0] === ip) {
  116. return ipandhostname[1];
  117. }
  118. }
  119. return null;
  120. }
  121. modifiedData = data.map(function (v) {
  122. if (resolveVal === '1') {
  123. var hostname = matchHostnames(v.gateway);
  124. if (hostname) {
  125. v.hostname = hostname;
  126. }
  127. }
  128. return v;
  129. });
  130. modifiedData.sort(compare);
  131. var result = { routes: modifiedData, has_v4: has_v4, has_v6: has_v6 };
  132. resolve(result);
  133. })
  134. .catch(function (err) {
  135. modifiedData = data;
  136. console.error(err);
  137. });
  138. })
  139. .catch(function (err) {
  140. reject(err);
  141. });
  142. });
  143. },
  144. load: function () {
  145. var self = this;
  146. poll.add(function () {
  147. self.render();
  148. }, 5);
  149. return Promise.all([uci.load('olsrd'), uci.load('luci_olsr')]);
  150. },
  151. render: function () {
  152. var routes_res;
  153. var has_v4;
  154. var has_v6;
  155. var self = this;
  156. return this.action_routes()
  157. .then(function (result) {
  158. routes_res = result.routes;
  159. has_v4 = result.has_v4;
  160. has_v6 = result.has_v6;
  161. var table = E('div', { 'class': 'table cbi-section-table', 'id': 'olsrd_routes' }, [
  162. E('div', { 'class': 'tr cbi-section-table-cell' }, [
  163. E('div', { 'class': 'th cbi-section-table-cell' }, _('Announced network')),
  164. E('div', { 'class': 'th cbi-section-table-cell' }, _('OLSR gateway')),
  165. E('div', { 'class': 'th cbi-section-table-cell' }, _('Interface')),
  166. E('div', { 'class': 'th cbi-section-table-cell' }, _('Metric')),
  167. E('div', { 'class': 'th cbi-section-table-cell' }, 'ETX'),
  168. ]),
  169. ]);
  170. var rv = [];
  171. for (var k = 0; k < routes_res.length; k++) {
  172. var route = routes_res[k];
  173. var ETX = (parseFloat(route.etx) || 0).toFixed(3);
  174. rv.push({
  175. hostname: route.hostname,
  176. dest: route.destination,
  177. genmask: route.genmask,
  178. gw: route.gateway,
  179. interface: route.networkInterface,
  180. metric: route.metric,
  181. etx: ETX,
  182. color: etx_color(parseFloat(ETX)),
  183. });
  184. }
  185. var rt = document.getElementById('olsrd_routes');
  186. if (rt) {
  187. var s =
  188. '<div class="tr cbi-section-table-cell">' +
  189. '<div class="th cbi-section-table-cell">Announced network</div>' +
  190. '<div class="th cbi-section-table-cell">OLSR gateway</div>' +
  191. '<div class="th cbi-section-table-cell">Interface</div>' +
  192. '<div class="th cbi-section-table-cell">Metric</div>' +
  193. '<div class="th cbi-section-table-cell">ETX</div>' +
  194. '</div>';
  195. for (var idx = 0; idx < rv.length; idx++) {
  196. var route = rv[idx];
  197. s +=
  198. '<div class="tr cbi-section-table-row cbi-rowstyle-' +
  199. (1 + (idx % 2)) +
  200. ' proto-' +
  201. route.proto +
  202. '">' +
  203. '<div class="td cbi-section-table-cell left">' +
  204. route.dest +
  205. '/' +
  206. route.genmask +
  207. '</div>' +
  208. '<div class="td cbi-section-table-cell left">' +
  209. '<a href="http://' +
  210. route.gw +
  211. '/cgi-bin-status.html">' +
  212. route.gw +
  213. '</a>';
  214. if (route.hostname) {
  215. if (route.proto == '6') {
  216. s += ' / <a href="http://[%q]/cgi-bin-status.html">%h</a>'.format(route.hostname, route.hostname || '')
  217. } else {
  218. s += ' / <a href="http://%q/cgi-bin-status.html">%h</a>'.format(route.hostname, route.hostname || '');
  219. }
  220. }
  221. s +=
  222. '</div>' +
  223. '<div class="td cbi-section-table-cell left">' +
  224. route.interface +
  225. '</div>' +
  226. '<div class="td cbi-section-table-cell left">' +
  227. route.metric +
  228. '</div>' +
  229. '<div class="td cbi-section-table-cell left" style="background-color:' +
  230. route.color +
  231. '">' +
  232. (route.etx || '?') +
  233. '</div>' +
  234. '</div>';
  235. }
  236. rt.innerHTML = s;
  237. }
  238. var i = 1;
  239. for (var k = 0; k < routes_res.length; k++) {
  240. var route = routes_res[k];
  241. var ETX = parseInt(route.etx) || 0;
  242. var color = etx_color(ETX);
  243. var tr = E('div', { 'class': 'tr cbi-section-table-row cbi-rowstyle-' + i + ' proto-' + route.proto }, [
  244. E('div', { 'class': 'td cbi-section-table-cell left' }, route.destination + '/' + route.genmask),
  245. E('div', { 'class': 'td cbi-section-table-cell left' }, [
  246. 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),
  247. route.hostname ? E('span', {}, [' / ', E('a', { 'href': 'http://%q/cgi-bin-status.html'.format(route.hostname) }, '%h'.format(route.hostname))]) : '',
  248. ]),
  249. E('div', { 'class': 'td cbi-section-table-cell left' }, route.networkInterface),
  250. E('div', { 'class': 'td cbi-section-table-cell left' }, route.metric),
  251. E('div', { 'class': 'td cbi-section-table-cell left', 'style': 'background-color:' + color }, [ETX.toFixed(3)]),
  252. ]);
  253. table.appendChild(tr);
  254. i = (i % 2) + 1;
  255. }
  256. var fieldset = E('fieldset', { 'class': 'cbi-section' }, [E('legend', {}, _('Overview of currently known routes to other OLSR nodes')), table]);
  257. var h2 = E('h2', { 'name': 'content' }, _('Known OLSR routes'));
  258. var divToggleButtons = E('div', { 'id': 'togglebuttons' });
  259. var statusOlsrLegend = E('div', {}, [
  260. E('h3', {}, [_('Legend') + ':']),
  261. E('ul', {}, [
  262. E('li', {}, [E('strong', {}, [_('LQ: ')]), _('Success rate of packages received from the neighbour')]),
  263. E('li', {}, [E('strong', {}, [_('NLQ: ')]), _('Success rate of packages sent to the neighbour')]),
  264. E('li', {}, [E('strong', {}, [_('ETX: ')]), _('Expected retransmission count')]),
  265. E('li', { 'style': 'list-style: none' }, [
  266. E('ul', {}, [
  267. E('li', {}, [E('strong', { 'style': 'color:#00cc00' }, [_('Green')]), ':', _('Very good (ETX < 2)')]),
  268. E('li', {}, [E('strong', { 'style': 'color:#ffcb05' }, [_('Yellow')]), ':', _('Good (2 < ETX < 4)')]),
  269. E('li', {}, [E('strong', { 'style': 'color:#ff6600' }, [_('Orange')]), ':', _('Still usable (4 < ETX < 10)')]),
  270. E('li', {}, [E('strong', { 'style': 'color:#bb3333' }, [_('Red')]), ':', _('Bad (ETX > 10)')]),
  271. ]),
  272. ]),
  273. E('li', {}, [E('strong', {}, [_('SNR: ')]), _('Signal Noise Ratio in dB')]),
  274. E('li', { 'style': 'list-style: none' }, [
  275. E('ul', {}, [
  276. E('li', {}, [E('strong', { 'style': 'color:#00cc00' }, [_('Green')]), ':', _('Very good (SNR > 30)')]),
  277. E('li', {}, [E('strong', { 'style': 'color:#ffcb05' }, [_('Yellow')]), ':', _('Good (30 > SNR > 20)')]),
  278. E('li', {}, [E('strong', { 'style': 'color:#ff6600' }, [_('Orange')]), ':', _('Still usable (20 > SNR > 5)')]),
  279. E('li', {}, [E('strong', { 'style': 'color:#bb3333' }, [_('Red')]), ':', _('Bad (SNR < 5)')]),
  280. ]),
  281. ]),
  282. ]),
  283. ]);
  284. var statusOlsrCommonJs = null;
  285. if (has_v4 && has_v6) {
  286. statusOlsrCommonJs = E('script', { 'type': 'text/javascript', 'src': L.resource('common/common_js.js') });
  287. }
  288. var result = E([], {}, [h2, divToggleButtons, fieldset, statusOlsrLegend, statusOlsrCommonJs]);
  289. return result;
  290. })
  291. .catch(function (error) {
  292. console.error(error);
  293. });
  294. },
  295. handleSaveApply: null,
  296. handleSave: null,
  297. handleReset: null,
  298. });