#!/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 =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()