luci-app-apinger: Add LuCI for Apinger 5887/head
authorJaymin Patel <jem.patel@gmail.com>
Sat, 16 Jul 2022 13:12:47 +0000 (18:42 +0530)
committerJaymin Patel <jem.patel@gmail.com>
Sun, 31 Jul 2022 20:54:52 +0000 (02:24 +0530)
LuCI Support for Apinger

Signed-off-by: Jaymin Patel <jem.patel@gmail.com>
applications/luci-app-apinger/Makefile [new file with mode: 0644]
applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/alarm_delay.js [new file with mode: 0644]
applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/alarm_down.js [new file with mode: 0644]
applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/alarm_loss.js [new file with mode: 0644]
applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/graphs.js [new file with mode: 0644]
applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/interfaces.js [new file with mode: 0644]
applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/overview.js [new file with mode: 0644]
applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/targets.js [new file with mode: 0644]
applications/luci-app-apinger/root/usr/share/luci/menu.d/luci-app-apinger.json [new file with mode: 0644]
applications/luci-app-apinger/root/usr/share/rpcd/acl.d/luci-app-apinger.json [new file with mode: 0644]

diff --git a/applications/luci-app-apinger/Makefile b/applications/luci-app-apinger/Makefile
new file mode 100644 (file)
index 0000000..78de53a
--- /dev/null
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 Jaymin Patel <jem.patel@gmail.com>
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI support for the Apinger
+LUCI_DEPENDS:=+apinger +apinger-rrd
+LUCI_PKGARCH:=all
+PKG_LICENSE:=GPL-2.0
+
+PKG_MAINTAINER:=Jaymin Patel <jem.patel@gmail.com>
+
+include ../../luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
diff --git a/applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/alarm_delay.js b/applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/alarm_delay.js
new file mode 100644 (file)
index 0000000..ed0c7c1
--- /dev/null
@@ -0,0 +1,30 @@
+'use strict';
+'require view';
+'require form';
+
+return view.extend({
+       render: function() {
+               var m, s, o;
+
+               m = new form.Map('apinger', _('Apinger - Delay Alarms'), 
+                        ('This alarm will be fired when target responses are delayed more than "Delay High"') + '<br />' +
+                       _('This alarm will be canceled, when the delay drops below "Delay Low"') + '<br />');
+
+               s = m.section(form.GridSection, 'alarm_delay');
+               s.anonymous = false;
+               s.addremove = true;
+               s.addbtntitle = _('Add Delay/Latency Alarm');
+
+               o = s.option(form.Value, 'delay_low', _('Delay Low (ms)'));
+               o.datatype = 'range(1-500)';
+               o.default = '30';
+               o.placeholder = '30';
+
+               o = s.option(form.Value, 'delay_high', _('Delay High (ms)'));
+               o.datatype = 'range(1-500)';
+               o.default = '50';
+               o.placeholder = '50';
+
+               return m.render();
+       },
+});
diff --git a/applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/alarm_down.js b/applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/alarm_down.js
new file mode 100644 (file)
index 0000000..59f15f2
--- /dev/null
@@ -0,0 +1,24 @@
+'use strict';
+'require view';
+'require form';
+
+return view.extend({
+       render: function() {
+               var m, s, o;
+
+               m = new form.Map('apinger', _('Apinger - Down Alarm'),
+                       _('This alarm will be fired when target does not respond for "Time"'));
+
+               s = m.section(form.GridSection, 'alarm_down');
+               s.anonymous = false;
+               s.addremove = true;
+               s.addbtntitle = _('Add Down Alarm');
+
+               o = s.option(form.Value, 'time', _('Time (s)'));
+               o.datatype = 'range(1-30)';
+               o.default = '1';
+               o.placeholder = '1';
+
+               return m.render();
+       },
+});
diff --git a/applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/alarm_loss.js b/applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/alarm_loss.js
new file mode 100644 (file)
index 0000000..73da7e8
--- /dev/null
@@ -0,0 +1,30 @@
+'use strict';
+'require view';
+'require form';
+
+return view.extend({
+       render: function() {
+               var m, s, o;
+
+               m = new form.Map('apinger', _('Apinger - Loss Alarms'),
+                       _('This alarm will be fired when packet loss goes over "Loss High"') + '<br />' +
+                       _('This alarm will be canceled, when the loss drops below "Loss Low"'));
+
+               s = m.section(form.GridSection, 'alarm_loss');
+               s.anonymous = false;
+               s.addremove = true;
+               s.addbtntitle = _('Add Loss Alarm');
+
+               o = s.option(form.Value, 'percent_low', _('Loss Low (%)'));
+               o.datatype = 'range(1-100)';
+               o.default = '10';
+               o.placeholder = '10';
+
+               o = s.option(form.Value, 'percent_high', _('Loss High (%)'));
+               o.datatype = 'range(1-100)';
+               o.default = '20';
+               o.placeholder = '20';
+
+               return m.render();
+       },
+});
diff --git a/applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/graphs.js b/applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/graphs.js
new file mode 100644 (file)
index 0000000..18b0f0a
--- /dev/null
@@ -0,0 +1,61 @@
+'use strict';
+'require view';
+'require uci';
+'require rpc';
+'require fs';
+'require ui';
+
+return view.extend({
+       callServiceList: rpc.declare({
+               object: 'service',
+               method: 'list',
+               params: [ 'name' ],
+               expect: { 'apinger': {} }
+       }),
+
+       callApingerUpdateGraphs: rpc.declare({
+               object: 'apinger',
+               method: 'update_graphs',
+               expect: { '': {} }
+       }),
+
+       load: function() {
+               return Promise.all([
+                       this.callServiceList('apinger'),
+                       this.callApingerUpdateGraphs(),
+               ]);
+       },
+
+       render: function(res) {
+               var running = Object.keys(res[0].instances || {}).length > 0;
+               var script = res[1]['rrdcgi'];
+
+               if (!running) {
+                       return ui.addNotification(null, E('h3', _('Service is not running'), 'danger'));
+               }
+
+               return fs.stat(script).then(function(res) {
+                       if ((res.type == "file") && (res.size > 100)) {
+                               return E([
+                                       E('h3', _('Apinger Targets RRD Graph')),
+                                       E('br'),
+                                       E('div', [
+                                               E('iframe', {
+                                                       src: script.replace(/^\/www/g, ''),
+                                                       scrolling: 'yes',
+                                                       style : 'width: 85vw; height: 100vh; border: none;'
+                                               })
+                                       ])
+                               ]);
+                       } else {
+                               return ui.addNotification(null, E('h3', _('No data available'), 'danger'));
+                       }
+               }).catch(function(err) {
+                       return ui.addNotification(null, E('h3', _('No access to server file'), 'danger'));
+               });
+       },
+
+       handleSaveApply: null,
+       handleSave: null,
+       handleReset: null
+});
diff --git a/applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/interfaces.js b/applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/interfaces.js
new file mode 100644 (file)
index 0000000..5f53b27
--- /dev/null
@@ -0,0 +1,31 @@
+'use strict';
+'require view';
+'require form';
+
+return view.extend({
+       render: function() {
+               var m, s, o;
+
+               m = new form.Map('apinger', _('Apinger - Interfaces'),
+                       _('Names must match the interface name found in /etc/config/network.'));
+
+               s = m.section(form.GridSection, 'interface');
+               s.anonymous = false;
+               s.addremove = true;
+               s.addbtntitle = _('Add Interface Instance');
+
+               o = s.option(form.Flag, 'debug', _('Debug'));
+               o.datatype = 'boolean';
+               o.default = false;
+
+               o = s.option(form.Value, 'status_interval', _('Status Update Interval'));
+               o.datatype = 'range(1-60)';
+               o.default = '5';
+
+               o = s.option(form.Value, 'rrd_interval', _('RRD Collection Interval'));
+               o.datatype = 'range(15-60)';
+               o.default = '30';
+
+               return m.render();
+       },
+});
diff --git a/applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/overview.js b/applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/overview.js
new file mode 100644 (file)
index 0000000..de74be6
--- /dev/null
@@ -0,0 +1,66 @@
+'use strict';
+'require view';
+'require rpc';
+'require form';
+'require poll';
+
+var callApingerStatus = rpc.declare({
+       object: 'apinger',
+       method: 'status',
+       expect: {  },
+});
+
+return view.extend({
+       render: function() {
+               var table =
+                       E('table', { 'class': 'table lases' }, [
+                               E('tr', { 'class': 'tr table-titles' }, [
+                                       E('th', { 'class': 'th' }, _('Interface')),
+                                       E('th', { 'class': 'th' }, _('Target')),
+                                       E('th', { 'class': 'th' }, _('Source IP')),
+                                       E('th', { 'class': 'th' }, _('Address')),
+                                       E('th', { 'class': 'th' }, _('Sent')),
+                                       E('th', { 'class': 'th' }, _('Received')),
+                                       E('th', { 'class': 'th' }, _('Latency')),
+                                       E('th', { 'class': 'th' }, _('Loss')),
+                                       E('th', { 'class': 'th' }, _('Active Alarms')),
+                                       E('th', { 'class': 'th' }, _('Time')),
+                                       E([])
+                               ])
+                       ]);
+
+               poll.add(function() {
+                       return callApingerStatus().then(function(targetInfo) {
+                               var targets = Array.isArray(targetInfo.targets) ? targetInfo.targets : [];
+
+                               cbi_update_table(table,
+                                       targets.map(function(target) {
+                                               return  [ 
+                                                       target.interface,
+                                                       target.target,
+                                                       target.srcip,
+                                                       target.address,
+                                                       target.sent,
+                                                       target.received,
+                                                       target.latency,
+                                                       target.loss,
+                                                       target.alarm,
+                                                       new Date(target.timestamp * 1000),
+                                               ];      
+                                       }),
+                                       E('em', _('There are no active targets'))
+                               );
+                       });
+               });
+
+               return E([
+                       E('h3', _('Apinger Targets')),
+                       E('br'),
+                       table
+               ]);
+       },
+
+       handleSave: null,
+       handleSaveApply:null,
+       handleReset: null
+});
diff --git a/applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/targets.js b/applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/targets.js
new file mode 100644 (file)
index 0000000..ae4d501
--- /dev/null
@@ -0,0 +1,80 @@
+'use strict';
+'require view';
+'require form';
+'require uci';
+
+return view.extend({
+       load: function() {
+               return Promise.all([
+                       uci.load('apinger'),
+               ])
+       },
+
+       render: function(data) {
+               var m, s, o;
+               var a_ifaces, a_down, a_delay, a_loss;
+
+               a_ifaces = uci.sections('apinger', 'interface');
+               a_down = uci.sections('apinger', 'alarm_down');
+               a_delay = uci.sections('apinger', 'alarm_delay');
+               a_loss = uci.sections('apinger', 'alarm_loss');
+
+               m = new form.Map('apinger', _('Apinger - Targets'),
+                       _('Interface: Interface to use to track target') + '<br />' +
+                       _('Address: Target address to be tracked') + '<br />' +
+                       _('Ping Interval: How often the probe should be sent') + '<br />' +
+                       _('Average Delay: How many replies should be used to compute average delay') + '<br />' +
+                       _('Average Loss: How many probes should be used to compute average loss') + '<br />' +
+                       _('Average Delay and Loss: The delay (in samples) after which loss is computed, without this delays larger than interval would be treated as loss') +
+                       '<br />');
+
+               s = m.section(form.GridSection, 'target');
+               s.anonymous = false;
+               s.addremove = true;
+               s.addbtntitle = _('Add Target');
+
+               o = s.option(form.ListValue, 'interface', _('Interface'));
+               for (var i = 0; i < a_ifaces.length; i++) {
+                       o.value(a_ifaces[i]['.name']);
+               }
+
+               o = s.option(form.Value, 'address', _('Address'));
+               o.datatype = 'ip4addr';
+
+               o = s.option(form.Value, 'probe_interval', _('Ping Interval'));
+               o.datatype = 'integer';
+
+               o= s.option(form.Value, 'avg_delay_samples', _('Average Delay'));
+               o.datatype = 'integer';
+
+               o = s.option(form.Value, 'avg_loss_samples', _('Average Loss'));
+               o.datatype = 'integer';
+
+               o = s.option(form.Value, 'avg_loss_delay_samples', _('Average Loss/Delay'));
+               o.datatype = 'integer';
+
+               o = s.option(form.Flag, 'rrd', _('Generate RRD Graphs'));
+               o.datatype = 'boolean';
+               o.default = false;
+
+               o = s.option(form.ListValue, 'alarm_down', _('Down Alarm'));
+               for (var i = 0; i < a_down.length; i++) {
+                       o.value(a_down[i]['.name']);
+               }
+               o.optional = true;
+
+               o = s.option(form.ListValue, 'alarm_delay', _('Delay Alarm'));
+               for (var i = 0; i < a_delay.length; i++) {
+                       o.value(a_delay[i]['.name']);
+               }
+               o.optional = true;
+
+               o = s.option(form.ListValue, 'alarm_loss', _('Loss Alarm'));
+               for (var i = 0; i < a_loss.length; i++) {
+                       o.value(a_loss[i]['.name']);
+               }
+               o.optional = true;
+
+               return m.render();
+       },
+});
diff --git a/applications/luci-app-apinger/root/usr/share/luci/menu.d/luci-app-apinger.json b/applications/luci-app-apinger/root/usr/share/luci/menu.d/luci-app-apinger.json
new file mode 100644 (file)
index 0000000..4b76d13
--- /dev/null
@@ -0,0 +1,73 @@
+{
+       "admin/services/apinger": {
+               "title": "Apinger",
+               "order": 90,
+               "action": {
+                       "type": "alias",
+                       "path": "admin/services/apinger/overview"
+               }
+       },
+
+       "admin/services/apinger/overview": {
+               "title": "Overview",
+               "order": 10,
+               "action": {
+                       "type": "view",
+                       "path": "apinger/overview"
+               }
+       },
+
+       "admin/services/apinger/graphs": {
+               "title": "Graphs",
+               "order": 11,
+               "action": {
+                       "type": "view",
+                       "path": "apinger/graphs"
+               }
+       },
+
+       "admin/services/apinger/interfaces": {
+               "title": "Interfaces",
+               "order": 19,
+               "action": {
+                       "type": "view",
+                       "path": "apinger/interfaces"
+               }
+       },
+
+       "admin/services/apinger/alarm_down": {
+               "title": "Alarm Down",
+               "order": 20,
+               "action": {
+                       "type": "view",
+                       "path": "apinger/alarm_down"
+               }
+       },
+
+       "admin/services/apinger/alarm_delay": {
+               "title": "Alarm Delay",
+               "order": 30,
+               "action": {
+                       "type": "view",
+                       "path": "apinger/alarm_delay"
+               }
+       },
+
+       "admin/services/apinger/alarm_loss": {
+               "title": "Alarm loss",
+               "order": 40,
+               "action": {
+                       "type": "view",
+                       "path": "apinger/alarm_loss"
+               }
+       },
+
+       "admin/services/apinger/targets": {
+               "title": "Targets",
+               "order": 90,
+               "action": {
+                       "type": "view",
+                       "path": "apinger/targets"
+               }
+       }
+}
diff --git a/applications/luci-app-apinger/root/usr/share/rpcd/acl.d/luci-app-apinger.json b/applications/luci-app-apinger/root/usr/share/rpcd/acl.d/luci-app-apinger.json
new file mode 100644 (file)
index 0000000..6906608
--- /dev/null
@@ -0,0 +1,19 @@
+{
+       "luci-app-apinger" : {
+               "description" : "Grant access to LuCI app Apinger",
+               "read" : {
+                       "ubus" : {
+                               "apinger" : [ "*" ],
+                               "file": [ "stat" ],
+                               "service": [ "list" ]
+                       },
+                       "uci": [ "apinger" ]
+               },
+               "write" : {
+                       "ubus" : {
+                               "apinger" : [ "*" ]
+                       },
+                       "uci": [ "apinger" ]
+               }
+       }
+}