From 18396eba9ef53d96069a89578e94390c4d96204c Mon Sep 17 00:00:00 2001 From: Matěj Cepl Date: Sun, 29 Sep 2019 00:25:26 +0200 Subject: Correctly diagnose openSUSE/SLE system. --- sos/policies/distros/suse.py | 197 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 179 insertions(+), 18 deletions(-) diff --git a/sos/policies/distros/suse.py b/sos/policies/distros/suse.py index 8c7900e5..d33053fc 100644 --- a/sos/policies/distros/suse.py +++ b/sos/policies/distros/suse.py @@ -9,18 +9,34 @@ import os import sys +import re from sos.report.plugins import RedHatPlugin, SuSEPlugin from sos.policies.distros import LinuxPolicy +from sos.policies.package_managers import PackageManager from sos.policies.package_managers.rpm import RpmPackageManager +from sos.presets import PresetDefaults from sos import _sos as _ +from sos import SoSOptions +OS_RELEASE = "/etc/os-release" -class SuSEPolicy(LinuxPolicy): - distro = "SuSE" - vendor = "SuSE" + +class SUSEPolicy(LinuxPolicy): + distro = "SUSE" + vendor = "SUSE" vendor_urls = [('Distribution Website', 'https://www.suse.com/')] + vendor_url = "https://www.suse.com/" _tmp_dir = "/var/tmp" + _rpmq_cmd = 'rpm -qa --queryformat "%{NAME}|%{VERSION}|%{RELEASE}\\n"' + _rpmql_cmd = 'rpm -qal' + _rpmv_cmd = 'rpm -V' + _rpmv_filter = ["debuginfo", "-devel"] + _in_container = False + _host_sysroot = '/' + default_scl_prefix = '/opt/suse' + name_pattern = 'friendly' + init = 'systemd' def __init__(self, sysroot=None, init=None, probe_runtime=True, remote_exec=None): @@ -41,26 +57,170 @@ class SuSEPolicy(LinuxPolicy): self.PATH += os.pathsep + "/usr/local/bin" self.PATH += os.pathsep + "/usr/local/sbin" self.set_exec_path() + self.load_presets() @classmethod def check(cls, remote=''): """This method checks to see if we are running on SuSE. It must be overriden by concrete subclasses to return True when running on an - OpenSuSE, SLES or other Suse distribution and False otherwise.""" + OpenSUSE, SLES or other Suse distribution and False otherwise.""" return False + def check_usrmove(self, pkgs): + """Test whether the running system implements UsrMove. + + If the 'filesystem' package is present, it will check that the + version is greater than 3. If the package is not present the + '/bin' and '/sbin' paths are checked and UsrMove is assumed + if both are symbolic links. + + :param pkgs: a packages dictionary + """ + if 'filesystem' not in pkgs: + return os.path.islink('/bin') and os.path.islink('/sbin') + else: + filesys_version = pkgs['filesystem']['version'] + return True if filesys_version[0] == '3' else False + + def mangle_package_path(self, files): + """Mangle paths for post-UsrMove systems. + + If the system implements UsrMove, all files will be in + '/usr/[s]bin'. This method substitutes all the /[s]bin + references in the 'files' list with '/usr/[s]bin'. + + :param files: the list of package managed files + """ + paths = [] + + def transform_path(path): + # Some packages actually own paths in /bin: in this case, + # duplicate the path as both the / and /usr version. + skip_paths = ["/bin/rpm", "/bin/mailx"] + if path in skip_paths: + return (path, os.path.join("/usr", path[1:])) + return (re.sub(r'(^)(/s?bin)', r'\1/usr\2', path),) + + if self.usrmove: + for f in files: + paths.extend(transform_path(f)) + return paths + else: + return files + + def _container_init(self): + """Check if sos is running in a container and perform container + specific initialisation based on ENV_HOST_SYSROOT. + """ + if ENV_CONTAINER in os.environ: + if os.environ[ENV_CONTAINER] in ['docker', 'oci']: + self._in_container = True + if ENV_HOST_SYSROOT in os.environ: + self._host_sysroot = os.environ[ENV_HOST_SYSROOT] + use_sysroot = self._in_container and self._host_sysroot != '/' + if use_sysroot: + host_tmp_dir = os.path.abspath(self._host_sysroot + self._tmp_dir) + self._tmp_dir = host_tmp_dir + return self._host_sysroot if use_sysroot else None + + def runlevel_by_service(self, name): + from subprocess import Popen, PIPE + ret = [] + p = Popen("LC_ALL=C /sbin/chkconfig --list %s" % name, + shell=True, + stdout=PIPE, + stderr=PIPE, + bufsize=-1, + close_fds=True) + out, err = p.communicate() + if err: + return ret + for tabs in out.split()[1:]: + try: + (runlevel, onoff) = tabs.split(":", 1) + except IndexError: + pass + else: + if onoff == "on": + ret.append(int(runlevel)) + return ret + def get_tmp_dir(self, opt_tmp_dir): if not opt_tmp_dir: return self._tmp_dir return opt_tmp_dir - def get_local_name(self): - return self.host_name() + +# Container environment variables on SUSE systems. +ENV_CONTAINER = 'container' +ENV_HOST_SYSROOT = 'HOST' + +_opts_verify = SoSOptions(verify=True) +_opts_all_logs = SoSOptions(all_logs=True) +_opts_all_logs_verify = SoSOptions(all_logs=True, verify=True) +_opts_all_logs_no_lsof = SoSOptions(all_logs=True, + plugopts=['process.lsof=off']) +_cb_plugs = ['abrt', 'block', 'boot', 'dnf', 'dracut', 'filesys', 'grub2', + 'hardware', 'host', 'kernel', 'logs', 'lvm2', 'memory', 'rpm', + 'process', 'systemd', 'yum', 'xfs'] + +SLE_RELEASE_STR = "SUSE Enterprise Linux" + +RHV = "rhv" +RHV_DESC = "SUSE Virtualization" + +SLE = "rhel" +SLE_DESC = SLE_RELEASE_STR + +RHOSP = "rhosp" +RHOSP_DESC = "SUSE OpenStack Platform" + +RHOCP = "ocp" +RHOCP_DESC = "OpenShift Container Platform by SUSE" + +RH_SATELLITE = "satellite" +RH_SATELLITE_DESC = "SUSE Satellite" +SAT_OPTS = SoSOptions(verify=True, plugopts=['apache.log=on']) + +CB = "cantboot" +CB_DESC = "For use when normal system startup fails" +CB_OPTS = SoSOptions(verify=True, all_logs=True, onlyplugins=_cb_plugs) +CB_NOTE = ("Data collection will be limited to a boot-affecting scope") + +NOTE_SIZE = "This preset may increase report size" +NOTE_TIME = "This preset may increase report run time" +NOTE_SIZE_TIME = "This preset may increase report size and run time" + +rhel_presets = { + RHV: PresetDefaults(name=RHV, desc=RHV_DESC, note=NOTE_TIME, + opts=_opts_verify), + SLE: PresetDefaults(name=SLE, desc=SLE_DESC), + RHOSP: PresetDefaults(name=RHOSP, desc=RHOSP_DESC, note=NOTE_SIZE, + opts=_opts_all_logs_no_lsof), + RHOCP: PresetDefaults(name=RHOCP, desc=RHOCP_DESC, note=NOTE_SIZE_TIME, + opts=_opts_all_logs_verify), + RH_SATELLITE: PresetDefaults(name=RH_SATELLITE, desc=RH_SATELLITE_DESC, + note=NOTE_TIME, opts=SAT_OPTS), + CB: PresetDefaults(name=CB, desc=CB_DESC, note=CB_NOTE, opts=CB_OPTS) +} + +# Legal disclaimer text for SUSE products +disclaimer_text = """ +Any information provided to %(vendor)s will be treated in \ +accordance with the published support policies at:\n + %(vendor_url)s + +The generated archive may contain data considered sensitive \ +and its content should be reviewed by the originating \ +organization before being passed to any third party. + +No changes will be made to system configuration. +""" -class OpenSuSEPolicy(SuSEPolicy): - distro = "OpenSuSE" - vendor = "SuSE" +class SLEPolicy(SUSEPolicy): + distro = SLE_RELEASE_STR + vendor = "SUSE" vendor_urls = [('Community Website', 'https://www.opensuse.org/')] msg = _("""\ This command will collect diagnostic and configuration \ @@ -70,10 +230,7 @@ applications. An archive containing the collected information will be \ generated in %(tmpdir)s and may be provided to a %(vendor)s \ support representative. - -No changes will be made to system configuration. -%(vendor_text)s -""") +""" + disclaimer_text + "%(vendor_text)s\n") def __init__(self, sysroot=None, init=None, probe_runtime=True, remote_exec=None): @@ -85,8 +242,12 @@ No changes will be made to system configuration. def check(cls, remote): """This method checks to see if we are running on SuSE. """ - - if remote: - return cls.distro in remote - - return os.path.isfile('/etc/SUSE-brand') + with open('/etc/os-release') as inf: + for l in inf: + if l.startswith('NAME'): + return 'SUSE' in l.split('=')[1].strip(' "\t\n') + + def opensuse_version(self): + pkg = self.pkg_by_name("openSUSE-release") or \ + self.all_pkgs_by_name_regex("openSUSE-release-.*")[-1] + return int(pkg["version"]) -- cgit