Refactor health data structure (#11914)

This feature comes with simplified data structures and versioning support.

Signed-off-by: Bala.FA <bala.gluster@gmail.com>
This commit is contained in:
Bala FA
2021-06-01 21:25:49 +05:30
committed by GitHub
parent 8347db8be3
commit 120951d9e9
9 changed files with 368 additions and 829 deletions
+69 -303
View File
@@ -19,329 +19,95 @@ package cmd
import (
"context"
"fmt"
"net/http"
"math"
"os"
"sync"
"syscall"
"github.com/minio/madmin-go"
"github.com/minio/minio/pkg/disk"
cpuhw "github.com/shirou/gopsutil/v3/cpu"
memhw "github.com/shirou/gopsutil/v3/mem"
"github.com/shirou/gopsutil/v3/process"
)
func getLocalCPUInfo(ctx context.Context, r *http.Request) madmin.ServerCPUInfo {
addr := r.Host
if globalIsDistErasure {
addr = globalLocalNodeName
}
info, err := cpuhw.InfoWithContext(ctx)
if err != nil {
return madmin.ServerCPUInfo{
Addr: addr,
Error: fmt.Sprintf("info: %v", err),
}
}
time, err := cpuhw.TimesWithContext(ctx, false)
if err != nil {
return madmin.ServerCPUInfo{
Addr: addr,
Error: fmt.Sprintf("times: %v", err),
}
}
return madmin.ServerCPUInfo{
Addr: addr,
CPUStat: info,
TimeStat: time,
// round returns value rounding to specified decimal places.
func round(f float64, n int) float64 {
if n <= 0 {
return math.Round(f)
}
p := math.Pow10(n)
return math.Round(f*p) / p
}
func getLocalDrives(ctx context.Context, parallel bool, endpointServerPools EndpointServerPools, r *http.Request) madmin.ServerDrivesInfo {
var drivesPerfInfo []madmin.DrivePerfInfo
func getDrivePerfInfo(ctx context.Context, parallel bool) []madmin.DrivePerfInfo {
pools := globalEndpoints
info := []madmin.DrivePerfInfo{}
var wg sync.WaitGroup
for _, ep := range endpointServerPools {
for _, endpoint := range ep.Endpoints {
// Only proceed for local endpoints
if endpoint.IsLocal {
if _, err := os.Stat(endpoint.Path); err != nil {
// Since this drive is not available, add relevant details and proceed
drivesPerfInfo = append(drivesPerfInfo, madmin.DrivePerfInfo{
Path: endpoint.Path,
Error: fmt.Sprintf("stat: %v", err),
})
continue
}
measurePath := pathJoin(minioMetaTmpBucket, mustGetUUID())
measure := func(path string) {
defer wg.Done()
driveInfo := madmin.DrivePerfInfo{
Path: path,
}
latency, throughput, err := disk.GetHealthInfo(ctx, path, pathJoin(path, measurePath))
if err != nil {
driveInfo.Error = fmt.Sprintf("health-info: %v", err)
} else {
driveInfo.Latency = latency
driveInfo.Throughput = throughput
}
drivesPerfInfo = append(drivesPerfInfo, driveInfo)
}
wg.Add(1)
for _, pool := range pools {
for _, endpoint := range pool.Endpoints {
if !endpoint.IsLocal {
continue
}
if parallel {
go measure(endpoint.Path)
if _, err := os.Stat(endpoint.Path); err != nil {
info = append(info, madmin.DrivePerfInfo{
Path: endpoint.Path,
Error: err.Error(),
})
continue
}
getHealthInfo := func(path string) {
defer wg.Done()
latency, throughput, err := disk.GetHealthInfo(
ctx, path, pathJoin(path, minioMetaTmpBucket, mustGetUUID()),
)
if err != nil {
info = append(info, madmin.DrivePerfInfo{
Path: path,
Error: err.Error(),
})
} else {
measure(endpoint.Path)
info = append(info, madmin.DrivePerfInfo{
Path: path,
Latency: madmin.Latency{
Avg: round(latency.Avg, 3),
Max: round(latency.Max, 3),
Min: round(latency.Min, 3),
Percentile50: round(latency.Percentile50, 3),
Percentile90: round(latency.Percentile90, 3),
Percentile99: round(latency.Percentile99, 3),
},
Throughput: madmin.Throughput{
Avg: uint64(round(throughput.Avg, 0)),
Max: uint64(round(throughput.Max, 0)),
Min: uint64(round(throughput.Min, 0)),
Percentile50: uint64(round(throughput.Percentile50, 0)),
Percentile90: uint64(round(throughput.Percentile90, 0)),
Percentile99: uint64(round(throughput.Percentile99, 0)),
},
})
}
}
wg.Add(1)
if parallel {
go getHealthInfo(endpoint.Path)
} else {
getHealthInfo(endpoint.Path)
}
}
}
wg.Wait()
addr := r.Host
if globalIsDistErasure {
addr = globalLocalNodeName
}
if parallel {
return madmin.ServerDrivesInfo{
Addr: addr,
Parallel: drivesPerfInfo,
}
}
return madmin.ServerDrivesInfo{
Addr: addr,
Serial: drivesPerfInfo,
}
return info
}
func getLocalMemInfo(ctx context.Context, r *http.Request) madmin.ServerMemInfo {
addr := r.Host
if globalIsDistErasure {
addr = globalLocalNodeName
}
swap, err := memhw.SwapMemoryWithContext(ctx)
if err != nil {
return madmin.ServerMemInfo{
Addr: addr,
Error: fmt.Sprintf("swap: %v", err),
}
}
vm, err := memhw.VirtualMemoryWithContext(ctx)
if err != nil {
return madmin.ServerMemInfo{
Addr: addr,
Error: fmt.Sprintf("virtual-mem: %v", err),
}
}
return madmin.ServerMemInfo{
Addr: addr,
SwapMem: swap,
VirtualMem: vm,
}
}
func getLocalProcInfo(ctx context.Context, r *http.Request) madmin.ServerProcInfo {
addr := r.Host
if globalIsDistErasure {
addr = globalLocalNodeName
}
errProcInfo := func(tag string, err error) madmin.ServerProcInfo {
return madmin.ServerProcInfo{
Addr: addr,
Error: fmt.Sprintf("%s: %v", tag, err),
}
}
selfPid := int32(syscall.Getpid())
self, err := process.NewProcess(selfPid)
if err != nil {
return errProcInfo("new-process", err)
}
processes := []*process.Process{self}
sysProcs := []madmin.SysProcess{}
for _, proc := range processes {
sysProc := madmin.SysProcess{}
sysProc.Pid = proc.Pid
bg, err := proc.BackgroundWithContext(ctx)
if err != nil {
return errProcInfo("background", err)
}
sysProc.Background = bg
cpuPercent, err := proc.CPUPercentWithContext(ctx)
if err != nil {
return errProcInfo("cpu-percent", err)
}
sysProc.CPUPercent = cpuPercent
children, _ := proc.ChildrenWithContext(ctx)
for _, c := range children {
sysProc.Children = append(sysProc.Children, c.Pid)
}
cmdLine, err := proc.CmdlineWithContext(ctx)
if err != nil {
return errProcInfo("cmdline", err)
}
sysProc.CmdLine = cmdLine
conns, err := proc.ConnectionsWithContext(ctx)
if err != nil {
return errProcInfo("conns", err)
}
sysProc.ConnectionCount = len(conns)
createTime, err := proc.CreateTimeWithContext(ctx)
if err != nil {
return errProcInfo("create-time", err)
}
sysProc.CreateTime = createTime
cwd, err := proc.CwdWithContext(ctx)
if err != nil {
return errProcInfo("cwd", err)
}
sysProc.Cwd = cwd
exe, err := proc.ExeWithContext(ctx)
if err != nil {
return errProcInfo("exe", err)
}
sysProc.Exe = exe
gids, err := proc.GidsWithContext(ctx)
if err != nil {
return errProcInfo("gids", err)
}
sysProc.Gids = gids
ioCounters, err := proc.IOCountersWithContext(ctx)
if err != nil {
return errProcInfo("iocounters", err)
}
sysProc.IOCounters = ioCounters
isRunning, err := proc.IsRunningWithContext(ctx)
if err != nil {
return errProcInfo("is-running", err)
}
sysProc.IsRunning = isRunning
memInfo, err := proc.MemoryInfoWithContext(ctx)
if err != nil {
return errProcInfo("mem-info", err)
}
sysProc.MemInfo = memInfo
memMaps, err := proc.MemoryMapsWithContext(ctx, true)
if err != nil {
return errProcInfo("mem-maps", err)
}
sysProc.MemMaps = memMaps
memPercent, err := proc.MemoryPercentWithContext(ctx)
if err != nil {
return errProcInfo("mem-percent", err)
}
sysProc.MemPercent = memPercent
name, err := proc.NameWithContext(ctx)
if err != nil {
return errProcInfo("name", err)
}
sysProc.Name = name
// Refer for more information on NetIOCounters
// is useless https://github.com/shirou/gopsutil/issues/429
nice, err := proc.NiceWithContext(ctx)
if err != nil {
return errProcInfo("nice", err)
}
sysProc.Nice = nice
numCtxSwitches, err := proc.NumCtxSwitchesWithContext(ctx)
if err != nil {
return errProcInfo("num-ctx-switches", err)
}
sysProc.NumCtxSwitches = numCtxSwitches
numFds, err := proc.NumFDsWithContext(ctx)
if err != nil {
return errProcInfo("num-fds", err)
}
sysProc.NumFds = numFds
numThreads, err := proc.NumThreadsWithContext(ctx)
if err != nil {
return errProcInfo("num-threads", err)
}
sysProc.NumThreads = numThreads
pageFaults, err := proc.PageFaultsWithContext(ctx)
if err != nil {
return errProcInfo("page-faults", err)
}
sysProc.PageFaults = pageFaults
parent, err := proc.ParentWithContext(ctx)
if err == nil {
sysProc.Parent = parent.Pid
}
ppid, err := proc.PpidWithContext(ctx)
if err == nil {
sysProc.Ppid = ppid
}
status, err := proc.StatusWithContext(ctx)
if err != nil {
return errProcInfo("status", err)
}
sysProc.Status = status[0]
tgid, err := proc.Tgid()
if err != nil {
return errProcInfo("tgid", err)
}
sysProc.Tgid = tgid
times, err := proc.TimesWithContext(ctx)
if err != nil {
return errProcInfo("times", err)
}
sysProc.Times = times
uids, err := proc.UidsWithContext(ctx)
if err != nil {
return errProcInfo("uids", err)
}
sysProc.Uids = uids
username, err := proc.UsernameWithContext(ctx)
if err != nil {
return errProcInfo("username", err)
}
sysProc.Username = username
sysProcs = append(sysProcs, sysProc)
}
return madmin.ServerProcInfo{
Addr: addr,
Processes: sysProcs,
func getDrivePerfInfos(ctx context.Context, addr string) madmin.DrivePerfInfos {
serialPerf := getDrivePerfInfo(ctx, false)
parallelPerf := getDrivePerfInfo(ctx, true)
return madmin.DrivePerfInfos{
Addr: addr,
SerialPerf: serialPerf,
ParallelPerf: parallelPerf,
}
}