diff options
-rw-r--r-- | sos/plugins/__init__.py | 31 | ||||
-rw-r--r-- | sos/policies/__init__.py | 115 | ||||
-rw-r--r-- | sos/policies/redhat.py | 1 |
3 files changed, 142 insertions, 5 deletions
diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py index 82fef18e..252de4d0 100644 --- a/sos/plugins/__init__.py +++ b/sos/plugins/__init__.py @@ -123,6 +123,7 @@ class Plugin(object): files = () commands = () kernel_mods = () + services = () archive = None profiles = () sysroot = '/' @@ -202,6 +203,22 @@ class Plugin(object): '''Is the package $package_name installed?''' return self.policy.pkg_by_name(package_name) is not None + def is_service(self, name): + '''Does the service $name exist on the system?''' + return self.policy.init_system.is_service(name) + + def service_is_enabled(self, name): + '''Is the service $name enabled?''' + return self.policy.init_system.is_enabled(name) + + def service_is_disabled(self, name): + '''Is the service $name disabled?''' + return self.policy.init_system.is_disabled(name) + + def get_service_status(self, name): + '''Return the reported status for service $name''' + return self.policy.init_system.get_service_status(name) + def do_cmd_private_sub(self, cmd): '''Remove certificate and key output archived by sosreport. cmd is the command name from which output is collected (i.e. exlcuding @@ -977,7 +994,8 @@ class Plugin(object): overridden. """ # some files or packages have been specified for this package - if any([self.files, self.packages, self.commands, self.kernel_mods]): + if any([self.files, self.packages, self.commands, self.kernel_mods, + self.services]): if isinstance(self.files, six.string_types): self.files = [self.files] @@ -990,6 +1008,9 @@ class Plugin(object): if isinstance(self.kernel_mods, six.string_types): self.kernel_mods = [self.kernel_mods] + if isinstance(self.services, six.string_types): + self.services = [self.services] + if isinstance(self, SCLPlugin): # save SCLs that match files or packages type(self)._scls_matched = [] @@ -1005,7 +1026,8 @@ class Plugin(object): return self._files_pkgs_or_cmds_present(self.files, self.packages, - self.commands) + self.commands, + self.services) if isinstance(self, SCLPlugin): # if files and packages weren't specified, we take all SCLs @@ -1013,7 +1035,7 @@ class Plugin(object): return True - def _files_pkgs_or_cmds_present(self, files, packages, commands): + def _files_pkgs_or_cmds_present(self, files, packages, commands, services): kernel_mods = self.policy.lsmod() def have_kmod(kmod): @@ -1022,7 +1044,8 @@ class Plugin(object): return (any(os.path.exists(fname) for fname in files) or any(self.is_installed(pkg) for pkg in packages) or any(is_executable(cmd) for cmd in commands) or - any(have_kmod(kmod) for kmod in self.kernel_mods)) + any(have_kmod(kmod) for kmod in self.kernel_mods) or + any(self.is_service(svc) for svc in services)) def default_enabled(self): """This decides whether a plugin should be automatically loaded or diff --git a/sos/policies/__init__.py b/sos/policies/__init__.py index 65d8aac6..d6255d3e 100644 --- a/sos/policies/__init__.py +++ b/sos/policies/__init__.py @@ -13,7 +13,8 @@ from os import environ from sos.utilities import (ImporterHelper, import_module, - shell_out) + shell_out, + sos_get_command_output) from sos.plugins import IndependentPlugin, ExperimentalPlugin from sos import _sos as _ from sos import SoSOptions, _arg_names @@ -49,6 +50,113 @@ def load(cache={}, sysroot=None): return cache['policy'] +class InitSystem(object): + """Encapsulates an init system to provide service-oriented functions to + sos. + + This should be used to query the status of services, such as if they are + enabled or disabled on boot, or if the service is currently running. + """ + + def __init__(self, init_cmd=None, list_cmd=None, query_cmd=None): + + self.services = {} + + self.init_cmd = init_cmd + self.list_cmd = "%s %s" % (self.init_cmd, list_cmd) or None + self.query_cmd = "%s %s" % (self.init_cmd, query_cmd) or None + + self.load_all_services() + + def is_enabled(self, name): + """Check if given service name is enabled """ + if self.services and name in self.services: + return self.services[name]['config'] == 'enabled' + return False + + def is_disabled(self, name): + """Check if a given service name is disabled """ + if self.services and name in self.services: + return self.services[name]['config'] == 'disabled' + return False + + def is_service(self, name): + """Checks if the given service name exists on the system at all, this + does not check for the service status + """ + return name in self.services + + def load_all_services(self): + """This loads all services known to the init system into a dict. + The dict should be keyed by the service name, and contain a dict of the + name and service status + """ + pass + + def _query_service(self, name): + """Query an individual service""" + if self.query_cmd: + res = sos_get_command_output("%s %s" % (self.query_cmd, name)) + if res['status'] == 0: + return res + else: + return None + return None + + def parse_query(self, output): + """Parses the output returned by the query command to make a + determination of what the state of the service is + + This should be overriden by anything that subclasses InitSystem + """ + return output + + def get_service_status(self, name): + """Returns the status for the given service name along with the output + of the query command + """ + svc = self._query_service(name) + if svc is not None: + return {'name': name, + 'status': self.parse_query(svc['output']), + 'output': svc['output'] + } + else: + return {'name': name, + 'status': 'missing', + 'output': '' + } + + +class SystemdInit(InitSystem): + + def __init__(self): + super(SystemdInit, self).__init__( + init_cmd='systemctl', + list_cmd='list-unit-files --type=service', + query_cmd='status' + ) + + def parse_query(self, output): + for line in output.splitlines(): + if line.strip().startswith('Active:'): + return line.split()[1] + return 'unknown' + + def load_all_services(self): + svcs = shell_out(self.list_cmd).splitlines() + for line in svcs: + try: + name = line.split('.service')[0] + config = line.split()[1] + self.services[name] = { + 'name': name, + 'config': config + } + except IndexError: + pass + + class PackageManager(object): """Encapsulates a package manager. If you provide a query_command to the constructor it should print each package on the system in the following @@ -676,11 +784,16 @@ class LinuxPolicy(Policy): distro = "Linux" vendor = "None" PATH = "/bin:/sbin:/usr/bin:/usr/sbin" + init = None _preferred_hash_name = None def __init__(self, sysroot=None): super(LinuxPolicy, self).__init__(sysroot=sysroot) + if self.init == 'systemd': + self.init_system = SystemdInit() + else: + self.init_system = InitSystem() def get_preferred_hash_name(self): diff --git a/sos/policies/redhat.py b/sos/policies/redhat.py index 5bfbade2..b494de3c 100644 --- a/sos/policies/redhat.py +++ b/sos/policies/redhat.py @@ -45,6 +45,7 @@ class RedHatPolicy(LinuxPolicy): _host_sysroot = '/' default_scl_prefix = '/opt/rh' name_pattern = 'friendly' + init = 'systemd' def __init__(self, sysroot=None): super(RedHatPolicy, self).__init__(sysroot=sysroot) |