diff --git a/cmd/support-top-net.go b/cmd/support-top-net.go index 4da285cb5a..b7341996b6 100644 --- a/cmd/support-top-net.go +++ b/cmd/support-top-net.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2022 MinIO, Inc. +// Copyright (c) 2015-2023 MinIO, Inc. // // This file is part of MinIO Object Storage stack // @@ -30,9 +30,14 @@ import ( var supportTopNetFlags = []cli.Flag{ cli.IntFlag{ Name: "count, c", - Usage: "show up to N Nets", + Usage: "show top count interfaces", Value: 10, }, + cli.IntFlag{ + Name: "interval", + Usage: "interval between requests in seconds", + Value: 1, + }, } var supportTopNetCmd = cli.Command{ @@ -95,18 +100,26 @@ func mainSupportTopNet(ctx *cli.Context) error { // MetricsOptions are options provided to Metrics call. opts := madmin.MetricsOptions{ Type: madmin.MetricNet, - Interval: time.Second, + Interval: time.Duration(ctx.Int("interval")) * time.Second, ByHost: true, N: ctx.Int("count"), } - p := tea.NewProgram(initTopNetUI(endpoint, ctx.Int("count"))) + p := tea.NewProgram(initTopNetUI()) go func() { out := func(m madmin.RealtimeMetrics) { - for endPoint, metric := range m.ByNet { + for endPoint, metric := range m.ByHost { + if metric.Net != nil { + p.Send(topNetResult{ + endPoint: endPoint, + stats: *metric.Net, + }) + } + } + if len(m.Errors) != 0 && len(m.Hosts) != 0 { p.Send(topNetResult{ - endPoint: endPoint, - stats: metric, + endPoint: m.Hosts[0], + error: m.Errors[0], }) } } diff --git a/cmd/top-net-spinner.go b/cmd/top-net-spinner.go index cb56c8bd42..7bb539e9d1 100644 --- a/cmd/top-net-spinner.go +++ b/cmd/top-net-spinner.go @@ -34,20 +34,22 @@ type topNetUI struct { spinner spinner.Model quitting bool - sortAsc bool - count int - endPoints []string + sortAsc bool - prevTopMap map[string]*madmin.NetMetrics - currTopMap map[string]*madmin.NetMetrics + currTopMap map[string]topNetResult } type topNetResult struct { final bool endPoint string + error string stats madmin.NetMetrics } +func (t topNetResult) GetTotalBytes() uint64 { + return t.stats.NetStats.RxBytes + t.stats.NetStats.TxBytes +} + func (m *topNetUI) Init() tea.Cmd { return m.spinner.Tick } @@ -62,8 +64,7 @@ func (m *topNetUI) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } return m, nil case topNetResult: - m.prevTopMap[msg.endPoint] = m.currTopMap[msg.endPoint] - m.currTopMap[msg.endPoint] = &msg.stats + m.currTopMap[msg.endPoint] = msg if msg.final { m.quitting = true return m, tea.Quit @@ -79,11 +80,6 @@ func (m *topNetUI) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } } -type metricNetMetricsWrap struct { - *madmin.NetMetrics - EndPoint string -} - func (m *topNetUI) View() string { var s strings.Builder // Set table header @@ -99,48 +95,53 @@ func (m *topNetUI) View() string { table.SetBorder(false) table.SetTablePadding("\t") // pad with tabs table.SetNoWhiteSpace(true) - table.SetHeader([]string{"SERVER", "Face", "RECEIVE", "TRANSMIT"}) + table.SetHeader([]string{"SERVER", "Face", "RECEIVE", "TRANSMIT", ""}) - var data []metricNetMetricsWrap + data := make([]topNetResult, 0, len(m.currTopMap)) - for endPoint, metric := range m.currTopMap { - data = append(data, metricNetMetricsWrap{NetMetrics: metric, EndPoint: endPoint}) + for _, metric := range m.currTopMap { + data = append(data, metric) } sort.Slice(data, func(i, j int) bool { if m.sortAsc { - return data[i].NetStats.RxBytes < data[j].NetStats.RxBytes + return data[i].GetTotalBytes() < data[j].GetTotalBytes() } - return data[i].NetStats.RxBytes >= data[j].NetStats.RxBytes + return data[i].GetTotalBytes() >= data[j].GetTotalBytes() }) - if len(data) > m.count { - data = data[:m.count] - } - dataRender := make([][]string, 0, len(data)) for _, d := range data { - dataRender = append(dataRender, []string{ - d.EndPoint, - whiteStyle.Render(d.InterfaceName), - whiteStyle.Render(fmt.Sprintf("%s/s", humanize.IBytes(uint64(d.NetStats.RxBytes)))), - whiteStyle.Render(fmt.Sprintf("%s/s", humanize.IBytes(uint64(d.NetStats.TxBytes)))), - }) + if d.error == "" { + dataRender = append(dataRender, []string{ + d.endPoint, + whiteStyle.Render(d.stats.InterfaceName), + whiteStyle.Render(fmt.Sprintf("%s/s", humanize.IBytes(d.stats.NetStats.RxBytes))), + whiteStyle.Render(fmt.Sprintf("%s/s", humanize.IBytes(d.stats.NetStats.TxBytes))), + "", + }) + } else { + dataRender = append(dataRender, []string{ + d.endPoint, + whiteStyle.Render(d.stats.NetStats.Name), + crossTickCell, + crossTickCell, + d.error, + }) + } } + table.AppendBulk(dataRender) table.Render() return s.String() } -func initTopNetUI(endpoint []string, count int) *topNetUI { +func initTopNetUI() *topNetUI { s := spinner.New() s.Spinner = spinner.Points s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("205")) return &topNetUI{ - count: count, - endPoints: endpoint, spinner: s, - prevTopMap: make(map[string]*madmin.NetMetrics), - currTopMap: make(map[string]*madmin.NetMetrics), + currTopMap: make(map[string]topNetResult), } } diff --git a/go.mod b/go.mod index b35a208113..0d6aaa0679 100644 --- a/go.mod +++ b/go.mod @@ -127,4 +127,4 @@ require ( gopkg.in/ini.v1 v1.67.0 // indirect ) -replace github.com/minio/madmin-go/v3 => github.com/jiuker/madmin-go/v3 v3.0.0-20230710073205-5f02950b07bb +replace github.com/minio/madmin-go/v3 => github.com/jiuker/madmin-go/v3 v3.0.0-20230711012357-0ff0db230c30 diff --git a/go.sum b/go.sum index 50d1ae87bf..666bd3c5a2 100644 --- a/go.sum +++ b/go.sum @@ -79,8 +79,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jedib0t/go-pretty/v6 v6.4.6 h1:v6aG9h6Uby3IusSSEjHaZNXpHFhzqMmjXcPq1Rjl9Jw= github.com/jedib0t/go-pretty/v6 v6.4.6/go.mod h1:Ndk3ase2CkQbXLLNf5QDHoYb6J9WtVfmHZu9n8rk2xs= -github.com/jiuker/madmin-go/v3 v3.0.0-20230710073205-5f02950b07bb h1:HWFuHuWkuiQLhDb+8CMG8kTPubzFoB/tfSfmkX46Zrw= -github.com/jiuker/madmin-go/v3 v3.0.0-20230710073205-5f02950b07bb/go.mod h1:lPrMoc1aeiIWmmrxBthkDqzMPQwC/Lu9ByuyM2wenJk= +github.com/jiuker/madmin-go/v3 v3.0.0-20230711012357-0ff0db230c30 h1:Yq+OQjZmrbuByCaH6v1xfWv2xoqL+UWQHyMnIUKPi/E= +github.com/jiuker/madmin-go/v3 v3.0.0-20230711012357-0ff0db230c30/go.mod h1:lPrMoc1aeiIWmmrxBthkDqzMPQwC/Lu9ByuyM2wenJk= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI=