Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds new "server report leafs" #1290

Merged
merged 2 commits into from
Mar 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 106 additions & 5 deletions cli/server_report_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,23 +84,23 @@ func configureServerReportCommand(srv *fisk.CmdClause) {
acct := report.Command("accounts", "Report on account activity").Alias("acct").Action(c.reportAccount)
acct.Arg("account", "Account to produce a report for").StringVar(&c.account)
acct.Arg("limit", "Limit the responses to a certain amount of servers").IntVar(&c.waitFor)
addFilterOpts(acct)
acct.Flag("sort", "Sort by a specific property (in-bytes,out-bytes,in-msgs,out-msgs,conns,subs)").Default("subs").EnumVar(&c.sort, "in-bytes", "out-bytes", "in-msgs", "out-msgs", "conns", "subs")
acct.Flag("top", "Limit results to the top results").Default("1000").IntVar(&c.topk)
addFilterOpts(acct)
acct.Flag("json", "Produce JSON output").Short('j').UnNegatableBoolVar(&c.json)

conns := report.Command("connections", "Report on connections").Alias("conn").Alias("connz").Alias("conns").Action(c.reportConnections)
conns.Arg("limit", "Limit the responses to a certain amount of servers").IntVar(&c.waitFor)
conns.Flag("account", "Limit report to a specific account").StringVar(&c.account)
addFilterOpts(conns)
conns.Flag("sort", "Sort by a specific property (in-bytes,out-bytes,in-msgs,out-msgs,uptime,cid,subs)").Default("subs").EnumVar(&c.sort, "in-bytes", "out-bytes", "in-msgs", "out-msgs", "uptime", "cid", "subs")
conns.Flag("top", "Limit results to the top results").Default("1000").IntVar(&c.topk)
conns.Flag("subject", "Limits responses only to those connections with matching subscription interest").StringVar(&c.subject)
conns.Flag("username", "Limits responses only to those connections for a specific authentication username").StringVar(&c.user)
conns.Flag("state", "Limits responses only to those connections that are in a specific state (open, closed, all)").PlaceHolder("STATE").Default("open").EnumVar(&c.stateFilter, "open", "closed", "all")
conns.Flag("closed-reason", "Filter results based on a closed reason").PlaceHolder("REASON").StringVar(&c.filterReason)
conns.Flag("json", "Produce JSON output").Short('j').UnNegatableBoolVar(&c.json)
conns.Flag("filter", "Expression based filter for connections").StringVar(&c.filterExpression)
addFilterOpts(conns)
conns.Flag("json", "Produce JSON output").Short('j').UnNegatableBoolVar(&c.json)

cpu := report.Command("cpu", "Report on CPU usage").Action(c.reportCPU)
addFilterOpts(cpu)
Expand All @@ -109,28 +109,129 @@ func configureServerReportCommand(srv *fisk.CmdClause) {
gateways := report.Command("gateways", "Repost on Gateway (Super Cluster) connections").Alias("super").Alias("gateway").Action(c.reportGateway)
gateways.Flag("filter-name", "Limits responses to a certain name").StringVar(&c.gatewayName)
gateways.Flag("sort", "Sorts by a specific property (server,cluster)").Default("cluster").EnumVar(&c.sort, "server", "cluster")
addFilterOpts(gateways)

health := report.Command("health", "Report on Server health").Action(c.reportHealth)
health.Flag("js-enabled", "Checks that JetStream should be enabled on all servers").Short('J').BoolVar(&c.jsEnabled)
health.Flag("server-only", "Restricts the health check to the JetStream server only, do not check streams and consumers").Short('S').BoolVar(&c.jsServerOnly)
health.Flag("account", "Check only a specific Account").StringVar(&c.account)
health.Flag("stream", "Check only a specific Stream").StringVar(&c.stream)
health.Flag("consumer", "Check only a specific Consumer").StringVar(&c.consumer)
addFilterOpts(health)

jsz := report.Command("jetstream", "Report on JetStream activity").Alias("jsz").Alias("js").Action(c.reportJetStream)
jsz.Arg("limit", "Limit the responses to a certain amount of servers").IntVar(&c.waitFor)
addFilterOpts(jsz)
jsz.Flag("account", "Produce the report for a specific account").StringVar(&c.account)
jsz.Flag("sort", "Sort by a specific property (name,cluster,streams,consumers,msgs,mbytes,mem,file,api,err").Default("cluster").EnumVar(&c.sort, "name", "cluster", "streams", "consumers", "msgs", "mbytes", "bytes", "mem", "file", "store", "api", "err")
jsz.Flag("compact", "Compact server names").Default("true").BoolVar(&c.compact)
addFilterOpts(jsz)

leafs := report.Command("leafnodes", "Report on Leafnode connections").Alias("leaf").Alias("leafz").Action(c.reportLeafs)
leafs.Flag("account", "Produce the report for a specific account").StringVar(&c.account)
leafs.Flag("sort", "Sort by a specific property (server,name,account,subs,in-bytes,out-bytes,in-msgs,out-msgs)").EnumVar(&c.sort, "server", "name", "account", "subs", "in-bytes", "out-bytes", "in-msgs", "out-msgs")
addFilterOpts(leafs)

mem := report.Command("mem", "Report on Memory usage").Action(c.reportMem)
addFilterOpts(mem)
mem.Flag("json", "Produce JSON output").Short('j').UnNegatableBoolVar(&c.json)

routes := report.Command("routes", "Report on Route (Cluster) connections").Alias("route").Action(c.reportRoute)
routes.Flag("cluster", "Limits the report to a specific cluster").StringVar(&c.cluster)
routes.Flag("sort", "Sort by a specific property (server,cluster,name,account,subs,in-bytes,out-bytes)").EnumVar(&c.sort, "server", "cluster", "name", "account", "subs", "in-bytes", "out-bytes")
addFilterOpts(routes)
}

func (c *SrvReportCmd) reportLeafs(_ *fisk.ParseContext) error {
nc, _, err := prepareHelper("", natsOpts()...)
if err != nil {
return err
}

req := server.LeafzEventOptions{
LeafzOptions: server.LeafzOptions{
Account: c.account,
},
EventFilterOptions: c.reqFilter(),
}

results, err := doReq(req, "$SYS.REQ.SERVER.PING.LEAFZ", c.waitFor, nc)
if err != nil {
return err
}

type leaf struct {
server *server.ServerInfo
leafs *server.LeafInfo
}

var leafs []*leaf
for _, result := range results {
s := &server.ServerAPILeafzResponse{}
err := json.Unmarshal(result, s)
if err != nil {
return err
}

if s.Error != nil {
return fmt.Errorf("%v", s.Error.Error())
}

for _, l := range s.Data.Leafs {
leafs = append(leafs, &leaf{
server: s.Server,
leafs: l,
})
}
}

sort.Slice(leafs, func(i, j int) bool {
switch c.sort {
case "name":
return c.boolReverse(leafs[i].leafs.Name < leafs[j].leafs.Name)
case "account":
return c.boolReverse(leafs[i].leafs.Account < leafs[j].leafs.Account)
case "subs":
return c.boolReverse(leafs[i].leafs.NumSubs < leafs[j].leafs.NumSubs)
case "in-bytes":
return c.boolReverse(leafs[i].leafs.InBytes < leafs[j].leafs.InBytes)
case "out-bytes":
return c.boolReverse(leafs[i].leafs.OutBytes < leafs[j].leafs.OutBytes)
case "in-msgs":
return c.boolReverse(leafs[i].leafs.InMsgs < leafs[j].leafs.InMsgs)
case "out-msgs":
return c.boolReverse(leafs[i].leafs.OutMsgs < leafs[j].leafs.OutMsgs)
default:
return c.boolReverse(leafs[i].server.Name < leafs[j].server.Name)
}
})

tbl := iu.NewTableWriter(opts(), "Leafnode Report")
tbl.AddHeaders("Server", "Name", "Account", "Address", "RTT", "Msgs In", "Msgs Out", "Bytes In", "Bytes Out", "Subs", "Compressed", "Spoke")

for _, lz := range leafs {
acct := lz.leafs.Account
if len(acct) > 23 {
acct = fmt.Sprintf("%s...%s", acct[0:10], acct[len(acct)-10:])
}

tbl.AddRow(
lz.server.Name,
lz.leafs.Name,
acct,
fmt.Sprintf("%s:%d", lz.leafs.IP, lz.leafs.Port),
lz.leafs.RTT,
f(lz.leafs.InMsgs),
f(lz.leafs.OutMsgs),
fiBytes(uint64(lz.leafs.InBytes)),
fiBytes(uint64(lz.leafs.OutBytes)),
f(lz.leafs.NumSubs),
f(lz.leafs.Compression),
f(lz.leafs.IsSpoke),
)
}

fmt.Println(tbl.Render())

return nil
}

func (c *SrvReportCmd) parseRtt(rtt string, crit time.Duration) string {
Expand Down
25 changes: 12 additions & 13 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ require (
github.com/choria-io/fisk v0.6.5-0.20250211084745-e47f4036cf7f
github.com/choria-io/scaffold v0.0.2
github.com/dustin/go-humanize v1.0.1
github.com/emicklei/dot v1.6.4
github.com/emicklei/dot v1.8.0
github.com/expr-lang/expr v1.16.9
github.com/fatih/color v1.18.0
github.com/ghodss/yaml v1.0.0
github.com/google/go-cmp v0.6.0
github.com/google/go-cmp v0.7.0
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/gosuri/uiprogress v0.0.1
github.com/jedib0t/go-pretty/v6 v6.6.6
github.com/jedib0t/go-pretty/v6 v6.6.7
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/klauspost/compress v1.18.0
github.com/nats-io/jsm.go v0.1.1-0.20250220111643-ccd9863e0621
Expand All @@ -24,12 +24,12 @@ require (
github.com/nats-io/nats.go v1.39.1
github.com/nats-io/nkeys v0.4.10
github.com/nats-io/nuid v1.0.1
github.com/prometheus/client_golang v1.21.0
github.com/prometheus/client_golang v1.21.1
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
github.com/synadia-io/jwt-auth-builder.go v0.0.4
github.com/synadia-io/jwt-auth-builder.go v0.0.6
github.com/tylertreat/hdrhistogram-writer v0.0.0-20210816161836-2e440612a39f
golang.org/x/crypto v0.33.0
golang.org/x/term v0.29.0
golang.org/x/crypto v0.36.0
golang.org/x/term v0.30.0
gopkg.in/gizak/termui.v1 v1.0.0-20151021151108-e62b5929642a
gopkg.in/yaml.v3 v3.0.1
)
Expand Down Expand Up @@ -61,12 +61,11 @@ require (
github.com/rivo/uniseg v0.4.7 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
go.uber.org/automaxprocs v1.6.0 // indirect
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/time v0.10.0 // indirect
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
golang.org/x/net v0.37.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/time v0.11.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
Loading
Loading