Projet

Général

Profil

Révision 13b59dd1

ID13b59dd1bbc42b6860fbbaffd7f45abef9083154
Parent 4a7a0de1
Enfant 17c0715a

Ajouté par Philip Paeps il y a plus de 12 ans

nsd_: new plugin to monitor NSD name servers

This plugin collects most statistics from NSD name servers. It
should be called nsd_by_type, nsd_by_rcode or nsd_hits to monitor
queries received by type, replies sent by rcode or the base query
volume, respectively. The plugin is friendly to the name server
and only sends one signal per run (even if three links exist).

Voir les différences:

plugins/network/dns/nsd_
1
#!/usr/local/bin/perl -w
2
#%# family=auto
3
#%# capabilities=autoconf
4
#
5
# Copyright (c) 2013 Philip Paeps
6
# All rights reserved.
7
#
8
# Redistribution and use in source and binary forms, with or without
9
# modification, are permitted provided that the following conditions
10
# are met:
11
# 1. Redistributions of source code must retain the above copyright
12
#    notice, this list of conditions and the following disclaimer
13
#    in this position and unchanged.
14
# 2. Redistributions in binary form must reproduce the above copyright
15
#    notice, this list of conditions and the following disclaimer in the
16
#    documentation and/or other materials provided with the distribution.
17
#
18
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
# SUCH DAMAGE.
29
#
30

  
31
#
32
# Plugin to monitor NSD performance.
33
# Contributed by: Philip Paeps <philip@paeps.cx>.
34
# Heavily inspired by the unbound_munin_ script by
35
# W.C.A. Wijgaards and the nsd3 script by J.T.Sage.
36
#
37
# Usage: nsd_FUNCTION
38
#
39
# Available functions:
40
#	by_type - incoming queries by type
41
#	by_rcode - answers by rcode
42
#	hits - base volume
43
#
44
# Example configuration:
45
#
46
#   [nsd_*]
47
#   user bind
48
#   env.logdir /var/log
49
#   env.logfile nsd.log
50
#
51
#   [nsd_by_type]
52
#   env.stats = A=A AAAA=AAAA MX=MX PTR=PTR TYPE252=AXFR SOA=SOA
53
#
54
# The stats line is an optional space-separated string of VALUE=Caption
55
# pairs of query types to pick out of the NSTATS returned by NSD.  Replace
56
# spaces in a caption value by _.
57
#
58
use strict;
59
use Munin::Plugin;
60
use POSIX;
61

  
62
my $LOGDIR = $ENV{'logdir'} || '/var/log';
63
my $LOGFILE = $ENV{'logfile'} || 'nsd.log';
64
my $logfile = "$LOGDIR/$LOGFILE";
65
my $pidfile = $ENV{'pidfile'} || '/var/run/nsd/nsd.pid';
66

  
67
sub signal_nsd {
68
	open(my $P, "< $pidfile") || die "Couldn't open pidfile: $!\n";
69
	my $pid = <$P>;
70
	close($P);
71

  
72
	chomp $pid;
73
	kill USR1 => $pid or die "Couldn't signal NSD: $!\n";
74

  
75
	# Give the daemon a chance to write statistics.
76
	sleep(0.5);
77
}
78

  
79
sub read_logfile($) {
80
	my $stats = shift;
81
	my $pos = undef;
82

  
83
	($pos) = restore_state();
84
	$pos = 0 unless defined($pos);
85
	my ($L, $rotated) = tail_open($logfile, $pos);
86

  
87
	my $last = 0;
88
	while (<$L>) {
89
		chomp $_;
90
		if (/([NX]STATS) (\d+)/) {
91
			$last = $2;
92
			push @$stats, {
93
				'type' => $1, 'when' => $2,
94
				'values' => { $_ =~ m/(\S+)=(\d+)/g },
95
			};
96
		}
97
	}
98
	$pos = tail_close($L);
99
	save_state($pos);
100

  
101
	return $last;
102
}
103

  
104
sub get_values($) {
105
	my $values = shift;
106

  
107
	my @stats = ();
108
	my $last = read_logfile(\@stats);
109

  
110
	# If the values found in the logfile are older than three minutes,
111
	# signal NSD to print fresh ones, and read them again.
112
	my $now = time;
113
	if ($now - $last > 3 * 60) {
114
		signal_nsd();
115
		read_logfile(\@stats);
116
	}
117

  
118
	# @stats is an array of the last lines found in the logfile.  Hopefully,
119
	# the last two lines will be NSTATS and XSTATS.  If not, we need to keep
120
	# looking.
121
	foreach (keys %$values) {
122
		foreach my $stat (reverse(@stats)) {
123
			$values->{$_} = $stat->{'values'}{$_} if ($stat->{'values'}{$_});
124
		}
125
	}
126

  
127
	return \@stats;
128
}
129

  
130
# Base volume
131
sub hits($) {
132
	my $function = shift;
133
	my %captions = ();
134

  
135
	%captions = (
136
		'RR' => 'UDP queries dropped',
137
		'SAns' => 'UDP queries answered',
138
		'RTCP' => 'TCP connections',
139
		'SErr' => 'Transmit errors',
140
	);
141

  
142
	# Print graph configuration.
143
	if ($function and $function eq "config") {
144
		print "graph_title NSD DNS traffic\n";
145
		print "graph_args --base 1000 -l 0\n";
146
		print "graph_vlabel queries / second\n";
147
		print "graph_category dns\n";
148
		foreach my $caption (keys %captions) {
149
			my $label = $captions{$caption};
150
			$label =~ s/_/ /;
151
			print lc($caption) . ".label $label\n";
152
			print lc($caption) . ".type DERIVE\n";
153
			print lc($caption) . ".min 0\n";
154
		}
155
		print "graph_info Base volume\n";
156
		exit 0;
157
	}
158

  
159
	my %values = map { $_ => 0 } keys %captions;
160
	get_values(\%values);
161
	foreach (keys %values) {
162
		print lc($_) . ".value $values{$_}\n";
163
	}
164

  
165
	exit 0;
166
}
167

  
168
# DNS queries by type
169
sub by_type($$) {
170
	my ($function, $stats) = @_;
171

  
172
	# Sensible default set of statistics.
173
	if (! $stats) {
174
		$stats = "A=A AAAA=AAAA MX=MX PTR=PTR TYPE252=AXFR SOA=SOA " .
175
		    "TXT=TXT DNSKEY=DNSKEY NS=NS SPF=SPF";
176
	}
177

  
178
	my %captions = map { split /=/ } split / /, $stats;
179

  
180
	# Print graph configuration.
181
	if ($function and $function eq "config") {
182
		print "graph_title NSD DNS queries by type\n";
183
		print "graph_args --base 1000 -l 0\n";
184
		print "graph_vlabel queries / second\n";
185
		print "graph_category dns\n";
186
		foreach my $caption (keys %captions) {
187
			my $label = $captions{$caption};
188
			$label =~ s/_/ /;
189
			print lc($caption) . ".label $label\n";
190
			print lc($caption) . ".type DERIVE\n";
191
			print lc($caption) . ".min 0\n";
192
		}
193
		print "graph_info Queries by DNS RR type requested\n";
194
		exit 0;
195
	}
196

  
197
	my %values = map { $_ => 0 } keys %captions;
198
	get_values(\%values);
199
	foreach (keys %values) {
200
		print lc($_) . ".value $values{$_}\n";
201
	}
202

  
203
	exit 0;
204
}
205

  
206
# Answers by rcode
207
sub by_rcode($) {
208
	my ($function) = shift;
209
	my %captions = ();
210

  
211
	%captions = (
212
		'SAns' => 'NOERROR',
213
		'SFail' => 'SERVFAIL',
214
		'SFErr' => 'FORMERR',
215
		'SNXD' => 'NXDOMAIN',
216
	);
217

  
218
	# Print graph configuration.
219
	if ($function and $function eq "config") {
220
		print "graph_title NSD DNS answers by rcode\n";
221
		print "graph_args --base 1000 -l 0\n";
222
		print "graph_vlabel answers / second\n";
223
		print "graph_category dns\n";
224
		foreach my $caption (keys %captions) {
225
			my $label = $captions{$caption};
226
			$label =~ s/_/ /;
227
			print lc($caption) . ".label $label\n";
228
			print lc($caption) . ".type DERIVE\n";
229
			print lc($caption) . ".min 0\n";
230
		}
231
		print "graph_info Answers by rcode returned\n";
232
		exit 0;
233
	}
234

  
235
	my %values = map { $_ => 0 } keys %captions;
236
	get_values(\%values);
237
	foreach (keys %values) {
238
		# SAns is the total number of answers sent, regardless of rcode.
239
		# Subtracting SERVFAIL, FORMERR and NXDOMAIN leaves IMPL and
240
		# REFUSED.  NSD will never send REFUSED, which leaves IMPL - so,
241
		# probably correct enough!
242
		if ($_ eq "SAns") {
243
			$values{$_} -= $values{'SFErr'};
244
			$values{$_} -= $values{'SFail'};
245
			$values{$_} -= $values{'SNXD'};
246
		}
247
		print lc($_) . ".value $values{$_}\n";
248
	}
249

  
250
	exit 0;
251
}
252

  
253
# Make sure we can access the pidfile and the logfile
254
# and that we can send a signal to NSD to print stats.
255
# Note that autoconf should exit 0 even if it fails.
256
sub autoconf {
257
	open(my $L, "< $logfile") || print "no ($logfile: $!)\n";
258
	open(my $P, "< $pidfile") || print "no ($pidfile: $!)\n";
259
	exit 0 unless defined($L) and defined($P);
260

  
261
	eval { signal_nsd() };
262
	if ($@) {
263
		chomp $@;
264
		warn "no ($@)\n";
265
		exit 0;
266
	}
267

  
268
	print "yes\n";
269
	exit 0;
270
}
271

  
272
if ($ARGV[0] and $ARGV[0] eq "autoconf") {
273
	autoconf();
274
}
275

  
276
if ($Munin::Plugin::me =~ /_by_type$/) {
277
	by_type($ARGV[0], $ENV{'stats'});
278
} elsif ($Munin::Plugin::me =~ /_by_rcode$/) {
279
	by_rcode($ARGV[0]);
280
} elsif ($Munin::Plugin::me =~ /_hits$/) {
281
	hits($ARGV[0]);
282
}
283

  
284
exit 0;

Formats disponibles : Unified diff