#!/usr/bin/env python3
# pylint: disable=invalid-name
# pylint: enable=invalid-name

"""Munin plugin to monitor systemd service status.

=head1 NAME

systemd_status - monitor systemd service status, including normal services,
mounts, hotplugs and socket activations

=head1 APPLICABLE SYSTEMS

Linux systems with systemd installed.

=head1 CONFIGURATION

No configuration is required for this plugin.

Warning level for systemd "failed" state is set to 0:0. If any of the services
enters "failed" state, Munin will emit warning.

=head1 AUTHOR

Kim B. Heino <b@bbbs.net>

=head1 LICENSE

GPLv2

=head1 MAGIC MARKERS

 #%# family=auto
 #%# capabilities=autoconf

=cut

"""

import os
import re
import subprocess
import sys


STATES = (
    'failed',
    'dead',
    'running',
    'exited',
    'active',
    'listening',
    'waiting',
    'plugged',
    'mounted',
)


def config():
    """Autoconfig values."""
    print('graph_title systemd services')
    print('graph_vlabel Services')
    print('graph_category processes')
    print('graph_args --base 1000 --lower-limit 0')
    print('graph_scale no')
    print('graph_info Number of services in given activation state.')
    for state in STATES:
        print('{state}.label Services in {state} state'.format(state=state))
    print('failed.warning 0:0')
    if os.environ.get('MUNIN_CAP_DIRTYCONFIG') == '1':
        fetch()


def fetch():
    """Print runtime values."""
    # Get data
    try:
        # deb9/py3.5 doesn't have encoding parameter in subprocess
        output = subprocess.check_output(['/bin/systemctl', 'list-units'])
    except (OSError, subprocess.CalledProcessError):
        return
    output = output.decode('utf-8', 'ignore')

    # Parse data
    states = {state: 0 for state in STATES}
    for line in output.splitlines():
        token = line.split()
        if len(token) < 4:
            continue
        if len(token[0]) < 3:  # Skip failed-bullet
            token = token[1:]
        if token[0].endswith('.scope'):
            continue  # Ignore scopes
        if re.match(r'user.*@\d+\.service', token[0]):
            continue  # These fail randomly in older systemd
        if token[3] in states:
            states[token[3]] = states[token[3]] + 1

    # Output
    for state in STATES:
        print('{}.value {}'.format(state, states[state]))


if __name__ == '__main__':
    if len(sys.argv) > 1 and sys.argv[1] == 'autoconf':
        print('yes' if os.path.exists('/run/systemd/system') else
              'no (systemd is not running)')
    elif len(sys.argv) > 1 and sys.argv[1] == 'config':
        config()
    else:
        fetch()