diff --git a/cmd/admin-handlers.go b/cmd/admin-handlers.go index 19780fede..51bd4e431 100644 --- a/cmd/admin-handlers.go +++ b/cmd/admin-handlers.go @@ -35,10 +35,12 @@ import ( "github.com/gorilla/mux" "github.com/minio/minio/cmd/logger" "github.com/minio/minio/pkg/auth" + "github.com/minio/minio/pkg/cpu" "github.com/minio/minio/pkg/disk" "github.com/minio/minio/pkg/handlers" "github.com/minio/minio/pkg/iam/policy" "github.com/minio/minio/pkg/madmin" + "github.com/minio/minio/pkg/mem" "github.com/minio/minio/pkg/quick" "github.com/tidwall/gjson" "github.com/tidwall/sjson" @@ -294,6 +296,24 @@ type ServerDrivesPerfInfo struct { Perf []disk.Performance `json:"perf"` } +// ServerCPULoadInfo holds informantion about cpu utilization +// of one minio node. It also reports any errors if encountered +// while trying to reach this server. +type ServerCPULoadInfo struct { + Addr string `json:"addr"` + Error string `json:"error,omitempty"` + Load []cpu.Load `json:"load"` +} + +// ServerMemUsageInfo holds informantion about memory utilization +// of one minio node. It also reports any errors if encountered +// while trying to reach this server. +type ServerMemUsageInfo struct { + Addr string `json:"addr"` + Error string `json:"error,omitempty"` + Usage []mem.Usage `json:"usage"` +} + // PerfInfoHandler - GET /minio/admin/v1/performance?perfType={perfType} // ---------- // Get all performance information based on input type @@ -301,6 +321,13 @@ type ServerDrivesPerfInfo struct { func (a adminAPIHandlers) PerfInfoHandler(w http.ResponseWriter, r *http.Request) { ctx := newContext(r, w, "PerfInfo") + // Get object layer instance. + objLayer := newObjectLayerFn() + if objLayer == nil { + writeErrorResponseJSON(w, ErrServerNotInitialized, r.URL) + return + } + // Authenticate request // Setting the region as empty so as the mc server info command is irrespective to the region. adminAPIErr := checkAdminRequestAuthType(ctx, r, "") @@ -313,8 +340,14 @@ func (a adminAPIHandlers) PerfInfoHandler(w http.ResponseWriter, r *http.Request perfType := vars["perfType"] if perfType == "drive" { + info := objLayer.StorageInfo(ctx) + if !(info.Backend.Type == BackendFS || info.Backend.Type == BackendErasure) { + + writeErrorResponseJSON(w, ErrMethodNotAllowed, r.URL) + return + } // Get drive performance details from local server's drive(s) - dp := localEndpointsPerf(globalEndpoints) + dp := localEndpointsDrivePerf(globalEndpoints) // Notify all other Minio peers to report drive performance numbers dps := globalNotificationSys.DrivePerfInfo() @@ -330,8 +363,42 @@ func (a adminAPIHandlers) PerfInfoHandler(w http.ResponseWriter, r *http.Request // Reply with performance information (across nodes in a // distributed setup) as json. writeSuccessResponseJSON(w, jsonBytes) + } else if perfType == "cpu" { + // Get CPU load details from local server's cpu(s) + cpu := localEndpointsCPULoad(globalEndpoints) + // Notify all other Minio peers to report cpu load numbers + cpus := globalNotificationSys.CPULoadInfo() + cpus = append(cpus, cpu) + + // Marshal API response + jsonBytes, err := json.Marshal(cpus) + if err != nil { + writeErrorResponseJSON(w, toAdminAPIErrCode(ctx, err), r.URL) + return + } + + // Reply with cpu load information (across nodes in a + // distributed setup) as json. + writeSuccessResponseJSON(w, jsonBytes) + } else if perfType == "mem" { + // Get mem usage details from local server(s) + m := localEndpointsMemUsage(globalEndpoints) + // Notify all other Minio peers to report mem usage numbers + mems := globalNotificationSys.MemUsageInfo() + mems = append(mems, m) + + // Marshal API response + jsonBytes, err := json.Marshal(mems) + if err != nil { + writeErrorResponseJSON(w, toAdminAPIErrCode(ctx, err), r.URL) + return + } + + // Reply with mem usage information (across nodes in a + // distributed setup) as json. + writeSuccessResponseJSON(w, jsonBytes) } else { - writeErrorResponseJSON(w, ErrNotImplemented, r.URL) + writeErrorResponseJSON(w, ErrMethodNotAllowed, r.URL) } return } diff --git a/cmd/admin-router.go b/cmd/admin-router.go index 068630073..175b77f13 100644 --- a/cmd/admin-router.go +++ b/cmd/admin-router.go @@ -63,9 +63,9 @@ func registerAdminRouter(router *mux.Router, enableIAM bool) { /// Health operations - // Performance command - return performance details based on input type - adminV1Router.Methods(http.MethodGet).Path("/performance").HandlerFunc(httpTraceAll(adminAPI.PerfInfoHandler)).Queries("perfType", "{perfType:.*}") } + // Performance command - return performance details based on input type + adminV1Router.Methods(http.MethodGet).Path("/performance").HandlerFunc(httpTraceAll(adminAPI.PerfInfoHandler)).Queries("perfType", "{perfType:.*}") // Profiling operations adminV1Router.Methods(http.MethodPost).Path("/profiling/start").HandlerFunc(httpTraceAll(adminAPI.StartProfilingHandler)). diff --git a/cmd/endpoint.go b/cmd/endpoint.go index 7bfaa6615..ab17edc99 100644 --- a/cmd/endpoint.go +++ b/cmd/endpoint.go @@ -29,7 +29,9 @@ import ( "github.com/minio/minio-go/pkg/set" "github.com/minio/minio/cmd/logger" + "github.com/minio/minio/pkg/cpu" "github.com/minio/minio/pkg/disk" + "github.com/minio/minio/pkg/mem" "github.com/minio/minio/pkg/mountinfo" ) @@ -198,9 +200,57 @@ func (endpoints EndpointList) GetString(i int) string { return endpoints[i].String() } -// localEndpointsPerf - returns ServerDrivesPerfInfo for only the +// localEndpointsMemUsage - returns ServerMemUsageInfo for only the // local endpoints from given list of endpoints -func localEndpointsPerf(endpoints EndpointList) ServerDrivesPerfInfo { +func localEndpointsMemUsage(endpoints EndpointList) ServerMemUsageInfo { + var memUsages []mem.Usage + var addr string + scratchSpace := map[string]bool{} + for _, endpoint := range endpoints { + // Only proceed for local endpoints + if endpoint.IsLocal { + if _, ok := scratchSpace[endpoint.Host]; ok { + continue + } + addr = GetLocalPeer(endpoints) + memUsage := mem.GetUsage() + memUsages = append(memUsages, memUsage) + scratchSpace[endpoint.Host] = true + } + } + return ServerMemUsageInfo{ + Addr: addr, + Usage: memUsages, + } +} + +// localEndpointsCPULoad - returns ServerCPULoadInfo for only the +// local endpoints from given list of endpoints +func localEndpointsCPULoad(endpoints EndpointList) ServerCPULoadInfo { + var cpuLoads []cpu.Load + var addr string + scratchSpace := map[string]bool{} + for _, endpoint := range endpoints { + // Only proceed for local endpoints + if endpoint.IsLocal { + if _, ok := scratchSpace[endpoint.Host]; ok { + continue + } + addr = GetLocalPeer(endpoints) + cpuLoad := cpu.GetLoad() + cpuLoads = append(cpuLoads, cpuLoad) + scratchSpace[endpoint.Host] = true + } + } + return ServerCPULoadInfo{ + Addr: addr, + Load: cpuLoads, + } +} + +// localEndpointsDrivePerf - returns ServerDrivesPerfInfo for only the +// local endpoints from given list of endpoints +func localEndpointsDrivePerf(endpoints EndpointList) ServerDrivesPerfInfo { var dps []disk.Performance var addr string for _, endpoint := range endpoints { diff --git a/cmd/notification.go b/cmd/notification.go index 24938a329..8ad2e2eea 100644 --- a/cmd/notification.go +++ b/cmd/notification.go @@ -537,6 +537,56 @@ func (sys *NotificationSys) DrivePerfInfo() []ServerDrivesPerfInfo { return reply } +// MemUsageInfo - Mem utilization information +func (sys *NotificationSys) MemUsageInfo() []ServerMemUsageInfo { + reply := make([]ServerMemUsageInfo, len(sys.peerRPCClientMap)) + var wg sync.WaitGroup + var i int + for addr, client := range sys.peerRPCClientMap { + wg.Add(1) + go func(addr xnet.Host, client *PeerRPCClient, idx int) { + defer wg.Done() + memi, err := client.MemUsageInfo() + if err != nil { + reqInfo := (&logger.ReqInfo{}).AppendTags("remotePeer", addr.String()) + ctx := logger.SetReqInfo(context.Background(), reqInfo) + logger.LogIf(ctx, err) + memi.Addr = addr.String() + memi.Error = err.Error() + } + reply[idx] = memi + }(addr, client, i) + i++ + } + wg.Wait() + return reply +} + +// CPULoadInfo - CPU utilization information +func (sys *NotificationSys) CPULoadInfo() []ServerCPULoadInfo { + reply := make([]ServerCPULoadInfo, len(sys.peerRPCClientMap)) + var wg sync.WaitGroup + var i int + for addr, client := range sys.peerRPCClientMap { + wg.Add(1) + go func(addr xnet.Host, client *PeerRPCClient, idx int) { + defer wg.Done() + cpui, err := client.CPULoadInfo() + if err != nil { + reqInfo := (&logger.ReqInfo{}).AppendTags("remotePeer", addr.String()) + ctx := logger.SetReqInfo(context.Background(), reqInfo) + logger.LogIf(ctx, err) + cpui.Addr = addr.String() + cpui.Error = err.Error() + } + reply[idx] = cpui + }(addr, client, i) + i++ + } + wg.Wait() + return reply +} + // NewNotificationSys - creates new notification system object. func NewNotificationSys(config *serverConfig, endpoints EndpointList) *NotificationSys { targetList := getNotificationTargets(config) diff --git a/cmd/peer-rpc-client.go b/cmd/peer-rpc-client.go index ea8d2909c..4b71473c7 100644 --- a/cmd/peer-rpc-client.go +++ b/cmd/peer-rpc-client.go @@ -150,6 +150,24 @@ func (rpcClient *PeerRPCClient) DrivePerfInfo() (ServerDrivesPerfInfo, error) { return reply, err } +// MemUsageInfo - returns mem utilization info for remote server +func (rpcClient *PeerRPCClient) MemUsageInfo() (ServerMemUsageInfo, error) { + args := AuthArgs{} + var reply ServerMemUsageInfo + + err := rpcClient.Call(peerServiceName+".MemUsageInfo", &args, &reply) + return reply, err +} + +// CPULoadInfo - returns cpu performance info for remote server +func (rpcClient *PeerRPCClient) CPULoadInfo() (ServerCPULoadInfo, error) { + args := AuthArgs{} + var reply ServerCPULoadInfo + + err := rpcClient.Call(peerServiceName+".CPULoadInfo", &args, &reply) + return reply, err +} + // NewPeerRPCClient - returns new peer RPC client. func NewPeerRPCClient(host *xnet.Host) (*PeerRPCClient, error) { scheme := "http" diff --git a/cmd/peer-rpc-server.go b/cmd/peer-rpc-server.go index e0b40af9f..b267cf13e 100644 --- a/cmd/peer-rpc-server.go +++ b/cmd/peer-rpc-server.go @@ -245,7 +245,27 @@ func (receiver *peerRPCReceiver) DrivePerfInfo(args *AuthArgs, reply *ServerDriv return errServerNotInitialized } - *reply = localEndpointsPerf(globalEndpoints) + *reply = localEndpointsDrivePerf(globalEndpoints) + return nil +} + +// CPULoadInfo - handles cpu performance RPC call +func (receiver *peerRPCReceiver) CPULoadInfo(args *AuthArgs, reply *ServerCPULoadInfo) error { + objAPI := newObjectLayerFn() + if objAPI == nil { + return errServerNotInitialized + } + *reply = localEndpointsCPULoad(globalEndpoints) + return nil +} + +// MemUsageInfo - handles mem utilization RPC call +func (receiver *peerRPCReceiver) MemUsageInfo(args *AuthArgs, reply *ServerMemUsageInfo) error { + objAPI := newObjectLayerFn() + if objAPI == nil { + return errServerNotInitialized + } + *reply = localEndpointsMemUsage(globalEndpoints) return nil } diff --git a/pkg/cpu/counter_darwin.go b/pkg/cpu/counter_darwin.go new file mode 100644 index 000000000..2968c8cd3 --- /dev/null +++ b/pkg/cpu/counter_darwin.go @@ -0,0 +1,29 @@ +/* + * Minio Cloud Storage, (C) 2019 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cpu + +import ( + "errors" +) + +func newCounter() (counter, error) { + return counter{}, errors.New("cpu metrics not implemented for darwin platform") +} + +func (c counter) now() time.Time { + return time.Time{} +} diff --git a/pkg/cpu/counter_linux.go b/pkg/cpu/counter_linux.go new file mode 100644 index 000000000..2a9f97d72 --- /dev/null +++ b/pkg/cpu/counter_linux.go @@ -0,0 +1,40 @@ +/* + * Minio Cloud Storage, (C) 2019 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cpu + +import ( + "syscall" + "time" + "unsafe" +) + +const ( + // ProcessClock corresponds to the High-resolution per-process + // timer from the CPU represented in stdlib as CLOCK_PROCESS_CPUTIME_ID + processClock = 2 +) + +func newCounter() (counter, error) { + return counter{}, nil +} + +func (c counter) now() time.Time { + var ts syscall.Timespec + syscall.Syscall(syscall.SYS_CLOCK_GETTIME, processClock, uintptr(unsafe.Pointer(&ts)), 0) + sec, nsec := ts.Unix() + return time.Unix(sec, nsec) +} diff --git a/pkg/cpu/counter_windows.go b/pkg/cpu/counter_windows.go new file mode 100644 index 000000000..0ee5cbc68 --- /dev/null +++ b/pkg/cpu/counter_windows.go @@ -0,0 +1,30 @@ +/* + * Minio Cloud Storage, (C) 2019 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cpu + +import ( + "errors" + "time" +) + +func newCounter() (counter, error) { + return counter{}, errors.New("cpu metrics not implemented for windows platform") +} + +func (c counter) now() time.Time { + return time.Time{} +} diff --git a/pkg/cpu/cpu.go b/pkg/cpu/cpu.go new file mode 100644 index 000000000..147aab3df --- /dev/null +++ b/pkg/cpu/cpu.go @@ -0,0 +1,91 @@ +/* + * Minio Cloud Storage, (C) 2019 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cpu + +import ( + "fmt" + "sync" + "time" +) + +const ( + // cpuLoadWindow is the interval of time for which the + // cpu utilization is measured + cpuLoadWindow = 200 * time.Millisecond + + // cpuLoadSampleSize is the number of samples measured + // for calculating cpu utilization + cpuLoadSampleSize = 3 + + // endOfTime represents the end of time + endOfTime = time.Duration(1<<63 - 1) +) + +// Load holds CPU utilization % measured in three intervals of 200ms each +type Load struct { + Avg string `json:"avg"` + Max string `json:"max"` + Min string `json:"min"` + Error string `json:"error,omitempty"` +} + +type counter struct{} + +// GetLoad returns the CPU utilization % of the current process +func GetLoad() Load { + vals := make(chan time.Duration, 3) + wg := sync.WaitGroup{} + for i := 0; i < cpuLoadSampleSize; i++ { + cpuCounter, err := newCounter() + if err != nil { + return Load{ + Error: err.Error(), + } + } + wg.Add(1) + go func() { + start := cpuCounter.now() + time.Sleep(cpuLoadWindow) + end := cpuCounter.now() + vals <- end.Sub(start) + wg.Done() + }() + } + wg.Wait() + + sum := time.Duration(0) + max := time.Duration(0) + min := (endOfTime) + for i := 0; i < cpuLoadSampleSize; i++ { + val := <-vals + sum = sum + val + if val > max { + max = val + } + if val < min { + min = val + } + } + close(vals) + avg := sum / 3 + return Load{ + Avg: fmt.Sprintf("%.2f%%", toFixed4(float64(avg)/float64(200*time.Millisecond))*100), + Max: fmt.Sprintf("%.2f%%", toFixed4(float64(max)/float64(200*time.Millisecond))*100), + Min: fmt.Sprintf("%.2f%%", toFixed4(float64(min)/float64(200*time.Millisecond))*100), + Error: "", + } +} diff --git a/pkg/cpu/helpers.go b/pkg/cpu/helpers.go new file mode 100644 index 000000000..96eb2466b --- /dev/null +++ b/pkg/cpu/helpers.go @@ -0,0 +1,30 @@ +/* + * Minio Cloud Storage, (C) 2019 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cpu + +import ( + "math" +) + +func round(num float64) int { + return int(num + math.Copysign(0.5, num)) +} + +func toFixed4(num float64) float64 { + output := math.Pow(10, float64(4)) + return float64(round(num*output)) / output +} diff --git a/pkg/madmin/API.md b/pkg/madmin/API.md index 0f8b1ecb5..c8c239931 100644 --- a/pkg/madmin/API.md +++ b/pkg/madmin/API.md @@ -39,7 +39,7 @@ func main() { | Service operations | Info operations | Healing operations | Config operations | IAM operations | Misc | |:----------------------------|:----------------------------|:--------------------------------------|:--------------------------|:------------------------------------|:------------------------------------| | [`ServiceStatus`](#ServiceStatus) | [`ServerInfo`](#ServerInfo) | [`Heal`](#Heal) | [`GetConfig`](#GetConfig) | [`AddUser`](#AddUser) | [`SetAdminCredentials`](#SetAdminCredentials) | -| [`ServiceSendAction`](#ServiceSendAction) | [`ServerDrivesPerfInfo`](#ServerDrivesPerfInfo) | [`SetConfig`](#SetConfig) | [`SetUserPolicy`](#SetUserPolicy) | [`StartProfiling`](#StartProfiling) | +| [`ServiceSendAction`](#ServiceSendAction) | [`ServerDrivesPerfInfo`](#ServerDrivesPerfInfo) | [`ServerCPULoadInfo`](#ServerCPULoadInfo) | [`ServerMemUsageInfo`](#ServerMemUsageInfo) | [`SetConfig`](#SetConfig) | [`SetUserPolicy`](#SetUserPolicy) | [`StartProfiling`](#StartProfiling) | | | | | [`GetConfigKeys`](#GetConfigKeys) | [`ListUsers`](#ListUsers) | [`DownloadProfilingData`](#DownloadProfilingData) | | | | | [`SetConfigKeys`](#SetConfigKeys) | [`AddCannedPolicy`](#AddCannedPolicy) | | @@ -222,6 +222,40 @@ Fetches drive performance information for all cluster nodes. Returned value is i |`disk.Performance.WriteSpeed` | _float64_ | Write speed on above path in Bytes/s. | |`disk.Performance.ReadSpeed` | _float64_ | Read speed on above path in Bytes/s. | + +### ServerCPULoadInfo() ([]ServerCPULoadInfo, error) + +Fetches CPU utilization for all cluster nodes. Returned value is in Bytes. + +| Param | Type | Description | +|-------|------|-------------| +|`cpui.Addr` | _string_ | Address of the server the following information is retrieved from. | +|`cpui.Error` | _string_ | Errors (if any) encountered while reaching this node | +|`cpui.CPULoad` | _cpu.Load_ | The load on the CPU. | + +| Param | Type | Description | +|-------|------|-------------| +|`cpu.Load.Avg` | _string_ | The average utilization % of the CPU measured in a 200ms interval | +|`cpu.Load.Min` | _string_ | The minimum utilization % of the CPU measured in a 200ms interval | +|`cpu.Load.Max` | _string_ | The maximum utilization % of the CPU measured in a 200ms interval | +|`cpu.Load.Error` | _string_ | Error (if any) encountered while accesing the CPU info | + + +### ServerMemUsageInfo() ([]ServerMemUsageInfo, error) + +Fetches Mem utilization for all cluster nodes. Returned value is in Bytes. + +| Param | Type | Description | +|-------|------|-------------| +|`memi.Addr` | _string_ | Address of the server the following information is retrieved from. | +|`memi.Error` | _string_ | Errors (if any) encountered while reaching this node | +|`memi.MemUsage` | _mem.Usage_ | The utilitzation of Memory | + +| Param | Type | Description | +|-------|------|-------------| +|`mem.Usage.Mem` | _string_ | The total number of bytes obtained from the OS | +|`mem.Usage.Error` | _string_ | Error (if any) encountered while accesing the CPU info | + ## 6. Heal operations diff --git a/pkg/madmin/examples/cpu-load-info.go b/pkg/madmin/examples/cpu-load-info.go new file mode 100644 index 000000000..d59287540 --- /dev/null +++ b/pkg/madmin/examples/cpu-load-info.go @@ -0,0 +1,44 @@ +// +build ignore + +/* + * Minio Cloud Storage, (C) 2019 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package main + +import ( + "log" + + "github.com/minio/minio/pkg/madmin" +) + +func main() { + // Note: YOUR-ACCESSKEYID, YOUR-SECRETACCESSKEY and my-bucketname are + // dummy values, please replace them with original values. + + // API requests are secure (HTTPS) if secure=true and insecure (HTTPS) otherwise. + // New returns an Minio Admin client object. + madmClnt, err := madmin.New("your-minio.example.com:9000", "YOUR-ACCESSKEYID", "YOUR-SECRETACCESSKEY", true) + if err != nil { + log.Fatalln(err) + } + + st, err := madmClnt.ServerCPULoadInfo() + if err != nil { + log.Fatalln(err) + } + log.Println(st) +} diff --git a/pkg/madmin/examples/mem-usage-info.go b/pkg/madmin/examples/mem-usage-info.go new file mode 100644 index 000000000..9e6f9bc59 --- /dev/null +++ b/pkg/madmin/examples/mem-usage-info.go @@ -0,0 +1,44 @@ +// +build ignore + +/* + * Minio Cloud Storage, (C) 2019 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package main + +import ( + "log" + + "github.com/minio/minio/pkg/madmin" +) + +func main() { + // Note: YOUR-ACCESSKEYID, YOUR-SECRETACCESSKEY and my-bucketname are + // dummy values, please replace them with original values. + + // API requests are secure (HTTPS) if secure=true and insecure (HTTPS) otherwise. + // New returns an Minio Admin client object. + madmClnt, err := madmin.New("your-minio.example.com:9000", "YOUR-ACCESSKEYID", "YOUR-SECRETACCESSKEY", true) + if err != nil { + log.Fatalln(err) + } + + st, err := madmClnt.ServerMemUsageInfo() + if err != nil { + log.Fatalln(err) + } + log.Println(st) +} diff --git a/pkg/madmin/info-commands.go b/pkg/madmin/info-commands.go index e2920341c..b3968117f 100644 --- a/pkg/madmin/info-commands.go +++ b/pkg/madmin/info-commands.go @@ -24,7 +24,9 @@ import ( "net/url" "time" + "github.com/minio/minio/pkg/cpu" "github.com/minio/minio/pkg/disk" + "github.com/minio/minio/pkg/mem" ) // BackendType - represents different backend types. @@ -193,3 +195,89 @@ func (adm *AdminClient) ServerDrivesPerfInfo() ([]ServerDrivesPerfInfo, error) { return info, nil } + +// ServerCPULoadInfo holds information about address and cpu load of +// a single server node +type ServerCPULoadInfo struct { + Addr string `json:"addr"` + Error string `json:"error,omitempty"` + Load []cpu.Load `json:"load"` +} + +// ServerCPULoadInfo - Returns cpu utilization information +func (adm *AdminClient) ServerCPULoadInfo() ([]ServerCPULoadInfo, error) { + v := url.Values{} + v.Set("perfType", string("cpu")) + resp, err := adm.executeMethod("GET", requestData{ + relPath: "/v1/performance", + queryValues: v, + }) + + defer closeResponse(resp) + if err != nil { + return nil, err + } + + // Check response http status code + if resp.StatusCode != http.StatusOK { + return nil, httpRespToErrorResponse(resp) + } + + // Unmarshal the server's json response + var info []ServerCPULoadInfo + + respBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + err = json.Unmarshal(respBytes, &info) + if err != nil { + return nil, err + } + + return info, nil +} + +// ServerMemUsageInfo holds information about address and memory utilization of +// a single server node +type ServerMemUsageInfo struct { + Addr string `json:"addr"` + Error string `json:"error,omitempty"` + Usage []mem.Usage `json:"usage"` +} + +// ServerMemUsageInfo - Returns mem utilization information +func (adm *AdminClient) ServerMemUsageInfo() ([]ServerMemUsageInfo, error) { + v := url.Values{} + v.Set("perfType", string("mem")) + resp, err := adm.executeMethod("GET", requestData{ + relPath: "/v1/performance", + queryValues: v, + }) + + defer closeResponse(resp) + if err != nil { + return nil, err + } + + // Check response http status code + if resp.StatusCode != http.StatusOK { + return nil, httpRespToErrorResponse(resp) + } + + // Unmarshal the server's json response + var info []ServerMemUsageInfo + + respBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + err = json.Unmarshal(respBytes, &info) + if err != nil { + return nil, err + } + + return info, nil +} diff --git a/pkg/mem/mem.go b/pkg/mem/mem.go new file mode 100644 index 000000000..95357d626 --- /dev/null +++ b/pkg/mem/mem.go @@ -0,0 +1,39 @@ +/* + * Minio Cloud Storage, (C) 2019 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package mem + +import ( + "runtime" + + humanize "github.com/dustin/go-humanize" +) + +// Usage holds memory utilization information in human readable format +type Usage struct { + Mem string `json:"mem"` + Error string `json:"error,omitempty"` +} + +// GetUsage measures the total memory provisioned for the current process +// from the OS +func GetUsage() Usage { + memStats := new(runtime.MemStats) + runtime.ReadMemStats(memStats) + return Usage{ + Mem: humanize.IBytes(memStats.Sys), + } +}