Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision Next revisionBoth sides next revision | ||
monitors:retds [2015/03/17 08:42] – [Known Bugs and Issues] wnelis | monitors:retds [2015/03/17 14:08] – [Installation] wnelis | ||
---|---|---|---|
Line 9: | Line 9: | ||
===== Description ===== | ===== Description ===== | ||
- | Script retds.pl retrieves the DNS statistics of both BIND named servers and windows DNS servers. A table within this script specifies which statistics are to be extracted and defines the RRD file name and the DS name of these statistics at the same time. The DNS servers to be monitored by this script are defined in the Xymon hosts.cfg file, using keyword RNAMED. | + | |
+ | Script retds.pl retrieves the DNS statistics of both BIND named servers | ||
Script retds.pl is a rewrite (in Perl) of script xymon-rnamedstats.sh written by Jerimy Laidman. It is extended to handle Windows DNS servers as well. | Script retds.pl is a rewrite (in Perl) of script xymon-rnamedstats.sh written by Jerimy Laidman. It is extended to handle Windows DNS servers as well. | ||
Line 17: | Line 18: | ||
Although script retds.pl is a server-side script, there is some work to do at the client-side as well. | Although script retds.pl is a server-side script, there is some work to do at the client-side as well. | ||
+ | |||
=== Client side on a BIND server === | === Client side on a BIND server === | ||
Line 24: | Line 26: | ||
For example, the statistics could be collected using the following crontab entry of user root: | For example, the statistics could be collected using the following crontab entry of user root: | ||
0-55/5 * * * * / | 0-55/5 * * * * / | ||
- | The associated RNAMED keyword in the Xymon hosts.cfg is: | + | |
- | RNAMED: | + | |
=== Client side on a Windows DNS server === | === Client side on a Windows DNS server === | ||
Line 39: | Line 40: | ||
copy c: | copy c: | ||
- | This information is retrieved by Xymon using the following RNAMED keyword: | ||
- | RNAMED: | ||
=== Server side === | === Server side === | ||
+ | |||
+ | Script detds.pl will typically be placed in directory / | ||
+ | |||
+ | [retds] | ||
+ | ENVFILE $XYMONHOME/ | ||
+ | CMD $XYMONHOME/ | ||
+ | LOGFILE $XYMONSERVERLOGS/ | ||
+ | INTERVAL 5m | ||
+ | |||
+ | In file $XYMONHOME/ | ||
+ | |||
+ | RNAMED: | ||
+ | TRENDS: | ||
+ | |||
+ | The same tags need to be specified for a Windows DNS server. Again, matching the example above the following values could be used: | ||
+ | |||
+ | RNAMED: | ||
+ | TRENDS: | ||
+ | |||
+ | In file $XYMONHOME/ | ||
===== Source ===== | ===== Source ===== | ||
- | ==== myscript.sh ==== | + | ==== retds.pl ==== |
<hidden onHidden=" | <hidden onHidden=" | ||
< | < | ||
+ | # | ||
+ | # | ||
+ | # This script collects statistics about the use of DNS servers. It can handle | ||
+ | # statistics of both BIND (named) and Windows DNS servers. The script gets the | ||
+ | # statistics over a remote connection, either one which delivers the output on | ||
+ | # standard output, or one which provides a shell prompt. It feeds the results | ||
+ | # into Xymon. | ||
+ | # | ||
+ | # This script is inspired by script xymon-rnamedstats.sh written by Jerimy | ||
+ | # Laidman. This script uses (almost) the same format for the parameters in the | ||
+ | # xymon hosts configuration file. It also generates similar names for the RRD | ||
+ | # files and the same DS names. | ||
+ | # | ||
+ | # Hosts to be queried for DNS-service statistics are flagged with the RNAMED | ||
+ | # key in the Xymon hosts.cfg file. This key allows for parameters to be | ||
+ | # specified. The format is of the RNAMED key is either | ||
+ | # RNAMED:" | ||
+ | # or | ||
+ | # " | ||
+ | # | ||
+ | # Allowed parameters are: | ||
+ | # cmd(< | ||
+ | # source((bind|dnscmd)) : selection of source, bind or dnscmd | ||
+ | # statsfile(< | ||
+ | # testname(< | ||
+ | # title(< | ||
+ | # | ||
+ | # In the command line, a few substitutable variables may be specified. They are | ||
+ | # replaced by their current value upon querying the host: | ||
+ | # %{H} : Host name as defined in the xymon hosts.cfg file | ||
+ | # %{h} : Host name, but without any domain name | ||
+ | # %{I} : IP address as defined in the xymon hosts.cfg file | ||
+ | # | ||
+ | # Note that this script is memoryless by design. This implies that it is not | ||
+ | # possible to check the query-rate against a threshold, as the rate is not | ||
+ | # known in this script. The rate is determined by RRD. | ||
+ | # | ||
+ | # Note with respect to pre-BIND 9.6 statistics: | ||
+ | # success The number of successful queries made to the server or zone. | ||
+ | # A successful query is defined as query which returns a NOERROR | ||
+ | # response with at least one answer RR. | ||
+ | # referral The number of queries which resulted in referral responses. | ||
+ | # nxrrset The number of queries which resulted in NXRRSET responses | ||
+ | # with no data. | ||
+ | # nxdomain The number of queries which resulted in NXDOMAIN responses. | ||
+ | # failure The number of queries which resulted in a failure response | ||
+ | # other than those above. | ||
+ | # recursion The number of queries which caused the server to perform | ||
+ | # | ||
+ | # | ||
+ | # Each query received by the server will cause exactly one of success, | ||
+ | # referral, nxrrset, nxdomain, or failure to be incremented, | ||
+ | # additionally cause the recursion counter to be incremented too. | ||
+ | # | ||
+ | # Written by W.J.M. Nelis, wim.nelis@nlr.nl, | ||
+ | # | ||
+ | use strict ; | ||
+ | use Time::Piece ; # Format time | ||
+ | use Time::Local ; | ||
+ | |||
+ | # | ||
+ | # Installation constants. | ||
+ | # ----------------------- | ||
+ | # | ||
+ | # Define the level of debugging output to the standard output / logfile: | ||
+ | # 0= none, 1= input and output, 2= intermediate results too, 3= name mapping too | ||
+ | # | ||
+ | my $Debug = 0 ; # Flag: Enable debug output | ||
+ | |||
+ | # | ||
+ | # Define the parameters to reach the Xymon server. | ||
+ | # | ||
+ | my $XyDisp= $ENV{XYMONSERVERHOSTNAME} ; # Name of monitor server | ||
+ | my $XySend= $ENV{XYMON} ; # Monitor interface program | ||
+ | my $XyHome= $ENV{XYMONHOME} ; # Home directory | ||
+ | my $FmtDate= " | ||
+ | | ||
+ | # | ||
+ | # Define the command to send to Xymon to retrieve the current configuration | ||
+ | # lines containing the bindstat test parameters. | ||
+ | # | ||
+ | my $XyGrep= " | ||
+ | |||
+ | # | ||
+ | # Define the default values for the parameters which can be specified with the | ||
+ | # RNAMED keyword in the Xymon hosts configuration file. | ||
+ | # | ||
+ | my $DefPar= { source | ||
+ | statsfile => '/ | ||
+ | testname | ||
+ | title => 'DNS statistics' | ||
+ | } ; | ||
+ | |||
+ | # | ||
+ | # Define the mapping of the long, hierarchical names of the statistics onto | ||
+ | # pairs of an RRD file name and a shorter, RRD-compatible name. | ||
+ | # | ||
+ | my %MapName= ( | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | |||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | |||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ) ; | ||
+ | |||
+ | # | ||
+ | # Define the printf format to write a statistic, DS name and value, into the | ||
+ | # ' | ||
+ | # COUNTER, in order to avoid huge spikes whenever the BIND service is restarted. | ||
+ | # A restart will result in a negative value, which is suppressed from the graph | ||
+ | # by setting the minimal value to zero. | ||
+ | # | ||
+ | my $DsDef= " | ||
+ | |||
+ | |||
+ | # | ||
+ | # Global variables. | ||
+ | # ----------------- | ||
+ | # | ||
+ | my $Result= '' | ||
+ | my @Work = () ; # Parameters from Xymon hosts config | ||
+ | my %Stat = () ; # Bind statistics | ||
+ | my @Lines = () ; # Just a bunch of line images | ||
+ | my $I ; # Loop control variable | ||
+ | |||
+ | # | ||
+ | # Function LogMessage is invoked to output a debugging message. | ||
+ | # | ||
+ | sub LogMessage($) { | ||
+ | my $Msg= shift ; chomp $Msg ; # Line without end-of-line | ||
+ | my $Now= localtime ; # Build object with UTS | ||
+ | $Now= $Now-> | ||
+ | |||
+ | my $Clr= (caller(1))[3] ; # Name of calling function | ||
+ | $Clr= ' | ||
+ | $Clr=~ s/^.+\:\:// ; # Remove package name | ||
+ | |||
+ | print "$Now $Clr: $Msg\n" | ||
+ | } # of LogMessage | ||
+ | |||
+ | # | ||
+ | # Function BuildEpochTime takes two strings, one describing a date, the other | ||
+ | # one describing a time. It returns the associated epoch time. | ||
+ | # | ||
+ | sub BuildEpochTime($$) { | ||
+ | my @Time= ( 0 ) ; # Elements of date and time | ||
+ | my $Time ; # Time stamp | ||
+ | |||
+ | push @Time, $2, $1 if $_[1]=~ m/ | ||
+ | push @Time, $1, $2, $3 if $_[0]=~ m/ | ||
+ | return 0 unless scalar(@Time) == 6 ; | ||
+ | $Time[4]-- ; # Adjust month ordinal | ||
+ | return timelocal( @Time ) ; # Timestamp | ||
+ | } # of BuildEpochTime | ||
+ | |||
+ | # | ||
+ | # Function BuildWorkList takes the lines from the Xymon hosts configuration | ||
+ | # file, extracts the relevant parts and saves them in list @Work. | ||
+ | # | ||
+ | sub BuildWorkList() { | ||
+ | my ($IP, | ||
+ | my ($RNamed, | ||
+ | my $W ; # Ref to element of @Work | ||
+ | |||
+ | $I= -1 ; | ||
+ | foreach ( @Lines ) { | ||
+ | $I++ ; %{$Work[$I]}= %$DefPar ; # Copy default values | ||
+ | $W= $Work[$I] ; # Short cut | ||
+ | $$W{ERROR}= 0 ; # No error found (yet) | ||
+ | LogMessage( " | ||
+ | |||
+ | # Handle the fixed Xymon parameters, the IP address and the name of the host. | ||
+ | chomp ; | ||
+ | ($IP, | ||
+ | $$W{IP}= $IP ; $$W{Host}= $HN ; # Save host parameters | ||
+ | |||
+ | # Handle keyword RNAMED: extract all its parameters. Two formats are allowed, | ||
+ | # one in which the whole parameter string is enclosed between double quotes and | ||
+ | # one in which the part after RNAMED: is enclosed between double quotes. | ||
+ | ($RNamed, | ||
+ | unless ( defined $RNamed ) { | ||
+ | ($RNamed, | ||
+ | $RNamed=~ s/ | ||
+ | } # of unless | ||
+ | LogMessage( " | ||
+ | |||
+ | while ( $Pars=~ s/ | ||
+ | $$W{$1}= $2 ; | ||
+ | LogMessage( " | ||
+ | } # of while | ||
+ | if ( $Pars ) { | ||
+ | Logmessage( "Error at $HN in \" | ||
+ | $$W{ERROR}= 1 ; | ||
+ | } # of unless | ||
+ | } # of foreach | ||
+ | } # of BuildWorkList | ||
+ | |||
+ | # | ||
+ | # Function PolishName returns the input string, after replacing all | ||
+ | # non-alphanumeric characters by an underscore. | ||
+ | # | ||
+ | sub PolishName($) { | ||
+ | my $Name= $_[0] ; # Fetch name | ||
+ | $Name=~ tr/ | ||
+ | $Name=~ s/_{2,}//g ; | ||
+ | LogMessage( " \" | ||
+ | return $Name ; | ||
+ | } # of PolishName | ||
+ | |||
+ | # | ||
+ | # Function Recent takes a time stamp and returns that value if the time stamp | ||
+ | # lies between now and 10 minutes ago. If the clock of the DNS server is up to | ||
+ | # 10 seconds ahead wrt the time on the Xymon server, the current time at the | ||
+ | # Xymon server is returned. In all other cases it will return a false value. | ||
+ | # | ||
+ | sub Recent($$) { | ||
+ | my $Age= time - $_[1] ; # Age | ||
+ | if ( $Age < -10 ) { | ||
+ | LogMessage( " | ||
+ | return 0 ; # Return a false value | ||
+ | } elsif ( $Age < 0 ) { | ||
+ | return time; # Correct for time slack | ||
+ | } elsif ( $Age > 600 ) { | ||
+ | LogMessage( " | ||
+ | return 0 ; # Return a false value | ||
+ | } else { | ||
+ | return $_[1] ; # Return a true value | ||
+ | } # of else | ||
+ | } # of Recent | ||
+ | |||
+ | # | ||
+ | # Function QueryServer retrieves the dns statistics from one server. All | ||
+ | # information needed is passed in the work list entry. The retrieved information | ||
+ | # is written to global list @Lines. | ||
+ | # | ||
+ | # The script to be executed at the (remote) bind server is stripped to the | ||
+ | # minimum. Add error detection and a session time limit. | ||
+ | # | ||
+ | sub QueryServer($) { | ||
+ | my $W= shift ; # Ref to worklist item | ||
+ | |||
+ | my $SHost= (split(/ | ||
+ | my $Cmd = $$W{cmd} ; # Retrieve command | ||
+ | if ( index($Cmd,' | ||
+ | $Cmd.= " $$W{Host}" | ||
+ | } else { | ||
+ | $Cmd=~ s/ | ||
+ | $Cmd=~ s/ | ||
+ | $Cmd=~ s/ | ||
+ | } #of else | ||
+ | |||
+ | if ( $$W{source} eq ' | ||
+ | my $StdIn= "{ echo \"cat $$W{statsfile}\" | ||
+ | $Cmd= " | ||
+ | } # of if | ||
+ | |||
+ | LogMessage( " Cmd = $Cmd" ) if $Debug ; | ||
+ | @Lines= `$Cmd` ; # Retrieve standard output | ||
+ | } # of QueryServer | ||
+ | |||
+ | # | ||
+ | # Function ParseBindStatistics takes the raw statistics of a BIND server and | ||
+ | # stores it in a multi-level data-structure. | ||
+ | # | ||
+ | sub ParseBindStatistics($) { | ||
+ | my $W= shift ; # Ref to worklist item | ||
+ | my $Section= '' | ||
+ | my $View = '' | ||
+ | my ($Var,$Val) ; | ||
+ | |||
+ | foreach ( @Lines ) { | ||
+ | chomp ; | ||
+ | next if m/^\s*$/ ; # Skip empty line | ||
+ | next if m/^---/ ; # Skip end-of-statistics line | ||
+ | next if m/ | ||
+ | |||
+ | |||
+ | if ( m/^\+\+\+ Statistics Dump \+\+\+ \((\d+)\)/ ) { | ||
+ | $Val= Recent( $$W{Host}, $1 ) ; # Check time of measurement | ||
+ | return unless $Val ; # Return if not a recent sample | ||
+ | $Stat{ToM}= $Val ; # Save time of measurement | ||
+ | $Section= ' | ||
+ | $View = '' | ||
+ | |||
+ | } elsif ( m/ | ||
+ | $Section= PolishName( $1 ) ; | ||
+ | $View = '' | ||
+ | } elsif ( m/ | ||
+ | $View = ' | ||
+ | } elsif ( m/ | ||
+ | $View = $1 ; | ||
+ | |||
+ | } elsif ( m/ | ||
+ | $Val= $1 ; $Var= PolishName( $2 ) ; | ||
+ | $Stat{Long}{$Section}{$View}{$Var}= $Val ; | ||
+ | LogMessage( " $Section - $View - $Var = $Val" ) if $Debug ; | ||
+ | } elsif ( m/ | ||
+ | $Var= PolishName( $1 ) ; $Val= $2 ; | ||
+ | $Stat{Long}{$Section}{$View}{$Var}= $Val ; | ||
+ | LogMessage( " $Section - $View - $Var = $Val" ) if $Debug ; | ||
+ | |||
+ | } else { | ||
+ | LogMessage( "Error - Unexpected BIND statistics line at $$W{Host}" | ||
+ | LogMessage( " | ||
+ | $$W{ERROR}= 1 ; | ||
+ | } # of else | ||
+ | } # of foreach | ||
+ | |||
+ | } # of ParseBindStatistics | ||
+ | |||
+ | # | ||
+ | # Function ParseDnscmdStatistics uses the hierarchical structure of the Windows | ||
+ | # DNS service statistics to identify and extract all values. | ||
+ | # | ||
+ | # The statistics are divided into sections, each section is divided in one or | ||
+ | # more views. A view consists of one or more lines containing the name of a | ||
+ | # variable and its value. A section starts with the section name in the leftmost | ||
+ | # column and is followed by a line of dashes. A view has the same format as a | ||
+ | # section, but it is not followed by a line of dashes. Moreover, a view can be | ||
+ | # given a value. This is considered to be a shorthand notation, that is | ||
+ | # < | ||
+ | # is considered to be equivalent to | ||
+ | # < | ||
+ | # < | ||
+ | # Within a view two levels of variables are defined, a top-level and a | ||
+ | # sub-level. The indentation of a top-level line is short, the indentation of | ||
+ | # a sub-level line is longer. Each section has its own indentation settings. | ||
+ | # Sub-level lines are ignored. | ||
+ | # | ||
+ | # The statistics are augmented with a time stamp at the start. The first line | ||
+ | # specifies the date, using format dd-mm-yyyy, the second line the time of | ||
+ | # collecting the statistics, using format hh:mm. | ||
+ | # | ||
+ | sub ParseDnscmdStatistics($) { | ||
+ | my $W= shift ; # Ref to worklist entry | ||
+ | my $Candidate= undef ; # Name of section or view | ||
+ | my $Section | ||
+ | my $View = undef ; # Name of view | ||
+ | my $Variable = undef ; # Name of variable | ||
+ | my $Value | ||
+ | my $Indent0 | ||
+ | my $Indent1 | ||
+ | |||
+ | return if @Lines < 4 ; | ||
+ | my $Date= shift @Lines ; # Fetch time of this set of | ||
+ | my $Time= shift @Lines ; # | ||
+ | $Time= BuildEpochTime( $Date, $Time ) ; | ||
+ | $Time= Recent( $$W{Host}, $Time ) ; | ||
+ | return unless $Time ; # Exit if sample is outdated | ||
+ | $Stat{ToM}= $Time ; # Save time of retrieval | ||
+ | |||
+ | foreach ( @Lines ) { | ||
+ | chomp ; # Remove trailing Lf | ||
+ | s/\cM$// ; # Remove trailing Cr | ||
+ | s/\(.+?\)// ; # Remove (text) | ||
+ | next if m/^\s*$/ ; # Skip an empty line | ||
+ | |||
+ | # Handle a name found on the previous line, which can be either the name of | ||
+ | # a section of the name of a view. | ||
+ | if ( defined $Candidate ) { | ||
+ | if ( m/^\-+\s*$/ ) { | ||
+ | $Section= $Candidate ; | ||
+ | $Candidate= undef ; | ||
+ | $View | ||
+ | $Indent0 | ||
+ | $Variable = undef ; | ||
+ | next ; | ||
+ | } else { | ||
+ | $View= $Candidate ; | ||
+ | $Candidate= undef ; | ||
+ | $Variable = undef ; | ||
+ | } # of else | ||
+ | } # of if | ||
+ | |||
+ | # Handle the (potential) section header. | ||
+ | if ( m/ | ||
+ | $Candidate= PolishName( $1 ) ; | ||
+ | |||
+ | # Handle the view header. | ||
+ | # } elsif ( m/ | ||
+ | # | ||
+ | } elsif ( m/ | ||
+ | $View= PolishName( $1 ) ; | ||
+ | $Variable= $View ; | ||
+ | $Value | ||
+ | $Stat{Long}{$Section}{$View}{$Variable}= $Value ; | ||
+ | LogMessage( " $Section - $View - $Variable = $Value" | ||
+ | |||
+ | # Handle a line, either a top-level of a sub-level definition. | ||
+ | } elsif ( ! defined $Indent0 ) { | ||
+ | if ( m/ | ||
+ | $Indent0 = $1 ; # Indentation of top-level | ||
+ | $Indent1 = $1 . ' | ||
+ | $Variable= PolishName( $2 ) ; | ||
+ | $Value | ||
+ | $View= ' | ||
+ | $Stat{Long}{$Section}{$View}{$Variable}= $Value ; | ||
+ | LogMessage( " $Section - $View - $Variable = $Value" | ||
+ | } else { | ||
+ | LogMessage( "Error - Unexpected WinDns statistics line at $$W{Host}" | ||
+ | LogMessage( " | ||
+ | $$W{ERROR}= 1 ; | ||
+ | } # of else | ||
+ | } elsif ( m/ | ||
+ | $Variable= PolishName( $1 ) ; | ||
+ | $Value | ||
+ | $View= ' | ||
+ | $Stat{Long}{$Section}{$View}{$Variable}= $Value ; | ||
+ | LogMessage( " $Section - $View - $Variable = $Value" | ||
+ | } elsif ( m/ | ||
+ | next ; # Ignore another deeper level | ||
+ | } elsif ( m/ | ||
+ | next ; # Ignore this deep statistic | ||
+ | |||
+ | # Handle the remaining cases. | ||
+ | } elsif ( m/^Command completed successfully./ | ||
+ | last ; # End of statistics found | ||
+ | } else { | ||
+ | LogMessage( "Error - Unexpected WinDns statistics line at $$W{Host}" | ||
+ | LogMessage( " | ||
+ | $$W{ERROR}= 1 ; | ||
+ | } # of else | ||
+ | } # of foreach | ||
+ | |||
+ | } # of ParseDnscmdStatistics | ||
+ | |||
+ | # | ||
+ | # Function ParseStatistics invokes the appropriate parser, depending on the | ||
+ | # source of the statistical information. | ||
+ | # | ||
+ | sub ParseStatistics($) { | ||
+ | my $W= shift ; # Ref to work list item | ||
+ | |||
+ | if ( $$W{source} eq ' | ||
+ | ParseBindStatistics( $W ) ; | ||
+ | } elsif ( $$W{source} eq ' | ||
+ | ParseDnscmdStatistics( $W ) ; | ||
+ | } # of elsif | ||
+ | } # of ParseStatistics | ||
+ | |||
+ | # | ||
+ | # Function MapName takes the results of one BIND server. It maps the (long) | ||
+ | # names of the variables onto short ones, which are usable as DS-names in an | ||
+ | # RRD file. At the same time, a default value is given for unknown variables. | ||
+ | # | ||
+ | sub MapName() { | ||
+ | my $LongName ; # Fully qualified name | ||
+ | my $M ; # Ref into statistics result hash | ||
+ | |||
+ | foreach my $Section | ||
+ | foreach ( keys %MapName ) { | ||
+ | next unless index($_, | ||
+ | $M= $MapName{$_} ; | ||
+ | $Stat{Short}{$$M[0]}{$$M[1]}= 0 ; | ||
+ | } # of foreach | ||
+ | # | ||
+ | foreach my $View ( keys %{$Stat{Long}{$Section}} ) { | ||
+ | foreach my $Stat ( keys %{$Stat{Long}{$Section}{$View}} ) { | ||
+ | $LongName= " | ||
+ | $LongName=~ s/\.\./\./ ; # Remove empty view name | ||
+ | next unless exists $MapName{$LongName} ; | ||
+ | |||
+ | LogMessage( "save \" | ||
+ | $M= $MapName{$LongName} ; # Ref into mapping | ||
+ | $Stat{Short}{$$M[0]}{$$M[1]}= $Stat{Long}{$Section}{$View}{$Stat} ; | ||
+ | } # of foreach | ||
+ | } # of foreach | ||
+ | } # of foreach | ||
+ | } # of MapName | ||
+ | |||
+ | # | ||
+ | # Build the Xymon message and inform Xymon. The message can be sent in one of | ||
+ | # two formats: a ' | ||
+ | # the name of the RRD file and the DS definition, requiring no additional config | ||
+ | # of Xymon. The latter shows up as a separate column with a graph, and requires | ||
+ | # the use of an additional server-side script (and configuration) to distribute | ||
+ | # the NCV data to the RRD files. | ||
+ | # | ||
+ | sub InformXymon($) { | ||
+ | my $Work = shift ; # Ref to work descriptor | ||
+ | my $XyTest= $$Work{testname} ; # Name of test | ||
+ | my $XyHost= $$Work{Host} ; # Name of BIND server | ||
+ | my $Colour= ' | ||
+ | my $Now ; # Time of retrieval of statistics | ||
+ | my ($Rrd,$DS) ; # Loop control variables | ||
+ | my $Key ; | ||
+ | |||
+ | if ( $XyTest ne ' | ||
+ | $Now= localtime( $Stat{ToM} ) ; | ||
+ | $Now= $Now-> | ||
+ | # | ||
+ | # Save the results in NCV format, using extended names which include the name | ||
+ | # of the RRD file. In Xymon, this data is channeled to a script defined in | ||
+ | # the --extra-script parameter of rrdstatus. | ||
+ | # | ||
+ | $Result = "< | ||
+ | foreach $Rrd ( sort keys %{$Stat{Short}} ) { | ||
+ | foreach $DS ( sort keys %{$Stat{Short}{$Rrd}} ) { | ||
+ | $Key= $Rrd ; $Key=~ s/\.rrd$// ; | ||
+ | $Result.= " | ||
+ | } # of foreach | ||
+ | } # of foreach | ||
+ | $Result.= " | ||
+ | |||
+ | $Result = " | ||
+ | " | ||
+ | $Result . " | ||
+ | `$XySend $XyDisp $Result` ; # Inform Xymon | ||
+ | |||
+ | } else { # Format is ' | ||
+ | # | ||
+ | # Save the results in the special ' | ||
+ | # or more sections formatted like: | ||
+ | # | ||
+ | # | ||
+ | # | ||
+ | $Result= '' | ||
+ | foreach $Rrd ( sort keys %{$Stat{Short}} ) { | ||
+ | next unless keys %{$Stat{Short}{$Rrd}} ; | ||
+ | $Result.= " | ||
+ | foreach $DS ( sort keys %{$Stat{Short}{$Rrd}} ) { | ||
+ | $Result.= sprintf( $DsDef, $DS, $Stat{Short}{$Rrd}{$DS} ) ; | ||
+ | } # of foreach | ||
+ | } # of foreach | ||
+ | |||
+ | if ( $Result ne '' | ||
+ | $Result =" | ||
+ | LogMessage( " | ||
+ | `$XySend $XyDisp $Result` ; # Inform Xymon | ||
+ | } # of if | ||
+ | } # of else | ||
+ | } # of InformXymon | ||
+ | |||
+ | |||
+ | # | ||
+ | # MAIN PROGRAM. | ||
+ | # ------------- | ||
+ | # | ||
+ | unless ( defined $XyDisp ) { | ||
+ | LogMessage( ' | ||
+ | exit ; | ||
+ | } # of unless | ||
+ | |||
+ | @Lines= `$XyGrep` ; # Get list of DNS servers | ||
+ | exit unless @Lines ; # Stop if nothing to do | ||
+ | BuildWorkList ; # Build nice list of work to do | ||
+ | |||
+ | foreach ( @Work ) { | ||
+ | next if $$_{ERROR} ; # Skip in case of RNAMED syntax error | ||
+ | %Stat= () ; # Clear statistics save area | ||
+ | QueryServer( $_ ) ; # Query next server | ||
+ | ParseStatistics( $_ ) ; # Extract statistics and format them | ||
+ | MapName ; # Map long name onto short RRD name | ||
+ | InformXymon( $_ ) ; # Send data to Xymon | ||
+ | } # of foreach | ||
+ | |||
</ | </ | ||
</ | </ | ||
+ | ==== rrd-status.pl ==== | ||
+ | |||
+ | If the statistics are sent to Xymon using a status message, a script like the one below is needed to parse the NCV data and pass them to an RRD. | ||
+ | |||
+ | <hidden onHidden=" | ||
+ | < | ||
+ | # | ||
+ | # | ||
+ | # This script handles a list of NCVs, send by a Xymon client, and prepares it to | ||
+ | # be stored in an RRA. This script is used in cases in which a fixed-size group | ||
+ | # of two or more values should be put together into a single RRA. The algorithm | ||
+ | # is specific for each test / client. | ||
+ | # | ||
+ | # This script is invoked with three parameters: the name of the host, the name | ||
+ | # of the test and the name of the file containing the message sent by the | ||
+ | # client, containing the NCVs to be handled. | ||
+ | # | ||
+ | use strict; | ||
+ | |||
+ | # | ||
+ | # Installation constants. | ||
+ | # ----------------------- | ||
+ | # | ||
+ | # %Struct defines the datasets of the various tests. | ||
+ | # | ||
+ | my %Struct= ( | ||
+ | bindstats => [ # Must be sorted! | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | ) ; # of %Struct | ||
+ | |||
+ | # | ||
+ | # Global variables. | ||
+ | # ----------------- | ||
+ | # | ||
+ | my ( $HostName, $TestName, $FileName )= @ARGV ; | ||
+ | # | ||
+ | my %Var= () ; # Save area measurements | ||
+ | my ( $Line, @Line ) ; # List of values of one measurement | ||
+ | my $key ; # Loop control variable | ||
+ | |||
+ | |||
+ | # | ||
+ | # Main program. | ||
+ | # ------------- | ||
+ | # | ||
+ | |||
+ | # | ||
+ | # An attempt has been undertaken to make this code a little bit more general. | ||
+ | # The name of an NCV should consist of two names separated by "/" | ||
+ | # name becomes (part of) the name of the RRA, the second name becomes the | ||
+ | # name of the DS. The DS-ses are written in sorted order. | ||
+ | # | ||
+ | if ( $TestName eq ' | ||
+ | open( FH, "<", | ||
+ | while ( <FH> ) { | ||
+ | chomp ; | ||
+ | next unless m/ | ||
+ | $Var{$1}{$2}= $3 ; | ||
+ | } # of while | ||
+ | close( FH ) ; | ||
+ | |||
+ | print @{$Struct{$TestName}} ; | ||
+ | foreach $key ( sort keys %Var ) { | ||
+ | @Line= () ; | ||
+ | push @Line, $Var{$key}{$_} foreach ( sort keys %{$Var{$key}} ) ; | ||
+ | if ( $TestName eq $key ) { | ||
+ | print " | ||
+ | } else { | ||
+ | print " | ||
+ | } # of else | ||
+ | print join( ":", | ||
+ | } # of foreach | ||
+ | |||
+ | } # of if | ||
+ | |||
+ | exit 0 ; | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | ==== graphs.cfg, bindstats ==== | ||
+ | |||
+ | The following snippet defines a graph showing the collected BIND statistics. Add this snippet to / | ||
+ | |||
+ | <hidden onHidden=" | ||
+ | < | ||
+ | [bindstats] | ||
+ | TITLE , Bind query rates | ||
+ | YAXIS Rate [q/s] | ||
+ | -l 0 | ||
+ | DEF: | ||
+ | DEF: | ||
+ | DEF: | ||
+ | DEF: | ||
+ | DEF: | ||
+ | DEF: | ||
+ | CDEF: | ||
+ | AREA: | ||
+ | GPRINT: | ||
+ | GPRINT: | ||
+ | GPRINT: | ||
+ | GPRINT: | ||
+ | AREA: | ||
+ | GPRINT: | ||
+ | GPRINT: | ||
+ | GPRINT: | ||
+ | GPRINT: | ||
+ | AREA: | ||
+ | GPRINT: | ||
+ | GPRINT: | ||
+ | GPRINT: | ||
+ | GPRINT: | ||
+ | AREA: | ||
+ | GPRINT: | ||
+ | GPRINT: | ||
+ | GPRINT: | ||
+ | GPRINT: | ||
+ | AREA: | ||
+ | GPRINT: | ||
+ | GPRINT: | ||
+ | GPRINT: | ||
+ | GPRINT: | ||
+ | LINE1: | ||
+ | GPRINT: | ||
+ | GPRINT: | ||
+ | GPRINT: | ||
+ | GPRINT: | ||
+ | LINE1: | ||
+ | GPRINT: | ||
+ | GPRINT: | ||
+ | GPRINT: | ||
+ | GPRINT: | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | ==== graphs.cfg, wdnsstats ==== | ||
+ | |||
+ | The following snippet defines a graph showing the collected Windows DNS statistics. | ||
+ | |||
+ | <hidden onHidden=" | ||
+ | < | ||
+ | To Be Defined | ||
+ | </ | ||
+ | </ | ||
===== Known Bugs and Issues ===== | ===== Known Bugs and Issues ===== | ||
By default, the statistics are presented to Xymon using a so-called ' | By default, the statistics are presented to Xymon using a so-called ' | ||
+ | |||
+ | This script is only tested using ' | ||
===== To Do ===== | ===== To Do ===== | ||