luci-app-statistics: convert graph rendering to client side js

This conversion requires cgi-io >= version 17 and uhttpd version >= 2020-02-12
to function properly.

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This commit is contained in:
Jo-Philipp Wich 2020-02-13 20:45:26 +01:00
parent 1d47f0c1a9
commit 9680fdea9e
95 changed files with 15542 additions and 14612 deletions

View file

@ -0,0 +1,717 @@
'use strict';
'require fs';
'require uci';
'require tools.prng as random';
function subst(str, val) {
return str.replace(/%(H|pn|pi|dt|di|ds)/g, function(m, p1) {
switch (p1) {
case 'H': return val.host || '';
case 'pn': return val.plugin || '';
case 'pi': return val.pinst || '';
case 'dt': return val.dtype || '';
case 'di': return val.dinst || '';
case 'ds': return val.dsrc || '';
}
});
}
var i18n = L.Class.singleton({
title: function(host, plugin, pinst, dtype, dinst, user_title) {
var title = user_title || 'p=%s/pi=%s/dt=%s/di=%s'.format(
plugin,
pinst || '(nil)',
dtype || '(nil)',
dinst || '(nil)'
);
return subst(title, {
host: host,
plugin: plugin,
pinst: pinst,
dtype: dtype,
dinst: dinst
});
},
label: function(host, plugin, pinst, dtype, dinst, user_label) {
var label = user_label || 'dt=%s/%di=%s'.format(
dtype || '(nil)',
dinst || '(nil)'
);
return subst(label, {
host: host,
plugin: plugin,
pinst: pinst,
dtype: dtype,
dinst: dinst
});
},
ds: function(host, source) {
var label = source.title || 'dt=%s/di=%s/ds=%s'.format(
source.type || '(nil)',
source.instance || '(nil)',
source.ds || '(nil)'
);
return subst(label, {
host: host,
dtype: source.type,
dinst: source.instance,
dsrc: source.ds
}).replace(/:/g, '\\:');
}
});
var colors = L.Class.singleton({
fromString: function(s) {
if (typeof(s) != 'string' || !s.match(/^[0-9a-fA-F]{6}$/))
return null;
return [
parseInt(s.substring(0, 2), 16),
parseInt(s.substring(2, 4), 16),
parseInt(s.substring(4, 6), 16)
];
},
asString: function(c) {
if (!Array.isArray(c) || c.length != 3)
return null;
return '%02x%02x%02x'.format(c[0], c[1], c[2]);
},
defined: function(i) {
var t = [
[230, 25, 75],
[245, 130, 48],
[255, 225, 25],
[60, 180, 75],
[70, 240, 240],
[0, 130, 200],
[0, 0, 128],
[170, 110, 40]
];
return this.asString(t[i % t.length]);
},
random: function() {
var r = random.get(255),
g = random.get(255),
min = 0, max = 255;
if (r + g < 255)
min = 255 - r - g;
else
max = 511 - r - g;
var b = min + Math.floor(random.get() * (max - min));
return [ r, g, b ];
},
faded: function(fg, bg, alpha) {
fg = this.fromString(fg) || (this.asString(fg) ? fg : null);
bg = this.fromString(bg) || (this.asString(bg) ? bg : [255, 255, 255]);
alpha = !isNaN(alpha) ? +alpha : 0.25;
if (!fg)
return null;
return [
(alpha * fg[0]) + ((1.0 - alpha) * bg[0]),
(alpha * fg[1]) + ((1.0 - alpha) * bg[1]),
(alpha * fg[2]) + ((1.0 - alpha) * bg[2])
];
}
});
var rrdtree = {},
graphdefs = {};
return L.Class.extend({
__init__: function() {
this.opts = {};
},
load: function() {
return Promise.all([
L.resolveDefault(fs.list('/www' + L.resource('statistics/rrdtool/definitions')), []),
fs.trimmed('/proc/sys/kernel/hostname'),
uci.load('luci_statistics')
]).then(L.bind(function(data) {
var definitions = data[0],
hostname = data[1];
this.opts.host = uci.get('luci_statistics', 'collectd', 'Hostname') || hostname;
this.opts.timespan = uci.get('luci_statistics', 'rrdtool', 'default_timespan') || 900;
this.opts.width = uci.get('luci_statistics', 'rrdtool', 'image_width') || 400;
this.opts.height = uci.get('luci_statistics', 'rrdtool', 'image_height') || 100;
this.opts.rrdpath = (uci.get('luci_statistics', 'collectd_rrdtool', 'DataDir') || '/tmp/rrd').replace(/\/$/, '');
this.opts.rrasingle = (uci.get('luci_statistics', 'collectd_rrdtool', 'RRASingle') == '1');
this.opts.rramax = (uci.get('luci_statistics', 'collectd_rrdtool', 'RRAMax') == '1');
graphdefs = {};
var tasks = [ this.scan() ];
for (var i = 0; i < definitions.length; i++) {
var m = definitions[i].name.match(/^(.+)\.js$/);
if (definitions[i].type != 'file' || m == null)
continue;
tasks.push(L.require('statistics.rrdtool.definitions.' + m[1]).then(L.bind(function(name, def) {
graphdefs[name] = def;
}, this, m[1])));
}
return Promise.all(tasks);
}, this));
},
ls: function() {
var dir = this.opts.rrdpath;
return L.resolveDefault(fs.list(dir), []).then(function(entries) {
var tasks = [];
for (var i = 0; i < entries.length; i++) {
if (entries[i].type != 'directory')
continue;
tasks.push(L.resolveDefault(fs.list(dir + '/' + entries[i].name), []).then(L.bind(function(entries) {
var tasks = [];
for (var j = 0; j < entries.length; j++) {
if (entries[j].type != 'directory')
continue;
tasks.push(L.resolveDefault(fs.list(dir + '/' + this.name + '/' + entries[j].name), []).then(L.bind(function(entries) {
return Object.assign(this, {
entries: entries.filter(function(e) {
return e.type == 'file' && e.name.match(/\.rrd$/);
})
});
}, entries[j])));
}
return Promise.all(tasks).then(L.bind(function(entries) {
return Object.assign(this, {
entries: entries
});
}, this));
}, entries[i])));
}
return Promise.all(tasks);
});
},
scan: function() {
return this.ls().then(L.bind(function(entries) {
rrdtree = {};
for (var i = 0; i < entries.length; i++) {
var hostInstance = entries[i].name;
rrdtree[hostInstance] = rrdtree[hostInstance] || {};
for (var j = 0; j < entries[i].entries.length; j++) {
var m = entries[i].entries[j].name.match(/^([^-]+)(?:-(.+))?$/);
if (!m)
continue;
var pluginName = m[1],
pluginInstance = m[2] || '';
rrdtree[hostInstance][pluginName] = rrdtree[hostInstance][pluginName] || {};
rrdtree[hostInstance][pluginName][pluginInstance] = rrdtree[hostInstance][pluginName][pluginInstance] || {};
for (var k = 0; k < entries[i].entries[j].entries.length; k++) {
var m = entries[i].entries[j].entries[k].name.match(/^([^-]+)(?:-(.+))?\.rrd$/);
if (!m)
continue;
var dataType = m[1],
dataInstance = m[2] || '';
rrdtree[hostInstance][pluginName][pluginInstance][dataType] = rrdtree[hostInstance][pluginName][pluginInstance][dataType] || [];
rrdtree[hostInstance][pluginName][pluginInstance][dataType].push(dataInstance);
}
}
}
}, this));
},
hostInstances: function() {
return Object.keys(rrdtree).sort();
},
pluginNames: function(hostInstance) {
return Object.keys(rrdtree[hostInstance] || {}).sort();
},
pluginInstances: function(hostInstance, pluginName) {
return Object.keys((rrdtree[hostInstance] || {})[pluginName] || {}).sort();
},
dataTypes: function(hostInstance, pluginName, pluginInstance) {
return Object.keys(((rrdtree[hostInstance] || {})[pluginName] || {})[pluginInstance] || {}).sort();
},
dataInstances: function(hostInstance, pluginName, pluginInstance, dataType) {
return ((((rrdtree[hostInstance] || {})[pluginName] || {})[pluginInstance] || {})[dataType] || []).sort();
},
pluginTitle: function(pluginName) {
var def = graphdefs[pluginName];
return (def ? def.title : null) || pluginName;
},
hasDefinition: function(pluginName) {
return (graphdefs[pluginName] != null);
},
_mkpath: function(host, plugin, plugin_instance, dtype, data_instance) {
var path = host + '/' + plugin;
if (plugin_instance != null && plugin_instance != '')
path += '-' + plugin_instance;
path += '/' + dtype;
if (data_instance != null && data_instance != '')
path += '-' + data_instance;
return path;
},
mkrrdpath: function(/* ... */) {
return '%s/%s.rrd'.format(
this.opts.rrdpath,
this._mkpath.apply(this, arguments)
).replace(/[\\:]/g, '\\$&');
},
_forcelol: function(list) {
return L.isObject(list[0]) ? list : [ list ];
},
_rrdtool: function(def, rrd, timespan, width, height) {
var cmdline = [
'graph', '-', '-a', 'PNG',
'-s', 'NOW-%s'.format(timespan || this.opts.timespan),
'-e', 'NOW-60',
'-w', width || this.opts.width,
'-h', height || this.opts.height
];
for (var i = 0; i < def.length; i++) {
var opt = String(def[i]);
if (rrd)
opt = opt.replace(/\{file\}/g, rrd);
cmdline.push(opt);
}
return fs.exec_direct('/usr/bin/rrdtool', cmdline, 'blob', true);
},
_generic: function(opts, host, plugin, plugin_instance, dtype, index) {
var defs = [],
gopts = this.opts,
_args = [],
_sources = [],
_stack_neg = [],
_stack_pos = [],
_longest_name = 0,
_has_totals = false;
function __def(source) {
var inst = source.sname,
rrd = source.rrd.replace(/[\\:]/g, '\\$&'),
ds = source.ds || 'value';
_args.push(
'DEF:%s_avg_raw=%s:%s:AVERAGE'.format(inst, rrd, ds),
'CDEF:%s_avg=%s_avg_raw,%s'.format(inst, inst, source.transform_rpn)
);
if (!gopts.rrasingle)
_args.push(
'DEF:%s_min_raw=%s:%s:MIN'.format(inst, rrd, ds),
'CDEF:%s_min=%s_min_raw,%s'.format(inst, inst, source.transform_rpn),
'DEF:%s_max_raw=%s:%s:MAX'.format(inst, rrd, ds),
'CDEF:%s_max=%s_max_raw,%s'.format(inst, inst, source.transform_rpn)
);
_args.push(
'CDEF:%s_nnl=%s_avg,UN,0,%s_avg,IF'.format(inst, inst, inst)
);
}
function __cdef(source) {
var prev;
if (source.flip)
prev = _stack_neg[_stack_neg.length - 1];
else
prev = _stack_pos[_stack_pos.length - 1];
/* is first source in stack or overlay source: source_stk = source_nnl */
if (prev == null || source.overlay) {
/* create cdef statement for cumulative stack (no NaNs) and also
for display (preserving NaN where no points should be displayed) */
if (gopts.rrasingle || !gopts.rramax)
_args.push(
'CDEF:%s_stk=%s_nnl'.format(source.sname, source.sname),
'CDEF:%s_plot=%s_avg'.format(source.sname, source.sname)
);
else
_args.push(
'CDEF:%s_stk=%s_nnl'.format(source.sname, source.sname),
'CDEF:%s_plot=%s_max'.format(source.sname, source.sname)
);
}
/* is subsequent source without overlay: source_stk = source_nnl + previous_stk */
else {
/* create cdef statement */
if (gopts.rrasingle || !gopts.rramax)
_args.push(
'CDEF:%s_stk=%s_nnl,%s_stk,+'.format(source.sname, source.sname, prev),
'CDEF:%s_plot=%s_avg,%s_stk,+'.format(source.sname, source.sname, prev)
);
else
_args.push(
'CDEF:%s_stk=%s_nnl,%s_stk,+'.format(source.sname, source.sname, prev),
'CDEF:%s_plot=%s_max,%s_stk,+'.format(source.sname, source.sname, prev)
);
}
/* create multiply by minus one cdef if flip is enabled */
if (source.flip) {
_args.push('CDEF:%s_neg=%s_plot,-1,*'.format(source.sname, source.sname));
/* push to negative stack if overlay is disabled */
if (!source.overlay)
_stack_neg.push(source.sname);
}
/* no flipping, push to positive stack if overlay is disabled */
else if (!source.overlay) {
/* push to positive stack */
_stack_pos.push(source.sname);
}
/* calculate total amount of data if requested */
if (source.total)
_args.push(
'CDEF:%s_avg_sample=%s_avg,UN,0,%s_avg,IF,sample_len,*'.format(source.sname, source.sname, source.sname),
'CDEF:%s_avg_sum=PREV,UN,0,PREV,IF,%s_avg_sample,+'.format(source.sname, source.sname, source.sname)
);
}
/* local helper: create cdefs required for calculating total values */
function __cdef_totals() {
if (_has_totals)
_args.push(
'CDEF:mytime=%s_avg,TIME,TIME,IF'.format(_sources[0].sname),
'CDEF:sample_len_raw=mytime,PREV(mytime),-',
'CDEF:sample_len=sample_len_raw,UN,0,sample_len_raw,IF'
);
}
/* local helper: create line and area statements */
function __line(source) {
var line_color, area_color, legend, variable;
/* find colors: try source, then opts.colors; fall back to random color */
if (typeof(source.color) == 'string') {
line_color = source.color;
area_color = colors.fromString(line_color);
}
else if (typeof(opts.colors[source.name.replace(/\W/g, '_')]) == 'string') {
line_color = opts.colors[source.name.replace(/\W/g, '_')];
area_color = colors.fromString(line_color);
}
else {
area_color = colors.random();
line_color = colors.asString(area_color);
}
/* derive area background color from line color */
area_color = colors.asString(colors.faded(area_color));
/* choose source_plot or source_neg variable depending on flip state */
variable = source.flip ? 'neg' : 'plot';
/* create legend */
legend = '%%-%us'.format(_longest_name).format(source.title);
/* create area is not disabled */
if (!source.noarea)
_args.push('AREA:%s_%s#%s'.format(source.sname, variable, area_color));
/* create line statement */
_args.push('LINE%d:%s_%s#%s:%s'.format(
source.width || (source.noarea ? 2 : 1),
source.sname, variable, line_color, legend
));
}
/* local helper: create gprint statements */
function __gprint(source) {
var numfmt = opts.number_format || '%6.1lf',
totfmt = opts.totals_format || '%5.1lf%s';
/* don't include MIN if rrasingle is enabled */
if (!gopts.rrasingle)
_args.push('GPRINT:%s_min:MIN:\tMin\\: %s'.format(source.sname, numfmt));
/* always include AVERAGE */
_args.push('GPRINT:%s_avg:AVERAGE:\tAvg\\: %s'.format(source.sname, numfmt));
/* don't include MAX if rrasingle is enabled */
if (!gopts.rrasingle)
_args.push('GPRINT:%s_max:MAX:\tMax\\: %s'.format(source.sname, numfmt));
/* include total count if requested else include LAST */
if (source.total)
_args.push('GPRINT:%s_avg_sum:LAST:(ca. %s Total)\\l'.format(source.sname, totfmt));
else
_args.push('GPRINT:%s_avg:LAST:\tLast\\: %s\\l'.format(source.sname, numfmt));
}
/*
* find all data sources
*/
/* find data types */
var data_types = dtype ? [ dtype ] : (opts.data.types || []);
if (!(dtype || opts.data.types)) {
if (L.isObject(opts.data.instances))
data_types.push.apply(data_types, Object.keys(opts.data.instances));
else if (L.isObject(opts.data.sources))
data_types.push.apply(data_types, Object.keys(opts.data.sources));
}
/* iterate over data types */
for (var i = 0; i < data_types.length; i++) {
/* find instances */
var data_instances;
if (!opts.per_instance) {
if (L.isObject(opts.data.instances) && Array.isArray(opts.data.instances[data_types[i]]))
data_instances = opts.data.instances[data_types[i]];
else
data_instances = this.dataInstances(host, plugin, plugin_instance, data_types[i]);
}
if (!Array.isArray(data_instances) || data_instances.length == 0)
data_instances = [ '' ];
/* iterate over data instances */
for (var j = 0; j < data_instances.length; j++) {
/* construct combined data type / instance name */
var dname = data_types[i];
if (data_instances[j].length)
dname += '_' + data_instances[j];
/* find sources */
var data_sources = [ 'value' ];
if (L.isObject(opts.data.sources)) {
if (Array.isArray(opts.data.sources[dname]))
data_sources = opts.data.sources[dname];
else if (Array.isArray(opts.data.sources[data_types[i]]))
data_sources = opts.data.sources[data_types[i]];
}
/* iterate over data sources */
for (var k = 0; k < data_sources.length; k++) {
var dsname = data_types[i] + '_' + data_instances[j].replace(/\W/g, '_') + '_' + data_sources[k],
altname = data_types[i] + '__' + data_sources[k];
/* find datasource options */
var dopts = {};
if (L.isObject(opts.data.options)) {
if (L.isObject(opts.data.options[dsname]))
dopts = opts.data.options[dsname];
else if (L.isObject(opts.data.options[altname]))
dopts = opts.data.options[altname];
else if (L.isObject(opts.data.options[dname]))
dopts = opts.data.options[dname];
else if (L.isObject(opts.data.options[data_types[i]]))
dopts = opts.data.options[data_types[i]];
}
/* store values */
var source = {
rrd: dopts.rrd || this.mkrrdpath(host, plugin, plugin_instance, data_types[i], data_instances[j]),
color: dopts.color || colors.asString(colors.random()),
flip: dopts.flip || false,
total: dopts.total || false,
overlay: dopts.overlay || false,
transform_rpn: dopts.transform_rpn || '0,+',
noarea: dopts.noarea || false,
title: dopts.title || null,
weight: dopts.weight || (dopts.negweight ? -+data_instances[j] : null) || (dopts.posweight ? +data_instances[j] : null) || null,
ds: data_sources[j],
type: data_types[i],
instance: data_instances[j],
index: _sources.length + 1,
sname: String(_sources.length + 1) + data_types[i]
};
_sources.push(source);
/* generate datasource title */
source.title = i18n.ds(host, source);
/* find longest name */
_longest_name = Math.max(_longest_name, source.title.length);
/* has totals? */
if (source.total)
_has_totals = true;
}
}
}
/*
* construct diagrams
*/
/* if per_instance is enabled then find all instances from the first datasource in diagram */
/* if per_instance is disabled then use an empty pseudo instance and use model provided values */
var instances = [ '' ];
if (opts.per_instance)
instances = this.dataInstances(host, plugin, plugin_instance, _sources[0].type);
/* iterate over instances */
for (var i = 0; i < instances.length; i++) {
/* store title and vlabel */
_args.push(
'-t', i18n.title(host, plugin, plugin_instance, _sources[0].type, instances[i], opts.title),
'-v', i18n.label(host, plugin, plugin_instance, _sources[0].type, instances[i], opts.vlabel)
);
if (opts.y_max)
_args.push('-u', String(opts.y_max));
if (opts.y_min)
_args.push('-l', String(opts.y_min));
if (opts.units_exponent)
_args.push('-X', String(opts.units_exponent));
if (opts.alt_autoscale)
_args.push('-A');
if (opts.alt_autoscale_max)
_args.push('-M');
/* store additional rrd options */
if (Array.isArray(opts.rrdopts))
for (var j = 0; j < opts.rrdopts.length; j++)
_args.push(String(opts.rrdopts[j]));
/* sort sources */
_sources.sort(function(a, b) {
var x = a.weight || a.index || 0,
y = b.weight || b.index || 0;
return +x < +y;
});
/* define colors in order */
if (opts.ordercolor)
for (var j = 0; j < _sources.length; j++)
_sources[j].color = colors.defined(j);
/* create DEF statements for each instance */
for (var j = 0; j < _sources.length; j++) {
/* fixup properties for per instance mode... */
if (opts.per_instance) {
_sources[j].instance = instances[i];
_sources[j].rrd = this.mkrrdpath(host, plugin, plugin_instance, _sources[j].type, instances[i]);
}
__def(_sources[j]);
}
/* create CDEF required for calculating totals */
__cdef_totals();
/* create CDEF statements for each instance in reversed order */
for (var j = _sources.length - 1; j >= 0; j--)
__cdef(_sources[j]);
/* create LINE1, AREA and GPRINT statements for each instance */
for (var j = 0; j < _sources.length; j++) {
__line(_sources[j]);
__gprint(_sources[j]);
}
/* push arg stack to definition list */
defs.push(_args);
/* reset stacks */
_args = [];
_stack_pos = [];
_stack_neg = [];
}
return defs;
},
render: function(plugin, plugin_instance, is_index, hostname, timespan, width, height) {
var pngs = [];
/* check for a whole graph handler */
var def = graphdefs[plugin];
if (def && typeof(def.rrdargs) == 'function') {
/* temporary image matrix */
var _images = [];
/* get diagram definitions */
var optlist = this._forcelol(def.rrdargs(this, hostname, plugin, plugin_instance, null, is_index));
for (var i = 0; i < optlist.length; i++) {
var opt = optlist[i];
if (!is_index || !opt.detail) {
_images[i] = [];
/* get diagram definition instances */
var diagrams = this._generic(opt, hostname, plugin, plugin_instance, null, i);
/* render all diagrams */
for (var j = 0; j < diagrams.length; j++) {
/* exec */
_images[i][j] = this._rrdtool(diagrams[j], null, timespan, width, height);
}
}
}
/* remember images - XXX: fixme (will cause probs with asymmetric data) */
for (var y = 0; y < _images[0].length; y++)
for (var x = 0; x < _images.length; x++)
pngs.push(_images[x][y]);
}
return Promise.all(pngs);
}
});

