====== 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 ==== #!/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 */ #* Modified 2008-2010 by David Baldwin */ #* */ #* 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 [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 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 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 , 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 = ) { 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); } ===== Known Bugs and Issues ===== ===== To Do ===== ===== Credits ===== original rootlogin.pl script from Henrik ===== Changelog ===== * **2010-07-29** * Initial release