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