github.com/minio/mc@v0.0.0-20240503112107-b471de8d1882/cmd/admin-decom-status.go (about) 1 // Copyright (c) 2015-2022 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "fmt" 22 "path/filepath" 23 "time" 24 25 "github.com/dustin/go-humanize" 26 "github.com/fatih/color" 27 "github.com/minio/cli" 28 json "github.com/minio/colorjson" 29 "github.com/minio/mc/pkg/probe" 30 "github.com/minio/pkg/v2/console" 31 ) 32 33 var adminDecommissionStatusCmd = cli.Command{ 34 Name: "status", 35 Usage: "show current decommissioning status", 36 Action: mainAdminDecommissionStatus, 37 OnUsageError: onUsageError, 38 Before: setGlobalsFromContext, 39 Flags: globalFlags, 40 CustomHelpTemplate: `NAME: 41 {{.HelpName}} - {{.Usage}} 42 43 USAGE: 44 {{.HelpName}} TARGET 45 46 FLAGS: 47 {{range .VisibleFlags}}{{.}} 48 {{end}} 49 EXAMPLES: 50 1. Show current decommissioning status. 51 {{.Prompt}} {{.HelpName}} myminio/ http://server{5...8}/disk{1...4} 52 2. List all current decommissioning status of all pools. 53 {{.Prompt}} {{.HelpName}} myminio/ 54 `, 55 } 56 57 // checkAdminDecommissionStatusSyntax - validate all the passed arguments 58 func checkAdminDecommissionStatusSyntax(ctx *cli.Context) { 59 if len(ctx.Args()) > 2 || len(ctx.Args()) == 0 { 60 showCommandHelpAndExit(ctx, 1) // last argument is exit code 61 } 62 } 63 64 // mainAdminDecommissionStatus is the handle for "mc admin decomission status" command. 65 func mainAdminDecommissionStatus(ctx *cli.Context) error { 66 checkAdminDecommissionStatusSyntax(ctx) 67 68 // Get the alias parameter from cli 69 args := ctx.Args() 70 aliasedURL := args.Get(0) 71 aliasedURL = filepath.Clean(aliasedURL) 72 73 // Create a new MinIO Admin Client 74 client, err := newAdminClient(aliasedURL) 75 fatalIf(err, "Unable to initialize admin connection.") 76 77 if pool := args.Get(1); pool != "" { 78 poolStatus, e := client.StatusPool(globalContext, pool) 79 fatalIf(probe.NewError(e).Trace(args...), "Unable to get status per pool") 80 81 if globalJSON { 82 statusJSONBytes, e := json.MarshalIndent(poolStatus, "", " ") 83 fatalIf(probe.NewError(e), "Unable to marshal into JSON.") 84 console.Println(string(statusJSONBytes)) 85 return nil 86 } 87 88 var msg string 89 if poolStatus.Decommission.Complete { 90 msg = color.GreenString(fmt.Sprintf("Decommission of pool %s is complete, you may now remove it from server command line", poolStatus.CmdLine)) 91 } else if poolStatus.Decommission.Failed { 92 msg = color.GreenString(fmt.Sprintf("Decommission of pool %s failed, please retry again", poolStatus.CmdLine)) 93 } else if poolStatus.Decommission.Canceled { 94 msg = color.GreenString(fmt.Sprintf("Decommission of pool %s was canceled, you may start again", poolStatus.CmdLine)) 95 } else if !poolStatus.Decommission.StartTime.IsZero() { 96 usedStart := (poolStatus.Decommission.TotalSize - poolStatus.Decommission.StartSize) 97 usedCurrent := (poolStatus.Decommission.TotalSize - poolStatus.Decommission.CurrentSize) 98 99 duration := float64(time.Since(poolStatus.Decommission.StartTime)) / float64(time.Second) 100 if usedStart > usedCurrent && duration > 10 { 101 copied := uint64(usedStart - usedCurrent) 102 speed := uint64(float64(copied) / duration) 103 msg = "Decommissioning rate at " + humanize.IBytes(speed) + "/sec " + "[" + humanize.IBytes( 104 uint64(usedCurrent)) + "/" + humanize.IBytes(uint64(poolStatus.Decommission.TotalSize)) + "]" 105 msg += "\nStarted: " + humanize.RelTime(time.Now().UTC(), poolStatus.Decommission.StartTime, "", "ago") 106 } else { 107 msg = "Decommissioning is starting..." 108 } 109 msg = color.GreenString(msg) 110 } else { 111 errorIf(errDummy().Trace(args...), "This pool is currently not scheduled for decomissioning") 112 return nil 113 } 114 fmt.Println(msg) 115 return nil 116 } 117 poolStatuses, e := client.ListPoolsStatus(globalContext) 118 fatalIf(probe.NewError(e).Trace(args...), "Unable to get status for all pools") 119 120 if globalJSON { 121 statusJSONBytes, e := json.MarshalIndent(poolStatuses, "", " ") 122 fatalIf(probe.NewError(e), "Unable to marshal into JSON.") 123 console.Println(string(statusJSONBytes)) 124 return nil 125 } 126 127 dspOrder := []col{colGreen} // Header 128 for i := 0; i < len(poolStatuses); i++ { 129 dspOrder = append(dspOrder, colGrey) 130 } 131 var printColors []*color.Color 132 for _, c := range dspOrder { 133 printColors = append(printColors, getPrintCol(c)) 134 } 135 136 tbl := console.NewTable(printColors, []bool{false, false, false, false}, 0) 137 138 cellText := make([][]string, len(poolStatuses)+1) 139 cellText[0] = []string{ 140 "ID", 141 "Pools", 142 "Raw Drives Usage", 143 "Status", 144 } 145 for idx, pool := range poolStatuses { 146 idx++ 147 totalSize := uint64(pool.Decommission.TotalSize) 148 usedCurrent := uint64(pool.Decommission.TotalSize - pool.Decommission.CurrentSize) 149 var capacity string 150 if totalSize == 0 { 151 capacity = "0% (total: 0B)" 152 } else { 153 capacity = fmt.Sprintf("%.1f%% (total: %s)", 100*float64(usedCurrent)/float64(totalSize), humanize.IBytes(totalSize)) 154 } 155 status := "Active" 156 if pool.Decommission != nil { 157 if pool.Decommission.Complete { 158 status = "Complete" 159 } else if pool.Decommission.Failed { 160 status = "Draining(Failed)" 161 } else if pool.Decommission.Canceled { 162 status = "Draining(Canceled)" 163 } else if !pool.Decommission.StartTime.IsZero() { 164 status = "Draining" 165 } 166 } 167 cellText[idx] = []string{ 168 humanize.Ordinal(pool.ID + 1), 169 pool.CmdLine, 170 capacity, 171 status, 172 } 173 } 174 return tbl.DisplayTable(cellText) 175 }