Move XContent Rendering and Cluster Health Status calculations to ClusterHealthResponse

This commit is contained in:
Boaz Leskes 2013-12-18 15:08:23 +01:00
parent d5192ecd31
commit 17e7d01753
4 changed files with 129 additions and 144 deletions

View File

@ -22,12 +22,21 @@ package org.elasticsearch.action.admin.cluster.health;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.routing.IndexRoutingTable;
import org.elasticsearch.cluster.routing.RoutingTableValidation;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentBuilderString;
import org.elasticsearch.rest.RestStatus;
import java.io.IOException; import java.io.IOException;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Lists.newArrayList;
@ -36,7 +45,7 @@ import static org.elasticsearch.action.admin.cluster.health.ClusterIndexHealth.r
/** /**
* *
*/ */
public class ClusterHealthResponse extends ActionResponse implements Iterable<ClusterIndexHealth> { public class ClusterHealthResponse extends ActionResponse implements Iterable<ClusterIndexHealth>, ToXContent {
private String clusterName; private String clusterName;
int numberOfNodes = 0; int numberOfNodes = 0;
@ -59,6 +68,48 @@ public class ClusterHealthResponse extends ActionResponse implements Iterable<Cl
this.validationFailures = validationFailures; this.validationFailures = validationFailures;
} }
public ClusterHealthResponse(String clusterName, String[] concreteIndices, ClusterState clusterState) {
this.clusterName = clusterName;
RoutingTableValidation validation = clusterState.routingTable().validate(clusterState.metaData());
validationFailures = validation.failures();
numberOfNodes = clusterState.nodes().size();
numberOfDataNodes = clusterState.nodes().dataNodes().size();
for (String index : concreteIndices) {
IndexRoutingTable indexRoutingTable = clusterState.routingTable().index(index);
IndexMetaData indexMetaData = clusterState.metaData().index(index);
if (indexRoutingTable == null) {
continue;
}
ClusterIndexHealth indexHealth = new ClusterIndexHealth(indexMetaData, indexRoutingTable);
indices.put(indexHealth.getIndex(), indexHealth);
}
status = ClusterHealthStatus.GREEN;
for (ClusterIndexHealth indexHealth : indices.values()) {
activePrimaryShards += indexHealth.activePrimaryShards;
activeShards += indexHealth.activeShards;
relocatingShards += indexHealth.relocatingShards;
initializingShards += indexHealth.initializingShards;
unassignedShards += indexHealth.unassignedShards;
if (indexHealth.getStatus() == ClusterHealthStatus.RED) {
status = ClusterHealthStatus.RED;
break;
} else if (indexHealth.getStatus() == ClusterHealthStatus.YELLOW && status != ClusterHealthStatus.RED) {
status = ClusterHealthStatus.YELLOW;
}
}
if (!validationFailures.isEmpty()) {
status = ClusterHealthStatus.RED;
} else if (clusterState.blocks().hasGlobalBlock(RestStatus.SERVICE_UNAVAILABLE)) {
status = ClusterHealthStatus.RED;
}
}
public String getClusterName() { public String getClusterName() {
return clusterName; return clusterName;
} }
@ -201,4 +252,73 @@ public class ClusterHealthResponse extends ActionResponse implements Iterable<Cl
} }
return builder.toString(); return builder.toString();
} }
static final class Fields {
static final XContentBuilderString CLUSTER_NAME = new XContentBuilderString("cluster_name");
static final XContentBuilderString STATUS = new XContentBuilderString("status");
static final XContentBuilderString TIMED_OUT = new XContentBuilderString("timed_out");
static final XContentBuilderString NUMBER_OF_NODES = new XContentBuilderString("number_of_nodes");
static final XContentBuilderString NUMBER_OF_DATA_NODES = new XContentBuilderString("number_of_data_nodes");
static final XContentBuilderString ACTIVE_PRIMARY_SHARDS = new XContentBuilderString("active_primary_shards");
static final XContentBuilderString ACTIVE_SHARDS = new XContentBuilderString("active_shards");
static final XContentBuilderString RELOCATING_SHARDS = new XContentBuilderString("relocating_shards");
static final XContentBuilderString INITIALIZING_SHARDS = new XContentBuilderString("initializing_shards");
static final XContentBuilderString UNASSIGNED_SHARDS = new XContentBuilderString("unassigned_shards");
static final XContentBuilderString VALIDATION_FAILURES = new XContentBuilderString("validation_failures");
static final XContentBuilderString INDICES = new XContentBuilderString("indices");
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.field(Fields.CLUSTER_NAME, getClusterName());
builder.field(Fields.STATUS, getStatus().name().toLowerCase(Locale.ROOT));
builder.field(Fields.TIMED_OUT, isTimedOut());
builder.field(Fields.NUMBER_OF_NODES, getNumberOfNodes());
builder.field(Fields.NUMBER_OF_DATA_NODES, getNumberOfDataNodes());
builder.field(Fields.ACTIVE_PRIMARY_SHARDS, getActivePrimaryShards());
builder.field(Fields.ACTIVE_SHARDS, getActiveShards());
builder.field(Fields.RELOCATING_SHARDS, getRelocatingShards());
builder.field(Fields.INITIALIZING_SHARDS, getInitializingShards());
builder.field(Fields.UNASSIGNED_SHARDS, getUnassignedShards());
String level = params.param("level", "cluster");
boolean outputIndices = "indices".equals(level) || "shards".equals(level);
if (!getValidationFailures().isEmpty()) {
builder.startArray(Fields.VALIDATION_FAILURES);
for (String validationFailure : getValidationFailures()) {
builder.value(validationFailure);
}
// if we don't print index level information, still print the index validation failures
// so we know why the status is red
if (!outputIndices) {
for (ClusterIndexHealth indexHealth : indices.values()) {
builder.startObject(indexHealth.getIndex());
if (!indexHealth.getValidationFailures().isEmpty()) {
builder.startArray(Fields.VALIDATION_FAILURES);
for (String validationFailure : indexHealth.getValidationFailures()) {
builder.value(validationFailure);
}
builder.endArray();
}
builder.endObject();
}
}
builder.endArray();
}
if (outputIndices) {
builder.startObject(Fields.INDICES);
for (ClusterIndexHealth indexHealth : indices.values()) {
builder.startObject(indexHealth.getIndex(), XContentBuilder.FieldCaseConversion.NONE);
indexHealth.toXContent(builder, params);
builder.endObject();
}
builder.endObject();
}
return builder;
}
} }

