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/>.
|
2019-03-14 19:27:31 -04:00
|
|
|
|
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/gob"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
2019-08-18 22:56:32 -04:00
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
2019-03-14 19:27:31 -04:00
|
|
|
"net/http"
|
2019-06-06 20:46:22 -04:00
|
|
|
"strconv"
|
2019-03-14 19:27:31 -04:00
|
|
|
"strings"
|
2021-07-27 15:55:56 -04:00
|
|
|
"sync"
|
|
|
|
"sync/atomic"
|
2019-03-14 19:27:31 -04:00
|
|
|
"time"
|
|
|
|
|
2021-10-28 10:36:34 -04:00
|
|
|
"github.com/dustin/go-humanize"
|
2021-07-27 15:55:56 -04:00
|
|
|
"github.com/google/uuid"
|
2019-03-14 19:27:31 -04:00
|
|
|
"github.com/gorilla/mux"
|
2021-05-06 11:52:02 -04:00
|
|
|
"github.com/minio/madmin-go"
|
2021-06-01 17:59:40 -04:00
|
|
|
b "github.com/minio/minio/internal/bucket/bandwidth"
|
|
|
|
"github.com/minio/minio/internal/event"
|
2021-07-27 15:55:56 -04:00
|
|
|
"github.com/minio/minio/internal/hash"
|
2021-11-29 12:05:46 -05:00
|
|
|
xhttp "github.com/minio/minio/internal/http"
|
2021-06-01 17:59:40 -04:00
|
|
|
"github.com/minio/minio/internal/logger"
|
2021-11-19 13:41:37 -05:00
|
|
|
"github.com/minio/pkg/randreader"
|
2020-10-28 12:18:35 -04:00
|
|
|
"github.com/tinylib/msgp/msgp"
|
2019-03-14 19:27:31 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
// To abstract a node over network.
|
2020-05-19 16:53:54 -04:00
|
|
|
type peerRESTServer struct{}
|
2019-03-14 19:27:31 -04:00
|
|
|
|
|
|
|
// GetLocksHandler - returns list of older lock from the server.
|
|
|
|
func (s *peerRESTServer) GetLocksHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := newContext(r, w, "GetLocks")
|
2020-12-10 10:28:37 -05:00
|
|
|
logger.LogIf(ctx, gob.NewEncoder(w).Encode(globalLockServer.DupLockMap()))
|
2019-03-14 19:27:31 -04:00
|
|
|
}
|
|
|
|
|
2019-06-06 20:46:22 -04:00
|
|
|
// DeletePolicyHandler - deletes a policy on the server.
|
|
|
|
func (s *peerRESTServer) DeletePolicyHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-10-09 12:59:52 -04:00
|
|
|
objAPI := newObjectLayerFn()
|
2019-06-06 20:46:22 -04:00
|
|
|
if objAPI == nil {
|
|
|
|
s.writeErrorResponse(w, errServerNotInitialized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
vars := mux.Vars(r)
|
|
|
|
policyName := vars[peerRESTPolicy]
|
|
|
|
if policyName == "" {
|
|
|
|
s.writeErrorResponse(w, errors.New("policyName is missing"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-11-29 17:38:57 -05:00
|
|
|
if err := globalIAMSys.DeletePolicy(r.Context(), policyName, false); err != nil {
|
2019-06-06 20:46:22 -04:00
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// LoadPolicyHandler - reloads a policy on the server.
|
|
|
|
func (s *peerRESTServer) LoadPolicyHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-10-09 12:59:52 -04:00
|
|
|
objAPI := newObjectLayerFn()
|
2019-06-06 20:46:22 -04:00
|
|
|
if objAPI == nil {
|
|
|
|
s.writeErrorResponse(w, errServerNotInitialized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
vars := mux.Vars(r)
|
|
|
|
policyName := vars[peerRESTPolicy]
|
|
|
|
if policyName == "" {
|
|
|
|
s.writeErrorResponse(w, errors.New("policyName is missing"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-11-15 17:14:22 -05:00
|
|
|
if err := globalIAMSys.LoadPolicy(r.Context(), objAPI, policyName); err != nil {
|
2019-06-06 20:46:22 -04:00
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-13 16:41:06 -04:00
|
|
|
// LoadPolicyMappingHandler - reloads a policy mapping on the server.
|
|
|
|
func (s *peerRESTServer) LoadPolicyMappingHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-10-09 12:59:52 -04:00
|
|
|
objAPI := newObjectLayerFn()
|
2019-08-13 16:41:06 -04:00
|
|
|
if objAPI == nil {
|
|
|
|
s.writeErrorResponse(w, errServerNotInitialized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
vars := mux.Vars(r)
|
|
|
|
userOrGroup := vars[peerRESTUserOrGroup]
|
|
|
|
if userOrGroup == "" {
|
|
|
|
s.writeErrorResponse(w, errors.New("user-or-group is missing"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-08-08 01:43:01 -04:00
|
|
|
_, isGroup := r.Form[peerRESTIsGroup]
|
2021-11-15 17:14:22 -05:00
|
|
|
if err := globalIAMSys.LoadPolicyMapping(r.Context(), objAPI, userOrGroup, isGroup); err != nil {
|
2019-08-13 16:41:06 -04:00
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-24 15:10:09 -04:00
|
|
|
// DeleteServiceAccountHandler - deletes a service account on the server.
|
|
|
|
func (s *peerRESTServer) DeleteServiceAccountHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-10-09 12:59:52 -04:00
|
|
|
objAPI := newObjectLayerFn()
|
2020-04-24 15:10:09 -04:00
|
|
|
if objAPI == nil {
|
|
|
|
s.writeErrorResponse(w, errServerNotInitialized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
vars := mux.Vars(r)
|
|
|
|
accessKey := vars[peerRESTUser]
|
|
|
|
if accessKey == "" {
|
|
|
|
s.writeErrorResponse(w, errors.New("service account name is missing"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-21 05:16:50 -05:00
|
|
|
if err := globalIAMSys.DeleteServiceAccount(r.Context(), accessKey, false); err != nil {
|
2020-04-24 15:10:09 -04:00
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// LoadServiceAccountHandler - reloads a service account on the server.
|
|
|
|
func (s *peerRESTServer) LoadServiceAccountHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-10-09 12:59:52 -04:00
|
|
|
objAPI := newObjectLayerFn()
|
2020-04-24 15:10:09 -04:00
|
|
|
if objAPI == nil {
|
|
|
|
s.writeErrorResponse(w, errServerNotInitialized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
vars := mux.Vars(r)
|
|
|
|
accessKey := vars[peerRESTUser]
|
|
|
|
if accessKey == "" {
|
|
|
|
s.writeErrorResponse(w, errors.New("service account parameter is missing"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-11-15 17:14:22 -05:00
|
|
|
if err := globalIAMSys.LoadServiceAccount(r.Context(), accessKey); err != nil {
|
2020-04-24 15:10:09 -04:00
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-06 20:46:22 -04:00
|
|
|
// DeleteUserHandler - deletes a user on the server.
|
|
|
|
func (s *peerRESTServer) DeleteUserHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-10-09 12:59:52 -04:00
|
|
|
objAPI := newObjectLayerFn()
|
2019-06-06 20:46:22 -04:00
|
|
|
if objAPI == nil {
|
|
|
|
s.writeErrorResponse(w, errServerNotInitialized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
vars := mux.Vars(r)
|
|
|
|
accessKey := vars[peerRESTUser]
|
|
|
|
if accessKey == "" {
|
|
|
|
s.writeErrorResponse(w, errors.New("username is missing"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-21 05:16:50 -05:00
|
|
|
if err := globalIAMSys.DeleteUser(r.Context(), accessKey, false); err != nil {
|
2019-06-06 20:46:22 -04:00
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// LoadUserHandler - reloads a user on the server.
|
|
|
|
func (s *peerRESTServer) LoadUserHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-10-09 12:59:52 -04:00
|
|
|
objAPI := newObjectLayerFn()
|
2019-06-06 20:46:22 -04:00
|
|
|
if objAPI == nil {
|
|
|
|
s.writeErrorResponse(w, errServerNotInitialized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
vars := mux.Vars(r)
|
|
|
|
accessKey := vars[peerRESTUser]
|
|
|
|
if accessKey == "" {
|
|
|
|
s.writeErrorResponse(w, errors.New("username is missing"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
temp, err := strconv.ParseBool(vars[peerRESTUserTemp])
|
|
|
|
if err != nil {
|
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-01-02 12:15:06 -05:00
|
|
|
userType := regUser
|
2020-03-17 13:36:13 -04:00
|
|
|
if temp {
|
|
|
|
userType = stsUser
|
|
|
|
}
|
|
|
|
|
2021-11-15 17:14:22 -05:00
|
|
|
if err = globalIAMSys.LoadUser(r.Context(), objAPI, accessKey, userType); err != nil {
|
2019-06-06 20:46:22 -04:00
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-02 17:25:00 -04:00
|
|
|
// LoadGroupHandler - reloads group along with members list.
|
|
|
|
func (s *peerRESTServer) LoadGroupHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-10-09 12:59:52 -04:00
|
|
|
objAPI := newObjectLayerFn()
|
2019-08-02 17:25:00 -04:00
|
|
|
if objAPI == nil {
|
|
|
|
s.writeErrorResponse(w, errServerNotInitialized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
vars := mux.Vars(r)
|
|
|
|
group := vars[peerRESTGroup]
|
2021-11-15 17:14:22 -05:00
|
|
|
err := globalIAMSys.LoadGroup(r.Context(), objAPI, group)
|
2019-08-02 17:25:00 -04:00
|
|
|
if err != nil {
|
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-14 19:27:31 -04:00
|
|
|
// StartProfilingHandler - Issues the start profiling command.
|
|
|
|
func (s *peerRESTServer) StartProfilingHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
vars := mux.Vars(r)
|
2020-01-10 20:19:58 -05:00
|
|
|
profiles := strings.Split(vars[peerRESTProfiler], ",")
|
|
|
|
if len(profiles) == 0 {
|
2019-03-14 19:27:31 -04:00
|
|
|
s.writeErrorResponse(w, errors.New("profiler name is missing"))
|
|
|
|
return
|
|
|
|
}
|
2020-01-10 20:19:58 -05:00
|
|
|
globalProfilerMu.Lock()
|
|
|
|
defer globalProfilerMu.Unlock()
|
|
|
|
if globalProfiler == nil {
|
|
|
|
globalProfiler = make(map[string]minioProfiler, 10)
|
|
|
|
}
|
2019-03-14 19:27:31 -04:00
|
|
|
|
2020-01-10 20:19:58 -05:00
|
|
|
// Stop profiler of all types if already running
|
|
|
|
for k, v := range globalProfiler {
|
|
|
|
for _, p := range profiles {
|
|
|
|
if p == k {
|
|
|
|
v.Stop()
|
|
|
|
delete(globalProfiler, k)
|
|
|
|
}
|
|
|
|
}
|
2019-03-14 19:27:31 -04:00
|
|
|
}
|
|
|
|
|
2020-01-10 20:19:58 -05:00
|
|
|
for _, profiler := range profiles {
|
|
|
|
prof, err := startProfiler(profiler)
|
|
|
|
if err != nil {
|
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
globalProfiler[profiler] = prof
|
2019-03-14 19:27:31 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-10 20:19:58 -05:00
|
|
|
// DownloadProfilingDataHandler - returns profiled data.
|
|
|
|
func (s *peerRESTServer) DownloadProfilingDataHandler(w http.ResponseWriter, r *http.Request) {
|
2019-03-14 19:27:31 -04:00
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := newContext(r, w, "DownloadProfiling")
|
|
|
|
profileData, err := getProfileData()
|
|
|
|
if err != nil {
|
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
logger.LogIf(ctx, gob.NewEncoder(w).Encode(profileData))
|
|
|
|
}
|
|
|
|
|
2019-12-11 17:27:03 -05:00
|
|
|
// ServerInfoHandler - returns Server Info
|
|
|
|
func (s *peerRESTServer) ServerInfoHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := newContext(r, w, "ServerInfo")
|
|
|
|
info := getLocalServerProperty(globalEndpoints, r)
|
|
|
|
|
|
|
|
logger.LogIf(ctx, gob.NewEncoder(w).Encode(info))
|
|
|
|
}
|
|
|
|
|
2020-11-20 15:52:53 -05:00
|
|
|
func (s *peerRESTServer) NetInfoHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := newContext(r, w, "NetInfo")
|
2020-03-27 00:07:39 -04:00
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Use this trailer to send additional headers after sending body
|
|
|
|
w.Header().Set("Trailer", "FinalStatus")
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/octet-stream")
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
|
|
|
n, err := io.Copy(ioutil.Discard, r.Body)
|
|
|
|
if err == io.ErrUnexpectedEOF {
|
|
|
|
w.Header().Set("FinalStatus", err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if err != nil && err != io.EOF {
|
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
w.Header().Set("FinalStatus", err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if n != r.ContentLength {
|
2020-11-20 15:52:53 -05:00
|
|
|
err := fmt.Errorf("Subnet health: short read: expected %d found %d", r.ContentLength, n)
|
2020-03-27 00:07:39 -04:00
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
w.Header().Set("FinalStatus", err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
w.Header().Set("FinalStatus", "Success")
|
|
|
|
}
|
|
|
|
|
2020-11-20 15:52:53 -05:00
|
|
|
func (s *peerRESTServer) DispatchNetInfoHandler(w http.ResponseWriter, r *http.Request) {
|
2020-03-27 00:07:39 -04:00
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-04-18 14:06:11 -04:00
|
|
|
done := keepHTTPResponseAlive(w)
|
|
|
|
|
2020-11-20 15:52:53 -05:00
|
|
|
ctx := newContext(r, w, "DispatchNetInfo")
|
2021-06-01 11:55:49 -04:00
|
|
|
info := globalNotificationSys.GetNetPerfInfo(ctx)
|
2020-03-27 00:07:39 -04:00
|
|
|
|
2020-05-11 23:41:38 -04:00
|
|
|
done(nil)
|
2020-03-27 00:07:39 -04:00
|
|
|
logger.LogIf(ctx, gob.NewEncoder(w).Encode(info))
|
|
|
|
}
|
|
|
|
|
2021-06-01 11:55:49 -04:00
|
|
|
// GetDrivePerfInfosHandler - returns all disk's serial/parallal performance information.
|
|
|
|
func (s *peerRESTServer) GetDrivePerfInfosHandler(w http.ResponseWriter, r *http.Request) {
|
2020-03-27 00:07:39 -04:00
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-11-20 15:52:53 -05:00
|
|
|
ctx, cancel := context.WithCancel(newContext(r, w, "DriveInfo"))
|
2020-03-27 00:07:39 -04:00
|
|
|
defer cancel()
|
|
|
|
|
2021-06-01 11:55:49 -04:00
|
|
|
info := getDrivePerfInfos(ctx, r.Host)
|
|
|
|
|
2020-03-27 00:07:39 -04:00
|
|
|
logger.LogIf(ctx, gob.NewEncoder(w).Encode(info))
|
|
|
|
}
|
|
|
|
|
2021-06-01 11:55:49 -04:00
|
|
|
// GetCPUsHandler - returns CPU info.
|
|
|
|
func (s *peerRESTServer) GetCPUsHandler(w http.ResponseWriter, r *http.Request) {
|
2020-03-27 00:07:39 -04:00
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-05-18 12:59:45 -04:00
|
|
|
ctx, cancel := context.WithCancel(r.Context())
|
2020-03-27 00:07:39 -04:00
|
|
|
defer cancel()
|
2020-05-18 12:59:45 -04:00
|
|
|
|
2021-06-01 11:55:49 -04:00
|
|
|
info := madmin.GetCPUs(ctx, r.Host)
|
2020-03-27 00:07:39 -04:00
|
|
|
|
|
|
|
logger.LogIf(ctx, gob.NewEncoder(w).Encode(info))
|
|
|
|
}
|
|
|
|
|
2021-06-01 11:55:49 -04:00
|
|
|
// GetPartitionsHandler - returns disk partition information.
|
|
|
|
func (s *peerRESTServer) GetPartitionsHandler(w http.ResponseWriter, r *http.Request) {
|
2020-03-27 00:07:39 -04:00
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-05-18 12:59:45 -04:00
|
|
|
ctx, cancel := context.WithCancel(r.Context())
|
2020-03-27 00:07:39 -04:00
|
|
|
defer cancel()
|
2020-05-18 12:59:45 -04:00
|
|
|
|
2021-06-01 11:55:49 -04:00
|
|
|
info := madmin.GetPartitions(ctx, r.Host)
|
2020-03-27 00:07:39 -04:00
|
|
|
|
|
|
|
logger.LogIf(ctx, gob.NewEncoder(w).Encode(info))
|
|
|
|
}
|
|
|
|
|
2021-06-01 11:55:49 -04:00
|
|
|
// GetOSInfoHandler - returns operating system's information.
|
|
|
|
func (s *peerRESTServer) GetOSInfoHandler(w http.ResponseWriter, r *http.Request) {
|
2020-03-27 00:07:39 -04:00
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-05-18 12:59:45 -04:00
|
|
|
ctx, cancel := context.WithCancel(r.Context())
|
2020-03-27 00:07:39 -04:00
|
|
|
defer cancel()
|
2020-05-18 12:59:45 -04:00
|
|
|
|
2021-06-01 11:55:49 -04:00
|
|
|
info := madmin.GetOSInfo(ctx, r.Host)
|
2020-03-27 00:07:39 -04:00
|
|
|
|
|
|
|
logger.LogIf(ctx, gob.NewEncoder(w).Encode(info))
|
|
|
|
}
|
|
|
|
|
2021-06-01 11:55:49 -04:00
|
|
|
// GetProcInfoHandler - returns this MinIO process information.
|
|
|
|
func (s *peerRESTServer) GetProcInfoHandler(w http.ResponseWriter, r *http.Request) {
|
2020-03-27 00:07:39 -04:00
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-05-18 12:59:45 -04:00
|
|
|
ctx, cancel := context.WithCancel(r.Context())
|
2020-03-27 00:07:39 -04:00
|
|
|
defer cancel()
|
2020-05-18 12:59:45 -04:00
|
|
|
|
2021-06-01 11:55:49 -04:00
|
|
|
info := madmin.GetProcInfo(ctx, r.Host)
|
2020-03-27 00:07:39 -04:00
|
|
|
|
|
|
|
logger.LogIf(ctx, gob.NewEncoder(w).Encode(info))
|
|
|
|
}
|
|
|
|
|
2021-06-01 11:55:49 -04:00
|
|
|
// GetMemInfoHandler - returns memory information.
|
|
|
|
func (s *peerRESTServer) GetMemInfoHandler(w http.ResponseWriter, r *http.Request) {
|
2020-03-27 00:07:39 -04:00
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-05-18 12:59:45 -04:00
|
|
|
ctx, cancel := context.WithCancel(r.Context())
|
2020-03-27 00:07:39 -04:00
|
|
|
defer cancel()
|
2020-05-18 12:59:45 -04:00
|
|
|
|
2021-06-01 11:55:49 -04:00
|
|
|
info := madmin.GetMemInfo(ctx, r.Host)
|
2020-03-27 00:07:39 -04:00
|
|
|
|
|
|
|
logger.LogIf(ctx, gob.NewEncoder(w).Encode(info))
|
|
|
|
}
|
|
|
|
|
2021-08-24 20:09:37 -04:00
|
|
|
// GetSysConfigHandler - returns system config information.
|
|
|
|
// (only the config that are of concern to minio)
|
|
|
|
func (s *peerRESTServer) GetSysConfigHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx, cancel := context.WithCancel(r.Context())
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
info := madmin.GetSysConfig(ctx, r.Host)
|
|
|
|
|
|
|
|
logger.LogIf(ctx, gob.NewEncoder(w).Encode(info))
|
|
|
|
}
|
|
|
|
|
2021-08-12 21:58:40 -04:00
|
|
|
// GetSysServicesHandler - returns system services information.
|
|
|
|
// (only the services that are of concern to minio)
|
|
|
|
func (s *peerRESTServer) GetSysServicesHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx, cancel := context.WithCancel(r.Context())
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
info := madmin.GetSysServices(ctx, r.Host)
|
|
|
|
|
|
|
|
logger.LogIf(ctx, gob.NewEncoder(w).Encode(info))
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetSysErrorsHandler - returns system level errors
|
2021-07-30 02:05:34 -04:00
|
|
|
func (s *peerRESTServer) GetSysErrorsHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx, cancel := context.WithCancel(r.Context())
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
info := madmin.GetSysErrors(ctx, r.Host)
|
|
|
|
|
|
|
|
logger.LogIf(ctx, gob.NewEncoder(w).Encode(info))
|
|
|
|
}
|
|
|
|
|
2020-05-19 16:53:54 -04:00
|
|
|
// DeleteBucketMetadataHandler - Delete in memory bucket metadata
|
|
|
|
func (s *peerRESTServer) DeleteBucketMetadataHandler(w http.ResponseWriter, r *http.Request) {
|
2019-03-14 19:27:31 -04:00
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
vars := mux.Vars(r)
|
|
|
|
bucketName := vars[peerRESTBucket]
|
|
|
|
if bucketName == "" {
|
|
|
|
s.writeErrorResponse(w, errors.New("Bucket name is missing"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-04-04 18:34:33 -04:00
|
|
|
globalReplicationStats.Delete(bucketName)
|
2020-05-19 16:53:54 -04:00
|
|
|
globalBucketMetadataSys.Remove(bucketName)
|
2021-06-24 11:39:58 -04:00
|
|
|
globalBucketTargetSys.Delete(bucketName)
|
2020-10-31 12:46:18 -04:00
|
|
|
if localMetacacheMgr != nil {
|
|
|
|
localMetacacheMgr.deleteBucketCache(bucketName)
|
|
|
|
}
|
2019-03-14 19:27:31 -04:00
|
|
|
}
|
|
|
|
|
2021-10-06 19:36:31 -04:00
|
|
|
// ReloadSiteReplicationConfigHandler - reloads site replication configuration from the disks
|
|
|
|
func (s *peerRESTServer) ReloadSiteReplicationConfigHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := newContext(r, w, "LoadSiteReplication")
|
|
|
|
|
|
|
|
objAPI := newObjectLayerFn()
|
|
|
|
if objAPI == nil {
|
|
|
|
s.writeErrorResponse(w, errServerNotInitialized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.LogIf(r.Context(), globalSiteReplicationSys.Init(ctx, objAPI))
|
|
|
|
}
|
|
|
|
|
2021-04-04 18:34:33 -04:00
|
|
|
// GetBucketStatsHandler - fetches current in-memory bucket stats, currently only
|
|
|
|
// returns BucketReplicationStatus
|
|
|
|
func (s *peerRESTServer) GetBucketStatsHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
vars := mux.Vars(r)
|
|
|
|
bucketName := vars[peerRESTBucket]
|
|
|
|
if bucketName == "" {
|
|
|
|
s.writeErrorResponse(w, errors.New("Bucket name is missing"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
bs := BucketStats{
|
|
|
|
ReplicationStats: globalReplicationStats.Get(bucketName),
|
|
|
|
}
|
|
|
|
logger.LogIf(r.Context(), msgp.Encode(w, &bs))
|
|
|
|
}
|
|
|
|
|
2020-05-19 16:53:54 -04:00
|
|
|
// LoadBucketMetadataHandler - reloads in memory bucket metadata
|
|
|
|
func (s *peerRESTServer) LoadBucketMetadataHandler(w http.ResponseWriter, r *http.Request) {
|
2019-03-14 19:27:31 -04:00
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
vars := mux.Vars(r)
|
2020-05-19 16:53:54 -04:00
|
|
|
bucketName := vars[peerRESTBucket]
|
|
|
|
if bucketName == "" {
|
|
|
|
s.writeErrorResponse(w, errors.New("Bucket name is missing"))
|
2019-03-14 19:27:31 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-10-09 12:59:52 -04:00
|
|
|
objAPI := newObjectLayerFn()
|
2019-03-14 19:27:31 -04:00
|
|
|
if objAPI == nil {
|
|
|
|
s.writeErrorResponse(w, errServerNotInitialized)
|
|
|
|
return
|
|
|
|
}
|
2020-01-10 05:35:06 -05:00
|
|
|
|
2020-05-19 16:53:54 -04:00
|
|
|
meta, err := loadBucketMetadata(r.Context(), objAPI, bucketName)
|
2019-03-14 19:27:31 -04:00
|
|
|
if err != nil {
|
2020-05-20 13:18:15 -04:00
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
2019-03-14 19:27:31 -04:00
|
|
|
}
|
|
|
|
|
2020-05-19 16:53:54 -04:00
|
|
|
globalBucketMetadataSys.Set(bucketName, meta)
|
2020-06-26 16:17:31 -04:00
|
|
|
|
|
|
|
if meta.notificationConfig != nil {
|
|
|
|
globalNotificationSys.AddRulesMap(bucketName, meta.notificationConfig.ToRulesMap())
|
|
|
|
}
|
2020-08-06 20:10:21 -04:00
|
|
|
|
|
|
|
if meta.bucketTargetConfig != nil {
|
2020-10-09 23:36:00 -04:00
|
|
|
globalBucketTargetSys.UpdateAllTargets(bucketName, meta.bucketTargetConfig)
|
2020-08-06 20:10:21 -04:00
|
|
|
}
|
2019-03-14 19:27:31 -04:00
|
|
|
}
|
|
|
|
|
2020-10-28 12:18:35 -04:00
|
|
|
// CycleServerBloomFilterHandler cycles bloom filter on server.
|
2020-04-27 13:06:21 -04:00
|
|
|
func (s *peerRESTServer) CycleServerBloomFilterHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := newContext(r, w, "CycleServerBloomFilter")
|
|
|
|
|
|
|
|
var req bloomFilterRequest
|
|
|
|
err := gob.NewDecoder(r.Body).Decode(&req)
|
|
|
|
if err != nil {
|
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
|
|
|
}
|
2020-10-28 12:18:35 -04:00
|
|
|
bf, err := intDataUpdateTracker.cycleFilter(ctx, req)
|
2020-04-27 13:06:21 -04:00
|
|
|
if err != nil {
|
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
|
|
|
}
|
2020-06-12 23:04:01 -04:00
|
|
|
|
2020-04-27 13:06:21 -04:00
|
|
|
logger.LogIf(ctx, gob.NewEncoder(w).Encode(bf))
|
|
|
|
}
|
|
|
|
|
2020-10-28 12:18:35 -04:00
|
|
|
func (s *peerRESTServer) GetMetacacheListingHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
ctx := newContext(r, w, "GetMetacacheListing")
|
|
|
|
|
|
|
|
var opts listPathOptions
|
|
|
|
err := gob.NewDecoder(r.Body).Decode(&opts)
|
|
|
|
if err != nil && err != io.EOF {
|
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
resp := localMetacacheMgr.getBucket(ctx, opts.Bucket).findCache(opts)
|
|
|
|
logger.LogIf(ctx, msgp.Encode(w, &resp))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *peerRESTServer) UpdateMetacacheListingHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
ctx := newContext(r, w, "UpdateMetacacheListing")
|
|
|
|
|
|
|
|
var req metacache
|
|
|
|
err := msgp.Decode(r.Body, &req)
|
|
|
|
if err != nil {
|
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
|
|
|
}
|
2020-11-03 15:47:52 -05:00
|
|
|
cache, err := localMetacacheMgr.updateCacheEntry(req)
|
2020-10-28 12:18:35 -04:00
|
|
|
if err != nil {
|
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// Return updated metadata.
|
|
|
|
logger.LogIf(ctx, msgp.Encode(w, &cache))
|
|
|
|
}
|
|
|
|
|
2019-03-14 19:27:31 -04:00
|
|
|
// PutBucketNotificationHandler - Set bucket policy.
|
|
|
|
func (s *peerRESTServer) PutBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
vars := mux.Vars(r)
|
|
|
|
bucketName := vars[peerRESTBucket]
|
|
|
|
if bucketName == "" {
|
|
|
|
s.writeErrorResponse(w, errors.New("Bucket name is missing"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var rulesMap event.RulesMap
|
|
|
|
if r.ContentLength < 0 {
|
|
|
|
s.writeErrorResponse(w, errInvalidArgument)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
err := gob.NewDecoder(r.Body).Decode(&rulesMap)
|
|
|
|
if err != nil {
|
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
globalNotificationSys.AddRulesMap(bucketName, rulesMap)
|
|
|
|
}
|
|
|
|
|
2020-05-23 20:38:39 -04:00
|
|
|
// Return disk IDs of all the local disks.
|
2020-12-01 16:50:33 -05:00
|
|
|
func getLocalDiskIDs(z *erasureServerPools) []string {
|
2020-05-23 20:38:39 -04:00
|
|
|
var ids []string
|
|
|
|
|
2021-01-06 12:35:47 -05:00
|
|
|
for poolIdx := range z.serverPools {
|
|
|
|
for _, set := range z.serverPools[poolIdx].sets {
|
2020-05-23 20:38:39 -04:00
|
|
|
disks := set.getDisks()
|
|
|
|
for _, disk := range disks {
|
|
|
|
if disk == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if disk.IsLocal() {
|
|
|
|
id, err := disk.GetDiskID()
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if id == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
ids = append(ids, id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ids
|
|
|
|
}
|
|
|
|
|
2020-06-17 17:49:26 -04:00
|
|
|
// HealthHandler - returns true of health
|
|
|
|
func (s *peerRESTServer) HealthHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
s.IsValid(w, r)
|
|
|
|
}
|
|
|
|
|
2020-05-23 20:38:39 -04:00
|
|
|
// GetLocalDiskIDs - Return disk IDs of all the local disks.
|
|
|
|
func (s *peerRESTServer) GetLocalDiskIDs(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := newContext(r, w, "GetLocalDiskIDs")
|
|
|
|
|
2020-10-09 12:59:52 -04:00
|
|
|
objLayer := newObjectLayerFn()
|
2020-05-23 20:38:39 -04:00
|
|
|
|
|
|
|
// Service not initialized yet
|
|
|
|
if objLayer == nil {
|
|
|
|
s.writeErrorResponse(w, errServerNotInitialized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-12-01 16:50:33 -05:00
|
|
|
z, ok := objLayer.(*erasureServerPools)
|
2020-05-23 20:38:39 -04:00
|
|
|
if !ok {
|
|
|
|
s.writeErrorResponse(w, errServerNotInitialized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ids := getLocalDiskIDs(z)
|
|
|
|
logger.LogIf(ctx, gob.NewEncoder(w).Encode(ids))
|
|
|
|
}
|
|
|
|
|
2019-08-28 18:04:43 -04:00
|
|
|
// ServerUpdateHandler - updates the current server.
|
|
|
|
func (s *peerRESTServer) ServerUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-07-23 11:03:31 -04:00
|
|
|
if r.ContentLength < 0 {
|
|
|
|
s.writeErrorResponse(w, errInvalidArgument)
|
|
|
|
return
|
2019-08-28 18:04:43 -04:00
|
|
|
}
|
2020-07-23 11:03:31 -04:00
|
|
|
|
|
|
|
var info serverUpdateInfo
|
|
|
|
err := gob.NewDecoder(r.Body).Decode(&info)
|
2019-08-28 18:04:43 -04:00
|
|
|
if err != nil {
|
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
|
|
|
}
|
2020-07-23 11:03:31 -04:00
|
|
|
|
2021-04-08 12:51:11 -04:00
|
|
|
if _, err = updateServer(info.URL, info.Sha256Sum, info.Time, info.ReleaseInfo, getMinioMode()); err != nil {
|
2020-07-23 11:03:31 -04:00
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
2019-08-28 18:04:43 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-23 15:02:16 -05:00
|
|
|
var errUnsupportedSignal = fmt.Errorf("unsupported signal")
|
2019-03-14 19:27:31 -04:00
|
|
|
|
|
|
|
// SignalServiceHandler - signal service handler.
|
|
|
|
func (s *peerRESTServer) SignalServiceHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
vars := mux.Vars(r)
|
|
|
|
signalString := vars[peerRESTSignal]
|
|
|
|
if signalString == "" {
|
|
|
|
s.writeErrorResponse(w, errors.New("signal name is missing"))
|
|
|
|
return
|
|
|
|
}
|
2019-08-27 14:37:47 -04:00
|
|
|
si, err := strconv.Atoi(signalString)
|
|
|
|
if err != nil {
|
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
signal := serviceSignal(si)
|
2019-08-28 18:04:43 -04:00
|
|
|
switch signal {
|
|
|
|
case serviceRestart:
|
|
|
|
globalServiceSignalCh <- signal
|
|
|
|
case serviceStop:
|
2019-03-14 19:27:31 -04:00
|
|
|
globalServiceSignalCh <- signal
|
2021-11-23 15:02:16 -05:00
|
|
|
case serviceFreeze:
|
|
|
|
freezeServices()
|
|
|
|
case serviceUnFreeze:
|
|
|
|
unfreezeServices()
|
2020-12-04 12:32:35 -05:00
|
|
|
case serviceReloadDynamic:
|
2021-01-22 15:09:24 -05:00
|
|
|
objAPI := newObjectLayerFn()
|
|
|
|
if objAPI == nil {
|
|
|
|
s.writeErrorResponse(w, errServerNotInitialized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
srvCfg, err := getValidConfig(objAPI)
|
2020-12-04 12:32:35 -05:00
|
|
|
if err != nil {
|
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
|
|
|
}
|
2021-01-22 15:09:24 -05:00
|
|
|
if err = applyDynamicConfig(r.Context(), objAPI, srvCfg); err != nil {
|
2020-12-04 12:32:35 -05:00
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
}
|
|
|
|
return
|
2019-03-14 19:27:31 -04:00
|
|
|
default:
|
|
|
|
s.writeErrorResponse(w, errUnsupportedSignal)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-12 13:01:23 -05:00
|
|
|
// ListenHandler sends http trace messages back to peer rest client
|
|
|
|
func (s *peerRESTServer) ListenHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
2021-01-18 23:35:38 -05:00
|
|
|
s.writeErrorResponse(w, errors.New("invalid request"))
|
2019-12-12 13:01:23 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-08-08 01:43:01 -04:00
|
|
|
values := r.Form
|
2019-12-16 23:30:57 -05:00
|
|
|
|
|
|
|
var prefix string
|
|
|
|
if len(values[peerRESTListenPrefix]) > 1 {
|
2021-01-18 23:35:38 -05:00
|
|
|
s.writeErrorResponse(w, errors.New("invalid request"))
|
2019-12-16 23:30:57 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(values[peerRESTListenPrefix]) == 1 {
|
|
|
|
if err := event.ValidateFilterRuleValue(values[peerRESTListenPrefix][0]); err != nil {
|
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
prefix = values[peerRESTListenPrefix][0]
|
|
|
|
}
|
|
|
|
|
|
|
|
var suffix string
|
|
|
|
if len(values[peerRESTListenSuffix]) > 1 {
|
2021-01-18 23:35:38 -05:00
|
|
|
s.writeErrorResponse(w, errors.New("invalid request"))
|
2019-12-16 23:30:57 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(values[peerRESTListenSuffix]) == 1 {
|
|
|
|
if err := event.ValidateFilterRuleValue(values[peerRESTListenSuffix][0]); err != nil {
|
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
suffix = values[peerRESTListenSuffix][0]
|
|
|
|
}
|
|
|
|
|
|
|
|
pattern := event.NewPattern(prefix, suffix)
|
|
|
|
|
|
|
|
var eventNames []event.Name
|
|
|
|
for _, ev := range values[peerRESTListenEvents] {
|
|
|
|
eventName, err := event.ParseName(ev)
|
|
|
|
if err != nil {
|
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
eventNames = append(eventNames, eventName)
|
|
|
|
}
|
|
|
|
|
|
|
|
rulesMap := event.NewRulesMap(eventNames, pattern, event.TargetID{ID: mustGetUUID()})
|
|
|
|
|
2019-12-12 13:01:23 -05:00
|
|
|
doneCh := make(chan struct{})
|
|
|
|
defer close(doneCh)
|
|
|
|
|
|
|
|
// Listen Publisher uses nonblocking publish and hence does not wait for slow subscribers.
|
|
|
|
// Use buffered channel to take care of burst sends or slow w.Write()
|
|
|
|
ch := make(chan interface{}, 2000)
|
|
|
|
|
2019-12-16 23:30:57 -05:00
|
|
|
globalHTTPListen.Subscribe(ch, doneCh, func(evI interface{}) bool {
|
|
|
|
ev, ok := evI.(event.Event)
|
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
2020-07-20 15:52:49 -04:00
|
|
|
if ev.S3.Bucket.Name != "" && values.Get(peerRESTListenBucket) != "" {
|
|
|
|
if ev.S3.Bucket.Name != values.Get(peerRESTListenBucket) {
|
|
|
|
return false
|
|
|
|
}
|
2019-12-20 14:45:03 -05:00
|
|
|
}
|
2020-04-27 09:25:05 -04:00
|
|
|
return rulesMap.MatchSimple(ev.EventName, ev.S3.Object.Key)
|
2019-12-12 13:01:23 -05:00
|
|
|
})
|
|
|
|
|
|
|
|
keepAliveTicker := time.NewTicker(500 * time.Millisecond)
|
|
|
|
defer keepAliveTicker.Stop()
|
|
|
|
|
|
|
|
enc := gob.NewEncoder(w)
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case ev := <-ch:
|
|
|
|
if err := enc.Encode(ev); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
w.(http.Flusher).Flush()
|
|
|
|
case <-keepAliveTicker.C:
|
|
|
|
if err := enc.Encode(&event.Event{}); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
w.(http.Flusher).Flush()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-27 02:24:07 -04:00
|
|
|
func extractTraceOptsFromPeerRequest(r *http.Request) (opts madmin.ServiceTraceOpts, err error) {
|
2021-08-08 01:43:01 -04:00
|
|
|
opts.S3 = r.Form.Get(peerRESTTraceS3) == "true"
|
|
|
|
opts.OS = r.Form.Get(peerRESTTraceOS) == "true"
|
|
|
|
opts.Storage = r.Form.Get(peerRESTTraceStorage) == "true"
|
|
|
|
opts.Internal = r.Form.Get(peerRESTTraceInternal) == "true"
|
|
|
|
opts.OnlyErrors = r.Form.Get(peerRESTTraceErr) == "true"
|
2021-03-27 02:24:07 -04:00
|
|
|
|
2021-08-08 01:43:01 -04:00
|
|
|
if t := r.Form.Get(peerRESTTraceThreshold); t != "" {
|
2021-03-27 02:24:07 -04:00
|
|
|
d, err := time.ParseDuration(t)
|
|
|
|
if err != nil {
|
|
|
|
return opts, err
|
|
|
|
}
|
|
|
|
opts.Threshold = d
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-06-08 18:54:41 -04:00
|
|
|
// TraceHandler sends http trace messages back to peer rest client
|
|
|
|
func (s *peerRESTServer) TraceHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
2021-03-27 02:24:07 -04:00
|
|
|
|
|
|
|
traceOpts, err := extractTraceOptsFromPeerRequest(r)
|
|
|
|
if err != nil {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
2019-06-08 18:54:41 -04:00
|
|
|
|
2019-06-27 01:41:12 -04:00
|
|
|
doneCh := make(chan struct{})
|
|
|
|
defer close(doneCh)
|
|
|
|
|
|
|
|
// Trace Publisher uses nonblocking publish and hence does not wait for slow subscribers.
|
|
|
|
// Use buffered channel to take care of burst sends or slow w.Write()
|
|
|
|
ch := make(chan interface{}, 2000)
|
2019-07-31 14:08:39 -04:00
|
|
|
|
2021-03-27 02:24:07 -04:00
|
|
|
globalTrace.Subscribe(ch, doneCh, func(entry interface{}) bool {
|
|
|
|
return mustTrace(entry, traceOpts)
|
2019-07-31 14:08:39 -04:00
|
|
|
})
|
|
|
|
|
|
|
|
keepAliveTicker := time.NewTicker(500 * time.Millisecond)
|
|
|
|
defer keepAliveTicker.Stop()
|
2019-06-27 01:41:12 -04:00
|
|
|
|
|
|
|
enc := gob.NewEncoder(w)
|
2019-06-08 18:54:41 -04:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case entry := <-ch:
|
2019-06-27 01:41:12 -04:00
|
|
|
if err := enc.Encode(entry); err != nil {
|
2019-06-08 18:54:41 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
w.(http.Flusher).Flush()
|
2019-07-31 14:08:39 -04:00
|
|
|
case <-keepAliveTicker.C:
|
2021-05-06 11:52:02 -04:00
|
|
|
if err := enc.Encode(&madmin.TraceInfo{}); err != nil {
|
2019-07-31 14:08:39 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
w.(http.Flusher).Flush()
|
2019-06-08 18:54:41 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-25 19:42:24 -04:00
|
|
|
func (s *peerRESTServer) BackgroundHealStatusHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
ctx := newContext(r, w, "BackgroundHealStatus")
|
|
|
|
|
2021-03-04 17:36:23 -05:00
|
|
|
state, ok := getBackgroundHealStatus(ctx, newObjectLayerFn())
|
2020-08-07 22:43:06 -04:00
|
|
|
if !ok {
|
|
|
|
s.writeErrorResponse(w, errServerNotInitialized)
|
|
|
|
return
|
|
|
|
}
|
2019-06-25 19:42:24 -04:00
|
|
|
|
|
|
|
logger.LogIf(ctx, gob.NewEncoder(w).Encode(state))
|
|
|
|
}
|
|
|
|
|
2022-01-10 12:07:49 -05:00
|
|
|
func (s *peerRESTServer) ReloadPoolMetaHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
objAPI := newObjectLayerFn()
|
|
|
|
if objAPI == nil {
|
|
|
|
s.writeErrorResponse(w, errServerNotInitialized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
pools, ok := objAPI.(*erasureServerPools)
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if err := pools.ReloadPoolMeta(r.Context()); err != nil {
|
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-19 13:30:42 -04:00
|
|
|
func (s *peerRESTServer) LoadTransitionTierConfigHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
go func() {
|
|
|
|
err := globalTierConfigMgr.Reload(context.Background(), newObjectLayerFn())
|
|
|
|
if err != nil {
|
|
|
|
logger.LogIf(context.Background(), fmt.Errorf("Failed to reload remote tier config %s", err))
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2019-09-03 14:10:48 -04:00
|
|
|
// ConsoleLogHandler sends console logs of this node back to peer rest client
|
|
|
|
func (s *peerRESTServer) ConsoleLogHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("Invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
w.Header().Set("Connection", "close")
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
|
|
|
doneCh := make(chan struct{})
|
|
|
|
defer close(doneCh)
|
|
|
|
|
|
|
|
ch := make(chan interface{}, 2000)
|
2019-10-11 21:50:54 -04:00
|
|
|
globalConsoleSys.Subscribe(ch, doneCh, "", 0, string(logger.All), nil)
|
2019-09-03 14:10:48 -04:00
|
|
|
|
|
|
|
enc := gob.NewEncoder(w)
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case entry := <-ch:
|
2019-09-22 04:24:32 -04:00
|
|
|
if err := enc.Encode(entry); err != nil {
|
2019-09-03 14:10:48 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
w.(http.Flusher).Flush()
|
|
|
|
case <-r.Context().Done():
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-14 19:27:31 -04:00
|
|
|
func (s *peerRESTServer) writeErrorResponse(w http.ResponseWriter, err error) {
|
|
|
|
w.WriteHeader(http.StatusForbidden)
|
|
|
|
w.Write([]byte(err.Error()))
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsValid - To authenticate and verify the time difference.
|
|
|
|
func (s *peerRESTServer) IsValid(w http.ResponseWriter, r *http.Request) bool {
|
|
|
|
if err := storageServerRequestValidate(r); err != nil {
|
|
|
|
s.writeErrorResponse(w, err)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-10-09 23:36:00 -04:00
|
|
|
// GetBandwidth gets the bandwidth for the buckets requested.
|
|
|
|
func (s *peerRESTServer) GetBandwidth(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
2021-01-18 23:35:38 -05:00
|
|
|
s.writeErrorResponse(w, errors.New("invalid request"))
|
2020-10-09 23:36:00 -04:00
|
|
|
return
|
|
|
|
}
|
2021-08-08 01:43:01 -04:00
|
|
|
|
|
|
|
bucketsString := r.Form.Get("buckets")
|
2020-10-09 23:36:00 -04:00
|
|
|
|
|
|
|
doneCh := make(chan struct{})
|
|
|
|
defer close(doneCh)
|
|
|
|
|
2020-10-12 12:04:55 -04:00
|
|
|
selectBuckets := b.SelectBuckets(strings.Split(bucketsString, ",")...)
|
|
|
|
report := globalBucketMonitor.GetReport(selectBuckets)
|
2020-10-09 23:36:00 -04:00
|
|
|
|
|
|
|
enc := gob.NewEncoder(w)
|
|
|
|
if err := enc.Encode(report); err != nil {
|
|
|
|
s.writeErrorResponse(w, errors.New("Encoding report failed: "+err.Error()))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-18 23:35:38 -05:00
|
|
|
// GetPeerMetrics gets the metrics to be federated across peers.
|
|
|
|
func (s *peerRESTServer) GetPeerMetrics(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("invalid request"))
|
|
|
|
}
|
|
|
|
|
|
|
|
doneCh := make(chan struct{})
|
|
|
|
defer close(doneCh)
|
|
|
|
|
|
|
|
enc := gob.NewEncoder(w)
|
|
|
|
|
2021-12-17 13:11:04 -05:00
|
|
|
ch := ReportMetrics(r.Context(), peerMetricsGroups)
|
2021-01-18 23:35:38 -05:00
|
|
|
for m := range ch {
|
|
|
|
if err := enc.Encode(m); err != nil {
|
|
|
|
s.writeErrorResponse(w, errors.New("Encoding metric failed: "+err.Error()))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-27 15:55:56 -04:00
|
|
|
// SpeedtestResult return value of the speedtest function
|
|
|
|
type SpeedtestResult struct {
|
2021-09-10 20:43:34 -04:00
|
|
|
Endpoint string
|
2021-07-27 15:55:56 -04:00
|
|
|
Uploads uint64
|
|
|
|
Downloads uint64
|
2021-08-31 17:08:23 -04:00
|
|
|
Error string
|
2021-07-27 15:55:56 -04:00
|
|
|
}
|
|
|
|
|
2021-11-19 13:41:37 -05:00
|
|
|
func newRandomReader(size int) io.Reader {
|
|
|
|
return io.LimitReader(randreader.New(), int64(size))
|
2021-07-27 15:55:56 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Runs the speedtest on local MinIO process.
|
2021-11-29 12:05:46 -05:00
|
|
|
func selfSpeedtest(ctx context.Context, size, concurrent int, duration time.Duration, storageClass string) (SpeedtestResult, error) {
|
2021-07-27 15:55:56 -04:00
|
|
|
objAPI := newObjectLayerFn()
|
|
|
|
if objAPI == nil {
|
2021-09-10 20:43:34 -04:00
|
|
|
return SpeedtestResult{}, errServerNotInitialized
|
2021-07-27 15:55:56 -04:00
|
|
|
}
|
|
|
|
|
2021-12-06 19:36:14 -05:00
|
|
|
var errOnce sync.Once
|
2021-09-10 20:43:34 -04:00
|
|
|
var retError string
|
2021-12-06 19:36:14 -05:00
|
|
|
var wg sync.WaitGroup
|
|
|
|
var totalBytesWritten uint64
|
|
|
|
var totalBytesRead uint64
|
2021-09-10 20:43:34 -04:00
|
|
|
|
2021-07-27 15:55:56 -04:00
|
|
|
objCountPerThread := make([]uint64, concurrent)
|
2021-09-10 20:43:34 -04:00
|
|
|
uploadsCtx, uploadsCancel := context.WithCancel(context.Background())
|
2021-12-06 19:36:14 -05:00
|
|
|
defer uploadsCancel()
|
2021-07-27 15:55:56 -04:00
|
|
|
|
|
|
|
go func() {
|
|
|
|
time.Sleep(duration)
|
2021-09-10 20:43:34 -04:00
|
|
|
uploadsCancel()
|
2021-07-27 15:55:56 -04:00
|
|
|
}()
|
|
|
|
|
2022-01-27 11:13:03 -05:00
|
|
|
objNamePrefix := "speedtest/objects/" + uuid.New().String()
|
2021-07-27 15:55:56 -04:00
|
|
|
|
|
|
|
wg.Add(concurrent)
|
|
|
|
for i := 0; i < concurrent; i++ {
|
|
|
|
go func(i int) {
|
|
|
|
defer wg.Done()
|
|
|
|
for {
|
2021-11-19 13:41:37 -05:00
|
|
|
hashReader, err := hash.NewReader(newRandomReader(size),
|
2021-07-27 15:55:56 -04:00
|
|
|
int64(size), "", "", int64(size))
|
|
|
|
if err != nil {
|
2021-12-06 19:36:14 -05:00
|
|
|
if !contextCanceled(uploadsCtx) {
|
|
|
|
errOnce.Do(func() {
|
|
|
|
retError = err.Error()
|
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
uploadsCancel()
|
|
|
|
return
|
2021-07-27 15:55:56 -04:00
|
|
|
}
|
|
|
|
reader := NewPutObjReader(hashReader)
|
2022-01-27 11:13:03 -05:00
|
|
|
objInfo, err := objAPI.PutObject(uploadsCtx, minioMetaBucket, fmt.Sprintf("%s.%d.%d",
|
2021-11-29 12:05:46 -05:00
|
|
|
objNamePrefix, i, objCountPerThread[i]), reader, ObjectOptions{
|
|
|
|
UserDefined: map[string]string{
|
|
|
|
xhttp.AmzStorageClass: storageClass,
|
|
|
|
},
|
|
|
|
})
|
2021-09-10 20:43:34 -04:00
|
|
|
if err != nil {
|
2021-12-06 19:36:14 -05:00
|
|
|
if !contextCanceled(uploadsCtx) {
|
|
|
|
errOnce.Do(func() {
|
|
|
|
retError = err.Error()
|
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
uploadsCancel()
|
|
|
|
return
|
2021-07-27 15:55:56 -04:00
|
|
|
}
|
2021-11-19 13:41:37 -05:00
|
|
|
atomic.AddUint64(&totalBytesWritten, uint64(objInfo.Size))
|
2021-07-27 15:55:56 -04:00
|
|
|
objCountPerThread[i]++
|
|
|
|
}
|
|
|
|
}(i)
|
|
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
|
2021-12-06 19:36:14 -05:00
|
|
|
// We already saw write failures, no need to proceed into read's
|
|
|
|
if retError != "" {
|
|
|
|
return SpeedtestResult{Uploads: totalBytesWritten, Downloads: totalBytesRead, Error: retError}, nil
|
|
|
|
}
|
2021-09-10 20:43:34 -04:00
|
|
|
|
2021-12-06 19:36:14 -05:00
|
|
|
downloadsCtx, downloadsCancel := context.WithCancel(context.Background())
|
|
|
|
defer downloadsCancel()
|
2021-07-27 15:55:56 -04:00
|
|
|
go func() {
|
|
|
|
time.Sleep(duration)
|
2021-09-10 20:43:34 -04:00
|
|
|
downloadsCancel()
|
2021-07-27 15:55:56 -04:00
|
|
|
}()
|
|
|
|
|
|
|
|
wg.Add(concurrent)
|
|
|
|
for i := 0; i < concurrent; i++ {
|
|
|
|
go func(i int) {
|
|
|
|
defer wg.Done()
|
|
|
|
var j uint64
|
2021-09-10 20:43:34 -04:00
|
|
|
if objCountPerThread[i] == 0 {
|
|
|
|
return
|
|
|
|
}
|
2021-07-27 15:55:56 -04:00
|
|
|
for {
|
|
|
|
if objCountPerThread[i] == j {
|
|
|
|
j = 0
|
|
|
|
}
|
2022-01-27 11:13:03 -05:00
|
|
|
r, err := objAPI.GetObjectNInfo(downloadsCtx, minioMetaBucket, fmt.Sprintf("%s.%d.%d",
|
2021-07-27 15:55:56 -04:00
|
|
|
objNamePrefix, i, j), nil, nil, noLock, ObjectOptions{})
|
2021-09-10 20:43:34 -04:00
|
|
|
if err != nil {
|
2021-12-06 19:36:14 -05:00
|
|
|
if !contextCanceled(downloadsCtx) {
|
|
|
|
errOnce.Do(func() {
|
|
|
|
retError = err.Error()
|
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
downloadsCancel()
|
|
|
|
return
|
2021-07-27 15:55:56 -04:00
|
|
|
}
|
2021-09-10 20:43:34 -04:00
|
|
|
n, err := io.Copy(ioutil.Discard, r)
|
2021-07-27 15:55:56 -04:00
|
|
|
r.Close()
|
2021-11-19 13:41:37 -05:00
|
|
|
if err == nil {
|
|
|
|
// Only capture success criteria - do not
|
|
|
|
// have to capture failed reads, truncated
|
|
|
|
// reads etc.
|
|
|
|
atomic.AddUint64(&totalBytesRead, uint64(n))
|
|
|
|
}
|
2021-09-10 20:43:34 -04:00
|
|
|
if err != nil {
|
2021-12-06 19:36:14 -05:00
|
|
|
if !contextCanceled(downloadsCtx) {
|
|
|
|
errOnce.Do(func() {
|
|
|
|
retError = err.Error()
|
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
downloadsCancel()
|
|
|
|
return
|
2021-07-27 15:55:56 -04:00
|
|
|
}
|
|
|
|
j++
|
|
|
|
}
|
|
|
|
}(i)
|
|
|
|
}
|
|
|
|
wg.Wait()
|
2021-12-06 19:36:14 -05:00
|
|
|
|
2021-09-10 20:43:34 -04:00
|
|
|
return SpeedtestResult{Uploads: totalBytesWritten, Downloads: totalBytesRead, Error: retError}, nil
|
2021-07-27 15:55:56 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *peerRESTServer) SpeedtestHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("invalid request"))
|
2021-11-23 15:02:16 -05:00
|
|
|
return
|
2021-07-27 15:55:56 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
objAPI := newObjectLayerFn()
|
|
|
|
if objAPI == nil {
|
|
|
|
s.writeErrorResponse(w, errServerNotInitialized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-08-08 01:43:01 -04:00
|
|
|
sizeStr := r.Form.Get(peerRESTSize)
|
|
|
|
durationStr := r.Form.Get(peerRESTDuration)
|
|
|
|
concurrentStr := r.Form.Get(peerRESTConcurrent)
|
2021-11-29 12:05:46 -05:00
|
|
|
storageClass := r.Form.Get(peerRESTStorageClass)
|
2021-07-27 15:55:56 -04:00
|
|
|
|
|
|
|
size, err := strconv.Atoi(sizeStr)
|
|
|
|
if err != nil {
|
|
|
|
size = 64 * humanize.MiByte
|
|
|
|
}
|
|
|
|
|
|
|
|
concurrent, err := strconv.Atoi(concurrentStr)
|
|
|
|
if err != nil {
|
|
|
|
concurrent = 32
|
|
|
|
}
|
|
|
|
|
|
|
|
duration, err := time.ParseDuration(durationStr)
|
|
|
|
if err != nil {
|
|
|
|
duration = time.Second * 10
|
|
|
|
}
|
|
|
|
|
2021-08-31 17:08:23 -04:00
|
|
|
done := keepHTTPResponseAlive(w)
|
|
|
|
|
2021-11-29 12:05:46 -05:00
|
|
|
result, err := selfSpeedtest(r.Context(), size, concurrent, duration, storageClass)
|
2021-07-27 15:55:56 -04:00
|
|
|
if err != nil {
|
2021-08-31 17:08:23 -04:00
|
|
|
result.Error = err.Error()
|
2021-07-27 15:55:56 -04:00
|
|
|
}
|
|
|
|
|
2021-08-31 17:08:23 -04:00
|
|
|
done(nil)
|
|
|
|
logger.LogIf(r.Context(), gob.NewEncoder(w).Encode(result))
|
2021-07-27 15:55:56 -04:00
|
|
|
}
|
|
|
|
|
2022-01-26 17:33:10 -05:00
|
|
|
// GetLastDayTierStatsHandler - returns per-tier stats in the last 24hrs for this server
|
|
|
|
func (s *peerRESTServer) GetLastDayTierStatsHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := newContext(r, w, "GetLastDayTierStats")
|
|
|
|
if objAPI := newObjectLayerFn(); objAPI == nil || globalTransitionState == nil {
|
|
|
|
s.writeErrorResponse(w, errServerNotInitialized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
result := globalTransitionState.getDailyAllTierStats()
|
|
|
|
logger.LogIf(ctx, gob.NewEncoder(w).Encode(result))
|
|
|
|
}
|
|
|
|
|
2022-02-02 01:38:05 -05:00
|
|
|
func (s *peerRESTServer) DriveSpeedTestHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !s.IsValid(w, r) {
|
|
|
|
s.writeErrorResponse(w, errors.New("invalid request"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
objAPI := newObjectLayerFn()
|
|
|
|
if objAPI == nil {
|
|
|
|
s.writeErrorResponse(w, errServerNotInitialized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
serial := r.Form.Get("serial") == "true"
|
|
|
|
blockSizeStr := r.Form.Get("blocksize")
|
|
|
|
fileSizeStr := r.Form.Get("filesize")
|
|
|
|
|
|
|
|
blockSize, err := strconv.ParseUint(blockSizeStr, 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
blockSize = 4 * humanize.MiByte // default value
|
|
|
|
}
|
|
|
|
|
|
|
|
fileSize, err := strconv.ParseUint(fileSizeStr, 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
fileSize = 1 * humanize.GiByte // default value
|
|
|
|
}
|
|
|
|
|
|
|
|
opts := madmin.DriveSpeedTestOpts{
|
|
|
|
Serial: serial,
|
|
|
|
BlockSize: blockSize,
|
|
|
|
FileSize: fileSize,
|
|
|
|
}
|
|
|
|
|
|
|
|
done := keepHTTPResponseAlive(w)
|
|
|
|
result := driveSpeedTest(r.Context(), opts)
|
|
|
|
done(nil)
|
|
|
|
|
|
|
|
logger.LogIf(r.Context(), gob.NewEncoder(w).Encode(result))
|
|
|
|
}
|
|
|
|
|
2019-03-14 19:27:31 -04:00
|
|
|
// registerPeerRESTHandlers - register peer rest router.
|
|
|
|
func registerPeerRESTHandlers(router *mux.Router) {
|
|
|
|
server := &peerRESTServer{}
|
2019-11-04 12:30:59 -05:00
|
|
|
subrouter := router.PathPrefix(peerRESTPrefix).Subrouter()
|
2020-06-17 17:49:26 -04:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodHealth).HandlerFunc(httpTraceHdrs(server.HealthHandler))
|
2019-11-04 12:30:59 -05:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodGetLocks).HandlerFunc(httpTraceHdrs(server.GetLocksHandler))
|
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodServerInfo).HandlerFunc(httpTraceHdrs(server.ServerInfoHandler))
|
2021-06-01 11:55:49 -04:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodProcInfo).HandlerFunc(httpTraceHdrs(server.GetProcInfoHandler))
|
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodMemInfo).HandlerFunc(httpTraceHdrs(server.GetMemInfoHandler))
|
2021-07-30 02:05:34 -04:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodSysErrors).HandlerFunc(httpTraceHdrs(server.GetSysErrorsHandler))
|
2021-08-12 21:58:40 -04:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodSysServices).HandlerFunc(httpTraceHdrs(server.GetSysServicesHandler))
|
2021-08-24 20:09:37 -04:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodSysConfig).HandlerFunc(httpTraceHdrs(server.GetSysConfigHandler))
|
2021-06-01 11:55:49 -04:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodOsInfo).HandlerFunc(httpTraceHdrs(server.GetOSInfoHandler))
|
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodDiskHwInfo).HandlerFunc(httpTraceHdrs(server.GetPartitionsHandler))
|
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodCPUInfo).HandlerFunc(httpTraceHdrs(server.GetCPUsHandler))
|
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodDriveInfo).HandlerFunc(httpTraceHdrs(server.GetDrivePerfInfosHandler))
|
2020-11-20 15:52:53 -05:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodNetInfo).HandlerFunc(httpTraceHdrs(server.NetInfoHandler))
|
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodDispatchNetInfo).HandlerFunc(httpTraceHdrs(server.DispatchNetInfoHandler))
|
2020-04-27 13:06:21 -04:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodCycleBloom).HandlerFunc(httpTraceHdrs(server.CycleServerBloomFilterHandler))
|
2020-05-19 16:53:54 -04:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodDeleteBucketMetadata).HandlerFunc(httpTraceHdrs(server.DeleteBucketMetadataHandler)).Queries(restQueries(peerRESTBucket)...)
|
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodLoadBucketMetadata).HandlerFunc(httpTraceHdrs(server.LoadBucketMetadataHandler)).Queries(restQueries(peerRESTBucket)...)
|
2021-04-04 18:34:33 -04:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodGetBucketStats).HandlerFunc(httpTraceHdrs(server.GetBucketStatsHandler)).Queries(restQueries(peerRESTBucket)...)
|
2019-11-04 12:30:59 -05:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodSignalService).HandlerFunc(httpTraceHdrs(server.SignalServiceHandler)).Queries(restQueries(peerRESTSignal)...)
|
2020-07-23 11:03:31 -04:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodServerUpdate).HandlerFunc(httpTraceHdrs(server.ServerUpdateHandler))
|
2019-11-04 12:30:59 -05:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodDeletePolicy).HandlerFunc(httpTraceAll(server.DeletePolicyHandler)).Queries(restQueries(peerRESTPolicy)...)
|
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodLoadPolicy).HandlerFunc(httpTraceAll(server.LoadPolicyHandler)).Queries(restQueries(peerRESTPolicy)...)
|
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodLoadPolicyMapping).HandlerFunc(httpTraceAll(server.LoadPolicyMappingHandler)).Queries(restQueries(peerRESTUserOrGroup)...)
|
2020-04-21 11:35:19 -04:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodDeleteUser).HandlerFunc(httpTraceAll(server.DeleteUserHandler)).Queries(restQueries(peerRESTUser)...)
|
2020-04-24 15:10:09 -04:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodDeleteServiceAccount).HandlerFunc(httpTraceAll(server.DeleteServiceAccountHandler)).Queries(restQueries(peerRESTUser)...)
|
2019-11-04 12:30:59 -05:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodLoadUser).HandlerFunc(httpTraceAll(server.LoadUserHandler)).Queries(restQueries(peerRESTUser, peerRESTUserTemp)...)
|
2020-04-24 15:10:09 -04:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodLoadServiceAccount).HandlerFunc(httpTraceAll(server.LoadServiceAccountHandler)).Queries(restQueries(peerRESTUser)...)
|
2019-11-04 12:30:59 -05:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodLoadGroup).HandlerFunc(httpTraceAll(server.LoadGroupHandler)).Queries(restQueries(peerRESTGroup)...)
|
|
|
|
|
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodStartProfiling).HandlerFunc(httpTraceAll(server.StartProfilingHandler)).Queries(restQueries(peerRESTProfiler)...)
|
2020-01-10 20:19:58 -05:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodDownloadProfilingData).HandlerFunc(httpTraceHdrs(server.DownloadProfilingDataHandler))
|
2019-11-04 12:30:59 -05:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodTrace).HandlerFunc(server.TraceHandler)
|
2019-12-16 23:30:57 -05:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodListen).HandlerFunc(httpTraceHdrs(server.ListenHandler))
|
2019-11-04 12:30:59 -05:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodBackgroundHealStatus).HandlerFunc(server.BackgroundHealStatusHandler)
|
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodLog).HandlerFunc(server.ConsoleLogHandler)
|
2020-05-23 20:38:39 -04:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodGetLocalDiskIDs).HandlerFunc(httpTraceHdrs(server.GetLocalDiskIDs))
|
2020-10-09 23:36:00 -04:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodGetBandwidth).HandlerFunc(httpTraceHdrs(server.GetBandwidth))
|
2020-10-28 12:18:35 -04:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodGetMetacacheListing).HandlerFunc(httpTraceHdrs(server.GetMetacacheListingHandler))
|
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodUpdateMetacacheListing).HandlerFunc(httpTraceHdrs(server.UpdateMetacacheListingHandler))
|
2021-01-18 23:35:38 -05:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodGetPeerMetrics).HandlerFunc(httpTraceHdrs(server.GetPeerMetrics))
|
2021-04-19 13:30:42 -04:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodLoadTransitionTierConfig).HandlerFunc(httpTraceHdrs(server.LoadTransitionTierConfigHandler))
|
2021-07-27 15:55:56 -04:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodSpeedtest).HandlerFunc(httpTraceHdrs(server.SpeedtestHandler))
|
2022-02-02 01:38:05 -05:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodDriveSpeedTest).HandlerFunc(httpTraceHdrs(server.DriveSpeedTestHandler))
|
2021-10-06 19:36:31 -04:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodReloadSiteReplicationConfig).HandlerFunc(httpTraceHdrs(server.ReloadSiteReplicationConfigHandler))
|
2022-01-10 12:07:49 -05:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodReloadPoolMeta).HandlerFunc(httpTraceHdrs(server.ReloadPoolMetaHandler))
|
2022-01-26 17:33:10 -05:00
|
|
|
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodGetLastDayTierStats).HandlerFunc(httpTraceHdrs(server.GetLastDayTierStatsHandler))
|
2019-03-14 19:27:31 -04:00
|
|
|
}
|