View file

@ -0,0 +1,177 @@
/* Licensed to the public under the Apache License 2.0. */
'use strict';
return L.Class.extend({
title: _('APC UPS'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
var rv = [];
/*
* Types and instances supported by APC UPS
* e.g. ups_types -> [ 'timeleft', 'charge', 'percent', 'voltage' ]
* e.g. ups_inst['voltage'] -> [ 'input', 'battery' ]
*/
var ups_types = graph.dataTypes(host, plugin, plugin_instance),
ups_inst = [];
for (var i = 0; i < ups_types.length; i++)
ups_inst.push(graph.dataInstances(host, plugin, plugin_instance, ups_types[i]));
/* Check if hash table or array is empty or nil-filled */
function empty(t) {
for (var k in t)
if (t[k] != null)
return false;
return true;
}
/* Append graph definition but only types/instances which are */
/* supported and available to the plugin and UPS. */
function add_supported(t, defs) {
var def_inst = defs['data']['instances'];
if (L.isObject(def_inst)) {
for (var k in def_inst) {
if (ups_types.find(function(t) { return t == k }).length) {
for (var i = def_inst[k].length - 1; i >= 0; i--)
if (!ups_inst[k].find(function(n) { return n == def_inst[k][i] }).length)
def_inst[k].splice(i, 1);
if (def_inst[k].length == 0)
def_inst[k] = null; /* can't assign v: immutable */
}
else {
def_inst[k] = null; /* can't assign v: immutable */
}
}
if (empty(def_inst))
return;
}
t.push(defs);
}
/* Graph definitions for APC UPS measurements MUST use only 'instances': */
/* e.g. instances = { voltage = { "input", "output" } } */
var voltagesdc = {
title: "%H: Voltages on APC UPS - Battery",
vlabel: "Volts DC",
alt_autoscale: true,
number_format: "%5.1lfV",
data: {
instances: {
voltage: [ "battery" ]
},
options: {
voltage: { title: "Battery voltage", noarea: true }
}
}
};
add_supported(rv, voltagesdc);
var voltagesac = {
title: "%H: Voltages on APC UPS - AC",
vlabel: "Volts AC",
alt_autoscale: true,
number_format: "%5.1lfV",
data: {
instances: {
voltage: [ "input", "output" ]
},
options: {
voltage_output : { color: "00e000", title: "Output voltage", noarea: true, overlay: true },
voltage_input : { color: "ffb000", title: "Input voltage", noarea: true, overlay: true }
}
}
};
add_supported(rv, voltagesac);
var percentload = {
title: "%H: Load on APC UPS ",
vlabel: "Percent",
y_min: "0",
y_max: "100",
number_format: "%5.1lf%%",
data: {
instances: {
percent: [ "load" ]
},
options: {
percent_load: { color: "00ff00", title: "Load level" }
}
}
};
add_supported(rv, percentload);
var charge_percent = {
title: "%H: Battery charge on APC UPS ",
vlabel: "Percent",
y_min: "0",
y_max: "100",
number_format: "%5.1lf%%",
data: {
instances: {
charge: [ "" ]
},
options: {
charge: { color: "00ff0b", title: "Charge level" }
}
}
};
add_supported(rv, charge_percent);
var temperature = {
title: "%H: Battery temperature on APC UPS ",
vlabel: "\u00b0C",
number_format: "%5.1lf\u00b0C",
data: {
instances: {
temperature: [ "" ]
},
options: {
temperature: { color: "ffb000", title: "Battery temperature" } }
}
};
add_supported(rv, temperature);
var timeleft = {
title: "%H: Time left on APC UPS ",
vlabel: "Minutes",
number_format: "%.1lfm",
data: {
instances: {
timeleft: [ "" ]
},
options: {
timeleft: { color: "0000ff", title: "Time left" }
}
}
};
add_supported(rv, timeleft);
var frequency = {
title: "%H: Incoming line frequency on APC UPS ",
vlabel: "Hz",
number_format: "%5.0lfhz",
data: {
instances: {
frequency: [ "input" ]
},
options: {
frequency_input: { color: "000fff", title: "Line frequency" }
}
}
};
add_supported(rv, frequency);
return rv;
}
});

View file

@ -0,0 +1,30 @@
/* Licensed to the public under the Apache License 2.0. */
'use strict';
return L.Class.extend({
title: _('Conntrack'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
return {
title: "%H: Conntrack entries",
vlabel: "Count",
number_format: "%5.0lf",
data: {
/* collectd 5.5+: specify "" to exclude "max" instance */
instances: {
conntrack: [ "" ]
},
sources: {
conntrack: [ "value" ]
},
options: {
conntrack: {
color: "0000ff",
title: "Tracked connections"
}
}
}
};
}
});

View file

@ -0,0 +1,25 @@
/* Licensed to the public under the Apache License 2.0. */
'use strict';
return L.Class.extend({
title: _('Context Switches'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
return {
title: "%H: Context switches",
alt_autoscale: true,
vlabel: "Switches/s",
number_format: "%5.0lf",
data: {
types: [ "contextswitch" ],
sources: {
contextswitch: [ "value" ]
},
options: {
contextswitch: { color: "0000ff", title: "Context switches", noarea: true, overlay: true }
}
}
};
}
});

View file

@ -0,0 +1,163 @@
/* Licensed to the public under the Apache License 2.0. */
'use strict';
'require uci';
return L.Class.extend({
title: _('Processor'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
var p = [];
var title = "%H: Processor usage";
if (plugin_instance != '')
title = "%H: Processor usage on core #%pi";
if (uci.get("luci_statistics", "collectd_cpu", "ReportByState") == "1") {
var cpu = {
title: title,
y_min: "0",
alt_autoscale_max: true,
vlabel: "Jiffies",
number_format: "%5.1lf",
data: {
instances: {
cpu: [
"idle",
"interrupt",
"nice",
"softirq",
"steal",
"system",
"user",
"wait"
]
},
options: {
cpu_idle: {
color: "ffffff",
title: "Idle"
},
cpu_interrupt: {
color: "a000a0",
title: "Interrupt"
},
cpu_nice: {
color: "00e000",
title: "Nice"
},
cpu_softirq: {
color: "ff00ff",
title: "Softirq"
},
cpu_steal: {
color: "000000",
title: "Steal"
},
cpu_system: {
color: "ff0000",
title: "System"
},
cpu_user: {
color: "0000ff",
title: "User"
},
cpu_wait: {
color: "ffb000",
title: "Wait"
}
}
}
};
var percent = {
title: title,
y_min: "0",
alt_autoscale_max: true,
vlabel: "Percent",
number_format: "%5.1lf%%",
data: {
instances: {
percent: [
"idle",
"interrupt",
"nice",
"softirq",
"steal",
"system",
"user",
"wait"
]
},
options: {
percent_idle: {
color: "ffffff",
title: "Idle"
},
percent_interrupt: {
color: "a000a0",
title: "Interrupt"
},
percent_nice: {
color: "00e000",
title: "Nice"
},
percent_softirq: {
color: "ff00ff",
title: "Softirq"
},
percent_steal: {
color: "000000",
title: "Steal"
},
percent_system: {
color: "ff0000",
title: "System"
},
percent_user: {
color: "0000ff",
title: "User"
},
percent_wait: {
color: "ffb000",
title: "Wait"
}
}
}
};
var types = graph.dataTypes(host, plugin, plugin_instance);
for (var i = 0; i < types.length; i++)
if (types[i] == 'cpu')
p.push(cpu);
else if (types[i] == 'percent')
p.push(percent);
}
else {
p = {
title: title,
y_min: "0",
alt_autoscale_max: true,
vlabel: "Percent",
number_format: "%5.1lf%%",
data: {
instances: {
percent: [
"active",
]
},
options: {
percent_active: {
color: "00e000",
title: "Active"
}
}
}
};
}
return p;
}
});

View file

@ -0,0 +1,57 @@
/* Licensed to the public under the Apache License 2.0. */
'use strict';
'require uci';
return L.Class.extend({
title: _('CPU Frequency'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
var cpufreq = {
title: "%H: Processor frequency - core %pi",
alt_autoscale: true,
vlabel: "Frequency (Hz)",
number_format: "%3.2lf%s",
data: {
types: [ "cpufreq" ],
options: {
cpufreq: { color: "ff0000", title: "Frequency" },
}
}
};
if (uci.get("luci_statistics", "collectd_cpufreq", "ExtraItems")) {
var transitions = {
title: "%H: Frequency transitions - core %pi",
alt_autoscale: true,
vlabel: "Transitions",
number_format: "%3.2lf%s",
data: {
types: [ "transitions" ],
options: {
transitions: { color: "0000ff", title: "Transitions", noarea: true },
}
}
};
var percentage = {
title: "%H: Frequency distribution - core %pi",
alt_autoscale: true,
vlabel: "Percent",
number_format: "%5.2lf%%",
ordercolor: true,
data: {
types: [ "percent" ],
options: {
percent: { title: "%di kHz", negweight: true },
}
}
};
return [ cpufreq, percentage, transitions ];
}
else {
return [ cpufreq ];
}
}
});

View file

@ -0,0 +1,28 @@
/*
* Copyright 2018 Chizhong Jin <pjincz@gmail.com>
* Licensed to the public under the BSD 3-clause license
*/
'use strict';
return L.Class.extend({
title: _('cUrl'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
return {
title: "%H: cUrl Response Time for #%pi",
y_min: "0",
alt_autoscale_max: true,
vlabel: "Response Time",
number_format: "%5.1lf%Ss",
data: {
types: [ "response_time" ],
options: {
response_time: {
title: ""
}
}
}
};
}
});

View file

@ -0,0 +1,83 @@
/* Licensed to the public under the Apache License 2.0. */
'use strict';
return L.Class.extend({
title: _('Disk Space Usage'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
var df_complex = {
title: "%H: Disk space usage on %pi",
vlabel: "Bytes",
number_format: "%5.1lf%sB",
data: {
instances: {
df_complex: [ "free", "used", "reserved" ]
},
options: {
df_complex_free: {
color: "00ff00",
overlay: false,
title: "free"
},
df_complex_used: {
color: "ff0000",
overlay: false,
title: "used"
},
df_complex_reserved: {
color: "0000ff",
overlay: false,
title: "reserved"
}
}
}
};
var percent_bytes = {
title: "%H: Disk space usage on %pi",
vlabel: "Percent",
number_format: "%5.2lf %%",
data: {
instances: {
percent_bytes: [ "free", "used", "reserved" ]
},
options: {
percent_bytes_free: {
color: "00ff00",
overlay: false,
title: "free"
},
percent_bytes_used: {
color: "ff0000",
overlay: false,
title: "used"
},
percent_bytes_reserved: {
color: "0000ff",
overlay: false,
title: "reserved"
}
}
}
};
var types = graph.dataTypes(host, plugin, plugin_instance);
for (var i = 0; i < types.length; i++)
if (types[i] == 'percent_bytes')
p.push(percent_bytes);
else if (types[i] == 'df_complex')
p.push(df_complex);
return p;
}
});

View file

@ -0,0 +1,64 @@
/*
* Copyright 2011 Manuel Munz <freifunk at somakoma dot de>
* Licensed to the public under the Apache License 2.0.
*/
'use strict';
return L.Class.extend({
title: _('Disk Usage'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
return [{
title: "%H: Disk I/O operations on %pi",
vlabel: "Operations/s",
number_format: "%5.1lf%sOp/s",
data: {
types: [ "disk_ops" ],
sources: {
disk_ops: [ "read", "write" ],
},
options: {
disk_ops__read: {
title: "Reads",
color: "00ff00",
flip : false
},
disk_ops__write: {
title: "Writes",
color: "ff0000",
flip : true
}
}
}
}, {
title: "%H: Disk I/O bandwidth on %pi",
vlabel: "Bytes/s",
number_format: "%5.1lf%sB/s",
detail: true,
data: {
types: [ "disk_octets" ],
sources: {
disk_octets: [ "read", "write" ]
},
options: {
disk_octets__read: {
title: "Read",
color: "00ff00",
flip : false
},
disk_octets__write: {
title: "Write",
color: "ff0000",
flip : true
}
}
}
}];
}
});

View file

@ -0,0 +1,79 @@
/*
* Copyright 2011 Manuel Munz <freifunk at somakoma dot de>
* Licensed to the public under the Apache License 2.0.
*/
'use strict';
return L.Class.extend({
title: _('DNS'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
var traffic = {
title: "%H: DNS traffic", vlabel: "Bit/s",
data: {
sources: {
dns_octets: [ "queries", "responses" ]
},
options: {
dns_octets__responses: {
total: true,
color: "00ff00",
title: "Responses"
},
dns_octets__queries: {
total: true,
color: "0000ff",
title: "Queries"
}
}
}
};
var opcode_query = {
title: "%H: DNS Opcode Query", vlabel: "Queries/s",
data: {
instances: {
dns_opcode: [ "Query" ]
},
options: {
dns_opcode_Query_value: {
total: true,
color: "0000ff",
title: "Queries/s"
}
}
}
};
var qtype = {
title: "%H: DNS QType", vlabel: "Queries/s",
data: {
sources: { dns_qtype: [ "" ] },
options: {
dns_qtype_AAAA_ : { title: "AAAA", noarea: true, total: true },
dns_qtype_A_ : { title: "A", noarea: true, total: true },
dns_qtype_A6_ : { title: "A6", noarea: true, total: true },
dns_qtype_TXT_ : { title: "TXT", noarea: true, total: true },
dns_qtype_MX_ : { title: "MX", noarea: true, total: true },
dns_qtype_NS_ : { title: "NS", noarea: true, total: true },
dns_qtype_ANY_ : { title: "ANY", noarea: true, total: true },
dns_qtype_CNAME_: { title: "CNAME", noarea: true, total: true },
dns_qtype_SOA_ : { title: "SOA", noarea: true, total: true },
dns_qtype_SRV_ : { title: "SRV", noarea: true, total: true },
dns_qtype_PTR_ : { title: "PTR", noarea: true, total: true },
dns_qtype_RP_ : { title: "RP", noarea: true, total: true },
dns_qtype_MAILB_: { title: "MAILB", noarea: true, total: true },
dns_qtype_IXFR_ : { title: "IXFR", noarea: true, total: true },
dns_qtype_HINFO_: { title: "HINFO", noarea: true, total: true },
},
}
};
return [ traffic, opcode_query, qtype ];
}
});

