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/>.
|
2018-03-15 16:03:41 -04:00
|
|
|
|
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
2018-10-09 17:00:01 -04:00
|
|
|
"bytes"
|
2018-03-15 16:27:16 -04:00
|
|
|
"context"
|
2020-04-27 13:06:21 -04:00
|
|
|
"encoding/json"
|
2021-04-04 18:34:33 -04:00
|
|
|
"errors"
|
2018-03-15 16:03:41 -04:00
|
|
|
"fmt"
|
2019-01-14 01:44:20 -05:00
|
|
|
"io"
|
2020-09-25 22:21:52 -04:00
|
|
|
"net/http"
|
2018-03-15 16:03:41 -04:00
|
|
|
"net/url"
|
2020-03-27 00:07:39 -04:00
|
|
|
"sort"
|
2018-08-24 02:31:14 -04:00
|
|
|
"strings"
|
2018-03-15 16:03:41 -04:00
|
|
|
"sync"
|
2018-06-06 04:51:56 -04:00
|
|
|
"time"
|
2018-03-15 16:03:41 -04:00
|
|
|
|
2021-05-27 11:02:39 -04:00
|
|
|
"github.com/bits-and-blooms/bloom/v3"
|
2020-10-28 12:18:35 -04:00
|
|
|
"github.com/cespare/xxhash/v2"
|
2019-09-26 02:08:24 -04:00
|
|
|
"github.com/klauspost/compress/zip"
|
2021-05-06 11:52:02 -04:00
|
|
|
"github.com/minio/madmin-go"
|
2020-07-14 12:38:05 -04:00
|
|
|
"github.com/minio/minio-go/v7/pkg/set"
|
2021-06-01 17:59:40 -04:00
|
|
|
bucketBandwidth "github.com/minio/minio/internal/bucket/bandwidth"
|
|
|
|
"github.com/minio/minio/internal/crypto"
|
|
|
|
"github.com/minio/minio/internal/event"
|
|
|
|
xhttp "github.com/minio/minio/internal/http"
|
|
|
|
"github.com/minio/minio/internal/logger"
|
|
|
|
"github.com/minio/minio/internal/sync/errgroup"
|
2021-05-30 00:16:42 -04:00
|
|
|
"github.com/minio/pkg/bucket/policy"
|
2021-06-14 17:54:37 -04:00
|
|
|
xnet "github.com/minio/pkg/net"
|
2018-03-15 16:03:41 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
// NotificationSys - notification system.
|
|
|
|
type NotificationSys struct {
|
|
|
|
sync.RWMutex
|
|
|
|
targetList *event.TargetList
|
2020-04-27 09:25:05 -04:00
|
|
|
targetResCh chan event.TargetIDResult
|
2018-03-15 16:03:41 -04:00
|
|
|
bucketRulesMap map[string]event.RulesMap
|
|
|
|
bucketRemoteTargetRulesMap map[string]map[event.TargetID]event.RulesMap
|
2021-01-18 23:35:38 -05:00
|
|
|
peerClients []*peerRESTClient // Excludes self
|
|
|
|
allPeerClients []*peerRESTClient // Includes nil client for self
|
2018-03-15 16:03:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetARNList - returns available ARNs.
|
2020-04-21 12:38:32 -04:00
|
|
|
func (sys *NotificationSys) GetARNList(onlyActive bool) []string {
|
2018-03-15 16:03:41 -04:00
|
|
|
arns := []string{}
|
2019-11-22 01:08:41 -05:00
|
|
|
if sys == nil {
|
|
|
|
return arns
|
|
|
|
}
|
2019-10-23 01:59:13 -04:00
|
|
|
region := globalServerRegion
|
2020-04-21 12:38:32 -04:00
|
|
|
for targetID, target := range sys.targetList.TargetMap() {
|
2020-07-20 15:52:49 -04:00
|
|
|
// httpclient target is part of ListenNotification
|
2018-08-24 02:31:14 -04:00
|
|
|
// which doesn't need to be listed as part of the ARN list
|
|
|
|
// This list is only meant for external targets, filter
|
|
|
|
// this out pro-actively.
|
|
|
|
if !strings.HasPrefix(targetID.ID, "httpclient+") {
|
2020-04-21 12:38:32 -04:00
|
|
|
if onlyActive && !target.HasQueueStore() {
|
|
|
|
if _, err := target.IsActive(); err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
2018-08-24 02:31:14 -04:00
|
|
|
arns = append(arns, targetID.ToARN(region).String())
|
|
|
|
}
|
2018-03-15 16:03:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return arns
|
|
|
|
}
|
|
|
|
|
2018-05-09 14:25:38 -04:00
|
|
|
// NotificationPeerErr returns error associated for a remote peer.
|
|
|
|
type NotificationPeerErr struct {
|
|
|
|
Host xnet.Host // Remote host on which the rpc call was initiated
|
|
|
|
Err error // Error returned by the remote peer for an rpc call
|
|
|
|
}
|
|
|
|
|
2019-01-14 01:44:20 -05:00
|
|
|
// A NotificationGroup is a collection of goroutines working on subtasks that are part of
|
|
|
|
// the same overall task.
|
|
|
|
//
|
|
|
|
// A zero NotificationGroup is valid and does not cancel on error.
|
|
|
|
type NotificationGroup struct {
|
|
|
|
wg sync.WaitGroup
|
|
|
|
errs []NotificationPeerErr
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithNPeers returns a new NotificationGroup with length of errs slice upto nerrs,
|
|
|
|
// upon Wait() errors are returned collected from all tasks.
|
|
|
|
func WithNPeers(nerrs int) *NotificationGroup {
|
|
|
|
return &NotificationGroup{errs: make([]NotificationPeerErr, nerrs)}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait blocks until all function calls from the Go method have returned, then
|
|
|
|
// returns the slice of errors from all function calls.
|
|
|
|
func (g *NotificationGroup) Wait() []NotificationPeerErr {
|
|
|
|
g.wg.Wait()
|
|
|
|
return g.errs
|
|
|
|
}
|
|
|
|
|
|
|
|
// Go calls the given function in a new goroutine.
|
|
|
|
//
|
|
|
|
// The first call to return a non-nil error will be
|
|
|
|
// collected in errs slice and returned by Wait().
|
|
|
|
func (g *NotificationGroup) Go(ctx context.Context, f func() error, index int, addr xnet.Host) {
|
|
|
|
g.wg.Add(1)
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
defer g.wg.Done()
|
|
|
|
g.errs[index] = NotificationPeerErr{
|
|
|
|
Host: addr,
|
|
|
|
}
|
|
|
|
for i := 0; i < 3; i++ {
|
|
|
|
if err := f(); err != nil {
|
|
|
|
g.errs[index].Err = err
|
|
|
|
// Last iteration log the error.
|
|
|
|
if i == 2 {
|
|
|
|
reqInfo := (&logger.ReqInfo{}).AppendTags("peerAddress", addr.String())
|
|
|
|
ctx := logger.SetReqInfo(ctx, reqInfo)
|
|
|
|
logger.LogIf(ctx, err)
|
2018-12-18 17:39:21 -05:00
|
|
|
}
|
|
|
|
// Wait for one second and no need wait after last attempt.
|
|
|
|
if i < 2 {
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
}
|
2019-01-14 01:44:20 -05:00
|
|
|
continue
|
2018-12-18 17:39:21 -05:00
|
|
|
}
|
2019-01-14 01:44:20 -05:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
2018-12-18 17:39:21 -05:00
|
|
|
|
2019-06-06 20:46:22 -04:00
|
|
|
// DeletePolicy - deletes policy across all peers.
|
|
|
|
func (sys *NotificationSys) DeletePolicy(policyName string) []NotificationPeerErr {
|
|
|
|
ng := WithNPeers(len(sys.peerClients))
|
|
|
|
for idx, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
client := client
|
2020-04-09 12:30:02 -04:00
|
|
|
ng.Go(GlobalContext, func() error {
|
2019-06-06 20:46:22 -04:00
|
|
|
return client.DeletePolicy(policyName)
|
|
|
|
}, idx, *client.host)
|
|
|
|
}
|
|
|
|
return ng.Wait()
|
|
|
|
}
|
|
|
|
|
|
|
|
// LoadPolicy - reloads a specific modified policy across all peers
|
|
|
|
func (sys *NotificationSys) LoadPolicy(policyName string) []NotificationPeerErr {
|
|
|
|
ng := WithNPeers(len(sys.peerClients))
|
|
|
|
for idx, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
client := client
|
2020-04-09 12:30:02 -04:00
|
|
|
ng.Go(GlobalContext, func() error {
|
2019-06-06 20:46:22 -04:00
|
|
|
return client.LoadPolicy(policyName)
|
|
|
|
}, idx, *client.host)
|
|
|
|
}
|
|
|
|
return ng.Wait()
|
|
|
|
}
|
|
|
|
|
2019-08-13 16:41:06 -04:00
|
|
|
// LoadPolicyMapping - reloads a policy mapping across all peers
|
|
|
|
func (sys *NotificationSys) LoadPolicyMapping(userOrGroup string, isGroup bool) []NotificationPeerErr {
|
|
|
|
ng := WithNPeers(len(sys.peerClients))
|
|
|
|
for idx, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
client := client
|
2020-04-09 12:30:02 -04:00
|
|
|
ng.Go(GlobalContext, func() error {
|
2019-08-13 16:41:06 -04:00
|
|
|
return client.LoadPolicyMapping(userOrGroup, isGroup)
|
|
|
|
}, idx, *client.host)
|
|
|
|
}
|
|
|
|
return ng.Wait()
|
|
|
|
}
|
|
|
|
|
2019-06-06 20:46:22 -04:00
|
|
|
// DeleteUser - deletes a specific user across all peers
|
|
|
|
func (sys *NotificationSys) DeleteUser(accessKey string) []NotificationPeerErr {
|
|
|
|
ng := WithNPeers(len(sys.peerClients))
|
|
|
|
for idx, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
client := client
|
2020-04-09 12:30:02 -04:00
|
|
|
ng.Go(GlobalContext, func() error {
|
2019-06-06 20:46:22 -04:00
|
|
|
return client.DeleteUser(accessKey)
|
|
|
|
}, idx, *client.host)
|
|
|
|
}
|
|
|
|
return ng.Wait()
|
|
|
|
}
|
|
|
|
|
|
|
|
// LoadUser - reloads a specific user across all peers
|
|
|
|
func (sys *NotificationSys) LoadUser(accessKey string, temp bool) []NotificationPeerErr {
|
|
|
|
ng := WithNPeers(len(sys.peerClients))
|
|
|
|
for idx, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
client := client
|
2020-04-09 12:30:02 -04:00
|
|
|
ng.Go(GlobalContext, func() error {
|
2019-06-06 20:46:22 -04:00
|
|
|
return client.LoadUser(accessKey, temp)
|
|
|
|
}, idx, *client.host)
|
|
|
|
}
|
|
|
|
return ng.Wait()
|
|
|
|
}
|
|
|
|
|
2019-08-02 17:25:00 -04:00
|
|
|
// LoadGroup - loads a specific group on all peers.
|
|
|
|
func (sys *NotificationSys) LoadGroup(group string) []NotificationPeerErr {
|
|
|
|
ng := WithNPeers(len(sys.peerClients))
|
|
|
|
for idx, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
client := client
|
2020-04-09 12:30:02 -04:00
|
|
|
ng.Go(GlobalContext, func() error { return client.LoadGroup(group) }, idx, *client.host)
|
2019-08-02 17:25:00 -04:00
|
|
|
}
|
|
|
|
return ng.Wait()
|
|
|
|
}
|
|
|
|
|
2020-04-24 15:10:09 -04:00
|
|
|
// DeleteServiceAccount - deletes a specific service account across all peers
|
|
|
|
func (sys *NotificationSys) DeleteServiceAccount(accessKey string) []NotificationPeerErr {
|
|
|
|
ng := WithNPeers(len(sys.peerClients))
|
|
|
|
for idx, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
client := client
|
|
|
|
ng.Go(GlobalContext, func() error {
|
|
|
|
return client.DeleteServiceAccount(accessKey)
|
|
|
|
}, idx, *client.host)
|
|
|
|
}
|
|
|
|
return ng.Wait()
|
|
|
|
}
|
|
|
|
|
|
|
|
// LoadServiceAccount - reloads a specific service account across all peers
|
|
|
|
func (sys *NotificationSys) LoadServiceAccount(accessKey string) []NotificationPeerErr {
|
|
|
|
ng := WithNPeers(len(sys.peerClients))
|
|
|
|
for idx, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
client := client
|
|
|
|
ng.Go(GlobalContext, func() error {
|
|
|
|
return client.LoadServiceAccount(accessKey)
|
|
|
|
}, idx, *client.host)
|
|
|
|
}
|
|
|
|
return ng.Wait()
|
|
|
|
}
|
|
|
|
|
2019-06-25 19:42:24 -04:00
|
|
|
// BackgroundHealStatus - returns background heal status of all peers
|
2020-08-07 16:22:53 -04:00
|
|
|
func (sys *NotificationSys) BackgroundHealStatus() ([]madmin.BgHealState, []NotificationPeerErr) {
|
|
|
|
ng := WithNPeers(len(sys.peerClients))
|
2019-06-25 19:42:24 -04:00
|
|
|
states := make([]madmin.BgHealState, len(sys.peerClients))
|
|
|
|
for idx, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
2020-08-07 22:43:06 -04:00
|
|
|
idx := idx
|
2020-08-07 16:22:53 -04:00
|
|
|
client := client
|
|
|
|
ng.Go(GlobalContext, func() error {
|
|
|
|
st, err := client.BackgroundHealStatus()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-06-25 19:42:24 -04:00
|
|
|
states[idx] = st
|
2020-08-07 16:22:53 -04:00
|
|
|
return nil
|
|
|
|
}, idx, *client.host)
|
2019-06-25 19:42:24 -04:00
|
|
|
}
|
|
|
|
|
2020-08-07 16:22:53 -04:00
|
|
|
return states, ng.Wait()
|
2019-06-25 19:42:24 -04:00
|
|
|
}
|
|
|
|
|
2019-01-14 01:44:20 -05:00
|
|
|
// StartProfiling - start profiling on remote peers, by initiating a remote RPC.
|
|
|
|
func (sys *NotificationSys) StartProfiling(profiler string) []NotificationPeerErr {
|
2019-03-14 19:27:31 -04:00
|
|
|
ng := WithNPeers(len(sys.peerClients))
|
|
|
|
for idx, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
2019-01-14 01:44:20 -05:00
|
|
|
client := client
|
2020-04-09 12:30:02 -04:00
|
|
|
ng.Go(GlobalContext, func() error {
|
2019-01-14 01:44:20 -05:00
|
|
|
return client.StartProfiling(profiler)
|
2019-03-14 19:27:31 -04:00
|
|
|
}, idx, *client.host)
|
2019-01-14 01:44:20 -05:00
|
|
|
}
|
|
|
|
return ng.Wait()
|
|
|
|
}
|
|
|
|
|
|
|
|
// DownloadProfilingData - download profiling data from all remote peers.
|
|
|
|
func (sys *NotificationSys) DownloadProfilingData(ctx context.Context, writer io.Writer) bool {
|
|
|
|
profilingDataFound := false
|
|
|
|
|
|
|
|
// Initialize a zip writer which will provide a zipped content
|
|
|
|
// of profiling data of all nodes
|
|
|
|
zipWriter := zip.NewWriter(writer)
|
|
|
|
defer zipWriter.Close()
|
|
|
|
|
2019-03-14 19:27:31 -04:00
|
|
|
for _, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
data, err := client.DownloadProfileData()
|
2019-01-14 01:44:20 -05:00
|
|
|
if err != nil {
|
2019-03-14 19:27:31 -04:00
|
|
|
reqInfo := (&logger.ReqInfo{}).AppendTags("peerAddress", client.host.String())
|
2019-01-14 01:44:20 -05:00
|
|
|
ctx := logger.SetReqInfo(ctx, reqInfo)
|
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
profilingDataFound = true
|
|
|
|
|
2020-01-10 20:19:58 -05:00
|
|
|
for typ, data := range data {
|
|
|
|
// Send profiling data to zip as file
|
|
|
|
header, zerr := zip.FileInfoHeader(dummyFileInfo{
|
2020-03-16 14:39:53 -04:00
|
|
|
name: fmt.Sprintf("profile-%s-%s", client.host.String(), typ),
|
2020-01-10 20:19:58 -05:00
|
|
|
size: int64(len(data)),
|
|
|
|
mode: 0600,
|
|
|
|
modTime: UTCNow(),
|
|
|
|
isDir: false,
|
|
|
|
sys: nil,
|
|
|
|
})
|
|
|
|
if zerr != nil {
|
|
|
|
reqInfo := (&logger.ReqInfo{}).AppendTags("peerAddress", client.host.String())
|
|
|
|
ctx := logger.SetReqInfo(ctx, reqInfo)
|
|
|
|
logger.LogIf(ctx, zerr)
|
|
|
|
continue
|
|
|
|
}
|
2021-01-20 18:49:53 -05:00
|
|
|
header.Method = zip.Deflate
|
2020-01-10 20:19:58 -05:00
|
|
|
zwriter, zerr := zipWriter.CreateHeader(header)
|
|
|
|
if zerr != nil {
|
|
|
|
reqInfo := (&logger.ReqInfo{}).AppendTags("peerAddress", client.host.String())
|
|
|
|
ctx := logger.SetReqInfo(ctx, reqInfo)
|
|
|
|
logger.LogIf(ctx, zerr)
|
|
|
|
continue
|
|
|
|
}
|
2020-12-27 01:58:06 -05:00
|
|
|
if _, err = io.Copy(zwriter, bytes.NewReader(data)); err != nil {
|
2020-01-10 20:19:58 -05:00
|
|
|
reqInfo := (&logger.ReqInfo{}).AppendTags("peerAddress", client.host.String())
|
|
|
|
ctx := logger.SetReqInfo(ctx, reqInfo)
|
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
continue
|
|
|
|
}
|
2019-01-14 01:44:20 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-10 20:19:58 -05:00
|
|
|
// Local host
|
2021-03-26 14:37:58 -04:00
|
|
|
thisAddr, err := xnet.ParseHost(globalLocalNodeName)
|
2019-01-14 01:44:20 -05:00
|
|
|
if err != nil {
|
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
return profilingDataFound
|
|
|
|
}
|
|
|
|
|
|
|
|
data, err := getProfileData()
|
|
|
|
if err != nil {
|
|
|
|
reqInfo := (&logger.ReqInfo{}).AppendTags("peerAddress", thisAddr.String())
|
|
|
|
ctx := logger.SetReqInfo(ctx, reqInfo)
|
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
return profilingDataFound
|
|
|
|
}
|
|
|
|
|
|
|
|
profilingDataFound = true
|
|
|
|
|
|
|
|
// Send profiling data to zip as file
|
2020-01-10 20:19:58 -05:00
|
|
|
for typ, data := range data {
|
|
|
|
header, zerr := zip.FileInfoHeader(dummyFileInfo{
|
2020-03-04 09:58:12 -05:00
|
|
|
name: fmt.Sprintf("profile-%s-%s", thisAddr, typ),
|
2020-01-10 20:19:58 -05:00
|
|
|
size: int64(len(data)),
|
|
|
|
mode: 0600,
|
|
|
|
modTime: UTCNow(),
|
|
|
|
isDir: false,
|
|
|
|
sys: nil,
|
|
|
|
})
|
|
|
|
if zerr != nil {
|
|
|
|
return profilingDataFound
|
|
|
|
}
|
2021-01-20 18:49:53 -05:00
|
|
|
header.Method = zip.Deflate
|
2019-01-14 01:44:20 -05:00
|
|
|
|
2020-01-10 20:19:58 -05:00
|
|
|
zwriter, zerr := zipWriter.CreateHeader(header)
|
|
|
|
if zerr != nil {
|
|
|
|
return profilingDataFound
|
|
|
|
}
|
2019-01-14 01:44:20 -05:00
|
|
|
|
2020-12-27 01:58:06 -05:00
|
|
|
if _, err = io.Copy(zwriter, bytes.NewReader(data)); err != nil {
|
2020-01-10 20:19:58 -05:00
|
|
|
return profilingDataFound
|
|
|
|
}
|
2019-01-14 01:44:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return profilingDataFound
|
|
|
|
}
|
|
|
|
|
2019-08-28 18:04:43 -04:00
|
|
|
// ServerUpdate - updates remote peers.
|
2021-04-08 12:51:11 -04:00
|
|
|
func (sys *NotificationSys) ServerUpdate(ctx context.Context, u *url.URL, sha256Sum []byte, lrTime time.Time, releaseInfo string) []NotificationPeerErr {
|
2019-08-28 18:04:43 -04:00
|
|
|
ng := WithNPeers(len(sys.peerClients))
|
|
|
|
for idx, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
client := client
|
2020-07-23 11:03:31 -04:00
|
|
|
ng.Go(ctx, func() error {
|
2021-04-08 12:51:11 -04:00
|
|
|
return client.ServerUpdate(ctx, u, sha256Sum, lrTime, releaseInfo)
|
2019-08-28 18:04:43 -04:00
|
|
|
}, idx, *client.host)
|
|
|
|
}
|
|
|
|
return ng.Wait()
|
|
|
|
}
|
|
|
|
|
2019-01-14 01:44:20 -05:00
|
|
|
// SignalService - calls signal service RPC call on all peers.
|
|
|
|
func (sys *NotificationSys) SignalService(sig serviceSignal) []NotificationPeerErr {
|
2019-03-14 19:27:31 -04:00
|
|
|
ng := WithNPeers(len(sys.peerClients))
|
|
|
|
for idx, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
2019-01-14 01:44:20 -05:00
|
|
|
client := client
|
2020-04-09 12:30:02 -04:00
|
|
|
ng.Go(GlobalContext, func() error {
|
2019-01-14 01:44:20 -05:00
|
|
|
return client.SignalService(sig)
|
2019-03-14 19:27:31 -04:00
|
|
|
}, idx, *client.host)
|
2019-01-14 01:44:20 -05:00
|
|
|
}
|
|
|
|
return ng.Wait()
|
|
|
|
}
|
|
|
|
|
2020-04-27 13:06:21 -04:00
|
|
|
// updateBloomFilter will cycle all servers to the current index and
|
|
|
|
// return a merged bloom filter if a complete one can be retrieved.
|
|
|
|
func (sys *NotificationSys) updateBloomFilter(ctx context.Context, current uint64) (*bloomFilter, error) {
|
|
|
|
var req = bloomFilterRequest{
|
|
|
|
Current: current,
|
|
|
|
Oldest: current - dataUsageUpdateDirCycles,
|
|
|
|
}
|
|
|
|
if current < dataUsageUpdateDirCycles {
|
|
|
|
req.Oldest = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load initial state from local...
|
|
|
|
var bf *bloomFilter
|
2020-10-28 12:18:35 -04:00
|
|
|
bfr, err := intDataUpdateTracker.cycleFilter(ctx, req)
|
2020-04-27 13:06:21 -04:00
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
if err == nil && bfr.Complete {
|
|
|
|
nbf := intDataUpdateTracker.newBloomFilter()
|
|
|
|
bf = &nbf
|
2020-12-27 01:58:06 -05:00
|
|
|
_, err = bf.ReadFrom(bytes.NewReader(bfr.Filter))
|
2020-04-27 13:06:21 -04:00
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var mu sync.Mutex
|
|
|
|
g := errgroup.WithNErrs(len(sys.peerClients))
|
|
|
|
for idx, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
client := client
|
|
|
|
g.Go(func() error {
|
|
|
|
serverBF, err := client.cycleServerBloomFilter(ctx, req)
|
|
|
|
if false && intDataUpdateTracker.debug {
|
|
|
|
b, _ := json.MarshalIndent(serverBF, "", " ")
|
|
|
|
logger.Info("Disk %v, Bloom filter: %v", client.host.Name, string(b))
|
|
|
|
}
|
|
|
|
// Keep lock while checking result.
|
|
|
|
mu.Lock()
|
|
|
|
defer mu.Unlock()
|
|
|
|
|
|
|
|
if err != nil || !serverBF.Complete || bf == nil {
|
2021-01-28 16:40:31 -05:00
|
|
|
logger.LogOnceIf(ctx, err, fmt.Sprintf("host:%s, cycle:%d", client.host, current), client.cycleServerBloomFilter)
|
2020-04-27 13:06:21 -04:00
|
|
|
bf = nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var tmp bloom.BloomFilter
|
2020-12-27 01:58:06 -05:00
|
|
|
_, err = tmp.ReadFrom(bytes.NewReader(serverBF.Filter))
|
2020-04-27 13:06:21 -04:00
|
|
|
if err != nil {
|
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
bf = nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if bf.BloomFilter == nil {
|
|
|
|
bf.BloomFilter = &tmp
|
|
|
|
} else {
|
|
|
|
err = bf.Merge(&tmp)
|
|
|
|
if err != nil {
|
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
bf = nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}, idx)
|
|
|
|
}
|
|
|
|
g.Wait()
|
|
|
|
return bf, nil
|
|
|
|
}
|
|
|
|
|
2021-04-04 18:34:33 -04:00
|
|
|
var errPeerNotReachable = errors.New("peer is not reachable")
|
|
|
|
|
2019-01-24 10:22:14 -05:00
|
|
|
// GetLocks - makes GetLocks RPC call on all peers.
|
2020-09-25 22:21:52 -04:00
|
|
|
func (sys *NotificationSys) GetLocks(ctx context.Context, r *http.Request) []*PeerLocks {
|
2019-03-14 19:27:31 -04:00
|
|
|
locksResp := make([]*PeerLocks, len(sys.peerClients))
|
2019-10-14 12:44:51 -04:00
|
|
|
g := errgroup.WithNErrs(len(sys.peerClients))
|
2019-03-14 19:27:31 -04:00
|
|
|
for index, client := range sys.peerClients {
|
2019-10-14 12:44:51 -04:00
|
|
|
index := index
|
|
|
|
g.Go(func() error {
|
2020-09-25 22:21:52 -04:00
|
|
|
if client == nil {
|
2021-04-04 18:34:33 -04:00
|
|
|
return errPeerNotReachable
|
2020-09-25 22:21:52 -04:00
|
|
|
}
|
|
|
|
serverLocksResp, err := sys.peerClients[index].GetLocks()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
locksResp[index] = &PeerLocks{
|
|
|
|
Addr: sys.peerClients[index].host.String(),
|
|
|
|
Locks: serverLocksResp,
|
2019-01-24 10:22:14 -05:00
|
|
|
}
|
2019-10-14 12:44:51 -04:00
|
|
|
return nil
|
|
|
|
}, index)
|
|
|
|
}
|
|
|
|
for index, err := range g.Wait() {
|
|
|
|
reqInfo := (&logger.ReqInfo{}).AppendTags("peerAddress",
|
|
|
|
sys.peerClients[index].host.String())
|
|
|
|
ctx := logger.SetReqInfo(ctx, reqInfo)
|
|
|
|
logger.LogOnceIf(ctx, err, sys.peerClients[index].host.String())
|
2019-01-24 10:22:14 -05:00
|
|
|
}
|
2020-09-25 22:21:52 -04:00
|
|
|
locksResp = append(locksResp, &PeerLocks{
|
|
|
|
Addr: getHostName(r),
|
2020-12-10 10:28:37 -05:00
|
|
|
Locks: globalLockServer.DupLockMap(),
|
2020-09-25 22:21:52 -04:00
|
|
|
})
|
2019-01-24 10:22:14 -05:00
|
|
|
return locksResp
|
|
|
|
}
|
|
|
|
|
2020-05-19 16:53:54 -04:00
|
|
|
// LoadBucketMetadata - calls LoadBucketMetadata call on all peers
|
|
|
|
func (sys *NotificationSys) LoadBucketMetadata(ctx context.Context, bucketName string) {
|
|
|
|
ng := WithNPeers(len(sys.peerClients))
|
|
|
|
for idx, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
2019-07-19 16:20:33 -04:00
|
|
|
}
|
2020-05-19 16:53:54 -04:00
|
|
|
client := client
|
|
|
|
ng.Go(ctx, func() error {
|
|
|
|
return client.LoadBucketMetadata(bucketName)
|
|
|
|
}, idx, *client.host)
|
|
|
|
}
|
|
|
|
for _, nErr := range ng.Wait() {
|
|
|
|
reqInfo := (&logger.ReqInfo{}).AppendTags("peerAddress", nErr.Host.String())
|
|
|
|
if nErr.Err != nil {
|
|
|
|
logger.LogIf(logger.SetReqInfo(ctx, reqInfo), nErr.Err)
|
2019-07-19 16:20:33 -04:00
|
|
|
}
|
2020-05-19 16:53:54 -04:00
|
|
|
}
|
2019-07-19 16:20:33 -04:00
|
|
|
}
|
|
|
|
|
2020-05-19 16:53:54 -04:00
|
|
|
// DeleteBucketMetadata - calls DeleteBucketMetadata call on all peers
|
|
|
|
func (sys *NotificationSys) DeleteBucketMetadata(ctx context.Context, bucketName string) {
|
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)
|
|
|
|
}
|
2020-02-05 04:42:34 -05:00
|
|
|
|
2020-05-19 16:53:54 -04:00
|
|
|
ng := WithNPeers(len(sys.peerClients))
|
|
|
|
for idx, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
2020-02-05 04:42:34 -05:00
|
|
|
}
|
2020-05-19 16:53:54 -04:00
|
|
|
client := client
|
|
|
|
ng.Go(ctx, func() error {
|
|
|
|
return client.DeleteBucketMetadata(bucketName)
|
|
|
|
}, idx, *client.host)
|
|
|
|
}
|
|
|
|
for _, nErr := range ng.Wait() {
|
|
|
|
reqInfo := (&logger.ReqInfo{}).AppendTags("peerAddress", nErr.Host.String())
|
|
|
|
if nErr.Err != nil {
|
|
|
|
logger.LogIf(logger.SetReqInfo(ctx, reqInfo), nErr.Err)
|
2018-05-09 18:11:51 -04:00
|
|
|
}
|
2020-05-19 16:53:54 -04:00
|
|
|
}
|
2018-03-15 16:03:41 -04:00
|
|
|
}
|
|
|
|
|
2021-04-04 18:34:33 -04:00
|
|
|
// GetClusterBucketStats - calls GetClusterBucketStats call on all peers for a cluster statistics view.
|
|
|
|
func (sys *NotificationSys) GetClusterBucketStats(ctx context.Context, bucketName string) []BucketStats {
|
|
|
|
ng := WithNPeers(len(sys.peerClients))
|
|
|
|
bucketStats := make([]BucketStats, len(sys.peerClients))
|
|
|
|
for index, client := range sys.peerClients {
|
|
|
|
index := index
|
2021-04-06 11:36:54 -04:00
|
|
|
client := client
|
2021-04-04 18:34:33 -04:00
|
|
|
ng.Go(ctx, func() error {
|
|
|
|
if client == nil {
|
|
|
|
return errPeerNotReachable
|
|
|
|
}
|
|
|
|
bs, err := client.GetBucketStats(bucketName)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
bucketStats[index] = bs
|
|
|
|
return nil
|
|
|
|
}, index, *client.host)
|
|
|
|
}
|
|
|
|
for _, nErr := range ng.Wait() {
|
|
|
|
reqInfo := (&logger.ReqInfo{}).AppendTags("peerAddress", nErr.Host.String())
|
|
|
|
if nErr.Err != nil {
|
|
|
|
logger.LogIf(logger.SetReqInfo(ctx, reqInfo), nErr.Err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bucketStats = append(bucketStats, BucketStats{
|
|
|
|
ReplicationStats: globalReplicationStats.Get(bucketName),
|
|
|
|
})
|
|
|
|
return bucketStats
|
|
|
|
}
|
|
|
|
|
2021-04-19 13:30:42 -04:00
|
|
|
// LoadTransitionTierConfig notifies remote peers to load their remote tier
|
|
|
|
// configs from config store.
|
|
|
|
func (sys *NotificationSys) LoadTransitionTierConfig(ctx context.Context) {
|
|
|
|
ng := WithNPeers(len(sys.peerClients))
|
|
|
|
for idx, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
client := client
|
|
|
|
ng.Go(ctx, func() error {
|
|
|
|
return client.LoadTransitionTierConfig(ctx)
|
|
|
|
}, idx, *client.host)
|
|
|
|
}
|
|
|
|
for _, nErr := range ng.Wait() {
|
|
|
|
reqInfo := (&logger.ReqInfo{}).AppendTags("peerAddress", nErr.Host.String())
|
|
|
|
if nErr.Err != nil {
|
|
|
|
logger.LogIf(logger.SetReqInfo(ctx, reqInfo), nErr.Err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-01 20:05:02 -04:00
|
|
|
// Loads notification policies for all buckets into NotificationSys.
|
2020-10-22 16:36:24 -04:00
|
|
|
func (sys *NotificationSys) load(buckets []BucketInfo) {
|
2018-03-15 16:03:41 -04:00
|
|
|
for _, bucket := range buckets {
|
2020-04-09 12:30:02 -04:00
|
|
|
ctx := logger.SetReqInfo(GlobalContext, &logger.ReqInfo{BucketName: bucket.Name})
|
2020-05-20 13:18:15 -04:00
|
|
|
config, err := globalBucketMetadataSys.GetNotificationConfig(bucket.Name)
|
2020-05-19 16:53:54 -04:00
|
|
|
if err != nil {
|
2020-10-22 16:36:24 -04:00
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
continue
|
2018-03-15 16:03:41 -04:00
|
|
|
}
|
2020-05-21 14:03:59 -04:00
|
|
|
config.SetRegion(globalServerRegion)
|
2020-05-20 13:18:15 -04:00
|
|
|
if err = config.Validate(globalServerRegion, globalNotificationSys.targetList); err != nil {
|
2020-05-19 16:53:54 -04:00
|
|
|
if _, ok := err.(*event.ErrARNNotFound); !ok {
|
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
}
|
2018-10-08 18:47:13 -04:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
sys.AddRulesMap(bucket.Name, config.ToRulesMap())
|
2018-03-15 16:03:41 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-12 23:04:01 -04:00
|
|
|
// Init - initializes notification system from notification.xml and listenxl.meta of all buckets.
|
2020-08-20 13:38:53 -04:00
|
|
|
func (sys *NotificationSys) Init(ctx context.Context, buckets []BucketInfo, objAPI ObjectLayer) error {
|
2018-10-08 18:47:13 -04:00
|
|
|
if objAPI == nil {
|
2020-05-04 12:42:58 -04:00
|
|
|
return errServerNotInitialized
|
2018-10-08 18:47:13 -04:00
|
|
|
}
|
|
|
|
|
2020-10-09 12:59:52 -04:00
|
|
|
// In gateway mode, notifications are not supported - except NAS gateway.
|
2020-02-02 04:52:07 -05:00
|
|
|
if globalIsGateway && !objAPI.IsNotificationSupported() {
|
2019-10-01 20:05:02 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-10-09 12:59:52 -04:00
|
|
|
logger.LogIf(ctx, sys.targetList.Add(globalConfigTargetList.Targets()...))
|
2019-12-22 01:35:50 -05:00
|
|
|
|
2020-04-27 09:25:05 -04:00
|
|
|
go func() {
|
|
|
|
for res := range sys.targetResCh {
|
|
|
|
if res.Err != nil {
|
|
|
|
reqInfo := &logger.ReqInfo{}
|
|
|
|
reqInfo.AppendTags("targetID", res.ID.Name)
|
2020-08-20 13:38:53 -04:00
|
|
|
logger.LogOnceIf(logger.SetReqInfo(GlobalContext, reqInfo), res.Err, res.ID)
|
2020-04-27 09:25:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2020-10-22 16:36:24 -04:00
|
|
|
go sys.load(buckets)
|
2020-08-20 13:38:53 -04:00
|
|
|
return nil
|
2018-10-08 18:47:13 -04:00
|
|
|
}
|
|
|
|
|
2018-03-15 16:03:41 -04:00
|
|
|
// AddRulesMap - adds rules map for bucket name.
|
|
|
|
func (sys *NotificationSys) AddRulesMap(bucketName string, rulesMap event.RulesMap) {
|
|
|
|
sys.Lock()
|
|
|
|
defer sys.Unlock()
|
|
|
|
|
|
|
|
rulesMap = rulesMap.Clone()
|
|
|
|
|
|
|
|
for _, targetRulesMap := range sys.bucketRemoteTargetRulesMap[bucketName] {
|
|
|
|
rulesMap.Add(targetRulesMap)
|
|
|
|
}
|
|
|
|
|
2018-08-23 13:23:18 -04:00
|
|
|
// Do not add for an empty rulesMap.
|
|
|
|
if len(rulesMap) == 0 {
|
|
|
|
delete(sys.bucketRulesMap, bucketName)
|
|
|
|
} else {
|
|
|
|
sys.bucketRulesMap[bucketName] = rulesMap
|
|
|
|
}
|
2018-03-15 16:03:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// RemoveRulesMap - removes rules map for bucket name.
|
|
|
|
func (sys *NotificationSys) RemoveRulesMap(bucketName string, rulesMap event.RulesMap) {
|
|
|
|
sys.Lock()
|
|
|
|
defer sys.Unlock()
|
|
|
|
|
|
|
|
sys.bucketRulesMap[bucketName].Remove(rulesMap)
|
|
|
|
if len(sys.bucketRulesMap[bucketName]) == 0 {
|
|
|
|
delete(sys.bucketRulesMap, bucketName)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-13 15:36:45 -05:00
|
|
|
// ConfiguredTargetIDs - returns list of configured target id's
|
|
|
|
func (sys *NotificationSys) ConfiguredTargetIDs() []event.TargetID {
|
2019-12-19 16:45:56 -05:00
|
|
|
if sys == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-12-13 15:36:45 -05:00
|
|
|
sys.RLock()
|
|
|
|
defer sys.RUnlock()
|
|
|
|
|
|
|
|
var targetIDs []event.TargetID
|
|
|
|
for _, rmap := range sys.bucketRulesMap {
|
|
|
|
for _, rules := range rmap {
|
|
|
|
for _, targetSet := range rules {
|
2020-04-27 09:25:05 -04:00
|
|
|
for id := range targetSet {
|
|
|
|
targetIDs = append(targetIDs, id)
|
|
|
|
}
|
2019-12-13 15:36:45 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-14 03:01:15 -04:00
|
|
|
// Filter out targets configured via env
|
|
|
|
var tIDs []event.TargetID
|
|
|
|
for _, targetID := range targetIDs {
|
|
|
|
if !globalEnvTargetList.Exists(targetID) {
|
|
|
|
tIDs = append(tIDs, targetID)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return tIDs
|
2019-12-13 15:36:45 -05:00
|
|
|
}
|
|
|
|
|
2018-03-15 16:03:41 -04:00
|
|
|
// RemoveNotification - removes all notification configuration for bucket name.
|
|
|
|
func (sys *NotificationSys) RemoveNotification(bucketName string) {
|
|
|
|
sys.Lock()
|
|
|
|
defer sys.Unlock()
|
|
|
|
|
|
|
|
delete(sys.bucketRulesMap, bucketName)
|
|
|
|
|
2020-04-27 09:25:05 -04:00
|
|
|
targetIDSet := event.NewTargetIDSet()
|
2018-03-15 16:03:41 -04:00
|
|
|
for targetID := range sys.bucketRemoteTargetRulesMap[bucketName] {
|
2020-04-27 09:25:05 -04:00
|
|
|
targetIDSet[targetID] = struct{}{}
|
2018-03-15 16:03:41 -04:00
|
|
|
delete(sys.bucketRemoteTargetRulesMap[bucketName], targetID)
|
|
|
|
}
|
2020-04-27 09:25:05 -04:00
|
|
|
sys.targetList.Remove(targetIDSet)
|
2018-03-15 16:03:41 -04:00
|
|
|
|
|
|
|
delete(sys.bucketRemoteTargetRulesMap, bucketName)
|
|
|
|
}
|
|
|
|
|
2020-04-27 09:25:05 -04:00
|
|
|
// RemoveAllRemoteTargets - closes and removes all notification targets.
|
2018-03-15 16:03:41 -04:00
|
|
|
func (sys *NotificationSys) RemoveAllRemoteTargets() {
|
2020-03-22 01:10:13 -04:00
|
|
|
sys.Lock()
|
|
|
|
defer sys.Unlock()
|
|
|
|
|
2018-03-15 16:03:41 -04:00
|
|
|
for _, targetMap := range sys.bucketRemoteTargetRulesMap {
|
2020-04-27 09:25:05 -04:00
|
|
|
targetIDSet := event.NewTargetIDSet()
|
|
|
|
for k := range targetMap {
|
|
|
|
targetIDSet[k] = struct{}{}
|
2018-03-15 16:03:41 -04:00
|
|
|
}
|
2020-04-27 09:25:05 -04:00
|
|
|
sys.targetList.Remove(targetIDSet)
|
2018-03-15 16:03:41 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send - sends event data to all matching targets.
|
2020-04-27 09:25:05 -04:00
|
|
|
func (sys *NotificationSys) Send(args eventArgs) {
|
2018-03-15 16:03:41 -04:00
|
|
|
sys.RLock()
|
|
|
|
targetIDSet := sys.bucketRulesMap[args.BucketName].Match(args.EventName, args.Object.Name)
|
|
|
|
sys.RUnlock()
|
2018-10-12 15:25:59 -04:00
|
|
|
|
2018-03-15 16:03:41 -04:00
|
|
|
if len(targetIDSet) == 0 {
|
2020-04-27 09:25:05 -04:00
|
|
|
return
|
2018-03-15 16:03:41 -04:00
|
|
|
}
|
|
|
|
|
2020-04-27 09:25:05 -04:00
|
|
|
sys.targetList.Send(args.ToEvent(true), targetIDSet, sys.targetResCh)
|
2018-03-15 16:03:41 -04:00
|
|
|
}
|
|
|
|
|
2021-06-01 11:55:49 -04:00
|
|
|
// GetNetPerfInfo - Net information
|
|
|
|
func (sys *NotificationSys) GetNetPerfInfo(ctx context.Context) madmin.NetPerfInfo {
|
2020-03-27 00:07:39 -04:00
|
|
|
var sortedGlobalEndpoints []string
|
|
|
|
|
|
|
|
/*
|
|
|
|
Ensure that only untraversed links are visited by this server
|
2020-11-20 15:52:53 -05:00
|
|
|
i.e. if net perf tests have been performed between a -> b, then do
|
2020-03-27 00:07:39 -04:00
|
|
|
not run it between b -> a
|
|
|
|
|
|
|
|
The graph of tests looks like this
|
|
|
|
|
|
|
|
a b c d
|
|
|
|
a | o | x | x | x |
|
|
|
|
b | o | o | x | x |
|
|
|
|
c | o | o | o | x |
|
|
|
|
d | o | o | o | o |
|
|
|
|
|
|
|
|
'x's should be tested, and 'o's should be skipped
|
|
|
|
*/
|
|
|
|
|
|
|
|
hostSet := set.NewStringSet()
|
|
|
|
for _, ez := range globalEndpoints {
|
|
|
|
for _, e := range ez.Endpoints {
|
2020-03-31 04:15:21 -04:00
|
|
|
if !hostSet.Contains(e.Host) {
|
|
|
|
sortedGlobalEndpoints = append(sortedGlobalEndpoints, e.Host)
|
|
|
|
hostSet.Add(e.Host)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Strings(sortedGlobalEndpoints)
|
|
|
|
var remoteTargets []*peerRESTClient
|
|
|
|
search := func(host string) *peerRESTClient {
|
|
|
|
for index, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if sys.peerClients[index].host.String() == host {
|
|
|
|
return client
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < len(sortedGlobalEndpoints); i++ {
|
2021-03-26 14:37:58 -04:00
|
|
|
if sortedGlobalEndpoints[i] != globalLocalNodeName {
|
2020-03-27 00:07:39 -04:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
for j := 0; j < len(sortedGlobalEndpoints); j++ {
|
|
|
|
if j > i {
|
|
|
|
remoteTarget := search(sortedGlobalEndpoints[j])
|
|
|
|
if remoteTarget != nil {
|
|
|
|
remoteTargets = append(remoteTargets, remoteTarget)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-01 11:55:49 -04:00
|
|
|
netInfos := make([]madmin.PeerNetPerfInfo, len(remoteTargets))
|
2020-03-27 00:07:39 -04:00
|
|
|
|
|
|
|
for index, client := range remoteTargets {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
var err error
|
2021-06-01 11:55:49 -04:00
|
|
|
netInfos[index], err = client.GetNetPerfInfo(ctx)
|
2020-03-27 00:07:39 -04:00
|
|
|
|
|
|
|
addr := client.host.String()
|
|
|
|
reqInfo := (&logger.ReqInfo{}).AppendTags("remotePeer", addr)
|
2020-04-09 12:30:02 -04:00
|
|
|
ctx := logger.SetReqInfo(GlobalContext, reqInfo)
|
2020-03-27 00:07:39 -04:00
|
|
|
logger.LogIf(ctx, err)
|
2020-11-20 15:52:53 -05:00
|
|
|
netInfos[index].Addr = addr
|
2020-03-27 00:07:39 -04:00
|
|
|
if err != nil {
|
2020-11-20 15:52:53 -05:00
|
|
|
netInfos[index].Error = err.Error()
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
}
|
2021-06-01 11:55:49 -04:00
|
|
|
return madmin.NetPerfInfo{
|
2021-07-12 13:16:10 -04:00
|
|
|
NodeCommon: madmin.NodeCommon{Addr: globalLocalNodeName},
|
2021-06-01 11:55:49 -04:00
|
|
|
RemotePeers: netInfos,
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-20 15:52:53 -05:00
|
|
|
// DispatchNetPerfInfo - Net perf information from other nodes
|
2021-06-01 11:55:49 -04:00
|
|
|
func (sys *NotificationSys) DispatchNetPerfInfo(ctx context.Context) []madmin.NetPerfInfo {
|
|
|
|
serverNetInfos := []madmin.NetPerfInfo{}
|
2020-03-27 00:07:39 -04:00
|
|
|
|
|
|
|
for index, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
2020-11-20 15:52:53 -05:00
|
|
|
serverNetInfo, err := sys.peerClients[index].DispatchNetInfo(ctx)
|
2020-03-27 00:07:39 -04:00
|
|
|
if err != nil {
|
2020-11-20 15:52:53 -05:00
|
|
|
serverNetInfo.Addr = client.host.String()
|
|
|
|
serverNetInfo.Error = err.Error()
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
2020-11-20 15:52:53 -05:00
|
|
|
serverNetInfos = append(serverNetInfos, serverNetInfo)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
2020-11-20 15:52:53 -05:00
|
|
|
return serverNetInfos
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
|
2020-11-20 15:52:53 -05:00
|
|
|
// DispatchNetPerfChan - Net perf information from other nodes
|
2021-06-01 11:55:49 -04:00
|
|
|
func (sys *NotificationSys) DispatchNetPerfChan(ctx context.Context) chan madmin.NetPerfInfo {
|
|
|
|
serverNetInfos := make(chan madmin.NetPerfInfo)
|
2020-05-22 20:56:45 -04:00
|
|
|
wg := sync.WaitGroup{}
|
|
|
|
|
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
for _, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
2020-11-20 15:52:53 -05:00
|
|
|
serverNetInfo, err := client.DispatchNetInfo(ctx)
|
2020-05-22 20:56:45 -04:00
|
|
|
if err != nil {
|
2020-11-20 15:52:53 -05:00
|
|
|
serverNetInfo.Addr = client.host.String()
|
|
|
|
serverNetInfo.Error = err.Error()
|
2020-05-22 20:56:45 -04:00
|
|
|
}
|
2020-11-20 15:52:53 -05:00
|
|
|
serverNetInfos <- serverNetInfo
|
2020-05-22 20:56:45 -04:00
|
|
|
}
|
|
|
|
wg.Done()
|
|
|
|
}()
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
wg.Wait()
|
2020-11-20 15:52:53 -05:00
|
|
|
close(serverNetInfos)
|
2020-05-22 20:56:45 -04:00
|
|
|
}()
|
|
|
|
|
2020-11-20 15:52:53 -05:00
|
|
|
return serverNetInfos
|
2020-05-22 20:56:45 -04:00
|
|
|
}
|
|
|
|
|
2021-06-01 11:55:49 -04:00
|
|
|
// GetParallelNetPerfInfo - Performs Net parallel tests
|
|
|
|
func (sys *NotificationSys) GetParallelNetPerfInfo(ctx context.Context) madmin.NetPerfInfo {
|
|
|
|
netInfos := []madmin.PeerNetPerfInfo{}
|
2020-03-31 20:08:28 -04:00
|
|
|
wg := sync.WaitGroup{}
|
|
|
|
|
|
|
|
for index, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
wg.Add(1)
|
|
|
|
go func(index int) {
|
2021-06-01 11:55:49 -04:00
|
|
|
netInfo, err := sys.peerClients[index].GetNetPerfInfo(ctx)
|
2020-11-20 15:52:53 -05:00
|
|
|
netInfo.Addr = sys.peerClients[index].host.String()
|
2020-03-31 20:08:28 -04:00
|
|
|
if err != nil {
|
2020-11-20 15:52:53 -05:00
|
|
|
netInfo.Error = err.Error()
|
2020-03-31 20:08:28 -04:00
|
|
|
}
|
2020-11-20 15:52:53 -05:00
|
|
|
netInfos = append(netInfos, netInfo)
|
2020-03-31 20:08:28 -04:00
|
|
|
wg.Done()
|
|
|
|
}(index)
|
|
|
|
}
|
|
|
|
wg.Wait()
|
2021-06-01 11:55:49 -04:00
|
|
|
return madmin.NetPerfInfo{
|
2021-07-12 13:16:10 -04:00
|
|
|
NodeCommon: madmin.NodeCommon{Addr: globalLocalNodeName},
|
2021-06-01 11:55:49 -04:00
|
|
|
RemotePeers: netInfos,
|
2020-03-31 20:08:28 -04:00
|
|
|
}
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
|
2021-06-01 11:55:49 -04:00
|
|
|
// GetDrivePerfInfos - Drive performance information
|
|
|
|
func (sys *NotificationSys) GetDrivePerfInfos(ctx context.Context) chan madmin.DrivePerfInfos {
|
|
|
|
updateChan := make(chan madmin.DrivePerfInfos)
|
2020-05-22 20:56:45 -04:00
|
|
|
wg := sync.WaitGroup{}
|
|
|
|
|
|
|
|
for _, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
wg.Add(1)
|
|
|
|
go func(client *peerRESTClient) {
|
2021-06-01 11:55:49 -04:00
|
|
|
reply, err := client.GetDrivePerfInfos(ctx)
|
2020-05-22 20:56:45 -04:00
|
|
|
|
|
|
|
addr := client.host.String()
|
|
|
|
reqInfo := (&logger.ReqInfo{}).AppendTags("remotePeer", addr)
|
|
|
|
ctx := logger.SetReqInfo(GlobalContext, reqInfo)
|
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
|
|
|
|
reply.Addr = addr
|
|
|
|
if err != nil {
|
|
|
|
reply.Error = err.Error()
|
|
|
|
}
|
|
|
|
|
|
|
|
updateChan <- reply
|
|
|
|
wg.Done()
|
|
|
|
}(client)
|
|
|
|
}
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
wg.Wait()
|
|
|
|
close(updateChan)
|
|
|
|
}()
|
|
|
|
|
|
|
|
return updateChan
|
|
|
|
}
|
|
|
|
|
2021-06-01 11:55:49 -04:00
|
|
|
// GetCPUs - Get all CPU information.
|
|
|
|
func (sys *NotificationSys) GetCPUs(ctx context.Context) []madmin.CPUs {
|
|
|
|
reply := make([]madmin.CPUs, len(sys.peerClients))
|
2020-03-27 00:07:39 -04:00
|
|
|
|
|
|
|
g := errgroup.WithNErrs(len(sys.peerClients))
|
|
|
|
for index, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
index := index
|
|
|
|
g.Go(func() error {
|
|
|
|
var err error
|
2021-06-01 11:55:49 -04:00
|
|
|
reply[index], err = sys.peerClients[index].GetCPUs(ctx)
|
2020-03-27 00:07:39 -04:00
|
|
|
return err
|
|
|
|
}, index)
|
|
|
|
}
|
|
|
|
|
|
|
|
for index, err := range g.Wait() {
|
|
|
|
if err != nil {
|
2021-08-12 21:58:40 -04:00
|
|
|
sys.addNodeErr(&reply[index], sys.peerClients[index], err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return reply
|
|
|
|
}
|
|
|
|
|
2021-06-01 11:55:49 -04:00
|
|
|
// GetPartitions - Disk partition information
|
|
|
|
func (sys *NotificationSys) GetPartitions(ctx context.Context) []madmin.Partitions {
|
|
|
|
reply := make([]madmin.Partitions, len(sys.peerClients))
|
2020-03-27 00:07:39 -04:00
|
|
|
|
|
|
|
g := errgroup.WithNErrs(len(sys.peerClients))
|
|
|
|
for index, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
index := index
|
|
|
|
g.Go(func() error {
|
|
|
|
var err error
|
2021-06-01 11:55:49 -04:00
|
|
|
reply[index], err = sys.peerClients[index].GetPartitions(ctx)
|
2020-03-27 00:07:39 -04:00
|
|
|
return err
|
|
|
|
}, index)
|
|
|
|
}
|
|
|
|
|
|
|
|
for index, err := range g.Wait() {
|
|
|
|
if err != nil {
|
2021-08-12 21:58:40 -04:00
|
|
|
sys.addNodeErr(&reply[index], sys.peerClients[index], err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return reply
|
|
|
|
}
|
|
|
|
|
2021-06-01 11:55:49 -04:00
|
|
|
// GetOSInfo - Get operating system's information
|
|
|
|
func (sys *NotificationSys) GetOSInfo(ctx context.Context) []madmin.OSInfo {
|
|
|
|
reply := make([]madmin.OSInfo, len(sys.peerClients))
|
2020-03-27 00:07:39 -04:00
|
|
|
|
|
|
|
g := errgroup.WithNErrs(len(sys.peerClients))
|
|
|
|
for index, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
index := index
|
|
|
|
g.Go(func() error {
|
|
|
|
var err error
|
2021-06-01 11:55:49 -04:00
|
|
|
reply[index], err = sys.peerClients[index].GetOSInfo(ctx)
|
2020-03-27 00:07:39 -04:00
|
|
|
return err
|
|
|
|
}, index)
|
|
|
|
}
|
|
|
|
|
2021-08-24 20:09:37 -04:00
|
|
|
for index, err := range g.Wait() {
|
|
|
|
if err != nil {
|
|
|
|
sys.addNodeErr(&reply[index], sys.peerClients[index], err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return reply
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetSysConfig - Get information about system config
|
|
|
|
// (only the config that are of concern to minio)
|
|
|
|
func (sys *NotificationSys) GetSysConfig(ctx context.Context) []madmin.SysConfig {
|
|
|
|
reply := make([]madmin.SysConfig, len(sys.peerClients))
|
|
|
|
|
|
|
|
g := errgroup.WithNErrs(len(sys.peerClients))
|
|
|
|
for index, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
index := index
|
|
|
|
g.Go(func() error {
|
|
|
|
var err error
|
|
|
|
reply[index], err = sys.peerClients[index].GetSysConfig(ctx)
|
|
|
|
return err
|
|
|
|
}, index)
|
|
|
|
}
|
|
|
|
|
2021-07-30 02:05:34 -04:00
|
|
|
for index, err := range g.Wait() {
|
|
|
|
if err != nil {
|
2021-08-12 21:58:40 -04:00
|
|
|
sys.addNodeErr(&reply[index], sys.peerClients[index], err)
|
2021-07-30 02:05:34 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return reply
|
|
|
|
}
|
|
|
|
|
2021-08-12 21:58:40 -04:00
|
|
|
// GetSysServices - Get information about system services
|
|
|
|
// (only the services that are of concern to minio)
|
|
|
|
func (sys *NotificationSys) GetSysServices(ctx context.Context) []madmin.SysServices {
|
|
|
|
reply := make([]madmin.SysServices, len(sys.peerClients))
|
|
|
|
|
|
|
|
g := errgroup.WithNErrs(len(sys.peerClients))
|
|
|
|
for index, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
index := index
|
|
|
|
g.Go(func() error {
|
|
|
|
var err error
|
|
|
|
reply[index], err = sys.peerClients[index].GetSELinuxInfo(ctx)
|
|
|
|
return err
|
|
|
|
}, index)
|
|
|
|
}
|
|
|
|
|
|
|
|
for index, err := range g.Wait() {
|
|
|
|
if err != nil {
|
|
|
|
sys.addNodeErr(&reply[index], sys.peerClients[index], err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return reply
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sys *NotificationSys) addNodeErr(nodeInfo madmin.NodeInfo, peerClient *peerRESTClient, err error) {
|
|
|
|
addr := peerClient.host.String()
|
|
|
|
reqInfo := (&logger.ReqInfo{}).AppendTags("remotePeer", addr)
|
|
|
|
ctx := logger.SetReqInfo(GlobalContext, reqInfo)
|
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
nodeInfo.SetAddr(addr)
|
|
|
|
nodeInfo.SetError(err.Error())
|
|
|
|
}
|
|
|
|
|
2021-07-30 02:05:34 -04:00
|
|
|
// GetSysErrors - Memory information
|
|
|
|
func (sys *NotificationSys) GetSysErrors(ctx context.Context) []madmin.SysErrors {
|
|
|
|
reply := make([]madmin.SysErrors, len(sys.peerClients))
|
|
|
|
|
|
|
|
g := errgroup.WithNErrs(len(sys.peerClients))
|
|
|
|
for index, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
index := index
|
|
|
|
g.Go(func() error {
|
|
|
|
var err error
|
|
|
|
reply[index], err = sys.peerClients[index].GetSysErrors(ctx)
|
|
|
|
return err
|
|
|
|
}, index)
|
|
|
|
}
|
|
|
|
|
2020-03-27 00:07:39 -04:00
|
|
|
for index, err := range g.Wait() {
|
|
|
|
if err != nil {
|
2021-08-12 21:58:40 -04:00
|
|
|
sys.addNodeErr(&reply[index], sys.peerClients[index], err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return reply
|
|
|
|
}
|
|
|
|
|
2021-06-01 11:55:49 -04:00
|
|
|
// GetMemInfo - Memory information
|
|
|
|
func (sys *NotificationSys) GetMemInfo(ctx context.Context) []madmin.MemInfo {
|
|
|
|
reply := make([]madmin.MemInfo, len(sys.peerClients))
|
2020-03-27 00:07:39 -04:00
|
|
|
|
|
|
|
g := errgroup.WithNErrs(len(sys.peerClients))
|
|
|
|
for index, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
index := index
|
|
|
|
g.Go(func() error {
|
|
|
|
var err error
|
2021-06-01 11:55:49 -04:00
|
|
|
reply[index], err = sys.peerClients[index].GetMemInfo(ctx)
|
2020-03-27 00:07:39 -04:00
|
|
|
return err
|
|
|
|
}, index)
|
|
|
|
}
|
|
|
|
|
|
|
|
for index, err := range g.Wait() {
|
|
|
|
if err != nil {
|
2021-08-12 21:58:40 -04:00
|
|
|
sys.addNodeErr(&reply[index], sys.peerClients[index], err)
|
2020-03-27 00:07:39 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return reply
|
|
|
|
}
|
|
|
|
|
2021-06-01 11:55:49 -04:00
|
|
|
// GetProcInfo - Process information
|
|
|
|
func (sys *NotificationSys) GetProcInfo(ctx context.Context) []madmin.ProcInfo {
|
|
|
|
reply := make([]madmin.ProcInfo, len(sys.peerClients))
|
2020-03-27 00:07:39 -04:00
|
|
|
|
|
|
|
g := errgroup.WithNErrs(len(sys.peerClients))
|
|
|
|
for index, client := range sys.peerClients {
|
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
index := index
|
|
|
|
g.Go(func() error {
|
|
|
|
var err error
|
2021-06-01 11:55:49 -04:00
|
|
|
reply[index], err = sys.peerClients[index].GetProcInfo(ctx)
|
2020-03-27 00:07:39 -04:00
|
|
|
return err
|
|
|
|
}, index)
|
|
|
|
}
|
|
|
|
|
2020-09-15 21:02:54 -04:00
|
|
|
for index, err := range g.Wait() {
|
|
|
|
if err != nil {
|
2021-08-12 21:58:40 -04:00
|
|
|
sys.addNodeErr(&reply[index], sys.peerClients[index], err)
|
2020-09-15 21:02:54 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return reply
|
|
|
|
}
|
|
|
|
|
2021-03-04 13:07:05 -05:00
|
|
|
func getOfflineDisks(offlineHost string, endpoints EndpointServerPools) []madmin.Disk {
|
|
|
|
var offlineDisks []madmin.Disk
|
|
|
|
for _, pool := range endpoints {
|
|
|
|
for _, ep := range pool.Endpoints {
|
|
|
|
if offlineHost == ep.Host {
|
|
|
|
offlineDisks = append(offlineDisks, madmin.Disk{
|
|
|
|
Endpoint: ep.String(),
|
|
|
|
State: string(madmin.ItemOffline),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return offlineDisks
|
|
|
|
}
|
|
|
|
|
2019-12-11 17:27:03 -05:00
|
|
|
// ServerInfo - calls ServerInfo RPC call on all peers.
|
|
|
|
func (sys *NotificationSys) ServerInfo() []madmin.ServerProperties {
|
|
|
|
reply := make([]madmin.ServerProperties, 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()
|
|
|
|
info, err := client.ServerInfo()
|
|
|
|
if err != nil {
|
|
|
|
info.Endpoint = client.host.String()
|
2021-03-02 20:28:04 -05:00
|
|
|
info.State = string(madmin.ItemOffline)
|
2021-03-04 13:07:05 -05:00
|
|
|
info.Disks = getOfflineDisks(info.Endpoint, globalEndpoints)
|
2019-12-11 17:27:03 -05:00
|
|
|
} else {
|
2021-03-02 20:28:04 -05:00
|
|
|
info.State = string(madmin.ItemOnline)
|
2019-12-11 17:27:03 -05:00
|
|
|
}
|
|
|
|
reply[idx] = info
|
|
|
|
}(client, i)
|
|
|
|
}
|
|
|
|
wg.Wait()
|
2021-03-01 00:26:28 -05:00
|
|
|
|
2019-12-11 17:27:03 -05:00
|
|
|
return reply
|
|
|
|
}
|
|
|
|
|
2020-05-23 20:38:39 -04:00
|
|
|
// GetLocalDiskIDs - return disk ids of the local disks of the peers.
|
2020-07-20 21:31:22 -04:00
|
|
|
func (sys *NotificationSys) GetLocalDiskIDs(ctx context.Context) (localDiskIDs [][]string) {
|
|
|
|
localDiskIDs = make([][]string, len(sys.peerClients))
|
2020-05-23 20:38:39 -04:00
|
|
|
var wg sync.WaitGroup
|
2020-07-20 21:31:22 -04:00
|
|
|
for idx, client := range sys.peerClients {
|
2020-05-23 20:38:39 -04:00
|
|
|
if client == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
wg.Add(1)
|
2020-07-20 21:31:22 -04:00
|
|
|
go func(idx int, client *peerRESTClient) {
|
2020-05-23 20:38:39 -04:00
|
|
|
defer wg.Done()
|
2020-07-20 21:31:22 -04:00
|
|
|
localDiskIDs[idx] = client.GetLocalDiskIDs(ctx)
|
|
|
|
}(idx, client)
|
2020-05-23 20:38:39 -04:00
|
|
|
}
|
|
|
|
wg.Wait()
|
2020-07-20 21:31:22 -04:00
|
|
|
return localDiskIDs
|
2020-05-23 20:38:39 -04:00
|
|
|
}
|
|
|
|
|
2020-12-22 12:16:07 -05:00
|
|
|
// returns all the peers that are currently online.
|
|
|
|
func (sys *NotificationSys) getOnlinePeers() []*peerRESTClient {
|
|
|
|
var peerClients []*peerRESTClient
|
|
|
|
for _, peerClient := range sys.allPeerClients {
|
|
|
|
if peerClient != nil && peerClient.IsOnline() {
|
|
|
|
peerClients = append(peerClients, peerClient)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return peerClients
|
|
|
|
}
|
|
|
|
|
2020-10-28 12:18:35 -04:00
|
|
|
// restClientFromHash will return a deterministic peerRESTClient based on s.
|
|
|
|
// Will return nil if client is local.
|
|
|
|
func (sys *NotificationSys) restClientFromHash(s string) (client *peerRESTClient) {
|
|
|
|
if len(sys.peerClients) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
2020-12-22 12:16:07 -05:00
|
|
|
peerClients := sys.getOnlinePeers()
|
2021-01-20 16:13:05 -05:00
|
|
|
if len(peerClients) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
2020-12-22 12:16:07 -05:00
|
|
|
idx := xxhash.Sum64String(s) % uint64(len(peerClients))
|
|
|
|
return peerClients[idx]
|
2020-10-28 12:18:35 -04:00
|
|
|
}
|
|
|
|
|
2018-03-15 16:03:41 -04:00
|
|
|
// NewNotificationSys - creates new notification system object.
|
2020-12-01 16:50:33 -05:00
|
|
|
func NewNotificationSys(endpoints EndpointServerPools) *NotificationSys {
|
2020-08-19 17:24:58 -04:00
|
|
|
// targetList/bucketRulesMap/bucketRemoteTargetRulesMap are populated by NotificationSys.Init()
|
2020-10-28 12:18:35 -04:00
|
|
|
remote, all := newPeerRestClients(endpoints)
|
2018-03-15 16:03:41 -04:00
|
|
|
return &NotificationSys{
|
2019-11-09 12:27:23 -05:00
|
|
|
targetList: event.NewTargetList(),
|
2020-04-27 09:25:05 -04:00
|
|
|
targetResCh: make(chan event.TargetIDResult),
|
2018-03-15 16:03:41 -04:00
|
|
|
bucketRulesMap: make(map[string]event.RulesMap),
|
|
|
|
bucketRemoteTargetRulesMap: make(map[string]map[event.TargetID]event.RulesMap),
|
2020-10-28 12:18:35 -04:00
|
|
|
peerClients: remote,
|
|
|
|
allPeerClients: all,
|
2019-11-09 12:27:23 -05:00
|
|
|
}
|
2018-03-15 16:03:41 -04:00
|
|
|
}
|
|
|
|
|
2021-01-18 23:35:38 -05:00
|
|
|
// GetPeerOnlineCount gets the count of online and offline nodes.
|
|
|
|
func GetPeerOnlineCount() (nodesOnline, nodesOffline int) {
|
|
|
|
nodesOnline = 1 // Self is always online.
|
|
|
|
nodesOffline = 0
|
|
|
|
servers := globalNotificationSys.ServerInfo()
|
|
|
|
for _, s := range servers {
|
2021-03-02 20:28:04 -05:00
|
|
|
if s.State == string(madmin.ItemOnline) {
|
2021-01-18 23:35:38 -05:00
|
|
|
nodesOnline++
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
nodesOffline++
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-03-15 16:03:41 -04:00
|
|
|
type eventArgs struct {
|
2018-08-23 17:40:54 -04:00
|
|
|
EventName event.Name
|
|
|
|
BucketName string
|
|
|
|
Object ObjectInfo
|
|
|
|
ReqParams map[string]string
|
|
|
|
RespElements map[string]string
|
|
|
|
Host string
|
|
|
|
UserAgent string
|
2018-03-15 16:03:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// ToEvent - converts to notification event.
|
2020-04-27 09:25:05 -04:00
|
|
|
func (args eventArgs) ToEvent(escape bool) event.Event {
|
2018-03-15 16:03:41 -04:00
|
|
|
eventTime := UTCNow()
|
|
|
|
uniqueID := fmt.Sprintf("%X", eventTime.UnixNano())
|
|
|
|
|
2018-08-23 17:40:54 -04:00
|
|
|
respElements := map[string]string{
|
2021-08-25 00:13:37 -04:00
|
|
|
"x-amz-request-id": args.RespElements["requestId"],
|
|
|
|
"x-minio-origin-endpoint": func() string {
|
|
|
|
if globalMinioEndpoint != "" {
|
|
|
|
return globalMinioEndpoint
|
|
|
|
}
|
|
|
|
return getAPIEndpoints()[0]
|
|
|
|
}(), // MinIO specific custom elements.
|
2018-08-23 17:40:54 -04:00
|
|
|
}
|
2018-12-18 13:05:26 -05:00
|
|
|
// Add deployment as part of
|
|
|
|
if globalDeploymentID != "" {
|
|
|
|
respElements["x-minio-deployment-id"] = globalDeploymentID
|
|
|
|
}
|
2018-08-23 17:40:54 -04:00
|
|
|
if args.RespElements["content-length"] != "" {
|
|
|
|
respElements["content-length"] = args.RespElements["content-length"]
|
|
|
|
}
|
2020-04-27 09:25:05 -04:00
|
|
|
keyName := args.Object.Name
|
|
|
|
if escape {
|
|
|
|
keyName = url.QueryEscape(args.Object.Name)
|
|
|
|
}
|
2018-03-15 16:03:41 -04:00
|
|
|
newEvent := event.Event{
|
|
|
|
EventVersion: "2.0",
|
|
|
|
EventSource: "minio:s3",
|
2018-11-02 21:40:08 -04:00
|
|
|
AwsRegion: args.ReqParams["region"],
|
2018-03-15 16:03:41 -04:00
|
|
|
EventTime: eventTime.Format(event.AMZTimeFormat),
|
|
|
|
EventName: args.EventName,
|
2021-03-31 16:21:10 -04:00
|
|
|
UserIdentity: event.Identity{PrincipalID: args.ReqParams["principalId"]},
|
2018-03-15 16:03:41 -04:00
|
|
|
RequestParameters: args.ReqParams,
|
2018-08-23 17:40:54 -04:00
|
|
|
ResponseElements: respElements,
|
2018-03-15 16:03:41 -04:00
|
|
|
S3: event.Metadata{
|
|
|
|
SchemaVersion: "1.0",
|
|
|
|
ConfigurationID: "Config",
|
|
|
|
Bucket: event.Bucket{
|
|
|
|
Name: args.BucketName,
|
2021-03-31 16:21:10 -04:00
|
|
|
OwnerIdentity: event.Identity{PrincipalID: args.ReqParams["principalId"]},
|
2018-04-24 18:53:30 -04:00
|
|
|
ARN: policy.ResourceARNPrefix + args.BucketName,
|
2018-03-15 16:03:41 -04:00
|
|
|
},
|
|
|
|
Object: event.Object{
|
2020-04-27 09:25:05 -04:00
|
|
|
Key: keyName,
|
2020-06-12 23:04:01 -04:00
|
|
|
VersionID: args.Object.VersionID,
|
2018-03-15 16:03:41 -04:00
|
|
|
Sequencer: uniqueID,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Source: event.Source{
|
|
|
|
Host: args.Host,
|
|
|
|
UserAgent: args.UserAgent,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2020-09-15 00:32:52 -04:00
|
|
|
if args.EventName != event.ObjectRemovedDelete && args.EventName != event.ObjectRemovedDeleteMarkerCreated {
|
2018-03-15 16:03:41 -04:00
|
|
|
newEvent.S3.Object.ETag = args.Object.ETag
|
|
|
|
newEvent.S3.Object.Size = args.Object.Size
|
|
|
|
newEvent.S3.Object.ContentType = args.Object.ContentType
|
2021-08-25 00:13:37 -04:00
|
|
|
newEvent.S3.Object.UserMetadata = make(map[string]string, len(args.Object.UserDefined))
|
|
|
|
for k, v := range args.Object.UserDefined {
|
|
|
|
if strings.HasPrefix(strings.ToLower(k), ReservedMetadataPrefixLower) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
newEvent.S3.Object.UserMetadata[k] = v
|
|
|
|
}
|
2018-03-15 16:03:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return newEvent
|
|
|
|
}
|
|
|
|
|
|
|
|
func sendEvent(args eventArgs) {
|
2020-05-24 14:19:17 -04:00
|
|
|
args.Object.Size, _ = args.Object.GetActualSize()
|
2019-10-21 02:48:19 -04:00
|
|
|
|
2021-03-03 14:13:31 -05:00
|
|
|
// avoid generating a notification for REPLICA creation event.
|
|
|
|
if _, ok := args.ReqParams[xhttp.MinIOSourceReplicationRequest]; ok {
|
|
|
|
return
|
|
|
|
}
|
2020-05-24 14:19:17 -04:00
|
|
|
// remove sensitive encryption entries in metadata.
|
2019-05-30 01:29:37 -04:00
|
|
|
crypto.RemoveSensitiveEntries(args.Object.UserDefined)
|
|
|
|
crypto.RemoveInternalEntries(args.Object.UserDefined)
|
|
|
|
|
2018-03-15 16:03:41 -04:00
|
|
|
// globalNotificationSys is not initialized in gateway mode.
|
|
|
|
if globalNotificationSys == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-01-04 12:40:30 -05:00
|
|
|
if globalHTTPListen.NumSubscribers() > 0 {
|
2020-04-27 09:25:05 -04:00
|
|
|
globalHTTPListen.Publish(args.ToEvent(false))
|
2019-12-13 14:33:11 -05:00
|
|
|
}
|
|
|
|
|
2020-04-27 09:25:05 -04:00
|
|
|
globalNotificationSys.Send(args)
|
2018-03-15 16:03:41 -04:00
|
|
|
}
|
2020-10-12 12:04:55 -04:00
|
|
|
|
|
|
|
// GetBandwidthReports - gets the bandwidth report from all nodes including self.
|
2021-04-18 15:41:13 -04:00
|
|
|
func (sys *NotificationSys) GetBandwidthReports(ctx context.Context, buckets ...string) madmin.BucketBandwidthReport {
|
|
|
|
reports := make([]*madmin.BucketBandwidthReport, len(sys.peerClients))
|
2020-10-12 12:04:55 -04:00
|
|
|
g := errgroup.WithNErrs(len(sys.peerClients))
|
2020-10-16 12:07:50 -04:00
|
|
|
for index := range sys.peerClients {
|
|
|
|
if sys.peerClients[index] == nil {
|
2020-10-12 12:04:55 -04:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
index := index
|
|
|
|
g.Go(func() error {
|
|
|
|
var err error
|
2020-10-16 12:07:50 -04:00
|
|
|
reports[index], err = sys.peerClients[index].MonitorBandwidth(ctx, buckets)
|
2020-10-12 12:04:55 -04:00
|
|
|
return err
|
|
|
|
}, index)
|
|
|
|
}
|
|
|
|
|
|
|
|
for index, err := range g.Wait() {
|
|
|
|
reqInfo := (&logger.ReqInfo{}).AppendTags("peerAddress",
|
|
|
|
sys.peerClients[index].host.String())
|
|
|
|
ctx := logger.SetReqInfo(ctx, reqInfo)
|
|
|
|
logger.LogOnceIf(ctx, err, sys.peerClients[index].host.String())
|
|
|
|
}
|
|
|
|
reports = append(reports, globalBucketMonitor.GetReport(bucketBandwidth.SelectBuckets(buckets...)))
|
2021-04-18 15:41:13 -04:00
|
|
|
consolidatedReport := madmin.BucketBandwidthReport{
|
|
|
|
BucketStats: make(map[string]madmin.BandwidthDetails),
|
2020-10-12 12:04:55 -04:00
|
|
|
}
|
|
|
|
for _, report := range reports {
|
|
|
|
if report == nil || report.BucketStats == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for bucket := range report.BucketStats {
|
|
|
|
d, ok := consolidatedReport.BucketStats[bucket]
|
|
|
|
if !ok {
|
2021-04-18 15:41:13 -04:00
|
|
|
consolidatedReport.BucketStats[bucket] = madmin.BandwidthDetails{}
|
2020-10-12 12:04:55 -04:00
|
|
|
d = consolidatedReport.BucketStats[bucket]
|
|
|
|
d.LimitInBytesPerSecond = report.BucketStats[bucket].LimitInBytesPerSecond
|
|
|
|
}
|
2020-10-16 12:07:50 -04:00
|
|
|
if d.LimitInBytesPerSecond < report.BucketStats[bucket].LimitInBytesPerSecond {
|
|
|
|
d.LimitInBytesPerSecond = report.BucketStats[bucket].LimitInBytesPerSecond
|
|
|
|
}
|
2020-10-12 12:04:55 -04:00
|
|
|
d.CurrentBandwidthInBytesPerSecond += report.BucketStats[bucket].CurrentBandwidthInBytesPerSecond
|
|
|
|
consolidatedReport.BucketStats[bucket] = d
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return consolidatedReport
|
|
|
|
}
|
2021-01-18 23:35:38 -05:00
|
|
|
|
|
|
|
// GetClusterMetrics - gets the cluster metrics from all nodes excluding self.
|
|
|
|
func (sys *NotificationSys) GetClusterMetrics(ctx context.Context) chan Metric {
|
2021-08-31 21:52:48 -04:00
|
|
|
if sys == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2021-01-18 23:35:38 -05:00
|
|
|
g := errgroup.WithNErrs(len(sys.peerClients))
|
|
|
|
peerChannels := make([]<-chan Metric, len(sys.peerClients))
|
|
|
|
for index := range sys.peerClients {
|
|
|
|
if sys.peerClients[index] == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
index := index
|
|
|
|
g.Go(func() error {
|
|
|
|
var err error
|
|
|
|
peerChannels[index], err = sys.peerClients[index].GetPeerMetrics(ctx)
|
|
|
|
return err
|
|
|
|
}, index)
|
|
|
|
}
|
|
|
|
|
|
|
|
ch := make(chan Metric)
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
for index, err := range g.Wait() {
|
|
|
|
reqInfo := (&logger.ReqInfo{}).AppendTags("peerAddress",
|
|
|
|
sys.peerClients[index].host.String())
|
|
|
|
ctx := logger.SetReqInfo(ctx, reqInfo)
|
|
|
|
if err != nil {
|
|
|
|
logger.LogOnceIf(ctx, err, sys.peerClients[index].host.String())
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
wg.Add(1)
|
|
|
|
go func(ctx context.Context, peerChannel <-chan Metric, wg *sync.WaitGroup) {
|
|
|
|
defer wg.Done()
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case m, ok := <-peerChannel:
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
ch <- m
|
|
|
|
case <-ctx.Done():
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}(ctx, peerChannels[index], &wg)
|
|
|
|
}
|
|
|
|
go func(wg *sync.WaitGroup, ch chan Metric) {
|
|
|
|
wg.Wait()
|
|
|
|
close(ch)
|
|
|
|
}(&wg, ch)
|
|
|
|
return ch
|
|
|
|
}
|
2021-07-27 15:55:56 -04:00
|
|
|
|
|
|
|
// Speedtest run GET/PUT tests at input concurrency for requested object size,
|
|
|
|
// optionally you can extend the tests longer with time.Duration.
|
2021-09-10 20:43:34 -04:00
|
|
|
func (sys *NotificationSys) Speedtest(ctx context.Context, size int, concurrent int, duration time.Duration) []SpeedtestResult {
|
|
|
|
length := len(sys.allPeerClients)
|
|
|
|
if length == 0 {
|
|
|
|
// For single node erasure setup.
|
|
|
|
length = 1
|
|
|
|
}
|
|
|
|
results := make([]SpeedtestResult, length)
|
2021-07-27 15:55:56 -04:00
|
|
|
|
2021-07-29 20:51:30 -04:00
|
|
|
scheme := "http"
|
|
|
|
if globalIsTLS {
|
|
|
|
scheme = "https"
|
|
|
|
}
|
|
|
|
|
2021-07-27 15:55:56 -04:00
|
|
|
var wg sync.WaitGroup
|
|
|
|
for index := range sys.peerClients {
|
|
|
|
if sys.peerClients[index] == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
wg.Add(1)
|
|
|
|
go func(index int) {
|
|
|
|
defer wg.Done()
|
|
|
|
r, err := sys.peerClients[index].Speedtest(ctx, size, concurrent, duration)
|
2021-07-29 20:51:30 -04:00
|
|
|
u := &url.URL{
|
|
|
|
Scheme: scheme,
|
|
|
|
Host: sys.peerClients[index].host.String(),
|
|
|
|
}
|
2021-09-10 20:43:34 -04:00
|
|
|
if err != nil {
|
|
|
|
results[index].Error = err.Error()
|
|
|
|
} else {
|
|
|
|
results[index] = r
|
2021-07-27 15:55:56 -04:00
|
|
|
}
|
2021-09-10 20:43:34 -04:00
|
|
|
results[index].Endpoint = u.String()
|
2021-07-27 15:55:56 -04:00
|
|
|
}(index)
|
|
|
|
}
|
|
|
|
|
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
defer wg.Done()
|
|
|
|
r, err := selfSpeedtest(ctx, size, concurrent, duration)
|
2021-08-03 00:50:20 -04:00
|
|
|
u := &url.URL{
|
|
|
|
Scheme: scheme,
|
|
|
|
Host: globalLocalNodeName,
|
|
|
|
}
|
2021-09-10 20:43:34 -04:00
|
|
|
if err != nil {
|
|
|
|
results[len(results)-1].Error = err.Error()
|
|
|
|
} else {
|
|
|
|
results[len(results)-1] = r
|
2021-07-27 15:55:56 -04:00
|
|
|
}
|
2021-09-10 20:43:34 -04:00
|
|
|
results[len(results)-1].Endpoint = u.String()
|
2021-07-27 15:55:56 -04:00
|
|
|
}()
|
|
|
|
wg.Wait()
|
|
|
|
|
|
|
|
return results
|
|
|
|
}
|