mirror of
https://github.com/minio/mc.git
synced 2025-08-09 13:22:43 +03:00
improve UI for netperf inspired from object perf test (#4117)
Refer for the behavior https://asciinema.org/a/501270
This commit is contained in:
@@ -19,6 +19,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/charmbracelet/bubbles/spinner"
|
"github.com/charmbracelet/bubbles/spinner"
|
||||||
@@ -40,8 +41,9 @@ type speedTestUI struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type speedTestResult struct {
|
type speedTestResult struct {
|
||||||
final bool
|
final bool
|
||||||
result madmin.SpeedTestResult
|
result *madmin.SpeedTestResult
|
||||||
|
nresult *madmin.NetperfResult
|
||||||
}
|
}
|
||||||
|
|
||||||
func initSpeedTestUI() *speedTestUI {
|
func initSpeedTestUI() *speedTestUI {
|
||||||
@@ -100,45 +102,86 @@ func (m *speedTestUI) View() string {
|
|||||||
table.SetBorder(false)
|
table.SetBorder(false)
|
||||||
table.SetTablePadding("\t") // pad with tabs
|
table.SetTablePadding("\t") // pad with tabs
|
||||||
table.SetNoWhiteSpace(true)
|
table.SetNoWhiteSpace(true)
|
||||||
|
|
||||||
res := m.result.result
|
res := m.result.result
|
||||||
|
nres := m.result.nresult
|
||||||
|
|
||||||
table.SetHeader([]string{"", "Throughput", "IOPS"})
|
if res != nil {
|
||||||
data := make([][]string, 2)
|
table.SetHeader([]string{"", "Throughput", "IOPS"})
|
||||||
|
data := make([][]string, 2)
|
||||||
|
|
||||||
if res.Version == "" {
|
if res.Version == "" {
|
||||||
data[0] = []string{
|
data[0] = []string{
|
||||||
"PUT",
|
"PUT",
|
||||||
whiteStyle.Render("-- KiB/sec"),
|
whiteStyle.Render("-- KiB/sec"),
|
||||||
whiteStyle.Render("-- objs/sec"),
|
whiteStyle.Render("-- objs/sec"),
|
||||||
|
}
|
||||||
|
data[1] = []string{
|
||||||
|
"GET",
|
||||||
|
whiteStyle.Render("-- KiB/sec"),
|
||||||
|
whiteStyle.Render("-- objs/sec"),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data[0] = []string{
|
||||||
|
"PUT",
|
||||||
|
whiteStyle.Render(humanize.IBytes(res.PUTStats.ThroughputPerSec) + "/s"),
|
||||||
|
whiteStyle.Render(humanize.Comma(int64(res.PUTStats.ObjectsPerSec)) + " objs/s"),
|
||||||
|
}
|
||||||
|
data[1] = []string{
|
||||||
|
"GET",
|
||||||
|
whiteStyle.Render(humanize.IBytes(res.GETStats.ThroughputPerSec) + "/s"),
|
||||||
|
whiteStyle.Render(humanize.Comma(int64(res.GETStats.ObjectsPerSec)) + " objs/s"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
data[1] = []string{
|
table.AppendBulk(data)
|
||||||
"GET",
|
table.Render()
|
||||||
whiteStyle.Render("-- KiB/sec"),
|
|
||||||
whiteStyle.Render("-- objs/sec"),
|
if m.quitting {
|
||||||
|
s.WriteString(fmt.Sprintf("\nSpeedtest: %s", m.result.String()))
|
||||||
|
if vstr := m.result.StringVerbose(); vstr != "" {
|
||||||
|
s.WriteString(vstr)
|
||||||
|
} else {
|
||||||
|
s.WriteString("\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if nres != nil {
|
||||||
data[0] = []string{
|
table.SetHeader([]string{"Node", "RX", "TX", ""})
|
||||||
"PUT",
|
data := make([][]string, 0, len(nres.NodeResults))
|
||||||
whiteStyle.Render(humanize.IBytes(res.PUTStats.ThroughputPerSec) + "/s"),
|
|
||||||
whiteStyle.Render(humanize.Comma(int64(res.PUTStats.ObjectsPerSec)) + " objs/s"),
|
for _, nodeResult := range nres.NodeResults {
|
||||||
|
if nodeResult.Error != "" {
|
||||||
|
data = append(data, []string{
|
||||||
|
nodeResult.Endpoint,
|
||||||
|
"✗",
|
||||||
|
"✗",
|
||||||
|
"Err: " + nodeResult.Error,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
data = append(data, []string{
|
||||||
|
nodeResult.Endpoint,
|
||||||
|
humanize.IBytes(uint64(nodeResult.RX)),
|
||||||
|
humanize.IBytes(uint64(nodeResult.TX)),
|
||||||
|
"✔",
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
data[1] = []string{
|
|
||||||
"GET",
|
sort.Slice(data, func(i, j int) bool {
|
||||||
whiteStyle.Render(humanize.IBytes(res.GETStats.ThroughputPerSec) + "/s"),
|
return data[i][0] < data[j][0]
|
||||||
whiteStyle.Render(humanize.Comma(int64(res.GETStats.ObjectsPerSec)) + " objs/s"),
|
})
|
||||||
|
|
||||||
|
table.AppendBulk(data)
|
||||||
|
table.Render()
|
||||||
|
|
||||||
|
if m.quitting {
|
||||||
|
s.WriteString("\nNetperf: ✔\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
table.AppendBulk(data)
|
|
||||||
table.Render()
|
|
||||||
|
|
||||||
if !m.quitting {
|
if !m.quitting {
|
||||||
s.WriteString(fmt.Sprintf("\nSpeedtest: %s", m.spinner.View()))
|
if nres != nil {
|
||||||
} else {
|
s.WriteString(fmt.Sprintf("\nNetperf: %s", m.spinner.View()))
|
||||||
s.WriteString(fmt.Sprintf("\nSpeedtest: %s", m.result.String()))
|
} else if res != nil {
|
||||||
if vstr := m.result.StringVerbose(); vstr != "" {
|
s.WriteString(fmt.Sprintf("\nSpeedtest: %s", m.spinner.View()))
|
||||||
s.WriteString(vstr)
|
|
||||||
} else {
|
|
||||||
s.WriteString("\n")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return s.String()
|
return s.String()
|
||||||
|
@@ -19,12 +19,10 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"os"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/briandowns/spinner"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
humanize "github.com/dustin/go-humanize"
|
|
||||||
"github.com/minio/cli"
|
"github.com/minio/cli"
|
||||||
json "github.com/minio/colorjson"
|
json "github.com/minio/colorjson"
|
||||||
"github.com/minio/madmin-go"
|
"github.com/minio/madmin-go"
|
||||||
@@ -34,15 +32,8 @@ import (
|
|||||||
type netperfResult madmin.NetperfResult
|
type netperfResult madmin.NetperfResult
|
||||||
|
|
||||||
func (m netperfResult) String() (msg string) {
|
func (m netperfResult) String() (msg string) {
|
||||||
for _, r := range m.NodeResults {
|
// string version is handled by banner.
|
||||||
msg += fmt.Sprintf("%s TX: %s/s RX: %s/s", r.Endpoint, humanize.IBytes(uint64(r.TX)), humanize.IBytes(uint64(r.RX)))
|
return ""
|
||||||
if r.Error != "" {
|
|
||||||
msg += " Error: " + r.Error
|
|
||||||
}
|
|
||||||
msg += "\n"
|
|
||||||
}
|
|
||||||
msg = strings.TrimSuffix(msg, "\n")
|
|
||||||
return msg
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m netperfResult) JSON() string {
|
func (m netperfResult) JSON() string {
|
||||||
@@ -71,15 +62,54 @@ func mainAdminSpeedtestNetperf(ctx *cli.Context, aliasedURL string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
s := spinner.New(spinner.CharSets[14], 100*time.Millisecond)
|
resultCh := make(chan madmin.NetperfResult)
|
||||||
|
go func() {
|
||||||
|
result, err := client.Netperf(ctxt, duration)
|
||||||
|
fatalIf(probe.NewError(err), "Unable to capture network perf results")
|
||||||
|
|
||||||
if !globalJSON {
|
resultCh <- result
|
||||||
s.Start()
|
close(resultCh)
|
||||||
|
}()
|
||||||
|
|
||||||
|
if globalJSON {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case result := <-resultCh:
|
||||||
|
printMsg(netperfResult(result))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := client.Netperf(ctxt, duration)
|
done := make(chan struct{})
|
||||||
s.Stop()
|
|
||||||
fatalIf(probe.NewError(err), "Failed to execute netperf")
|
p := tea.NewProgram(initSpeedTestUI())
|
||||||
printMsg(netperfResult(result))
|
go func() {
|
||||||
|
if e := p.Start(); e != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case result := <-resultCh:
|
||||||
|
p.Send(speedTestResult{
|
||||||
|
nresult: &result,
|
||||||
|
final: true,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
p.Send(speedTestResult{
|
||||||
|
nresult: &madmin.NetperfResult{},
|
||||||
|
})
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
<-done
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
@@ -105,7 +105,6 @@ EXAMPLES:
|
|||||||
|
|
||||||
5. Run network throughput test:
|
5. Run network throughput test:
|
||||||
{{.Prompt}} {{.HelpName}} net myminio
|
{{.Prompt}} {{.HelpName}} net myminio
|
||||||
|
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +230,7 @@ func mainSupportPerf(ctx *cli.Context) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
printMsg(speedTestResult{
|
printMsg(speedTestResult{
|
||||||
result: result,
|
result: &result,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -250,15 +249,12 @@ func mainSupportPerf(ctx *cli.Context) error {
|
|||||||
go func() {
|
go func() {
|
||||||
var result madmin.SpeedTestResult
|
var result madmin.SpeedTestResult
|
||||||
for result = range resultCh {
|
for result = range resultCh {
|
||||||
if result.Version == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
p.Send(speedTestResult{
|
p.Send(speedTestResult{
|
||||||
result: result,
|
result: &result,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
p.Send(speedTestResult{
|
p.Send(speedTestResult{
|
||||||
result: result,
|
result: &result,
|
||||||
final: true,
|
final: true,
|
||||||
})
|
})
|
||||||
}()
|
}()
|
||||||
|
Reference in New Issue
Block a user