| 
									
										
										
										
											2022-10-26 03:36:57 +08:00
										 |  |  | // Copyright (c) 2022 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/>.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type rebalPoolProgress struct { | 
					
						
							|  |  |  | 	NumObjects  uint64        `json:"objects"` | 
					
						
							|  |  |  | 	NumVersions uint64        `json:"versions"` | 
					
						
							|  |  |  | 	Bytes       uint64        `json:"bytes"` | 
					
						
							|  |  |  | 	Bucket      string        `json:"bucket"` | 
					
						
							|  |  |  | 	Object      string        `json:"object"` | 
					
						
							|  |  |  | 	Elapsed     time.Duration `json:"elapsed"` | 
					
						
							|  |  |  | 	ETA         time.Duration `json:"eta"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type rebalancePoolStatus struct { | 
					
						
							|  |  |  | 	ID       int               `json:"id"`                 // Pool index (zero-based)
 | 
					
						
							|  |  |  | 	Status   string            `json:"status"`             // Active if rebalance is running, empty otherwise
 | 
					
						
							|  |  |  | 	Used     float64           `json:"used"`               // Percentage used space
 | 
					
						
							|  |  |  | 	Progress rebalPoolProgress `json:"progress,omitempty"` // is empty when rebalance is not running
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // rebalanceAdminStatus holds rebalance status related information exported to mc, console, etc.
 | 
					
						
							|  |  |  | type rebalanceAdminStatus struct { | 
					
						
							|  |  |  | 	ID        string                // identifies the ongoing rebalance operation by a uuid
 | 
					
						
							|  |  |  | 	Pools     []rebalancePoolStatus `json:"pools"` // contains all pools, including inactive
 | 
					
						
							|  |  |  | 	StoppedAt time.Time             `json:"stoppedAt,omitempty"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func rebalanceStatus(ctx context.Context, z *erasureServerPools) (r rebalanceAdminStatus, err error) { | 
					
						
							|  |  |  | 	// Load latest rebalance status
 | 
					
						
							|  |  |  | 	meta := &rebalanceMeta{} | 
					
						
							|  |  |  | 	err = meta.load(ctx, z.serverPools[0]) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return r, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Compute disk usage percentage
 | 
					
						
							| 
									
										
										
										
											2023-12-22 08:56:43 +08:00
										 |  |  | 	si := z.StorageInfo(ctx, true) | 
					
						
							| 
									
										
										
										
											2022-10-26 03:36:57 +08:00
										 |  |  | 	diskStats := make([]struct { | 
					
						
							|  |  |  | 		AvailableSpace uint64 | 
					
						
							|  |  |  | 		TotalSpace     uint64 | 
					
						
							|  |  |  | 	}, len(z.serverPools)) | 
					
						
							|  |  |  | 	for _, disk := range si.Disks { | 
					
						
							| 
									
										
										
										
											2023-01-30 21:03:07 +08:00
										 |  |  | 		// Ignore invalid.
 | 
					
						
							|  |  |  | 		if disk.PoolIndex < 0 || len(diskStats) <= disk.PoolIndex { | 
					
						
							|  |  |  | 			// https://github.com/minio/minio/issues/16500
 | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-10-26 03:36:57 +08:00
										 |  |  | 		diskStats[disk.PoolIndex].AvailableSpace += disk.AvailableSpace | 
					
						
							|  |  |  | 		diskStats[disk.PoolIndex].TotalSpace += disk.TotalSpace | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	stopTime := meta.StoppedAt | 
					
						
							|  |  |  | 	r = rebalanceAdminStatus{ | 
					
						
							|  |  |  | 		ID:        meta.ID, | 
					
						
							|  |  |  | 		StoppedAt: meta.StoppedAt, | 
					
						
							|  |  |  | 		Pools:     make([]rebalancePoolStatus, len(meta.PoolStats)), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for i, ps := range meta.PoolStats { | 
					
						
							|  |  |  | 		r.Pools[i] = rebalancePoolStatus{ | 
					
						
							|  |  |  | 			ID:     i, | 
					
						
							|  |  |  | 			Status: ps.Info.Status.String(), | 
					
						
							|  |  |  | 			Used:   float64(diskStats[i].TotalSpace-diskStats[i].AvailableSpace) / float64(diskStats[i].TotalSpace), | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if !ps.Participating { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// for participating pools, total bytes to be rebalanced by this pool is given by,
 | 
					
						
							|  |  |  | 		// pf_c = (f_i + x)/c_i,
 | 
					
						
							|  |  |  | 		// pf_c - percentage free space across pools, f_i - ith pool's free space, c_i - ith pool's capacity
 | 
					
						
							|  |  |  | 		// i.e. x = c_i*pfc -f_i
 | 
					
						
							|  |  |  | 		totalBytesToRebal := float64(ps.InitCapacity)*meta.PercentFreeGoal - float64(ps.InitFreeSpace) | 
					
						
							|  |  |  | 		elapsed := time.Since(ps.Info.StartTime) | 
					
						
							|  |  |  | 		eta := time.Duration(totalBytesToRebal * float64(elapsed) / float64(ps.Bytes)) | 
					
						
							|  |  |  | 		if !ps.Info.EndTime.IsZero() { | 
					
						
							|  |  |  | 			stopTime = ps.Info.EndTime | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if !stopTime.IsZero() { // rebalance is stopped or completed
 | 
					
						
							|  |  |  | 			elapsed = stopTime.Sub(ps.Info.StartTime) | 
					
						
							|  |  |  | 			eta = 0 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		r.Pools[i].Progress = rebalPoolProgress{ | 
					
						
							|  |  |  | 			NumObjects:  ps.NumObjects, | 
					
						
							|  |  |  | 			NumVersions: ps.NumVersions, | 
					
						
							|  |  |  | 			Bytes:       ps.Bytes, | 
					
						
							|  |  |  | 			Elapsed:     elapsed, | 
					
						
							|  |  |  | 			ETA:         eta, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return r, nil | 
					
						
							|  |  |  | } |