From 311a8474d663f224f556666b060067a9a6ac57f4 Mon Sep 17 00:00:00 2001 From: "T. von Dein" Date: Fri, 10 Jul 2026 13:37:59 +0200 Subject: [PATCH] feature/node-usage (#88) --- README.md | 2 ++ cmd/node.go | 17 +++++++++++ pkg/es/api.go | 1 + pkg/es/node.go | 79 ++++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 94 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1b34314..2225344 100644 --- a/README.md +++ b/README.md @@ -538,6 +538,7 @@ index - manage indicies close - close an index fields - show info about field capabilities ilm - show ilm status + du - show index disk usage alias - manage index aliases create - create an index alias list - list index aliases @@ -555,6 +556,7 @@ node - manage nodes list - list nodes show - show details about a node clients - show node http clients + usage - show node usage stats role - manage roles list - list roles show - show details about a role diff --git a/cmd/node.go b/cmd/node.go index 9c34f64..4f8bcc2 100644 --- a/cmd/node.go +++ b/cmd/node.go @@ -36,6 +36,7 @@ func Node(conf *cfg.Config) *cli.Command { NodeList(conf), NodeShow(conf), NodeClients(conf), + NodeUsage(conf), }, } } @@ -103,3 +104,19 @@ func NodeClients(conf *cfg.Config) *cli.Command { }, } } + +func NodeUsage(conf *cfg.Config) *cli.Command { + return &cli.Command{ + Name: "usage", + Usage: "show node usage stats", + UsageText: "usage [options] []", + + ShellComplete: func(ctx context.Context, cmd *cli.Command) { + complete(conf, cmd, Cnode) + }, + + Action: func(ctx context.Context, cmd *cli.Command) error { + return es.NodeUsage(conf, cmd.Args().Get(0)) + }, + } +} diff --git a/pkg/es/api.go b/pkg/es/api.go index 22ea635..303daa0 100644 --- a/pkg/es/api.go +++ b/pkg/es/api.go @@ -280,6 +280,7 @@ func prettyfiJson(conf *cfg.Config, raw []byte) (string, error) { err := json.Indent(&pretty, raw, "", "\t") if err != nil { + //nolint:nilerr return string(raw), nil } diff --git a/pkg/es/node.go b/pkg/es/node.go index 5ece30a..a0c6e8c 100644 --- a/pkg/es/node.go +++ b/pkg/es/node.go @@ -37,11 +37,11 @@ func NodeList(conf *cfg.Config) error { slog.Debug("ES result", "nodes", nodes) - table := printer.NewTable(conf, 7, len(nodes)) - table.Addheaders("name", "ip", "load1m", "load5m", "load15m", "ram %", "heap %") + table := printer.NewTableEmpty(conf).WithHeaders( + "name", "ip", "load1m", "load5m", "load15m", "ram %", "heap %") - for idx, node := range nodes { - table.Entries[idx] = []any{ + for _, node := range nodes { + table.AddRow( *node.Name, *node.Ip, *node.Load1M, @@ -49,7 +49,7 @@ func NodeList(conf *cfg.Config) error { *node.Load15M, node.RamPercent, node.HeapPercent, - } + ) } table.Sort() @@ -235,3 +235,72 @@ func NodeClients(conf *cfg.Config, nodename string) error { return table.Print() } + +func NodeUsage(conf *cfg.Config, nodeid string) error { + usage := conf.DefaultCluster.ES().Nodes.Usage() + + if nodeid != "" { + usage.NodeId(nodeid) + } + + stats, err := usage.Do(context.Background()) + if err != nil { + return fmt.Errorf("failed to get node usage: %w", esErrorString(err)) + } + + slog.Debug("ES result", "usage", stats) + + table := printer.NewTableEmpty(conf).WithHeaders( + "node", + "bulk", + "doc get", + "doc mget", + "doc update", + "index doc", + "index stats", + "search", + "msearch", + "open pit", + ) + + for id, actions := range stats.Nodes { + node, err := getNodeName(conf, id) + if err != nil { + return err + } + + stat := actions.RestActions + + table.AddRow( + node, + stat["bulk_action"], + stat["document_get_action"], + stat["document_mget_action"], + stat["document_update_action"], + stat["document_index_action"], + stat["indices_stats_action"], + stat["search_action"], + stat["msearch_action"], + stat["open_point_in_time"], + ) + } + + return table.Print() +} + +func getNodeName(conf *cfg.Config, id string) (string, error) { + res, err := conf.DefaultCluster.ES().Nodes.Info(). + Metric("os"). + Do(context.Background()) + if err != nil { + return "", fmt.Errorf("failed to get node info: %w", esErrorString(err)) + } + + for nodeid, node := range res.Nodes { + if id == nodeid { + return node.Name, nil + } + } + + return "", nil +}