Differences

This shows you the differences between two versions of the page.

Link to this comparison view

monitors:check-client [2010/08/23 22:23] (current)
Line 1: Line 1:
 +====== client-check.pl ======
 +
 +^ Author | [[ dbaldwin@users.sf.net | David Baldwin ]] |
 +^ Compatibility | Xymon 4.2 |
 +^ Requirements | Perl, [[monitors:​XymonExt.pm]] |
 +^ Download | client-check.pl |
 +^ Last Update | 2010-07-29 |
 +
 +===== Description =====
 +
 +===== Installation =====
 +=== Client side ===
 +
 +=== Server side ===
 +
 +client-check.pl and XymonExt.pm in $BBHOME/ext
 +
 +===== Source =====
 +==== client-check.pl ====
 +
 +<hidden onHidden="​Show Code ⇲" onVisible="​Hide Code ⇱">​
 +<code perl>
 +#​!/​usr/​bin/​perl -w
 + 
 +#​*---------------------------------------------------------------------------*/​
 +#* client-check.pl : Xymon client message processor. ​                        */
 +#*                                                                           */
 +#* This perl program is a server-side module using the                       */
 +#* data sent by the Xymon clients. This program is fed data from the         */
 +#* Xymon "​client"​ channel via the hobbitd_channel program; each client ​      */
 +#* message is processed by looking at various sections and generates ​        */
 +#* a test status report when various conditions are met                      */
 +#*                                                                           */
 +#* Original 2007-Jan-28 by Henrik Storner <​henrik@hswn.dk> ​                  */
 +#* Modified 2008-2010 by David Baldwin <​david.baldwin@ausport.gov.au> ​       */
 +#*                                                                           */
 +#* This program is in the public domain, and may be used freely for          */
 +#* creating your own Xymon server-side modules. ​                             */
 +#*                                                                           */
 +#​*---------------------------------------------------------------------------*/​
 +#
 +# CONFIG START
 +
 +# default route where other than .1 address on subnet
 +my %netCheckRoutes = ( prod => { net => "​1.1",​ mask => "/​16",​ gw => "​1.1.0.10"​},​
 +      );
 +
 +# check minimum available memory in host under [free] section
 +my %memCheckTable = ( esx => { host => "​esx.*", ​ min => 799000},
 +      );
 +
 +# CONFIG END
 +
 +=pod
 +
 +=head1 NAME
 +
 + ​client-check.pl - Perl script to check client messages on xymon/​hobbit server
 +
 +=head1 SYNOPSIS
 +
 +Install script in F<​$BBHOME/​ext/​client-check.pl>​
 +
 +Ensure you also have F<​$BBHOME/​ext/​XymonExt.pm>​ installed
 +
 +Add following lines to F</​etc/​hobbit/​hobbitlaunch.cfg>​
 +
 + ​[client-check]
 + ENVFILE /​usr/​lib/​hobbit/​server/​etc/​hobbitserver.cfg
 + NEEDS hobbitd
 + CMD hobbitd_channel --channel=client --log=$BBSERVERLOGS/​client-check.log $BBHOME/​ext/​client-check.pl
 +
 +=head1 DESCRIPTION
 +
 +Requires: C<​$BBHOME>​ environment variable to be set
 +
 +Requires: F<​XymonExt.pm>​ module
 +
 +See CONFIG section at top of script
 +
 +=head1 TESTING
 +
 +If you wish to add your own tests, this script can be run against current client report for that host
 +
 +C<bbcmd client-check.pl -d MYHOST>
 +
 + ​2010-07-29 14:04:25 Using default environment file /​usr/​lib/​hobbit/​server/​etc/​hobbitserver.cfg
 + DBG: using clientlog for MYHOST (1.1.1.2)
 + DBG: clientID linux (linux)
 + /​usr/​lib/​hobbit/​server/​bin/​bb 1.1.1.1 "​status MYHOST.who green OK
 +
 + &​green No root login active
 +
 +
 + ​someuser pts/1        Jul 26 12:29 (1.1.1.3)
 + "​
 +
 + DBG: netCheck MYHOST (1.1.1.1) GW: 1.1.0.10 SM: 0.0.0.0
 + /​usr/​lib/​hobbit/​server/​bin/​bb 1.1.1.1 "​status MYHOST green OK
 +
 + &​green Default route OK
 +
 +
 + ​Kernel IP routing table
 + ​Destination ​    ​Gateway ​        ​Genmask ​        ​Flags ​  MSS Window ​ irtt Iface
 + ​1.1.0.0 ​        ​0.0.0.0 ​        ​255.255.0.0 ​    ​U ​        0 0          0 eth0
 + ​0.0.0.0 ​        ​1.1.0.10 ​       0.0.0.0 ​        ​UG ​       0 0          0 eth0
 + "​
 +
 +=cut
 + 
 +my $xymonlib;
 +BEGIN { $xymonlib = $ENV{BBHOME} && "​$ENV{BBHOME}/​ext"​ || "/​usr/​lib/​hobbit/​server/​ext";​ }
 + 
 +use strict;
 +use lib $xymonlib;
 +use XymonExt;
 +
 + 
 +my $bb;
 +my $bbdisp;
 + 
 +my $hostname = "";​
 +my $clientip = "";​
 +my $clientname = "";​
 +my $clientos = "";​
 +my $msgtxt = "";​
 +my %sections = ();
 +my $cursection = "";​
 + 
 +my $debug=0;
 +my $ipsubre = qr/​\d|\d\d|1\d\d|2[0-4]\d|25[0-5]/;​
 +my $ipre = qr/​$ipsubre\.$ipsubre\.$ipsubre\.$ipsubre/;​
 +$|=1;
 + 
 +# Get the BB and BBDISP environment settings.
 +$bb = $ENV{"​BB"​} || die "BB not defined";​
 +$bbdisp = $ENV{"​BBDISP"​} || die "​BBDISP not defined";​
 +
 +for(my $i=0; $i<​=$#​ARGV;​ $i++) {
 +  if($ARGV[$i] =~ /​^-(d|-debug)$/​) {
 +    $debug++;
 +  } elsif($i=$#​ARGV) {
 +    $debug ||= 1;
 +    $clientname=$ARGV[$i];​
 +  }
 +}
 + 
 + 
 +if($clientname ne ""​) {
 +  open CL,"bb $bbdisp \'​clientlog $clientname\'​|";​
 +  $hostname = $clientname;​
 +  $clientip = XymonExt->​HostIP($hostname);​
 +  warn "DBG: using clientlog for $hostname ($clientip)\n"​ if $debug;
 +} else {
 +  open CL,"/​dev/​stdin";​
 +}
 +# Main routine. ​
 +#
 +# This reads client messages from <CL>, looking for the
 +# delimiters that separate each message, and also looking for the
 +# section markers that delimit each part of the client message.
 +# When a message is complete, the processmessage() subroutine
 +# is invoked. $msgtxt contains the complete message, and the
 +# %sections hash contains the individual sections of the client ​
 +# message.
 + 
 +while (my $line = <CL>) {
 + if ($line =~ /​^\@\@client\#/​) {
 + # It's the start of a new client message - the header looks like this:
 + # @@client#​830759/​HOSTNAME|1169985951.340108|10.60.65.152|HOSTNAME|sunos|sunos
 + 
 + # Grab the hostname field from the header
 + my @hdrfields = split(/\|/, $line);
 + $hostname = $hdrfields[3];​
 + $hostname =~ s/,/./g;
 + XymonExt->​import;​ # if don't do this, won't see changes to bb-hosts
 + $clientip = XymonExt->​HostIP($hostname);​
 + warn "DBG: processing $hostname ($clientip)\n"​ if $debug;
 + 
 + # Clear the variables we use to store the message in
 + $msgtxt = "";​
 + %sections = ();
 + $cursection = "";​ # none found yet!
 +                $clientname = "";​
 +                $clientos = "";​
 + }
 + elsif ($line =~ /^\@\@/) {
 + # End of a message. Do something with it.
 + runTests();​
 + }
 + elsif ($line =~ /​^\[(.+)\]/​) {
 + # Start of new message section.
 + 
 + $cursection = $1;
 + $sections{ $cursection } = "​\n";​
 + }
 + elsif ($line =~ /​^client\s+\S+\.([^.]+)\s+(\w+)/​) {
 + # client header - used when testing against current client report
 + #   ​client esxsv01,​ausport,​gov,​au.linux linux
 + #   ​client sa01sv1.ausport.gov.au.bbwin win32
 +
 + $msgtxt .= $line;
 + 
 +                $clientname = $1;
 +                $clientos = $2;
 + warn "DBG: clientID $clientname ($clientos)\n"​ if $debug;
 + }
 + else {
 + # Add another line to the entire message text variable,
 + # and the the current section.
 + $msgtxt .= $line;
 + if($cursection) {
 + $sections{ $cursection } .= $line;
 + } else {
 + my $time = scalar localtime;
 + warn "$time client-check:​ no current section for client $hostname\n$line";​
 + }
 + }
 +}
 +# End of a message. Do something with it. Will only get here using clientlog (we hope)
 +runTests();
 +
 +sub runTests {
 +
 +    whoCheck();
 +    netCheck();
 +    memCheck();
 +}
 + 
 +sub report {
 + 
 + my($hostname,​ $hobbitcolumn,​ $color, $summary, $statusmsg) = @_;
 + # Build the command we use to send a status to the Xymon daemon
 + 
 + if($debug) {
 +   print "Would report:​\n\"​status " . $hostname . "​."​ . $hobbitcolumn . " " . $color . " " . $summary . "​\n"​ . $statusmsg . "​\"​\n";​
 + } else {
 + # And send the message
 +    ​XymonExt->​Report($hostname,​ $hobbitcolumn,​ $color, ​ "​$summary\n$statusmsg"​);​
 + }
 +}
 +
 +# This subroutine processes the client message. In this case,
 +# we watch the [who] section of the client message and alert
 +# if there is a root login active.
 + 
 +sub whoCheck {
 + my $color;
 + my $summary;
 + my $statusmsg;
 + my $sec = "​who";​
 + my $hobbitcolumn = "​who";​
 + 
 + # Dont do anything unless we have the "​who"​ section
 + return unless ( $sections{$sec} );
 + 
 + # Is there a "​root"​ login somewhere in the "​who"​ section?
 + # Note that we must match with /m because there are multiple
 + # lines in the [who] section.
 + if ( $sections{$sec} =~ /^root /m ) {
 + $color = "​yellow";​
 + $summary = "ROOT login active";​
 + $statusmsg = "&​yellow ROOT login detected!\n\n"​ . $sections{$sec};​
 + }
 + else {
 + $color = "​green";​
 + $summary = "​OK";​
 + $statusmsg = "&​green No root login active\n\n"​ . $sections{$sec};​
 + }
 + report($hostname,​ $hobbitcolumn,​ $color, $summary, $statusmsg);​
 +}
 +
 +# This subroutine processes the client message. In this case,
 +# we watch the [route] section of the client message and alert
 +# if the default route is incorrect
 + 
 +sub netCheck {
 + my $color;
 + my $summary;
 + my $statusmsg;
 + my $sec = "​route";​
 + my $hobbitcolumn = "​route";​
 + 
 + # Dont do anything unless we have the "​route"​ section
 + return unless ( $sections{$sec} );
 + 
 + # Is there a default gateway somewhere in the "​route"​ section?
 + # Note that we must match with /m because there are multiple
 + # lines
 + $color = "​green";​
 + $summary = "​OK";​
 + $statusmsg = "&​green Default route OK\n\n"​ . $sections{$sec};​
 + #darwin form:
 + # default ​           1.1.0.1 ​           UGSc        6       ​91 ​   en0
 + # 1.1/​16 ​            ​link#​4 ​            ​UCS ​       40        0    en0
 + # 1.1.0.1 ​           0:​11:​11:​11:​11:​87 ​  ​UHLW ​       0        0    en0   1196
 +
 + if ( $sections{$sec} =~ /​^(?:​0\.0\.0\.0|default)\s+($ipre)\s+($ipre|\w+)\s+(\S+)\s/​m ) {
 + my $gw = ($clientname eq "​bbwin"​) ? $2 : $1;
 + my $sm = ($clientname eq "​bbwin"​) ? $1 : $2;
 + warn "DBG: netCheck $hostname ($clientip) GW: $gw SM: $sm\n" if $debug;
 + my $isok = 1;
 + my $found = 0;
 + foreach my $sn (keys %netCheckRoutes) {
 +     my %snr = %{$netCheckRoutes{$sn}};​
 +     warn "DBG: NET: $sn IP: $snr{net} GW: $snr{gw} SM: $snr{mask}\n"​ if $debug;
 +     if(! exists $snr{ipre}) {
 + my $netre = $snr{net};
 + $netre =~ s/\./\\./g;
 + $netre =~ s/\.$//;
 +         $netCheckRoutes{$sn}{ipre} = qr/​^$netre\./;​
 +     }
 +     if( $clientip =~ $netCheckRoutes{$sn}{ipre} ) {
 +         $found = 1;
 + $isok = $gw eq $snr{gw};
 + $summary = "Found defgw: $gw OK";
 + last
 +     }
 + }
 + if( !$found && $gw =~ /​^(.*)\.1$/​) {
 +         my $net = $1;
 +         warn "DBG: DEFAULT IP: $net GW: $gw SM: /24\n" if $debug;
 + $net =~ s/\./\\./g;
 + $isok = 0 unless $clientip =~ /^$net/;
 + $summary = "Found defgw: $gw OK" if $isok;
 + }
 + if(!$isok) {
 + $color = "​red";​
 + $summary = "Bad default route $gw for $clientip found";​
 + $statusmsg = "&​red Bad default route $gw for $clientip found\n\n"​ . $sections{$sec};​
 + }
 + } else {
 + $color = "​red";​
 + $summary = "​Error";​
 + $statusmsg = "&​red No default route found\n\n"​ . $sections{$sec};​
 + }
 + 
 + report($hostname,​ $hobbitcolumn,​ $color, $summary, $statusmsg);​
 +}
 +
 +# This subroutine processes the client message. In this case,
 +# we watch the [free] section of the client message and alert
 +# if the machine is an ESX server and the physical memory size is not 800MB
 + 
 +sub memCheck {
 + my $color;
 + my $summary;
 + my $statusmsg;
 + my $sec = "​free";​
 + my $hobbitcolumn = "​pmem";​
 + my $min;
 + 
 + # Dont do anything unless we have the "​free"​ section in host starting with "​esx"​
 + return unless ( $sections{$sec} );
 + my $found = 0;
 + foreach my $mh (keys %memCheckTable) {
 +     my %mhr = %{$memCheckTable{$mh}};​
 +     warn "DBG: HOST: $mh MIN: $mhr{min}\n"​ if $debug;
 +     if(! exists $mhr{hostre}) {
 + my $hostre = $mhr{host};
 + $memCheckTable{$mh}{hostre} = qr/​^$hostre\./;​
 +     }
 +     $found = $hostname =~ $memCheckTable{$mh}{hostre};​
 +     $min = $mhr{min};
 +     last if $found;
 + }
 + return unless ( $found );
 + 
 + # Get the Mem: line
 + # Note that we must match with /m because there are multiple
 + # lines
 + $color = "​green";​
 + $summary = "​OK";​
 +
 + if ( $sections{$sec} =~ /​^Mem:​\s+(\d+)\s+(\d+)\s+(\d+)\s/​m ) {
 + my $tot = $1;
 +         $statusmsg = sprintf("&​green memory allocation %dMB OK (at least %dMB)\n\n",​$tot/​1024,​$min/​1024) . $sections{$sec};​
 + warn "DBG: memCheck $hostname ($clientip) PhysMEM: $tot\n"​ if $debug;
 + my $isok = $tot > $min;
 + if(!$isok) {
 + $color = "​red";​
 + $summary = sprintf("​Bad memory allocation %dMB for %s found (require at least %dMB)",​$tot/​1024,​$hostname,​$min/​1024);​
 + $statusmsg = "&​red ". sprintf("​Bad memory allocation %dMB for %s found (require at least %dMB)\n\n",​$tot/​1024,​$hostname,​$min/​1024) . $sections{$sec};​
 + }
 + } else {
 + $color = "​red";​
 + $summary = "​Error";​
 + $statusmsg = "&​red No Mem: line found\n\n"​ . $sections{$sec};​
 + }
 + 
 + report($hostname,​ $hobbitcolumn,​ $color, $summary, $statusmsg);​
 +}
 +
 +</​code>​
 +</​hidden>​
 +
 +===== Known  Bugs and Issues =====
 +
 +===== To Do =====
 +
 +===== Credits =====
 +
 +original rootlogin.pl script from Henrik
 +
 +===== Changelog =====
 +
 +  * **2010-07-29**
 +    * Initial release
  
  • monitors/check-client.txt
  • Last modified: 2010/08/23 22:23
  • (external edit)