Projet

Général

Profil

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

root / plugins / systemd / timesync_status @ 3e015885

Historique | Voir | Annoter | Télécharger (3,34 ko)

1
#!/usr/bin/env python3
2
import re
3
import subprocess
4
import sys
5
import textwrap
6

    
7

    
8
'''
9
=head1 NAME
10

    
11
timesync_status - monitor ntp status with systemd-timesyncd
12

    
13
=head1 APPLICABLE SYSTEMS
14

    
15
All systems using systemd-timesyncd as its NTP-client. However, this
16
plugin itself also needs Python 3.5+ to call subprocess.run.
17

    
18
=head1 CONFIGURATION
19

    
20
This plugin should work out-of-the-box with autoconf. It does expect
21
timedatectl to be on $PATH, but that should always be the case in a
22
normal system.
23

    
24
=head1 INTERPRETATION
25

    
26
This plugin shows a graph with one line for every NTP metric it measure.
27
Metrics are shown with their usual name, and are explained in their
28
respective info fields.
29

    
30
This plugin issues no warnings or critical states.
31

    
32
=head1 MAGIC MARKERS
33

    
34
 #%# family=auto
35
 #%# capabilities=autoconf
36

    
37
=head1 VERSION
38

    
39
1.0
40

    
41
=head1 AUTHOR
42

    
43
Bert Peters <bert@bertptrs.nl>
44

    
45
=head1 LICENSE
46

    
47
GNU General Public License v2.0 only
48

    
49
SPDX-License-Identifier: LGPL-2.0-only
50

    
51
=cut
52
'''
53

    
54

    
55
def parse_time(value):
56
    value = value.strip()
57
    if ' ' in value:
58
        return sum(parse_time(x) for x in value.split(' '))
59

    
60
    # If time is exactly zero (for example Jitter), there is no unit (suffix)
61
    if value == "0" or value == "-0":
62
        return 0
63

    
64
    match = re.match(r'^([+-]?[0-9.]+)([a-z]+)$', value)
65
    if not match:
66
        raise ValueError('Invalid time ' + value)
67

    
68
    value = float(match.group(1))
69
    suffix = match.group(2)
70

    
71
    if suffix == 'min':
72
        value *= 60
73
    elif suffix == 'ms':
74
        value /= 1000
75
    elif suffix == 'us':
76
        value /= 1e6
77

    
78
    return value
79

    
80

    
81
def parse_response(data):
82
    values = {}
83
    for line in data.splitlines():
84
        k, v = line.split(': ', 1)
85
        values[k.strip()] = v.strip()
86

    
87
    return values
88

    
89

    
90
def retrieve():
91
    result = subprocess.run(['timedatectl', 'timesync-status'], capture_output=True)
92
    if result.returncode != 0:
93
        sys.exit('timedatectl failed')
94

    
95
    output = result.stdout.decode('utf-8')
96
    values = parse_response(output)
97

    
98
    print('offset.value', parse_time(values['Offset']))
99
    print('delay.value', parse_time(values['Delay']))
100
    print('delay.extinfo', 'Server', values['Server'])
101
    print('jitter.value', parse_time(values['Jitter']))
102
    print('poll.value', parse_time(values['Poll interval'].split('(')[0]))
103

    
104

    
105
def autoconf():
106
    result = subprocess.run(['timedatectl', 'status'], capture_output=True)
107
    if result.returncode != 0:
108
        print('no (failed to run timedatectl)')
109
        return
110

    
111
    values = parse_response(result.stdout.decode('utf-8'))
112
    if values['NTP service'] == 'active':
113
        print('yes')
114
    else:
115
        print('no (ntp service not running)')
116

    
117

    
118
def config():
119
    print(textwrap.dedent('''\
120
            graph_title Timesync status
121
            graph_vlabel s
122
            graph_category time
123

    
124
            offset.label Offset
125
            offset.info Time difference between source and local
126

    
127
            delay.label Delay
128
            delay.info Roundtrip time to the NTP-server
129

    
130
            jitter.label Jitter
131
            jitter.info Difference in offset between two subsequent samples
132

    
133
            poll.label Polling time
134
            poll.info Time between two subsequent NTP-polls
135
    '''))
136

    
137

    
138
if __name__ == '__main__':
139
    if len(sys.argv) > 1 and sys.argv[1] == 'config':
140
        config()
141
    elif len(sys.argv) > 1 and sys.argv[1] == 'autoconf':
142
        autoconf()
143
    else:
144
        retrieve()