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