Projet

Général

Profil

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

root / plugins / chrony / chrony_ @ 2c912170

Historique | Voir | Annoter | Télécharger (7,46 ko)

1
#!/usr/bin/perl -w
2
# -*- mode: cperl; cperl-indent-level: 8; -*-
3

    
4
=head1 NAME
5

    
6
chrony_ - Wildcard plugin to monitor chrony statistics for a
7
particular remote NTP source
8

    
9
=head1 CONFIGURATION
10

    
11
This is a wildcard plugin. The wildcard suffix in the symlink is the
12
hostname, IPv4, or IPv6 address of the NTP sources that you want to
13
monitor. The IP address must be one which appears in C<chronyc sources>
14
output. If given a hostname, it must resolve to an IP address which
15
appears in C<chronyc -n sources> output; this plugin will try all of the A or
16
AAAA records returned. If you use a dynamic association method, such
17
as "pool" or one of the broadcast or multicast methods, this plugin
18
will probably not work very well for you, as your NTP sources could be
19
changing frequently.
20

    
21
Examples:
22

    
23
=over
24

    
25
=item chrony_time.example.com
26

    
27
=item chrony_203.0.113.1
28

    
29
=item chrony_2001:db8::1
30

    
31
=back
32

    
33
Plugin configuration parameters
34

    
35
# path to the crhonyc executable when it is not in the default path
36
env.chronycpath /usr/local/bin/chronyc
37

    
38
# graphlimit sets the horizontal axis for seconds and PPM the
39
# displayed range is from -graphlimit to +graphlimit
40
env.graphlimit 1.0
41

    
42
=head1 AUTHOR
43

    
44
Based on the similar ntp_ wildcard plugin of which the original author
45
unknown and which was rewritten by Kenyon Ralph
46
<kenyon@kenyonralph.com>.
47

    
48
Adopted for chronyc and modified by Olaf Kolkman. 
49
(https://github.com/Kolkman)
50

    
51
=head1 LICENSE
52

    
53
GPL2
54

    
55
=head1 VERSION
56

    
57
 VERSION 0.1.1 - 2 Nov 2020
58

    
59

    
60
=head1 MAGIC MARKERS
61
Used by munin-node-configure.
62

    
63
 #%# family=auto
64
 #%# capabilities=autoconf suggest
65

    
66

    
67
=head1 KNOWN ISSUES
68

    
69
The plugin will only work with 'external' sources. It will not recognize the names of internal refclocks.
70

    
71

    
72
=cut
73

    
74
use English qw( -no_match_vars );
75
use strict;
76
use warnings;
77

    
78

    
79
my $retNetIP;
80
my $retNetDNS;
81
my $retDataVal;
82
BEGIN{
83
	# Import the namespaces for symbols used globally below
84
	if (! eval "require Net::IP;") {
85
		$retNetIP = "Net::IP";
86
	}else{
87
		Net::IP->import();
88
	}
89

    
90
	if (! eval "require Net::DNS;") {
91
		$retNetDNS = "Net::DNS";
92
	}
93

    
94
	if (! eval "require Data::Validate::IP;") {
95
		$retDataVal = "Data::Validate::IP";
96
	}else{
97
		Data::Validate::IP->import();
98
	}
99

    
100

    
101
}
102

    
103

    
104

    
105
my $chronyc = $ENV{'chronycpath'} || `which chronyc`;
106
my $graphlimit = $ENV{'graphlimit'} || 1.0;
107

    
108

    
109
if ($ARGV[0] and $ARGV[0] eq "autoconf") {
110
        `$chronyc help >/dev/null 2>/dev/null`;
111
        if ($CHILD_ERROR eq "0") {
112
		if ($retNetIP || $retNetDNS || $retDataVal){
113
			print "no (missing perl libraries: ";
114
			print $retNetIP . " " if $retNetIP;
115
			print $retNetDNS . " " if $retNetDNS;
116
			print $retDataVal . " " if $retDataVal;
117
			print ")\n";
118
		}
119
                if (`$chronyc -n sources | wc -l` > 0) {
120
                        print "yes\n";
121
                        exit 0;
122
                } else {
123
                        print "no (chronyc sources returned no sources)\n";
124
                        exit 0;
125
                }
126
        } else {
127
                print "no (chronyc not found)\n";
128
                exit 0;
129
        }
130
}
131

    
132
if ($ARGV[0] and $ARGV[0] eq "suggest") {
133
        foreach my $line (`$chronyc -n sources`) {
134
                if ($line =~ m/^??\s+\S+\s+\d+/) {
135
                        my (undef, $peer_addr , undef, undef, undef, undef, undef, undef, undef) = split(/\s+/, $line);
136
                        unless (( $peer_addr eq "0.0.0.0") ){
137
				my $hostname;
138
				if (is_ip($peer_addr) and  $hostname = `$chronyc sourcename $peer_addr`){
139
					print $hostname;
140
				}else{
141
					# Bit of a last resort, not sure if this path is ever triggered.
142
					my $resolver = Net::DNS::Resolver->new;
143
					$resolver->tcp_timeout(5);
144
					$resolver->udp_timeout(5);
145
					my $query = $resolver->search($peer_addr, "PTR");
146
					if ($query) {
147
						foreach my $rr ($query->answer) {
148
							if ("PTR" eq $rr->type) {
149
								print $hostname=$rr->ptrdname."\n";
150
							}
151
						}
152
					}
153
				}
154
				print $peer_addr."\n" unless $hostname;
155
			}
156

    
157
                }
158
        }
159
        exit 0;
160
}
161

    
162

    
163
$0 =~ /chrony_(.+)*$/;
164
my $name = $1;
165

    
166
die "No hostname or IP address provided" unless defined $name;
167

    
168
if ($ARGV[0] and $ARGV[0] eq "config") {
169
        print "graph_title CHRONY statistics for source $name\n";
170
        print "graph_args --base 1000 --vertical-label (seconds,ppm) --lower-limit -$graphlimit --upper-limit $graphlimit --rigid \n";
171
        print "graph_category time\n";
172
        print "freq.label Frequency (ppm) \n";
173
        print "freq.cdef freq,1,*\n";
174
        print "freqsk.label Freq Skew (ppm)\n";
175
        print "freqsk.cdef freqsk,1,*\n";
176
        print "offset.label Offset (sx10)\n";
177
        print "offset.cdef offset,10,*\n";
178
        print "stddev.label Std Deviation (sx100)\n";
179
        print "stddev.cdef stddev,100,*\n";
180
        exit 0;
181
}
182

    
183
my $srcadr;
184
my $freq;
185
my $freqsk;
186
my $offset;
187
my $stddev;
188
my @associations = `$chronyc -n sourcestats`;
189

    
190
foreach my $line (@associations) {
191
	if ($line =~ m/^??\s+\S+\s+\d+/) {
192
		( $srcadr , undef, undef, undef, $freq, $freqsk, $offset, $stddev) = split(/\s+/, $line);
193
                last if lc($srcadr) eq lc($name);
194
		next unless is_ip($srcadr);
195
		# the sourcename comes with a bonus newline
196
		last if (lc($name."\n") eq lc (`$chronyc sourcename $srcadr`)) 
197
        }
198
}
199

    
200
my $matched = 0;
201
my $sourcename="";
202

    
203
if ( is_ip($srcadr) ) {
204
	$sourcename=`$chronyc sourcename $srcadr`;
205
	chop($sourcename);
206
};
207

    
208

    
209
if (is_ip($srcadr) and (lc($srcadr) ne lc($name)) and (lc($name) ne lc ($sourcename)) ){
210
	my @addresses;
211
	my $resolver = Net::DNS::Resolver->new;
212
	$resolver->tcp_timeout(5);
213
	$resolver->udp_timeout(5);
214
	my $query = $resolver->search($name, "AAAA");
215

    
216
	if ($query) {
217
		foreach my $rr ($query->answer) {
218
			if ("AAAA" eq $rr->type) {
219
				push(@addresses, new Net::IP($rr->address));
220
			}
221
		}
222
	}
223

    
224
	$query = $resolver->search($name, "A");
225
	if ($query) {
226
		foreach my $rr ($query->answer) {
227
			if ("A" eq $rr->type) {
228
				push(@addresses, new Net::IP($rr->address));
229
			}
230
		}
231
	}
232

    
233
      ASSOCS: foreach my $line (@associations) {
234
		if ($line =~ m/^??\s+\S+\s+\d+/) {
235
			( $srcadr , undef, undef, undef, $freq, $freqsk, $offset, $stddev) = split(/\s+/, $line);
236
			next unless is_ip($srcadr);
237
			my $srcadr_ip = new Net::IP($srcadr);
238
		      ADDRS: foreach my $addr (@addresses) {
239

    
240
				if (defined($srcadr_ip->overlaps($addr)) and $srcadr_ip->overlaps($addr) == $IP_IDENTICAL) {
241
					$matched = 1;
242
					last ASSOCS;
243
				}
244
			}
245
		}
246
	}
247
}
248

    
249

    
250

    
251

    
252

    
253
if (lc($srcadr) ne lc($name) and  lc($name) ne lc ($sourcename) and $matched == 0) {
254
	die "$name is not a peer of this chronyd";
255
}
256

    
257
if ($offset =~ /(.?\d+)(\S+)/){
258
	$offset=$1*1e-3 if $2 eq "ms";
259
 	$offset=$1*1e-6 if $2 eq "us";
260
 	$offset=$1*1e-9 if $2 eq "ns";
261
}
262

    
263
if ($stddev =~ /(\d+)(\S+)/){
264
	$stddev=$1*1e-3 if $2 eq "ms";
265
 	$stddev=$1*1e-6 if $2 eq "us";
266
 	$stddev=$1*1e-9 if $2 eq "ns";
267
}
268

    
269

    
270

    
271

    
272

    
273

    
274
print <<"EOT";
275
freq.value $freq
276
freqsk.value $freqsk
277
offset.value $offset
278
stddev.value $stddev
279
EOT
280

    
281
exit 0;
282

    
283
# vim:syntax=perl
284

    
285
#  (c) 2020 Olaf Kolkman
286
#  (c) ???? Kenyon Ralph
287
#  (c) ???? ????????????
288
#
289
#  This program is free software; you can redistribute it and/or modify
290
#  it under the terms of the GNU General Public License as published by
291
#  the Free Software Foundation; version 2 dated June, 1991.
292
#
293
#  This program is distributed in the hope that it will be useful,
294
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
295
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
296
#  GNU General Public License for more details.
297
#
298
#  You should have received a copy of the GNU General Public License along
299
#  with this program; if not, write to the Free Software Foundation, Inc.,
300
#  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.