Blob Blame History Raw
From 7097f737339f0cde6da923a4ce16a008d229cda7 Mon Sep 17 00:00:00 2001
From: Pavel Moravec <pmoravec@redhat.com>
Date: Mon, 16 Sep 2019 17:13:27 +0200
Subject: [PATCH 1/2] [plugins] extend SoSPredicate by command output inclusion
 test

Add a predicate type in form

cmd_outputs={'cmd': 'foo --help', 'output': 'bar'}

that checks whether output of given command contains given string.

Multiple commands/outputs can be provided in a list.

Related to: #1682

Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
---
 sos/plugins/__init__.py | 57 +++++++++++++++++++++++++++++++++++------
 1 file changed, 49 insertions(+), 8 deletions(-)

diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py
index a0b291bea..516a61109 100644
--- a/sos/plugins/__init__.py
+++ b/sos/plugins/__init__.py
@@ -115,6 +115,9 @@ class SoSPredicate(object):
     #: Services enablement list
     services = []
 
+    # Command output inclusion pairs {'cmd': 'foo --help', 'output': 'bar'}
+    cmd_outputs = []
+
     def __str(self, quote=False, prefix="", suffix=""):
         """Return a string representation of this SoSPredicate with
             optional prefix, suffix and value quoting.
@@ -128,14 +131,23 @@ class SoSPredicate(object):
 
         services = self.services
         services = [quotes % s for s in services] if quote else services
-        pstr += "services=[%s]" % (",".join(services))
+        pstr += "services=[%s], " % (",".join(services))
+
+        cmdoutputs = [
+            "{ %s: %s, %s: %s }" % (quotes % "cmd",
+                                    quotes % cmdoutput['cmd'],
+                                    quotes % "output",
+                                    quotes % cmdoutput['output'])
+            for cmdoutput in self.cmd_outputs
+        ]
+        pstr += "cmdoutputs=[%s]" % (",".join(cmdoutputs))
 
         return prefix + pstr + suffix
 
     def __str__(self):
         """Return a string representation of this SoSPredicate.
 
-            "dry_run=False, kmods=[], services=[]"
+            "dry_run=False, kmods=[], services=[], cmdoutputs=[]"
         """
         return self.__str()
 
@@ -143,7 +155,7 @@ class SoSPredicate(object):
         """Return a machine readable string representation of this
             SoSPredicate.
 
-            "SoSPredicate(dry_run=False, kmods=[], services=[])"
+            "SoSPredicate(dry_run=False, kmods=[], services=[], cmdoutputs=[])"
         """
         return self.__str(quote=True, prefix="SoSPredicate(", suffix=")")
 
@@ -170,15 +182,39 @@ class SoSPredicate(object):
         else:
             return all(_svcs)
 
+    def _eval_cmd_output(self, cmd_output):
+        '''Does 'cmd' output contain string 'output'?'''
+        if 'cmd' not in cmd_output or 'output' not in cmd_output:
+            return False
+        result = sos_get_command_output(cmd_output['cmd'])
+        if result['status'] != 0:
+            return False
+        for line in result['output'].splitlines():
+            if cmd_output['output'] in line:
+                return True
+        return False
+
+    def _eval_cmd_outputs(self):
+        if not self.cmd_outputs:
+            return True
+
+        _cmds = [self._eval_cmd_output(c) for c in self.cmd_outputs]
+
+        if self.required['commands'] == 'any':
+            return any(_cmds)
+        else:
+            return all(_cmds)
+
     def __nonzero__(self):
         """Predicate evaluation hook.
         """
 
         # Null predicate?
-        if not any([self.kmods, self.services, self.dry_run]):
+        if not any([self.kmods, self.services, self.cmd_outputs, self.dry_run]):
             return True
 
-        return ((self._eval_kmods() and self._eval_services()) and not
+        return ((self._eval_kmods() and self._eval_services() and
+                 self._eval_cmd_outputs()) and not
                 self.dry_run)
 
     def __bool__(self):
@@ -187,14 +223,17 @@ class SoSPredicate(object):
         return self.__nonzero__()
 
     def __init__(self, owner, dry_run=False, kmods=[], services=[],
-                 required={}):
+                 cmd_outputs=[], required={}):
         """Initialise a new SoSPredicate object.
         """
         self._owner = owner
         self.kmods = list(kmods)
         self.services = list(services)
+        if not isinstance(cmd_outputs, list):
+            cmd_outputs = [cmd_outputs]
+        self.cmd_outputs = cmd_outputs
         self.dry_run = dry_run | self._owner.commons['cmdlineopts'].dry_run
-        self.required = {'kmods': 'any', 'services': 'any'}
+        self.required = {'kmods': 'any', 'services': 'any', 'commands': 'any'}
         self.required.update({
             k: v for k, v in required.items() if
             required[k] != self.required[k]

From 47e434c50e63f80e4b620e74d81c636c8c8a8d97 Mon Sep 17 00:00:00 2001
From: Pavel Moravec <pmoravec@redhat.com>
Date: Mon, 16 Sep 2019 17:15:40 +0200
Subject: [PATCH 2/2] [grub2] call grub2-config with --no-grubenv-update when
 appropriate

On some newer grub2 versions, grub2-config removes extra args in
$kernel_opts until --no-grubenv-update option is used.

Test if the option is present in "grub2-config --help" and if so, use it.

Resolves: #1682

Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
---
diff --git a/sos/plugins/grub2.py b/sos/plugins/grub2.py
index 9786de44d..0ca6fe096 100644
--- a/sos/plugins/grub2.py
+++ b/sos/plugins/grub2.py
@@ -6,7 +6,8 @@
 #
 # See the LICENSE file in the source distribution for further information.
 
-from sos.plugins import Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin
+from sos.plugins import (Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin,
+                         SoSPredicate)
 
 
 class Grub2(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin):
@@ -32,9 +33,16 @@ def setup(self):
         self.add_cmd_output("ls -lanR /boot")
         # call grub2-mkconfig with GRUB_DISABLE_OS_PROBER=true to prevent
         # possible unwanted loading of some kernel modules
+        # 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'
-        self.add_cmd_output("grub2-mkconfig", env=env)
+        grub_cmd = 'grub2-mkconfig'
+        co = {'cmd': 'grub2-mkconfig --help', '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)
 
     def postproc(self):
         # the trailing space is required; python treats '_' as whitespace