View file

@ -0,0 +1,22 @@
/*
* Copyright 2015 Hannu Nyman <hannu.nyman@iki.fi>
* Licensed to the public under the Apache License 2.0
*/
'use strict';
return L.Class.extend({
title: _('Entropy'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
return {
title: "%H: Available entropy",
vlabel: "bits",
number_format: "%4.0lf",
data: {
types: [ "entropy" ],
options: { entropy: { title: "Entropy %di" } }
}
};
}
});

View file

@ -0,0 +1,110 @@
/* Licensed to the public under the Apache License 2.0. */
'use strict';
return L.Class.extend({
title: _('Interfaces'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
/*
* traffic diagram
*/
var traffic = {
/* draw this diagram for each plugin instance */
per_instance: true,
title: "%H: Transfer on %pi",
vlabel: "Bytes/s",
/* diagram data description */
data: {
/* defined sources for data types, if omitted assume a single DS named "value" (optional) */
sources: {
if_octets: [ "tx", "rx" ]
},
/* special options for single data lines */
options: {
if_octets__tx: {
total: true, /* report total amount of bytes */
color: "00ff00", /* tx is green */
title: "Bytes (TX)"
},
if_octets__rx: {
flip : true, /* flip rx line */
total: true, /* report total amount of bytes */
color: "0000ff", /* rx is blue */
title: "Bytes (RX)"
}
}
}
};
/*
* packet diagram
*/
var packets = {
/* draw this diagram for each plugin instance */
per_instance: true,
title: "%H: Packets on %pi",
vlabel: "Packets/s",
/* diagram data description */
data: {
/* data type order */
types: [ "if_packets", "if_errors" ],
/* defined sources for data types */
sources: {
if_packets: [ "tx", "rx" ],
if_errors : [ "tx", "rx" ]
},
/* special options for single data lines */
options: {
/* processed packets (tx DS) */
if_packets__tx: {
weight : 1,
overlay: true, /* don't summarize */
total : true, /* report total amount of bytes */
color : "00ff00", /* processed tx is green */
title : "Processed (TX)"
},
/* processed packets (rx DS) */
if_packets__rx: {
weight : 2,
overlay: true, /* don't summarize */
flip : true, /* flip rx line */
total : true, /* report total amount of bytes */
color : "0000ff", /* processed rx is blue */
title : "Processed (RX)"
},
/* packet errors (tx DS) */
if_errors__tx: {
weight : 0,
overlay: true, /* don't summarize */
total : true, /* report total amount of packets */
color : "ff5500", /* tx errors are orange */
title : "Errors (TX)"
},
/* packet errors (rx DS) */
if_errors__rx: {
weight : 3,
overlay: true, /* don't summarize */
flip : true, /* flip rx line */
total : true, /* report total amount of packets */
color : "ff0000", /* rx errors are red */
title : "Errors (RX)"
}
}
}
};
return [ traffic, packets ];
}
});

View file

@ -0,0 +1,39 @@
/* Licensed to the public under the Apache License 2.0. */
'use strict';
return L.Class.extend({
title: _('Firewall (IPv6)'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
return [{
title: "%H: Firewall: Processed bytes in %pi",
vlabel: "Bytes/s",
number_format: "%5.1lf%sB/s",
totals_format: "%5.1lf%sB",
data: {
types: [ "ipt_bytes" ],
options: {
ipt_bytes: {
total: true,
title: "%di"
}
}
}
}, {
title: "%H: Firewall: Processed packets in %pi",
vlabel: "Packets/s",
number_format: "%5.1lf P/s",
totals_format: "%5.1lf%s",
data: {
types: [ "ipt_packets" ],
options: {
ipt_packets: {
total: true,
title: "%di"
}
}
}
}];
}
});

View file

@ -0,0 +1,39 @@
/* Licensed to the public under the Apache License 2.0. */
'use strict';
return L.Class.extend({
title: _('Firewall'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
return [{
title: "%H: Firewall: Processed bytes in %pi",
vlabel: "Bytes/s",
number_format: "%5.1lf%sB/s",
totals_format: "%5.1lf%sB",
data: {
types: [ "ipt_bytes" ],
options: {
ipt_bytes: {
total: true,
title: "%di"
}
}
}
}, {
title: "%H: Firewall: Processed packets in %pi",
vlabel: "Packets/s",
number_format: "%5.1lf P/s",
totals_format: "%5.1lf%s",
data: {
types: [ "ipt_packets" ],
options: {
ipt_packets: {
total: true,
title: "%di"
}
}
}
}];
}
});

View file

@ -0,0 +1,19 @@
/* Licensed to the public under the Apache License 2.0. */
'use strict';
return L.Class.extend({
title: _('Interrupts'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
return {
title: "%H: Interrupts", vlabel: "Issues/s",
number_format: "%5.0lf", data: {
types: [ "irq" ],
options: {
irq: { title: "IRQ %di", noarea: true }
}
}
};
}
});

View file

@ -0,0 +1,95 @@
/* Licensed to the public under the Apache License 2.0. */
'use strict';
return L.Class.extend({
title: _('Wireless'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
/*
* signal/noise diagram
*/
var snr = {
title: "%H: Signal and noise on %pi",
detail: true,
vlabel: "dBm",
number_format: "%5.1lf dBm",
data: {
types: [ "signal_power", "signal_noise" ],
options: {
signal_power: {
title : "Signal",
overlay: true,
color : "0000ff"
},
signal_noise: {
title : "Noise",
overlay: true,
color : "ff0000"
}
}
}
};
/*
* signal quality diagram
*/
var quality = {
title: "%H: Signal quality on %pi",
vlabel: "Quality",
number_format: "%3.0lf",
data: {
types: [ "signal_quality" ],
options: {
signal_quality: {
title : "Quality",
noarea: true,
color : "0000ff"
}
}
}
};
/*
* phy rate diagram
*/
var bitrate = {
title: "%H: Average phy rate on %pi",
detail: true,
vlabel: "MBit/s",
number_format: "%5.1lf%sBit/s",
data: {
types: [ "bitrate" ],
options: {
bitrate: {
title: "Rate",
color: "00ff00"
}
}
}
};
/*
* associated stations
*/
var stations = {
title: "%H: Associated stations on %pi",
detail: true,
vlabel: "Stations",
y_min: "0",
alt_autoscale_max: true,
number_format: "%3.0lf",
data: {
types: [ "stations" ],
options: {
stations: {
title: "Stations",
color: "0000ff"
}
}
}
};
return [ quality, snr, bitrate, stations ];
}
});

View file

@ -0,0 +1,41 @@
/* Licensed to the public under the Apache License 2.0. */
'use strict';
return L.Class.extend({
title: _('System Load'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
return {
title: "%H: Load", vlabel: "Load",
y_min: "0",
units_exponent: "0",
number_format: "%5.2lf", data: {
sources: {
load: [ "shortterm", "midterm", "longterm" ]
},
options: {
load__shortterm: {
color: "ff0000",
title: "1 minute",
noarea: true,
weight: 3
},
load__midterm: {
color: "ff6600",
title: "5 minutes",
overlay: true,
weight: 1
},
load__longterm: {
color: "ffaa00",
title: "15 minutes",
overlay: true,
weight: 2
}
}
}
};
}
});

View file

@ -0,0 +1,97 @@
/*
* Copyright 2011 Manuel Munz <freifunk at somakoma dot de>
* Licensed to the public under the Apache License 2.0.
*/
'use strict';
return L.Class.extend({
title: _('Memory'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
var p = [];
var memory = {
title: "%H: Memory usage",
vlabel: "MB",
number_format: "%5.1lf%s",
y_min: "0",
alt_autoscale_max: true,
data: {
instances: {
memory: [
"free",
"buffered",
"cached",
"used"
]
},
options: {
memory_buffered: {
color: "0000ff",
title: "Buffered"
},
memory_cached: {
color: "ff00ff",
title: "Cached"
},
memory_used: {
color: "ff0000",
title: "Used"
},
memory_free: {
color: "00ff00",
title: "Free"
}
}
}
};
var percent = {
title: "%H: Memory usage",
vlabel: "Percent",
number_format: "%5.1lf%%",
y_min: "0",
alt_autoscale_max: true,
data: {
instances: {
percent: [
"free",
"buffered",
"cached",
"used"
]
},
options: {
percent_buffered: {
color: "0000ff",
title: "Buffered"
},
percent_cached: {
color: "ff00ff",
title: "Cached"
},
percent_used: {
color: "ff0000",
title: "Used"
},
percent_free: {
color: "00ff00",
title: "Free"
}
}
}
};
var types = graph.dataTypes(host, plugin, plugin_instance);
for (var i = 0; i < types.length; i++)
if (types[i] == 'percent')
p.push(percent);
else if (types[i] == 'memory')
p.push(memory);
return p;
}
});

View file

@ -0,0 +1,208 @@
/* Licensed to the public under the Apache License 2.0. */
'use strict';
return L.Class.extend({
title: _('Netlink'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
/*
* traffic diagram
*/
var traffic = {
title: "%H: Netlink - Transfer on %pi",
vlabel: "Bytes/s",
/* diagram data description */
data: {
/* defined sources for data types, if omitted assume a single DS named "value" (optional) */
sources: {
if_octets: [ "tx", "rx" ]
},
/* special options for single data lines */
options: {
if_octets__tx: {
title: "Bytes (TX)",
total: true, /* report total amount of bytes */
color: "00ff00" /* tx is green */
},
if_octets__rx: {
title: "Bytes (RX)",
flip : true, /* flip rx line */
total: true, /* report total amount of bytes */
color: "0000ff" /* rx is blue */
}
}
}
};
/*
* packet diagram
*/
var packets = {
title: "%H: Netlink - Packets on %pi",
vlabel: "Packets/s", detail: true,
/* diagram data description */
data: {
/* data type order */
types: [ "if_packets", "if_dropped", "if_errors" ],
/* defined sources for data types */
sources: {
if_packets: [ "tx", "rx" ],
if_dropped: [ "tx", "rx" ],
if_errors : [ "tx", "rx" ]
},
/* special options for single data lines */
options: {
/* processed packets (tx DS) */
if_packets__tx: {
weight : 2,
title : "Total (TX)",
overlay: true, /* don't summarize */
total : true, /* report total amount of bytes */
color : "00ff00" /* processed tx is green */
},
/* processed packets (rx DS) */
if_packets__rx: {
weight : 3,
title : "Total (RX)",
overlay: true, /* don't summarize */
flip : true, /* flip rx line */
total : true, /* report total amount of bytes */
color : "0000ff" /* processed rx is blue */
},
/* dropped packets (tx DS) */
if_dropped__tx: {
weight : 1,
title : "Dropped (TX)",
overlay: true, /* don't summarize */
total : true, /* report total amount of bytes */
color : "660055" /* dropped tx is ... dunno ;) */
},
/* dropped packets (rx DS) */
if_dropped__rx: {
weight : 4,
title : "Dropped (RX)",
overlay: true, /* don't summarize */
flip : true, /* flip rx line */
total : true, /* report total amount of bytes */
color : "ff00ff" /* dropped rx is violett */
},
/* packet errors (tx DS) */
if_errors__tx: {
weight : 0,
title : "Errors (TX)",
overlay: true, /* don't summarize */
total : true, /* report total amount of packets */
color : "ff5500" /* tx errors are orange */
},
/* packet errors (rx DS) */
if_errors__rx: {
weight : 5,
title : "Errors (RX)",
overlay: true, /* don't summarize */
flip : true, /* flip rx line */
total : true, /* report total amount of packets */
color : "ff0000" /* rx errors are red */
}
}
}
};
/*
* multicast diagram
*/
var multicast = {
title: "%H: Netlink - Multicast on %pi",
vlabel: "Packets/s", detail: true,
/* diagram data description */
data: {
/* data type order */
types: [ "if_multicast" ],
/* special options for single data lines */
options: {
/* multicast packets */
if_multicast: {
title: "Packets",
total: true, /* report total amount of packets */
color: "0000ff" /* multicast is blue */
}
}
}
};
/*
* collision diagram
*/
var collisions = {
title: "%H: Netlink - Collisions on %pi",
vlabel: "Collisions/s", detail: true,
/* diagram data description */
data: {
/* data type order */
types: [ "if_collisions" ],
/* special options for single data lines */
options: {
/* collision rate */
if_collisions: {
title: "Collisions",
total: true, /* report total amount of packets */
color: "ff0000" /* collsions are red */
}
}
}
};
/*
* error diagram
*/
var errors = {
title: "%H: Netlink - Errors on %pi",
vlabel: "Errors/s", detail: true,
/* diagram data description */
data: {
/* data type order */
types: [ "if_tx_errors", "if_rx_errors" ],
/* data type instances */
instances: {
if_tx_errors: [ "aborted", "carrier", "fifo", "heartbeat", "window" ],
if_rx_errors: [ "length", "missed", "over", "crc", "fifo", "frame" ]
},
/* special options for single data lines */
options: {
if_tx_errors_aborted_value : { total: true, color: "ffff00", title: "Aborted (TX)" },
if_tx_errors_carrier_value : { total: true, color: "ffcc00", title: "Carrier (TX)" },
if_tx_errors_fifo_value : { total: true, color: "ff9900", title: "Fifo (TX)" },
if_tx_errors_heartbeat_value: { total: true, color: "ff6600", title: "Heartbeat (TX)" },
if_tx_errors_window_value : { total: true, color: "ff3300", title: "Window (TX)" },
if_rx_errors_length_value : { flip: true, total: true, color: "ff0000", title: "Length (RX)" },
if_rx_errors_missed_value : { flip: true, total: true, color: "ff0033", title: "Missed (RX)" },
if_rx_errors_over_value : { flip: true, total: true, color: "ff0066", title: "Over (RX)" },
if_rx_errors_crc_value : { flip: true, total: true, color: "ff0099", title: "CRC (RX)" },
if_rx_errors_fifo_value : { flip: true, total: true, color: "ff00cc", title: "Fifo (RX)" },
if_rx_errors_frame_value : { flip: true, total: true, color: "ff00ff", title: "Frame (RX)" }
}
}
};
return [ traffic, packets, multicast, collisions, errors ];
}
});

View file

@ -0,0 +1,130 @@
/* Licensed to the public under the Apache License 2.0. */
'use strict';
return L.Class.extend({
title: _('UPS'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
var voltages_ac = {
title: "%H: AC voltages on UPS \"%pi\"",
vlabel: "V",
number_format: "%5.1lfV",
data: {
instances: {
voltage: [ "input", "output" ]
},
options: {
voltage_output : { color: "00e000", title: "Output voltage", noarea: true, overlay: true },
voltage_input : { color: "ffb000", title: "Input voltage", noarea: true, overlay: true }
}
}
};
var voltages_dc = {
title: "%H: Battery voltage on UPS \"%pi\"",
vlabel: "V",
number_format: "%5.1lfV",
data: {
instances: {
voltage: [ "battery" ]
},
options: {
voltage: { color: "0000ff", title: "Battery voltage", noarea: true, overlay: true }
}
}
};
var currents = {
title: "%H: Current on UPS \"%pi\"",
vlabel: "A",
number_format: "%5.3lfA",
data: {
instances: {
current: [ "battery", "output" ]
},
options: {
current_output : { color: "00e000", title: "Output current", noarea: true, overlay: true },
current_battery: { color: "0000ff", title: "Battery current", noarea: true, overlay: true }
}
}
};
var percentage = {
title: "%H: Battery charge/load on UPS \"%pi\"",
vlabel: "Percent",
y_min: "0",
y_max: "100",
number_format: "%5.1lf%%",
data: {
instances: {
percent: [ "charge", "load" ]
},
options: {
percent_charge: { color: "00ff00", title: "Charge level", noarea: true, overlay: true },
percent_load: { color: "ff0000", title: "Load", noarea: true, overlay: true }
}
}
};
/* Note: This is in ISO8859-1 for rrdtool. Welcome to the 20th century. */
var temperature = {
title: "%H: Battery temperature on UPS \"%pi\"",
vlabel: "\u00b0C",
number_format: "%5.1lf\u00b0C",
data: {
instances: {
temperature: "battery"
},
options: {
temperature_battery: { color: "ffb000", title: "Battery temperature", noarea: true }
}
}
};
var timeleft = {
title: "%H: Time left on UPS \"%pi\"",
vlabel: "Minutes",
number_format: "%.1lfm",
data: {
instances: {
timeleft: [ "battery" ]
},
options: {
timeleft_battery: { color: "0000ff", title: "Time left", transform_rpn: "60,/", noarea: true }
}
}
};
var power = {
title: "%H: Power on UPS \"%pi\"",
vlabel: "Power",
number_format: "%5.1lf%%",
data: {
instances: {
power: [ "ups" ]
},
options: {
power_ups: { color: "00ff00", title: "Power level" }
}
}
};
var frequencies = {
title: "%H: Frequencies on UPS \"%pi\"",
vlabel: "Hz",
number_format: "%5.1lfHz",
data: {
instances: {
frequency: [ "input", "output" ]
},
options: {
frequency_output : { color: "00e000", title: "Output frequency", noarea: true, overlay: true },
frequency_input : { color: "ffb000", title: "Input frequency", noarea: true, overlay: true }
}
}
};
return [ voltages_ac, voltages_dc, currents, percentage, temperature, timeleft, power, frequencies ];
}
});

View file

