Blame SOURCES/sos-bz1715470-rhv-postgres-from-scl.patch

28306f
From 6db459e2b21a798d93cc79e705e8e02f1bbd24c1 Mon Sep 17 00:00:00 2001
28306f
From: Jake Hunsaker <jhunsake@redhat.com>
28306f
Date: Tue, 24 Jul 2018 17:40:25 -0400
28306f
Subject: [PATCH] [Policies|Plugins] Add services member
28306f
28306f
Adds a services member to facilitate plugin enablement. This is tied to
28306f
a new InitSystem class that gets attached to policies. The InitSystem
28306f
class is used to determine services that are present on the system and
28306f
what those service statuses currently are (e.g. enabled/disable).
28306f
28306f
Plugins can now specify a set of services to enable the plugin on if
28306f
that service exists on the system, similar to the file, command, and
28306f
package checks.
28306f
28306f
Additionally, the Plugin class now has methods to check on service
28306f
states, and make decisions based off of. For example:
28306f
28306f
	def setup(self):
28306f
	    if self.is_service('foobar'):
28306f
	        self.add_cmd_output('barfoo')
28306f
28306f
Currently, only systemd has actual functionality for this. The base
28306f
InitSystem inherited by policies by default will always return False for
28306f
service checks, thus resulting in the same behavior as before this
28306f
change.
28306f
28306f
The Red Hat family of distributions has been set to systemd, as all
28306f
current versions of those distributions use systemd.
28306f
28306f
Closes: #83
28306f
Resolves: #1387
28306f
28306f
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
28306f
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
28306f
---
28306f
 sos/plugins/__init__.py  |  31 +++++++++--
28306f
 sos/policies/__init__.py | 115 ++++++++++++++++++++++++++++++++++++++-
28306f
 sos/policies/redhat.py   |   1 +
28306f
 3 files changed, 142 insertions(+), 5 deletions(-)
28306f
28306f
diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py
28306f
index 82fef18e5..252de4d05 100644
28306f
--- a/sos/plugins/__init__.py
28306f
+++ b/sos/plugins/__init__.py
28306f
@@ -123,6 +123,7 @@ class Plugin(object):
28306f
     files = ()
28306f
     commands = ()
28306f
     kernel_mods = ()
28306f
+    services = ()
28306f
     archive = None
28306f
     profiles = ()
28306f
     sysroot = '/'
28306f
@@ -202,6 +203,22 @@ def is_installed(self, package_name):
28306f
         '''Is the package $package_name installed?'''
28306f
         return self.policy.pkg_by_name(package_name) is not None
28306f
 
28306f
+    def is_service(self, name):
28306f
+        '''Does the service $name exist on the system?'''
28306f
+        return self.policy.init_system.is_service(name)
28306f
+
28306f
+    def service_is_enabled(self, name):
28306f
+        '''Is the service $name enabled?'''
28306f
+        return self.policy.init_system.is_enabled(name)
28306f
+
28306f
+    def service_is_disabled(self, name):
28306f
+        '''Is the service $name disabled?'''
28306f
+        return self.policy.init_system.is_disabled(name)
28306f
+
28306f
+    def get_service_status(self, name):
28306f
+        '''Return the reported status for service $name'''
28306f
+        return self.policy.init_system.get_service_status(name)
28306f
+
28306f
     def do_cmd_private_sub(self, cmd):
