ntp_states
Name
ntp_states - Plugin to monitor NTP states. States are the Select Field as documented at http://www.eecis.udel.edu/~mills/ntp/html/decode.html#peer
Configuration
The following configuration parameters are used by this plugin:
[ntp_states]
env.lowercase - Lowercase hostnames after lookup
env.show_syspeer_stratum - Display the stratum of the system peer, field sys_peer_stratum
Set the variable env.lowercase to anything to lowercase hostnames.
Default Configuration
[ntp_states]
env.lowercase <undefined>
env.show_syspeer_stratum <undefined>
Authors
Original author unknown. Rewritten by Kenyon Ralph kenyon@kenyonralph.com.
License
Same as munin.
Magic Markers
#%# family=auto
#%# capabilities=autoconf
#!/usr/bin/perl -w
=head1 NAME
ntp_states - Plugin to monitor NTP states. States are the Select Field as
documented at http://www.eecis.udel.edu/~mills/ntp/html/decode.html#peer
=head1 CONFIGURATION
The following configuration parameters are used by this plugin:
[ntp_states]
env.lowercase - Lowercase hostnames after lookup
env.show_syspeer_stratum - Display the stratum of the system peer, field sys_peer_stratum
Set the variable env.lowercase to anything to lowercase hostnames.
=head2 DEFAULT CONFIGURATION
[ntp_states]
env.lowercase <undefined>
env.show_syspeer_stratum <undefined>
=head1 AUTHORS
Original author unknown. Rewritten by Kenyon Ralph <kenyon@kenyonralph.com>.
=head1 LICENSE
Same as munin.
=head1 MAGIC MARKERS
#%# family=auto
#%# capabilities=autoconf
=cut
use English qw( -no_match_vars );
use Munin::Plugin;
use strict;
use warnings;
use File::Spec;
eval { require Net::DNS; Net::DNS->import(); };
my $has_net_dns = $EVAL_ERROR ? 0 : 1;
# Include /usr/local/sbin in the path to use the ntpq installed by
# ports on FreeBSD instead of the base system, which is probably
# older.
$ENV{'PATH'} = File::Spec->catdir(File::Spec->rootdir(), 'usr', 'local', 'sbin') . ":" . $ENV{'PATH'};
if ($ARGV[0] and $ARGV[0] eq "autoconf") {
if (!$has_net_dns) {
print "no (missing perl module Net::DNS)\n";
} else {
`ntpq -c help >/dev/null 2>/dev/null`;
if ($CHILD_ERROR eq "0") {
if (`ntpq -n -c peers 2>/dev/null | wc -l` > 0) {
print "yes\n";
} else {
print "no (ntpq -p returned no peers)\n";
}
} else {
print "no (ntpq not found)\n";
}
}
exit 0;
}
die "Aborting due to missing perl module 'Net::DNS'" unless ($has_net_dns);
my %stateval = (
"reject" => 0,
"falsetick" => 1,
"excess" => 2,
"backup" => 3,
"outlyer" => 4,
"outlier" => 4,
"candidate" => 5,
"sys.peer" => 6,
"pps.peer" => 7,
"insane" => 8
);
my %peers_condition;
my $syspeer_stratum_value = 15;
my $resolver = Net::DNS::Resolver->new;
$resolver->tcp_timeout(5);
$resolver->udp_timeout(5);
# Returns a hash whose keys are peer IP addresses and values are
# condition numbers.
sub make_hash {
# ntpq -c associations output:
#ind assid status conf reach auth condition last_event cnt
#===========================================================
# 1 63933 931a yes yes none outlier sys_peer 1
# 2 63934 943a yes yes none candidate sys_peer 3
foreach my $line (`ntpq -c associations`) {
if ($line =~ m/^\s*\d+/) {
my (undef, undef, $assid, undef, undef, undef, undef, $condition_str, undef, undef) = split(/\s+/, $line);
chomp(my $peerinfo = `ntpq -n -c "readvar $assid srcadr"`);
$peerinfo =~ s/\s//g;
my ($peer_addr) = ($peerinfo =~ m/srcadr=(.*)/);
# some states get the last letter cut off in
# ntp version 4.2.4p8 (and probably others) in
# the associations output
if ($condition_str eq "candidat") {
$condition_str = "candidate";
}
if ($condition_str eq "falsetic") {
$condition_str = "falsetick";
}
# save the sys.peer's stratum if configured to graph it
if ($condition_str eq "sys.peer" and exists $ENV{"show_syspeer_stratum"}) {
chomp(my $stratum = `ntpq -n -c "readvar $assid stratum"`);
$stratum =~ s/\s//g;
($syspeer_stratum_value) = ($stratum =~ m/stratum=(.*)/);
}
if (exists $stateval{$condition_str}) {
$peers_condition{$peer_addr} = $stateval{$condition_str};
} else {
$peers_condition{$peer_addr} = 9;
}
}
}
}
# Takes an address and returns a pair: the field name, and the
# Internet hostname
sub make_names {
my $addr = shift;
my $host;
my $packet = $resolver->query($addr);
# Can use core perl routines (from the Socket module) to do
# the address -> hostname lookup in perls newer than 5.10.1
# with, but that's all I have to test with right now. So using
# libnet-dns-perl.
if ($packet) {
my @answer = $packet->answer;
foreach my $rr (@answer) {
if ("PTR" eq $rr->type) {
$host = $rr->ptrdname;
}
}
}
$host = defined $host ? $host : $addr;
my $hostname = $host;
my $fieldname = "peer_" . $hostname;
$fieldname = lc $fieldname if exists $ENV{"lowercase"};
$fieldname = clean_fieldname($fieldname);
return ($fieldname, $hostname);
}
&make_hash;
if ($ARGV[0] and $ARGV[0] eq "config") {
print "graph_title NTP states\n";
print "graph_args --base 1000 --vertical-label state --lower-limit 0\n";
print "graph_category time\n";
print "graph_info These are graphs of the states of this system's NTP peers. The states translate as follows: 0=reject, 1=falsetick, 2=excess, 3=backup, 4=outlier, 5=candidate, 6=system peer, 7=PPS peer. See http://www.eecis.udel.edu/~mills/ntp/html/decode.html for more information on the meaning of these conditions. If show_syspeer_stratum is specified, the sys.peer stratum is also graphed.\n";
foreach my $addr (keys %peers_condition) {
my ($fieldname, $hostname) = &make_names($addr);
print "$fieldname.label $hostname\n";
}
# print config for the sys.peer's stratum if configured to graph it
if (exists $ENV{"show_syspeer_stratum"}) {
print "sys_peer_stratum.label sys.peer stratum\n";
print "sys_peer_stratum.draw LINE1\n";
print "sys_peer_stratum.colour 000000\n";
}
exit 0;
}
foreach my $addr (keys %peers_condition) {
my ($fieldname, $hostname) = &make_names($addr);
print "$fieldname.value ", $peers_condition{$addr}, "\n";
}
# include the sys.peer's stratum if configured to graph it
if (exists $ENV{"show_syspeer_stratum"} ) {
print "sys_peer_stratum.value ", $syspeer_stratum_value, "\n";
}
exit 0;