Projet

Général

Profil

Paste
Télécharger au format
Statistiques
| Branche: | Révision:

root / plugins / network / nsd3 @ a7139bca

Historique | Voir | Annoter | Télécharger (4,74 ko)

1 a7139bca Lars Kruse
#!/usr/bin/env python
2 2f01b83c J.T. Sage
#%# family=auto
3 6f28e159 J.T. Sage
#%# capabilities=autoconf
4 2f01b83c J.T. Sage
5
6
"""
7 6f28e159 J.T. Sage
=head1 NAME
8
9
	nsd - Munin plugin to monitor the number of queries a running process
10
	      of nsd3 has recievied.
11
12
=head1 APPLICABLE SYSTEMS
13
14 8713eb37 Lars Kruse
	Linux or *nix system with a logging installation of NSD v3 installed.
15 6f28e159 J.T. Sage
	  (http://nlnetlabs.nl/projects/nsd/)
16
17
=head1 CONFIGURATION
18 17f78427 Lars Kruse
19 6f28e159 J.T. Sage
	The plugin needs access to the nsd logfile and the nsd pid file to
20
	force the running nsd process to write the current statistics.
21
22 17f78427 Lars Kruse
	Tip: To see if it's already set up correctly, just run this plugin
23 fba800ae Veres Lajos
	with the parameter "autoconf".  If you get a "yes", everything should
24 6f28e159 J.T. Sage
	work like a charm already.
25
26
	This configuration section shows the defaults of the plugin:
27
28 17f78427 Lars Kruse
	The stats line is a set of space-separated values that you wish to
29 6f28e159 J.T. Sage
	retrieve from NSD.  The format is VALUE=Caption.  For spaces in a
30
	caption value, replace them with an underscore (_).
31
32
	[nsd]
33
	        env.statsfile /var/log/nsd.log
34
        	env.pidfile /var/run/nsd3/nsd.pid
35
		env.stats "A=A AAAA=AAAA MX=MX PTR=PTR TYPE252=AXFR SNXD=NXDOMAIN RQ=Total_Successful"
36
37 17f78427 Lars Kruse
	If you need to set a user for the logfile to be readable, and most
38 fba800ae Veres Lajos
	importantly, the process to receive the signal, you may specify it.
39 6f28e159 J.T. Sage
	For example:
40
41
	[nsd]
42
		user nsd
43
44
=head1 INTERPRETATION
45 17f78427 Lars Kruse
46
	The plugin shows the number of queries that nsd has received,
47 6f28e159 J.T. Sage
	averaged over a period to gain the number of queries per second.
48
	For most servers, these values will be very low.  In the event
49
	of a misconfiguration, the plugin will return undefined values.
50
51
=head1 MAGIC MARKERS
52 17f78427 Lars Kruse
53 6f28e159 J.T. Sage
	#%# family=auto
54
	#%# capabilities=autoconf
55
56
=head1 VERSION
57
58
	v1.0.1
59 17f78427 Lars Kruse
60 6f28e159 J.T. Sage
=head1 AUTHOR
61
62
	J.T.Sage <jtsage@gmail.com>
63 17f78427 Lars Kruse
64 6f28e159 J.T. Sage
=head1 LICENSE
65 17f78427 Lars Kruse
66 6f28e159 J.T. Sage
	GPLv2
67 17f78427 Lars Kruse
68 6f28e159 J.T. Sage
=cut
69 2f01b83c J.T. Sage
"""
70
71
import os
72
import sys
73
import subprocess
74
import time
75
import re
76 6f28e159 J.T. Sage
import signal
77 2f01b83c J.T. Sage
78
STATS_FILE = os.environ.get('statsfile', '/var/log/nsd.log')
79
PID_FILE = os.environ.get('pidfile', '/var/run/nsd3/nsd.pid')
80 fba800ae Veres Lajos
STATS_STRING = os.environ.get('stats', "A=A AAAA=AAAA MX=MX PTR=PTR TYPE252=AXFR SNXD=NXDOMAIN RQ=Total_Successful")
81 6f28e159 J.T. Sage
82
BOTH_LISTS = STATS_STRING.split()
83 2f01b83c J.T. Sage
84
def print_config():
85 6f28e159 J.T. Sage
	"""Generates and prints a munin config for a given chart."""
86
87
	print "graph_title NSD3 Queries"
88
	print "graph_vlabel qps"
89
	print "graph_category network"
90
	print "graph_info Queries per second"
91
	for x in BOTH_LISTS:
92
		val = x.split('=')
93
		name = val[0].lower()
94
		label = val[1].replace('_', ' ')
95
		print name + '.label ' + label
96
		print name + '.type DERIVE'
97
		print name + '.min 0'
98
99
	sys.exit(0)
100 2f01b83c J.T. Sage
101
def print_values():
102 6f28e159 J.T. Sage
	"""Gets NSD's latest stats."""
103
104
	bigfail = False
105
	if ( not os.access(STATS_FILE, os.R_OK) ) :
106
		bigfail = True
107
	if ( not os.access(PID_FILE, os.R_OK) ) :
108
		bigfail = True
109
110
	if ( not bigfail ):
111
		pidf = open(PID_FILE, 'r')
112
		pidn = pidf.read()
113
		pidf.close();
114
		try:
115
			os.kill(int(pidn.strip()), signal.SIGUSR1)
116
	        except OSError:
117
			bigfail = True
118
119
120
	time.sleep(.5) # Wait for the log to write.
121
122
	if ( not bigfail ):
123
		statf = open(STATS_FILE, 'r')
124
		stats = tail(statf, 10)
125
126
		nstats = []
127
		xstats = []
128
129
		for line in stats:
130
			if "XSTATS" in line:
131
				xstats.append(line.strip())
132
			if "NSTATS" in line:
133
				nstats.append(line.strip())
134
135
		statsline = nstats[-1] + xstats[-1]
136
	else:
137
		statsline = " "
138
139
	relist = []
140
	for x in BOTH_LISTS:
141
                val = x.split('=')
142
                name = val[0].lower()
143
		rxp = val[0]
144
		relist.append([name, rxp])
145
146
	for point in relist:
147
		matches = re.compile(' '+point[1]+'=(\d+)').findall(statsline)
148
		if bigfail:
149
			print point[0]+'.value U'
150
		elif matches == []:
151
			print point[0]+'.value 0'
152
		else:
153
			print point[0]+'.value '+matches[0]
154 2f01b83c J.T. Sage
155
def tail( f, window=20 ):
156 6f28e159 J.T. Sage
	f.seek( 0, 2 )
157
	bytes = f.tell()
158
	size = window
159
	block = -1
160
	while size > 0 and bytes+block*1024  > 0:
161
		f.seek( block*1024, 2 ) # from the end!
162
		data = f.read( 1024 )
163
		linesFound = data.count('\n')
164
		size -= linesFound
165
		block -= 1
166
	f.seek( block*1024, 2 )
167
	f.readline() # find a newline
168
	lastBlocks = list( f.readlines() )
169
	return lastBlocks[-window:]
170 2f01b83c J.T. Sage
171
if __name__ == '__main__':
172 6f28e159 J.T. Sage
	if len(sys.argv) > 1:
173
		if  sys.argv[1] == 'autoconf':
174
			if ( not os.path.isfile(STATS_FILE) ) :
175
				print 'no (Log file not found)'
176
			elif ( not os.path.isfile(PID_FILE) ) :
177
				print 'no (PID file not found)'
178
			elif ( not os.access(STATS_FILE, os.R_OK) ) :
179
				print 'no (Log file exists, access denied for read)'
180
                        elif ( not os.access(PID_FILE, os.R_OK) ) :
181
                                print 'no (PID file exists, access denied for read)'
182
			else:
183
				pidf = open(PID_FILE, 'r')
184
				pidn = pidf.read()
185
				pidf.close();
186
				try:
187
					os.kill(int(pidn.strip()), signal.SIGUSR1)
188
				except OSError as (errno, strerror):
189
					print 'no (Unable to signal process :: '+strerror+')'
190
					sys.exit(0)
191
				print 'yes'
192
			sys.exit(0)
193
194
	if len(sys.argv) > 1 and sys.argv[1] == 'config':
195
		print_config()
196
		sys.exit(0)
197
198
	print_values()