Blame SOURCES/sos-bz1937418-add-cmd-timeout.patch

24a42c
From 90b6b709e9f4002376b656b155d00d85382f1828 Mon Sep 17 00:00:00 2001
24a42c
From: Pavel Moravec <pmoravec@redhat.com>
24a42c
Date: Mon, 29 Mar 2021 16:23:01 +0200
24a42c
Subject: [PATCH] [report] add --cmd-timeout option
24a42c
24a42c
Add --cmd-timeout option to configure command timeout. Plugin-specific
24a42c
option of the same name (i.e. -k logs.cmd-timeout=60) can control the
24a42c
timeout per plugin.
24a42c
24a42c
Option defaults and global/plugin-specific option preference follows the
24a42c
--plugin-timeout rules.
24a42c
24a42c
Resolves: #2466
24a42c
24a42c
Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
24a42c
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
24a42c
---
24a42c
 man/en/sos-report.1            | 18 +++++++++-
24a42c
 sos/collector/__init__.py      |  3 ++
24a42c
 sos/collector/sosnode.py       |  5 +++
24a42c
 sos/options.py                 |  3 +-
24a42c
 sos/report/__init__.py         |  5 ++-
24a42c
 sos/report/plugins/__init__.py | 63 ++++++++++++++++++++++++----------
24a42c
 6 files changed, 76 insertions(+), 21 deletions(-)
24a42c
24a42c
diff --git a/man/en/sos-report.1 b/man/en/sos-report.1
24a42c
index 81005959..51cf3436 100644
24a42c
--- a/man/en/sos-report.1
24a42c
+++ b/man/en/sos-report.1
24a42c
@@ -17,6 +17,7 @@ sosreport \- Collect and package diagnostic and support data
24a42c
           [--label label] [--case-id id]\fR
24a42c
           [--threads threads]\fR
24a42c
           [--plugin-timeout TIMEOUT]\fR
24a42c
+          [--cmd-timeout TIMEOUT]\fR
24a42c
           [-s|--sysroot SYSROOT]\fR
24a42c
           [-c|--chroot {auto|always|never}\fR
24a42c
           [--tmp-dir directory]\fR
24a42c
@@ -247,7 +248,7 @@ Specify a timeout in seconds to allow each plugin to run for. A value of 0
24a42c
 means no timeout will be set. A value of -1 is used to indicate the default
24a42c
 timeout of 300 seconds.
24a42c
 
24a42c
-Note that this options sets the timeout for all plugins. If you want to set
24a42c
+Note that this option sets the timeout for all plugins. If you want to set
24a42c
 a timeout for a specific plugin, use the 'timeout' plugin option available to
24a42c
 all plugins - e.g. '-k logs.timeout=600'.
24a42c
 
24a42c
@@ -255,6 +256,21 @@ The plugin-specific timeout option will override this option. For example, using
24a42c
 \'--plugin-timeout=60 -k logs.timeout=600\' will set a timeout of 600 seconds for
24a42c
 the logs plugin and 60 seconds for all other enabled plugins.
24a42c
 .TP
24a42c
+.B \--cmd-timeout TIMEOUT
24a42c
+Specify a timeout limit in seconds for a command execution. Same defaults logic
24a42c
+from --plugin-timeout applies here.
24a42c
+
24a42c
+This option sets the command timeout for all plugins. If you want to set a cmd
24a42c
+timeout for a specific plugin, use the 'cmd-timeout' plugin option available to
24a42c
+all plugins - e.g. '-k logs.cmd-timeout=600'.
24a42c
+
24a42c
+Again, the same plugin/global precedence logic as for --plugin-timeout applies
24a42c
+here.
24a42c
+
24a42c
+Note that setting --cmd-timeout (or -k logs.cmd-timeout) high should be followed
24a42c
+by increasing the --plugin-timeout equivalent, otherwise the plugin can easily
24a42c
+timeout on slow commands execution.
24a42c
+.TP
24a42c
 .B \--case-id NUMBER
24a42c
 Specify a case identifier to associate with the archive.
24a42c
 Identifiers may include alphanumeric characters, commas and periods ('.').
24a42c
diff --git a/sos/collector/__init__.py b/sos/collector/__init__.py
24a42c
index 406c8f35..1ae73508 100644
24a42c
--- a/sos/collector/__init__.py
24a42c
+++ b/sos/collector/__init__.py
24a42c
@@ -82,6 +82,7 @@ class SoSCollector(SoSComponent):
24a42c
         'password_per_node': False,
24a42c
         'plugin_options': [],
24a42c
         'plugin_timeout': None,
24a42c
+        'cmd_timeout': None,
24a42c
         'preset': '',
24a42c
         'save_group': '',
24a42c
         'since': '',
24a42c
@@ -276,6 +277,8 @@ class SoSCollector(SoSComponent):
24a42c
                              help='Do not collect env vars in sosreports')
