# -*- coding: utf-8 -*- """ Get information about running/failed units and boot process """ import re import subprocess from logparse import config from logparse.formatting import * from logparse.load_parsers import Parser from logparse.util import resolve # The following list changes with each systemd version. # Run `systemctl --state=help` to view currently implemented states. # The numbers correspond to degrees of severity for later formatting. BAD_STATES = {"bad": 4, "failed": 4, "not-found": 4, "bad-setting": 2, "error": 3, "masked": 2, "dead": 3, "abandoned": 3} SYS_STATUS = {'running': 0, 'initializing': 1, 'starting': 1, 'stopping': 1, 'degraded': 3, 'unknown': 4, 'offline': 5} class Unit(): """ Class with some basic variables to represent a systemd unit. """ def __init__(self, name, loaded, active, sub, description): self.name = name self.loaded = loaded self.active = active self.sub = sub self.description = description def status(): """ Finds the status of a unit on-demand with `systemctl is-active`. Currently not used anywhere. """ try: p = subprocess.Popen(["systemctl", "is-active", self.name], stdout=subprocess.PIPE) (output, err) = p.communicate() status = output.decode('utf-8') return status except Exception as e: logger.warning("Failed to get status for unit {0}: {1}".format( self.name, str(e))) class Systemd(Parser): def __init__(self): super().__init__() self.name = "systemd" self.info = "Information about running/failed units and boot process" def parse_log(self): logger.debug("Starting systemd section") section = Section("systemd") # Get overall system status try: p = subprocess.Popen(["systemctl", "is-system-running"], stdout = subprocess.PIPE) (output, err) = p.communicate() except Exception as e: logger.warning("Failed to get system status: " + str(e)) else: status_raw = str(output.decode('utf-8')).split()[0] section.append_data(Data("System status", [status_raw], severity=SYS_STATUS[status_raw])) # Get summary of systemd units try: p = subprocess.Popen( ["systemctl", "list-units"], stdout = subprocess.PIPE) (output, err) = p.communicate() except Exception as e: logger.warning("Failed to get list of unit files: " + str(e)) units_raw = None else: units_raw = output.decode('utf-8') unit_properties = [Unit(*line.split(maxsplit=4)) for line in units_raw.replace("●", " ") \ .splitlines()[1:-7]] unit_states = {} for u in unit_properties: if not u.sub in unit_states: unit_states[u.sub] = [] unit_states[u.sub].append(u.name) ok_data = Data() for state, unit_list in unit_states.items(): if state in BAD_STATES: logger.debug("Found critical unit {0} with status {1}" .format(u.name, u.sub)) section.append_data(Data( plural(state + " unit", len(unit_list)), unit_list, severity=BAD_STATES[state]) .truncl(config.prefs.getint("logparse", "maxlist"))) else: ok_data.items.append(" ".join([str(len(unit_list)), state])) if len(ok_data.items) > 0 \ and config.prefs.getboolean("systemd", "show-all"): ok_data.subtitle = plural("unit", len(ok_data.items)) \ + " in a non-critical state" ok_data.truncl(config.prefs.getint("logparse", "maxlist")) section.append_data(ok_data) logger.info("Finished systemd section") return section def check_dependencies(self): # Check if systemctl is set up cmd = "systemctl --version" if self._check_dependency_command(cmd)[0] != 0: return (False, ["systemctl"]) else: return (True, None)