1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
|
-- Authors: Sadrul Habib Chowdhury <imadil@gmail.com>
-- License: Public domain
-- Last Changed: Unknown
--
-- statusd_netmon.lua: monitor the speed of a network interface
--
-- Thanx to Tuomo for pointing out a problem in the previous script.
--
-- In case this doesn't work for someone, do let me know :)
--
-- Author
-- Sadrul Habib Chowdhury (Adil)
-- imadil at gmail dot com
--
-- Support for per-stat monitors and thresholds added by Jeremy
-- Hankins.
--
--
-- Monitor values available with this monitor:
--
-- netmon
-- netmon_kbsin
-- netmon_kbsout
-- netmon_avgin
-- netmon_avgout
-- netmon_count
--
-- To use the average values or the count you need show_avg and
-- show_count turned on, respectively. If you want the default format
-- (which you get with %netmon) but with colors for important and
-- critical thresholds, try:
--
-- %netmon_kbsin/%netmon_kbsout (%netmon_avgin/%netmon_avgout)
if not statusd_netmon then
statusd_netmon = {
device = "eth0",
show_avg = 1, -- show average stat?
avg_sec = 60, -- default, shows average of 1 minute
show_count = 0, -- show tcp connection count?
interval = 1*1000, -- update every second
-- Threshold information. These values should likely be tweaked to
-- suit local conditions.
important = {
kbsin = 1/10,
kbsout = 1/10,
avgin = 1/10,
avgout = 1/10,
count = 4,
},
critical = {
kbsin = 30,
kbsout = 30,
avgin = 5,
avgout = 5,
count = 50,
}
}
end
local timer = nil -- the timer
local positions = {} -- positions where the entries will be
local last = {} -- the last readings
local history_in = {} -- history to calculate the average
local history_out = {}
local total_in, total_out = 0, 0
local counter = 0 --
local settings = table.join(statusd.get_config("netmon"), statusd_netmon)
--
-- tokenize the string
--
local function tokenize(str)
local ret = {}
local i = 0
local k = nil
for k in string.gmatch(str, '(%w+)') do
ret[i] = k
i = i + 1
end
return ret
end
--
-- get the connection count
--
local function get_connection_count()
local f = io.popen('netstat -st', 'r')
if not f then return nil end
local output = f:read('*a')
if string.len(output) == 0 then return nil end
local s, e, connections =
string.find(output, '%s+(%d+)%s+connections established%s')
f:close()
return tonumber(connections)
end
--
-- calculate the average
--
local function calc_avg(lin, lout)
if counter == settings.avg_sec then
counter = 0
end
total_in = total_in - history_in[counter] + lin
history_in[counter] = lin
total_out = total_out - history_out[counter] + lout
history_out[counter] = lout
counter = counter + 1
return total_in/settings.avg_sec, total_out/settings.avg_sec
end
--
-- parse the information
--
local function parse_netmon_info()
local s
local lin, lout
for s in io.lines('/proc/net/dev') do
local f = string.find(s, settings.device)
if f then
local t = tokenize(s)
return t[positions[0]], t[positions[1]]
end
end
return nil, nil
end
--
-- Return a hint value for the given meter
--
local function get_hint(meter, val)
local hint = "normal"
local crit = settings.critical[meter]
local imp = settings.important[meter]
if crit and val > crit then
hint = "critical"
elseif imp and val > imp then
hint = "important"
end
return hint
end
--
-- update the netmon monitor
--
local function update_netmon_info()
local s
local lin, lout
local function fmt(num)
return(string.format("%.1fK", num))
end
lin, lout = parse_netmon_info()
if not lin or not lout then
-- you should never reach here
statusd.inform("netmon", "oops")
statusd.inform("netmon_hint", "critical")
return
end
last[0], lin = lin, lin - last[0]
last[1], lout = lout, lout - last[1]
local kbsin = lin/1024
local kbsout = lout/1024
local output = string.format("%.1fK/%.1fK", kbsin, kbsout)
if settings.show_avg == 1 then
local avgin, avgout = calc_avg(lin/1024, lout/1024)
output = output .. string.format(" (%.1fK/%.1fK)", avgin, avgout)
statusd.inform("netmon_avgin", fmt(avgin))
statusd.inform("netmon_avgin_hint",
get_hint("avgin", avgin))
statusd.inform("netmon_avgout", fmt(avgout))
statusd.inform("netmon_avgout_hint",
get_hint("avgout", avgout))
end
if settings.show_count == 1 then
local count = get_connection_count()
if count then
output = "[" .. tostring(count) .. "] " .. output
statusd.inform("netmon_count", tostring(count))
statusd.inform("netmon_count_hint",
get_hint("count", count))
else
output = "[?] " .. output
statusd.inform("netmon_count", "?")
statusd.inform("netmon_count_hint", "critical")
end
end
statusd.inform("netmon_kbsin", fmt(kbsin))
statusd.inform("netmon_kbsin_hint",
get_hint("kbsin", kbsin))
statusd.inform("netmon_kbsout", fmt(kbsout))
statusd.inform("netmon_kbsout_hint",
get_hint("kbsout", kbsout))
statusd.inform("netmon", output)
timer:set(settings.interval, update_netmon_info)
end
--
-- is everything ok to begin with?
--
local function sanity_check()
local f = io.open('/proc/net/dev', 'r')
local e
if not f then
return false
end
local s = f:read('*line')
s = f:read('*line') -- the second line, which should give
-- us the positions of the info we seek
local t = tokenize(s)
local n = #(t)
local i = 0
for i = 0,n do
if t[i] == "bytes" then
positions[0] = i
break
end
end
i = positions[0] + 1
for i=i,n do
if t[i] == "bytes" then
positions[1] = i
break
end
end
if not positions[0] or not positions[1] then
return false
end
s = f:read('*a') -- read the whole file
if not string.find(s, settings.device) then
return false -- the device does not exist
end
return true
end
--
-- start the timer
--
local function init_netmon_monitor()
if sanity_check() then
timer = statusd.create_timer()
last[0], last[1] = parse_netmon_info()
if settings.show_avg == 1 then
for i=0,settings.avg_sec-1 do
history_in[i], history_out[i] = 0, 0
end
end
statusd.inform("netmon_template", "xxxxxxxxxxxxxxxxxxxxxxx")
update_netmon_info()
else
statusd.inform("netmon", "oops")
statusd.inform("netmon_hint", "critical")
end
end
init_netmon_monitor()
|