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