mirror of
https://github.com/minio/minio.git
synced 2024-12-24 06:05:55 -05:00
admin: ServerInfo() returns info for each node (#4150)
ServerInfo() will gather information from all nodes before returning it back to the client.
This commit is contained in:
parent
df346753e1
commit
83abad0b37
@ -26,6 +26,7 @@ import (
|
||||
"net/url"
|
||||
"path"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -229,14 +230,22 @@ type ServerHTTPStats struct {
|
||||
SuccessDELETEStats ServerHTTPMethodStats `json:"successDELETEs"`
|
||||
}
|
||||
|
||||
// ServerInfo holds the information that will be returned by ServerInfo API
|
||||
type ServerInfo struct {
|
||||
// ServerInfoData holds storage, connections and other
|
||||
// information of a given server.
|
||||
type ServerInfoData struct {
|
||||
StorageInfo StorageInfo `json:"storage"`
|
||||
ConnStats ServerConnStats `json:"network"`
|
||||
HTTPStats ServerHTTPStats `json:"http"`
|
||||
Properties ServerProperties `json:"server"`
|
||||
}
|
||||
|
||||
// ServerInfo holds server information result of one node
|
||||
type ServerInfo struct {
|
||||
Error error
|
||||
Addr string
|
||||
Data *ServerInfoData
|
||||
}
|
||||
|
||||
// ServerInfoHandler - GET /?info
|
||||
// ----------
|
||||
// Get server information
|
||||
@ -248,55 +257,37 @@ func (adminAPI adminAPIHandlers) ServerInfoHandler(w http.ResponseWriter, r *htt
|
||||
return
|
||||
}
|
||||
|
||||
// Build storage info
|
||||
objLayer := newObjectLayerFn()
|
||||
if objLayer == nil {
|
||||
writeErrorResponse(w, ErrServerNotInitialized, r.URL)
|
||||
return
|
||||
}
|
||||
storage := objLayer.StorageInfo()
|
||||
// Web service response
|
||||
reply := make([]ServerInfo, len(globalAdminPeers))
|
||||
|
||||
// Build list of enabled ARNs queues
|
||||
var arns []string
|
||||
for queueArn := range globalEventNotifier.GetAllExternalTargets() {
|
||||
arns = append(arns, queueArn)
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// Gather server information for all nodes
|
||||
for i, p := range globalAdminPeers {
|
||||
wg.Add(1)
|
||||
|
||||
// Gather information from a peer in a goroutine
|
||||
go func(idx int, peer adminPeer) {
|
||||
defer wg.Done()
|
||||
|
||||
// Initialize server info at index
|
||||
reply[idx] = ServerInfo{Addr: peer.addr}
|
||||
|
||||
serverInfoData, err := peer.cmdRunner.ServerInfoData()
|
||||
if err != nil {
|
||||
errorIf(err, "Unable to get server info from %s.", peer.addr)
|
||||
reply[idx].Error = err
|
||||
return
|
||||
}
|
||||
|
||||
reply[idx].Data = &serverInfoData
|
||||
}(i, p)
|
||||
}
|
||||
|
||||
// Fetch uptimes from all peers. This may fail to due to lack
|
||||
// of read-quorum availability.
|
||||
uptime, err := getPeerUptimes(globalAdminPeers)
|
||||
if err != nil {
|
||||
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
||||
errorIf(err, "Unable to get uptime from majority of servers.")
|
||||
return
|
||||
}
|
||||
|
||||
// Build server properties information
|
||||
properties := ServerProperties{
|
||||
Version: Version,
|
||||
CommitID: CommitID,
|
||||
Region: serverConfig.GetRegion(),
|
||||
SQSARN: arns,
|
||||
Uptime: uptime,
|
||||
}
|
||||
|
||||
// Build network info
|
||||
connStats := ServerConnStats{
|
||||
TotalInputBytes: globalConnStats.getTotalInputBytes(),
|
||||
TotalOutputBytes: globalConnStats.getTotalOutputBytes(),
|
||||
}
|
||||
httpStats := globalHTTPStats.toServerHTTPStats()
|
||||
|
||||
// Build the whole returned information
|
||||
info := ServerInfo{
|
||||
StorageInfo: storage,
|
||||
ConnStats: connStats,
|
||||
HTTPStats: httpStats,
|
||||
Properties: properties,
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
// Marshal API response
|
||||
jsonBytes, err := json.Marshal(info)
|
||||
jsonBytes, err := json.Marshal(reply)
|
||||
if err != nil {
|
||||
writeErrorResponse(w, ErrInternalError, r.URL)
|
||||
errorIf(err, "Failed to marshal storage info into json.")
|
||||
|
@ -1300,17 +1300,29 @@ func TestAdminServerInfo(t *testing.T) {
|
||||
t.Errorf("Expected to succeed but failed with %d", rec.Code)
|
||||
}
|
||||
|
||||
result := ServerInfo{}
|
||||
err = json.NewDecoder(rec.Body).Decode(&result)
|
||||
results := []ServerInfo{}
|
||||
err = json.NewDecoder(rec.Body).Decode(&results)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to decode set config result json %v", err)
|
||||
}
|
||||
|
||||
if result.StorageInfo.Free == 0 {
|
||||
t.Error("Expected StorageInfo.Free to be non empty")
|
||||
if len(results) == 0 {
|
||||
t.Error("Expected at least one server info result")
|
||||
}
|
||||
if result.Properties.Region != globalMinioDefaultRegion {
|
||||
t.Errorf("Expected %s, got %s", globalMinioDefaultRegion, result.Properties.Region)
|
||||
|
||||
for _, serverInfo := range results {
|
||||
if len(serverInfo.Addr) == 0 {
|
||||
t.Error("Expected server address to be non empty")
|
||||
}
|
||||
if serverInfo.Error != nil {
|
||||
t.Errorf("Unexpected error = %v\n", serverInfo.Error)
|
||||
}
|
||||
if serverInfo.Data.StorageInfo.Free == 0 {
|
||||
t.Error("Expected StorageInfo.Free to be non empty")
|
||||
}
|
||||
if serverInfo.Data.Properties.Region != globalMinioDefaultRegion {
|
||||
t.Errorf("Expected %s, got %s", globalMinioDefaultRegion, serverInfo.Data.Properties.Region)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ const (
|
||||
serviceRestartRPC = "Admin.Restart"
|
||||
listLocksRPC = "Admin.ListLocks"
|
||||
reInitDisksRPC = "Admin.ReInitDisks"
|
||||
uptimeRPC = "Admin.Uptime"
|
||||
serverInfoDataRPC = "Admin.ServerInfoData"
|
||||
getConfigRPC = "Admin.GetConfig"
|
||||
writeTmpConfigRPC = "Admin.WriteTmpConfig"
|
||||
commitConfigRPC = "Admin.CommitConfig"
|
||||
@ -59,7 +59,7 @@ type adminCmdRunner interface {
|
||||
Restart() error
|
||||
ListLocks(bucket, prefix string, duration time.Duration) ([]VolumeLockInfo, error)
|
||||
ReInitDisks() error
|
||||
Uptime() (time.Duration, error)
|
||||
ServerInfoData() (ServerInfoData, error)
|
||||
GetConfig() ([]byte, error)
|
||||
WriteTmpConfig(tmpFileName string, configBytes []byte) error
|
||||
CommitConfig(tmpFileName string) error
|
||||
@ -112,26 +112,48 @@ func (rc remoteAdminClient) ReInitDisks() error {
|
||||
return rc.Call(reInitDisksRPC, &args, &reply)
|
||||
}
|
||||
|
||||
// Uptime - Returns the uptime of this server. Timestamp is taken
|
||||
// after object layer is initialized.
|
||||
func (lc localAdminClient) Uptime() (time.Duration, error) {
|
||||
// ServerInfoData - Returns the server info of this server.
|
||||
func (lc localAdminClient) ServerInfoData() (ServerInfoData, error) {
|
||||
if globalBootTime.IsZero() {
|
||||
return time.Duration(0), errServerNotInitialized
|
||||
return ServerInfoData{}, errServerNotInitialized
|
||||
}
|
||||
|
||||
return UTCNow().Sub(globalBootTime), nil
|
||||
// Build storage info
|
||||
objLayer := newObjectLayerFn()
|
||||
if objLayer == nil {
|
||||
return ServerInfoData{}, errServerNotInitialized
|
||||
}
|
||||
storage := objLayer.StorageInfo()
|
||||
|
||||
var arns []string
|
||||
for queueArn := range globalEventNotifier.GetAllExternalTargets() {
|
||||
arns = append(arns, queueArn)
|
||||
}
|
||||
|
||||
return ServerInfoData{
|
||||
StorageInfo: storage,
|
||||
ConnStats: globalConnStats.toServerConnStats(),
|
||||
HTTPStats: globalHTTPStats.toServerHTTPStats(),
|
||||
Properties: ServerProperties{
|
||||
Uptime: UTCNow().Sub(globalBootTime),
|
||||
Version: Version,
|
||||
CommitID: CommitID,
|
||||
SQSARN: arns,
|
||||
Region: serverConfig.GetRegion(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Uptime - returns the uptime of the server to which the RPC call is made.
|
||||
func (rc remoteAdminClient) Uptime() (time.Duration, error) {
|
||||
// ServerInfo - returns the server info of the server to which the RPC call is made.
|
||||
func (rc remoteAdminClient) ServerInfoData() (ServerInfoData, error) {
|
||||
args := AuthRPCArgs{}
|
||||
reply := UptimeReply{}
|
||||
err := rc.Call(uptimeRPC, &args, &reply)
|
||||
reply := ServerInfoDataReply{}
|
||||
err := rc.Call(serverInfoDataRPC, &args, &reply)
|
||||
if err != nil {
|
||||
return time.Duration(0), err
|
||||
return ServerInfoData{}, err
|
||||
}
|
||||
|
||||
return reply.Uptime, nil
|
||||
return reply.ServerInfoData, nil
|
||||
}
|
||||
|
||||
// GetConfig - returns config.json of the local server.
|
||||
@ -384,7 +406,8 @@ func getPeerUptimes(peers adminPeers) (time.Duration, error) {
|
||||
wg.Add(1)
|
||||
go func(idx int, peer adminPeer) {
|
||||
defer wg.Done()
|
||||
uptimes[idx].uptime, uptimes[idx].err = peer.cmdRunner.Uptime()
|
||||
serverInfoData, rpcErr := peer.cmdRunner.ServerInfoData()
|
||||
uptimes[idx].uptime, uptimes[idx].err = serverInfoData.Properties.Uptime, rpcErr
|
||||
}(i, peer)
|
||||
}
|
||||
wg.Wait()
|
||||
|
@ -53,10 +53,10 @@ type ListLocksReply struct {
|
||||
volLocks []VolumeLockInfo
|
||||
}
|
||||
|
||||
// UptimeReply - wraps the uptime response over RPC.
|
||||
type UptimeReply struct {
|
||||
// ServerInfoDataReply - wraps the server info response over RPC.
|
||||
type ServerInfoDataReply struct {
|
||||
AuthRPCReply
|
||||
Uptime time.Duration
|
||||
ServerInfoData ServerInfoData
|
||||
}
|
||||
|
||||
// ConfigReply - wraps the server config response over RPC.
|
||||
@ -122,8 +122,8 @@ func (s *adminCmd) ReInitDisks(args *AuthRPCArgs, reply *AuthRPCReply) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Uptime - returns the time when object layer was initialized on this server.
|
||||
func (s *adminCmd) Uptime(args *AuthRPCArgs, reply *UptimeReply) error {
|
||||
// ServerInfo - returns the server info when object layer was initialized on this server.
|
||||
func (s *adminCmd) ServerInfoData(args *AuthRPCArgs, reply *ServerInfoDataReply) error {
|
||||
if err := args.IsAuthenticated(); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -132,12 +132,29 @@ func (s *adminCmd) Uptime(args *AuthRPCArgs, reply *UptimeReply) error {
|
||||
return errServerNotInitialized
|
||||
}
|
||||
|
||||
// N B The uptime is computed assuming that the system time is
|
||||
// monotonic. This is not the case in time pkg in Go, see
|
||||
// https://github.com/golang/go/issues/12914. This is expected
|
||||
// to be fixed by go1.9.
|
||||
*reply = UptimeReply{
|
||||
Uptime: UTCNow().Sub(globalBootTime),
|
||||
// Build storage info
|
||||
objLayer := newObjectLayerFn()
|
||||
if objLayer == nil {
|
||||
return errServerNotInitialized
|
||||
}
|
||||
storageInfo := objLayer.StorageInfo()
|
||||
|
||||
var arns []string
|
||||
for queueArn := range globalEventNotifier.GetAllExternalTargets() {
|
||||
arns = append(arns, queueArn)
|
||||
}
|
||||
|
||||
reply.ServerInfoData = ServerInfoData{
|
||||
Properties: ServerProperties{
|
||||
Uptime: UTCNow().Sub(globalBootTime),
|
||||
Version: Version,
|
||||
CommitID: CommitID,
|
||||
Region: serverConfig.GetRegion(),
|
||||
SQSARN: arns,
|
||||
},
|
||||
StorageInfo: storageInfo,
|
||||
ConnStats: globalConnStats.toServerConnStats(),
|
||||
HTTPStats: globalHTTPStats.toServerHTTPStats(),
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -52,6 +52,14 @@ func (s *ConnStats) getTotalOutputBytes() uint64 {
|
||||
return s.totalOutputBytes.Load()
|
||||
}
|
||||
|
||||
// Return connection stats (total input/output bytes)
|
||||
func (s *ConnStats) toServerConnStats() ServerConnStats {
|
||||
return ServerConnStats{
|
||||
TotalInputBytes: s.getTotalInputBytes(),
|
||||
TotalOutputBytes: s.getTotalOutputBytes(),
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare new ConnStats structure
|
||||
func newConnStats() *ConnStats {
|
||||
return &ConnStats{}
|
||||
|
@ -121,7 +121,9 @@ func (c *ConnMux) PeekProtocol() (string, error) {
|
||||
// bytes received from the client.
|
||||
func (c *ConnMux) Read(b []byte) (n int, err error) {
|
||||
// Update total incoming number of bytes.
|
||||
defer globalConnStats.incInputBytes(n)
|
||||
defer func() {
|
||||
globalConnStats.incInputBytes(n)
|
||||
}()
|
||||
|
||||
n, err = c.peeker.Read(b)
|
||||
if err != nil {
|
||||
@ -141,7 +143,9 @@ func (c *ConnMux) Read(b []byte) (n int, err error) {
|
||||
// keeps track of the total bytes written by the server.
|
||||
func (c *ConnMux) Write(b []byte) (n int, err error) {
|
||||
// Update total outgoing number of bytes.
|
||||
defer globalConnStats.incOutputBytes(n)
|
||||
defer func() {
|
||||
globalConnStats.incOutputBytes(n)
|
||||
}()
|
||||
|
||||
// Call the conn write wrapper.
|
||||
return c.Conn.Write(b)
|
||||
|
@ -122,7 +122,30 @@ If successful restarts the running minio service, for distributed setup restarts
|
||||
|
||||
```
|
||||
|
||||
## 3. Lock operations
|
||||
## 3. Info operations
|
||||
|
||||
<a name="ServerInfo"></a>
|
||||
### ServerInfo() ([]ServerInfo, error)
|
||||
Fetch all information for all cluster nodes, such as uptime, region, network statistics, etc..
|
||||
|
||||
|
||||
__Example__
|
||||
|
||||
```go
|
||||
|
||||
serversInfo, err := madmClnt.ServerInfo()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
for _, peerInfo := range serversInfo {
|
||||
log.Printf("Node: %s, Info: %v\n", peerInfo.Addr, peerInfo.Data)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## 4. Lock operations
|
||||
|
||||
<a name="ListLocks"></a>
|
||||
### ListLocks(bucket, prefix string, duration time.Duration) ([]VolumeLockInfo, error)
|
||||
@ -154,7 +177,7 @@ __Example__
|
||||
|
||||
```
|
||||
|
||||
## 4. Heal operations
|
||||
## 5. Heal operations
|
||||
|
||||
<a name="ListObjectsHeal"></a>
|
||||
### ListObjectsHeal(bucket, prefix string, recursive bool, doneCh <-chan struct{}) (<-chan ObjectInfo, error)
|
||||
@ -360,7 +383,7 @@ If upload is successfully healed returns nil, otherwise returns error indicating
|
||||
log.Println("Heal-upload result: ", healResult)
|
||||
```
|
||||
|
||||
## 5. Config operations
|
||||
## 6. Config operations
|
||||
|
||||
<a name="GetConfig"></a>
|
||||
### GetConfig() ([]byte, error)
|
||||
@ -384,23 +407,6 @@ __Example__
|
||||
log.Println("config received successfully: ", string(buf.Bytes()))
|
||||
```
|
||||
|
||||
## 6. Misc operations
|
||||
|
||||
<a name="SetCredentials"></a>
|
||||
|
||||
### SetCredentials() error
|
||||
Set new credentials of a Minio setup.
|
||||
|
||||
__Example__
|
||||
|
||||
``` go
|
||||
err = madmClnt.SetCredentials("YOUR-NEW-ACCESSKEY", "YOUR-NEW-SECRETKEY")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
log.Println("New credentials successfully set.")
|
||||
|
||||
```
|
||||
|
||||
<a name="SetConfig"></a>
|
||||
### SetConfig(config io.Reader) (SetConfigResult, error)
|
||||
@ -435,3 +441,22 @@ __Example__
|
||||
}
|
||||
log.Println("SetConfig: ", string(buf.Bytes()))
|
||||
```
|
||||
|
||||
## 7. Misc operations
|
||||
|
||||
<a name="SetCredentials"></a>
|
||||
|
||||
### SetCredentials() error
|
||||
Set new credentials of a Minio setup.
|
||||
|
||||
__Example__
|
||||
|
||||
``` go
|
||||
err = madmClnt.SetCredentials("YOUR-NEW-ACCESSKEY", "YOUR-NEW-SECRETKEY")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
log.Println("New credentials successfully set.")
|
||||
|
||||
```
|
||||
|
||||
|
@ -74,17 +74,24 @@ type ServerConnStats struct {
|
||||
TotalOutputBytes uint64 `json:"received"`
|
||||
}
|
||||
|
||||
// ServerInfo holds the whole server information that will be
|
||||
// returned by ServerInfo API.
|
||||
type ServerInfo struct {
|
||||
// ServerInfoData holds storage, connections and other
|
||||
// information of a given server
|
||||
type ServerInfoData struct {
|
||||
StorageInfo StorageInfo `json:"storage"`
|
||||
ConnStats ServerConnStats `json:"network"`
|
||||
Properties ServerProperties `json:"server"`
|
||||
}
|
||||
|
||||
// ServerInfo holds server information result of one node
|
||||
type ServerInfo struct {
|
||||
Error error `json:"error"`
|
||||
Addr string `json:"addr"`
|
||||
Data *ServerInfoData `json:"data"`
|
||||
}
|
||||
|
||||
// ServerInfo - Connect to a minio server and call Server Info Management API
|
||||
// to fetch server's information represented by ServerInfo structure
|
||||
func (adm *AdminClient) ServerInfo() (ServerInfo, error) {
|
||||
func (adm *AdminClient) ServerInfo() ([]ServerInfo, error) {
|
||||
// Prepare web service request
|
||||
reqData := requestData{}
|
||||
reqData.queryValues = make(url.Values)
|
||||
@ -94,26 +101,26 @@ func (adm *AdminClient) ServerInfo() (ServerInfo, error) {
|
||||
resp, err := adm.executeMethod("GET", reqData)
|
||||
defer closeResponse(resp)
|
||||
if err != nil {
|
||||
return ServerInfo{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check response http status code
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return ServerInfo{}, httpRespToErrorResponse(resp)
|
||||
return nil, httpRespToErrorResponse(resp)
|
||||
}
|
||||
|
||||
// Unmarshal the server's json response
|
||||
var info ServerInfo
|
||||
var serversInfo []ServerInfo
|
||||
|
||||
respBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return ServerInfo{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(respBytes, &info)
|
||||
err = json.Unmarshal(respBytes, &serversInfo)
|
||||
if err != nil {
|
||||
return ServerInfo{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return info, nil
|
||||
return serversInfo, nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user