From 3f09d369d679d3e4413849cf67f757c647a66965 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Tue, 27 May 2008 20:32:04 +0000 Subject: [PATCH] * luci/statistics: implement flip, overlay and total options for diagram models, improved/fixed netlink and wireless models --- .../luci-statistics/luasrc/statistics/rrdtool.lua | 239 ++++++++++++++++----- .../statistics/rrdtool/definitions/netlink.lua | 93 ++++---- .../statistics/rrdtool/definitions/wireless.lua | 7 +- 3 files changed, 250 insertions(+), 89 deletions(-) diff --git a/applications/luci-statistics/luasrc/statistics/rrdtool.lua b/applications/luci-statistics/luasrc/statistics/rrdtool.lua index b399866a8..783314e8c 100644 --- a/applications/luci-statistics/luasrc/statistics/rrdtool.lua +++ b/applications/luci-statistics/luasrc/statistics/rrdtool.lua @@ -104,65 +104,123 @@ end function Graph._generic( self, opts ) - local images = { } + local images = { } + local rrasingle = false -- XXX: fixme - -- remember images - table.insert( images, opts.image ) + -- internal state variables + local _stack_neg = { } + local _stack_pos = { } + local _longest_name = 0 + local _has_totals = false - -- insert provided addition rrd options - self:_push( { "-t", opts.title or "Unknown title" } ) - self:_push( opts.rrd ) + -- some convenient aliases + local _ti = table.insert + local _sf = string.format - -- construct an array of safe instance names - local inst_names = { } - for i, source in ipairs(opts.sources) do - inst_names[i] = i .. source.name:gsub("[^A-Za-z0-9%-_]","_") - end + -- local helper: create definitions for min, max, avg and create *_nnl (not null) variable from avg + function __def(source) - -- create DEF statements for each instance, find longest instance name - local longest_name = 0 - for i, source in ipairs(opts.sources) do - if source.name:len() > longest_name then - longest_name = source.name:len() + local inst = source.sname + local rrd = source.rrd + local ds = source.ds + + if not ds or ds:len() == 0 then ds = "value" end + + local rv = { _sf( "DEF:%s_avg=%s:%s:AVERAGE", inst, rrd, ds ) } + + if not rrasingle then + _ti( rv, _sf( "DEF:%s_min=%s:%s:MIN", inst, rrd, ds ) ) + _ti( rv, _sf( "DEF:%s_max=%s:%s:MAX", inst, rrd, ds ) ) end - local ds = source.ds or "value" + _ti( rv, _sf( "CDEF:%s_nnl=%s_avg,UN,0,%s_avg,IF", inst, inst, inst ) ) - self:_push( "DEF:" .. inst_names[i] .. "_min=" ..source.rrd .. ":" .. ds .. ":MIN" ) - self:_push( "DEF:" .. inst_names[i] .. "_avg=" ..source.rrd .. ":" .. ds .. ":AVERAGE" ) - self:_push( "DEF:" .. inst_names[i] .. "_max=" ..source.rrd .. ":" .. ds .. ":MAX" ) - self:_push( "CDEF:" .. inst_names[i] .. "_nnl=" .. inst_names[i] .. "_avg,UN,0," .. inst_names[i] .. "_avg,IF" ) + return rv end - -- create CDEF statement for last instance name - self:_push( "CDEF:" .. inst_names[#inst_names] .. "_stk=" .. inst_names[#inst_names] .. "_nnl" ) - - -- create CDEF statements for each instance - for i, source in ipairs(inst_names) do - if i > 1 then - self:_push( - "CDEF:" .. - inst_names[1 + #inst_names - i] .. "_stk=" .. - inst_names[1 + #inst_names - i] .. "_nnl," .. - inst_names[2 + #inst_names - i] .. "_stk,+" - ) + -- local helper: create cdefs depending on source options like flip and overlay + function __cdef(source) + + local rv = { } + local prev + + -- find previous source, choose stack depending on flip state + if source.flip then + prev = _stack_neg[#_stack_neg] + else + prev = _stack_pos[#_stack_pos] end - end - -- create LINE and GPRINT statements for each instance - for i, source in ipairs(opts.sources) do + -- is first source in stack or overlay source: source_stk = source_nnl + if not prev or source.overlay then + -- create cdef statement + _ti( rv, _sf( "CDEF:%s_stk=%s_nnl", source.sname, source.sname ) ) - local legend = string.format( - "%-" .. longest_name .. "s", - source.name - ) + -- is subsequent source without overlay: source_stk = source_nnl + previous_stk + else + -- create cdef statement + _ti( rv, _sf( + "CDEF:%s_stk=%s_nnl,%s_stk,+", source.sname, source.sname, prev + ) ) + end - local numfmt = opts.number_format or "%6.1lf" + -- create multiply by minus one cdef if flip is enabled + if source.flip then + + -- create cdef statement: source_stk = source_stk * -1 + _ti( rv, _sf( "CDEF:%s_neg=%s_stk,-1,*", source.sname, source.sname ) ) + + -- push to negative stack if overlay is disabled + if not source.overlay then + _ti( _stack_neg, source.sname ) + end + + -- no flipping, push to positive stack if overlay is disabled + elseif not source.overlay then + + -- push to positive stack + _ti( _stack_pos, source.sname ) + end + + -- calculate total amount of data if requested + if source.total then + _ti( rv, _sf( + "CDEF:%s_avg_sample=%s_avg,UN,0,%s_avg,IF,sample_len,*", + source.sname, source.sname, source.sname + ) ) + + _ti( rv, _sf( + "CDEF:%s_avg_sum=PREV,UN,0,PREV,IF,%s_avg_sample,+", + source.sname, source.sname, source.sname + ) ) + end + + + return rv + end + + -- local helper: create cdefs required for calculating total values + function __cdef_totals() + if _has_totals then + return { + _sf( "CDEF:mytime=%s_avg,TIME,TIME,IF", opts.sources[1].sname ), + "CDEF:sample_len_raw=mytime,PREV(mytime),-", + "CDEF:sample_len=sample_len_raw,UN,0,sample_len_raw,IF" + } + else + return { } + end + end + + -- local helper: create line and area statements + function __area(source) local line_color local area_color + local legend + local var - -- find color: try source, then opts.colors; fall back to random color + -- find colors: try source, then opts.colors; fall back to random color if type(source.color) == "string" then line_color = source.color area_color = self.colors:from_string( line_color ) @@ -177,13 +235,98 @@ function Graph._generic( self, opts ) -- derive area background color from line color area_color = self.colors:to_string( self.colors:faded( area_color ) ) + -- choose source_stk or source_neg variable depending on flip state + if source.flip then + var = "neg" + else + var = "stk" + end + + -- create legend + legend = _sf( "%-" .. _longest_name .. "s", source.name ) + + -- create area and line1 statement + return { + _sf( "AREA:%s_%s#%s", source.sname, var, area_color ), + _sf( "LINE1:%s_%s#%s:%s", source.sname, var, line_color, legend ) + } + end - self:_push( "AREA:" .. inst_names[i] .. "_stk#" .. area_color ) - self:_push( "LINE1:" .. inst_names[i] .. "_stk#" .. line_color .. ":" .. legend ) - self:_push( "GPRINT:" .. inst_names[i] .. "_min:MIN:" .. numfmt .. " Min" ) - self:_push( "GPRINT:" .. inst_names[i] .. "_avg:AVERAGE:" .. numfmt .. " Avg" ) - self:_push( "GPRINT:" .. inst_names[i] .. "_max:MAX:" .. numfmt .. " Max" ) - self:_push( "GPRINT:" .. inst_names[i] .. "_avg:LAST:" .. numfmt .. " Last\\l" ) + -- local helper: create gprint statements + function __gprint(source) + + local rv = { } + local numfmt = opts.number_format or "%6.1lf" + local totfmt = opts.totals_format or "%5.1lf%s" + + -- don't include MIN if rrasingle is enabled + if not rrasingle then + _ti( rv, _sf( "GPRINT:%s_min:MIN:%s Min", source.sname, numfmt ) ) + end + + -- always include AVERAGE + _ti( rv, _sf( "GPRINT:%s_avg:AVERAGE:%s Avg", source.sname, numfmt ) ) + + -- don't include MAX if rrasingle is enabled + if not rrasingle then + _ti( rv, _sf( "GPRINT:%s_max:MAX:%s Max", source.sname, numfmt ) ) + end + + -- include total count if requested else include LAST + if source.total then + _ti( rv, _sf( "GPRINT:%s_avg_sum:LAST:(ca. %s Total)", source.sname, totfmt ) ) + else + _ti( rv, _sf( "GPRINT:%s_avg:LAST:%s Last", source.sname, numfmt ) ) + end + + -- end label line + rv[#rv] = rv[#rv] .. "\\l" + + + return rv + end + + + -- remember images + _ti( images, opts.image ) + + -- insert provided addition rrd options + self:_push( { "-t", opts.title or "Unknown title" } ) + self:_push( opts.rrd ) + + -- store index and safe instance name within each source object, + -- find longest instance name + for i, source in ipairs(opts.sources) do + + if source.name:len() > _longest_name then + _longest_name = source.name:len() + end + + if source.total then + _has_totals = true + end + + source.index = i + source.sname = i .. source.name:gsub("[^A-Za-z0-9%-_]","_") + end + + -- create DEF statements for each instance, find longest instance name + for i, source in ipairs(opts.sources) do + self:_push( __def( source ) ) + end + + -- create CDEF required for calculating totals + self:_push( __cdef_totals() ) + + -- create CDEF statements for each instance in reversed order + for i, source in ipairs(opts.sources) do + self:_push( __cdef( opts.sources[1 + #opts.sources - i] ) ) + end + + -- create LINE1, AREA and GPRINT statements for each instance + for i, source in ipairs(opts.sources) do + self:_push( __area( source ) ) + self:_push( __gprint( source ) ) end return images diff --git a/applications/luci-statistics/luasrc/statistics/rrdtool/definitions/netlink.lua b/applications/luci-statistics/luasrc/statistics/rrdtool/definitions/netlink.lua index 3cee6abc0..3960b9c53 100644 --- a/applications/luci-statistics/luasrc/statistics/rrdtool/definitions/netlink.lua +++ b/applications/luci-statistics/luasrc/statistics/rrdtool/definitions/netlink.lua @@ -6,63 +6,71 @@ function rrdargs( graph, host, plugin, plugin_instance ) -- diagram names local dtypes_names = { + "Verkehr", "Pakete", + "Multicast-Pakete", "Paketkollisionen", "Paketfehler", - "Verkehr", "RX-Fehler", "TX-Fehler" } -- diagram units local dtypes_units = { + "Bytes/s", + "Pakete/s", "Pakete/s", "Kollisionen/s", "Fehler/s", -- (?) - "Bytes/s", "Fehler/s", "Fehler/s" } -- data source overrides local dtypes_sources = { - if_errors = { "rx", "tx" }, -- if_errors has rx and tx - if_octets = { "rx", "tx" } -- if_octets has rx and tx + if_errors = { "tx", "rx" }, -- if_errors has tx and rx + if_octets = { "tx", "rx" }, -- if_octets has tx and rx + if_packets = { "tx", "rx" }, -- if_packets has tx and rx + if_dropped = { "tx", "rx" }, -- if_dopped has tx and rx } -- diagram data types local dtypes_list = { - -- diagram 1: combined interface packet statistics + -- diagram 1: interface traffic statistics + { + if_octets = { "" } -- bytes/s + }, + + -- diagram 2: combined interface packet statistics { if_dropped = { "" }, -- packets/s - if_multicast = { "" }, -- packets/s if_packets = { "" } -- packets/s }, - -- diagram 2: interface collision statistics + -- diagram 3: multicast count { - if_collisions = { "" } -- collisions/s + if_multicast = { "" } -- packets/s }, - -- diagram 3: interface error statistics + -- diagram 4: interface collision statistics { - if_errors = { "" } -- errors/s (?) + if_collisions = { "" } -- collisions/s }, - -- diagram 4: interface traffic statistics + -- diagram 5: interface error statistics { - if_octets = { "" } -- bytes/s + if_errors = { "" } -- errors/s (?) }, - -- diagram 5: interface rx error statistics + -- diagram 6: interface rx error statistics { if_rx_errors = { -- errors/s "length", "missed", "over", "crc", "fifo", "frame" } }, - -- diagram 6: interface tx error statistics + -- diagram 7: interface tx error statistics { if_tx_errors = { -- errors/s "aborted", "carrier", "fifo", "heartbeat", "window" @@ -75,45 +83,51 @@ function rrdargs( graph, host, plugin, plugin_instance ) -- diagram 1 { - if_dropped = "ff0000", - if_multicast = "0000ff", - if_packets = "00ff00" + if_octets__tx_ = "00ff00", + if_octets__rx_ = "0000ff" }, -- diagram 2 { - if_collisions = "ff0000" + if_dropped__tx_ = "ff0000", + if_dropped__rx_ = "ff5500", + if_packets__tx_ = "00ff00", + if_packets__rx_ = "0000ff" }, -- diagram 3 { - if_errors__tx_ = "ff0000", - if_errors__rx_ = "ff5500" + if_multicast = "0000ff" }, -- diagram 4 { - if_octets__tx_ = "00ff00", - if_octets__rx_ = "0000ff" + if_collisions = "ff0000" }, -- diagram 5 { - length = "0000ff", - missed = "ff5500", - over = "ff0066", - crc = "ff0000", - fifo = "00ff00", - frame = "ffff00" + if_errors__tx_ = "ff0000", + if_errors__rx_ = "ff5500" }, -- diagram 6 { - aborted = "ff0000", - carrier = "ffff00", - fifo = "00ff00", - heartbeat = "0000ff", - window = "8800ff" + length = "0000ff", + missed = "ff5500", + over = "ff0066", + crc = "ff0000", + fifo = "00ff00", + frame = "ffff00" + }, + + -- diagram 7 + { + aborted = "ff0000", + carrier = "ffff00", + fifo = "00ff00", + heartbeat = "0000ff", + window = "8800ff" } } @@ -141,16 +155,19 @@ function rrdargs( graph, host, plugin, plugin_instance ) -- has override for i, ds in ipairs(dtypes_sources[dtype]) do table.insert( opts.sources, { - ds = ds, -- override - name = name .. " (" .. ds .. ")", - rrd = graph:mkrrdpath( host, plugin, plugin_instance, dtype, inst ) + ds = ds, -- override + name = name .. " (" .. ds .. ")", + rrd = graph:mkrrdpath( host, plugin, plugin_instance, dtype, inst ), + flip = ( ds == "rx" ), + total = ( ds == "rx" or ds == "tx" ) } ) end else -- no override, assume single "value" data source table.insert( opts.sources, { - name = name, - rrd = graph:mkrrdpath( host, plugin, plugin_instance, dtype, inst ) + name = name, + rrd = graph:mkrrdpath( host, plugin, plugin_instance, dtype, inst ), + total = ( name == "if_multicast" ) } ) end end diff --git a/applications/luci-statistics/luasrc/statistics/rrdtool/definitions/wireless.lua b/applications/luci-statistics/luasrc/statistics/rrdtool/definitions/wireless.lua index e315ca521..708aee311 100644 --- a/applications/luci-statistics/luasrc/statistics/rrdtool/definitions/wireless.lua +++ b/applications/luci-statistics/luasrc/statistics/rrdtool/definitions/wireless.lua @@ -2,7 +2,7 @@ module("luci.statistics.rrdtool.definitions.wireless", package.seeall) function rrdargs( graph, host, plugin, plugin_instance ) - dtypes = { "signal_power", "signal_noise" } + dtypes = { "signal_noise", "signal_power" } opts = { } opts.sources = { } @@ -16,8 +16,9 @@ function rrdargs( graph, host, plugin, plugin_instance ) for i, dtype in ipairs(dtypes) do opts.sources[i] = { - name = dtype, - rrd = graph:mkrrdpath( host, plugin, plugin_instance, dtype ) + name = dtype, + rrd = graph:mkrrdpath( host, plugin, plugin_instance, dtype ), + overlay = true -- don't summarize values } end -- 2.11.0