#!/usr/bin/env python3 # -*- python -*- # This plugin graphs the rate of sent, received, ignored, and dropped # NTP packets for an ntpd process. Similarly to the if_ plugins, # received packets are graphed as negative values, and sent packets # are graphed as positive values. Ignored and dropped packets are # graphed as positive values. # # The values are retrieved using ntpq or ntpdc, depending on the # version of the NTP distribution. # # Symlink this plugin into the node's plugins directory (like # /etc/munin/plugins). # # Copyright © 2016 Kenyon Ralph # # This program is free software. It comes without any warranty, to the # extent permitted by applicable law. You can redistribute it and/or # modify it under the terms of the Do What The Fuck You Want To Public # License, Version 2, as published by Sam Hocevar. See # http://www.wtfpl.net/ for more details. # # The latest version of this plugin can be found in the munin contrib # repository at https://github.com/munin-monitoring/contrib. Issues # with this plugin may be reported there. Patches accepted through the # normal github process of forking the repository and submitting a # pull request with your commits. import os from subprocess import check_output import sys import re if len(sys.argv) == 2 and sys.argv[1] == 'config': print('graph_title NTP traffic') print('graph_vlabel Packets/${graph_period} received(-)/sent(+)') print('graph_info This graph shows the packet rates of this ntpd. ' 'Bad means packets received with bad length or format. ' 'Authfailed means packets for which authentication failed.') print('graph_category time') print('received.label Received') print('received.type DERIVE') print('received.graph no') print('received.min 0') print('sent.label Rx/Tx') print('sent.type DERIVE') print('sent.negative received') print('sent.min 0') print('dropped.label Dropped') print('dropped.type DERIVE') print('dropped.min 0') print('ignored.label Ignored') print('ignored.type DERIVE') print('ignored.min 0') print('bad.label Bad') print('bad.type DERIVE') print('bad.min 0') print('authfail.label Authfailed') print('authfail.type DERIVE') print('authfail.min 0') print('declined.label Declined') print('declined.type DERIVE') print('declined.min 0') print('restricted.label Restricted') print('restricted.type DERIVE') print('restricted.min 0') print('kod.label KoD responses') print('kod.type DERIVE') print('kod.min 0') sys.exit(0) os.environ['PATH'] = '/usr/local/sbin:/usr/local/bin:' + os.environ['PATH'] def get_stats(cmd): return check_output([cmd, '-c', 'iostats', '-c', 'sysstats'], universal_newlines=True).splitlines() # compute the stats binary to be used version = check_output(['ntpq', '-c', 'version'], universal_newlines=True) ntpsec = False if version.startswith('ntpsec'): ntpsec = True stats_output = get_stats('ntpq') elif int(version.split()[1][0:5].replace('.', '')) >= 427: stats_output = get_stats('ntpq') else: stats_output = get_stats('ntpdc') # ntpsec uses a multi value output format stats = dict() if ntpsec is True: for line in stats_output: s_line = line.split(':', 1) stats[s_line[0]] = re.split(r'\s+', s_line[1])[1] else: for line in stats_output: if len(line.split(':')) == 2: stats[line.split(':')[0]] = int(line.split(':')[1]) print('received.value ' + str(stats['received packets'])) print('sent.value ' + str(stats['packets sent'])) print('dropped.value ' + str(stats['dropped packets'])) print('ignored.value ' + str(stats['ignored packets'])) print('bad.value ' + str(stats['bad length or format'])) print('authfail.value ' + str(stats['authentication failed'])) print('declined.value ' + str(stats['declined'])) print('restricted.value ' + str(stats['restricted'])) print('kod.value ' + str(stats['KoD responses']))