#!/usr/bin/python3 -tt # -*- coding: utf-8 -*- """Munin plugin to monitor chrony NTP daemon status. Copyright 2014, Kim B. Heino, b@bbbs.net, Foobar Oy License GPLv2+ #%# capabilities=autoconf #%# family=auto """ import os import subprocess import sys FIELDS = { 'stratum': 'Stratum', 'systime': 'System time', 'delay': 'Root delay', 'frequency': 'Frequency', } def get_values(): """Run "chronyc tracking" and parse it's output. Return: list of (label, value, description) """ try: output = subprocess.run(['/usr/bin/chronyc', 'tracking'], stdout=subprocess.PIPE, check=False, encoding='utf-8', errors='ignore') except FileNotFoundError: return {} lines = output.stdout.splitlines() ret = {} for label in FIELDS: for line in lines: if line.startswith(FIELDS[label]): value = float(line.split(':', 1)[1].split()[0]) if ' slow' in line: value = -value ret[label] = value return ret def config(): """Print plugin config.""" print('multigraph chrony_stratum') print('graph_title Chrony stratum') print('graph_vlabel stratum') print('graph_category time') print('graph_args --base 1000 --lower-limit 0') print('stratum.label Stratum') print('stratum.warning 1:9') print('multigraph chrony_systime') print('graph_title Chrony system time offset') print('graph_vlabel seconds') print('graph_category time') print('graph_args --base 1000') print('systime.label System time offset to NTP time') print('multigraph chrony_delay') print('graph_title Chrony network delay') print('graph_vlabel seconds') print('graph_category time') print('graph_args --base 1000') print('delay.label Network path delay') print('multigraph chrony_frequency') print('graph_title Chrony clock frequency error') print('graph_vlabel ppm') print('graph_category time') print('graph_args --base 1000') print('frequency.label Local clock frequency error') if os.environ.get('MUNIN_CAP_DIRTYCONFIG') == '1': fetch() def fetch(): """Print values.""" values = get_values() for key in FIELDS: print('multigraph chrony_{}'.format(key)) if key == 'stratum' and values[key] == 0: print('{}.value U'.format(key)) else: print('{}.value {:.8f}'.format(key, values[key])) if __name__ == '__main__': if len(sys.argv) > 1 and sys.argv[1] == 'autoconf': print('yes' if get_values() else 'no') elif len(sys.argv) > 1 and sys.argv[1] == 'config': config() else: fetch()