diff -Nru sosreport-4.0/.cirrus.yml sosreport-4.1/.cirrus.yml --- sosreport-4.0/.cirrus.yml 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/.cirrus.yml 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,101 @@ +--- + +# Main environment vars to set for all tasks +env: + + FEDORA_VER: "33" + FEDORA_PRIOR_VER: "32" + FEDORA_NAME: "fedora-${FEDORA_VER}" + FEDORA_PRIOR_NAME: "fedora-${FEDORA_PRIOR_VER}" + #RHEL_NAME: "rhel-8.3" + + UBUNTU_NAME: "ubuntu-20.04" + UBUNTU_PRIOR_NAME: "ubuntu-18.04" + + RH_PROJECT: "sos-devel-jobs" + UBUNTU_PROJECT: "ubuntu-os-cloud" + + # These are generated images pushed to GCP from Red Hat + FEDORA_IMAGE_NAME: "f${FEDORA_VER}-server-sos-testing" + FEDORA_PRIOR_IMAGE_NAME: "f${FEDORA_PRIOR_VER}-server-sos-testing" + #RHEL_IMAGE_NAME: "${RHEL_NAME}-server-sos-testing" + + # Images exist on GCP already + UBUNTU_IMAGE_NAME: "ubuntu-2004-focal-v20201111" + UBUNTU_PRIOR_IMAGE_NAME: "ubuntu-1804-bionic-v20201111" + +# Default task timeout +timeout_in: 30m + +gcp_credentials: ENCRYPTED[!77d4c8251094346c41db63cb05eba2ff98eaff04e58c5d0e2a8e2c6f159f7d601b3fe9a2a4fce1666297e371f2fc8752!] + +# Run a simple lint on the community cluster +flake8_task: + alias: "flake8_test" + name: "Flake8 linting test" + container: + image: alpine/flake8:latest + flake_script: flake8 sos + +# nose tests, again on the community cluster +nosetests_task: + alias: nosetests + name: "Nosetests" + container: + image: python:slim + setup_script: pip install nose + nose_script: nosetests -v --with-cover --cover-package=sos tests/ + +# Run a check on newer upstream python versions to check for possible +# breaks/changes in common modules. This is not meant to check any of the actual +# collections or archive integrity. +py_break_task: + alias: "py_break" + name: "Breakage test python-$PY_VERSION" + container: + image: "python:${PY_VERSION}" + matrix: + - env: + PY_VERSION: "latest" + - env: + PY_VERSION: "3.9" + setup_script: pip install -r requirements.txt + main_script: ./bin/sos report --batch + +# Run our standard test script across multiple distros on GCP +report_function_task: + alias: "functional_report" + name: "Report functionality test - $BUILD_NAME" + gce_instance: &standardvm + image_project: "${PROJECT}" + image_name: "${VM_IMAGE_NAME}" + cpu: 2 + memory: "2Gb" + # minimum disk size is 20 + disk: 20 + matrix: + - env: + PROJECT: ${RH_PROJECT} + BUILD_NAME: ${FEDORA_NAME} + VM_IMAGE_NAME: ${FEDORA_IMAGE_NAME} + - env: + PROJECT: ${RH_PROJECT} + BUILD_NAME: ${FEDORA_PRIOR_NAME} + VM_IMAGE_NAME: ${FEDORA_PRIOR_IMAGE_NAME} + # - env: + # BUILD_NAME: ${RHEL_NAME} + # VM_IMAGE_NAME: ${RHEL_IMAGE_NAME} + - env: + PROJECT: ${UBUNTU_PROJECT} + BUILD_NAME: ${UBUNTU_NAME} + VM_IMAGE_NAME: ${UBUNTU_IMAGE_NAME} + - env: + PROJECT: ${UBUNTU_PROJECT} + BUILD_NAME: ${UBUNTU_PRIOR_NAME} + VM_IMAGE_NAME: ${UBUNTU_PRIOR_IMAGE_NAME} + # Ubuntu has sos installed by default, so remove that to avoid any conflicts + setup_script: | + if [ $(command -v apt) ] && [ $(command -v sosreport) ]; then + apt -y purge sosreport + fi + main_script: ./tests/simple.sh diff -Nru sosreport-4.0/debian/changelog sosreport-4.1/debian/changelog --- sosreport-4.0/debian/changelog 2021-02-24 15:40:56.000000000 +0000 +++ sosreport-4.1/debian/changelog 2021-03-22 12:19:59.000000000 +0000 @@ -1,3 +1,45 @@ +sosreport (4.1-1ubuntu1) hirsute; urgency=medium + + * New 4.1 upstream minor release. + - https://github.com/sosreport/sos/releases/tag/4.1 + + * d/test/simple.sh: + - Update the script from upstream + - Modify the script to use /tmp as a target, instead + of /var/tmp. + + * d/test/control: + - Adding isolation-machine as simple.sh wants to + interact with the kernel. + + * Former patches: + - d/p/0002-fix-dict-order-py38-incompatibility.patch + - d/p/0003-sosclean-fix-handling-of-filepath-with-archive-name.patch + - d/p/0004-sosclean-fix-tarball-skipping-regex.patch + - d/p/0005-ceph-collect-balancer-and-pg-autoscale-status.patch + - d/p/0006-rabbitmq-add-info-on-maybe-stuck-processes.patch + - d/p/0007-rabbitmq-add-10sec-timeout-to-call-to-maybestuck.patch + - d/p/0008-networking-include-ip-neigh-and-rule-info.patch + - d/p/0009-conntrack-add-conntrack-info.patch + - d/p/0010-conntrack-gather-per-namespace-data.patch + - d/p/0011-ceph-include-time-sync-status-for-ceph-mon.patch + - d/p/0012-apt-move-unattended-upgrades-log-collection.patch + - d/p/0013-bcache-add-a-new-plugin-for-bcache.patch + - d/p/0014-k8s-add-cdk-master-auth-webhook-to-journal.patch + - d/p/0015-k8s-fix-cdk-related-file-paths.patch + - d/p/0016-systemd-prefer-resolvectl-over-systemd-resolve.patch + - d/p/0017-ovn-extend-information.patch + - d/p/0018-ua-prefer-new-ua-cmd-over-the-deprecated-one.patch + - d/p/0019-ovn-fix-sbctl-cmd-execution.patch + + * Remaining patches: + - d/p/0001-debian-change-tmp-dir-location.patch + + * New patches: + - d/p/0002-clean-prevent-parsing-ubuntu-user.patch + + -- Eric Desrochers Mon, 22 Mar 2021 12:19:59 +0000 + sosreport (4.0-1ubuntu9) hirsute; urgency=medium [Edward Hope-Morley] @@ -7,7 +49,7 @@ sosreport (4.0-1ubuntu8) hirsute; urgency=medium - * d/p/0018-ua-prefer-new-ua-cmd-over-the-deprecated-one.patch + * d/p/0018-ua-prefer-new-ua-cmd-over-the-deprecated-one.patch -- Eric Desrochers Tue, 23 Feb 2021 08:49:59 -0500 @@ -88,7 +130,7 @@ when the archive name is included in the filename inside the archive. (LP: #1896222) - * d/p/0004-sosclean-fix-tarball-skipping-regex.patch: + * d/p/0004-sosclean-fix-tarball-skipping-regex.patch: - Fix tarball skipping regex [Dan Hill] diff -Nru sosreport-4.0/debian/control sosreport-4.1/debian/control --- sosreport-4.0/debian/control 2020-08-19 22:49:05.000000000 +0000 +++ sosreport-4.1/debian/control 2021-03-22 12:19:59.000000000 +0000 @@ -2,7 +2,7 @@ Maintainer: Eric Desrochers Section: admin Priority: optional -Standards-Version: 4.5.0 +Standards-Version: 4.5.1 Build-Depends: debhelper-compat (= 13), dh-python, diff -Nru sosreport-4.0/debian/patches/0002-clean-prevent-parsing-ubuntu-user.patch sosreport-4.1/debian/patches/0002-clean-prevent-parsing-ubuntu-user.patch --- sosreport-4.0/debian/patches/0002-clean-prevent-parsing-ubuntu-user.patch 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/debian/patches/0002-clean-prevent-parsing-ubuntu-user.patch 2021-03-22 12:19:38.000000000 +0000 @@ -0,0 +1,20 @@ +Description: [cleaner] Skip Ubuntu user to prevent false positive + Default ubuntu user uses UID 1000 by default. + +Author: Eric Desrochers +Origin: upstream, https://github.com/sosreport/sos/commit/70d4e1a1e901ada4e84005a93c2d23f6116aa2b5 +Bug: https://github.com/sosreport/sos/issues/2454 +Index: sos-4.1/sos/cleaner/parsers/username_parser.py +=================================================================== +--- sos-4.1.orig/sos/cleaner/parsers/username_parser.py ++++ sos-4.1/sos/cleaner/parsers/username_parser.py +@@ -30,7 +30,8 @@ class SoSUsernameParser(SoSCleanerParser + skip_list = [ + 'nobody', + 'nfsnobody', +- 'root' ++ 'root', ++ 'ubuntu' + ] + + def __init__(self, conf_file=None, opt_names=None): diff -Nru sosreport-4.0/debian/patches/0002-fix-dict-order-py38-incompatibility.patch sosreport-4.1/debian/patches/0002-fix-dict-order-py38-incompatibility.patch --- sosreport-4.0/debian/patches/0002-fix-dict-order-py38-incompatibility.patch 2020-08-19 22:49:24.000000000 +0000 +++ sosreport-4.1/debian/patches/0002-fix-dict-order-py38-incompatibility.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -Description: Fix dict order incompability - Fix dictionary keys changed during iteration. -Author: Eric Desrochers -Origin: upstream, https://github.com/sosreport/sos/commit/1d7bab6c7 -Bug: https://github.com/sosreport/sos/issues/2206 ---- sos-4.0.orig/sos/options.py -+++ sos-4.0/sos/options.py -@@ -186,7 +186,7 @@ class SoSOptions(): - if 'verbose' in odict.keys(): - odict['verbosity'] = int(odict.pop('verbose')) - # convert options names -- for key in odict.keys(): -+ for key in list(odict): - if '-' in key: - odict[key.replace('-', '_')] = odict.pop(key) - # set the values according to the config file diff -Nru sosreport-4.0/debian/patches/0003-sosclean-fix-handling-of-filepath-with-archive-name.patch sosreport-4.1/debian/patches/0003-sosclean-fix-handling-of-filepath-with-archive-name.patch --- sosreport-4.0/debian/patches/0003-sosclean-fix-handling-of-filepath-with-archive-name.patch 2020-09-18 13:23:04.000000000 +0000 +++ sosreport-4.1/debian/patches/0003-sosclean-fix-handling-of-filepath-with-archive-name.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -Description: [cleaner] Fix handling of filepath when archive name is in the filepath - Fixes the splitting of filepaths within the archive, when the archive - name is included in the filename inside the archive. - - Related: #2236 -Author: Jake Hunsaker -Origin: upstream, https://github.com/sosreport/sos/pull/2238/commits/ca7cb3440618492e9cf5c7fee77623066bd4b0b7 -Bug: https://github.com/sosreport/sos/issues/2236 -Bug-Ubuntu: https://launchpad.net/bugs/1896222 ---- a/sos/cleaner/__init__.py -+++ b/sos/cleaner/__init__.py -@@ -483,7 +483,7 @@ - - file_list = archive.get_file_list() - for fname in file_list: -- short_name = fname.split(archive.archive_name)[1].lstrip('/') -+ short_name = fname.split(archive.archive_name + '/')[1] - if archive.should_skip_file(short_name): - continue - try: diff -Nru sosreport-4.0/debian/patches/0004-sosclean-fix-tarball-skipping-regex.patch sosreport-4.1/debian/patches/0004-sosclean-fix-tarball-skipping-regex.patch --- sosreport-4.0/debian/patches/0004-sosclean-fix-tarball-skipping-regex.patch 2020-09-18 13:23:04.000000000 +0000 +++ sosreport-4.1/debian/patches/0004-sosclean-fix-tarball-skipping-regex.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -Description: [obfuscation_archive] Fix tarball skipping regex - The regex used to skip tarballs was slightly too vague, in that we were - checking for any characters following 'tar', instead of any characters - following 'tar.'. This in turn lead to the potential for skipping files - that included the original sosreport archive's name. - - Make the regex more exact to no longer make these accidental matches, - and only match on true tarballs. - - Closes: sosreport#2236 - Resolves: sosreport#2238 - -Author: Jake Hunsaker -Origin: upstream, https://github.com/sosreport/sos/pull/2238/commits/fec409c9198b02fa13527f6bef6a1d63bd402dbf -Bug: https://github.com/sosreport/sos/issues/2236 -Bug-Ubuntu: https://bugs.launchpad.net/bugs/1896222 ---- a/sos/cleaner/obfuscation_archive.py -+++ b/sos/cleaner/obfuscation_archive.py -@@ -73,7 +73,9 @@ - 'sys/kernel/debug', - 'sys/module', - 'var/log/.*dnf.*', -- '.*.tar.*', # TODO: support archive unpacking -+ '.*\.tar$', # TODO: support archive unpacking -+ # Be explicit with these tar matches to avoid matching commands -+ '.*\.tar\.xz', - '.*.gz' - ] - diff -Nru sosreport-4.0/debian/patches/0005-ceph-collect-balancer-and-pg-autoscale-status.patch sosreport-4.1/debian/patches/0005-ceph-collect-balancer-and-pg-autoscale-status.patch --- sosreport-4.0/debian/patches/0005-ceph-collect-balancer-and-pg-autoscale-status.patch 2020-09-18 13:23:04.000000000 +0000 +++ sosreport-4.1/debian/patches/0005-ceph-collect-balancer-and-pg-autoscale-status.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -Description: [ceph] collect balancer and pg-autoscale status - Adding the following two commands to the ceph plug-in: - ceph osd pool autoscale-status - ceph balancer status - - The autoscale-status command lists each pool, its relative utilization, - and any suggested changes to the PG count. - - The balancer status command reports any recorded or active plans. - - [0] https://docs.ceph.com/docs/master/rados/operations/placement-groups/ - [1] https://docs.ceph.com/docs/mimic/mgr/balancer/ - - Closes: #2211 - Resolves: #2212 - -Author: Dan Hill daniel.hill@canonical.com -Origin: upstream, https://github.com/sosreport/sos/commit/52f4661e2b594134b98e2967b02cc860d7963fef -Bug: https://github.com/sosreport/sos/issues/2211 -Bug-Ubuntu: https://bugs.launchpad.net/bugs/1893109 ---- a/sos/report/plugins/ceph.py -+++ b/sos/report/plugins/ceph.py -@@ -65,6 +65,7 @@ - "ceph mon_status", - "ceph quorum_status", - "ceph mgr module ls", -+ "ceph balancer status", - "ceph mgr metadata", - "ceph osd metadata", - "ceph osd erasure-code-profile ls", -@@ -95,6 +96,7 @@ - "osd perf", - "osd blocked-by", - "osd pool ls detail", -+ "osd pool autoscale-status", - "osd numa-status", - "device ls", - "mon dump", diff -Nru sosreport-4.0/debian/patches/0006-rabbitmq-add-info-on-maybe-stuck-processes.patch sosreport-4.1/debian/patches/0006-rabbitmq-add-info-on-maybe-stuck-processes.patch --- sosreport-4.0/debian/patches/0006-rabbitmq-add-info-on-maybe-stuck-processes.patch 2020-09-18 13:22:43.000000000 +0000 +++ sosreport-4.1/debian/patches/0006-rabbitmq-add-info-on-maybe-stuck-processes.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,36 +0,0 @@ -Description: [rabbitmq] Add information on `maybe_stuck()` processes for RMQ - We have seen environments in which simple commands such as - rabbitmqctl list_queues - Get stuck and do not produce any output. We found it helpful to run - rabbitmqctl eval 'rabbitmq_diagnostics:maybe_stuck().' - - To obtain a list of "stuck" Erlang processes for further analysis. - This change adds the output of the above command to the sosreport. - - Depends-On: #2232 - Closes: https://bugs.launchpad.net/ubuntu/+source/sosreport/+bug/1890846 - Resolves: #2189 - -Author: Nicolas Bock -Origin: upstream, https://github.com/sosreport/sos/commit/af5891786ff8c4e33c341fe9cca690fc77ac768f -Bug: https://github.com/sosreport/sos/issues/2232 -Bug-Ubuntu: https://launchpad.net/bugs/1890846 ---- a/sos/report/plugins/rabbitmq.py -+++ b/sos/report/plugins/rabbitmq.py -@@ -37,8 +37,16 @@ - self.fmt_container_cmd(container, 'rabbitmqctl report'), - foreground=True - ) -+ self.add_cmd_output( -+ self.fmt_container_cmd( -+ container, "rabbitmqctl eval " -+ "'rabbit_diagnostics:maybe_stuck().'"), -+ foreground=True -+ ) - else: - self.add_cmd_output("rabbitmqctl report") -+ self.add_cmd_output( -+ "rabbitmqctl eval 'rabbit_diagnostics:maybe_stuck().'") - - self.add_copy_spec([ - "/etc/rabbitmq/*", diff -Nru sosreport-4.0/debian/patches/0007-rabbitmq-add-10sec-timeout-to-call-to-maybestuck.patch sosreport-4.1/debian/patches/0007-rabbitmq-add-10sec-timeout-to-call-to-maybestuck.patch --- sosreport-4.0/debian/patches/0007-rabbitmq-add-10sec-timeout-to-call-to-maybestuck.patch 2020-09-21 14:47:01.000000000 +0000 +++ sosreport-4.1/debian/patches/0007-rabbitmq-add-10sec-timeout-to-call-to-maybestuck.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -Description: [rabbitmq] Add 10 second timeout to call to `maybe_stuck()`. - The `maybe_stuck()` function might run for a long time if it is - tracking down a lot of potentially stuck processes. In order to - prevent long run times of `sos report` the `cmd` timeout should be set - explicitly. As a compromise between acceptable execution times and - information gathered from the plugin, this change introduces a timeout - of 10 seconds for the `maybe_stuck()` call. - -Author: Nicolas Bock -Origin: upstream, https://github.com/sosreport/sos/pull/2239/commits/5b5ebe2d2c9d579931c8d6a78101e16d6abfa527 ---- a/sos/report/plugins/rabbitmq.py -+++ b/sos/report/plugins/rabbitmq.py -@@ -41,12 +41,13 @@ - self.fmt_container_cmd( - container, "rabbitmqctl eval " - "'rabbit_diagnostics:maybe_stuck().'"), -- foreground=True -+ foreground=True, timeout=10 - ) - else: - self.add_cmd_output("rabbitmqctl report") - self.add_cmd_output( -- "rabbitmqctl eval 'rabbit_diagnostics:maybe_stuck().'") -+ "rabbitmqctl eval 'rabbit_diagnostics:maybe_stuck().'", -+ timeout=10) - - self.add_copy_spec([ - "/etc/rabbitmq/*", diff -Nru sosreport-4.0/debian/patches/0008-networking-include-ip-neigh-and-rule-info.patch sosreport-4.1/debian/patches/0008-networking-include-ip-neigh-and-rule-info.patch --- sosreport-4.0/debian/patches/0008-networking-include-ip-neigh-and-rule-info.patch 2020-10-26 15:04:33.000000000 +0000 +++ sosreport-4.1/debian/patches/0008-networking-include-ip-neigh-and-rule-info.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -Description: [networking] Include ns ip neigh and ip rule info - Resolves: #2282 - Closes: #2281 -Author: Edward Hope-Morley -Origin: upstream, https://github.com/sosreport/sos/commit/0c5b4c15f880b1e96ac70ced1de2d7d4f989c19e -Bug: https://github.com/sosreport/sos/issues/2281 -Bug-Ubuntu: https://launchpad.net/bugs/1901555 ---- a/sos/report/plugins/networking.py -+++ b/sos/report/plugins/networking.py -@@ -268,6 +268,8 @@ - self.add_cmd_output([ - ns_cmd_prefix + "ip address show", - ns_cmd_prefix + "ip route show table all", -+ ns_cmd_prefix + "ip -s -s neigh show", -+ ns_cmd_prefix + "ip rule list", - ns_cmd_prefix + "iptables-save", - ns_cmd_prefix + "netstat %s -neopa" % self.ns_wide, - ns_cmd_prefix + "netstat -s", diff -Nru sosreport-4.0/debian/patches/0009-conntrack-add-conntrack-info.patch sosreport-4.1/debian/patches/0009-conntrack-add-conntrack-info.patch --- sosreport-4.0/debian/patches/0009-conntrack-add-conntrack-info.patch 2020-11-23 18:10:12.000000000 +0000 +++ sosreport-4.1/debian/patches/0009-conntrack-add-conntrack-info.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,61 +0,0 @@ -Origin: upstream, https://github.com/sosreport/sos/commit/e6af5287b6bde901690c58090f958960a536660a -Bug-Ubuntu: https://bugs.launchpad.net/bugs/1898077 -From: Hemanth Nakkina -Date: Sun, 27 Sep 2020 02:28:11 +0000 -Subject: [PATCH] [conntrack] add conntrack info - -The plugin conntrackd is renamed to conntrack. Added the following -conntrack commands to the plugin. -conntrack -L -o extended -conntrack -S - -Closes: #2049 -Resolves: #2251 - -Signed-off-by: Hemanth Nakkina hemanth.nakkina@canonical.com -Signed-off-by: Jake Hunsaker ---- - .../plugins/{conntrackd.py => conntrack.py} | 15 +++++++++++---- - 1 file changed, 11 insertions(+), 4 deletions(-) - rename sos/report/plugins/{conntrackd.py => conntrack.py} (70%) - -diff --git a/sos/report/plugins/conntrackd.py b/sos/report/plugins/conntrack.py -similarity index 70% -rename from sos/report/plugins/conntrackd.py -rename to sos/report/plugins/conntrack.py -index 0244c9c63..79fc28096 100644 ---- a/sos/report/plugins/conntrackd.py -+++ b/sos/report/plugins/conntrack.py -@@ -11,16 +11,17 @@ - UbuntuPlugin, SuSEPlugin) - - --class Conntrackd(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin, SuSEPlugin): -+class Conntrack(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin, SuSEPlugin): - -- short_desc = 'conntrackd - netfilter connection tracking user-space daemon' -+ short_desc = 'conntrack - netfilter connection tracking' - -- plugin_name = 'conntrackd' -+ plugin_name = 'conntrack' - profiles = ('network', 'cluster') - -- packages = ('conntrack-tools', 'conntrackd') -+ packages = ('conntrack-tools', 'conntrack', 'conntrackd') - - def setup(self): -+ # Collect info from conntrackd - self.add_copy_spec("/etc/conntrackd/conntrackd.conf") - self.add_cmd_output([ - "conntrackd -s network", -@@ -33,4 +34,10 @@ def setup(self): - "conntrackd -s expect", - ]) - -+ # Collect info from conntrack -+ self.add_cmd_output([ -+ "conntrack -L -o extended", -+ "conntrack -S", -+ ]) -+ - # vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/debian/patches/0010-conntrack-gather-per-namespace-data.patch sosreport-4.1/debian/patches/0010-conntrack-gather-per-namespace-data.patch --- sosreport-4.0/debian/patches/0010-conntrack-gather-per-namespace-data.patch 2020-11-23 18:10:12.000000000 +0000 +++ sosreport-4.1/debian/patches/0010-conntrack-gather-per-namespace-data.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,89 +0,0 @@ -Origin: upstream, https://github.com/sosreport/sos/commit/ef6e1bd537a9da2c0368b15988755bc4412b39e8 -Bug-Ubuntu: https://bugs.launchpad.net/bugs/1898077 -From ef6e1bd537a9da2c0368b15988755bc4412b39e8 Mon Sep 17 00:00:00 2001 -From: Mauricio Faria de Oliveira -Date: Wed, 11 Nov 2020 23:52:51 +0000 -Subject: [PATCH] [conntrack] gather per-namespace data - -Run conntrack commands per-namespace, as the ebpf/networking plugins: - -Test-case: -``` - # ip netns - ns3 - ns2 - ns1 -``` - -Before: -``` - # ./bin/sos report -o conntrack,ebpf,networking --batch - - # tar tf /tmp/sosreport-*.tar.xz | grep ip_netns_exec - .../sos_commands/ebpf/ip_netns_exec_ns1_bpftool_net_list - .../sos_commands/ebpf/ip_netns_exec_ns2_bpftool_net_list - .../sos_commands/ebpf/ip_netns_exec_ns3_bpftool_net_list - ... - .../sos_commands/networking/ip_netns_exec_ns1_ip_address_show - ... - .../sos_commands/networking/ip_netns_exec_ns2_ip_address_show - ... - .../sos_commands/networking/ip_netns_exec_ns3_ip_address_show - ... -``` - -After: - -``` - # ./bin/sos report -o conntrack,ebpf,networking --batch - - # tar tf /tmp/sosreport-*.tar.xz | grep ip_netns_exec - .../sos_commands/conntrack/ip_netns_exec_ns1_conntrack_-L_-o_extended - .../sos_commands/conntrack/ip_netns_exec_ns1_conntrack_-S - .../sos_commands/conntrack/ip_netns_exec_ns2_conntrack_-L_-o_extended - .../sos_commands/conntrack/ip_netns_exec_ns2_conntrack_-S - .../sos_commands/conntrack/ip_netns_exec_ns3_conntrack_-L_-o_extended - .../sos_commands/conntrack/ip_netns_exec_ns3_conntrack_-S - .../sos_commands/ebpf/ip_netns_exec_ns1_bpftool_net_list - .../sos_commands/ebpf/ip_netns_exec_ns2_bpftool_net_list - .../sos_commands/ebpf/ip_netns_exec_ns3_bpftool_net_list - ... - .../sos_commands/networking/ip_netns_exec_ns1_ip_address_show - ... - .../sos_commands/networking/ip_netns_exec_ns2_ip_address_show - ... - .../sos_commands/networking/ip_netns_exec_ns3_ip_address_show - ... -``` - -Signed-off-by: Mauricio Faria de Oliveira -Signed-off-by: Jake Hunsaker ---- - sos/report/plugins/conntrack.py | 18 ++++++++++++++++++ - 1 file changed, 18 insertions(+) - ---- a/sos/report/plugins/conntrack.py -+++ b/sos/report/plugins/conntrack.py -@@ -40,4 +40,22 @@ - "conntrack -S", - ]) - -+ # Capture additional data from namespaces; each command is run -+ # per-namespace -+ ip_netns = self.exec_cmd("ip netns") -+ cmd_prefix = "ip netns exec " -+ if ip_netns['status'] == 0: -+ out_ns = [] -+ for line in ip_netns['output'].splitlines(): -+ # If there's no namespaces, no need to continue -+ if line.startswith("Object \"netns\" is unknown") \ -+ or line.isspace() \ -+ or line[:1].isspace(): -+ continue -+ out_ns.append(line.partition(' ')[0]) -+ for namespace in out_ns: -+ ns_cmd_prefix = cmd_prefix + namespace + " " -+ self.add_cmd_output(ns_cmd_prefix + "conntrack -L -o extended") -+ self.add_cmd_output(ns_cmd_prefix + "conntrack -S") -+ - # vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/debian/patches/0011-ceph-include-time-sync-status-for-ceph-mon.patch sosreport-4.1/debian/patches/0011-ceph-include-time-sync-status-for-ceph-mon.patch --- sosreport-4.0/debian/patches/0011-ceph-include-time-sync-status-for-ceph-mon.patch 2021-01-05 16:18:09.000000000 +0000 +++ sosreport-4.1/debian/patches/0011-ceph-include-time-sync-status-for-ceph-mon.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -Description: [ceph] include time-sync-status for ceph mon - Ceph mons might get into time sync problems if ntp/chrony - isn't installed or configured correctly. Since Luminous - release, upstream support 'time-sync-status' to detect this - more easily. -Author: Ponnuvel Palaniyappan -Origin: upstream, https://github.com/sosreport/sos/commit/05c94ea7681a763cf17f57a11a0b50948b98f824 -Bug: https://github.com/sosreport/sos/issues/2356 -Bug-Ubuntu: https://launchpad.net/bugs/1910264 ---- a/sos/report/plugins/ceph.py -+++ b/sos/report/plugins/ceph.py -@@ -108,6 +108,7 @@ - "fs dump", - "pg dump", - "pg stat", -+ "time-sync-status", - ] - - self.add_cmd_output([ diff -Nru sosreport-4.0/debian/patches/0012-apt-move-unattended-upgrades-log-collection.patch sosreport-4.1/debian/patches/0012-apt-move-unattended-upgrades-log-collection.patch --- sosreport-4.0/debian/patches/0012-apt-move-unattended-upgrades-log-collection.patch 2021-01-29 17:09:52.000000000 +0000 +++ sosreport-4.1/debian/patches/0012-apt-move-unattended-upgrades-log-collection.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -From fcd9410bcfcb2c1f1d565fb62f6348bf9d5310e0 Mon Sep 17 00:00:00 2001 -From: Eric Desrochers -Date: Wed, 25 Nov 2020 12:29:59 -0500 -Subject: [PATCH] [apt] Move unattended-upgrades log collection - -Having u-u logs collection inside the apt -plugin make more sense as it is using apt -functionnalities and python modules such as -apt, apt_inst, apt_pkg. - -u-u is a mechanism to automatically install -security upgrades on Debian/Ubuntu. It is -enabled by default at installation. - -Resolves: #2323 - -Signed-off-by: Eric Desrochers -Signed-off-by: Jake Hunsaker ---- - sos/report/plugins/apt.py | 4 +++- - sos/report/plugins/logs.py | 1 - - 2 files changed, 3 insertions(+), 2 deletions(-) - ---- a/sos/report/plugins/apt.py -+++ b/sos/report/plugins/apt.py -@@ -20,7 +20,9 @@ - - def setup(self): - self.add_copy_spec([ -- "/etc/apt", "/var/log/apt" -+ "/etc/apt", -+ "/var/log/apt", -+ "/var/log/unattended-upgrades" - ]) - - self.add_forbidden_path("/etc/apt/auth.conf") ---- a/sos/report/plugins/logs.py -+++ b/sos/report/plugins/logs.py -@@ -49,7 +49,6 @@ - "/etc/rsyslog.d", - "/var/log/boot.log", - "/var/log/installer", -- "/var/log/unattended-upgrades", - "/var/log/messages*", - "/var/log/secure*", - "/var/log/udev", diff -Nru sosreport-4.0/debian/patches/0013-bcache-add-a-new-plugin-for-bcache.patch sosreport-4.1/debian/patches/0013-bcache-add-a-new-plugin-for-bcache.patch --- sosreport-4.0/debian/patches/0013-bcache-add-a-new-plugin-for-bcache.patch 2021-01-29 17:10:40.000000000 +0000 +++ sosreport-4.1/debian/patches/0013-bcache-add-a-new-plugin-for-bcache.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -From 9fba4ec1b4649006140e48ba1b1c8c286f6ad1ef Mon Sep 17 00:00:00 2001 -From: Ponnuvel Palaniyappan -Date: Sun, 24 Jan 2021 18:40:13 +0000 -Subject: [PATCH] [bcache] Add a new plugin for bcache - -bcache is used as a caching device (typically an SSD) for -HDDs; bcache stats are useful to identify performance problems. - -Closes: #2378 -Resolves: #2384 - -Signed-off-by: Ponnuvel Palaniyappan -Signed-off-by: Jake Hunsaker ---- - sos/report/plugins/bcache.py | 56 ++++++++++++++++++++++++++++++++++++ - 1 file changed, 56 insertions(+) - create mode 100644 sos/report/plugins/bcache.py - ---- /dev/null -+++ b/sos/report/plugins/bcache.py -@@ -0,0 +1,56 @@ -+# Copyright (C) 2021, Canonical ltd -+# Ponnuvel Palaniyappan -+ -+# This file is part of the sos project: https://github.com/sosreport/sos -+# -+# This copyrighted material is made available to anyone wishing to use, -+# modify, copy, or redistribute it subject to the terms and conditions of -+# version 2 of the GNU General Public License. -+# -+# See the LICENSE file in the source distribution for further information. -+ -+from sos.report.plugins import Plugin, IndependentPlugin, SoSPredicate -+ -+ -+class Bcache(Plugin, IndependentPlugin): -+ -+ short_desc = 'Bcache statistics' -+ -+ plugin_name = 'bcache' -+ profiles = ('storage', 'hardware') -+ files = ('/sys/fs/bcache',) -+ -+ def setup(self): -+ -+ # Caution: reading /sys/fs/bcache/*/cache0/priority_stats is known -+ # to degrade performance on old kernels. Needs care if that's ever -+ # considered for inclusion here. -+ # see: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1840043 -+ self.add_forbidden_path([ -+ '/sys/fs/bcache/*/*/priority_stats', -+ ]) -+ -+ self.add_copy_spec([ -+ '/sys/block/bcache*/bcache/cache/internal/copy_gc_enabled', -+ '/sys/block/bcache*/bcache/cache_mode', -+ '/sys/block/bcache*/bcache/dirty_data', -+ '/sys/block/bcache*/bcache/io_errors', -+ '/sys/block/bcache*/bcache/sequential_cutoff', -+ '/sys/block/bcache*/bcache/stats_hour/bypassed', -+ '/sys/block/bcache*/bcache/stats_hour/cache_hit_ratio', -+ '/sys/block/bcache*/bcache/stats_hour/cache_hits', -+ '/sys/block/bcache*/bcache/stats_hour/cache_misses', -+ '/sys/block/bcache*/bcache/writeback_percent', -+ '/sys/fs/bcache/*/average_key_size', -+ '/sys/fs/bcache/*/bdev*/*', -+ '/sys/fs/bcache/*/bdev*/stat_*/*', -+ '/sys/fs/bcache/*/block_size', -+ '/sys/fs/bcache/*/bucket_size', -+ '/sys/fs/bcache/*/cache_available_percent', -+ '/sys/fs/bcache/*/congested_*_threshold_us', -+ '/sys/fs/bcache/*/internal/*', -+ '/sys/fs/bcache/*/stats_*/*', -+ '/sys/fs/bcache/*/tree_depth', -+ ], pred=SoSPredicate(self, kmods=['bcache'])) -+ -+# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/debian/patches/0014-k8s-add-cdk-master-auth-webhook-to-journal.patch sosreport-4.1/debian/patches/0014-k8s-add-cdk-master-auth-webhook-to-journal.patch --- sosreport-4.0/debian/patches/0014-k8s-add-cdk-master-auth-webhook-to-journal.patch 2021-01-29 17:12:09.000000000 +0000 +++ sosreport-4.1/debian/patches/0014-k8s-add-cdk-master-auth-webhook-to-journal.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -From 8b226bf91d808bca6295eeeab8773c1f70a410a3 Mon Sep 17 00:00:00 2001 -From: Felipe Reyes -Date: Wed, 27 Jan 2021 14:16:43 -0300 -Subject: [PATCH] [kubernetes] Add cdk.master.auth-webhook to journal - collection - -In Ubuntu CDK when the deployed with Keystone authentication the -service cdk.master.auth-webhook is deployed to handle that integration -this change includes this unit to collect its journal. - -Resolves: #2387 - -Signed-off-by: Felipe Reyes -Signed-off-by: Jake Hunsaker ---- - sos/report/plugins/kubernetes.py | 12 ++++++++++++ - 1 file changed, 12 insertions(+) - -diff --git a/sos/report/plugins/kubernetes.py b/sos/report/plugins/kubernetes.py -index 31e48721..0ad6add5 100644 ---- a/sos/report/plugins/kubernetes.py -+++ b/sos/report/plugins/kubernetes.py -@@ -211,4 +211,16 @@ class UbuntuKubernetes(Kubernetes, UbuntuPlugin): - elif path.exists('/etc/kubernetes/admin.conf'): - kube_cmd = "kubectl --kubeconfig=/etc/kubernetes/admin.conf" - -+ services = ( -+ # CDK -+ 'cdk.master.auth-webhook', -+ ) -+ -+ def setup(self): -+ for svc in self.services: -+ self.add_journal(units=svc) -+ -+ super(UbuntuKubernetes, self).setup() -+ -+ - # vim: et ts=5 sw=4 --- -2.25.1 - diff -Nru sosreport-4.0/debian/patches/0015-k8s-fix-cdk-related-file-paths.patch sosreport-4.1/debian/patches/0015-k8s-fix-cdk-related-file-paths.patch --- sosreport-4.0/debian/patches/0015-k8s-fix-cdk-related-file-paths.patch 2021-01-29 17:13:05.000000000 +0000 +++ sosreport-4.1/debian/patches/0015-k8s-fix-cdk-related-file-paths.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -From eab7fbf9366ce61cde635b4c44c176ef8abcb158 Mon Sep 17 00:00:00 2001 -From: Felipe Reyes -Date: Fri, 4 Dec 2020 16:05:19 -0300 -Subject: [PATCH] [kubernetes] Fix CDK related file paths - -The `files` property is used to determined if it's the kubernetes -master node is where the plugin is runnig since it's the only node -capable of running kubectl. In Ubuntu CDK environments the file that's -only available in the master is /root/cdk/cdk_addons_kubectl_config -while /root/cdk/kubeproxyconfig is also available in the workers. - -Signed-off-by: Felipe Reyes -Signed-off-by: Jake Hunsaker ---- - sos/report/plugins/kubernetes.py | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - ---- a/sos/report/plugins/kubernetes.py -+++ b/sos/report/plugins/kubernetes.py -@@ -205,9 +205,9 @@ - class UbuntuKubernetes(Kubernetes, UbuntuPlugin): - - packages = ('kubernetes',) -- files = ('/root/cdk/kubeproxyconfig', '/etc/kubernetes') -- if path.exists('/root/cdk/kubeproxyconfig'): -- kube_cmd = "kubectl --kubeconfig=/root/cdk/kubeproxyconfig" -+ files = ('/root/cdk/cdk_addons_kubectl_config', '/etc/kubernetes') -+ if path.exists('/root/cdk/cdk_addons_kubectl_config'): -+ kube_cmd = "kubectl --kubeconfig=/root/cdk/cdk_addons_kubectl_config" - elif path.exists('/etc/kubernetes/admin.conf'): - kube_cmd = "kubectl --kubeconfig=/etc/kubernetes/admin.conf" - diff -Nru sosreport-4.0/debian/patches/0016-systemd-prefer-resolvectl-over-systemd-resolve.patch sosreport-4.1/debian/patches/0016-systemd-prefer-resolvectl-over-systemd-resolve.patch --- sosreport-4.0/debian/patches/0016-systemd-prefer-resolvectl-over-systemd-resolve.patch 2021-01-29 17:16:28.000000000 +0000 +++ sosreport-4.1/debian/patches/0016-systemd-prefer-resolvectl-over-systemd-resolve.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -From e1d35c1ad2e925929ff9e6a2df876d7e931e9e74 Mon Sep 17 00:00:00 2001 -From: Michael Biebl -Date: Tue, 26 Jan 2021 16:23:04 +0100 -Subject: [PATCH] [systemd] Prefer resolvectl over systemd-resolve - -The latter is a deprecated compat symlink. - -See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=979264 - -Resolves: #2385 - -Signed-off-by: Michael Biebl -Signed-off-by: Jake Hunsaker ---- a/sos/report/plugins/systemd.py -+++ b/sos/report/plugins/systemd.py -@@ -10,6 +10,7 @@ - - from sos.report.plugins import (Plugin, RedHatPlugin, DebianPlugin, - UbuntuPlugin, CosPlugin, SoSPredicate) -+from sos.utilities import is_executable - - - class Systemd(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin, CosPlugin): -@@ -47,11 +48,17 @@ - "timedatectl" - ]) - -- # systemd-resolve command starts systemd-resolved service if that -+ # resolvectl command starts systemd-resolved service if that - # is not running, so gate the commands by this predicate -+ if is_executable('resolvectl'): -+ resolvectl_status = 'resolvectl status' -+ resolvectl_statistics = 'resolvectl statistics' -+ else: -+ resolvectl_status = 'systemd-resolve --status' -+ resolvectl_statistics = 'systemd-resolve --statistics' - self.add_cmd_output([ -- "systemd-resolve --status", -- "systemd-resolve --statistics", -+ resolvectl_status, -+ resolvectl_statistics, - ], pred=SoSPredicate(self, services=["systemd-resolved"])) - - self.add_cmd_output("systemd-analyze plot", diff -Nru sosreport-4.0/debian/patches/0017-ovn-extend-information.patch sosreport-4.1/debian/patches/0017-ovn-extend-information.patch --- sosreport-4.0/debian/patches/0017-ovn-extend-information.patch 2021-02-11 16:06:24.000000000 +0000 +++ sosreport-4.1/debian/patches/0017-ovn-extend-information.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -Description: [ovn-{central,host}] Extend information - Extend the ovn-central and ovn-host plugins to include - logs from their respective applications. For ovn-central - extend to also include Load_Balancer, ACL and - Logical_Switch_Port nbdb tables. -Author: Edward Hope-Morley -Origin: upstream, https://github.com/sosreport/sos/commit/fd6bab7ba88ffac9392fae627dcdbe56ec557b2a -Bug: https://github.com/sosreport/sos/issues/2409 -Bug-Ubuntu: https://bugs.launchpad.net/bugs/1915072 ---- a/sos/report/plugins/ovn_central.py -+++ b/sos/report/plugins/ovn_central.py -@@ -76,6 +76,11 @@ - if ovs_rundir: - self.add_copy_spec(os.path.join(ovs_rundir, pidfile)) - -+ if self.get_option("all_logs"): -+ self.add_copy_spec("/var/log/ovn/") -+ else: -+ self.add_copy_spec("/var/log/ovn/*.log") -+ - # Some user-friendly versions of DB output - cmds = [ - 'ovn-nbctl show', -@@ -85,6 +90,9 @@ - 'ovn-nbctl get-connection', - 'ovn-sbctl get-ssl', - 'ovn-sbctl get-connection', -+ 'ovn-nbctl list Load_Balancer', -+ 'ovn-nbctl list ACL', -+ 'ovn-nbctl list Logical_Switch_Port', - ] - - schema_dir = '/usr/share/openvswitch' ---- a/sos/report/plugins/ovn_host.py -+++ b/sos/report/plugins/ovn_host.py -@@ -29,6 +29,12 @@ - def setup(self): - if os.environ.get('OVS_RUNDIR'): - pid_paths.append(os.environ.get('OVS_RUNDIR')) -+ -+ if self.get_option("all_logs"): -+ self.add_copy_spec("/var/log/ovn/") -+ else: -+ self.add_copy_spec("/var/log/ovn/*.log") -+ - self.add_copy_spec([os.path.join(pp, pidfile) for pp in pid_paths]) - - self.add_copy_spec('/etc/sysconfig/ovn-controller') diff -Nru sosreport-4.0/debian/patches/0018-ua-prefer-new-ua-cmd-over-the-deprecated-one.patch sosreport-4.1/debian/patches/0018-ua-prefer-new-ua-cmd-over-the-deprecated-one.patch --- sosreport-4.0/debian/patches/0018-ua-prefer-new-ua-cmd-over-the-deprecated-one.patch 2021-02-23 13:48:55.000000000 +0000 +++ sosreport-4.1/debian/patches/0018-ua-prefer-new-ua-cmd-over-the-deprecated-one.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -From e79bb911f3e35a91c46cff39f324571b1d0af0bd Mon Sep 17 00:00:00 2001 -From: Eric Desrochers -Date: Mon, 22 Feb 2021 15:39:01 -0500 -Subject: [PATCH] [ubuntu] Prefer ua over ubuntu-advantages cmd - -'ubuntu-advantage' cli has been shorten to 'ua' -in version 19 and then symlink at package level -to make them both available for users as a -smooth transition. - -While most stable Ubuntu releases are at version 19 and -onwards, others aren't yet. The idea is to prefer 'ua' -but fall back to the deprecated 'ubuntu-advantage' cli -as needed. - -Signed-off-by: Eric Desrochers ---- - sos/report/plugins/ubuntu.py | 8 +++++++- - 1 file changed, 7 insertions(+), 1 deletion(-) - ---- a/sos/report/plugins/ubuntu.py -+++ b/sos/report/plugins/ubuntu.py -@@ -7,6 +7,7 @@ - # See the LICENSE file in the source distribution for further information. - - from sos.report.plugins import Plugin, UbuntuPlugin -+from sos.utilities import is_executable - - - class Ubuntu(Plugin, UbuntuPlugin): -@@ -23,7 +24,12 @@ - ]) - - if self.is_installed('ubuntu-advantage-tools'): -- self.add_cmd_output("ubuntu-advantage status") -+ if is_executable('ua'): -+ ua_tools_status = 'ua status' -+ else: -+ ua_tools_status = 'ubuntu-advantage status' -+ self.add_cmd_output(ua_tools_status) -+ - if not self.get_option("all_logs"): - self.add_copy_spec([ - "/var/log/ubuntu-advantage.log", diff -Nru sosreport-4.0/debian/patches/0019-ovn-fix-sbctl-cmd-execution.patch sosreport-4.1/debian/patches/0019-ovn-fix-sbctl-cmd-execution.patch --- sosreport-4.0/debian/patches/0019-ovn-fix-sbctl-cmd-execution.patch 2021-02-24 15:38:26.000000000 +0000 +++ sosreport-4.1/debian/patches/0019-ovn-fix-sbctl-cmd-execution.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,88 +0,0 @@ -From a5b1069ae7cacef081a5b6958c61a99c1b1e034a Mon Sep 17 00:00:00 2001 -From: Edward Hope-Morley -Date: Tue, 23 Feb 2021 14:41:23 +0000 -Subject: [PATCH] [ovn_central] Fix ovn_central sbctl command execution - -sbctl commands can only be run on a leader node, so gate the commands -from running on non-leader nodes with a SoSPredicate - -Closes: #2418 -Resolves: #2419 - -Signed-off-by: Edward Hope-Morley -Signed-off-by: Jake Hunsaker ---- - sos/report/plugins/ovn_central.py | 40 +++++++++++++++++++++++-------- - 1 file changed, 30 insertions(+), 10 deletions(-) ---- a/sos/report/plugins/ovn_central.py -+++ b/sos/report/plugins/ovn_central.py -@@ -8,7 +8,13 @@ - # - # See the LICENSE file in the source distribution for further information. - --from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin -+from sos.report.plugins import ( -+ Plugin, -+ RedHatPlugin, -+ DebianPlugin, -+ UbuntuPlugin, -+ SoSPredicate, -+) - import json - import os - -@@ -82,28 +88,40 @@ - self.add_copy_spec("/var/log/ovn/*.log") - - # Some user-friendly versions of DB output -- cmds = [ -+ nbctl_cmds = [ - 'ovn-nbctl show', -- 'ovn-sbctl show', -- 'ovn-sbctl lflow-list', - 'ovn-nbctl get-ssl', - 'ovn-nbctl get-connection', -- 'ovn-sbctl get-ssl', -- 'ovn-sbctl get-connection', - 'ovn-nbctl list Load_Balancer', - 'ovn-nbctl list ACL', - 'ovn-nbctl list Logical_Switch_Port', - ] - -+ sbctl_cmds = [ -+ 'ovn-sbctl show', -+ 'ovn-sbctl lflow-list', -+ 'ovn-sbctl get-ssl', -+ 'ovn-sbctl get-connection', -+ ] -+ - schema_dir = '/usr/share/openvswitch' - - nb_tables = self.get_tables_from_schema(os.path.join( - schema_dir, 'ovn-nb.ovsschema')) -- sb_tables = self.get_tables_from_schema(os.path.join( -- schema_dir, 'ovn-sb.ovsschema'), ['Logical_Flow']) - -- self.add_database_output(nb_tables, cmds, 'ovn-nbctl') -- self.add_database_output(sb_tables, cmds, 'ovn-sbctl') -+ self.add_database_output(nb_tables, nbctl_cmds, 'ovn-nbctl') -+ -+ cmds = nbctl_cmds -+ -+ # Can only run sbdb commands if we are the leader -+ co = {'cmd': "ovs-appctl -t {} cluster/status OVN_Southbound". -+ 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(os.path.join( -+ schema_dir, '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 - # the container. -@@ -137,3 +155,4 @@ - class DebianOVNCentral(OVNCentral, DebianPlugin, UbuntuPlugin): - - packages = ('ovn-central', ) -+ ovn_sbdb_sock_path = '/var/run/ovn/ovnsb_db.ctl' diff -Nru sosreport-4.0/debian/patches/series sosreport-4.1/debian/patches/series --- sosreport-4.0/debian/patches/series 2021-02-24 15:30:02.000000000 +0000 +++ sosreport-4.1/debian/patches/series 2021-03-22 12:18:08.000000000 +0000 @@ -1,19 +1,2 @@ +0002-clean-prevent-parsing-ubuntu-user.patch 0001-debian-change-tmp-dir-location.patch -0002-fix-dict-order-py38-incompatibility.patch -0003-sosclean-fix-handling-of-filepath-with-archive-name.patch -0004-sosclean-fix-tarball-skipping-regex.patch -0005-ceph-collect-balancer-and-pg-autoscale-status.patch -0006-rabbitmq-add-info-on-maybe-stuck-processes.patch -0007-rabbitmq-add-10sec-timeout-to-call-to-maybestuck.patch -0008-networking-include-ip-neigh-and-rule-info.patch -0009-conntrack-add-conntrack-info.patch -0010-conntrack-gather-per-namespace-data.patch -0011-ceph-include-time-sync-status-for-ceph-mon.patch -0012-apt-move-unattended-upgrades-log-collection.patch -0013-bcache-add-a-new-plugin-for-bcache.patch -0014-k8s-add-cdk-master-auth-webhook-to-journal.patch -0015-k8s-fix-cdk-related-file-paths.patch -0016-systemd-prefer-resolvectl-over-systemd-resolve.patch -0017-ovn-extend-information.patch -0018-ua-prefer-new-ua-cmd-over-the-deprecated-one.patch -0019-ovn-fix-sbctl-cmd-execution.patch diff -Nru sosreport-4.0/debian/tests/control sosreport-4.1/debian/tests/control --- sosreport-4.0/debian/tests/control 2020-08-19 22:49:24.000000000 +0000 +++ sosreport-4.1/debian/tests/control 2021-03-22 12:19:59.000000000 +0000 @@ -1,3 +1,3 @@ Tests: simple.sh Depends: sosreport -Restrictions: needs-root, allow-stderr +Restrictions: needs-root, allow-stderr, isolation-machine diff -Nru sosreport-4.0/debian/tests/simple.sh sosreport-4.1/debian/tests/simple.sh --- sosreport-4.0/debian/tests/simple.sh 2020-08-19 22:49:24.000000000 +0000 +++ sosreport-4.1/debian/tests/simple.sh 2021-03-22 12:19:59.000000000 +0000 @@ -1,3 +1,4 @@ +#!/bin/bash # This file is part of the sos project: https://github.com/sosreport/sos # # This copyrighted material is made available to anyone wishing to use, @@ -5,7 +6,6 @@ # version 2 of the GNU General Public License. # # See the LICENSE file in the source distribution for further information. -#/bin/bash # A quick port of the travis tests to bash, requires root # TODO # * look into using a framework.. @@ -14,12 +14,13 @@ # * make it better validate archives and contents PYTHON=${1:-/usr/bin/python3} -SOSPATH=${2:-./bin/sos report} +SOSPATH=${2:-./bin/sos report --batch --tmp-dir=/tmp } NUMOFFAILURES=0 -summary="Summary\n" +summary="\nSummary\n" +FAIL_LIST="" -run_expecting_sucess () { +run_expecting_success () { #$1 - is command options #$2 - kind of check to do, so far only extract FAIL=false @@ -35,7 +36,7 @@ echo "### Success" else echo "!!! FAILED !!!" - FAIL=true + add_failure "$1 failed during execution" fi end=`date +%s` @@ -43,8 +44,7 @@ echo "#### Sos Total time (seconds):" $runtime if [ -s /dev/shm/stderr ]; then - FAIL=true - echo "!!! FAILED !!!" + add_failure "test generated stderr output, see above" echo "### start stderr" cat /dev/shm/stderr echo "### end stderr" @@ -56,21 +56,19 @@ if [ "extract" = "$2" ]; then echo "### start extraction" - rm -f /tmp/sosreport*md5 + rm -f /tmp/sosreport*sha256 mkdir /tmp/sosreport_test/ tar xfa /tmp/sosreport*.tar* -C /tmp/sosreport_test --strip-components=1 if [ -s /tmp/sosreport_test/sos_logs/*errors.txt ]; then FAIL=true echo "!!! FAILED !!!" + add_failure "Test $1 generated errors" echo "#### *errors.txt output" ls -alh /tmp/sosreport_test/sos_logs/ cat /tmp/sosreport_test/sos_logs/*errors.txt fi echo "### stop extraction" fi - - size="$(grep Size /dev/shm/stdout)" - summary="${summary} \n failures ${FAIL} \t time ${runtime} \t ${size} \t ${1} " echo "######### DONE WITH $1 #########" @@ -82,7 +80,160 @@ fi } -# If /etc/sos/sos.conf doesn't exist let's just make it.. +update_summary () { + size="$(grep Size /dev/shm/stdout)" + size="$(echo "${size:-"Size 0.00MiB"}")" + summary="${summary} \n failures ${FAIL} \t time ${runtime} \t ${size} \t ${1} " +} + +update_failures () { + if $FAIL; then + NUMOFFAILURES=$(($NUMOFFAILURES + 1)) + fi +} + +add_failure () { + FAIL=true + echo "!!! TEST FAILED: $1 !!!" + FAIL_LIST="${FAIL_LIST}\n \t ${FUNCNAME[1]}: \t\t ${1}" +} + +# Test a no frills run with verbosity and make sure the expected items exist +test_normal_report () { + cmd="-vvv" + # get a list of initial kmods loaded + kmods=( $(lsmod | cut -f1 -d ' ' | sort) ) + run_expecting_success "$cmd" extract + if [ $? -eq 0 ]; then + if [ ! -f /tmp/sosreport_test/sos_reports/sos.html ]; then + add_failure "did not generate html reports" + fi + if [ ! -f /tmp/sosreport_test/sos_reports/manifest.json ]; then + add_failure "did not generate manifest.json" + fi + if [ ! -f /tmp/sosreport_test/free ]; then + add_failure "did not create free symlink in archive root" + fi + if [ ! "$(grep "DEBUG" /tmp/sosreport_test/sos_logs/sos.log)" ]; then + add_failure "did not find debug logging when using -vvv" + fi + # new list, see if we added any + new_kmods=( $(lsmod | cut -f1 -d ' ' | sort) ) + if [ "$(printf '%s\n' "${kmods[@]}" "${new_kmods[@]}" | sort | uniq -u)" ]; then + add_failure "new kernel modules loaded during execution" + echo "$(printf '%s\n' "${kmods[@]}" "${new_kmods[@]}" | sort | uniq -u)" + fi + update_failures + update_summary "$cmd" + fi +} + +# Test for correctly skipping html generation, and label setting +test_noreport_label_only () { + cmd="--no-report --label TEST -o hardware" + run_expecting_success "$cmd" extract + if [ $? -eq 0 ]; then + if [ -f /tmp/sosreport_test/sos_reports/sos.html ]; then + add_failure "html report generated when --no-report used" + fi + if [ ! $(grep /tmp/sosreport-*TEST* /dev/shm/stdout) ]; then + add_failure "no label set on archive" + fi + count=$(find /tmp/sosreport_test/sos_commands/* -type d | wc -l) + if [[ "$count" -gt 1 ]]; then + add_failure "more than one plugin ran when using -o hardware" + fi + update_failures + fi + update_summary "$cmd" +} + +# test using mask +test_mask () { + cmd="--mask" + run_expecting_success "$cmd" extract + if [ $? -eq 0 ]; then + if [ ! $(grep host0 /tmp/sosreport_test/hostname) ]; then + add_failure "hostname not obfuscated with --mask" + fi + # we don't yet support binary obfuscation, so skip binary matches + if [ "$(grep -rI `hostname` /tmp/sosreport_test/*)" ]; then + add_failure "hostname not obfuscated in all places" + echo "$(grep -rI `hostname` /tmp/sosreport_test/*)" + fi + # only tests first interface + mac_addr=$(cat /sys/class/net/$(ip route show default | awk '/default/ {print $5}')/address) + if [ "$(grep -rI $mac_addr /tmp/sosreport_test/*)" ]; then + add_failure "MAC address not obfuscated in all places" + echo "$(grep -rI $mac_addr /tmp/sosreport_test/*)" + fi + # only tests first interface + ip_addr=$(ip route show default | awk '/default/ {print $3}') + if [ "$(grep -rI $ip_addr /tmp/sosreport_test/*)" ]; then + add_failure "IP address not obfuscated in all places" + echo "$(grep -rI $ip_addr /tmp/sosreport/_test/*)" + fi + update_failures + fi + update_summary "$cmd" +} + +# test log-size, env vars, and compression type +test_logsize_env_gzip () { + cmd="--log-size 0 --no-env-vars -z gzip" + run_expecting_success "$cmd" extract + if [ $? -eq 0 ]; then + if [ -f /tmp/sosreport_test/environment ]; then + add_failure "env vars captured when using --no-env-vars" + fi + if [ ! $(grep /tmp/sosreport*.gz /dev/shm/stdout) ]; then + add_failure "archive was not gzip compressed using -z gzip" + fi + update_failures + fi + update_summary "$cmd" +} + +# test plugin enablement, plugopts and at the same time ensure our list option parsing is working +test_enable_opts_postproc () { + cmd="-e opencl -v -k kernel.with-timer,libraries.ldconfigv --no-postproc" + run_expecting_success "$cmd" extract + if [ $? -eq 0 ]; then + if [ ! "$(grep "opencl" /dev/shm/stdout)" ]; then + add_failure "force enabled plugin opencl did not run" + fi + if [ ! -f /tmp/sosreport_test/proc/timer* ]; then + add_failure "/proc/timer* not captured when using -k kernel.with-timer" + fi + if [ ! -f /tmp/sosreport_test/sos_commands/libraries/ldconfig_-v* ]; then + add_failure "ldconfig -v not captured when using -k libraries.ldconfigv" + fi + if [ "$(grep "substituting" /tmp/sosreport_test/sos_logs/sos.log)" ]; then + add_failure "post-processing ran while using --no-post-proc" + fi + + update_failures + update_summary "$cmd" + fi +} + +# test if --build and --threads work properly +test_build_threads () { + cmd="--build -t1 -o host,kernel,filesys,hardware,date,logs" + run_expecting_success "$cmd" + if [ $? -eq 0 ]; then + if [ ! "$(grep "Your sosreport build tree" /dev/shm/stdout)" ]; then + add_failure "did not save the build tree" + fi + if [ $(grep "Finishing plugins" /dev/shm/stdout) ]; then + add_failure "did not limit threads when using --threads 1" + fi + update_failures + update_summary "$cmd" + fi +} + +# If /etc/sos/sos.conf doesn't exist let's just make it if [ -f /etc/sos/sos.conf ]; then echo "/etc/sos/sos.conf already exists" else @@ -91,29 +242,27 @@ touch /etc/sos/sos.conf fi + # Runs not generating sosreports -run_expecting_sucess " -l" -run_expecting_sucess " --list-presets" -run_expecting_sucess " --list-profiles" - -# Test generating sosreports, 3 (new) options at a time -# Trying to do --batch (1 label/archive/report/verbosity change) (other changes) -run_expecting_sucess " --batch --build --no-env-vars " # Only --build test -run_expecting_sucess " --batch --no-report -o hardware " extract -run_expecting_sucess " --batch --label TEST -a -c never" extract -run_expecting_sucess " --batch --debug --log-size 0 -c always" extract -run_expecting_sucess " --batch -z xz --log-size 1" extract -run_expecting_sucess " --batch -z gzip" extract -run_expecting_sucess " --batch -t 1 -n hardware" extract -run_expecting_sucess " --batch --quiet -e opencl -k kernel.with-timer" extract -run_expecting_sucess " --batch --case-id 10101 --all-logs --since=$(date -d "yesterday 13:00" '+%Y%m%d') " extract -run_expecting_sucess " --batch --verbose --no-postproc" extract -run_expecting_sucess " --batch --mask" extract +run_expecting_success " -l"; update_summary "List plugins" +run_expecting_success " --list-presets"; update_summary "List presets" +run_expecting_success " --list-profiles"; update_summary "List profiles" + +# Runs generating sosreports +# TODO: +# - find a way to test if --since is working +test_build_threads +test_normal_report +test_enable_opts_postproc +test_noreport_label_only +test_logsize_env_gzip +test_mask -echo $summary +echo -e $summary if [ $NUMOFFAILURES -gt 0 ]; then - echo "FAILED $NUMOFFAILURES" + echo -e "\nTests Failed: $NUMOFFAILURES\nFailures within each test:" + echo -e $FAIL_LIST exit 1 else echo "Everything worked!" diff -Nru sosreport-4.0/docs/clusters.rst sosreport-4.1/docs/clusters.rst --- sosreport-4.0/docs/clusters.rst 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/docs/clusters.rst 2021-02-25 18:46:49.000000000 +0000 @@ -1,5 +1,5 @@ -``sos.collector.clusters`` --- Cluster Profiles -================================================ +``sos.collector.clusters`` --- Cluster Interface +================================================= .. automodule:: sos.collector.clusters :noindex: diff -Nru sosreport-4.0/docs/conf.py sosreport-4.1/docs/conf.py --- sosreport-4.0/docs/conf.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/docs/conf.py 2021-02-25 18:46:49.000000000 +0000 @@ -59,9 +59,9 @@ # built documents. # # The short X.Y version. -version = '4.0' +version = '4.1' # The full version, including alpha/beta/rc tags. -release = '4.0' +release = '4.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff -Nru sosreport-4.0/docs/index.rst sosreport-4.1/docs/index.rst --- sosreport-4.0/docs/index.rst 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/docs/index.rst 2021-02-25 18:46:49.000000000 +0000 @@ -58,9 +58,7 @@ .. code:: - to install locally (as root) ==> make install - to build an rpm ==> make rpm - to build a deb ==> make deb + python3 setup.py install Pre-built Packaging ^^^^^^^^^^^^^^^^^^^ @@ -80,16 +78,6 @@ API === -Plugin Reference -^^^^^^^^^^^^^^^^ - -.. toctree:: - :maxdepth: 2 - - plugins - clusters - parsers - Core Reference ^^^^^^^^^^^^^^ @@ -97,6 +85,8 @@ :maxdepth: 4 archive + clusters + parsers policies plugins reporting diff -Nru sosreport-4.0/docs/parsers.rst sosreport-4.1/docs/parsers.rst --- sosreport-4.0/docs/parsers.rst 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/docs/parsers.rst 2021-02-25 18:46:49.000000000 +0000 @@ -1,5 +1,5 @@ -``sos.cleaner.parsers`` --- Cleaning Parser Definition -======================================================= +``sos.cleaner.parsers`` --- Parser Interface +============================================= .. automodule:: sos.cleaner.parsers :noindex: diff -Nru sosreport-4.0/man/en/sos.1 sosreport-4.1/man/en/sos.1 --- sosreport-4.0/man/en/sos.1 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/man/en/sos.1 2021-02-25 18:46:49.000000000 +0000 @@ -50,7 +50,7 @@ \fBsos-collector\fR. .TP -.B clean|mask +.B clean|cleaner|mask This subcommand takes input of either 1) an sosreport tarball, 2) a collection of sosreport tarballs such as from \fBcollect\fR, or 3) the unpackaged directory of an sosreport and obfuscates potentially sensitive system information @@ -63,7 +63,8 @@ See \fB sos clean --help\fR and \fBman sos-clean\fR for more information. -May be invoked via either \fBsos clean\fR, \fBsos mask\fR, or via the \fB--clean\fR or \fB --mask\fR options +May be invoked via either \fBsos clean\fR, \fBsos cleaner\fR, \fBsos mask\fR, +or via the \fB--clean\fR, \fB--cleaner\fR or \fB --mask\fR options for \fBreport\fR and \fBcollect\fR. .SH GLOBAL OPTIONS diff -Nru sosreport-4.0/man/en/sos-clean.1 sosreport-4.1/man/en/sos-clean.1 --- sosreport-4.0/man/en/sos-clean.1 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/man/en/sos-clean.1 2021-02-25 18:46:49.000000000 +0000 @@ -4,7 +4,9 @@ .SH SYNOPSIS .B sos clean TARGET [options] [\-\-domains] - [\-\-map] + [\-\-keywords] + [\-\-keyword-file] + [\-\-map-file] [\-\-jobs] [\-\-no-update] @@ -54,7 +56,11 @@ integer based on the keyword's index in the parser. Note that keywords will be replaced as both standalone words and in substring matches. .TP -.B \-\-map FILE +.B \-\-keyword-file FILE +Provide a file that contains a list of keywords that should be obfuscated. Each word must +be specified on a newline within the file. +.TP +.B \-\-map-file FILE Provide a location to a valid mapping file to use as a reference for existing obfuscation pairs. If one is found, the contents are loaded before parsing is started. This allows consistency between runs of this command for obfuscated pairs. By default, sos will write the generated private map file diff -Nru sosreport-4.0/man/en/sos-collect.1 sosreport-4.1/man/en/sos-collect.1 --- sosreport-4.0/man/en/sos-collect.1 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/man/en/sos-collect.1 2021-02-25 18:46:49.000000000 +0000 @@ -30,6 +30,8 @@ [\-\-password] [\-\-password\-per\-node] [\-\-preset PRESET] + [\-\-skip-commands COMMANDS] + [\-\-skip-files FILES] [\-s|\-\-sysroot SYSROOT] [\-\-ssh\-user SSH_USER] [\-\-sos-cmd SOS_CMD] @@ -261,6 +263,18 @@ \fB\-p\fR SSH_PORT, \fB\-\-ssh\-port\fR SSH_PORT Specify SSH port for all nodes. Use this if SSH runs on any port other than 22. .TP +\fB\-\-skip-commands\fR COMMANDS +A comma delimited list of commands to skip execution of, but still allowing the +rest of the plugin that calls the command to run. This will generally need to +be some form of UNIX shell-style wildcard matching. For example, using a value +of \fBhostname\fR will skip only that single command, while using \fBhostname*\fR +will skip all commands with names that begin with the string "hostname". +.TP +\fB\-\-skip-files\fR FILES +A comma delimited list of files or filepath wildcard matches to skip collection +of. Values may either be exact filepaths or paths using UNIX shell-style wildcards, +for example \fB/etc/sos/*\fR. +.TP \fB\-\-ssh\-user\fR SSH_USER Specify an SSH user for sos collect to connect to nodes with. Default is root. diff -Nru sosreport-4.0/man/en/sos-collector.1 sosreport-4.1/man/en/sos-collector.1 --- sosreport-4.0/man/en/sos-collector.1 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/man/en/sos-collector.1 2021-02-25 18:46:49.000000000 +0000 @@ -30,6 +30,8 @@ [\-\-password] [\-\-password\-per\-node] [\-\-preset PRESET] + [\-\-skip-commands COMMANDS] + [\-\-skip-files FILES] [\-s|\-\-sysroot SYSROOT] [\-\-ssh\-user SSH_USER] [\-\-sos-cmd SOS_CMD] @@ -261,6 +263,18 @@ \fB\-p\fR SSH_PORT, \fB\-\-ssh\-port\fR SSH_PORT Specify SSH port for all nodes. Use this if SSH runs on any port other than 22. .TP +\fB\-\-skip-commands\fR COMMANDS +A comma delimited list of commands to skip execution of, but still allowing the +rest of the plugin that calls the command to run. This will generally need to +be some form of UNIX shell-style wildcard matching. For example, using a value +of \fBhostname\fR will skip only that single command, while using \fBhostname*\fR +will skip all commands with names that begin with the string "hostname". +.TP +\fB\-\-skip-files\fR FILES +A comma delimited list of files or filepath wildcard matches to skip collection +of. Values may either be exact filepaths or paths using UNIX shell-style wildcards, +for example \fB/etc/sos/*\fR. +.TP \fB\-\-ssh\-user\fR SSH_USER Specify an SSH user for sos collect to connect to nodes with. Default is root. diff -Nru sosreport-4.0/man/en/sos-mask.1 sosreport-4.1/man/en/sos-mask.1 --- sosreport-4.0/man/en/sos-mask.1 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/man/en/sos-mask.1 2021-02-25 18:46:49.000000000 +0000 @@ -4,7 +4,9 @@ .SH SYNOPSIS .B sos clean TARGET [options] [\-\-domains] - [\-\-map] + [\-\-keywords] + [\-\-keyword-file] + [\-\-map-file] [\-\-jobs] [\-\-no-update] @@ -54,7 +56,11 @@ integer based on the keyword's index in the parser. Note that keywords will be replaced as both standalone words and in substring matches. .TP -.B \-\-map FILE +.B \-\-keyword-file FILE +Provide a file that contains a list of keywords that should be obfuscated. Each word must +be specified on a newline within the file. +.TP +.B \-\-map-file FILE Provide a location to a valid mapping file to use as a reference for existing obfuscation pairs. If one is found, the contents are loaded before parsing is started. This allows consistency between runs of this command for obfuscated pairs. By default, sos will write the generated private map file diff -Nru sosreport-4.0/man/en/sos-report.1 sosreport-4.1/man/en/sos-report.1 --- sosreport-4.0/man/en/sos-report.1 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/man/en/sos-report.1 2021-02-25 18:46:49.000000000 +0000 @@ -14,7 +14,7 @@ [--preset preset] [--add-preset add_preset]\fR [--del-preset del_preset] [--desc description]\fR [--batch] [--build] [--debug] [--dry-run]\fR - [--label label] [--case-id id] [--ticket-number nr]\fR + [--label label] [--case-id id]\fR [--threads threads]\fR [--plugin-timeout TIMEOUT]\fR [-s|--sysroot SYSROOT]\fR @@ -26,6 +26,8 @@ [--log-size]\fR [--all-logs]\fR [--since YYYYMMDD[HHMMSS]]\fR + [--skip-commands commands]\fR + [--skip-files files]\fR [--allow-system-changes]\fR [-z|--compression-type method]\fR [--encrypt-key KEY]\fR @@ -149,7 +151,7 @@ .B \--list-profiles Display a list of available profiles and the plugins that they enable. .TP -.B \-p, \--profile NAME +.B \-p, \--profile, \--profiles NAME Only run plugins that correspond to the given profile. Multiple profiles may be specified as a comma-separated list; the set of plugins executed is the union of each of the profile's plugin sets. Currently defined @@ -180,6 +182,18 @@ This also affects \--all-logs. The date string will be padded with zeros if HHMMSS is not specified. .TP +.B \--skip-commands COMMANDS +A comma delimited list of commands to skip execution of, but still allowing the +rest of the plugin that calls the command to run. This will generally need to +be some form of UNIX shell-style wildcard matching. For example, using a value +of \fBhostname\fR will skip only that single command, while using \fBhostname*\fR +will skip all commands with names that begin with the string "hostname". +.TP +.B \--skip-files FILES +A comma delimited list of files or filepath wildcard matches to skip collection +of. Values may either be exact filepaths or paths using UNIX shell-style wildcards, +for example \fB/etc/sos/*\fR. +.TP .B \--allow-system-changes Run commands even if they can change the system (e.g. load kernel modules). .TP @@ -229,7 +243,8 @@ .TP .B \--plugin-timeout TIMEOUT Specify a timeout in seconds to allow each plugin to run for. A value of 0 -means no timeout will be set. +means no timeout will be set. A value of -1 is used to indicate the default +timeout of 300 seconds. Note that this options sets the timeout for all plugins. If you want to set a timeout for a specific plugin, use the 'timeout' plugin option available to @@ -242,12 +257,6 @@ .B \--case-id NUMBER Specify a case identifier to associate with the archive. Identifiers may include alphanumeric characters, commas and periods ('.'). -Synonymous with \--ticket-number. -.TP -.B \--ticket-number NUMBER -Specify a ticket number or other identifier to associate with the archive. -Identifiers may include alphanumeric characters, commas and periods ('.'). -Synonymous with \--case-id. .TP .B \--build Do not archive copied data. Causes sosreport to leave an uncompressed @@ -299,6 +308,12 @@ If this option is unused and upload is request, and a vendor default is not set, you will be prompted for one. If --batch is used and this option is omitted, no username will be collected and thus uploads will fail if no vendor default is set. + +You also have the option of providing this value via the SOSUPLOADUSER environment +variable. If this variable is set, then no username prompt will occur and --batch +may be used provided all other required values (case number, upload password) +are provided. + .TP .B \-\-upload-pass PASS Specify the password to use for authentication with the destination server. @@ -312,6 +327,11 @@ be collected by sos and be in the archive. If a password must be provided by you for uploading, it is strongly recommended to not use --batch and enter the password when prompted rather than using this option. + +You also have the option of providing this value via the SOSUPLOADPASSWORD environment +variable. If this variable is set, then no password prompt will occur and --batch may +be used provided all other required values (case number, upload user) are provided. + .TP .B \--upload-directory DIR Specify a directory to upload to, if one is not specified by a vendor default location diff -Nru sosreport-4.0/man/en/sosreport.1 sosreport-4.1/man/en/sosreport.1 --- sosreport-4.0/man/en/sosreport.1 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/man/en/sosreport.1 2021-02-25 18:46:49.000000000 +0000 @@ -14,7 +14,7 @@ [--preset preset] [--add-preset add_preset]\fR [--del-preset del_preset] [--desc description]\fR [--batch] [--build] [--debug] [--dry-run]\fR - [--label label] [--case-id id] [--ticket-number nr]\fR + [--label label] [--case-id id]\fR [--threads threads]\fR [--plugin-timeout TIMEOUT]\fR [-s|--sysroot SYSROOT]\fR @@ -26,6 +26,8 @@ [--log-size]\fR [--all-logs]\fR [--since YYYYMMDD[HHMMSS]]\fR + [--skip-commands commands]\fR + [--skip-files files]\fR [--allow-system-changes]\fR [-z|--compression-type method]\fR [--encrypt-key KEY]\fR @@ -149,7 +151,7 @@ .B \--list-profiles Display a list of available profiles and the plugins that they enable. .TP -.B \-p, \--profile NAME +.B \-p, \--profile, \--profiles NAME Only run plugins that correspond to the given profile. Multiple profiles may be specified as a comma-separated list; the set of plugins executed is the union of each of the profile's plugin sets. Currently defined @@ -180,6 +182,18 @@ This also affects \--all-logs. The date string will be padded with zeros if HHMMSS is not specified. .TP +.B \--skip-commands COMMANDS +A comma delimited list of commands to skip execution of, but still allowing the +rest of the plugin that calls the command to run. This will generally need to +be some form of UNIX shell-style wildcard matching. For example, using a value +of \fBhostname\fR will skip only that single command, while using \fBhostname*\fR +will skip all commands with names that begin with the string "hostname". +.TP +.B \--skip-files FILES +A comma delimited list of files or filepath wildcard matches to skip collection +of. Values may either be exact filepaths or paths using UNIX shell-style wildcards, +for example \fB/etc/sos/*\fR. +.TP .B \--allow-system-changes Run commands even if they can change the system (e.g. load kernel modules). .TP @@ -229,7 +243,8 @@ .TP .B \--plugin-timeout TIMEOUT Specify a timeout in seconds to allow each plugin to run for. A value of 0 -means no timeout will be set. +means no timeout will be set. A value of -1 is used to indicate the default +timeout of 300 seconds. Note that this options sets the timeout for all plugins. If you want to set a timeout for a specific plugin, use the 'timeout' plugin option available to @@ -242,12 +257,6 @@ .B \--case-id NUMBER Specify a case identifier to associate with the archive. Identifiers may include alphanumeric characters, commas and periods ('.'). -Synonymous with \--ticket-number. -.TP -.B \--ticket-number NUMBER -Specify a ticket number or other identifier to associate with the archive. -Identifiers may include alphanumeric characters, commas and periods ('.'). -Synonymous with \--case-id. .TP .B \--build Do not archive copied data. Causes sosreport to leave an uncompressed @@ -299,6 +308,12 @@ If this option is unused and upload is request, and a vendor default is not set, you will be prompted for one. If --batch is used and this option is omitted, no username will be collected and thus uploads will fail if no vendor default is set. + +You also have the option of providing this value via the SOSUPLOADUSER environment +variable. If this variable is set, then no username prompt will occur and --batch +may be used provided all other required values (case number, upload password) +are provided. + .TP .B \-\-upload-pass PASS Specify the password to use for authentication with the destination server. @@ -312,6 +327,11 @@ be collected by sos and be in the archive. If a password must be provided by you for uploading, it is strongly recommended to not use --batch and enter the password when prompted rather than using this option. + +You also have the option of providing this value via the SOSUPLOADPASSWORD environment +variable. If this variable is set, then no password prompt will occur and --batch may +be used provided all other required values (case number, upload user) are provided. + .TP .B \--upload-directory DIR Specify a directory to upload to, if one is not specified by a vendor default location diff -Nru sosreport-4.0/plugins_overview.py sosreport-4.1/plugins_overview.py --- sosreport-4.0/plugins_overview.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/plugins_overview.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,119 @@ +# this script generates for each plugin: +# - its name +# - URL to upstream code +# - list of distros +# - list of profiles +# - list of packages that enable the plugin (no other enabling pieces) +# - list of paths it collects (add_copy_spec) +# - list of paths it forbits to collect (add_forbidden_path) +# - list of commands it calls (add_cmd_output) +# +# Output of the script: +# - a JSON object with plugins in keys +# - or CSV format in case "csv" cmdline is provided +# +# TODO: +# - improve parsing that will be never ideal :) +# - add other methods: +# - add_blockdev_cmd +# - add_string_as_file +# - ?? + +import os +import re +import json +import sys + +PLUGDIR = 'sos/report/plugins' + +plugs_data = {} # the map of all plugins data to collect +plugcontent = '' # content of plugin file just being processed + +# method to parse an item of a_s_c/a_c_o/.. methods +# we work on an assumption the item is a string quoted by \" or optionally +# by \'. If we detect at least 2 such chars in the item, take what is between those. +def add_valid_item(dest, item): + for qoutemark in "\"\'": + split = item.split(qoutemark) + if len(split) > 2: + dest.append(split[1]) + return + +# method to find in `plugcontent` all items of given method (a_c_s/a_c_o/..) split +# by comma; add each valid item to the `dest` list +def add_all_items(method, dest, wrapopen='\(', wrapclose='\)'): + regexp = "%s%s(.*?)%s" % (method, wrapopen, wrapclose) + for match in re.findall(regexp, plugcontent, flags=re.MULTILINE|re.DOTALL): + # tuple of distros ended by either (class|from|import) + if isinstance(match,tuple): + for item in list(match): + if item not in ['class', 'from', 'import']: + for it in item.split(','): + # dirty hack to remove spaces and "Plugin" + if "Plugin" not in it: + continue + it = it.strip(' ()')[0:-6] + if len(it): + dest.append(it) + # list of specs separated by comma .. + elif match.startswith('[') or match.startswith('('): + for item in match.split(','): + add_valid_item(dest, item) + # .. or a singleton spec + else: + add_valid_item(dest, match) + +# main body: traverse report's plugins directory and for each plugin, grep for +# add_copy_spec / add_forbidden_path / add_cmd_output there +for plugfile in sorted(os.listdir(PLUGDIR)): + # ignore non-py files and __init__.py + if not plugfile.endswith('.py') or plugfile == '__init__.py': + continue + plugname = plugfile[:-3] +# if plugname != 'bcache': +# continue + plugs_data[plugname] = { + 'sourcecode': 'https://github.com/sosreport/sos/blob/master/sos/report/plugins/%s.py' % plugname, + 'distros': [], + 'profiles': [], + 'packages': [], + 'copyspecs': [], + 'forbidden': [], + 'commands': [], + 'service_status': [], + 'journals': [], + 'env': [], + } + plugcontent = open(os.path.join(PLUGDIR, plugfile)).read().replace('\n','') + add_all_items("from sos.report.plugins import ", plugs_data[plugname]['distros'], wrapopen='', wrapclose='(class|from|import)') + add_all_items("profiles = ", plugs_data[plugname]['profiles'], wrapopen='') + add_all_items("packages = ", plugs_data[plugname]['packages'], wrapopen='') + add_all_items("add_copy_spec", plugs_data[plugname]['copyspecs']) + add_all_items("add_forbidden_path", plugs_data[plugname]['forbidden']) + add_all_items("add_cmd_output", plugs_data[plugname]['commands']) + add_all_items("collect_cmd_output", plugs_data[plugname]['commands']) + add_all_items("add_service_status", plugs_data[plugname]['service_status']) + add_all_items("add_journal", plugs_data[plugname]['journals']) + add_all_items("add_env_var", plugs_data[plugname]['env']) + +# print output; if "csv" is cmdline argument, print in CSV format, else JSON +if (len(sys.argv) > 1) and (sys.argv[1] == "csv"): + print("plugin;url;distros;profiles;packages;copyspecs;forbidden;commands;service_status;journals;env_vars") + for plugname in plugs_data.keys(): + plugin = plugs_data[plugname] + # determine max number of lines - usually "max(len(copyspec),len(commands))" + # ignore 'sourcecode' key as it + maxline = 1 + plugkeys = list(plugin.keys()) + plugkeys.remove('sourcecode') + for key in plugkeys: + maxline = max(maxline, len(plugin[key])) + for line in range(maxline): + out = ";" if line>0 else ("%s;%s" % (plugname, plugin['sourcecode'])) + for key in plugkeys: + out += ";" + if line + +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +from sos.cleaner.mappings import SoSMap + + +class SoSUsernameMap(SoSMap): + """Mapping to store usernames matched from ``lastlog`` output. + + Usernames are obfuscated as ``obfuscateduserX`` where ``X`` is a counter + that gets incremented for every new username found. + + Note that this specifically obfuscates user_names_ and not UIDs. + """ + + name_count = 0 + + def load_names_from_options(self, opt_names): + for name in opt_names: + if name not in self.dataset.keys(): + self.add(name) + + def sanitize_item(self, username): + """Obfuscate a new username not currently found in the map + """ + ob_name = "obfuscateduser%s" % self.name_count + self.name_count += 1 + if ob_name in self.dataset.values(): + return self.sanitize_item(username) + return ob_name diff -Nru sosreport-4.0/sos/cleaner/obfuscation_archive.py sosreport-4.1/sos/cleaner/obfuscation_archive.py --- sosreport-4.0/sos/cleaner/obfuscation_archive.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/cleaner/obfuscation_archive.py 2021-02-25 18:46:49.000000000 +0000 @@ -73,7 +73,9 @@ 'sys/kernel/debug', 'sys/module', 'var/log/.*dnf.*', - '.*.tar.*', # TODO: support archive unpacking + r'.*\.tar$', # TODO: support archive unpacking + # Be explicit with these tar matches to avoid matching commands + r'.*\.tar\.xz', '.*.gz' ] diff -Nru sosreport-4.0/sos/cleaner/parsers/hostname_parser.py sosreport-4.1/sos/cleaner/parsers/hostname_parser.py --- sosreport-4.0/sos/cleaner/parsers/hostname_parser.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/cleaner/parsers/hostname_parser.py 2021-02-25 18:46:49.000000000 +0000 @@ -22,9 +22,25 @@ ] def __init__(self, conf_file=None, opt_domains=None): - self.mapping = SoSHostnameMap(opt_domains) - self.short_names = [] + self.mapping = SoSHostnameMap() super(SoSHostnameParser, self).__init__(conf_file) + self.mapping.load_domains_from_map() + self.mapping.load_domains_from_options(opt_domains) + self.short_names = [] + self.load_short_names_from_mapping() + self.mapping.set_initial_counts() + + def load_short_names_from_mapping(self): + """When we load the mapping file into the hostname map, we have to do + some dancing to get those loaded properly into the "intermediate" dicts + that the map uses to hold hosts and domains. Similarly, we need to also + extract shortnames known to the map here. + """ + for hname in self.mapping.dataset.keys(): + if len(hname.split('.')) == 1: + # we have a short name only with no domain + if hname not in self.short_names: + self.short_names.append(hname) def load_hostname_into_map(self, hostname_string): """Force add the domainname found in /sos_commands/host/hostname into diff -Nru sosreport-4.0/sos/cleaner/parsers/keyword_parser.py sosreport-4.1/sos/cleaner/parsers/keyword_parser.py --- sosreport-4.0/sos/cleaner/parsers/keyword_parser.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/cleaner/parsers/keyword_parser.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,6 +8,8 @@ # # See the LICENSE file in the source distribution for further information. +import os + from sos.cleaner.parsers import SoSCleanerParser from sos.cleaner.mappings.keyword_map import SoSKeywordMap @@ -20,7 +22,7 @@ map_file_key = 'keyword_map' prep_map_file = '' - def __init__(self, conf_file=None, keywords=None): + def __init__(self, conf_file=None, keywords=None, keyword_file=None): self.mapping = SoSKeywordMap() self.user_keywords = [] super(SoSKeywordParser, self).__init__(conf_file) @@ -28,6 +30,9 @@ self.user_keywords.append(_keyword) if keywords: self.user_keywords.extend(keywords) + if keyword_file and os.path.exists(keyword_file): + with open(keyword_file, 'r') as kwf: + self.user_keywords.extend(kwf.read().splitlines()) def parse_line(self, line): count = 0 diff -Nru sosreport-4.0/sos/cleaner/parsers/mac_parser.py sosreport-4.1/sos/cleaner/parsers/mac_parser.py --- sosreport-4.0/sos/cleaner/parsers/mac_parser.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/cleaner/parsers/mac_parser.py 2021-02-25 18:46:49.000000000 +0000 @@ -11,6 +11,8 @@ from sos.cleaner.parsers import SoSCleanerParser from sos.cleaner.mappings.mac_map import SoSMacMap +import re + class SoSMacParser(SoSCleanerParser): """Handles parsing for MAC addresses""" @@ -21,7 +23,7 @@ r'(([^:|-])([0-9a-fA-F]{2}(:|-)){7}[0-9a-fA-F]{2}(\s|$))', r'(([^:|-])([0-9a-fA-F]{4}(:|-)){3}[0-9a-fA-F]{4}(\s|$))', # IPv4, avoiding matching a substring within IPv6 addresses - r'(([^:|-])([0-9a-fA-F]{2}([:-])){5}([0-9a-fA-F]){2}(\s|$))' + r'(([^:|-])([0-9a-fA-F]{2}([:-])){5}([0-9a-fA-F]){2}(.)?(\s|$|\W))' ] map_file_key = 'mac_map' prep_map_file = 'sos_commands/networking/ip_-d_address' @@ -29,3 +31,29 @@ def __init__(self, conf_file=None): self.mapping = SoSMacMap() super(SoSMacParser, self).__init__(conf_file) + + def reduce_mac_match(self, match): + """Strips away leading and trailing non-alphanum characters from any + matched string to leave us with just the bare MAC addr + """ + while not (match[0].isdigit() or match[0].isalpha()): + match = match[1:] + while not (match[-1].isdigit() or match[-1].isalpha()): + match = match[0:-1] + # just to be safe, call strip() to remove any padding + return match.strip() + + def parse_line(self, line): + count = 0 + for skip_pattern in self.skip_line_patterns: + if re.match(skip_pattern, line, re.I): + return line, count + for pattern in self.regex_patterns: + matches = [m[0] for m in re.findall(pattern, line, re.I)] + if matches: + count += len(matches) + for match in matches: + stripped_match = self.reduce_mac_match(match) + new_match = self.mapping.get(stripped_match) + line = line.replace(stripped_match, new_match) + return line, count diff -Nru sosreport-4.0/sos/cleaner/parsers/username_parser.py sosreport-4.1/sos/cleaner/parsers/username_parser.py --- sosreport-4.0/sos/cleaner/parsers/username_parser.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/cleaner/parsers/username_parser.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,58 @@ +# Copyright 2020 Red Hat, Inc. Jake Hunsaker + +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + + +from sos.cleaner.parsers import SoSCleanerParser +from sos.cleaner.mappings.username_map import SoSUsernameMap + + +class SoSUsernameParser(SoSCleanerParser): + """Parser for obfuscating usernames within an sosreport archive. + + Note that this parser does not rely on regex matching directly, like most + other parsers do. Instead, usernames are discovered via scraping the + collected output of lastlog. As such, we do not discover new usernames + later on, and only usernames present in lastlog output will be obfuscated, + and those passed via the --usernames option if one is provided. + """ + + name = 'Username Parser' + map_file_key = 'username_map' + prep_map_file = 'sos_commands/login/lastlog_-u_1000-60000' + regex_patterns = [] + skip_list = [ + 'nobody', + 'nfsnobody', + 'root' + ] + + def __init__(self, conf_file=None, opt_names=None): + self.mapping = SoSUsernameMap() + super(SoSUsernameParser, self).__init__(conf_file) + self.mapping.load_names_from_options(opt_names) + + def load_usernames_into_map(self, fname): + """Since we don't get the list of usernames from a straight regex for + this parser, we need to override the initial parser prepping here. + """ + with open(fname, 'r') as lastfile: + for line in lastfile.read().splitlines()[1:]: + user = line.split()[0] + if user in self.skip_list: + continue + self.mapping.get(user) + + def parse_line(self, line): + count = 0 + for username in self.mapping.dataset.keys(): + if username in line: + count = line.count(username) + line = line.replace(username, self.mapping.get(username)) + return line, count diff -Nru sosreport-4.0/sos/collector/clusters/__init__.py sosreport-4.1/sos/collector/clusters/__init__.py --- sosreport-4.0/sos/collector/clusters/__init__.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/collector/clusters/__init__.py 2021-02-25 18:46:49.000000000 +0000 @@ -60,7 +60,7 @@ self.master = None self.cluster_ssh_key = None self.tmpdir = commons['tmpdir'] - self.opts = commons['opts'] + self.opts = commons['cmdlineopts'] self.cluster_type = [self.__class__.__name__] for cls in self.__class__.__bases__: if cls.__name__ != 'Cluster': diff -Nru sosreport-4.0/sos/collector/__init__.py sosreport-4.1/sos/collector/__init__.py --- sosreport-4.0/sos/collector/__init__.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/collector/__init__.py 2021-02-25 18:46:49.000000000 +0000 @@ -65,6 +65,7 @@ 'image': '', 'jobs': 4, 'keywords': [], + 'keyword_file': None, 'label': '', 'list_options': False, 'log_size': 0, @@ -84,13 +85,21 @@ 'preset': '', 'save_group': '', 'since': '', + 'skip_commands': [], + 'skip_files': [], 'skip_plugins': [], 'sos_opt_line': '', 'ssh_key': '', 'ssh_port': 22, 'ssh_user': 'root', 'timeout': 600, - 'verify': False + 'verify': False, + 'usernames': [], + 'upload': False, + 'upload_url': None, + 'upload_directory': None, + 'upload_user': None, + 'upload_pass': None, } def __init__(self, parser, parsed_args, cmdline_args): @@ -271,6 +280,12 @@ help=('Escapes archived files older than date. ' 'This will also affect --all-logs. ' 'Format: YYYYMMDD[HHMMSS]')) + sos_grp.add_argument('--skip-commands', default=[], action='extend', + dest='skip_commands', + help="do not execute these commands") + sos_grp.add_argument('--skip-files', default=[], action='extend', + dest='skip_files', + help="do not collect these files") sos_grp.add_argument('--verify', action="store_true", help='perform pkg verification during collection') @@ -334,13 +349,26 @@ help='Specify an SSH user. Default root') collect_grp.add_argument('--timeout', type=int, required=False, help='Timeout for sosreport on each node.') + collect_grp.add_argument("--upload", action="store_true", + default=False, + help="Upload archive to a policy-default " + "location") + collect_grp.add_argument("--upload-url", default=None, + help="Upload the archive to specified server") + collect_grp.add_argument("--upload-directory", default=None, + help="Specify upload directory for archive") + collect_grp.add_argument("--upload-user", default=None, + help="Username to authenticate with") + collect_grp.add_argument("--upload-pass", default=None, + help="Password to authenticate with") # Group the cleaner options together cleaner_grp = parser.add_argument_group( 'Cleaner/Masking Options', 'These options control how data obfuscation is performed' ) - cleaner_grp.add_argument('--clean', '--mask', dest='clean', + cleaner_grp.add_argument('--clean', '--cleaner', '--mask', + dest='clean', default=False, action='store_true', help='Obfuscate sensistive information') cleaner_grp.add_argument('--domains', dest='domains', default=[], @@ -349,6 +377,9 @@ cleaner_grp.add_argument('--keywords', action='extend', default=[], dest='keywords', help='List of keywords to obfuscate') + cleaner_grp.add_argument('--keyword-file', default=None, + dest='keyword_file', + help='Provide a file a keywords to obfuscate') cleaner_grp.add_argument('--no-update', action='store_true', default=False, dest='no_update', help='Do not update the default cleaner map') @@ -356,6 +387,9 @@ default='/etc/sos/cleaner/default_mapping', help=('Provide a previously generated mapping' ' file for obfuscation')) + cleaner_grp.add_argument('--usernames', dest='usernames', default=[], + action='extend', + help='List of usernames to obfuscate') def _check_for_control_persist(self): """Checks to see if the local system supported SSH ControlPersist. @@ -399,8 +433,8 @@ to hand to other collector mechanisms """ self.commons = { + 'cmdlineopts': self.opts, 'need_sudo': True if self.opts.ssh_user != 'root' else False, - 'opts': self.opts, 'tmpdir': self.tmpdir, 'hostlen': len(self.opts.master) or len(self.hostname), 'policy': self.policy @@ -630,6 +664,7 @@ return fname def prep(self): + self.policy.set_commons(self.commons) if (not self.opts.password and not self.opts.password_per_node): self.log_debug('password not specified, assuming SSH keys') @@ -683,6 +718,8 @@ % (self.opts.group, err)) self._exit(1) + self.policy.pre_work() + if self.opts.master: self.connect_to_master() self.opts.no_local = True @@ -732,6 +769,7 @@ self.cluster = self.clusters['jbon'] else: self.cluster = self.clusters[self.opts.cluster_type] + self.cluster_type = self.opts.cluster_type self.cluster.master = self.master else: @@ -785,6 +823,13 @@ self.ui_log.info("\t%-*s" % (self.commons['hostlen'], node)) self.ui_log.info('') + if not self.opts.batch: + try: + input("\nPress ENTER to continue with these nodes, or press " + "CTRL-C to quit\n") + self.ui_log.info("") + except KeyboardInterrupt: + self.exit("Exiting on user cancel", 130) def configure_sos_cmd(self): """Configures the sosreport command that is run on the nodes""" @@ -1101,11 +1146,18 @@ self.log_info(msg % (self.retrieved, self.report_num)) self.close_all_connections() if self.retrieved > 0: - self.create_cluster_archive() + arc_name = self.create_cluster_archive() else: msg = 'No sosreports were collected, nothing to archive...' self.exit(msg, 1) + if self.opts.upload and self.get_upload_url(): + try: + self.policy.upload_archive(arc_name) + self.ui_log.info("Uploaded archive successfully") + except Exception as err: + self.ui_log.error("Upload attempt failed: %s" % err) + def _collect(self, client): """Runs sosreport on each node""" try: @@ -1158,8 +1210,6 @@ self.log_info('Creating archive of sosreports...') for fname in arc_paths: dest = fname.split('/')[-1] - if fname.endswith(('.md5',)): - dest = os.path.join('checksums', fname.split('/')[-1]) if do_clean: dest = cleaner.obfuscate_string(dest) name = os.path.join(self.tmpdir, fname) @@ -1169,7 +1219,7 @@ checksum = cleaner.get_new_checksum(fname) if checksum: name = os.path.join('checksums', fname.split('/')[-1]) - name += '.md5' + name += '.sha256' self.archive.add_string(checksum, name) self.archive.add_file(self.sos_log_file, dest=os.path.join('sos_logs', 'sos.log')) @@ -1218,6 +1268,7 @@ self.ui_log.info('\nThe following archive has been created. ' 'Please provide it to your support team.') self.ui_log.info('\t%s\n' % final_name) + return final_name except Exception as err: msg = ("Could not finalize archive: %s\n\nData may still be " "available uncompressed at %s" % (err, self.archive_path)) diff -Nru sosreport-4.0/sos/collector/sosnode.py sosreport-4.1/sos/collector/sosnode.py --- sosreport-4.0/sos/collector/sosnode.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/collector/sosnode.py 2021-02-25 18:46:49.000000000 +0000 @@ -18,7 +18,8 @@ from distutils.version import LooseVersion from pipes import quote -from sos.policies import load, InitSystem +from sos.policies import load +from sos.policies.init_systems import InitSystem from sos.collector.exceptions import (InvalidPasswordException, TimeoutPasswordAuthException, PasswordRequestException, @@ -36,7 +37,7 @@ load_facts=True): self.address = address.strip() self.commons = commons - self.opts = commons['opts'] + self.opts = commons['cmdlineopts'] self.tmpdir = commons['tmpdir'] self.hostlen = commons['hostlen'] self.need_sudo = commons['need_sudo'] @@ -62,6 +63,7 @@ 'presets': [], 'sos_cmd': commons['sos_cmd'] } + self.sos_bin = 'sosreport' filt = ['localhost', '127.0.0.1'] self.soslog = logging.getLogger('sos') self.ui_log = logging.getLogger('sos_ui') @@ -114,6 +116,7 @@ self.manifest.add_field('hostname', self._hostname) self.manifest.add_field('policy', self.host.distro) self.manifest.add_field('sos_version', self.sos_info['version']) + self.manifest.add_field('final_sos_command', '') def check_in_container(self): """ @@ -390,9 +393,6 @@ self.log_debug("Error while trying to create new SSH control " "socket: %s" % err) raise - if cmd.startswith('sosreport'): - cmd = cmd.replace('sosreport', self.host.sos_bin_path) - need_root = True if use_container and self.host.containerized: cmd = self.host.format_container_command(cmd) if need_root: @@ -426,6 +426,7 @@ """Run a sosreport on the node, then collect it""" self.sos_cmd = self.finalize_sos_cmd() self.log_info('Final sos command set to %s' % self.sos_cmd) + self.manifest.add_field('final_sos_command', self.sos_cmd) try: path = self.execute_sos_command() if path: @@ -619,7 +620,7 @@ sos_cmd = self.sos_info['sos_cmd'] label = self.determine_sos_label() if label: - self.sos_cmd = '%s %s ' % (sos_cmd, quote(label)) + sos_cmd = '%s %s ' % (sos_cmd, quote(label)) if self.opts.sos_opt_line: return '%s %s' % (sos_cmd, self.opts.sos_opt_line) @@ -649,6 +650,25 @@ if self.opts.since: sos_opts.append('--since=%s' % quote(self.opts.since)) + # sos-4.0 changes the binary + if self.check_sos_version('4.0'): + self.sos_bin = 'sos report' + + if self.check_sos_version('4.1'): + if self.opts.skip_commands: + sos_opts.append( + '--skip-commands=%s' % (quote(self.opts.skip_commands)) + ) + if self.opts.skip_files: + sos_opts.append( + '--skip-files=%s' % (quote(self.opts.skip_files)) + ) + + sos_cmd = sos_cmd.replace( + 'sosreport', + os.path.join(self.host.sos_bin_path, self.sos_bin) + ) + if self.opts.only_plugins: plugs = [o for o in self.opts.only_plugins if self._plugin_exists(o)] @@ -701,7 +721,6 @@ 'not exist on node' % self.opts.preset) _sos_cmd = "%s %s" % (sos_cmd, ' '.join(sos_opts)) - self.manifest.add_field('final_sos_command', _sos_cmd) return _sos_cmd def determine_sos_label(self): @@ -754,6 +773,7 @@ self.ui_msg('Generating sosreport...') try: path = False + checksum = False res = self.run_command(self.sos_cmd, timeout=self.opts.timeout, get_pty=True, need_root=True, @@ -762,6 +782,19 @@ for line in res['stdout'].splitlines(): if fnmatch.fnmatch(line, '*sosreport-*tar*'): path = line.strip() + if line.startswith((" sha256\t", " md5\t")): + checksum = line.split("\t")[1] + elif line.startswith("The checksum is: "): + checksum = line.split()[3] + + if checksum is not None: + self.manifest.add_field('checksum', checksum) + if len(checksum) == 32: + self.manifest.add_field('checksum_type', 'md5') + elif len(checksum) == 64: + self.manifest.add_field('checksum_type', 'sha256') + else: + self.manifest.add_field('checksum_type', 'unknown') else: err = self.determine_sos_error(res['status'], res['stdout']) self.log_debug("Error running sosreport. rc = %s msg = %s" @@ -838,10 +871,6 @@ except Exception: self.log_error('Failed to make archive readable') return False - try: - self.make_archive_readable(self.sos_path + '.md5') - except Exception: - self.log_debug('Failed to make md5 readable') self.soslog.info('Retrieving sosreport from %s' % self.address) self.ui_msg('Retrieving sosreport...') ret = self.retrieve_file(self.sos_path) @@ -851,9 +880,6 @@ else: self.log_error('Failed to retrieve sosreport') raise SystemExit - self.hash_retrieved = self.retrieve_file(self.sos_path + '.md5') - if self.hash_retrieved: - self.file_list.append(self.sos_path.split('/')[-1] + '.md5') return True else: # sos sometimes fails but still returns a 0 exit code @@ -882,7 +908,9 @@ def cleanup(self): """Remove the sos archive from the node once we have it locally""" self.remove_sos_archive() - if self.hash_retrieved: + if os.path.isfile(self.sos_path + '.sha256'): + self.remove_file(self.sos_path + '.sha256') + elif os.path.isfile(self.sos_path + '.md5'): self.remove_file(self.sos_path + '.md5') cleanup = self.host.set_cleanup_cmd() if cleanup: diff -Nru sosreport-4.0/sos/component.py sosreport-4.1/sos/component.py --- sosreport-4.0/sos/component.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/component.py 2021-02-25 18:46:49.000000000 +0000 @@ -108,6 +108,7 @@ try: import sos.policies self.policy = sos.policies.load(sysroot=self.opts.sysroot) + self.sysroot = self.policy.host_sysroot() except KeyboardInterrupt: self._exit(0) self._is_root = self.policy.is_root() @@ -123,6 +124,9 @@ self.manifest.add_field('policy', self.policy.distro) self.manifest.add_section('components') + def execute(self): + raise NotImplementedError + def get_exit_handler(self): def exit_handler(signum, frame): self.exit_process = True @@ -138,7 +142,7 @@ use a standardized env var to redirect to the host's filesystem instead """ if self.opts.tmp_dir: - return self.opts.tmp_dir + return os.path.abspath(self.opts.tmp_dir) tmpdir = '/var/tmp' @@ -189,7 +193,7 @@ for opt, val in codict.items(): if opt not in cmdopts.arg_defaults.keys(): continue - if val and val != opts.arg_defaults[opt]: + if val not in [None, [], ''] and val != opts.arg_defaults[opt]: setattr(opts, opt, val) return opts @@ -243,13 +247,13 @@ auto_archive = self.policy.get_preferred_archive() self.archive = auto_archive(archive_name, self.tmpdir, self.policy, self.opts.threads, - enc_opts, self.opts.sysroot, + enc_opts, self.sysroot, self.manifest) else: self.archive = TarFileArchive(archive_name, self.tmpdir, self.policy, self.opts.threads, - enc_opts, self.opts.sysroot, + enc_opts, self.sysroot, self.manifest) self.archive.set_debug(True if self.opts.debug else False) diff -Nru sosreport-4.0/sos/__init__.py sosreport-4.1/sos/__init__.py --- sosreport-4.0/sos/__init__.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/__init__.py 2021-02-25 18:46:49.000000000 +0000 @@ -14,7 +14,7 @@ This module houses the i18n setup and message function. The default is to use gettext to internationalize messages. """ -__version__ = "4.0" +__version__ = "4.1" import os import sys @@ -110,14 +110,15 @@ for comp in self._components: _com_subparser = self.subparsers.add_parser( comp, - aliases=self._components[comp][1] + aliases=self._components[comp][1], + prog="sos %s" % comp ) _com_subparser.usage = "sos %s [options]" % comp _com_subparser.register('action', 'extend', SosListOption) self._add_common_options(_com_subparser) self._components[comp][0].add_parser_options(parser=_com_subparser) _com_subparser.set_defaults(component=comp) - self.args, _unknown = self.parser.parse_known_args(self.cmdline) + self.args = self.parser.parse_args(self.cmdline) self._init_component() def _add_common_options(self, parser): diff -Nru sosreport-4.0/sos/missing.py sosreport-4.1/sos/missing.py --- sosreport-4.0/sos/missing.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/missing.py 2021-02-25 18:46:49.000000000 +0000 @@ -14,6 +14,10 @@ load_policy = False configure_logging = False desc = '(unavailable) Collect an sos report from multiple nodes' + missing_msg = ( + 'It appears likely that your distribution separately ships a package ' + 'called sos-collector. Please install it to enable this function' + ) def execute(self): sys.stderr.write( @@ -30,15 +34,34 @@ """ return [] + @classmethod + def add_parser_options(cls, parser): + """Set the --help output for collect to a message that shows that + the functionality is unavailable + """ + msg = "%s %s" % ( + 'WARNING: `collect` is not available with this installation!', + cls.missing_msg + ) + parser.epilog = msg + return parser + class MissingPexpect(MissingCollect): """This is used as a placeholder for when the collect component is locally installed, but cannot be used due to a missing pexpect dependency. """ + missing_msg = ( + 'Please install the python3-pexpect package for your distribution in ' + 'order to enable this function' + ) + def execute(self): sys.stderr.write( "The collect command is unavailable due to a missing dependency " "on python3-pexpect.\n\nPlease install python3-pexpect to enable " "this functionality.\n" ) + +# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/options.py sosreport-4.1/sos/options.py --- sosreport-4.0/sos/options.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/options.py 2021-02-25 18:46:49.000000000 +0000 @@ -186,7 +186,7 @@ if 'verbose' in odict.keys(): odict['verbosity'] = int(odict.pop('verbose')) # convert options names - for key in odict.keys(): + for key in list(odict): if '-' in key: odict[key.replace('-', '_')] = odict.pop(key) # set the values according to the config file @@ -282,6 +282,9 @@ """ if name in ("add_preset", "del_preset", "desc", "note"): return False + # Exception list for options that still need to be reported when 0 + if name in ['log_size', 'plugin_timeout'] and value == 0: + return True return has_value(name, value) def argify(name, value): diff -Nru sosreport-4.0/sos/policies/amazon.py sosreport-4.1/sos/policies/amazon.py --- sosreport-4.0/sos/policies/amazon.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/policies/amazon.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,43 +0,0 @@ -# Copyright (C) Red Hat, Inc. 2019 - -# This file is part of the sos project: https://github.com/sosreport/sos -# -# This copyrighted material is made available to anyone wishing to use, -# modify, copy, or redistribute it subject to the terms and conditions of -# version 2 of the GNU General Public License. -# -# See the LICENSE file in the source distribution for further information. - -from sos.policies.redhat import RedHatPolicy, OS_RELEASE -import os - - -class AmazonPolicy(RedHatPolicy): - - distro = "Amazon Linux" - vendor = "Amazon" - vendor_url = "https://aws.amazon.com" - - def __init__(self, sysroot=None, init=None, probe_runtime=True, - remote_exec=None): - super(AmazonPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime, - remote_exec=remote_exec) - - @classmethod - def check(cls, remote=''): - - if remote: - return cls.distro in remote - - if not os.path.exists(OS_RELEASE): - return False - - with open(OS_RELEASE, 'r') as f: - for line in f: - if line.startswith('NAME'): - if 'Amazon Linux' in line: - return True - return False - -# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/policies/cos.py sosreport-4.1/sos/policies/cos.py --- sosreport-4.0/sos/policies/cos.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/policies/cos.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ -# Copyright (C) Red Hat, Inc. 2020 - -# This file is part of the sos project: https://github.com/sosreport/sos -# -# This copyrighted material is made available to anyone wishing to use, -# modify, copy, or redistribute it subject to the terms and conditions of -# version 2 of the GNU General Public License. -# -# See the LICENSE file in the source distribution for further information. - -from sos.report.plugins import CosPlugin -from sos.policies import LinuxPolicy - - -def _blank_or_comment(line): - """Test whether line is empty of contains a comment. - - Test whether the ``line`` argument is either blank, or a - whole-line comment. - - :param line: the line of text to be checked. - :returns: ``True`` if the line is blank or a comment, - and ``False`` otherwise. - :rtype: bool - """ - return not line.strip() or line.lstrip().startswith('#') - - -class CosPolicy(LinuxPolicy): - distro = "Container-Optimized OS" - vendor = "Google Cloud Platform" - vendor_url = "https://cloud.google.com/container-optimized-os/" - valid_subclasses = [CosPlugin] - PATH = "/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin" - - @classmethod - def check(cls, remote=''): - if remote: - return cls.distro in remote - - try: - with open('/etc/os-release', 'r') as fp: - os_release = dict(line.strip().split('=') for line in fp - if not _blank_or_comment(line)) - return os_release['ID'] == 'cos' - except (IOError, KeyError): - return False - -# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/policies/debian.py sosreport-4.1/sos/policies/debian.py --- sosreport-4.0/sos/policies/debian.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/policies/debian.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -from sos.report.plugins import DebianPlugin -from sos.policies import PackageManager, LinuxPolicy - -import os - - -class DebianPolicy(LinuxPolicy): - distro = "Debian" - vendor = "the Debian project" - vendor_url = "https://www.debian.org/" - ticket_number = "" - _debq_cmd = "dpkg-query -W -f='${Package}|${Version}\\n'" - _debv_cmd = "dpkg --verify" - _debv_filter = "" - name_pattern = 'friendly' - valid_subclasses = [DebianPlugin] - PATH = "/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games" \ - + ":/usr/local/sbin:/usr/local/bin" - sos_pkg_name = 'sosreport' - sos_bin_path = '/usr/bin/sosreport' - - 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.ticket_number = "" - self.package_manager = PackageManager(query_command=self._debq_cmd, - verify_command=self._debv_cmd, - verify_filter=self._debv_filter, - chroot=sysroot, - remote_exec=remote_exec) - - self.valid_subclasses = [DebianPlugin] - - def _get_pkg_name_for_binary(self, binary): - # for binary not specified inside {..}, return binary itself - return { - "xz": "xz-utils" - }.get(binary, binary) - - @classmethod - def check(cls, remote=''): - """This method checks to see if we are running on Debian. - It returns True or False.""" - - if remote: - return cls.distro in remote - - return os.path.isfile('/etc/debian_version') - - def dist_version(self): - try: - with open('/etc/lsb-release', 'r') as fp: - rel_string = fp.read() - if "wheezy/sid" in rel_string: - return 6 - elif "jessie/sid" in rel_string: - return 7 - return False - except IOError: - return False - -# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/policies/distros/amazon.py sosreport-4.1/sos/policies/distros/amazon.py --- sosreport-4.0/sos/policies/distros/amazon.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/policies/distros/amazon.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,43 @@ +# Copyright (C) Red Hat, Inc. 2019 + +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +from sos.policies.distros.redhat import RedHatPolicy, OS_RELEASE +import os + + +class AmazonPolicy(RedHatPolicy): + + distro = "Amazon Linux" + vendor = "Amazon" + vendor_urls = [('Distribution Website', 'https://aws.amazon.com')] + + def __init__(self, sysroot=None, init=None, probe_runtime=True, + remote_exec=None): + super(AmazonPolicy, self).__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) + + @classmethod + def check(cls, remote=''): + + if remote: + return cls.distro in remote + + if not os.path.exists(OS_RELEASE): + return False + + with open(OS_RELEASE, 'r') as f: + for line in f: + if line.startswith('NAME'): + if 'Amazon Linux' in line: + return True + return False + +# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/policies/distros/cos.py sosreport-4.1/sos/policies/distros/cos.py --- sosreport-4.0/sos/policies/distros/cos.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/policies/distros/cos.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,52 @@ +# Copyright (C) Red Hat, Inc. 2020 + +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +from sos.report.plugins import CosPlugin, IndependentPlugin +from sos.policies.distros import LinuxPolicy + + +def _blank_or_comment(line): + """Test whether line is empty of contains a comment. + + Test whether the ``line`` argument is either blank, or a + whole-line comment. + + :param line: the line of text to be checked. + :returns: ``True`` if the line is blank or a comment, + and ``False`` otherwise. + :rtype: bool + """ + return not line.strip() or line.lstrip().startswith('#') + + +class CosPolicy(LinuxPolicy): + distro = "Container-Optimized OS" + vendor = "Google Cloud Platform" + vendor_urls = [ + ('Distribution Website', + 'https://cloud.google.com/container-optimized-os/') + ] + valid_subclasses = [CosPlugin, IndependentPlugin] + PATH = "/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin" + + @classmethod + def check(cls, remote=''): + if remote: + return cls.distro in remote + + try: + with open('/etc/os-release', 'r') as fp: + os_release = dict(line.strip().split('=') for line in fp + if not _blank_or_comment(line)) + return os_release['ID'] == 'cos' + except (IOError, KeyError): + return False + +# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/policies/distros/debian.py sosreport-4.1/sos/policies/distros/debian.py --- sosreport-4.0/sos/policies/distros/debian.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/policies/distros/debian.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,62 @@ +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +from sos.report.plugins import DebianPlugin +from sos.policies.distros import LinuxPolicy +from sos.policies.package_managers.dpkg import DpkgPackageManager + +import os + + +class DebianPolicy(LinuxPolicy): + distro = "Debian" + vendor = "the Debian project" + vendor_urls = [('Community Website', 'https://www.debian.org/')] + name_pattern = 'friendly' + valid_subclasses = [DebianPlugin] + PATH = "/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games" \ + + ":/usr/local/sbin:/usr/local/bin" + sos_pkg_name = 'sosreport' + + 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, + remote_exec=remote_exec) + self.valid_subclasses += [DebianPlugin] + + def _get_pkg_name_for_binary(self, binary): + # for binary not specified inside {..}, return binary itself + return { + "xz": "xz-utils" + }.get(binary, binary) + + @classmethod + def check(cls, remote=''): + """This method checks to see if we are running on Debian. + It returns True or False.""" + + if remote: + return cls.distro in remote + + return os.path.isfile('/etc/debian_version') + + def dist_version(self): + try: + with open('/etc/lsb-release', 'r') as fp: + rel_string = fp.read() + if "wheezy/sid" in rel_string: + return 6 + elif "jessie/sid" in rel_string: + return 7 + return False + except IOError: + return False + +# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/policies/distros/ibmkvm.py sosreport-4.1/sos/policies/distros/ibmkvm.py --- sosreport-4.0/sos/policies/distros/ibmkvm.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/policies/distros/ibmkvm.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,84 @@ +# Copyright (C) IBM Corporation, 2015 +# +# Authors: Kamalesh Babulal +# +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +from sos.report.plugins import PowerKVMPlugin, ZKVMPlugin +from sos.policies.distros.redhat import RedHatPolicy + +import os + + +class PowerKVMPolicy(RedHatPolicy): + distro = "PowerKVM" + vendor = "IBM" + vendor_urls = [ + ('Commercial Support', + 'http://www-03.ibm.com/systems/power/software/linux/powerkvm') + ] + + def __init__(self, sysroot=None, init=None, probe_runtime=True, + remote_exec=None): + super(PowerKVMPolicy, self).__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) + self.valid_subclasses += [PowerKVMPlugin] + + @classmethod + def check(cls, remote=''): + """This method checks to see if we are running on PowerKVM. + It returns True or False.""" + + if remote: + return cls.distro in remote + + return os.path.isfile('/etc/ibm_powerkvm-release') + + def dist_version(self): + try: + with open('/etc/ibm_powerkvm-release', 'r') as fp: + version_string = fp.read() + return version_string[2][0] + except IOError: + return False + + +class ZKVMPolicy(RedHatPolicy): + distro = "IBM Hypervisor" + vendor = "IBM Hypervisor" + vendor_urls = [ + ('Commercial Support', + 'http://www.ibm.com/systems/z/linux/IBMHypervisor/support/') + ] + + def __init__(self, sysroot=None): + super(ZKVMPolicy, self).__init__(sysroot=sysroot) + self.valid_subclasses += [ZKVMPlugin] + + @classmethod + def check(cls, remote=''): + """This method checks to see if we are running on IBM Z KVM. It + returns True or False.""" + + if remote: + return cls.distro in remote + + return os.path.isfile('/etc/base-release') + + def dist_version(self): + try: + with open('/etc/base-release', 'r') as fp: + version_string = fp.read() + return version_string.split(' ', 4)[3][0] + except IOError: + return False + + +# vim: set ts=4 sw=4 diff -Nru sosreport-4.0/sos/policies/distros/__init__.py sosreport-4.1/sos/policies/distros/__init__.py --- sosreport-4.0/sos/policies/distros/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/policies/distros/__init__.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,532 @@ +# Copyright (C) 2020 Red Hat, Inc., Jake Hunsaker + +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +import os +import re + +from getpass import getpass + +from sos import _sos as _ +from sos.policies import Policy +from sos.policies.init_systems import InitSystem +from sos.policies.init_systems.systemd import SystemdInit +from sos.policies.runtimes.podman import PodmanContainerRuntime +from sos.policies.runtimes.docker import DockerContainerRuntime + +from sos.utilities import shell_out + + +try: + import requests + REQUESTS_LOADED = True +except ImportError: + REQUESTS_LOADED = False + + +class LinuxPolicy(Policy): + """This policy is meant to be an abc class that provides common + implementations used in Linux distros""" + + distro = "Linux" + vendor = "None" + PATH = "/bin:/sbin:/usr/bin:/usr/sbin" + init = None + # _ prefixed class attrs are used for storing any vendor-defined defaults + # the non-prefixed attrs are used by the upload methods, and will be set + # to the cmdline/config file values, if provided. If not provided, then + # those attrs will be set to the _ prefixed values as a fallback. + # TL;DR Use _upload_* for policy default values, use upload_* when wanting + # to actual use the value in a method/override + _upload_url = None + _upload_directory = '/' + _upload_user = None + _upload_password = None + _use_https_streaming = False + default_container_runtime = 'docker' + _preferred_hash_name = None + upload_url = None + upload_user = None + upload_password = None + # collector-focused class attrs + containerized = False + container_image = None + sos_path_strip = None + sos_pkg_name = None + sos_bin_path = '/usr/bin' + sos_container_name = 'sos-collector-tmp' + container_version_command = None + + def __init__(self, sysroot=None, init=None, probe_runtime=True): + super(LinuxPolicy, self).__init__(sysroot=sysroot, + probe_runtime=probe_runtime) + self.init_kernel_modules() + + if init is not None: + self.init_system = init + elif os.path.isdir("/run/systemd/system/"): + self.init_system = SystemdInit() + else: + self.init_system = InitSystem() + + self.runtimes = {} + if self.probe_runtime: + _crun = [ + PodmanContainerRuntime(policy=self), + DockerContainerRuntime(policy=self) + ] + for runtime in _crun: + if runtime.check_is_active(): + self.runtimes[runtime.name] = runtime + if runtime.name == self.default_container_runtime: + self.runtimes['default'] = self.runtimes[runtime.name] + self.runtimes[runtime.name].load_container_info() + + if self.runtimes and 'default' not in self.runtimes.keys(): + # still allow plugins to query a runtime present on the system + # even if that is not the policy default one + idx = list(self.runtimes.keys()) + self.runtimes['default'] = self.runtimes[idx[0]] + + @classmethod + def set_forbidden_paths(cls): + return [ + '/etc/passwd', + '/etc/shadow' + ] + + def default_runlevel(self): + try: + with open("/etc/inittab") as fp: + pattern = r"id:(\d{1}):initdefault:" + text = fp.read() + return int(re.findall(pattern, text)[0]) + except (IndexError, IOError): + return 3 + + def kernel_version(self): + return self.release + + def host_name(self): + return self.hostname + + def is_kernel_smp(self): + return self.smp + + def get_arch(self): + return self.machine + + def get_local_name(self): + """Returns the name usd in the pre_work step""" + return self.host_name() + + def sanitize_filename(self, name): + return re.sub(r"[^-a-z,A-Z.0-9]", "", name) + + def init_kernel_modules(self): + """Obtain a list of loaded kernel modules to reference later for plugin + enablement and SoSPredicate checks + """ + lines = shell_out("lsmod", timeout=0).splitlines() + self.kernel_mods = [line.split()[0].strip() for line in lines] + + def pre_work(self): + # this method will be called before the gathering begins + + cmdline_opts = self.commons['cmdlineopts'] + caseid = cmdline_opts.case_id if cmdline_opts.case_id else "" + + # Set the cmdline settings to the class attrs that are referenced later + # The policy default '_' prefixed versions of these are untouched to + # allow fallback + self.upload_url = cmdline_opts.upload_url + self.upload_user = cmdline_opts.upload_user + self.upload_directory = cmdline_opts.upload_directory + self.upload_password = cmdline_opts.upload_pass + + if not cmdline_opts.batch and not \ + cmdline_opts.quiet: + try: + if caseid: + self.case_id = caseid + else: + self.case_id = input(_("Please enter the case id " + "that you are generating this " + "report for [%s]: ") % caseid) + # Policies will need to handle the prompts for user information + if cmdline_opts.upload and self.get_upload_url(): + self.prompt_for_upload_user() + self.prompt_for_upload_password() + self._print() + except KeyboardInterrupt: + self._print() + raise + + if cmdline_opts.case_id: + self.case_id = cmdline_opts.case_id + + return + + def prompt_for_upload_user(self): + """Should be overridden by policies to determine if a user needs to + be provided or not + """ + if not self.get_upload_user(): + msg = "Please provide upload user for %s: " % self.get_upload_url() + self.upload_user = input(_(msg)) + + def prompt_for_upload_password(self): + """Should be overridden by policies to determine if a password needs to + be provided for upload or not + """ + if not self.get_upload_password() and (self.get_upload_user() != + self._upload_user): + msg = ("Please provide the upload password for %s: " + % self.get_upload_user()) + self.upload_password = getpass(msg) + + def upload_archive(self, archive): + """ + Entry point for sos attempts to upload the generated archive to a + policy or user specified location. + + Curerntly there is support for HTTPS, SFTP, and FTP. HTTPS uploads are + preferred for policy-defined defaults. + + Policies that need to override uploading methods should override the + respective upload_https(), upload_sftp(), and/or upload_ftp() methods + and should NOT override this method. + + :param archive: The archive filepath to use for upload + :type archive: ``str`` + + In order to enable this for a policy, that policy needs to implement + the following: + + Required Class Attrs + + :_upload_url: The default location to use. Note these MUST include + protocol header + :_upload_user: Default username, if any else None + :_upload_password: Default password, if any else None + :_use_https_streaming: Set to True if the HTTPS endpoint supports + streaming data + + The following Class Attrs may optionally be overidden by the Policy + + :_upload_directory: Default FTP server directory, if any + + + The following methods may be overridden by ``Policy`` as needed + + `prompt_for_upload_user()` + Determines if sos should prompt for a username or not. + + `get_upload_user()` + Determines if the default or a different username should be used + + `get_upload_https_auth()` + Format authentication data for HTTPS uploads + + `get_upload_url_string()` + Print a more human-friendly string than vendor URLs + """ + self.upload_archive = archive + if not self.upload_url: + self.upload_url = self.get_upload_url() + if not self.upload_url: + raise Exception("No upload destination provided by policy or by " + "--upload-url") + upload_func = self._determine_upload_type() + print(_("Attempting upload to %s" % self.get_upload_url_string())) + return upload_func() + + def _determine_upload_type(self): + """Based on the url provided, determine what type of upload to attempt. + + Note that this requires users to provide a FQDN address, such as + https://myvendor.com/api or ftp://myvendor.com instead of + myvendor.com/api or myvendor.com + """ + prots = { + 'ftp': self.upload_ftp, + 'sftp': self.upload_sftp, + 'https': self.upload_https + } + if '://' not in self.upload_url: + raise Exception("Must provide protocol in upload URL") + prot, url = self.upload_url.split('://') + if prot not in prots.keys(): + raise Exception("Unsupported or unrecognized protocol: %s" % prot) + return prots[prot] + + def get_upload_https_auth(self, user=None, password=None): + """Formats the user/password credentials using basic auth + + :param user: The username for upload + :type user: ``str`` + + :param password: Password for `user` to use for upload + :type password: ``str`` + + :returns: The user/password auth suitable for use in reqests calls + :rtype: ``requests.auth.HTTPBasicAuth()`` + """ + if not user: + user = self.get_upload_user() + if not password: + password = self.get_upload_password() + + return requests.auth.HTTPBasicAuth(user, password) + + def get_upload_url(self): + """Helper function to determine if we should use the policy default + upload url or one provided by the user + + :returns: The URL to use for upload + :rtype: ``str`` + """ + return self.upload_url or self._upload_url + + def get_upload_url_string(self): + """Used by distro policies to potentially change the string used to + report upload location from the URL to a more human-friendly string + """ + return self.get_upload_url() + + def get_upload_user(self): + """Helper function to determine if we should use the policy default + upload user or one provided by the user + + :returns: The username to use for upload + :rtype: ``str`` + """ + return (os.getenv('SOSUPLOADUSER', None) or + self.upload_user or + self._upload_user) + + def get_upload_password(self): + """Helper function to determine if we should use the policy default + upload password or one provided by the user + + A user provided password, either via option or the 'SOSUPLOADPASSWORD' + environment variable will have precendent over any policy value + + :returns: The password to use for upload + :rtype: ``str`` + """ + return (os.getenv('SOSUPLOADPASSWORD', None) or + self.upload_password or + self._upload_password) + + def upload_sftp(self): + """Attempts to upload the archive to an SFTP location. + + Due to the lack of well maintained, secure, and generally widespread + python libraries for SFTP, sos will shell-out to the system's local ssh + installation in order to handle these uploads. + + Do not override this method with one that uses python-paramiko, as the + upstream sos team will reject any PR that includes that dependency. + """ + raise NotImplementedError("SFTP support is not yet implemented") + + def _upload_https_streaming(self, archive): + """If upload_https() needs to use requests.put(), this method is used + to provide streaming functionality + + Policies should override this method instead of the base upload_https() + + :param archive: The open archive file object + """ + return requests.put(self.get_upload_url(), data=archive, + auth=self.get_upload_https_auth()) + + def _get_upload_headers(self): + """Define any needed headers to be passed with the POST request here + """ + return {} + + def _upload_https_no_stream(self, archive): + """If upload_https() needs to use requests.post(), this method is used + to provide non-streaming functionality + + Policies should override this method instead of the base upload_https() + + :param archive: The open archive file object + """ + files = { + 'file': (archive.name.split('/')[-1], archive, + self._get_upload_headers()) + } + return requests.post(self.get_upload_url(), files=files, + auth=self.get_upload_https_auth()) + + def upload_https(self): + """Attempts to upload the archive to an HTTPS location. + + Policies may define whether this upload attempt should use streaming + or non-streaming data by setting the `use_https_streaming` class + attr to True + + :returns: ``True`` if upload is successful + :rtype: ``bool`` + + :raises: ``Exception`` if upload was unsuccessful + """ + if not REQUESTS_LOADED: + raise Exception("Unable to upload due to missing python requests " + "library") + + with open(self.upload_archive, 'rb') as arc: + if not self._use_https_streaming: + r = self._upload_https_no_stream(arc) + else: + r = self._upload_https_streaming(arc) + if r.status_code != 201: + if r.status_code == 401: + raise Exception( + "Authentication failed: invalid user credentials" + ) + raise Exception("POST request returned %s: %s" + % (r.status_code, r.reason)) + return True + + def upload_ftp(self, url=None, directory=None, user=None, password=None): + """Attempts to upload the archive to either the policy defined or user + provided FTP location. + + :param url: The URL to upload to + :type url: ``str`` + + :param directory: The directory on the FTP server to write to + :type directory: ``str`` or ``None`` + + :param user: The user to authenticate with + :type user: ``str`` + + :param password: The password to use for `user` + :type password: ``str`` + + :returns: ``True`` if upload is successful + :rtype: ``bool`` + + :raises: ``Exception`` if upload in unsuccessful + """ + try: + import ftplib + import socket + except ImportError: + # socket is part of the standard library, should only fail here on + # ftplib + raise Exception("missing python ftplib library") + + if not url: + url = self.get_upload_url() + if url is None: + raise Exception("no FTP server specified by policy, use --upload-" + "url to specify a location") + + url = url.replace('ftp://', '') + + if not user: + user = self.get_upload_user() + + if not password: + password = self.get_upload_password() + + if not directory: + directory = self.upload_directory or self._upload_directory + + try: + session = ftplib.FTP(url, user, password, timeout=15) + if not session: + raise Exception("connection failed, did you set a user and " + "password?") + session.cwd(directory) + except socket.timeout: + raise Exception("timeout hit while connecting to %s" % url) + except socket.gaierror: + raise Exception("unable to connect to %s" % url) + except ftplib.error_perm as err: + errno = str(err).split()[0] + if errno == '503': + raise Exception("could not login as '%s'" % user) + if errno == '530': + raise Exception("invalid password for user '%s'" % user) + if errno == '550': + raise Exception("could not set upload directory to %s" + % directory) + raise Exception("error trying to establish session: %s" + % str(err)) + + try: + with open(self.upload_archive, 'rb') as _arcfile: + session.storbinary( + "STOR %s" % self.upload_archive.split('/')[-1], + _arcfile + ) + session.quit() + return True + except IOError: + raise Exception("could not open archive file") + + def set_sos_prefix(self): + """If sosreport commands need to always be prefixed with something, + for example running in a specific container image, then it should be + defined here. + + If no prefix should be set, return an empty string instead of None. + """ + return '' + + def set_cleanup_cmd(self): + """If a host requires additional cleanup, the command should be set and + returned here + """ + return '' + + def create_sos_container(self): + """Returns the command that will create the container that will be + used for running commands inside a container on hosts that require it. + + This will use the container runtime defined for the host type to + launch a container. From there, we use the defined runtime to exec into + the container's namespace. + """ + return '' + + def restart_sos_container(self): + """Restarts the container created for sos collect if it has stopped. + + This is called immediately after create_sos_container() as the command + to create the container will exit and the container will stop. For + current container runtimes, subsequently starting the container will + default to opening a bash shell in the container to keep it running, + thus allowing us to exec into it again. + """ + return "%s start %s" % (self.container_runtime, + self.sos_container_name) + + def format_container_command(self, cmd): + """Returns the command that allows us to exec into the created + container for sos collect. + + :param cmd: The command to run in the sos container + :type cmd: ``str`` + + :returns: The command to execute to run `cmd` in the container + :rtype: ``str`` + """ + if self.container_runtime: + return '%s exec %s %s' % (self.container_runtime, + self.sos_container_name, + cmd) + else: + return cmd diff -Nru sosreport-4.0/sos/policies/distros/redhat.py sosreport-4.1/sos/policies/distros/redhat.py --- sosreport-4.0/sos/policies/distros/redhat.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/policies/distros/redhat.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,526 @@ +# Copyright (C) Steve Conklin + +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +import os +import sys +import re + +from sos.report.plugins import RedHatPlugin +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.package_managers.rpm import RpmPackageManager +from sos import _sos as _ + + +OS_RELEASE = "/etc/os-release" +RHEL_RELEASE_STR = "Red Hat Enterprise Linux" +ATOMIC_RELEASE_STR = "Atomic" + + +class RedHatPolicy(LinuxPolicy): + distro = "Red Hat" + vendor = "Red Hat" + vendor_urls = [ + ('Distribution Website', 'https://www.redhat.com/'), + ('Commercial Support', 'https://www.access.redhat.com/') + ] + _redhat_release = '/etc/redhat-release' + _tmp_dir = "/var/tmp" + _in_container = False + _host_sysroot = '/' + default_scl_prefix = '/opt/rh' + name_pattern = 'friendly' + upload_url = 'dropbox.redhat.com' + upload_user = 'anonymous' + upload_directory = '/incoming' + default_container_runtime = 'podman' + sos_pkg_name = 'sos' + sos_bin_path = '/usr/sbin' + + def __init__(self, sysroot=None, init=None, probe_runtime=True, + remote_exec=None): + 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) + + self.valid_subclasses += [RedHatPlugin] + + self.pkgs = self.package_manager.all_pkgs() + + # If rpm query failed, exit + if not self.pkgs: + sys.stderr.write("Could not obtain installed package list") + sys.exit(1) + + self.usrmove = self.check_usrmove(self.pkgs) + + if self.usrmove: + self.PATH = "/usr/sbin:/usr/bin:/root/bin" + else: + self.PATH = "/sbin:/bin:/usr/sbin:/usr/bin:/root/bin" + 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 Red Hat. It must be + overriden by concrete subclasses to return True when running on a + Fedora, RHEL or other Red Hat distribution or False otherwise. + + If `remote` is provided, it should be the contents of a remote host's + os-release, or comparable, file to be used in place of the locally + available one. + """ + 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', '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 = [] + 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 + + +# 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 \ +accordance with the published support policies at:\n + %(vendor_urls)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. +""" + +RH_API_HOST = "https://access.redhat.com" +RH_FTP_HOST = "ftp://dropbox.redhat.com" + + +class RHELPolicy(RedHatPolicy): + distro = RHEL_RELEASE_STR + vendor = "Red Hat" + msg = _("""\ +This command will collect diagnostic and configuration \ +information from this %(distro)s system and installed \ +applications. + +An archive containing the collected information will be \ +generated in %(tmpdir)s and may be provided to a %(vendor)s \ +support representative. +""" + disclaimer_text + "%(vendor_text)s\n") + _upload_url = RH_FTP_HOST + _upload_user = 'anonymous' + _upload_directory = '/incoming' + + def __init__(self, sysroot=None, init=None, probe_runtime=True, + remote_exec=None): + super(RHELPolicy, self).__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) + self.register_presets(RHEL_PRESETS) + + @classmethod + def check(cls, remote=''): + """Test to see if the running host is a RHEL installation. + + Checks for the presence of the "Red Hat Enterprise Linux" + release string at the beginning of the NAME field in the + `/etc/os-release` file and returns ``True`` if it is + found, and ``False`` otherwise. + + :returns: ``True`` if the host is running RHEL or ``False`` + otherwise. + """ + + if remote: + return cls.distro in remote + + if not os.path.exists(OS_RELEASE): + return False + + with open(OS_RELEASE, "r") as f: + for line in f: + if line.startswith("NAME"): + (name, value) = line.split("=") + value = value.strip("\"'") + if value.startswith(cls.distro): + return True + return False + + def prompt_for_upload_user(self): + if self.commons['cmdlineopts'].upload_user: + return + # Not using the default, so don't call this prompt for RHCP + if self.commons['cmdlineopts'].upload_url: + super(RHELPolicy, self).prompt_for_upload_user() + return + if self.case_id and not self.get_upload_user(): + self.upload_user = input(_( + "Enter your Red Hat Customer Portal username (empty to use " + "public dropbox): ") + ) + if not self.upload_user: + self.upload_url = RH_FTP_HOST + self.upload_user = self._upload_user + + def _upload_user_set(self): + user = self.get_upload_user() + return user and (user != 'anonymous') + + def get_upload_url(self): + if self.upload_url: + return self.upload_url + if self.commons['cmdlineopts'].upload_url: + return self.commons['cmdlineopts'].upload_url + # anonymous FTP server should be used as fallback when either: + # - case id is not set, or + # - upload user isn't set AND batch mode prevents to prompt for it + if (not self.case_id) or \ + ((not self._upload_user_set()) and + self.commons['cmdlineopts'].batch): + self.upload_user = self._upload_user + if self.upload_directory is None: + self.upload_directory = self._upload_directory + self.upload_password = None + return RH_FTP_HOST + else: + rh_case_api = "/hydra/rest/cases/%s/attachments" + return RH_API_HOST + rh_case_api % self.case_id + + def _get_upload_headers(self): + if self.get_upload_url().startswith(RH_API_HOST): + return {'isPrivate': 'false', 'cache-control': 'no-cache'} + return {} + + def get_upload_url_string(self): + if self.get_upload_url().startswith(RH_API_HOST): + return "Red Hat Customer Portal" + return self.upload_url or RH_FTP_HOST + + def get_upload_user(self): + # if this is anything other than dropbox, annonymous won't work + if self.upload_url != RH_FTP_HOST: + return os.getenv('SOSUPLOADUSER', None) or self.upload_user + return self._upload_user + + def upload_archive(self, archive): + """Override the base upload_archive to provide for automatic failover + from RHCP failures to the public RH dropbox + """ + try: + uploaded = super(RHELPolicy, self).upload_archive(archive) + except Exception: + uploaded = False + if not uploaded and self.upload_url.startswith(RH_API_HOST): + print("Upload to Red Hat Customer Portal failed. Trying %s" + % RH_FTP_HOST) + self.upload_url = RH_FTP_HOST + uploaded = super(RHELPolicy, self).upload_archive(archive) + return uploaded + + def dist_version(self): + try: + rr = self.package_manager.all_pkgs_by_name_regex("redhat-release*") + pkgname = self.pkgs[rr[0]]["version"] + if pkgname[0] == "4": + return 4 + elif pkgname[0] in ["5Server", "5Client"]: + return 5 + elif pkgname[0] == "6": + return 6 + elif pkgname[0] == "7": + return 7 + elif pkgname[0] == "8": + return 8 + except Exception: + pass + return False + + def probe_preset(self): + # Emergency or rescue mode? + for target in ["rescue", "emergency"]: + if self.init_system.is_running("%s.target" % target): + return self.find_preset(CB) + # Package based checks + if self.pkg_by_name("satellite-common") is not None: + return self.find_preset(RH_SATELLITE) + if self.pkg_by_name("rhosp-release") is not None: + return self.find_preset(RHOSP) + if self.pkg_by_name("cfme") is not None: + return self.find_preset(RH_CFME) + if self.pkg_by_name("ovirt-engine") is not None or \ + self.pkg_by_name("vdsm") is not None: + return self.find_preset(RHV) + + # Vanilla RHEL is default + return self.find_preset(RHEL) + + +class CentOsPolicy(RHELPolicy): + distro = "CentOS" + vendor = "CentOS" + vendor_urls = [('Community Website', 'https://www.centos.org/')] + + +class RedHatAtomicPolicy(RHELPolicy): + distro = "Red Hat Atomic Host" + msg = _("""\ +This command will collect diagnostic and configuration \ +information from this %(distro)s system. + +An archive containing the collected information will be \ +generated in %(tmpdir)s and may be provided to a %(vendor)s \ +support representative. +""" + disclaimer_text + "%(vendor_text)s\n") + + containerzed = True + container_runtime = 'docker' + container_image = 'registry.access.redhat.com/rhel7/support-tools' + sos_path_strip = '/host' + container_version_command = 'rpm -q sos' + + def __init__(self, sysroot=None, init=None, probe_runtime=True, + remote_exec=None): + super(RedHatAtomicPolicy, self).__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) + self.register_presets(ATOMIC_PRESETS) + + @classmethod + def check(cls, remote=''): + + if remote: + return cls.distro in remote + + atomic = False + if ENV_HOST_SYSROOT not in os.environ: + return atomic + host_release = os.environ[ENV_HOST_SYSROOT] + cls._redhat_release + if not os.path.exists(host_release): + return False + try: + for line in open(host_release, "r").read().splitlines(): + atomic |= ATOMIC_RELEASE_STR in line + except IOError: + pass + return atomic + + def probe_preset(self): + if self.pkg_by_name('atomic-openshift'): + return self.find_preset(RHOCP) + + return self.find_preset(ATOMIC) + + def create_sos_container(self): + _cmd = ("{runtime} run -di --name {name} --privileged --ipc=host" + " --net=host --pid=host -e HOST=/host -e NAME={name} -e " + "IMAGE={image} -v /run:/run -v /var/log:/var/log -v " + "/etc/machine-id:/etc/machine-id -v " + "/etc/localtime:/etc/localtime -v /:/host {image}") + return _cmd.format(runtime=self.container_runtime, + name=self.sos_container_name, + image=self.container_image) + + def set_cleanup_cmd(self): + return 'docker rm --force sos-collector-tmp' + + +class RedHatCoreOSPolicy(RHELPolicy): + distro = "Red Hat CoreOS" + msg = _("""\ +This command will collect diagnostic and configuration \ +information from this %(distro)s system. + +An archive containing the collected information will be \ +generated in %(tmpdir)s and may be provided to a %(vendor)s \ +support representative. +""" + disclaimer_text + "%(vendor_text)s\n") + + containerized = True + container_runtime = 'podman' + container_image = 'registry.redhat.io/rhel8/support-tools' + sos_path_strip = '/host' + container_version_command = 'rpm -q sos' + + def __init__(self, sysroot=None, init=None, probe_runtime=True, + remote_exec=None): + super(RedHatCoreOSPolicy, self).__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) + + @classmethod + def check(cls, remote=''): + + if remote: + return 'CoreOS' in remote + + coreos = False + if ENV_HOST_SYSROOT not in os.environ: + return coreos + host_release = os.environ[ENV_HOST_SYSROOT] + cls._redhat_release + try: + for line in open(host_release, 'r').read().splitlines(): + coreos |= 'Red Hat Enterprise Linux CoreOS' in line + except IOError: + pass + return coreos + + def probe_preset(self): + # As of the creation of this policy, RHCOS is only available for + # RH OCP environments. + return self.find_preset(RHOCP) + + def create_sos_container(self): + _cmd = ("{runtime} run -di --name {name} --privileged --ipc=host" + " --net=host --pid=host -e HOST=/host -e NAME={name} -e " + "IMAGE={image} -v /run:/run -v /var/log:/var/log -v " + "/etc/machine-id:/etc/machine-id -v " + "/etc/localtime:/etc/localtime -v /:/host {image}") + return _cmd.format(runtime=self.container_runtime, + name=self.sos_container_name, + image=self.container_image) + + def set_cleanup_cmd(self): + return 'podman rm --force %s' % self.sos_container_name + + +class CentOsAtomicPolicy(RedHatAtomicPolicy): + distro = "CentOS Atomic Host" + vendor = "CentOS" + vendor_urls = [('Community Website', 'https://www.centos.org/')] + + +class FedoraPolicy(RedHatPolicy): + + distro = "Fedora" + vendor = "the Fedora Project" + vendor_urls = [ + ('Community Website', 'https://fedoraproject.org/'), + ('Community Forums', 'https://discussion.fedoraproject.org/') + ] + + def __init__(self, sysroot=None, init=None, probe_runtime=True, + remote_exec=None): + super(FedoraPolicy, self).__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) + + @classmethod + def check(cls, remote=''): + """This method checks to see if we are running on Fedora. It returns + True or False.""" + + if remote: + return cls.distro in remote + + return os.path.isfile('/etc/fedora-release') + + def fedora_version(self): + pkg = self.pkg_by_name("fedora-release") or \ + self.all_pkgs_by_name_regex("fedora-release-.*")[-1] + return int(pkg["version"]) + +# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/policies/distros/suse.py sosreport-4.1/sos/policies/distros/suse.py --- sosreport-4.0/sos/policies/distros/suse.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/policies/distros/suse.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,115 @@ +# Copyright (C) 2015 Red Hat, Inc. Bryn M. Reeves +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +import os +import sys + +from sos.report.plugins import RedHatPlugin, SuSEPlugin +from sos.policies.distros import LinuxPolicy +from sos.policies.package_managers.rpm import RpmPackageManager +from sos import _sos as _ + + +class SuSEPolicy(LinuxPolicy): + distro = "SuSE" + vendor = "SuSE" + vendor_urls = [('Distribution Website', 'https://www.suse.com/')] + _tmp_dir = "/var/tmp" + + def __init__(self, sysroot=None, init=None, probe_runtime=True, + remote_exec=None): + super(SuSEPolicy, self).__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime) + self.valid_subclasses += [SuSEPlugin, RedHatPlugin] + + self.usrmove = False + self.package_manager = RpmPackageManager() + + pkgs = self.package_manager.all_pkgs() + + # If rpm query timed out after timeout duration exit + if not pkgs: + print("Could not obtain installed package list", file=sys.stderr) + sys.exit(1) + + self.PATH = "/usr/sbin:/usr/bin:/root/bin:/sbin" + self.PATH += os.pathsep + "/usr/local/bin" + self.PATH += os.pathsep + "/usr/local/sbin" + self.set_exec_path() + + @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.""" + return False + + 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() + + +class OpenSuSEPolicy(SuSEPolicy): + distro = "OpenSuSE" + vendor = "SuSE" + vendor_urls = [('Community Website', 'https://www.opensuse.org/')] + msg = _("""\ +This command will collect diagnostic and configuration \ +information from this %(distro)s system and installed \ +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 +""") + + def __init__(self, sysroot=None, init=None, probe_runtime=True, + remote_exec=None): + super(OpenSuSEPolicy, self).__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) + + @classmethod + 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') diff -Nru sosreport-4.0/sos/policies/distros/ubuntu.py sosreport-4.1/sos/policies/distros/ubuntu.py --- sosreport-4.0/sos/policies/distros/ubuntu.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/policies/distros/ubuntu.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,81 @@ +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +from sos.report.plugins import UbuntuPlugin +from sos.policies.distros.debian import DebianPolicy + +import os + + +class UbuntuPolicy(DebianPolicy): + distro = "Ubuntu" + vendor = "Canonical" + vendor_urls = [ + ('Community Website', 'https://www.ubuntu.com/'), + ('Commercial Support', 'https://www.canonical.com') + ] + PATH = "/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games" \ + + ":/usr/local/sbin:/usr/local/bin:/snap/bin" + _upload_url = "https://files.support.canonical.com/uploads/" + _upload_user = "ubuntu" + _upload_password = "ubuntu" + _use_https_streaming = True + + def __init__(self, sysroot=None, init=None, probe_runtime=True, + remote_exec=None): + super(UbuntuPolicy, self).__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) + self.valid_subclasses += [UbuntuPlugin] + + @classmethod + def check(cls, remote=''): + """This method checks to see if we are running on Ubuntu. + It returns True or False.""" + + if remote: + return cls.distro in remote + + try: + with open('/etc/lsb-release', 'r') as fp: + return "Ubuntu" in fp.read() + except IOError: + return False + + def dist_version(self): + """ Returns the version stated in DISTRIB_RELEASE + """ + try: + with open('/etc/lsb-release', 'r') as fp: + lines = fp.readlines() + for line in lines: + if "DISTRIB_RELEASE" in line: + return line.split("=")[1].strip() + return False + except IOError: + return False + + def get_upload_https_auth(self): + if self.upload_url.startswith(self._upload_url): + return (self._upload_user, self._upload_password) + else: + return super(UbuntuPolicy, self).get_upload_https_auth() + + def get_upload_url_string(self): + if self.upload_url.startswith(self._upload_url): + return "Canonical Support File Server" + else: + return self.get_upload_url() + + def get_upload_url(self): + if not self.upload_url or self.upload_url.startswith(self._upload_url): + fname = os.path.basename(self.upload_archive) + return self._upload_url + fname + super(UbuntuPolicy, self).get_upload_url() + +# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/policies/ibmkvm.py sosreport-4.1/sos/policies/ibmkvm.py --- sosreport-4.0/sos/policies/ibmkvm.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/policies/ibmkvm.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -# Copyright (C) IBM Corporation, 2015 -# -# Authors: Kamalesh Babulal -# -# This file is part of the sos project: https://github.com/sosreport/sos -# -# This copyrighted material is made available to anyone wishing to use, -# modify, copy, or redistribute it subject to the terms and conditions of -# version 2 of the GNU General Public License. -# -# See the LICENSE file in the source distribution for further information. - -from sos.report.plugins import PowerKVMPlugin, ZKVMPlugin, RedHatPlugin -from sos.policies.redhat import RedHatPolicy - -import os - - -class PowerKVMPolicy(RedHatPolicy): - distro = "PowerKVM" - vendor = "IBM" - vendor_url = "http://www-03.ibm.com/systems/power/software/linux/powerkvm" - - def __init__(self, sysroot=None, init=None, probe_runtime=True, - remote_exec=None): - super(PowerKVMPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime, - remote_exec=remote_exec) - self.valid_subclasses = [PowerKVMPlugin, RedHatPlugin] - - @classmethod - def check(cls, remote=''): - """This method checks to see if we are running on PowerKVM. - It returns True or False.""" - - if remote: - return cls.distro in remote - - return os.path.isfile('/etc/ibm_powerkvm-release') - - def dist_version(self): - try: - with open('/etc/ibm_powerkvm-release', 'r') as fp: - version_string = fp.read() - return version_string[2][0] - except IOError: - return False - - -class ZKVMPolicy(RedHatPolicy): - distro = "IBM Hypervisor" - vendor = "IBM Hypervisor" - vendor_url = "http://www.ibm.com/systems/z/linux/IBMHypervisor/support/" - - def __init__(self, sysroot=None): - super(ZKVMPolicy, self).__init__(sysroot=sysroot) - self.valid_subclasses = [ZKVMPlugin, RedHatPlugin] - - @classmethod - def check(cls, remote=''): - """This method checks to see if we are running on IBM Z KVM. It - returns True or False.""" - - if remote: - return cls.distro in remote - - return os.path.isfile('/etc/base-release') - - def dist_version(self): - try: - with open('/etc/base-release', 'r') as fp: - version_string = fp.read() - return version_string.split(' ', 4)[3][0] - except IOError: - return False - - -# vim: set ts=4 sw=4 diff -Nru sosreport-4.0/sos/policies/__init__.py sosreport-4.1/sos/policies/__init__.py --- sosreport-4.0/sos/policies/__init__.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/policies/__init__.py 2021-02-25 18:46:49.000000000 +0000 @@ -1,38 +1,24 @@ import os -import re import platform import time import json -import fnmatch import tempfile import random import string -from getpass import getpass from pwd import getpwuid -from sos.utilities import (ImporterHelper, - import_module, - is_executable, - shell_out, - sos_get_command_output, - get_human_readable) +from sos.presets import (NO_PRESET, GENERIC_PRESETS, PRESETS_PATH, + PresetDefaults, DESC, NOTE, OPTS) +from sos.policies.package_managers import PackageManager +from sos.utilities import ImporterHelper, import_module, get_human_readable from sos.report.plugins import IndependentPlugin, ExperimentalPlugin from sos.options import SoSOptions from sos import _sos as _ from textwrap import fill -from pipes import quote - -PRESETS_PATH = "/etc/sos/presets.d" - -try: - import requests - REQUESTS_LOADED = True -except ImportError: - REQUESTS_LOADED = False def import_policy(name): - policy_fqname = "sos.policies.%s" % name + policy_fqname = "sos.policies.distros.%s" % name try: return import_module(policy_fqname, Policy) except ImportError: @@ -44,8 +30,8 @@ if 'policy' in cache: return cache.get('policy') - import sos.policies - helper = ImporterHelper(sos.policies) + import sos.policies.distros + helper = ImporterHelper(sos.policies.distros) for module in helper.get_modules(): for policy in import_policy(module): if policy.check(remote=remote_check): @@ -59,690 +45,6 @@ return cache['policy'] -class ContainerRuntime(object): - """Encapsulates a container runtime that provides the ability to plugins to - check runtime status, check for the presence of specific containers, and - to format commands to run in those containers - - :param policy: The loaded policy for the system - :type policy: ``Policy()`` - - :cvar name: The name of the container runtime, e.g. 'podman' - :vartype name: ``str`` - - :cvar containers: A list of containers known to the runtime - :vartype containers: ``list`` - - :cvar images: A list of images known to the runtime - :vartype images: ``list`` - - :cvar binary: The binary command to run for the runtime, must exit within - $PATH - :vartype binary: ``str`` - """ - - name = 'Undefined' - containers = [] - images = [] - volumes = [] - binary = '' - active = False - - def __init__(self, policy=None): - self.policy = policy - self.run_cmd = "%s exec " % self.binary - - def load_container_info(self): - """If this runtime is found to be active, attempt to load information - on the objects existing in the runtime. - """ - self.containers = self.get_containers() - self.images = self.get_images() - self.volumes = self.get_volumes() - - def check_is_active(self): - """Check to see if the container runtime is both present AND active. - - Active in this sense means that the runtime can be used to glean - information about the runtime itself and containers that are running. - - :returns: ``True`` if the runtime is active, else ``False`` - :rtype: ``bool`` - """ - if is_executable(self.binary): - self.active = True - return True - return False - - def get_containers(self, get_all=False): - """Get a list of containers present on the system. - - :param get_all: If set, include stopped containers as well - :type get_all: ``bool`` - """ - containers = [] - _cmd = "%s ps %s" % (self.binary, '-a' if get_all else '') - if self.active: - out = sos_get_command_output(_cmd) - if out['status'] == 0: - for ent in out['output'].splitlines()[1:]: - ent = ent.split() - # takes the form (container_id, container_name) - containers.append((ent[0], ent[-1])) - return containers - - def get_container_by_name(self, name): - """Get the container ID for the container matching the provided - name - - :param name: The name of the container, note this can be a regex - :type name: ``str`` - - :returns: The id of the first container to match `name`, else ``None`` - :rtype: ``str`` - """ - if not self.active or name is None: - return None - for c in self.containers: - if re.match(name, c[1]): - return c[1] - return None - - def get_images(self): - """Get a list of images present on the system - - :returns: A list of 2-tuples containing (image_name, image_id) - :rtype: ``list`` - """ - images = [] - fmt = '{{lower .Repository}}:{{lower .Tag}} {{lower .ID}}' - if self.active: - out = sos_get_command_output("%s images --format '%s'" - % (self.binary, fmt)) - if out['status'] == 0: - for ent in out['output'].splitlines(): - ent = ent.split() - # takes the form (image_name, image_id) - images.append((ent[0], ent[1])) - return images - - def get_volumes(self): - """Get a list of container volumes present on the system - - :returns: A list of volume IDs on the system - :rtype: ``list`` - """ - vols = [] - if self.active: - out = sos_get_command_output("%s volume ls" % self.binary) - if out['status'] == 0: - for ent in out['output'].splitlines()[1:]: - ent = ent.split() - vols.append(ent[-1]) - return vols - - def fmt_container_cmd(self, container, cmd): - """Format a command to run inside a container using the runtime - - :param container: The name or ID of the container in which to run - :type container: ``str`` - - :param cmd: The command to run inside `container` - :type cmd: ``str`` - - :returns: Formatted string to run `cmd` inside `container` - :rtype: ``str`` - """ - return "%s %s %s" % (self.run_cmd, container, quote(cmd)) - - def get_logs_command(self, container): - """Get the command string used to dump container logs from the - runtime - - :param container: The name or ID of the container to get logs for - :type container: ``str`` - - :returns: Formatted runtime command to get logs from `container` - :type: ``str`` - """ - return "%s logs -t %s" % (self.binary, container) - - -class DockerContainerRuntime(ContainerRuntime): - """Runtime class to use for systems running Docker""" - - name = 'docker' - binary = 'docker' - - def check_is_active(self): - # the daemon must be running - if (is_executable('docker') and - self.policy.init_system.is_running('docker')): - self.active = True - return True - return False - - -class PodmanContainerRuntime(ContainerRuntime): - """Runtime class to use for systems running Podman""" - - name = 'podman' - binary = 'podman' - - -class InitSystem(object): - """Encapsulates an init system to provide service-oriented functions to - sos. - - This should be used to query the status of services, such as if they are - enabled or disabled on boot, or if the service is currently running. - - :param init_cmd: The binary used to interact with the init system - :type init_cmd: ``str`` - - :param list_cmd: The list subcmd given to `init_cmd` to list services - :type list_cmd: ``str`` - - :param query_cmd: The query subcmd given to `query_cmd` to query the - status of services - :type query_cmd: ``str`` - - """ - - def __init__(self, init_cmd=None, list_cmd=None, query_cmd=None): - """Initialize a new InitSystem()""" - - self.services = {} - - 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 - - def is_enabled(self, name): - """Check if given service name is enabled - - :param name: The name of the service - :type name: ``str`` - - :returns: ``True`` if the service is enabled, else ``False`` - :rtype: ``bool`` - """ - if self.services and name in self.services: - return self.services[name]['config'] == 'enabled' - return False - - def is_disabled(self, name): - """Check if a given service name is disabled - :param name: The name of the service - :type name: ``str`` - - :returns: ``True`` if the service is disabled, else ``False`` - :rtype: ``bool`` - """ - if self.services and name in self.services: - return self.services[name]['config'] == 'disabled' - return False - - def is_service(self, name): - """Checks if the given service name exists on the system at all, this - does not check for the service status - - :param name: The name of the service - :type name: ``str`` - - :returns: ``True`` if the service exists, else ``False`` - :rtype: ``bool`` - """ - return name in self.services - - def is_running(self, name): - """Checks if the given service name is in a running state. - - This should be overridden by initsystems that subclass InitSystem - - :param name: The name of the service - :type name: ``str`` - - :returns: ``True`` if the service is running, else ``False`` - :rtype: ``bool`` - """ - # This is going to be primarily used in gating if service related - # commands are going to be run or not. Default to always returning - # True when an actual init system is not specified by policy so that - # we don't inadvertantly restrict sosreports on those systems - return True - - def load_all_services(self): - """This loads all services known to the init system into a dict. - The dict should be keyed by the service name, and contain a dict of the - name and service status - - This must be overridden by anything that subclasses `InitSystem` in - order for service methods to function properly - """ - pass - - 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)) - except Exception: - return None - return None - - def parse_query(self, output): - """Parses the output returned by the query command to make a - determination of what the state of the service is - - This should be overriden by anything that subclasses InitSystem - - :param output: The raw output from querying the service with the - configured `query_cmd` - :type output: ``str`` - - :returns: A state for the service, e.g. 'active', 'disabled', etc... - :rtype: ``str`` - """ - return output - - def get_service_names(self, regex): - """Get a list of all services discovered on the system that match the - given regex. - - :param regex: The service name regex to match against - :type regex: ``str`` - """ - reg = re.compile(regex, re.I) - return [s for s in self.services.keys() if reg.match(s)] - - def get_service_status(self, name): - """Get the status for the given service name along with the output - of the query command - - :param name: The name of the service - :type name: ``str`` - - :returns: Service status and query_cmd output from the init system - :rtype: ``dict`` with keys `name`, `status`, and `output` - """ - _default = { - 'name': name, - 'status': 'missing', - 'output': '' - } - if name not in self.services: - return _default - if 'status' in self.services[name]: - # service status has been queried before, return existing info - return self.services[name] - svc = self._query_service(name) - if svc is not None: - self.services[name]['status'] = self.parse_query(svc['output']) - self.services[name]['output'] = svc['output'] - return self.services[name] - return _default - - -class SystemdInit(InitSystem): - """InitSystem abstraction for SystemD systems""" - - def __init__(self): - super(SystemdInit, self).__init__( - init_cmd='systemctl', - list_cmd='list-unit-files --type=service', - query_cmd='status' - ) - self.load_all_services() - - def parse_query(self, output): - for line in output.splitlines(): - if line.strip().startswith('Active:'): - return line.split()[1] - return 'unknown' - - def load_all_services(self): - svcs = shell_out(self.list_cmd).splitlines()[1:] - for line in svcs: - try: - name = line.split('.service')[0] - config = line.split()[1] - self.services[name] = { - 'name': name, - 'config': config - } - except IndexError: - pass - - def is_running(self, name): - svc = self.get_service_status(name) - return svc['status'] == 'active' - - -class PackageManager(object): - """Encapsulates a package manager. If you provide a query_command to the - constructor it should print each package on the system in the following - format:: - - package name|package.version - - You may also subclass this class and provide a get_pkg_list method to - build the list of packages and versions. - - :cvar query_command: The command to use for querying packages - :vartype query_command: ``str`` or ``None`` - - :cvar verify_command: The command to use for verifying packages - :vartype verify_command: ``str`` or ``None`` - - :cvar verify_filter: Optional filter to use for controlling package - verification - :vartype verify_filter: ``str or ``None`` - - :cvar files_command: The command to use for getting file lists for packages - :vartype files_command: ``str`` or ``None`` - - :cvar chroot: Perform a chroot when executing `files_command` - :vartype chroot: ``bool`` - - :cvar remote_exec: If package manager is on a remote system (e.g. for - sos collect), prepend this SSH command to run remotely - :vartype remote_exec: ``str`` or ``None`` - """ - - query_command = None - verify_command = None - verify_filter = None - chroot = None - files = None - - def __init__(self, chroot=None, query_command=None, - verify_command=None, verify_filter=None, - files_command=None, remote_exec=None): - self.packages = {} - self.files = [] - - self.query_command = query_command if query_command else None - self.verify_command = verify_command if verify_command else None - self.verify_filter = verify_filter if verify_filter else None - self.files_command = files_command if files_command else None - - # if needed, append the remote command to these so that this returns - # the remote package details, not local - if remote_exec: - for cmd in ['query_command', 'verify_command', 'files_command']: - if getattr(self, cmd) is not None: - _cmd = getattr(self, cmd) - setattr(self, cmd, "%s %s" % (remote_exec, quote(_cmd))) - - if chroot: - self.chroot = chroot - - def all_pkgs_by_name(self, name): - """ - Get a list of packages that match name. - - :param name: The name of the package - :type name: ``str`` - - :returns: List of all packages matching `name` - :rtype: ``list`` - """ - return fnmatch.filter(self.all_pkgs().keys(), name) - - def all_pkgs_by_name_regex(self, regex_name, flags=0): - """ - Get a list of packages that match regex_name. - - :param regex_name: The regex to use for matching package names against - :type regex_name: ``str`` - - :param flags: Flags for the `re` module when matching `regex_name` - - :returns: All packages matching `regex_name` - :rtype: ``list`` - """ - reg = re.compile(regex_name, flags) - return [pkg for pkg in self.all_pkgs().keys() if reg.match(pkg)] - - def pkg_by_name(self, name): - """ - Get a single package that matches name. - - :param name: The name of the package - :type name: ``str`` - - :returns: The first package that matches `name` - :rtype: ``str`` - """ - pkgmatches = self.all_pkgs_by_name(name) - if (len(pkgmatches) != 0): - return self.all_pkgs_by_name(name)[-1] - else: - return None - - def get_pkg_list(self): - """Returns a dictionary of packages in the following - format:: - - {'package_name': {'name': 'package_name', - 'version': 'major.minor.version'}} - - """ - if self.query_command: - cmd = self.query_command - pkg_list = shell_out( - cmd, timeout=0, chroot=self.chroot - ).splitlines() - - for pkg in pkg_list: - if '|' not in pkg: - continue - elif pkg.count("|") == 1: - name, version = pkg.split("|") - release = None - elif pkg.count("|") == 2: - name, version, release = pkg.split("|") - self.packages[name] = { - 'name': name, - 'version': version.split(".") - } - release = release if release else None - self.packages[name]['release'] = release - - return self.packages - - def pkg_version(self, pkg): - """Returns the entry in self.packages for pkg if it exists - - :param pkg: The name of the package - :type pkg: ``str`` - - :returns: Package name and version, if package exists - :rtype: ``dict`` if found, else ``None`` - """ - pkgs = self.all_pkgs() - if pkg in pkgs: - return pkgs[pkg] - return None - - def all_pkgs(self): - """ - Get a list of all packages. - - :returns: All packages, with name and version, installed on the system - :rtype: ``dict`` - """ - if not self.packages: - self.packages = self.get_pkg_list() - return self.packages - - def pkg_nvra(self, pkg): - """Get the name, version, release, and architecture for a package - - :param pkg: The name of the package - :type pkg: ``str`` - - :returns: name, version, release, and arch of the package - :rtype: ``tuple`` - """ - fields = pkg.split("-") - version, release, arch = fields[-3:] - name = "-".join(fields[:-3]) - return (name, version, release, arch) - - def all_files(self): - """ - Get a list of files known by the package manager - - :returns: All files known by the package manager - :rtype: ``list`` - """ - if self.files_command and not self.files: - cmd = self.files_command - files = shell_out(cmd, timeout=0, chroot=self.chroot) - self.files = files.splitlines() - return self.files - - def build_verify_command(self, packages): - """build_verify_command(self, packages) -> str - Generate a command to verify the list of packages given - in ``packages`` using the native package manager's - verification tool. - - The command to be executed is returned as a string that - may be passed to a command execution routine (for e.g. - ``sos_get_command_output()``. - - :param packages: a string, or a list of strings giving - package names to be verified. - :returns: a string containing an executable command - that will perform verification of the given - packages. - :rtype: str or ``NoneType`` - """ - if not self.verify_command: - return None - - # The re.match(pkg) used by all_pkgs_by_name_regex() may return - # an empty list (`[[]]`) when no package matches: avoid building - # an rpm -V command line with the empty string as the package - # list in this case. - by_regex = self.all_pkgs_by_name_regex - verify_list = filter(None, map(by_regex, packages)) - - # No packages after regex match? - if not verify_list: - return None - - verify_packages = "" - for package_list in verify_list: - for package in package_list: - if any([f in package for f in self.verify_filter]): - continue - if len(verify_packages): - verify_packages += " " - verify_packages += package - return self.verify_command + " " + verify_packages - - -#: Constants for on-disk preset fields -DESC = "desc" -NOTE = "note" -OPTS = "args" - - -class PresetDefaults(object): - """Preset command line defaults to allow for quick reference to sets of - commonly used options - - :param name: The name of the new preset - :type name: ``str`` - - :param desc: A description for the new preset - :type desc: ``str`` - - :param note: Note for the new preset - :type note: ``str`` - - :param opts: Options set for the new preset - :type opts: ``SoSOptions`` - """ - #: Preset name, used for selection - name = None - #: Human readable preset description - desc = None - #: Notes on preset behaviour - note = None - #: Options set for this preset - opts = SoSOptions() - - #: ``True`` if this preset if built-in or ``False`` otherwise. - builtin = True - - def __str__(self): - """Return a human readable string representation of this - ``PresetDefaults`` object. - """ - return ("name=%s desc=%s note=%s opts=(%s)" % - (self.name, self.desc, self.note, str(self.opts))) - - def __repr__(self): - """Return a machine readable string representation of this - ``PresetDefaults`` object. - """ - return ("PresetDefaults(name='%s' desc='%s' note='%s' opts=(%s)" % - (self.name, self.desc, self.note, repr(self.opts))) - - def __init__(self, name="", desc="", note=None, opts=SoSOptions()): - """Initialise a new ``PresetDefaults`` object with the specified - arguments. - - :returns: The newly initialised ``PresetDefaults`` - """ - self.name = name - self.desc = desc - self.note = note - self.opts = opts - - def write(self, presets_path): - """Write this preset to disk in JSON notation. - - :param presets_path: the directory where the preset will be written - :type presets_path: ``str`` - """ - if self.builtin: - raise TypeError("Cannot write built-in preset") - - # Make dictionaries of PresetDefaults values - odict = self.opts.dict() - pdict = {self.name: {DESC: self.desc, NOTE: self.note, OPTS: odict}} - - if not os.path.exists(presets_path): - os.makedirs(presets_path, mode=0o755) - - with open(os.path.join(presets_path, self.name), "w") as pfile: - json.dump(pdict, pfile) - - def delete(self, presets_path): - """Delete a preset from disk - - :param presets_path: the directory where the preset is saved - :type presets_path: ``str`` - """ - os.unlink(os.path.join(presets_path, self.name)) - - -NO_PRESET = 'none' -NO_PRESET_DESC = 'Do not load a preset' -NO_PRESET_NOTE = 'Use to disable automatically loaded presets' - -GENERIC_PRESETS = { - NO_PRESET: PresetDefaults(name=NO_PRESET, desc=NO_PRESET_DESC, - note=NO_PRESET_NOTE, opts=SoSOptions()) - } - - class Policy(object): """Policies represent distributions that sos supports, and define the way in which sos behaves on those distributions. A policy should define at @@ -767,8 +69,9 @@ :cvar vendor: The name of the vendor producing the distribution :vartype vendor: ``str`` - :cvar vendor_url: URL for the vendor's website, or support portal - :vartype vendor_url: ``str`` + :cvar vendor_urls: List of URLs for the vendor's website, or support portal + :vartype vendor_urls: ``list`` of ``tuples`` formatted + ``(``description``, ``url``)`` :cvar vendor_text: Additional text to add to the banner message :vartype vendor_text: ``str`` @@ -786,7 +89,7 @@ For more information on %(vendor)s visit: - %(vendor_url)s + %(vendor_urls)s The generated archive may contain data considered sensitive and its content \ should be reviewed by the originating organization before being passed to \ @@ -799,7 +102,7 @@ distro = "Unknown" vendor = "Unknown" - vendor_url = "http://www.example.com/" + vendor_urls = [('Example URL', "http://www.example.com/")] vendor_text = "" PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" default_scl_prefix = "" @@ -819,26 +122,11 @@ self.case_id = None self.probe_runtime = probe_runtime self.package_manager = PackageManager() - self._valid_subclasses = [] + self.valid_subclasses = [IndependentPlugin] self.set_exec_path() self._host_sysroot = sysroot self.register_presets(GENERIC_PRESETS) - def get_valid_subclasses(self): - return [IndependentPlugin] + self._valid_subclasses - - def set_valid_subclasses(self, subclasses): - self._valid_subclasses = subclasses - - def del_valid_subclasses(self): - del self._valid_subclasses - - valid_subclasses = property(get_valid_subclasses, - set_valid_subclasses, - del_valid_subclasses, - "list of subclasses that this policy can " - "process") - def check(self, remote=''): """ This function is responsible for determining if the underlying system @@ -853,6 +141,34 @@ """ return False + @property + def forbidden_paths(self): + """This property is used to determine the list of forbidden paths + set by the policy. Note that this property will construct a + *cumulative* list based on all subclasses of a given policy. + + :returns: All patterns of policy forbidden paths + :rtype: ``list`` + """ + if not hasattr(self, '_forbidden_paths'): + self._forbidden_paths = [] + for cls in self.__class__.__mro__: + if hasattr(cls, 'set_forbidden_paths'): + self._forbidden_paths.extend(cls.set_forbidden_paths()) + return list(set(self._forbidden_paths)) + + @classmethod + def set_forbidden_paths(cls): + """Use this to *append* policy-specifc forbidden paths that apply to + all plugins. Setting this classmethod on an invidual policy will *not* + override subclass-specific paths + """ + return [ + '*.pyc', + '*.pyo', + '*.swp' + ] + def in_container(self): """Are we running inside a container? @@ -1062,7 +378,7 @@ def get_preferred_hash_name(self): """Returns the string name of the hashlib-supported checksum algorithm to use""" - return "md5" + return "sha256" def display_results(self, archive, directory, checksum, archivestat=None, map_file=None): @@ -1111,7 +427,7 @@ self._print(" " + self.get_preferred_hash_name() + "\t" + checksum) self._print() self._print(_("Please send this file to your support " - "representative.")) + "representative.")) self._print() def _print(self, msg=None, always=False): @@ -1138,7 +454,7 @@ changes_text = "No changes will be made to system configuration." width = 72 _msg = self.msg % {'distro': self.distro, 'vendor': self.vendor, - 'vendor_url': self.vendor_url, + 'vendor_urls': self._fmt_vendor_urls(), 'vendor_text': self.vendor_text, 'tmpdir': self.commons['tmpdir'], 'changes_text': changes_text} @@ -1147,6 +463,19 @@ _fmt = _fmt + fill(line, width, replace_whitespace=False) + '\n' return _fmt + def _fmt_vendor_urls(self): + """Formats all items in the ``vendor_urls`` class attr into a usable + string for the banner message. + + :returns: Formatted string of URLS + :rtype: ``str`` + """ + width = max([len(v[0]) for v in self.vendor_urls]) + return "\n".join("\t{desc:<{width}} : {url}".format( + desc=u[0], width=width, url=u[1]) + for u in self.vendor_urls + ) + def register_presets(self, presets, replace=False): """Add new presets to this policy object. @@ -1260,503 +589,4 @@ return self.msg % {'distro': self.system} -class LinuxPolicy(Policy): - """This policy is meant to be an abc class that provides common - implementations used in Linux distros""" - - distro = "Linux" - vendor = "None" - PATH = "/bin:/sbin:/usr/bin:/usr/sbin" - init = None - # _ prefixed class attrs are used for storing any vendor-defined defaults - # the non-prefixed attrs are used by the upload methods, and will be set - # to the cmdline/config file values, if provided. If not provided, then - # those attrs will be set to the _ prefixed values as a fallback. - # TL;DR Use _upload_* for policy default values, use upload_* when wanting - # to actual use the value in a method/override - _upload_url = None - _upload_directory = '/' - _upload_user = None - _upload_password = None - _use_https_streaming = False - default_container_runtime = 'docker' - _preferred_hash_name = None - upload_url = None - upload_user = None - upload_password = None - # collector-focused class attrs - containerized = False - container_image = None - sos_path_strip = None - sos_pkg_name = None - sos_bin_path = None - sos_container_name = 'sos-collector-tmp' - container_version_command = None - - def __init__(self, sysroot=None, init=None, probe_runtime=True): - super(LinuxPolicy, self).__init__(sysroot=sysroot, - probe_runtime=probe_runtime) - self.init_kernel_modules() - - if init is not None: - self.init_system = init - elif os.path.isdir("/run/systemd/system/"): - self.init_system = SystemdInit() - else: - self.init_system = InitSystem() - - self.runtimes = {} - if self.probe_runtime: - _crun = [ - PodmanContainerRuntime(policy=self), - DockerContainerRuntime(policy=self) - ] - for runtime in _crun: - if runtime.check_is_active(): - self.runtimes[runtime.name] = runtime - if runtime.name == self.default_container_runtime: - self.runtimes['default'] = self.runtimes[runtime.name] - self.runtimes[runtime.name].load_container_info() - - if self.runtimes and 'default' not in self.runtimes.keys(): - # still allow plugins to query a runtime present on the system - # even if that is not the policy default one - idx = list(self.runtimes.keys()) - self.runtimes['default'] = self.runtimes[idx[0]] - - def get_preferred_hash_name(self): - - if self._preferred_hash_name: - return self._preferred_hash_name - - checksum = "md5" - try: - fp = open("/proc/sys/crypto/fips_enabled", "r") - except IOError: - self._preferred_hash_name = checksum - return checksum - - fips_enabled = fp.read() - if fips_enabled.find("1") >= 0: - checksum = "sha256" - fp.close() - self._preferred_hash_name = checksum - return checksum - - def default_runlevel(self): - try: - with open("/etc/inittab") as fp: - pattern = r"id:(\d{1}):initdefault:" - text = fp.read() - return int(re.findall(pattern, text)[0]) - except (IndexError, IOError): - return 3 - - def kernel_version(self): - return self.release - - def host_name(self): - return self.hostname - - def is_kernel_smp(self): - return self.smp - - def get_arch(self): - return self.machine - - def get_local_name(self): - """Returns the name usd in the pre_work step""" - return self.host_name() - - def sanitize_filename(self, name): - return re.sub(r"[^-a-z,A-Z.0-9]", "", name) - - def init_kernel_modules(self): - """Obtain a list of loaded kernel modules to reference later for plugin - enablement and SoSPredicate checks - """ - lines = shell_out("lsmod", timeout=0).splitlines() - self.kernel_mods = [line.split()[0].strip() for line in lines] - - def pre_work(self): - # this method will be called before the gathering begins - - cmdline_opts = self.commons['cmdlineopts'] - caseid = cmdline_opts.case_id if cmdline_opts.case_id else "" - - # Set the cmdline settings to the class attrs that are referenced later - # The policy default '_' prefixed versions of these are untouched to - # allow fallback - self.upload_url = cmdline_opts.upload_url - self.upload_user = cmdline_opts.upload_user - self.upload_directory = cmdline_opts.upload_directory - self.upload_password = cmdline_opts.upload_pass - - if not cmdline_opts.batch and not \ - cmdline_opts.quiet: - try: - if caseid: - self.case_id = caseid - else: - self.case_id = input(_("Please enter the case id " - "that you are generating this " - "report for [%s]: ") % caseid) - # Policies will need to handle the prompts for user information - if cmdline_opts.upload or self.upload_url: - self.prompt_for_upload_user() - self.prompt_for_upload_password() - self._print() - except KeyboardInterrupt: - self._print() - raise - - if cmdline_opts.case_id: - self.case_id = cmdline_opts.case_id - - return - - def prompt_for_upload_user(self): - """Should be overridden by policies to determine if a user needs to - be provided or not - """ - if not self.upload_user and not self._upload_user: - msg = "Please provide upload user for %s: " % self.get_upload_url() - self.upload_user = input(_(msg)) - - def prompt_for_upload_password(self): - """Should be overridden by policies to determine if a password needs to - be provided for upload or not - """ - if ((not self.upload_password and not self._upload_password) and - self.upload_user): - msg = ( - "Please provide the upload password for %s: " - % self.upload_user - ) - self.upload_password = getpass(msg) - - def upload_archive(self, archive): - """ - Entry point for sos attempts to upload the generated archive to a - policy or user specified location. - - Curerntly there is support for HTTPS, SFTP, and FTP. HTTPS uploads are - preferred for policy-defined defaults. - - Policies that need to override uploading methods should override the - respective upload_https(), upload_sftp(), and/or upload_ftp() methods - and should NOT override this method. - - :param archive: The archive filepath to use for upload - :type archive: ``str`` - - In order to enable this for a policy, that policy needs to implement - the following: - - Required Class Attrs - - :_upload_url: The default location to use. Note these MUST include - protocol header - :_upload_user: Default username, if any else None - :_upload_password: Default password, if any else None - :_use_https_streaming: Set to True if the HTTPS endpoint supports - streaming data - - The following Class Attrs may optionally be overidden by the Policy - - :_upload_directory: Default FTP server directory, if any - - - The following methods may be overridden by ``Policy`` as needed - - `prompt_for_upload_user()` - Determines if sos should prompt for a username or not. - - `get_upload_user()` - Determines if the default or a different username should be used - - `get_upload_https_auth()` - Format authentication data for HTTPS uploads - - `get_upload_url_string()` - Print a more human-friendly string than vendor URLs - """ - self.upload_archive = archive - self.upload_url = self.get_upload_url() - if not self.upload_url: - raise Exception("No upload destination provided by policy or by " - "--upload-url") - upload_func = self._determine_upload_type() - print(_("Attempting upload to %s" % self.get_upload_url_string())) - return upload_func() - - def _determine_upload_type(self): - """Based on the url provided, determine what type of upload to attempt. - - Note that this requires users to provide a FQDN address, such as - https://myvendor.com/api or ftp://myvendor.com instead of - myvendor.com/api or myvendor.com - """ - prots = { - 'ftp': self.upload_ftp, - 'sftp': self.upload_sftp, - 'https': self.upload_https - } - if '://' not in self.upload_url: - raise Exception("Must provide protocol in upload URL") - prot, url = self.upload_url.split('://') - if prot not in prots.keys(): - raise Exception("Unsupported or unrecognized protocol: %s" % prot) - return prots[prot] - - def get_upload_https_auth(self, user=None, password=None): - """Formats the user/password credentials using basic auth - - :param user: The username for upload - :type user: ``str`` - - :param password: Password for `user` to use for upload - :type password: ``str`` - - :returns: The user/password auth suitable for use in reqests calls - :rtype: ``requests.auth.HTTPBasicAuth()`` - """ - if not user: - user = self.get_upload_user() - if not password: - password = self.get_upload_password() - - return requests.auth.HTTPBasicAuth(user, password) - - def get_upload_url(self): - """Helper function to determine if we should use the policy default - upload url or one provided by the user - - :returns: The URL to use for upload - :rtype: ``str`` - """ - return self.upload_url or self._upload_url - - def get_upload_url_string(self): - """Used by distro policies to potentially change the string used to - report upload location from the URL to a more human-friendly string - """ - return self.get_upload_url() - - def get_upload_user(self): - """Helper function to determine if we should use the policy default - upload user or one provided by the user - - :returns: The username to use for upload - :rtype: ``str`` - """ - return self.upload_user or self._upload_user - - def get_upload_password(self): - """Helper function to determine if we should use the policy default - upload password or one provided by the user - - :returns: The password to use for upload - :rtype: ``str`` - """ - return self.upload_password or self._upload_password - - def upload_sftp(self): - """Attempts to upload the archive to an SFTP location. - - Due to the lack of well maintained, secure, and generally widespread - python libraries for SFTP, sos will shell-out to the system's local ssh - installation in order to handle these uploads. - - Do not override this method with one that uses python-paramiko, as the - upstream sos team will reject any PR that includes that dependency. - """ - raise NotImplementedError("SFTP support is not yet implemented") - - def _upload_https_streaming(self, archive): - """If upload_https() needs to use requests.put(), this method is used - to provide streaming functionality - - Policies should override this method instead of the base upload_https() - - :param archive: The open archive file object - """ - return requests.put(self.get_upload_url(), data=archive, - auth=self.get_upload_https_auth()) - - def _get_upload_headers(self): - """Define any needed headers to be passed with the POST request here - """ - return {} - - def _upload_https_no_stream(self, archive): - """If upload_https() needs to use requests.post(), this method is used - to provide non-streaming functionality - - Policies should override this method instead of the base upload_https() - - :param archive: The open archive file object - """ - files = { - 'file': (archive.name.split('/')[-1], archive, - self._get_upload_headers()) - } - return requests.post(self.get_upload_url(), files=files, - auth=self.get_upload_https_auth()) - - def upload_https(self): - """Attempts to upload the archive to an HTTPS location. - - Policies may define whether this upload attempt should use streaming - or non-streaming data by setting the `use_https_streaming` class - attr to True - - :returns: ``True`` if upload is successful - :rtype: ``bool`` - - :raises: ``Exception`` if upload was unsuccessful - """ - if not REQUESTS_LOADED: - raise Exception("Unable to upload due to missing python requests " - "library") - - with open(self.upload_archive, 'rb') as arc: - if not self._use_https_streaming: - r = self._upload_https_no_stream(arc) - else: - r = self._upload_https_streaming(arc) - if r.status_code != 201: - if r.status_code == 401: - raise Exception( - "Authentication failed: invalid user credentials" - ) - raise Exception("POST request returned %s: %s" - % (r.status_code, r.reason)) - return True - - def upload_ftp(self, url=None, directory=None, user=None, password=None): - """Attempts to upload the archive to either the policy defined or user - provided FTP location. - - :param url: The URL to upload to - :type url: ``str`` - - :param directory: The directory on the FTP server to write to - :type directory: ``str`` or ``None`` - - :param user: The user to authenticate with - :type user: ``str`` - - :param password: The password to use for `user` - :type password: ``str`` - - :returns: ``True`` if upload is successful - :rtype: ``bool`` - - :raises: ``Exception`` if upload in unsuccessful - """ - try: - import ftplib - import socket - except ImportError: - # socket is part of the standard library, should only fail here on - # ftplib - raise Exception("missing python ftplib library") - - if not url: - url = self.get_upload_url() - if url is None: - raise Exception("no FTP server specified by policy, use --upload-" - "url to specify a location") - - url = url.replace('ftp://', '') - - if not user: - user = self.get_upload_user() - - if not password: - password = self.get_upload_password() - - if not directory: - directory = self._upload_directory - - try: - session = ftplib.FTP(url, user, password) - session.cwd(directory) - except socket.gaierror: - raise Exception("unable to connect to %s" % url) - except ftplib.error_perm as err: - errno = str(err).split()[0] - if errno == 503: - raise Exception("could not login as '%s'" % user) - if errno == 550: - raise Exception("could not set upload directory to %s" - % directory) - - try: - with open(self.upload_archive, 'rb') as _arcfile: - session.storbinary( - "STOR %s" % self.upload_archive.split('/')[-1], - _arcfile - ) - session.quit() - return True - except IOError: - raise Exception("could not open archive file") - - def set_sos_prefix(self): - """If sosreport commands need to always be prefixed with something, - for example running in a specific container image, then it should be - defined here. - - If no prefix should be set, return an empty string instead of None. - """ - return '' - - def set_cleanup_cmd(self): - """If a host requires additional cleanup, the command should be set and - returned here - """ - return '' - - def create_sos_container(self): - """Returns the command that will create the container that will be - used for running commands inside a container on hosts that require it. - - This will use the container runtime defined for the host type to - launch a container. From there, we use the defined runtime to exec into - the container's namespace. - """ - return '' - - def restart_sos_container(self): - """Restarts the container created for sos collect if it has stopped. - - This is called immediately after create_sos_container() as the command - to create the container will exit and the container will stop. For - current container runtimes, subsequently starting the container will - default to opening a bash shell in the container to keep it running, - thus allowing us to exec into it again. - """ - return "%s start %s" % (self.container_runtime, - self.sos_container_name) - - def format_container_command(self, cmd): - """Returns the command that allows us to exec into the created - container for sos collect. - - :param cmd: The command to run in the sos container - :type cmd: ``str`` - - :returns: The command to execute to run `cmd` in the container - :rtype: ``str`` - """ - if self.container_runtime: - return '%s exec %s %s' % (self.container_runtime, - self.sos_container_name, - cmd) - else: - return cmd - - # vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/policies/init_systems/__init__.py sosreport-4.1/sos/policies/init_systems/__init__.py --- sosreport-4.0/sos/policies/init_systems/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/policies/init_systems/__init__.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,169 @@ +# Copyright (C) 2020 Red Hat, Inc., Jake Hunsaker + +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +import re +from sos.utilities import sos_get_command_output + + +class InitSystem(): + """Encapsulates an init system to provide service-oriented functions to + sos. + + This should be used to query the status of services, such as if they are + enabled or disabled on boot, or if the service is currently running. + + :param init_cmd: The binary used to interact with the init system + :type init_cmd: ``str`` + + :param list_cmd: The list subcmd given to `init_cmd` to list services + :type list_cmd: ``str`` + + :param query_cmd: The query subcmd given to `query_cmd` to query the + status of services + :type query_cmd: ``str`` + + """ + + def __init__(self, init_cmd=None, list_cmd=None, query_cmd=None): + """Initialize a new InitSystem()""" + + self.services = {} + + 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 + + def is_enabled(self, name): + """Check if given service name is enabled + + :param name: The name of the service + :type name: ``str`` + + :returns: ``True`` if the service is enabled, else ``False`` + :rtype: ``bool`` + """ + if self.services and name in self.services: + return self.services[name]['config'] == 'enabled' + return False + + def is_disabled(self, name): + """Check if a given service name is disabled + :param name: The name of the service + :type name: ``str`` + + :returns: ``True`` if the service is disabled, else ``False`` + :rtype: ``bool`` + """ + if self.services and name in self.services: + return self.services[name]['config'] == 'disabled' + return False + + def is_service(self, name): + """Checks if the given service name exists on the system at all, this + does not check for the service status + + :param name: The name of the service + :type name: ``str`` + + :returns: ``True`` if the service exists, else ``False`` + :rtype: ``bool`` + """ + return name in self.services + + def is_running(self, name): + """Checks if the given service name is in a running state. + + This should be overridden by initsystems that subclass InitSystem + + :param name: The name of the service + :type name: ``str`` + + :returns: ``True`` if the service is running, else ``False`` + :rtype: ``bool`` + """ + # This is going to be primarily used in gating if service related + # commands are going to be run or not. Default to always returning + # True when an actual init system is not specified by policy so that + # we don't inadvertantly restrict sosreports on those systems + return True + + def load_all_services(self): + """This loads all services known to the init system into a dict. + The dict should be keyed by the service name, and contain a dict of the + name and service status + + This must be overridden by anything that subclasses `InitSystem` in + order for service methods to function properly + """ + pass + + 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)) + except Exception: + return None + return None + + def parse_query(self, output): + """Parses the output returned by the query command to make a + determination of what the state of the service is + + This should be overriden by anything that subclasses InitSystem + + :param output: The raw output from querying the service with the + configured `query_cmd` + :type output: ``str`` + + :returns: A state for the service, e.g. 'active', 'disabled', etc... + :rtype: ``str`` + """ + return output + + def get_service_names(self, regex): + """Get a list of all services discovered on the system that match the + given regex. + + :param regex: The service name regex to match against + :type regex: ``str`` + """ + reg = re.compile(regex, re.I) + return [s for s in self.services.keys() if reg.match(s)] + + def get_service_status(self, name): + """Get the status for the given service name along with the output + of the query command + + :param name: The name of the service + :type name: ``str`` + + :returns: Service status and query_cmd output from the init system + :rtype: ``dict`` with keys `name`, `status`, and `output` + """ + _default = { + 'name': name, + 'status': 'missing', + 'output': '' + } + if name not in self.services: + return _default + if 'status' in self.services[name]: + # service status has been queried before, return existing info + return self.services[name] + svc = self._query_service(name) + if svc is not None: + self.services[name]['status'] = self.parse_query(svc['output']) + self.services[name]['output'] = svc['output'] + return self.services[name] + return _default + + +# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/policies/init_systems/systemd.py sosreport-4.1/sos/policies/init_systems/systemd.py --- sosreport-4.0/sos/policies/init_systems/systemd.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/policies/init_systems/systemd.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,49 @@ +# Copyright (C) 2020 Red Hat, Inc., Jake Hunsaker + +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +from sos.policies.init_systems import InitSystem +from sos.utilities import shell_out + + +class SystemdInit(InitSystem): + """InitSystem abstraction for SystemD systems""" + + def __init__(self): + super(SystemdInit, self).__init__( + init_cmd='systemctl', + list_cmd='list-unit-files --type=service', + query_cmd='status' + ) + self.load_all_services() + + def parse_query(self, output): + for line in output.splitlines(): + if line.strip().startswith('Active:'): + return line.split()[1] + return 'unknown' + + def load_all_services(self): + svcs = shell_out(self.list_cmd).splitlines()[1:] + for line in svcs: + try: + name = line.split('.service')[0] + config = line.split()[1] + self.services[name] = { + 'name': name, + 'config': config + } + except IndexError: + pass + + def is_running(self, name): + svc = self.get_service_status(name) + return svc['status'] == 'active' + +# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/policies/package_managers/dpkg.py sosreport-4.1/sos/policies/package_managers/dpkg.py --- sosreport-4.0/sos/policies/package_managers/dpkg.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/policies/package_managers/dpkg.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,23 @@ +# Copyright 2020 Red Hat, Inc. Jake Hunsaker + +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +from sos.policies.package_managers import PackageManager + + +class DpkgPackageManager(PackageManager): + """Subclass for dpkg-based distrubitons + """ + + query_command = "dpkg-query -W -f='${Package}|${Version}\\n'" + verify_command = "dpkg --verify" + verify_filter = "" + + +# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/policies/package_managers/__init__.py sosreport-4.1/sos/policies/package_managers/__init__.py --- sosreport-4.0/sos/policies/package_managers/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/policies/package_managers/__init__.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,246 @@ +# Copyright 2020 Red Hat, Inc. Jake Hunsaker + +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +import re +import fnmatch + +from sos.utilities import shell_out +from pipes import quote + + +class PackageManager(): + """Encapsulates a package manager. If you provide a query_command to the + constructor it should print each package on the system in the following + format:: + + package name|package.version + + You may also subclass this class and provide a get_pkg_list method to + build the list of packages and versions. + + :cvar query_command: The command to use for querying packages + :vartype query_command: ``str`` or ``None`` + + :cvar verify_command: The command to use for verifying packages + :vartype verify_command: ``str`` or ``None`` + + :cvar verify_filter: Optional filter to use for controlling package + verification + :vartype verify_filter: ``str or ``None`` + + :cvar files_command: The command to use for getting file lists for packages + :vartype files_command: ``str`` or ``None`` + + :cvar chroot: Perform a chroot when executing `files_command` + :vartype chroot: ``bool`` + + :cvar remote_exec: If package manager is on a remote system (e.g. for + sos collect), prepend this SSH command to run remotely + :vartype remote_exec: ``str`` or ``None`` + """ + + query_command = None + verify_command = None + verify_filter = None + files_command = None + chroot = None + files = None + + def __init__(self, chroot=None, query_command=None, + verify_command=None, verify_filter=None, + files_command=None, remote_exec=None): + self.packages = {} + self.files = [] + + self.query_command = query_command or self.query_command + self.verify_command = verify_command or self.verify_command + self.verify_filter = verify_filter or self.verify_filter + self.files_command = files_command or self.files_command + + # if needed, append the remote command to these so that this returns + # the remote package details, not local + if remote_exec: + for cmd in ['query_command', 'verify_command', 'files_command']: + if getattr(self, cmd) is not None: + _cmd = getattr(self, cmd) + setattr(self, cmd, "%s %s" % (remote_exec, quote(_cmd))) + + if chroot: + self.chroot = chroot + + def all_pkgs_by_name(self, name): + """ + Get a list of packages that match name. + + :param name: The name of the package + :type name: ``str`` + + :returns: List of all packages matching `name` + :rtype: ``list`` + """ + return fnmatch.filter(self.all_pkgs().keys(), name) + + def all_pkgs_by_name_regex(self, regex_name, flags=0): + """ + Get a list of packages that match regex_name. + + :param regex_name: The regex to use for matching package names against + :type regex_name: ``str`` + + :param flags: Flags for the `re` module when matching `regex_name` + + :returns: All packages matching `regex_name` + :rtype: ``list`` + """ + reg = re.compile(regex_name, flags) + return [pkg for pkg in self.all_pkgs().keys() if reg.match(pkg)] + + def pkg_by_name(self, name): + """ + Get a single package that matches name. + + :param name: The name of the package + :type name: ``str`` + + :returns: The first package that matches `name` + :rtype: ``str`` + """ + pkgmatches = self.all_pkgs_by_name(name) + if (len(pkgmatches) != 0): + return self.all_pkgs_by_name(name)[-1] + else: + return None + + def get_pkg_list(self): + """Returns a dictionary of packages in the following + format:: + + {'package_name': {'name': 'package_name', + 'version': 'major.minor.version'}} + + """ + if self.query_command: + cmd = self.query_command + pkg_list = shell_out( + cmd, timeout=0, chroot=self.chroot + ).splitlines() + + for pkg in pkg_list: + if '|' not in pkg: + continue + elif pkg.count("|") == 1: + name, version = pkg.split("|") + release = None + elif pkg.count("|") == 2: + name, version, release = pkg.split("|") + self.packages[name] = { + 'name': name, + 'version': version.split(".") + } + release = release if release else None + self.packages[name]['release'] = release + + return self.packages + + def pkg_version(self, pkg): + """Returns the entry in self.packages for pkg if it exists + + :param pkg: The name of the package + :type pkg: ``str`` + + :returns: Package name and version, if package exists + :rtype: ``dict`` if found, else ``None`` + """ + pkgs = self.all_pkgs() + if pkg in pkgs: + return pkgs[pkg] + return None + + def all_pkgs(self): + """ + Get a list of all packages. + + :returns: All packages, with name and version, installed on the system + :rtype: ``dict`` + """ + if not self.packages: + self.packages = self.get_pkg_list() + return self.packages + + def pkg_nvra(self, pkg): + """Get the name, version, release, and architecture for a package + + :param pkg: The name of the package + :type pkg: ``str`` + + :returns: name, version, release, and arch of the package + :rtype: ``tuple`` + """ + fields = pkg.split("-") + version, release, arch = fields[-3:] + name = "-".join(fields[:-3]) + return (name, version, release, arch) + + def all_files(self): + """ + Get a list of files known by the package manager + + :returns: All files known by the package manager + :rtype: ``list`` + """ + if self.files_command and not self.files: + cmd = self.files_command + files = shell_out(cmd, timeout=0, chroot=self.chroot) + self.files = files.splitlines() + return self.files + + def build_verify_command(self, packages): + """build_verify_command(self, packages) -> str + Generate a command to verify the list of packages given + in ``packages`` using the native package manager's + verification tool. + + The command to be executed is returned as a string that + may be passed to a command execution routine (for e.g. + ``sos_get_command_output()``. + + :param packages: a string, or a list of strings giving + package names to be verified. + :returns: a string containing an executable command + that will perform verification of the given + packages. + :rtype: str or ``NoneType`` + """ + if not self.verify_command: + return None + + # The re.match(pkg) used by all_pkgs_by_name_regex() may return + # an empty list (`[[]]`) when no package matches: avoid building + # an rpm -V command line with the empty string as the package + # list in this case. + by_regex = self.all_pkgs_by_name_regex + verify_list = filter(None, map(by_regex, packages)) + + # No packages after regex match? + if not verify_list: + return None + + verify_packages = "" + for package_list in verify_list: + for package in package_list: + if any([f in package for f in self.verify_filter]): + continue + if len(verify_packages): + verify_packages += " " + verify_packages += package + return self.verify_command + " " + verify_packages + + +# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/policies/package_managers/rpm.py sosreport-4.1/sos/policies/package_managers/rpm.py --- sosreport-4.0/sos/policies/package_managers/rpm.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/policies/package_managers/rpm.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,24 @@ +# Copyright 2020 Red Hat, Inc. Jake Hunsaker + +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +from sos.policies.package_managers import PackageManager + + +class RpmPackageManager(PackageManager): + """Package Manager for RPM-based distributions + """ + + query_command = 'rpm -qa --queryformat "%{NAME}|%{VERSION}|%{RELEASE}\\n"' + files_command = 'rpm -qal' + verify_command = 'rpm -V' + verify_filter = ["debuginfo", "-devel"] + + +# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/policies/redhat.py sosreport-4.1/sos/policies/redhat.py --- sosreport-4.0/sos/policies/redhat.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/policies/redhat.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,563 +0,0 @@ -# Copyright (C) Steve Conklin - -# This file is part of the sos project: https://github.com/sosreport/sos -# -# This copyrighted material is made available to anyone wishing to use, -# modify, copy, or redistribute it subject to the terms and conditions of -# version 2 of the GNU General Public License. -# -# See the LICENSE file in the source distribution for further information. - -import os -import sys -import re - -from sos.report.plugins import RedHatPlugin -from sos.policies import LinuxPolicy, PackageManager, PresetDefaults -from sos import _sos as _ -from sos.options import SoSOptions - -OS_RELEASE = "/etc/os-release" - - -class RedHatPolicy(LinuxPolicy): - distro = "Red Hat" - vendor = "Red Hat" - vendor_url = "https://www.redhat.com/" - _redhat_release = '/etc/redhat-release' - _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/rh' - name_pattern = 'friendly' - upload_url = 'dropbox.redhat.com' - upload_user = 'anonymous' - upload_directory = '/incoming' - default_container_runtime = 'podman' - sos_pkg_name = 'sos' - sos_bin_path = '/usr/sbin/sosreport' - - def __init__(self, sysroot=None, init=None, probe_runtime=True, - remote_exec=None): - super(RedHatPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime) - self.ticket_number = "" - 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 = PackageManager(query_command=self._rpmq_cmd, - verify_command=self._rpmv_cmd, - verify_filter=self._rpmv_filter, - files_command=self._rpmql_cmd, - chroot=sysroot, - remote_exec=remote_exec) - - self.valid_subclasses = [RedHatPlugin] - - self.pkgs = self.package_manager.all_pkgs() - - # If rpm query failed, exit - if not self.pkgs: - sys.stderr.write("Could not obtain installed package list") - sys.exit(1) - - self.usrmove = self.check_usrmove(self.pkgs) - - if self.usrmove: - self.PATH = "/usr/sbin:/usr/bin:/root/bin" - else: - self.PATH = "/sbin:/bin:/usr/sbin:/usr/bin:/root/bin" - 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 Red Hat. It must be - overriden by concrete subclasses to return True when running on a - Fedora, RHEL or other Red Hat distribution or False otherwise. - - If `remote` is provided, it should be the contents of a remote host's - os-release, or comparable, file to be used in place of the locally - available one. - """ - 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 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 = [] - 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 - - -# Container environment variables on Red Hat 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) -_cb_profiles = ['boot', 'storage', 'system'] -_cb_plugopts = ['boot.all-images=on', 'rpm.rpmva=on', 'rpm.rpmdb=on'] - -RHEL_RELEASE_STR = "Red Hat Enterprise Linux" - -RHV = "rhv" -RHV_DESC = "Red Hat Virtualization" - -RHEL = "rhel" -RHEL_DESC = RHEL_RELEASE_STR - -RHOSP = "rhosp" -RHOSP_DESC = "Red Hat OpenStack Platform" - -RHOCP = "ocp" -RHOCP_DESC = "OpenShift Container Platform by Red Hat" -RHOSP_OPTS = SoSOptions(plugopts=[ - 'process.lsof=off', - 'networking.ethtool_namespaces=False', - 'networking.namespaces=200']) - -RH_CFME = "cfme" -RH_CFME_DESC = "Red Hat CloudForms" - -RH_SATELLITE = "satellite" -RH_SATELLITE_DESC = "Red Hat Satellite" -SAT_OPTS = SoSOptions(log_size=100, plugopts=['apache.log=on']) - -CB = "cantboot" -CB_DESC = "For use when normal system startup fails" -CB_OPTS = SoSOptions( - verify=True, all_logs=True, profiles=_cb_profiles, - plugopts=_cb_plugopts - ) -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), - RHEL: PresetDefaults(name=RHEL, desc=RHEL_DESC), - RHOSP: PresetDefaults(name=RHOSP, desc=RHOSP_DESC, opts=RHOSP_OPTS), - RHOCP: PresetDefaults(name=RHOCP, desc=RHOCP_DESC, note=NOTE_SIZE_TIME, - opts=_opts_all_logs_verify), - RH_CFME: PresetDefaults(name=RH_CFME, desc=RH_CFME_DESC, note=NOTE_TIME, - opts=_opts_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 Red Hat 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. -""" - -RH_API_HOST = "https://access.redhat.com" -RH_FTP_HOST = "ftp://dropbox.redhat.com" - - -class RHELPolicy(RedHatPolicy): - distro = RHEL_RELEASE_STR - vendor = "Red Hat" - vendor_url = "https://access.redhat.com/support/" - msg = _("""\ -This command will collect diagnostic and configuration \ -information from this %(distro)s system and installed \ -applications. - -An archive containing the collected information will be \ -generated in %(tmpdir)s and may be provided to a %(vendor)s \ -support representative. -""" + disclaimer_text + "%(vendor_text)s\n") - _upload_url = RH_FTP_HOST - _upload_user = 'anonymous' - _upload_directory = '/incoming' - - def __init__(self, sysroot=None, init=None, probe_runtime=True, - remote_exec=None): - super(RHELPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime, - remote_exec=remote_exec) - self.register_presets(rhel_presets) - - @classmethod - def check(cls, remote=''): - """Test to see if the running host is a RHEL installation. - - Checks for the presence of the "Red Hat Enterprise Linux" - release string at the beginning of the NAME field in the - `/etc/os-release` file and returns ``True`` if it is - found, and ``False`` otherwise. - - :returns: ``True`` if the host is running RHEL or ``False`` - otherwise. - """ - - if remote: - return cls.distro in remote - - if not os.path.exists(OS_RELEASE): - return False - - with open(OS_RELEASE, "r") as f: - for line in f: - if line.startswith("NAME"): - (name, value) = line.split("=") - value = value.strip("\"'") - if value.startswith(cls.distro): - return True - return False - - def prompt_for_upload_user(self): - if self.commons['cmdlineopts'].upload_user: - return - # Not using the default, so don't call this prompt for RHCP - if self.commons['cmdlineopts'].upload_url: - super(RHELPolicy, self).prompt_for_upload_user() - return - if self.case_id: - self.upload_user = input(_( - "Enter your Red Hat Customer Portal username (empty to use " - "public dropbox): ") - ) - - def get_upload_url(self): - if self.commons['cmdlineopts'].upload_url: - return self.commons['cmdlineopts'].upload_url - if (not self.case_id or not self.upload_user or not - self.upload_password): - # Cannot use the RHCP. Use anonymous dropbox - self.upload_user = self._upload_user - self.upload_directory = self._upload_directory - self.upload_password = None - return RH_FTP_HOST - else: - rh_case_api = "/hydra/rest/cases/%s/attachments" - return RH_API_HOST + rh_case_api % self.case_id - - def _get_upload_headers(self): - if self.get_upload_url().startswith(RH_API_HOST): - return {'isPrivate': 'false', 'cache-control': 'no-cache'} - return {} - - def get_upload_url_string(self): - if self.get_upload_url().startswith(RH_API_HOST): - return "Red Hat Customer Portal" - return self.upload_url or RH_FTP_HOST - - def get_upload_user(self): - # if this is anything other than dropbox, annonymous won't work - if self.upload_url != RH_FTP_HOST: - return self.upload_user - return self._upload_user - - def dist_version(self): - try: - rr = self.package_manager.all_pkgs_by_name_regex("redhat-release*") - pkgname = self.pkgs[rr[0]]["version"] - if pkgname[0] == "4": - return 4 - elif pkgname[0] in ["5Server", "5Client"]: - return 5 - elif pkgname[0] == "6": - return 6 - elif pkgname[0] == "7": - return 7 - elif pkgname[0] == "8": - return 8 - except Exception: - pass - return False - - def probe_preset(self): - # Emergency or rescue mode? - for target in ["rescue", "emergency"]: - if self.init_system.is_running("%s.target" % target): - return self.find_preset(CB) - # Package based checks - if self.pkg_by_name("satellite-common") is not None: - return self.find_preset(RH_SATELLITE) - if self.pkg_by_name("rhosp-release") is not None: - return self.find_preset(RHOSP) - if self.pkg_by_name("cfme") is not None: - return self.find_preset(RH_CFME) - if self.pkg_by_name("ovirt-engine") is not None or \ - self.pkg_by_name("vdsm") is not None: - return self.find_preset(RHV) - - # Vanilla RHEL is default - return self.find_preset(RHEL) - - -class CentOsPolicy(RHELPolicy): - distro = "CentOS" - vendor = "CentOS" - vendor_url = "https://www.centos.org/" - - -ATOMIC = "atomic" -ATOMIC_RELEASE_STR = "Atomic" -ATOMIC_DESC = "Red Hat Enterprise Linux Atomic Host" - -atomic_presets = { - ATOMIC: PresetDefaults(name=ATOMIC, desc=ATOMIC_DESC, note=NOTE_TIME, - opts=_opts_verify) -} - - -class RedHatAtomicPolicy(RHELPolicy): - distro = "Red Hat Atomic Host" - msg = _("""\ -This command will collect diagnostic and configuration \ -information from this %(distro)s system. - -An archive containing the collected information will be \ -generated in %(tmpdir)s and may be provided to a %(vendor)s \ -support representative. -""" + disclaimer_text + "%(vendor_text)s\n") - - containerzed = True - container_runtime = 'docker' - container_image = 'registry.access.redhat.com/rhel7/support-tools' - sos_path_strip = '/host' - container_version_command = 'rpm -q sos' - - def __init__(self, sysroot=None, init=None, probe_runtime=True, - remote_exec=None): - super(RedHatAtomicPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime, - remote_exec=remote_exec) - self.register_presets(atomic_presets) - - @classmethod - def check(cls, remote=''): - - if remote: - return cls.distro in remote - - atomic = False - if ENV_HOST_SYSROOT not in os.environ: - return atomic - host_release = os.environ[ENV_HOST_SYSROOT] + cls._redhat_release - if not os.path.exists(host_release): - return False - try: - for line in open(host_release, "r").read().splitlines(): - atomic |= ATOMIC_RELEASE_STR in line - except IOError: - pass - return atomic - - def probe_preset(self): - if self.pkg_by_name('atomic-openshift'): - return self.find_preset(RHOCP) - - return self.find_preset(ATOMIC) - - def create_sos_container(self): - _cmd = ("{runtime} run -di --name {name} --privileged --ipc=host" - " --net=host --pid=host -e HOST=/host -e NAME={name} -e " - "IMAGE={image} -v /run:/run -v /var/log:/var/log -v " - "/etc/machine-id:/etc/machine-id -v " - "/etc/localtime:/etc/localtime -v /:/host {image}") - return _cmd.format(runtime=self.container_runtime, - name=self.sos_container_name, - image=self.container_image) - - def set_cleanup_cmd(self): - return 'docker rm --force sos-collector-tmp' - - -class RedHatCoreOSPolicy(RHELPolicy): - distro = "Red Hat CoreOS" - msg = _("""\ -This command will collect diagnostic and configuration \ -information from this %(distro)s system. - -An archive containing the collected information will be \ -generated in %(tmpdir)s and may be provided to a %(vendor)s \ -support representative. -""" + disclaimer_text + "%(vendor_text)s\n") - - containerized = True - container_runtime = 'podman' - container_image = 'registry.redhat.io/rhel8/support-tools' - sos_path_strip = '/host' - container_version_command = 'rpm -q sos' - - def __init__(self, sysroot=None, init=None, probe_runtime=True, - remote_exec=None): - super(RedHatCoreOSPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime, - remote_exec=remote_exec) - - @classmethod - def check(cls, remote=''): - - if remote: - return 'CoreOS' in remote - - coreos = False - if ENV_HOST_SYSROOT not in os.environ: - return coreos - host_release = os.environ[ENV_HOST_SYSROOT] + cls._redhat_release - try: - for line in open(host_release, 'r').read().splitlines(): - coreos |= 'Red Hat Enterprise Linux CoreOS' in line - except IOError: - pass - return coreos - - def probe_preset(self): - # As of the creation of this policy, RHCOS is only available for - # RH OCP environments. - return self.find_preset(RHOCP) - - def create_sos_container(self): - _cmd = ("{runtime} run -di --name {name} --privileged --ipc=host" - " --net=host --pid=host -e HOST=/host -e NAME={name} -e " - "IMAGE={image} -v /run:/run -v /var/log:/var/log -v " - "/etc/machine-id:/etc/machine-id -v " - "/etc/localtime:/etc/localtime -v /:/host {image}") - return _cmd.format(runtime=self.container_runtime, - name=self.sos_container_name, - image=self.container_image) - - def set_cleanup_cmd(self): - return 'podman rm --force %s' % self.sos_container_name - - -class CentOsAtomicPolicy(RedHatAtomicPolicy): - distro = "CentOS Atomic Host" - vendor = "CentOS" - vendor_url = "https://www.centos.org/" - - -class FedoraPolicy(RedHatPolicy): - - distro = "Fedora" - vendor = "the Fedora Project" - vendor_url = "https://fedoraproject.org/" - - def __init__(self, sysroot=None, init=None, probe_runtime=True, - remote_exec=None): - super(FedoraPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime, - remote_exec=remote_exec) - - @classmethod - def check(cls, remote=''): - """This method checks to see if we are running on Fedora. It returns - True or False.""" - - if remote: - return cls.distro in remote - - return os.path.isfile('/etc/fedora-release') - - def fedora_version(self): - pkg = self.pkg_by_name("fedora-release") or \ - self.all_pkgs_by_name_regex("fedora-release-.*")[-1] - return int(pkg["version"]) - -# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/policies/runtimes/docker.py sosreport-4.1/sos/policies/runtimes/docker.py --- sosreport-4.0/sos/policies/runtimes/docker.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/policies/runtimes/docker.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,30 @@ +# Copyright (C) 2020 Red Hat, Inc., Jake Hunsaker + +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +from sos.policies.runtimes import ContainerRuntime +from sos.utilities import is_executable + + +class DockerContainerRuntime(ContainerRuntime): + """Runtime class to use for systems running Docker""" + + name = 'docker' + binary = 'docker' + + def check_is_active(self): + # the daemon must be running + if (is_executable('docker') and + (self.policy.init_system.is_running('docker') or + self.policy.init_system.is_running('snap.docker.dockerd'))): + self.active = True + return True + return False + +# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/policies/runtimes/__init__.py sosreport-4.1/sos/policies/runtimes/__init__.py --- sosreport-4.0/sos/policies/runtimes/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/policies/runtimes/__init__.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,173 @@ +# Copyright (C) 2020 Red Hat, Inc., Jake Hunsaker + +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +import re + +from pipes import quote +from sos.utilities import sos_get_command_output, is_executable + + +class ContainerRuntime(): + """Encapsulates a container runtime that provides the ability to plugins to + check runtime status, check for the presence of specific containers, and + to format commands to run in those containers + + :param policy: The loaded policy for the system + :type policy: ``Policy()`` + + :cvar name: The name of the container runtime, e.g. 'podman' + :vartype name: ``str`` + + :cvar containers: A list of containers known to the runtime + :vartype containers: ``list`` + + :cvar images: A list of images known to the runtime + :vartype images: ``list`` + + :cvar binary: The binary command to run for the runtime, must exit within + $PATH + :vartype binary: ``str`` + """ + + name = 'Undefined' + containers = [] + images = [] + volumes = [] + binary = '' + active = False + + def __init__(self, policy=None): + self.policy = policy + self.run_cmd = "%s exec " % self.binary + + def load_container_info(self): + """If this runtime is found to be active, attempt to load information + on the objects existing in the runtime. + """ + self.containers = self.get_containers() + self.images = self.get_images() + self.volumes = self.get_volumes() + + def check_is_active(self): + """Check to see if the container runtime is both present AND active. + + Active in this sense means that the runtime can be used to glean + information about the runtime itself and containers that are running. + + :returns: ``True`` if the runtime is active, else ``False`` + :rtype: ``bool`` + """ + if is_executable(self.binary): + self.active = True + return True + return False + + def get_containers(self, get_all=False): + """Get a list of containers present on the system. + + :param get_all: If set, include stopped containers as well + :type get_all: ``bool`` + """ + containers = [] + _cmd = "%s ps %s" % (self.binary, '-a' if get_all else '') + if self.active: + out = sos_get_command_output(_cmd) + if out['status'] == 0: + for ent in out['output'].splitlines()[1:]: + ent = ent.split() + # takes the form (container_id, container_name) + containers.append((ent[0], ent[-1])) + return containers + + def get_container_by_name(self, name): + """Get the container ID for the container matching the provided + name + + :param name: The name of the container, note this can be a regex + :type name: ``str`` + + :returns: The id of the first container to match `name`, else ``None`` + :rtype: ``str`` + """ + if not self.active or name is None: + return None + for c in self.containers: + if re.match(name, c[1]): + return c[1] + return None + + def get_images(self): + """Get a list of images present on the system + + :returns: A list of 2-tuples containing (image_name, image_id) + :rtype: ``list`` + """ + images = [] + fmt = '{{lower .Repository}}:{{lower .Tag}} {{lower .ID}}' + if self.active: + out = sos_get_command_output("%s images --format '%s'" + % (self.binary, fmt)) + if out['status'] == 0: + for ent in out['output'].splitlines(): + ent = ent.split() + # takes the form (image_name, image_id) + images.append((ent[0], ent[1])) + return images + + def get_volumes(self): + """Get a list of container volumes present on the system + + :returns: A list of volume IDs on the system + :rtype: ``list`` + """ + vols = [] + if self.active: + out = sos_get_command_output("%s volume ls" % self.binary) + if out['status'] == 0: + for ent in out['output'].splitlines()[1:]: + ent = ent.split() + vols.append(ent[-1]) + return vols + + def fmt_container_cmd(self, container, cmd, quotecmd): + """Format a command to run inside a container using the runtime + + :param container: The name or ID of the container in which to run + :type container: ``str`` + + :param cmd: The command to run inside `container` + :type cmd: ``str`` + + :param quotecmd: Whether the cmd should be quoted. + :type quotecmd: ``bool`` + + :returns: Formatted string to run `cmd` inside `container` + :rtype: ``str`` + """ + if quotecmd: + quoted_cmd = quote(cmd) + else: + quoted_cmd = cmd + return "%s %s %s" % (self.run_cmd, container, quoted_cmd) + + def get_logs_command(self, container): + """Get the command string used to dump container logs from the + runtime + + :param container: The name or ID of the container to get logs for + :type container: ``str`` + + :returns: Formatted runtime command to get logs from `container` + :type: ``str`` + """ + return "%s logs -t %s" % (self.binary, container) + + +# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/policies/runtimes/podman.py sosreport-4.1/sos/policies/runtimes/podman.py --- sosreport-4.0/sos/policies/runtimes/podman.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/policies/runtimes/podman.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,21 @@ +# Copyright (C) 2020 Red Hat, Inc., Jake Hunsaker + +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +from sos.policies.runtimes import ContainerRuntime + + +class PodmanContainerRuntime(ContainerRuntime): + """Runtime class to use for systems running Podman""" + + name = 'podman' + binary = 'podman' + + +# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/policies/suse.py sosreport-4.1/sos/policies/suse.py --- sosreport-4.0/sos/policies/suse.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/policies/suse.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,113 +0,0 @@ -# Copyright (C) 2015 Red Hat, Inc. Bryn M. Reeves -# This file is part of the sos project: https://github.com/sosreport/sos -# -# This copyrighted material is made available to anyone wishing to use, -# modify, copy, or redistribute it subject to the terms and conditions of -# version 2 of the GNU General Public License. -# -# See the LICENSE file in the source distribution for further information. - -import os -import sys - -from sos.report.plugins import RedHatPlugin, SuSEPlugin -from sos.policies import LinuxPolicy, PackageManager -from sos import _sos as _ - - -class SuSEPolicy(LinuxPolicy): - distro = "SuSE" - vendor = "SuSE" - vendor_url = "https://www.suse.com/" - _tmp_dir = "/var/tmp" - - def __init__(self, sysroot=None, init=None, probe_runtime=True, - remote_exec=None): - super(SuSEPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime) - self.ticket_number = "" - self.package_manager = PackageManager( - 'rpm -qa --queryformat "%{NAME}|%{VERSION}\\n"', - remote_exec=remote_exec) - self.valid_subclasses = [SuSEPlugin, RedHatPlugin] - - pkgs = self.package_manager.all_pkgs() - - # If rpm query timed out after timeout duration exit - if not pkgs: - print("Could not obtain installed package list", file=sys.stderr) - sys.exit(1) - - self.PATH = "/usr/sbin:/usr/bin:/root/bin" - self.PATH += os.pathsep + "/usr/local/bin" - self.PATH += os.pathsep + "/usr/local/sbin" - self.set_exec_path() - - @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.""" - return False - - 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() - - -class OpenSuSEPolicy(SuSEPolicy): - distro = "OpenSuSE" - vendor = "SuSE" - vendor_url = "https://www.opensuse.org/" - msg = _("""\ -This command will collect diagnostic and configuration \ -information from this %(distro)s system and installed \ -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 -""") - - def __init__(self, sysroot=None, init=None, probe_runtime=True): - super(OpenSuSEPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime) - - @classmethod - 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-release')) diff -Nru sosreport-4.0/sos/policies/ubuntu.py sosreport-4.1/sos/policies/ubuntu.py --- sosreport-4.0/sos/policies/ubuntu.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/policies/ubuntu.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,70 +0,0 @@ -from sos.report.plugins import UbuntuPlugin, DebianPlugin -from sos.policies.debian import DebianPolicy - -import os - - -class UbuntuPolicy(DebianPolicy): - distro = "Ubuntu" - vendor = "Canonical" - vendor_url = "https://www.ubuntu.com/" - PATH = "/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games" \ - + ":/usr/local/sbin:/usr/local/bin:/snap/bin" - _upload_url = "https://files.support.canonical.com/uploads/" - _upload_user = "ubuntu" - _upload_password = "ubuntu" - _use_https_streaming = True - - def __init__(self, sysroot=None, init=None, probe_runtime=True, - remote_exec=None): - super(UbuntuPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime, - remote_exec=remote_exec) - self.valid_subclasses = [UbuntuPlugin, DebianPlugin] - - @classmethod - def check(cls, remote=''): - """This method checks to see if we are running on Ubuntu. - It returns True or False.""" - - if remote: - return cls.distro in remote - - try: - with open('/etc/lsb-release', 'r') as fp: - return "Ubuntu" in fp.read() - except IOError: - return False - - def dist_version(self): - """ Returns the version stated in DISTRIB_RELEASE - """ - try: - with open('/etc/lsb-release', 'r') as fp: - lines = fp.readlines() - for line in lines: - if "DISTRIB_RELEASE" in line: - return line.split("=")[1].strip() - return False - except IOError: - return False - - def get_upload_https_auth(self): - if self.upload_url.startswith(self._upload_url): - return (self._upload_user, self._upload_password) - else: - return super(UbuntuPolicy, self).get_upload_https_auth() - - def get_upload_url_string(self): - if self.upload_url.startswith(self._upload_url): - return "Canonical Support File Server" - else: - return self.get_upload_url() - - def get_upload_url(self): - if not self.upload_url or self.upload_url.startswith(self._upload_url): - fname = os.path.basename(self.upload_archive) - return self._upload_url + fname - super(UbuntuPolicy, self).get_upload_url() - -# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/presets/__init__.py sosreport-4.1/sos/presets/__init__.py --- sosreport-4.0/sos/presets/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/presets/__init__.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,115 @@ +# Copyright (C) 2020 Red Hat, Inc., Jake Hunsaker + +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +import json +import os + +from sos.options import SoSOptions + +PRESETS_PATH = "/etc/sos/presets.d" + +#: Constants for on-disk preset fields +DESC = "desc" +NOTE = "note" +OPTS = "args" + + +class PresetDefaults(): + """Preset command line defaults to allow for quick reference to sets of + commonly used options + + :param name: The name of the new preset + :type name: ``str`` + + :param desc: A description for the new preset + :type desc: ``str`` + + :param note: Note for the new preset + :type note: ``str`` + + :param opts: Options set for the new preset + :type opts: ``SoSOptions`` + """ + #: Preset name, used for selection + name = None + #: Human readable preset description + desc = None + #: Notes on preset behaviour + note = None + #: Options set for this preset + opts = SoSOptions() + + #: ``True`` if this preset if built-in or ``False`` otherwise. + builtin = True + + def __str__(self): + """Return a human readable string representation of this + ``PresetDefaults`` object. + """ + return ("name=%s desc=%s note=%s opts=(%s)" % + (self.name, self.desc, self.note, str(self.opts))) + + def __repr__(self): + """Return a machine readable string representation of this + ``PresetDefaults`` object. + """ + return ("PresetDefaults(name='%s' desc='%s' note='%s' opts=(%s)" % + (self.name, self.desc, self.note, repr(self.opts))) + + def __init__(self, name="", desc="", note=None, opts=SoSOptions()): + """Initialise a new ``PresetDefaults`` object with the specified + arguments. + + :returns: The newly initialised ``PresetDefaults`` + """ + self.name = name + self.desc = desc + self.note = note + self.opts = opts + + def write(self, presets_path): + """Write this preset to disk in JSON notation. + + :param presets_path: the directory where the preset will be written + :type presets_path: ``str`` + """ + if self.builtin: + raise TypeError("Cannot write built-in preset") + + # Make dictionaries of PresetDefaults values + odict = self.opts.dict() + pdict = {self.name: {DESC: self.desc, NOTE: self.note, OPTS: odict}} + + if not os.path.exists(presets_path): + os.makedirs(presets_path, mode=0o755) + + with open(os.path.join(presets_path, self.name), "w") as pfile: + json.dump(pdict, pfile) + + def delete(self, presets_path): + """Delete a preset from disk + + :param presets_path: the directory where the preset is saved + :type presets_path: ``str`` + """ + os.unlink(os.path.join(presets_path, self.name)) + + +NO_PRESET = 'none' +NO_PRESET_DESC = 'Do not load a preset' +NO_PRESET_NOTE = 'Use to disable automatically loaded presets' + +GENERIC_PRESETS = { + NO_PRESET: PresetDefaults(name=NO_PRESET, desc=NO_PRESET_DESC, + note=NO_PRESET_NOTE, opts=SoSOptions()) +} + + +# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/presets/redhat/__init__.py sosreport-4.1/sos/presets/redhat/__init__.py --- sosreport-4.0/sos/presets/redhat/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/presets/redhat/__init__.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,84 @@ +# Copyright (C) 2020 Red Hat, Inc., Jake Hunsaker + +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +from sos.options import SoSOptions +from sos.presets import PresetDefaults + + +RHEL_RELEASE_STR = "Red Hat Enterprise Linux" + +_opts_verify = SoSOptions(verify=True) +_opts_all_logs = SoSOptions(all_logs=True) +_opts_all_logs_verify = SoSOptions(all_logs=True, verify=True) +_cb_profiles = ['boot', 'storage', 'system'] +_cb_plugopts = ['boot.all-images=on', 'rpm.rpmva=on', 'rpm.rpmdb=on'] + + +RHV = "rhv" +RHV_DESC = "Red Hat Virtualization" + +RHEL = "rhel" +RHEL_DESC = RHEL_RELEASE_STR + +RHOSP = "rhosp" +RHOSP_DESC = "Red Hat OpenStack Platform" + +RHOCP = "ocp" +RHOCP_DESC = "OpenShift Container Platform by Red Hat" +RHOSP_OPTS = SoSOptions(plugopts=[ + 'process.lsof=off', + 'networking.ethtool_namespaces=False', + 'networking.namespaces=200']) + +RH_CFME = "cfme" +RH_CFME_DESC = "Red Hat CloudForms" + +RH_SATELLITE = "satellite" +RH_SATELLITE_DESC = "Red Hat Satellite" +SAT_OPTS = SoSOptions(log_size=100, plugopts=['apache.log=on']) + +CB = "cantboot" +CB_DESC = "For use when normal system startup fails" +CB_OPTS = SoSOptions( + verify=True, all_logs=True, profiles=_cb_profiles, + plugopts=_cb_plugopts + ) +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), + RHEL: PresetDefaults(name=RHEL, desc=RHEL_DESC), + RHOSP: PresetDefaults(name=RHOSP, desc=RHOSP_DESC, opts=RHOSP_OPTS), + RHOCP: PresetDefaults(name=RHOCP, desc=RHOCP_DESC, note=NOTE_SIZE_TIME, + opts=_opts_all_logs_verify), + RH_CFME: PresetDefaults(name=RH_CFME, desc=RH_CFME_DESC, note=NOTE_TIME, + opts=_opts_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) +} + + +ATOMIC = "atomic" +ATOMIC_RELEASE_STR = "Atomic" +ATOMIC_DESC = "Red Hat Enterprise Linux Atomic Host" + +ATOMIC_PRESETS = { + ATOMIC: PresetDefaults(name=ATOMIC, desc=ATOMIC_DESC, note=NOTE_TIME, + opts=_opts_verify) +} + + +# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/report/__init__.py sosreport-4.1/sos/report/__init__.py --- sosreport-4.0/sos/report/__init__.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/__init__.py 2021-02-25 18:46:49.000000000 +0000 @@ -17,7 +17,8 @@ from datetime import datetime import glob import sos.report.plugins -from sos.utilities import ImporterHelper, SoSTimeoutError +from sos.utilities import (ImporterHelper, SoSTimeoutError, + sos_get_command_output) from shutil import rmtree import hashlib from concurrent.futures import ThreadPoolExecutor, TimeoutError @@ -87,6 +88,7 @@ 'experimental': False, 'enable_plugins': [], 'keywords': [], + 'keyword_file': None, 'plugopts': [], 'label': '', 'list_plugins': False, @@ -94,8 +96,10 @@ 'list_profiles': False, 'log_size': 25, 'map_file': '/etc/sos/cleaner/default_mapping', + 'skip_commands': [], + 'skip_files': [], 'skip_plugins': [], - 'noreport': False, + 'no_report': False, 'no_env_vars': False, 'no_postproc': False, 'no_update': False, @@ -107,6 +111,7 @@ 'since': None, 'verify': False, 'allow_system_changes': False, + 'usernames': [], 'upload': False, 'upload_url': None, 'upload_directory': None, @@ -150,6 +155,12 @@ self.opts.merge(self.preset.opts) # re-apply any cmdline overrides to the preset self.opts = self.apply_options_from_cmdline(self.opts) + if hasattr(self.preset.opts, 'verbosity') and \ + self.preset.opts.verbosity > 0: + print('\nWARNING: It is not recommended to set verbosity via the ' + 'preset as it might have\nunforseen consequences for your ' + 'report logs.\n') + self._setup_logging() self._set_directories() @@ -171,6 +182,7 @@ self._exit(1) self._get_hardware_devices() + self._get_namespaces() @classmethod def add_parser_options(cls, parser): @@ -211,7 +223,8 @@ report_grp.add_argument("-e", "--enable-plugins", action="extend", dest="enable_plugins", type=str, help="enable these plugins", default=[]) - report_grp.add_argument("-k", "--plugin-option", action="extend", + report_grp.add_argument("-k", "--plugin-option", "--plugopts", + action="extend", dest="plugopts", type=str, help="plugin options in plugname.option=value " "format (see -l)", default=[]) @@ -236,7 +249,7 @@ dest="skip_plugins", type=str, help="disable these plugins", default=[]) report_grp.add_argument("--no-report", action="store_true", - dest="noreport", default=False, + dest="no_report", default=False, help="disable plaintext/HTML reporting") report_grp.add_argument("--no-env-vars", action="store_true", dest="no_env_vars", default=False, @@ -253,10 +266,17 @@ help="A preset identifier", default="auto") report_grp.add_argument("--plugin-timeout", default=None, help="set a timeout for all plugins") - report_grp.add_argument("-p", "--profile", action="extend", - dest="profiles", type=str, default=[], + report_grp.add_argument("-p", "--profile", "--profiles", + action="extend", dest="profiles", type=str, + default=[], help="enable plugins used by the given " "profiles") + report_grp.add_argument('--skip-commands', default=[], action='extend', + dest='skip_commands', + help="do not execute these commands") + report_grp.add_argument('--skip-files', default=[], action='extend', + dest='skip_files', + help="do not collect these files") report_grp.add_argument("--verify", action="store_true", dest="verify", default=False, help="perform data verification during " @@ -289,7 +309,8 @@ 'Cleaner/Masking Options', 'These options control how data obfuscation is performed' ) - cleaner_grp.add_argument('--clean', '--mask', dest='clean', + cleaner_grp.add_argument('--clean', '--cleaner', '--mask', + dest='clean', default=False, action='store_true', help='Obfuscate sensistive information') cleaner_grp.add_argument('--domains', dest='domains', default=[], @@ -298,6 +319,9 @@ cleaner_grp.add_argument('--keywords', action='extend', default=[], dest='keywords', help='List of keywords to obfuscate') + cleaner_grp.add_argument('--keyword-file', default=None, + dest='keyword_file', + help='Provide a file a keywords to obfuscate') cleaner_grp.add_argument('--no-update', action='store_true', default=False, dest='no_update', help='Do not update the default cleaner map') @@ -305,6 +329,9 @@ default='/etc/sos/cleaner/default_mapping', help=('Provide a previously generated mapping' ' file for obfuscation')) + cleaner_grp.add_argument('--usernames', dest='usernames', default=[], + action='extend', + help='List of usernames to obfuscate') def print_header(self): print("\n%s\n" % _("sosreport (version %s)" % (__version__,))) @@ -350,6 +377,27 @@ self.soslog.error("Could not get block device list: %s" % err) return [] + def _get_namespaces(self): + self.namespaces = { + 'network': self._get_network_namespaces() + } + + def _get_network_namespaces(self): + """Enumerate a list of network namespaces on this system so that + plugins can iterate over them + + Note that stderr is not collected, so no handling of error lines. + """ + out_ns = [] + + ip_netns = sos_get_command_output("ip netns") + if ip_netns['status'] == 0: + for line in ip_netns['output'].splitlines(): + if line.isspace() or line[:1].isspace(): + continue + out_ns.append(line.partition(' ')[0]) + return out_ns + def get_commons(self): return { 'cmddir': self.cmddir, @@ -361,7 +409,8 @@ 'sysroot': self.sysroot, 'verbosity': self.opts.verbosity, 'cmdlineopts': self.opts, - 'devices': self.devices + 'devices': self.devices, + 'namespaces': self.namespaces } def get_temp_file(self): @@ -808,15 +857,6 @@ self.policy.pre_work() try: self.ui_log.info(_(" Setting up archive ...")) - compression_methods = ('auto', 'bzip2', 'gzip', 'xz') - method = self.opts.compression_type - if method not in compression_methods: - compression_list = ', '.join(compression_methods) - self.ui_log.error("") - self.ui_log.error("Invalid compression specified: " + method) - self.ui_log.error("Valid types are: " + compression_list) - self.ui_log.error("") - self._exit(1) self.setup_archive() self._make_archive_paths() return @@ -1334,7 +1374,7 @@ self.collect() if not self.opts.no_env_vars: self.collect_env_vars() - if not self.opts.noreport: + if not self.opts.no_report: self.generate_reports() if not self.opts.no_postproc: self.postproc() diff -Nru sosreport-4.0/sos/report/plugins/anacron.py sosreport-4.1/sos/report/plugins/anacron.py --- sosreport-4.0/sos/report/plugins/anacron.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/anacron.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Anacron(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Anacron(Plugin, IndependentPlugin): short_desc = 'Anacron job scheduling service' diff -Nru sosreport-4.0/sos/report/plugins/apache.py sosreport-4.1/sos/report/plugins/apache.py --- sosreport-4.0/sos/report/plugins/apache.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/apache.py 2021-02-25 18:46:49.000000000 +0000 @@ -29,9 +29,12 @@ "apachectl -t" ]) - # The foreman plugin collects these files with a greater size limit: + # The foreman and pulp plugins collect these files; # do not collect them here to avoid collisions in the archive paths. - self.add_forbidden_path("/var/log/{}*/foreman*".format(self.apachepkg)) + self.add_forbidden_path([ + "/var/log/{}*/foreman*".format(self.apachepkg), + "/var/log/{}*/pulp*".format(self.apachepkg) + ]) class RedHatApache(Apache, RedHatPlugin): diff -Nru sosreport-4.0/sos/report/plugins/apt.py sosreport-4.1/sos/report/plugins/apt.py --- sosreport-4.0/sos/report/plugins/apt.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/apt.py 2021-02-25 18:46:49.000000000 +0000 @@ -20,7 +20,9 @@ def setup(self): self.add_copy_spec([ - "/etc/apt", "/var/log/apt" + "/etc/apt", + "/var/log/apt", + "/var/log/unattended-upgrades" ]) self.add_forbidden_path("/etc/apt/auth.conf") diff -Nru sosreport-4.0/sos/report/plugins/arcconf.py sosreport-4.1/sos/report/plugins/arcconf.py --- sosreport-4.0/sos/report/plugins/arcconf.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/arcconf.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,28 @@ +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + + +# This sosreport plugin is meant for sas adapters. +# This plugin logs inforamtion on each adapter it finds. + +from sos.report.plugins import Plugin, IndependentPlugin + + +class arcconf(Plugin, IndependentPlugin): + + short_desc = 'arcconf Integrated RAID adapter information' + + plugin_name = "arcconf" + commands = ("arcconf",) + + def setup(self): + + # get list of adapters + self.add_cmd_output("arcconf getconfig 1") + +# vim: et ts=4 sw=4 diff -Nru sosreport-4.0/sos/report/plugins/ata.py sosreport-4.1/sos/report/plugins/ata.py --- sosreport-4.0/sos/report/plugins/ata.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/ata.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,11 +6,11 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin +from sos.report.plugins import Plugin, IndependentPlugin import os -class Ata(Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin): +class Ata(Plugin, IndependentPlugin): short_desc = 'ATA and IDE information' diff -Nru sosreport-4.0/sos/report/plugins/auditd.py sosreport-4.1/sos/report/plugins/auditd.py --- sosreport-4.0/sos/report/plugins/auditd.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/auditd.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Auditd(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Auditd(Plugin, IndependentPlugin): short_desc = 'Audit daemon information' @@ -26,7 +26,7 @@ "/etc/audisp/", ]) self.add_cmd_output([ - "ausearch --input-logs -m avc,user_avc -ts today", + "ausearch --input-logs -m avc,user_avc,fanotify -ts today", "auditctl -s", "auditctl -l" ]) diff -Nru sosreport-4.0/sos/report/plugins/azure.py sosreport-4.1/sos/report/plugins/azure.py --- sosreport-4.0/sos/report/plugins/azure.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/azure.py 2021-02-25 18:46:49.000000000 +0000 @@ -27,7 +27,8 @@ "/etc/default/kv-kvp-daemon-init", "/etc/waagent.conf", "/sys/module/hv_netvsc/parameters/ring_size", - "/sys/module/hv_storvsc/parameters/storvsc_ringbuffer_size" + "/sys/module/hv_storvsc/parameters/storvsc_ringbuffer_size", + "/var/lib/AzureEnhancedMonitor" ]) # Adds all files under /var/log/azure to the sosreport diff -Nru sosreport-4.0/sos/report/plugins/bcache.py sosreport-4.1/sos/report/plugins/bcache.py --- sosreport-4.0/sos/report/plugins/bcache.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/bcache.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,56 @@ +# Copyright (C) 2021, Canonical ltd +# Ponnuvel Palaniyappan + +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +from sos.report.plugins import Plugin, IndependentPlugin, SoSPredicate + + +class Bcache(Plugin, IndependentPlugin): + + short_desc = 'Bcache statistics' + + plugin_name = 'bcache' + profiles = ('storage', 'hardware') + files = ('/sys/fs/bcache',) + + def setup(self): + + # Caution: reading /sys/fs/bcache/*/cache0/priority_stats is known + # to degrade performance on old kernels. Needs care if that's ever + # considered for inclusion here. + # see: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1840043 + self.add_forbidden_path([ + '/sys/fs/bcache/*/*/priority_stats', + ]) + + self.add_copy_spec([ + '/sys/block/bcache*/bcache/cache/internal/copy_gc_enabled', + '/sys/block/bcache*/bcache/cache_mode', + '/sys/block/bcache*/bcache/dirty_data', + '/sys/block/bcache*/bcache/io_errors', + '/sys/block/bcache*/bcache/sequential_cutoff', + '/sys/block/bcache*/bcache/stats_hour/bypassed', + '/sys/block/bcache*/bcache/stats_hour/cache_hit_ratio', + '/sys/block/bcache*/bcache/stats_hour/cache_hits', + '/sys/block/bcache*/bcache/stats_hour/cache_misses', + '/sys/block/bcache*/bcache/writeback_percent', + '/sys/fs/bcache/*/average_key_size', + '/sys/fs/bcache/*/bdev*/*', + '/sys/fs/bcache/*/bdev*/stat_*/*', + '/sys/fs/bcache/*/block_size', + '/sys/fs/bcache/*/bucket_size', + '/sys/fs/bcache/*/cache_available_percent', + '/sys/fs/bcache/*/congested_*_threshold_us', + '/sys/fs/bcache/*/internal/*', + '/sys/fs/bcache/*/stats_*/*', + '/sys/fs/bcache/*/tree_depth', + ], pred=SoSPredicate(self, kmods=['bcache'])) + +# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/report/plugins/block.py sosreport-4.1/sos/report/plugins/block.py --- sosreport-4.0/sos/report/plugins/block.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/block.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Block(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Block(Plugin, IndependentPlugin): short_desc = 'Block device information' @@ -28,7 +28,8 @@ "blkid -c /dev/null", "blockdev --report", "ls -lanR /dev", - "ls -lanR /sys/block" + "ls -lanR /sys/block", + "lsblk -O -P", ]) # legacy location for non-/run distributions diff -Nru sosreport-4.0/sos/report/plugins/boot.py sosreport-4.1/sos/report/plugins/boot.py --- sosreport-4.0/sos/report/plugins/boot.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/boot.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,11 +6,11 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin from glob import glob -class Boot(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Boot(Plugin, IndependentPlugin): short_desc = 'Bootloader information' @@ -33,7 +33,8 @@ ]) self.add_cmd_output([ "ls -lanR /boot", - "lsinitrd" + "lsinitrd", + "ls -lanR /sys/firmware", ]) self.add_cmd_output([ diff -Nru sosreport-4.0/sos/report/plugins/btrfs.py sosreport-4.1/sos/report/plugins/btrfs.py --- sosreport-4.0/sos/report/plugins/btrfs.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/btrfs.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Btrfs(Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin): +class Btrfs(Plugin, IndependentPlugin): short_desc = 'Btrfs filesystem' diff -Nru sosreport-4.0/sos/report/plugins/candlepin.py sosreport-4.1/sos/report/plugins/candlepin.py --- sosreport-4.0/sos/report/plugins/candlepin.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/candlepin.py 2021-02-25 18:46:49.000000000 +0000 @@ -93,7 +93,8 @@ a large amount of quoting in sos logs referencing the command being run """ csvformat = "-A -F , -X" if csv else "" - _dbcmd = "psql -h %s -p 5432 -U candlepin -d candlepin %s -c %s" + _dbcmd = "psql --no-password -h %s -p 5432 -U candlepin \ + -d candlepin %s -c %s" return _dbcmd % (self.dbhost, csvformat, quote(query)) def postproc(self): diff -Nru sosreport-4.0/sos/report/plugins/ceph.py sosreport-4.1/sos/report/plugins/ceph.py --- sosreport-4.0/sos/report/plugins/ceph.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/ceph.py 2021-02-25 18:46:49.000000000 +0000 @@ -66,6 +66,7 @@ "ceph quorum_status", "ceph mgr module ls", "ceph mgr metadata", + "ceph balancer status", "ceph osd metadata", "ceph osd erasure-code-profile ls", "ceph report", @@ -95,6 +96,7 @@ "osd perf", "osd blocked-by", "osd pool ls detail", + "osd pool autoscale-status", "osd numa-status", "device ls", "mon dump", @@ -106,6 +108,7 @@ "fs dump", "pg dump", "pg stat", + "time-sync-status", ] self.add_cmd_output([ diff -Nru sosreport-4.0/sos/report/plugins/cifs.py sosreport-4.1/sos/report/plugins/cifs.py --- sosreport-4.0/sos/report/plugins/cifs.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/cifs.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Cifs(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Cifs(Plugin, IndependentPlugin): short_desc = 'SMB file system information' plugin_name = 'cifs' diff -Nru sosreport-4.0/sos/report/plugins/clear_containers.py sosreport-4.1/sos/report/plugins/clear_containers.py --- sosreport-4.0/sos/report/plugins/clear_containers.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/clear_containers.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,12 +8,10 @@ import re -from sos.report.plugins import (Plugin, RedHatPlugin, DebianPlugin, - UbuntuPlugin, SuSEPlugin) +from sos.report.plugins import Plugin, IndependentPlugin -class ClearContainers(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin, - SuSEPlugin): +class ClearContainers(Plugin, IndependentPlugin): short_desc = 'Intel(R) Clear Containers configuration' diff -Nru sosreport-4.0/sos/report/plugins/cloud_init.py sosreport-4.1/sos/report/plugins/cloud_init.py --- sosreport-4.0/sos/report/plugins/cloud_init.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/cloud_init.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class CloudInit(Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin): +class CloudInit(Plugin, IndependentPlugin): short_desc = 'cloud-init instance configurations' diff -Nru sosreport-4.0/sos/report/plugins/cockpit.py sosreport-4.1/sos/report/plugins/cockpit.py --- sosreport-4.0/sos/report/plugins/cockpit.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/cockpit.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Cockpit(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Cockpit(Plugin, IndependentPlugin): short_desc = 'Cockpit Web Service' diff -Nru sosreport-4.0/sos/report/plugins/collectd.py sosreport-4.1/sos/report/plugins/collectd.py --- sosreport-4.0/sos/report/plugins/collectd.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/collectd.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. import re -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Collectd(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Collectd(Plugin, IndependentPlugin): short_desc = 'Collectd config collector' plugin_name = "collectd" diff -Nru sosreport-4.0/sos/report/plugins/composer.py sosreport-4.1/sos/report/plugins/composer.py --- sosreport-4.0/sos/report/plugins/composer.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/composer.py 2021-02-25 18:46:49.000000000 +0000 @@ -1,7 +1,14 @@ -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. +from sos.report.plugins import Plugin, IndependentPlugin -class Composer(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Composer(Plugin, IndependentPlugin): short_desc = 'Lorax Composer' diff -Nru sosreport-4.0/sos/report/plugins/conntrackd.py sosreport-4.1/sos/report/plugins/conntrackd.py --- sosreport-4.0/sos/report/plugins/conntrackd.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/conntrackd.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,36 +0,0 @@ -# Copyright (C) 2017 Red Hat, Inc., Marcus Linden -# This file is part of the sos project: https://github.com/sosreport/sos -# -# This copyrighted material is made available to anyone wishing to use, -# modify, copy, or redistribute it subject to the terms and conditions of -# version 2 of the GNU General Public License. -# -# See the LICENSE file in the source distribution for further information. - -from sos.report.plugins import (Plugin, RedHatPlugin, DebianPlugin, - UbuntuPlugin, SuSEPlugin) - - -class Conntrackd(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin, SuSEPlugin): - - short_desc = 'conntrackd - netfilter connection tracking user-space daemon' - - plugin_name = 'conntrackd' - profiles = ('network', 'cluster') - - packages = ('conntrack-tools', 'conntrackd') - - def setup(self): - self.add_copy_spec("/etc/conntrackd/conntrackd.conf") - self.add_cmd_output([ - "conntrackd -s network", - "conntrackd -s cache", - "conntrackd -s runtime", - "conntrackd -s link", - "conntrackd -s rsqueue", - "conntrackd -s queue", - "conntrackd -s ct", - "conntrackd -s expect", - ]) - -# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/report/plugins/conntrack.py sosreport-4.1/sos/report/plugins/conntrack.py --- sosreport-4.0/sos/report/plugins/conntrack.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/conntrack.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,50 @@ +# Copyright (C) 2017 Red Hat, Inc., Marcus Linden +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +from sos.report.plugins import Plugin, IndependentPlugin + + +class Conntrack(Plugin, IndependentPlugin): + + short_desc = 'conntrack - netfilter connection tracking' + + plugin_name = 'conntrack' + profiles = ('network', 'cluster') + + packages = ('conntrack-tools', 'conntrack', 'conntrackd') + + def setup(self): + # Collect info from conntrackd + self.add_copy_spec("/etc/conntrackd/conntrackd.conf") + self.add_cmd_output([ + "conntrackd -s network", + "conntrackd -s cache", + "conntrackd -s runtime", + "conntrackd -s link", + "conntrackd -s rsqueue", + "conntrackd -s queue", + "conntrackd -s ct", + "conntrackd -s expect", + ]) + + # Collect info from conntrack + self.add_cmd_output([ + "conntrack -L -o extended", + "conntrack -S", + ]) + + # Capture additional data from namespaces; each command is run + # per-namespace + cmd_prefix = "ip netns exec " + for namespace in self.get_network_namespaces(): + ns_cmd_prefix = cmd_prefix + namespace + " " + self.add_cmd_output(ns_cmd_prefix + "conntrack -L -o extended") + self.add_cmd_output(ns_cmd_prefix + "conntrack -S") + +# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/report/plugins/container_log.py sosreport-4.1/sos/report/plugins/container_log.py --- sosreport-4.0/sos/report/plugins/container_log.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/container_log.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,11 +8,11 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin import os -class ContainerLog(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class ContainerLog(Plugin, IndependentPlugin): short_desc = 'All logs under /var/log/containers' plugin_name = 'container_log' diff -Nru sosreport-4.0/sos/report/plugins/containers_common.py sosreport-4.1/sos/report/plugins/containers_common.py --- sosreport-4.0/sos/report/plugins/containers_common.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/containers_common.py 2021-02-25 18:46:49.000000000 +0000 @@ -41,6 +41,7 @@ 'podman unshare cat /proc/self/uid_map', 'podman unshare cat /proc/self/gid_map', 'podman images', + 'podman images --digests', 'podman pod ps', 'podman port --all', 'podman ps', diff -Nru sosreport-4.0/sos/report/plugins/crio.py sosreport-4.1/sos/report/plugins/crio.py --- sosreport-4.0/sos/report/plugins/crio.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/crio.py 2021-02-25 18:46:49.000000000 +0000 @@ -31,6 +31,7 @@ "/etc/crictl.yaml", "/etc/crio/crio.conf", "/etc/crio/seccomp.json", + "/etc/crio/crio.conf.d/", "/etc/systemd/system/cri-o.service", "/etc/sysconfig/crio-*" ]) @@ -43,7 +44,10 @@ ]) self.add_journal(units="crio") - self.add_cmd_output("ls -alhR /etc/cni") + self.add_cmd_output([ + "ls -alhR /etc/cni", + "crio config" + ]) # base cri-o installation does not require cri-tools, which is what # supplies the crictl utility diff -Nru sosreport-4.0/sos/report/plugins/cron.py sosreport-4.1/sos/report/plugins/cron.py --- sosreport-4.0/sos/report/plugins/cron.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/cron.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Cron(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Cron(Plugin, IndependentPlugin): short_desc = 'Cron job scheduler' diff -Nru sosreport-4.0/sos/report/plugins/crypto.py sosreport-4.1/sos/report/plugins/crypto.py --- sosreport-4.0/sos/report/plugins/crypto.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/crypto.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Crypto(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Crypto(Plugin, IndependentPlugin): short_desc = 'System crypto services information' diff -Nru sosreport-4.0/sos/report/plugins/cups.py sosreport-4.1/sos/report/plugins/cups.py --- sosreport-4.0/sos/report/plugins/cups.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/cups.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Cups(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Cups(Plugin, IndependentPlugin): short_desc = 'CUPS IPP print service' diff -Nru sosreport-4.0/sos/report/plugins/date.py sosreport-4.1/sos/report/plugins/date.py --- sosreport-4.0/sos/report/plugins/date.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/date.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Date(Plugin, RedHatPlugin, DebianPlugin): +class Date(Plugin, IndependentPlugin): short_desc = 'Basic system time information' diff -Nru sosreport-4.0/sos/report/plugins/dbus.py sosreport-4.1/sos/report/plugins/dbus.py --- sosreport-4.0/sos/report/plugins/dbus.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/dbus.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Dbus(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Dbus(Plugin, IndependentPlugin): short_desc = 'D-Bus message bus' diff -Nru sosreport-4.0/sos/report/plugins/devicemapper.py sosreport-4.1/sos/report/plugins/devicemapper.py --- sosreport-4.0/sos/report/plugins/devicemapper.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/devicemapper.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class DeviceMapper(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class DeviceMapper(Plugin, IndependentPlugin): short_desc = 'device-mapper framework' diff -Nru sosreport-4.0/sos/report/plugins/devices.py sosreport-4.1/sos/report/plugins/devices.py --- sosreport-4.0/sos/report/plugins/devices.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/devices.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,11 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import (Plugin, RedHatPlugin, DebianPlugin, - UbuntuPlugin, CosPlugin) +from sos.report.plugins import Plugin, IndependentPlugin -class Devices(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin, CosPlugin): +class Devices(Plugin, IndependentPlugin): short_desc = 'devices specific commands' diff -Nru sosreport-4.0/sos/report/plugins/dlm.py sosreport-4.1/sos/report/plugins/dlm.py --- sosreport-4.0/sos/report/plugins/dlm.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/dlm.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,11 +6,11 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin import re -class Dlm(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Dlm(Plugin, IndependentPlugin): short_desc = 'DLM (Distributed lock manager)' diff -Nru sosreport-4.0/sos/report/plugins/dmraid.py sosreport-4.1/sos/report/plugins/dmraid.py --- sosreport-4.0/sos/report/plugins/dmraid.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/dmraid.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Dmraid(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Dmraid(Plugin, IndependentPlugin): short_desc = 'dmraid software RAID' diff -Nru sosreport-4.0/sos/report/plugins/ds.py sosreport-4.1/sos/report/plugins/ds.py --- sosreport-4.0/sos/report/plugins/ds.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/ds.py 2021-02-25 18:46:49.000000000 +0000 @@ -1,6 +1,7 @@ # Copyright (C) 2007 Red Hat, Inc., Kent Lamb # Copyright (C) 2014 Red Hat, Inc., Bryn M. Reeves - +# Copyright (C) 2021 Red Hat, Inc., Mark Reynolds +# # This file is part of the sos project: https://github.com/sosreport/sos # # This copyrighted material is made available to anyone wishing to use, @@ -48,6 +49,7 @@ if d[0:5] == 'slapd': certpath = os.path.join("/etc/dirsrv", d) self.add_cmd_output("certutil -L -d %s" % certpath) + self.add_cmd_output("dsctl %s healthcheck" % d) except OSError: self._log_warn("could not list /etc/dirsrv") @@ -70,4 +72,6 @@ "/opt/redhat-ds/slapd-*/logs" ]) + self.add_cmd_output("ls -l /var/lib/dirsrv/slapd-*/db/*") + # vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/report/plugins/ebpf.py sosreport-4.1/sos/report/plugins/ebpf.py --- sosreport-4.0/sos/report/plugins/ebpf.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/ebpf.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,11 +6,11 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin import json -class Ebpf(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Ebpf(Plugin, IndependentPlugin): short_desc = 'eBPF tool' plugin_name = 'ebpf' @@ -67,19 +67,9 @@ ]) # Capture list of bpf program attachments from namespaces - ip_netns = self.exec_cmd("ip netns") cmd_prefix = "ip netns exec " - if ip_netns['status'] == 0: - out_ns = [] - for line in ip_netns['output'].splitlines(): - # If there's no namespaces, no need to continue - if line.startswith("Object \"netns\" is unknown") \ - or line.isspace() \ - or line[:1].isspace(): - continue - out_ns.append(line.partition(' ')[0]) - for namespace in out_ns: - ns_cmd_prefix = cmd_prefix + namespace + " " - self.add_cmd_output(ns_cmd_prefix + "bpftool net list") + for namespace in self.get_network_namespaces(): + ns_cmd_prefix = cmd_prefix + namespace + " " + self.add_cmd_output(ns_cmd_prefix + "bpftool net list") # vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/report/plugins/elastic.py sosreport-4.1/sos/report/plugins/elastic.py --- sosreport-4.0/sos/report/plugins/elastic.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/elastic.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,11 +8,11 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin import re -class Elastic(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Elastic(Plugin, IndependentPlugin): short_desc = 'ElasticSearch service' plugin_name = 'elastic' diff -Nru sosreport-4.0/sos/report/plugins/fibrechannel.py sosreport-4.1/sos/report/plugins/fibrechannel.py --- sosreport-4.0/sos/report/plugins/fibrechannel.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/fibrechannel.py 2021-02-25 18:46:49.000000000 +0000 @@ -17,9 +17,19 @@ plugin_name = 'fibrechannel' profiles = ('hardware', 'storage', 'system') - files = ('/sys/class/fc_host') + files = ('/sys/class/fc_host', '/sys/class/fc_remote_ports') + option_list = [ + ("debug", "enable debug logs", "fast", True) + ] + + # vendor specific debug paths + debug_paths = [ + '/sys/kernel/debug/qla2*/' + ] def setup(self): self.add_blockdev_cmd("udevadm info -a %(dev)s", devices='fibre') + if self.get_option('debug'): + self.add_copy_spec(self.debug_paths) # vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/report/plugins/filesys.py sosreport-4.1/sos/report/plugins/filesys.py --- sosreport-4.0/sos/report/plugins/filesys.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/filesys.py 2021-02-25 18:46:49.000000000 +0000 @@ -43,6 +43,8 @@ "lslocks" ]) + self.add_forbidden_path('/proc/fs/panfs') + if self.get_option('lsof'): self.add_cmd_output("lsof -b +M -n -l -P", root_symlink="lsof") diff -Nru sosreport-4.0/sos/report/plugins/firewall_tables.py sosreport-4.1/sos/report/plugins/firewall_tables.py --- sosreport-4.0/sos/report/plugins/firewall_tables.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/firewall_tables.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,87 @@ +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +from sos.report.plugins import (Plugin, IndependentPlugin, SoSPredicate) + + +class firewall_tables(Plugin, IndependentPlugin): + + short_desc = 'firewall tables' + + plugin_name = "firewall_tables" + profiles = ('network', 'system') + + def collect_iptable(self, tablename): + """ Collecting iptables rules for a table loads either kernel module + of the table name (for kernel <= 3), or nf_tables (for kernel >= 4). + If neither module is present, the rules must be empty.""" + + modname = "iptable_" + tablename + cmd = "iptables -t " + tablename + " -nvL" + self.add_cmd_output( + cmd, + pred=SoSPredicate(self, kmods=[modname, 'nf_tables'])) + + def collect_ip6table(self, tablename): + """ Same as function above, but for ipv6 """ + + modname = "ip6table_" + tablename + cmd = "ip6tables -t " + tablename + " -nvL" + self.add_cmd_output( + cmd, + pred=SoSPredicate(self, kmods=[modname, 'nf_tables'])) + + def collect_nftables(self): + """ Collects nftables rulesets with 'nft' commands if the modules + are present """ + + self.add_cmd_output( + "nft list ruleset", + pred=SoSPredicate(self, kmods=['nf_tables']) + ) + + def setup(self): + # collect iptables -t for any existing table, if we can't read the + # tables, collect 2 default ones (mangle, filter) + try: + ip_tables_names = open("/proc/net/ip_tables_names").read() + except IOError: + ip_tables_names = "mangle\nfilter\n" + for table in ip_tables_names.splitlines(): + self.collect_iptable(table) + # collect the same for ip6tables + try: + ip_tables_names = open("/proc/net/ip6_tables_names").read() + except IOError: + ip_tables_names = "mangle\nfilter\n" + for table in ip_tables_names.splitlines(): + self.collect_ip6table(table) + + self.collect_nftables() + + # When iptables is called it will load the modules + # iptables_filter (for kernel <= 3) or + # nf_tables (for kernel >= 4) if they are not loaded. + # The same goes for ipv6. + self.add_cmd_output( + "iptables -vnxL", + pred=SoSPredicate(self, kmods=['iptable_filter', 'nf_tables']) + ) + + self.add_cmd_output( + "ip6tables -vnxL", + pred=SoSPredicate(self, kmods=['ip6table_filter', 'nf_tables']) + ) + + self.add_copy_spec([ + "/etc/nftables", + "/etc/sysconfig/nftables.conf", + "/etc/nftables.conf", + ]) + +# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/report/plugins/foreman.py sosreport-4.1/sos/report/plugins/foreman.py --- sosreport-4.0/sos/report/plugins/foreman.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/foreman.py 2021-02-25 18:46:49.000000000 +0000 @@ -264,7 +264,7 @@ if csv: query = "COPY (%s) TO STDOUT " \ "WITH (FORMAT 'csv', DELIMITER ',', HEADER)" % query - _dbcmd = "psql -h %s -p 5432 -U foreman -d foreman -c %s" + _dbcmd = "psql --no-password -h %s -p 5432 -U foreman -d foreman -c %s" return _dbcmd % (self.dbhost, quote(query)) def postproc(self): diff -Nru sosreport-4.0/sos/report/plugins/fwupd.py sosreport-4.1/sos/report/plugins/fwupd.py --- sosreport-4.0/sos/report/plugins/fwupd.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/fwupd.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Fwupd(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Fwupd(Plugin, IndependentPlugin): short_desc = 'fwupd information' diff -Nru sosreport-4.0/sos/report/plugins/gdm.py sosreport-4.1/sos/report/plugins/gdm.py --- sosreport-4.0/sos/report/plugins/gdm.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/gdm.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Gdm(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Gdm(Plugin, IndependentPlugin): short_desc = 'GNOME display manager' diff -Nru sosreport-4.0/sos/report/plugins/gfs2.py sosreport-4.1/sos/report/plugins/gfs2.py --- sosreport-4.0/sos/report/plugins/gfs2.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/gfs2.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Gfs2(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Gfs2(Plugin, IndependentPlugin): short_desc = 'GFS2 (Global Filesystem 2)' diff -Nru sosreport-4.0/sos/report/plugins/gluster.py sosreport-4.1/sos/report/plugins/gluster.py --- sosreport-4.0/sos/report/plugins/gluster.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/gluster.py 2021-02-25 18:46:49.000000000 +0000 @@ -55,6 +55,9 @@ def setup(self): self.add_forbidden_path("/var/lib/glusterd/geo-replication/secret.pem") + self.add_forbidden_path( + "/var/lib/glusterd/glusterfind/glusterfind_*_secret.pem" + ) self.add_cmd_output([ "gluster peer status", @@ -72,7 +75,10 @@ "/etc/glusterfs", "/var/lib/glusterd/", # collect nfs-ganesha related configuration - "/run/gluster/shared_storage/nfs-ganesha/" + "/run/gluster/shared_storage/nfs-ganesha/", + # collect status files and public ssh keys + "/var/lib/glusterd/.keys/", + "/var/lib/glusterd/glusterfind/" ] + glob.glob('/run/gluster/*tier-dht/*')) if not self.get_option("all_logs"): diff -Nru sosreport-4.0/sos/report/plugins/grafana.py sosreport-4.1/sos/report/plugins/grafana.py --- sosreport-4.0/sos/report/plugins/grafana.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/grafana.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Grafana(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Grafana(Plugin, IndependentPlugin): short_desc = 'Fetch Grafana configuration, logs and CLI output' plugin_name = "grafana" diff -Nru sosreport-4.0/sos/report/plugins/grub2.py sosreport-4.1/sos/report/plugins/grub2.py --- sosreport-4.0/sos/report/plugins/grub2.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/grub2.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,11 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import (Plugin, RedHatPlugin, DebianPlugin, - UbuntuPlugin, SoSPredicate) +from sos.report.plugins import Plugin, IndependentPlugin, SoSPredicate -class Grub2(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Grub2(Plugin, IndependentPlugin): short_desc = 'GRUB2 bootloader' @@ -36,13 +35,11 @@ # further, check if the command supports --no-grubenv-update option # to prevent removing of extra args in $kernel_opts, and (only) if so, # call the command with this argument - env = {} - env['GRUB_DISABLE_OS_PROBER'] = 'true' grub_cmd = 'grub2-mkconfig' - co = {'cmd': 'grub2-mkconfig --help', 'output': '--no-grubenv-update'} + co = {'cmd': '%s --help' % grub_cmd, 'output': '--no-grubenv-update'} if self.test_predicate(self, pred=SoSPredicate(self, cmd_outputs=co)): grub_cmd += ' --no-grubenv-update' - self.add_cmd_output(grub_cmd, env=env) + self.add_cmd_output(grub_cmd, env={'GRUB_DISABLE_OS_PROBER': 'true'}) def postproc(self): # the trailing space is required; python treats '_' as whitespace diff -Nru sosreport-4.0/sos/report/plugins/grub.py sosreport-4.1/sos/report/plugins/grub.py --- sosreport-4.0/sos/report/plugins/grub.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/grub.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Grub(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Grub(Plugin, IndependentPlugin): short_desc = 'GRUB bootloader' diff -Nru sosreport-4.0/sos/report/plugins/gssproxy.py sosreport-4.1/sos/report/plugins/gssproxy.py --- sosreport-4.0/sos/report/plugins/gssproxy.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/gssproxy.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class GSSProxy(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class GSSProxy(Plugin, IndependentPlugin): short_desc = 'GSSAPI Proxy' diff -Nru sosreport-4.0/sos/report/plugins/haproxy.py sosreport-4.1/sos/report/plugins/haproxy.py --- sosreport-4.0/sos/report/plugins/haproxy.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/haproxy.py 2021-02-25 18:46:49.000000000 +0000 @@ -38,6 +38,9 @@ self.add_copy_spec("/var/log/haproxy.log") + self.add_service_status('haproxy') + self.add_journal(units='haproxy') + # collect haproxy overview - curl to IP address taken from haproxy.cfg # as 2nd word on line below "haproxy.stats" # so parse haproxy.cfg until "haproxy.stats" read, and take 2nd word diff -Nru sosreport-4.0/sos/report/plugins/hardware.py sosreport-4.1/sos/report/plugins/hardware.py --- sosreport-4.0/sos/report/plugins/hardware.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/hardware.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Hardware(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Hardware(Plugin, IndependentPlugin): short_desc = 'General hardware information' diff -Nru sosreport-4.0/sos/report/plugins/host.py sosreport-4.1/sos/report/plugins/host.py --- sosreport-4.0/sos/report/plugins/host.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/host.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Host(Plugin, RedHatPlugin, DebianPlugin): +class Host(Plugin, IndependentPlugin): short_desc = 'Host information' diff -Nru sosreport-4.0/sos/report/plugins/hpasm.py sosreport-4.1/sos/report/plugins/hpasm.py --- sosreport-4.0/sos/report/plugins/hpasm.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/hpasm.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Hpasm(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Hpasm(Plugin, IndependentPlugin): short_desc = 'HP Advanced Server Management' diff -Nru sosreport-4.0/sos/report/plugins/hyperv.py sosreport-4.1/sos/report/plugins/hyperv.py --- sosreport-4.0/sos/report/plugins/hyperv.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/hyperv.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Hyperv(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Hyperv(Plugin, IndependentPlugin): """Hyper-V client information""" plugin_name = "hyperv" diff -Nru sosreport-4.0/sos/report/plugins/i18n.py sosreport-4.1/sos/report/plugins/i18n.py --- sosreport-4.0/sos/report/plugins/i18n.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/i18n.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class I18n(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class I18n(Plugin, IndependentPlugin): short_desc = 'Internationalization' diff -Nru sosreport-4.0/sos/report/plugins/infiniband.py sosreport-4.1/sos/report/plugins/infiniband.py --- sosreport-4.0/sos/report/plugins/infiniband.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/infiniband.py 2021-02-25 18:46:49.000000000 +0000 @@ -9,10 +9,10 @@ # See the LICENSE file in the source distribution for further information. import os -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Infiniband(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Infiniband(Plugin, IndependentPlugin): short_desc = 'Infiniband information' diff -Nru sosreport-4.0/sos/report/plugins/__init__.py sosreport-4.1/sos/report/plugins/__init__.py --- sosreport-4.0/sos/report/plugins/__init__.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/__init__.py 2021-02-25 18:46:49.000000000 +0000 @@ -470,7 +470,8 @@ predicate = None cmd_predicate = None _default_plug_opts = [ - ('timeout', 'Timeout in seconds for plugin', 'fast', -1), + ('timeout', 'Timeout in seconds for plugin. The default value (-1) ' + + 'defers to the general plugin timeout, 300 seconds', 'fast', -1), ('postproc', 'Enable post-processing collected plugin data', 'fast', True) ] @@ -495,6 +496,8 @@ self.policy = commons['policy'] self.devices = commons['devices'] self.manifest = None + self.skip_files = commons['cmdlineopts'].skip_files + self.skip_commands = commons['cmdlineopts'].skip_commands self.soslog = self.commons['soslog'] if 'soslog' in self.commons \ else logging.getLogger('sos') @@ -1080,6 +1083,25 @@ def _is_forbidden_path(self, path): return _path_in_path_list(path, self.forbidden_paths) + def _is_policy_forbidden_path(self, path): + return any([ + fnmatch.fnmatch(path, fp) for fp in self.policy.forbidden_paths + ]) + + def _is_skipped_path(self, path): + """Check if the given path matches a user-provided specification to + ignore collection of via the ``--skip-files`` option + + :param path: The filepath being collected + :type path: ``str`` + + :returns: ``True`` if file should be skipped, else ``False`` + """ + for _skip_path in self.skip_files: + if fnmatch.fnmatch(path, _skip_path): + return True + return False + def _copy_node(self, path, st): dev_maj = os.major(st.st_rdev) dev_min = os.minor(st.st_rdev) @@ -1425,16 +1447,28 @@ if self._is_forbidden_path(_file): self._log_debug("skipping forbidden path '%s'" % _file) continue + if self._is_policy_forbidden_path(_file): + self._log_debug("skipping policy forbidden path '%s'" + % _file) + continue + if self._is_skipped_path(_file): + self._log_debug("skipping excluded path '%s'" % _file) + continue if limit_reached: self._log_info("skipping '%s' over size limit" % _file) continue try: - filestat = os.stat(_file) + file_size = os.stat(_file)[stat.ST_SIZE] except OSError: - self._log_info("failed to stat '%s'" % _file) - continue - current_size += filestat[stat.ST_SIZE] + # if _file is a broken symlink, we should collect it, + # otherwise skip it + if os.path.islink(_file): + file_size = 0 + else: + self._log_info("failed to stat '%s', skipping" % _file) + continue + current_size += file_size if sizelimit and current_size > sizelimit: limit_reached = True @@ -1448,8 +1482,7 @@ strfile = ( file_name.replace(os.path.sep, ".") + ".tailed" ) - add_size = (sizelimit + filestat[stat.ST_SIZE] - - current_size) + add_size = sizelimit + file_size - current_size self.add_string_as_file(tail(_file, add_size), strfile) rel_path = os.path.relpath('/', os.path.dirname(_file)) link_path = os.path.join(rel_path, 'sos_strings', @@ -1464,7 +1497,7 @@ self._add_copy_paths([_file]) # in the corner case we just reached the sizelimit, we # should collect the whole file and stop - limit_reached = (current_size == sizelimit) + limit_reached = (sizelimit and current_size == sizelimit) if self.manifest: self.manifest.files.append({ 'specification': copyspec, @@ -1577,6 +1610,14 @@ pred = kwargs.pop('pred') if 'pred' in kwargs else None soscmd = SoSCommand(**kwargs) self._log_debug("packed command: " + soscmd.__str__()) + for _skip_cmd in self.skip_commands: + # This probably seems weird to be doing filename matching on the + # commands, however we want to remain consistent with our regex + # matching with file paths, which sysadmins are almost guaranteed + # to assume will use shell-style unix matching + if fnmatch.fnmatch(soscmd.cmd, _skip_cmd): + self._log_debug("skipping excluded command '%s'" % soscmd.cmd) + return if self.test_predicate(cmd=True, pred=pred): self.collect_cmds.append(soscmd) self._log_info("added cmd output '%s'" % soscmd.cmd) @@ -2005,7 +2046,7 @@ def exec_cmd(self, cmd, timeout=cmd_timeout, stderr=True, chroot=True, runat=None, env=None, binary=False, pred=None, - foreground=False, container=False): + foreground=False, container=False, quotecmd=False): """Execute a command right now and return the output and status, but do not save the output within the archive. @@ -2044,6 +2085,9 @@ this name :type container: ``str`` + :param quotecmd: Whether the cmd should be quoted. + :type quotecmd: ``bool`` + :returns: Command exit status and output :rtype: ``dict`` """ @@ -2062,14 +2106,14 @@ "runtime detected on host." % (cmd, container)) return _default if self.container_exists(container): - cmd = self.fmt_container_cmd(container, cmd) + cmd = self.fmt_container_cmd(container, cmd, quotecmd) else: self._log_info("Cannot run cmd '%s' in container %s: no such " "container is running." % (cmd, container)) return sos_get_command_output(cmd, timeout=timeout, chroot=root, chdir=runat, binary=binary, env=env, - foreground=foreground) + foreground=foreground, stderr=stderr) def _get_container_runtime(self, runtime=None): """Based on policy and request by the plugin, return a usable @@ -2100,6 +2144,24 @@ return con is not None return False + def get_all_containers_by_regex(self, regex, get_all=False): + """Get a list of all container names and ID matching a regex + + :param regex: The regular expression to match + :type regex: ``str`` + + :param get_all: Return all containers found, even terminated ones + :type get_all: ``bool`` + + :returns: All container IDs and names matching ``regex`` + :rtype: ``list`` of ``tuples`` as (id, name) + """ + _runtime = self._get_container_runtime() + if _runtime is not None: + _containers = _runtime.get_containers(get_all=get_all) + return [c for c in _containers if re.match(regex, c[1])] + return [] + def get_container_by_name(self, name): """Get the container ID for a specific container @@ -2192,7 +2254,7 @@ if _runtime is not None: self.add_cmd_output(_runtime.get_logs_command(container), **kwargs) - def fmt_container_cmd(self, container, cmd): + def fmt_container_cmd(self, container, cmd, quotecmd=False): """Format a command to be executed by the loaded ``ContainerRuntime`` in a specified container @@ -2202,13 +2264,16 @@ :param cmd: The command to run within the container :type cmd: ``str`` + :param quotecmd: Whether the cmd should be quoted. + :type quotecmd: ``bool`` + :returns: The command to execute so that the specified `cmd` will run within the `container` and not on the host :rtype: ``str`` """ if self.container_exists(container): _runtime = self._get_container_runtime() - return _runtime.fmt_container_cmd(container, cmd) + return _runtime.fmt_container_cmd(container, cmd, quotecmd) return cmd def is_module_loaded(self, module_name): @@ -2504,7 +2569,8 @@ commands, services): type(self)._scls_matched.append(scl) - return len(type(self)._scls_matched) > 0 + if type(self)._scls_matched: + return True return self._check_plugin_triggers(self.files, self.packages, @@ -2629,6 +2695,41 @@ continue return pids + def get_network_namespaces(self, ns_pattern=None, ns_max=0): + return self.filter_namespaces(self.commons['namespaces']['network'], + ns_pattern, ns_max) + + def filter_namespaces(self, ns_list, ns_pattern=None, ns_max=0): + """Filter a list of namespaces by regex pattern or max number of + namespaces (options originally present in the networking plugin.) + """ + out_ns = [] + + # Regex initialization outside of for loop + if ns_pattern: + pattern = ( + '(?:%s$)' % '$|'.join(ns_pattern.split()).replace('*', '.*') + ) + for ns in ns_list: + # if ns_pattern defined, append only namespaces + # matching with pattern + if ns_pattern: + if bool(re.match(pattern, ns)): + out_ns.append(ns) + + # if ns_max is defined and ns_pattern is not defined + # remove from out_ns namespaces with higher index than defined + elif ns_max != 0: + out_ns.append(ns) + if len(out_ns) == ns_max: + self._log_warn("Limiting namespace iteration " + "to first %s namespaces found" + % ns_max) + break + else: + out_ns.append(ns) + return out_ns + class RedHatPlugin(object): """Tagging class for Red Hat's Linux distributions""" @@ -2694,6 +2795,8 @@ """Same as add_cmd_output, except that it wraps command in "scl enable" call and sets proper PATH. """ + if scl not in self.scls_matched: + return if isinstance(cmds, str): cmds = [cmds] scl_cmds = [] @@ -2718,6 +2821,8 @@ """Same as add_copy_spec, except that it prepends path to SCL root to "copyspecs". """ + if scl not in self.scls_matched: + return if isinstance(copyspecs, str): copyspecs = [copyspecs] scl_copyspecs = [] @@ -2725,15 +2830,6 @@ scl_copyspecs.append(self.convert_copyspec_scl(scl, copyspec)) self.add_copy_spec(scl_copyspecs) - def add_copy_spec_limit_scl(self, scl, copyspec, **kwargs): - """Same as add_copy_spec_limit, except that it prepends path to SCL - root to "copyspec". - """ - self.add_copy_spec_limit( - self.convert_copyspec_scl(scl, copyspec), - **kwargs - ) - class PowerKVMPlugin(RedHatPlugin): """Tagging class for IBM PowerKVM Linux""" diff -Nru sosreport-4.0/sos/report/plugins/insights.py sosreport-4.1/sos/report/plugins/insights.py --- sosreport-4.0/sos/report/plugins/insights.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/insights.py 2021-02-25 18:46:49.000000000 +0000 @@ -17,6 +17,7 @@ profiles = ('system', 'sysmgmt') config = ( '/etc/insights-client/insights-client.conf', + '/etc/insights-client/.registered', '/etc/redhat-access-insights/redhat-access-insights.conf' ) diff -Nru sosreport-4.0/sos/report/plugins/ipa.py sosreport-4.1/sos/report/plugins/ipa.py --- sosreport-4.0/sos/report/plugins/ipa.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/ipa.py 2021-02-25 18:46:49.000000000 +0000 @@ -50,13 +50,13 @@ def retrieve_pki_logs(self, ipa_version): if ipa_version == "v4": self.add_copy_spec([ - "/var/log/pki/pki-tomcat/ca/debug", + "/var/log/pki/pki-tomcat/ca/debug*", "/var/log/pki/pki-tomcat/ca/system", "/var/log/pki/pki-tomcat/ca/transactions", "/var/log/pki/pki-tomcat/ca/selftests.log", "/var/log/pki/pki-tomcat/catalina.*", "/var/log/pki/pki-ca-spawn.*", - "/var/log/pki/pki-tomcat/kra/debug", + "/var/log/pki/pki-tomcat/kra/debug*", "/var/log/pki/pki-tomcat/kra/system", "/var/log/pki/pki-tomcat/kra/transactions", "/var/log/pki/pki-kra-spawn.*" diff -Nru sosreport-4.0/sos/report/plugins/iprconfig.py sosreport-4.1/sos/report/plugins/iprconfig.py --- sosreport-4.0/sos/report/plugins/iprconfig.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/iprconfig.py 2021-02-25 18:46:49.000000000 +0000 @@ -9,10 +9,10 @@ # This plugin enables collection of logs for Power systems import re -from sos.report.plugins import Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class IprConfig(Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin): +class IprConfig(Plugin, IndependentPlugin): short_desc = 'IBM Power RAID storage adapter configuration information' @@ -32,6 +32,7 @@ "iprconfig -c show-af-disks", "iprconfig -c show-all-af-disks", "iprconfig -c show-slots", + "iprconfig -c dump" ]) show_ioas = self.collect_cmd_output("iprconfig -c show-ioas") diff -Nru sosreport-4.0/sos/report/plugins/java.py sosreport-4.1/sos/report/plugins/java.py --- sosreport-4.0/sos/report/plugins/java.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/java.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Java(Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin): +class Java(Plugin, IndependentPlugin): short_desc = 'Java runtime' diff -Nru sosreport-4.0/sos/report/plugins/kata_containers.py sosreport-4.1/sos/report/plugins/kata_containers.py --- sosreport-4.0/sos/report/plugins/kata_containers.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/kata_containers.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,12 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import (Plugin, RedHatPlugin, DebianPlugin, - UbuntuPlugin, SuSEPlugin) +from sos.report.plugins import Plugin, IndependentPlugin -class KataContainers(Plugin, RedHatPlugin, DebianPlugin, - UbuntuPlugin, SuSEPlugin): +class KataContainers(Plugin, IndependentPlugin): short_desc = 'Kata Containers configuration' diff -Nru sosreport-4.0/sos/report/plugins/kdump.py sosreport-4.1/sos/report/plugins/kdump.py --- sosreport-4.0/sos/report/plugins/kdump.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/kdump.py 2021-02-25 18:46:49.000000000 +0000 @@ -71,7 +71,9 @@ self.add_copy_spec([ "/etc/kdump.conf", "/etc/udev/rules.d/*kexec.rules", - "/var/crash/*/vmcore-dmesg.txt" + "/var/crash/*/vmcore-dmesg.txt", + "/var/crash/*/kexec-dmesg.log", + "/var/log/kdump.log" ]) try: path = self.read_kdump_conffile() @@ -80,6 +82,7 @@ path = "/var/crash" self.add_copy_spec("{}/*/vmcore-dmesg.txt".format(path)) + self.add_copy_spec("{}/*/kexec-dmesg.log".format(path)) class DebianKDump(KDump, DebianPlugin, UbuntuPlugin): diff -Nru sosreport-4.0/sos/report/plugins/keepalived.py sosreport-4.1/sos/report/plugins/keepalived.py --- sosreport-4.0/sos/report/plugins/keepalived.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/keepalived.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Keepalived(Plugin, RedHatPlugin): +class Keepalived(Plugin, IndependentPlugin): short_desc = 'Keepalived routing server' diff -Nru sosreport-4.0/sos/report/plugins/kernel.py sosreport-4.1/sos/report/plugins/kernel.py --- sosreport-4.0/sos/report/plugins/kernel.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/kernel.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,13 +6,12 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import (Plugin, RedHatPlugin, DebianPlugin, - UbuntuPlugin, CosPlugin) +from sos.report.plugins import Plugin, IndependentPlugin import os import glob -class Kernel(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin, CosPlugin): +class Kernel(Plugin, IndependentPlugin): short_desc = 'Linux kernel' diff -Nru sosreport-4.0/sos/report/plugins/kimchi.py sosreport-4.1/sos/report/plugins/kimchi.py --- sosreport-4.0/sos/report/plugins/kimchi.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/kimchi.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Kimchi(Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin): +class Kimchi(Plugin, IndependentPlugin): short_desc = 'kimchi-related information' diff -Nru sosreport-4.0/sos/report/plugins/kubernetes.py sosreport-4.1/sos/report/plugins/kubernetes.py --- sosreport-4.0/sos/report/plugins/kubernetes.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/kubernetes.py 2021-02-25 18:46:49.000000000 +0000 @@ -205,10 +205,22 @@ class UbuntuKubernetes(Kubernetes, UbuntuPlugin): packages = ('kubernetes',) - files = ('/root/cdk/kubeproxyconfig', '/etc/kubernetes') - if path.exists('/root/cdk/kubeproxyconfig'): - kube_cmd = "kubectl --kubeconfig=/root/cdk/kubeproxyconfig" + files = ('/root/cdk/cdk_addons_kubectl_config', '/etc/kubernetes') + if path.exists('/root/cdk/cdk_addons_kubectl_config'): + kube_cmd = "kubectl --kubeconfig=/root/cdk/cdk_addons_kubectl_config" elif path.exists('/etc/kubernetes/admin.conf'): kube_cmd = "kubectl --kubeconfig=/etc/kubernetes/admin.conf" + services = ( + # CDK + 'cdk.master.auth-webhook', + ) + + def setup(self): + for svc in self.services: + self.add_journal(units=svc) + + super(UbuntuKubernetes, self).setup() + + # vim: et ts=5 sw=4 diff -Nru sosreport-4.0/sos/report/plugins/kvm.py sosreport-4.1/sos/report/plugins/kvm.py --- sosreport-4.0/sos/report/plugins/kvm.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/kvm.py 2021-02-25 18:46:49.000000000 +0000 @@ -9,10 +9,10 @@ # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Kvm(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Kvm(Plugin, IndependentPlugin): short_desc = 'Kernel virtual machine' diff -Nru sosreport-4.0/sos/report/plugins/libraries.py sosreport-4.1/sos/report/plugins/libraries.py --- sosreport-4.0/sos/report/plugins/libraries.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/libraries.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Libraries(Plugin, RedHatPlugin, UbuntuPlugin): +class Libraries(Plugin, IndependentPlugin): short_desc = 'Dynamic shared libraries' diff -Nru sosreport-4.0/sos/report/plugins/libreswan.py sosreport-4.1/sos/report/plugins/libreswan.py --- sosreport-4.0/sos/report/plugins/libreswan.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/libreswan.py 2021-02-25 18:46:49.000000000 +0000 @@ -9,10 +9,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Libreswan(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Libreswan(Plugin, IndependentPlugin): short_desc = 'Libreswan IPsec' diff -Nru sosreport-4.0/sos/report/plugins/libvirt.py sosreport-4.1/sos/report/plugins/libvirt.py --- sosreport-4.0/sos/report/plugins/libvirt.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/libvirt.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,12 +6,12 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin +from sos.report.plugins import Plugin, IndependentPlugin import glob import os -class Libvirt(Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin): +class Libvirt(Plugin, IndependentPlugin): short_desc = 'libvirt virtualization API' diff -Nru sosreport-4.0/sos/report/plugins/lightdm.py sosreport-4.1/sos/report/plugins/lightdm.py --- sosreport-4.0/sos/report/plugins/lightdm.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/lightdm.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class LightDm(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class LightDm(Plugin, IndependentPlugin): short_desc = 'Light Display Manager' packages = ('lightdm', ) diff -Nru sosreport-4.0/sos/report/plugins/login.py sosreport-4.1/sos/report/plugins/login.py --- sosreport-4.0/sos/report/plugins/login.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/login.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Login(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Login(Plugin, IndependentPlugin): short_desc = 'login information' diff -Nru sosreport-4.0/sos/report/plugins/logrotate.py sosreport-4.1/sos/report/plugins/logrotate.py --- sosreport-4.0/sos/report/plugins/logrotate.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/logrotate.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class LogRotate(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class LogRotate(Plugin, IndependentPlugin): short_desc = 'LogRotate service' diff -Nru sosreport-4.0/sos/report/plugins/logs.py sosreport-4.1/sos/report/plugins/logs.py --- sosreport-4.0/sos/report/plugins/logs.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/logs.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,11 +8,10 @@ import os import glob -from sos.report.plugins import (Plugin, RedHatPlugin, DebianPlugin, - UbuntuPlugin, CosPlugin) +from sos.report.plugins import Plugin, IndependentPlugin -class Logs(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin, CosPlugin): +class Logs(Plugin, IndependentPlugin): short_desc = 'System logs' @@ -49,7 +48,6 @@ "/etc/rsyslog.d", "/var/log/boot.log", "/var/log/installer", - "/var/log/unattended-upgrades", "/var/log/messages*", "/var/log/secure*", "/var/log/udev", diff -Nru sosreport-4.0/sos/report/plugins/lstopo.py sosreport-4.1/sos/report/plugins/lstopo.py --- sosreport-4.0/sos/report/plugins/lstopo.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/lstopo.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,11 +6,11 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin from sos.utilities import is_executable -class Lstopo(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Lstopo(Plugin, IndependentPlugin): short_desc = 'Machine topology information' diff -Nru sosreport-4.0/sos/report/plugins/lvm2.py sosreport-4.1/sos/report/plugins/lvm2.py --- sosreport-4.0/sos/report/plugins/lvm2.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/lvm2.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,11 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import (Plugin, RedHatPlugin, DebianPlugin, - UbuntuPlugin, SoSPredicate) +from sos.report.plugins import Plugin, IndependentPlugin, SoSPredicate -class Lvm2(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Lvm2(Plugin, IndependentPlugin): short_desc = 'Logical Volume Manager 2' diff -Nru sosreport-4.0/sos/report/plugins/lxd.py sosreport-4.1/sos/report/plugins/lxd.py --- sosreport-4.0/sos/report/plugins/lxd.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/lxd.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,7 +8,7 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, UbuntuPlugin +from sos.report.plugins import Plugin, UbuntuPlugin, SoSPredicate class LXD(Plugin, UbuntuPlugin): @@ -20,9 +20,30 @@ commands = ('lxd',) def setup(self): + + lxd_kmods = [ + 'bpfilter', + 'ebtable_filter', + 'ebtables', + 'ip6table_filter', + 'ip6table_mangle', + 'ip6table_nat', + 'ip6table_raw', + 'ip6_tables', + 'iptable_filter', + 'iptable_mangle', + 'iptable_nat', + 'iptable_raw', + 'nf_nat', + 'nf_tables', + ] + + lxd_pred = SoSPredicate(self, kmods=lxd_kmods, + required={'kmods': 'all'}) + snap_list = self.exec_cmd('snap list lxd') if snap_list["status"] == 0: - self.add_cmd_output("lxd.buginfo") + self.add_cmd_output("lxd.buginfo", pred=lxd_pred) else: self.add_copy_spec([ "/etc/default/lxd-bridge", @@ -35,7 +56,7 @@ "lxc network list", "lxc profile list", "lxc storage list" - ]) + ], pred=lxd_pred) self.add_cmd_output([ "find /var/lib/lxd -maxdepth 2 -type d -ls", diff -Nru sosreport-4.0/sos/report/plugins/md.py sosreport-4.1/sos/report/plugins/md.py --- sosreport-4.0/sos/report/plugins/md.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/md.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Md(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Md(Plugin, IndependentPlugin): short_desc = 'MD RAID subsystem' @@ -18,6 +18,8 @@ def setup(self): self.add_cmd_output("mdadm -D /dev/md*") + self.add_blockdev_cmd("mdadm -E %(dev)s", + blacklist=['ram.*', 'zram.*']) self.add_copy_spec([ "/proc/mdstat", "/etc/mdadm.conf", diff -Nru sosreport-4.0/sos/report/plugins/memory.py sosreport-4.1/sos/report/plugins/memory.py --- sosreport-4.0/sos/report/plugins/memory.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/memory.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,11 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import (Plugin, RedHatPlugin, DebianPlugin, - UbuntuPlugin, CosPlugin) +from sos.report.plugins import Plugin, IndependentPlugin -class Memory(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin, CosPlugin): +class Memory(Plugin, IndependentPlugin): short_desc = 'Memory configuration and use' diff -Nru sosreport-4.0/sos/report/plugins/mpt.py sosreport-4.1/sos/report/plugins/mpt.py --- sosreport-4.0/sos/report/plugins/mpt.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/mpt.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Mpt(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Mpt(Plugin, IndependentPlugin): short_desc = 'LSI Message Passing Technology' files = ('/proc/mpt',) diff -Nru sosreport-4.0/sos/report/plugins/multipath.py sosreport-4.1/sos/report/plugins/multipath.py --- sosreport-4.0/sos/report/plugins/multipath.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/multipath.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Multipath(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Multipath(Plugin, IndependentPlugin): short_desc = 'Device-mapper multipath tools' diff -Nru sosreport-4.0/sos/report/plugins/mvcli.py sosreport-4.1/sos/report/plugins/mvcli.py --- sosreport-4.0/sos/report/plugins/mvcli.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/mvcli.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,35 @@ +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + + +# This sosreport plugin is meant for sas adapters. +# This plugin logs inforamtion on each adapter it finds. + +from sos.report.plugins import Plugin, IndependentPlugin + + +class mvCLI(Plugin, IndependentPlugin): + + short_desc = 'mvCLI Integrated RAID adapter information' + + plugin_name = "mvcli" + commands = ("/opt/marvell/bin/mvcli",) + + def setup(self): + + # get list of adapters + subcmds = [ + 'info -o vd', + 'info -o pd', + 'info -o hba', + 'smart -p 0', + ] + + self.add_cmd_output(["/opt/marvell/bin/mvcli %s" % s for s in subcmds]) + +# vim: et ts=4 sw=4 diff -Nru sosreport-4.0/sos/report/plugins/networking.py sosreport-4.1/sos/report/plugins/networking.py --- sosreport-4.0/sos/report/plugins/networking.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/networking.py 2021-02-25 18:46:49.000000000 +0000 @@ -9,7 +9,6 @@ from sos.report.plugins import (Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin, SoSPredicate) from os import listdir -from re import match class Networking(Plugin): @@ -28,41 +27,13 @@ ("namespaces", "Number of namespaces to collect, 0 for unlimited. " + "Incompatible with the namespace_pattern plugin option", "slow", 0), ("ethtool_namespaces", "Define if ethtool commands should be " + - "collected for namespaces", "slow", True) + "collected for namespaces", "slow", True), + ("eepromdump", "collect 'ethtool -e' for all devices", "slow", False) ] # switch to enable netstat "wide" (non-truncated) output mode ns_wide = "-W" - def collect_iptable(self, tablename): - """ Collecting iptables rules for a table loads either kernel module - of the table name (for kernel <= 3), or nf_tables (for kernel >= 4). - If neither module is present, the rules must be empty.""" - - modname = "iptable_" + tablename - cmd = "iptables -t " + tablename + " -nvL" - self.add_cmd_output( - cmd, - pred=SoSPredicate(self, kmods=[modname, 'nf_tables'])) - - def collect_ip6table(self, tablename): - """ Same as function above, but for ipv6 """ - - modname = "ip6table_" + tablename - cmd = "ip6tables -t " + tablename + " -nvL" - self.add_cmd_output( - cmd, - pred=SoSPredicate(self, kmods=[modname, 'nf_tables'])) - - def collect_nftables(self): - """ Collects nftables rulesets with 'nft' commands if the modules - are present """ - - self.add_cmd_output( - "nft list ruleset", - pred=SoSPredicate(self, kmods=['nf_tables']) - ) - def setup(self): super(Networking, self).setup() @@ -90,9 +61,6 @@ "/etc/host*", "/etc/resolv.conf", "/etc/network*", - "/etc/nftables", - "/etc/sysconfig/nftables.conf", - "/etc/nftables.conf", "/etc/dnsmasq*", "/sys/class/net/*/device/numa_node", "/sys/class/net/*/flags", @@ -114,23 +82,6 @@ self.add_cmd_output("ip -o addr", root_symlink="ip_addr") self.add_cmd_output("route -n", root_symlink="route") self.add_cmd_output("plotnetcfg") - # collect iptables -t for any existing table, if we can't read the - # tables, collect 3 default ones (nat, mangle, filter) - try: - ip_tables_names = open("/proc/net/ip_tables_names").read() - except IOError: - ip_tables_names = "nat\nmangle\nfilter\n" - for table in ip_tables_names.splitlines(): - self.collect_iptable(table) - # collect the same for ip6tables - try: - ip_tables_names = open("/proc/net/ip6_tables_names").read() - except IOError: - ip_tables_names = "nat\nmangle\nfilter\n" - for table in ip_tables_names.splitlines(): - self.collect_ip6table(table) - - self.collect_nftables() self.add_cmd_output("netstat %s -neopa" % self.ns_wide, root_symlink="netstat") @@ -151,8 +102,16 @@ "ip neigh show nud noarp", "biosdevname -d", "tc -s qdisc show", + "devlink dev param show", + "devlink dev info", ]) + devlinks = self.collect_cmd_output("devlink dev") + if devlinks['status'] == 0: + devlinks_list = devlinks['output'].splitlines() + for devlink in devlinks_list: + self.add_cmd_output("devlink dev eswitch show %s" % devlink) + # below commands require some kernel module(s) to be loaded # run them only if the modules are loaded, or if explicitly requested # via --allow-system-changes option @@ -167,20 +126,6 @@ ], required={'kmods': 'all'}) self.add_cmd_output(ss_cmd, pred=ss_pred, changes=True) - # When iptables is called it will load the modules - # iptables_filter (for kernel <= 3) or - # nf_tables (for kernel >= 4) if they are not loaded. - # The same goes for ipv6. - self.add_cmd_output( - "iptables -vnxL", - pred=SoSPredicate(self, kmods=['iptable_filter', 'nf_tables']) - ) - - self.add_cmd_output( - "ip6tables -vnxL", - pred=SoSPredicate(self, kmods=['ip6table_filter', 'nf_tables']) - ) - # Get ethtool output for every device that does not exist in a # namespace. for eth in listdir("/sys/class/net/"): @@ -202,19 +147,19 @@ "ethtool -l " + eth, "ethtool --phy-statistics " + eth, "ethtool --show-priv-flags " + eth, - "ethtool --show-eee " + eth + "ethtool --show-eee " + eth, + "tc -s filter show dev " + eth ], tags=eth) - # skip EEPROM collection for 'bnx2x' NICs as this command - # can pause the NIC and is not production safe. - bnx_output = { - "cmd": "ethtool -i %s" % eth, - "output": "bnx2x" - } - bnx_pred = SoSPredicate(self, - cmd_outputs=bnx_output, - required={'cmd_outputs': 'none'}) - self.add_cmd_output("ethtool -e %s" % eth, pred=bnx_pred) + # skip EEPROM collection by default, as it might hang or + # negatively impact the system on some device types + if self.get_option("eepromdump"): + cmd = "ethtool -e %s" % eth + self._log_warn("WARNING (about to collect '%s'): collecting " + "an eeprom dump is known to cause certain NIC " + "drivers (e.g. bnx2x/tg3) to interrupt device " + "operation" % cmd) + self.add_cmd_output(cmd) # Collect information about bridges (some data already collected via # "ip .." commands) @@ -230,78 +175,54 @@ # Capture additional data from namespaces; each command is run # per-namespace. - ip_netns = self.collect_cmd_output("ip netns") + self.add_cmd_output("ip netns") cmd_prefix = "ip netns exec " - if ip_netns['status'] == 0: - out_ns = [] - # Regex initialization outside of for loop - if self.get_option("namespace_pattern"): - pattern = '(?:%s$)' % '$|'.join( - self.get_option("namespace_pattern").split() - ).replace('*', '.*') - for line in ip_netns['output'].splitlines(): - # If there's no namespaces, no need to continue - if line.startswith("Object \"netns\" is unknown") \ - or line.isspace() \ - or line[:1].isspace(): - continue - # if namespace_pattern defined, append only namespaces - # matching with pattern - if self.get_option("namespace_pattern"): - if bool(match(pattern, line)): - out_ns.append(line.partition(' ')[0]) - - # if namespaces is defined and namespace_pattern is not defined - # remove from out_ns namespaces with higher index than defined - elif self.get_option("namespaces") != 0: - out_ns.append(line.partition(' ')[0]) - if len(out_ns) == self.get_option("namespaces"): - self._log_warn("Limiting namespace iteration " + - "to first %s namespaces found" - % self.get_option("namespaces")) - break - else: - out_ns.append(line.partition(' ')[0]) - - for namespace in out_ns: + for namespace in self.get_network_namespaces( + self.get_option("namespace_pattern"), + self.get_option("namespaces")): + ns_cmd_prefix = cmd_prefix + namespace + " " + self.add_cmd_output([ + ns_cmd_prefix + "ip address show", + ns_cmd_prefix + "ip route show table all", + ns_cmd_prefix + "ip -s -s neigh show", + ns_cmd_prefix + "ip rule list", + ns_cmd_prefix + "iptables-save", + ns_cmd_prefix + "ip6tables-save", + ns_cmd_prefix + "netstat %s -neopa" % self.ns_wide, + ns_cmd_prefix + "netstat -s", + ns_cmd_prefix + "netstat %s -agn" % self.ns_wide, + ]) + + ss_cmd = ns_cmd_prefix + "ss -peaonmi" + # --allow-system-changes is handled directly in predicate + # evaluation, so plugin code does not need to separately + # check for it + self.add_cmd_output(ss_cmd, pred=ss_pred) + + # Collect ethtool commands only when ethtool_namespaces + # is set to true. + if self.get_option("ethtool_namespaces"): + # Devices that exist in a namespace use less ethtool + # parameters. Run this per namespace. + for namespace in self.get_network_namespaces( + self.get_option("namespace_pattern"), + self.get_option("namespaces")): ns_cmd_prefix = cmd_prefix + namespace + " " - self.add_cmd_output([ - ns_cmd_prefix + "ip address show", - ns_cmd_prefix + "ip route show table all", - ns_cmd_prefix + "iptables-save", - ns_cmd_prefix + "netstat %s -neopa" % self.ns_wide, - ns_cmd_prefix + "netstat -s", - ns_cmd_prefix + "netstat %s -agn" % self.ns_wide, - ]) - - ss_cmd = ns_cmd_prefix + "ss -peaonmi" - # --allow-system-changes is handled directly in predicate - # evaluation, so plugin code does not need to separately - # check for it - self.add_cmd_output(ss_cmd, pred=ss_pred) - - # Collect ethtool commands only when ethtool_namespaces - # is set to true. - if self.get_option("ethtool_namespaces"): - # Devices that exist in a namespace use less ethtool - # parameters. Run this per namespace. - for namespace in out_ns: - ns_cmd_prefix = cmd_prefix + namespace + " " - netns_netdev_list = self.exec_cmd( - ns_cmd_prefix + "ls -1 /sys/class/net/" - ) - for eth in netns_netdev_list['output'].splitlines(): - # skip 'bonding_masters' file created when loading the - # bonding module but the file does not correspond to - # a device - if eth == "bonding_masters": - continue - self.add_cmd_output([ - ns_cmd_prefix + "ethtool " + eth, - ns_cmd_prefix + "ethtool -i " + eth, - ns_cmd_prefix + "ethtool -k " + eth, - ns_cmd_prefix + "ethtool -S " + eth - ]) + netns_netdev_list = self.exec_cmd( + ns_cmd_prefix + "ls -1 /sys/class/net/" + ) + for eth in netns_netdev_list['output'].splitlines(): + # skip 'bonding_masters' file created when loading the + # bonding module but the file does not correspond to + # a device + if eth == "bonding_masters": + continue + self.add_cmd_output([ + ns_cmd_prefix + "ethtool " + eth, + ns_cmd_prefix + "ethtool -i " + eth, + ns_cmd_prefix + "ethtool -k " + eth, + ns_cmd_prefix + "ethtool -S " + eth + ]) return @@ -333,18 +254,13 @@ "/etc/resolvconf", "/etc/network/interfaces", "/etc/network/interfaces.d", - "/etc/ufw", - "/var/log/ufw.Log", "/etc/resolv.conf", "/run/netplan/*.yaml", "/etc/netplan/*.yaml", "/lib/netplan/*.yaml", "/run/systemd/network" ]) - self.add_cmd_output([ - "ufw status numbered", - "ufw app list" - ]) + if self.get_option("traceroute"): self.add_cmd_output("/usr/sbin/traceroute -n %s" % self.trace_host) diff -Nru sosreport-4.0/sos/report/plugins/networkmanager.py sosreport-4.1/sos/report/plugins/networkmanager.py --- sosreport-4.0/sos/report/plugins/networkmanager.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/networkmanager.py 2021-02-25 18:46:49.000000000 +0000 @@ -109,7 +109,6 @@ for net_conf in files: self.do_file_sub( "/etc/NetworkManager/system-connections/"+net_conf, - r"psk=(.*)", r"psk=***") - + r"(psk|password)=(.*)", r"\1=***") # vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/report/plugins/nfsganesha.py sosreport-4.1/sos/report/plugins/nfsganesha.py --- sosreport-4.0/sos/report/plugins/nfsganesha.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/nfsganesha.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class NfsGanesha(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class NfsGanesha(Plugin, IndependentPlugin): short_desc = 'NFS-Ganesha file server information' plugin_name = 'nfsganesha' diff -Nru sosreport-4.0/sos/report/plugins/nfs.py sosreport-4.1/sos/report/plugins/nfs.py --- sosreport-4.0/sos/report/plugins/nfs.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/nfs.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Nfs(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Nfs(Plugin, IndependentPlugin): short_desc = 'Network file system information' plugin_name = 'nfs' diff -Nru sosreport-4.0/sos/report/plugins/nginx.py sosreport-4.1/sos/report/plugins/nginx.py --- sosreport-4.0/sos/report/plugins/nginx.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/nginx.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Nginx(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Nginx(Plugin, IndependentPlugin): short_desc = 'nginx http daemon' plugin_name = "nginx" diff -Nru sosreport-4.0/sos/report/plugins/nis.py sosreport-4.1/sos/report/plugins/nis.py --- sosreport-4.0/sos/report/plugins/nis.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/nis.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Nis(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Nis(Plugin, IndependentPlugin): short_desc = 'Network Information Service' plugin_name = 'nis' diff -Nru sosreport-4.0/sos/report/plugins/npm.py sosreport-4.1/sos/report/plugins/npm.py --- sosreport-4.0/sos/report/plugins/npm.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/npm.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,13 +8,11 @@ # # See the LICENSE file in the source distribution for further information. import os -import json -from sos.report.plugins import (Plugin, RedHatPlugin, DebianPlugin, - UbuntuPlugin, SuSEPlugin) +from sos.report.plugins import Plugin, IndependentPlugin -class Npm(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin, SuSEPlugin): +class Npm(Plugin, IndependentPlugin): short_desc = 'Information from available npm modules' plugin_name = 'npm' @@ -36,45 +34,6 @@ runat=working_directory ) - def _find_modules_in_npm_cache(self): - """ - Example 'npm cache ls' output - ~/.npm - ~/.npm/acorn - ~/.npm/acorn/1.2.2 - ~/.npm/acorn/1.2.2/package.tgz - ~/.npm/acorn/1.2.2/package - ~/.npm/acorn/1.2.2/package/package.json - ~/.npm/acorn/4.0.3 - ~/.npm/acorn/4.0.3/package.tgz - ~/.npm/acorn/4.0.3/package - ~/.npm/acorn/4.0.3/package/package.json - ~/.npm/registry.npmjs.org - ~/.npm/registry.npmjs.org/acorn - ~/.npm/registry.npmjs.org/acorn/.cache.json - - https://docs.npmjs.com/cli/cache - """ - output = {} - # with chroot=True (default) the command fails when run as non-root - user_cache = self.collect_cmd_output("npm cache ls", chroot=False) - if user_cache['status'] == 0: - # filter out dirs with .cache.json ('registry.npmjs.org') - for package in [line for line in user_cache['output'].splitlines() - if line.endswith('package.tgz')]: - five_tuple = package.split(os.path.sep) - if len(five_tuple) != 5: # sanity check - continue - home, cache, name, version, package_tgz = five_tuple - if name not in output: - output[name] = [version] - else: - output[name].append(version) - self._log_debug("modules in cache: %s" % output) - - outfn = self._make_command_filename("npm_cache_modules") - self.add_string_as_file(json.dumps(output), outfn) - def setup(self): if self.get_option("project_path"): project_path = os.path.abspath(os.path.expanduser( @@ -87,7 +46,6 @@ self._get_npm_output("npm ls -g --json", "npm_ls_global") self._get_npm_output("npm config list -l", "npm_config_list_global") - self._find_modules_in_npm_cache() class NpmViaNodeJS(Npm): diff -Nru sosreport-4.0/sos/report/plugins/nscd.py sosreport-4.1/sos/report/plugins/nscd.py --- sosreport-4.0/sos/report/plugins/nscd.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/nscd.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Nscd(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Nscd(Plugin, IndependentPlugin): short_desc = 'Name service caching daemon' plugin_name = 'nscd' diff -Nru sosreport-4.0/sos/report/plugins/nss.py sosreport-4.1/sos/report/plugins/nss.py --- sosreport-4.0/sos/report/plugins/nss.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/nss.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class NSS(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class NSS(Plugin, IndependentPlugin): short_desc = 'Network Security Services configuration' diff -Nru sosreport-4.0/sos/report/plugins/numa.py sosreport-4.1/sos/report/plugins/numa.py --- sosreport-4.0/sos/report/plugins/numa.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/numa.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,11 +8,11 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin import os.path -class Numa(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Numa(Plugin, IndependentPlugin): short_desc = 'NUMA state and configuration' diff -Nru sosreport-4.0/sos/report/plugins/nvidia.py sosreport-4.1/sos/report/plugins/nvidia.py --- sosreport-4.0/sos/report/plugins/nvidia.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/nvidia.py 2021-02-25 18:46:49.000000000 +0000 @@ -9,10 +9,10 @@ # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Nvidia(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Nvidia(Plugin, IndependentPlugin): short_desc = 'Nvidia GPU information' diff -Nru sosreport-4.0/sos/report/plugins/nvme.py sosreport-4.1/sos/report/plugins/nvme.py --- sosreport-4.0/sos/report/plugins/nvme.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/nvme.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Nvme(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Nvme(Plugin, IndependentPlugin): short_desc = 'Collect config and system information about NVMe devices' diff -Nru sosreport-4.0/sos/report/plugins/nvmetcli.py sosreport-4.1/sos/report/plugins/nvmetcli.py --- sosreport-4.0/sos/report/plugins/nvmetcli.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/nvmetcli.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class NvmetCli(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class NvmetCli(Plugin, IndependentPlugin): short_desc = 'Collect config and system information for nvmetcli' diff -Nru sosreport-4.0/sos/report/plugins/omsa.py sosreport-4.1/sos/report/plugins/omsa.py --- sosreport-4.0/sos/report/plugins/omsa.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/omsa.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class omsa(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class omsa(Plugin, IndependentPlugin): short_desc = 'Dell OpenManage Server Administrator (OMSA)' diff -Nru sosreport-4.0/sos/report/plugins/opencl.py sosreport-4.1/sos/report/plugins/opencl.py --- sosreport-4.0/sos/report/plugins/opencl.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/opencl.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class OpenCL(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class OpenCL(Plugin, IndependentPlugin): short_desc = 'OpenCL' diff -Nru sosreport-4.0/sos/report/plugins/opengl.py sosreport-4.1/sos/report/plugins/opengl.py --- sosreport-4.0/sos/report/plugins/opengl.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/opengl.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class OpenGL(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class OpenGL(Plugin, IndependentPlugin): short_desc = 'OpenGL' diff -Nru sosreport-4.0/sos/report/plugins/openstack_ansible.py sosreport-4.1/sos/report/plugins/openstack_ansible.py --- sosreport-4.0/sos/report/plugins/openstack_ansible.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/openstack_ansible.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class OpenStackAnsible(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class OpenStackAnsible(Plugin, IndependentPlugin): short_desc = 'OpenStack-Ansible' diff -Nru sosreport-4.0/sos/report/plugins/openstack_aodh.py sosreport-4.1/sos/report/plugins/openstack_aodh.py --- sosreport-4.0/sos/report/plugins/openstack_aodh.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/openstack_aodh.py 2021-02-25 18:46:49.000000000 +0000 @@ -10,16 +10,15 @@ # See the LICENSE file in the source distribution for further information. import os -from sos.report.plugins import Plugin, RedHatPlugin +from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin -class OpenStackAodh(Plugin, RedHatPlugin): +class OpenStackAodh(Plugin): short_desc = 'OpenStack Alarm service' plugin_name = "openstack_aodh" profiles = ('openstack', 'openstack_controller') - packages = ('openstack-selinux',) var_puppet_gen = "/var/lib/config-data/puppet-generated/aodh" def setup(self): @@ -88,4 +87,19 @@ ) +class DebianOpenStackAodh(OpenStackAodh, DebianPlugin, UbuntuPlugin): + + packages = ( + 'aodh-api', + 'aodh-evaluator', + 'aodh-notifier', + 'aodh-listener', + 'python-aodhclient' + ) + + +class RedHatOpenStackAodh(OpenStackAodh, RedHatPlugin): + + packages = ('openstack-selinux',) + # vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/report/plugins/openstack_barbican.py sosreport-4.1/sos/report/plugins/openstack_barbican.py --- sosreport-4.0/sos/report/plugins/openstack_barbican.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/openstack_barbican.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,54 @@ +# Copyright (C) 2019 Mirantis, Inc., Denis Egorenko + +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +from sos.report.plugins import Plugin, DebianPlugin, UbuntuPlugin + + +class OpenStackBarbican(Plugin, DebianPlugin, UbuntuPlugin): + """OpenStack Barbican Secure storage service""" + plugin_name = "openstack_barbican" + profiles = ('openstack', 'openstack_controller') + + packages = ( + 'barbican-common', + 'barbican-keystone-listener', + 'barbican-worker' + ) + + requires_root = False + + def setup(self): + self.add_copy_spec("/etc/barbican/") + + if self.get_option("all_logs"): + self.add_copy_spec("/var/log/barbican/*") + else: + self.add_copy_spec("/var/log/barbican/*.log") + + self.add_forbidden_path("/etc/barbican/*.pem") + self.add_forbidden_path("/etc/barbican/alias/*") + + def postproc(self): + self.do_file_sub( + "/etc/barbican/barbican.conf", + r"(password|rabbit_password[\t\ ]*=[\t\ ]*)(.+)", + r"\1********" + ) + + connection_keys = ["transport_url", "sql_connection"] + + self.do_path_regex_sub( + "/etc/barbican/barbican.conf", + r"((?m)^\s*(%s)\s*=\s*(.*)://(\w*):)(.*)(@(.*))" % + "|".join(connection_keys), + r"\1*********\6") + + +# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/report/plugins/openstack_ceilometer.py sosreport-4.1/sos/report/plugins/openstack_ceilometer.py --- sosreport-4.0/sos/report/plugins/openstack_ceilometer.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/openstack_ceilometer.py 2021-02-25 18:46:49.000000000 +0000 @@ -51,10 +51,9 @@ "admin_password", "connection_password", "host_password", "memcache_secret_key", "os_password", "password", "qpid_password", "rabbit_password", "readonly_user_password", "secret_key", - "ssl_key_password", "telemetry_secret", "metering_secret", - "transport_url" + "ssl_key_password", "telemetry_secret", "metering_secret" ] - connection_keys = ["connection"] + connection_keys = ["connection", "backend_url", "transport_url"] self.apply_regex_sub( r"((?m)^\s*(%s)\s*=\s*)(.*)" % "|".join(protect_keys), diff -Nru sosreport-4.0/sos/report/plugins/openstack_cinder.py sosreport-4.1/sos/report/plugins/openstack_cinder.py --- sosreport-4.0/sos/report/plugins/openstack_cinder.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/openstack_cinder.py 2021-02-25 18:46:49.000000000 +0000 @@ -30,7 +30,7 @@ # check if either standalone (cinder-api) or httpd wsgi (cinder_wsgi) # is up and running - cinder_process = ["cinder_wsgi", "cinder-api"] + cinder_process = ["cinder_wsgi", "cinder-wsgi", "cinder-api"] in_ps = False for process in cinder_process: in_ps = self.check_process_by_name(process) @@ -48,6 +48,7 @@ suggest_filename="cinder_db_version" ) + self.add_forbidden_path('/etc/cinder/volumes') self.add_copy_spec([ "/etc/cinder/", self.var_puppet_gen + "/etc/cinder/", @@ -117,6 +118,14 @@ def setup(self): super(DebianCinder, self).setup() + if self.get_option("all_logs"): + self.add_copy_spec([ + "/var/log/apache/cinder*", + ]) + else: + self.add_copy_spec([ + "/var/log/apache/cinder*.log", + ]) class RedHatCinder(OpenStackCinder, RedHatPlugin): diff -Nru sosreport-4.0/sos/report/plugins/openstack_glance.py sosreport-4.1/sos/report/plugins/openstack_glance.py --- sosreport-4.0/sos/report/plugins/openstack_glance.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/openstack_glance.py 2021-02-25 18:46:49.000000000 +0000 @@ -24,6 +24,7 @@ option_list = [] var_puppet_gen = "/var/lib/config-data/puppet-generated/glance_api" + service_name = "openstack-glance-api.service" def setup(self): if self.get_option("all_logs"): @@ -45,7 +46,7 @@ # is running in_container = self.container_exists('glance_api') - if self.is_service_running('openstack-glance-api') or in_container: + if self.is_service_running(self.service_name) or in_container: glance_config = "" # if containerized we need to pass the config to the cont. if in_container: @@ -107,6 +108,7 @@ 'glance-registry', 'python-glance' ) + service_name = 'glance-api.service' class RedHatGlance(OpenStackGlance, RedHatPlugin): diff -Nru sosreport-4.0/sos/report/plugins/openstack_heat.py sosreport-4.1/sos/report/plugins/openstack_heat.py --- sosreport-4.0/sos/report/plugins/openstack_heat.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/openstack_heat.py 2021-02-25 18:46:49.000000000 +0000 @@ -21,6 +21,7 @@ option_list = [] var_puppet_gen = "/var/lib/config-data/puppet-generated/heat" + service_name = "openstack-heat-api.service" def setup(self): @@ -28,7 +29,7 @@ # is running in_container = self.container_exists('.*heat_api') - if self.is_service_running('openstack-heat-api') or in_container: + if self.is_service_running(self.service_name) or in_container: heat_config = "" # if containerized we need to pass the config to the cont. if in_container: @@ -126,6 +127,7 @@ 'python-heat', 'python-heatclient' ) + service_name = 'heat-api.service' class RedHatHeat(OpenStackHeat, RedHatPlugin): diff -Nru sosreport-4.0/sos/report/plugins/openstack_ironic.py sosreport-4.1/sos/report/plugins/openstack_ironic.py --- sosreport-4.0/sos/report/plugins/openstack_ironic.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/openstack_ironic.py 2021-02-25 18:46:49.000000000 +0000 @@ -20,41 +20,95 @@ profiles = ('openstack', 'openstack_undercloud') var_puppet_gen = "/var/lib/config-data/puppet-generated/ironic" + ins_puppet_gen = var_puppet_gen + "_inspector" def setup(self): - self.conf_list = [ - "/etc/ironic/*", - self.var_puppet_gen + "/etc/ironic/*", - self.var_puppet_gen + "_api/etc/ironic/*" - ] - self.add_copy_spec([ - "/etc/ironic/", - self.var_puppet_gen + "/etc/xinetd.conf", - self.var_puppet_gen + "/etc/xinetd.d/", - self.var_puppet_gen + "/etc/ironic/", - self.var_puppet_gen + "/etc/httpd/conf/", - self.var_puppet_gen + "/etc/httpd/conf.d/", - self.var_puppet_gen + "/etc/httpd/conf.modules.d/*.conf", - self.var_puppet_gen + "/etc/my.cnf.d/tripleo.cnf", - self.var_puppet_gen + "_api/etc/ironic/", - self.var_puppet_gen + "_api/etc/httpd/conf/", - self.var_puppet_gen + "_api/etc/httpd/conf.d/", - self.var_puppet_gen + "_api/etc/httpd/conf.modules.d/*.conf", - self.var_puppet_gen + "_api/etc/my.cnf.d/tripleo.cnf" - ]) - if self.get_option("all_logs"): + in_container = self.container_exists('.*ironic_api') + + if in_container: + self.conf_list = [ + self.var_puppet_gen + "/etc/ironic/*", + self.var_puppet_gen + "/etc/ironic-inspector/*", + self.var_puppet_gen + "_api/etc/ironic/*", + self.ins_puppet_gen + "/etc/ironic-inspector/*", + self.ins_puppet_gen + "/var/lib/httpboot/inspector.ipxe" + ] self.add_copy_spec([ - "/var/log/ironic/", + "/var/lib/ironic-inspector/", + "/var/log/containers/ironic-inspector/ramdisk/", + self.var_puppet_gen + "/etc/xinetd.conf", + self.var_puppet_gen + "/etc/xinetd.d/", + self.var_puppet_gen + "/etc/ironic/", + self.var_puppet_gen + "/etc/ironic-inspector/", + self.var_puppet_gen + "/etc/httpd/conf/", + self.var_puppet_gen + "/etc/httpd/conf.d/", + self.var_puppet_gen + "/etc/httpd/conf.modules.d/*.conf", + self.var_puppet_gen + "/etc/my.cnf.d/tripleo.cnf", + self.var_puppet_gen + "_api/etc/ironic/", + self.var_puppet_gen + "_api/etc/httpd/conf/", + self.var_puppet_gen + "_api/etc/httpd/conf.d/", + self.var_puppet_gen + "_api/etc/httpd/conf.modules.d/*.conf", + self.var_puppet_gen + "_api/etc/my.cnf.d/tripleo.cnf", + self.ins_puppet_gen + "/etc/ironic-inspector/*", + self.ins_puppet_gen + "/var/lib/httpboot/inspector.ipxe" ]) + + if self.get_option("all_logs"): + self.add_copy_spec([ + "/var/log/containers/ironic/", + "/var/log/containers/ironic-inspector/" + ]) + else: + self.add_copy_spec([ + "/var/log/containers/ironic/*.log", + "/var/log/containers/ironic-inspector/*.log", + ]) + + for path in ['/var/lib/ironic', '/httpboot', '/tftpboot', + self.ins_puppet_gen + '/var/lib/httpboot/', + self.ins_puppet_gen + '/var/lib/tftpboot/']: + self.add_cmd_output('ls -laRt %s' % path) + self.add_cmd_output('ls -laRt %s' % + (self.var_puppet_gen + path)) + + # Let's get the packages from the containers, always helpful when + # troubleshooting. + for container_name in ['ironic_inspector_dnsmasq', + 'ironic_inspector', 'ironic_pxe_http', + 'ironic_pxe_tftp', 'ironic_neutron_agent', + 'ironic_conductor', 'ironic_api']: + if self.container_exists('.*' + container_name): + self.add_cmd_output(self.fmt_container_cmd(container_name, + 'rpm -qa')) + else: + self.conf_list = [ + "/etc/ironic/*", + "/etc/ironic-inspector/*", + ] self.add_copy_spec([ - "/var/log/ironic/*.log", + "/etc/ironic/", + "/etc/ironic-inspector/", + "/var/lib/ironic-inspector/", + "/var/log/ironic-inspector/ramdisk/", + "/etc/my.cnf.d/tripleo.cnf", + "/var/lib/httpboot/inspector.ipxe" ]) - for path in ['/var/lib/ironic', '/httpboot', '/tftpboot']: - self.add_cmd_output('ls -laRt %s' % path) - self.add_cmd_output('ls -laRt %s' % (self.var_puppet_gen + path)) + if self.get_option("all_logs"): + self.add_copy_spec([ + "/var/log/ironic/", + "/var/log/ironic-inspector/", + ]) + else: + self.add_copy_spec([ + "/var/log/ironic/*.log", + "/var/log/ironic-inspector/*.log", + ]) + + for path in ['/var/lib/ironic', '/httpboot', '/tftpboot']: + self.add_cmd_output('ls -laRt %s' % path) vars_all = [p in os.environ for p in [ 'OS_USERNAME', 'OS_PASSWORD']] @@ -136,6 +190,7 @@ def setup(self): super(RedHatIronic, self).setup() + # ironic-discoverd was renamed to ironic-inspector in Liberty # is the optional ironic-discoverd service installed? if any([self.is_installed(p) for p in self.discoverd_packages]): self.conf_list.append('/etc/ironic-discoverd/*') @@ -146,18 +201,6 @@ self.add_journal(units="openstack-ironic-discoverd") self.add_journal(units="openstack-ironic-discoverd-dnsmasq") - # ironic-discoverd was renamed to ironic-inspector in Liberty - self.conf_list.append('/etc/ironic-inspector/*') - self.conf_list.append(self.var_puppet_gen + '/etc/ironic-inspector/*') - self.add_copy_spec('/etc/ironic-inspector/') - self.add_copy_spec(self.var_puppet_gen + '/etc/ironic-inspector/') - self.add_copy_spec('/var/lib/ironic-inspector/') - if self.get_option("all_logs"): - self.add_copy_spec('/var/log/ironic-inspector/') - else: - self.add_copy_spec('/var/log/ironic-inspector/*.log') - self.add_copy_spec('/var/log/ironic-inspector/ramdisk/') - self.add_journal(units="openstack-ironic-inspector-dnsmasq") if self.osc_available: diff -Nru sosreport-4.0/sos/report/plugins/openstack_nova.py sosreport-4.1/sos/report/plugins/openstack_nova.py --- sosreport-4.0/sos/report/plugins/openstack_nova.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/openstack_nova.py 2021-02-25 18:46:49.000000000 +0000 @@ -24,6 +24,7 @@ profiles = ('openstack', 'openstack_controller', 'openstack_compute') var_puppet_gen = "/var/lib/config-data/puppet-generated/nova" + service_name = "openstack-nova-api.service" def setup(self): @@ -31,7 +32,7 @@ # is running in_container = self.container_exists('.*nova_api') - if self.is_service_running('openstack-nova-api') or in_container: + if self.is_service_running(self.service_name) or in_container: nova_config = "" # if containerized we need to pass the config to the cont. if in_container: @@ -184,6 +185,7 @@ 'python-novaclient', 'python-novnc' ) + service_name = "nova-api.service" def setup(self): super(DebianNova, self).setup() diff -Nru sosreport-4.0/sos/report/plugins/openstack_octavia.py sosreport-4.1/sos/report/plugins/openstack_octavia.py --- sosreport-4.0/sos/report/plugins/openstack_octavia.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/openstack_octavia.py 2021-02-25 18:46:49.000000000 +0000 @@ -107,7 +107,18 @@ class DebianOctavia(OpenStackOctavia, DebianPlugin, UbuntuPlugin): - packages = ('octavia-common',) + packages = ('octavia-common', 'octavia-api', ) + + def setup(self): + super(DebianOctavia, self).setup() + if self.get_option("all_logs"): + self.add_copy_spec([ + "/var/log/apache2/octavia*", + ]) + else: + self.add_copy_spec([ + "/var/log/apache2/octavia*.log", + ]) class RedHatOctavia(OpenStackOctavia, RedHatPlugin): diff -Nru sosreport-4.0/sos/report/plugins/openstack_placement.py sosreport-4.1/sos/report/plugins/openstack_placement.py --- sosreport-4.0/sos/report/plugins/openstack_placement.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/openstack_placement.py 2021-02-25 18:46:49.000000000 +0000 @@ -18,6 +18,7 @@ profiles = ('openstack', 'openstack_controller') var_puppet_gen = "/var/lib/config-data/puppet-generated/placement" + service_name = 'openstack-placement-api' def setup(self): @@ -26,7 +27,7 @@ in_container = self.container_exists('.*placement_api') - if self.is_service_running('openstack-placement-api') or in_container: + if self.is_service_running(self.service_name) or in_container: placement_config = "" # if containerized we need to pass the config to the cont. if in_container: @@ -84,9 +85,14 @@ class DebianPlacement(OpenStackPlacement, DebianPlugin, UbuntuPlugin): packages = ('placement') + service_name = 'placement-api' def setup(self): super(DebianPlacement, self).setup() + if self.get_option("all_logs"): + self.add_copy_spec("/var/log/apache2/placement*") + else: + self.add_copy_spec("/var/log/apache2/placement*.log") class RedHatPlacement(OpenStackPlacement, RedHatPlugin): diff -Nru sosreport-4.0/sos/report/plugins/openstack_tripleo.py sosreport-4.1/sos/report/plugins/openstack_tripleo.py --- sosreport-4.0/sos/report/plugins/openstack_tripleo.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/openstack_tripleo.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,11 +8,11 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin import re -class OpenStackTripleO(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class OpenStackTripleO(Plugin, IndependentPlugin): short_desc = 'Installation information from OpenStack Installer' @@ -23,12 +23,14 @@ def setup(self): # Notes: recursion is max 2 for container-puppet and tripleo-config # Those directories are present on all OpenStack nodes - self.add_copy_spec([ + self.tripleo_log_paths = [ '/var/log/paunch.log', '/var/lib/container-puppet/', '/var/lib/tripleo-config/', + '/var/lib/tripleo/', '/etc/puppet/hieradata/' - ]) + ] + self.add_copy_spec(self.tripleo_log_paths) def postproc(self): # Ensures we do not leak passwords from the tripleo-config and @@ -38,10 +40,7 @@ r'([":\s]+)(.*[^"])([",]+)' rgxp = re.compile(secrets, re.IGNORECASE) - self.do_path_regex_sub('/var/lib/tripleo-config/', - rgxp, r'\1\3*********\5') - self.do_path_regex_sub('/etc/puppet/hieradata/', - rgxp, r'\1\3*********\5') - + for path in self.tripleo_log_paths: + self.do_path_regex_sub(path, rgxp, r'\1\3*********\5') # vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/report/plugins/openvswitch.py sosreport-4.1/sos/report/plugins/openvswitch.py --- sosreport-4.0/sos/report/plugins/openvswitch.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/openvswitch.py 2021-02-25 18:46:49.000000000 +0000 @@ -13,6 +13,8 @@ from os.path import join as path_join from os import environ +import re + class OpenVSwitch(Plugin): @@ -33,6 +35,31 @@ "ovs-vsctl -t 5 get Open_vSwitch . other_config:dpdk-init") check_dpdk = (dpdk_enabled["status"] == 0 and dpdk_enabled["output"].startswith('"true"')) + check_6wind = any([self.is_installed(p) for p in + ['6windgate-fp', 'nuage-openvswitch']]) + actl = "ovs-appctl" + + files_6wind = [ + "/etc/systemd/system/multi-user.target.wants/openvswitch.service", + "/etc/sysctl.d/60-6wind-system-auto-reboot.conf", + "/etc/openvswitch/system-id.conf", + "/etc/openvswitch/*.db", + "/etc/ld.so.conf.d/linux-fp-sync-fptun.conf", + "/etc/NetworkManager/conf.d/fpn0.conf", + "/etc/default/openvswitch", + "/etc/logrotate.d/openvswitch", + "/etc/linux-fp-sync.env", + "/etc/fp-daemons.env", + "/etc/fp-vdev.ini", + "/etc/fpm.env", + "/etc/6WINDGate/fp.config", + "/etc/6WINDGate/fpnsdk.config", + "/etc/dms.d/fp-dms.conf", + "/etc/dms.d/fpmd-dms.conf", + "/etc/dms.d/fpsd-dms.conf", + "/etc/fast-path.env", + "/etc/fps-fp.env", + ] if environ.get('OVS_LOGDIR'): log_dirs.append(environ.get('OVS_LOGDIR')) @@ -44,7 +71,8 @@ self.add_copy_spec([ "/run/openvswitch/ovsdb-server.pid", - "/run/openvswitch/ovs-vswitchd.pid" + "/run/openvswitch/ovs-vswitchd.pid", + "/run/openvswitch/ovs-monitor-ipsec.pid" ]) self.add_cmd_output([ @@ -60,7 +88,7 @@ "ls -laZ /var/lib/vhost_sockets", # List devices and their drivers "dpdk_nic_bind --status", - "dpdk_devbind.py --status", + "dpdk-devbind.py --status", "driverctl list-devices", "driverctl list-overrides", # Capture a list of all bond devices @@ -95,7 +123,11 @@ # Capture DPDK pmd performance counters "ovs-appctl dpif-netdev/pmd-perf-show", # Capture ofproto tunnel configs - "ovs-appctl ofproto/list-tunnels" + "ovs-appctl ofproto/list-tunnels", + # Capture ipsec tunnel information + "ovs-appctl -t ovs-monitor-ipsec tunnels/show", + "ovs-appctl -t ovs-monitor-ipsec xfrm/state", + "ovs-appctl -t ovs-monitor-ipsec xfrm/policies" ]) # Gather systemd services logs @@ -103,17 +135,63 @@ self.add_journal(units="openvswitch-nonetwork") self.add_journal(units="ovs-vswitchd") self.add_journal(units="ovsdb-server") + self.add_journal(units="ovs-configuration") + self.add_journal(units="openvswitch-ipsec") + + if check_6wind: + self.add_copy_spec(files_6wind) + self.add_cmd_output([ + # Various fast-path stats + "fp-cli fp-vswitch-stats", + "fp-cli dpdk-core-port-mapping", + "fp-cpu-usage", + "fp-cli fp-vswitch-masks", + "fp-cli fp-vswitch-flows", + "fp-shmem-dpvi", + "fp-cli stats non-zero", + "fp-cli stats", + "fp-cli dpdk-cp-filter-budget", + "ovs-appctl vm/port-detailed-show", + "ovs-appctl upcall/show", + "fp-cli nfct4", + "ovs-appctl vm/port-vip-list-show", + "fp-shmem-ports -s", + "ovs-dpctl show -s", + "fpcmd fp-vswitch-flows", + "fp-cli fp-vswitch-ports percore", + "fp-cli dpdk-debug-pool", + "fp-cli dump-size", + "fp-cli conf runtime", + "fp-cli conf compiled", + "fp-cli iface", + "ovs-appctl memory/show", + ]) + self.add_journal(units="virtual-accelerator") + for table in ['filter', 'mangle', 'raw', 'nat']: + self.add_cmd_output(["fpcmd nf4-rules %s" % table]) + + # 6wind doesn't care on which bridge the ports are, there's only + # one bridge and it's alubr0 + port_list = self.collect_cmd_output("fp-cli fp-vswitch-ports") + if port_list['status'] == 0: + for port in port_list['output'].splitlines(): + m = re.match(r'^([\d]+):[\s]+([^\s]+)', port) + if m: + port_name = m.group(2) + self.add_cmd_output([ + "fp-cli dpdk-cp-filter-budget %s" % port_name, + ]) # Gather the datapath information for each datapath dp_list_result = self.collect_cmd_output('ovs-appctl dpctl/dump-dps') if dp_list_result['status'] == 0: for dp in dp_list_result['output'].splitlines(): self.add_cmd_output([ - "ovs-appctl dpctl/show -s %s" % dp, - "ovs-appctl dpctl/dump-flows -m %s" % dp, - "ovs-appctl dpctl/dump-conntrack -m %s" % dp, - "ovs-appctl dpctl/ct-stats-show -m %s" % dp, - "ovs-appctl dpctl/ipf-get-status %s" % dp, + "%s dpctl/show -s %s" % (actl, dp), + "%s dpctl/dump-flows -m %s" % (actl, dp), + "%s dpctl/dump-conntrack -m %s" % (actl, dp), + "%s dpctl/ct-stats-show -m %s" % (actl, dp), + "%s dpctl/ipf-get-status %s" % (actl, dp), ]) # Gather additional output for each OVS bridge on the host. @@ -121,11 +199,11 @@ if br_list_result['status'] == 0: for br in br_list_result['output'].splitlines(): self.add_cmd_output([ - "ovs-appctl bridge/dump-flows --offload-stats %s" % br, - "ovs-appctl dpif/show-dp-features %s" % br, - "ovs-appctl fdb/show %s" % br, - "ovs-appctl fdb/stats-show %s" % br, - "ovs-appctl mdb/show %s" % br, + "%s bridge/dump-flows --offload-stats %s" % (actl, br), + "%s dpif/show-dp-features %s" % (actl, br), + "%s fdb/show %s" % (actl, br), + "%s fdb/stats-show %s" % (actl, br), + "%s mdb/show %s" % (actl, br), "ovs-ofctl dump-flows %s" % br, "ovs-ofctl dump-ports-desc %s" % br, "ovs-ofctl dump-ports %s" % br, @@ -148,6 +226,7 @@ ovs_list_bridge_cmd = "ovs-vsctl -t 5 list bridge %s" % br br_info = self.collect_cmd_output(ovs_list_bridge_cmd) + br_protos = [] for line in br_info['output'].splitlines(): if "protocols" in line: br_protos_ln = line[line.find("[")+1:line.find("]")] @@ -175,6 +254,8 @@ # Not all ports are "bond"s, but all "bond"s are # a single port "ovs-appctl bond/show %s" % port, + # In the case of IPSec, we should pull the config + "ovs-vsctl get Interface %s options" % port, ]) if check_dpdk: @@ -192,12 +273,53 @@ self.add_cmd_output( "ovs-appctl netdev-dpdk/get-mempool-info %s" % iface) + if check_6wind: + self.add_cmd_output([ + "%s evpn/vip-list-show %s" % (actl, br), + "%s bridge/dump-conntracks-summary %s" % (actl, br), + "%s bridge/acl-table ingress/egress %s" % (actl, br), + "%s bridge/acl-table %s" % (actl, br), + "%s ofproto/show %s" % (actl, br), + ]) + + vrf_list = self.collect_cmd_output( + "%s vrf/list %s" % (actl, br)) + if vrf_list['status'] == 0: + vrfs = vrf_list['output'].split()[1:] + for vrf in vrfs: + self.add_cmd_output([ + "%s vrf/route-table %s" % (actl, vrf), + ]) + + evpn_list = self.collect_cmd_output( + "ovs-appctl evpn/list %s" % br) + if evpn_list['status'] == 0: + evpns = evpn_list['output'].split()[1:] + for evpn in evpns: + self.add_cmd_output([ + "%s evpn/mac-table %s" % (actl, evpn), + "%s evpn/arp-table %s" % (actl, evpn), + "%s evpn/dump-flows %s %s" % (actl, br, evpn), + "%s evpn/dhcp-pool-show %s %s" % ( + actl, br, evpn), + "%s evpn/dhcp-relay-show %s %s" % ( + actl, br, evpn), + "%s evpn/dhcp-static-show %s %s" % ( + actl, br, evpn), + "%s evpn/dhcp-table-show %s %s" % ( + actl, br, evpn), + "%s evpn/proxy-arp-filter-list %s %s" % ( + actl, br, evpn), + "%s evpn/show %s %s" % (actl, br, evpn), + "%s port/dscp-table %s %s" % (actl, br, evpn), + ]) class RedHatOpenVSwitch(OpenVSwitch, RedHatPlugin): packages = ('openvswitch', 'openvswitch2.*', - 'openvswitch-dpdk', 'nuage-openvswitch') + 'openvswitch-dpdk', 'nuage-openvswitch' + '6windgate-fp') class DebianOpenVSwitch(OpenVSwitch, DebianPlugin, UbuntuPlugin): diff -Nru sosreport-4.0/sos/report/plugins/os_net_config.py sosreport-4.1/sos/report/plugins/os_net_config.py --- sosreport-4.0/sos/report/plugins/os_net_config.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/os_net_config.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class OsNetConfig(Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin): +class OsNetConfig(Plugin, IndependentPlugin): short_desc = 'OpenStack Net Config' diff -Nru sosreport-4.0/sos/report/plugins/ovirt_hosted_engine.py sosreport-4.1/sos/report/plugins/ovirt_hosted_engine.py --- sosreport-4.0/sos/report/plugins/ovirt_hosted_engine.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/ovirt_hosted_engine.py 2021-02-25 18:46:49.000000000 +0000 @@ -39,6 +39,7 @@ '/etc/ovirt-hosted-engine-ha/broker-log.conf', '/etc/ovirt-hosted-engine-ha/notifications/state_transition.txt', '/run/ovirt-hosted-engine-ha/vm.conf', + '/var/run/ovirt-hosted-engine-ha/vm.conf', '/var/lib/ovirt-hosted-engine-ha/broker.conf', ]) diff -Nru sosreport-4.0/sos/report/plugins/ovirt_imageio.py sosreport-4.1/sos/report/plugins/ovirt_imageio.py --- sosreport-4.0/sos/report/plugins/ovirt_imageio.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/ovirt_imageio.py 2021-02-25 18:46:49.000000000 +0000 @@ -29,15 +29,19 @@ # Add configuration files self.add_copy_spec([ '/etc/ovirt-imageio-daemon/logger.conf', + '/etc/ovirt-imageio-daemon/daemon.conf', '/etc/ovirt-imageio-proxy/ovirt-imageio-proxy.conf', + '/etc/ovirt-imageio/conf.d/*.conf', ]) if all_logs: logs = ['/var/log/ovirt-imageio-proxy/image-proxy.log*', - '/var/log/ovirt-imageio-daemon/daemon.log*'] + '/var/log/ovirt-imageio-daemon/daemon.log*', + '/var/log/ovirt-imageio/daemon.log*'] else: logs = ['/var/log/ovirt-imageio-proxy/image-proxy.log', - '/var/log/ovirt-imageio-daemon/daemon.log'] + '/var/log/ovirt-imageio-daemon/daemon.log', + '/var/log/ovirt-imageio/daemon.log'] # Add log files self.add_copy_spec(logs) diff -Nru sosreport-4.0/sos/report/plugins/ovirt_node.py sosreport-4.1/sos/report/plugins/ovirt_node.py --- sosreport-4.0/sos/report/plugins/ovirt_node.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/ovirt_node.py 2021-02-25 18:46:49.000000000 +0000 @@ -1,3 +1,4 @@ +# Copyright (C) 2021 Red Hat, Inc., Lev Veyde # Copyright (C) 2018 Red Hat, Inc., # This file is part of the sos project: https://github.com/sosreport/sos # @@ -31,6 +32,15 @@ '/tmp/imgbased.log', ]) + certificates = [ + '/etc/pki/vdsm/certs/cacert.pem', + '/etc/pki/vdsm/certs/vdsmcert.pem', + '/etc/pki/vdsm/libvirt-spice/ca-cert.pem', + '/etc/pki/vdsm/libvirt-spice/server-cert.pem', + '/etc/pki/vdsm/libvirt-vnc/ca-cert.pem', + '/etc/pki/vdsm/libvirt-vnc/server-cert.pem', + ] + # Collect runtime info self.add_cmd_output([ 'imgbase layout', @@ -38,5 +48,10 @@ 'nodectl info', ]) + # Collect certificate info + self.add_cmd_output([ + 'openssl x509 -in %s -text -noout' % c for c in certificates + ]) + # vim: expandtab tabstop=4 shiftwidth=4 diff -Nru sosreport-4.0/sos/report/plugins/ovirt.py sosreport-4.1/sos/report/plugins/ovirt.py --- sosreport-4.0/sos/report/plugins/ovirt.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/ovirt.py 2021-02-25 18:46:49.000000000 +0000 @@ -1,3 +1,4 @@ +# Copyright (C) 2021 Red Hat, Inc., Lev Veyde # Copyright (C) 2014 Red Hat, Inc., Sandro Bonazzola # Copyright (C) 2014 Red Hat, Inc., Bryn M. Reeves # Copyright (C) 2010 Red Hat, Inc. @@ -87,11 +88,38 @@ self.add_forbidden_path('/var/log/ovirt-engine/dump') self.add_cmd_output('ls -l /var/log/ovirt-engine/dump/') + certificates = [ + '/etc/pki/ovirt-engine/ca.pem', + '/etc/pki/ovirt-engine/apache-ca.pem', + '/etc/pki/ovirt-engine/certs/engine.cer', + '/etc/pki/ovirt-engine/certs/apache.cer', + '/etc/pki/ovirt-engine/certs/websocket-proxy.cer', + '/etc/pki/ovirt-engine/certs/jboss.cer', + '/etc/pki/ovirt-engine/certs/imageio-proxy.cer', + '/etc/pki/ovirt-engine/certs/ovirt-provider-ovn.cer', + ] + + keystores = [ + ('mypass', '/etc/pki/ovirt-engine/.truststore'), + ('changeit', '/var/lib/ovirt-engine/external_truststore'), + ] + self.add_cmd_output([ # Copy all engine tunables and domain information "engine-config --all", # clearer diff from factory defaults (only on ovirt>=4.2.8) - "engine-config -d" + "engine-config -d", + ]) + + self.add_cmd_output([ + # process certificate files + "openssl x509 -in %s -text -noout" % c for c in certificates + ]) + + self.add_cmd_output([ + # process TrustStore certificates + "keytool -list -storepass %s -rfc -keystore %s" % + (p, c) for (p, c) in keystores ]) # 3.x line uses engine-manage-domains, 4.x uses ovirt-aaa-jdbc-tool @@ -137,12 +165,15 @@ "/var/lib/ovirt-engine-reports/jboss_runtime/config" ]) - # Copying host certs. + # Copying host certs; extra copy the hidden .truststore file self.add_forbidden_path([ "/etc/pki/ovirt-engine/keys", "/etc/pki/ovirt-engine/private" ]) - self.add_copy_spec("/etc/pki/ovirt-engine/") + self.add_copy_spec([ + "/etc/pki/ovirt-engine/", + "/etc/pki/ovirt-engine/.truststore", + ]) def postproc(self): """ diff -Nru sosreport-4.0/sos/report/plugins/ovn_central.py sosreport-4.1/sos/report/plugins/ovn_central.py --- sosreport-4.0/sos/report/plugins/ovn_central.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/ovn_central.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,7 +8,13 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import ( + Plugin, + RedHatPlugin, + DebianPlugin, + UbuntuPlugin, + SoSPredicate, +) import json import os @@ -76,13 +82,25 @@ if ovs_rundir: self.add_copy_spec(os.path.join(ovs_rundir, pidfile)) + if self.get_option("all_logs"): + self.add_copy_spec("/var/log/ovn/") + else: + self.add_copy_spec("/var/log/ovn/*.log") + # Some user-friendly versions of DB output - cmds = [ + nbctl_cmds = [ 'ovn-nbctl show', - 'ovn-sbctl show', - 'ovn-sbctl lflow-list', 'ovn-nbctl get-ssl', 'ovn-nbctl get-connection', + 'ovn-nbctl list loadbalancer', + 'ovn-nbctl list Load_Balancer', + 'ovn-nbctl list ACL', + 'ovn-nbctl list Logical_Switch_Port', + ] + + sbctl_cmds = [ + 'ovn-sbctl show', + 'ovn-sbctl lflow-list', 'ovn-sbctl get-ssl', 'ovn-sbctl get-connection', ] @@ -91,11 +109,20 @@ nb_tables = self.get_tables_from_schema(os.path.join( schema_dir, 'ovn-nb.ovsschema')) - sb_tables = self.get_tables_from_schema(os.path.join( - schema_dir, 'ovn-sb.ovsschema'), ['Logical_Flow']) - self.add_database_output(nb_tables, cmds, 'ovn-nbctl') - self.add_database_output(sb_tables, cmds, 'ovn-sbctl') + self.add_database_output(nb_tables, nbctl_cmds, 'ovn-nbctl') + + cmds = nbctl_cmds + + # Can only run sbdb commands if we are the leader + co = {'cmd': "ovs-appctl -t {} cluster/status OVN_Southbound". + 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(os.path.join( + schema_dir, '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 # the container. @@ -124,8 +151,10 @@ class RedHatOVNCentral(OVNCentral, RedHatPlugin): packages = ('openvswitch-ovn-central', 'ovn2.*-central', ) + ovn_sbdb_sock_path = '/var/run/openvswitch/ovnsb_db.ctl' class DebianOVNCentral(OVNCentral, DebianPlugin, UbuntuPlugin): packages = ('ovn-central', ) + ovn_sbdb_sock_path = '/var/run/ovn/ovnsb_db.ctl' diff -Nru sosreport-4.0/sos/report/plugins/ovn_host.py sosreport-4.1/sos/report/plugins/ovn_host.py --- sosreport-4.0/sos/report/plugins/ovn_host.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/ovn_host.py 2021-02-25 18:46:49.000000000 +0000 @@ -29,6 +29,12 @@ def setup(self): if os.environ.get('OVS_RUNDIR'): pid_paths.append(os.environ.get('OVS_RUNDIR')) + + if self.get_option("all_logs"): + self.add_copy_spec("/var/log/ovn/") + else: + self.add_copy_spec("/var/log/ovn/*.log") + self.add_copy_spec([os.path.join(pp, pidfile) for pp in pid_paths]) self.add_copy_spec('/etc/sysconfig/ovn-controller') diff -Nru sosreport-4.0/sos/report/plugins/pci.py sosreport-4.1/sos/report/plugins/pci.py --- sosreport-4.0/sos/report/plugins/pci.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/pci.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,11 +6,11 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin +from sos.report.plugins import Plugin, IndependentPlugin import os -class Pci(Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin): +class Pci(Plugin, IndependentPlugin): short_desc = 'PCI devices' diff -Nru sosreport-4.0/sos/report/plugins/perl.py sosreport-4.1/sos/report/plugins/perl.py --- sosreport-4.0/sos/report/plugins/perl.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/perl.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Perl(Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin): +class Perl(Plugin, IndependentPlugin): short_desc = 'Perl runtime' diff -Nru sosreport-4.0/sos/report/plugins/pmem.py sosreport-4.1/sos/report/plugins/pmem.py --- sosreport-4.0/sos/report/plugins/pmem.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/pmem.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class pmem(Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin): +class pmem(Plugin, IndependentPlugin): """This plugin collects data from Persistent Memory devices, commonly referred to as NVDIMM's or Storage Class Memory (SCM) """ @@ -22,11 +22,12 @@ commands = ('ndctl', 'daxctl', 'ipmctl') def setup(self): - # Copy the contents of the /etc/ndctl directory - # and /etc/ipmctl.conf file + # Copy the contents of the /etc/ndctl & /var/log/ipmctl + # directories and /etc/ipmctl.conf file self.add_copy_spec([ "/etc/ndctl", - "/etc/ipmctl.conf" + "/etc/ipmctl.conf", + "/var/log/ipmctl" ]) """ Use the ndctl-list(1) command to collect: @@ -68,23 +69,27 @@ self.add_cmd_output([ "ipmctl version", "ipmctl show -cap", + "ipmctl show -cel", "ipmctl show -dimm", "ipmctl show -a -dimm", "ipmctl show -dimm -pcd", "ipmctl show -dimm -performance", "ipmctl show -error Thermal -dimm", "ipmctl show -error Media -dimm", - "ipmctl show -event", "ipmctl show -firmware", + "ipmctl show -goal", "ipmctl show -memoryresources", + "ipmctl show -performance", "ipmctl show -preferences", "ipmctl show -region", "ipmctl show -sensor", + "ipmctl show -a -sensor", "ipmctl show -socket", "ipmctl show -system", "ipmctl show -system -capabilities", - "ipmctl show -system -host", - "ipmctl show -topology" + "ipmctl show -a -system -capabilities", + "ipmctl show -topology", + "ipmctl show -a -topology" ]) # vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/report/plugins/podman.py sosreport-4.1/sos/report/plugins/podman.py --- sosreport-4.0/sos/report/plugins/podman.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/podman.py 2021-02-25 18:46:49.000000000 +0000 @@ -37,6 +37,7 @@ subcmds = [ 'info', 'images', + 'images --digests', 'pod ps', 'port --all', 'ps', diff -Nru sosreport-4.0/sos/report/plugins/postfix.py sosreport-4.1/sos/report/plugins/postfix.py --- sosreport-4.0/sos/report/plugins/postfix.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/postfix.py 2021-02-25 18:46:49.000000000 +0000 @@ -17,6 +17,41 @@ packages = ('postfix',) + def forbidden_ssl_keys_files(self): + # list of attributes defining a location of a SSL key file + # we must forbid from collection + forbid_attributes = [ + "lmtp_tls_dkey_file", + "lmtp_tls_eckey_file", + "lmtp_tls_key_file", + "smtp_tls_dkey_file", + "smtp_tls_eckey_file", + "smtp_tls_key_file", + "smtpd_tls_dkey_file", + "smtpd_tls_eckey_file", + "smtpd_tls_key_file", + "tls_legacy_public_key_fingerprints", + "tlsproxy_tls_dkey_file", + "tlsproxy_tls_eckey_file", + "tlsproxy_tls_key_file", + "smtpd_tls_dh1024_param_file", + "smtpd_tls_dh512_param_file", + "tlsproxy_tls_dh1024_param_file", + "tlsproxy_tls_dh512_param_file", + ] + fp = [] + try: + with open('/etc/postfix/main.cf', 'r') as cffile: + for line in cffile.readlines(): + # ignore comments and take the first word after '=' + if line.startswith('#'): + continue + words = line.split('=') + if words[0].strip() in forbid_attributes: + fp.append(words[1].split()[0]) + finally: + return fp + def setup(self): self.add_copy_spec([ "/etc/postfix/", @@ -31,6 +66,7 @@ "/etc/postfix/*.crt", "/etc/postfix/ssl/", ]) + self.add_forbidden_path(self.forbidden_ssl_keys_files()) class RedHatPostfix(Postfix, RedHatPlugin): diff -Nru sosreport-4.0/sos/report/plugins/postgresql.py sosreport-4.1/sos/report/plugins/postgresql.py --- sosreport-4.0/sos/report/plugins/postgresql.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/postgresql.py 2021-02-25 18:46:49.000000000 +0000 @@ -83,47 +83,41 @@ packages = ( 'postgresql', 'rh-postgresql95-postgresql-server', - 'rh-postgresql10-postgresql-server' + 'rh-postgresql10-postgresql-server', + 'rh-postgresql12-postgresql-server', ) def setup(self): super(RedHatPostgreSQL, self).setup() pghome = self.get_option("pghome") + dirs = [pghome] - scl = None for pkg in self.packages[1:]: # The scl name, package name, and service name all differ slightly # but is at least consistent in doing so across versions, so we # need to do some mangling here - if self.is_service_running(pkg.replace('-server', '')): - scl = pkg.split('-postgresql-')[0] - - # Copy PostgreSQL log files. - for filename in find("*.log", pghome): - self.add_copy_spec(filename) - for filename in find("*.log", self.convert_copyspec_scl(scl, pghome)): - self.add_copy_spec(filename) - - # Copy PostgreSQL config files. - for filename in find("*.conf", pghome): - self.add_copy_spec(filename) - for filename in find("*.conf", self.convert_copyspec_scl(scl, pghome)): - self.add_copy_spec(filename) - - self.add_copy_spec(os.path.join(pghome, "data", "PG_VERSION")) - self.add_copy_spec(os.path.join(pghome, "data", "postmaster.opts")) - - self.add_copy_spec_scl(scl, os.path.join(pghome, "data", "PG_VERSION")) - self.add_copy_spec_scl(scl, os.path.join( - pghome, - "data", - "postmaster.opts" - ) - ) - - if scl and scl in self.scls_matched: - self.do_pg_dump(scl=scl, filename="pgdump-scl-%s.tar" % scl) + scl = pkg.split('-postgresql-')[0] + _dir = self.convert_copyspec_scl(scl, pghome) + dirs.append(_dir) + if os.path.isdir(_dir): + self.add_cmd_output("du -sh %s" % _dir) + if (self.is_service_running(pkg.replace('-server', '')) and + scl in self.scls_matched): + self.do_pg_dump(scl=scl, filename="pgdump-scl-%s.tar" % scl) + + for _dir in dirs: + # Copy PostgreSQL log files. + for filename in find("*.log", _dir): + self.add_copy_spec(filename) + + # Copy PostgreSQL config files. + for filename in find("*.conf", _dir): + self.add_copy_spec(filename) + + # copy PG_VERSION and postmaster.opts + for f in ["PG_VERSION", "postmaster.opts"]: + self.add_copy_spec(os.path.join(_dir, "data", f)) class DebianPostgreSQL(PostgreSQL, DebianPlugin, UbuntuPlugin): diff -Nru sosreport-4.0/sos/report/plugins/powerpc.py sosreport-4.1/sos/report/plugins/powerpc.py --- sosreport-4.0/sos/report/plugins/powerpc.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/powerpc.py 2021-02-25 18:46:49.000000000 +0000 @@ -10,10 +10,10 @@ # specific logs for Pseries, PowerNV platforms. import os -from sos.report.plugins import Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class PowerPC(Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin): +class PowerPC(Plugin, IndependentPlugin): short_desc = 'IBM Power systems' @@ -50,7 +50,10 @@ "ppc64_cpu --run-mode", "ppc64_cpu --frequency", "ppc64_cpu --dscr", - "diag_encl -v" + "diag_encl -v", + "lsvpd -D", + "lsmcode -A", + "lscfg -v" ]) if ispSeries: @@ -60,7 +63,10 @@ "/proc/ppc64/systemcfg", "/var/log/platform", "/var/log/drmgr", - "/var/log/drmgr.0" + "/var/log/drmgr.0", + "/var/log/hcnmgr", + "/var/ct/IBM.DRM.stderr", + "/var/ct/IW/log/mc/IBM.DRM/trace*" ]) ctsnap_path = self.get_cmd_output_path(name="ctsnap", make=True) self.add_cmd_output([ @@ -71,8 +77,10 @@ "serv_config -l", "bootlist -m both -r", "lparstat -i", - "ctsnap -xrunrpttr -d %s" % (ctsnap_path) + "ctsnap -xrunrpttr -d %s" % (ctsnap_path), + "lsdevinfo" ]) + self.add_service_status("hcn-init") if isPowerNV: self.add_copy_spec([ @@ -81,7 +89,8 @@ "/proc/ppc64/topology_updates", "/sys/firmware/opal/msglog", "/var/log/opal-elog/", - "/var/log/opal-prd" + "/var/log/opal-prd", + "/var/log/opal-prd.log*" ]) if os.path.isdir("/var/log/dump"): self.add_cmd_output("ls -l /var/log/dump") diff -Nru sosreport-4.0/sos/report/plugins/ppp.py sosreport-4.1/sos/report/plugins/ppp.py --- sosreport-4.0/sos/report/plugins/ppp.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/ppp.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Ppp(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Ppp(Plugin, IndependentPlugin): short_desc = 'Point-to-point protocol' diff -Nru sosreport-4.0/sos/report/plugins/processor.py sosreport-4.1/sos/report/plugins/processor.py --- sosreport-4.0/sos/report/plugins/processor.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/processor.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Processor(Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin): +class Processor(Plugin, IndependentPlugin): short_desc = 'CPU information' diff -Nru sosreport-4.0/sos/report/plugins/process.py sosreport-4.1/sos/report/plugins/process.py --- sosreport-4.0/sos/report/plugins/process.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/process.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,11 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import (Plugin, RedHatPlugin, DebianPlugin, - UbuntuPlugin, CosPlugin) +from sos.report.plugins import Plugin, IndependentPlugin -class Process(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin, CosPlugin): +class Process(Plugin, IndependentPlugin): short_desc = 'process information' @@ -41,17 +40,18 @@ if self.get_option("smaps"): self.add_copy_spec("/proc/[0-9]*/smaps") - self.add_cmd_output("ps auxwww", root_symlink="ps", - tags=['ps_aux', 'ps_auxww', 'ps_auxwww']) + self.add_cmd_output("ps auxwwwm", root_symlink="ps", + tags=['ps_aux', 'ps_auxww', 'ps_auxwww', + 'ps_auxwwwm']) self.add_cmd_output("pstree -lp", root_symlink="pstree") if self.get_option("lsof"): - self.add_cmd_output("lsof -b +M -n -l -c ''", root_symlink="lsof") + self.add_cmd_output("lsof +M -n -l -c ''", root_symlink="lsof", + timeout=15) if self.get_option("lsof-threads"): - self.add_cmd_output("lsof -b +M -n -l") + self.add_cmd_output("lsof +M -n -l", timeout=15) self.add_cmd_output([ - "ps auxwwwm", "ps alxwww", "ps -elfL", "%s %s" % (ps_axo, ps_group_opts), @@ -61,4 +61,9 @@ if self.get_option("samples"): self.add_cmd_output("iotop -b -o -d 0.5 -t -n %s" % self.get_option("samples")) + + self.add_cmd_output([ + "pidstat -p ALL -rudvwsRU --human -h", + "pidstat -tl" + ]) # vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/report/plugins/ptp.py sosreport-4.1/sos/report/plugins/ptp.py --- sosreport-4.0/sos/report/plugins/ptp.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/ptp.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Ptp(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Ptp(Plugin, IndependentPlugin): short_desc = 'Precision time protocol' diff -Nru sosreport-4.0/sos/report/plugins/pulp.py sosreport-4.1/sos/report/plugins/pulp.py --- sosreport-4.0/sos/report/plugins/pulp.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/pulp.py 2021-02-25 18:46:49.000000000 +0000 @@ -18,7 +18,8 @@ short_desc = 'Pulp platform' plugin_name = "pulp" - packages = ("pulp-server", "pulp-katello") + packages = ("pulp-server", "pulp-katello", "python3-pulpcore") + files = ("/etc/pulp/settings.py") option_list = [ ('tasks', 'number of tasks to collect from DB queries', 'fast', 200) ] @@ -63,6 +64,7 @@ self.add_copy_spec([ "/etc/pulp/*.conf", + "/etc/pulp/settings.py", "/etc/pulp/server/plugins.conf.d/", "/etc/default/pulp*", "/var/log/httpd/pulp-http.log*", @@ -128,6 +130,11 @@ "qpid-stat -%s --ssl-certificate=%s -b amqps://localhost:5671" % (opt, self.messaging_cert_file) for opt in "quc" ]) + self.add_cmd_output( + "sudo -u pulp PULP_SETTINGS='/etc/pulp/settings.py' " + "DJANGO_SETTINGS_MODULE='pulpcore.app.settings' dynaconf list", + suggest_filename="dynaconf_list" + ) def build_mongo_cmd(self, query): _cmd = "bash -c %s" @@ -153,4 +160,12 @@ repl = r"\1********\4" self.do_path_regex_sub("/etc/pulp(.*)(.json$)", jreg, repl) + # obfuscate SECRET_KEY = .. and 'PASSWORD': .. in dynaconf list output + # and also in settings.py + # count with option that PASSWORD is with(out) quotes or in capitals + key_pass_re = r"(SECRET_KEY\s*=|(password|PASSWORD)(\"|'|:)+)\s*(\S*)" + repl = r"\1 ********" + self.do_path_regex_sub("/etc/pulp/settings.py", key_pass_re, repl) + self.do_cmd_output_sub("dynaconf list", key_pass_re, repl) + # vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/report/plugins/puppet.py sosreport-4.1/sos/report/plugins/puppet.py --- sosreport-4.0/sos/report/plugins/puppet.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/puppet.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,11 +6,11 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin from glob import glob -class Puppet(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Puppet(Plugin, IndependentPlugin): short_desc = 'Puppet service' diff -Nru sosreport-4.0/sos/report/plugins/python.py sosreport-4.1/sos/report/plugins/python.py --- sosreport-4.0/sos/report/plugins/python.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/python.py 2021-02-25 18:46:49.000000000 +0000 @@ -9,7 +9,7 @@ # See the LICENSE file in the source distribution for further information. from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin -import sos.policies +from sos.policies.distros.redhat import RHELPolicy import os import json import hashlib @@ -42,7 +42,7 @@ def setup(self): self.add_cmd_output(['python2 -V', 'python3 -V']) - if isinstance(self.policy, sos.policies.redhat.RHELPolicy) and \ + if isinstance(self.policy, RHELPolicy) and \ self.policy.dist_version() > 7: self.python_version = "/usr/libexec/platform-python -V" super(RedHatPython, self).setup() diff -Nru sosreport-4.0/sos/report/plugins/rabbitmq.py sosreport-4.1/sos/report/plugins/rabbitmq.py --- sosreport-4.0/sos/report/plugins/rabbitmq.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/rabbitmq.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class RabbitMQ(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class RabbitMQ(Plugin, IndependentPlugin): short_desc = 'RabbitMQ messaging service' plugin_name = 'rabbitmq' @@ -37,8 +37,17 @@ self.fmt_container_cmd(container, 'rabbitmqctl report'), foreground=True ) + self.add_cmd_output( + self.fmt_container_cmd( + container, "rabbitmqctl eval " + "'rabbit_diagnostics:maybe_stuck().'"), + foreground=True, timeout=10 + ) else: self.add_cmd_output("rabbitmqctl report") + self.add_cmd_output( + "rabbitmqctl eval 'rabbit_diagnostics:maybe_stuck().'", + timeout=10) self.add_copy_spec([ "/etc/rabbitmq/*", diff -Nru sosreport-4.0/sos/report/plugins/rear.py sosreport-4.1/sos/report/plugins/rear.py --- sosreport-4.0/sos/report/plugins/rear.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/rear.py 2021-02-25 18:46:49.000000000 +0000 @@ -20,12 +20,14 @@ def setup(self): # don't collect recovery ISOs or tar archives self.add_forbidden_path([ - '/var/log/rear/*.iso', - '/var/log/rear/*.tar.gz' + '/var/lib/rear/output/*' ]) self.add_copy_spec([ '/etc/rear/*conf', + '/etc/rear/mappings/*', + '/var/lib/rear/layout/*', + '/var/lib/rear/recovery/*', '/var/log/rear/*log*' ]) diff -Nru sosreport-4.0/sos/report/plugins/rhui.py sosreport-4.1/sos/report/plugins/rhui.py --- sosreport-4.0/sos/report/plugins/rhui.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/rhui.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -# This file is part of the sos project: https://github.com/sosreport/sos -# -# This copyrighted material is made available to anyone wishing to use, -# modify, copy, or redistribute it subject to the terms and conditions of -# version 2 of the GNU General Public License. -# -# See the LICENSE file in the source distribution for further information. - -from sos.report.plugins import Plugin, RedHatPlugin - - -class Rhui(Plugin, RedHatPlugin): - - short_desc = 'Red Hat Update Infrastructure' - - plugin_name = 'rhui' - profiles = ('sysmgmt',) - - rhui_debug_path = "/usr/share/rh-rhua/rhui-debug.py" - - packages = ["rh-rhui-tools"] - files = [rhui_debug_path] - - def setup(self): - cds_installed = [ - self.is_installed("pulp-cds"), - self.is_installed("rhui-mirrorlist") - ] - if any(cds_installed): - cds = "--cds" - else: - cds = "" - - rhui_debug_dst_path = self.get_cmd_output_path() - self.add_cmd_output( - "python %s %s --dir %s" - % (self.rhui_debug_path, cds, rhui_debug_dst_path), - suggest_filename="rhui-debug") - return - -# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/report/plugins/rpm.py sosreport-4.1/sos/report/plugins/rpm.py --- sosreport-4.0/sos/report/plugins/rpm.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/rpm.py 2021-02-25 18:46:49.000000000 +0000 @@ -65,4 +65,6 @@ suggest_filename='lsof_D_var_lib_rpm') self.add_copy_spec("/var/lib/rpm") + self.add_cmd_output("rpm --showrc") + # vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/report/plugins/ruby.py sosreport-4.1/sos/report/plugins/ruby.py --- sosreport-4.0/sos/report/plugins/ruby.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/ruby.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Ruby(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Ruby(Plugin, IndependentPlugin): short_desc = 'Ruby runtime' diff -Nru sosreport-4.0/sos/report/plugins/s390.py sosreport-4.1/sos/report/plugins/s390.py --- sosreport-4.0/sos/report/plugins/s390.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/s390.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class S390(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class S390(Plugin, IndependentPlugin): short_desc = 'IBM S/390' @@ -25,6 +25,7 @@ "/proc/crypto", "/proc/dasd/devices", "/proc/dasd/statistics", + "/etc/dasd.conf", "/proc/qeth", "/proc/qeth_perf", "/proc/qeth_ipa_takeover", diff -Nru sosreport-4.0/sos/report/plugins/saltmaster.py sosreport-4.1/sos/report/plugins/saltmaster.py --- sosreport-4.0/sos/report/plugins/saltmaster.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/saltmaster.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,20 +6,31 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class SaltMaster(Plugin, RedHatPlugin, DebianPlugin): +class SaltMaster(Plugin, IndependentPlugin): short_desc = 'Salt Master' plugin_name = 'saltmaster' profiles = ('sysmgmt',) - packages = ('salt-master',) + packages = ('salt-master', 'salt-api',) def setup(self): - self.add_copy_spec("/var/log/salt/master") + if self.get_option("all_logs"): + self.add_copy_spec("/var/log/salt") + else: + self.add_copy_spec("/var/log/salt/master") + + self.add_copy_spec("/etc/salt") + self.add_forbidden_path("/etc/salt/pki/*/*.pem") self.add_cmd_output("salt-key --list all") + def postproc(self): + regexp = r'((?m)^\s+.*(pass|secret|(?adm user environment lowsid = sid.lower() + fname = "%s_%sadm_%s_userenv" % (sid, lowsid, inst) self.add_cmd_output( - "su - %sadm -c \"sapcontrol -nr %s -function \ - GetEnvironment\"" % (lowsid, inst), - suggest_filename="%s_%sadm_%s_userenv" - % (sid, lowsid, inst)) + user_cmd % (lowsid, inst), + suggest_filename=fname + ) # traverse the sids list, collecting info about dbclient for sid in sidsunique: - for line in get_directory_listing("/usr/sap/%s/" % sid): - if 'DVEB' in line: - self.add_cmd_output( - "grep 'client driver' /usr/sap/%s/%s/work/dev_w0" - % (sid, line), suggest_filename="%s_dbclient" - % sid) + self.add_copy_spec("/usr/sap/%s/*DVEB*/work/dev_w0" % sid) def collect_list_dbs(self): # list installed sap dbs @@ -112,22 +103,23 @@ if dbtype == 'db6': # IBM DB2 self.add_cmd_output( - "su - %s -c \"db2 get dbm cfg\"" - % dbadm, suggest_filename="%s_%s_db2_info" - % (sid, dbadm)) + "su - %s -c \"db2 get dbm cfg\"" % dbadm, + suggest_filename="%s_%s_db2_info" % (sid, dbadm) + ) - if dbtype == 'sap': + elif dbtype == 'sap': # SAP MAXDB sid = fields[2][:-1] self.add_copy_spec( - "/sapdb/%s/data/config/%s.pah" % (sid, sid)) + "/sapdb/%s/data/config/%s.pah" % (sid, sid) + ) - if dbtype == 'ora': + elif dbtype == 'ora': # Oracle sid = fields[2][:-1] self.add_copy_spec("/oracle/%s/*/dbs/init.ora" % sid) - if dbtype == 'syb': + elif dbtype == 'syb': # Sybase sid = fields[2][:-1] self.add_copy_spec("/sybase/%s/ASE*/%s.cfg" % (sid, sid)) @@ -137,7 +129,12 @@ self.collect_list_dbs() # run sapconf in check mode - self.add_cmd_output("sapconf -n", - suggest_filename="sapconf_checkmode") + # + # since the command creates a limits.d file on its own, + # we must predicate it by presence of the file + if os.path.exists('/etc/security/limits.d/99-sap-limits.conf') \ + or self.get_option('allow_system_changes'): + self.add_cmd_output("sapconf -n", + suggest_filename="sapconf_checkmode") # vim: et ts=4 sw=4 diff -Nru sosreport-4.0/sos/report/plugins/sas3ircu.py sosreport-4.1/sos/report/plugins/sas3ircu.py --- sosreport-4.0/sos/report/plugins/sas3ircu.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/sas3ircu.py 2021-02-25 18:46:49.000000000 +0000 @@ -10,10 +10,10 @@ # This sosreport plugin is meant for sas adapters. # This plugin logs inforamtion on each adapter it finds. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class SAS3ircu(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class SAS3ircu(Plugin, IndependentPlugin): short_desc = 'SAS-3 Integrated RAID adapter information' diff -Nru sosreport-4.0/sos/report/plugins/scsi.py sosreport-4.1/sos/report/plugins/scsi.py --- sosreport-4.0/sos/report/plugins/scsi.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/scsi.py 2021-02-25 18:46:49.000000000 +0000 @@ -7,10 +7,10 @@ # See the LICENSE file in the source distribution for further information. from glob import glob -from sos.report.plugins import Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Scsi(Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin): +class Scsi(Plugin, IndependentPlugin): short_desc = 'SCSI devices' @@ -29,10 +29,18 @@ ]) self.add_cmd_output("lsscsi -i", suggest_filename="lsscsi") - self.add_cmd_output("sg_map -x") - self.add_cmd_output("lspath") - self.add_cmd_output("lsmap -all") - self.add_cmd_output("lsnports") + + self.add_cmd_output([ + "sg_map -x", + "lspath", + "lsmap -all", + "lsnports", + "lsscsi -H", + "lsscsi -g", + "lsscsi -d", + "lsscsi -s", + "lsscsi -L" + ]) scsi_hosts = glob("/sys/class/scsi_host/*") self.add_blockdev_cmd("udevadm info -a %(dev)s", devices=scsi_hosts, diff -Nru sosreport-4.0/sos/report/plugins/snap.py sosreport-4.1/sos/report/plugins/snap.py --- sosreport-4.0/sos/report/plugins/snap.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/snap.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,31 @@ +# Copyright (c) 2017 Bryan Quigley +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +from sos.report.plugins import Plugin, IndependentPlugin + + +class Snap(Plugin, IndependentPlugin): + + short_desc = 'Snap packages' + + plugin_name = 'snap' + profiles = ('system', 'sysmgmt', 'packagemanager') + packages = ('snapd',) + + def setup(self): + self.add_cmd_output("snap list --all", root_symlink="installed-snaps") + self.add_cmd_output([ + "snap --version", + "snap changes" + ]) + self.add_cmd_output("snap debug connectivity", timeout=10) + self.add_service_status("snapd") + self.add_journal(units="snapd") + +# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/report/plugins/snappy.py sosreport-4.1/sos/report/plugins/snappy.py --- sosreport-4.0/sos/report/plugins/snappy.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/snappy.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ -# Copyright (c) 2017 Bryan Quigley -# This file is part of the sos project: https://github.com/sosreport/sos -# -# This copyrighted material is made available to anyone wishing to use, -# modify, copy, or redistribute it subject to the terms and conditions of -# version 2 of the GNU General Public License. -# -# See the LICENSE file in the source distribution for further information. - -from sos.report.plugins import Plugin, UbuntuPlugin, DebianPlugin, RedHatPlugin - - -class Snappy(Plugin, UbuntuPlugin, DebianPlugin, RedHatPlugin): - - short_desc = 'Snap packages' - - plugin_name = 'snappy' - profiles = ('system', 'sysmgmt', 'packagemanager') - packages = ('snapd',) - - def setup(self): - self.add_cmd_output("snap list --all", root_symlink="installed-snaps") - self.add_cmd_output([ - "snap --version", - "snap changes" - ]) - self.add_cmd_output("snap debug connectivity", timeout=10) - self.add_service_status("snapd") - self.add_journal(units="snapd") - -# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/report/plugins/sos_extras.py sosreport-4.1/sos/report/plugins/sos_extras.py --- sosreport-4.0/sos/report/plugins/sos_extras.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/sos_extras.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,12 +6,12 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin +from sos.report.plugins import Plugin, IndependentPlugin import os import stat -class SosExtras(Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin): +class SosExtras(Plugin, IndependentPlugin): short_desc = 'Collect extra data defined in /etc/sos/extras.d' diff -Nru sosreport-4.0/sos/report/plugins/ssh.py sosreport-4.1/sos/report/plugins/ssh.py --- sosreport-4.0/sos/report/plugins/ssh.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/ssh.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Ssh(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Ssh(Plugin, IndependentPlugin): short_desc = 'Secure shell service' diff -Nru sosreport-4.0/sos/report/plugins/stratis.py sosreport-4.1/sos/report/plugins/stratis.py --- sosreport-4.0/sos/report/plugins/stratis.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/stratis.py 2021-02-25 18:46:49.000000000 +0000 @@ -24,8 +24,11 @@ 'pool list', 'filesystem list', 'blockdev list', + 'key list', 'daemon redundancy', - 'daemon version' + 'daemon version', + 'report engine_state_report', + '--version', ] self.add_cmd_output(["stratis %s" % subcmd for subcmd in subcmds]) diff -Nru sosreport-4.0/sos/report/plugins/subscription_manager.py sosreport-4.1/sos/report/plugins/subscription_manager.py --- sosreport-4.0/sos/report/plugins/subscription_manager.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/subscription_manager.py 2021-02-25 18:46:49.000000000 +0000 @@ -45,8 +45,8 @@ self.add_cmd_output(["rct cat-cert %s" % cert for cert in certs]) def postproc(self): - passwdreg = r"(proxy_password(\s)*=(\s)*)(.*)" - repl = r"\1 ********" + passwdreg = r"(proxy_password(\s)*=(\s)*)(\S+)\n" + repl = r"\1********\n" self.do_path_regex_sub("/etc/rhsm/rhsm.conf", passwdreg, repl) # vim: et ts=4 sw=4 diff -Nru sosreport-4.0/sos/report/plugins/sudo.py sosreport-4.1/sos/report/plugins/sudo.py --- sosreport-4.0/sos/report/plugins/sudo.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/sudo.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Sudo(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Sudo(Plugin, IndependentPlugin): short_desc = 'Sudo command execution' plugin_name = 'sudo' diff -Nru sosreport-4.0/sos/report/plugins/sunrpc.py sosreport-4.1/sos/report/plugins/sunrpc.py --- sosreport-4.0/sos/report/plugins/sunrpc.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/sunrpc.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class SunRPC(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class SunRPC(Plugin, IndependentPlugin): short_desc = 'Sun RPC service' diff -Nru sosreport-4.0/sos/report/plugins/systemd.py sosreport-4.1/sos/report/plugins/systemd.py --- sosreport-4.0/sos/report/plugins/systemd.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/systemd.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,12 +8,11 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import (Plugin, RedHatPlugin, DebianPlugin, - UbuntuPlugin, CosPlugin, SoSPredicate) +from sos.report.plugins import Plugin, IndependentPlugin, SoSPredicate +from sos.utilities import is_executable -class Systemd(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin, CosPlugin): - +class Systemd(Plugin, IndependentPlugin): short_desc = 'System management daemon' plugin_name = "systemd" @@ -42,16 +41,23 @@ "systemd-analyze", "systemd-analyze blame", "systemd-analyze dump", + "systemd-inhibit --list", "journalctl --list-boots", "ls -lR /lib/systemd", "timedatectl" ]) - # systemd-resolve command starts systemd-resolved service if that + # resolvectl command starts systemd-resolved service if that # is not running, so gate the commands by this predicate + if is_executable('resolvectl'): + resolvectl_status = 'resolvectl status' + resolvectl_statistics = 'resolvectl statistics' + else: + resolvectl_status = 'systemd-resolve --status' + resolvectl_statistics = 'systemd-resolve --statistics' self.add_cmd_output([ - "systemd-resolve --status", - "systemd-resolve --statistics", + resolvectl_status, + resolvectl_statistics, ], pred=SoSPredicate(self, services=["systemd-resolved"])) self.add_cmd_output("systemd-analyze plot", @@ -71,7 +77,10 @@ "/run/systemd/system", "/run/systemd/users", "/etc/modules-load.d/*.conf", - "/etc/yum/protected.d/systemd.conf" + "/etc/yum/protected.d/systemd.conf", + "/etc/tmpfiles.d/*.conf", + "/run/tmpfiles.d/*.conf", + "/usr/lib/tmpfiles.d/*.conf", ]) self.add_forbidden_path('/dev/null') diff -Nru sosreport-4.0/sos/report/plugins/system.py sosreport-4.1/sos/report/plugins/system.py --- sosreport-4.0/sos/report/plugins/system.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/system.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class System(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class System(Plugin, IndependentPlugin): short_desc = 'core system information' diff -Nru sosreport-4.0/sos/report/plugins/systemtap.py sosreport-4.1/sos/report/plugins/systemtap.py --- sosreport-4.0/sos/report/plugins/systemtap.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/systemtap.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class SystemTap(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class SystemTap(Plugin, IndependentPlugin): short_desc = 'SystemTap dynamic instrumentation' diff -Nru sosreport-4.0/sos/report/plugins/sysvipc.py sosreport-4.1/sos/report/plugins/sysvipc.py --- sosreport-4.0/sos/report/plugins/sysvipc.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/sysvipc.py 2021-02-25 18:46:49.000000000 +0000 @@ -7,10 +7,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class SysVIPC(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class SysVIPC(Plugin, IndependentPlugin): short_desc = 'SysV IPC' diff -Nru sosreport-4.0/sos/report/plugins/targetcli.py sosreport-4.1/sos/report/plugins/targetcli.py --- sosreport-4.0/sos/report/plugins/targetcli.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/targetcli.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class TargetCli(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class TargetCli(Plugin, IndependentPlugin): short_desc = 'TargetCLI TCM/LIO configuration' packages = ('targetcli', 'python-rtslib') diff -Nru sosreport-4.0/sos/report/plugins/teamd.py sosreport-4.1/sos/report/plugins/teamd.py --- sosreport-4.0/sos/report/plugins/teamd.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/teamd.py 2021-02-25 18:46:49.000000000 +0000 @@ -7,10 +7,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Teamd(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Teamd(Plugin, IndependentPlugin): short_desc = 'Network Interface Teaming' plugin_name = 'teamd' diff -Nru sosreport-4.0/sos/report/plugins/tftpserver.py sosreport-4.1/sos/report/plugins/tftpserver.py --- sosreport-4.0/sos/report/plugins/tftpserver.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/tftpserver.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class TftpServer(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class TftpServer(Plugin, IndependentPlugin): short_desc = 'TFTP Server information' plugin_name = 'tftpserver' diff -Nru sosreport-4.0/sos/report/plugins/tigervnc.py sosreport-4.1/sos/report/plugins/tigervnc.py --- sosreport-4.0/sos/report/plugins/tigervnc.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/tigervnc.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,28 @@ +# Copyright (C) 2021 Red Hat, Inc., Jake Hunsaker + +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +from sos.report.plugins import Plugin, RedHatPlugin + + +class TigerVNC(Plugin, RedHatPlugin): + + plugin_name = 'tigervnc' + packages = ('tigervnc-server',) + + def setup(self): + self.add_copy_spec([ + '/etc/tigervnc/vncserver-config-defaults', + '/etc/tigervnc/vncserver-config-mandatory', + '/etc/tigervnc/vncserver.users' + ]) + + self.add_cmd_output('vncserver -list') + +# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/report/plugins/ubuntu.py sosreport-4.1/sos/report/plugins/ubuntu.py --- sosreport-4.0/sos/report/plugins/ubuntu.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/ubuntu.py 2021-02-25 18:46:49.000000000 +0000 @@ -7,6 +7,7 @@ # See the LICENSE file in the source distribution for further information. from sos.report.plugins import Plugin, UbuntuPlugin +from sos.utilities import is_executable class Ubuntu(Plugin, UbuntuPlugin): @@ -23,7 +24,12 @@ ]) if self.is_installed('ubuntu-advantage-tools'): - self.add_cmd_output("ubuntu-advantage status") + if is_executable('ua'): + ua_tools_status = 'ua status' + else: + ua_tools_status = 'ubuntu-advantage status' + self.add_cmd_output(ua_tools_status) + if not self.get_option("all_logs"): self.add_copy_spec([ "/var/log/ubuntu-advantage.log", diff -Nru sosreport-4.0/sos/report/plugins/udev.py sosreport-4.1/sos/report/plugins/udev.py --- sosreport-4.0/sos/report/plugins/udev.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/udev.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Udev(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Udev(Plugin, IndependentPlugin): short_desc = 'udev dynamic device management' diff -Nru sosreport-4.0/sos/report/plugins/ufw.py sosreport-4.1/sos/report/plugins/ufw.py --- sosreport-4.0/sos/report/plugins/ufw.py 1970-01-01 00:00:00.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/ufw.py 2021-02-25 18:46:49.000000000 +0000 @@ -0,0 +1,34 @@ +# This file is part of the sos project: https://github.com/sosreport/sos +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# version 2 of the GNU General Public License. +# +# See the LICENSE file in the source distribution for further information. + +from sos.report.plugins import (Plugin, IndependentPlugin, SoSPredicate) + + +class ufw(Plugin, IndependentPlugin): + + short_desc = 'Uncomplicated FireWall' + + plugin_name = 'ufw' + profiles = ('system', 'network') + packages = ('ufw',) + + def setup(self): + self.add_copy_spec([ + "/etc/ufw", + "/var/log/ufw.Log" + ]) + + ufw_pred = SoSPredicate(self, kmods=['bpfilter', 'iptable_filter'], + required={'kmods': 'all'}) + + self.add_cmd_output([ + "ufw status numbered", + "ufw app list" + ], pred=ufw_pred) + +# vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/report/plugins/upstart.py sosreport-4.1/sos/report/plugins/upstart.py --- sosreport-4.0/sos/report/plugins/upstart.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/upstart.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Upstart(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Upstart(Plugin, IndependentPlugin): short_desc = 'Upstart init system' diff -Nru sosreport-4.0/sos/report/plugins/usb.py sosreport-4.1/sos/report/plugins/usb.py --- sosreport-4.0/sos/report/plugins/usb.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/usb.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Usb(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Usb(Plugin, IndependentPlugin): short_desc = 'USB devices' diff -Nru sosreport-4.0/sos/report/plugins/virsh.py sosreport-4.1/sos/report/plugins/virsh.py --- sosreport-4.0/sos/report/plugins/virsh.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/virsh.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class LibvirtClient(Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin): +class LibvirtClient(Plugin, IndependentPlugin): short_desc = 'client for libvirt virtualization API' diff -Nru sosreport-4.0/sos/report/plugins/vulkan.py sosreport-4.1/sos/report/plugins/vulkan.py --- sosreport-4.0/sos/report/plugins/vulkan.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/vulkan.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Vulkan(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Vulkan(Plugin, IndependentPlugin): short_desc = 'Vulkan' diff -Nru sosreport-4.0/sos/report/plugins/wireless.py sosreport-4.1/sos/report/plugins/wireless.py --- sosreport-4.0/sos/report/plugins/wireless.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/wireless.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,31 +6,24 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin, SoSPredicate -class Wireless(Plugin, DebianPlugin, UbuntuPlugin): +class Wireless(Plugin, IndependentPlugin): - short_desc = 'Wireless' + short_desc = 'Wireless Device Information' plugin_name = 'wireless' profiles = ('hardware', 'desktop', 'network') - files = ('/sbin/iw', '/usr/sbin/iw') + commands = ('iw', ) def setup(self): + wireless_pred = SoSPredicate(self, kmods=['cfg80211']) self.add_cmd_output([ "iw list", "iw dev", "iwconfig", "iwlist scanning" - ]) - - -class RedHatWireless(Wireless, RedHatPlugin): - - short_desc = 'Wireless' - - files = ('/usr/sbin/iw', '/usr/sbin/iwlist') - packages = ('iw', 'wireless-tools') + ], pred=wireless_pred) # vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/report/plugins/x11.py sosreport-4.1/sos/report/plugins/x11.py --- sosreport-4.0/sos/report/plugins/x11.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/x11.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class X11(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class X11(Plugin, IndependentPlugin): short_desc = 'X windowing system' diff -Nru sosreport-4.0/sos/report/plugins/xdp.py sosreport-4.1/sos/report/plugins/xdp.py --- sosreport-4.0/sos/report/plugins/xdp.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/xdp.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,11 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, \ - UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Xdp(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Xdp(Plugin, IndependentPlugin): short_desc = 'XDP program information' plugin_name = 'xdp' diff -Nru sosreport-4.0/sos/report/plugins/xfs.py sosreport-4.1/sos/report/plugins/xfs.py --- sosreport-4.0/sos/report/plugins/xfs.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/xfs.py 2021-02-25 18:46:49.000000000 +0000 @@ -6,10 +6,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Xfs(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Xfs(Plugin, IndependentPlugin): short_desc = 'XFS filesystem' diff -Nru sosreport-4.0/sos/report/plugins/xinetd.py sosreport-4.1/sos/report/plugins/xinetd.py --- sosreport-4.0/sos/report/plugins/xinetd.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/xinetd.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,10 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Xinetd(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): +class Xinetd(Plugin, IndependentPlugin): short_desc = 'xinetd information' diff -Nru sosreport-4.0/sos/report/plugins/yum.py sosreport-4.1/sos/report/plugins/yum.py --- sosreport-4.0/sos/report/plugins/yum.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/yum.py 2021-02-25 18:46:49.000000000 +0000 @@ -109,4 +109,9 @@ except IndexError: pass + def postproc(self): + regexp = r"(proxy_password(\s)*=(\s)*)(\S+)\n" + repl = r"\1********\n" + self.do_path_regex_sub("/etc/yum.repos.d/*", regexp, repl) + # vim: set et ts=4 sw=4 : diff -Nru sosreport-4.0/sos/report/plugins/zfs.py sosreport-4.1/sos/report/plugins/zfs.py --- sosreport-4.0/sos/report/plugins/zfs.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos/report/plugins/zfs.py 2021-02-25 18:46:49.000000000 +0000 @@ -7,10 +7,10 @@ # See the LICENSE file in the source distribution for further information. -from sos.report.plugins import Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin +from sos.report.plugins import Plugin, IndependentPlugin -class Zfs(Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin): +class Zfs(Plugin, IndependentPlugin): short_desc = 'ZFS filesystem' diff -Nru sosreport-4.0/sos.spec sosreport-4.1/sos.spec --- sosreport-4.0/sos.spec 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/sos.spec 2021-02-25 18:46:49.000000000 +0000 @@ -2,7 +2,7 @@ Summary: A set of tools to gather troubleshooting information from a system Name: sos -Version: 4.0 +Version: 4.1 Release: 1%{?dist} Group: Applications/System Source0: https://github.com/sosreport/sos/archive/%{name}-%{version}.tar.gz @@ -12,8 +12,8 @@ Url: https://github.com/sosreport/sos/ BuildRequires: python3-devel BuildRequires: gettext -Requires: libxml2-python -Requires: rpm-python +Requires: python3-libxml2 +Requires: python3-rpm Requires: tar Requires: xz Requires: python3-pexpect @@ -41,11 +41,11 @@ install -d -m 755 ${RPM_BUILD_ROOT}/etc/sos/extras.d install -m 644 %{name}.conf ${RPM_BUILD_ROOT}/etc/sos/%{name}.conf -rm ${RPM_BUILD_ROOT}/sos.conf +rm -rf ${RPM_BUILD_ROOT}/usr/config/ %find_lang %{name} || echo 0 -%files +%files -f %{name}.lang %{_sbindir}/sos %{_sbindir}/sosreport %{_sbindir}/sos-collector @@ -61,6 +61,9 @@ %config(noreplace) %{_sysconfdir}/sos/sos.conf %changelog +* Thu Feb 25 2021 Jake Hunsaker = 4.1 +- New upstream release + * Mon Aug 17 2020 Jake Hunsaker = 4.0 - New upstream release diff -Nru sosreport-4.0/tests/cleaner_tests.py sosreport-4.1/tests/cleaner_tests.py --- sosreport-4.0/tests/cleaner_tests.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/tests/cleaner_tests.py 2021-02-25 18:46:49.000000000 +0000 @@ -24,7 +24,8 @@ def setUp(self): self.mac_map = SoSMacMap() self.ip_map = SoSIPMap() - self.host_map = SoSHostnameMap(['redhat.com']) + self.host_map = SoSHostnameMap() + self.host_map.load_domains_from_options(['redhat.com']) self.kw_map = SoSKeywordMap() def test_mac_map_obfuscate_valid_v4(self): diff -Nru sosreport-4.0/tests/option_tests.py sosreport-4.1/tests/option_tests.py --- sosreport-4.0/tests/option_tests.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/tests/option_tests.py 2021-02-25 18:46:49.000000000 +0000 @@ -8,7 +8,8 @@ import unittest from sos.report.plugins import Plugin -from sos.policies import LinuxPolicy, InitSystem +from sos.policies.distros import LinuxPolicy +from sos.policies.init_systems import InitSystem class MockOptions(object): @@ -16,6 +17,8 @@ dry_run = False log_size = 25 allow_system_changes = False + skip_commands = [] + skip_files = [] class GlobalOptionTest(unittest.TestCase): diff -Nru sosreport-4.0/tests/plugin_tests.py sosreport-4.1/tests/plugin_tests.py --- sosreport-4.0/tests/plugin_tests.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/tests/plugin_tests.py 2021-02-25 18:46:49.000000000 +0000 @@ -14,7 +14,8 @@ from sos.report.plugins import Plugin, regex_findall, _mangle_command from sos.archive import TarFileArchive -from sos.policies import LinuxPolicy, InitSystem +from sos.policies.distros import LinuxPolicy +from sos.policies.init_systems import InitSystem PATH = os.path.dirname(__file__) @@ -114,6 +115,8 @@ log_size = 25 allow_system_changes = False no_postproc = False + skip_files = [] + skip_commands = [] class PluginToolTests(unittest.TestCase): diff -Nru sosreport-4.0/tests/policy_tests.py sosreport-4.1/tests/policy_tests.py --- sosreport-4.0/tests/policy_tests.py 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/tests/policy_tests.py 2021-02-25 18:46:49.000000000 +0000 @@ -7,7 +7,9 @@ # See the LICENSE file in the source distribution for further information. import unittest -from sos.policies import Policy, PackageManager, import_policy +from sos.policies import Policy, import_policy +from sos.policies.distros import LinuxPolicy +from sos.policies.package_managers import PackageManager from sos.report.plugins import (Plugin, IndependentPlugin, RedHatPlugin, DebianPlugin) @@ -16,6 +18,14 @@ distro = "Faux" +class FauxLinuxPolicy(LinuxPolicy): + distro = "FauxLinux" + + @classmethod + def set_forbidden_paths(cls): + return ['/etc/secret'] + + class FauxPlugin(Plugin, IndependentPlugin): pass @@ -30,12 +40,19 @@ class PolicyTests(unittest.TestCase): + def test_independent_only(self): p = FauxPolicy() p.valid_subclasses = [] self.assertTrue(p.validate_plugin(FauxPlugin)) + def test_forbidden_paths_building(self): + p = FauxLinuxPolicy(probe_runtime=False) + self.assertTrue('*.pyc' in p.forbidden_paths) + self.assertTrue('/etc/passwd' in p.forbidden_paths) + self.assertTrue('/etc/secret' in p.forbidden_paths) + def test_redhat(self): p = FauxPolicy() p.valid_subclasses = [RedHatPlugin] diff -Nru sosreport-4.0/tests/simple.sh sosreport-4.1/tests/simple.sh --- sosreport-4.0/tests/simple.sh 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/tests/simple.sh 2021-02-25 18:46:49.000000000 +0000 @@ -1,3 +1,4 @@ +#!/bin/bash # This file is part of the sos project: https://github.com/sosreport/sos # # This copyrighted material is made available to anyone wishing to use, @@ -5,7 +6,6 @@ # version 2 of the GNU General Public License. # # See the LICENSE file in the source distribution for further information. -#/bin/bash # A quick port of the travis tests to bash, requires root # TODO # * look into using a framework.. @@ -14,12 +14,13 @@ # * make it better validate archives and contents PYTHON=${1:-/usr/bin/python3} -SOSPATH=${2:-./bin/sos report} +SOSPATH=${2:-./bin/sos report --batch --tmp-dir=/var/tmp } NUMOFFAILURES=0 -summary="Summary\n" +summary="\nSummary\n" +FAIL_LIST="" -run_expecting_sucess () { +run_expecting_success () { #$1 - is command options #$2 - kind of check to do, so far only extract FAIL=false @@ -35,7 +36,7 @@ echo "### Success" else echo "!!! FAILED !!!" - FAIL=true + add_failure "$1 failed during execution" fi end=`date +%s` @@ -43,8 +44,7 @@ echo "#### Sos Total time (seconds):" $runtime if [ -s /dev/shm/stderr ]; then - FAIL=true - echo "!!! FAILED !!!" + add_failure "test generated stderr output, see above" echo "### start stderr" cat /dev/shm/stderr echo "### end stderr" @@ -56,21 +56,19 @@ if [ "extract" = "$2" ]; then echo "### start extraction" - rm -f /var/tmp/sosreport*md5 + rm -f /var/tmp/sosreport*sha256 mkdir /var/tmp/sosreport_test/ tar xfa /var/tmp/sosreport*.tar* -C /var/tmp/sosreport_test --strip-components=1 if [ -s /var/tmp/sosreport_test/sos_logs/*errors.txt ]; then FAIL=true echo "!!! FAILED !!!" + add_failure "Test $1 generated errors" echo "#### *errors.txt output" ls -alh /var/tmp/sosreport_test/sos_logs/ cat /var/tmp/sosreport_test/sos_logs/*errors.txt fi echo "### stop extraction" fi - - size="$(grep Size /dev/shm/stdout)" - summary="${summary} \n failures ${FAIL} \t time ${runtime} \t ${size} \t ${1} " echo "######### DONE WITH $1 #########" @@ -82,7 +80,160 @@ fi } -# If /etc/sos/sos.conf doesn't exist let's just make it.. +update_summary () { + size="$(grep Size /dev/shm/stdout)" + size="$(echo "${size:-"Size 0.00MiB"}")" + summary="${summary} \n failures ${FAIL} \t time ${runtime} \t ${size} \t ${1} " +} + +update_failures () { + if $FAIL; then + NUMOFFAILURES=$(($NUMOFFAILURES + 1)) + fi +} + +add_failure () { + FAIL=true + echo "!!! TEST FAILED: $1 !!!" + FAIL_LIST="${FAIL_LIST}\n \t ${FUNCNAME[1]}: \t\t ${1}" +} + +# Test a no frills run with verbosity and make sure the expected items exist +test_normal_report () { + cmd="-vvv" + # get a list of initial kmods loaded + kmods=( $(lsmod | cut -f1 -d ' ' | sort) ) + run_expecting_success "$cmd" extract + if [ $? -eq 0 ]; then + if [ ! -f /var/tmp/sosreport_test/sos_reports/sos.html ]; then + add_failure "did not generate html reports" + fi + if [ ! -f /var/tmp/sosreport_test/sos_reports/manifest.json ]; then + add_failure "did not generate manifest.json" + fi + if [ ! -f /var/tmp/sosreport_test/free ]; then + add_failure "did not create free symlink in archive root" + fi + if [ ! "$(grep "DEBUG" /var/tmp/sosreport_test/sos_logs/sos.log)" ]; then + add_failure "did not find debug logging when using -vvv" + fi + # new list, see if we added any + new_kmods=( $(lsmod | cut -f1 -d ' ' | sort) ) + if [ "$(printf '%s\n' "${kmods[@]}" "${new_kmods[@]}" | sort | uniq -u)" ]; then + add_failure "new kernel modules loaded during execution" + echo "$(printf '%s\n' "${kmods[@]}" "${new_kmods[@]}" | sort | uniq -u)" + fi + update_failures + update_summary "$cmd" + fi +} + +# Test for correctly skipping html generation, and label setting +test_noreport_label_only () { + cmd="--no-report --label TEST -o hardware" + run_expecting_success "$cmd" extract + if [ $? -eq 0 ]; then + if [ -f /var/tmp/sosreport_test/sos_reports/sos.html ]; then + add_failure "html report generated when --no-report used" + fi + if [ ! $(grep /var/tmp/sosreport-*TEST* /dev/shm/stdout) ]; then + add_failure "no label set on archive" + fi + count=$(find /var/tmp/sosreport_test/sos_commands/* -type d | wc -l) + if [[ "$count" -gt 1 ]]; then + add_failure "more than one plugin ran when using -o hardware" + fi + update_failures + fi + update_summary "$cmd" +} + +# test using mask +test_mask () { + cmd="--mask" + run_expecting_success "$cmd" extract + if [ $? -eq 0 ]; then + if [ ! $(grep host0 /var/tmp/sosreport_test/hostname) ]; then + add_failure "hostname not obfuscated with --mask" + fi + # we don't yet support binary obfuscation, so skip binary matches + if [ "$(grep -rI `hostname` /var/tmp/sosreport_test/*)" ]; then + add_failure "hostname not obfuscated in all places" + echo "$(grep -rI `hostname` /var/tmp/sosreport_test/*)" + fi + # only tests first interface + mac_addr=$(cat /sys/class/net/$(ip route show default | awk '/default/ {print $5}')/address) + if [ "$(grep -rI $mac_addr /var/tmp/sosreport_test/*)" ]; then + add_failure "MAC address not obfuscated in all places" + echo "$(grep -rI $mac_addr /var/tmp/sosreport_test/*)" + fi + # only tests first interface + ip_addr=$(ip route show default | awk '/default/ {print $3}') + if [ "$(grep -rI $ip_addr /var/tmp/sosreport_test/*)" ]; then + add_failure "IP address not obfuscated in all places" + echo "$(grep -rI $ip_addr /var/tmp/sosreport/_test/*)" + fi + update_failures + fi + update_summary "$cmd" +} + +# test log-size, env vars, and compression type +test_logsize_env_gzip () { + cmd="--log-size 0 --no-env-vars -z gzip" + run_expecting_success "$cmd" extract + if [ $? -eq 0 ]; then + if [ -f /var/tmp/sosreport_test/environment ]; then + add_failure "env vars captured when using --no-env-vars" + fi + if [ ! $(grep /var/tmp/sosreport*.gz /dev/shm/stdout) ]; then + add_failure "archive was not gzip compressed using -z gzip" + fi + update_failures + fi + update_summary "$cmd" +} + +# test plugin enablement, plugopts and at the same time ensure our list option parsing is working +test_enable_opts_postproc () { + cmd="-e opencl -v -k kernel.with-timer,libraries.ldconfigv --no-postproc" + run_expecting_success "$cmd" extract + if [ $? -eq 0 ]; then + if [ ! "$(grep "opencl" /dev/shm/stdout)" ]; then + add_failure "force enabled plugin opencl did not run" + fi + if [ ! -f /var/tmp/sosreport_test/proc/timer* ]; then + add_failure "/proc/timer* not captured when using -k kernel.with-timer" + fi + if [ ! -f /var/tmp/sosreport_test/sos_commands/libraries/ldconfig_-v* ]; then + add_failure "ldconfig -v not captured when using -k libraries.ldconfigv" + fi + if [ "$(grep "substituting" /var/tmp/sosreport_test/sos_logs/sos.log)" ]; then + add_failure "post-processing ran while using --no-post-proc" + fi + + update_failures + update_summary "$cmd" + fi +} + +# test if --build and --threads work properly +test_build_threads () { + cmd="--build -t1 -o host,kernel,filesys,hardware,date,logs" + run_expecting_success "$cmd" + if [ $? -eq 0 ]; then + if [ ! "$(grep "Your sosreport build tree" /dev/shm/stdout)" ]; then + add_failure "did not save the build tree" + fi + if [ $(grep "Finishing plugins" /dev/shm/stdout) ]; then + add_failure "did not limit threads when using --threads 1" + fi + update_failures + update_summary "$cmd" + fi +} + +# If /etc/sos/sos.conf doesn't exist let's just make it if [ -f /etc/sos/sos.conf ]; then echo "/etc/sos/sos.conf already exists" else @@ -91,29 +242,27 @@ touch /etc/sos/sos.conf fi + # Runs not generating sosreports -run_expecting_sucess " -l" -run_expecting_sucess " --list-presets" -run_expecting_sucess " --list-profiles" - -# Test generating sosreports, 3 (new) options at a time -# Trying to do --batch (1 label/archive/report/verbosity change) (other changes) -run_expecting_sucess " --batch --build --no-env-vars " # Only --build test -run_expecting_sucess " --batch --no-report -o hardware " extract -run_expecting_sucess " --batch --label TEST -a -c never" extract -run_expecting_sucess " --batch --debug --log-size 0 -c always" extract -run_expecting_sucess " --batch -z xz --log-size 1" extract -run_expecting_sucess " --batch -z gzip" extract -run_expecting_sucess " --batch -t 1 -n hardware" extract -run_expecting_sucess " --batch --quiet -e opencl -k kernel.with-timer" extract -run_expecting_sucess " --batch --case-id 10101 --all-logs --since=$(date -d "yesterday 13:00" '+%Y%m%d') " extract -run_expecting_sucess " --batch --verbose --no-postproc" extract -run_expecting_sucess " --batch --mask" extract +run_expecting_success " -l"; update_summary "List plugins" +run_expecting_success " --list-presets"; update_summary "List presets" +run_expecting_success " --list-profiles"; update_summary "List profiles" + +# Runs generating sosreports +# TODO: +# - find a way to test if --since is working +test_build_threads +test_normal_report +test_enable_opts_postproc +test_noreport_label_only +test_logsize_env_gzip +test_mask -echo $summary +echo -e $summary if [ $NUMOFFAILURES -gt 0 ]; then - echo "FAILED $NUMOFFAILURES" + echo -e "\nTests Failed: $NUMOFFAILURES\nFailures within each test:" + echo -e $FAIL_LIST exit 1 else echo "Everything worked!" diff -Nru sosreport-4.0/.travis.yml sosreport-4.1/.travis.yml --- sosreport-4.0/.travis.yml 2020-08-17 21:41:02.000000000 +0000 +++ sosreport-4.1/.travis.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,65 +0,0 @@ -jobs: - include: - - name: "20.04 flake8 and native run (py3.8)" - os: linux - dist: focal - language: shell - install: sudo apt-get update; sudo apt install flake8 python3-pexpect; - script: - - "flake8 sos tests bin/*" - - "sudo ./tests/simple.sh" - - name: "18.04 pycodestyle and native run (py3.6)" - os: linux - dist: bionic - language: shell - install: sudo apt-get update; sudo apt install python3-pexpect; - script: "sudo ./tests/simple.sh" - - name: "18.04 native run for arm64" - os: linux - dist: bionic - arch: arm64 - language: shell - script: "sudo ./tests/simple.sh" - - name: "18.04 native s390x" - os: linux - dist: bionic - arch: s390x - language: shell - script: "sudo ./tests/simple.sh" - - name: "nosetests and travis Python 3.6" - os: linux - dist: bionic - language: python - python: "3.6" - install: pip install -r requirements.txt; python3 setup.py install; - script: - - "nosetests -v --with-cover --cover-package=sos --cover-html" - - "sudo ./tests/simple.sh ~/virtualenv/python$TRAVIS_PYTHON_VERSION/bin/python" - - name: "nosetests and travis Python 3.7" - os: linux - dist: bionic - language: python - python: "3.7" - install: pip install -r requirements.txt; python3 setup.py install; - script: - - "nosetests -v --with-cover --cover-package=sos --cover-html" - - "sudo ./tests/simple.sh ~/virtualenv/python$TRAVIS_PYTHON_VERSION/bin/python" - - name: "nosetests and travis Python 3.8" - os: linux - dist: bionic - language: python - python: "3.8" - install: pip install -r requirements.txt; python3 setup.py install; - script: - - "nosetests -v --with-cover --cover-package=sos --cover-html" - - "sudo ./tests/simple.sh ~/virtualenv/python$TRAVIS_PYTHON_VERSION/bin/python" - -notifications: - email: - sos-devel@redhat.com - irc: - channels: - - "us.freenode.net#sosreport" - on_success: change -git: - depth: 20