28306f
         '''Remove certificate and key output archived by sosreport. cmd
28306f
         is the command name from which output is collected (i.e. exlcuding
28306f
@@ -977,7 +994,8 @@ def check_enabled(self):
28306f
         overridden.
28306f
         """
28306f
         # some files or packages have been specified for this package
28306f
-        if any([self.files, self.packages, self.commands, self.kernel_mods]):
28306f
+        if any([self.files, self.packages, self.commands, self.kernel_mods,
28306f
+                self.services]):
28306f
             if isinstance(self.files, six.string_types):
28306f
                 self.files = [self.files]
28306f
 
28306f
@@ -990,6 +1008,9 @@ def check_enabled(self):
28306f
             if isinstance(self.kernel_mods, six.string_types):
28306f
                 self.kernel_mods = [self.kernel_mods]
28306f
 
28306f
+            if isinstance(self.services, six.string_types):
28306f
+                self.services = [self.services]
28306f
+
28306f
             if isinstance(self, SCLPlugin):
28306f
                 # save SCLs that match files or packages
28306f
                 type(self)._scls_matched = []
28306f
@@ -1005,7 +1026,8 @@ def check_enabled(self):
28306f
 
28306f
             return self._files_pkgs_or_cmds_present(self.files,
28306f
                                                     self.packages,
28306f
-                                                    self.commands)
28306f
+                                                    self.commands,
28306f
+                                                    self.services)
28306f
 
28306f
         if isinstance(self, SCLPlugin):
28306f
             # if files and packages weren't specified, we take all SCLs
28306f
@@ -1013,7 +1035,7 @@ def check_enabled(self):
28306f
 
28306f
         return True
28306f
 
28306f
-    def _files_pkgs_or_cmds_present(self, files, packages, commands):
28306f
+    def _files_pkgs_or_cmds_present(self, files, packages, commands, services):
28306f
             kernel_mods = self.policy.lsmod()
28306f
 
28306f
             def have_kmod(kmod):
28306f
@@ -1022,7 +1044,8 @@ def have_kmod(kmod):
28306f
             return (any(os.path.exists(fname) for fname in files) or
28306f
                     any(self.is_installed(pkg) for pkg in packages) or
28306f
                     any(is_executable(cmd) for cmd in commands) or
28306f
-                    any(have_kmod(kmod) for kmod in self.kernel_mods))
28306f
+                    any(have_kmod(kmod) for kmod in self.kernel_mods) or
28306f
+                    any(self.is_service(svc) for svc in services))
28306f
 
28306f
     def default_enabled(self):
28306f
         """This decides whether a plugin should be automatically loaded or
28306f
diff --git a/sos/policies/__init__.py b/sos/policies/__init__.py
28306f
index 65d8aac63..d6255d3ee 100644
28306f
--- a/sos/policies/__init__.py
28306f
+++ b/sos/policies/__init__.py
28306f
@@ -13,7 +13,8 @@
28306f
 
28306f
 from sos.utilities import (ImporterHelper,
28306f
                            import_module,
28306f
-                           shell_out)
28306f
+                           shell_out,
28306f
+                           sos_get_command_output)
28306f
 from sos.plugins import IndependentPlugin, ExperimentalPlugin
28306f
 from sos import _sos as _
28306f
 from sos import SoSOptions, _arg_names
28306f
@@ -49,6 +50,113 @@ def load(cache={}, sysroot=None):
28306f
     return cache['policy']
28306f
 
28306f
 
28306f
+class InitSystem(object):
28306f
+    """Encapsulates an init system to provide service-oriented functions to
28306f
+    sos.
28306f
+
28306f
+    This should be used to query the status of services, such as if they are
28306f
+    enabled or disabled on boot, or if the service is currently running.
28306f
+    """
28306f
+
28306f
+    def __init__(self, init_cmd=None, list_cmd=None, query_cmd=None):
28306f
+
28306f
+        self.services = {}
28306f
+
28306f
+        self.init_cmd = init_cmd
28306f
+        self.list_cmd = "%s %s" % (self.init_cmd, list_cmd) or None
28306f
+        self.query_cmd = "%s %s" % (self.init_cmd, query_cmd) or None
28306f
+
28306f
+        self.load_all_services()
28306f
+
28306f
+    def is_enabled(self, name):
28306f
+        """Check if given service name is enabled """
28306f
+        if self.services and name in self.services:
28306f
+            return self.services[name]['config'] == 'enabled'
28306f
+        return False
28306f
+
28306f
+    def is_disabled(self, name):
28306f
+        """Check if a given service name is disabled """
28306f
+        if self.services and name in self.services:
28306f
+            return self.services[name]['config'] == 'disabled'
28306f
+        return False
28306f
+
28306f
+    def is_service(self, name):
28306f
+        """Checks if the given service name exists on the system at all, this
28306f
+        does not check for the service status
28306f
+        """
28306f
+        return name in self.services
28306f
+
28306f
+    def load_all_services(self):
28306f
+        """This loads all services known to the init system into a dict.
28306f
+        The dict should be keyed by the service name, and contain a dict of the
28306f
+        name and service status
28306f
+        """
28306f
+        pass
28306f
+
28306f
+    def _query_service(self, name):
28306f
+        """Query an individual service"""
28306f
+        if self.query_cmd:
28306f
+            res = sos_get_command_output("%s %s" % (self.query_cmd, name))
28306f
+            if res['status'] == 0:
28306f
+                return res
28306f
+            else:
28306f
+                return None
28306f
+        return None
28306f
+
28306f
+    def parse_query(self, output):
28306f
+        """Parses the output returned by the query command to make a
28306f
+        determination of what the state of the service is
28306f
+
28306f
+        This should be overriden by anything that subclasses InitSystem
28306f
+        """
28306f
+        return output
28306f
+
28306f
+    def get_service_status(self, name):
28306f
+        """Returns the status for the given service name along with the output
28306f
+        of the query command
28306f
+        """
28306f
+        svc = self._query_service(name)
28306f
+        if svc is not None:
28306f
+            return {'name': name,
28306f
+                    'status': self.parse_query(svc['output']),
28306f
+                    'output': svc['output']
28306f
+                    }
28306f
+        else:
28306f
+            return {'name': name,
28306f
+                    'status': 'missing',
28306f
+                    'output': ''
28306f
+                    }
28306f
+
28306f
+
28306f
+class SystemdInit(InitSystem):
28306f
+
28306f
+    def __init__(self):
28306f
+        super(SystemdInit, self).__init__(
28306f
+            init_cmd='systemctl',
28306f
+            list_cmd='list-unit-files --type=service',
28306f
+            query_cmd='status'
28306f
+        )
28306f
+
28306f
+    def parse_query(self, output):
28306f
+        for line in output.splitlines():
28306f
+            if line.strip().startswith('Active:'):
28306f
+                return line.split()[1]
28306f
+        return 'unknown'
28306f
+
28306f
+    def load_all_services(self):
28306f
+        svcs = shell_out(self.list_cmd).splitlines()
28306f
+        for line in svcs:
28306f
+            try:
28306f
+                name = line.split('.service')[0]
28306f
+                config = line.split()[1]
28306f
+                self.services[name] = {
28306f
+                    'name': name,
28306f
+                    'config': config
28306f
+                }
28306f
+            except IndexError:
28306f
+                pass
28306f
+
28306f
+
28306f
 class PackageManager(object):
28306f
     """Encapsulates a package manager. If you provide a query_command to the
28306f
     constructor it should print each package on the system in the following
28306f
@@ -676,11 +784,16 @@ class LinuxPolicy(Policy):
28306f
     distro = "Linux"
28306f
     vendor = "None"
28306f
     PATH = "/bin:/sbin:/usr/bin:/usr/sbin"
28306f
+    init = None
28306f
 
28306f
     _preferred_hash_name = None
28306f
 
28306f
     def __init__(self, sysroot=None):
28306f
         super(LinuxPolicy, self).__init__(sysroot=sysroot)
28306f
+        if self.init == 'systemd':
28306f
+            self.init_system = SystemdInit()
28306f
+        else:
28306f
+            self.init_system = InitSystem()
28306f
 
28306f
     def get_preferred_hash_name(self):
28306f
 
28306f
diff --git a/sos/policies/redhat.py b/sos/policies/redhat.py
28306f
index 5bfbade28..b494de3c4 100644
28306f
--- a/sos/policies/redhat.py
28306f
+++ b/sos/policies/redhat.py
28306f
@@ -45,6 +45,7 @@ class RedHatPolicy(LinuxPolicy):
28306f
     _host_sysroot = '/'
28306f
     default_scl_prefix = '/opt/rh'
28306f
     name_pattern = 'friendly'
28306f
+    init = 'systemd'
28306f
 
28306f
     def __init__(self, sysroot=None):
28306f
         super(RedHatPolicy, self).__init__(sysroot=sysroot)
28306f
From 7b9284e948f2e9076c92741ed5b95fec7934af8d Mon Sep 17 00:00:00 2001
28306f
From: Jake Hunsaker <jhunsake@redhat.com>
28306f
Date: Fri, 15 Feb 2019 16:03:53 -0500
28306f
Subject: [PATCH] [policy|plugin] Add 'is_running' check for services
28306f
28306f
Adds a method to the InitSystem class used by policies and plugins to
28306f
check if a given service name is running. Plugins can make use of this
28306f
through the new self.service_is_running() method.
28306f
28306f
For policies that use the base InitSystem class, this method will always
28306f
return True as the service_is_running() method is likely to be used when
28306f
determining if we should run commands or not, and we do not want to
28306f
incorrectly stop running those commands where they would collect
28306f
meaningful output today.
28306f
28306f
The SystemD init system for policies properly checks to see if the given
28306f
service is active or not when reporting is the service is running.
28306f
28306f
Resolves: #1567
28306f
28306f
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
28306f
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
28306f
---
28306f
 sos/plugins/__init__.py  |  6 +++++-
28306f
 sos/policies/__init__.py | 22 ++++++++++++++++++----
28306f
 2 files changed, 23 insertions(+), 5 deletions(-)
28306f
28306f
diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py
28306f
index 47b028a85..030e7a305 100644
28306f
--- a/sos/plugins/__init__.py
28306f
+++ b/sos/plugins/__init__.py
28306f
@@ -215,9 +215,13 @@ def service_is_disabled(self, name):
28306f
         '''Is the service $name disabled?'''
28306f
         return self.policy.init_system.is_disabled(name)
28306f
 
28306f
+    def service_is_running(self, name):
28306f
+        '''Is the service $name currently running?'''
28306f
+        return self.policy.init_system.is_running(name)
28306f
+
28306f
     def get_service_status(self, name):
28306f
         '''Return the reported status for service $name'''
28306f
-        return self.policy.init_system.get_service_status(name)
28306f
+        return self.policy.init_system.get_service_status(name)['status']
28306f
 
28306f
     def do_cmd_private_sub(self, cmd):
28306f
         '''Remove certificate and key output archived by sosreport. cmd
