Skip to content

Instantly share code, notes, and snippets.

@ttshivers
Last active July 7, 2017 02:41
Show Gist options
  • Save ttshivers/748e8e5762b0f3c7e9f108165a90cbc2 to your computer and use it in GitHub Desktop.
Save ttshivers/748e8e5762b0f3c7e9f108165a90cbc2 to your computer and use it in GitHub Desktop.
Xymon Yum Script
#!/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
'newpackage' => [ 'New Package', 3 ], # 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 = $ENV{CLIENTHOSTNAME}; # Host under test
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 %ShortClassLst = () ;
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' ;
push @{$ShortClassLst{$Class}}, "$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 $_" ;
push @{$ShortClassLst{Anonymous}}, "$_" ;
} # 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" ;
$Result.= "<table>\n" ;
$Result.= " <tr><th align='left'>Category</th><th align='left'>Update</th><th>Status</th></tr>\n" ;
foreach $Class ( reverse sort keys %ClassCnt ) {
$SubColour= $ClassCnt{$Class} == 0 ? 3 : $Class{$Class}[1] ;
$Colour = min( $Colour, $SubColour ) ;
$SubColour= $ColourOf[$SubColour] ;
foreach my $update (@{$ShortClassLst{$Class}}) {
$Result.= "<tr style='color:$SubColour'><td>$Class</td><td>$update</td><td> &$SubColour</td></tr>";
}
}
$Result.= "</table>\n" ;
} # of unless
} # of else
$Result= "<b>Yum update status</b>\n" .
"\n\n" . $Result . "\n" ;
InformXymon ;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment