1
0
mirror of https://github.com/minio/mc.git synced 2025-12-08 22:28:28 +03:00

rename mc admin top to mc support top (#4191)

This commit is contained in:
Ashish Kumar Sinha
2022-08-11 01:32:42 +05:30
committed by GitHub
parent c2f1770cec
commit c12f6e46c3
10 changed files with 363 additions and 213 deletions

View File

@@ -18,13 +18,8 @@
package cmd
import (
"context"
"os"
tea "github.com/charmbracelet/bubbletea"
"github.com/minio/cli"
"github.com/minio/madmin-go"
"github.com/minio/mc/pkg/probe"
"github.com/minio/pkg/console"
)
var adminTopAPIFlags = []cli.Flag{
@@ -54,83 +49,11 @@ var adminTopAPICmd = cli.Command{
Before: setGlobalsFromContext,
Flags: append(adminTopAPIFlags, globalFlags...),
HideHelpCommand: true,
CustomHelpTemplate: `NAME:
{{.HelpName}} - {{.Usage}}
USAGE:
{{.HelpName}} [FLAGS] TARGET
FLAGS:
{{range .VisibleFlags}}{{.}}
{{end}}
EXAMPLES:
1. Display current in-progress all S3 API calls.
{{.Prompt}} {{.HelpName}} myminio/
2. Display current in-progress all 's3.PutObject' API calls.
{{.Prompt}} {{.HelpName}} --name s3.PutObject myminio/
CustomHelpTemplate: `Please use 'mc support top api'
`,
}
// checkAdminTopAPISyntax - validate all the passed arguments
func checkAdminTopAPISyntax(ctx *cli.Context) {
if len(ctx.Args()) == 0 || len(ctx.Args()) > 1 {
cli.ShowCommandHelpAndExit(ctx, "api", 1) // last argument is exit code
}
}
func mainAdminTopAPI(ctx *cli.Context) error {
checkAdminTopAPISyntax(ctx)
aliasedURL := ctx.Args().Get(0)
// Create a new MinIO Admin Client
client, err := newAdminClient(aliasedURL)
if err != nil {
fatalIf(err.Trace(aliasedURL), "Unable to initialize admin client.")
return nil
}
ctxt, cancel := context.WithCancel(globalContext)
defer cancel()
opts, e := tracingOpts(ctx, ctx.StringSlice("call"))
fatalIf(probe.NewError(e), "Unable to start tracing")
mopts := matchOpts{
funcNames: ctx.StringSlice("name"),
apiPaths: ctx.StringSlice("path"),
nodes: ctx.StringSlice("node"),
}
// Start listening on all trace activity.
traceCh := client.ServiceTrace(ctxt, opts)
done := make(chan struct{})
p := tea.NewProgram(initTraceUI())
go func() {
if e := p.Start(); e != nil {
os.Exit(1)
}
close(done)
}()
go func() {
for apiCallInfo := range traceCh {
if apiCallInfo.Err != nil {
fatalIf(probe.NewError(apiCallInfo.Err), "Unable to fetch top API events")
}
if matchTrace(mopts, apiCallInfo) {
p.Send(topAPIResult{
apiCallInfo: apiCallInfo,
})
}
p.Send(topAPIResult{
apiCallInfo: madmin.ServiceTraceInfo{},
})
}
}()
<-done
console.Infoln("Please use 'mc support top api'")
return nil
}

View File

@@ -18,14 +18,7 @@
package cmd
import (
"fmt"
"time"
"github.com/fatih/color"
"github.com/minio/cli"
json "github.com/minio/colorjson"
"github.com/minio/madmin-go"
"github.com/minio/mc/pkg/probe"
"github.com/minio/pkg/console"
)
@@ -49,117 +42,11 @@ var adminTopLocksCmd = cli.Command{
Action: mainAdminTopLocks,
OnUsageError: onUsageError,
Flags: append(globalFlags, topLocksFlag...),
CustomHelpTemplate: `NAME:
{{.HelpName}} - {{.Usage}}
USAGE:
{{.HelpName}} TARGET
FLAGS:
{{range .VisibleFlags}}{{.}}
{{end}}
EXAMPLES:
1. Get a list of the 10 oldest locks on a MinIO cluster.
{{.Prompt}} {{.HelpName}} myminio/
CustomHelpTemplate: `Please use 'mc support top locks'
`,
}
// lockMessage struct to list lock information.
type lockMessage struct {
Status string `json:"status"`
Lock madmin.LockEntry `json:"locks"`
}
func getTimeDiff(timeStamp time.Time) (string, string) {
now := time.Now().UTC()
diff := now.Sub(timeStamp)
hours := int(diff.Hours())
minutes := int(diff.Minutes()) % 60
seconds := int(diff.Seconds()) % 60
if hours == 0 {
if minutes == 0 {
return "Lock", fmt.Sprint(seconds, " seconds")
}
return "Lock", fmt.Sprint(minutes, " minutes")
}
return "StaleLock", fmt.Sprint(hours, " hours")
}
// String colorized oldest locks message.
func (u lockMessage) String() string {
const (
timeFieldMaxLen = 20
resourceFieldMaxLen = -1
typeFieldMaxLen = 6
)
lockState, timeDiff := getTimeDiff(u.Lock.Timestamp)
return console.Colorize(lockState, newPrettyTable(" ",
Field{"Time", timeFieldMaxLen},
Field{"Type", typeFieldMaxLen},
Field{"Resource", resourceFieldMaxLen},
).buildRow(timeDiff, u.Lock.Type, u.Lock.Resource))
}
// JSON jsonified top oldest locks message.
func (u lockMessage) JSON() string {
u.Status = "success"
statusJSONBytes, e := json.MarshalIndent(u, "", " ")
fatalIf(probe.NewError(e), "Unable to marshal into JSON.")
return string(statusJSONBytes)
}
// checkAdminTopLocksSyntax - validate all the passed arguments
func checkAdminTopLocksSyntax(ctx *cli.Context) {
if len(ctx.Args()) == 0 || len(ctx.Args()) > 1 {
cli.ShowCommandHelpAndExit(ctx, "locks", 1) // last argument is exit code
}
}
func mainAdminTopLocks(ctx *cli.Context) error {
checkAdminTopLocksSyntax(ctx)
// Get the alias parameter from cli
args := ctx.Args()
aliasedURL := args.Get(0)
// Create a new MinIO Admin Client
client, err := newAdminClient(aliasedURL)
fatalIf(err, "Unable to initialize admin connection.")
// Call top locks API
entries, e := client.TopLocksWithOpts(globalContext, madmin.TopLockOpts{
Count: ctx.Int("count"),
Stale: ctx.Bool("stale"),
})
fatalIf(probe.NewError(e), "Unable to get server locks list.")
console.SetColor("StaleLock", color.New(color.FgRed, color.Bold))
console.SetColor("Lock", color.New(color.FgBlue, color.Bold))
console.SetColor("Headers", color.New(color.FgGreen, color.Bold))
// Print
printLocks(entries)
console.Infoln("Please use 'mc support top locks'")
return nil
}
func printHeaders() {
timeFieldMaxLen := 20
resourceFieldMaxLen := -1
typeFieldMaxLen := 6
console.Println(console.Colorize("Headers", newPrettyTable(" ",
Field{"Time", timeFieldMaxLen},
Field{"Type", typeFieldMaxLen},
Field{"Resource", resourceFieldMaxLen},
).buildRow("Time", "Type", "Resource")))
}
// Prints oldest locks.
func printLocks(locks madmin.LockEntries) {
if !globalJSON {
printHeaders()
}
for _, entry := range locks {
printMsg(lockMessage{Lock: entry})
}
}

View File

@@ -434,6 +434,8 @@ var completeCmds = map[string]complete.Predictor{
"/support/perf": aliasCompleter,
"/support/metrics": aliasCompleter,
"/support/status": aliasCompleter,
"/support/top/locks": aliasCompleter,
"/support/top/api": aliasCompleter,
"/license/register": aliasCompleter,
"/license/info": aliasCompleter,

136
cmd/support-top-api.go Normal file
View File

@@ -0,0 +1,136 @@
// Copyright (c) 2015-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"
"os"
tea "github.com/charmbracelet/bubbletea"
"github.com/minio/cli"
"github.com/minio/madmin-go"
"github.com/minio/mc/pkg/probe"
)
var supportTopAPIFlags = []cli.Flag{
cli.StringSliceFlag{
Name: "name",
Usage: "summarize current calls for matching API name",
},
cli.StringSliceFlag{
Name: "path",
Usage: "summarize current API calls only on matching path",
},
cli.StringSliceFlag{
Name: "node",
Usage: "summarize current API calls only on matching servers",
},
cli.BoolFlag{
Name: "errors, e",
Usage: "summarize current API calls throwing only errors",
},
}
var supportTopAPICmd = cli.Command{
Name: "api",
Usage: "summarize API events on MinIO server in real-time",
Action: mainSupportTopAPI,
OnUsageError: onUsageError,
Before: setGlobalsFromContext,
Flags: append(supportTopAPIFlags, globalFlags...),
HideHelpCommand: true,
CustomHelpTemplate: `NAME:
{{.HelpName}} - {{.Usage}}
USAGE:
{{.HelpName}} [FLAGS] TARGET
FLAGS:
{{range .VisibleFlags}}{{.}}
{{end}}
EXAMPLES:
1. Display current in-progress all S3 API calls.
{{.Prompt}} {{.HelpName}} myminio/
2. Display current in-progress all 's3.PutObject' API calls.
{{.Prompt}} {{.HelpName}} --name s3.PutObject myminio/
`,
}
// checkSupportTopAPISyntax - validate all the passed arguments
func checkSupportTopAPISyntax(ctx *cli.Context) {
if len(ctx.Args()) == 0 || len(ctx.Args()) > 1 {
cli.ShowCommandHelpAndExit(ctx, "api", 1) // last argument is exit code
}
}
func mainSupportTopAPI(ctx *cli.Context) error {
checkSupportTopAPISyntax(ctx)
aliasedURL := ctx.Args().Get(0)
// Create a new MinIO Admin Client
client, err := newAdminClient(aliasedURL)
if err != nil {
fatalIf(err.Trace(aliasedURL), "Unable to initialize admin client.")
return nil
}
ctxt, cancel := context.WithCancel(globalContext)
defer cancel()
opts, e := tracingOpts(ctx, ctx.StringSlice("call"))
fatalIf(probe.NewError(e), "Unable to start tracing")
mopts := matchOpts{
funcNames: ctx.StringSlice("name"),
apiPaths: ctx.StringSlice("path"),
nodes: ctx.StringSlice("node"),
}
// Start listening on all trace activity.
traceCh := client.ServiceTrace(ctxt, opts)
done := make(chan struct{})
p := tea.NewProgram(initTraceUI())
go func() {
if e := p.Start(); e != nil {
os.Exit(1)
}
close(done)
}()
go func() {
for apiCallInfo := range traceCh {
if apiCallInfo.Err != nil {
fatalIf(probe.NewError(apiCallInfo.Err), "Unable to fetch top API events")
}
if matchTrace(mopts, apiCallInfo) {
p.Send(topAPIResult{
apiCallInfo: apiCallInfo,
})
}
p.Send(topAPIResult{
apiCallInfo: madmin.ServiceTraceInfo{},
})
}
}()
<-done
return nil
}

164
cmd/support-top-locks.go Normal file
View File

@@ -0,0 +1,164 @@
// 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/>.
package cmd
import (
"fmt"
"time"
"github.com/fatih/color"
"github.com/minio/cli"
json "github.com/minio/colorjson"
"github.com/minio/madmin-go"
"github.com/minio/mc/pkg/probe"
"github.com/minio/pkg/console"
)
var supportTopLocksFlag = []cli.Flag{
cli.BoolFlag{
Name: "stale",
Usage: "list stale locks ask",
},
cli.IntFlag{
Name: "count",
Usage: "number of top locks",
Hidden: true,
Value: 10,
},
}
var supportTopLocksCmd = cli.Command{
Name: "locks",
Usage: "get a list of the 10 oldest locks on a MinIO cluster.",
Before: setGlobalsFromContext,
Action: mainSupportTopLocks,
OnUsageError: onUsageError,
Flags: append(supportTopLocksFlag, globalFlags...),
CustomHelpTemplate: `NAME:
{{.HelpName}} - {{.Usage}}
USAGE:
{{.HelpName}} TARGET
FLAGS:
{{range .VisibleFlags}}{{.}}
{{end}}
EXAMPLES:
1. Get a list of the 10 oldest locks on a MinIO cluster.
{{.Prompt}} {{.HelpName}} myminio/
`,
}
// lockMessage struct to list lock information.
type lockMessage struct {
Status string `json:"status"`
Lock madmin.LockEntry `json:"locks"`
}
func getTimeDiff(timeStamp time.Time) (string, string) {
now := time.Now().UTC()
diff := now.Sub(timeStamp)
hours := int(diff.Hours())
minutes := int(diff.Minutes()) % 60
seconds := int(diff.Seconds()) % 60
if hours == 0 {
if minutes == 0 {
return "Lock", fmt.Sprint(seconds, " seconds")
}
return "Lock", fmt.Sprint(minutes, " minutes")
}
return "StaleLock", fmt.Sprint(hours, " hours")
}
// String colorized oldest locks message.
func (u lockMessage) String() string {
const (
timeFieldMaxLen = 20
resourceFieldMaxLen = -1
typeFieldMaxLen = 6
)
lockState, timeDiff := getTimeDiff(u.Lock.Timestamp)
return console.Colorize(lockState, newPrettyTable(" ",
Field{"Time", timeFieldMaxLen},
Field{"Type", typeFieldMaxLen},
Field{"Resource", resourceFieldMaxLen},
).buildRow(timeDiff, u.Lock.Type, u.Lock.Resource))
}
// JSON jsonified top oldest locks message.
func (u lockMessage) JSON() string {
u.Status = "success"
statusJSONBytes, e := json.MarshalIndent(u, "", " ")
fatalIf(probe.NewError(e), "Unable to marshal into JSON.")
return string(statusJSONBytes)
}
// checkAdminTopLocksSyntax - validate all the passed arguments
func checkSupportTopLocksSyntax(ctx *cli.Context) {
if len(ctx.Args()) == 0 || len(ctx.Args()) > 1 {
cli.ShowCommandHelpAndExit(ctx, "locks", 1) // last argument is exit code
}
}
func mainSupportTopLocks(ctx *cli.Context) error {
checkSupportTopLocksSyntax(ctx)
// Get the alias parameter from cli
args := ctx.Args()
aliasedURL := args.Get(0)
// Create a new MinIO Admin Client
client, err := newAdminClient(aliasedURL)
fatalIf(err, "Unable to initialize admin connection.")
// Call top locks API
entries, e := client.TopLocksWithOpts(globalContext, madmin.TopLockOpts{
Count: ctx.Int("count"),
Stale: ctx.Bool("stale"),
})
fatalIf(probe.NewError(e), "Unable to get server locks list.")
console.SetColor("StaleLock", color.New(color.FgRed, color.Bold))
console.SetColor("Lock", color.New(color.FgBlue, color.Bold))
console.SetColor("Headers", color.New(color.FgGreen, color.Bold))
// Print
printLocks(entries)
return nil
}
func printHeaders() {
timeFieldMaxLen := 20
resourceFieldMaxLen := -1
typeFieldMaxLen := 6
console.Println(console.Colorize("Headers", newPrettyTable(" ",
Field{"Time", timeFieldMaxLen},
Field{"Type", typeFieldMaxLen},
Field{"Resource", resourceFieldMaxLen},
).buildRow("Time", "Type", "Resource")))
}
// Prints oldest locks.
func printLocks(locks madmin.LockEntries) {
if !globalJSON {
printHeaders()
}
for _, entry := range locks {
printMsg(lockMessage{Lock: entry})
}
}

42
cmd/support-top.go Normal file
View File

@@ -0,0 +1,42 @@
// 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/>.
package cmd
import "github.com/minio/cli"
var supportTopSubcommands = []cli.Command{
supportTopAPICmd,
supportTopLocksCmd,
}
var supportTopCmd = cli.Command{
Name: "top",
Usage: "provide top like statistics for MinIO",
Action: mainSupportTop,
Before: setGlobalsFromContext,
Flags: globalFlags,
Subcommands: supportTopSubcommands,
HideHelpCommand: true,
}
// mainSupportTop is the handle for "mc support top" command.
func mainSupportTop(ctx *cli.Context) error {
commandNotFound(ctx, supportTopSubcommands)
return nil
// Sub-commands like "locks" have their own main.
}

View File

@@ -37,6 +37,7 @@ var supportSubcommands = []cli.Command{
supportPerfCmd,
supportInspectCmd,
supportProfileCmd,
supportTopCmd,
}
var supportCmd = cli.Command{

View File

@@ -17,7 +17,6 @@ prometheus manages prometheus config
kms perform KMS management operations
bucket manage buckets defined in the MinIO server
tier manage remote tier targets for ILM transition
top provide top like statistics for MinIO
trace show http trace for MinIO server
console show console logs for MinIO server
```
@@ -647,23 +646,6 @@ mc admin config import myminio < /tmp/my-serverconfig
Healing is automatic on server side which runs on a continuous basis on a low priority thread, `mc admin heal` is deprecated and will be removed in future.
<a name="top"></a>
### Command `top` - provide top like statistics for MinIO
NOTE: This command is only applicable for a distributed MinIO setup. It is not supported on single node and gateway deployments.
```
NAME:
mc admin top - provide top like statistics for MinIO
COMMANDS:
locks Get a list of the 10 oldest locks on a MinIO cluster.
```
*Example: Get a list of the 10 oldest locks on a distributed MinIO cluster, where 'myminio' is the MinIO cluster alias.*
```
mc admin top locks myminio
```
<a name="trace"></a>
### Command `trace` - Show http trace for MinIO server

View File

@@ -1925,6 +1925,7 @@ NAME:
mc support inspect upload raw object contents for analysis
mc support profile generate profile data for debugging
mc support logs configure/display MinIO console logs
mc support top provide top like statistics for MinIO
```
@@ -1968,6 +1969,16 @@ Enable logs for cluster with alias 'play'
mc support logs enable play
```
Get a list of the 10 oldest locks on a distributed MinIO cluster, where 'myminio' is the MinIO cluster alias.*
```
mc admin top locks myminio
```
Display current in-progress all 's3.PutObject' API calls.
```
mc support top api --name s3.PutObject myminio/
```
<a name="ping"></a>
### Command `ping`

2
go.sum
View File

@@ -60,6 +60,7 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
@@ -272,6 +273,7 @@ github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47e
github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s=
github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=