logparse / parsers / systemd.pyon commit rename parsers, better journald integration (e1f7605)
   1# -*- coding: utf-8 -*-
   2
   3"""
   4Get information about running/failed units and boot process
   5"""
   6
   7import re
   8import subprocess
   9
  10from logparse import config
  11from logparse.formatting import *
  12from logparse.load_parsers import Parser
  13from logparse.util import resolve
  14
  15# The following list changes with each systemd version.
  16# Run `systemctl --state=help` to view currently implemented states.
  17# The numbers correspond to degrees of severity for later formatting.
  18BAD_STATES = {"bad": 4, "failed": 4, "not-found": 4, "bad-setting": 2,
  19        "error": 3, "masked": 2, "dead": 3, "abandoned": 3}
  20SYS_STATUS = {'running': 0, 'initializing': 1, 'starting': 1, 'stopping': 1,
  21        'degraded': 3, 'unknown': 4, 'offline': 5}
  22
  23
  24class Unit():
  25    """
  26    Class with some basic variables to represent a systemd unit.
  27    """
  28
  29    def __init__(self, name, loaded, active, sub, description):
  30        self.name = name
  31        self.loaded = loaded
  32        self.active = active
  33        self.sub = sub
  34        self.description = description
  35
  36    def status():
  37        """
  38        Finds the status of a unit on-demand with `systemctl is-active`.
  39        Currently not used anywhere.
  40        """
  41        try:
  42            p =  subprocess.Popen(["systemctl", "is-active",  self.name],
  43                    stdout=subprocess.PIPE)
  44            (output, err) = p.communicate()
  45            status = output.decode('utf-8')
  46            return status
  47        except Exception as e:
  48            logger.warning("Failed to get status for unit {0}: {1}".format(
  49                self.name, str(e)))
  50
  51
  52class Systemd(Parser):
  53
  54    def __init__(self):
  55        super().__init__()
  56        self.name = "systemd"
  57        self.info = "Information about running/failed units and boot process"
  58
  59    def parse_log(self):
  60
  61        logger.debug("Starting systemd section")
  62        section = Section("systemd")
  63
  64        # Get overall system status
  65        try:
  66            p = subprocess.Popen(["systemctl", "is-system-running"],
  67                    stdout = subprocess.PIPE)
  68            (output, err) = p.communicate()
  69        except Exception as e:
  70            logger.warning("Failed to get system status: " + str(e))
  71        else:
  72            status_raw = str(output.decode('utf-8')).split()[0]
  73            section.append_data(Data("System status", [status_raw],
  74                    severity=SYS_STATUS[status_raw]))
  75
  76        # Get summary of systemd units
  77        try:
  78            p = subprocess.Popen(
  79                    ["systemctl", "list-units"], stdout = subprocess.PIPE)
  80            (output, err) = p.communicate()
  81        except Exception as e:
  82            logger.warning("Failed to get list of unit files: " + str(e))
  83            units_raw = None
  84        else:
  85            units_raw = output.decode('utf-8')
  86            unit_properties = [Unit(*line.split(maxsplit=4))
  87                    for line in units_raw.replace("●", " ") \
  88                        .splitlines()[1:-7]]
  89            unit_states = {}
  90
  91            for u in unit_properties:
  92                if not u.sub in unit_states:
  93                    unit_states[u.sub] = []
  94                unit_states[u.sub].append(u.name)
  95
  96            ok_data = Data()
  97
  98            for state, unit_list in unit_states.items():
  99                if state in BAD_STATES:
 100                    logger.debug("Found critical unit {0} with status {1}"
 101                            .format(u.name, u.sub))
 102                    section.append_data(Data(
 103                        plural(state + " unit", len(unit_list)), unit_list,
 104                        severity=BAD_STATES[state])
 105                        .truncl(config.prefs.getint("logparse", "maxlist")))
 106                else:
 107                    ok_data.items.append(" ".join([str(len(unit_list)), state]))
 108
 109            if len(ok_data.items) > 0 \
 110                    and config.prefs.getboolean("systemd", "show-all"):
 111                ok_data.subtitle = plural("unit", len(ok_data.items)) \
 112                    + " in a non-critical state"
 113                ok_data.truncl(config.prefs.getint("logparse", "maxlist"))
 114                section.append_data(ok_data)
 115
 116        logger.info("Finished systemd section")
 117        return section
 118
 119    def check_dependencies(self):
 120
 121        # Check if systemctl is set up
 122        cmd = "systemctl --version"
 123        if self._check_dependency_command(cmd)[0] != 0:
 124            return (False, ["systemctl"])
 125        else:
 126            return (True, None)