From 18cb15559d91868d4964a358d2fd2a63579b9714 Mon Sep 17 00:00:00 2001 From: Ashish Kumar Sinha Date: Thu, 17 Oct 2019 16:39:50 +0530 Subject: [PATCH] Add network hardware info (#8358) peerRESTVersion changed to v6 --- cmd/admin-handlers.go | 18 +++++++++++ cmd/admin-server-info.go | 34 ++++++++++++++++++++ cmd/notification.go | 23 ++++++++++++++ cmd/peer-rest-client.go | 13 +++++++- cmd/peer-rest-common.go | 3 +- cmd/peer-rest-server.go | 15 +++++++++ pkg/madmin/README.md | 36 ++++++++++++++++----- pkg/madmin/examples/hw-cpu-info.go | 3 +- pkg/madmin/examples/hw-network-info.go | 44 ++++++++++++++++++++++++++ pkg/madmin/hardware-commands.go | 44 ++++++++++++++++++++++++++ 10 files changed, 222 insertions(+), 11 deletions(-) create mode 100644 pkg/madmin/examples/hw-network-info.go diff --git a/cmd/admin-handlers.go b/cmd/admin-handlers.go index 32f2422c4..36eed2497 100644 --- a/cmd/admin-handlers.go +++ b/cmd/admin-handlers.go @@ -1823,6 +1823,24 @@ func (a adminAPIHandlers) ServerHardwareInfoHandler(w http.ResponseWriter, r *ht // distributed setup) as json. writeSuccessResponseJSON(w, jsonBytes) + case madmin.NETWORK: + // Get Network hardware details from local server's network(s) + network := getLocalNetworkInfo(globalEndpoints, r) + // Notify all other MinIO peers to report network hardware + networks := globalNotificationSys.NetworkInfo() + networks = append(networks, network) + + // Marshal API response + jsonBytes, err := json.Marshal(networks) + if err != nil { + writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) + return + } + + // Reply with cpu network information (across nodes in a + // distributed setup) as json. + writeSuccessResponseJSON(w, jsonBytes) + default: writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL) } diff --git a/cmd/admin-server-info.go b/cmd/admin-server-info.go index 941c66906..3c0f2d3c4 100644 --- a/cmd/admin-server-info.go +++ b/cmd/admin-server-info.go @@ -17,6 +17,7 @@ package cmd import ( + "net" "net/http" "os" @@ -147,3 +148,36 @@ func getLocalCPUInfo(endpoints EndpointList, r *http.Request) madmin.ServerCPUHa CPUInfo: cpuHardwares, } } + +// getLocalNetworkInfo - returns ServerNetworkHardwareInfo only for the +// local endpoints from given list of endpoints +func getLocalNetworkInfo(endpoints EndpointList, r *http.Request) madmin.ServerNetworkHardwareInfo { + var networkHardwares []net.Interface + seenHosts := set.NewStringSet() + for _, endpoint := range endpoints { + if seenHosts.Contains(endpoint.Host) { + continue + } + // Add to the list of visited hosts + seenHosts.Add(endpoint.Host) + // Only proceed for local endpoints + if endpoint.IsLocal { + networkHardware, err := net.Interfaces() + if err != nil { + return madmin.ServerNetworkHardwareInfo{ + Error: err.Error(), + } + } + networkHardwares = append(networkHardwares, networkHardware...) + } + } + addr := r.Host + if globalIsDistXL { + addr = GetLocalPeer(endpoints) + } + + return madmin.ServerNetworkHardwareInfo{ + Addr: addr, + NetworkInfo: networkHardwares, + } +} diff --git a/cmd/notification.go b/cmd/notification.go index 9f293d38d..869465ed6 100644 --- a/cmd/notification.go +++ b/cmd/notification.go @@ -1072,6 +1072,29 @@ func (sys *NotificationSys) CPUInfo() []madmin.ServerCPUHardwareInfo { return reply } +// NetworkInfo - Network Hardware info +func (sys *NotificationSys) NetworkInfo() []madmin.ServerNetworkHardwareInfo { + reply := make([]madmin.ServerNetworkHardwareInfo, len(sys.peerClients)) + var wg sync.WaitGroup + for i, client := range sys.peerClients { + if client == nil { + continue + } + wg.Add(1) + go func(client *peerRESTClient, idx int) { + defer wg.Done() + netinfo, err := client.NetworkInfo() + if err != nil { + netinfo.Addr = client.host.String() + netinfo.Error = err.Error() + } + reply[idx] = netinfo + }(client, 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-rest-client.go b/cmd/peer-rest-client.go index 0cec7e24c..6dc2b9a0e 100644 --- a/cmd/peer-rest-client.go +++ b/cmd/peer-rest-client.go @@ -166,7 +166,7 @@ func (client *peerRESTClient) CPULoadInfo() (info ServerCPULoadInfo, err error) return info, err } -// CpuInfo - fetch CPU hardware information for a remote node. +// CPUInfo - fetch CPU hardware information for a remote node. func (client *peerRESTClient) CPUInfo() (info madmin.ServerCPUHardwareInfo, err error) { respBody, err := client.call(peerRESTMethodHardwareCPUInfo, nil, nil, -1) if err != nil { @@ -177,6 +177,17 @@ func (client *peerRESTClient) CPUInfo() (info madmin.ServerCPUHardwareInfo, err return info, err } +// NetworkInfo - fetch network hardware information for a remote node. +func (client *peerRESTClient) NetworkInfo() (info madmin.ServerNetworkHardwareInfo, err error) { + respBody, err := client.call(peerRESTMethodHardwareNetworkInfo, nil, nil, -1) + if err != nil { + return + } + defer http.DrainBody(respBody) + err = gob.NewDecoder(respBody).Decode(&info) + return info, err +} + // DrivePerfInfo - fetch Drive performance information for a remote node. func (client *peerRESTClient) DrivePerfInfo(size int64) (info madmin.ServerDrivesPerfInfo, err error) { params := make(url.Values) diff --git a/cmd/peer-rest-common.go b/cmd/peer-rest-common.go index 36b30b551..8aacae4fb 100644 --- a/cmd/peer-rest-common.go +++ b/cmd/peer-rest-common.go @@ -16,7 +16,7 @@ package cmd -const peerRESTVersion = "v5" +const peerRESTVersion = "v6" const peerRESTPath = minioReservedBucketPath + "/peer/" + peerRESTVersion const ( @@ -53,6 +53,7 @@ const ( peerRESTMethodBucketLifecycleRemove = "removebucketlifecycle" peerRESTMethodLog = "log" peerRESTMethodHardwareCPUInfo = "cpuhardwareinfo" + peerRESTMethodHardwareNetworkInfo = "networkhardwareinfo" ) const ( diff --git a/cmd/peer-rest-server.go b/cmd/peer-rest-server.go index 85bb7b1ee..7841368db 100644 --- a/cmd/peer-rest-server.go +++ b/cmd/peer-rest-server.go @@ -438,6 +438,20 @@ func (s *peerRESTServer) CPUInfoHandler(w http.ResponseWriter, r *http.Request) logger.LogIf(ctx, gob.NewEncoder(w).Encode(info)) } +// NetworkInfoHandler - returns Network Hardware info. +func (s *peerRESTServer) NetworkInfoHandler(w http.ResponseWriter, r *http.Request) { + if !s.IsValid(w, r) { + s.writeErrorResponse(w, errors.New("Invalid request")) + return + } + + ctx := newContext(r, w, "NetworkInfo") + info := getLocalNetworkInfo(globalEndpoints, r) + + defer w.(http.Flusher).Flush() + logger.LogIf(ctx, gob.NewEncoder(w).Encode(info)) +} + // DrivePerfInfoHandler - returns Drive Performance info. func (s *peerRESTServer) DrivePerfInfoHandler(w http.ResponseWriter, r *http.Request) { if !s.IsValid(w, r) { @@ -990,6 +1004,7 @@ func registerPeerRESTHandlers(router *mux.Router) { subrouter.Methods(http.MethodPost).Path(SlashSeparator + peerRESTMethodMemUsageInfo).HandlerFunc(httpTraceHdrs(server.MemUsageInfoHandler)) subrouter.Methods(http.MethodPost).Path(SlashSeparator + peerRESTMethodDrivePerfInfo).HandlerFunc(httpTraceHdrs(server.DrivePerfInfoHandler)).Queries(restQueries(peerRESTDrivePerfSize)...) subrouter.Methods(http.MethodPost).Path(SlashSeparator + peerRESTMethodHardwareCPUInfo).HandlerFunc(httpTraceHdrs(server.CPUInfoHandler)) + subrouter.Methods(http.MethodPost).Path(SlashSeparator + peerRESTMethodHardwareNetworkInfo).HandlerFunc(httpTraceHdrs(server.NetworkInfoHandler)) subrouter.Methods(http.MethodPost).Path(SlashSeparator + peerRESTMethodDeleteBucket).HandlerFunc(httpTraceHdrs(server.DeleteBucketHandler)).Queries(restQueries(peerRESTBucket)...) subrouter.Methods(http.MethodPost).Path(SlashSeparator + peerRESTMethodSignalService).HandlerFunc(httpTraceHdrs(server.SignalServiceHandler)).Queries(restQueries(peerRESTSignal)...) subrouter.Methods(http.MethodPost).Path(SlashSeparator + peerRESTMethodServerUpdate).HandlerFunc(httpTraceHdrs(server.ServerUpdateHandler)).Queries(restQueries(peerRESTUpdateURL, peerRESTSha256Hex, peerRESTLatestRelease)...) diff --git a/pkg/madmin/README.md b/pkg/madmin/README.md index a8eaa342d..eef3ff520 100644 --- a/pkg/madmin/README.md +++ b/pkg/madmin/README.md @@ -42,14 +42,15 @@ func main() { } ``` -| Service operations | Info operations | Healing operations | Config operations | Top operations | IAM operations | Misc | KMS | -|:------------------------------------|:---------------------------------------------------|:-------------------|:--------------------------|:------------------------|:--------------------------------------|:--------------------------------------------------|:--------------------------------| -| [`ServiceRestart`](#ServiceRestart) | [`ServerInfo`](#ServerInfo) | [`Heal`](#Heal) | [`GetConfig`](#GetConfig) | [`TopLocks`](#TopLocks) | [`AddUser`](#AddUser) | | [`GetKeyStatus`](#GetKeyStatus) | -| [`ServiceStop`](#ServiceStop) | [`ServerCPULoadInfo`](#ServerCPULoadInfo) | | [`SetConfig`](#SetConfig) | | [`SetUserPolicy`](#SetUserPolicy) | [`StartProfiling`](#StartProfiling) | | -| | [`ServerMemUsageInfo`](#ServerMemUsageInfo) | | | | [`ListUsers`](#ListUsers) | [`DownloadProfilingData`](#DownloadProfilingData) | | -| [`ServiceTrace`](#ServiceTrace) | [`ServerDrivesPerfInfo`](#ServerDrivesPerfInfo) | | | | [`AddCannedPolicy`](#AddCannedPolicy) | [`ServerUpdate`](#ServerUpdate) | | -| | [`NetPerfInfo`](#NetPerfInfo) | | | | | | | -| | [`ServerCPUHardwareInfo`](#ServerCPUHardwareInfo) | | | | | | | +| Service operations | Info operations | Healing operations | Config operations | Top operations | IAM operations | Misc | KMS | +|:------------------------------------|:------------------------------------------------------------|:-------------------|:--------------------------|:------------------------|:--------------------------------------|:--------------------------------------------------|:--------------------------------| +| [`ServiceRestart`](#ServiceRestart) | [`ServerInfo`](#ServerInfo) | [`Heal`](#Heal) | [`GetConfig`](#GetConfig) | [`TopLocks`](#TopLocks) | [`AddUser`](#AddUser) | | [`GetKeyStatus`](#GetKeyStatus) | +| [`ServiceStop`](#ServiceStop) | [`ServerCPULoadInfo`](#ServerCPULoadInfo) | | [`SetConfig`](#SetConfig) | | [`SetUserPolicy`](#SetUserPolicy) | [`StartProfiling`](#StartProfiling) | | +| | [`ServerMemUsageInfo`](#ServerMemUsageInfo) | | | | [`ListUsers`](#ListUsers) | [`DownloadProfilingData`](#DownloadProfilingData) | | +| [`ServiceTrace`](#ServiceTrace) | [`ServerDrivesPerfInfo`](#ServerDrivesPerfInfo) | | | | [`AddCannedPolicy`](#AddCannedPolicy) | [`ServerUpdate`](#ServerUpdate) | | +| | [`NetPerfInfo`](#NetPerfInfo) | | | | | | | +| | [`ServerCPUHardwareInfo`](#ServerCPUHardwareInfo) | | | | | | | +| | [`ServerNetworkHardwareInfo`](#ServerNetworkHardwareInfo) | | | | | | | ## 1. Constructor @@ -314,6 +315,25 @@ Fetches hardware information of CPU. | `CPUInfo.Flags` |_[]string_| Flags | | `CPUInfo.Microcode` | _string_ | Micro codes | + +### ServerNetworkHardwareInfo() ([]ServerNetworkHardwareInfo, error) + +Fetches hardware information of CPU. + +| Param | Type | Description | +|-------------------|---------------------|---------------------------------------------------------------------| +| `hwi.Addr` | _string_ | Address of the server the following information is retrieved from. | +| `hwi.Error` | _string_ | Errors (if any) encountered while reaching this node | +| `hwi.NetworkInfo` | _[]net.Interface_ | The network hardware info | + +| Param | Type | Description | +|----------------------------|----------|-----------------------------------------------------------| +| `NetworkInfo.Index` | _int32_ | positive integer that starts at one, zero is never used. | +| `NetworkInfo.MTU` | _int32_ | maximum transmission unit | +| `NetworkInfo.Name` | _string_ | e.g., "en0", "lo0", "eth0.100" | +| `NetworkInfo.HardwareAddr` | _[]byte_ | IEEE MAC-48, EUI-48 and EUI-64 form | +| `NetworkInfo.Flags` | _uint32_ | e.g., FlagUp, FlagLoopback, FlagMulticast | + ## 5. Heal operations diff --git a/pkg/madmin/examples/hw-cpu-info.go b/pkg/madmin/examples/hw-cpu-info.go index eafd40e4e..00241ba59 100644 --- a/pkg/madmin/examples/hw-cpu-info.go +++ b/pkg/madmin/examples/hw-cpu-info.go @@ -20,8 +20,9 @@ package main import ( - "github.com/minio/minio/pkg/madmin" "log" + + "github.com/minio/minio/pkg/madmin" ) func main() { diff --git a/pkg/madmin/examples/hw-network-info.go b/pkg/madmin/examples/hw-network-info.go new file mode 100644 index 000000000..a15062497 --- /dev/null +++ b/pkg/madmin/examples/hw-network-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 correct values. + + // API requests are secure (HTTPS) if secure=true and insecure (HTTP) 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.ServerNetworkHardwareInfo() + if err != nil { + log.Fatalln(err) + } + log.Println(st) + +} diff --git a/pkg/madmin/hardware-commands.go b/pkg/madmin/hardware-commands.go index dba5c6dd2..36e5eebde 100644 --- a/pkg/madmin/hardware-commands.go +++ b/pkg/madmin/hardware-commands.go @@ -20,6 +20,7 @@ package madmin import ( "encoding/json" "io/ioutil" + "net" "net/http" "net/url" @@ -34,6 +35,8 @@ const ( HARDWARE = "hwType" // CPU represents hardware as cpu CPU HardwareType = "cpu" + // NETWORK hardware Info + NETWORK HardwareType = "network" ) // ServerCPUHardwareInfo holds informantion about cpu hardware @@ -76,3 +79,44 @@ func (adm *AdminClient) ServerCPUHardwareInfo() ([]ServerCPUHardwareInfo, error) } return cpuInfo, nil } + +// ServerNetworkHardwareInfo holds informantion about cpu hardware +type ServerNetworkHardwareInfo struct { + Addr string `json:"addr"` + Error string `json:"error,omitempty"` + NetworkInfo []net.Interface `json:"network"` +} + +// ServerNetworkHardwareInfo - Returns network hardware information +func (adm *AdminClient) ServerNetworkHardwareInfo() ([]ServerNetworkHardwareInfo, error) { + v := url.Values{} + v.Set(HARDWARE, string(NETWORK)) + resp, err := adm.executeMethod("GET", requestData{ + relPath: "/v1/hardware", + 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 networkInfo []ServerNetworkHardwareInfo + + respBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + err = json.Unmarshal(respBytes, &networkInfo) + if err != nil { + return nil, err + } + return networkInfo, nil +}