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

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