diff options
author | Jake Hunsaker <jhunsake@redhat.com> | 2023-02-07 12:54:49 -0500 |
---|---|---|
committer | Jake Hunsaker <jhunsake@redhat.com> | 2023-03-07 17:34:32 -0500 |
commit | 61e36af743cd0bffcac2c0759ac13d2493caec93 (patch) | |
tree | 81a8e93279bf13cd3099cff99bdecd92f170b9b0 | |
parent | e8dc0e55988b36d0476bcae741652208356f0f07 (diff) | |
download | sos-61e36af743cd0bffcac2c0759ac13d2493caec93.tar.gz |
[report] Allow users to constrain sos process priority
Adds a new `--low-priority` option to report, which will attempt to
constrain the process priority for the report generation. We do this by
attempting to set ourselves to an 'idle' IO class, as well as setting
our niceness to 19 to avoid contending for CPU time.
This is also exposed via `sos collect`, however users should note that
this will not be effective until the sos-4.5.1 release.
Closes: #3127
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
-rw-r--r-- | man/en/sos-report.1 | 6 | ||||
-rw-r--r-- | sos/collector/__init__.py | 3 | ||||
-rw-r--r-- | sos/collector/sosnode.py | 2 | ||||
-rw-r--r-- | sos/policies/distros/__init__.py | 37 | ||||
-rw-r--r-- | sos/report/__init__.py | 17 | ||||
-rw-r--r-- | tests/report_tests/low_priority_tests.py | 33 |
6 files changed, 96 insertions, 2 deletions
diff --git a/man/en/sos-report.1 b/man/en/sos-report.1 index 4facd556..c4859b8f 100644 --- a/man/en/sos-report.1 +++ b/man/en/sos-report.1 @@ -33,6 +33,7 @@ sos report \- Collect and package diagnostic and support data [--skip-commands commands]\fR [--skip-files files]\fR [--allow-system-changes]\fR + [--low-priority]\fR [-z|--compression-type method]\fR [--encrypt]\fR [--encrypt-key KEY]\fR @@ -230,6 +231,11 @@ for example \fB/etc/sos/*\fR. .B \--allow-system-changes Run commands even if they can change the system (e.g. load kernel modules). .TP +.B \--low-priority +Set sos to execute as a low priority process so that is does not interfere with +other processes running on the system. Specific distributions may set their own +constraints, but by default this involves setting process niceness to 19 and, if +available, setting an idle IO class via ionice. .B \-z, \--compression-type METHOD Override the default compression type specified by the active policy. .TP diff --git a/sos/collector/__init__.py b/sos/collector/__init__.py index 78628761..63816cf6 100644 --- a/sos/collector/__init__.py +++ b/sos/collector/__init__.py @@ -95,6 +95,7 @@ class SoSCollector(SoSComponent): 'label': '', 'list_options': False, 'log_size': 0, + 'low_priority': False, 'map_file': '/etc/sos/cleaner/default_mapping', 'primary': '', 'namespaces': None, @@ -309,6 +310,8 @@ class SoSCollector(SoSComponent): sos_grp.add_argument('--log-size', default=0, type=int, help='Limit the size of individual logs ' '(not journals) in MiB') + sos_grp.add_argument('--low-priority', action='store_true', + default=False, help='Run reports as low priority') sos_grp.add_argument('-n', '--skip-plugins', action="extend", help='Skip these plugins') sos_grp.add_argument('-o', '--only-plugins', action="extend", diff --git a/sos/collector/sosnode.py b/sos/collector/sosnode.py index 19609d98..f45df4f6 100644 --- a/sos/collector/sosnode.py +++ b/sos/collector/sosnode.py @@ -648,6 +648,8 @@ class SosNode(): if self.check_sos_version('4.5.2'): if self.opts.journal_size: sos_opts.append(f"--journal-size={self.opts.journal_size}") + if self.opts.low_priority: + sos_opts.append('--low-priority') self.update_cmd_from_cluster() diff --git a/sos/policies/distros/__init__.py b/sos/policies/distros/__init__.py index d848e8e3..2db57fbf 100644 --- a/sos/policies/distros/__init__.py +++ b/sos/policies/distros/__init__.py @@ -21,7 +21,8 @@ from sos.policies.runtimes.crio import CrioContainerRuntime from sos.policies.runtimes.podman import PodmanContainerRuntime from sos.policies.runtimes.docker import DockerContainerRuntime -from sos.utilities import shell_out, is_executable, bold +from sos.utilities import (shell_out, is_executable, bold, + sos_get_command_output) try: @@ -277,6 +278,9 @@ class LinuxPolicy(Policy): cmdline_opts = self.commons['cmdlineopts'] caseid = cmdline_opts.case_id if cmdline_opts.case_id else "" + if cmdline_opts.low_priority: + self._configure_low_priority() + # Set the cmdline settings to the class attrs that are referenced later # The policy default '_' prefixed versions of these are untouched to # allow fallback @@ -317,6 +321,37 @@ class LinuxPolicy(Policy): return + def _configure_low_priority(self): + """Used to constrain sos to a 'low priority' execution, potentially + letting individual policies set their own definition of what that is. + + By default, this will attempt to assign sos to an idle io class via + ionice if available. We will also renice our own pid to 19 in order to + not cause competition with other host processes for CPU time. + """ + _pid = os.getpid() + if is_executable('ionice'): + ret = sos_get_command_output( + f"ionice -c3 -p {_pid}", timeout=5 + ) + if ret['status'] == 0: + self.soslog.info('Set IO class to idle') + else: + msg = (f"Error setting IO class to idle: {ret['output']} " + f"(exit code {ret['status']})") + self.soslog.error(msg) + else: + self.ui_log.warning( + "Warning: unable to constrain report to idle IO class: " + "ionice is not available." + ) + + try: + os.nice(20) + self.soslog.info('Set niceness of report to 19') + except Exception as err: + self.soslog.error(f"Error setting report niceness to 19: {err}") + def prompt_for_upload_user(self): """Should be overridden by policies to determine if a user needs to be provided or not diff --git a/sos/report/__init__.py b/sos/report/__init__.py index c7dd858b..70fe9c8c 100644 --- a/sos/report/__init__.py +++ b/sos/report/__init__.py @@ -101,6 +101,7 @@ class SoSReport(SoSComponent): 'list_presets': False, 'list_profiles': False, 'log_size': 25, + 'low_priority': False, 'map_file': '/etc/sos/cleaner/default_mapping', 'skip_commands': [], 'skip_files': [], @@ -268,6 +269,10 @@ class SoSReport(SoSComponent): type=int, default=25, help="limit the size of collected logs " "(not journals) in MiB") + report_grp.add_argument("--low-priority", action="store_true", + default=False, + help="generate report with low system priority" + ) report_grp.add_argument("--namespaces", default=None, help="limit number of namespaces to collect " "output for - 0 means unlimited") @@ -1714,6 +1719,16 @@ class SoSReport(SoSComponent): self.report_md.add_field('preset', self.preset.name if self.preset else 'unset') self.report_md.add_list('profiles', self.opts.profiles) + + _io_class = 'unknown' + if is_executable('ionice'): + _io = sos_get_command_output(f"ionice -p {os.getpid()}") + if _io['status'] == 0: + _io_class = _io['output'].split()[0].strip(':') + self.report_md.add_section('priority') + self.report_md.priority.add_field('io_class', _io_class) + self.report_md.priority.add_field('niceness', os.nice(0)) + self.report_md.add_section('devices') for key, value in self.devices.items(): self.report_md.devices.add_field(key, value) @@ -1795,9 +1810,9 @@ class SoSReport(SoSComponent): if not self.verify_plugins(): return False - self.add_manifest_data() self.batch() self.prework() + self.add_manifest_data() self.setup() self.collect() if not self.opts.no_env_vars: diff --git a/tests/report_tests/low_priority_tests.py b/tests/report_tests/low_priority_tests.py new file mode 100644 index 00000000..97bf0a28 --- /dev/null +++ b/tests/report_tests/low_priority_tests.py @@ -0,0 +1,33 @@ +# 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 os.path import exists +from sos_tests import StageOneReportTest + + +class LowPrioTest(StageOneReportTest): + """ + Ensures that --low-priority properly sets our defined constraints on our + own process + + :avocado: tags=stageone + """ + + sos_cmd = '--low-priority -o kernel' + + def test_ionice_class_set(self): + _class = self.manifest['components']['report']['priority']['io_class'] + if exists('/usr/bin/ionice'): + self.assertSosLogContains('Set IO class to idle') + self.assertEqual(_class, 'idle') + else: + self.assertEqual(_class, 'unknown') + + def test_niceness_set(self): + self.assertSosLogContains('Set niceness of report to 19') + self.assertEqual(self.manifest['components']['report']['priority']['niceness'], 19) |