From 44b604cdb855305cb4624b69689bab0843b9a024 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Wed, 28 Nov 2018 12:11:52 +0100 Subject: [PATCH] Create plugin timesync_status to monitor systemd ntp. --- plugins/systemd/timesync_status | 136 ++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100755 plugins/systemd/timesync_status diff --git a/plugins/systemd/timesync_status b/plugins/systemd/timesync_status new file mode 100755 index 000000000..0b200c606 --- /dev/null +++ b/plugins/systemd/timesync_status @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +import re +import subprocess +import sys +import textwrap + + +''' +=head1 NAME + +timesync_status - monitor ntp status with systemd-timesyncd + +=head1 APPLICABLE SYSTEMS + +All systems using systemd-timesyncd as its NTP-client. However, this +plugin itself also needs Python 3.5+ to call subprocess.run. + +=head1 CONFIGURATION + +This plugin should work out-of-the-box with autoconf. It does expect +timedatectl to be on $PATH, but that should always be the case in a +normal system. + +=head1 INTERPRETATION + +This plugin shows a graph with one line for every NTP metric it measure. +Metrics are shown with their usual name, and are explained in their +respective info fields. + +This plugin issues no warnings or critical states. + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 VERSION + +1.0 + +=head1 AUTHOR + +Bert Peters + +=head1 LICENSE + +GPLv2 +''' + + +def parse_time(value): + value = value.strip() + if ' ' in value: + return sum(parse_time(x) for x in value.split(' ')) + + match = re.match(r'^([+-]?[0-9.]+)([a-z]+)$', value) + if not match: + raise ValueError('Invalid time ' + value) + + value = float(match.group(1)) + suffix = match.group(2) + + if suffix == 'min': + value *= 60 + elif suffix == 'ms': + value /= 1000 + elif suffix == 'us': + value /= 1e6 + + return value + + +def parse_response(data): + values = {} + for line in data.splitlines(): + k, v = line.split(': ', 1) + values[k.strip()] = v.strip() + + return values + + +def retrieve(): + result = subprocess.run(['timedatectl', 'timesync-status'], capture_output=True) + if result.returncode != 0: + sys.exit('timedatectl failed') + + output = result.stdout.decode('utf-8') + values = parse_response(output) + + print('offset.value', parse_time(values['Offset'])) + print('delay.value', parse_time(values['Delay'])) + print('delay.extinfo', 'Server', values['Server']) + print('jitter.value', parse_time(values['Jitter'])) + print('poll.value', parse_time(values['Poll interval'].split('(')[0])) + + +def autoconf(): + result = subprocess.run(['timedatectl', 'status'], capture_output=True) + if result.returncode != 0: + print('no (failed to run timedatectl)') + return + + values = parse_response(result.stdout.decode('utf-8')) + if values['NTP service'] == 'active': + print('yes') + else: + print('no (ntp service not running)') + + +def config(): + print(textwrap.dedent('''\ + graph_title Timesync status + graph_vlabel s + graph_category time + + offset.label Offset + offset.info Time difference between source and local + + delay.label Delay + delay.info Roundtrip time to the NTP-server + + jitter.label Jitter + jitter.info Difference in offset between two subsequent samples + + poll.label Polling time + poll.info Time between two subsequent NTP-polls + ''')) + + +if __name__ == '__main__': + if len(sys.argv) == 1: + retrieve() + elif sys.argv[1] == 'config': + config() + elif sys.argv[1] == 'autoconf': + autoconf()