github.com/minio/mc@v0.0.0-20240503112107-b471de8d1882/cmd/support-top-drive.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 "context" 22 "time" 23 24 tea "github.com/charmbracelet/bubbletea" 25 "github.com/minio/cli" 26 "github.com/minio/madmin-go/v3" 27 "github.com/minio/mc/pkg/probe" 28 ) 29 30 var supportTopDriveFlags = []cli.Flag{ 31 cli.IntFlag{ 32 Name: "count, c", 33 Usage: "show up to N drives", 34 Value: 10, 35 }, 36 } 37 38 var supportTopDriveCmd = cli.Command{ 39 Name: "drive", 40 Aliases: []string{"disk"}, 41 HiddenAliases: true, 42 Usage: "show real-time drive metrics", 43 Action: mainSupportTopDrive, 44 OnUsageError: onUsageError, 45 Before: setGlobalsFromContext, 46 Flags: append(supportTopDriveFlags, supportGlobalFlags...), 47 HideHelpCommand: true, 48 CustomHelpTemplate: `NAME: 49 {{.HelpName}} - {{.Usage}} 50 51 USAGE: 52 {{.HelpName}} [FLAGS] TARGET 53 54 FLAGS: 55 {{range .VisibleFlags}}{{.}} 56 {{end}} 57 EXAMPLES: 58 1. Display drive metrics 59 {{.Prompt}} {{.HelpName}} myminio/ 60 `, 61 } 62 63 // checkSupportTopDriveSyntax - validate all the passed arguments 64 func checkSupportTopDriveSyntax(ctx *cli.Context) { 65 if len(ctx.Args()) == 0 || len(ctx.Args()) > 1 { 66 showCommandHelpAndExit(ctx, 1) // last argument is exit code 67 } 68 } 69 70 func mainSupportTopDrive(ctx *cli.Context) error { 71 checkSupportTopDriveSyntax(ctx) 72 73 aliasedURL := ctx.Args().Get(0) 74 alias, _ := url2Alias(aliasedURL) 75 validateClusterRegistered(alias, false) 76 77 // Create a new MinIO Admin Client 78 client, err := newAdminClient(aliasedURL) 79 if err != nil { 80 fatalIf(err.Trace(aliasedURL), "Unable to initialize admin client.") 81 return nil 82 } 83 84 ctxt, cancel := context.WithCancel(globalContext) 85 defer cancel() 86 87 info, e := client.ServerInfo(ctxt) 88 fatalIf(probe.NewError(e).Trace(aliasedURL), "Unable to initialize admin client.") 89 90 var disks []madmin.Disk 91 for _, srv := range info.Servers { 92 disks = append(disks, srv.Disks...) 93 } 94 95 // MetricsOptions are options provided to Metrics call. 96 opts := madmin.MetricsOptions{ 97 Type: madmin.MetricsDisk, 98 Interval: time.Second, 99 ByDisk: true, 100 N: ctx.Int("count"), 101 } 102 103 p := tea.NewProgram(initTopDriveUI(disks, ctx.Int("count"))) 104 go func() { 105 out := func(m madmin.RealtimeMetrics) { 106 for name, metric := range m.ByDisk { 107 p.Send(topDriveResult{ 108 diskName: name, 109 stats: metric.IOStats, 110 }) 111 } 112 } 113 114 e := client.Metrics(ctxt, opts, out) 115 if e != nil { 116 fatalIf(probe.NewError(e), "Unable to fetch top drives events") 117 } 118 p.Quit() 119 }() 120 121 if _, e := p.Run(); e != nil { 122 cancel() 123 fatalIf(probe.NewError(e).Trace(aliasedURL), "Unable to fetch top drive events") 124 } 125 126 return nil 127 }