#!@@PERL@@ -w # -*- mode: cperl; cperl-indent-level: 8; -*- =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 env.show_syspeer_stratum =head1 AUTHORS Original author unknown. Rewritten by Kenyon Ralph . =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 | 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=outlyer, 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; # vim:syntax=perl