monitors:yum

Yum

Author Wim Nelis
Compatibility Xymon 4.2
Requirements Perl
Download None
Last Update 2017-01-10

This client-side script queries the servers in RedHatNetwork (RHN) for outstanding updates. Additionally, the classification (security level) of each of the updates is retrieved. The number of outstanding updates per class is shown in a table. An alert is generated if there are important updates to be installed.

Client side

Install script yum.pl in subdirectory ~xymon/client/ext.

Show Code ⇲

Hide Code ⇱

#!/usr/bin/perl -w
#
# This script determines the number of outstanding updates (patches) for this
# RHEL host. The count is reported to the Xymon monitoring server. This script
# requires that:
#  A- the yum-security plug-in is installed, and
#  B- that sudo is configured to have this script run `yum`.
#
# Written by W.J.M. Nelis, wim.nelis@nlr.nl
#
use strict ;
use Time::Piece ;                       # Format time

#
# Installation constants.
#
my $XyDisp= exists $ENV{XYMSRV} ? $ENV{XYMSRV} : $ENV{XYMONSERVERHOSTNAME} ;
my $XySend= $ENV{XYMON} ;               # Monitor interface program
my $XyLife= '+13h' ;                    # Status lifetime, default 30m
my $FmtDate= "%Y.%m.%d %H:%M:%S" ;      # Default date format
   $FmtDate= $ENV{XYMONDATEFORMAT} if exists $ENV{XYMONDATEFORMAT} ;
my $TestName= 'update' ;                # Test name

my @ColourOf= ( 'red', 'yellow', 'clear', 'green' ) ;
#
# Define the names and the status colours for the various classes of updates.
# The names are chosen such that the order sorted on name matches an increased
# urge to install.
#
my %Class= (
     'anonymous'        => [ 'Anonymous',   2 ],        # Catch undefined classes
     'bugfix'           => [ 'Bugfix',      3 ],
     'enhancement'      => [ 'Enhancement', 3 ],
     'Low/Sec.'         => [ 'Low',         3 ],
     'Moderate/Sec.'    => [ 'Moderate',    1 ],
     'Important/Sec.'   => [ 'Serious',     1 ],
     'Critical/Sec.'    => [ 'Vulnerable',  0 ],
     'security'         => [ 'Vulnerable',  0 ],
   ) ;
  $Class{$Class{$_}[0]}= $Class{$_}     foreach ( keys %Class ) ;

#
# Variable $YumCmd defines the shell command to retrieve the list of outstanding
# patches. The return codes of this command are:
#    0 - no updates
#    1 - error
#  100 - updates available
# Variable $YumClass defines the shell command to retrieve a list of updates,
# including the classification of each of the updates. This list contains
# duplicates of updates which are NOT listed by check-update!
#
my $YumCmd  = 'sudo yum check-update  2>&1' ;   # Retrieve list of patches
my $YumClass= 'sudo yum list-security 2>&1' ;   # Retrieve list of classifications

#
# Global variable allocation.
#
my $Now= localtime ;                    # Timestamp of tests
   $Now= $Now->strftime( $FmtDate ) ;
my $Colour= 3 ;                         # Test status
my $SubColour ;                         # Status of one update class
my $HostName = `hostname` ;             # Host under test
   chomp $HostName ;
my $Result ;                            # Status message for xymon
my $Count ;                             # Number of updates
my %Update= () ;                        # List of updates
my $Update ;                            # Full name of an update
my %ClassLst= () ;                      # List of updates per class
my %ClassCnt= () ;                      # Updates per class
my $Class ;                             # Classification of an update
my @Lines ;                             # Output of $YumCmd
my $Lines ;                             # Concatenated output
my (@PrvLine,@Fields) ;                 # Field on a line image

sub min($$) { return $_[0] < $_[1] ? $_[0] : $_[1] ; }

#
# Function InformXymon sends the message, in global variable $Result, to the
# xymon server.
#
sub InformXymon() {
  $Result= "\"status$XyLife $HostName.$TestName $ColourOf[$Colour] $Now\n" .
           "$Result\"\n" ;
  `$XySend $XyDisp $Result` ;           # Inform Xymon

  $Result= '' ;                         # Reset message parameters
  $Colour=  3 ;
}  # of InformXymon

##
## M A I N   P R O G R A M
## -----------------------
##

@Lines = `$YumCmd` ;                    # Retrieve list of updates
$Lines = join( '', @Lines ) ;           # Single string for error checks
$Result= undef ;                        # Preset message for Xymon

