File: statusd_netmon.lua

package info (click to toggle)
notion 4.0.2%2Bdfsg-6
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 4,676 kB
  • sloc: ansic: 47,508; sh: 2,096; makefile: 603; perl: 270
file content (289 lines) | stat: -rw-r--r-- 6,797 bytes parent folder | download | duplicates (4)
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()