github.com/minio/mc@v0.0.0-20240503112107-b471de8d1882/cmd/health_v1.go (about) 1 // Copyright (c) 2015-2022 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "encoding/json" 22 "reflect" 23 "sync" 24 "time" 25 26 "github.com/minio/madmin-go/v3" 27 "github.com/minio/mc/pkg/probe" 28 "github.com/minio/minio-go/v7/pkg/set" 29 "github.com/shirou/gopsutil/v3/cpu" 30 "github.com/shirou/gopsutil/v3/disk" 31 "github.com/shirou/gopsutil/v3/mem" 32 ) 33 34 // HwServersV1 - hardware health Info 35 type HwServersV1 struct { 36 Servers []HwServerV1 `json:"servers,omitempty"` 37 } 38 39 // HwServerV1 - server health Info 40 type HwServerV1 struct { 41 Addr string `json:"addr"` 42 CPUs []HwCPUV1 `json:"cpus,omitempty"` 43 Drives []HwDriveV1 `json:"drives,omitempty"` 44 MemInfo HwMemV1 `json:"meminfo,omitempty"` 45 Perf HwPerfV1 `json:"perf,omitempty"` 46 } 47 48 // HwCPUV1 - CPU Info 49 type HwCPUV1 struct { 50 CPUStat []cpu.InfoStat `json:"cpu,omitempty"` 51 TimesStat []cpu.TimesStat `json:"time,omitempty"` 52 Error string `json:"error,omitempty"` 53 } 54 55 // HwDriveV1 - Drive info 56 type HwDriveV1 struct { 57 Counters map[string]disk.IOCountersStat `json:"counters,omitempty"` 58 Partitions []madmin.PartitionStat `json:"partitions,omitempty"` 59 Usage []*disk.UsageStat `json:"usage,omitempty"` 60 Error string `json:"error,omitempty"` 61 } 62 63 // HwMemV1 - Includes host virtual and swap mem information 64 type HwMemV1 struct { 65 SwapMem *mem.SwapMemoryStat `json:"swap,omitempty"` 66 VirtualMem *mem.VirtualMemoryStat `json:"virtualmem,omitempty"` 67 Error string `json:"error,omitempty"` 68 } 69 70 // HwPerfV1 - hardware performance 71 type HwPerfV1 struct { 72 Net HwNetPerfV1 `json:"net,omitempty"` 73 Drive HwDrivePerfV1 `json:"drives,omitempty"` 74 } 75 76 // HwNetPerfV1 - Network performance info 77 type HwNetPerfV1 struct { 78 Serial []madmin.NetPerfInfoV0 `json:"serial,omitempty"` 79 Parallel []madmin.NetPerfInfoV0 `json:"parallel,omitempty"` 80 } 81 82 // HwDrivePerfV1 - Network performance info 83 type HwDrivePerfV1 struct { 84 Serial []madmin.DrivePerfInfoV0 `json:"serial,omitempty"` 85 Parallel []madmin.DrivePerfInfoV0 `json:"parallel,omitempty"` 86 Error string `json:"error,omitempty"` 87 } 88 89 // SwInfoV1 - software health Info 90 type SwInfoV1 struct { 91 Minio MinioHealthInfoV1 `json:"minio,omitempty"` 92 OsInfo []madmin.ServerOsInfo `json:"osinfos,omitempty"` 93 } 94 95 // MinioHealthInfoV1 - Health info of the MinIO cluster 96 type MinioHealthInfoV1 struct { 97 Info madmin.InfoMessage `json:"info,omitempty"` 98 Config interface{} `json:"config,omitempty"` 99 ProcInfo []madmin.ServerProcInfo `json:"procinfos,omitempty"` 100 Error string `json:"error,omitempty"` 101 } 102 103 // ClusterHealthV1 - main struct of the health report 104 type ClusterHealthV1 struct { 105 TimeStamp time.Time `json:"timestamp,omitempty"` 106 Status string `json:"status"` 107 Error string `json:"error,omitempty"` 108 Hardware HwServersV1 `json:"hardware,omitempty"` 109 Software SwInfoV1 `json:"software,omitempty"` 110 } 111 112 func (ch ClusterHealthV1) String() string { 113 return ch.JSON() 114 } 115 116 // JSON jsonifies service status message. 117 func (ch ClusterHealthV1) JSON() string { 118 statusJSONBytes, e := json.MarshalIndent(ch, " ", " ") 119 fatalIf(probe.NewError(e), "Unable to marshal into JSON.") 120 121 return string(statusJSONBytes) 122 } 123 124 // GetStatus - return status of the health info 125 func (ch ClusterHealthV1) GetStatus() string { 126 return ch.Status 127 } 128 129 // GetError - return error from the health info 130 func (ch ClusterHealthV1) GetError() string { 131 return ch.Error 132 } 133 134 // GetTimestamp - return timestamp from the health info 135 func (ch ClusterHealthV1) GetTimestamp() time.Time { 136 return ch.TimeStamp 137 } 138 139 // MapHealthInfoToV1 - maps the health info returned by minio server to V1 format 140 func MapHealthInfoToV1(healthInfo madmin.HealthInfoV0, err error) ClusterHealthV1 { 141 ch := ClusterHealthV1{} 142 ch.TimeStamp = healthInfo.TimeStamp 143 if err != nil { 144 ch.Status = "Error" 145 ch.Error = err.Error() 146 return ch 147 } 148 149 ch.Status = "Success" 150 151 serverAddrs := set.NewStringSet() 152 153 var serverCPUs map[string][]HwCPUV1 154 var serverDrives map[string][]HwDriveV1 155 var serverMems map[string]HwMemV1 156 var serverNetPerfSerial, serverNetPerfParallel map[string][]madmin.NetPerfInfoV0 157 var serverDrivePerf map[string]HwDrivePerfV1 158 159 mapCPUs := func() { serverCPUs = mapServerCPUs(healthInfo) } 160 mapDrives := func() { serverDrives = mapServerDrives(healthInfo) } 161 mapMems := func() { serverMems = mapServerMems(healthInfo) } 162 mapNetPerf := func() { serverNetPerfSerial, serverNetPerfParallel = mapServerNetPerf(healthInfo) } 163 mapDrivePerf := func() { serverDrivePerf = mapServerDrivePerf(healthInfo) } 164 165 parallelize(mapCPUs, mapDrives, mapMems, mapNetPerf, mapDrivePerf) 166 167 addKeysToSet(reflect.ValueOf(serverCPUs).MapKeys(), &serverAddrs) 168 addKeysToSet(reflect.ValueOf(serverDrives).MapKeys(), &serverAddrs) 169 addKeysToSet(reflect.ValueOf(serverMems).MapKeys(), &serverAddrs) 170 addKeysToSet(reflect.ValueOf(serverNetPerfSerial).MapKeys(), &serverAddrs) 171 if len(healthInfo.Perf.NetParallel.Addr) > 0 { 172 serverAddrs.Add(healthInfo.Perf.NetParallel.Addr) 173 } 174 addKeysToSet(reflect.ValueOf(serverDrivePerf).MapKeys(), &serverAddrs) 175 176 // Merge hardware info 177 hw := HwServersV1{Servers: []HwServerV1{}} 178 for addr := range serverAddrs { 179 perf := HwPerfV1{ 180 Net: HwNetPerfV1{ 181 Serial: serverNetPerfSerial[addr], 182 Parallel: serverNetPerfParallel[addr], 183 }, 184 Drive: serverDrivePerf[addr], 185 } 186 hw.Servers = append(hw.Servers, HwServerV1{ 187 Addr: addr, 188 CPUs: serverCPUs[addr], 189 Drives: serverDrives[addr], 190 MemInfo: serverMems[addr], 191 Perf: perf, 192 }) 193 } 194 195 ch.Hardware = hw 196 197 ch.Software = SwInfoV1{ 198 Minio: MinioHealthInfoV1{ 199 Info: healthInfo.Minio.Info, 200 Config: healthInfo.Minio.Config, 201 Error: healthInfo.Minio.Error, 202 ProcInfo: healthInfo.Sys.ProcInfo, 203 }, 204 OsInfo: healthInfo.Sys.OsInfo, 205 } 206 return ch 207 } 208 209 func parallelize(functions ...func()) { 210 var waitGroup sync.WaitGroup 211 waitGroup.Add(len(functions)) 212 213 defer waitGroup.Wait() 214 215 for _, function := range functions { 216 go func(fn func()) { 217 defer waitGroup.Done() 218 fn() 219 }(function) 220 } 221 } 222 223 func addKeysToSet(input []reflect.Value, output *set.StringSet) { 224 for _, key := range input { 225 output.Add(key.String()) 226 } 227 } 228 229 func mapServerCPUs(healthInfo madmin.HealthInfoV0) map[string][]HwCPUV1 { 230 serverCPUs := map[string][]HwCPUV1{} 231 for _, ci := range healthInfo.Sys.CPUInfo { 232 cpus, ok := serverCPUs[ci.Addr] 233 if !ok { 234 cpus = []HwCPUV1{} 235 } 236 cpus = append(cpus, HwCPUV1{ 237 CPUStat: ci.CPUStat, 238 TimesStat: ci.TimeStat, 239 Error: ci.Error, 240 }) 241 serverCPUs[ci.Addr] = cpus 242 } 243 return serverCPUs 244 } 245 246 func mapServerDrives(healthInfo madmin.HealthInfoV0) map[string][]HwDriveV1 { 247 serverDrives := map[string][]HwDriveV1{} 248 for _, di := range healthInfo.Sys.DiskHwInfo { 249 drives, ok := serverDrives[di.Addr] 250 if !ok { 251 drives = []HwDriveV1{} 252 } 253 drives = append(drives, HwDriveV1{ 254 Counters: di.Counters, 255 Partitions: di.Partitions, 256 Usage: di.Usage, 257 Error: di.Error, 258 }) 259 serverDrives[di.Addr] = drives 260 } 261 return serverDrives 262 } 263 264 func mapServerMems(healthInfo madmin.HealthInfoV0) map[string]HwMemV1 { 265 serverMems := map[string]HwMemV1{} 266 for _, mi := range healthInfo.Sys.MemInfo { 267 serverMems[mi.Addr] = HwMemV1{ 268 SwapMem: mi.SwapMem, 269 VirtualMem: mi.VirtualMem, 270 Error: mi.Error, 271 } 272 } 273 return serverMems 274 } 275 276 func mapServerNetPerf(healthInfo madmin.HealthInfoV0) (map[string][]madmin.NetPerfInfoV0, map[string][]madmin.NetPerfInfoV0) { 277 snpSerial := map[string][]madmin.NetPerfInfoV0{} 278 for _, serverPerf := range healthInfo.Perf.Net { 279 snpSerial[serverPerf.Addr] = serverPerf.Net 280 } 281 282 snpParallel := map[string][]madmin.NetPerfInfoV0{} 283 snpParallel[healthInfo.Perf.NetParallel.Addr] = healthInfo.Perf.NetParallel.Net 284 285 return snpSerial, snpParallel 286 } 287 288 func mapServerDrivePerf(healthInfo madmin.HealthInfoV0) map[string]HwDrivePerfV1 { 289 sdp := map[string]HwDrivePerfV1{} 290 for _, drivePerf := range healthInfo.Perf.DriveInfo { 291 sdp[drivePerf.Addr] = HwDrivePerfV1{ 292 Serial: drivePerf.Serial, 293 Parallel: drivePerf.Parallel, 294 Error: drivePerf.Error, 295 } 296 } 297 return sdp 298 }