summaryrefslogtreecommitdiffhomepage
path: root/applications/luci-app-statistics/htdocs
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2020-02-13 20:45:26 +0100
committerJo-Philipp Wich <jo@mein.io>2020-02-13 20:45:26 +0100
commit9680fdea9e2e38bfafe0d97967925dd9fc836a05 (patch)
treeccc5e30e098eab84c4eaf2da8bda97dbfd5a17df /applications/luci-app-statistics/htdocs
parent1d47f0c1a913ccbcba86061daa20e1e336d7b559 (diff)
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>
Diffstat (limited to 'applications/luci-app-statistics/htdocs')
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool.js717
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/apcups.js177
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/conntrack.js30
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/contextswitch.js25
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/cpu.js163
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/cpufreq.js57
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/curl.js28
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/df.js83
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/disk.js64
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/dns.js79
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/entropy.js22
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/interface.js110
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/ip6tables.js39
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/iptables.js39
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/irq.js19
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/iwinfo.js95
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/load.js41
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/memory.js97
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/netlink.js208
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/nut.js130
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/olsrd.js126
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/openvpn.js51
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/ping.js62
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/processes.js120
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/sensors.js25
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/splash_leases.js29
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/tcpconns.js28
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/thermal.js26
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/uptime.js32
-rw-r--r--applications/luci-app-statistics/htdocs/luci-static/resources/view/statistics/graphs.js205
30 files changed, 2927 insertions, 0 deletions
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool.js
new file mode 100644
index 000000000..c99d1e683
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool.js
@@ -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);
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/apcups.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/apcups.js
new file mode 100644
index 000000000..bcebf3034
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/apcups.js
@@ -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;
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/conntrack.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/conntrack.js
new file mode 100644
index 000000000..d62c3cd0c
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/conntrack.js
@@ -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"
+ }
+ }
+ }
+ };
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/contextswitch.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/contextswitch.js
new file mode 100644
index 000000000..fc4c80a93
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/contextswitch.js
@@ -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 }
+ }
+ }
+ };
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/cpu.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/cpu.js
new file mode 100644
index 000000000..4e130704a
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/cpu.js
@@ -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;
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/cpufreq.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/cpufreq.js
new file mode 100644
index 000000000..c12260ea9
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/cpufreq.js
@@ -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 ];
+ }
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/curl.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/curl.js
new file mode 100644
index 000000000..af78dd0b9
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/curl.js
@@ -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: ""
+ }
+ }
+ }
+ };
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/df.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/df.js
new file mode 100644
index 000000000..240d1da61
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/df.js
@@ -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;
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/disk.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/disk.js
new file mode 100644
index 000000000..52542a1f8
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/disk.js
@@ -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
+ }
+ }
+ }
+ }];
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/dns.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/dns.js
new file mode 100644
index 000000000..9e71bb42c
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/dns.js
@@ -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 ];
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/entropy.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/entropy.js
new file mode 100644
index 000000000..574724c42
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/entropy.js
@@ -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" } }
+ }
+ };
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/interface.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/interface.js
new file mode 100644
index 000000000..04b1b8d1e
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/interface.js
@@ -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 ];
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/ip6tables.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/ip6tables.js
new file mode 100644
index 000000000..1b9755cce
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/ip6tables.js
@@ -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"
+ }
+ }
+ }
+ }];
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/iptables.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/iptables.js
new file mode 100644
index 000000000..a115c044b
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/iptables.js
@@ -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"
+ }
+ }
+ }
+ }];
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/irq.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/irq.js
new file mode 100644
index 000000000..158fbce9e
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/irq.js
@@ -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 }
+ }
+ }
+ };
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/iwinfo.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/iwinfo.js
new file mode 100644
index 000000000..e832359e2
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/iwinfo.js
@@ -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 ];
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/load.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/load.js
new file mode 100644
index 000000000..8b1e6c284
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/load.js
@@ -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
+ }
+ }
+ }
+ };
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/memory.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/memory.js
new file mode 100644
index 000000000..4c52fc781
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/memory.js
@@ -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;
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/netlink.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/netlink.js
new file mode 100644
index 000000000..f7d55a89d
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/netlink.js
@@ -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 ];
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/nut.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/nut.js
new file mode 100644
index 000000000..a9cb770c7
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/nut.js
@@ -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 ];
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/olsrd.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/olsrd.js
new file mode 100644
index 000000000..4ccc417da
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/olsrd.js
@@ -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;
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/openvpn.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/openvpn.js
new file mode 100644
index 000000000..08951018f
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/openvpn.js
@@ -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" }
+ }
+ }
+ }
+ ];
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/ping.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/ping.js
new file mode 100644
index 000000000..063db2189
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/ping.js
@@ -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 ];
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/processes.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/processes.js
new file mode 100644
index 000000000..c11770970
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/processes.js
@@ -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" }
+ }
+ }
+ }
+ ];
+ }
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/sensors.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/sensors.js
new file mode 100644
index 000000000..72806df88
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/sensors.js
@@ -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"
+ }
+ }
+ }
+ };
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/splash_leases.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/splash_leases.js
new file mode 100644
index 000000000..64741f16e
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/splash_leases.js
@@ -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 }
+ }
+ }
+ };
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/tcpconns.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/tcpconns.js
new file mode 100644
index 000000000..f2a2d7c22
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/tcpconns.js
@@ -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 }
+ }
+ }
+ }
+ };
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/thermal.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/thermal.js
new file mode 100644
index 000000000..6ff303de9
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/thermal.js
@@ -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
+ }
+ }
+ }
+ };
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/uptime.js b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/uptime.js
new file mode 100644
index 000000000..c764897b5
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/uptime.js
@@ -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
+ }
+ }
+ }
+ };
+ }
+});
diff --git a/applications/luci-app-statistics/htdocs/luci-static/resources/view/statistics/graphs.js b/applications/luci-app-statistics/htdocs/luci-static/resources/view/statistics/graphs.js
new file mode 100644
index 000000000..2abe78dac
--- /dev/null
+++ b/applications/luci-app-statistics/htdocs/luci-static/resources/view/statistics/graphs.js
@@ -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
+});