From 7097f737339f0cde6da923a4ce16a008d229cda7 Mon Sep 17 00:00:00 2001 From: Pavel Moravec 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 --- 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 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 --- 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