Skip to content

Commit eca557e

Browse files
committed
Merge pull request ganglia#207 from ironsmile/nestats-module
New module: net stats from /proc/net/(netstat|snmp)
2 parents 738f9cc + 3891d34 commit eca557e

4 files changed

Lines changed: 262 additions & 58 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
.idea
22
*.pyc
3+
*.sublime-project
4+
*.sublime-workspace

network/netstats/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
netstats
2+
=====
3+
4+
Exports raw stats found in `/proc/net/netstat` and /proc/net/snmp`.
5+
6+
Install
7+
-------
8+
9+
Copy netstats.py from python_modules to your python modules directory, e.g. :
10+
11+
- /usr/lib/ganglia/python_modules
12+
- /usr/lib64/ganglia/python_modules
13+
14+
Copy netstats.pyconf to the gmond conf.d directory, e.g. :
15+
16+
- /etc/ganglia/conf.d/
17+
18+
Tune the netstats.pyconf file to match your needs and then restart gmond.
19+
20+
## AUTHOR
21+
22+
Author: Doychin Atanasov https://github.com/ironsmile
Lines changed: 12 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
#######################################################################
2-
# Use this config only if you interested in few metrics instead of
3-
# the 100 or so metrics possible
4-
#######################################################################
51

62
modules {
73
module {
@@ -10,68 +6,26 @@ modules {
106
}
117
}
128

9+
1310
collection_group {
1411
collect_every = 15
1512
time_threshold = 45
1613

17-
metric {
18-
name = "tcpext_tcploss_percentage"
19-
title = "TCP loss percentage"
20-
value_threshold = 1.0
21-
}
14+
# You can enumerate all metrics you want to see from the /proc/net/netstat and
15+
# /proc/net/snmp files. The name of the metric will be "netstat_CATEGORY_NAME".
16+
# Where CATEGORY is a row like Tcp from snmp or TcpExt from netstat and
17+
# NAME is a column like RcvbufErrors from the snmp or InMcastPkts in netstat.
18+
#
19+
# Few examples below:
2220

2321
metric {
24-
name = "tcp_retrans_percentage"
25-
title = "TCP retransmit percentage"
26-
value_threshold = 1.0
22+
name = "netstat_TcpExt_SyncookiesRecv"
23+
title = "TCP syncookies received"
2724
}
2825

2926
metric {
30-
name = "tcp_outsegs"
31-
title = "TCP segments sent"
32-
value_threshold = 1.0
33-
}
34-
35-
metric {
36-
name = "tcp_insegs"
37-
title = "TCP segments received"
38-
value_threshold = 1.0
39-
}
40-
41-
metric {
42-
name = "udp_indatagrams"
43-
title = "UDP packets in"
44-
value_threshold = 1.0
45-
}
46-
metric {
47-
name = "udp_outdatagrams"
48-
title = "UDP packets out"
49-
value_threshold = 1.0
50-
}
51-
metric {
52-
name = "udp_inerrors"
53-
title = "UDP packet receive errors"
54-
value_threshold = 1.0
27+
name = "netstat_Tcp_ActiveOpens"
28+
title = "TCP active opened connections"
5529
}
5630

57-
metric {
58-
name = "udp_rcvbuferrors"
59-
title = "UDP Receive buffer errors"
60-
value_threshold = 1.0
61-
}
62-
63-
64-
65-
metric {
66-
name = "tcpext_listendrops"
67-
title = "SYNs sent to LISTENing sockets ignored"
68-
value_threshold = 1.0
69-
}
70-
71-
metric {
72-
name = "tcp_attemptfails"
73-
title = "TCP Failed connection attempts"
74-
value_threshold = 1.0
75-
}
76-
77-
}
31+
}
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
import re
5+
import sys
6+
import traceback
7+
import os
8+
import threading
9+
import time
10+
11+
MODULE_NAME = "netstat"
12+
STATS_FILE = "/proc/net/netstat"
13+
SNMP_FILE = "/proc/net/snmp"
14+
15+
descriptors = list()
16+
Desc_Skel = {}
17+
_Worker_Thread = None
18+
_Lock = threading.Lock() # synchronization lock
19+
Debug = False
20+
21+
22+
def dprint(f, *v):
23+
if Debug:
24+
print >>sys.stderr, MODULE_NAME + ": " + f % v
25+
26+
27+
def floatable(str):
28+
try:
29+
float(str)
30+
return True
31+
except:
32+
return False
33+
34+
35+
class UpdateMetricThread(threading.Thread):
36+
def __init__(self, params):
37+
threading.Thread.__init__(self)
38+
39+
self.running = False
40+
self.shuttingdown = False
41+
self.refresh_rate = params["refresh_rate"]
42+
self.mp = params["metrix_prefix"]
43+
self.metric = {}
44+
self.last_metric = {}
45+
46+
def shutdown(self):
47+
self.shuttingdown = True
48+
49+
if not self.running:
50+
return
51+
52+
self.join()
53+
54+
def run(self):
55+
self.running = True
56+
57+
while not self.shuttingdown:
58+
_Lock.acquire()
59+
updated = self.update_metric()
60+
_Lock.release()
61+
62+
if not updated:
63+
time.sleep(0.2)
64+
else:
65+
if "time" in self.last_metric:
66+
dprint("metric delta period %.3f" % (
67+
self.metric['time'] -
68+
self.last_metric['time']))
69+
70+
self.running = False
71+
72+
def update_metric(self):
73+
if "time" in self.metric:
74+
if (time.time() - self.metric['time']) < self.refresh_rate:
75+
return False
76+
77+
dprint("updating metrics")
78+
79+
self.last_metric = self.metric.copy()
80+
81+
update_dict = {
82+
'time': time.time(),
83+
}
84+
85+
try:
86+
for stat_type, key, value in netstats_iterator():
87+
update_dict['%s_%s_%s' % (self.mp, stat_type, key)] = int(value)
88+
89+
except IOError as err:
90+
dprint("unable to open stats file. %s" % err)
91+
return False
92+
93+
self.metric.update(update_dict)
94+
95+
return True
96+
97+
def metric_delta(self, name):
98+
val = 0
99+
100+
if name in self.metric and name in self.last_metric:
101+
_Lock.acquire()
102+
if self.metric['time'] - self.last_metric['time'] != 0:
103+
val = (self.metric[name] - self.last_metric[name]) / (
104+
self.metric['time'] - self.last_metric['time'])
105+
_Lock.release()
106+
107+
return float(val)
108+
109+
110+
def metric_init(params):
111+
global descriptors, Desc_Skel, _Worker_Thread, Debug
112+
113+
# initialize skeleton of descriptors
114+
Desc_Skel = {
115+
'name': 'XXX',
116+
'call_back': metric_delta,
117+
'time_max': 60,
118+
'value_type': 'float',
119+
'format': '%.2f',
120+
'units': 'XXX',
121+
'slope': 'XXX', # zero|positive|negative|both
122+
'description': 'XXX',
123+
'groups': 'network'
124+
}
125+
126+
params["refresh_rate"] = params.get("refresh_rate", 15)
127+
params["metrix_prefix"] = params.get("metrix_prefix", MODULE_NAME)
128+
Debug = params.get("debug", False)
129+
130+
dprint("debugging has been turned on")
131+
132+
_Worker_Thread = UpdateMetricThread(params)
133+
_Worker_Thread.start()
134+
135+
mp = params["metrix_prefix"]
136+
137+
try:
138+
for stat_type, key, value in netstats_iterator():
139+
descriptors.append(create_desc(Desc_Skel, {
140+
"name": '%s_%s_%s' % (mp, stat_type, key),
141+
"units": "number",
142+
"slope": "both",
143+
"description": "Netstat %s metric %s" % (stat_type, key)
144+
}))
145+
except IOError:
146+
return
147+
148+
return descriptors
149+
150+
151+
def file_iterator(file_name):
152+
f = open(file_name, 'r')
153+
154+
line_number = -1
155+
labels = []
156+
labels_type = None
157+
158+
with f:
159+
for line in f:
160+
line_number += 1
161+
162+
if not re.search(':', line):
163+
continue
164+
165+
are_labels = (line_number % 2 == 0)
166+
167+
tokens = re.split('[:\s]+', line.strip())
168+
169+
if are_labels:
170+
labels_type = tokens[0].strip(':')
171+
labels = tokens[1:]
172+
continue
173+
174+
values_type = tokens[0].strip(':')
175+
176+
if values_type != labels_type:
177+
dprint("Expected values of type `%s` but they were `%s`" % (
178+
labels_type, values_type))
179+
continue
180+
181+
for ind, value in enumerate(tokens[1:]):
182+
yield values_type, labels[ind], value
183+
184+
185+
def netstats_iterator():
186+
for vt, key, val in file_iterator(STATS_FILE):
187+
yield vt, key, val
188+
189+
for vt, key, val in file_iterator(SNMP_FILE):
190+
yield vt, key, val
191+
192+
193+
def create_desc(skel, prop):
194+
d = skel.copy()
195+
for k, v in prop.iteritems():
196+
d[k] = v
197+
return d
198+
199+
200+
def metric_delta(name):
201+
return _Worker_Thread.metric_delta(name)
202+
203+
204+
def metric_cleanup():
205+
_Worker_Thread.shutdown()
206+
207+
if __name__ == '__main__':
208+
params = {
209+
"debug": True,
210+
"refresh_rate": 15
211+
}
212+
213+
try:
214+
metric_init(params)
215+
216+
while True:
217+
time.sleep(params['refresh_rate'])
218+
for d in descriptors:
219+
v = d['call_back'](d['name'])
220+
print ('value for %s is ' + d['format']) % (d['name'], v)
221+
except KeyboardInterrupt:
222+
time.sleep(0.2)
223+
os._exit(1)
224+
except:
225+
traceback.print_exc()
226+
os._exit(1)

0 commit comments

Comments
 (0)