import sos-4.2-15.el8

This commit is contained in:
CentOS Sources 2022-02-25 05:27:50 +00:00 committed by Stepan Oksanichenko
parent f3fbf14566
commit d9791f66ed
7 changed files with 1394 additions and 854 deletions

View File

@ -417,3 +417,900 @@ index ef61fb344..e0617b45e 100644
self.ui_log.info("")
# package up and compress the results
From f22efe044f1f0565b57d6aeca2081a5227e0312c Mon Sep 17 00:00:00 2001
From: Jake Hunsaker <jhunsake@redhat.com>
Date: Mon, 14 Feb 2022 09:37:30 -0500
Subject: [PATCH] [utilities] Don't try to chroot to /
With the recent fix for sysroot being `None` to always being (correctly)
`/`, we should guard against situations where `sos_get_command_output()`
would now try to chroot to `/` before running any command. Incidentally,
this would also cause our unittests to fail if they were run by a
non-root user.
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
---
sos/utilities.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sos/utilities.py b/sos/utilities.py
index 6b13415b..d782123a 100644
--- a/sos/utilities.py
+++ b/sos/utilities.py
@@ -120,7 +120,7 @@ def sos_get_command_output(command, timeout=TIMEOUT_DEFAULT, stderr=False,
# closure are caught in the parent (chroot and chdir are bound from
# the enclosing scope).
def _child_prep_fn():
- if (chroot):
+ if chroot and chroot != '/':
os.chroot(chroot)
if (chdir):
os.chdir(chdir)
--
2.34.1
From 3d064102f8ca6662fd9602512e1cb05cf8746dfd Mon Sep 17 00:00:00 2001
From: Jake Hunsaker <jhunsake@redhat.com>
Date: Mon, 27 Sep 2021 19:01:16 -0400
Subject: [PATCH] [Systemd, Policy] Correct InitSystem chrooting when chroot is
needed
This commit resolves a situation in which `sos` is being run in a
container but the `SystemdInit` InitSystem would not properly load
information from the host, thus causing the `Plugin.is_service*()`
methods to erroneously fail or return `False`.
Fix this scenario by pulling the `_container_init()` and related logic
to check for a containerized host sysroot out of the Red Hat specific
policy and into the base `LinuxPolicy` class so that the init system can
be initialized with the correct sysroot, which is now used to chroot the
calls to the relevant `systemctl` commands.
For now, this does impose the use of looking for the `container` env var
(automatically set by docker, podman, and crio regardless of
distribution) and the use of the `HOST` env var to read where the host's
`/` filesystem is mounted within the container. If desired in the
future, this can be changed to allow policy-specific overrides. For now
however, this extends host collection via an sos container for all
distributions currently shipping sos.
Note that this issue only affected the `InitSystem` abstraction for
loading information about local services, and did not affect init system
related commands called by plugins as part of those collections.
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
---
sos/policies/distros/__init__.py | 28 ++++++++++++++++++++++++++-
sos/policies/distros/redhat.py | 27 +-------------------------
sos/policies/init_systems/__init__.py | 13 +++++++++++--
sos/policies/init_systems/systemd.py | 7 ++++---
4 files changed, 43 insertions(+), 32 deletions(-)
diff --git a/sos/policies/distros/__init__.py b/sos/policies/distros/__init__.py
index f5b9fd5b01..c33a356a75 100644
--- a/sos/policies/distros/__init__.py
+++ b/sos/policies/distros/__init__.py
@@ -29,6 +29,10 @@
except ImportError:
REQUESTS_LOADED = False
+# Container environment variables for detecting if we're in a container
+ENV_CONTAINER = 'container'
+ENV_HOST_SYSROOT = 'HOST'
+
class LinuxPolicy(Policy):
"""This policy is meant to be an abc class that provides common
@@ -69,10 +73,17 @@ def __init__(self, sysroot=None, init=None, probe_runtime=True):
probe_runtime=probe_runtime)
self.init_kernel_modules()
+ # need to set _host_sysroot before PackageManager()
+ if sysroot:
+ self._container_init()
+ self._host_sysroot = sysroot
+ else:
+ sysroot = self._container_init()
+
if init is not None:
self.init_system = init
elif os.path.isdir("/run/systemd/system/"):
- self.init_system = SystemdInit()
+ self.init_system = SystemdInit(chroot=sysroot)
else:
self.init_system = InitSystem()
@@ -130,6 +141,21 @@ def get_local_name(self):
def sanitize_filename(self, name):
return re.sub(r"[^-a-z,A-Z.0-9]", "", name)
+ 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', 'podman']:
+ 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 is not None
+ 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 init_kernel_modules(self):
"""Obtain a list of loaded kernel modules to reference later for plugin
enablement and SoSPredicate checks
diff --git a/sos/policies/distros/redhat.py b/sos/policies/distros/redhat.py
index b3a84336be..3476e21fb2 100644
--- a/sos/policies/distros/redhat.py
+++ b/sos/policies/distros/redhat.py
@@ -17,7 +17,7 @@
from sos.presets.redhat import (RHEL_PRESETS, ATOMIC_PRESETS, RHV, RHEL,
CB, RHOSP, RHOCP, RH_CFME, RH_SATELLITE,
ATOMIC)
-from sos.policies.distros import LinuxPolicy
+from sos.policies.distros import LinuxPolicy, ENV_HOST_SYSROOT
from sos.policies.package_managers.rpm import RpmPackageManager
from sos import _sos as _
@@ -56,12 +56,6 @@ def __init__(self, sysroot=None, init=None, probe_runtime=True,
super(RedHatPolicy, self).__init__(sysroot=sysroot, init=init,
probe_runtime=probe_runtime)
self.usrmove = False
- # need to set _host_sysroot before PackageManager()
- if sysroot:
- self._container_init()
- self._host_sysroot = sysroot
- else:
- sysroot = self._container_init()
self.package_manager = RpmPackageManager(chroot=sysroot,
remote_exec=remote_exec)
@@ -140,21 +134,6 @@ def transform_path(path):
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', 'podman']:
- 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 is not None
- 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 = []
@@ -183,10 +162,6 @@ def get_tmp_dir(self, opt_tmp_dir):
return opt_tmp_dir
-# Container environment variables on Red Hat systems.
-ENV_CONTAINER = 'container'
-ENV_HOST_SYSROOT = 'HOST'
-
# Legal disclaimer text for Red Hat products
disclaimer_text = """
Any information provided to %(vendor)s will be treated in \
diff --git a/sos/policies/init_systems/__init__.py b/sos/policies/init_systems/__init__.py
index dd663e6522..beac44cee3 100644
--- a/sos/policies/init_systems/__init__.py
+++ b/sos/policies/init_systems/__init__.py
@@ -29,9 +29,14 @@ class InitSystem():
status of services
:type query_cmd: ``str``
+ :param chroot: Location to chroot to for any command execution, i.e. the
+ sysroot if we're running in a container
+ :type chroot: ``str`` or ``None``
+
"""
- def __init__(self, init_cmd=None, list_cmd=None, query_cmd=None):
+ def __init__(self, init_cmd=None, list_cmd=None, query_cmd=None,
+ chroot=None):
"""Initialize a new InitSystem()"""
self.services = {}
@@ -39,6 +44,7 @@ def __init__(self, init_cmd=None, list_cmd=None, query_cmd=None):
self.init_cmd = init_cmd
self.list_cmd = "%s %s" % (self.init_cmd, list_cmd) or None
self.query_cmd = "%s %s" % (self.init_cmd, query_cmd) or None
+ self.chroot = chroot
def is_enabled(self, name):
"""Check if given service name is enabled
@@ -108,7 +114,10 @@ def _query_service(self, name):
"""Query an individual service"""
if self.query_cmd:
try:
- return sos_get_command_output("%s %s" % (self.query_cmd, name))
+ return sos_get_command_output(
+ "%s %s" % (self.query_cmd, name),
+ chroot=self.chroot
+ )
except Exception:
return None
return None
diff --git a/sos/policies/init_systems/systemd.py b/sos/policies/init_systems/systemd.py
index 1b138f97b3..76dc57e27f 100644
--- a/sos/policies/init_systems/systemd.py
+++ b/sos/policies/init_systems/systemd.py
@@ -15,11 +15,12 @@
class SystemdInit(InitSystem):
"""InitSystem abstraction for SystemD systems"""
- def __init__(self):
+ def __init__(self, chroot=None):
super(SystemdInit, self).__init__(
init_cmd='systemctl',
list_cmd='list-unit-files --type=service',
- query_cmd='status'
+ query_cmd='status',
+ chroot=chroot
)
self.load_all_services()
@@ -30,7 +31,7 @@ def parse_query(self, output):
return 'unknown'
def load_all_services(self):
- svcs = shell_out(self.list_cmd).splitlines()[1:]
+ svcs = shell_out(self.list_cmd, chroot=self.chroot).splitlines()[1:]
for line in svcs:
try:
name = line.split('.service')[0]
From e869bc84c714bfc2249bbcb84e14908049ee42c4 Mon Sep 17 00:00:00 2001
From: Jake Hunsaker <jhunsake@redhat.com>
Date: Mon, 27 Sep 2021 12:07:08 -0400
Subject: [PATCH] [Plugin,utilities] Add sysroot wrapper for os.path.join
Adds a wrapper for `os.path.join()` which accounts for non-/ sysroots,
like we have done previously for other `os.path` methods. Further
updates `Plugin()` to use this wrapper where appropriate.
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
---
sos/report/plugins/__init__.py | 43 +++++++++++++++++-----------------
sos/utilities.py | 6 +++++
2 files changed, 28 insertions(+), 21 deletions(-)
diff --git a/sos/report/plugins/__init__.py b/sos/report/plugins/__init__.py
index c635b8de9..1f84bca49 100644
--- a/sos/report/plugins/__init__.py
+++ b/sos/report/plugins/__init__.py
@@ -13,7 +13,7 @@
from sos.utilities import (sos_get_command_output, import_module, grep,
fileobj, tail, is_executable, TIMEOUT_DEFAULT,
path_exists, path_isdir, path_isfile, path_islink,
- listdir)
+ listdir, path_join)
import os
import glob
@@ -708,19 +708,6 @@ def _log_info(self, msg):
def _log_debug(self, msg):
self.soslog.debug(self._format_msg(msg))
- def join_sysroot(self, path):
- """Join a given path with the configured sysroot
-
- :param path: The filesystem path that needs to be joined
- :type path: ``str``
-
- :returns: The joined filesystem path
- :rtype: ``str``
- """
- if path[0] == os.sep:
- path = path[1:]
- return os.path.join(self.sysroot, path)
-
def strip_sysroot(self, path):
"""Remove the configured sysroot from a filesystem path
@@ -1176,7 +1163,7 @@ def _copy_dir(self, srcpath):
def _get_dest_for_srcpath(self, srcpath):
if self.use_sysroot():
- srcpath = self.join_sysroot(srcpath)
+ srcpath = self.path_join(srcpath)
for copied in self.copied_files:
if srcpath == copied["srcpath"]:
return copied["dstpath"]
@@ -1284,7 +1271,7 @@ def add_forbidden_path(self, forbidden, recursive=False):
forbidden = [forbidden]
if self.use_sysroot():
- forbidden = [self.join_sysroot(f) for f in forbidden]
+ forbidden = [self.path_join(f) for f in forbidden]
for forbid in forbidden:
self._log_info("adding forbidden path '%s'" % forbid)
@@ -1438,7 +1425,7 @@ def add_copy_spec(self, copyspecs, sizelimit=None, maxage=None,
since = self.get_option('since')
logarchive_pattern = re.compile(r'.*((\.(zip|gz|bz2|xz))|[-.][\d]+)$')
- configfile_pattern = re.compile(r"^%s/*" % self.join_sysroot("etc"))
+ configfile_pattern = re.compile(r"^%s/*" % self.path_join("etc"))
if not self.test_predicate(pred=pred):
self._log_info("skipped copy spec '%s' due to predicate (%s)" %
@@ -1468,7 +1455,7 @@ def add_copy_spec(self, copyspecs, sizelimit=None, maxage=None,
return False
if self.use_sysroot():
- copyspec = self.join_sysroot(copyspec)
+ copyspec = self.path_join(copyspec)
files = self._expand_copy_spec(copyspec)
@@ -1683,7 +1670,7 @@ def _add_device_cmd(self, cmds, devices, timeout=None, sizelimit=None,
if not _dev_ok:
continue
if prepend_path:
- device = os.path.join(prepend_path, device)
+ device = self.path_join(prepend_path, device)
_cmd = cmd % {'dev': device}
self._add_cmd_output(cmd=_cmd, timeout=timeout,
sizelimit=sizelimit, chroot=chroot,
@@ -2592,7 +2579,7 @@ def __expand(paths):
if self.path_isfile(path) or self.path_islink(path):
found_paths.append(path)
elif self.path_isdir(path) and self.listdir(path):
- found_paths.extend(__expand(os.path.join(path, '*')))
+ found_paths.extend(__expand(self.path_join(path, '*')))
else:
found_paths.append(path)
except PermissionError:
@@ -2608,7 +2595,7 @@ def __expand(paths):
if (os.access(copyspec, os.R_OK) and self.path_isdir(copyspec) and
self.listdir(copyspec)):
# the directory exists and is non-empty, recurse through it
- copyspec = os.path.join(copyspec, '*')
+ copyspec = self.path_join(copyspec, '*')
expanded = glob.glob(copyspec, recursive=True)
recursed_files = []
for _path in expanded:
@@ -2877,6 +2864,20 @@ def listdir(self, path):
"""
return listdir(path, self.commons['cmdlineopts'].sysroot)
+ def path_join(self, path, *p):
+ """Helper to call the sos.utilities wrapper that allows the
+ corresponding `os` call to account for sysroot
+
+ :param path: The leading path passed to os.path.join()
+ :type path: ``str``
+
+ :param p: Following path section(s) to be joined with ``path``,
+ an empty parameter will result in a path that ends with
+ a separator
+ :type p: ``str``
+ """
+ return path_join(path, *p, sysroot=self.sysroot)
+
def postproc(self):
"""Perform any postprocessing. To be replaced by a plugin if required.
"""
diff --git a/sos/utilities.py b/sos/utilities.py
index c940e066d..b75751539 100644
--- a/sos/utilities.py
+++ b/sos/utilities.py
@@ -242,6 +242,12 @@ def listdir(path, sysroot):
return _os_wrapper(path, sysroot, 'listdir', os)
+def path_join(path, *p, sysroot=os.sep):
+ if not path.startswith(sysroot):
+ path = os.path.join(sysroot, path.lstrip(os.sep))
+ return os.path.join(path, *p)
+
+
class AsyncReader(threading.Thread):
"""Used to limit command output to a given size without deadlocking
sos.
From 9596473d1779b9c48e9923c220aaf2b8d9b3bebf Mon Sep 17 00:00:00 2001
From: Jake Hunsaker <jhunsake@redhat.com>
Date: Thu, 18 Nov 2021 13:17:14 -0500
Subject: [PATCH] [global] Align sysroot determination and usage across sos
The determination of sysroot - being automatic, user-specified, or
controlled via environment variables in a container - has gotten muddied
over time. This has resulted in different parts of the project;
`Policy`, `Plugin`, `SoSComponent`, etc... to not always be in sync when
sysroot is not `/`, thus causing varying and unexpected/unintended
behavior.
Fix this by only determining sysroot within `Policy()` initialization,
and then using that determination across all aspects of the project that
use or reference sysroot.
This results in several changes:
- `PackageManager()` will now (again) correctly reference host package
lists when sos is run in a container.
- `ContainerRuntime()` is now able to activate when sos is running in a
container.
- Plugins will now properly use sysroot for _all_ plugin enablement
triggers.
- Plugins, Policy, and SoSComponents now all reference the
`self.sysroot` variable, rather than changing between `sysroot`.
`_host_sysroot`, and `commons['sysroot']`. `_host_sysroot` has been
removed from `Policy`.
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
---
sos/archive.py | 2 +-
sos/component.py | 2 +-
sos/policies/__init__.py | 11 +----------
sos/policies/distros/__init__.py | 33 +++++++++++++++++++------------
sos/policies/distros/debian.py | 2 +-
sos/policies/distros/redhat.py | 3 +--
sos/policies/runtimes/__init__.py | 15 +++++++++-----
sos/policies/runtimes/docker.py | 4 ++--
sos/report/__init__.py | 6 ++----
sos/report/plugins/__init__.py | 22 +++++++++++----------
sos/report/plugins/unpackaged.py | 7 ++++---
sos/utilities.py | 13 ++++++++----
12 files changed, 64 insertions(+), 56 deletions(-)
diff --git a/sos/archive.py b/sos/archive.py
index b02b247595..e3c68b7789 100644
--- a/sos/archive.py
+++ b/sos/archive.py
@@ -153,7 +153,7 @@ def dest_path(self, name):
return (os.path.join(self._archive_root, name))
def join_sysroot(self, path):
- if path.startswith(self.sysroot):
+ if not self.sysroot or path.startswith(self.sysroot):
return path
if path[0] == os.sep:
path = path[1:]
diff --git a/sos/component.py b/sos/component.py
index 5ac6e47f4f..dba0aabf2b 100644
--- a/sos/component.py
+++ b/sos/component.py
@@ -109,7 +109,7 @@ def __init__(self, parser, parsed_args, cmdline_args):
try:
import sos.policies
self.policy = sos.policies.load(sysroot=self.opts.sysroot)
- self.sysroot = self.policy.host_sysroot()
+ self.sysroot = self.policy.sysroot
except KeyboardInterrupt:
self._exit(0)
self._is_root = self.policy.is_root()
diff --git a/sos/policies/__init__.py b/sos/policies/__init__.py
index fb8db1d724..ef9188deb4 100644
--- a/sos/policies/__init__.py
+++ b/sos/policies/__init__.py
@@ -110,7 +110,6 @@ class Policy(object):
presets = {"": PresetDefaults()}
presets_path = PRESETS_PATH
_in_container = False
- _host_sysroot = '/'
def __init__(self, sysroot=None, probe_runtime=True):
"""Subclasses that choose to override this initializer should call
@@ -124,7 +123,7 @@ def __init__(self, sysroot=None, probe_runtime=True):
self.package_manager = PackageManager()
self.valid_subclasses = [IndependentPlugin]
self.set_exec_path()
- self._host_sysroot = sysroot
+ self.sysroot = sysroot
self.register_presets(GENERIC_PRESETS)
def check(self, remote=''):
@@ -177,14 +176,6 @@ def in_container(self):
"""
return self._in_container
- def host_sysroot(self):
- """Get the host's default sysroot
-
- :returns: Host sysroot
- :rtype: ``str`` or ``None``
- """
- return self._host_sysroot
-
def dist_version(self):
"""
Return the OS version
diff --git a/sos/policies/distros/__init__.py b/sos/policies/distros/__init__.py
index 7bdc81b852..c69fc1e73c 100644
--- a/sos/policies/distros/__init__.py
+++ b/sos/policies/distros/__init__.py
@@ -71,19 +71,18 @@ class LinuxPolicy(Policy):
def __init__(self, sysroot=None, init=None, probe_runtime=True):
super(LinuxPolicy, self).__init__(sysroot=sysroot,
probe_runtime=probe_runtime)
- self.init_kernel_modules()
- # need to set _host_sysroot before PackageManager()
if sysroot:
- self._container_init()
- self._host_sysroot = sysroot
+ self.sysroot = sysroot
else:
- sysroot = self._container_init()
+ self.sysroot = self._container_init()
+
+ self.init_kernel_modules()
if init is not None:
self.init_system = init
elif os.path.isdir("/run/systemd/system/"):
- self.init_system = SystemdInit(chroot=sysroot)
+ self.init_system = SystemdInit(chroot=self.sysroot)
else:
self.init_system = InitSystem()
@@ -149,27 +148,30 @@ def _container_init(self):
if os.environ[ENV_CONTAINER] in ['docker', 'oci', 'podman']:
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 is not None
+ _host_sysroot = os.environ[ENV_HOST_SYSROOT]
+ use_sysroot = self._in_container and _host_sysroot is not None
if use_sysroot:
- host_tmp_dir = os.path.abspath(self._host_sysroot + self._tmp_dir)
+ host_tmp_dir = os.path.abspath(_host_sysroot + self._tmp_dir)
self._tmp_dir = host_tmp_dir
- return self._host_sysroot if use_sysroot else None
+ return _host_sysroot if use_sysroot else None
def init_kernel_modules(self):
"""Obtain a list of loaded kernel modules to reference later for plugin
enablement and SoSPredicate checks
"""
self.kernel_mods = []
+ release = os.uname().release
# first load modules from lsmod
- lines = shell_out("lsmod", timeout=0).splitlines()
+ lines = shell_out("lsmod", timeout=0, chroot=self.sysroot).splitlines()
self.kernel_mods.extend([
line.split()[0].strip() for line in lines[1:]
])
# next, include kernel builtins
- builtins = "/usr/lib/modules/%s/modules.builtin" % os.uname().release
+ builtins = self.join_sysroot(
+ "/usr/lib/modules/%s/modules.builtin" % release
+ )
try:
with open(builtins, "r") as mfile:
for line in mfile:
@@ -186,7 +188,7 @@ def init_kernel_modules(self):
'dm_mod': 'CONFIG_BLK_DEV_DM'
}
- booted_config = "/boot/config-%s" % os.uname().release
+ booted_config = self.join_sysroot("/boot/config-%s" % release)
kconfigs = []
try:
with open(booted_config, "r") as kfile:
@@ -200,6 +202,11 @@ def init_kernel_modules(self):
if config_strings[builtin] in kconfigs:
self.kernel_mods.append(builtin)
+ def join_sysroot(self, path):
+ if self.sysroot and self.sysroot != '/':
+ path = os.path.join(self.sysroot, path.lstrip('/'))
+ return path
+
def pre_work(self):
# this method will be called before the gathering begins
diff --git a/sos/policies/distros/debian.py b/sos/policies/distros/debian.py
index 95b389a65e..639fd5eba3 100644
--- a/sos/policies/distros/debian.py
+++ b/sos/policies/distros/debian.py
@@ -27,7 +27,7 @@ def __init__(self, sysroot=None, init=None, probe_runtime=True,
remote_exec=None):
super(DebianPolicy, self).__init__(sysroot=sysroot, init=init,
probe_runtime=probe_runtime)
- self.package_manager = DpkgPackageManager(chroot=sysroot,
+ self.package_manager = DpkgPackageManager(chroot=self.sysroot,
remote_exec=remote_exec)
self.valid_subclasses += [DebianPlugin]
diff --git a/sos/policies/distros/redhat.py b/sos/policies/distros/redhat.py
index eb44240736..4b14abaf3a 100644
--- a/sos/policies/distros/redhat.py
+++ b/sos/policies/distros/redhat.py
@@ -42,7 +42,6 @@ class RedHatPolicy(LinuxPolicy):
_redhat_release = '/etc/redhat-release'
_tmp_dir = "/var/tmp"
_in_container = False
- _host_sysroot = '/'
default_scl_prefix = '/opt/rh'
name_pattern = 'friendly'
upload_url = None
@@ -57,7 +56,7 @@ def __init__(self, sysroot=None, init=None, probe_runtime=True,
probe_runtime=probe_runtime)
self.usrmove = False
- self.package_manager = RpmPackageManager(chroot=sysroot,
+ self.package_manager = RpmPackageManager(chroot=self.sysroot,
remote_exec=remote_exec)
self.valid_subclasses += [RedHatPlugin]
diff --git a/sos/policies/runtimes/__init__.py b/sos/policies/runtimes/__init__.py
index f28d6a1df3..2e60ad2361 100644
--- a/sos/policies/runtimes/__init__.py
+++ b/sos/policies/runtimes/__init__.py
@@ -64,7 +64,7 @@ def check_is_active(self):
:returns: ``True`` if the runtime is active, else ``False``
:rtype: ``bool``
"""
- if is_executable(self.binary):
+ if is_executable(self.binary, self.policy.sysroot):
self.active = True
return True
return False
@@ -78,7 +78,7 @@ def get_containers(self, get_all=False):
containers = []
_cmd = "%s ps %s" % (self.binary, '-a' if get_all else '')
if self.active:
- out = sos_get_command_output(_cmd)
+ out = sos_get_command_output(_cmd, chroot=self.policy.sysroot)
if out['status'] == 0:
for ent in out['output'].splitlines()[1:]:
ent = ent.split()
@@ -112,8 +112,10 @@ def get_images(self):
images = []
fmt = '{{lower .Repository}}:{{lower .Tag}} {{lower .ID}}'
if self.active:
- out = sos_get_command_output("%s images --format '%s'"
- % (self.binary, fmt))
+ out = sos_get_command_output(
+ "%s images --format '%s'" % (self.binary, fmt),
+ chroot=self.policy.sysroot
+ )
if out['status'] == 0:
for ent in out['output'].splitlines():
ent = ent.split()
@@ -129,7 +131,10 @@ def get_volumes(self):
"""
vols = []
if self.active:
- out = sos_get_command_output("%s volume ls" % self.binary)
+ out = sos_get_command_output(
+ "%s volume ls" % self.binary,
+ chroot=self.policy.sysroot
+ )
if out['status'] == 0:
for ent in out['output'].splitlines()[1:]:
ent = ent.split()
diff --git a/sos/policies/runtimes/docker.py b/sos/policies/runtimes/docker.py
index 759dfaf6a0..e81f580ec3 100644
--- a/sos/policies/runtimes/docker.py
+++ b/sos/policies/runtimes/docker.py
@@ -18,9 +18,9 @@ class DockerContainerRuntime(ContainerRuntime):
name = 'docker'
binary = 'docker'
- def check_is_active(self):
+ def check_is_active(self, sysroot=None):
# the daemon must be running
- if (is_executable('docker') and
+ if (is_executable('docker', sysroot) and
(self.policy.init_system.is_running('docker') or
self.policy.init_system.is_running('snap.docker.dockerd'))):
self.active = True
diff --git a/sos/report/__init__.py b/sos/report/__init__.py
index a4c92accd3..a6c72778fc 100644
--- a/sos/report/__init__.py
+++ b/sos/report/__init__.py
@@ -173,14 +173,12 @@ def __init__(self, parser, args, cmdline):
self._set_directories()
msg = "default"
- host_sysroot = self.policy.host_sysroot()
+ self.sysroot = self.policy.sysroot
# set alternate system root directory
if self.opts.sysroot:
msg = "cmdline"
- self.sysroot = self.opts.sysroot
- elif self.policy.in_container() and host_sysroot != os.sep:
+ elif self.policy.in_container() and self.sysroot != os.sep:
msg = "policy"
- self.sysroot = host_sysroot
self.soslog.debug("set sysroot to '%s' (%s)" % (self.sysroot, msg))
if self.opts.chroot not in chroot_modes:
diff --git a/sos/report/plugins/__init__.py b/sos/report/plugins/__init__.py
index 46028bb124..e180ae1727 100644
--- a/sos/report/plugins/__init__.py
+++ b/sos/report/plugins/__init__.py
@@ -724,7 +724,7 @@ def strip_sysroot(self, path):
"""
if not self.use_sysroot():
return path
- if path.startswith(self.sysroot):
+ if self.sysroot and path.startswith(self.sysroot):
return path[len(self.sysroot):]
return path
@@ -743,8 +743,10 @@ def tmp_in_sysroot(self):
``False``
:rtype: ``bool``
"""
- paths = [self.sysroot, self.archive.get_tmp_dir()]
- return os.path.commonprefix(paths) == self.sysroot
+ # if sysroot is still None, that implies '/'
+ _sysroot = self.sysroot or '/'
+ paths = [_sysroot, self.archive.get_tmp_dir()]
+ return os.path.commonprefix(paths) == _sysroot
def is_installed(self, package_name):
"""Is the package $package_name installed?
@@ -2621,7 +2623,7 @@ def __expand(paths):
return list(set(expanded))
def _collect_copy_specs(self):
- for path in self.copy_paths:
+ for path in sorted(self.copy_paths, reverse=True):
self._log_info("collecting path '%s'" % path)
self._do_copy_path(path)
self.generate_copyspec_tags()
@@ -2749,7 +2751,7 @@ def _check_plugin_triggers(self, files, packages, commands, services,
return ((any(self.path_exists(fname) for fname in files) or
any(self.is_installed(pkg) for pkg in packages) or
- any(is_executable(cmd) for cmd in commands) or
+ any(is_executable(cmd, self.sysroot) for cmd in commands) or
any(self.is_module_loaded(mod) for mod in self.kernel_mods) or
any(self.is_service(svc) for svc in services) or
any(self.container_exists(cntr) for cntr in containers)) and
@@ -2817,7 +2819,7 @@ def path_exists(self, path):
:returns: True if the path exists in sysroot, else False
:rtype: ``bool``
"""
- return path_exists(path, self.commons['cmdlineopts'].sysroot)
+ return path_exists(path, self.sysroot)
def path_isdir(self, path):
"""Helper to call the sos.utilities wrapper that allows the
@@ -2830,7 +2832,7 @@ def path_isdir(self, path):
:returns: True if the path is a dir, else False
:rtype: ``bool``
"""
- return path_isdir(path, self.commons['cmdlineopts'].sysroot)
+ return path_isdir(path, self.sysroot)
def path_isfile(self, path):
"""Helper to call the sos.utilities wrapper that allows the
@@ -2843,7 +2845,7 @@ def path_isfile(self, path):
:returns: True if the path is a file, else False
:rtype: ``bool``
"""
- return path_isfile(path, self.commons['cmdlineopts'].sysroot)
+ return path_isfile(path, self.sysroot)
def path_islink(self, path):
"""Helper to call the sos.utilities wrapper that allows the
@@ -2856,7 +2858,7 @@ def path_islink(self, path):
:returns: True if the path is a link, else False
:rtype: ``bool``
"""
- return path_islink(path, self.commons['cmdlineopts'].sysroot)
+ return path_islink(path, self.sysroot)
def listdir(self, path):
"""Helper to call the sos.utilities wrapper that allows the
@@ -2869,7 +2871,7 @@ def listdir(self, path):
:returns: Contents of path, if it is a directory
:rtype: ``list``
"""
- return listdir(path, self.commons['cmdlineopts'].sysroot)
+ return listdir(path, self.sysroot)
def path_join(self, path, *p):
"""Helper to call the sos.utilities wrapper that allows the
diff --git a/sos/report/plugins/unpackaged.py b/sos/report/plugins/unpackaged.py
index 772b1d1fbb..24203c4b13 100644
--- a/sos/report/plugins/unpackaged.py
+++ b/sos/report/plugins/unpackaged.py
@@ -58,10 +58,11 @@ def format_output(files):
"""
expanded = []
for f in files:
- if self.path_islink(f):
- expanded.append("{} -> {}".format(f, os.readlink(f)))
+ fp = self.path_join(f)
+ if self.path_islink(fp):
+ expanded.append("{} -> {}".format(fp, os.readlink(fp)))
else:
- expanded.append(f)
+ expanded.append(fp)
return expanded
# Check command predicate to avoid costly processing
diff --git a/sos/utilities.py b/sos/utilities.py
index b757515397..d66309334b 100644
--- a/sos/utilities.py
+++ b/sos/utilities.py
@@ -96,11 +96,15 @@ def grep(pattern, *files_or_paths):
return matches
-def is_executable(command):
+def is_executable(command, sysroot=None):
"""Returns if a command matches an executable on the PATH"""
paths = os.environ.get("PATH", "").split(os.path.pathsep)
candidates = [command] + [os.path.join(p, command) for p in paths]
+ if sysroot:
+ candidates += [
+ os.path.join(sysroot, c.lstrip('/')) for c in candidates
+ ]
return any(os.access(path, os.X_OK) for path in candidates)
@@ -216,8 +220,9 @@ def get_human_readable(size, precision=2):
def _os_wrapper(path, sysroot, method, module=os.path):
- if sysroot not in [None, '/']:
- path = os.path.join(sysroot, path.lstrip('/'))
+ if sysroot and sysroot != os.sep:
+ if not path.startswith(sysroot):
+ path = os.path.join(sysroot, path.lstrip('/'))
_meth = getattr(module, method)
return _meth(path)
@@ -243,7 +248,7 @@ def listdir(path, sysroot):
def path_join(path, *p, sysroot=os.sep):
- if not path.startswith(sysroot):
+ if sysroot and not path.startswith(sysroot):
path = os.path.join(sysroot, path.lstrip(os.sep))
return os.path.join(path, *p)
From a43124e1f6217107838eed4d70339d100cbbc77a Mon Sep 17 00:00:00 2001
From: Pavel Moravec <pmoravec@redhat.com>
Date: Wed, 9 Feb 2022 19:45:27 +0100
Subject: [PATCH] [policies] Set fallback to None sysroot
9596473 commit added a regression allowing to set sysroot to None
when running sos report on a regular system (outside a container). In
such a case, we need to fallback to '/' sysroot.
Resolves: #2846
Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
---
sos/policies/distros/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sos/policies/distros/__init__.py b/sos/policies/distros/__init__.py
index f3c1de11..9048f1c4 100644
--- a/sos/policies/distros/__init__.py
+++ b/sos/policies/distros/__init__.py
@@ -78,7 +78,7 @@ class LinuxPolicy(Policy):
if sysroot:
self.sysroot = sysroot
else:
- self.sysroot = self._container_init()
+ self.sysroot = self._container_init() or '/'
self.init_kernel_modules()
--
2.34.1

View File

@ -1797,3 +1797,33 @@ index 229c7de4..3208a655 100644
--
2.31.1
From 7ebb2ce0bcd13c1b3aada648aceb20b5aff636d9 Mon Sep 17 00:00:00 2001
From: Jake Hunsaker <jhunsake@redhat.com>
Date: Tue, 15 Feb 2022 14:18:02 -0500
Subject: [PATCH] [host] Skip entire /etc/sos/cleaner directory
While `default_mapping` is typically the only file expected under
`/etc/sos/cleaner/` it is possible for other mapping files (such as
backups) to appear there.
Make the `add_forbidden_path()` spec here target the entire cleaner
directory to avoid ever capturing these map files.
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
---
sos/report/plugins/host.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sos/report/plugins/host.py b/sos/report/plugins/host.py
index 5e21da7b8e..95a3b9cd95 100644
--- a/sos/report/plugins/host.py
+++ b/sos/report/plugins/host.py
@@ -20,7 +20,7 @@ class Host(Plugin, IndependentPlugin):
def setup(self):
- self.add_forbidden_path('/etc/sos/cleaner/default_mapping')
+ self.add_forbidden_path('/etc/sos/cleaner')
self.add_cmd_output('hostname', root_symlink='hostname')
self.add_cmd_output('uptime', root_symlink='uptime')

View File

@ -1608,159 +1608,6 @@ index 00000000..a4897f19
--
2.31.1
From e869bc84c714bfc2249bbcb84e14908049ee42c4 Mon Sep 17 00:00:00 2001
From: Jake Hunsaker <jhunsake@redhat.com>
Date: Mon, 27 Sep 2021 12:07:08 -0400
Subject: [PATCH 1/2] [Plugin,utilities] Add sysroot wrapper for os.path.join
Adds a wrapper for `os.path.join()` which accounts for non-/ sysroots,
like we have done previously for other `os.path` methods. Further
updates `Plugin()` to use this wrapper where appropriate.
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
---
sos/report/plugins/__init__.py | 43 +++++++++++++++++-----------------
sos/utilities.py | 6 +++++
2 files changed, 28 insertions(+), 21 deletions(-)
diff --git a/sos/report/plugins/__init__.py b/sos/report/plugins/__init__.py
index c635b8de..1f84bca4 100644
--- a/sos/report/plugins/__init__.py
+++ b/sos/report/plugins/__init__.py
@@ -13,7 +13,7 @@
from sos.utilities import (sos_get_command_output, import_module, grep,
fileobj, tail, is_executable, TIMEOUT_DEFAULT,
path_exists, path_isdir, path_isfile, path_islink,
- listdir)
+ listdir, path_join)
import os
import glob
@@ -708,19 +708,6 @@ class Plugin():
def _log_debug(self, msg):
self.soslog.debug(self._format_msg(msg))
- def join_sysroot(self, path):
- """Join a given path with the configured sysroot
-
- :param path: The filesystem path that needs to be joined
- :type path: ``str``
-
- :returns: The joined filesystem path
- :rtype: ``str``
- """
- if path[0] == os.sep:
- path = path[1:]
- return os.path.join(self.sysroot, path)
-
def strip_sysroot(self, path):
"""Remove the configured sysroot from a filesystem path
@@ -1176,7 +1163,7 @@ class Plugin():
def _get_dest_for_srcpath(self, srcpath):
if self.use_sysroot():
- srcpath = self.join_sysroot(srcpath)
+ srcpath = self.path_join(srcpath)
for copied in self.copied_files:
if srcpath == copied["srcpath"]:
return copied["dstpath"]
@@ -1284,7 +1271,7 @@ class Plugin():
forbidden = [forbidden]
if self.use_sysroot():
- forbidden = [self.join_sysroot(f) for f in forbidden]
+ forbidden = [self.path_join(f) for f in forbidden]
for forbid in forbidden:
self._log_info("adding forbidden path '%s'" % forbid)
@@ -1438,7 +1425,7 @@ class Plugin():
since = self.get_option('since')
logarchive_pattern = re.compile(r'.*((\.(zip|gz|bz2|xz))|[-.][\d]+)$')
- configfile_pattern = re.compile(r"^%s/*" % self.join_sysroot("etc"))
+ configfile_pattern = re.compile(r"^%s/*" % self.path_join("etc"))
if not self.test_predicate(pred=pred):
self._log_info("skipped copy spec '%s' due to predicate (%s)" %
@@ -1468,7 +1455,7 @@ class Plugin():
return False
if self.use_sysroot():
- copyspec = self.join_sysroot(copyspec)
+ copyspec = self.path_join(copyspec)
files = self._expand_copy_spec(copyspec)
@@ -1683,7 +1670,7 @@ class Plugin():
if not _dev_ok:
continue
if prepend_path:
- device = os.path.join(prepend_path, device)
+ device = self.path_join(prepend_path, device)
_cmd = cmd % {'dev': device}
self._add_cmd_output(cmd=_cmd, timeout=timeout,
sizelimit=sizelimit, chroot=chroot,
@@ -2592,7 +2579,7 @@ class Plugin():
if self.path_isfile(path) or self.path_islink(path):
found_paths.append(path)
elif self.path_isdir(path) and self.listdir(path):
- found_paths.extend(__expand(os.path.join(path, '*')))
+ found_paths.extend(__expand(self.path_join(path, '*')))
else:
found_paths.append(path)
except PermissionError:
@@ -2608,7 +2595,7 @@ class Plugin():
if (os.access(copyspec, os.R_OK) and self.path_isdir(copyspec) and
self.listdir(copyspec)):
# the directory exists and is non-empty, recurse through it
- copyspec = os.path.join(copyspec, '*')
+ copyspec = self.path_join(copyspec, '*')
expanded = glob.glob(copyspec, recursive=True)
recursed_files = []
for _path in expanded:
@@ -2877,6 +2864,20 @@ class Plugin():
"""
return listdir(path, self.commons['cmdlineopts'].sysroot)
+ def path_join(self, path, *p):
+ """Helper to call the sos.utilities wrapper that allows the
+ corresponding `os` call to account for sysroot
+
+ :param path: The leading path passed to os.path.join()
+ :type path: ``str``
+
+ :param p: Following path section(s) to be joined with ``path``,
+ an empty parameter will result in a path that ends with
+ a separator
+ :type p: ``str``
+ """
+ return path_join(path, *p, sysroot=self.sysroot)
+
def postproc(self):
"""Perform any postprocessing. To be replaced by a plugin if required.
"""
diff --git a/sos/utilities.py b/sos/utilities.py
index c940e066..b7575153 100644
--- a/sos/utilities.py
+++ b/sos/utilities.py
@@ -242,6 +242,12 @@ def listdir(path, sysroot):
return _os_wrapper(path, sysroot, 'listdir', os)
+def path_join(path, *p, sysroot=os.sep):
+ if not path.startswith(sysroot):
+ path = os.path.join(sysroot, path.lstrip(os.sep))
+ return os.path.join(path, *p)
+
+
class AsyncReader(threading.Thread):
"""Used to limit command output to a given size without deadlocking
sos.
--
2.31.1
From 07d96d52ef69b9f8fe1ef32a1b88089d31c33fe8 Mon Sep 17 00:00:00 2001
From: Jake Hunsaker <jhunsake@redhat.com>
Date: Mon, 27 Sep 2021 12:28:27 -0400
@ -3679,697 +3526,6 @@ index e84b52da..1bfa741f 100644
--
2.31.1
From 3d064102f8ca6662fd9602512e1cb05cf8746dfd Mon Sep 17 00:00:00 2001
From: Jake Hunsaker <jhunsake@redhat.com>
Date: Mon, 27 Sep 2021 19:01:16 -0400
Subject: [PATCH] [Systemd, Policy] Correct InitSystem chrooting when chroot is
needed
This commit resolves a situation in which `sos` is being run in a
container but the `SystemdInit` InitSystem would not properly load
information from the host, thus causing the `Plugin.is_service*()`
methods to erroneously fail or return `False`.
Fix this scenario by pulling the `_container_init()` and related logic
to check for a containerized host sysroot out of the Red Hat specific
policy and into the base `LinuxPolicy` class so that the init system can
be initialized with the correct sysroot, which is now used to chroot the
calls to the relevant `systemctl` commands.
For now, this does impose the use of looking for the `container` env var
(automatically set by docker, podman, and crio regardless of
distribution) and the use of the `HOST` env var to read where the host's
`/` filesystem is mounted within the container. If desired in the
future, this can be changed to allow policy-specific overrides. For now
however, this extends host collection via an sos container for all
distributions currently shipping sos.
Note that this issue only affected the `InitSystem` abstraction for
loading information about local services, and did not affect init system
related commands called by plugins as part of those collections.
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
---
sos/policies/distros/__init__.py | 28 ++++++++++++++++++++++++++-
sos/policies/distros/redhat.py | 27 +-------------------------
sos/policies/init_systems/__init__.py | 13 +++++++++++--
sos/policies/init_systems/systemd.py | 7 ++++---
4 files changed, 43 insertions(+), 32 deletions(-)
diff --git a/sos/policies/distros/__init__.py b/sos/policies/distros/__init__.py
index f5b9fd5b01..c33a356a75 100644
--- a/sos/policies/distros/__init__.py
+++ b/sos/policies/distros/__init__.py
@@ -29,6 +29,10 @@
except ImportError:
REQUESTS_LOADED = False
+# Container environment variables for detecting if we're in a container
+ENV_CONTAINER = 'container'
+ENV_HOST_SYSROOT = 'HOST'
+
class LinuxPolicy(Policy):
"""This policy is meant to be an abc class that provides common
@@ -69,10 +73,17 @@ def __init__(self, sysroot=None, init=None, probe_runtime=True):
probe_runtime=probe_runtime)
self.init_kernel_modules()
+ # need to set _host_sysroot before PackageManager()
+ if sysroot:
+ self._container_init()
+ self._host_sysroot = sysroot
+ else:
+ sysroot = self._container_init()
+
if init is not None:
self.init_system = init
elif os.path.isdir("/run/systemd/system/"):
- self.init_system = SystemdInit()
+ self.init_system = SystemdInit(chroot=sysroot)
else:
self.init_system = InitSystem()
@@ -130,6 +141,21 @@ def get_local_name(self):
def sanitize_filename(self, name):
return re.sub(r"[^-a-z,A-Z.0-9]", "", name)
+ 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', 'podman']:
+ 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 is not None
+ 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 init_kernel_modules(self):
"""Obtain a list of loaded kernel modules to reference later for plugin
enablement and SoSPredicate checks
diff --git a/sos/policies/distros/redhat.py b/sos/policies/distros/redhat.py
index b3a84336be..3476e21fb2 100644
--- a/sos/policies/distros/redhat.py
+++ b/sos/policies/distros/redhat.py
@@ -17,7 +17,7 @@
from sos.presets.redhat import (RHEL_PRESETS, ATOMIC_PRESETS, RHV, RHEL,
CB, RHOSP, RHOCP, RH_CFME, RH_SATELLITE,
ATOMIC)
-from sos.policies.distros import LinuxPolicy
+from sos.policies.distros import LinuxPolicy, ENV_HOST_SYSROOT
from sos.policies.package_managers.rpm import RpmPackageManager
from sos import _sos as _
@@ -56,12 +56,6 @@ def __init__(self, sysroot=None, init=None, probe_runtime=True,
super(RedHatPolicy, self).__init__(sysroot=sysroot, init=init,
probe_runtime=probe_runtime)
self.usrmove = False
- # need to set _host_sysroot before PackageManager()
- if sysroot:
- self._container_init()
- self._host_sysroot = sysroot
- else:
- sysroot = self._container_init()
self.package_manager = RpmPackageManager(chroot=sysroot,
remote_exec=remote_exec)
@@ -140,21 +134,6 @@ def transform_path(path):
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', 'podman']:
- 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 is not None
- 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 = []
@@ -183,10 +162,6 @@ def get_tmp_dir(self, opt_tmp_dir):
return opt_tmp_dir
-# Container environment variables on Red Hat systems.
-ENV_CONTAINER = 'container'
-ENV_HOST_SYSROOT = 'HOST'
-
# Legal disclaimer text for Red Hat products
disclaimer_text = """
Any information provided to %(vendor)s will be treated in \
diff --git a/sos/policies/init_systems/__init__.py b/sos/policies/init_systems/__init__.py
index dd663e6522..beac44cee3 100644
--- a/sos/policies/init_systems/__init__.py
+++ b/sos/policies/init_systems/__init__.py
@@ -29,9 +29,14 @@ class InitSystem():
status of services
:type query_cmd: ``str``
+ :param chroot: Location to chroot to for any command execution, i.e. the
+ sysroot if we're running in a container
+ :type chroot: ``str`` or ``None``
+
"""
- def __init__(self, init_cmd=None, list_cmd=None, query_cmd=None):
+ def __init__(self, init_cmd=None, list_cmd=None, query_cmd=None,
+ chroot=None):
"""Initialize a new InitSystem()"""
self.services = {}
@@ -39,6 +44,7 @@ def __init__(self, init_cmd=None, list_cmd=None, query_cmd=None):
self.init_cmd = init_cmd
self.list_cmd = "%s %s" % (self.init_cmd, list_cmd) or None
self.query_cmd = "%s %s" % (self.init_cmd, query_cmd) or None
+ self.chroot = chroot
def is_enabled(self, name):
"""Check if given service name is enabled
@@ -108,7 +114,10 @@ def _query_service(self, name):
"""Query an individual service"""
if self.query_cmd:
try:
- return sos_get_command_output("%s %s" % (self.query_cmd, name))
+ return sos_get_command_output(
+ "%s %s" % (self.query_cmd, name),
+ chroot=self.chroot
+ )
except Exception:
return None
return None
diff --git a/sos/policies/init_systems/systemd.py b/sos/policies/init_systems/systemd.py
index 1b138f97b3..76dc57e27f 100644
--- a/sos/policies/init_systems/systemd.py
+++ b/sos/policies/init_systems/systemd.py
@@ -15,11 +15,12 @@
class SystemdInit(InitSystem):
"""InitSystem abstraction for SystemD systems"""
- def __init__(self):
+ def __init__(self, chroot=None):
super(SystemdInit, self).__init__(
init_cmd='systemctl',
list_cmd='list-unit-files --type=service',
- query_cmd='status'
+ query_cmd='status',
+ chroot=chroot
)
self.load_all_services()
@@ -30,7 +31,7 @@ def parse_query(self, output):
return 'unknown'
def load_all_services(self):
- svcs = shell_out(self.list_cmd).splitlines()[1:]
+ svcs = shell_out(self.list_cmd, chroot=self.chroot).splitlines()[1:]
for line in svcs:
try:
name = line.split('.service')[0]
--
2.31.1
From 9596473d1779b9c48e9923c220aaf2b8d9b3bebf Mon Sep 17 00:00:00 2001
From: Jake Hunsaker <jhunsake@redhat.com>
Date: Thu, 18 Nov 2021 13:17:14 -0500
Subject: [PATCH] [global] Align sysroot determination and usage across sos
The determination of sysroot - being automatic, user-specified, or
controlled via environment variables in a container - has gotten muddied
over time. This has resulted in different parts of the project;
`Policy`, `Plugin`, `SoSComponent`, etc... to not always be in sync when
sysroot is not `/`, thus causing varying and unexpected/unintended
behavior.
Fix this by only determining sysroot within `Policy()` initialization,
and then using that determination across all aspects of the project that
use or reference sysroot.
This results in several changes:
- `PackageManager()` will now (again) correctly reference host package
lists when sos is run in a container.
- `ContainerRuntime()` is now able to activate when sos is running in a
container.
- Plugins will now properly use sysroot for _all_ plugin enablement
triggers.
- Plugins, Policy, and SoSComponents now all reference the
`self.sysroot` variable, rather than changing between `sysroot`.
`_host_sysroot`, and `commons['sysroot']`. `_host_sysroot` has been
removed from `Policy`.
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
---
sos/archive.py | 2 +-
sos/component.py | 2 +-
sos/policies/__init__.py | 11 +----------
sos/policies/distros/__init__.py | 33 +++++++++++++++++++------------
sos/policies/distros/debian.py | 2 +-
sos/policies/distros/redhat.py | 3 +--
sos/policies/runtimes/__init__.py | 15 +++++++++-----
sos/policies/runtimes/docker.py | 4 ++--
sos/report/__init__.py | 6 ++----
sos/report/plugins/__init__.py | 22 +++++++++++----------
sos/report/plugins/unpackaged.py | 7 ++++---
sos/utilities.py | 13 ++++++++----
12 files changed, 64 insertions(+), 56 deletions(-)
diff --git a/sos/archive.py b/sos/archive.py
index b02b2475..e3c68b77 100644
--- a/sos/archive.py
+++ b/sos/archive.py
@@ -153,7 +153,7 @@ class FileCacheArchive(Archive):
return (os.path.join(self._archive_root, name))
def join_sysroot(self, path):
- if path.startswith(self.sysroot):
+ if not self.sysroot or path.startswith(self.sysroot):
return path
if path[0] == os.sep:
path = path[1:]
diff --git a/sos/component.py b/sos/component.py
index 5ac6e47f..dba0aabf 100644
--- a/sos/component.py
+++ b/sos/component.py
@@ -109,7 +109,7 @@ class SoSComponent():
try:
import sos.policies
self.policy = sos.policies.load(sysroot=self.opts.sysroot)
- self.sysroot = self.policy.host_sysroot()
+ self.sysroot = self.policy.sysroot
except KeyboardInterrupt:
self._exit(0)
self._is_root = self.policy.is_root()
diff --git a/sos/policies/__init__.py b/sos/policies/__init__.py
index fb8db1d7..ef9188de 100644
--- a/sos/policies/__init__.py
+++ b/sos/policies/__init__.py
@@ -110,7 +110,6 @@ any third party.
presets = {"": PresetDefaults()}
presets_path = PRESETS_PATH
_in_container = False
- _host_sysroot = '/'
def __init__(self, sysroot=None, probe_runtime=True):
"""Subclasses that choose to override this initializer should call
@@ -124,7 +123,7 @@ any third party.
self.package_manager = PackageManager()
self.valid_subclasses = [IndependentPlugin]
self.set_exec_path()
- self._host_sysroot = sysroot
+ self.sysroot = sysroot
self.register_presets(GENERIC_PRESETS)
def check(self, remote=''):
@@ -177,14 +176,6 @@ any third party.
"""
return self._in_container
- def host_sysroot(self):
- """Get the host's default sysroot
-
- :returns: Host sysroot
- :rtype: ``str`` or ``None``
- """
- return self._host_sysroot
-
def dist_version(self):
"""
Return the OS version
diff --git a/sos/policies/distros/__init__.py b/sos/policies/distros/__init__.py
index 7bdc81b8..c69fc1e7 100644
--- a/sos/policies/distros/__init__.py
+++ b/sos/policies/distros/__init__.py
@@ -71,19 +71,18 @@ class LinuxPolicy(Policy):
def __init__(self, sysroot=None, init=None, probe_runtime=True):
super(LinuxPolicy, self).__init__(sysroot=sysroot,
probe_runtime=probe_runtime)
- self.init_kernel_modules()
- # need to set _host_sysroot before PackageManager()
if sysroot:
- self._container_init()
- self._host_sysroot = sysroot
+ self.sysroot = sysroot
else:
- sysroot = self._container_init()
+ self.sysroot = self._container_init()
+
+ self.init_kernel_modules()
if init is not None:
self.init_system = init
elif os.path.isdir("/run/systemd/system/"):
- self.init_system = SystemdInit(chroot=sysroot)
+ self.init_system = SystemdInit(chroot=self.sysroot)
else:
self.init_system = InitSystem()
@@ -149,27 +148,30 @@ class LinuxPolicy(Policy):
if os.environ[ENV_CONTAINER] in ['docker', 'oci', 'podman']:
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 is not None
+ _host_sysroot = os.environ[ENV_HOST_SYSROOT]
+ use_sysroot = self._in_container and _host_sysroot is not None
if use_sysroot:
- host_tmp_dir = os.path.abspath(self._host_sysroot + self._tmp_dir)
+ host_tmp_dir = os.path.abspath(_host_sysroot + self._tmp_dir)
self._tmp_dir = host_tmp_dir
- return self._host_sysroot if use_sysroot else None
+ return _host_sysroot if use_sysroot else None
def init_kernel_modules(self):
"""Obtain a list of loaded kernel modules to reference later for plugin
enablement and SoSPredicate checks
"""
self.kernel_mods = []
+ release = os.uname().release
# first load modules from lsmod
- lines = shell_out("lsmod", timeout=0).splitlines()
+ lines = shell_out("lsmod", timeout=0, chroot=self.sysroot).splitlines()
self.kernel_mods.extend([
line.split()[0].strip() for line in lines[1:]
])
# next, include kernel builtins
- builtins = "/usr/lib/modules/%s/modules.builtin" % os.uname().release
+ builtins = self.join_sysroot(
+ "/usr/lib/modules/%s/modules.builtin" % release
+ )
try:
with open(builtins, "r") as mfile:
for line in mfile:
@@ -186,7 +188,7 @@ class LinuxPolicy(Policy):
'dm_mod': 'CONFIG_BLK_DEV_DM'
}
- booted_config = "/boot/config-%s" % os.uname().release
+ booted_config = self.join_sysroot("/boot/config-%s" % release)
kconfigs = []
try:
with open(booted_config, "r") as kfile:
@@ -200,6 +202,11 @@ class LinuxPolicy(Policy):
if config_strings[builtin] in kconfigs:
self.kernel_mods.append(builtin)
+ def join_sysroot(self, path):
+ if self.sysroot and self.sysroot != '/':
+ path = os.path.join(self.sysroot, path.lstrip('/'))
+ return path
+
def pre_work(self):
# this method will be called before the gathering begins
diff --git a/sos/policies/distros/debian.py b/sos/policies/distros/debian.py
index 95b389a6..639fd5eb 100644
--- a/sos/policies/distros/debian.py
+++ b/sos/policies/distros/debian.py
@@ -27,7 +27,7 @@ class DebianPolicy(LinuxPolicy):
remote_exec=None):
super(DebianPolicy, self).__init__(sysroot=sysroot, init=init,
probe_runtime=probe_runtime)
- self.package_manager = DpkgPackageManager(chroot=sysroot,
+ self.package_manager = DpkgPackageManager(chroot=self.sysroot,
remote_exec=remote_exec)
self.valid_subclasses += [DebianPlugin]
diff --git a/sos/policies/distros/redhat.py b/sos/policies/distros/redhat.py
index eb442407..4b14abaf 100644
--- a/sos/policies/distros/redhat.py
+++ b/sos/policies/distros/redhat.py
@@ -42,7 +42,6 @@ class RedHatPolicy(LinuxPolicy):
_redhat_release = '/etc/redhat-release'
_tmp_dir = "/var/tmp"
_in_container = False
- _host_sysroot = '/'
default_scl_prefix = '/opt/rh'
name_pattern = 'friendly'
upload_url = None
@@ -57,7 +56,7 @@ class RedHatPolicy(LinuxPolicy):
probe_runtime=probe_runtime)
self.usrmove = False
- self.package_manager = RpmPackageManager(chroot=sysroot,
+ self.package_manager = RpmPackageManager(chroot=self.sysroot,
remote_exec=remote_exec)
self.valid_subclasses += [RedHatPlugin]
diff --git a/sos/policies/runtimes/__init__.py b/sos/policies/runtimes/__init__.py
index f28d6a1d..2e60ad23 100644
--- a/sos/policies/runtimes/__init__.py
+++ b/sos/policies/runtimes/__init__.py
@@ -64,7 +64,7 @@ class ContainerRuntime():
:returns: ``True`` if the runtime is active, else ``False``
:rtype: ``bool``
"""
- if is_executable(self.binary):
+ if is_executable(self.binary, self.policy.sysroot):
self.active = True
return True
return False
@@ -78,7 +78,7 @@ class ContainerRuntime():
containers = []
_cmd = "%s ps %s" % (self.binary, '-a' if get_all else '')
if self.active:
- out = sos_get_command_output(_cmd)
+ out = sos_get_command_output(_cmd, chroot=self.policy.sysroot)
if out['status'] == 0:
for ent in out['output'].splitlines()[1:]:
ent = ent.split()
@@ -112,8 +112,10 @@ class ContainerRuntime():
images = []
fmt = '{{lower .Repository}}:{{lower .Tag}} {{lower .ID}}'
if self.active:
- out = sos_get_command_output("%s images --format '%s'"
- % (self.binary, fmt))
+ out = sos_get_command_output(
+ "%s images --format '%s'" % (self.binary, fmt),
+ chroot=self.policy.sysroot
+ )
if out['status'] == 0:
for ent in out['output'].splitlines():
ent = ent.split()
@@ -129,7 +131,10 @@ class ContainerRuntime():
"""
vols = []
if self.active:
- out = sos_get_command_output("%s volume ls" % self.binary)
+ out = sos_get_command_output(
+ "%s volume ls" % self.binary,
+ chroot=self.policy.sysroot
+ )
if out['status'] == 0:
for ent in out['output'].splitlines()[1:]:
ent = ent.split()
diff --git a/sos/policies/runtimes/docker.py b/sos/policies/runtimes/docker.py
index 759dfaf6..e81f580e 100644
--- a/sos/policies/runtimes/docker.py
+++ b/sos/policies/runtimes/docker.py
@@ -18,9 +18,9 @@ class DockerContainerRuntime(ContainerRuntime):
name = 'docker'
binary = 'docker'
- def check_is_active(self):
+ def check_is_active(self, sysroot=None):
# the daemon must be running
- if (is_executable('docker') and
+ if (is_executable('docker', sysroot) and
(self.policy.init_system.is_running('docker') or
self.policy.init_system.is_running('snap.docker.dockerd'))):
self.active = True
diff --git a/sos/report/__init__.py b/sos/report/__init__.py
index a4c92acc..a6c72778 100644
--- a/sos/report/__init__.py
+++ b/sos/report/__init__.py
@@ -173,14 +173,12 @@ class SoSReport(SoSComponent):
self._set_directories()
msg = "default"
- host_sysroot = self.policy.host_sysroot()
+ self.sysroot = self.policy.sysroot
# set alternate system root directory
if self.opts.sysroot:
msg = "cmdline"
- self.sysroot = self.opts.sysroot
- elif self.policy.in_container() and host_sysroot != os.sep:
+ elif self.policy.in_container() and self.sysroot != os.sep:
msg = "policy"
- self.sysroot = host_sysroot
self.soslog.debug("set sysroot to '%s' (%s)" % (self.sysroot, msg))
if self.opts.chroot not in chroot_modes:
diff --git a/sos/report/plugins/__init__.py b/sos/report/plugins/__init__.py
index 46028bb1..e180ae17 100644
--- a/sos/report/plugins/__init__.py
+++ b/sos/report/plugins/__init__.py
@@ -724,7 +724,7 @@ class Plugin():
"""
if not self.use_sysroot():
return path
- if path.startswith(self.sysroot):
+ if self.sysroot and path.startswith(self.sysroot):
return path[len(self.sysroot):]
return path
@@ -743,8 +743,10 @@ class Plugin():
``False``
:rtype: ``bool``
"""
- paths = [self.sysroot, self.archive.get_tmp_dir()]
- return os.path.commonprefix(paths) == self.sysroot
+ # if sysroot is still None, that implies '/'
+ _sysroot = self.sysroot or '/'
+ paths = [_sysroot, self.archive.get_tmp_dir()]
+ return os.path.commonprefix(paths) == _sysroot
def is_installed(self, package_name):
"""Is the package $package_name installed?
@@ -2621,7 +2623,7 @@ class Plugin():
return list(set(expanded))
def _collect_copy_specs(self):
- for path in self.copy_paths:
+ for path in sorted(self.copy_paths, reverse=True):
self._log_info("collecting path '%s'" % path)
self._do_copy_path(path)
self.generate_copyspec_tags()
@@ -2749,7 +2751,7 @@ class Plugin():
return ((any(self.path_exists(fname) for fname in files) or
any(self.is_installed(pkg) for pkg in packages) or
- any(is_executable(cmd) for cmd in commands) or
+ any(is_executable(cmd, self.sysroot) for cmd in commands) or
any(self.is_module_loaded(mod) for mod in self.kernel_mods) or
any(self.is_service(svc) for svc in services) or
any(self.container_exists(cntr) for cntr in containers)) and
@@ -2817,7 +2819,7 @@ class Plugin():
:returns: True if the path exists in sysroot, else False
:rtype: ``bool``
"""
- return path_exists(path, self.commons['cmdlineopts'].sysroot)
+ return path_exists(path, self.sysroot)
def path_isdir(self, path):
"""Helper to call the sos.utilities wrapper that allows the
@@ -2830,7 +2832,7 @@ class Plugin():
:returns: True if the path is a dir, else False
:rtype: ``bool``
"""
- return path_isdir(path, self.commons['cmdlineopts'].sysroot)
+ return path_isdir(path, self.sysroot)
def path_isfile(self, path):
"""Helper to call the sos.utilities wrapper that allows the
@@ -2843,7 +2845,7 @@ class Plugin():
:returns: True if the path is a file, else False
:rtype: ``bool``
"""
- return path_isfile(path, self.commons['cmdlineopts'].sysroot)
+ return path_isfile(path, self.sysroot)
def path_islink(self, path):
"""Helper to call the sos.utilities wrapper that allows the
@@ -2856,7 +2858,7 @@ class Plugin():
:returns: True if the path is a link, else False
:rtype: ``bool``
"""
- return path_islink(path, self.commons['cmdlineopts'].sysroot)
+ return path_islink(path, self.sysroot)
def listdir(self, path):
"""Helper to call the sos.utilities wrapper that allows the
@@ -2869,7 +2871,7 @@ class Plugin():
:returns: Contents of path, if it is a directory
:rtype: ``list``
"""
- return listdir(path, self.commons['cmdlineopts'].sysroot)
+ return listdir(path, self.sysroot)
def path_join(self, path, *p):
"""Helper to call the sos.utilities wrapper that allows the
diff --git a/sos/report/plugins/unpackaged.py b/sos/report/plugins/unpackaged.py
index 772b1d1f..24203c4b 100644
--- a/sos/report/plugins/unpackaged.py
+++ b/sos/report/plugins/unpackaged.py
@@ -58,10 +58,11 @@ class Unpackaged(Plugin, RedHatPlugin):
"""
expanded = []
for f in files:
- if self.path_islink(f):
- expanded.append("{} -> {}".format(f, os.readlink(f)))
+ fp = self.path_join(f)
+ if self.path_islink(fp):
+ expanded.append("{} -> {}".format(fp, os.readlink(fp)))
else:
- expanded.append(f)
+ expanded.append(fp)
return expanded
# Check command predicate to avoid costly processing
diff --git a/sos/utilities.py b/sos/utilities.py
index b7575153..d6630933 100644
--- a/sos/utilities.py
+++ b/sos/utilities.py
@@ -96,11 +96,15 @@ def grep(pattern, *files_or_paths):
return matches
-def is_executable(command):
+def is_executable(command, sysroot=None):
"""Returns if a command matches an executable on the PATH"""
paths = os.environ.get("PATH", "").split(os.path.pathsep)
candidates = [command] + [os.path.join(p, command) for p in paths]
+ if sysroot:
+ candidates += [
+ os.path.join(sysroot, c.lstrip('/')) for c in candidates
+ ]
return any(os.access(path, os.X_OK) for path in candidates)
@@ -216,8 +220,9 @@ def get_human_readable(size, precision=2):
def _os_wrapper(path, sysroot, method, module=os.path):
- if sysroot not in [None, '/']:
- path = os.path.join(sysroot, path.lstrip('/'))
+ if sysroot and sysroot != os.sep:
+ if not path.startswith(sysroot):
+ path = os.path.join(sysroot, path.lstrip('/'))
_meth = getattr(module, method)
return _meth(path)
@@ -243,7 +248,7 @@ def listdir(path, sysroot):
def path_join(path, *p, sysroot=os.sep):
- if not path.startswith(sysroot):
+ if sysroot and not path.startswith(sysroot):
path = os.path.join(sysroot, path.lstrip(os.sep))
return os.path.join(path, *p)
--
2.31.1
From 8bf602108f75db10e449eff5e2266c6466504086 Mon Sep 17 00:00:00 2001
From: Nadia Pinaeva <npinaeva@redhat.com>
Date: Thu, 2 Dec 2021 16:30:44 +0100
@ -5868,20 +5024,122 @@ index cb20772fd..b59eade9a 100644
def test_ip_parser_valid_ipv4_line(self):
line = 'foobar foo 10.0.0.1/24 barfoo bar'
From: Pavel Moravec <pmoravec@redhat.com>
Subject: downstream-only patch to allow container_runtime change on 4.2
sos cluster/collector already, as any 4.2 released version will support
it.
diff -rup a/sos/collector/sosnode.py b/sos/collector/sosnode.py
From 2ae16e0245e1b01b8547e507abb69c11871a8467 Mon Sep 17 00:00:00 2001
From: Jake Hunsaker <jhunsake@redhat.com>
Date: Mon, 21 Feb 2022 14:37:09 -0500
Subject: [PATCH] [sosnode] Handle downstream versioning for runtime option
check
First, adds parsing and formatting for an sos installation's release
version according to the loaded package manager for that node.
Adds a fallback version check for 4.2-13 for RHEL downstreams that
backport the `container-runtime` option into sos-4.2.
Carry this in upstream to account for use cases where a workstation used
to run `collect` from may be from a different stream than those used by
cluster nodes.
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
---
sos/collector/sosnode.py | 60 ++++++++++++++++++++++++++++++++++------
1 file changed, 51 insertions(+), 9 deletions(-)
diff --git a/sos/collector/sosnode.py b/sos/collector/sosnode.py
index 7bbe0cd1..d9b998b0 100644
--- a/sos/collector/sosnode.py
+++ b/sos/collector/sosnode.py
@@ -586,8 +586,6 @@ class SosNode():
if self.opts.cmd_timeout:
@@ -275,21 +275,34 @@ class SosNode():
def _load_sos_info(self):
"""Queries the node for information about the installed version of sos
"""
+ ver = None
+ rel = None
if self.host.container_version_command is None:
pkg = self.host.package_manager.pkg_version(self.host.sos_pkg_name)
if pkg is not None:
ver = '.'.join(pkg['version'])
- self.sos_info['version'] = ver
+ if pkg['release']:
+ rel = pkg['release']
+
else:
# use the containerized policy's command
pkgs = self.run_command(self.host.container_version_command,
use_container=True, need_root=True)
if pkgs['status'] == 0:
- ver = pkgs['output'].strip().split('-')[1]
- if ver:
- self.sos_info['version'] = ver
- else:
- self.sos_info['version'] = None
+ _, ver, rel = pkgs['output'].strip().split('-')
+
+ if ver:
+ if len(ver.split('.')) == 2:
+ # safeguard against maintenance releases throwing off the
+ # comparison by LooseVersion
+ ver += '.0'
+ try:
+ ver += '-%s' % rel.split('.')[0]
+ except Exception as err:
+ self.log_debug("Unable to fully parse sos release: %s" % err)
+
+ self.sos_info['version'] = ver
+
if self.sos_info['version']:
self.log_info('sos version is %s' % self.sos_info['version'])
else:
@@ -381,9 +394,37 @@ class SosNode():
"""Checks to see if the sos installation on the node is AT LEAST the
given ver. This means that if the installed version is greater than
ver, this will still return True
+
+ :param ver: Version number we are trying to verify is installed
+ :type ver: ``str``
+
+ :returns: True if installed version is at least ``ver``, else False
+ :rtype: ``bool``
"""
- return self.sos_info['version'] is not None and \
- LooseVersion(self.sos_info['version']) >= ver
+ def _format_version(ver):
+ # format the version we're checking to a standard form of X.Y.Z-R
+ try:
+ _fver = ver.split('-')[0]
+ _rel = ''
+ if '-' in ver:
+ _rel = '-' + ver.split('-')[-1].split('.')[0]
+ if len(_fver.split('.')) == 2:
+ _fver += '.0'
+
+ return _fver + _rel
+ except Exception as err:
+ self.log_debug("Unable to format '%s': %s" % (ver, err))
+ return ver
+
+ _ver = _format_version(ver)
+
+ try:
+ _node_ver = LooseVersion(self.sos_info['version'])
+ _test_ver = LooseVersion(_ver)
+ return _node_ver >= _test_ver
+ except Exception as err:
+ self.log_error("Error checking sos version: %s" % err)
+ return False
def is_installed(self, pkg):
"""Checks if a given package is installed on the node"""
@@ -587,7 +628,8 @@ class SosNode():
sos_opts.append('--cmd-timeout=%s'
% quote(str(self.opts.cmd_timeout)))
-
- if self.check_sos_version('4.3'):
+ # handle downstream versions that backported this option
+ if self.check_sos_version('4.3') or self.check_sos_version('4.2-13'):
if self.opts.container_runtime != 'auto':
sos_opts.append(
"--container-runtime=%s" % self.opts.container_runtime
--
2.34.1

View File

@ -43,3 +43,210 @@ index 78604a15a..25c38cccc 100644
class DebianOVNHost(OVNHost, DebianPlugin, UbuntuPlugin):
From 21fc376d97a5f74743e2b7cf7069349e874b979e Mon Sep 17 00:00:00 2001
From: Hemanth Nakkina <hemanth.nakkina@canonical.com>
Date: Fri, 4 Feb 2022 07:57:59 +0530
Subject: [PATCH] [ovn-central] collect NB/SB ovsdb-server cluster status
Add commands to collect cluster status of Northbound and
Southbound ovsdb servers.
Resolves: #2840
Signed-off-by: Hemanth Nakkina hemanth.nakkina@canonical.com
---
sos/report/plugins/ovn_central.py | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/sos/report/plugins/ovn_central.py b/sos/report/plugins/ovn_central.py
index 0f947d4c5..2f0438df3 100644
--- a/sos/report/plugins/ovn_central.py
+++ b/sos/report/plugins/ovn_central.py
@@ -84,6 +84,14 @@ def setup(self):
else:
self.add_copy_spec("/var/log/ovn/*.log")
+ # ovsdb nb/sb cluster status commands
+ ovsdb_cmds = [
+ 'ovs-appctl -t {} cluster/status OVN_Northbound'.format(
+ self.ovn_nbdb_sock_path),
+ 'ovs-appctl -t {} cluster/status OVN_Southbound'.format(
+ self.ovn_sbdb_sock_path),
+ ]
+
# Some user-friendly versions of DB output
nbctl_cmds = [
'ovn-nbctl show',
@@ -109,7 +117,8 @@ def setup(self):
self.add_database_output(nb_tables, nbctl_cmds, 'ovn-nbctl')
- cmds = nbctl_cmds
+ cmds = ovsdb_cmds
+ cmds += nbctl_cmds
# Can only run sbdb commands if we are the leader
co = {'cmd': "ovs-appctl -t {} cluster/status OVN_Southbound".
@@ -148,10 +157,12 @@ def setup(self):
class RedHatOVNCentral(OVNCentral, RedHatPlugin):
packages = ('openvswitch-ovn-central', 'ovn.*-central', )
+ ovn_nbdb_sock_path = '/var/run/openvswitch/ovnnb_db.ctl'
ovn_sbdb_sock_path = '/var/run/openvswitch/ovnsb_db.ctl'
class DebianOVNCentral(OVNCentral, DebianPlugin, UbuntuPlugin):
packages = ('ovn-central', )
+ ovn_nbdb_sock_path = '/var/run/ovn/ovnnb_db.ctl'
ovn_sbdb_sock_path = '/var/run/ovn/ovnsb_db.ctl'
From d0f9d507b0ec63c9e8f3e5d7b6507d9d0f97c038 Mon Sep 17 00:00:00 2001
From: Jake Hunsaker <jhunsake@redhat.com>
Date: Tue, 15 Feb 2022 16:24:47 -0500
Subject: [PATCH] [runtimes] Allow container IDs to be used with
`container_exists()`
As container runtimes can interchange container names and container IDs,
sos should also allow the use of container IDs when checking for the
presence of a given container.
In particular, this change unblocks the use of `Plugin.exec_cmd()` when
used in conjunction with `Plugin.get_container_by_name()` to pick a
container based on a provided regex that the container name may match.
Related: #2856
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
---
sos/policies/runtimes/__init__.py | 17 +++++++++++++++++
sos/report/plugins/__init__.py | 6 +++---
2 files changed, 20 insertions(+), 3 deletions(-)
diff --git a/sos/policies/runtimes/__init__.py b/sos/policies/runtimes/__init__.py
index 5ac673544..d28373496 100644
--- a/sos/policies/runtimes/__init__.py
+++ b/sos/policies/runtimes/__init__.py
@@ -147,6 +147,23 @@ def get_volumes(self):
vols.append(ent[-1])
return vols
+ def container_exists(self, container):
+ """Check if a given container ID or name exists on the system from the
+ perspective of the container runtime.
+
+ Note that this will only check _running_ containers
+
+ :param container: The name or ID of the container
+ :type container: ``str``
+
+ :returns: True if the container exists, else False
+ :rtype: ``bool``
+ """
+ for _contup in self.containers:
+ if container in _contup:
+ return True
+ return False
+
def fmt_container_cmd(self, container, cmd, quotecmd):
"""Format a command to run inside a container using the runtime
diff --git a/sos/report/plugins/__init__.py b/sos/report/plugins/__init__.py
index 2988be089..cc5cb65bc 100644
--- a/sos/report/plugins/__init__.py
+++ b/sos/report/plugins/__init__.py
@@ -2593,7 +2593,7 @@ def container_exists(self, name):
"""If a container runtime is present, check to see if a container with
a given name is currently running
- :param name: The name of the container to check presence of
+ :param name: The name or ID of the container to check presence of
:type name: ``str``
:returns: ``True`` if `name` exists, else ``False``
@@ -2601,8 +2601,8 @@ def container_exists(self, name):
"""
_runtime = self._get_container_runtime()
if _runtime is not None:
- con = _runtime.get_container_by_name(name)
- return con is not None
+ return (_runtime.container_exists(name) or
+ _runtime.get_container_by_name(name) is not None)
return False
def get_all_containers_by_regex(self, regex, get_all=False):
From de9b020a72d1ceda39587db4c6d5acf72cd90da2 Mon Sep 17 00:00:00 2001
From: Fernando Royo <froyo@redhat.com>
Date: Tue, 15 Feb 2022 10:00:38 +0100
Subject: [PATCH] [ovn_central] Rename container responsable of Red Hat
ovn_central plugin
ovn_central plugin is running by container with
name 'ovn-dbs-bundle*', a typo has been identified and
this cause plugin ovn_central not enabled by default as it
does not recognize any container responsible of this.
This patch fix this container name match, searching schema db
keeping backward compatibility with openvswitch.
---
sos/report/plugins/ovn_central.py | 23 ++++++++++++-----------
1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/sos/report/plugins/ovn_central.py b/sos/report/plugins/ovn_central.py
index 2f0438df..2f34bff0 100644
--- a/sos/report/plugins/ovn_central.py
+++ b/sos/report/plugins/ovn_central.py
@@ -24,7 +24,7 @@ class OVNCentral(Plugin):
short_desc = 'OVN Northd'
plugin_name = "ovn_central"
profiles = ('network', 'virt')
- containers = ('ovs-db-bundle.*',)
+ containers = ('ovn-dbs-bundle.*',)
def get_tables_from_schema(self, filename, skip=[]):
if self._container_name:
@@ -66,7 +66,7 @@ class OVNCentral(Plugin):
cmds.append('%s list %s' % (ovn_cmd, table))
def setup(self):
- self._container_name = self.get_container_by_name('ovs-dbs-bundle.*')
+ self._container_name = self.get_container_by_name(self.containers[0])
ovs_rundir = os.environ.get('OVS_RUNDIR')
for pidfile in ['ovnnb_db.pid', 'ovnsb_db.pid', 'ovn-northd.pid']:
@@ -110,12 +110,11 @@ class OVNCentral(Plugin):
'ovn-sbctl get-connection',
]
- schema_dir = '/usr/share/openvswitch'
-
- nb_tables = self.get_tables_from_schema(self.path_join(
- schema_dir, 'ovn-nb.ovsschema'))
-
- self.add_database_output(nb_tables, nbctl_cmds, 'ovn-nbctl')
+ # backward compatibility
+ for path in ['/usr/share/openvswitch', '/usr/share/ovn']:
+ nb_tables = self.get_tables_from_schema(self.path_join(
+ path, 'ovn-nb.ovsschema'))
+ self.add_database_output(nb_tables, nbctl_cmds, 'ovn-nbctl')
cmds = ovsdb_cmds
cmds += nbctl_cmds
@@ -125,9 +124,11 @@ class OVNCentral(Plugin):
format(self.ovn_sbdb_sock_path),
"output": "Leader: self"}
if self.test_predicate(self, pred=SoSPredicate(self, cmd_outputs=co)):
- sb_tables = self.get_tables_from_schema(self.path_join(
- schema_dir, 'ovn-sb.ovsschema'), ['Logical_Flow'])
- self.add_database_output(sb_tables, sbctl_cmds, 'ovn-sbctl')
+ # backward compatibility
+ for path in ['/usr/share/openvswitch', '/usr/share/ovn']:
+ sb_tables = self.get_tables_from_schema(self.path_join(
+ path, 'ovn-sb.ovsschema'), ['Logical_Flow'])
+ self.add_database_output(sb_tables, sbctl_cmds, 'ovn-sbctl')
cmds += sbctl_cmds
# If OVN is containerized, we need to run the above commands inside
--
2.34.1

View File

@ -0,0 +1,94 @@
From 5824cd5d3bddf39e0382d568419e2453abc93d8a Mon Sep 17 00:00:00 2001
From: Jake Hunsaker <jhunsake@redhat.com>
Date: Mon, 30 Aug 2021 15:09:07 -0400
Subject: [PATCH] [options] Fix logging on plugopts in effective sos command
First, provide a special-case handling for plugin options specified in
sos.conf in `SoSOptions.to_args().has_value()` that allows for plugin
options to be included in the "effective options now" log message.
Second, move the logging of said message (and thus the merging of
preset options, if used), to being _prior_ to the loading of plugin
options.
Combined, plugin options specified in sos.conf will now be logged
properly and this logging will occur before we set (and log the setting
of) those options.
Resolves: #2663
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
---
sos/options.py | 2 ++
sos/report/__init__.py | 30 ++++++++++++++++--------------
2 files changed, 18 insertions(+), 14 deletions(-)
diff --git a/sos/options.py b/sos/options.py
index a014a022..7bea3ffc 100644
--- a/sos/options.py
+++ b/sos/options.py
@@ -281,6 +281,8 @@ class SoSOptions():
null_values = ("False", "None", "[]", '""', "''", "0")
if not value or value in null_values:
return False
+ if name == 'plugopts' and value:
+ return True
if name in self.arg_defaults:
if str(value) == str(self.arg_defaults[name]):
return False
diff --git a/sos/report/__init__.py b/sos/report/__init__.py
index b0159e5b..82484f1d 100644
--- a/sos/report/__init__.py
+++ b/sos/report/__init__.py
@@ -925,20 +925,6 @@ class SoSReport(SoSComponent):
self._exit(1)
def setup(self):
- # Log command line options
- msg = "[%s:%s] executing 'sos %s'"
- self.soslog.info(msg % (__name__, "setup", " ".join(self.cmdline)))
-
- # Log active preset defaults
- preset_args = self.preset.opts.to_args()
- msg = ("[%s:%s] using '%s' preset defaults (%s)" %
- (__name__, "setup", self.preset.name, " ".join(preset_args)))
- self.soslog.info(msg)
-
- # Log effective options after applying preset defaults
- self.soslog.info("[%s:%s] effective options now: %s" %
- (__name__, "setup", " ".join(self.opts.to_args())))
-
self.ui_log.info(_(" Setting up plugins ..."))
for plugname, plug in self.loaded_plugins:
try:
@@ -1386,11 +1372,27 @@ class SoSReport(SoSComponent):
self.report_md.add_list('disabled_plugins', self.opts.skip_plugins)
self.report_md.add_section('plugins')
+ def _merge_preset_options(self):
+ # Log command line options
+ msg = "[%s:%s] executing 'sos %s'"
+ self.soslog.info(msg % (__name__, "setup", " ".join(self.cmdline)))
+
+ # Log active preset defaults
+ preset_args = self.preset.opts.to_args()
+ msg = ("[%s:%s] using '%s' preset defaults (%s)" %
+ (__name__, "setup", self.preset.name, " ".join(preset_args)))
+ self.soslog.info(msg)
+
+ # Log effective options after applying preset defaults
+ self.soslog.info("[%s:%s] effective options now: %s" %
+ (__name__, "setup", " ".join(self.opts.to_args())))
+
def execute(self):
try:
self.policy.set_commons(self.get_commons())
self.load_plugins()
self._set_all_options()
+ self._merge_preset_options()
self._set_tunables()
self._check_for_unknown_plugins()
self._set_plugin_options()
--
2.34.1

View File

@ -0,0 +1,39 @@
From 7069e99d1c5c443f96a98a7ed6db67fa14683e67 Mon Sep 17 00:00:00 2001
From: Pavel Moravec <pmoravec@redhat.com>
Date: Thu, 17 Feb 2022 09:14:15 +0100
Subject: [PATCH] [report] Honor plugins' hardcoded plugin_timeout
Currently, plugin's plugin_timeout hardcoded default is superseded by
whatever --plugin-timeout value, even when this option is not used and
we eval it to TIMEOUT_DEFAULT.
In this case of not setting --plugin-timeout either -k plugin.timeout,
honour plugin's plugin_timeout instead.
Resolves: #2863
Closes: #2864
Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
---
sos/report/plugins/__init__.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/sos/report/plugins/__init__.py b/sos/report/plugins/__init__.py
index cc5cb65b..336b4d22 100644
--- a/sos/report/plugins/__init__.py
+++ b/sos/report/plugins/__init__.py
@@ -636,7 +636,10 @@ class Plugin():
if opt_timeout is None:
_timeout = own_timeout
elif opt_timeout is not None and own_timeout == -1:
- _timeout = int(opt_timeout)
+ if opt_timeout == TIMEOUT_DEFAULT:
+ _timeout = default_timeout
+ else:
+ _timeout = int(opt_timeout)
elif opt_timeout is not None and own_timeout > -1:
_timeout = own_timeout
else:
--
2.34.1

View File

@ -5,7 +5,7 @@
Summary: A set of tools to gather troubleshooting information from a system
Name: sos
Version: 4.2
Release: 13%{?dist}
Release: 15%{?dist}
Group: Applications/System
Source0: https://github.com/sosreport/sos/archive/%{version}/sos-%{version}.tar.gz
Source1: sos-audit-%{auditversion}.tgz
@ -42,7 +42,8 @@ Patch18: sos-bz2036697-ocp-backports.patch
Patch19: sos-bz2043102-foreman-tasks-msgpack.patch
Patch20: sos-bz2041488-virsh-in-foreground.patch
Patch21: sos-bz2042966-ovn-proper-package-enablement.patch
Patch22: sos-bz2054882-plugopt-logging-effective-opts.patch
Patch23: sos-bz2055547-honour-plugins-timeout-hardcoded.patch
%description
Sos is a set of tools that gathers information about system
@ -74,6 +75,8 @@ support technicians and developers.
%patch19 -p1
%patch20 -p1
%patch21 -p1
%patch22 -p1
%patch23 -p1
%build
%py3_build
@ -140,6 +143,18 @@ of the system. Currently storage and filesystem commands are audited.
%ghost /etc/audit/rules.d/40-sos-storage.rules
%changelog
* Wed Feb 23 2022 Pavel Moravec <pmoravec@redhat.com> = 4.2-15
- [sosnode] Handle downstream versioning for runtime option
Resolves: bz2036697
- [options] Fix logging on plugopts in effective sos command
Resolves: bz2054882
- [report] Honor plugins' hardcoded plugin_timeout
Resolves: bz2055547
- [policies] Set fallback to None sysroot, don't chroot to '/'
Resolves: bz1873185
- [ovn_central] Rename container responsable of Red Hat
Resolves: bz2042966
* Wed Jan 26 2022 Pavel Moravec <pmoravec@redhat.com> = 4.2-13
- [virsh] Catch parsing exception
Resolves: bz2041488