#!/usr/bin/python3

# Copyright (c) 2019 Karol Babioch <karol@babioch.de>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

# This is a script that will check the systemd(1) status by parsing the output
# of systemctl(1). It will signal warning (1) and/or critical (2) conditions
# via the exit code when the system - as perceived by systemd - it not (yet)
# in optimal (i.e. running) state. This can, for instance, be used as a simple
# Nagios / Icinga check in order to monitor the overall system status.

from subprocess import CalledProcessError, SubprocessError
import argparse
import logging
import subprocess
import sys
import re

logger = logging.getLogger(__name__)
logger.addHandler(logging.StreamHandler())

EXIT_OK = 0
EXIT_WARNING = 1
EXIT_CRITICAL = 2
EXIT_UNKNOWN = 3

parser = argparse.ArgumentParser(description='Checks current system state as perceived by systemd')
parser.add_argument('--systemctl-path', dest='systemctl_path', help='Location of systemctl binary (systemctl in $PATH)', type=str, default='systemctl')
parser.add_argument('-d', '--debug', dest='debug', help='Enable debugging mode', action='store_true')
args = parser.parse_args()

if args.debug:
    logger.setLevel(logging.DEBUG)

def systemctl(cmdline):
    cmdline = [args.systemctl_path, '--no-pager', '--no-legend'] + cmdline
    logger.debug("Running: %s", cmdline)
    try:
        output = subprocess.check_output(cmdline, encoding=sys.getdefaultencoding())
    except (OSError, CalledProcessError, SubprocessError) as error:
        logger.debug("Exception: %s", error)
        print("Failed to invoke systemctl")
        sys.exit(EXIT_UNKNOWN)
    logger.debug("Output: %s", repr(output))
    return output

def systemctl_status():
    output = systemctl(["status"]).splitlines()[1:4]
    status = dict()
    status['state'] = re.match(r'^\s+State: (.*)$', output[0]).group(1)
    status['jobs'] = int(re.match(r'^\s+Jobs: (\d+) queued$', output[1]).group(1))
    status['failed'] = int(re.match(r'^\s+Failed: (\d+) units$', output[2]).group(1))
    return status

def systemctl_failed():
    output = systemctl(["--failed"])
    failed = []
    for line in output.splitlines():
        failed.append(line.split()[0])
    return failed

def systemctl_list_jobs():
    output = systemctl(["list-jobs"])
    jobs = []
    for job in output.splitlines():
        jobs.append(line.split()[0])
    return jobs

exit = EXIT_OK
status = systemctl_status()

jobs = "0"
if status['jobs'] > 0:
    jobs = systemctl_jobs()
    jobs = "{} ({})".format(len(jobs), ", ".join(jobs))
    exit = EXIT_WARNING

failed = "0"
if status['failed'] > 0:
    failed = systemctl_failed()
    failed = "{} ({})".format(len(failed), ", ".join(failed))
    exit = EXIT_CRITICAL

if status['state'] != 'running':
    exit = EXIT_CRITICAL

print("state: {}, jobs: {}, failed: {}".format(status['state'], jobs, failed))
sys.exit(exit)
