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() |
