enhance/clusterstatus (#44)

fixes #35
This commit is contained in:
T. von Dein
2026-06-22 14:11:32 +02:00
parent 9b5ba0775b
commit a81d6b94e9
2 changed files with 73 additions and 12 deletions

View File

@@ -27,6 +27,8 @@ import (
"codeberg.org/scip/esctl/pkg/cfg" "codeberg.org/scip/esctl/pkg/cfg"
"codeberg.org/scip/esctl/pkg/printer" "codeberg.org/scip/esctl/pkg/printer"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
"github.com/elastic/go-elasticsearch/v9/typedapi/cat/indices"
"github.com/elastic/go-elasticsearch/v9/typedapi/cat/tasks"
"github.com/elastic/go-elasticsearch/v9/typedapi/ccr/stats" "github.com/elastic/go-elasticsearch/v9/typedapi/ccr/stats"
"github.com/elastic/go-elasticsearch/v9/typedapi/cluster/health" "github.com/elastic/go-elasticsearch/v9/typedapi/cluster/health"
clusterstats "github.com/elastic/go-elasticsearch/v9/typedapi/cluster/stats" clusterstats "github.com/elastic/go-elasticsearch/v9/typedapi/cluster/stats"
@@ -39,6 +41,8 @@ const (
ResponseInfo ResponseInfo
ResponseCcr ResponseCcr
ResponseStats ResponseStats
ResponseIndices
ResponseTasks
) )
type ClusterIndices map[string]map[string]*types.IndicesRecord type ClusterIndices map[string]map[string]*types.IndicesRecord
@@ -49,6 +53,8 @@ type apiResponse struct {
health *health.Response health *health.Response
ccr *stats.Response ccr *stats.Response
stats *clusterstats.Response stats *clusterstats.Response
indices *indices.Response
tasks *tasks.Response
which int which int
} }
@@ -94,7 +100,7 @@ func ClusterList(conf *cfg.Config) error {
// have to do 3 of'em for each cluster. This speeds things up. // have to do 3 of'em for each cluster. This speeds things up.
func ClusterStatus(conf *cfg.Config) error { func ClusterStatus(conf *cfg.Config) error {
clusters := []string{} clusters := []string{}
gocount := 3 gocount := 5
if conf.Verbose { if conf.Verbose {
gocount++ gocount++
} }
@@ -120,6 +126,8 @@ func ClusterStatus(conf *cfg.Config) error {
go getClusterData(es, wg, responses, "health") go getClusterData(es, wg, responses, "health")
go getClusterData(es, wg, responses, "info") go getClusterData(es, wg, responses, "info")
go getClusterData(es, wg, responses, "ccrstats") go getClusterData(es, wg, responses, "ccrstats")
go getClusterData(es, wg, responses, "indices")
go getClusterData(es, wg, responses, "tasks")
if conf.Verbose { if conf.Verbose {
go getClusterData(es, wg, responses, "stats") go getClusterData(es, wg, responses, "stats")
@@ -131,6 +139,8 @@ func ClusterStatus(conf *cfg.Config) error {
var info *info.Response var info *info.Response
var ccrstats *stats.Response var ccrstats *stats.Response
var clusterstats *clusterstats.Response var clusterstats *clusterstats.Response
var indexstats *indices.Response
var taskstatus *tasks.Response
for i := 0; i < gocount; i++ { for i := 0; i < gocount; i++ {
r := <-responses r := <-responses
@@ -148,11 +158,17 @@ func ClusterStatus(conf *cfg.Config) error {
info = r.info info = r.info
case ResponseStats: case ResponseStats:
clusterstats = r.stats clusterstats = r.stats
case ResponseIndices:
indexstats = r.indices
case ResponseTasks:
taskstatus = r.tasks
} }
} }
slog.Debug("ES result", "cluster health", clusterhealth) slog.Debug("ES result", "cluster health", clusterhealth)
isleader := len(ccrstats.AutoFollowStats.AutoFollowedClusters) == 0
ccrfollowing := "" ccrfollowing := ""
if len(ccrstats.AutoFollowStats.AutoFollowedClusters) > 0 { if len(ccrstats.AutoFollowStats.AutoFollowedClusters) > 0 {
// is following another cluster // is following another cluster
@@ -163,6 +179,22 @@ func ClusterStatus(conf *cfg.Config) error {
) )
} }
// look for red indices, if any
redindices := 0
for _, index := range *indexstats {
if *index.Health == "red" {
redindices++
}
}
// look for long running tasks
longtasks := 0
for _, task := range *taskstatus {
if strings.Contains(*task.RunningTime, "d") {
longtasks++
}
}
table := printer.NewTable(conf, 2, 7) table := printer.NewTable(conf, 2, 7)
table.Addheaders(cluster, "status") table.Addheaders(cluster, "status")
@@ -170,10 +202,22 @@ func ClusterStatus(conf *cfg.Config) error {
{"Cluster Name", clusterhealth.ClusterName}, {"Cluster Name", clusterhealth.ClusterName},
{"ES Status", printer.Colorize(conf, clusterhealth.Status.Name, clusterhealth.Status.Name)}, {"ES Status", printer.Colorize(conf, clusterhealth.Status.Name, clusterhealth.Status.Name)},
{"ES Version", info.Version.Int}, {"ES Version", info.Version.Int},
{"Is Leader", fmt.Sprintf("%t", isleader)},
{"Active Shards", fmt.Sprintf("%d", clusterhealth.ActiveShards)}, {"Active Shards", fmt.Sprintf("%d", clusterhealth.ActiveShards)},
{"Active Primary Shards", fmt.Sprintf("%d", clusterhealth.ActivePrimaryShards)}, {"Active Primary Shards", fmt.Sprintf("%d", clusterhealth.ActivePrimaryShards)},
{"Unassigned Shards", fmt.Sprintf("%d", clusterhealth.UnassignedShards)},
{"Unassigned Primary Shards", fmt.Sprintf("%d", clusterhealth.UnassignedPrimaryShards)},
{"Pending Tasks", fmt.Sprintf("%d", clusterhealth.NumberOfPendingTasks)},
{"Nodes", fmt.Sprintf("%d", clusterhealth.NumberOfNodes)}, {"Nodes", fmt.Sprintf("%d", clusterhealth.NumberOfNodes)},
{"Red Indices", fmt.Sprintf("%d", redindices)},
{"Long Running Tasks", fmt.Sprintf("%d", longtasks)},
}
if !isleader {
table.Entries = append(table.Entries, [][]string{
{"AutoFollow (success/failed indices)", ccrfollowing}, {"AutoFollow (success/failed indices)", ccrfollowing},
{"Followed Indices", fmt.Sprintf("%d", len(ccrstats.FollowStats.Indices))},
}...)
} }
if conf.Verbose { if conf.Verbose {
@@ -202,11 +246,8 @@ func gatherClusterStats(conf *cfg.Config, clusterstats *clusterstats.Response, t
clusterstats.Nodes.Jvm.Versions[0].VmVersion}, " ") clusterstats.Nodes.Jvm.Versions[0].VmVersion}, " ")
} }
isleader := checkClusterIsLeader(conf, conf.CurrentCluster)
table.Entries = append(table.Entries, [][]string{ table.Entries = append(table.Entries, [][]string{
{"Indicies", fmt.Sprintf("%d", clusterstats.Indices.Count)}, {"Indicies", fmt.Sprintf("%d", clusterstats.Indices.Count)},
{"Is Leader", fmt.Sprintf("%t", isleader)},
{"Docs", fmt.Sprintf("%d", clusterstats.Indices.Docs.Count)}, {"Docs", fmt.Sprintf("%d", clusterstats.Indices.Docs.Count)},
{"Total Size", humanize.Bytes(uint64(clusterstats.Indices.Docs.TotalSizeInBytes))}, {"Total Size", humanize.Bytes(uint64(clusterstats.Indices.Docs.TotalSizeInBytes))},
{"Total Queries", fmt.Sprintf("%d", querycount)}, {"Total Queries", fmt.Sprintf("%d", querycount)},
@@ -217,7 +258,7 @@ func gatherClusterStats(conf *cfg.Config, clusterstats *clusterstats.Response, t
humanize.Bytes(uint64(clusterstats.Indices.Store.SizeInBytes)), humanize.Bytes(uint64(clusterstats.Indices.Store.SizeInBytes)),
humanize.Bytes(uint64(*clusterstats.Indices.Store.TotalDataSetSizeInBytes)), humanize.Bytes(uint64(*clusterstats.Indices.Store.TotalDataSetSizeInBytes)),
)}, )},
{"JVM Heap", fmt.Sprintf( {"JVM Heap Memory", fmt.Sprintf(
"%s/%s", "%s/%s",
humanize.Bytes(uint64(clusterstats.Nodes.Jvm.Mem.HeapUsedInBytes)), humanize.Bytes(uint64(clusterstats.Nodes.Jvm.Mem.HeapUsedInBytes)),
humanize.Bytes(uint64(clusterstats.Nodes.Jvm.Mem.HeapMaxInBytes)), humanize.Bytes(uint64(clusterstats.Nodes.Jvm.Mem.HeapMaxInBytes)),

View File

@@ -390,6 +390,26 @@ func getClusterData(es *elasticsearch.TypedClient, wg *sync.WaitGroup, reschan c
ar.stats = res ar.stats = res
ar.which = ResponseStats ar.which = ResponseStats
arerr = err arerr = err
case "indices":
res, err := es.Cat.Indices().
Header("content-type", "application/json").
Header("accept", "application/json").
Do(context.Background())
ar.indices = &res
ar.which = ResponseIndices
arerr = err
case "tasks":
res, err := es.Cat.Tasks().
Header("content-type", "application/json").
Header("accept", "application/json").
Do(context.Background())
ar.tasks = &res
ar.which = ResponseTasks
arerr = err
} }
if arerr != nil { if arerr != nil {