mirror of
https://github.com/opinkerfi/nagios-plugins.git
synced 2024-11-21 18:03:45 +01:00
Merge pull request #39 from opinkerfi/plugin_check_yum_update
Updated check_yum to 0.8.9
This commit is contained in:
commit
9eda5324d5
@ -1,24 +1,29 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/env python
|
||||||
# coding=utf-8
|
#
|
||||||
|
# Author: Hari Sekhon
|
||||||
|
# Date: 2008-04-29 17:21:08 +0100 (Tue, 29 Apr 2008)
|
||||||
|
#
|
||||||
|
# https://github.com/harisekhon/nagios-plugins
|
||||||
|
#
|
||||||
|
# License: see accompanying LICENSE file
|
||||||
|
#
|
||||||
|
|
||||||
"""Nagios plugin to check the YUM package management system for package updates.
|
"""
|
||||||
Can optionally alert on any available updates as well as just
|
Nagios plugin to test for Yum updates on RedHat / CentOS Linux.
|
||||||
security related updates"""
|
|
||||||
|
|
||||||
__title__ = "check_yum"
|
Can optionally alert on any available updates as well as just security related updates
|
||||||
__version__ = "0.8.0"
|
|
||||||
|
|
||||||
# Standard Nagios return codes
|
See also: check_yum.pl (also part of the Advanced Nagios Plugins Collection)
|
||||||
OK = 0
|
|
||||||
WARNING = 1
|
Tested on CentOS 5 / 6 / 7
|
||||||
CRITICAL = 2
|
"""
|
||||||
UNKNOWN = 3
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import signal
|
import signal
|
||||||
OLD_PYTHON = False
|
OLD_PYTHON = False
|
||||||
|
# pylint: disable=wrong-import-position
|
||||||
try:
|
try:
|
||||||
from subprocess import Popen, PIPE, STDOUT
|
from subprocess import Popen, PIPE, STDOUT
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@ -26,28 +31,41 @@ except ImportError:
|
|||||||
import commands
|
import commands
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
|
|
||||||
|
__author__ = "Hari Sekhon"
|
||||||
|
__title__ = "Nagios Plugin for Yum updates on RedHat/CentOS systems"
|
||||||
|
__version__ = "0.8.9"
|
||||||
|
|
||||||
|
# Standard Nagios return codes
|
||||||
|
OK = 0
|
||||||
|
WARNING = 1
|
||||||
|
CRITICAL = 2
|
||||||
|
UNKNOWN = 3
|
||||||
|
|
||||||
DEFAULT_TIMEOUT = 30
|
DEFAULT_TIMEOUT = 30
|
||||||
|
|
||||||
|
support_msg = "Please make sure you have upgraded to the latest version from " + \
|
||||||
|
"https://github.com/harisekhon/nagios-plugins. If the problem persists, " + \
|
||||||
|
"please raise a ticket at https://github.com/harisekhon/nagios-plugins/issues "+ \
|
||||||
|
"with the full -vvv output"
|
||||||
|
|
||||||
def end(status, message, perfdata=''):
|
def end(status, message):
|
||||||
"""Exits the plugin with first arg as the return code and the second
|
"""Exits the plugin with first arg as the return code and the second
|
||||||
arg as the message to output"""
|
arg as the message to output"""
|
||||||
|
|
||||||
check = "YUM "
|
check = "YUM "
|
||||||
if status == OK:
|
if status == OK:
|
||||||
print "%sOK: %s | %s" % (check, message, perfdata)
|
print "%sOK: %s" % (check, message)
|
||||||
sys.exit(OK)
|
sys.exit(OK)
|
||||||
elif status == WARNING:
|
elif status == WARNING:
|
||||||
print "%sWARNING: %s | %s" % (check, message, perfdata)
|
print "%sWARNING: %s" % (check, message)
|
||||||
sys.exit(WARNING)
|
sys.exit(WARNING)
|
||||||
elif status == CRITICAL:
|
elif status == CRITICAL:
|
||||||
print "%sCRITICAL: %s | %s" % (check, message, perfdata)
|
print "%sCRITICAL: %s" % (check, message)
|
||||||
sys.exit(CRITICAL)
|
sys.exit(CRITICAL)
|
||||||
else:
|
else:
|
||||||
print "UNKNOWN: %s" % message
|
print "UNKNOWN: %s" % message
|
||||||
sys.exit(UNKNOWN)
|
sys.exit(UNKNOWN)
|
||||||
|
|
||||||
|
|
||||||
YUM = "/usr/bin/yum"
|
YUM = "/usr/bin/yum"
|
||||||
|
|
||||||
def check_yum_usable():
|
def check_yum_usable():
|
||||||
@ -62,26 +80,28 @@ def check_yum_usable():
|
|||||||
end(UNKNOWN, "%s is not executable" % YUM)
|
end(UNKNOWN, "%s is not executable" % YUM)
|
||||||
|
|
||||||
|
|
||||||
class YumTester:
|
class YumTester(object):
|
||||||
"""Class to hold all portage test functions and state"""
|
"""Class to hold all portage test functions and state"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Initialize all object variables"""
|
"""Initialize all object variables"""
|
||||||
|
|
||||||
self.all_updates = False
|
self.all_updates = False
|
||||||
self.no_cache_update = False
|
self.no_cache_update = False
|
||||||
self.no_warn_on_lock = False
|
self.no_warn_on_lock = False
|
||||||
self.enable_repo = ""
|
self.enable_repo = ""
|
||||||
self.disable_repo = ""
|
self.disable_repo = ""
|
||||||
self.timeout = DEFAULT_TIMEOUT
|
self.disable_plugin = ""
|
||||||
self.verbosity = 0
|
self.yum_config = ""
|
||||||
|
self.timeout = DEFAULT_TIMEOUT
|
||||||
|
self.verbosity = 0
|
||||||
self.warn_on_any_update = False
|
self.warn_on_any_update = False
|
||||||
|
|
||||||
def validate_all_variables(self):
|
def validate_all_variables(self):
|
||||||
"""Validates all object variables to make sure the
|
"""Validates all object variables to make sure the
|
||||||
environment is sane"""
|
environment is sane"""
|
||||||
|
|
||||||
if self.timeout == None:
|
if self.timeout is None:
|
||||||
self.timeout = DEFAULT_TIMEOUT
|
self.timeout = DEFAULT_TIMEOUT
|
||||||
try:
|
try:
|
||||||
self.timeout = int(self.timeout)
|
self.timeout = int(self.timeout)
|
||||||
@ -92,7 +112,7 @@ class YumTester:
|
|||||||
if self.timeout < 1 or self.timeout > 3600:
|
if self.timeout < 1 or self.timeout > 3600:
|
||||||
end(UNKNOWN, "Timeout must be a number between 1 and 3600 seconds")
|
end(UNKNOWN, "Timeout must be a number between 1 and 3600 seconds")
|
||||||
|
|
||||||
if self.verbosity == None:
|
if self.verbosity is None:
|
||||||
self.verbosity = 0
|
self.verbosity = 0
|
||||||
try:
|
try:
|
||||||
self.verbosity = int(self.verbosity)
|
self.verbosity = int(self.verbosity)
|
||||||
@ -104,13 +124,13 @@ class YumTester:
|
|||||||
|
|
||||||
|
|
||||||
def run(self, cmd):
|
def run(self, cmd):
|
||||||
"""runs a system command and returns
|
"""runs a system command and returns
|
||||||
an array of lines of the output"""
|
an array of lines of the output"""
|
||||||
|
|
||||||
if cmd == "" or cmd == None:
|
if not cmd:
|
||||||
end(UNKNOWN, "Internal python error - " \
|
end(UNKNOWN, "Internal python error - " \
|
||||||
+ "no cmd supplied for run function")
|
+ "no cmd supplied for run function")
|
||||||
|
|
||||||
if self.no_cache_update:
|
if self.no_cache_update:
|
||||||
cmd += " -C"
|
cmd += " -C"
|
||||||
|
|
||||||
@ -121,32 +141,43 @@ class YumTester:
|
|||||||
for repo in self.disable_repo.split(","):
|
for repo in self.disable_repo.split(","):
|
||||||
cmd += " --disablerepo=%s" % repo
|
cmd += " --disablerepo=%s" % repo
|
||||||
|
|
||||||
|
if self.disable_plugin:
|
||||||
|
# --disableplugin can take a comma separated list directly
|
||||||
|
#for plugin in self.disable_plugin.split(","):
|
||||||
|
#cmd += " --disableplugin=%s" % plugin
|
||||||
|
cmd += " --disableplugin=%s" % self.disable_plugin
|
||||||
|
|
||||||
|
if self.yum_config:
|
||||||
|
for repo in self.yum_config.split(","):
|
||||||
|
cmd += " --config=%s" % repo
|
||||||
|
|
||||||
self.vprint(3, "running command: %s" % cmd)
|
self.vprint(3, "running command: %s" % cmd)
|
||||||
|
|
||||||
if OLD_PYTHON:
|
if OLD_PYTHON:
|
||||||
self.vprint(3, "subprocess not available, probably old python " \
|
self.vprint(3, "subprocess not available, probably old python " \
|
||||||
+ "version, using shell instead")
|
+ "version, using shell instead")
|
||||||
|
os.environ['LANG'] = "en_US"
|
||||||
returncode, stdout = commands.getstatusoutput(cmd)
|
returncode, stdout = commands.getstatusoutput(cmd)
|
||||||
if returncode >= 256:
|
if returncode >= 256:
|
||||||
returncode = returncode / 256
|
returncode = returncode / 256
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
process = Popen( cmd.split(),
|
env = {'LANG': 'en_US'}
|
||||||
stdin=PIPE,
|
process = Popen(cmd.split(), stdin=PIPE, stdout=PIPE, stderr=STDOUT, env=env)
|
||||||
stdout=PIPE,
|
|
||||||
stderr=STDOUT )
|
|
||||||
except OSError, error:
|
except OSError, error:
|
||||||
error = str(error)
|
error = str(error)
|
||||||
if error == "No such file or directory":
|
if error == "No such file or directory":
|
||||||
end(UNKNOWN, "Cannot find utility '%s'" % cmd.split()[0])
|
end(UNKNOWN, "Cannot find utility '%s'" % cmd.split()[0])
|
||||||
end(UNKNOWN, "Error trying to run utility '%s' - %s" \
|
end(UNKNOWN, "Error trying to run utility '%s' - %s" \
|
||||||
% (cmd.split()[0], error))
|
% (cmd.split()[0], error))
|
||||||
|
|
||||||
output = process.communicate()
|
output = process.communicate()
|
||||||
|
# for using debug outputs, either do not comment above line or explicitly set exit code below
|
||||||
|
#output = [open(os.path.dirname(__file__) + '/test_input.txt').read(), '']
|
||||||
returncode = process.returncode
|
returncode = process.returncode
|
||||||
stdout = output[0]
|
stdout = output[0]
|
||||||
|
|
||||||
if stdout == None or stdout == "":
|
if not stdout:
|
||||||
end(UNKNOWN, "No output from utility '%s'" % cmd.split()[0])
|
end(UNKNOWN, "No output from utility '%s'" % cmd.split()[0])
|
||||||
|
|
||||||
self.vprint(3, "Returncode: '%s'\nOutput: '%s'" \
|
self.vprint(3, "Returncode: '%s'\nOutput: '%s'" \
|
||||||
@ -158,19 +189,21 @@ class YumTester:
|
|||||||
|
|
||||||
|
|
||||||
def check_returncode(self, returncode, output):
|
def check_returncode(self, returncode, output):
|
||||||
"""Takes the returncode and output (as an array of lines)
|
"""Takes the returncode and output (as an array of lines)
|
||||||
of the YUM program execution and tests for failures, exits
|
of the yum program execution and tests for failures, exits
|
||||||
with an appropriate message if any are found"""
|
with an appropriate message if any are found"""
|
||||||
|
|
||||||
if returncode == 0:
|
if returncode == 0:
|
||||||
pass
|
for line in output:
|
||||||
|
if "You must run this command as root" in line:
|
||||||
|
end(UNKNOWN, "You must run this plugin as root")
|
||||||
elif returncode == 100:
|
elif returncode == 100:
|
||||||
# Updates Available
|
# Updates Available
|
||||||
pass
|
pass
|
||||||
elif returncode == 200:
|
elif returncode == 200:
|
||||||
if "lock" in output[-2] or "another copy is running" in output[-2]:
|
if "lock" in output[-2] or "another copy is running" in output[-2]:
|
||||||
msg = "Cannot check for updates, " \
|
msg = "Cannot check for updates, " \
|
||||||
+ "another instance of YUM is running"
|
+ "another instance of yum is running"
|
||||||
if self.no_warn_on_lock:
|
if self.no_warn_on_lock:
|
||||||
end(OK, msg)
|
end(OK, msg)
|
||||||
else:
|
else:
|
||||||
@ -179,10 +212,15 @@ class YumTester:
|
|||||||
output = self.strip_output(output)
|
output = self.strip_output(output)
|
||||||
end(UNKNOWN, "%s" % output)
|
end(UNKNOWN, "%s" % output)
|
||||||
else:
|
else:
|
||||||
if not 'Loading "security" plugin' in output \
|
if 'No more mirrors to try' in output:
|
||||||
|
end(UNKNOWN, 'connectivity issue to repos: \'No more mirrors to try\'. ' + \
|
||||||
|
'You could also try running --cache-only and ' + \
|
||||||
|
'scheduling a separate \'yum makecache\' via cron or similar')
|
||||||
|
elif (not ('Loading "security" plugin' in output or 'Loaded plugins:.*security' in output)) \
|
||||||
or "Command line error: no such option: --security" in output:
|
or "Command line error: no such option: --security" in output:
|
||||||
end(UNKNOWN, "Security plugin for YUM is required. Try to " \
|
end(UNKNOWN, "Security plugin for yum is required. Try to " \
|
||||||
+ "'yum install yum-security' and then re-run " \
|
+ "'yum install yum-security' (RHEL5) or " \
|
||||||
|
+ "'yum install yum-plugin-security' (RHEL6) and then re-run " \
|
||||||
+ "this plugin. Alternatively, to just alert on " \
|
+ "this plugin. Alternatively, to just alert on " \
|
||||||
+ "any update which does not require the security " \
|
+ "any update which does not require the security " \
|
||||||
+ "plugin, try --all-updates")
|
+ "plugin, try --all-updates")
|
||||||
@ -192,8 +230,8 @@ class YumTester:
|
|||||||
|
|
||||||
|
|
||||||
def strip_output(self, output):
|
def strip_output(self, output):
|
||||||
"""Cleans up the output from the plugin and returns it.
|
"""Cleans up the output from the plugin and returns it.
|
||||||
Takes and returns an array of the lines of output
|
Takes and returns an array of the lines of output
|
||||||
and returns a single string"""
|
and returns a single string"""
|
||||||
|
|
||||||
self.vprint(3, "stripping output of 'Loading ... plugin' lines")
|
self.vprint(3, "stripping output of 'Loading ... plugin' lines")
|
||||||
@ -243,149 +281,137 @@ class YumTester:
|
|||||||
|
|
||||||
return number_security_updates, number_other_updates
|
return number_security_updates, number_other_updates
|
||||||
|
|
||||||
|
|
||||||
def get_all_updates(self):
|
def get_all_updates(self):
|
||||||
"""Gets all updates. Returns a single integer of the
|
"""Gets all updates. Returns a single integer of the
|
||||||
number of available updates"""
|
number of available updates"""
|
||||||
|
|
||||||
cmd = "%s check-update" % YUM
|
cmd = "%s check-update" % YUM
|
||||||
|
|
||||||
output = self.run(cmd)
|
output = self.run(cmd)
|
||||||
|
|
||||||
output2 = "\n".join(output).split("\n\n")
|
output2 = [_ for _ in "\n".join(output).split("\n\n") if _]
|
||||||
if self.verbosity >= 4 :
|
if self.verbosity >= 4:
|
||||||
for section in output2:
|
for section in output2:
|
||||||
print "\nSection:\n%s\n" % section
|
print "\nSection:\n%s\n" % section
|
||||||
if len(output2) > 2 or \
|
if len(output2) > 2 or \
|
||||||
not ( "Setting up repositories" in output2[0] or \
|
not ("Setting up repositories" in output2[0] or \
|
||||||
"Loaded plugins: " in output2[0] or \
|
"Loaded plugins: " in output2[0] or \
|
||||||
re.search('Loading\s+".+"\s+plugin', output2[0]) ):
|
re.search(r'Loading\s+".+"\s+plugin', output2[0])):
|
||||||
end(WARNING, "Yum output signature does not match current known " \
|
end(WARNING, "Yum output signature does not match current known " \
|
||||||
+ "format. Please make sure you have upgraded to the " \
|
+ "format. " + support_msg)
|
||||||
+ "latest version of this plugin. If the problem " \
|
number_packages = 0
|
||||||
+ "persists, please contact the author for a fix")
|
|
||||||
if len(output2) == 1:
|
if len(output2) == 1:
|
||||||
# There are no updates but we have passed
|
# There are no updates but we have passed
|
||||||
# the loading and setting up of repositories
|
# the loading and setting up of repositories
|
||||||
number_packages = 0
|
pass
|
||||||
else:
|
else:
|
||||||
number_packages = len([x for x in output2[1].split("\n") \
|
for line in output2[1].split("\n"):
|
||||||
if len(x.split()) > 1 ])
|
if len(line.split()) > 1 and \
|
||||||
|
line[0:1] != " " and \
|
||||||
|
"Obsoleting Packages" not in line:
|
||||||
|
number_packages += 1
|
||||||
|
|
||||||
try:
|
try:
|
||||||
number_packages = int(number_packages)
|
number_packages = int(number_packages)
|
||||||
if number_packages < 0:
|
if number_packages < 0:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
except ValueError:
|
except ValueError:
|
||||||
end(UNKNOWN, "Error parsing package information, invalid package " \
|
end(UNKNOWN, "Error parsing package information, invalid package " \
|
||||||
+ "number, YUM output may have changed. Please make " \
|
+ "number, yum output may have changed. " + support_msg)
|
||||||
+ "sure you have upgraded to the latest version of " \
|
|
||||||
+ "this plugin. If the problem persists, then please " \
|
|
||||||
+ "contact the author for a fix")
|
|
||||||
|
|
||||||
# Extra layer of checks. This is a security plugin so it's preferable
|
# Extra layer of checks. This is a security plugin so it's preferable
|
||||||
# to fail on error rather than pass silently leaving you with an
|
# to fail on error rather than pass silently leaving you with an
|
||||||
# insecure system
|
# insecure system
|
||||||
count = 0
|
count = 0
|
||||||
|
re_kernel_security_update = re.compile('^Security: kernel-.+ is an installed security update')
|
||||||
|
re_kernel_update = re.compile('^Security: kernel-.+ is the currently running version')
|
||||||
re_package_format = \
|
re_package_format = \
|
||||||
re.compile("^.+\.(i[3456]86|x86_64|noarch)\s+.+\s+.+$")
|
re.compile(r'^.+\.(i[3456]86|x86_64|noarch)\s+.+\s+.+$')
|
||||||
# This is to work around a YUM truncation issue effectively changing
|
# This is to work around a yum truncation issue effectively changing
|
||||||
# the package output format. Currently only very long kmod lines
|
# the package output format. Currently only very long kmod lines
|
||||||
# are seen to have caused this so we stick to what we know for safety
|
# are seen to have caused this so we stick to what we know for safety
|
||||||
# and raise an unknown error on anything else for maximum security
|
# and raise an unknown error on anything else for maximum security
|
||||||
#re_package_format_truncated = \
|
#re_package_format_truncated = \
|
||||||
# re.compile("^[\w-]+-kmod-\d[\d\.-]+.*\s+.+\s+.+$")
|
# re.compile("^[\w-]+-kmod-\d[\d\.-]+.*\s+.+\s+.+$")
|
||||||
|
obsoleting_packages = False
|
||||||
for line in output:
|
for line in output:
|
||||||
|
if ' excluded ' in line:
|
||||||
|
continue
|
||||||
|
elif obsoleting_packages and line[0:1] == " ":
|
||||||
|
continue
|
||||||
|
elif "Obsoleting Packages" in line:
|
||||||
|
obsoleting_packages = True
|
||||||
|
continue
|
||||||
|
elif re_kernel_security_update.match(line):
|
||||||
|
end(WARNING, 'Kernel security update is installed but requires a reboot')
|
||||||
|
elif re_kernel_update.match(line):
|
||||||
|
continue
|
||||||
if re_package_format.match(line):
|
if re_package_format.match(line):
|
||||||
count += 1
|
count += 1
|
||||||
if count != number_packages:
|
if count != number_packages:
|
||||||
end(UNKNOWN, "Error parsing package information, inconsistent " \
|
end(UNKNOWN, "Error parsing package information, inconsistent " \
|
||||||
+ "package count, yum output may have changed. Please " \
|
+ "package count (%d count vs %s num packages)" % (count, number_packages) \
|
||||||
+ "make sure you have upgraded to the latest version " \
|
+ ", yum output may have changed. " + support_msg)
|
||||||
+ "of this plugin. If the problem persists, then " \
|
|
||||||
+ "please contact the author for a fix")
|
|
||||||
|
|
||||||
return number_packages, "'updates'=%s" % (number_packages)
|
return number_packages
|
||||||
|
|
||||||
def get_security_updateinfo(self):
|
|
||||||
"""Fetches errata numbers and package names"""
|
|
||||||
|
|
||||||
cmd = "%s list-security" % YUM
|
|
||||||
|
|
||||||
output = self.run(cmd)
|
|
||||||
|
|
||||||
errata = []
|
|
||||||
|
|
||||||
for line in output:
|
|
||||||
try:
|
|
||||||
if line.split()[1] != "security" and line.split()[1][-4:] != "Sec.":
|
|
||||||
continue
|
|
||||||
except:
|
|
||||||
continue
|
|
||||||
(advisoryid, etype, package) = line.split()
|
|
||||||
errata.append( { "name": package, "advisory": advisoryid } )
|
|
||||||
|
|
||||||
return errata
|
|
||||||
|
|
||||||
def get_security_updates(self):
|
def get_security_updates(self):
|
||||||
"""Gets all updates, but differentiates between
|
"""Gets all updates, but differentiates between
|
||||||
security and normal updates. Returns a tuple of the number
|
security and normal updates. Returns a tuple of the number
|
||||||
of security and normal updates"""
|
of security and normal updates"""
|
||||||
|
|
||||||
cmd = "%s --security check-update" % YUM
|
cmd = "%s --security check-update" % YUM
|
||||||
|
|
||||||
output = self.run(cmd)
|
output = self.run(cmd)
|
||||||
|
|
||||||
re_security_summary_rhel5 = re.compile("Needed \d+ of \d+ packages, for security")
|
re_security_summary = \
|
||||||
re_security_summary_rhel6 = re.compile("\d+ package\(s\) needed for security, out of \d+ available")
|
re.compile(r'Needed (\d+) of (\d+) packages, for security')
|
||||||
re_no_security_updates_available_rhel5 = re.compile("No packages needed, for security, \d+ available")
|
re_summary_rhel6 = re.compile(r'(\d+) package\(s\) needed for security, out of (\d+) available')
|
||||||
re_no_security_updates_available_rhel6 = re.compile("No packages needed for security; \d+ packages available")
|
re_no_sec_updates = \
|
||||||
|
re.compile(r'No packages needed,? for security[;,] (\d+) (?:packages )?available')
|
||||||
|
re_kernel_update = re.compile(r'^Security: kernel-.+ is an installed security update')
|
||||||
summary_line_found = False
|
summary_line_found = False
|
||||||
for line in output:
|
for line in output:
|
||||||
if re_no_security_updates_available_rhel5.match(line):
|
_ = re_summary_rhel6.match(line)
|
||||||
|
if _:
|
||||||
|
summary_line_found = True
|
||||||
|
number_security_updates = _.group(1)
|
||||||
|
number_total_updates = _.group(2)
|
||||||
|
break
|
||||||
|
_ = re_no_sec_updates.match(line)
|
||||||
|
if _:
|
||||||
summary_line_found = True
|
summary_line_found = True
|
||||||
number_security_updates = 0
|
number_security_updates = 0
|
||||||
number_total_updates = line.split()[5]
|
number_total_updates = _.group(1)
|
||||||
break
|
break
|
||||||
if re_no_security_updates_available_rhel6.match(line):
|
_ = re_security_summary.match(line)
|
||||||
|
if _:
|
||||||
summary_line_found = True
|
summary_line_found = True
|
||||||
number_security_updates = 0
|
number_security_updates = _.group(1)
|
||||||
number_total_updates = line.split()[5]
|
number_total_updates = _.group(2)
|
||||||
break
|
|
||||||
if re_security_summary_rhel5.match(line):
|
|
||||||
summary_line_found = True
|
|
||||||
number_security_updates = line.split()[1]
|
|
||||||
number_total_updates = line.split()[3]
|
|
||||||
break
|
|
||||||
if re_security_summary_rhel6.match(line):
|
|
||||||
summary_line_found = True
|
|
||||||
number_security_updates = line.split()[0]
|
|
||||||
number_total_updates = line.split()[7]
|
|
||||||
break
|
break
|
||||||
|
_ = re_kernel_update.match(line)
|
||||||
|
if _:
|
||||||
|
end(CRITICAL, "Kernel security update is installed but requires a reboot")
|
||||||
|
|
||||||
if not summary_line_found:
|
if not summary_line_found:
|
||||||
end(WARNING, "Cannot find summary line in YUM output. Please " \
|
end(WARNING, "Cannot find summary line in yum output. " + support_msg)
|
||||||
+ "make sure you have upgraded to the latest version " \
|
|
||||||
+ "of this plugin. If the problem persists, please " \
|
|
||||||
+ "contact the author for a fix")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
number_security_updates = int(number_security_updates)
|
number_security_updates = int(number_security_updates)
|
||||||
number_total_updates = int(number_total_updates)
|
number_total_updates = int(number_total_updates)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
end(WARNING, "Error parsing package information, YUM output " \
|
end(WARNING, "Error parsing package information, yum output " \
|
||||||
+ "may have changed. Please make sure you have " \
|
+ "may have changed. " + support_msg)
|
||||||
+ "upgraded to the latest version of this plugin. " \
|
|
||||||
+ "If the problem persists, the please contact the " \
|
|
||||||
+ "author for a fix")
|
|
||||||
|
|
||||||
number_other_updates = number_total_updates - number_security_updates
|
number_other_updates = number_total_updates - number_security_updates
|
||||||
|
|
||||||
if len(output) > number_total_updates + 25:
|
from_excluded_regex = re.compile(' from .+ excluded ')
|
||||||
|
if len([_ for _ in output if not from_excluded_regex.search(_)]) > number_total_updates + 25:
|
||||||
end(WARNING, "Yum output signature is larger than current known " \
|
end(WARNING, "Yum output signature is larger than current known " \
|
||||||
+ "format, please make sure you have upgraded to the " \
|
+ "format. " + support_msg)
|
||||||
+ "latest version of this plugin. If the problem " \
|
|
||||||
+ "persists, please contact the author for a fix")
|
|
||||||
|
|
||||||
return number_security_updates, number_other_updates
|
return number_security_updates, number_other_updates
|
||||||
|
|
||||||
@ -394,26 +420,25 @@ class YumTester:
|
|||||||
"""Starts tests and controls logic flow"""
|
"""Starts tests and controls logic flow"""
|
||||||
|
|
||||||
check_yum_usable()
|
check_yum_usable()
|
||||||
self.vprint(3, "%s - Version %s\n" \
|
self.vprint(3, "%s - Version %s\nAuthor: %s\n" \
|
||||||
% (__title__, __version__))
|
% (__title__, __version__, __author__))
|
||||||
|
|
||||||
self.validate_all_variables()
|
self.validate_all_variables()
|
||||||
self.set_timeout()
|
self.set_timeout()
|
||||||
|
|
||||||
if self.all_updates:
|
if self.all_updates:
|
||||||
return self.test_all_updates()
|
return self.test_all_updates()
|
||||||
else:
|
return self.test_security_updates()
|
||||||
return self.test_security_updates()
|
|
||||||
|
|
||||||
|
|
||||||
def test_all_updates(self):
|
def test_all_updates(self):
|
||||||
"""Tests for all updates, and returns a tuple
|
"""Tests for all updates, and returns a tuple
|
||||||
of the status code and output"""
|
of the status code and output"""
|
||||||
|
|
||||||
status = UNKNOWN
|
status = UNKNOWN
|
||||||
message = "code error - please contact author for a fix"
|
message = "code error. " + support_msg
|
||||||
|
|
||||||
number_updates, perfdata = self.get_all_updates()
|
number_updates = self.get_all_updates()
|
||||||
if number_updates == 0:
|
if number_updates == 0:
|
||||||
status = OK
|
status = OK
|
||||||
message = "0 Updates Available"
|
message = "0 Updates Available"
|
||||||
@ -424,20 +449,20 @@ class YumTester:
|
|||||||
else:
|
else:
|
||||||
message = "%s Updates Available" % number_updates
|
message = "%s Updates Available" % number_updates
|
||||||
|
|
||||||
return status, message, perfdata
|
message += " | total_updates_available=%s" % number_updates
|
||||||
|
|
||||||
|
return status, message
|
||||||
|
|
||||||
|
|
||||||
def test_security_updates(self):
|
def test_security_updates(self):
|
||||||
"""Tests for security updates and returns a tuple
|
"""Tests for security updates and returns a tuple
|
||||||
of the status code and output"""
|
of the status code and output"""
|
||||||
|
|
||||||
status = UNKNOWN
|
status = UNKNOWN
|
||||||
message = "code error - please contact author for a fix"
|
message = "code error. " + support_msg
|
||||||
|
|
||||||
number_security_updates, number_other_updates = \
|
number_security_updates, number_other_updates = \
|
||||||
self.get_security_updates()
|
self.get_security_updates()
|
||||||
|
|
||||||
perfdata = "'security_updates'=%s 'other_updates'=%s" % (number_security_updates, number_other_updates)
|
|
||||||
if number_security_updates == 0:
|
if number_security_updates == 0:
|
||||||
status = OK
|
status = OK
|
||||||
message = "0 Security Updates Available"
|
message = "0 Security Updates Available"
|
||||||
@ -448,6 +473,7 @@ class YumTester:
|
|||||||
elif number_security_updates > 1:
|
elif number_security_updates > 1:
|
||||||
message = "%s Security Updates Available" \
|
message = "%s Security Updates Available" \
|
||||||
% number_security_updates
|
% number_security_updates
|
||||||
|
|
||||||
if number_other_updates != 0:
|
if number_other_updates != 0:
|
||||||
if self.warn_on_any_update and status != CRITICAL:
|
if self.warn_on_any_update and status != CRITICAL:
|
||||||
status = WARNING
|
status = WARNING
|
||||||
@ -456,13 +482,10 @@ class YumTester:
|
|||||||
else:
|
else:
|
||||||
message += ". %s Non-Security Updates Available" \
|
message += ". %s Non-Security Updates Available" \
|
||||||
% number_other_updates
|
% number_other_updates
|
||||||
|
message += " | security_updates_available=%s non_security_updates_available=%s total_updates_available=%s" \
|
||||||
if number_security_updates and self.long_output:
|
% (number_security_updates, number_other_updates, number_security_updates + number_other_updates)
|
||||||
errata = self.get_security_updateinfo()
|
|
||||||
for e in errata:
|
return status, message
|
||||||
message += "\n%s - %s" % (e['advisory'], e['name'])
|
|
||||||
|
|
||||||
return status, message, perfdata
|
|
||||||
|
|
||||||
|
|
||||||
def vprint(self, threshold, message):
|
def vprint(self, threshold, message):
|
||||||
@ -479,88 +502,97 @@ def main():
|
|||||||
tester = YumTester()
|
tester = YumTester()
|
||||||
parser = OptionParser()
|
parser = OptionParser()
|
||||||
|
|
||||||
parser.add_option( "--all-updates",
|
parser.add_option("-A",
|
||||||
action="store_true",
|
"--all-updates",
|
||||||
dest="all_updates",
|
action="store_true",
|
||||||
help="Does not distinguish between security and " \
|
dest="all_updates",
|
||||||
+ "non-security updates, but returns critical for " \
|
help="Does not distinguish between security and " \
|
||||||
+ "any available update. This may be used if the " \
|
+ "non-security updates, but returns critical for " \
|
||||||
+ "YUM security plugin is absent or you want to " \
|
+ "any available update. This may be used if the " \
|
||||||
+ "maintain every single package at the latest " \
|
+ "yum security plugin is absent or you want to " \
|
||||||
+ "version. You may want to use " \
|
+ "maintain every single package at the latest " \
|
||||||
+ "--warn-on-any-update instead of this option")
|
+ "version. You may want to use " \
|
||||||
|
+ "--warn-on-any-update instead of this option")
|
||||||
|
|
||||||
parser.add_option( "--warn-on-any-update",
|
parser.add_option("-W",
|
||||||
action="store_true",
|
"--warn-on-any-update",
|
||||||
dest="warn_on_any_update",
|
action="store_true",
|
||||||
help="Warns if there are any (non-security) package " \
|
dest="warn_on_any_update",
|
||||||
+ "updates available. By default only warns when " \
|
help="Warns if there are any (non-security) package " \
|
||||||
+ "security related updates are available. If " \
|
+ "updates available. By default only warns when " \
|
||||||
+ "--all-updates is used, then this option is " \
|
+ "security related updates are available. If " \
|
||||||
+ "redundant as --all-updates will return a " \
|
+ "--all-updates is used, then this option is " \
|
||||||
+ "critical result on any available update, " \
|
+ "redundant as --all-updates will return a " \
|
||||||
+ "whereas using this switch still allows you to " \
|
+ "critical result on any available update, " \
|
||||||
+ "differentiate between the severity of updates ")
|
+ "whereas using this switch still allows you to " \
|
||||||
|
+ "differentiate between the severity of updates ")
|
||||||
|
|
||||||
parser.add_option( "-C",
|
parser.add_option("-C",
|
||||||
"--cache-only",
|
"--cache-only",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
dest="no_cache_update",
|
dest="no_cache_update",
|
||||||
help="Run entirely from cache and do not update the " \
|
help="Run entirely from cache and do not update the " \
|
||||||
+ "cache when running YUM. Useful if you have " \
|
+ "cache when running yum. Useful if you have " \
|
||||||
+ "'yum makecache' cronned so that the nagios " \
|
+ "'yum makecache' cronned so that the nagios " \
|
||||||
+ "check itself doesn't have to do it, possibly " \
|
+ "check itself doesn't have to do it, possibly " \
|
||||||
+ "speeding up execution (by 1-2 seconds in tests)")
|
+ "speeding up execution (by 1-2 seconds in tests)")
|
||||||
|
|
||||||
parser.add_option( "--no-warn-on-lock",
|
parser.add_option("-c",
|
||||||
action="store_true",
|
"--config",
|
||||||
dest="no_warn_on_lock",
|
dest="yum_config",
|
||||||
help="Return OK instead of WARNING when YUM is locked " \
|
help="Run with custom repository config in order to use " \
|
||||||
+ "and fails to check for updates due to another " \
|
+ "custom repositories in case of special setup for")
|
||||||
+ "instance running. This is not recommended from " \
|
|
||||||
+ "the security standpoint, but may be wanted to " \
|
|
||||||
+ "reduce the number of alerts that may " \
|
|
||||||
+ "intermittently pop up when someone is running " \
|
|
||||||
+ "YUM interactively for package management")
|
|
||||||
|
|
||||||
parser.add_option( "--enablerepo",
|
parser.add_option("-N",
|
||||||
dest="repository_to_enable",
|
"--no-warn-on-lock",
|
||||||
help="Explicitly enables a reposity when calling YUM. "
|
action="store_true",
|
||||||
+ "Can take a comma separated list of repositories")
|
dest="no_warn_on_lock",
|
||||||
|
help="Return OK instead of WARNING when yum is locked " \
|
||||||
|
+ "and fails to check for updates due to another " \
|
||||||
|
+ "instance running. This is not recommended from " \
|
||||||
|
+ "the security standpoint, but may be wanted to " \
|
||||||
|
+ "reduce the number of alerts that may " \
|
||||||
|
+ "intermittently pop up when someone is running " \
|
||||||
|
+ "yum for package management")
|
||||||
|
|
||||||
parser.add_option( "--disablerepo",
|
parser.add_option("-e",
|
||||||
dest="repository_to_disable",
|
"--enablerepo",
|
||||||
help="Explicitly disables a repository when calling YUM "
|
dest="repository_to_enable",
|
||||||
+ "Can take a comma separated list of repositories")
|
help="Explicitly enables a reposity when calling yum. " +
|
||||||
|
"Can take a comma separated list of repositories")
|
||||||
|
|
||||||
parser.add_option( "-l",
|
parser.add_option("-d",
|
||||||
"--long-output",
|
"--disablerepo",
|
||||||
action="store_true",
|
dest="repository_to_disable",
|
||||||
dest="long_output",
|
help="Explicitly disables a repository when calling yum. " \
|
||||||
help="Shows more detailed output including the errata "
|
+ "Can take a comma separated list of repositories")
|
||||||
+ "ID.")
|
|
||||||
|
|
||||||
parser.add_option( "-t",
|
parser.add_option("--disableplugin",
|
||||||
"--timeout",
|
dest="plugin_to_disable",
|
||||||
dest="timeout",
|
help="Explicitly disables a plugin when calling yum. " \
|
||||||
help="Sets a timeout in seconds after which the " \
|
+ "Can take a comma separated list of plugins")
|
||||||
+"plugin will exit (defaults to %s seconds). " \
|
|
||||||
|
parser.add_option("-t",
|
||||||
|
"--timeout",
|
||||||
|
dest="timeout",
|
||||||
|
help="Sets a timeout in seconds after which the " \
|
||||||
|
+"plugin will exit (defaults to %s seconds). " \
|
||||||
% DEFAULT_TIMEOUT)
|
% DEFAULT_TIMEOUT)
|
||||||
|
|
||||||
parser.add_option( "-v",
|
parser.add_option("-v",
|
||||||
"--verbose",
|
"--verbose",
|
||||||
action="count",
|
action="count",
|
||||||
dest="verbosity",
|
dest="verbosity",
|
||||||
help="Verbose mode. Can be used multiple times to " \
|
help="Verbose mode. Can be used multiple times to " \
|
||||||
+ "increase output. Use -vvv for debugging output. " \
|
+ "increase output. Use -vvv for debugging output. " \
|
||||||
+ "By default only one result line is printed as " \
|
+ "By default only one result line is printed as " \
|
||||||
+ "per Nagios standards")
|
+ "per Nagios standards")
|
||||||
|
|
||||||
parser.add_option( "-V",
|
parser.add_option("-V",
|
||||||
"--version",
|
"--version",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
dest="version",
|
dest="version",
|
||||||
help="Print version number and exit")
|
help="Print version number and exit")
|
||||||
|
|
||||||
(options, args) = parser.parse_args()
|
(options, args) = parser.parse_args()
|
||||||
|
|
||||||
@ -568,23 +600,24 @@ def main():
|
|||||||
parser.print_help()
|
parser.print_help()
|
||||||
sys.exit(UNKNOWN)
|
sys.exit(UNKNOWN)
|
||||||
|
|
||||||
tester.all_updates = options.all_updates
|
tester.all_updates = options.all_updates
|
||||||
tester.no_cache_update = options.no_cache_update
|
tester.no_cache_update = options.no_cache_update
|
||||||
tester.no_warn_on_lock = options.no_warn_on_lock
|
tester.no_warn_on_lock = options.no_warn_on_lock
|
||||||
tester.enable_repo = options.repository_to_enable
|
tester.enable_repo = options.repository_to_enable
|
||||||
tester.disable_repo = options.repository_to_disable
|
tester.disable_repo = options.repository_to_disable
|
||||||
tester.timeout = options.timeout
|
tester.disable_plugin = options.plugin_to_disable
|
||||||
tester.verbosity = options.verbosity
|
tester.yum_config = options.yum_config
|
||||||
tester.warn_on_any_update = options.warn_on_any_update
|
tester.timeout = options.timeout
|
||||||
tester.long_output = options.long_output
|
tester.verbosity = options.verbosity
|
||||||
|
tester.warn_on_any_update = options.warn_on_any_update
|
||||||
|
|
||||||
if options.version:
|
if options.version:
|
||||||
print "%s - Version %s\n" \
|
print "%s - Version %s\nAuthor: %s\n" \
|
||||||
% (__title__, __version__)
|
% (__title__, __version__, __author__)
|
||||||
sys.exit(OK)
|
sys.exit(OK)
|
||||||
|
|
||||||
result, output, perfdata = tester.test_yum_updates()
|
result, output = tester.test_yum_updates()
|
||||||
end(result, output, perfdata)
|
end(result, output)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@ -593,37 +626,3 @@ if __name__ == "__main__":
|
|||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print "Caught Control-C..."
|
print "Caught Control-C..."
|
||||||
sys.exit(CRITICAL)
|
sys.exit(CRITICAL)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#Copyright © ?–?, Hari Sekhon <harisekhon@gmail.com>.
|
|
||||||
#Copyright © 2012, Christoph Anton Mitterer <mail@christoph.anton.mitterer.name>.
|
|
||||||
#All rights reserved.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#This program is free software; you can redistribute it and/or
|
|
||||||
#modify it under the terms of the GNU General Public License
|
|
||||||
#as published by the Free Software Foundation; version 2
|
|
||||||
#of the License.
|
|
||||||
#
|
|
||||||
#This program is distributed in the hope that it will be useful,
|
|
||||||
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
#GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
#You should have received a copy of the GNU General Public License
|
|
||||||
#along with this program; if not, write to the Free Software
|
|
||||||
#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
|
Loading…
Reference in New Issue
Block a user