@ -0,0 +1,126 @@
/*
* Copyright 2011 Manuel Munz <freifunk at somakoma dot de>
* Licensed to the public under the Apache License 2.0.
*/
'use strict';
return L.Class.extend({
title: _('OLSRd'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
var g = [];
if (plugin_instance == "routes") {
g.push({
/* diagram data description */
title: "%H: Total amount of OLSR routes", vlabel: "n",
number_format: "%5.0lf", data: {
types: [ "routes" ],
options: {
routes: {
color: "ff0000",
title: "Total number of routes"
}
}
}
}, {
title: "%H: Average route ETX", vlabel: "ETX", detail: true,
number_format: "%5.1lf", data: {
instances: [ "average" ], /* falls es irgendwann mal welche pro ip gibt, wie bei links, dann werden die hier excludiert */
types: [ "route_etx" ],
options: {
route_etx: {
title: "Average route ETX"
}
}
}
}, {
title: "%H: Average route metric", vlabel: "metric", detail: true,
number_format: "%5.1lf", data: {
instances: [ "average" ], /* falls es irgendwann mal welche pro ip gibt, wie bei links, dann werden die hier excludiert */
types: [ "route_metric" ],
options: {
route_metric: {
title: "Average route metric"
}
}
}
});
}
else if (plugin_instance == "links") {
g.push({
/* diagram data description */
title: "%H: Total amount of OLSR neighbours", vlabel: "n",
number_format: "%5.0lf", data: {
instances: [ "" ],
types: [ "links" ],
options: {
links: {
color: "00ff00",
title: "Number of neighbours"
}
}
}
});
var instances = graph.dataInstances(host, plugin, plugin_instance, "signal_quality").sort();
/* define one diagram per host, containing the rx and lq values */
for (var i = 0; i < instances.length; i += 2) {
var dsn1 = "signal_quality_%s_value".format(instances[i].replace(/\W+/g, '_')),
dsn2 = "signal_quality_%s_value".format(instances[i+1].replace(/\W+/g, '_')),
host = instances[i].match(/^[^-]+-([^-]+)-.+$/),
host = host ? host[1] : 'avg',
opts = {};
opts[dsn1] = { color: "00ff00", title: "LQ (%s)".format(host) };
opts[dns2] = { color: "0000ff", title: "NLQ (%s)".format(host), flip: true };
g.push({
title: "%H: Signal Quality (%s)".format(host), vlabel: "ETX",
number_format: "%5.2lf", detail: true,
data: {
types: [ "signal_quality" ],
instances: {
signal_quality: [ instances[i], instances[i+1] ],
},
options: opts
}
});
}
}
else if (plugin_instance == "topology") {
g.push({
title: "%H: Total amount of OLSR links", vlabel: "n",
number_format: "%5.0lf", data: {
instances: [ "" ],
types: [ "links" ],
options: {
links: {
color: "0000ff",
title: "Total number of links"
}
}
}
}, {
title: "%H: Average signal quality", vlabel: "n",
number_format: "%5.2lf", detail: true,
data: {
instances: [ "average" ], /* exclude possible per-ip stuff */
types: [ "signal_quality" ],
options: {
signal_quality: {
color: "0000ff",
title: "Average signal quality"
}
}
}
});
}
return g;
}
});

View file

@ -0,0 +1,51 @@
/* Licensed to the public under the Apache License 2.0. */
'use strict';
return L.Class.extend({
title: _('OpenVPN'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
var inst = plugin_instance.replace(/^openvpn\.(.+)\.status$/, '$1');
return [
{
title: "%%H: OpenVPN \"%s\" - Traffic".format(inst),
vlabel: "Bytes/s",
data: {
instances: {
if_octets: [ "traffic", "overhead" ]
},
sources: {
if_octets: [ "tx", "rx" ]
},
options: {
if_octets_traffic_tx : { weight: 0, title: "Bytes (TX)", total: true, color: "00ff00" },
if_octets_overhead_tx: { weight: 1, title: "Overhead (TX)", total: true, color: "ff9900" },
if_octets_overhead_rx: { weight: 2, title: "Overhead (RX)", total: true, flip: true, color: "ff00ff" },
if_octets_traffic_rx : { weight: 3, title: "Bytes (RX)", total: true, flip: true, color: "0000ff" }
}
}
},
{
title: "%%H: OpenVPN \"%s\" - Compression".format(inst),
vlabel: "Bytes/s",
data: {
instances: {
compression: [ "data_out", "data_in" ]
},
sources: {
compression: [ "uncompressed", "compressed" ]
},
options: {
compression_data_out_uncompressed: { weight: 0, title: "Uncompressed (TX)", total: true, color: "00ff00" },
compression_data_out_compressed : { weight: 1, title: "Compressed (TX)", total: true, color: "008800" },
compression_data_in_compressed : { weight: 2, title: "Compressed (RX)", total: true, flip: true, color: "000088" },
compression_data_in_uncompressed : { weight: 3, title: "Uncompressed (RX)", total: true, flip: true, color: "0000ff" }
}
}
}
];
}
});

View file

@ -0,0 +1,62 @@
/* Licensed to the public under the Apache License 2.0. */
'use strict';
return L.Class.extend({
title: _('Ping'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
var ping = {
title: "%H: ICMP Round Trip Time",
vlabel: "ms",
number_format: "%5.1lf ms",
data: {
sources: {
ping: [ "value" ]
},
options: {
ping__value: {
noarea: true,
overlay: true,
title: "%di"
}
}
}
};
var droprate = {
title: "%H: ICMP Drop Rate",
vlabel: "%",
number_format: "%5.2lf %%",
data: {
types: [ "ping_droprate" ],
options: {
ping_droprate: {
noarea: true,
overlay: true,
title: "%di",
transform_rpn: "100,*"
}
}
}
};
var stddev = {
title: "%H: ICMP Standard Deviation",
vlabel: "ms",
number_format: "%5.1lf ms",
data: {
types: [ "ping_stddev" ],
options: {
ping_stddev: {
noarea: true,
overlay: true,
title: "%di"
}
}
}
};
return [ ping, droprate, stddev ];
}
});

View file

@ -0,0 +1,120 @@
/* Licensed to the public under the Apache License 2.0. */
'use strict';
return L.Class.extend({
title: _('Processes'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
if (plugin_instance == "") {
return {
title: "%H: Processes",
vlabel: "Processes/s",
data: {
instances: {
ps_state: [
"sleeping", "running", "paging",
"blocked", "stopped", "zombies"
]
},
options: {
ps_state_sleeping: { color: "0000ff", title: "Sleeping" },
ps_state_running : { color: "008000", title: "Running" },
ps_state_paging : { color: "ffff00", title: "Paging" },
ps_state_blocked : { color: "ff5000", title: "Blocked" },
ps_state_stopped : { color: "555555", title: "Stopped" },
ps_state_zombies : { color: "ff0000", title: "Zombies" }
}
}
};
}
else {
return [
{
title: "%H: CPU time used by %pi",
vlabel: "Jiffies",
data: {
sources: {
ps_cputime: [ "syst", "user" ]
},
options: {
ps_cputime__user: {
color : "0000ff",
title : "User",
overlay: true
},
ps_cputime__syst: {
color : "ff0000",
title : "System",
overlay: true
}
}
}
},
{
title: "%H: Threads and processes belonging to %pi",
vlabel: "Count",
detail: true,
data: {
sources: {
ps_count: [ "threads", "processes" ]
},
options: {
ps_count__threads : { color: "00ff00", title: "Threads" },
ps_count__processes: { color: "0000bb", title: "Processes" }
}
}
},
{
title: "%H: Page faults in %pi",
vlabel: "Page faults",
detail: true,
data: {
sources: {
ps_pagefaults: [ "minflt", "majflt" ]
},
options: {
ps_pagefaults__minflt: { color: "0000ff", title: "Minor" },
ps_pagefaults__majflt: { color: "ff0000", title: "Major" }
}
}
},
{
title: "%H: Resident segment size (RSS) of %pi",
vlabel: "Bytes",
detail: true,
number_format: "%5.1lf%sB",
data: {
types: [ "ps_rss" ],
options: {
ps_rss: { color: "0000ff", title: "Resident segment" }
}
}
},
{
title: "%H: Virtual memory size (VSZ) of %pi",
vlabel: "Bytes",
detail: true,
number_format: "%5.1lf%sB",
data: {
types: [ "ps_vm" ],
options: {
ps_vm: { color: "0000ff", title: "Virtual memory" }
}
}
}
];
}
}
});

View file

@ -0,0 +1,25 @@
/* Licensed to the public under the Apache License 2.0. */
'use strict';
return L.Class.extend({
title: _('Sensors'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
return {
per_instance: true,
title: "%H: %pi - %di",
vlabel: "\xb0C",
number_format: "%4.1lf\xb0C",
data: {
types: [ "temperature" ],
options: {
temperature__value: {
color: "ff0000",
title: "Temperature"
}
}
}
};
}
});

View file

@ -0,0 +1,29 @@
/*
* Copyright 2013 Freifunk Augsburg / Michael Wendland <michael@michiwend.com>
* Licensed to the public under the Apache License 2.0.
*/
'use strict';
return L.Class.extend({
title: _('Splash Leases'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
return {
title: "%H: Splash Leases",
vlabel: "Active Clients",
y_min: "0",
number_format: "%5.1lf",
data: {
sources: {
splash_leases: [ "leased", "whitelisted", "blacklisted" ]
},
options: {
splash_leases__leased : { color: "00CC00", title: "Leased", overlay: false },
splash_leases__whitelisted: { color: "0000FF", title: "Whitelisted", overlay: false },
splash_leases__blacklisted: { color: "FF0000", title: "Blacklisted", overlay: false }
}
}
};
}
});

View file

@ -0,0 +1,28 @@
/* Licensed to the public under the Apache License 2.0. */
'use strict';
return L.Class.extend({
title: _('TCP Connections'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
return {
title: "%H: TCP connections to port %pi",
vlabel: "Connections/s",
number_format: "%5.0lf",
data: {
types: [ "tcp_connections" ],
instances: {
tcp_connections: [
"SYN_SENT", "SYN_RECV", "LISTEN", "ESTABLISHED",
"LAST_ACK", "TIME_WAIT", "CLOSING", "CLOSE_WAIT",
"CLOSED", "FIN_WAIT1", "FIN_WAIT2"
],
options: {
load__ESTABLISHED: { title: "%di", noarea: true }
}
}
}
};
}
});

View file

@ -0,0 +1,26 @@
/* Licensed to the public under the Apache License 2.0. */
'use strict';
return L.Class.extend({
title: _('Thermal'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
return {
title: "%H: Temperature of %pi",
alt_autoscale: true,
vlabel: "Celsius",
number_format: "%3.1lf%s",
data: {
types: [ "temperature" ],
options: {
temperature: {
color: "ff0000",
title: "Temperature",
noarea: true
}
}
}
};
}
});

View file

@ -0,0 +1,32 @@
/*
Copyright 2013 Thomas Endt <tmo26@gmx.de>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
*/
'use strict';
return L.Class.extend({
title: _('Uptime'),
rrdargs: function(graph, host, plugin, plugin_instance, dtype) {
return {
title: "%H: Uptime",
vlabel: "seconds",
number_format: "%5.0lf%s",
data: {
types: [ "uptime" ],
options: {
uptime: {
title: "Uptime %di",
noarea: true
}
}
}
};
}
});

View file

@ -0,0 +1,205 @@
'use strict';
'require ui';
'require uci';
'require statistics.rrdtool as rrdtool';
var pollFn = null,
activePlugin = null,
activeInstance = null;
return L.view.extend({
load: function() {
return rrdtool.load();
},
updatePluginTab: function(host, span, time, ev) {
var container = ev.target,
plugin = ev.detail.tab,
plugin_instances = rrdtool.pluginInstances(host.value, plugin);
activePlugin = plugin;
L.dom.content(container, [
E('p', {}, [
E('em', { 'class': 'spinning' }, [ _('Loading data…') ])
])
]);
Promise.all(plugin_instances.map(function(instance) {
return rrdtool.render(plugin, instance, false, host.value, span.value, Math.max(200, container.offsetWidth - 100));
})).then(function(blobs) {
var multiple = blobs.length > 1;
L.dom.content(container, E('div', {}, blobs.map(function(blobs, i) {
var plugin_instance = plugin_instances[i];
return E('div', {
'class': 'center',
'data-tab': multiple ? i : null,
'data-tab-title': multiple ? '%s: %s'.format(rrdtool.pluginTitle(plugin), plugin_instances[i]) : null,
'data-plugin': plugin,
'data-plugin-instance': plugin_instances[i],
'cbi-tab-active': function(ev) { activeInstance = ev.target.getAttribute('data-plugin-instance') }
}, blobs.map(function(blob) {
return E('img', {
'src': URL.createObjectURL(new Blob([blob], { type: 'image/png' }))
});
}));
})));
if (multiple)
ui.tabs.initTabGroup(container.lastElementChild.childNodes);
else
activeInstance = plugin_instances[0];
});
},
updateGraphs: function(host, span, time, container, ev) {
var plugin_names = rrdtool.pluginNames(host.value);
container.querySelectorAll('img').forEach(function(img) {
URL.revokeObjectURL(img.src);
});
L.dom.content(container, null);
if (container.hasAttribute('data-initialized')) {
container.removeAttribute('data-initialized');
container.parentNode.removeChild(container.previousElementSibling);
}
for (var i = 0; i < plugin_names.length; i++) {
if (!rrdtool.hasDefinition(plugin_names[i]))
continue;
container.appendChild(E('div', {
'data-tab': plugin_names[i],
'data-tab-title': rrdtool.pluginTitle(plugin_names[i]),
'cbi-tab-active': L.bind(this.updatePluginTab, this, host, span, time)
}, [
E('p', {}, [
E('em', { 'class': 'spinning' }, [ _('Loading data…') ])
])
]));
}
ui.tabs.initTabGroup(container.childNodes);
},
refreshGraphs: function(host, span, time, container) {
var div = document.querySelector('[data-plugin="%s"][data-plugin-instance="%s"]'.format(activePlugin, activeInstance || ''));
return rrdtool.render(activePlugin, activeInstance || '', false, host.value, span.value, Math.max(200, container.offsetWidth - 100)).then(function(blobs) {
return Promise.all(blobs.map(function(blob) {
return new Promise(function(resolveFn, rejectFn) {
var img = E('img', { 'src': URL.createObjectURL(new Blob([blob], { type: 'image/png' })) });
img.onload = function(ev) { resolveFn(img) };
img.onerror = function(ev) { resolveFn(img) };
});
})).then(function(imgs) {
while (div.childNodes.length > imgs.length)
div.removeChild(div.lastElementChild);
for (var i = 0; i < imgs.length; i++) {
if (i < div.childNodes.length) {
URL.revokeObjectURL(div.childNodes[i].src);
div.childNodes[i].src = imgs[i].src;
}
else {
div.appendChild(E('img', { 'src': imgs[i].src }));
}
}
});
});
},
togglePolling: function(host, span, time, container, ev) {
var btn = ev.currentTarget;
if (pollFn) {
L.Poll.remove(pollFn);
pollFn = null;
}
if (time.value != '0') {
pollFn = L.bind(this.refreshGraphs, this, host, span, time, container);
L.Poll.add(pollFn, +time.value);
}
},
render: function() {
var hosts = rrdtool.hostInstances();
return hosts.length ? this.renderGraphs() : this.renderNoData();
},
renderNoData: function() {
ui.showModal(_('No RRD data found'), [
E('p', {}, _('There is no RRD data available yet to render graphs.')),
E('p', {}, _('You need to configure <em>collectd</em> to gather data into <em>.rrd</em> files.')),
E('div', { 'class': 'right' }, [
E('button', {
'class': 'cbi-button',
'click': function(ev) { location.href = 'collectd' }
}, [ _('Setup collectd') ])
])
]);
},
renderGraphs: function() {
var hostSel = E('select', { 'style': 'max-width:170px', 'data-name': 'host' }, rrdtool.hostInstances().map(function(host) {
return E('option', {
'selected': (rrdtool.opts.host == host) ? 'selected' : null
}, [ host ])
}));
var spanSel = E('select', { 'style': 'max-width:170px', 'data-name': 'timespan' }, L.toArray(uci.get('luci_statistics', 'collectd_rrdtool', 'RRATimespans')).map(function(span) {
return E('option', {
'selected': (rrdtool.opts.timespan == span) ? 'selected' : null
}, [ span ])
}));
var timeSel = E('select', { 'style': 'max-width:170px', 'data-name': 'refresh' }, [
E('option', { 'value': 0 }, [ _('Do not refresh') ]),
E('option', { 'value': 5 }, [ _('Every 5 seconds') ]),
E('option', { 'value': 30 }, [ _('Every 30 seconds') ]),
E('option', { 'value': 60 }, [ _('Every minute') ])
]);
var graphDiv = E('div', { 'data-name': 'graphs' });
var view = E([], [
E('h2', {}, [ _('Statistics') ]),
E('div', {}, [
E('div', {}, [
hostSel,
E('button', {
'class': 'cbi-button cbi-button-apply',
'click': ui.createHandlerFn(this, 'updateGraphs', hostSel, spanSel, timeSel, graphDiv, )
}, [ _('Display Host »') ]),
' ',
spanSel,
E('button', {
'class': 'cbi-button cbi-button-apply',
'click': ui.createHandlerFn(this, 'updateGraphs', hostSel, spanSel, timeSel, graphDiv)
}, [ _('Display timespan »') ]),
' ',
timeSel,
E('button', {
'class': 'cbi-button cbi-button-apply',
'click': ui.createHandlerFn(this, 'togglePolling', hostSel, spanSel, timeSel, graphDiv)
}, [ _('Apply interval »') ])
]),
E('hr'),
graphDiv
])
]);
requestAnimationFrame(L.bind(this.updateGraphs, this, hostSel, spanSel, timeSel, graphDiv));
return view;
},
handleSave: null,
handleSaveApply: null,
handleReset: null
});

View file

@ -1,132 +0,0 @@
-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
-- Copyright 2012 Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.controller.luci_statistics.luci_statistics", package.seeall)
function index()
require("nixio.fs")
require("luci.util")
require("luci.statistics.datatree")
-- create toplevel menu nodes
local st = entry({"admin", "statistics"}, template("admin_statistics/index"), _("Statistics"), 80)
st.index = true
entry({"admin", "statistics", "collectd"}, view("statistics/collectd"), _("Setup"), 20).subindex = true
-- output views
local page = entry( { "admin", "statistics", "graph" }, template("admin_statistics/index"), _("Graphs"), 10)
page.setuser = "nobody"
page.setgroup = "nogroup"
local vars = luci.http.formvalue(nil, true)
local span = vars.timespan or nil
local host = vars.host or nil
-- get rrd data tree
local tree = luci.statistics.datatree.Instance(host)
local _, plugin, idx
for _, plugin, idx in luci.util.vspairs( tree:plugins() ) do
-- get plugin instances
local instances = tree:plugin_instances( plugin )
-- load plugin menu entry from the description
local plugin_name = "luci.statistics.rrdtool.definitions." .. plugin
local stat, def = pcall( require, plugin_name )
if stat and def and type(def.item) == "function" then
entry(
{ "admin", "statistics", "graph", plugin },
call("statistics_render"), def.item(), idx
).query = { timespan = span , host = host }
end
-- if more then one instance is found then generate submenu
if #instances > 1 then
local _, inst, idx2
for _, inst, idx2 in luci.util.vspairs(instances) do
-- instance menu entry
entry(
{ "admin", "statistics", "graph", plugin, inst },
call("statistics_render"), inst, idx2
).query = { timespan = span , host = host }
end
end
end
end
function statistics_render()
require("luci.statistics.rrdtool")
require("luci.template")
require("luci.model.uci")
local vars = luci.http.formvalue()
local req = luci.dispatcher.context.request
local path = luci.dispatcher.context.path
local uci = luci.model.uci.cursor()
local spans = luci.util.split( uci:get( "luci_statistics", "collectd_rrdtool", "RRATimespans" ), "%s+", nil, true )
local span = vars.timespan or uci:get( "luci_statistics", "rrdtool", "default_timespan" ) or spans[1]
local host = vars.host or uci:get( "luci_statistics", "collectd", "Hostname" ) or luci.sys.hostname()
local opts = { host = vars.host }
local graph = luci.statistics.rrdtool.Graph( luci.util.parse_units( span ), opts )
local hosts = graph.tree:host_instances()
local is_index = false
local i, p, inst, idx
-- deliver image
if vars.img then
local l12 = require "luci.ltn12"
local png = io.open(graph.opts.imgpath .. "/" .. vars.img:gsub("%.+", "."), "r")
if png then
luci.http.prepare_content("image/png")
l12.pump.all(l12.source.file(png), luci.http.write)
end
return
end
local plugin, instances
local images = { }
-- find requested plugin and instance
for i, p in ipairs( luci.dispatcher.context.path ) do
if luci.dispatcher.context.path[i] == "graph" then
plugin = luci.dispatcher.context.path[i+1]
instances = { luci.dispatcher.context.path[i+2] }
end
end
-- no instance requested, find all instances
if #instances == 0 then
--instances = { graph.tree:plugin_instances( plugin )[1] }
instances = graph.tree:plugin_instances( plugin )
is_index = (#instances > 1)
-- index instance requested
elseif instances[1] == "-" then
instances[1] = ""
is_index = true
end
-- render graphs
for i, inst in luci.util.vspairs( instances ) do
for i, img in luci.util.vspairs( graph:render( plugin, inst, is_index ) ) do
table.insert( images, graph:strippngpath( img ) )
images[images[#images]] = inst
end
end
luci.template.render( "public_statistics/graph", {
images = images,
plugin = plugin,
timespans = spans,
current_timespan = span,
hosts = hosts,
current_host = host,
is_index = is_index
} )
end

View file

@ -1,205 +0,0 @@
-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.datatree", package.seeall)
local util = require("luci.util")
local sys = require("luci.sys")
local fs = require("nixio.fs")
local uci = require("luci.model.uci").cursor()
local sections = uci:get_all("luci_statistics")
Instance = util.class()
function Instance.__init__( self, host )
self._host = host or sys.hostname()
self._libdir = "/usr/lib/collectd"
self._rrddir = "/tmp/rrd"
if sections and sections.collectd then
self._host = host or sections.collectd.Hostname or sys.hostname()
self._libdir = sections.collectd.PluginDir or "/usr/lib/collectd"
end
if sections and sections.collectd_rrdtool then
self._rrddir = sections.collectd_rrdtool.DataDir or "/tmp/rrd"
end
self._libdir = self._libdir:gsub("/$","")
self._rrddir = self._rrddir:gsub("/$","")
self._plugins = { }
self:_scan()
end
function Instance._mkpath( self, plugin, pinstance )
local dir = self._rrddir .. "/" .. self._host
if type(plugin) == "string" and plugin:len() > 0 then
dir = dir .. "/" .. plugin
if type(pinstance) == "string" and pinstance:len() > 0 then
dir = dir .. "-" .. pinstance
end
end
return dir
end
function Instance._ls( self, ... )
local ditr = fs.dir(self:_mkpath(...))
if ditr then
local dirs = { }
while true do
local d = ditr()
if not d then break end
dirs[#dirs+1] = d
end
return dirs
end
end
function Instance._notzero( self, table )
for k in pairs(table) do
return true
end
return false
end
function Instance._scan( self )
local dirs = self:_ls()
if not dirs then
return
end
-- for i, plugin in ipairs( dirs ) do
-- if plugin:match("%w+.so") then
-- self._plugins[ plugin:gsub("%.so$", "") ] = { }
-- end
-- end
for _, dir in ipairs(dirs) do
if dir ~= "." and dir ~= ".." and
fs.stat(self:_mkpath(dir)).type == "dir"
then
local plugin = dir:gsub("%-.+$", "")
if not self._plugins[plugin] then
self._plugins[plugin] = { }
end
end
end
for plugin, instances in pairs( self._plugins ) do
local dirs = self:_ls()
if type(dirs) == "table" then
for i, dir in ipairs(dirs) do
if dir:find( plugin .. "%-" ) or dir == plugin then
local instance = ""
if dir ~= plugin then
instance = dir:gsub( plugin .. "%-", "", 1 )
end
instances[instance] = { }
end
end
end
for instance, data_instances in pairs( instances ) do
dirs = self:_ls(plugin, instance)
if type(dirs) == "table" then
for i, file in ipairs(dirs) do
if file:find("%.rrd") then
file = file:gsub("%.rrd","")
local data_type
local data_instance
if file:find("%-") then
data_type = file:gsub( "%-.+","" )
data_instance = file:gsub( "[^%-]-%-", "", 1 )
else
data_type = file
data_instance = ""
end
if not data_instances[data_type] then
data_instances[data_type] = { data_instance }
else
table.insert( data_instances[data_type], data_instance )
end
end
end
end
end
end
end
function Instance.plugins( self )
local rv = { }
for plugin, val in pairs( self._plugins ) do
if self:_notzero( val ) then
table.insert( rv, plugin )
end
end
return rv
end
function Instance.plugin_instances( self, plugin )
local rv = { }
for instance, val in pairs( self._plugins[plugin] ) do
table.insert( rv, instance )
end
return rv
end
function Instance.data_types( self, plugin, instance )
local rv = { }
local p = self._plugins[plugin]
if type(p) == "table" and type(p[instance]) == "table" then
for type, val in pairs(p[instance]) do
table.insert( rv, type )
end
end
return rv
end
function Instance.data_instances( self, plugin, instance, dtype )
local rv = { }
local p = self._plugins[plugin]
if type(p) == "table" and type(p[instance]) == "table" and type(p[instance][dtype]) == "table" then
for i, instance in ipairs(p[instance][dtype]) do
table.insert( rv, instance )
end
end
return rv
end
function Instance.host_instances( self )
local hosts_path = fs.glob(self._rrddir..'/*')
local hosts = { }
if hosts_path then
local path
for path in hosts_path do
hosts[#hosts+1] = fs.basename(path)
end
end
return hosts
end

View file

@ -1,79 +0,0 @@
-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.i18n", package.seeall)
local util = require("luci.util")
local i18n = require("luci.i18n")
Instance = util.class()
function Instance.__init__( self, graph )
self.i18n = i18n
self.graph = graph
end
function Instance._subst( self, str, val )
str = str:gsub( "%%H", self.graph.opts.host or "" )
str = str:gsub( "%%pn", val.plugin or "" )
str = str:gsub( "%%pi", val.pinst or "" )
str = str:gsub( "%%dt", val.dtype or "" )
str = str:gsub( "%%di", val.dinst or "" )
str = str:gsub( "%%ds", val.dsrc or "" )
return str
end
function Instance.title( self, plugin, pinst, dtype, dinst, user_title )
local title = user_title or
"p=%s/pi=%s/dt=%s/di=%s" % {
plugin,
(pinst and #pinst > 0) and pinst or "(nil)",
(dtype and #dtype > 0) and dtype or "(nil)",
(dinst and #dinst > 0) and dinst or "(nil)"
}
return self:_subst( title, {
plugin = plugin,
pinst = pinst,
dtype = dtype,
dinst = dinst
} )
end
function Instance.label( self, plugin, pinst, dtype, dinst, user_label )
local label = user_label or
"dt=%s/di=%s" % {
(dtype and #dtype > 0) and dtype or "(nil)",
(dinst and #dinst > 0) and dinst or "(nil)"
}
return self:_subst( label, {
plugin = plugin,
pinst = pinst,
dtype = dtype,
dinst = dinst
} )
end
function Instance.ds( self, source )
local label = source.title or
"dt=%s/di=%s/ds=%s" % {
(source.type and #source.type > 0) and source.type or "(nil)",
(source.instance and #source.instance > 0) and source.instance or "(nil)",
(source.ds and #source.ds > 0) and source.ds or "(nil)"
}
return self:_subst( label, {
dtype = source.type,
dinst = source.instance,
dsrc = source.ds
} ):gsub(":", "\\:")
end

View file

@ -1,581 +0,0 @@
-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool", package.seeall)
local tree = require("luci.statistics.datatree")
local colors = require("luci.statistics.rrdtool.colors")
local i18n = require("luci.statistics.i18n")
local uci = require("luci.model.uci").cursor()
local util = require("luci.util")
local sys = require("luci.sys")
local fs = require("nixio.fs")
Graph = util.class()
function Graph.__init__( self, timespan, opts )
opts = opts or { }
local sections = uci:get_all( "luci_statistics" )
-- options
opts.timespan = timespan or sections.rrdtool.default_timespan or 900
opts.rrasingle = opts.rrasingle or ( sections.collectd_rrdtool.RRASingle == "1" )
opts.rramax = opts.rramax or ( sections.collectd_rrdtool.RRAMax == "1" )
opts.host = opts.host or sections.collectd.Hostname or sys.hostname()
opts.width = opts.width or sections.rrdtool.image_width or 400
opts.height = opts.height or sections.rrdtool.image_height or 100
opts.rrdpath = opts.rrdpath or sections.collectd_rrdtool.DataDir or "/tmp/rrd"
opts.imgpath = opts.imgpath or sections.rrdtool.image_path or "/tmp/rrdimg"
opts.rrdpath = opts.rrdpath:gsub("/$","")
opts.imgpath = opts.imgpath:gsub("/$","")
-- helper classes
self.colors = colors.Instance()
self.tree = tree.Instance(opts.host)
self.i18n = i18n.Instance( self )
-- rrdtool default args
self.args = {
"-a", "PNG",
"-s", "NOW-" .. opts.timespan,
"-w", opts.width,
"-h", opts.height
}
-- store options
self.opts = opts
end
function Graph._mkpath( self, plugin, plugin_instance, dtype, dtype_instance )
local t = self.opts.host .. "/" .. plugin
if type(plugin_instance) == "string" and plugin_instance:len() > 0 then
t = t .. "-" .. plugin_instance
end
t = t .. "/" .. dtype
if type(dtype_instance) == "string" and dtype_instance:len() > 0 then
t = t .. "-" .. dtype_instance
end
return t
end
function Graph.mkrrdpath( self, ... )
return string.format( "%s/%s.rrd", self.opts.rrdpath, self:_mkpath( ... ):gsub("\\", "\\\\"):gsub(":", "\\:") )
end
function Graph.mkpngpath( self, ... )
return string.format( "%s/%s.%i.png", self.opts.imgpath, self:_mkpath( ... ), self.opts.timespan )
end
function Graph.strippngpath( self, path )
return path:sub( self.opts.imgpath:len() + 2 )
end
function Graph._forcelol( self, list )
if type(list[1]) ~= "table" then
return( { list } )
end
return( list )
end
function Graph._rrdtool( self, def, rrd )
-- prepare directory
local dir = def[1]:gsub("/[^/]+$","")
fs.mkdirr( dir )
-- construct commandline
local cmdline = { "rrdtool", "graph" }
-- copy default arguments to def stack
for i, opt in ipairs(self.args) do
table.insert( def, 1 + i, opt )
end
-- construct commandline from def stack
for i, opt in ipairs(def) do
opt = opt .. "" -- force string
if rrd then
opt = opt:gsub( "{file}", rrd )
end
cmdline[#cmdline+1] = util.shellquote(opt)
end
-- execute rrdtool
local rrdtool = io.popen(table.concat(cmdline, " "))
rrdtool:close()
end
function Graph._generic( self, opts, plugin, plugin_instance, dtype, index )
-- generated graph defs
local defs = { }
-- internal state variables
local _args = { }
local _sources = { }
local _stack_neg = { }
local _stack_pos = { }
local _longest_name = 0
local _has_totals = false
-- some convenient aliases
local _ti = table.insert
local _sf = string.format
-- local helper: append a string.format() formatted string to given table
function _tif( list, fmt, ... )
table.insert( list, string.format( fmt, ... ) )
end
-- local helper: create definitions for min, max, avg and create *_nnl (not null) variable from avg
function __def(source)
local inst = source.sname
local rrd = source.rrd:gsub(":", "\\:")
local ds = source.ds
if not ds or ds:len() == 0 then ds = "value" end
_tif( _args, "DEF:%s_avg_raw=%s:%s:AVERAGE", inst, rrd, ds )
_tif( _args, "CDEF:%s_avg=%s_avg_raw,%s", inst, inst, source.transform_rpn )
if not self.opts.rrasingle then
_tif( _args, "DEF:%s_min_raw=%s:%s:MIN", inst, rrd, ds )
_tif( _args, "CDEF:%s_min=%s_min_raw,%s", inst, inst, source.transform_rpn )
_tif( _args, "DEF:%s_max_raw=%s:%s:MAX", inst, rrd, ds )
_tif( _args, "CDEF:%s_max=%s_max_raw,%s", inst, inst, source.transform_rpn )
end
_tif( _args, "CDEF:%s_nnl=%s_avg,UN,0,%s_avg,IF", inst, inst, inst )
end
-- local helper: create cdefs depending on source options like flip and overlay
function __cdef(source)
local prev
-- find previous source, choose stack depending on flip state
if source.flip then
prev = _stack_neg[#_stack_neg]
else
prev = _stack_pos[#_stack_pos]
end
-- is first source in stack or overlay source: source_stk = source_nnl
if not prev or source.overlay then
if self.opts.rrasingle or not self.opts.rramax then
-- create cdef statement for cumulative stack (no NaNs) and also
-- for display (preserving NaN where no points should be displayed)
_tif( _args, "CDEF:%s_stk=%s_nnl", source.sname, source.sname )
_tif( _args, "CDEF:%s_plot=%s_avg", source.sname, source.sname )
else
-- create cdef statement for cumulative stack (no NaNs) and also
-- for display (preserving NaN where no points should be displayed)
_tif( _args, "CDEF:%s_stk=%s_nnl", source.sname, source.sname )
_tif( _args, "CDEF:%s_plot=%s_max", source.sname, source.sname )
end
-- is subsequent source without overlay: source_stk = source_nnl + previous_stk
else
if self.opts.rrasingle or not self.opts.rramax then
-- create cdef statement
_tif( _args, "CDEF:%s_stk=%s_nnl,%s_stk,+", source.sname, source.sname, prev )
_tif( _args, "CDEF:%s_plot=%s_avg,%s_stk,+", source.sname, source.sname, prev )
else
-- create cdef statement
_tif( _args, "CDEF:%s_stk=%s_nnl,%s_stk,+", source.sname, source.sname, prev )
_tif( _args, "CDEF:%s_plot=%s_max,%s_stk,+", source.sname, source.sname, prev )
end
end
-- create multiply by minus one cdef if flip is enabled
if source.flip then
-- create cdef statement: source_stk = source_stk * -1
_tif( _args, "CDEF:%s_neg=%s_plot,-1,*", source.sname, source.sname )
-- push to negative stack if overlay is disabled
if not source.overlay then
_ti( _stack_neg, source.sname )
end
-- no flipping, push to positive stack if overlay is disabled
elseif not source.overlay then
-- push to positive stack
_ti( _stack_pos, source.sname )
end
-- calculate total amount of data if requested
if source.total then
_tif( _args,
"CDEF:%s_avg_sample=%s_avg,UN,0,%s_avg,IF,sample_len,*",
source.sname, source.sname, source.sname
)
_tif( _args,
"CDEF:%s_avg_sum=PREV,UN,0,PREV,IF,%s_avg_sample,+",
source.sname, source.sname, source.sname
)
end
end
-- local helper: create cdefs required for calculating total values
function __cdef_totals()
if _has_totals then
_tif( _args, "CDEF:mytime=%s_avg,TIME,TIME,IF", _sources[1].sname )
_ti( _args, "CDEF:sample_len_raw=mytime,PREV(mytime),-" )
_ti( _args, "CDEF:sample_len=sample_len_raw,UN,0,sample_len_raw,IF" )
end
end
-- local helper: create line and area statements
function __line(source)
local line_color
local area_color
local legend
local var
-- find colors: try source, then opts.colors; fall back to random color
if type(source.color) == "string" then
line_color = source.color
area_color = self.colors:from_string( line_color )
elseif type(opts.colors[source.name:gsub("[^%w]","_")]) == "string" then
line_color = opts.colors[source.name:gsub("[^%w]","_")]
area_color = self.colors:from_string( line_color )
else
area_color = self.colors:random()
line_color = self.colors:to_string( area_color )
end
-- derive area background color from line color
area_color = self.colors:to_string( self.colors:faded( area_color ) )
-- choose source_plot or source_neg variable depending on flip state
if source.flip then
var = "neg"
else
var = "plot"
end
-- create legend
legend = _sf( "%-" .. _longest_name .. "s", source.title )
-- create area if not disabled
if not source.noarea then
_tif( _args, "AREA:%s_%s#%s", source.sname, var, area_color )
end
-- create line1 statement
_tif( _args, "LINE%d:%s_%s#%s:%s",
source.width or (source.noarea and 2 or 1),
source.sname, var, line_color, legend )
end
-- local helper: create gprint statements
function __gprint(source)
local numfmt = opts.number_format or "%6.1lf"
local totfmt = opts.totals_format or "%5.1lf%s"
-- don't include MIN if rrasingle is enabled
if not self.opts.rrasingle then
_tif( _args, "GPRINT:%s_min:MIN:\tMin\\: %s", source.sname, numfmt )
end
-- always include AVERAGE
_tif( _args, "GPRINT:%s_avg:AVERAGE:\tAvg\\: %s", source.sname, numfmt )
-- don't include MAX if rrasingle is enabled
if not self.opts.rrasingle then
_tif( _args, "GPRINT:%s_max:MAX:\tMax\\: %s", source.sname, numfmt )
end
-- include total count if requested else include LAST
if source.total then
_tif( _args, "GPRINT:%s_avg_sum:LAST:(ca. %s Total)\\l", source.sname, totfmt )
else
_tif( _args, "GPRINT:%s_avg:LAST:\tLast\\: %s\\l", source.sname, numfmt )
end
end
--
-- find all data sources
--
-- find data types
local data_types
if dtype then
data_types = { dtype }
else
data_types = opts.data.types or { }
end
if not ( dtype or opts.data.types ) then
if opts.data.instances then
for k, v in pairs(opts.data.instances) do
_ti( data_types, k )
end
elseif opts.data.sources then
for k, v in pairs(opts.data.sources) do
_ti( data_types, k )
end
end
end
-- iterate over data types
for i, dtype in ipairs(data_types) do
-- find instances
local data_instances
if not opts.per_instance then
if type(opts.data.instances) == "table" and type(opts.data.instances[dtype]) == "table" then
data_instances = opts.data.instances[dtype]
else
data_instances = self.tree:data_instances( plugin, plugin_instance, dtype )
end
end
if type(data_instances) ~= "table" or #data_instances == 0 then data_instances = { "" } end
-- iterate over data instances
for i, dinst in ipairs(data_instances) do
-- construct combined data type / instance name
local dname = dtype
if dinst:len() > 0 then
dname = dname .. "_" .. dinst
end
-- find sources
local data_sources = { "value" }
if type(opts.data.sources) == "table" then
if type(opts.data.sources[dname]) == "table" then
data_sources = opts.data.sources[dname]
elseif type(opts.data.sources[dtype]) == "table" then
data_sources = opts.data.sources[dtype]
end
end
-- iterate over data sources
for i, dsource in ipairs(data_sources) do
local dsname = dtype .. "_" .. dinst:gsub("[^%w]","_") .. "_" .. dsource
local altname = dtype .. "__" .. dsource
--assert(dtype ~= "ping", dsname .. " or " .. altname)
-- find datasource options
local dopts = { }
if type(opts.data.options) == "table" then
if type(opts.data.options[dsname]) == "table" then
dopts = opts.data.options[dsname]
elseif type(opts.data.options[altname]) == "table" then
dopts = opts.data.options[altname]
elseif type(opts.data.options[dname]) == "table" then
dopts = opts.data.options[dname]
elseif type(opts.data.options[dtype]) == "table" then
dopts = opts.data.options[dtype]
end
end
-- store values
_ti( _sources, {
rrd = dopts.rrd or self:mkrrdpath( plugin, plugin_instance, dtype, dinst ),
color = dopts.color or self.colors:to_string( self.colors:random() ),
flip = dopts.flip or false,
total = dopts.total or false,
overlay = dopts.overlay or false,
transform_rpn = dopts.transform_rpn or "0,+",
noarea = dopts.noarea or false,
title = dopts.title or nil,
weight = dopts.weight or
(dopts.negweight and -tonumber(dinst)) or
(dopts.posweight and tonumber(dinst)) or nil,
ds = dsource,
type = dtype,
instance = dinst,
index = #_sources + 1,
sname = ( #_sources + 1 ) .. dtype
} )
-- generate datasource title
_sources[#_sources].title = self.i18n:ds( _sources[#_sources] )
-- find longest name ...
if _sources[#_sources].title:len() > _longest_name then
_longest_name = _sources[#_sources].title:len()
end
-- has totals?
if _sources[#_sources].total then
_has_totals = true
end
end
end
end
--
-- construct diagrams
--
-- if per_instance is enabled then find all instances from the first datasource in diagram
-- if per_instance is disabled then use an empty pseudo instance and use model provided values
local instances = { "" }
if opts.per_instance then
instances = self.tree:data_instances( plugin, plugin_instance, _sources[1].type )
end
-- iterate over instances
for i, instance in ipairs(instances) do
-- store title and vlabel
_ti( _args, "-t" )
_ti( _args, self.i18n:title( plugin, plugin_instance, _sources[1].type, instance, opts.title ) )
_ti( _args, "-v" )
_ti( _args, self.i18n:label( plugin, plugin_instance, _sources[1].type, instance, opts.vlabel ) )
if opts.y_max then
_ti ( _args, "-u" )
_ti ( _args, opts.y_max )
end
if opts.y_min then
_ti ( _args, "-l" )
_ti ( _args, opts.y_min )
end
if opts.units_exponent then
_ti ( _args, "-X" )
_ti ( _args, opts.units_exponent )
end
if opts.alt_autoscale then
_ti ( _args, "-A" )
end
if opts.alt_autoscale_max then
_ti ( _args, "-M" )
end
-- store additional rrd options
if opts.rrdopts then
for i, o in ipairs(opts.rrdopts) do _ti( _args, o ) end
end
-- sort sources
table.sort(_sources, function(a, b)
local x = a.weight or a.index or 0
local y = b.weight or b.index or 0
return x < y
end)
-- define colors in order
if opts.ordercolor then
for i, source in ipairs(_sources) do
source.color = self.colors:defined(i)
end
end
-- create DEF statements for each instance
for i, source in ipairs(_sources) do
-- fixup properties for per instance mode...
if opts.per_instance then
source.instance = instance
source.rrd = self:mkrrdpath( plugin, plugin_instance, source.type, instance )
end
__def( source )
end
-- create CDEF required for calculating totals
__cdef_totals()
-- create CDEF statements for each instance in reversed order
for i, source in ipairs(_sources) do
__cdef( _sources[1 + #_sources - i] )
end
-- create LINE1, AREA and GPRINT statements for each instance
for i, source in ipairs(_sources) do
__line( source )
__gprint( source )
end
-- prepend image path to arg stack
_ti( _args, 1, self:mkpngpath( plugin, plugin_instance, index .. instance ) )
-- push arg stack to definition list
_ti( defs, _args )
-- reset stacks
_args = { }
_stack_pos = { }
_stack_neg = { }
end
return defs
end
function Graph.render( self, plugin, plugin_instance, is_index )
dtype_instances = dtype_instances or { "" }
local pngs = { }
-- check for a whole graph handler
local plugin_def = "luci.statistics.rrdtool.definitions." .. plugin
local stat, def = pcall( require, plugin_def )
if stat and def and type(def.rrdargs) == "function" then
-- temporary image matrix
local _images = { }
-- get diagram definitions
for i, opts in ipairs( self:_forcelol( def.rrdargs( self, plugin, plugin_instance, nil, is_index ) ) ) do
if not is_index or not opts.detail then
_images[i] = { }
-- get diagram definition instances
local diagrams = self:_generic( opts, plugin, plugin_instance, nil, i )
-- render all diagrams
for j, def in ipairs( diagrams ) do
-- remember image
_images[i][j] = def[1]
-- exec
self:_rrdtool( def )
end
end
end
-- remember images - XXX: fixme (will cause probs with asymmetric data)
for y = 1, #_images[1] do
for x = 1, #_images do
table.insert( pngs, _images[x][y] )
end
end
end
return pngs
end

View file

@ -1,77 +0,0 @@
-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.colors", package.seeall)
local util = require("luci.util")
Instance = util.class()
function Instance.from_string( self, s )
return {
tonumber(s:sub(1,2), 16),
tonumber(s:sub(3,4), 16),
tonumber(s:sub(5,6), 16)
}
end
function Instance.to_string( self, c )
return string.format(
"%02x%02x%02x",
math.floor(c[1]),
math.floor(c[2]),
math.floor(c[3])
)
end
function Instance.defined( self, i )
local t = {
{230, 25, 75},
{245, 130, 48},
{255, 225, 25},
{60, 180, 75},
{70, 240, 240},
{0, 130, 200},
{0, 0, 128},
{170, 110, 40}
}
return string.format(
"%02x%02x%02x",
t[(i-1) % #t + 1][1], t[(i-1) % #t +1][2], t[(i-1) % #t + 1][3] )
end
function Instance.random( self )
local r = math.random(255)
local g = math.random(255)
local min = 0
local max = 255
if ( r + g ) < 255 then
min = 255 - r - g
else
max = 511 - r - g
end
local b = min + math.floor( math.random() * ( max - min ) )
return { r, g, b }
end
function Instance.faded( self, fg, opts )
opts = opts or {}
opts.background = opts.background or { 255, 255, 255 }
opts.alpha = opts.alpha or 0.25
if type(opts.background) == "string" then
opts.background = _string_to_color(opts.background)
end
local bg = opts.background
return {
( opts.alpha * fg[1] ) + ( ( 1.0 - opts.alpha ) * bg[1] ),
( opts.alpha * fg[2] ) + ( ( 1.0 - opts.alpha ) * bg[2] ),
( opts.alpha * fg[3] ) + ( ( 1.0 - opts.alpha ) * bg[3] )
}
end

View file

@ -1,178 +0,0 @@
-- Copyright 2015 Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.apcups",package.seeall)
function item()
return luci.i18n.translate("APC UPS")
end
function rrdargs( graph, plugin, plugin_instance )
local lu = require("luci.util")
local rv = { }
-- Types and instances supported by APC UPS
-- e.g. ups_types -> { 'timeleft', 'charge', 'percent', 'voltage' }
-- e.g. ups_inst['voltage'] -> { 'input', 'battery' }
local ups_types = graph.tree:data_types( plugin, plugin_instance )
local ups_inst = {}
for _, t in ipairs(ups_types) do
ups_inst[t] = graph.tree:data_instances( plugin, plugin_instance, t )
end
-- Check if hash table or array is empty or nil-filled
local function empty( t )
for _, v in pairs(t) do
if type(v) then return false end
end
return true
end
-- Append graph definition but only types/instances which are
-- supported and available to the plugin and UPS.
local function add_supported( t, defs )
local def_inst = defs['data']['instances']
if type(def_inst) == "table" then
for k, v in pairs( def_inst ) do
if lu.contains( ups_types, k) then
for j = #v, 1, -1 do
if not lu.contains( ups_inst[k], v[j] ) then
table.remove( v, j )
end
end
if #v == 0 then
def_inst[k] = nil -- can't assign v: immutable
end
else
def_inst[k] = nil -- can't assign v: immutable
end
end
if empty(def_inst) then return end
end
table.insert( t, defs )
end
-- Graph definitions for APC UPS measurements MUST use only 'instances':
-- e.g. instances = { voltage = { "input", "output" } }
local voltagesdc = {
title = "%H: Voltages on APC UPS - Battery",
vlabel = "Volts DC",
alt_autoscale = true,
number_format = "%5.1lfV",
data = {
instances = {
voltage = { "battery" }
},
options = {
voltage = { title = "Battery voltage", noarea=true }
}
}
}
add_supported( rv, voltagesdc )
local voltagesac = {
title = "%H: Voltages on APC UPS - AC",
vlabel = "Volts AC",
alt_autoscale = true,
number_format = "%5.1lfV",
data = {
instances = {
voltage = { "input", "output" }
},
options = {
voltage_output = { color = "00e000", title = "Output voltage", noarea=true, overlay=true },
voltage_input = { color = "ffb000", title = "Input voltage", noarea=true, overlay=true }
}
}
}
add_supported( rv, voltagesac )
local percentload = {
title = "%H: Load on APC UPS ",
vlabel = "Percent",
y_min = "0",
y_max = "100",
number_format = "%5.1lf%%",
data = {
instances = {
percent = { "load" }
},
options = {
percent_load = { color = "00ff00", title = "Load level" }
}
}
}
add_supported( rv, percentload )
local charge_percent = {
title = "%H: Battery charge on APC UPS ",
vlabel = "Percent",
y_min = "0",
y_max = "100",
number_format = "%5.1lf%%",
data = {
instances = {
charge = { "" }
},
options = {
charge = { color = "00ff0b", title = "Charge level" }
}
}
}
add_supported( rv, charge_percent )
local temperature = {
title = "%H: Battery temperature on APC UPS ",
vlabel = "\176C",
number_format = "%5.1lf\176C",
data = {
instances = {
temperature = { "" }
},
options = {
temperature = { color = "ffb000", title = "Battery temperature" } }
}
}
add_supported( rv, temperature )
local timeleft = {
title = "%H: Time left on APC UPS ",
vlabel = "Minutes",
number_format = "%.1lfm",
data = {
instances = {
timeleft = { "" }
},
options = {
timeleft = { color = "0000ff", title = "Time left" }
}
}
}
add_supported( rv, timeleft )
local frequency = {
title = "%H: Incoming line frequency on APC UPS ",
vlabel = "Hz",
number_format = "%5.0lfhz",
data = {
instances = {
frequency = { "input" }
},
options = {
frequency_input = { color = "000fff", title = "Line frequency" }
}
}
}
add_supported( rv, frequency )
return rv
end

View file

@ -1,32 +0,0 @@
-- Copyright 2011 Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.conntrack",package.seeall)
function item()
return luci.i18n.translate("Conntrack")
end
function rrdargs( graph, plugin, plugin_instance, dtype )
return {
title = "%H: Conntrack entries",
vlabel = "Count",
number_format = "%5.0lf",
data = {
-- collectd 5.5+: specify "" to exclude "max" instance
instances = {
conntrack = { "" }
},
sources = {
conntrack = { "value" }
},
options = {
conntrack = {
color = "0000ff",
title = "Tracked connections"
}
}
}
}
end

View file

@ -1,27 +0,0 @@
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.contextswitch",package.seeall)
function item()
return luci.i18n.translate("Context Switches")
end
function rrdargs( graph, plugin, plugin_instance, dtype )
return {
title = "%H: Context switches",
alt_autoscale = true,
vlabel = "Switches/s",
number_format = "%5.0lf",
data = {
types = { "contextswitch" },
sources = {
contextswitch = { "value" }
},
options = {
contextswitch = { color = "0000ff", title = "Context switches", noarea=true, overlay=true }
}
}
}
end

View file

@ -1,169 +0,0 @@
-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.cpu",package.seeall)
local uci = require("luci.model.uci").cursor()
local reportbystate = uci:get("luci_statistics", "collectd_cpu", "ReportByState") or "0"
function item()
return luci.i18n.translate("Processor")
end
function rrdargs( graph, plugin, plugin_instance, dtype )
local p = {}
local title = "%H: Processor usage"
if #plugin_instance > 0 then
title = "%H: Processor usage on core #%pi"
end
if reportbystate == "1" then
local cpu = {
title = title,
y_min = "0",
alt_autoscale_max = true,
vlabel = "Jiffies",
number_format = "%5.1lf",
data = {
instances = {
cpu = {
"idle",
"interrupt",
"nice",
"softirq",
"steal",
"system",
"user",
"wait"
}
},
options = {
cpu_idle = {
color = "ffffff",
title = "Idle"
},
cpu_interrupt = {
color = "a000a0",
title = "Interrupt"
},
cpu_nice = {
color = "00e000",
title = "Nice"
},
cpu_softirq = {
color = "ff00ff",
title = "Softirq"
},
cpu_steal = {
color = "000000",
title = "Steal"
},
cpu_system = {
color = "ff0000",
title = "System"
},
cpu_user = {
color = "0000ff",
title = "User"
},
cpu_wait = {
color = "ffb000",
title = "Wait"
}
}
}
}
local percent = {
title = title,
y_min = "0",
alt_autoscale_max = true,
vlabel = "Percent",
number_format = "%5.1lf%%",
data = {
instances = {
percent = {
"idle",
"interrupt",
"nice",
"softirq",
"steal",
"system",
"user",
"wait"
}
},
options = {
percent_idle = {
color = "ffffff",
title = "Idle"
},
percent_interrupt = {
color = "a000a0",
title = "Interrupt"
},
percent_nice = {
color = "00e000",
title = "Nice"
},
percent_softirq = {
color = "ff00ff",
title = "Softirq"
},
percent_steal = {
color = "000000",
title = "Steal"
},
percent_system = {
color = "ff0000",
title = "System"
},
percent_user = {
color = "0000ff",
title = "User"
},
percent_wait = {
color = "ffb000",
title = "Wait"
}
}
}
}
local types = graph.tree:data_types( plugin, plugin_instance )
for _, t in ipairs(types) do
if t == "cpu" then
p[#p+1] = cpu
end
if t == "percent" then
p[#p+1] = percent
end
end
else
p = {
title = title,
y_min = "0",
alt_autoscale_max = true,
vlabel = "Percent",
number_format = "%5.1lf%%",
data = {
instances = {
percent = {
"active",
}
},
options = {
percent_active = {
color = "00e000",
title = "Active"
}
}
}
}
end
return p
end

View file

@ -1,61 +0,0 @@
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.cpufreq",package.seeall)
local uci = require("luci.model.uci").cursor()
local extraitems = uci:get("luci_statistics", "collectd_cpufreq", "ExtraItems") or nil
function item()
return luci.i18n.translate("CPU Frequency")
end
function rrdargs( graph, plugin, plugin_instance, dtype )
local cpufreq = {
title = "%H: Processor frequency - core %pi",
alt_autoscale = true,
vlabel = "Frequency (Hz)",
number_format = "%3.2lf%s",
data = {
types = {"cpufreq" },
options = {
cpufreq = { color = "ff0000", title = "Frequency" },
}
}
}
if extraitems then
local transitions = {
title = "%H: Frequency transitions - core %pi",
alt_autoscale = true,
vlabel = "Transitions",
number_format = "%3.2lf%s",
data = {
types = { "transitions" },
options = {
transitions = { color = "0000ff", title = "Transitions", noarea=true },
}
}
}
local percentage = {
title = "%H: Frequency distribution - core %pi",
alt_autoscale = true,
vlabel = "Percent",
number_format = "%5.2lf%%",
ordercolor = true,
data = {
types = { "percent" },
options = {
percent = { title = "%di kHz", negweight = true },
}
}
}
return { cpufreq, percentage, transitions }
else
return { cpufreq }
end
end

View file

@ -1,27 +0,0 @@
-- Copyright 2018 Chizhong Jin <pjincz@gmail.com>
-- Licensed to the public under the BSD 3-clause license
module("luci.statistics.rrdtool.definitions.curl", package.seeall)
function item()
return luci.i18n.translate("cUrl")
end
function rrdargs( graph, plugin, plugin_instance, dtype )
return {
title = "%H: cUrl Response Time for #%pi",
y_min = "0",
alt_autoscale_max = true,
vlabel = "Response Time",
number_format = "%5.1lf%Ss",
data = {
types = { "response_time" },
options = {
response_time = {
title = ""
}
}
}
}
end

View file

@ -1,90 +0,0 @@
-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.df", package.seeall)
function item()
return luci.i18n.translate("Disk Space Usage")
end
function rrdargs( graph, plugin, plugin_instance, dtype )
local df_complex = {
title = "%H: Disk space usage on %pi",
vlabel = "Bytes",
number_format = "%5.1lf%sB",
data = {
instances = {
df_complex = { "free", "used", "reserved" }
},
options = {
df_complex_free = {
color = "00ff00",
overlay = false,
title = "free"
},
df_complex_used = {
color = "ff0000",
overlay = false,
title = "used"
},
df_complex_reserved = {
color = "0000ff",
overlay = false,
title = "reserved"
}
}
}
}
local percent_bytes = {
title = "%H: Disk space usage on %pi",
vlabel = "Percent",
number_format = "%5.2lf %%",
data = {
instances = {
percent_bytes = { "free", "used", "reserved" }
},
options = {
percent_bytes_free = {
color = "00ff00",
overlay = false,
title = "free"
},
percent_bytes_used = {
color = "ff0000",
overlay = false,
title = "used"
},
percent_bytes_reserved = {
color = "0000ff",
overlay = false,
title = "reserved"
}
}
}
}
local types = graph.tree:data_types( plugin, plugin_instance )
local p = {}
for _, t in ipairs(types) do
if t == "percent_bytes" then
p[#p+1] = percent_bytes
end
if t == "df_complex" then
p[#p+1] = df_complex
end
end
return p
end

View file

@ -1,67 +0,0 @@
-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.disk", package.seeall)
function item()
return luci.i18n.translate("Disk Usage")
end
function rrdargs( graph, plugin, plugin_instance, dtype )
return {
{
title = "%H: Disk I/O operations on %pi",
vlabel = "Operations/s",
number_format = "%5.1lf%sOp/s",
data = {
types = { "disk_ops" },
sources = {
disk_ops = { "read", "write" },
},
options = {
disk_ops__read = {
title = "Reads",
color = "00ff00",
flip = false
},
disk_ops__write = {
title = "Writes",
color = "ff0000",
flip = true
}
}
}
},
{
title = "%H: Disk I/O bandwidth on %pi",
vlabel = "Bytes/s",
number_format = "%5.1lf%sB/s",
detail = true,
data = {
types = { "disk_octets" },
sources = {
disk_octets = { "read", "write" }
},
options = {
disk_octets__read = {
title = "Read",
color = "00ff00",
flip = false
},
disk_octets__write = {
title = "Write",
color = "ff0000",
flip = true
}
}
}
}
}
end

View file

@ -1,75 +0,0 @@
-- Copyright 2011 Manuel Munz <freifunk at somakoma dot de>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.dns", package.seeall)
function item()
return luci.i18n.translate("DNS")
end
function rrdargs( graph, plugin, plugin_instance )
local traffic = {
title = "%H: DNS traffic", vlabel = "Bit/s",
data = {
sources = {
dns_octets = { "queries", "responses" }
},
options = {
dns_octets__responses = {
total = true,
color = "00ff00",
title = "Responses"
},
dns_octets__queries = {
total = true,
color = "0000ff",
title = "Queries"
}
}
}
}
local opcode_query = {
title = "%H: DNS Opcode Query", vlabel = "Queries/s",
data = {
instances = { dns_opcode = { "Query" } },
options = {
dns_opcode_Query_value = {
total = true,
color = "0000ff",
title = "Queries/s"
},
}
}
}
local qtype = {
title = "%H: DNS QType", vlabel = "Queries/s",
data = {
sources = { dns_qtype = { "" } },
options = {
dns_qtype_AAAA_ = { title = "AAAA", noarea = true, total = true },
dns_qtype_A_ = { title = "A", noarea = true, total = true },
dns_qtype_A6_ = { title = "A6", noarea = true, total = true },
dns_qtype_TXT_ = { title = "TXT", noarea = true, total = true },
dns_qtype_MX_ = { title = "MX", noarea = true, total = true },
dns_qtype_NS_ = { title = "NS", noarea = true, total = true },
dns_qtype_ANY_ = { title = "ANY", noarea = true, total = true },
dns_qtype_CNAME_= { title = "CNAME", noarea = true, total = true },
dns_qtype_SOA_ = { title = "SOA", noarea = true, total = true },
dns_qtype_SRV_ = { title = "SRV", noarea = true, total = true },
dns_qtype_PTR_ = { title = "PTR", noarea = true, total = true },
dns_qtype_RP_ = { title = "RP", noarea = true, total = true },
dns_qtype_MAILB_= { title = "MAILB", noarea = true, total = true },
dns_qtype_IXFR_ = { title = "IXFR", noarea = true, total = true },
dns_qtype_HINFO_= { title = "HINFO", noarea = true, total = true },
},
}
}
return { traffic, opcode_query, qtype }
end

View file

@ -1,23 +0,0 @@
-- Copyright 2015 Hannu Nyman <hannu.nyman@iki.fi>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.entropy", package.seeall)
function item()
return luci.i18n.translate("Entropy")
end
function rrdargs( graph, plugin, plugin_instance, dtype )
return {
title = "%H: Available entropy",
vlabel = "bits",
number_format = "%4.0lf",
data = {
types = { "entropy" },
options = { entropy = { title = "Entropy %di" } }
}
}
end

View file

@ -1,113 +0,0 @@
-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.interface", package.seeall)
function item()
return luci.i18n.translate("Interfaces")
end
function rrdargs( graph, plugin, plugin_instance )
--
-- traffic diagram
--
local traffic = {
-- draw this diagram for each plugin instance
per_instance = true,
title = "%H: Transfer on %pi",
vlabel = "Bytes/s",
-- diagram data description
data = {
-- defined sources for data types, if omitted assume a single DS named "value" (optional)
sources = {
if_octets = { "tx", "rx" }
},
-- special options for single data lines
options = {
if_octets__tx = {
total = true, -- report total amount of bytes
color = "00ff00", -- tx is green
title = "Bytes (TX)"
},
if_octets__rx = {
flip = true, -- flip rx line
total = true, -- report total amount of bytes
color = "0000ff", -- rx is blue
title = "Bytes (RX)"
}
}
}
}
--
-- packet diagram
--
local packets = {
-- draw this diagram for each plugin instance
per_instance = true,
title = "%H: Packets on %pi",
vlabel = "Packets/s",
-- diagram data description
data = {
-- data type order
types = { "if_packets", "if_errors" },
-- defined sources for data types
sources = {
if_packets = { "tx", "rx" },
if_errors = { "tx", "rx" }
},
-- special options for single data lines
options = {
-- processed packets (tx DS)
if_packets__tx = {
weight = 1,
overlay = true, -- don't summarize
total = true, -- report total amount of bytes
color = "00ff00", -- processed tx is green
title = "Processed (TX)"
},
-- processed packets (rx DS)
if_packets__rx = {
weight = 2,
overlay = true, -- don't summarize
flip = true, -- flip rx line
total = true, -- report total amount of bytes
color = "0000ff", -- processed rx is blue
title = "Processed (RX)"
},
-- packet errors (tx DS)
if_errors__tx = {
weight = 0,
overlay = true, -- don't summarize
total = true, -- report total amount of packets
color = "ff5500", -- tx errors are orange
title = "Errors (TX)"
},
-- packet errors (rx DS)
if_errors__rx = {
weight = 3,
overlay = true, -- don't summarize
flip = true, -- flip rx line
total = true, -- report total amount of packets
color = "ff0000", -- rx errors are red
title = "Errors (RX)"
}
}
}
}
return { traffic, packets }
end

View file

@ -1,45 +0,0 @@
-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.iptables", package.seeall)
function item()
return luci.i18n.translate("Firewall")
end
function rrdargs( graph, plugin, plugin_instance, dtype )
return {
{
title = "%H: Firewall: Processed bytes in %pi",
vlabel = "Bytes/s",
number_format = "%5.1lf%sB/s",
totals_format = "%5.1lf%sB",
data = {
types = { "ipt_bytes" },
options = {
ipt_bytes = {
total = true,
title = "%di"
}
}
}
},
{
title = "%H: Firewall: Processed packets in %pi",
vlabel = "Packets/s",
number_format = "%5.1lf P/s",
totals_format = "%5.1lf%s",
data = {
types = { "ipt_packets" },
options = {
ipt_packets = {
total = true,
title = "%di"
}
}
}
}
}
end

View file

@ -1,22 +0,0 @@
-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.irq", package.seeall)
function item()
return luci.i18n.translate("Interrupts")
end
function rrdargs( graph, plugin, plugin_instance, dtype )
return {
title = "%H: Interrupts", vlabel = "Issues/s",
number_format = "%5.0lf", data = {
types = { "irq" },
options = {
irq = { title = "IRQ %di", noarea = true }
}
}
}
end

View file

@ -1,96 +0,0 @@
-- Copyright 2011 Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.iwinfo", package.seeall)
function item()
return luci.i18n.translate("Wireless")
end
function rrdargs( graph, plugin, plugin_instance )
--
-- signal/noise diagram
--
local snr = {
title = "%H: Signal and noise on %pi",
vlabel = "dBm",
number_format = "%5.1lf dBm",
data = {
types = { "signal_noise", "signal_power" },
options = {
signal_power = {
title = "Signal",
overlay = true,
color = "0000ff"
},
signal_noise = {
title = "Noise",
overlay = true,
color = "ff0000"
}
}
}
}
--
-- signal quality diagram
--
local quality = {
title = "%H: Signal quality on %pi",
vlabel = "Quality",
number_format = "%3.0lf",
data = {
types = { "signal_quality" },
options = {
signal_quality = {
title = "Quality",
noarea = true,
color = "0000ff"
}
}
}
}
--
-- phy rate diagram
--
local bitrate = {
title = "%H: Average phy rate on %pi",
vlabel = "MBit/s",
number_format = "%5.1lf%sBit/s",
data = {
types = { "bitrate" },
options = {
bitrate = {
title = "Rate",
color = "00ff00"
}
}
}
}
--
-- associated stations
--
local stations = {
title = "%H: Associated stations on %pi",
vlabel = "Stations",
y_min = "0",
alt_autoscale_max = true,
number_format = "%3.0lf",
data = {
types = { "stations" },
options = {
stations = {
title = "Stations",
color = "0000ff"
}
}
}
}
return { snr, quality, bitrate, stations }
end

View file

@ -1,43 +0,0 @@
-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.load", package.seeall)
function item()
return luci.i18n.translate("System Load")
end
function rrdargs( graph, plugin, plugin_instance, dtype )
return {
title = "%H: Load", vlabel = "Load",
y_min = "0",
units_exponent = "0",
number_format = "%5.2lf", data = {
sources = {
load = { "shortterm", "midterm", "longterm" }
},
options = {
load__shortterm = {
color = "ff0000",
title = "1 minute",
noarea = true,
weight = 3
},
load__midterm = {
color = "ff6600",
title = "5 minutes",
overlay = true,
weight = 1
},
load__longterm = {
color = "ffaa00",
title = "15 minutes",
overlay = true,
weight = 2
}
}
}
}
end

View file

@ -1,99 +0,0 @@
-- Copyright 2011 Manuel Munz <freifunk at somakoma dot de>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.memory",package.seeall)
function item()
return luci.i18n.translate("Memory")
end
function rrdargs( graph, plugin, plugin_instance, dtype )
local p = {}
local memory = {
title = "%H: Memory usage",
vlabel = "MB",
number_format = "%5.1lf%s",
y_min = "0",
alt_autoscale_max = true,
data = {
instances = {
memory = {
"free",
"buffered",
"cached",
"used"
}
},
options = {
memory_buffered = {
color = "0000ff",
title = "Buffered"
},
memory_cached = {
color = "ff00ff",
title = "Cached"
},
memory_used = {
color = "ff0000",
title = "Used"
},
memory_free = {
color = "00ff00",
title = "Free"
}
}
}
}
local percent = {
title = "%H: Memory usage",
vlabel = "Percent",
number_format = "%5.1lf%%",
y_min = "0",
alt_autoscale_max = true,
data = {
instances = {
percent = {
"free",
"buffered",
"cached",
"used"
}
},
options = {
percent_buffered = {
color = "0000ff",
title = "Buffered"
},
percent_cached = {
color = "ff00ff",
title = "Cached"
},
percent_used = {
color = "ff0000",
title = "Used"
},
percent_free = {
color = "00ff00",
title = "Free"
}
}
}
}
local types = graph.tree:data_types( plugin, plugin_instance )
for _, t in ipairs(types) do
if t == "percent" then
p[#p+1] = percent
end
if t == "memory" then
p[#p+1] = memory
end
end
return p
end

View file

@ -1,210 +0,0 @@
-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.netlink", package.seeall)
function item()
return luci.i18n.translate("Netlink")
end
function rrdargs( graph, plugin, plugin_instance )
--
-- traffic diagram
--
local traffic = {
title = "%H: Netlink - Transfer on %pi",
vlabel = "Bytes/s",
-- diagram data description
data = {
-- defined sources for data types, if omitted assume a single DS named "value" (optional)
sources = {
if_octets = { "tx", "rx" }
},
-- special options for single data lines
options = {
if_octets__tx = {
title = "Bytes (TX)",
total = true, -- report total amount of bytes
color = "00ff00" -- tx is green
},
if_octets__rx = {
title = "Bytes (RX)",
flip = true, -- flip rx line
total = true, -- report total amount of bytes
color = "0000ff" -- rx is blue
}
}
}
}
--
-- packet diagram
--
local packets = {
title = "%H: Netlink - Packets on %pi",
vlabel = "Packets/s", detail = true,
-- diagram data description
data = {
-- data type order
types = { "if_packets", "if_dropped", "if_errors" },
-- defined sources for data types
sources = {
if_packets = { "tx", "rx" },
if_dropped = { "tx", "rx" },
if_errors = { "tx", "rx" }
},
-- special options for single data lines
options = {
-- processed packets (tx DS)
if_packets__tx = {
weight = 2,
title = "Total (TX)",
overlay = true, -- don't summarize
total = true, -- report total amount of bytes
color = "00ff00" -- processed tx is green
},
-- processed packets (rx DS)
if_packets__rx = {
weight = 3,
title = "Total (RX)",
overlay = true, -- don't summarize
flip = true, -- flip rx line
total = true, -- report total amount of bytes
color = "0000ff" -- processed rx is blue
},
-- dropped packets (tx DS)
if_dropped__tx = {
weight = 1,
title = "Dropped (TX)",
overlay = true, -- don't summarize
total = true, -- report total amount of bytes
color = "660055" -- dropped tx is ... dunno ;)
},
-- dropped packets (rx DS)
if_dropped__rx = {
weight = 4,
title = "Dropped (RX)",
overlay = true, -- don't summarize
flip = true, -- flip rx line
total = true, -- report total amount of bytes
color = "ff00ff" -- dropped rx is violett
},
-- packet errors (tx DS)
if_errors__tx = {
weight = 0,
title = "Errors (TX)",
overlay = true, -- don't summarize
total = true, -- report total amount of packets
color = "ff5500" -- tx errors are orange
},
-- packet errors (rx DS)
if_errors__rx = {
weight = 5,
title = "Errors (RX)",
overlay = true, -- don't summarize
flip = true, -- flip rx line
total = true, -- report total amount of packets
color = "ff0000" -- rx errors are red
}
}
}
}
--
-- multicast diagram
--
local multicast = {
title = "%H: Netlink - Multicast on %pi",
vlabel = "Packets/s", detail = true,
-- diagram data description
data = {
-- data type order
types = { "if_multicast" },
-- special options for single data lines
options = {
-- multicast packets
if_multicast = {
title = "Packets",
total = true, -- report total amount of packets
color = "0000ff" -- multicast is blue
}
}
}
}
--
-- collision diagram
--
local collisions = {
title = "%H: Netlink - Collisions on %pi",
vlabel = "Collisions/s", detail = true,
-- diagram data description
data = {
-- data type order
types = { "if_collisions" },
-- special options for single data lines
options = {
-- collision rate
if_collisions = {
title = "Collisions",
total = true, -- report total amount of packets
color = "ff0000" -- collsions are red
}
}
}
}
--
-- error diagram
--
local errors = {
title = "%H: Netlink - Errors on %pi",
vlabel = "Errors/s", detail = true,
-- diagram data description
data = {
-- data type order
types = { "if_tx_errors", "if_rx_errors" },
-- data type instances
instances = {
if_tx_errors = { "aborted", "carrier", "fifo", "heartbeat", "window" },
if_rx_errors = { "length", "missed", "over", "crc", "fifo", "frame" }
},
-- special options for single data lines
options = {
if_tx_errors_aborted_value = { total = true, color = "ffff00", title = "Aborted (TX)" },
if_tx_errors_carrier_value = { total = true, color = "ffcc00", title = "Carrier (TX)" },
if_tx_errors_fifo_value = { total = true, color = "ff9900", title = "Fifo (TX)" },
if_tx_errors_heartbeat_value = { total = true, color = "ff6600", title = "Heartbeat (TX)" },
if_tx_errors_window_value = { total = true, color = "ff3300", title = "Window (TX)" },
if_rx_errors_length_value = { flip = true, total = true, color = "ff0000", title = "Length (RX)" },
if_rx_errors_missed_value = { flip = true, total = true, color = "ff0033", title = "Missed (RX)" },
if_rx_errors_over_value = { flip = true, total = true, color = "ff0066", title = "Over (RX)" },
if_rx_errors_crc_value = { flip = true, total = true, color = "ff0099", title = "CRC (RX)" },
if_rx_errors_fifo_value = { flip = true, total = true, color = "ff00cc", title = "Fifo (RX)" },
if_rx_errors_frame_value = { flip = true, total = true, color = "ff00ff", title = "Frame (RX)" }
}
}
}
return { traffic, packets, multicast, collisions, errors }
end

View file

@ -1,130 +0,0 @@
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.nut",package.seeall)
function item()
return luci.i18n.translate("UPS")
end
function rrdargs( graph, plugin, plugin_instance, dtype )
local voltages_ac = {
title = "%H: AC voltages on UPS \"%pi\"",
vlabel = "V",
number_format = "%5.1lfV",
data = {
instances = {
voltage = { "input", "output" }
},
options = {
voltage_output = { color = "00e000", title = "Output voltage", noarea=true, overlay=true },
voltage_input = { color = "ffb000", title = "Input voltage", noarea=true, overlay=true }
}
}
}
local voltages_dc = {
title = "%H: Battery voltage on UPS \"%pi\"",
vlabel = "V",
number_format = "%5.1lfV",
data = {
instances = {
voltage = { "battery" }
},
options = {
voltage = { color = "0000ff", title = "Battery voltage", noarea=true, overlay=true }
}
}
}
local currents = {
title = "%H: Current on UPS \"%pi\"",
vlabel = "A",
number_format = "%5.3lfA",
data = {
instances = {
current = { "battery", "output" }
},
options = {
current_output = { color = "00e000", title = "Output current", noarea=true, overlay=true },
current_battery = { color = "0000ff", title = "Battery current", noarea=true, overlay=true }
}
}
}
local percentage = {
title = "%H: Battery charge/load on UPS \"%pi\"",
vlabel = "Percent",
y_min = "0",
y_max = "100",
number_format = "%5.1lf%%",
data = {
instances = {
percent = { "charge", "load" }
},
options = {
percent_charge = { color = "00ff00", title = "Charge level", noarea=true, overlay=true },
percent_load = { color = "ff0000", title = "Load", noarea=true, overlay=true }
}
}
}
-- Note: This is in ISO8859-1 for rrdtool. Welcome to the 20th century.
local temperature = {
title = "%H: Battery temperature on UPS \"%pi\"",
vlabel = "\176C",
number_format = "%5.1lf\176C",
data = {
instances = {
temperature = "battery"
},
options = {
temperature_battery = { color = "ffb000", title = "Battery temperature", noarea=true }
}
}
}
local timeleft = {
title = "%H: Time left on UPS \"%pi\"",
vlabel = "Minutes",
number_format = "%.1lfm",
data = {
instances = {
timeleft = { "battery" }
},
options = {
timeleft_battery = { color = "0000ff", title = "Time left", transform_rpn = "60,/", noarea=true }
}
}
}
local power = {
title = "%H: Power on UPS \"%pi\"",
vlabel = "Power",
number_format = "%5.1lf%%",
data = {
instances = {
power = { "ups" }
},
options = {
power_ups = { color = "00ff00", title = "Power level" }
}
}
}
local frequencies = {
title = "%H: Frequencies on UPS \"%pi\"",
vlabel = "Hz",
number_format = "%5.1lfHz",
data = {
instances = {
frequency = { "input", "output" }
},
options = {
frequency_output = { color = "00e000", title = "Output frequency", noarea=true, overlay=true },
frequency_input = { color = "ffb000", title = "Input frequency", noarea=true, overlay=true }
}
}
}
return { voltages_ac, voltages_dc, currents, percentage, temperature, timeleft, power, frequencies }
end

View file

@ -1,140 +0,0 @@
-- Copyright 2011 Manuel Munz <freifunk at somakoma dot de>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.olsrd", package.seeall)
function item()
return luci.i18n.translate("OLSRd")
end
function rrdargs( graph, plugin, plugin_instance, dtype )
local g = { }
if plugin_instance == "routes" then
g[#g+1] = {
-- diagram data description
title = "%H: Total amount of OLSR routes", vlabel = "n",
number_format = "%5.0lf", data = {
types = { "routes" },
options = {
routes = {
color = "ff0000",
title = "Total number of routes"
}
}
}
}
g[#g+1] = {
title = "%H: Average route ETX", vlabel = "ETX", detail = true,
number_format = "%5.1lf",data = {
instances = { "average" }, -- falls es irgendwann mal welche pro ip gibt, wie bei links, dann werden die hier excludiert
types = { "route_etx" },
options = {
route_etx = {
title = "Average route ETX"
}
}
}
}
g[#g+1] = {
title = "%H: Average route metric", vlabel = "metric", detail = true,
number_format = "%5.1lf", data = {
instances = { "average" }, -- falls es irgendwann mal welche pro ip gibt, wie bei links, dann werden die hier excludiert
types = { "route_metric" },
options = {
route_metric = {
title = "Average route metric"
}
}
}
}
elseif plugin_instance == "links" then
g[#g+1] = {
-- diagram data description
title = "%H: Total amount of OLSR neighbours", vlabel = "n",
number_format = "%5.0lf", data = {
instances = { "" },
types = { "links" },
options = {
links = {
color = "00ff00",
title = "Number of neighbours"
}
}
}
}
local instances = graph.tree:data_instances(plugin, plugin_instance, "signal_quality")
table.sort(instances)
-- define one diagram per host, containing the rx and lq values
local i
for i = 1, #instances, 2 do
local dsn1 = "signal_quality_%s_value" % instances[i]:gsub("[^%w]+", "_")
local dsn2 = "signal_quality_%s_value" % instances[i+1]:gsub("[^%w]+", "_")
local host = instances[i]:match("^[^%-]+%-([^%-]+)%-.+")
g[#g+1] = {
title = "%H: Signal Quality" .. " (" .. (host or "avg") ..")", vlabel = "ETX",
number_format = "%5.2lf", detail = true,
data = {
types = { "signal_quality" },
instances = {
signal_quality = { instances[i], instances[i+1] },
},
options = {
[dsn1] = {
color = "00ff00",
title = "LQ (%s)" % (host or "avg"),
},
[dsn2] = {
color = "0000ff",
title = "NLQ (%s)" % (host or "avg"),
flip = true
}
}
}
}
end
elseif plugin_instance == "topology" then
g[#g+1] = {
title= "%H: Total amount of OLSR links", vlabel = "n",
number_format = "%5.0lf", data = {
instances = { "" },
types = { "links" },
options = {
links = {
color = "0000ff",
title = "Total number of links"
}
}
}
}
g[#g+1] = {
title= "%H: Average signal quality", vlabel = "n",
number_format = "%5.2lf", detail = true,
data = {
instances = { "average" }, -- exclude possible per-ip stuff
types = { "signal_quality" },
options = {
signal_quality = {
color = "0000ff",
title = "Average signal quality"
}
}
}
}
end
return g
end

View file

@ -1,52 +0,0 @@
-- Copyright 2015 Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.openvpn", package.seeall)
function item()
return luci.i18n.translate("OpenVPN")
end
function rrdargs( graph, plugin, plugin_instance )
local inst = plugin_instance:gsub("^openvpn%.(.+)%.status$", "%1")
return {
{
title = "%%H: OpenVPN \"%s\" - Traffic" % inst,
vlabel = "Bytes/s",
data = {
instances = {
if_octets = { "traffic", "overhead" }
},
sources = {
if_octets = { "tx", "rx" }
},
options = {
if_octets_traffic_tx = { weight = 0, title = "Bytes (TX)", total = true, color = "00ff00" },
if_octets_overhead_tx = { weight = 1, title = "Overhead (TX)", total = true, color = "ff9900" },
if_octets_overhead_rx = { weight = 2, title = "Overhead (RX)", total = true, flip = true, color = "ff00ff" },
if_octets_traffic_rx = { weight = 3, title = "Bytes (RX)", total = true, flip = true, color = "0000ff" }
}
}
},
{
title = "%%H: OpenVPN \"%s\" - Compression" % inst,
vlabel = "Bytes/s",
data = {
instances = {
compression = { "data_out", "data_in" }
},
sources = {
compression = { "uncompressed", "compressed" }
},
options = {
compression_data_out_uncompressed = { weight = 0, title = "Uncompressed (TX)", total = true, color = "00ff00" },
compression_data_out_compressed = { weight = 1, title = "Compressed (TX)", total = true, color = "008800" },
compression_data_in_compressed = { weight = 2, title = "Compressed (RX)", total = true, flip = true, color = "000088" },
compression_data_in_uncompressed = { weight = 3, title = "Uncompressed (RX)", total = true, flip = true, color = "0000ff" }
}
}
}
}
end

View file

@ -1,70 +0,0 @@
-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.ping", package.seeall)
function item()
return luci.i18n.translate("Ping")
end
function rrdargs( graph, plugin, plugin_instance, dtype )
local ping = {
title = "%H: ICMP Round Trip Time",
vlabel = "ms",
number_format = "%5.1lf ms",
data = {
sources = {
ping = {
"value"
}
},
options = {
ping__value = {
noarea = true,
overlay = true,
title = "%di"
}
}
}
}
local droprate = {
title = "%H: ICMP Drop Rate",
vlabel = "%",
number_format = "%5.2lf %%",
data = {
types = {
"ping_droprate"
},
options = {
ping_droprate = {
noarea = true,
overlay = true,
title = "%di",
transform_rpn = "100,*"
}
}
}
}
local stddev = {
title = "%H: ICMP Standard Deviation",
vlabel = "ms",
number_format = "%5.1lf ms",
data = {
types = {
"ping_stddev"
},
options = {
ping_stddev = {
noarea = true,
overlay = true,
title = "%di"
}
}
}
}
return { ping, droprate, stddev }
end

View file

@ -1,121 +0,0 @@
-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.processes", package.seeall)
function item()
return luci.i18n.translate("Processes")
end
function rrdargs( graph, plugin, plugin_instance, dtype )
if plugin_instance == "" then
return {
title = "%H: Processes",
vlabel = "Processes/s",
data = {
instances = {
ps_state = {
"sleeping", "running", "paging",
"blocked", "stopped", "zombies"
}
},
options = {
ps_state_sleeping = { color = "0000ff", title = "Sleeping" },
ps_state_running = { color = "008000", title = "Running" },
ps_state_paging = { color = "ffff00", title = "Paging" },
ps_state_blocked = { color = "ff5000", title = "Blocked" },
ps_state_stopped = { color = "555555", title = "Stopped" },
ps_state_zombies = { color = "ff0000", title = "Zombies" }
}
}
}
else
return {
{
title = "%H: CPU time used by %pi",
vlabel = "Jiffies",
data = {
sources = {
ps_cputime = { "syst", "user" }
},
options = {
ps_cputime__user = {
color = "0000ff",
title = "User",
overlay = true
},
ps_cputime__syst = {
color = "ff0000",
title = "System",
overlay = true
}
}
}
},
{
title = "%H: Threads and processes belonging to %pi",
vlabel = "Count",
detail = true,
data = {
sources = {
ps_count = { "threads", "processes" }
},
options = {
ps_count__threads = { color = "00ff00", title = "Threads" },
ps_count__processes = { color = "0000bb", title = "Processes" }
}
}
},
{
title = "%H: Page faults in %pi",
vlabel = "Page faults",
detail = true,
data = {
sources = {
ps_pagefaults = { "minflt", "majflt" }
},
options = {
ps_pagefaults__minflt = { color = "0000ff", title = "Minor" },
ps_pagefaults__majflt = { color = "ff0000", title = "Major" }
}
}
},
{
title = "%H: Resident segment size (RSS) of %pi",
vlabel = "Bytes",
detail = true,
number_format = "%5.1lf%sB",
data = {
types = { "ps_rss" },
options = {
ps_rss = { color = "0000ff", title = "Resident segment" }
}
}
},
{
title = "%H: Virtual memory size (VSZ) of %pi",
vlabel = "Bytes",
detail = true,
number_format = "%5.1lf%sB",
data = {
types = { "ps_vm" },
options = {
ps_vm = { color = "0000ff", title = "Virtual memory" }
}
}
}
}
end
end

View file

@ -1,29 +0,0 @@
-- Copyright 2015 Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.sensors", package.seeall)
function item()
return luci.i18n.translate("Sensors")
end
function rrdargs( graph, plugin, plugin_instance )
return {
{
per_instance = true,
title = "%H: %pi - %di",
vlabel = "\176C",
number_format = "%4.1lf\176C",
data = {
types = { "temperature" },
options = {
temperature__value = {
color = "ff0000",
title = "Temperature"
}
}
}
}
}
end

View file

@ -1,30 +0,0 @@
-- Copyright 2013 Freifunk Augsburg / Michael Wendland <michael@michiwend.com>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.splash_leases", package.seeall)
function item()
return luci.i18n.translate("Splash Leases")
end
function rrdargs( graph, plugin, plugin_instance, dtype )
return {
title = "%H: Splash Leases",
vlabel = "Active Clients",
y_min = "0",
number_format = "%5.1lf",
data = {
sources = {
splash_leases = { "leased", "whitelisted", "blacklisted" }
},
options = {
splash_leases__leased = { color = "00CC00", title = "Leased", overlay = false },
splash_leases__whitelisted = { color = "0000FF", title = "Whitelisted", overlay = false },
splash_leases__blacklisted = { color = "FF0000", title = "Blacklisted", overlay = false }
}
}
}
end

View file

@ -1,30 +0,0 @@
-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.tcpconns", package.seeall)
function item()
return luci.i18n.translate("TCP Connections")
end
function rrdargs( graph, plugin, plugin_instance, dtype )
return {
title = "%H: TCP connections to port %pi",
vlabel = "Connections/s",
number_format = "%5.0lf",
data = {
types = { "tcp_connections" },
instances = {
tcp_connections = {
"SYN_SENT", "SYN_RECV", "LISTEN", "ESTABLISHED",
"LAST_ACK", "TIME_WAIT", "CLOSING", "CLOSE_WAIT",
"CLOSED", "FIN_WAIT1", "FIN_WAIT2"
},
options = {
load__ESTABLISHED = { title = "%di", noarea = true }
}
}
}
}
end

View file

@ -1,24 +0,0 @@
-- Licensed to the public under the Apache License 2.0.
module("luci.statistics.rrdtool.definitions.thermal",package.seeall)
function item()
return luci.i18n.translate("Thermal")
end
function rrdargs( graph, plugin, plugin_instance, dtype )
return {
title = "%H: Temperature of %pi",
alt_autoscale = true,
vlabel = "Celsius",
number_format = "%3.1lf%s",
data = {
types = { "temperature" },
options = {
temperature = { color = "ff0000", title = "Temperature", noarea=true },
}
}
}
end

View file

@ -1,30 +0,0 @@
--[[
Copyright 2013 Thomas Endt <tmo26@gmx.de>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
module("luci.statistics.rrdtool.definitions.uptime", package.seeall)
function item()
return luci.i18n.translate("Uptime")
end
function rrdargs( graph, plugin, plugin_instance, dtype )
return {
title = "%H: Uptime", vlabel = "seconds",
number_format = "%5.0lf%s", data = {
types = { "uptime" },
options = {
uptime = { title = "Uptime %di", noarea = true }
}
}
}
end

View file

@ -1,17 +0,0 @@
<%#
Copyright 2008 Steven Barth <steven@midlink.org>
Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
Licensed to the public under the Apache License 2.0.
-%>
<%+header%>
<h2 name="content"><%:Statistics%></h2>
<p><%_The statistics package uses <a href="https://collectd.org/">Collectd</a>
to gather data and <a href="http://oss.oetiker.ch/rrdtool/">RRDtool</a> to
render diagram images.%></p>
<p><%_You can install additional collectd-mod-* plugins to enable more statistics.%></p>
<%+footer%>

View file

@ -1,38 +0,0 @@
<%#
Copyright 2008 Steven Barth <steven@midlink.org>
Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
Licensed to the public under the Apache License 2.0.
-%>
<%+header%>
<h2 name="content"><%:Statistics%></h2>
<form action="" method="get">
<select name="host">
<% for i, host in ipairs(hosts) do %>
<option<% if host == current_host then %> selected="selected"<% end %>><%=pcdata(host)%></option>
<% end %>
</select>
<input class="cbi-button cbi-button-apply" type="submit" name="submit" value="<%:Display Host »%>" />
<select name="timespan">
<% for i, span in ipairs(timespans) do %>
<option<% if span == current_timespan then %> selected="selected"<% end %>><%=span%></option>
<% end %>
</select>
<input class="cbi-button cbi-button-apply" type="submit" name="submit" value="<%:Display timespan »%>" />
</form>
<br />
<hr />
<br />
<div style="text-align: center">
<% for i, img in ipairs(images) do %>
<img src="<%=REQUEST_URI%>?img=<%=img%>&#38;host=<%=current_host%>" />
<br />
<% end %>
</div>
<%+footer%>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,30 @@
{
"admin/statistics": {
"title": "Statistics",
"order": 80,
"action": {
"type": "firstchild"
},
"depends": {
"uci": { "luci_statistics": true }
}
},
"admin/statistics/graphs": {
"title": "Graphs",
"order": 10,
"action": {
"type": "view",
"path": "statistics/graphs"
}
},
"admin/statistics/collectd": {
"title": "Setup",
"order": 20,
"action": {
"type": "view",
"path": "statistics/collectd"
}
}
}