diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/stats/IndexStats.java b/server/src/main/java/org/elasticsearch/action/admin/indices/stats/IndexStats.java index 89ff910faebf0..5affc6a9f39e4 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/stats/IndexStats.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/stats/IndexStats.java @@ -8,6 +8,10 @@ package org.elasticsearch.action.admin.indices.stats; +import org.elasticsearch.cluster.health.ClusterHealthStatus; +import org.elasticsearch.cluster.metadata.IndexMetadata; +import org.elasticsearch.core.Nullable; + import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -20,11 +24,23 @@ public class IndexStats implements Iterable { private final String uuid; + private final ClusterHealthStatus health; + + private final IndexMetadata.State state; + private final ShardStats shards[]; - public IndexStats(String index, String uuid, ShardStats[] shards) { + public IndexStats( + String index, + String uuid, + @Nullable ClusterHealthStatus health, + @Nullable IndexMetadata.State state, + ShardStats[] shards + ) { this.index = index; this.uuid = uuid; + this.health = health; + this.state = state; this.shards = shards; } @@ -36,6 +52,14 @@ public String getUuid() { return uuid; } + public ClusterHealthStatus getHealth() { + return health; + } + + public IndexMetadata.State getState() { + return state; + } + public ShardStats[] getShards() { return this.shards; } @@ -48,11 +72,7 @@ public Map getIndexShards() { } Map> tmpIndexShards = new HashMap<>(); for (ShardStats shard : shards) { - List lst = tmpIndexShards.get(shard.getShardRouting().id()); - if (lst == null) { - lst = new ArrayList<>(); - tmpIndexShards.put(shard.getShardRouting().id(), lst); - } + List lst = tmpIndexShards.computeIfAbsent(shard.getShardRouting().id(), ignored -> new ArrayList<>()); lst.add(shard); } indexShards = new HashMap<>(); @@ -106,11 +126,15 @@ public CommonStats getPrimaries() { public static class IndexStatsBuilder { private final String indexName; private final String uuid; + private final ClusterHealthStatus health; + private final IndexMetadata.State state; private final List shards = new ArrayList<>(); - public IndexStatsBuilder(String indexName, String uuid) { + public IndexStatsBuilder(String indexName, String uuid, @Nullable ClusterHealthStatus health, @Nullable IndexMetadata.State state) { this.indexName = indexName; this.uuid = uuid; + this.health = health; + this.state = state; } public IndexStatsBuilder add(ShardStats shardStats) { @@ -119,7 +143,7 @@ public IndexStatsBuilder add(ShardStats shardStats) { } public IndexStats build() { - return new IndexStats(indexName, uuid, shards.toArray(new ShardStats[shards.size()])); + return new IndexStats(indexName, uuid, health, state, shards.toArray(new ShardStats[shards.size()])); } } } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsResponse.java b/server/src/main/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsResponse.java index 7b4b14c606aaa..25c804a340a72 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsResponse.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsResponse.java @@ -8,9 +8,14 @@ package org.elasticsearch.action.admin.indices.stats; +import org.elasticsearch.Version; import org.elasticsearch.action.admin.indices.stats.IndexStats.IndexStatsBuilder; import org.elasticsearch.action.support.DefaultShardOperationFailedException; import org.elasticsearch.action.support.broadcast.BroadcastResponse; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.health.ClusterHealthStatus; +import org.elasticsearch.cluster.health.ClusterIndexHealth; +import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; @@ -21,20 +26,33 @@ import java.io.IOException; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; import static java.util.Collections.unmodifiableMap; public class IndicesStatsResponse extends BroadcastResponse { - private ShardStats[] shards; + private final Map indexHealthMap; + + private final Map indexStateMap; + + private final ShardStats[] shards; private Map shardStatsMap; IndicesStatsResponse(StreamInput in) throws IOException { super(in); - shards = in.readArray(ShardStats::new, (size) -> new ShardStats[size]); + shards = in.readArray(ShardStats::new, ShardStats[]::new); + if (in.getVersion().onOrAfter(Version.V_8_1_0)) { + indexHealthMap = in.readMap(StreamInput::readString, ClusterHealthStatus::readFrom); + indexStateMap = in.readMap(StreamInput::readString, IndexMetadata.State::readFrom); + } else { + indexHealthMap = Map.of(); + indexStateMap = Map.of(); + } } IndicesStatsResponse( @@ -42,10 +60,28 @@ public class IndicesStatsResponse extends BroadcastResponse { int totalShards, int successfulShards, int failedShards, - List shardFailures + List shardFailures, + ClusterState clusterState ) { super(totalShards, successfulShards, failedShards, shardFailures); this.shards = shards; + Objects.requireNonNull(clusterState); + Objects.requireNonNull(shards); + Map indexHealthModifiableMap = new HashMap<>(); + Map indexStateModifiableMap = new HashMap<>(); + for (ShardStats shard : shards) { + Index index = shard.getShardRouting().index(); + IndexMetadata indexMetadata = clusterState.getMetadata().index(index); + if (indexMetadata != null) { + indexHealthModifiableMap.computeIfAbsent( + index.getName(), + ignored -> new ClusterIndexHealth(indexMetadata, clusterState.routingTable().index(index)).getStatus() + ); + indexStateModifiableMap.computeIfAbsent(index.getName(), ignored -> indexMetadata.getState()); + } + } + indexHealthMap = unmodifiableMap(indexHealthModifiableMap); + indexStateMap = unmodifiableMap(indexStateModifiableMap); } public Map asMap() { @@ -83,7 +119,7 @@ public Map getIndices() { Index index = shard.getShardRouting().index(); IndexStatsBuilder indexStatsBuilder = indexToIndexStatsBuilder.computeIfAbsent( index.getName(), - k -> new IndexStatsBuilder(k, index.getUUID()) + k -> new IndexStatsBuilder(k, index.getUUID(), indexHealthMap.get(index.getName()), indexStateMap.get(index.getName())) ); indexStatsBuilder.add(shard); } @@ -128,6 +164,10 @@ public CommonStats getPrimaries() { public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeArray(shards); + if (out.getVersion().onOrAfter(Version.V_8_1_0)) { + out.writeMap(indexHealthMap, StreamOutput::writeString, (o, s) -> s.writeTo(o)); + out.writeMap(indexStateMap, StreamOutput::writeString, (o, s) -> s.writeTo(o)); + } } @Override @@ -157,6 +197,12 @@ protected void addCustomXContentFields(XContentBuilder builder, Params params) t for (IndexStats indexStats : getIndices().values()) { builder.startObject(indexStats.getIndex()); builder.field("uuid", indexStats.getUuid()); + if (indexStats.getHealth() != null) { + builder.field("health", indexStats.getHealth().toString().toLowerCase(Locale.ROOT)); + } + if (indexStats.getState() != null) { + builder.field("status", indexStats.getState().toString().toLowerCase(Locale.ROOT)); + } builder.startObject("primaries"); indexStats.getPrimaries().toXContent(builder, params); builder.endObject(); diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/stats/TransportIndicesStatsAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/stats/TransportIndicesStatsAction.java index 1f24c178f34b4..44bb62cd0f04e 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/stats/TransportIndicesStatsAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/stats/TransportIndicesStatsAction.java @@ -99,7 +99,8 @@ protected IndicesStatsResponse newResponse( totalShards, successfulShards, failedShards, - shardFailures + shardFailures, + clusterState ); } diff --git a/server/src/main/java/org/elasticsearch/cluster/health/ClusterHealthStatus.java b/server/src/main/java/org/elasticsearch/cluster/health/ClusterHealthStatus.java index 960728624ed6c..d025ddab26af6 100644 --- a/server/src/main/java/org/elasticsearch/cluster/health/ClusterHealthStatus.java +++ b/server/src/main/java/org/elasticsearch/cluster/health/ClusterHealthStatus.java @@ -41,16 +41,12 @@ public void writeTo(StreamOutput out) throws IOException { */ public static ClusterHealthStatus readFrom(StreamInput in) throws IOException { byte value = in.readByte(); - switch (value) { - case 0: - return GREEN; - case 1: - return YELLOW; - case 2: - return RED; - default: - throw new IllegalArgumentException("No cluster health status for value [" + value + "]"); - } + return switch (value) { + case 0 -> GREEN; + case 1 -> YELLOW; + case 2 -> RED; + default -> throw new IllegalArgumentException("No cluster health status for value [" + value + "]"); + }; } public static ClusterHealthStatus fromString(String status) { diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadata.java index 7e4e3a4d98ca0..275ccf3e8fe9a 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadata.java @@ -120,7 +120,7 @@ public class IndexMetadata implements Diffable, ToXContentFragmen EnumSet.of(ClusterBlockLevel.WRITE) ); - public enum State { + public enum State implements Writeable { OPEN((byte) 0), CLOSE((byte) 1); @@ -143,6 +143,15 @@ public static State fromId(byte id) { throw new IllegalStateException("No state match for id [" + id + "]"); } + public static State readFrom(StreamInput in) throws IOException { + byte id = in.readByte(); + return switch (id) { + case 0 -> OPEN; + case 1 -> CLOSE; + default -> throw new IllegalStateException("No state match for id [" + id + "]"); + }; + } + public static State fromString(String state) { if ("open".equals(state)) { return OPEN; @@ -151,6 +160,11 @@ public static State fromString(String state) { } throw new IllegalStateException("No state match for [" + state + "]"); } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeByte(id); + } } static Setting buildNumberOfShardsSetting() { diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverActionTests.java b/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverActionTests.java index a78e2c98ea1e2..afe7d3b2ab607 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverActionTests.java @@ -422,7 +422,8 @@ public static IndicesStatsResponse randomIndicesStatsResponse(final IndexMetadat shardStats.size(), shardStats.size(), 0, - emptyList() + emptyList(), + ClusterState.EMPTY_STATE ); } } diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsResponseTests.java b/server/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsResponseTests.java index f22d156bb7362..e1873b7714bcb 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsResponseTests.java @@ -8,6 +8,7 @@ package org.elasticsearch.action.admin.indices.stats; +import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.ShardRoutingState; import org.elasticsearch.cluster.routing.TestShardRouting; @@ -35,7 +36,7 @@ public class IndicesStatsResponseTests extends ESTestCase { public void testInvalidLevel() { - final IndicesStatsResponse response = new IndicesStatsResponse(null, 0, 0, 0, null); + final IndicesStatsResponse response = new IndicesStatsResponse(new ShardStats[0], 0, 0, 0, null, ClusterState.EMPTY_STATE); final String level = randomAlphaOfLength(16); final ToXContent.Params params = new ToXContent.MapParams(Collections.singletonMap("level", level)); final IllegalArgumentException e = expectThrows( @@ -81,7 +82,8 @@ public void testGetIndices() { 0, 0, 0, - null + null, + ClusterState.EMPTY_STATE ); Map indexStats = indicesStatsResponse.getIndices(); diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsTests.java b/server/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsTests.java index 7ca740fac29a3..1ba21924a36c2 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/indices/stats/IndicesStatsTests.java @@ -9,9 +9,14 @@ package org.elasticsearch.action.admin.indices.stats; import org.elasticsearch.action.ActionFuture; +import org.elasticsearch.action.admin.indices.close.CloseIndexRequest; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.support.DefaultShardOperationFailedException; +import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.action.support.WriteRequest.RefreshPolicy; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.health.ClusterHealthStatus; +import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.IndexModule; @@ -23,6 +28,7 @@ import org.elasticsearch.xcontent.XContentFactory; import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; @@ -143,6 +149,41 @@ public void testUuidOnRootStatsIndices() { assertEquals(uuid, rsp.getIndex("test").getUuid()); } + public void testIndexHealth() { + String greenIndex = "green-index"; + createIndex(greenIndex); + String yellowIndex = "yellow-index"; + createIndex(yellowIndex, Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1).build()); + + IndicesStatsResponse rsp = client().admin().indices().prepareStats().all().get(); + + IndexStats greenIndexStats = rsp.getIndex(greenIndex); + assertEquals(ClusterHealthStatus.GREEN, greenIndexStats.getHealth()); + + IndexStats yellowIndexStats = rsp.getIndex(yellowIndex); + assertEquals(ClusterHealthStatus.YELLOW, yellowIndexStats.getHealth()); + } + + public void testIndexState() throws ExecutionException, InterruptedException { + String openIndex = "open-index"; + createIndex(openIndex); + String closeIndex = "close-index"; + createIndex(closeIndex); + + client().admin().indices().close(new CloseIndexRequest(closeIndex)).get(); + + IndicesStatsResponse rsp = client().admin() + .indices() + .prepareStats() + .setIndicesOptions(IndicesOptions.STRICT_EXPAND_OPEN_CLOSED) + .get(); + IndexStats openIndexStats = rsp.getIndex(openIndex); + assertEquals(IndexMetadata.State.OPEN, openIndexStats.getState()); + + IndexStats closeIndexStats = rsp.getIndex(closeIndex); + assertEquals(IndexMetadata.State.CLOSE, closeIndexStats.getState()); + } + /** * Gives access to package private IndicesStatsResponse constructor for test purpose. **/ @@ -151,8 +192,9 @@ public static IndicesStatsResponse newIndicesStatsResponse( int totalShards, int successfulShards, int failedShards, - List shardFailures + List shardFailures, + ClusterState clusterState ) { - return new IndicesStatsResponse(shards, totalShards, successfulShards, failedShards, shardFailures); + return new IndicesStatsResponse(shards, totalShards, successfulShards, failedShards, shardFailures, clusterState); } } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/WaitForNoFollowersStepTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/WaitForNoFollowersStepTests.java index 7ee0f0060c2fa..063f57c7e3d9d 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/WaitForNoFollowersStepTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/WaitForNoFollowersStepTests.java @@ -14,6 +14,7 @@ import org.elasticsearch.action.admin.indices.stats.IndicesStatsRequest; import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse; import org.elasticsearch.action.admin.indices.stats.ShardStats; +import org.elasticsearch.cluster.health.ClusterHealthStatus; import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.common.Strings; @@ -147,7 +148,7 @@ public void testNoShardStats() { ShardStats sStats = new ShardStats(null, mockShardPath(), null, null, null, null); ShardStats[] shardStats = new ShardStats[1]; shardStats[0] = sStats; - mockIndexStatsCall(indexName, new IndexStats(indexName, "uuid", shardStats)); + mockIndexStatsCall(indexName, new IndexStats(indexName, "uuid", ClusterHealthStatus.GREEN, IndexMetadata.State.OPEN, shardStats)); final SetOnce conditionMetHolder = new SetOnce<>(); final SetOnce stepInfoHolder = new SetOnce<>(); @@ -233,7 +234,7 @@ private IndexStats randomIndexStats(boolean isLeaderIndex, int numOfShards) { for (int i = 0; i < numOfShards; i++) { shardStats[i] = randomShardStats(isLeaderIndex); } - return new IndexStats(randomAlphaOfLength(5), randomAlphaOfLength(10), shardStats); + return new IndexStats(randomAlphaOfLength(5), randomAlphaOfLength(10), null, null, shardStats); } private ShardStats randomShardStats(boolean isLeaderIndex) { diff --git a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/indices/IndicesStatsMonitoringDocTests.java b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/indices/IndicesStatsMonitoringDocTests.java index 8ec4bd8141756..1c7ed14ba799e 100644 --- a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/indices/IndicesStatsMonitoringDocTests.java +++ b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/indices/IndicesStatsMonitoringDocTests.java @@ -51,6 +51,8 @@ public void setUp() throws Exception { new IndexStats( "index-0", "dcvO5uZATE-EhIKc3tk9Bg", + null, + null, new ShardStats[] { // Primaries new ShardStats(mockShardRouting(true), mockShardPath(), mockCommonStats(), null, null, null),