From 9680fdea9e2e38bfafe0d97967925dd9fc836a05 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Thu, 13 Feb 2020 20:45:26 +0100 Subject: 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 --- .../luci-static/resources/statistics/rrdtool.js | 717 +++++++++++++++++++++ .../statistics/rrdtool/definitions/apcups.js | 177 +++++ .../statistics/rrdtool/definitions/conntrack.js | 30 + .../rrdtool/definitions/contextswitch.js | 25 + .../statistics/rrdtool/definitions/cpu.js | 163 +++++ .../statistics/rrdtool/definitions/cpufreq.js | 57 ++ .../statistics/rrdtool/definitions/curl.js | 28 + .../resources/statistics/rrdtool/definitions/df.js | 83 +++ .../statistics/rrdtool/definitions/disk.js | 64 ++ .../statistics/rrdtool/definitions/dns.js | 79 +++ .../statistics/rrdtool/definitions/entropy.js | 22 + .../statistics/rrdtool/definitions/interface.js | 110 ++++ .../statistics/rrdtool/definitions/ip6tables.js | 39 ++ .../statistics/rrdtool/definitions/iptables.js | 39 ++ .../statistics/rrdtool/definitions/irq.js | 19 + .../statistics/rrdtool/definitions/iwinfo.js | 95 +++ .../statistics/rrdtool/definitions/load.js | 41 ++ .../statistics/rrdtool/definitions/memory.js | 97 +++ .../statistics/rrdtool/definitions/netlink.js | 208 ++++++ .../statistics/rrdtool/definitions/nut.js | 130 ++++ .../statistics/rrdtool/definitions/olsrd.js | 126 ++++ .../statistics/rrdtool/definitions/openvpn.js | 51 ++ .../statistics/rrdtool/definitions/ping.js | 62 ++ .../statistics/rrdtool/definitions/processes.js | 120 ++++ .../statistics/rrdtool/definitions/sensors.js | 25 + .../rrdtool/definitions/splash_leases.js | 29 + .../statistics/rrdtool/definitions/tcpconns.js | 28 + .../statistics/rrdtool/definitions/thermal.js | 26 + .../statistics/rrdtool/definitions/uptime.js | 32 + .../resources/view/statistics/graphs.js | 205 ++++++ 30 files changed, 2927 insertions(+) create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/apcups.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/conntrack.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/contextswitch.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/cpu.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/cpufreq.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/curl.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/df.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/disk.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/dns.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/entropy.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/interface.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/ip6tables.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/iptables.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/irq.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/iwinfo.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/load.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/memory.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/netlink.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/nut.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/olsrd.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/openvpn.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/ping.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/processes.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/sensors.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/splash_leases.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/tcpconns.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/thermal.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/uptime.js create mode 100644 applications/luci-app-statistics/htdocs/luci-static/resources/view/statistics/graphs.js (limited to 'applications/luci-app-statistics/htdocs/luci-static') 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 0000000000..c99d1e6838 --- /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 0000000000..bcebf30340 --- /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 0000000000..d62c3cd0c9 --- /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 0000000000..fc4c80a93b --- /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 0000000000..4e130704aa --- /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 0000000000..c12260ea9d --- /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 0000000000..af78dd0b91 --- /dev/null +++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/curl.js @@ -0,0 +1,28 @@ +/* + * Copyright 2018 Chizhong Jin + * 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 0000000000..240d1da617 --- /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 0000000000..52542a1f84 --- /dev/null +++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/disk.js @@ -0,0 +1,64 @@ +/* + * Copyright 2011 Manuel Munz + * 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 0000000000..9e71bb42c5 --- /dev/null +++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/dns.js @@ -0,0 +1,79 @@ +/* + * Copyright 2011 Manuel Munz + * 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 0000000000..574724c421 --- /dev/null +++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/entropy.js @@ -0,0 +1,22 @@ +/* + * Copyright 2015 Hannu Nyman + * 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 0000000000..04b1b8d1ea --- /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 0000000000..1b9755cce3 --- /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 0000000000..a115c044b3 --- /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 0000000000..158fbce9e3 --- /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 0000000000..e832359e2b --- /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 0000000000..8b1e6c2844 --- /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 0000000000..4c52fc7811 --- /dev/null +++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/memory.js @@ -0,0 +1,97 @@ +/* + * Copyright 2011 Manuel Munz + * 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 0000000000..f7d55a89db --- /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 0000000000..a9cb770c7d --- /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 0000000000..4ccc417da7 --- /dev/null +++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/olsrd.js @@ -0,0 +1,126 @@ +/* + * Copyright 2011 Manuel Munz + * 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 0000000000..08951018f4 --- /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 0000000000..063db21895 --- /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 0000000000..c117709701 --- /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 0000000000..72806df880 --- /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 0000000000..64741f16e3 --- /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 + * 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 0000000000..f2a2d7c222 --- /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 0000000000..6ff303de98 --- /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 0000000000..c764897b54 --- /dev/null +++ b/applications/luci-app-statistics/htdocs/luci-static/resources/statistics/rrdtool/definitions/uptime.js @@ -0,0 +1,32 @@ +/* +Copyright 2013 Thomas Endt + +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 0000000000..2abe78dac7 --- /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 collectd to gather data into .rrd 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 +}); -- cgit v1.2.3