From 7f86a21317f8369324ecc447a0a0bf01b60fac3e Mon Sep 17 00:00:00 2001 From: Anis Elleuch Date: Wed, 15 Feb 2017 19:45:45 +0100 Subject: [PATCH] admin: Add ServerInfo API() (#3743) --- cmd/admin-handlers.go | 92 +++++++++++++++++++++++++ cmd/admin-router.go | 3 + cmd/event-notifier.go | 17 ++++- cmd/server-startup-msg.go | 6 +- pkg/madmin/info-commands.go | 119 +++++++++++++++++++++++++++++++++ pkg/madmin/service-commands.go | 45 +------------ 6 files changed, 235 insertions(+), 47 deletions(-) create mode 100644 pkg/madmin/info-commands.go diff --git a/cmd/admin-handlers.go b/cmd/admin-handlers.go index 4a7bb547b..016db0a24 100644 --- a/cmd/admin-handlers.go +++ b/cmd/admin-handlers.go @@ -188,6 +188,98 @@ func (adminAPI adminAPIHandlers) ServiceCredentialsHandler(w http.ResponseWriter w.WriteHeader(http.StatusOK) } +// ServerProperties holds some server information such as, version, region +// uptime, etc.. +type ServerProperties struct { + Uptime time.Duration `json:"uptime"` + Version string `json:"version"` + CommitID string `json:"commitID"` + Region string `json:"region"` + SQSARN []string `json:"sqsARN"` +} + +// ServerConnStats holds transferred bytes from/to the server +type ServerConnStats struct { + TotalInputBytes uint64 `json:"transferred"` + TotalOutputBytes uint64 `json:"received"` + Throughput uint64 `json:"throughput,omitempty"` +} + +// ServerInfo holds the information that will be returned by ServerInfo API +type ServerInfo struct { + StorageInfo StorageInfo `json:"storage"` + ConnStats ServerConnStats `json:"network"` + Properties ServerProperties `json:"server"` +} + +// ServerInfoHandler - GET /?server-info +// ---------- +// Get server information +func (adminAPI adminAPIHandlers) ServerInfoHandler(w http.ResponseWriter, r *http.Request) { + // Authenticate request + adminAPIErr := checkRequestAuthType(r, "", "", "") + if adminAPIErr != ErrNone { + writeErrorResponse(w, adminAPIErr, r.URL) + return + } + + // Build storage info + objLayer := newObjectLayerFn() + if objLayer == nil { + writeErrorResponse(w, ErrServerNotInitialized, r.URL) + return + } + storage := objLayer.StorageInfo() + + // Build list of enabled ARNs queues + var arns []string + for queueArn := range globalEventNotifier.GetAllExternalTargets() { + arns = append(arns, queueArn) + } + + // 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(), + } + + // Build the whole returned information + info := ServerInfo{ + StorageInfo: storage, + ConnStats: connStats, + Properties: properties, + } + + // Marshal API response + jsonBytes, err := json.Marshal(info) + if err != nil { + writeErrorResponse(w, ErrInternalError, r.URL) + errorIf(err, "Failed to marshal storage info into json.") + return + } + // Reply with storage information (across nodes in a + // distributed setup) as json. + writeSuccessResponseJSON(w, jsonBytes) +} + // validateLockQueryParams - Validates query params for list/clear locks management APIs. func validateLockQueryParams(vars url.Values) (string, string, time.Duration, APIErrorCode) { bucket := vars.Get(string(mgmtBucket)) diff --git a/cmd/admin-router.go b/cmd/admin-router.go index 3136670f6..b22794c97 100644 --- a/cmd/admin-router.go +++ b/cmd/admin-router.go @@ -39,6 +39,9 @@ func registerAdminRouter(mux *router.Router) { // Service update credentials adminRouter.Methods("POST").Queries("service", "").Headers(minioAdminOpHeader, "set-credentials").HandlerFunc(adminAPI.ServiceCredentialsHandler) + // Info operations + adminRouter.Methods("GET").Queries("info", "").HandlerFunc(adminAPI.ServerInfoHandler) + /// Lock operations // List Locks diff --git a/cmd/event-notifier.go b/cmd/event-notifier.go index 8ebc5462d..9db12cccf 100644 --- a/cmd/event-notifier.go +++ b/cmd/event-notifier.go @@ -162,9 +162,22 @@ func newNotificationEvent(event eventData) NotificationEvent { return nEvent } -// Fetch the external target. No locking needed here since this map is -// never written after initial startup. +// Fetch all external targets. This returns a copy of the current map of +// external notification targets. +func (en eventNotifier) GetAllExternalTargets() map[string]*logrus.Logger { + en.external.rwMutex.RLock() + defer en.external.rwMutex.RUnlock() + targetsCopy := make(map[string]*logrus.Logger) + for k, v := range en.external.targets { + targetsCopy[k] = v + } + return targetsCopy +} + +// Fetch the external target. func (en eventNotifier) GetExternalTarget(queueARN string) *logrus.Logger { + en.external.rwMutex.RLock() + defer en.external.rwMutex.RUnlock() return en.external.targets[queueARN] } diff --git a/cmd/server-startup-msg.go b/cmd/server-startup-msg.go index 55012fc7c..53718aa74 100644 --- a/cmd/server-startup-msg.go +++ b/cmd/server-startup-msg.go @@ -101,10 +101,12 @@ func printEventNotifiers() { return } arnMsg := colorBlue("SQS ARNs: ") - if len(globalEventNotifier.external.targets) == 0 { + // Get all configured external notification targets + externalTargets := globalEventNotifier.GetAllExternalTargets() + if len(externalTargets) == 0 { arnMsg += colorBold(fmt.Sprintf(getFormatStr(len(""), 1), "")) } - for queueArn := range globalEventNotifier.external.targets { + for queueArn := range externalTargets { arnMsg += colorBold(fmt.Sprintf(getFormatStr(len(queueArn), 1), queueArn)) } console.Println(arnMsg) diff --git a/pkg/madmin/info-commands.go b/pkg/madmin/info-commands.go new file mode 100644 index 000000000..1c41fdf80 --- /dev/null +++ b/pkg/madmin/info-commands.go @@ -0,0 +1,119 @@ +/* + * Minio Cloud Storage, (C) 2017 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 madmin + +import ( + "encoding/json" + "io/ioutil" + "net/http" + "net/url" + "time" +) + +// BackendType - represents different backend types. +type BackendType int + +// Enum for different backend types. +const ( + Unknown BackendType = iota + // Filesystem backend. + FS + // Multi disk Erasure (single, distributed) backend. + Erasure + + // Add your own backend. +) + +// StorageInfo - represents total capacity of underlying storage. +type StorageInfo struct { + // Total disk space. + Total int64 + // Free available disk space. + Free int64 + // Backend type. + Backend struct { + // Represents various backend types, currently on FS and Erasure. + Type BackendType + + // Following fields are only meaningful if BackendType is Erasure. + OnlineDisks int // Online disks during server startup. + OfflineDisks int // Offline disks during server startup. + ReadQuorum int // Minimum disks required for successful read operations. + WriteQuorum int // Minimum disks required for successful write operations. + } +} + +// ServerProperties holds some of the server's information such as uptime, +// version, region, .. +type ServerProperties struct { + Uptime time.Duration `json:"uptime"` + Version string `json:"version"` + CommitID string `json:"commitID"` + Region string `json:"region"` + SQSARN []string `json:"sqsARN"` +} + +// ServerConnStats holds network information +type ServerConnStats struct { + TotalInputBytes uint64 `json:"transferred"` + TotalOutputBytes uint64 `json:"received"` +} + +// ServerInfo holds the whole server information that will be +// returned by ServerInfo API. +type ServerInfo struct { + StorageInfo StorageInfo `json:"storage"` + ConnStats ServerConnStats `json:"network"` + Properties ServerProperties `json:"server"` +} + +// 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) { + // Prepare web service request + reqData := requestData{} + reqData.queryValues = make(url.Values) + reqData.queryValues.Set("info", "") + reqData.customHeaders = make(http.Header) + + resp, err := adm.executeMethod("GET", reqData) + defer closeResponse(resp) + if err != nil { + return ServerInfo{}, err + } + + // Check response http status code + if resp.StatusCode != http.StatusOK { + return ServerInfo{}, httpRespToErrorResponse(resp) + } + + // Unmarshal the server's json response + var info ServerInfo + + respBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return ServerInfo{}, err + } + + err = json.Unmarshal(respBytes, &info) + if err != nil { + return ServerInfo{}, err + } + + return info, nil +} diff --git a/pkg/madmin/service-commands.go b/pkg/madmin/service-commands.go index 292340ed7..ee22a3feb 100644 --- a/pkg/madmin/service-commands.go +++ b/pkg/madmin/service-commands.go @@ -1,5 +1,5 @@ /* - * Minio Cloud Storage, (C) 2016 Minio, Inc. + * Minio Cloud Storage, (C) 2016, 2017 Minio, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,50 +28,9 @@ import ( "time" ) -// BackendType - represents different backend types. -type BackendType int - -// Enum for different backend types. -const ( - Unknown BackendType = iota - // Filesystem backend. - FS - // Multi disk Erasure (single, distributed) backend. - Erasure - - // Add your own backend. -) - -// StorageInfo - represents total capacity of underlying storage. -type StorageInfo struct { - // Total disk space. - Total int64 - // Free available disk space. - Free int64 - // Backend type. - Backend struct { - // Represents various backend types, currently on FS and Erasure. - Type BackendType - - // Following fields are only meaningful if BackendType is Erasure. - OnlineDisks int // Online disks during server startup. - OfflineDisks int // Offline disks during server startup. - ReadQuorum int // Minimum disks required for successful read operations. - WriteQuorum int // Minimum disks required for successful write operations. - } -} - -// ServerVersion - server version -type ServerVersion struct { - Version string `json:"version"` - CommitID string `json:"commitID"` -} - // ServiceStatusMetadata - contains the response of service status API type ServiceStatusMetadata struct { - StorageInfo StorageInfo `json:"storageInfo"` - ServerVersion ServerVersion `json:"serverVersion"` - Uptime time.Duration `json:"uptime"` + Uptime time.Duration `json:"uptime"` } // ServiceStatus - Connect to a minio server and call Service Status Management API