From 6c151fcddbabc0fcdd9de8c5088153e84f5b0ccd Mon Sep 17 00:00:00 2001 From: Jaymin Patel Date: Sat, 16 Jul 2022 18:42:47 +0530 Subject: [PATCH] luci-app-apinger: Add LuCI for Apinger LuCI Support for Apinger Signed-off-by: Jaymin Patel --- applications/luci-app-apinger/Makefile | 19 +++++ .../resources/view/apinger/alarm_delay.js | 30 +++++++ .../resources/view/apinger/alarm_down.js | 24 ++++++ .../resources/view/apinger/alarm_loss.js | 30 +++++++ .../resources/view/apinger/graphs.js | 61 ++++++++++++++ .../resources/view/apinger/interfaces.js | 31 +++++++ .../resources/view/apinger/overview.js | 66 +++++++++++++++ .../resources/view/apinger/targets.js | 80 +++++++++++++++++++ .../share/luci/menu.d/luci-app-apinger.json | 73 +++++++++++++++++ .../share/rpcd/acl.d/luci-app-apinger.json | 19 +++++ 10 files changed, 433 insertions(+) create mode 100644 applications/luci-app-apinger/Makefile create mode 100644 applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/alarm_delay.js create mode 100644 applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/alarm_down.js create mode 100644 applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/alarm_loss.js create mode 100644 applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/graphs.js create mode 100644 applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/interfaces.js create mode 100644 applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/overview.js create mode 100644 applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/targets.js create mode 100644 applications/luci-app-apinger/root/usr/share/luci/menu.d/luci-app-apinger.json create mode 100644 applications/luci-app-apinger/root/usr/share/rpcd/acl.d/luci-app-apinger.json diff --git a/applications/luci-app-apinger/Makefile b/applications/luci-app-apinger/Makefile new file mode 100644 index 0000000000..78de53a7e8 --- /dev/null +++ b/applications/luci-app-apinger/Makefile @@ -0,0 +1,19 @@ +# +# Copyright (C) 2022 Jaymin Patel +# +# 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 + +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 index 0000000000..ed0c7c1b98 --- /dev/null +++ b/applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/alarm_delay.js @@ -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"') + '
' + + _('This alarm will be canceled, when the delay drops below "Delay Low"') + '
'); + + 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 index 0000000000..59f15f2ae8 --- /dev/null +++ b/applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/alarm_down.js @@ -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 index 0000000000..73da7e879b --- /dev/null +++ b/applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/alarm_loss.js @@ -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"') + '
' + + _('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 index 0000000000..18b0f0a339 --- /dev/null +++ b/applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/graphs.js @@ -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 index 0000000000..5f53b27639 --- /dev/null +++ b/applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/interfaces.js @@ -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 index 0000000000..de74be676d --- /dev/null +++ b/applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/overview.js @@ -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 index 0000000000..ae4d501b54 --- /dev/null +++ b/applications/luci-app-apinger/htdocs/luci-static/resources/view/apinger/targets.js @@ -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') + '
' + + _('Address: Target address to be tracked') + '
' + + _('Ping Interval: How often the probe should be sent') + '
' + + _('Average Delay: How many replies should be used to compute average delay') + '
' + + _('Average Loss: How many probes should be used to compute average loss') + '
' + + _('Average Delay and Loss: The delay (in samples) after which loss is computed, without this delays larger than interval would be treated as loss') + + '
'); + + 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 index 0000000000..4b76d133f5 --- /dev/null +++ b/applications/luci-app-apinger/root/usr/share/luci/menu.d/luci-app-apinger.json @@ -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 index 0000000000..69066081e2 --- /dev/null +++ b/applications/luci-app-apinger/root/usr/share/rpcd/acl.d/luci-app-apinger.json @@ -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" ] + } + } +} -- 2.30.2