24a42c
         sos_grp.add_argument('--plugin-timeout', type=int, default=None,
24a42c
                              help='Set the global plugin timeout value')
24a42c
+        sos_grp.add_argument('--cmd-timeout', type=int, default=None,
24a42c
+                             help='Set the global command timeout value')
24a42c
         sos_grp.add_argument('--since', default=None,
24a42c
                              help=('Escapes archived files older than date. '
24a42c
                                    'This will also affect --all-logs. '
24a42c
diff --git a/sos/collector/sosnode.py b/sos/collector/sosnode.py
24a42c
index a1679655..dbbee12e 100644
24a42c
--- a/sos/collector/sosnode.py
24a42c
+++ b/sos/collector/sosnode.py
24a42c
@@ -664,6 +664,11 @@ class SosNode():
24a42c
                     '--skip-files=%s' % (quote(self.opts.skip_files))
24a42c
                 )
24a42c
 
24a42c
+        if self.check_sos_version('4.2'):
24a42c
+            if self.opts.cmd_timeout:
24a42c
+                sos_opts.append('--cmd-timeout=%s'
24a42c
+                                % quote(str(self.opts.cmd_timeout)))
24a42c
+
24a42c
         sos_cmd = sos_cmd.replace(
24a42c
             'sosreport',
24a42c
             os.path.join(self.host.sos_bin_path, self.sos_bin)
24a42c
diff --git a/sos/options.py b/sos/options.py
24a42c
index b82a7d36..1eda55d6 100644
24a42c
--- a/sos/options.py
24a42c
+++ b/sos/options.py
24a42c
@@ -283,7 +283,8 @@ class SoSOptions():
24a42c
             if name in ("add_preset", "del_preset", "desc", "note"):
24a42c
                 return False
24a42c
             # Exception list for options that still need to be reported when 0
24a42c
-            if name in ['log_size', 'plugin_timeout'] and value == 0:
24a42c
+            if name in ['log_size', 'plugin_timeout', 'cmd_timeout'] \
24a42c
+               and value == 0:
24a42c
                 return True
24a42c
             return has_value(name, value)
24a42c
 
24a42c
diff --git a/sos/report/__init__.py b/sos/report/__init__.py
24a42c
index 25478ba7..945d0fc1 100644
24a42c
--- a/sos/report/__init__.py
24a42c
+++ b/sos/report/__init__.py
24a42c
@@ -107,6 +107,7 @@ class SoSReport(SoSComponent):
24a42c
         'only_plugins': [],
24a42c
         'preset': 'auto',
24a42c
         'plugin_timeout': 300,
24a42c
+        'cmd_timeout': 300,
24a42c
         'profiles': [],
24a42c
         'since': None,
24a42c
         'verify': False,
24a42c
@@ -266,6 +267,8 @@ class SoSReport(SoSComponent):
24a42c
                                 help="A preset identifier", default="auto")
24a42c
         report_grp.add_argument("--plugin-timeout", default=None,
24a42c
                                 help="set a timeout for all plugins")
24a42c
+        report_grp.add_argument("--cmd-timeout", default=None,
24a42c
+                                help="set a command timeout for all plugins")
24a42c
         report_grp.add_argument("-p", "--profile", "--profiles",
24a42c
                                 action="extend", dest="profiles", type=str,
24a42c
                                 default=[],
24a42c
@@ -709,7 +712,7 @@ class SoSReport(SoSComponent):
24a42c
 
24a42c
             self.ui_log.info(_("The following plugin options are available:"))
24a42c
             for (plug, plugname, optname, optparm) in self.all_options:
24a42c
-                if optname in ('timeout', 'postproc'):
24a42c
+                if optname in ('timeout', 'postproc', 'cmd-timeout'):
24a42c
                     continue
24a42c
                 # format option value based on its type (int or bool)
24a42c
                 if type(optparm["enabled"]) == bool:
24a42c
diff --git a/sos/report/plugins/__init__.py b/sos/report/plugins/__init__.py
24a42c
index 02625eb1..779119af 100644
24a42c
--- a/sos/report/plugins/__init__.py
24a42c
+++ b/sos/report/plugins/__init__.py
24a42c
@@ -472,6 +472,9 @@ class Plugin(object):
24a42c
     _default_plug_opts = [
24a42c
         ('timeout', 'Timeout in seconds for plugin. The default value (-1) ' +
24a42c
             'defers to the general plugin timeout, 300 seconds', 'fast', -1),
24a42c
+        ('cmd-timeout', 'Timeout in seconds for a command execution. The ' +
24a42c
+            'default value (-1) defers to the general cmd timeout, 300 ' +
24a42c
+            'seconds', 'fast', -1),
24a42c
         ('postproc', 'Enable post-processing collected plugin data', 'fast',
24a42c
          True)
24a42c
     ]
24a42c
@@ -532,16 +535,15 @@ class Plugin(object):
24a42c
         self.manifest.add_list('commands', [])
24a42c
         self.manifest.add_list('files', [])
24a42c
 
24a42c
-    @property
24a42c
-    def timeout(self):
24a42c
-        """Returns either the default plugin timeout value, the value as
24a42c
-        provided on the commandline via -k plugin.timeout=value, or the value
24a42c
-        of the global --plugin-timeout option.
24a42c
+    def timeout_from_options(self, optname, plugoptname, default_timeout):
24a42c
+        """Returns either the default [plugin|cmd] timeout value, the value as
24a42c
+        provided on the commandline via -k plugin.[|cmd-]timeout=value, or the
24a42c
+        value of the global --[plugin|cmd]-timeout option.
24a42c
         """
24a42c
         _timeout = None
24a42c
         try:
24a42c
-            opt_timeout = self.get_option('plugin_timeout')
24a42c
-            own_timeout = int(self.get_option('timeout'))
24a42c
+            opt_timeout = self.get_option(optname)
24a42c
+            own_timeout = int(self.get_option(plugoptname))
24a42c
             if opt_timeout is None:
24a42c
                 _timeout = own_timeout
24a42c
             elif opt_timeout is not None and own_timeout == -1:
24a42c
@@ -551,10 +553,30 @@ class Plugin(object):
24a42c
             else:
24a42c
                 return None
24a42c
         except ValueError:
24a42c
-            return self.plugin_timeout  # Default to known safe value
24a42c
+            return default_timeout  # Default to known safe value
24a42c
         if _timeout is not None and _timeout > -1:
24a42c
             return _timeout
24a42c
-        return self.plugin_timeout
24a42c
+        return default_timeout
24a42c
+
24a42c
+    @property
24a42c
+    def timeout(self):
24a42c
+        """Returns either the default plugin timeout value, the value as
24a42c
+        provided on the commandline via -k plugin.timeout=value, or the value
24a42c
+        of the global --plugin-timeout option.
24a42c
+        """
24a42c
+        _timeout = self.timeout_from_options('plugin_timeout', 'timeout',
24a42c
+                                             self.plugin_timeout)
24a42c
+        return _timeout
24a42c
+
24a42c
+    @property
24a42c
+    def cmdtimeout(self):
24a42c
+        """Returns either the default command timeout value, the value as
24a42c
+        provided on the commandline via -k plugin.cmd-timeout=value, or the
24a42c
+        value of the global --cmd-timeout option.
24a42c
+        """
24a42c
+        _cmdtimeout = self.timeout_from_options('cmd_timeout', 'cmd-timeout',
24a42c
+                                                self.cmd_timeout)
24a42c
+        return _cmdtimeout
24a42c
 
24a42c
     def set_timeout_hit(self):
24a42c
         self._timeout_hit = True
24a42c
@@ -1235,8 +1257,8 @@ class Plugin(object):
24a42c
         """
24a42c
 
24a42c
         global_options = (
24a42c
-            'all_logs', 'allow_system_changes', 'log_size', 'plugin_timeout',
24a42c
-            'since', 'verify'
24a42c
+            'all_logs', 'allow_system_changes', 'cmd_timeout', 'log_size',
24a42c
+            'plugin_timeout', 'since', 'verify'
24a42c
         )
24a42c
 
24a42c
         if optionname in global_options:
24a42c
@@ -1505,7 +1527,7 @@ class Plugin(object):
24a42c
                     'tags': _spec_tags
24a42c
                 })
24a42c
 
24a42c
-    def add_blockdev_cmd(self, cmds, devices='block', timeout=300,
24a42c
+    def add_blockdev_cmd(self, cmds, devices='block', timeout=None,
24a42c
                          sizelimit=None, chroot=True, runat=None, env=None,
24a42c
                          binary=False, prepend_path=None, whitelist=[],
24a42c
                          blacklist=[], tags=[]):
24a42c
@@ -1569,7 +1591,7 @@ class Plugin(object):
24a42c
                              whitelist=whitelist, blacklist=blacklist,
24a42c
                              tags=_dev_tags)
24a42c
 
24a42c
-    def _add_device_cmd(self, cmds, devices, timeout=300, sizelimit=None,
24a42c
+    def _add_device_cmd(self, cmds, devices, timeout=None, sizelimit=None,
24a42c
                         chroot=True, runat=None, env=None, binary=False,
24a42c
                         prepend_path=None, whitelist=[], blacklist=[],
24a42c
                         tags=[]):
24a42c
@@ -1627,7 +1649,7 @@ class Plugin(object):
24a42c
                                  changes=soscmd.changes)
24a42c
 
24a42c
     def add_cmd_output(self, cmds, suggest_filename=None,
24a42c
-                       root_symlink=None, timeout=cmd_timeout, stderr=True,
24a42c
+                       root_symlink=None, timeout=None, stderr=True,
24a42c
                        chroot=True, runat=None, env=None, binary=False,
24a42c
                        sizelimit=None, pred=None, subdir=None,
24a42c
                        changes=False, foreground=False, tags=[]):
