#!/usr/bin/gawk -f # # netdraw -- 2007-12-09, edited 2008-09-14 # # script to draw last 24 hours network interface usage from samples # captured by netsample driven by crontab entry at 5 minute intervals # # Copyright (C) 2007 Grant Coady GPLv2 # # CLI options # end_date="yyyy-mm-dd" # produce a daily activity image for the date specified, providing the # date is within the sample data # BEGIN { ARGV[ARGC++] = "/etc/netdraw.conf" # grab configuration file } /^$|^#/ { next } NR == FNR { conf[$1] = $2 if ($1 == "datafile") ARGV[ARGC++] = conf["datapath"] "/" $2 next } { split($1, dt, "[.:]") date = dt[1] hour = dt[2] min = dt[3] if (FNR > 1) { rx = ($2 - old_rx) / 300 # discover Bps transferred tx = ($3 - old_tx) / 300 if (rx < 0) rx = rxs[i] # use last value if reconnect/rollover if (tx < 0) tx = txs[i] rxs[++i] = rx # store txs[i] = tx if (min == 29) # place hours label pip[i] = hour else pip[i] = "x" if (min == 59) # hour boundary for pips pip[i] = "p" if (!hour && min == 4) { # first sample for the day.. pip[i] = "d" trx = ttx = 0 # ..so clear daily total } trx += rx # accumulate daily total ttx += tx } old_rx = $2; old_tx = $3 # save for next sample start # option: select chart date, eg: netdraw end_date="2007-12-08" if (date == end_date && hour == 23 && min == 59) { # divert the output destination to match end_date conf["dayfly"] = "/tmp/netdraw-" end_date ".fly" conf["imagtemp"] = "/tmp/netdraw-" end_date ".png" nextfile # force end of data collection } } # create fly script for image generation END { set_conf() # massage configuration information mx = 288 # max samples to display, 24 hours start = i - (mx-1) # set data array start reference if (start < 1) start = 1 # not past the start of data if (fullscale < 1) { # auto calibrate vertical scale if (fullscale < 0) # may request.. k = start # ..use displayed samples for scale else k = 1 # ..use all samples for scale for (j = k; j <= i; j++) { # discover max sample if (rxs[j] > max) max = rxs[j] if (txs[j] > max) max = txs[j] } m = 4000 # set auto-range interval if (max > 120000) m = 40000 # large if (max > 1200000) m = 400000 # huge fullscale = m while (fullscale < max) # auto-calibrate fullscale += m } else { # clip data to fullscale for (j = start; j <= i; j++) { if (rxs[j] > fullscale) rxs[j] = fullscale if (txs[j] > fullscale) txs[j] = fullscale } } if (fullscale > 1000000) # create fullscale label text fs_label = sprintf("%1.1fMB/s", fullscale / 1000000) else fs_label = sprintf("%dkB/s",fullscale / 1000) fs = fullscale / 80 # fullscale to 80px chart height fs2 = fullscale / 160 # half for integer rounding ox = 18 # left chart origin oy = 98 # baseline chart origin ot = 2 # origin for top label text ob = 115 # origin for bottom label text set_col_mix() fly_chart(320, 132) # start chart fly script fly_scale() # show vertical scale fly_date() # show the date fly_title() # paint title label fly_totals() # paint daily totals # paint the chart gridlines and outline, baseline painted later fly_line(ox-1, oy-80, ox-1, oy, conf["col_grid"]) # left fly_line(ox+mx, oy-80, ox+mx, oy, conf["col_grid"]) # right fly_line(ox-3, oy-81, ox+mx, oy-81, conf["col_grid"]) # top fly_dlin(ox-1, oy-61, ox+mx, oy-61, conf["col_grid"]) fly_dlin(ox-1, oy-41, ox+mx, oy-41, conf["col_grid"]) # mid fly_dlin(ox-1, oy-21, ox+mx, oy-21, conf["col_grid"]) fly_string(ox+1, ob, conf["axis_x"], conf["col_text"]) fly_strgup(ox-16, oy-2, conf["axis_y"], conf["col_text"]) fly_fsquar(ox+mx-52, ot + 3, 7, conf["col_rx"]) # paint chart key fly_string(ox+mx-41, ot, "Rx", conf["col_text"]) fly_fsquar(ox+mx-21, ot + 3, 7, conf["col_tx"]) fly_string(ox+mx-11, ot, "Tx", conf["col_text"]) x = ox for (j = start; j <= i; j++) { if (pip[j] == "p") # paint baseline pips fly_line(x, oy+1, x, oy+2, conf["col_base"]) if (pip[j] == "d") today++ if (pip[j] != "x" && pip[j] != "p" && pip[j] != "d") if (!today) fly_strgup(x-7, oy+14, pip[j],conf["col_fade"]) else fly_strgup(x-7, oy+14, pip[j],conf["col_text"]) ry = scale(rxs[j]) # paint the data ty = scale(txs[j]) if (ry == ty) fly_line(x, oy, x, oy - ry, col_mix) else if (ry > ty) { fly_line(x, oy, x, oy - ry, conf["col_rx"]) fly_line(x, oy, x, oy - ty, col_mix) } else { fly_line(x, oy, x, oy - ty, conf["col_tx"]) fly_line(x, oy, x, oy - ry, col_mix) } x++ } # paint baseline over chart zero data fly_line(ox-3, oy, ox+mx, oy, conf["col_base"]) # call fly to create the new image file system("fly -q -i "out" && mv -f "conf["imagtemp"]" "conf["htmlpath"]) # touch the .html file to allow page refresh system("touch " conf["htmlpath"] "/index.html") } function scale(s, t) { t = int((s + fs2) / fs) # to chart scale if (!t && s) t++ # don't lose near zero data return t } function set_conf() { out = conf["dayfly"] fullscale = conf["fullscale"] gsub(/_/, " ", conf["axis_x"]) # replace '_' with ' ' for labels gsub(/_/, " ", conf["axis_y"]) gsub(/_/, " ", conf["title"]) } function set_col_mix( cr, ct, a, b, c) { split(conf["col_rx"], cr, ",") split(conf["col_tx"], ct, ",") a = (cr[1] + ct[1]) / 2 b = (cr[2] + ct[2]) / 2 c = (cr[3] + ct[3]) / 2 col_mix = sprintf("%d,%d,%d", a, b, c) } function fly_chart(x, y) { print "# netdraw" > out print "new" > out print "size " x "," y > out print "type png" > out print "name " conf["imagtemp"] > out print "fill 1,1," conf["col_back"] > out } function fly_scale() { fly_string(ox-1, ot, fs_label, conf["col_text"]) } function fly_title( x) { if (!conf["title"]) return x = int(((length(conf["title"]) * 6) / 2)) # get half width of title x = int(ox + (mx/2) - x) # centre it fly_string(x, ob, conf["title"], conf["col_text"]) } function tot_format(x) { x *= 300 if (conf["showtotGB"]) { if (x > 1000000000) return sprintf("%1.2fGB", x / 1000000000) } if (x > 1000000) return sprintf("%1.2fMB", x / 1000000) return sprintf("%dkB", x / 1000) } function fly_totals( s, x) { if (!conf["totals"]) return s = tot_format(trx) x = ox + mx/2 - ((length(s) + 1) * 6) fly_string(x, ot, s, conf["col_rx"]) s = tot_format(ttx) x = ox + mx/2 + 6 fly_string(x, ot, s, conf["col_tx"]) if (conf["title"]) return s = tot_format(trx + ttx) x = ox + mx/2 - ((length(s) + 1) * 3) fly_string(x, ob, s, col_mix) } function fly_date( d, x) { split($1, dt, "."); d = dt[1] x = ox + mx+1 - 60 # right justify fly_string(x, ob, d, conf["col_text"]) } function fly_string(x, y, t, c) { print "string " conf["col_shdw"] "," x + 1 "," y ",small," t > out print "string " conf["col_shdw"] "," x + 1 "," y + 1 ",small," t > out print "string " c "," x "," y ",small," t > out } function fly_strgup(x, y, t, c) { print "stringup " conf["col_shdw"] "," x + 1 "," y ",small," t > out print "stringup " conf["col_shdw"] "," x + 1 "," y + 1 ",small," t >out print "stringup " c "," x "," y ",small," t > out } function fly_fsquar(x, y, s, c) { print "fsquare " x+1 "," y+1 "," s "," conf["col_shdw"] > out print "fsquare " x "," y "," s "," c > out } function fly_line(x1, y1, x2, y2, c) { print "line " x1 "," y1 "," x2 "," y2 "," c > out } function fly_dlin(x1, y1, x2, y2, c) { print "dline " x1 "," y1 "," x2 "," y2 "," c > out } # end