190 lines
		
	
	
	
		
			5.3 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			190 lines
		
	
	
	
		
			5.3 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
#!/usr/bin/python3
 | 
						|
from __future__ import division
 | 
						|
import posixpath
 | 
						|
import re
 | 
						|
import subprocess
 | 
						|
import sys
 | 
						|
 | 
						|
 | 
						|
class Version(object):
 | 
						|
    """Crude version abstraction for systems without distutils.version"""
 | 
						|
 | 
						|
    def __init__(self, version_str):
 | 
						|
        self.version_str = version_str
 | 
						|
 | 
						|
    def __str__(self):
 | 
						|
        return self.version_str
 | 
						|
 | 
						|
    def __repr__(self):
 | 
						|
        return 'Version(\'%s\')' % self.version_str
 | 
						|
 | 
						|
    def components(self):
 | 
						|
        # Split the version into components, by word boundaries, or a
 | 
						|
        # change between numbers and non-numbers and vice-versa. re.split()
 | 
						|
        # does not work on zero-length delimiters, so we have to use a
 | 
						|
        # sub+split.
 | 
						|
        return re.sub(r'\b|(?<=\d)(?=\D)|(?<=\D)(?=\d)', '\n',
 | 
						|
                      self.version_str).split('\n')
 | 
						|
 | 
						|
    def __eq__(self, other):
 | 
						|
        return self.version_str == other.version_str
 | 
						|
 | 
						|
    def __gt__(self, other):
 | 
						|
        def num_gt(str_, str_other):
 | 
						|
            m = re.search('^\d+', str_)
 | 
						|
            m_other = re.search('^\d+', str_other)
 | 
						|
            if m and m_other:
 | 
						|
                return int(m.group(0)) > int(m_other.group(0))
 | 
						|
            else:
 | 
						|
                return str_ > str_other
 | 
						|
 | 
						|
        for self_c, other_c in zip(self.components(), other.components()):
 | 
						|
            if self_c == other_c:
 | 
						|
                continue
 | 
						|
            else:
 | 
						|
                return num_gt(self_c, other_c)
 | 
						|
        return False
 | 
						|
 | 
						|
    # Note: not using functools.total_ordering to support Python 2.6
 | 
						|
 | 
						|
    def __lt__(self, other):
 | 
						|
        return not (self == other) and not (self > other)
 | 
						|
 | 
						|
 | 
						|
def proc_version():
 | 
						|
    """Return the content of /proc/version"""
 | 
						|
 | 
						|
    proc_version = None
 | 
						|
 | 
						|
    # Not using a with statement here, to support Python 2.4
 | 
						|
    v = open('/proc/version', 'r')
 | 
						|
    try:
 | 
						|
        proc_version = v.read()
 | 
						|
    finally:
 | 
						|
        v.close()
 | 
						|
    return proc_version
 | 
						|
 | 
						|
 | 
						|
def running_kernel_version():
 | 
						|
    proc_version_ = proc_version()
 | 
						|
 | 
						|
    if re.search('Debian', proc_version_):
 | 
						|
        # Remove gcc version first
 | 
						|
        proc_version_ = re.sub('\(gcc.*?\)', '', proc_version_)
 | 
						|
 | 
						|
        # Then look for the Debian kernel version
 | 
						|
        m = re.search('((?<=Debian )(\S+)|(?<=PVE )(\S+))', proc_version_)
 | 
						|
        if m:
 | 
						|
            version_str = m.group(1).strip('()')
 | 
						|
            version = clean_kernel_version(version_str)
 | 
						|
            return version
 | 
						|
 | 
						|
    else:
 | 
						|
        m = re.search('(?<=Linux version )(\S+)', proc_version_)
 | 
						|
        if m:
 | 
						|
            version = clean_kernel_version(m.group(0))
 | 
						|
            return version
 | 
						|
 | 
						|
 | 
						|
def is_debian():
 | 
						|
    return posixpath.exists('/etc/debian_version')
 | 
						|
 | 
						|
 | 
						|
def is_fedora():
 | 
						|
    return posixpath.exists('/etc/fedora-release')
 | 
						|
 | 
						|
 | 
						|
def is_redhat():
 | 
						|
    return posixpath.exists('/etc/redhat-release')
 | 
						|
 | 
						|
 | 
						|