28306f
diff --git a/sos/policies/__init__.py b/sos/policies/__init__.py
28306f
index d6255d3ee..d0b180152 100644
28306f
--- a/sos/policies/__init__.py
28306f
+++ b/sos/policies/__init__.py
28306f
@@ -86,6 +86,17 @@ def is_service(self, name):
28306f
         """
28306f
         return name in self.services
28306f
 
28306f
+    def is_running(self, name):
28306f
+        """Checks if the given service name is in a running state.
28306f
+
28306f
+        This should be overridden by initsystems that subclass InitSystem
28306f
+        """
28306f
+        # This is going to be primarily used in gating if service related
28306f
+        # commands are going to be run or not. Default to always returning
28306f
+        # True when an actual init system is not specified by policy so that
28306f
+        # we don't inadvertantly restrict sosreports on those systems
28306f
+        return True
28306f
+
28306f
     def load_all_services(self):
28306f
         """This loads all services known to the init system into a dict.
28306f
         The dict should be keyed by the service name, and contain a dict of the
28306f
@@ -96,10 +107,9 @@ def load_all_services(self):
28306f
     def _query_service(self, name):
28306f
         """Query an individual service"""
28306f
         if self.query_cmd:
28306f
-            res = sos_get_command_output("%s %s" % (self.query_cmd, name))
28306f
-            if res['status'] == 0:
28306f
-                return res
28306f
-            else:
28306f
+            try:
28306f
+                return sos_get_command_output("%s %s" % (self.query_cmd, name))
28306f
+            except Exception:
28306f
                 return None
