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  }