#
# Check the output of the command for some error conditions.
#
if ( @Lines == 0 ) {
  $Colour= 2 ;                  # Measurement failed
  $Result= 'No update status received' ;
} elsif ( $Lines[0] !~ m/^Loaded plugins/ ) {
  $Colour= 2 ;                  # Measurement failed
  $Result= "Unexpected update status received:\n" ;
  foreach ( @Lines ) {
    chomp ;
    $Result.= "  $_\n" ;
  }  # of foreach
} elsif ( $Lines =~ m/run this command as root/ ) {
  $Colour= 2 ;
  $Result= 'No update status received, insufficient privileges' ;
} elsif ( $Lines=~ m/There was an error communicating with RHN/ ) {
  $Colour= 2 ;
  $Result= 'Communication with RHN failed' ;
} else {

  $ClassCnt{$Class{$_}[0]}= 0   foreach ( keys %Class ) ;
  $Count= 0 ;
#
# Check the output for the number of outstanding updates.
#
  unless ( defined $Result ) {
    foreach ( @Lines ) {
      chomp ;
      last                      if m/^Obsoleting Packages/ ;
      @Fields= split ;

 # Handle an incomplete line. If the name or the version number are long
 # strings, the fields may be presented on two lines in stead of one.
      if ( @Fields < 3 ) {
        if ( @PrvLine ) {
          unshift @Fields, @PrvLine ;
        } else {
          @PrvLine= @Fields ;
          next ;
        }  # of else
      }  # of if
      @PrvLine= () ;

      next                      unless @Fields == 3 ;
      next                      unless $Fields[1] =~ m/^\d/ ;
      $Count++ ;
      $Update= $Fields[0] ;             # Short name of update
      $Update= "$1-$Fields[1].$2"       if $Update =~ m/^(.+)\.(.+?)$/ ;
#     print STDERR "$Update\n" ;        # Log...
      $Update{$Update}= 'Anonymous' ;   # Set classification
    }  # of foreach

#
# Go retrieve the classification of each update and count the number of
# available updates in each class.
#
    @Lines= `$YumClass` ;
    foreach ( @Lines ) {
      chomp ;
      @Fields= split ;
      next                      unless @Fields == 3 ;
      next                      unless exists $Update{$Fields[2]} ;

      $Class= $Fields[1] ;              # Classification
      unless ( exists $Class{$Class} ) {
        print "$Now Unknown class '$Class'\n" ;
      }  # of unless
      $Class= exists $Class{$Class} ? $Class{$Class}[0] : 'Anonymous' ;
      $Update{$Fields[2]}= $Class ;     # Save classification
      push @{$ClassLst{$Class}}, "$Fields[0]  $Fields[2]"       unless $Class eq 'Anonymous' ;
    }  # of foreach
    $ClassCnt{$Update{$_}}++    foreach ( keys %Update ) ;
#
# The list of updates in class "Anonymous" must be build seperately.
#
    if ( $ClassCnt{Anonymous} ) {
      foreach ( sort keys %Update ) {
        next                    unless $Update{$_} eq 'Anonymous' ;
        push @{$ClassLst{Anonymous}}, "RHNA-0000:0000  $_" ;
      }  # of foreach
    }  # of if

#
# Build a table showing the results.
#
    $Result = "<table>\n" ;
    $Result.= " <tr><th align='left'>Category</th><th>Count</th><th>Status</th></tr>\n" ;
    foreach $Class ( sort keys %ClassCnt ) {
      $SubColour= $ClassCnt{$Class} == 0 ? 3 : $Class{$Class}[1] ;
      $Colour   = min( $Colour, $SubColour ) ;
      $SubColour= $ColourOf[$SubColour] ;
      $ClassLst{$Class}= [ 'None' ]     unless exists $ClassLst{$Class} ;
      $Result.= " <tr><td><span title='" ;
      $Result.= join( "\n", @{$ClassLst{$Class}} ) ;
      $Result.= "'>$Class</span></td>" .
                "<td align='right'>$ClassCnt{$Class}</td>" .
                "<td>   &$SubColour</tr>\n" ;
    }  # of foreach

    $Result.= " <tr><td>&nbsp;</td><td>-----</td><td>&nbsp;</td></tr>\n" ;
    $Result.= " <tr><td>Total</td><td align='right'>$Count</td><td>   &$ColourOf[$Colour]</td></tr>\n" ;
    $Result.= "</table>\n" ;
  }  # of unless
}  # of else

$Result= "<b>Yum update status</b>\n" .
         "\n\n" . $Result . "\n" ;
InformXymon ;

Extend file ~xymon/client/etc/clientlaunch.cfg with the following snippet:

[yum]
        ENVFILE $XYMONCLIENTHOME/etc/xymonclient.cfg
        CMD $XYMONCLIENTHOME/ext/yum.pl
        LOGFILE $XYMONCLIENTLOGS/yum.log
        CRONDATE 0 0-23/6 * * *
  • 2017-01-10
    • Initial release
  • monitors/yum.txt
  • Last modified: 2017/02/12 11:04
  • by wnelis