master
go 576 lines 16.4 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package ceph
4
5 import (
6 "fmt"
7 "strings"
8
9 "github.com/netdata/netdata/go/plugins/plugin/framework/collectorapi"
10 )
11
12 const (
13 prioClusterStatus = collectorapi.Priority + iota
14 prioClusterHostsCount
15 prioClusterMonitorsCount
16 prioClusterOSDsCount
17 prioClusterOSDsByStatusCount
18 prioClusterManagersCount
19 prioClusterObjectGatewaysCount
20 prioClusterIScsiGatewaysCount
21 prioClusterIScsiGatewaysByStatusCount
22
23 prioClusterPhysCapacityUtilization
24 prioClusterPhysCapacityUsage
25 prioClusterObjectsCount
26 prioClusterObjectsByStatusPercent
27 prioClusterPoolsCount
28 prioClusterPGsCount
29 prioClusterPGsByStatusCount
30 prioClusterPGsPerOsdCount
31
32 prioClusterClientIO
33 prioClusterClientIOPS
34 prioClusterClientRecoveryThroughput
35 prioClusterScrubStatus
36
37 prioOsdStatus
38 prioOsdSpaceUsage
39 prioOsdIO
40 prioOsdIOPS
41 prioOsdLatency
42
43 prioPoolSpaceUtilization
44 prioPoolSpaceUsage
45 prioPoolObjectsCount
46 prioPoolIO
47 prioPoolIOPS
48 )
49
50 var clusterCharts = collectorapi.Charts{
51 clusterStatusChart.Copy(),
52 clusterHostsCountChart.Copy(),
53 clusterMonitorsCountChart.Copy(),
54 clusterOsdsCountChart.Copy(),
55 clusterOsdsByStatusCountChart.Copy(),
56 clusterManagersCountChart.Copy(),
57 clusterObjectGatewaysCountChart.Copy(),
58 clusterIScsiGatewaysCountChart.Copy(),
59 clusterIScsiGatewaysByStatusCountChart.Copy(),
60
61 clusterPhysCapacityUtilizationChart.Copy(),
62 clusterPhysCapacityUsageChart.Copy(),
63 clusterObjectsCountChart.Copy(),
64 clusterObjectsByStatusPercentChart.Copy(),
65 clusterPoolsCountChart.Copy(),
66 clusterPGsCountChart.Copy(),
67 clusterPGsByStatusCountChart.Copy(),
68 clusterPgsPerOsdCountChart.Copy(),
69
70 clusterClientIOChart.Copy(),
71 clusterClientIOPSChart.Copy(),
72 clusterRecoveryThroughputChart.Copy(),
73 clusterScrubStatusChart.Copy(),
74 }
75
76 var osdChartsTmpl = collectorapi.Charts{
77 osdStatusChartTmpl.Copy(),
78 osdSpaceUsageChartTmpl.Copy(),
79 osdIOChartTmpl.Copy(),
80 osdIOPSChartTmpl.Copy(),
81 osdLatencyChartTmpl.Copy(),
82 }
83
84 var poolChartsTmpl = collectorapi.Charts{
85 poolSpaceUtilizationChartTmpl.Copy(),
86 poolSpaceUsageChartTmpl.Copy(),
87 poolObjectsCountChartTmpl.Copy(),
88 poolIOChartTmpl.Copy(),
89 poolIOPSChartTmpl.Copy(),
90 }
91
92 var (
93 clusterStatusChart = collectorapi.Chart{
94 ID: "cluster_status",
95 Title: "Ceph Cluster Status",
96 Fam: "status",
97 Units: "status",
98 Ctx: "ceph.cluster_status",
99 Type: collectorapi.Line,
100 Priority: prioClusterStatus,
101 Dims: collectorapi.Dims{
102 {ID: "health_ok", Name: "ok"},
103 {ID: "health_err", Name: "err"},
104 {ID: "health_warn", Name: "warn"},
105 },
106 }
107 clusterHostsCountChart = collectorapi.Chart{
108 ID: "cluster_hosts_count",
109 Title: "Ceph Cluster Hosts",
110 Fam: "status",
111 Units: "hosts",
112 Ctx: "ceph.cluster_hosts_count",
113 Type: collectorapi.Line,
114 Priority: prioClusterHostsCount,
115 Dims: collectorapi.Dims{
116 {ID: "hosts_num", Name: "hosts"},
117 },
118 }
119 clusterMonitorsCountChart = collectorapi.Chart{
120 ID: "cluster_monitors_count",
121 Title: "Ceph Cluster Monitors",
122 Fam: "status",
123 Units: "monitors",
124 Ctx: "ceph.cluster_monitors_count",
125 Type: collectorapi.Line,
126 Priority: prioClusterMonitorsCount,
127 Dims: collectorapi.Dims{
128 {ID: "monitors_num", Name: "monitors"},
129 },
130 }
131 clusterOsdsCountChart = collectorapi.Chart{
132 ID: "cluster_osds_count",
133 Title: "Ceph Cluster OSDs",
134 Fam: "status",
135 Units: "osds",
136 Ctx: "ceph.cluster_osds_count",
137 Type: collectorapi.Line,
138 Priority: prioClusterOSDsCount,
139 Dims: collectorapi.Dims{
140 {ID: "osds_num", Name: "osds"},
141 },
142 }
143 clusterOsdsByStatusCountChart = collectorapi.Chart{
144 ID: "cluster_osds_by_status_count",
145 Title: "Ceph Cluster OSDs by Status",
146 Fam: "status",
147 Units: "osds",
148 Ctx: "ceph.cluster_osds_by_status_count",
149 Type: collectorapi.Line,
150 Priority: prioClusterOSDsByStatusCount,
151 Dims: collectorapi.Dims{
152 {ID: "osds_up_num", Name: "up"},
153 {ID: "osds_down_num", Name: "down"},
154 {ID: "osds_in_num", Name: "in"},
155 {ID: "osds_out_num", Name: "out"},
156 },
157 }
158 clusterManagersCountChart = collectorapi.Chart{
159 ID: "cluster_managers_count",
160 Title: "Ceph Cluster Managers",
161 Fam: "status",
162 Units: "managers",
163 Ctx: "ceph.cluster_managers_count",
164 Type: collectorapi.Line,
165 Priority: prioClusterManagersCount,
166 Dims: collectorapi.Dims{
167 {ID: "mgr_active_num", Name: "active"},
168 {ID: "mgr_standby_num", Name: "standby"},
169 },
170 }
171 clusterObjectGatewaysCountChart = collectorapi.Chart{
172 ID: "cluster_object_gateways_count",
173 Title: "Ceph Cluster Object Gateways (RGW)",
174 Fam: "status",
175 Units: "gateways",
176 Ctx: "ceph.cluster_object_gateways_count",
177 Type: collectorapi.Line,
178 Priority: prioClusterObjectGatewaysCount,
179 Dims: collectorapi.Dims{
180 {ID: "rgw_num", Name: "object"},
181 },
182 }
183 clusterIScsiGatewaysCountChart = collectorapi.Chart{
184 ID: "cluster_iscsi_gateways_count",
185 Title: "Ceph Cluster iSCSI Gateways",
186 Fam: "status",
187 Units: "gateways",
188 Ctx: "ceph.cluster_iscsi_gateways_count",
189 Type: collectorapi.Line,
190 Priority: prioClusterIScsiGatewaysCount,
191 Dims: collectorapi.Dims{
192 {ID: "iscsi_daemons_num", Name: "iscsi"},
193 },
194 }
195 clusterIScsiGatewaysByStatusCountChart = collectorapi.Chart{
196 ID: "cluster_iscsi_gateways_by_status_count",
197 Title: "Ceph Cluster iSCSI Gateways by Status",
198 Fam: "status",
199 Units: "gateways",
200 Ctx: "ceph.cluster_iscsi_gateways_by_status_count",
201 Type: collectorapi.Line,
202 Priority: prioClusterIScsiGatewaysByStatusCount,
203 Dims: collectorapi.Dims{
204 {ID: "iscsi_daemons_up_num", Name: "up"},
205 {ID: "iscsi_daemons_down_num", Name: "down"},
206 },
207 }
208 )
209
210 var (
211 clusterPhysCapacityUtilizationChart = collectorapi.Chart{
212 ID: "cluster_physical_capacity_utilization",
213 Title: "Ceph Cluster Physical Capacity Utilization",
214 Fam: "capacity",
215 Units: "percent",
216 Ctx: "ceph.cluster_physical_capacity_utilization",
217 Type: collectorapi.Area,
218 Priority: prioClusterPhysCapacityUtilization,
219 Dims: collectorapi.Dims{
220 {ID: "raw_capacity_utilization", Name: "utilization", Div: precision},
221 },
222 }
223 clusterPhysCapacityUsageChart = collectorapi.Chart{
224 ID: "cluster_physical_capacity_usage",
225 Title: "Ceph Cluster Physical Capacity Usage",
226 Fam: "capacity",
227 Units: "bytes",
228 Ctx: "ceph.cluster_physical_capacity_usage",
229 Type: collectorapi.Stacked,
230 Priority: prioClusterPhysCapacityUsage,
231 Dims: collectorapi.Dims{
232 {ID: "raw_capacity_avail_bytes", Name: "avail"},
233 {ID: "raw_capacity_used_bytes", Name: "used"},
234 },
235 }
236 clusterObjectsCountChart = collectorapi.Chart{
237 ID: "cluster_objects_count",
238 Title: "Ceph Cluster Objects",
239 Fam: "capacity",
240 Units: "objects",
241 Ctx: "ceph.cluster_objects_count",
242 Type: collectorapi.Line,
243 Priority: prioClusterObjectsCount,
244 Dims: collectorapi.Dims{
245 {ID: "objects_num", Name: "objects"},
246 },
247 }
248 clusterObjectsByStatusPercentChart = collectorapi.Chart{
249 ID: "cluster_objects_by_status",
250 Title: "Ceph Cluster Objects by Status",
251 Fam: "capacity",
252 Units: "percent",
253 Ctx: "ceph.cluster_objects_by_status_distribution",
254 Type: collectorapi.Stacked,
255 Priority: prioClusterObjectsByStatusPercent,
256 Dims: collectorapi.Dims{
257 {ID: "objects_healthy_num", Name: "healthy", Algo: collectorapi.PercentOfAbsolute},
258 {ID: "objects_misplaced_num", Name: "misplaced", Algo: collectorapi.PercentOfAbsolute},
259 {ID: "objects_degraded_num", Name: "degraded", Algo: collectorapi.PercentOfAbsolute},
260 {ID: "objects_unfound_num", Name: "unfound", Algo: collectorapi.PercentOfAbsolute},
261 },
262 }
263 clusterPoolsCountChart = collectorapi.Chart{
264 ID: "cluster_pools_count",
265 Title: "Ceph Cluster Pools",
266 Fam: "capacity",
267 Units: "pools",
268 Ctx: "ceph.cluster_pools_count",
269 Type: collectorapi.Line,
270 Priority: prioClusterPoolsCount,
271 Dims: collectorapi.Dims{
272 {ID: "pools_num", Name: "pools"},
273 },
274 }
275 clusterPGsCountChart = collectorapi.Chart{
276 ID: "cluster_pgs_count",
277 Title: "Ceph Cluster Placement Groups",
278 Fam: "capacity",
279 Units: "pgs",
280 Ctx: "ceph.cluster_pgs_count",
281 Type: collectorapi.Line,
282 Priority: prioClusterPGsCount,
283 Dims: collectorapi.Dims{
284 {ID: "pgs_num", Name: "pgs"},
285 },
286 }
287 clusterPGsByStatusCountChart = collectorapi.Chart{
288 ID: "cluster_pgs_by_status_count",
289 Title: "Ceph Cluster Placement Groups by Status",
290 Fam: "capacity",
291 Units: "pgs",
292 Ctx: "ceph.cluster_pgs_by_status_count",
293 Type: collectorapi.Stacked,
294 Priority: prioClusterPGsByStatusCount,
295 Dims: collectorapi.Dims{
296 {ID: "pg_status_category_clean", Name: "clean"},
297 {ID: "pg_status_category_working", Name: "working"},
298 {ID: "pg_status_category_warning", Name: "warning"},
299 {ID: "pg_status_category_unknown", Name: "unknown"},
300 },
301 }
302 clusterPgsPerOsdCountChart = collectorapi.Chart{
303 ID: "cluster_pgs_per_osd_count",
304 Title: "Ceph Cluster Placement Groups per OSD",
305 Fam: "capacity",
306 Units: "pgs",
307 Ctx: "ceph.cluster_pgs_per_osd_count",
308 Type: collectorapi.Line,
309 Priority: prioClusterPGsPerOsdCount,
310 Dims: collectorapi.Dims{
311 {ID: "pgs_per_osd", Name: "per_osd"},
312 },
313 }
314 )
315
316 var (
317 clusterClientIOChart = collectorapi.Chart{
318 ID: "cluster_client_io",
319 Title: "Ceph Cluster Client IO",
320 Fam: "performance",
321 Units: "bytes/s",
322 Ctx: "ceph.cluster_client_io",
323 Type: collectorapi.Area,
324 Priority: prioClusterClientIO,
325 Dims: collectorapi.Dims{
326 {ID: "client_perf_read_bytes_sec", Name: "read"},
327 {ID: "client_perf_write_bytes_sec", Name: "written", Mul: -1},
328 },
329 }
330 clusterClientIOPSChart = collectorapi.Chart{
331 ID: "cluster_client_iops",
332 Title: "Ceph Cluster Client IOPS",
333 Fam: "performance",
334 Units: "ops/s",
335 Ctx: "ceph.cluster_client_iops",
336 Type: collectorapi.Line,
337 Priority: prioClusterClientIOPS,
338 Dims: collectorapi.Dims{
339 {ID: "client_perf_read_op_per_sec", Name: "read"},
340 {ID: "client_perf_write_op_per_sec", Name: "write", Mul: -1},
341 },
342 }
343 clusterRecoveryThroughputChart = collectorapi.Chart{
344 ID: "cluster_recovery_throughput",
345 Title: "Ceph Cluster Recovery Throughput",
346 Fam: "performance",
347 Units: "bytes/s",
348 Ctx: "ceph.cluster_recovery_throughput",
349 Type: collectorapi.Line,
350 Priority: prioClusterClientRecoveryThroughput,
351 Dims: collectorapi.Dims{
352 {ID: "client_perf_recovering_bytes_per_sec", Name: "recovery"},
353 },
354 }
355 clusterScrubStatusChart = collectorapi.Chart{
356 ID: "cluster_scrub_status",
357 Title: "Ceph Cluster Scrubbing Status",
358 Fam: "performance",
359 Units: "status",
360 Ctx: "ceph.cluster_scrub_status",
361 Type: collectorapi.Line,
362 Priority: prioClusterScrubStatus,
363 Dims: collectorapi.Dims{
364 {ID: "scrub_status_disabled", Name: "disabled"},
365 {ID: "scrub_status_active", Name: "active"},
366 {ID: "scrub_status_inactive", Name: "inactive"},
367 },
368 }
369 )
370
371 var (
372 osdStatusChartTmpl = collectorapi.Chart{
373 ID: "osd_%s_status",
374 Title: "Ceph OSD Status",
375 Fam: "osd",
376 Units: "status",
377 Ctx: "ceph.osd_status",
378 Type: collectorapi.Line,
379 Priority: prioOsdStatus,
380 Dims: collectorapi.Dims{
381 {ID: "osd_%s_status_up", Name: "up"},
382 {ID: "osd_%s_status_down", Name: "down"},
383 {ID: "osd_%s_status_in", Name: "in"},
384 {ID: "osd_%s_status_out", Name: "out"},
385 },
386 }
387 osdSpaceUsageChartTmpl = collectorapi.Chart{
388 ID: "osd_%s_space_usage",
389 Title: "Ceph OSD Space Usage",
390 Fam: "osd",
391 Units: "bytes",
392 Ctx: "ceph.osd_space_usage",
393 Type: collectorapi.Stacked,
394 Priority: prioOsdSpaceUsage,
395 Dims: collectorapi.Dims{
396 {ID: "osd_%s_space_avail_bytes", Name: "avail"},
397 {ID: "osd_%s_space_used_bytes", Name: "used"},
398 },
399 }
400 osdIOChartTmpl = collectorapi.Chart{
401 ID: "osd_%s_io",
402 Title: "Ceph OSD IO",
403 Fam: "osd",
404 Units: "bytes/s",
405 Ctx: "ceph.osd_io",
406 Type: collectorapi.Area,
407 Priority: prioOsdIO,
408 Dims: collectorapi.Dims{
409 {ID: "osd_%s_read_bytes", Name: "read", Algo: collectorapi.Incremental},
410 {ID: "osd_%s_written_bytes", Name: "written", Algo: collectorapi.Incremental, Mul: -1},
411 },
412 }
413 osdIOPSChartTmpl = collectorapi.Chart{
414 ID: "osd_%s_iops",
415 Title: "Ceph OSD IOPS",
416 Fam: "osd",
417 Units: "ops/s",
418 Ctx: "ceph.osd_iops",
419 Type: collectorapi.Line,
420 Priority: prioOsdIOPS,
421 Dims: collectorapi.Dims{
422 {ID: "osd_%s_read_ops", Name: "read", Algo: collectorapi.Incremental},
423 {ID: "osd_%s_write_ops", Name: "write", Algo: collectorapi.Incremental},
424 },
425 }
426 osdLatencyChartTmpl = collectorapi.Chart{
427 ID: "osd_%s_latency",
428 Title: "Ceph OSD Latency",
429 Fam: "osd",
430 Units: "milliseconds",
431 Ctx: "ceph.osd_latency",
432 Type: collectorapi.Line,
433 Priority: prioOsdLatency,
434 Dims: collectorapi.Dims{
435 {ID: "osd_%s_commit_latency_ms", Name: "commit"},
436 {ID: "osd_%s_apply_latency_ms", Name: "apply"},
437 },
438 }
439 )
440
441 var (
442 poolSpaceUtilizationChartTmpl = collectorapi.Chart{
443 ID: "pool_%s_space_utilization",
444 Title: "Ceph Pool Space Utilization",
445 Fam: "pool",
446 Units: "percent",
447 Ctx: "ceph.pool_space_utilization",
448 Type: collectorapi.Area,
449 Priority: prioPoolSpaceUtilization,
450 Dims: collectorapi.Dims{
451 {ID: "pool_%s_space_utilization", Name: "utilization", Div: precision},
452 },
453 }
454 poolSpaceUsageChartTmpl = collectorapi.Chart{
455 ID: "pool_%s_space_usage",
456 Title: "Ceph Pool Space Usage",
457 Fam: "pool",
458 Units: "bytes",
459 Ctx: "ceph.pool_space_usage",
460 Type: collectorapi.Stacked,
461 Priority: prioPoolSpaceUsage,
462 Dims: collectorapi.Dims{
463 {ID: "pool_%s_space_avail_bytes", Name: "avail"},
464 {ID: "pool_%s_space_used_bytes", Name: "used"},
465 },
466 }
467 poolObjectsCountChartTmpl = collectorapi.Chart{
468 ID: "pool_%s_objects_count",
469 Title: "Ceph Pool Objects",
470 Fam: "pool",
471 Units: "objects",
472 Ctx: "ceph.pool_objects_count",
473 Type: collectorapi.Line,
474 Priority: prioPoolObjectsCount,
475 Dims: collectorapi.Dims{
476 {ID: "pool_%s_objects", Name: "objects"},
477 },
478 }
479 poolIOChartTmpl = collectorapi.Chart{
480 ID: "pool_%s_io",
481 Title: "Ceph Pool IO",
482 Fam: "pool",
483 Units: "bytes/s",
484 Ctx: "ceph.pool_io",
485 Type: collectorapi.Area,
486 Priority: prioPoolIO,
487 Dims: collectorapi.Dims{
488 {ID: "pool_%s_read_bytes", Name: "read", Algo: collectorapi.Incremental},
489 {ID: "pool_%s_written_bytes", Name: "written", Algo: collectorapi.Incremental, Mul: -1},
490 },
491 }
492 poolIOPSChartTmpl = collectorapi.Chart{
493 ID: "pool_%s_iops",
494 Title: "Ceph Pool IOPS",
495 Fam: "pool",
496 Units: "ops/s",
497 Ctx: "ceph.pool_iops",
498 Type: collectorapi.Line,
499 Priority: prioPoolIOPS,
500 Dims: collectorapi.Dims{
501 {ID: "pool_%s_read_ops", Name: "read", Algo: collectorapi.Incremental},
502 {ID: "pool_%s_write_ops", Name: "write", Algo: collectorapi.Incremental, Mul: -1},
503 },
504 }
505 )
506
507 func (c *Collector) addClusterCharts() {
508 charts := clusterCharts.Copy()
509
510 for _, chart := range *charts {
511 chart.Labels = []collectorapi.Label{
512 {Key: "fsid", Value: c.fsid},
513 }
514 }
515
516 if err := c.Charts().Add(*charts...); err != nil {
517 c.Warning(err)
518 }
519 }
520
521 func (c *Collector) addOsdCharts(osdUuid, devClass, osdName string) {
522 charts := osdChartsTmpl.Copy()
523
524 for _, chart := range *charts {
525 chart.ID = fmt.Sprintf(chart.ID, osdUuid)
526 chart.ID = cleanChartID(chart.ID)
527 chart.Labels = []collectorapi.Label{
528 {Key: "fsid", Value: c.fsid},
529 {Key: "osd_uuid", Value: osdUuid},
530 {Key: "osd_name", Value: osdName},
531 {Key: "device_class", Value: devClass},
532 }
533 for _, dim := range chart.Dims {
534 dim.ID = fmt.Sprintf(dim.ID, osdUuid)
535 }
536 }
537
538 if err := c.Charts().Add(*charts...); err != nil {
539 c.Warning(err)
540 }
541 }
542
543 func (c *Collector) addPoolCharts(poolName string) {
544 charts := poolChartsTmpl.Copy()
545
546 for _, chart := range *charts {
547 chart.ID = fmt.Sprintf(chart.ID, poolName)
548 chart.ID = cleanChartID(chart.ID)
549 chart.Labels = []collectorapi.Label{
550 {Key: "fsid", Value: c.fsid},
551 {Key: "pool_name", Value: poolName},
552 }
553 for _, dim := range chart.Dims {
554 dim.ID = fmt.Sprintf(dim.ID, poolName)
555 }
556 }
557
558 if err := c.Charts().Add(*charts...); err != nil {
559 c.Warning(err)
560 }
561 }
562
563 func (c *Collector) removeCharts(prefix string) {
564 prefix = cleanChartID(prefix)
565 for _, chart := range *c.Charts() {
566 if strings.HasPrefix(chart.ID, prefix) {
567 chart.MarkRemove()
568 chart.MarkNotCreated()
569 }
570 }
571 }
572
573 func cleanChartID(id string) string {
574 r := strings.NewReplacer(".", "_", " ", "_")
575 return strings.ToLower(r.Replace(id))
576 }