View File

@ -70,13 +70,6 @@ public class ClusterIndexHealth implements Iterable<ClusterShardHealth>, Streama
private ClusterIndexHealth() { private ClusterIndexHealth() {
} }
public ClusterIndexHealth(String index, int numberOfShards, int numberOfReplicas, List<String> validationFailures) {
this.index = index;
this.numberOfShards = numberOfShards;
this.numberOfReplicas = numberOfReplicas;
this.validationFailures = validationFailures;
}
public ClusterIndexHealth(IndexMetaData indexMetaData, IndexRoutingTable indexRoutingTable) { public ClusterIndexHealth(IndexMetaData indexMetaData, IndexRoutingTable indexRoutingTable) {
this.index = indexMetaData.index(); this.index = indexMetaData.index();
this.numberOfShards = indexMetaData.getNumberOfShards(); this.numberOfShards = indexMetaData.getNumberOfShards();
@ -277,7 +270,7 @@ public class ClusterIndexHealth implements Iterable<ClusterShardHealth>, Streama
builder.endArray(); builder.endArray();
} }
if (params.paramAsBoolean("output_shards", false)) { if ("shards".equals(params.param("level", "indices"))) {
builder.startObject(Fields.SHARDS); builder.startObject(Fields.SHARDS);
for (ClusterShardHealth shardHealth : shards.values()) { for (ClusterShardHealth shardHealth : shards.values()) {

View File

@ -26,13 +26,10 @@ import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterService; import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.ProcessedClusterStateUpdateTask; import org.elasticsearch.cluster.ProcessedClusterStateUpdateTask;
import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.Strings;
import org.elasticsearch.cluster.routing.IndexRoutingTable;
import org.elasticsearch.cluster.routing.RoutingTableValidation;
import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.indices.IndexMissingException; import org.elasticsearch.indices.IndexMissingException;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService; import org.elasticsearch.transport.TransportService;
@ -226,54 +223,16 @@ public class TransportClusterHealthAction extends TransportMasterNodeOperationAc
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("Calculating health based on state version [{}]", clusterState.version()); logger.trace("Calculating health based on state version [{}]", clusterState.version());
} }
RoutingTableValidation validation = clusterState.routingTable().validate(clusterState.metaData());
ClusterHealthResponse response = new ClusterHealthResponse(clusterName.value(), validation.failures());
response.numberOfNodes = clusterState.nodes().size();
response.numberOfDataNodes = clusterState.nodes().dataNodes().size();
String[] concreteIndices; String[] concreteIndices;
try { try {
concreteIndices = clusterState.metaData().concreteIndicesIgnoreMissing(request.indices()); concreteIndices = clusterState.metaData().concreteIndicesIgnoreMissing(request.indices());
} catch (IndexMissingException e) { } catch (IndexMissingException e) {
// one of the specified indices is not there - treat it as RED.
ClusterHealthResponse response = new ClusterHealthResponse(clusterName.value(), Strings.EMPTY_ARRAY, clusterState);
response.status = ClusterHealthStatus.RED;
return response; return response;
} }
for (String index : concreteIndices) {
IndexRoutingTable indexRoutingTable = clusterState.routingTable().index(index);
IndexMetaData indexMetaData = clusterState.metaData().index(index);
if (indexRoutingTable == null) {
continue;
}
ClusterIndexHealth indexHealth = new ClusterIndexHealth(indexMetaData, indexRoutingTable); return new ClusterHealthResponse(clusterName.value(), concreteIndices, clusterState);
response.indices.put(indexHealth.getIndex(), indexHealth);
}
for (ClusterIndexHealth indexHealth : response) {
response.activePrimaryShards += indexHealth.activePrimaryShards;
response.activeShards += indexHealth.activeShards;
response.relocatingShards += indexHealth.relocatingShards;
response.initializingShards += indexHealth.initializingShards;
response.unassignedShards += indexHealth.unassignedShards;
}
response.status = ClusterHealthStatus.GREEN;
if (!response.getValidationFailures().isEmpty()) {
response.status = ClusterHealthStatus.RED;
} else if (clusterState.blocks().hasGlobalBlock(RestStatus.SERVICE_UNAVAILABLE)) {
response.status = ClusterHealthStatus.RED;
} else {
for (ClusterIndexHealth indexHealth : response) {
if (indexHealth.getStatus() == ClusterHealthStatus.RED) {
response.status = ClusterHealthStatus.RED;
break;
}
if (indexHealth.getStatus() == ClusterHealthStatus.YELLOW) {
response.status = ClusterHealthStatus.YELLOW;
}
}
}
return response;
} }
} }

