go.d chrony add serverstats query (disabled for now) (#18055)
Ilya Mashchenko committed
Jul 1, 2024 at 22:29 UTC
eb93428ebac1bc71a61705cd6960e332a0ba2612
5 files changed
+449
-101
src/go/collectors/go.d.plugin/modules/chrony/charts.go
+266
-92
@@ -3,140 +3,211 @@
3
package chrony
4
5
import (
6
+ "errors"
7
+
8
"github.com/netdata/netdata/go/go.d.plugin/agent/module"
9
)
10
11
+const (
12
+ prioStratum = module.Priority + iota
13
+ prioCurrentCorrection
14
+ prioRootDelay
15
+ prioRootDispersion
16
+ prioLastOffset
17
+ prioRmsOffset
18
+ prioFrequency
19
+ prioResidualFrequency
20
+ prioSkew
21
+ prioUpdateInterval
22
+ prioRefMeasurementTime
23
+ prioLeapStatus
24
+ prioActivity
25
+ prioNTPPackets
26
+ prioCommandPackets
27
+ prioNKEConnections
28
+ prioClientLogRecords
29
+)
30
+
31
var charts = module.Charts{
10
- {
11
- ID: "stratum",
12
- Title: "Distance to the reference clock",
13
- Units: "level",
14
- Fam: "stratum",
15
- Ctx: "chrony.stratum",
32
+ stratumChart.Copy(),
33
+
34
+ currentCorrectionChart.Copy(),
35
+
36
+ rootDelayChart.Copy(),
37
+ rootDispersionChart.Copy(),
38
+
39
+ lastOffsetChart.Copy(),
40
+ rmsOffsetChart.Copy(),
41
+
42
+ frequencyChart.Copy(),
43
+ residualFrequencyChart.Copy(),
44
+
45
+ skewChart.Copy(),
46
+
47
+ updateIntervalChart.Copy(),
48
+ refMeasurementTimeChart.Copy(),
49
+
50
+ leapStatusChart.Copy(),
51
+
52
+ activityChart.Copy(),
53
+}
54
+
55
+// Tracking charts
56
+var (
57
+ stratumChart = module.Chart{
58
+ ID: "stratum",
59
+ Title: "Distance to the reference clock",
60
+ Units: "level",
61
+ Fam: "stratum",
62
+ Ctx: "chrony.stratum",
63
+ Priority: prioStratum,
64
Dims: module.Dims{
65
{ID: "stratum", Name: "stratum"},
66
},
19
- },
20
- {
21
- ID: "current_correction",
22
- Title: "Current correction",
23
- Units: "seconds",
24
- Fam: "correction",
25
- Ctx: "chrony.current_correction",
67
+ }
68
+
69
+ currentCorrectionChart = module.Chart{
70
+ ID: "current_correction",
71
+ Title: "Current correction",
72
+ Units: "seconds",
73
+ Fam: "correction",
74
+ Ctx: "chrony.current_correction",
75
+ Priority: prioCurrentCorrection,
76
Dims: module.Dims{
77
{ID: "current_correction", Div: scaleFactor},
78
},
29
- },
30
- {
31
- ID: "root_delay",
32
- Title: "Network path delay to stratum-1",
33
- Units: "seconds",
34
- Fam: "root",
35
- Ctx: "chrony.root_delay",
79
+ }
80
+
81
+ rootDelayChart = module.Chart{
82
+ ID: "root_delay",
83
+ Title: "Network path delay to stratum-1",
84
+ Units: "seconds",
85
+ Fam: "root",
86
+ Ctx: "chrony.root_delay",
87
+ Priority: prioRootDelay,
88
Dims: module.Dims{
89
{ID: "root_delay", Div: scaleFactor},
90
},
39
- },
40
- {
41
- ID: "root_dispersion",
42
- Title: "Dispersion accumulated back to stratum-1",
43
- Units: "seconds",
44
- Fam: "root",
45
- Ctx: "chrony.root_dispersion",
91
+ }
92
+ rootDispersionChart = module.Chart{
93
+ ID: "root_dispersion",
94
+ Title: "Dispersion accumulated back to stratum-1",
95
+ Units: "seconds",
96
+ Fam: "root",
97
+ Ctx: "chrony.root_dispersion",
98
+ Priority: prioRootDispersion,
99
Dims: module.Dims{
100
{ID: "root_dispersion", Div: scaleFactor},
101
},
49
- },
50
- {
51
- ID: "last_offset",
52
- Title: "Offset on the last clock update",
53
- Units: "seconds",
54
- Fam: "offset",
55
- Ctx: "chrony.last_offset",
102
+ }
103
+
104
+ lastOffsetChart = module.Chart{
105
+ ID: "last_offset",
106
+ Title: "Offset on the last clock update",
107
+ Units: "seconds",
108
+ Fam: "offset",
109
+ Ctx: "chrony.last_offset",
110
+ Priority: prioLastOffset,
111
Dims: module.Dims{
112
{ID: "last_offset", Name: "offset", Div: scaleFactor},
113
},
59
- },
60
- {
61
- ID: "rms_offset",
62
- Title: "Long-term average of the offset value",
63
- Units: "seconds",
64
- Fam: "offset",
65
- Ctx: "chrony.rms_offset",
114
+ }
115
+ rmsOffsetChart = module.Chart{
116
+ ID: "rms_offset",
117
+ Title: "Long-term average of the offset value",
118
+ Units: "seconds",
119
+ Fam: "offset",
120
+ Ctx: "chrony.rms_offset",
121
+ Priority: prioRmsOffset,
122
Dims: module.Dims{
123
{ID: "rms_offset", Name: "offset", Div: scaleFactor},
124
},
69
- },
70
- {
71
- ID: "frequency",
72
- Title: "Frequency",
73
- Units: "ppm",
74
- Fam: "frequency",
75
- Ctx: "chrony.frequency",
125
+ }
126
+
127
+ frequencyChart = module.Chart{
128
+ ID: "frequency",
129
+ Title: "Frequency",
130
+ Units: "ppm",
131
+ Fam: "frequency",
132
+ Ctx: "chrony.frequency",
133
+ Priority: prioFrequency,
134
Dims: module.Dims{
135
{ID: "frequency", Div: scaleFactor},
136
},
79
- },
80
- {
81
- ID: "residual_frequency",
82
- Title: "Residual frequency",
83
- Units: "ppm",
84
- Fam: "frequency",
85
- Ctx: "chrony.residual_frequency",
137
+ }
138
+ residualFrequencyChart = module.Chart{
139
+ ID: "residual_frequency",
140
+ Title: "Residual frequency",
141
+ Units: "ppm",
142
+ Fam: "frequency",
143
+ Ctx: "chrony.residual_frequency",
144
+ Priority: prioResidualFrequency,
145
Dims: module.Dims{
146
{ID: "residual_frequency", Div: scaleFactor},
147
},
89
- },
90
- {
91
- ID: "skew",
92
- Title: "Skew",
93
- Units: "ppm",
94
- Fam: "frequency",
95
- Ctx: "chrony.skew",
148
+ }
149
+
150
+ skewChart = module.Chart{
151
+ ID: "skew",
152
+ Title: "Skew",
153
+ Units: "ppm",
154
+ Fam: "frequency",
155
+ Ctx: "chrony.skew",
156
+ Priority: prioSkew,
157
Dims: module.Dims{
158
{ID: "skew", Div: scaleFactor},
159
},
99
- },
100
- {
101
- ID: "update_interval",
102
- Title: "Interval between the last two clock updates",
103
- Units: "seconds",
104
- Fam: "updates",
105
- Ctx: "chrony.update_interval",
160
+ }
161
+
162
+ updateIntervalChart = module.Chart{
163
+ ID: "update_interval",
164
+ Title: "Interval between the last two clock updates",
165
+ Units: "seconds",
166
+ Fam: "updates",
167
+ Ctx: "chrony.update_interval",
168
+ Priority: prioUpdateInterval,
169
Dims: module.Dims{
170
{ID: "update_interval", Div: scaleFactor},
171
},
109
- },
110
- {
111
- ID: "ref_measurement_time",
112
- Title: "Time since the last measurement",
113
- Units: "seconds",
114
- Fam: "updates",
115
- Ctx: "chrony.ref_measurement_time",
172
+ }
173
+ refMeasurementTimeChart = module.Chart{
174
+ ID: "ref_measurement_time",
175
+ Title: "Time since the last measurement",
176
+ Units: "seconds",
177
+ Fam: "updates",
178
+ Ctx: "chrony.ref_measurement_time",
179
+ Priority: prioRefMeasurementTime,
180
Dims: module.Dims{
181
{ID: "ref_measurement_time"},
182
},
119
- },
120
- {
121
- ID: "leap_status",
122
- Title: "Leap status",
123
- Units: "status",
124
- Fam: "leap status",
125
- Ctx: "chrony.leap_status",
183
+ }
184
+
185
+ leapStatusChart = module.Chart{
186
+ ID: "leap_status",
187
+ Title: "Leap status",
188
+ Units: "status",
189
+ Fam: "leap status",
190
+ Ctx: "chrony.leap_status",
191
+ Priority: prioLeapStatus,
192
Dims: module.Dims{
193
{ID: "leap_status_normal", Name: "normal"},
194
{ID: "leap_status_insert_second", Name: "insert_second"},
195
{ID: "leap_status_delete_second", Name: "delete_second"},
196
{ID: "leap_status_unsynchronised", Name: "unsynchronised"},
197
},
132
- },
133
- {
134
- ID: "activity",
135
- Title: "Peers activity",
136
- Units: "sources",
137
- Fam: "activity",
138
- Ctx: "chrony.activity",
139
- Type: module.Stacked,
198
+ }
199
+)
200
+
201
+// Activity charts
202
+var (
203
+ activityChart = module.Chart{
204
+ ID: "activity",
205
+ Title: "Peers activity",
206
+ Units: "sources",
207
+ Fam: "activity",
208
+ Ctx: "chrony.activity",
209
+ Type: module.Stacked,
210
+ Priority: prioActivity,
211
Dims: module.Dims{
212
{ID: "online_sources", Name: "online"},
213
{ID: "offline_sources", Name: "offline"},
@@ -144,5 +215,108 @@ var charts = module.Charts{
215
{ID: "burst_offline_sources", Name: "burst_offline"},
216
{ID: "unresolved_sources", Name: "unresolved"},
217
},
147
- },
218
+ }
219
+)
220
+
221
+var serverStatsVer1Charts = module.Charts{
222
+ ntpPacketsChart.Copy(),
223
+ commandPacketsChart.Copy(),
224
+ clientLogRecordsChart.Copy(),
225
+}
226
+
227
+var serverStatsVer2Charts = module.Charts{
228
+ ntpPacketsChart.Copy(),
229
+ commandPacketsChart.Copy(),
230
+ clientLogRecordsChart.Copy(),
231
+ nkeConnectionChart.Copy(),
232
+}
233
+
234
+var serverStatsVer3Charts = module.Charts{
235
+ ntpPacketsChart.Copy(),
236
+ commandPacketsChart.Copy(),
237
+ clientLogRecordsChart.Copy(),
238
+ nkeConnectionChart.Copy(),
239
+}
240
+
241
+var serverStatsVer4Charts = module.Charts{
242
+ ntpPacketsChart.Copy(),
243
+ commandPacketsChart.Copy(),
244
+ clientLogRecordsChart.Copy(),
245
+ nkeConnectionChart.Copy(),
246
+}
247
+
248
+// ServerStats charts
249
+var (
250
+ ntpPacketsChart = module.Chart{
251
+ ID: "ntp_packets",
252
+ Title: "NTP packets",
253
+ Units: "packets/s",
254
+ Fam: "client requests",
255
+ Ctx: "chrony.ntp_packets",
256
+ Type: module.Stacked,
257
+ Priority: prioNTPPackets,
258
+ Dims: module.Dims{
259
+ {ID: "ntp_packets_received", Name: "received", Algo: module.Incremental},
260
+ {ID: "ntp_packets_dropped", Name: "dropped", Algo: module.Incremental},
261
+ },
262
+ }
263
+ commandPacketsChart = module.Chart{
264
+ ID: "command_packets",
265
+ Title: "Command packets",
266
+ Units: "packets/s",
267
+ Fam: "client requests",
268
+ Ctx: "chrony.command_packets",
269
+ Type: module.Stacked,
270
+ Priority: prioCommandPackets,
271
+ Dims: module.Dims{
272
+ {ID: "command_packets_received", Name: "received", Algo: module.Incremental},
273
+ {ID: "command_packets_dropped", Name: "dropped", Algo: module.Incremental},
274
+ },
275
+ }
276
+ nkeConnectionChart = module.Chart{
277
+ ID: "nke_connections",
278
+ Title: "NTS-KE connections",
279
+ Units: "connections/s",
280
+ Fam: "client requests",
281
+ Ctx: "chrony.nke_connections",
282
+ Type: module.Stacked,
283
+ Priority: prioNKEConnections,
284
+ Dims: module.Dims{
285
+ {ID: "nke_connections_accepted", Name: "accepted", Algo: module.Incremental},
286
+ {ID: "nke_connections_dropped", Name: "dropped", Algo: module.Incremental},
287
+ },
288
+ }
289
+ clientLogRecordsChart = module.Chart{
290
+ ID: "client_log_records",
291
+ Title: "Client log records",
292
+ Units: "records/s",
293
+ Fam: "client requests",
294
+ Ctx: "chrony.client_log_records",
295
+ Type: module.Stacked,
296
+ Priority: prioClientLogRecords,
297
+ Dims: module.Dims{
298
+ {ID: "client_log_records_dropped", Name: "dropped", Algo: module.Incremental},
299
+ },
300
+ }
301
+)
302
+
303
+func (c *Chrony) addServerStatsCharts(stats *serverStats) {
304
+ var err error
305
+
306
+ switch {
307
+ case stats.v1 != nil:
308
+ err = c.Charts().Add(*serverStatsVer1Charts.Copy()...)
309
+ case stats.v2 != nil:
310
+ err = c.Charts().Add(*serverStatsVer2Charts.Copy()...)
311
+ case stats.v3 != nil:
312
+ err = c.Charts().Add(*serverStatsVer3Charts.Copy()...)
313
+ case stats.v4 != nil:
314
+ err = c.Charts().Add(*serverStatsVer4Charts.Copy()...)
315
+ default:
316
+ err = errors.New("unknown stats chart")
317
+ }
318
+
319
+ if err != nil {
320
+ c.Warning(err)
321
+ }
322
}
src/go/collectors/go.d.plugin/modules/chrony/chrony.go
+9
-4
@@ -5,11 +5,13 @@ package chrony
5
import (
6
_ "embed"
7
"errors"
8
+ "sync"
9
"time"
10
10
- "github.com/facebook/time/ntp/chrony"
11
"github.com/netdata/netdata/go/go.d.plugin/agent/module"
12
"github.com/netdata/netdata/go/go.d.plugin/pkg/web"
13
+
14
+ "github.com/facebook/time/ntp/chrony"
15
)
16
17
//go:embed "config_schema.json"
@@ -29,8 +31,9 @@ func New() *Chrony {
31
Address: "127.0.0.1:323",
32
Timeout: web.Duration(time.Second),
33
},
32
- charts: charts.Copy(),
33
- newClient: newChronyClient,
34
+ charts: charts.Copy(),
35
+ addStatsChartsOnce: &sync.Once{},
36
+ newClient: newChronyClient,
37
}
38
}
39
@@ -45,7 +48,8 @@ type (
48
module.Base
49
Config `yaml:",inline" json:""`
50
48
- charts *module.Charts
51
+ charts *module.Charts
52
+ addStatsChartsOnce *sync.Once
53
54
client chronyClient
55
newClient func(c Config) (chronyClient, error)
@@ -53,6 +57,7 @@ type (
57
chronyClient interface {
58
Tracking() (*chrony.ReplyTracking, error)
59
Activity() (*chrony.ReplyActivity, error)
60
+ ServerStats() (*serverStats, error)
61
Close()
62
}
63
)
src/go/collectors/go.d.plugin/modules/chrony/chrony_test.go
+28
-3
@@ -240,9 +240,10 @@ func prepareChronyWithMock(m *mockClient) *Chrony {
240
}
241
242
type mockClient struct {
243
- errOnTracking bool
244
- errOnActivity bool
245
- closeCalled bool
243
+ errOnTracking bool
244
+ errOnActivity bool
245
+ errOnServerStats bool
246
+ closeCalled bool
247
}
248
249
func (m *mockClient) Tracking() (*chrony.ReplyTracking, error) {
@@ -286,6 +287,30 @@ func (m *mockClient) Activity() (*chrony.ReplyActivity, error) {
287
return &reply, nil
288
}
289
290
+func (m *mockClient) ServerStats() (*serverStats, error) {
291
+ if m.errOnServerStats {
292
+ return nil, errors.New("mockClient.ServerStats call error")
293
+ }
294
+
295
+ reply := serverStats{
296
+ v3: &chrony.ServerStats3{
297
+ NTPHits: 10,
298
+ NKEHits: 10,
299
+ CMDHits: 10,
300
+ NTPDrops: 1,
301
+ NKEDrops: 1,
302
+ CMDDrops: 1,
303
+ LogDrops: 1,
304
+ NTPAuthHits: 10,
305
+ NTPInterleavedHits: 10,
306
+ NTPTimestamps: 0,
307
+ NTPSpanSeconds: 0,
308
+ },
309
+ }
310
+
311
+ return &reply, nil
312
+}
313
+
314
func (m *mockClient) Close() {
315
m.closeCalled = true
316
}
src/go/collectors/go.d.plugin/modules/chrony/client.go
+85
-2
@@ -19,6 +19,7 @@ func newChronyClient(c Config) (chronyClient, error) {
19
conn: conn,
20
client: &chrony.Client{Connection: conn},
21
}
22
+
23
return client, nil
24
}
25
@@ -28,7 +29,9 @@ type simpleClient struct {
29
}
30
31
func (sc *simpleClient) Tracking() (*chrony.ReplyTracking, error) {
31
- reply, err := sc.client.Communicate(chrony.NewTrackingPacket())
32
+ req := chrony.NewTrackingPacket()
33
+
34
+ reply, err := sc.client.Communicate(req)
35
if err != nil {
36
return nil, err
37
}
@@ -41,7 +44,9 @@ func (sc *simpleClient) Tracking() (*chrony.ReplyTracking, error) {
44
}
45
46
func (sc *simpleClient) Activity() (*chrony.ReplyActivity, error) {
44
- reply, err := sc.client.Communicate(chrony.NewActivityPacket())
47
+ req := chrony.NewActivityPacket()
48
+
49
+ reply, err := sc.client.Communicate(req)
50
if err != nil {
51
return nil, err
52
}
@@ -53,6 +58,84 @@ func (sc *simpleClient) Activity() (*chrony.ReplyActivity, error) {
58
return activity, nil
59
}
60
61
+type serverStats struct {
62
+ v1 *chrony.ServerStats
63
+ v2 *chrony.ServerStats2
64
+ v3 *chrony.ServerStats3
65
+ v4 *chrony.ServerStats4
66
+}
67
+
68
+func (sc *simpleClient) ServerStats() (*serverStats, error) {
69
+ req := chrony.NewServerStatsPacket()
70
+
71
+ reply, err := sc.client.Communicate(req)
72
+ if err != nil {
73
+ return nil, err
74
+ }
75
+
76
+ var stats serverStats
77
+
78
+ switch v := reply.(type) {
79
+ case *chrony.ReplyServerStats:
80
+ stats.v1 = &chrony.ServerStats{
81
+ NTPHits: v.NTPHits,
82
+ CMDHits: v.CMDHits,
83
+ NTPDrops: v.NTPDrops,
84
+ CMDDrops: v.CMDDrops,
85
+ LogDrops: v.LogDrops,
86
+ }
87
+ case *chrony.ReplyServerStats2:
88
+ stats.v2 = &chrony.ServerStats2{
89
+ NTPHits: v.NTPHits,
90
+ NKEHits: v.NKEHits,
91
+ CMDHits: v.CMDHits,
92
+ NTPDrops: v.NTPDrops,
93
+ NKEDrops: v.NKEDrops,
94
+ CMDDrops: v.CMDDrops,
95
+ LogDrops: v.LogDrops,
96
+ NTPAuthHits: v.NTPAuthHits,
97
+ }
98
+ case *chrony.ReplyServerStats3:
99
+ stats.v3 = &chrony.ServerStats3{
100
+ NTPHits: v.NTPHits,
101
+ NKEHits: v.NKEHits,
102
+ CMDHits: v.CMDHits,
103
+ NTPDrops: v.NTPDrops,
104
+ NKEDrops: v.NKEDrops,
105
+ CMDDrops: v.CMDDrops,
106
+ LogDrops: v.LogDrops,
107
+ NTPAuthHits: v.NTPAuthHits,
108
+ NTPInterleavedHits: v.NTPInterleavedHits,
109
+ NTPTimestamps: v.NTPTimestamps,
110
+ NTPSpanSeconds: v.NTPSpanSeconds,
111
+ }
112
+ case *chrony.ReplyServerStats4:
113
+ stats.v4 = &chrony.ServerStats4{
114
+ NTPHits: v.NTPHits,
115
+ NKEHits: v.NKEHits,
116
+ CMDHits: v.CMDHits,
117
+ NTPDrops: v.NTPDrops,
118
+ NKEDrops: v.NKEDrops,
119
+ CMDDrops: v.CMDDrops,
120
+ LogDrops: v.LogDrops,
121
+ NTPAuthHits: v.NTPAuthHits,
122
+ NTPInterleavedHits: v.NTPInterleavedHits,
123
+ NTPTimestamps: v.NTPTimestamps,
124
+ NTPSpanSeconds: v.NTPSpanSeconds,
125
+ NTPDaemonRxtimestamps: v.NTPDaemonRxtimestamps,
126
+ NTPDaemonTxtimestamps: v.NTPDaemonTxtimestamps,
127
+ NTPKernelRxtimestamps: v.NTPKernelRxtimestamps,
128
+ NTPKernelTxtimestamps: v.NTPKernelTxtimestamps,
129
+ NTPHwRxTimestamps: v.NTPHwRxTimestamps,
130
+ NTPHwTxTimestamps: v.NTPHwTxTimestamps,
131
+ }
132
+ default:
133
+ return nil, fmt.Errorf("unexpected reply type, want=ReplyServerStats, got=%T", reply)
134
+ }
135
+
136
+ return &stats, nil
137
+}
138
+
139
func (sc *simpleClient) Close() {
140
if sc.conn != nil {
141
_ = sc.conn.Close()
src/go/collectors/go.d.plugin/modules/chrony/collect.go
+61
@@ -3,7 +3,9 @@
3
package chrony
4
5
import (
6
+ "errors"
7
"fmt"
8
+ "strings"
9
"time"
10
)
11
@@ -26,6 +28,14 @@ func (c *Chrony) collect() (map[string]int64, error) {
28
if err := c.collectActivity(mx); err != nil {
29
return mx, err
30
}
31
+ if strings.HasPrefix(c.Address, "/") {
32
+ // TODO: Allowed only through the Unix domain socket (requires "_chrony" group membership).
33
+ // See https://github.com/facebook/time/blob/18207c5d8ddc7242e8d4192985898b6dbe66932c/cmd/ntpcheck/checker/chrony.go#L38
34
+ // ^^ For some reason doesn't work, Chrony doesn't respond. Additional configuration needed?
35
+ //if err := c.collectServerStats(mx); err != nil {
36
+ // return mx, err
37
+ //}
38
+ }
39
40
return mx, nil
41
}
@@ -82,6 +92,57 @@ func (c *Chrony) collectActivity(mx map[string]int64) error {
92
return nil
93
}
94
95
+func (c *Chrony) collectServerStats(mx map[string]int64) error {
96
+ stats, err := c.client.ServerStats()
97
+ if err != nil {
98
+ return fmt.Errorf("error on collecting server stats: %v", err)
99
+ }
100
+
101
+ switch {
102
+ case stats.v4 != nil:
103
+ mx["ntp_packets_received"] = int64(stats.v4.NTPHits)
104
+ mx["ntp_packets_dropped"] = int64(stats.v4.NTPDrops)
105
+ mx["command_packets_received"] = int64(stats.v4.CMDHits)
106
+ mx["command_packets_dropped"] = int64(stats.v4.CMDDrops)
107
+ mx["client_log_records_dropped"] = int64(stats.v4.LogDrops)
108
+ mx["nke_connections_accepted"] = int64(stats.v4.NKEHits)
109
+ mx["nke_connections_dropped"] = int64(stats.v4.NKEDrops)
110
+ mx["authenticated_ntp_packets"] = int64(stats.v4.NTPAuthHits)
111
+ mx["interleaved_ntp_packets"] = int64(stats.v4.NTPInterleavedHits)
112
+ case stats.v3 != nil:
113
+ mx["ntp_packets_received"] = int64(stats.v3.NTPHits)
114
+ mx["ntp_packets_dropped"] = int64(stats.v3.NTPDrops)
115
+ mx["command_packets_received"] = int64(stats.v3.CMDHits)
116
+ mx["command_packets_dropped"] = int64(stats.v3.CMDDrops)
117
+ mx["client_log_records_dropped"] = int64(stats.v3.LogDrops)
118
+ mx["nke_connections_accepted"] = int64(stats.v3.NKEHits)
119
+ mx["nke_connections_dropped"] = int64(stats.v3.NKEDrops)
120
+ mx["authenticated_ntp_packets"] = int64(stats.v3.NTPAuthHits)
121
+ mx["interleaved_ntp_packets"] = int64(stats.v3.NTPInterleavedHits)
122
+ case stats.v2 != nil:
123
+ mx["ntp_packets_received"] = int64(stats.v2.NTPHits)
124
+ mx["ntp_packets_dropped"] = int64(stats.v2.NTPDrops)
125
+ mx["command_packets_received"] = int64(stats.v2.CMDHits)
126
+ mx["command_packets_dropped"] = int64(stats.v2.CMDDrops)
127
+ mx["client_log_records_dropped"] = int64(stats.v2.LogDrops)
128
+ mx["nke_connections_accepted"] = int64(stats.v2.NKEHits)
129
+ mx["nke_connections_dropped"] = int64(stats.v2.NKEDrops)
130
+ mx["authenticated_ntp_packets"] = int64(stats.v2.NTPAuthHits)
131
+ case stats.v1 != nil:
132
+ mx["ntp_packets_received"] = int64(stats.v1.NTPHits)
133
+ mx["ntp_packets_dropped"] = int64(stats.v1.NTPDrops)
134
+ mx["command_packets_received"] = int64(stats.v1.CMDHits)
135
+ mx["command_packets_dropped"] = int64(stats.v1.CMDDrops)
136
+ mx["client_log_records_dropped"] = int64(stats.v1.LogDrops)
137
+ default:
138
+ return errors.New("invalid server stats reply")
139
+ }
140
+
141
+ c.addStatsChartsOnce.Do(func() { c.addServerStatsCharts(stats) })
142
+
143
+ return nil
144
+}
145
+
146
func boolToInt(v bool) int64 {
147
if v {
148
return 1