aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMauricio Faria de Oliveira <mfo@canonical.com>2020-11-11 20:40:02 +0000
committerJake Hunsaker <jhunsake@redhat.com>2020-11-17 11:16:27 -0500
commitddc12aedae473c8af33792533afb736f6f5fff7c (patch)
tree471434aef16beee8e5dae33be11cd9e36a23e88c
parent7c88d13ee1780c8f351b9cd26df2401c153ccaad (diff)
downloadsos-ddc12aedae473c8af33792533afb736f6f5fff7c.tar.gz
[Plugin] exec_cmd(): pass stderr along to sos_get_command_output()
The sosreport of a system with this issue: ``` # ip netns Error: Peer netns reference is invalid. Error: Peer netns reference is invalid. test-ns ``` Shows this difference between the plugins ebpf and networking (callers of `ip netns`): ``` # ./bin/sos report -o ebpf,networking --batch # tar tf /tmp/sosreport-*.tar.xz | grep ip_netns_exec .../sos_commands/ebpf/ip_netns_exec_test-ns_bpftool_net_list .../sos_commands/networking/ip_netns_exec_Error_ip6tables-save .../sos_commands/networking/ip_netns_exec_Error_ip6tables-save.1 ... .../sos_commands/networking/ip_netns_exec_test-ns_ip6tables-save ... ``` Only the networking plugin called `ip netns exec <ns> <cmd>` on `Error:` lines. Hmm. ... The networking plugin calls `collect_cmd_output()`, which has parameter stderr=True` as default: ``` def collect_cmd_output(self, cmd, suggest_filename=None, ... stderr=True, ... ``` The ebpf plugin calls `exec_cmd()`, BUT it has parameter `stderr=True` as default AS WELL: ``` def exec_cmd(self, cmd, timeout=cmd_timeout, stderr=True, ... ``` So, why the difference? ... Well, it turns out `exec_cmd()` does NOT pass the `stderr` parameter along to `sos_get_command_output()`: [but `collect_cmd_output()` does.] ``` return sos_get_command_output(cmd, timeout=timeout, chroot=root, chdir=runat, binary=binary, env=env, foreground=foreground) ``` And `sos_get_command_output()` has `stderr=False` as default: ``` def sos_get_command_output(command, timeout=300, stderr=False, ... ``` Thus, despite `exec_cmd()` has `stderr=True` as default it is _ignored_, as its callee `sos_get_command_output()` has `stderr=False` as default. This explains the output difference between the ebpf/networking plugins. ... Looking back for details, the origin of the change is in PR#1807 [1], with commit e8bb94c (refactor command functions/introduce `exec_cmd()`) and commit e51d3e6 (update caller plugins). The PR has no mentions/discussion around `stderr` in comments/review, so it's apparently an oversight during the refactor, I guess. Because, previously the refactored callers of `sos_get_command_output()` (`get_command_output()`, `call_ext_prog()`, `[_]get_cmd_output_now()`) all had `stderr=True` as parameter/default, passing it along to callee. (The only exception is in `_collect_cmd_output()` which does the first call in the chroot with `stderr`, and falls back to non-chroot without `stderr`, but it was already that way. Thus not changing this case.) ... So, it seems right and safe and opportune to fix this inconsistency between default `stderr` in `exec_cmd()` / `sos_get_command_output()` by just passing it along between them: Right: because it restores the previous behavior (before refactor) assumed/accepted by callers that did not specify `stderr=False`. Safe: because it restores the actual behavior to previous behavior, and if plugins handled `stderr` lines previously, they should also handle it now, for the most part (i.e., not any new `stderr` lines, but they are currently ignoring those, and should get them covered.) Opportune: because if any plugin breaks _now_ because of this, it is a new/post-refactor change that decided to use `exec_cmd()` with the default that is inconsistent with actual behavior, and it seems okay to take bugs for that, so to identify and fix such cases. Hopefully those points are reasonable with the project's philosophy. ... Test-case: Setup: ``` $ sudo python3 from sos import SoS from sos.report.plugins import Plugin sos = SoS(['report']) plugin = Plugin(sos._component.get_commons()) ``` Before: ``` >>> print(plugin.exec_cmd('ip netns')['output']) test-ns >>> print(plugin.exec_cmd('ip netns', stderr=True)['output']) test-ns >>> print(plugin.exec_cmd('ip netns', stderr=False)['output']) test-ns ``` After: ``` >>> print(plugin.exec_cmd('ip netns')['output']) Error: Peer netns reference is invalid. Error: Peer netns reference is invalid. test-ns >>> print(plugin.exec_cmd('ip netns', stderr=True)['output']) Error: Peer netns reference is invalid. Error: Peer netns reference is invalid. test-ns >>> print(plugin.exec_cmd('ip netns', stderr=False)['output']) test-ns ``` ... Test-suite: ``` # ./tests/simple.sh <...> Summary failures false time 2 -l failures false time 3 --list-presets failures false time 3 --list-profiles failures false time 17 --batch --build --no-env-vars failures false time 3 Size 14.23KiB --batch --no-report -o hardware failures false time 20 Size 2.99MiB --batch --label TEST -a -c never failures false time 20 Size 2.81MiB --batch --debug --log-size 0 -c always failures false time 20 Size 2.84MiB --batch -z xz --log-size 1 failures false time 19 Size 4.01MiB --batch -z gzip failures false time 32 Size 2.76MiB --batch -t 1 -n hardware failures false time 19 --batch --quiet -e opencl -k kernel.with-timer failures false time 22 Size 4.31MiB --batch --case-id 10101 --all-logs --since=20201110 failures false time 21 Size 3.06MiB --batch --verbose --no-postproc failures false time 39 Size 2.82MiB --batch --mask Everything worked! ``` [1] https://github.com/sosreport/sos/pull/1807 Resolves: #2306 Signed-off-by: Mauricio Faria de Oliveira <mfo@canonical.com> Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
-rw-r--r--sos/report/plugins/__init__.py2
1 files changed, 1 insertions, 1 deletions
diff --git a/sos/report/plugins/__init__.py b/sos/report/plugins/__init__.py
index ed0a07ef..6eeef5c1 100644
--- a/sos/report/plugins/__init__.py
+++ b/sos/report/plugins/__init__.py
@@ -2099,7 +2099,7 @@ class Plugin(object):
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