View File

@ -19,25 +19,20 @@
package org.elasticsearch.rest.action.admin.cluster.health; package org.elasticsearch.rest.action.admin.cluster.health;
import com.google.common.collect.ImmutableMap;
import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest; import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus; import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus;
import org.elasticsearch.action.admin.cluster.health.ClusterIndexHealth;
import org.elasticsearch.client.Client; import org.elasticsearch.client.Client;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentBuilderString;
import org.elasticsearch.rest.*; import org.elasticsearch.rest.*;
import org.elasticsearch.rest.action.support.RestXContentBuilder; import org.elasticsearch.rest.action.support.RestXContentBuilder;
import java.io.IOException; import java.io.IOException;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import static org.elasticsearch.client.Requests.clusterHealthRequest; import static org.elasticsearch.client.Requests.clusterHealthRequest;
import static org.elasticsearch.rest.RestStatus.PRECONDITION_FAILED; import static org.elasticsearch.rest.RestStatus.PRECONDITION_FAILED;
@ -47,8 +42,6 @@ import static org.elasticsearch.rest.RestStatus.PRECONDITION_FAILED;
*/ */
public class RestClusterHealthAction extends BaseRestHandler { public class RestClusterHealthAction extends BaseRestHandler {
private static final Map<String, String> SHARD_LEVEL_PARAMS = ImmutableMap.of("output_shards", "true");
@Inject @Inject
public RestClusterHealthAction(Settings settings, Client client, RestController controller) { public RestClusterHealthAction(Settings settings, Client client, RestController controller) {
super(settings, client); super(settings, client);
@ -62,7 +55,6 @@ public class RestClusterHealthAction extends BaseRestHandler {
ClusterHealthRequest clusterHealthRequest = clusterHealthRequest(Strings.splitStringByCommaToArray(request.param("index"))); ClusterHealthRequest clusterHealthRequest = clusterHealthRequest(Strings.splitStringByCommaToArray(request.param("index")));
clusterHealthRequest.local(request.paramAsBoolean("local", clusterHealthRequest.local())); clusterHealthRequest.local(request.paramAsBoolean("local", clusterHealthRequest.local()));
clusterHealthRequest.listenerThreaded(false); clusterHealthRequest.listenerThreaded(false);
int level = 0;
try { try {
clusterHealthRequest.masterNodeTimeout(request.paramAsTime("master_timeout", clusterHealthRequest.masterNodeTimeout())); clusterHealthRequest.masterNodeTimeout(request.paramAsTime("master_timeout", clusterHealthRequest.masterNodeTimeout()));
clusterHealthRequest.timeout(request.paramAsTime("timeout", clusterHealthRequest.timeout())); clusterHealthRequest.timeout(request.paramAsTime("timeout", clusterHealthRequest.timeout()));
@ -73,16 +65,6 @@ public class RestClusterHealthAction extends BaseRestHandler {
clusterHealthRequest.waitForRelocatingShards(request.paramAsInt("wait_for_relocating_shards", clusterHealthRequest.waitForRelocatingShards())); clusterHealthRequest.waitForRelocatingShards(request.paramAsInt("wait_for_relocating_shards", clusterHealthRequest.waitForRelocatingShards()));
clusterHealthRequest.waitForActiveShards(request.paramAsInt("wait_for_active_shards", clusterHealthRequest.waitForActiveShards())); clusterHealthRequest.waitForActiveShards(request.paramAsInt("wait_for_active_shards", clusterHealthRequest.waitForActiveShards()));
clusterHealthRequest.waitForNodes(request.param("wait_for_nodes", clusterHealthRequest.waitForNodes())); clusterHealthRequest.waitForNodes(request.param("wait_for_nodes", clusterHealthRequest.waitForNodes()));
String sLevel = request.param("level");
if (sLevel != null) {
if ("cluster".equals(sLevel)) {
level = 0;
} else if ("indices".equals(sLevel)) {
level = 1;
} else if ("shards".equals(sLevel)) {
level = 2;
}
}
} catch (Exception e) { } catch (Exception e) {
try { try {
XContentBuilder builder = RestXContentBuilder.restContentBuilder(request); XContentBuilder builder = RestXContentBuilder.restContentBuilder(request);
@ -92,7 +74,7 @@ public class RestClusterHealthAction extends BaseRestHandler {
} }
return; return;
} }
final int fLevel = level;
client.admin().cluster().health(clusterHealthRequest, new ActionListener<ClusterHealthResponse>() { client.admin().cluster().health(clusterHealthRequest, new ActionListener<ClusterHealthResponse>() {
@Override @Override
public void onResponse(ClusterHealthResponse response) { public void onResponse(ClusterHealthResponse response) {
@ -105,61 +87,7 @@ public class RestClusterHealthAction extends BaseRestHandler {
//} //}
XContentBuilder builder = RestXContentBuilder.restContentBuilder(request); XContentBuilder builder = RestXContentBuilder.restContentBuilder(request);
builder.startObject(); builder.startObject();
response.toXContent(builder, request);
builder.field(Fields.CLUSTER_NAME, response.getClusterName());
builder.field(Fields.STATUS, response.getStatus().name().toLowerCase(Locale.ROOT));
builder.field(Fields.TIMED_OUT, response.isTimedOut());
builder.field(Fields.NUMBER_OF_NODES, response.getNumberOfNodes());
builder.field(Fields.NUMBER_OF_DATA_NODES, response.getNumberOfDataNodes());
builder.field(Fields.ACTIVE_PRIMARY_SHARDS, response.getActivePrimaryShards());
builder.field(Fields.ACTIVE_SHARDS, response.getActiveShards());
builder.field(Fields.RELOCATING_SHARDS, response.getRelocatingShards());
builder.field(Fields.INITIALIZING_SHARDS, response.getInitializingShards());
builder.field(Fields.UNASSIGNED_SHARDS, response.getUnassignedShards());
if (!response.getValidationFailures().isEmpty()) {
builder.startArray(Fields.VALIDATION_FAILURES);
for (String validationFailure : response.getValidationFailures()) {
builder.value(validationFailure);
}
// if we don't print index level information, still print the index validation failures
// so we know why the status is red
if (fLevel == 0) {
for (ClusterIndexHealth indexHealth : response) {
builder.startObject(indexHealth.getIndex());
if (!indexHealth.getValidationFailures().isEmpty()) {
builder.startArray(Fields.VALIDATION_FAILURES);
for (String validationFailure : indexHealth.getValidationFailures()) {
builder.value(validationFailure);
}
builder.endArray();
}
builder.endObject();
}
}
builder.endArray();
}
if (fLevel > 0) {
builder.startObject(Fields.INDICES);
for (ClusterIndexHealth indexHealth : response) {
builder.startObject(indexHealth.getIndex(), XContentBuilder.FieldCaseConversion.NONE);
ToXContent.Params params;
if (fLevel > 1) {
params = new ToXContent.DelegatingMapParams(SHARD_LEVEL_PARAMS, request);
} else {
params = request;
}
indexHealth.toXContent(builder, params);
builder.endObject();
}
builder.endObject();
}
builder.endObject(); builder.endObject();
channel.sendResponse(new XContentRestResponse(request, status, builder)); channel.sendResponse(new XContentRestResponse(request, status, builder));
@ -178,19 +106,4 @@ public class RestClusterHealthAction extends BaseRestHandler {
} }
}); });
} }
static final class Fields {
static final XContentBuilderString CLUSTER_NAME = new XContentBuilderString("cluster_name");
static final XContentBuilderString STATUS = new XContentBuilderString("status");
static final XContentBuilderString TIMED_OUT = new XContentBuilderString("timed_out");
static final XContentBuilderString NUMBER_OF_NODES = new XContentBuilderString("number_of_nodes");
static final XContentBuilderString NUMBER_OF_DATA_NODES = new XContentBuilderString("number_of_data_nodes");
static final XContentBuilderString ACTIVE_PRIMARY_SHARDS = new XContentBuilderString("active_primary_shards");
static final XContentBuilderString ACTIVE_SHARDS = new XContentBuilderString("active_shards");
static final XContentBuilderString RELOCATING_SHARDS = new XContentBuilderString("relocating_shards");
static final XContentBuilderString INITIALIZING_SHARDS = new XContentBuilderString("initializing_shards");
static final XContentBuilderString UNASSIGNED_SHARDS = new XContentBuilderString("unassigned_shards");
static final XContentBuilderString VALIDATION_FAILURES = new XContentBuilderString("validation_failures");
static final XContentBuilderString INDICES = new XContentBuilderString("indices");
}
} }