2021-04-18 15:41:13 -04:00
|
|
|
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
|
|
//
|
|
|
|
// This file is part of MinIO Object Storage stack
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU Affero General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2020-03-27 00:07:39 -04:00
|
|
|
|
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2021-02-05 15:37:15 -05:00
|
|
|
"fmt"
|
2020-03-27 00:07:39 -04:00
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"sync"
|
|
|
|
"syscall"
|
|
|
|
|
|
|
|
"github.com/minio/minio/pkg/disk"
|
|
|
|
"github.com/minio/minio/pkg/madmin"
|
2021-03-01 03:15:46 -05:00
|
|
|
cpuhw "github.com/shirou/gopsutil/v3/cpu"
|
|
|
|
memhw "github.com/shirou/gopsutil/v3/mem"
|
|
|
|
"github.com/shirou/gopsutil/v3/process"
|
2020-03-27 00:07:39 -04:00
|
|
|
)
|
|
|
|
|
2020-11-20 15:52:53 -05:00
|
|
|
func getLocalCPUInfo(ctx context.Context, r *http.Request) madmin.ServerCPUInfo {
|
2020-05-18 12:59:45 -04:00
|
|
|
addr := r.Host
|
2020-06-12 23:04:01 -04:00
|
|
|
if globalIsDistErasure {
|
2021-03-26 14:37:58 -04:00
|
|
|
addr = globalLocalNodeName
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
info, err := cpuhw.InfoWithContext(ctx)
|
|
|
|
if err != nil {
|
2020-11-20 15:52:53 -05:00
|
|
|
return madmin.ServerCPUInfo{
|
2020-03-27 00:07:39 -04:00
|
|
|
Addr: addr,
|
2021-02-05 15:37:15 -05:00
|
|
|
Error: fmt.Sprintf("info: %v", err),
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
time, err := cpuhw.TimesWithContext(ctx, false)
|
|
|
|
if err != nil {
|
2020-11-20 15:52:53 -05:00
|
|
|
return madmin.ServerCPUInfo{
|
2020-03-27 00:07:39 -04:00
|
|
|
Addr: addr,
|
2021-02-05 15:37:15 -05:00
|
|
|
Error: fmt.Sprintf("times: %v", err),
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-20 15:52:53 -05:00
|
|
|
return madmin.ServerCPUInfo{
|
2020-03-27 00:07:39 -04:00
|
|
|
Addr: addr,
|
|
|
|
CPUStat: info,
|
|
|
|
TimeStat: time,
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-12-01 16:50:33 -05:00
|
|
|
func getLocalDrives(ctx context.Context, parallel bool, endpointServerPools EndpointServerPools, r *http.Request) madmin.ServerDrivesInfo {
|
2020-11-20 15:52:53 -05:00
|
|
|
var drivesPerfInfo []madmin.DrivePerfInfo
|
2020-05-18 12:59:45 -04:00
|
|
|
var wg sync.WaitGroup
|
2020-12-01 16:50:33 -05:00
|
|
|
for _, ep := range endpointServerPools {
|
2020-05-18 12:59:45 -04:00
|
|
|
for _, endpoint := range ep.Endpoints {
|
2020-03-27 00:07:39 -04:00
|
|
|
// 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
|
2020-11-20 15:52:53 -05:00
|
|
|
drivesPerfInfo = append(drivesPerfInfo, madmin.DrivePerfInfo{
|
2020-03-27 00:07:39 -04:00
|
|
|
Path: endpoint.Path,
|
2021-02-05 15:37:15 -05:00
|
|
|
Error: fmt.Sprintf("stat: %v", err),
|
2020-03-27 00:07:39 -04:00
|
|
|
})
|
|
|
|
continue
|
|
|
|
}
|
2020-04-12 22:37:09 -04:00
|
|
|
measurePath := pathJoin(minioMetaTmpBucket, mustGetUUID())
|
2020-05-18 12:59:45 -04:00
|
|
|
measure := func(path string) {
|
|
|
|
defer wg.Done()
|
2020-11-20 15:52:53 -05:00
|
|
|
driveInfo := madmin.DrivePerfInfo{
|
2020-05-18 12:59:45 -04:00
|
|
|
Path: path,
|
|
|
|
}
|
2020-11-20 15:52:53 -05:00
|
|
|
latency, throughput, err := disk.GetHealthInfo(ctx, path, pathJoin(path, measurePath))
|
2020-03-27 00:07:39 -04:00
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
driveInfo.Error = fmt.Sprintf("health-info: %v", err)
|
2020-05-18 12:59:45 -04:00
|
|
|
} else {
|
2020-11-20 15:52:53 -05:00
|
|
|
driveInfo.Latency = latency
|
|
|
|
driveInfo.Throughput = throughput
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
2020-11-20 15:52:53 -05:00
|
|
|
drivesPerfInfo = append(drivesPerfInfo, driveInfo)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
wg.Add(1)
|
|
|
|
|
|
|
|
if parallel {
|
2020-05-18 12:59:45 -04:00
|
|
|
go measure(endpoint.Path)
|
2020-03-27 00:07:39 -04:00
|
|
|
} else {
|
2020-05-18 12:59:45 -04:00
|
|
|
measure(endpoint.Path)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
wg.Wait()
|
2020-06-12 23:04:01 -04:00
|
|
|
|
2020-05-18 12:59:45 -04:00
|
|
|
addr := r.Host
|
2020-06-12 23:04:01 -04:00
|
|
|
if globalIsDistErasure {
|
2021-03-26 14:37:58 -04:00
|
|
|
addr = globalLocalNodeName
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
if parallel {
|
2020-11-20 15:52:53 -05:00
|
|
|
return madmin.ServerDrivesInfo{
|
2020-03-27 00:07:39 -04:00
|
|
|
Addr: addr,
|
2020-11-20 15:52:53 -05:00
|
|
|
Parallel: drivesPerfInfo,
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
}
|
2020-11-20 15:52:53 -05:00
|
|
|
return madmin.ServerDrivesInfo{
|
2020-03-27 00:07:39 -04:00
|
|
|
Addr: addr,
|
2020-11-20 15:52:53 -05:00
|
|
|
Serial: drivesPerfInfo,
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-20 15:52:53 -05:00
|
|
|
func getLocalMemInfo(ctx context.Context, r *http.Request) madmin.ServerMemInfo {
|
2020-05-18 12:59:45 -04:00
|
|
|
addr := r.Host
|
2020-06-12 23:04:01 -04:00
|
|
|
if globalIsDistErasure {
|
2021-03-26 14:37:58 -04:00
|
|
|
addr = globalLocalNodeName
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
swap, err := memhw.SwapMemoryWithContext(ctx)
|
|
|
|
if err != nil {
|
2020-11-20 15:52:53 -05:00
|
|
|
return madmin.ServerMemInfo{
|
2020-03-27 00:07:39 -04:00
|
|
|
Addr: addr,
|
2021-02-05 15:37:15 -05:00
|
|
|
Error: fmt.Sprintf("swap: %v", err),
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
vm, err := memhw.VirtualMemoryWithContext(ctx)
|
|
|
|
if err != nil {
|
2020-11-20 15:52:53 -05:00
|
|
|
return madmin.ServerMemInfo{
|
2020-03-27 00:07:39 -04:00
|
|
|
Addr: addr,
|
2021-02-05 15:37:15 -05:00
|
|
|
Error: fmt.Sprintf("virtual-mem: %v", err),
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-20 15:52:53 -05:00
|
|
|
return madmin.ServerMemInfo{
|
2020-03-27 00:07:39 -04:00
|
|
|
Addr: addr,
|
|
|
|
SwapMem: swap,
|
|
|
|
VirtualMem: vm,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-20 15:52:53 -05:00
|
|
|
func getLocalProcInfo(ctx context.Context, r *http.Request) madmin.ServerProcInfo {
|
2020-05-18 12:59:45 -04:00
|
|
|
addr := r.Host
|
2020-06-12 23:04:01 -04:00
|
|
|
if globalIsDistErasure {
|
2021-03-26 14:37:58 -04:00
|
|
|
addr = globalLocalNodeName
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
|
2021-02-05 15:37:15 -05:00
|
|
|
errProcInfo := func(tag string, err error) madmin.ServerProcInfo {
|
2020-11-20 15:52:53 -05:00
|
|
|
return madmin.ServerProcInfo{
|
2020-03-27 00:07:39 -04:00
|
|
|
Addr: addr,
|
2021-02-05 15:37:15 -05:00
|
|
|
Error: fmt.Sprintf("%s: %v", tag, err),
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
selfPid := int32(syscall.Getpid())
|
|
|
|
self, err := process.NewProcess(selfPid)
|
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("new-process", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
processes := []*process.Process{self}
|
|
|
|
|
2020-11-20 15:52:53 -05:00
|
|
|
sysProcs := []madmin.SysProcess{}
|
2020-03-27 00:07:39 -04:00
|
|
|
for _, proc := range processes {
|
2020-11-20 15:52:53 -05:00
|
|
|
sysProc := madmin.SysProcess{}
|
2020-03-27 00:07:39 -04:00
|
|
|
sysProc.Pid = proc.Pid
|
|
|
|
|
|
|
|
bg, err := proc.BackgroundWithContext(ctx)
|
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("background", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
sysProc.Background = bg
|
|
|
|
|
|
|
|
cpuPercent, err := proc.CPUPercentWithContext(ctx)
|
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("cpu-percent", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
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 {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("cmdline", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
sysProc.CmdLine = cmdLine
|
|
|
|
|
|
|
|
conns, err := proc.ConnectionsWithContext(ctx)
|
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("conns", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
2021-02-06 00:32:28 -05:00
|
|
|
sysProc.ConnectionCount = len(conns)
|
2020-03-27 00:07:39 -04:00
|
|
|
|
|
|
|
createTime, err := proc.CreateTimeWithContext(ctx)
|
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("create-time", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
sysProc.CreateTime = createTime
|
|
|
|
|
|
|
|
cwd, err := proc.CwdWithContext(ctx)
|
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("cwd", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
sysProc.Cwd = cwd
|
|
|
|
|
|
|
|
exe, err := proc.ExeWithContext(ctx)
|
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("exe", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
sysProc.Exe = exe
|
|
|
|
|
|
|
|
gids, err := proc.GidsWithContext(ctx)
|
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("gids", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
sysProc.Gids = gids
|
|
|
|
|
|
|
|
ioCounters, err := proc.IOCountersWithContext(ctx)
|
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("iocounters", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
sysProc.IOCounters = ioCounters
|
|
|
|
|
|
|
|
isRunning, err := proc.IsRunningWithContext(ctx)
|
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("is-running", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
sysProc.IsRunning = isRunning
|
|
|
|
|
|
|
|
memInfo, err := proc.MemoryInfoWithContext(ctx)
|
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("mem-info", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
sysProc.MemInfo = memInfo
|
|
|
|
|
|
|
|
memMaps, err := proc.MemoryMapsWithContext(ctx, true)
|
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("mem-maps", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
sysProc.MemMaps = memMaps
|
|
|
|
|
|
|
|
memPercent, err := proc.MemoryPercentWithContext(ctx)
|
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("mem-percent", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
sysProc.MemPercent = memPercent
|
|
|
|
|
|
|
|
name, err := proc.NameWithContext(ctx)
|
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("name", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
sysProc.Name = name
|
|
|
|
|
2021-03-01 03:15:46 -05:00
|
|
|
// Refer for more information on NetIOCounters
|
|
|
|
// is useless https://github.com/shirou/gopsutil/issues/429
|
2020-03-27 00:07:39 -04:00
|
|
|
|
|
|
|
nice, err := proc.NiceWithContext(ctx)
|
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("nice", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
sysProc.Nice = nice
|
|
|
|
|
|
|
|
numCtxSwitches, err := proc.NumCtxSwitchesWithContext(ctx)
|
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("num-ctx-switches", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
sysProc.NumCtxSwitches = numCtxSwitches
|
|
|
|
|
|
|
|
numFds, err := proc.NumFDsWithContext(ctx)
|
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("num-fds", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
sysProc.NumFds = numFds
|
|
|
|
|
|
|
|
numThreads, err := proc.NumThreadsWithContext(ctx)
|
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("num-threads", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
sysProc.NumThreads = numThreads
|
|
|
|
|
|
|
|
pageFaults, err := proc.PageFaultsWithContext(ctx)
|
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("page-faults", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
sysProc.PageFaults = pageFaults
|
|
|
|
|
|
|
|
parent, err := proc.ParentWithContext(ctx)
|
2020-04-18 14:06:11 -04:00
|
|
|
if err == nil {
|
|
|
|
sysProc.Parent = parent.Pid
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
ppid, err := proc.PpidWithContext(ctx)
|
2020-04-18 14:06:11 -04:00
|
|
|
if err == nil {
|
|
|
|
sysProc.Ppid = ppid
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
status, err := proc.StatusWithContext(ctx)
|
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("status", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
2021-03-01 03:15:46 -05:00
|
|
|
sysProc.Status = status[0]
|
2020-03-27 00:07:39 -04:00
|
|
|
|
|
|
|
tgid, err := proc.Tgid()
|
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("tgid", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
sysProc.Tgid = tgid
|
|
|
|
|
|
|
|
times, err := proc.TimesWithContext(ctx)
|
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("times", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
sysProc.Times = times
|
|
|
|
|
|
|
|
uids, err := proc.UidsWithContext(ctx)
|
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("uids", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
sysProc.Uids = uids
|
|
|
|
|
|
|
|
username, err := proc.UsernameWithContext(ctx)
|
|
|
|
if err != nil {
|
2021-02-05 15:37:15 -05:00
|
|
|
return errProcInfo("username", err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
sysProc.Username = username
|
|
|
|
|
|
|
|
sysProcs = append(sysProcs, sysProc)
|
|
|
|
}
|
|
|
|
|
2020-11-20 15:52:53 -05:00
|
|
|
return madmin.ServerProcInfo{
|
2020-03-27 00:07:39 -04:00
|
|
|
Addr: addr,
|
|
|
|
Processes: sysProcs,
|
|
|
|
}
|
|
|
|
}
|