luci-app-apinger: Add LuCI for Apinger
LuCI Support for Apinger Signed-off-by: Jaymin Patel <jem.patel@gmail.com>
This commit is contained in:
parent
b0b9a34f8b
commit
6c151fcddb
10 changed files with 433 additions and 0 deletions
19
applications/luci-app-apinger/Makefile
Normal file
19
applications/luci-app-apinger/Makefile
Normal file
|
@ -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
|
||||
|
|
@ -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();
|
||||
},
|
||||
});
|
|
@ -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();
|
||||
},
|
||||
});
|
|
@ -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();
|
||||
},
|
||||
});
|
|
@ -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
|
||||
});
|
|
@ -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();
|
||||
},
|
||||
});
|
|
@ -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
|
||||
});
|
|
@ -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();
|
||||
},
|
||||
});
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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" ]
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue