From 36d1d22272629ddc9387a9341da45ea5a414523f Mon Sep 17 00:00:00 2001 From: Boaz Leskes Date: Wed, 18 Dec 2013 11:51:28 +0100 Subject: [PATCH] Added cluster health status to the Cluster Stats API Relates to #4460 --- docs/reference/cluster/stats.asciidoc | 1 + .../stats/ClusterStatsNodeResponse.java | 24 ++++++++++++++- .../cluster/stats/ClusterStatsResponse.java | 30 ++++++++++++++++++- .../stats/TransportClusterStatsAction.java | 30 ++++++++++++++++++- .../cluster/stats/ClusterStatsTests.java | 11 ++++++- 5 files changed, 92 insertions(+), 4 deletions(-) diff --git a/docs/reference/cluster/stats.asciidoc b/docs/reference/cluster/stats.asciidoc index 7ead927a62aee..e6ed12f6719a5 100644 --- a/docs/reference/cluster/stats.asciidoc +++ b/docs/reference/cluster/stats.asciidoc @@ -18,6 +18,7 @@ Will return, for example: -------------------------------------------------- { "cluster_name": "elasticsearch", + "status": "green", "indices": { "count": 3, "shards": { diff --git a/src/main/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsNodeResponse.java b/src/main/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsNodeResponse.java index 597edbe986ecb..644c48e5e1ccf 100644 --- a/src/main/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsNodeResponse.java +++ b/src/main/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsNodeResponse.java @@ -19,11 +19,13 @@ package org.elasticsearch.action.admin.cluster.stats; +import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus; import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; import org.elasticsearch.action.admin.cluster.node.stats.NodeStats; import org.elasticsearch.action.admin.indices.stats.ShardStats; import org.elasticsearch.action.support.nodes.NodeOperationResponse; import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.common.Nullable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -34,15 +36,17 @@ public class ClusterStatsNodeResponse extends NodeOperationResponse { private NodeInfo nodeInfo; private NodeStats nodeStats; private ShardStats[] shardsStats; + private ClusterHealthStatus clusterStatus; ClusterStatsNodeResponse() { } - public ClusterStatsNodeResponse(DiscoveryNode node, NodeInfo nodeInfo, NodeStats nodeStats, ShardStats[] shardsStats) { + public ClusterStatsNodeResponse(DiscoveryNode node, @Nullable ClusterHealthStatus clusterStatus, NodeInfo nodeInfo, NodeStats nodeStats, ShardStats[] shardsStats) { super(node); this.nodeInfo = nodeInfo; this.nodeStats = nodeStats; this.shardsStats = shardsStats; + this.clusterStatus = clusterStatus; } public NodeInfo nodeInfo() { @@ -53,6 +57,14 @@ public NodeStats nodeStats() { return this.nodeStats; } + /** + * Cluster Health Status, only populated on master nodes. + */ + @Nullable + public ClusterHealthStatus clusterStatus() { + return clusterStatus; + } + public ShardStats[] shardsStats() { return this.shardsStats; } @@ -66,6 +78,10 @@ public static ClusterStatsNodeResponse readNodeResponse(StreamInput in) throws I @Override public void readFrom(StreamInput in) throws IOException { super.readFrom(in); + clusterStatus = null; + if (in.readBoolean()) { + clusterStatus = ClusterHealthStatus.fromValue(in.readByte()); + } this.nodeInfo = NodeInfo.readNodeInfo(in); this.nodeStats = NodeStats.readNodeStats(in); int size = in.readVInt(); @@ -78,6 +94,12 @@ public void readFrom(StreamInput in) throws IOException { @Override public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); + if (clusterStatus == null) { + out.writeBoolean(false); + } else { + out.writeBoolean(true); + out.writeByte(clusterStatus.value()); + } nodeInfo.writeTo(out); nodeStats.writeTo(out); out.writeVInt(shardsStats.length); diff --git a/src/main/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsResponse.java b/src/main/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsResponse.java index be74ffc48ec9a..4a975aea445a2 100644 --- a/src/main/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsResponse.java +++ b/src/main/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsResponse.java @@ -19,6 +19,7 @@ package org.elasticsearch.action.admin.cluster.stats; +import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus; import org.elasticsearch.action.support.nodes.NodesOperationResponse; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.common.io.stream.StreamInput; @@ -30,6 +31,7 @@ import java.io.IOException; import java.util.Iterator; +import java.util.Locale; import java.util.Map; /** @@ -40,6 +42,7 @@ public class ClusterStatsResponse extends NodesOperationResponse iterator() { public void readFrom(StreamInput in) throws IOException { super.readFrom(in); timestamp = in.readVLong(); + status = null; + if (in.readBoolean()) { + // it may be that the master switched on us while doing the operation. In this case the status may be null. + status = ClusterHealthStatus.fromValue(in.readByte()); + } clusterUUID = in.readString(); nodesStats = ClusterStatsNodes.readNodeStats(in); indicesStats = ClusterStatsIndices.readIndicesStats(in); @@ -99,6 +118,12 @@ public void readFrom(StreamInput in) throws IOException { public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeVLong(timestamp); + if (status == null) { + out.writeBoolean(false); + } else { + out.writeBoolean(true); + out.writeByte(status.value()); + } out.writeString(clusterUUID); nodesStats.writeTo(out); indicesStats.writeTo(out); @@ -109,6 +134,7 @@ static final class Fields { static final XContentBuilderString INDICES = new XContentBuilderString("indices"); static final XContentBuilderString UUID = new XContentBuilderString("uuid"); static final XContentBuilderString CLUSTER_NAME = new XContentBuilderString("cluster_name"); + static final XContentBuilderString STATUS = new XContentBuilderString("status"); } @Override @@ -118,7 +144,9 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws if (params.paramAsBoolean("output_uuid", false)) { builder.field(Fields.UUID, clusterUUID); } - + if (status != null) { + builder.field(Fields.STATUS, status.name().toLowerCase(Locale.ROOT)); + } builder.startObject(Fields.INDICES); indicesStats.toXContent(builder, params); builder.endObject(); diff --git a/src/main/java/org/elasticsearch/action/admin/cluster/stats/TransportClusterStatsAction.java b/src/main/java/org/elasticsearch/action/admin/cluster/stats/TransportClusterStatsAction.java index e2fea307c2c2d..4a449db523942 100644 --- a/src/main/java/org/elasticsearch/action/admin/cluster/stats/TransportClusterStatsAction.java +++ b/src/main/java/org/elasticsearch/action/admin/cluster/stats/TransportClusterStatsAction.java @@ -20,6 +20,8 @@ package org.elasticsearch.action.admin.cluster.stats; import org.elasticsearch.ElasticSearchException; +import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus; +import org.elasticsearch.action.admin.cluster.health.ClusterIndexHealth; import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; import org.elasticsearch.action.admin.cluster.node.stats.NodeStats; import org.elasticsearch.action.admin.indices.stats.CommonStatsFlags; @@ -28,6 +30,8 @@ import org.elasticsearch.action.support.nodes.TransportNodesOperationAction; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterService; +import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.cluster.routing.IndexRoutingTable; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -129,7 +133,31 @@ protected ClusterStatsNodeResponse nodeOperation(ClusterStatsNodeRequest nodeReq } - return new ClusterStatsNodeResponse(nodeInfo.getNode(), nodeInfo, nodeStats, shardsStats.toArray(new ShardStats[shardsStats.size()])); + ClusterHealthStatus clusterStatus = null; + if (clusterService.state().nodes().localNodeMaster()) { + // populate cluster status + clusterStatus = ClusterHealthStatus.GREEN; + for (IndexRoutingTable indexRoutingTable : clusterService.state().routingTable()) { + IndexMetaData indexMetaData = clusterService.state().metaData().index(indexRoutingTable.index()); + if (indexRoutingTable == null) { + continue; + } + + ClusterIndexHealth indexHealth = new ClusterIndexHealth(indexMetaData, indexRoutingTable); + switch (indexHealth.getStatus()) { + case RED: + clusterStatus = ClusterHealthStatus.RED; + break; + case YELLOW: + if (clusterStatus != ClusterHealthStatus.RED) { + clusterStatus = ClusterHealthStatus.YELLOW; + } + break; + } + } + } + + return new ClusterStatsNodeResponse(nodeInfo.getNode(), clusterStatus, nodeInfo, nodeStats, shardsStats.toArray(new ShardStats[shardsStats.size()])); } diff --git a/src/test/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsTests.java b/src/test/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsTests.java index 6d5d5d51a7aef..325d5b26ad2eb 100644 --- a/src/test/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsTests.java +++ b/src/test/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsTests.java @@ -20,6 +20,7 @@ import org.elasticsearch.Version; +import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus; import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.monitor.sigar.SigarService; import org.elasticsearch.test.ElasticsearchIntegrationTest; @@ -68,9 +69,15 @@ private void assertShardStats(ClusterStatsIndices.ShardStats stats, int indices, @Test public void testIndicesShardStats() { cluster().startNode(); + + ClusterStatsResponse response = client().admin().cluster().prepareClusterStats().get(); + assertThat(response.getStatus(), Matchers.equalTo(ClusterHealthStatus.GREEN)); + + prepareCreate("test1").setSettings("number_of_shards", 2, "number_of_replicas", 1).get(); ensureYellow(); - ClusterStatsResponse response = client().admin().cluster().prepareClusterStats().get(); + response = client().admin().cluster().prepareClusterStats().get(); + assertThat(response.getStatus(), Matchers.equalTo(ClusterHealthStatus.YELLOW)); assertThat(response.indicesStats.getDocs().getCount(), Matchers.equalTo(0l)); assertThat(response.indicesStats.getIndexCount(), Matchers.equalTo(1)); assertShardStats(response.getIndicesStats().getShards(), 1, 2, 2, 0.0); @@ -81,12 +88,14 @@ public void testIndicesShardStats() { index("test1", "type", "1", "f", "f"); refresh(); // make the doc visible response = client().admin().cluster().prepareClusterStats().get(); + assertThat(response.getStatus(), Matchers.equalTo(ClusterHealthStatus.GREEN)); assertThat(response.indicesStats.getDocs().getCount(), Matchers.equalTo(1l)); assertShardStats(response.getIndicesStats().getShards(), 1, 4, 2, 1.0); prepareCreate("test2").setSettings("number_of_shards", 3, "number_of_replicas", 0).get(); ensureGreen(); response = client().admin().cluster().prepareClusterStats().get(); + assertThat(response.getStatus(), Matchers.equalTo(ClusterHealthStatus.GREEN)); assertThat(response.indicesStats.getIndexCount(), Matchers.equalTo(2)); assertShardStats(response.getIndicesStats().getShards(), 2, 7, 5, 2.0 / 5);