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