1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
|
#!@@PERL@@ -w
# -*- mode: cperl; cperl-indent-level: 8; -*-
=head1 NAME
ntp_ - Wildcard plugin to monitor NTP statistics for a particular remote NTP peer
=head1 CONFIGURATION
This is a wildcard plugin. The wildcard suffix in the symlink is the
hostname, IPv4, or IPv6 address of the NTP peer that you want to
monitor. The IP address must be one which appears in C<ntpq -np>
output. If given a hostname, it must resolve to an IP address which
appears in C<ntpq -np> output; this plugin will try all of the A or
AAAA records returned. If you use a dynamic association method, such
as "pool" or one of the broadcast or multicast methods, this plugin
will probably not work very well for you, as your NTP peers could be
changing frequently.
Examples:
=over
=item ntp_time.example.com
=item ntp_203.0.113.1
=item ntp_2001:db8::1
=back
The following environment variables are used by this plugin:
[ntp_*]
env.lowercase - Lowercase hostnames after lookup
env.nodelay 1 - Set to 1 to disable graphing of delay
=head1 AUTHOR
Original author unknown. Rewritten by Kenyon Ralph <kenyon@kenyonralph.com>.
=head1 LICENSE
Same as munin.
=head1 MAGIC MARKERS
#%# family=auto
#%# capabilities=autoconf suggest
=cut
use English qw( -no_match_vars );
use strict;
use warnings;
# work around "strict" check for this variable exported by "Net::IP"
my $ip_identical;
eval {
no warnings "once";
require Net::DNS;
Net::DNS->import();
require Net::IP;
Net::IP->import();
$ip_identical = $Net::IP::IP_IDENTICAL;
};
my $has_requirements = $EVAL_ERROR ? 0 : 1;
if ($ARGV[0] and $ARGV[0] eq "autoconf") {
if (!$has_requirements) {
print "no (missing Net::DNS or Net::IP modules)\n";
} else {
`ntpq -c help >/dev/null 2>/dev/null`;
if ($CHILD_ERROR eq "0") {
if (`ntpq -n -c peers | wc -l` > 0) {
print "yes\n";
} else {
print "no (ntpq -p returned no peers)\n";
}
} else {
print "no (ntpq not found)\n";
}
}
exit 0;
}
if ($ARGV[0] and $ARGV[0] eq "suggest") {
foreach my $line (`ntpq -c associations`) {
if ($line =~ m/^\s*\d+/) {
my (undef, undef, $assid, undef, undef, undef, undef, undef, undef, undef) = split(/\s+/, $line);
chomp(my $peerinfo = `ntpq -n -c "readvar $assid srcadr"`);
$peerinfo =~ s/\R/ /g;
my ($peer_addr) = ($peerinfo =~ m/srcadr=(.*)/);
print $peer_addr, "\n" unless $peer_addr eq "0.0.0.0";
}
}
exit 0;
}
my $nodelay = $ENV{'nodelay'} || 0;
$0 =~ /ntp_(.+)*$/;
my $name = $1;
die "No hostname or IP address provided" unless defined $name;
if ($ARGV[0] and $ARGV[0] eq "config") {
print "graph_title NTP statistics for peer $name\n";
print "graph_args --base 1000 --vertical-label seconds --lower-limit 0\n";
print "graph_category time\n";
print "delay.label Delay\n";
print "delay.graph no\n" if $nodelay;
print "delay.cdef delay,1000,/\n";
print "offset.label Offset\n";
print "offset.cdef offset,1000,/\n";
print "jitter.label Jitter\n";
print "jitter.cdef jitter,1000,/\n";
exit 0;
}
my $srcadr = "";
my $delay = "U";
my $offset = "U";
my $jitter = "U";
my @associations = `ntpq -c associations`;
foreach my $line (@associations) {
if ($line =~ m/^\s*\d+/) {
my (undef, undef, $assid, undef, undef, undef, undef, undef, undef, undef) = split(/\s+/, $line);
chomp(my $peerinfo = `ntpq -n -c "readvar $assid srcadr,delay,offset,jitter"`);
$peerinfo =~ s/\R/ /g;
($srcadr) = ($peerinfo =~ m/srcadr=([^, ]+)/);
($delay) = ($peerinfo =~ m/delay=([^, ]+)/);
($offset) = ($peerinfo =~ m/offset=([^, ]+)/);
($jitter) = ($peerinfo =~ m/jitter=([^, ]+)/);
last if lc($srcadr) eq lc($name);
}
}
my $matched = 0;
if (lc($srcadr) ne lc($name)) {
my @addresses;
my $resolver = Net::DNS::Resolver->new;
$resolver->tcp_timeout(5);
$resolver->udp_timeout(5);
my $query = $resolver->search($name, "AAAA");
if ($query) {
foreach my $rr ($query->answer) {
if ("AAAA" eq $rr->type) {
push(@addresses, new Net::IP($rr->address));
}
}
}
$query = $resolver->search($name, "A");
if ($query) {
foreach my $rr ($query->answer) {
if ("A" eq $rr->type) {
push(@addresses, new Net::IP($rr->address));
}
}
}
ASSOCS: foreach my $line (@associations) {
if ($line =~ m/^\s*\d+/) {
my (undef, undef, $assid, undef, undef, undef, undef, undef, undef, undef) = split(/\s+/, $line);
chomp(my $peerinfo = `ntpq -n -c "readvar $assid srcadr,delay,offset,jitter"`);
$peerinfo =~ s/\R/ /g;
($srcadr) = ($peerinfo =~ m/srcadr=([^, ]+)/);
($delay) = ($peerinfo =~ m/delay=([^, ]+)/);
($offset) = ($peerinfo =~ m/offset=([^, ]+)/);
($jitter) = ($peerinfo =~ m/jitter=([^, ]+)/);
($srcadr) = new Net::IP($srcadr);
ADDRS: foreach my $addr (@addresses) {
if (defined($srcadr->overlaps($addr)) and $srcadr->overlaps($addr) == $ip_identical) {
$matched = 1;
last ASSOCS;
}
}
}
}
}
if (lc($srcadr) ne lc($name) and $matched == 0) {
die "$name is not a peer of this ntpd";
}
print <<"EOT";
delay.value $delay
offset.value $offset
jitter.value $jitter
EOT
exit 0;
# vim:syntax=perl
|