def installed_kernel_versions():
 | 
						|
    """
 | 
						|
    Debian-based kernels come in different flavors.
 | 
						|
 | 
						|
    On some of them, like Proxmox' PVE kernels, special care has to be taken
 | 
						|
    when selecting the right packages to run `dpkg-query` on.
 | 
						|
    """
 | 
						|
    if is_debian():
 | 
						|
        return \
 | 
						|
          installed_kernel_versions_debian(pkgname='linux-image') \
 | 
						|
          or installed_kernel_versions_debian(pkgname='pve-kernel',
 | 
						|
                                              pkgquery='pve-kernel*-pve')
 | 
						|
    if is_fedora() or is_redhat():
 | 
						|
        return installed_kernel_versions_fedora()
 | 
						|
    return [None]
 | 
						|
 | 
						|
 | 
						|
def check_output(cmd):
 | 
						|
    """Emulate subprocess.check_output for ancient Python versions"""
 | 
						|
    return subprocess.Popen(
 | 
						|
        cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE
 | 
						|
    ).communicate()[0]
 | 
						|
 | 
						|
 | 
						|
def installed_kernel_versions_debian(pkgname='linux-image', pkgquery=None):
 | 
						|
    if not pkgquery:
 | 
						|
        pkgquery = '%s*' % pkgname
 | 
						|
    dpkg_out = check_output(
 | 
						|
        ['dpkg-query',
 | 
						|
         '--show', '--showformat', '${Package} ${Version}\n',
 | 
						|
         pkgquery])
 | 
						|
    dpkg_out = dpkg_out.decode('ascii', 'ignore').strip()
 | 
						|
 | 
						|
    versions = dpkg_out.split('\n')
 | 
						|
    versions = [v for v in versions if re.search('^%s-\d.* \S+$' % pkgname, v)]
 | 
						|
    versions = [clean_kernel_version(v.split(' ')[1]) for v in versions]
 | 
						|
 | 
						|
    return versions
 | 
						|
 | 
						|
 | 
						|
def installed_kernel_versions_fedora():
 | 
						|
    rpm_out = check_output(
 | 
						|
        ['rpm', '--queryformat=%{VERSION}-%{RELEASE}\n', '-q', 'kernel'])
 | 
						|
    rpm_out = rpm_out.decode('ascii', 'ignore').strip()
 | 
						|
 | 
						|
    versions = rpm_out.split('\n')
 | 
						|
    versions = [clean_kernel_version(v) for v in versions]
 | 
						|
 | 
						|
    return versions
 | 
						|
 | 
						|
 | 
						|
def installed_kernel_version():
 | 
						|
    installed = sorted(installed_kernel_versions())
 | 
						|
    if not installed:
 | 
						|
        print('KERNEL UNKNOWN - unable to determine installed kernel version')
 | 
						|
        sys.exit(3)
 | 
						|
    return installed[-1]
 | 
						|
 | 
						|
 | 
						|
def clean_kernel_version(version):
 | 
						|
    # arch
 | 
						|
    version = re.sub('\.(x86_64|i[3-6]86)', '', version)
 | 
						|
    # Fedora release
 | 
						|
    version = re.sub('\.fc\d+', '', version)
 | 
						|
    # RHEL release
 | 
						|
    version = re.sub('\.el[\d\._]+$', '', version)
 | 
						|
 | 
						|
    return Version(version)
 | 
						|
 | 
						|
 | 
						|
def main():
 | 
						|
    if len(sys.argv) > 1:
 | 
						|
        print('This plugin no longer takes the expected kernel version as' +
 | 
						|
              'an argument')
 | 
						|
        sys.exit(3)
 | 
						|
 | 
						|
    running = running_kernel_version()
 | 
						|
    installed = installed_kernel_version()
 | 
						|
 | 
						|
    if running == installed:
 | 
						|
        print('KERNEL OK - running version %s' % running)
 | 
						|
        sys.exit(0)
 | 
						|
    else:
 | 
						|
        print('KERNEL WARNING - running version %s, installed: %s' %
 | 
						|
              (running, installed))
 | 
						|
        sys.exit(1)
 | 
						|
 | 
						|
 | 
						|
if __name__ == '__main__':
 | 
						|
    main()
 |