28306f
         return None
28306f
 
28306f
@@ -156,6 +166,10 @@ def load_all_services(self):
28306f
             except IndexError:
28306f
                 pass
28306f
 
28306f
+    def is_running(self, name):
28306f
+        svc = self.get_service_status(name)
28306f
+        return svc['status'] == 'active'
28306f
+
28306f
 
28306f
 class PackageManager(object):
28306f
     """Encapsulates a package manager. If you provide a query_command to the
28306f
From 3e736ad53d254aba8795b3d5d8ce0ec4f827ab1c Mon Sep 17 00:00:00 2001
28306f
From: Jake Hunsaker <jhunsake@redhat.com>
28306f
Date: Fri, 8 Feb 2019 13:19:56 -0500
28306f
Subject: [PATCH] [postgresql] Use postgres 10 scl if installed
28306f
28306f
Updates the plugin to check if the specified SCL is running, as some
28306f
systems may have multiple SCL versions installed, but only one will be
28306f
running at a time. We now use the running version for a pgdump.
28306f
28306f
This is primarily aimed at RHV environments as 4.3 and later use version
28306f
10.
28306f
28306f
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
28306f
---
28306f
 sos/plugins/postgresql.py | 17 ++++++++++++++---
28306f
 1 file changed, 14 insertions(+), 3 deletions(-)
28306f
28306f
diff --git a/sos/plugins/postgresql.py b/sos/plugins/postgresql.py
28306f
index aef431f8a..e641c3b44 100644
28306f
--- a/sos/plugins/postgresql.py
28306f
+++ b/sos/plugins/postgresql.py
28306f
@@ -80,14 +80,25 @@ def setup(self):
28306f
 
28306f
 class RedHatPostgreSQL(PostgreSQL, SCLPlugin):
28306f
 
28306f
-    packages = ('postgresql', 'rh-postgresql95-postgresql-server', )
28306f
+    packages = (
28306f
+        'postgresql',
28306f
+        'rh-postgresql95-postgresql-server',
28306f
+        'rh-postgresql10-postgresql-server'
28306f
+    )
28306f
 
28306f
     def setup(self):
28306f
         super(RedHatPostgreSQL, self).setup()
28306f
 
28306f
-        scl = "rh-postgresql95"
28306f
         pghome = self.get_option("pghome")
28306f
 
28306f
+        scl = None
28306f
+        for pkg in self.packages[1:]:
28306f
+            # The scl name, package name, and service name all differ slightly
28306f
+            # but is at least consistent in doing so across versions, so we
28306f
+            # need to do some mangling here
28306f
+            if self.service_is_running(pkg.replace('-server', '')):
28306f
+                scl = pkg.split('-postgresql-')[0]
28306f
+
28306f
         # Copy PostgreSQL log files.
28306f
         for filename in find("*.log", pghome):
28306f
             self.add_copy_spec(filename)
28306f
@@ -111,7 +122,7 @@ def setup(self):
28306f
             )
28306f
         )
28306f
 
28306f
-        if scl in self.scls_matched:
28306f
+        if scl and scl in self.scls_matched:
28306f
             self.do_pg_dump(scl=scl, filename="pgdump-scl-%s.tar" % scl)
28306f
 
28306f
 
28306f
From 0ba743bbf9df335dd47ec45a450e63d72d7ce494 Mon Sep 17 00:00:00 2001
28306f
From: Pavel Moravec <pmoravec@redhat.com>
28306f
Date: Wed, 5 Sep 2018 12:34:48 +0200
28306f
Subject: [PATCH] [plugins] fix 6db459e for SCL services
28306f
28306f
Calling _files_pkgs_or_cmds_present for SCLs lacks "services"
28306f
argument that was added in 6db459e commit.
28306f
28306f
Also it is worth renaming the method to more generic
28306f
_check_plugin_triggers .
28306f
28306f
Resolves: #1416
28306f
28306f
Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
28306f
---
28306f
 sos/plugins/__init__.py | 18 ++++++++++--------
28306f
 1 file changed, 10 insertions(+), 8 deletions(-)
28306f
28306f
diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py
28306f
index 97f3cc592..3abe29db6 100644
28306f
--- a/sos/plugins/__init__.py
28306f
+++ b/sos/plugins/__init__.py
28306f
@@ -1033,16 +1033,18 @@ def check_enabled(self):
28306f
                     files = [f % {"scl_name": scl} for f in self.files]
28306f
                     packages = [p % {"scl_name": scl} for p in self.packages]
28306f
                     commands = [c % {"scl_name": scl} for c in self.commands]
28306f
-                    if self._files_pkgs_or_cmds_present(files,
28306f
-                                                        packages,
28306f
-                                                        commands):
28306f
+                    services = [s % {"scl_name": scl} for s in self.services]
28306f
+                    if self._check_plugin_triggers(files,
28306f
+                                                   packages,
28306f
+                                                   commands,
28306f
+                                                   services):
28306f
                         type(self)._scls_matched.append(scl)
28306f
                 return len(type(self)._scls_matched) > 0
28306f
 
28306f
-            return self._files_pkgs_or_cmds_present(self.files,
28306f
-                                                    self.packages,
28306f
-                                                    self.commands,
28306f
-                                                    self.services)
28306f
+            return self._check_plugin_triggers(self.files,
28306f
+                                               self.packages,
28306f
+                                               self.commands,
28306f
+                                               self.services)
28306f
 
28306f
         if isinstance(self, SCLPlugin):
28306f
             # if files and packages weren't specified, we take all SCLs
28306f
@@ -1050,7 +1052,7 @@ def check_enabled(self):
28306f
 
28306f
         return True
28306f
 
28306f
-    def _files_pkgs_or_cmds_present(self, files, packages, commands, services):
28306f
+    def _check_plugin_triggers(self, files, packages, commands, services):
28306f
             kernel_mods = self.policy.lsmod()
28306f
 
28306f
             def have_kmod(kmod):