| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | package ceph |
| 4 | |
| 5 | import ( |
| 6 | "strings" |
| 7 | |
| 8 | "github.com/netdata/netdata/go/plugins/pkg/web" |
| 9 | ) |
| 10 | |
| 11 | func (c *Collector) collectHealth(mx map[string]int64) error { |
| 12 | req, err := web.NewHTTPRequestWithPath(c.RequestConfig, urlPathApiHealthMinimal) |
| 13 | if err != nil { |
| 14 | return err |
| 15 | } |
| 16 | |
| 17 | req.Header.Set("Accept", hdrAcceptVersion) |
| 18 | req.Header.Set("Content-Type", hdrContentTypeJson) |
| 19 | req.Header.Set("Authorization", "Bearer "+c.token) |
| 20 | |
| 21 | var resp apiHealthMinimalResponse |
| 22 | |
| 23 | if err := c.webClient().RequestJSON(req, &resp); err != nil { |
| 24 | return err |
| 25 | } |
| 26 | |
| 27 | for _, v := range []string{"health_err", "health_warn", "health_ok"} { |
| 28 | mx[v] = 0 |
| 29 | } |
| 30 | mx[strings.ToLower(resp.Health.Status)] = 1 |
| 31 | |
| 32 | mx["mgr_active_num"] = 0 |
| 33 | if resp.MgrMap.ActiveName != "" { |
| 34 | mx["mgr_active_num"] = 1 |
| 35 | } |
| 36 | mx["mgr_standby_num"] = int64(len(resp.MgrMap.Standbys)) |
| 37 | mx["hosts_num"] = resp.Hosts |
| 38 | mx["rgw_num"] = resp.Rgw |
| 39 | mx["monitors_num"] = int64(len(resp.MonStatus.MonMap.Mons)) |
| 40 | mx["osds_num"] = int64(len(resp.OsdMap.Osds)) |
| 41 | |
| 42 | for _, v := range []string{"up", "down", "in", "out"} { |
| 43 | mx["osds_"+v+"_num"] = 0 |
| 44 | } |
| 45 | for _, v := range resp.OsdMap.Osds { |
| 46 | s := map[int64]string{0: "out", 1: "in"} |
| 47 | mx["osds_"+s[v.In]+"_num"]++ |
| 48 | |
| 49 | s = map[int64]string{0: "down", 1: "up"} |
| 50 | mx["osds_"+s[v.Up]+"_num"]++ |
| 51 | } |
| 52 | |
| 53 | mx["pools_num"] = int64(len(resp.Pools)) |
| 54 | mx["iscsi_daemons_num"] = resp.IscsiDaemons.Up + resp.IscsiDaemons.Down |
| 55 | mx["iscsi_daemons_up_num"] = resp.IscsiDaemons.Up |
| 56 | mx["iscsi_daemons_down_num"] = resp.IscsiDaemons.Down |
| 57 | |
| 58 | df := resp.Df.Stats |
| 59 | mx["raw_capacity_used_bytes"] = df.TotalBytes - df.TotalAvailBytes |
| 60 | mx["raw_capacity_avail_bytes"] = df.TotalAvailBytes |
| 61 | mx["raw_capacity_utilization"] = 0 |
| 62 | if df.TotalAvailBytes > 0 { |
| 63 | mx["raw_capacity_utilization"] = int64(float64(df.TotalBytes-df.TotalAvailBytes) / float64(df.TotalBytes) * 100 * precision) |
| 64 | } |
| 65 | |
| 66 | objs := resp.PgInfo.ObjectStats |
| 67 | mx["objects_num"] = objs.NumObjects |
| 68 | mx["objects_healthy_num"] = objs.NumObjects - (objs.NumObjectsMisplaced + objs.NumObjectsDegraded + objs.NumObjectsUnfound) |
| 69 | mx["objects_misplaced_num"] = objs.NumObjectsMisplaced |
| 70 | mx["objects_degraded_num"] = objs.NumObjectsDegraded |
| 71 | mx["objects_unfound_num"] = objs.NumObjectsUnfound |
| 72 | mx["pgs_per_osd"] = int64(resp.PgInfo.PgsPerOsd) |
| 73 | |
| 74 | mx["pgs_num"] = 0 |
| 75 | for _, v := range []string{"clean", "working", "warning", "unknown"} { |
| 76 | mx["pg_status_category_"+v] = 0 |
| 77 | } |
| 78 | for k, v := range resp.PgInfo.Statuses { |
| 79 | mx["pg_status_category_"+pgStatusCategory(k)] += v |
| 80 | mx["pgs_num"] += v |
| 81 | } |
| 82 | |
| 83 | perf := resp.ClientPerf |
| 84 | mx["client_perf_read_bytes_sec"] = int64(perf.ReadBytesSec) |
| 85 | mx["client_perf_read_op_per_sec"] = int64(perf.ReadOpPerSec) |
| 86 | mx["client_perf_write_bytes_sec"] = int64(perf.WriteBytesSec) |
| 87 | mx["client_perf_write_op_per_sec"] = int64(perf.WriteOpPerSec) |
| 88 | mx["client_perf_recovering_bytes_per_sec"] = int64(perf.RecoveringBytesPerSec) |
| 89 | |
| 90 | for _, v := range []string{"disabled", "active", "inactive"} { |
| 91 | mx["scrub_status_"+v] = 0 |
| 92 | } |
| 93 | mx["scrub_status_"+strings.ToLower(resp.ScrubStatus)] = 1 |
| 94 | |
| 95 | return nil |
| 96 | } |
| 97 | |
| 98 | func pgStatusCategory(status string) string { |
| 99 | // 'status' is formated as 'status1+status2+...+statusN' |
| 100 | |
| 101 | states := strings.Split(status, "+") |
| 102 | |
| 103 | var clean, working, warning, unknown int |
| 104 | |
| 105 | for _, s := range states { |
| 106 | switch s { |
| 107 | case "active", "clean": |
| 108 | clean++ |
| 109 | case "activating", |
| 110 | "backfill_wait", |
| 111 | "backfilling", |
| 112 | "creating", |
| 113 | "deep", |
| 114 | "degraded", |
| 115 | "forced_backfill", |
| 116 | "forced_recovery", |
| 117 | "peering", |
| 118 | "peered", |
| 119 | "recovering", |
| 120 | "recovery_wait", |
| 121 | "repair", |
| 122 | "scrubbing", |
| 123 | "snaptrim", |
| 124 | "snaptrim_wait": |
| 125 | working++ |
| 126 | case "backfill_toofull", |
| 127 | "backfill_unfound", |
| 128 | "down", |
| 129 | "incomplete", |
| 130 | "inconsistent", |
| 131 | "recovery_toofull", |
| 132 | "recovery_unfound", |
| 133 | "remapped", |
| 134 | "snaptrim_error", |
| 135 | "stale", |
| 136 | "undersized": |
| 137 | warning++ |
| 138 | default: |
| 139 | unknown++ |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | switch { |
| 144 | case warning > 0: |
| 145 | return "warning" |
| 146 | case unknown > 0: |
| 147 | return "unknown" |
| 148 | case working > 0: |
| 149 | return "working" |
| 150 | case clean > 0: |
| 151 | return "clean" |
| 152 | default: |
| 153 | return "unknown" |
| 154 | } |
| 155 | } |