Projet

Général

Profil

Paste
Télécharger au format
Statistiques
| Branche: | Révision:

root / plugins / chrony / chrony_status @ 357c3586

Historique | Voir | Annoter | Télécharger (4,71 ko)

1
#!/usr/bin/env python3
2

    
3
"""Munin plugin to monitor chrony NTP daemon status.
4

    
5
=head1 NAME
6

    
7
chrony_status - monitor chrony NTP daemon status
8

    
9
=head1 APPLICABLE SYSTEMS
10

    
11
Systems with chrony installed.
12

    
13
=head1 CONFIGURATION
14

    
15
Needs to be run as the user running chronyd (or root) in order to access the
16
Unix domain socket which chronyc uses to communicate with chronyd. Example
17
/etc/munin/plugin-conf.d/chrony_status.conf:
18

    
19
    [chrony_status]
20
    user _chrony
21

    
22
=head1 INTERPRETATION
23

    
24
Monitor Chrony's stratum value (with warning), time offset, network delay, clock
25
frequency, packets received, and packets dropped. It would be very easy to
26
monitor all of Chrony's values, but IMHO they aren't needed. The most important
27
information is stratum, giving "synced" / "not synced" value.
28

    
29
=head1 AUTHOR
30

    
31
Kim B. Heino <b@bbbs.net>
32

    
33
=head1 LICENSE
34

    
35
GPLv2
36

    
37
=head1 MAGIC MARKERS
38

    
39
 #%# family=auto
40
 #%# capabilities=autoconf
41

    
42
=cut
43
"""
44

    
45
import os
46
import subprocess
47
import sys
48

    
49

    
50
FIELDS = {
51
    'stratum': 'Stratum',
52
    'systime': 'System time',
53
    'delay': 'Root delay',
54
    'frequency': 'Frequency',
55
    'received': 'NTP packets received',
56
    'dropped': 'NTP packets dropped',
57
    'command_received': 'Command packets received',
58
    'command_dropped': 'Command packets dropped',
59
    'client_log_records_dropped': 'Client log records dropped',
60
}
61

    
62

    
63
def get_values():
64
    """Run `chronyc tracking` and `chronyc serverstats` and parse their output.
65

    
66
    Return: list of (label, value, description)
67
    """
68
    try:
69
        output = subprocess.run(['chronyc', '-m', 'tracking', 'serverstats'],
70
                                stdout=subprocess.PIPE, check=False,
71
                                encoding='utf-8', errors='ignore')
72
    except FileNotFoundError:
73
        return {}
74
    lines = output.stdout.splitlines()
75
    ret = {}
76
    for label in FIELDS:
77
        for line in lines:
78
            if line.startswith(FIELDS[label]):
79
                value = float(line.split(':', 1)[1].split()[0])
80
                if ' slow' in line:
81
                    value = -value
82
                ret[label] = value
83
    return ret
84

    
85

    
86
def config():
87
    """Print plugin config."""
88
    print('multigraph chrony_stratum')
89
    print('graph_title Chrony stratum')
90
    print('graph_vlabel stratum')
91
    print('graph_category time')
92
    print('graph_args --base 1000 --lower-limit 0')
93
    print('stratum.label Stratum')
94
    print('stratum.warning 1:9')
95
    # Use long unknown_limit to allow server reboot without Munin warning.
96
    # Clock doesn't drift fast so there's no hurry with warning.
97
    print('stratum.unknown_limit 15')
98

    
99
    print('multigraph chrony_systime')
100
    print('graph_title Chrony system time offset')
101
    print('graph_vlabel seconds')
102
    print('graph_category time')
103
    print('graph_args --base 1000')
104
    print('systime.label System time offset to NTP time')
105

    
106
    print('multigraph chrony_delay')
107
    print('graph_title Chrony network delay')
108
    print('graph_vlabel seconds')
109
    print('graph_category time')
110
    print('graph_args --base 1000')
111
    print('delay.label Network path delay')
112

    
113
    print('multigraph chrony_frequency')
114
    print('graph_title Chrony clock frequency error')
115
    print('graph_vlabel ppm')
116
    print('graph_category time')
117
    print('graph_args --base 1000')
118
    print('frequency.label Local clock frequency error')
119

    
120
    print('multigraph chrony_serverstats')
121
    print('graph_title Chrony server statistics')
122
    print('graph_vlabel Packets/${graph_period}')
123
    print('graph_category time')
124
    print('graph_args --base 1000')
125
    print('received.label Packets received')
126
    print('received.type DERIVE')
127
    print('received.min 0')
128
    print('dropped.label Packets dropped')
129
    print('dropped.type DERIVE')
130
    print('dropped.min 0')
131
    print('command_received.label Command packets received')
132
    print('command_received.type DERIVE')
133
    print('command_received.min 0')
134
    print('command_dropped.label Command packets dropped')
135
    print('command_dropped.type DERIVE')
136
    print('command_dropped.min 0')
137
    print('client_log_records_dropped.label Client log records dropped')
138
    print('client_log_records_dropped.type DERIVE')
139
    print('client_log_records_dropped.min 0')
140

    
141
    if os.environ.get('MUNIN_CAP_DIRTYCONFIG') == '1':
142
        fetch()
143

    
144

    
145
def fetch():
146
    """Print values."""
147
    values = get_values()
148
    for key in FIELDS:
149
        print('multigraph chrony_{}'.format(key))
150
        if key == 'stratum' and values[key] == 0:
151
            print('{}.value U'.format(key))
152
        else:
153
            print('{}.value {:.8f}'.format(key, values[key]))
154

    
155

    
156
if __name__ == '__main__':
157
    if len(sys.argv) > 1 and sys.argv[1] == 'autoconf':
158
        print('yes' if get_values() else 'no (chrony is not running)')
159
    elif len(sys.argv) > 1 and sys.argv[1] == 'config':
160
        config()
161
    else:
162
        fetch()