Repository: nats-io/natscli

Issue:
Add server profile collection for RAFTZ and IPQUEUESZ
### Proposed change

Please add collection for `RAFTZ` and `IPQUEUESZ` in `nats server request profile` that was added in 2.10.26+.  https://github.com/nats-io/nats-server/pull/6439

`RAFTZ` should allow for the `account` and `group` filters. 
`IPQUEUESZ` should default to `all: true` and allow for `filter`. 

### Use case

Sometimes collecting this data is necessary to diagnose server level issues. Adding this can make it easier for users to collect the data.

### Contribution

_No response_

Discussion:
@ripienaar: So we need for `nats server req profile` and for audit@ripienaar: @ploubser points out its not `req profile` but rather whole new `req` sub commands, we'll get to those a bit later then 

Solution patch:
diff --git a/cli/server_request_command.go b/cli/server_request_command.go
index 76f5cad..b66d22a 100644
--- a/cli/server_request_command.go
+++ b/cli/server_request_command.go
@@ -69,6 +69,8 @@ type SrvRequestCmd struct {
 	profileDebug int
 	profileDir   string
 	filterEmpty  bool
+	group        string
+	queueFilter  string
 }
 
 func configureServerRequestCommand(srv *fisk.CmdClause) {
@@ -104,6 +106,10 @@ func configureServerRequestCommand(srv *fisk.CmdClause) {
 	gwyz.Flag("accounts", "Show account detail").UnNegatableBoolVar(&c.detail)
 	gwyz.Flag("subscriptions", "Show subscription details").Default("true").BoolVar(&c.accountSubscriptions)
 
+	ipq := req.Command("ipqueue", "Show IP Queue details").Alias("ipq").Alias("ipqueuesz").Action(c.ipqz)
+	ipq.Flag("all", "Shows all available information").Default("true").BoolVar(&c.includeAll)
+	ipq.Flag("filter", "Filter results for specific queues").StringVar(&c.queueFilter)
+
 	healthz := req.Command("jetstream-health", "Request JetStream health status").Alias("healthz").Action(c.healthz)
 	healthz.Arg("wait", "Wait for a certain number of responses").Uint32Var(&c.waitFor)
 	healthz.Flag("js-enabled", "Checks that JetStream should be enabled on all servers").Short('J').BoolVar(&c.jsEnabled)
@@ -138,6 +144,10 @@ func configureServerRequestCommand(srv *fisk.CmdClause) {
 	profilez.Arg("dir", "Set the output directory for profile files").Default(".").ExistingDirVar(&c.profileDir)
 	profilez.Flag("level", "Set the debug level of the profile").IntVar(&c.profileDebug)
 
+	raftz := req.Command("raft", "Show RAFT state details").Alias("raftz").Action(c.raftz)
+	raftz.Flag("account", "Filters on an specific account").StringVar(&c.account)
+	raftz.Flag("group", "Filters on a specific group").StringVar(&c.group)
+
 	routez := req.Command("routes", "Show route details").Alias("route").Alias("routez").Action(c.routez)
 	routez.Arg("wait", "Wait for a certain number of responses").Uint32Var(&c.waitFor)
 	routez.Flag("subscriptions", "Show subscription detail").UnNegatableBoolVar(&c.detail)
@@ -153,6 +163,58 @@ func configureServerRequestCommand(srv *fisk.CmdClause) {
 
 }
 
+func (c *SrvRequestCmd) ipqz(_ *fisk.ParseContext) error {
+	nc, _, err := prepareHelper("", natsOpts()...)
+	if err != nil {
+		return err
+	}
+
+	opts := server.IpqueueszEventOptions{
+		IpqueueszOptions: server.IpqueueszOptions{
+			All:    c.includeAll,
+			Filter: c.queueFilter,
+		},
+		EventFilterOptions: c.reqFilter(),
+	}
+
+	res, err := c.doReq("IPQUEUESZ", &opts, nc)
+	if err != nil {
+		return err
+	}
+
+	for _, m := range res {
+		fmt.Println(string(m))
+	}
+
+	return nil
+}
+
+func (c *SrvRequestCmd) raftz(_ *fisk.ParseContext) error {
+	nc, _, err := prepareHelper("", natsOpts()...)
+	if err != nil {
+		return err
+	}
+
+	opts := server.RaftzEventOptions{
+		RaftzOptions: server.RaftzOptions{
+			AccountFilter: c.account,
+			GroupFilter:   c.group,
+		},
+		EventFilterOptions: c.reqFilter(),
+	}
+
+	res, err := c.doReq("RAFTZ", &opts, nc)
+	if err != nil {
+		return err
+	}
+
+	for _, m := range res {
+		fmt.Println(string(m))
+	}
+
+	return nil
+}
+
 func (c *SrvRequestCmd) kick(_ *fisk.ParseContext) error {
 	nc, _, err := prepareHelper("", natsOpts()...)
 	if err != nil {



Test patch:
diff --git a/nats/tests/server_command_test.go b/nats/tests/server_command_test.go
index 1894514..c004e10 100644
--- a/nats/tests/server_command_test.go
+++ b/nats/tests/server_command_test.go
@@ -14,6 +14,7 @@
 package main
 
 import (
+	"bufio"
 	"encoding/json"
 	"fmt"
 	"log"
@@ -21,6 +22,7 @@ import (
 	"path/filepath"
 	"runtime"
 	"runtime/debug"
+	"strings"
 	"testing"
 	"time"
 
@@ -1241,6 +1243,36 @@ func TestServerRequest(t *testing.T) {
 		}
 	})
 
+	t.Run("ipq command", func(t *testing.T) {
+		srv, _, _ := setupServerTest(t)
+		defer srv.Shutdown()
+
+		output := string(runNatsCli(t, fmt.Sprintf("--server='%s' %s server request ipq", srv.ClientURL(), sysUserCreds)))
+		expected := map[string]any{
+			"server": map[string]any{
+				"name":      "TEST_SERVER",
+				"host":      "0.0.0.0",
+				"id":        `[A-Z0-9]{52}`,
+				"ver":       `\d+\.\d+\.\d+`,
+				"jetstream": "true",
+				"flags":     `\d+`,
+				"seq":       `\d+`,
+				"time":      `.+`,
+			},
+			"data": map[string]any{
+				"Routed JS API Requests": map[string]any{},
+				"SendQ":                  map[string]any{},
+				"System recvQ":           map[string]any{},
+				"System recvQ Pings":     map[string]any{},
+				"delayed API responses":  map[string]any{},
+			},
+		}
+		err := expectMatchJSON(t, output, expected)
+		if err != nil {
+			t.Error(err)
+		}
+	})
+
 	t.Run("jetstream-health command", func(t *testing.T) {
 		srv, _, _ := setupServerTest(t)
 		defer srv.Shutdown()
@@ -1393,6 +1425,43 @@ func TestServerRequest(t *testing.T) {
 		}
 	})
 
+	t.Run("raftz command", func(t *testing.T) {
+		withJSCluster(t, func(t *testing.T, servers []*server.Server, conn *nats.Conn, mgr *jsm.Manager) error {
+			output := string(runNatsCli(t, fmt.Sprintf("--server='%s' %s server request raft", servers[0].ClientURL(), sysUserCreds)))
+
+			var lines []string
+			sc := bufio.NewScanner(strings.NewReader(output))
+			for sc.Scan() {
+				lines = append(lines, sc.Text())
+			}
+
+			if len(lines) != 3 {
+				t.Errorf("failed raftz: %s", output)
+			}
+
+			for _, line := range lines {
+				expected := map[string]any{
+					"server": map[string]any{
+						"host":    "localhost",
+						"cluster": "TEST",
+					},
+					"data": map[string]any{
+						"SYS": map[string]any{
+							"_meta_": map[string]any{},
+						},
+					},
+				}
+
+				err := expectMatchJSON(t, line, expected)
+				if err != nil {
+					t.Error(err)
+				}
+			}
+
+			return nil
+		})
+	})
+
 	t.Run("routes command", func(t *testing.T) {
 		srv, _, _ := setupServerTest(t)
 		defer srv.Shutdown()