Projet

Général

Profil

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

root / plugins / network / nsd3 @ dd4afac8

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

1
#!/usr/bin/python
2
#%# family=auto
3
#%# capabilities=autoconf
4

    
5

    
6
"""
7
=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
	Linux or *nix system with a logging installtion of NSD v3 installed.
15
	  (http://nlnetlabs.nl/projects/nsd/)
16

    
17
=head1 CONFIGURATION
18
	
19
	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
	Tip: To see if it's already set up correctly, just run this plugin 
23
	with the paramater "autoconf".  If you get a "yes", everything should
24
	work like a charm already.
25

    
26
	This configuration section shows the defaults of the plugin:
27

    
28
	The stats line is a set of space-seperated values that you wish to 
29
	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
	If you need to set a user for the logfile to be readable, and most 
38
	importantly, the process to recieve the signal, you may specify it.
39
	For example:
40

    
41
	[nsd]
42
		user nsd
43

    
44
=head1 INTERPRETATION
45
	
46
	The plugin shows the number of queries that nsd has recieved, 
47
	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
	
53
	#%# family=auto
54
	#%# capabilities=autoconf
55

    
56
=head1 VERSION
57

    
58
	v1.0.1
59
	
60
=head1 AUTHOR
61

    
62
	J.T.Sage <jtsage@gmail.com>
63
	
64
=head1 LICENSE
65
	
66
	GPLv2
67
	
68
=cut
69
"""
70

    
71
import os
72
import sys
73
import subprocess
74
import time
75
import re
76
import signal
77

    
78
STATS_FILE = os.environ.get('statsfile', '/var/log/nsd.log')
79
PID_FILE = os.environ.get('pidfile', '/var/run/nsd3/nsd.pid')
80
STATS_STRING = os.environ.get('stats', "A=A AAAA=AAAA MX=MX PTR=PTR TYPE252=AXFR SNXD=NXDOMAIN RQ=Total_Succesful")
81

    
82
BOTH_LISTS = STATS_STRING.split()
83

    
84
def print_config():
85
	"""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

    
101
def print_values():
102
	"""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

    
155
def tail( f, window=20 ):
156
	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

    
171
if __name__ == '__main__':
172
	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()