24a42c
@@ -1849,7 +1871,7 @@ class Plugin(object):
24a42c
         self._log_debug("added string ...'%s' as '%s'" % (summary, filename))
24a42c
 
24a42c
     def _collect_cmd_output(self, cmd, suggest_filename=None,
24a42c
-                            root_symlink=False, timeout=cmd_timeout,
24a42c
+                            root_symlink=False, timeout=None,
24a42c
                             stderr=True, chroot=True, runat=None, env=None,
24a42c
                             binary=False, sizelimit=None, subdir=None,
24a42c
                             changes=False, foreground=False, tags=[]):
24a42c
@@ -1883,6 +1905,8 @@ class Plugin(object):
24a42c
         if self._timeout_hit:
24a42c
             return
24a42c
 
24a42c
+        if timeout is None:
24a42c
+            timeout = self.cmdtimeout
24a42c
         _tags = []
24a42c
 
24a42c
         if isinstance(tags, str):
24a42c
@@ -1975,7 +1999,7 @@ class Plugin(object):
24a42c
         return result
24a42c
 
24a42c
     def collect_cmd_output(self, cmd, suggest_filename=None,
24a42c
-                           root_symlink=False, timeout=cmd_timeout,
24a42c
+                           root_symlink=False, timeout=None,
24a42c
                            stderr=True, chroot=True, runat=None, env=None,
24a42c
                            binary=False, sizelimit=None, pred=None,
24a42c
                            subdir=None, tags=[]):
24a42c
@@ -2044,7 +2068,7 @@ class Plugin(object):
24a42c
             tags=tags
24a42c
         )
24a42c
 
24a42c
-    def exec_cmd(self, cmd, timeout=cmd_timeout, stderr=True, chroot=True,
24a42c
+    def exec_cmd(self, cmd, timeout=None, stderr=True, chroot=True,
24a42c
                  runat=None, env=None, binary=False, pred=None,
24a42c
                  foreground=False, container=False, quotecmd=False):
24a42c
         """Execute a command right now and return the output and status, but
24a42c
@@ -2095,6 +2119,9 @@ class Plugin(object):
24a42c
         if not self.test_predicate(cmd=True, pred=pred):
24a42c
             return _default
24a42c
 
24a42c
+        if timeout is None:
24a42c
+            timeout = self.cmdtimeout
24a42c
+
24a42c
         if chroot or self.commons['cmdlineopts'].chroot == 'always':
24a42c
             root = self.sysroot
24a42c
         else:
24a42c
@@ -2331,7 +2358,7 @@ class Plugin(object):
24a42c
 
24a42c
     def add_journal(self, units=None, boot=None, since=None, until=None,
24a42c
                     lines=None, allfields=False, output=None,
24a42c
-                    timeout=cmd_timeout, identifier=None, catalog=None,
24a42c
+                    timeout=None, identifier=None, catalog=None,
24a42c
                     sizelimit=None, pred=None, tags=[]):
24a42c
         """Collect journald logs from one of more units.
24a42c
 
24a42c
-- 
24a42c
2.26.3
24a42c