summaryrefslogtreecommitdiff
path: root/core/src/main/java/org/elasticsearch/action/admin/cluster
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main/java/org/elasticsearch/action/admin/cluster')
-rw-r--r--core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainAction.java48
-rw-r--r--core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainRequest.java195
-rw-r--r--core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainRequestBuilder.java66
-rw-r--r--core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainResponse.java61
-rw-r--r--core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplanation.java200
-rw-r--r--core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/TransportClusterAllocationExplainAction.java194
-rw-r--r--core/src/main/java/org/elasticsearch/action/admin/cluster/node/stats/NodeStats.java2
-rw-r--r--core/src/main/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsNodes.java14
8 files changed, 765 insertions, 15 deletions
diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainAction.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainAction.java
new file mode 100644
index 0000000000..d34ac63602
--- /dev/null
+++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainAction.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.action.admin.cluster.allocation;
+
+import org.elasticsearch.action.Action;
+import org.elasticsearch.client.ElasticsearchClient;
+
+/**
+ * Action for explaining shard allocation for a shard in the cluster
+ */
+public class ClusterAllocationExplainAction extends Action<ClusterAllocationExplainRequest,
+ ClusterAllocationExplainResponse,
+ ClusterAllocationExplainRequestBuilder> {
+
+ public static final ClusterAllocationExplainAction INSTANCE = new ClusterAllocationExplainAction();
+ public static final String NAME = "cluster:monitor/allocation/explain";
+
+ private ClusterAllocationExplainAction() {
+ super(NAME);
+ }
+
+ @Override
+ public ClusterAllocationExplainResponse newResponse() {
+ return new ClusterAllocationExplainResponse();
+ }
+
+ @Override
+ public ClusterAllocationExplainRequestBuilder newRequestBuilder(ElasticsearchClient client) {
+ return new ClusterAllocationExplainRequestBuilder(client, this);
+ }
+}
diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainRequest.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainRequest.java
new file mode 100644
index 0000000000..d14785127d
--- /dev/null
+++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainRequest.java
@@ -0,0 +1,195 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.action.admin.cluster.allocation;
+
+import org.elasticsearch.ElasticsearchParseException;
+import org.elasticsearch.action.ActionRequestValidationException;
+import org.elasticsearch.action.support.master.MasterNodeRequest;
+import org.elasticsearch.common.Nullable;
+import org.elasticsearch.common.Strings;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.io.stream.StreamOutput;
+import org.elasticsearch.common.xcontent.XContentParser;
+
+import java.io.IOException;
+import java.util.Objects;
+
+import static org.elasticsearch.action.ValidateActions.addValidationError;
+
+/**
+ * A request to explain the allocation of a shard in the cluster
+ */
+public class ClusterAllocationExplainRequest extends MasterNodeRequest<ClusterAllocationExplainRequest> {
+
+ private String index;
+ private Integer shard;
+ private Boolean primary;
+ private boolean includeYesDecisions = false;
+
+ /** Explain the first unassigned shard */
+ public ClusterAllocationExplainRequest() {
+ this.index = null;
+ this.shard = null;
+ this.primary = null;
+ }
+
+ /**
+ * Create a new allocation explain request. If {@code primary} is false, the first unassigned replica
+ * will be picked for explanation. If no replicas are unassigned, the first assigned replica will
+ * be explained.
+ */
+ public ClusterAllocationExplainRequest(String index, int shard, boolean primary) {
+ this.index = index;
+ this.shard = shard;
+ this.primary = primary;
+ }
+
+ @Override
+ public ActionRequestValidationException validate() {
+ ActionRequestValidationException validationException = null;
+ if (this.useAnyUnassignedShard() == false) {
+ if (this.index == null) {
+ validationException = addValidationError("index must be specified", validationException);
+ }
+ if (this.shard == null) {
+ validationException = addValidationError("shard must be specified", validationException);
+ }
+ if (this.primary == null) {
+ validationException = addValidationError("primary must be specified", validationException);
+ }
+ }
+ return validationException;
+ }
+
+ /**
+ * Returns {@code true} iff the first unassigned shard is to be used
+ */
+ public boolean useAnyUnassignedShard() {
+ return this.index == null && this.shard == null && this.primary == null;
+ }
+
+ public ClusterAllocationExplainRequest setIndex(String index) {
+ this.index = index;
+ return this;
+ }
+
+ @Nullable
+ public String getIndex() {
+ return this.index;
+ }
+
+ public ClusterAllocationExplainRequest setShard(Integer shard) {
+ this.shard = shard;
+ return this;
+ }
+
+ @Nullable
+ public int getShard() {
+ return this.shard;
+ }
+
+ public ClusterAllocationExplainRequest setPrimary(Boolean primary) {
+ this.primary = primary;
+ return this;
+ }
+
+ @Nullable
+ public boolean isPrimary() {
+ return this.primary;
+ }
+
+ public void includeYesDecisions(boolean includeYesDecisions) {
+ this.includeYesDecisions = includeYesDecisions;
+ }
+
+ /** Returns true if all decisions should be included. Otherwise only "NO" and "THROTTLE" decisions are returned */
+ public boolean includeYesDecisions() {
+ return this.includeYesDecisions;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("ClusterAllocationExplainRequest[");
+ if (this.useAnyUnassignedShard()) {
+ sb.append("useAnyUnassignedShard=true");
+ } else {
+ sb.append("index=").append(index);
+ sb.append(",shard=").append(shard);
+ sb.append(",primary?=").append(primary);
+ }
+ sb.append(",includeYesDecisions?=").append(includeYesDecisions);
+ return sb.toString();
+ }
+
+ public static ClusterAllocationExplainRequest parse(XContentParser parser) throws IOException {
+ String currentFieldName = null;
+ String index = null;
+ Integer shard = null;
+ Boolean primary = null;
+ XContentParser.Token token;
+ while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
+ if (token == XContentParser.Token.FIELD_NAME) {
+ currentFieldName = parser.currentName();
+ } else if (token.isValue()) {
+ if ("index".equals(currentFieldName)) {
+ index = parser.text();
+ } else if ("shard".equals(currentFieldName)) {
+ shard = parser.intValue();
+ } else if ("primary".equals(currentFieldName)) {
+ primary = parser.booleanValue();
+ } else {
+ throw new ElasticsearchParseException("unexpected field [" + currentFieldName + "] in allocation explain request");
+ }
+
+ } else if (token == XContentParser.Token.START_OBJECT) {
+ // the object was started
+ continue;
+ } else {
+ throw new ElasticsearchParseException("unexpected token [" + token + "] in allocation explain request");
+ }
+ }
+
+ if (index == null && shard == null && primary == null) {
+ // If it was an empty body, use the "any unassigned shard" request
+ return new ClusterAllocationExplainRequest();
+ } else if (index == null || shard == null || primary == null) {
+ throw new ElasticsearchParseException("'index', 'shard', and 'primary' must be specified in allocation explain request");
+ }
+ return new ClusterAllocationExplainRequest(index, shard, primary);
+ }
+
+ @Override
+ public void readFrom(StreamInput in) throws IOException {
+ super.readFrom(in);
+ this.index = in.readOptionalString();
+ this.shard = in.readOptionalVInt();
+ this.primary = in.readOptionalBoolean();
+ this.includeYesDecisions = in.readBoolean();
+ }
+
+ @Override
+ public void writeTo(StreamOutput out) throws IOException {
+ super.writeTo(out);
+ out.writeOptionalString(index);
+ out.writeOptionalVInt(shard);
+ out.writeOptionalBoolean(primary);
+ out.writeBoolean(includeYesDecisions);
+ }
+}
diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainRequestBuilder.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainRequestBuilder.java
new file mode 100644
index 0000000000..1a1950c7f1
--- /dev/null
+++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainRequestBuilder.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.action.admin.cluster.allocation;
+
+import org.elasticsearch.action.support.IndicesOptions;
+import org.elasticsearch.action.support.master.MasterNodeOperationRequestBuilder;
+import org.elasticsearch.client.ElasticsearchClient;
+
+/**
+ * Builder for requests to explain the allocation of a shard in the cluster
+ */
+public class ClusterAllocationExplainRequestBuilder
+ extends MasterNodeOperationRequestBuilder<ClusterAllocationExplainRequest,
+ ClusterAllocationExplainResponse,
+ ClusterAllocationExplainRequestBuilder> {
+
+ public ClusterAllocationExplainRequestBuilder(ElasticsearchClient client, ClusterAllocationExplainAction action) {
+ super(client, action, new ClusterAllocationExplainRequest());
+ }
+
+ /** The index name to use when finding the shard to explain */
+ public ClusterAllocationExplainRequestBuilder setIndex(String index) {
+ request.setIndex(index);
+ return this;
+ }
+
+ /** The shard number to use when finding the shard to explain */
+ public ClusterAllocationExplainRequestBuilder setShard(int shard) {
+ request.setShard(shard);
+ return this;
+ }
+
+ /** Whether the primary or replica should be explained */
+ public ClusterAllocationExplainRequestBuilder setPrimary(boolean primary) {
+ request.setPrimary(primary);
+ return this;
+ }
+
+ /**
+ * Signal that the first unassigned shard should be used
+ */
+ public ClusterAllocationExplainRequestBuilder useAnyUnassignedShard() {
+ request.setIndex(null);
+ request.setShard(null);
+ request.setPrimary(null);
+ return this;
+ }
+
+}
diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainResponse.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainResponse.java
new file mode 100644
index 0000000000..cc586bd1a5
--- /dev/null
+++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainResponse.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.action.admin.cluster.allocation;
+
+import org.elasticsearch.action.ActionResponse;
+import org.elasticsearch.cluster.ClusterName;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.io.stream.StreamOutput;
+
+import java.io.IOException;
+
+/**
+ * Explanation response for a shard in the cluster
+ */
+public class ClusterAllocationExplainResponse extends ActionResponse {
+
+ private ClusterAllocationExplanation cae;
+
+ public ClusterAllocationExplainResponse() {
+ }
+
+ public ClusterAllocationExplainResponse(ClusterAllocationExplanation cae) {
+ this.cae = cae;
+ }
+
+ /**
+ * Return the explanation for shard allocation in the cluster
+ */
+ public ClusterAllocationExplanation getExplanation() {
+ return this.cae;
+ }
+
+ @Override
+ public void readFrom(StreamInput in) throws IOException {
+ super.readFrom(in);
+ this.cae = new ClusterAllocationExplanation(in);
+ }
+
+ @Override
+ public void writeTo(StreamOutput out) throws IOException {
+ super.writeTo(out);
+ cae.writeTo(out);
+ }
+}
diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplanation.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplanation.java
new file mode 100644
index 0000000000..6b4173734b
--- /dev/null
+++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplanation.java
@@ -0,0 +1,200 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.action.admin.cluster.allocation;
+
+import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
+import org.elasticsearch.cluster.node.DiscoveryNode;
+import org.elasticsearch.cluster.routing.UnassignedInfo;
+import org.elasticsearch.cluster.routing.allocation.decider.Decision;
+import org.elasticsearch.common.Nullable;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.io.stream.StreamOutput;
+import org.elasticsearch.common.io.stream.Writeable;
+import org.elasticsearch.common.xcontent.ToXContent;
+import org.elasticsearch.common.xcontent.ToXContent.Params;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.index.shard.ShardId;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A {@code ClusterAllocationExplanation} is an explanation of why a shard may or may not be allocated to nodes. It also includes weights
+ * for where the shard is likely to be assigned. It is an immutable class
+ */
+public final class ClusterAllocationExplanation implements ToXContent, Writeable<ClusterAllocationExplanation> {
+
+ private final ShardId shard;
+ private final boolean primary;
+ private final String assignedNodeId;
+ private final Map<DiscoveryNode, Decision> nodeToDecision;
+ private final Map<DiscoveryNode, Float> nodeWeights;
+ private final UnassignedInfo unassignedInfo;
+
+ public ClusterAllocationExplanation(StreamInput in) throws IOException {
+ this.shard = ShardId.readShardId(in);
+ this.primary = in.readBoolean();
+ this.assignedNodeId = in.readOptionalString();
+ this.unassignedInfo = in.readOptionalWriteable(UnassignedInfo::new);
+
+ Map<DiscoveryNode, Decision> ntd = null;
+ int size = in.readVInt();
+ ntd = new HashMap<>(size);
+ for (int i = 0; i < size; i++) {
+ DiscoveryNode dn = DiscoveryNode.readNode(in);
+ Decision decision = Decision.readFrom(in);
+ ntd.put(dn, decision);
+ }
+ this.nodeToDecision = ntd;
+
+ Map<DiscoveryNode, Float> ntw = null;
+ size = in.readVInt();
+ ntw = new HashMap<>(size);
+ for (int i = 0; i < size; i++) {
+ DiscoveryNode dn = DiscoveryNode.readNode(in);
+ float weight = in.readFloat();
+ ntw.put(dn, weight);
+ }
+ this.nodeWeights = ntw;
+ }
+
+ public ClusterAllocationExplanation(ShardId shard, boolean primary, @Nullable String assignedNodeId,
+ UnassignedInfo unassignedInfo, Map<DiscoveryNode, Decision> nodeToDecision,
+ Map<DiscoveryNode, Float> nodeWeights) {
+ this.shard = shard;
+ this.primary = primary;
+ this.assignedNodeId = assignedNodeId;
+ this.unassignedInfo = unassignedInfo;
+ this.nodeToDecision = nodeToDecision == null ? Collections.emptyMap() : nodeToDecision;
+ this.nodeWeights = nodeWeights == null ? Collections.emptyMap() : nodeWeights;
+ }
+
+ public ShardId getShard() {
+ return this.shard;
+ }
+
+ public boolean isPrimary() {
+ return this.primary;
+ }
+
+ /** Return turn if the shard is assigned to a node */
+ public boolean isAssigned() {
+ return this.assignedNodeId != null;
+ }
+
+ /** Return the assigned node id or null if not assigned */
+ @Nullable
+ public String getAssignedNodeId() {
+ return this.assignedNodeId;
+ }
+
+ /** Return the unassigned info for the shard or null if the shard is assigned */
+ @Nullable
+ public UnassignedInfo getUnassignedInfo() {
+ return this.unassignedInfo;
+ }
+
+ /** Return a map of node to decision for shard allocation */
+ public Map<DiscoveryNode, Decision> getNodeDecisions() {
+ return this.nodeToDecision;
+ }
+
+ /**
+ * Return a map of node to balancer "weight" for allocation. Higher weights mean the balancer wants to allocated the shard to that node
+ * more
+ */
+ public Map<DiscoveryNode, Float> getNodeWeights() {
+ return this.nodeWeights;
+ }
+
+ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
+ builder.startObject(); {
+ builder.startObject("shard"); {
+ builder.field("index", shard.getIndexName());
+ builder.field("index_uuid", shard.getIndex().getUUID());
+ builder.field("id", shard.getId());
+ builder.field("primary", primary);
+ }
+ builder.endObject(); // end shard
+ builder.field("assigned", this.assignedNodeId != null);
+ // If assigned, show the node id of the node it's assigned to
+ if (assignedNodeId != null) {
+ builder.field("assigned_node_id", this.assignedNodeId);
+ }
+ // If we have unassigned info, show that
+ if (unassignedInfo != null) {
+ unassignedInfo.toXContent(builder, params);
+ }
+ builder.startObject("nodes");
+ for (Map.Entry<DiscoveryNode, Float> entry : nodeWeights.entrySet()) {
+ DiscoveryNode node = entry.getKey();
+ builder.startObject(node.getId()); {
+ builder.field("node_name", node.getName());
+ builder.startObject("node_attributes"); {
+ for (ObjectObjectCursor<String, String> attrKV : node.attributes()) {
+ builder.field(attrKV.key, attrKV.value);
+ }
+ }
+ builder.endObject(); // end attributes
+ Decision d = nodeToDecision.get(node);
+ if (node.getId().equals(assignedNodeId)) {
+ builder.field("final_decision", "CURRENTLY_ASSIGNED");
+ } else {
+ builder.field("final_decision", d.type().toString());
+ }
+ builder.field("weight", entry.getValue());
+ d.toXContent(builder, params);
+ }
+ builder.endObject(); // end node <uuid>
+ }
+ builder.endObject(); // end nodes
+ }
+ builder.endObject(); // end wrapping object
+ return builder;
+ }
+
+ @Override
+ public ClusterAllocationExplanation readFrom(StreamInput in) throws IOException {
+ return new ClusterAllocationExplanation(in);
+ }
+
+ @Override
+ public void writeTo(StreamOutput out) throws IOException {
+ this.getShard().writeTo(out);
+ out.writeBoolean(this.isPrimary());
+ out.writeOptionalString(this.getAssignedNodeId());
+ out.writeOptionalWriteable(this.getUnassignedInfo());
+
+ Map<DiscoveryNode, Decision> ntd = this.getNodeDecisions();
+ out.writeVInt(ntd.size());
+ for (Map.Entry<DiscoveryNode, Decision> entry : ntd.entrySet()) {
+ entry.getKey().writeTo(out);
+ Decision.writeTo(entry.getValue(), out);
+ }
+ Map<DiscoveryNode, Float> ntw = this.getNodeWeights();
+ out.writeVInt(ntw.size());
+ for (Map.Entry<DiscoveryNode, Float> entry : ntw.entrySet()) {
+ entry.getKey().writeTo(out);
+ out.writeFloat(entry.getValue());
+ }
+ }
+}
diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/TransportClusterAllocationExplainAction.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/TransportClusterAllocationExplainAction.java
new file mode 100644
index 0000000000..b9b31634bb
--- /dev/null
+++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/allocation/TransportClusterAllocationExplainAction.java
@@ -0,0 +1,194 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.action.admin.cluster.allocation;
+
+import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
+import org.elasticsearch.ElasticsearchException;
+import org.elasticsearch.action.ActionListener;
+import org.elasticsearch.action.support.ActionFilters;
+import org.elasticsearch.action.support.master.TransportMasterNodeAction;
+import org.elasticsearch.cluster.ClusterInfoService;
+import org.elasticsearch.cluster.ClusterName;
+import org.elasticsearch.cluster.ClusterState;
+import org.elasticsearch.cluster.block.ClusterBlockException;
+import org.elasticsearch.cluster.block.ClusterBlockLevel;
+import org.elasticsearch.cluster.metadata.IndexMetaData;
+import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
+import org.elasticsearch.cluster.metadata.MetaData;
+import org.elasticsearch.cluster.metadata.MetaData.Custom;
+import org.elasticsearch.cluster.node.DiscoveryNode;
+import org.elasticsearch.cluster.routing.RoutingNode;
+import org.elasticsearch.cluster.routing.RoutingNodes;
+import org.elasticsearch.cluster.routing.RoutingNodes.RoutingNodesIterator;
+import org.elasticsearch.cluster.routing.RoutingTable;
+import org.elasticsearch.cluster.routing.ShardRouting;
+import org.elasticsearch.cluster.routing.UnassignedInfo;
+import org.elasticsearch.cluster.routing.allocation.AllocationService;
+import org.elasticsearch.cluster.routing.allocation.RoutingAllocation;
+import org.elasticsearch.cluster.routing.allocation.RoutingExplanations;
+import org.elasticsearch.cluster.routing.allocation.allocator.ShardsAllocator;
+import org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders;
+import org.elasticsearch.cluster.routing.allocation.decider.Decision;
+import org.elasticsearch.cluster.service.ClusterService;
+import org.elasticsearch.common.inject.Inject;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.threadpool.ThreadPool;
+import org.elasticsearch.transport.TransportService;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The {@code TransportClusterAllocationExplainAction} is responsible for actually executing the explanation of a shard's allocation on the
+ * master node in the cluster.
+ */
+public class TransportClusterAllocationExplainAction
+ extends TransportMasterNodeAction<ClusterAllocationExplainRequest, ClusterAllocationExplainResponse> {
+
+ private final AllocationService allocationService;
+ private final ClusterInfoService clusterInfoService;
+ private final AllocationDeciders allocationDeciders;
+ private final ShardsAllocator shardAllocator;
+
+ @Inject
+ public TransportClusterAllocationExplainAction(Settings settings, TransportService transportService, ClusterService clusterService,
+ ThreadPool threadPool, ActionFilters actionFilters,
+ IndexNameExpressionResolver indexNameExpressionResolver,
+ AllocationService allocationService, ClusterInfoService clusterInfoService,
+ AllocationDeciders allocationDeciders, ShardsAllocator shardAllocator) {
+ super(settings, ClusterAllocationExplainAction.NAME, transportService, clusterService, threadPool, actionFilters,
+ indexNameExpressionResolver, ClusterAllocationExplainRequest::new);
+ this.allocationService = allocationService;
+ this.clusterInfoService = clusterInfoService;
+ this.allocationDeciders = allocationDeciders;
+ this.shardAllocator = shardAllocator;
+ }
+
+ @Override
+ protected String executor() {
+ return ThreadPool.Names.MANAGEMENT;
+ }
+
+ @Override
+ protected ClusterBlockException checkBlock(ClusterAllocationExplainRequest request, ClusterState state) {
+ return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_READ);
+ }
+
+ @Override
+ protected ClusterAllocationExplainResponse newResponse() {
+ return new ClusterAllocationExplainResponse();
+ }
+
+ /**
+ * Return the decisions for the given {@code ShardRouting} on the given {@code RoutingNode}. If {@code includeYesDecisions} is not true,
+ * only non-YES (NO and THROTTLE) decisions are returned.
+ */
+ public static Decision tryShardOnNode(ShardRouting shard, RoutingNode node, RoutingAllocation allocation, boolean includeYesDecisions) {
+ Decision d = allocation.deciders().canAllocate(shard, node, allocation);
+ if (includeYesDecisions) {
+ return d;
+ } else {
+ Decision.Multi nonYesDecisions = new Decision.Multi();
+ List<Decision> decisions = d.getDecisions();
+ for (Decision decision : decisions) {
+ if (decision.type() != Decision.Type.YES) {
+ nonYesDecisions.add(decision);
+ }
+ }
+ return nonYesDecisions;
+ }
+ }
+
+ /**
+ * For the given {@code ShardRouting}, return the explanation of the allocation for that shard on all nodes. If {@code
+ * includeYesDecisions} is true, returns all decisions, otherwise returns only 'NO' and 'THROTTLE' decisions.
+ */
+ public static ClusterAllocationExplanation explainShard(ShardRouting shard, RoutingAllocation allocation, RoutingNodes routingNodes,
+ boolean includeYesDecisions, ShardsAllocator shardAllocator) {
+ // don't short circuit deciders, we want a full explanation
+ allocation.debugDecision(true);
+ // get the existing unassigned info if available
+ UnassignedInfo ui = shard.unassignedInfo();
+
+ RoutingNodesIterator iter = routingNodes.nodes();
+ Map<DiscoveryNode, Decision> nodeToDecision = new HashMap<>();
+ while (iter.hasNext()) {
+ RoutingNode node = iter.next();
+ DiscoveryNode discoNode = node.node();
+ if (discoNode.isDataNode()) {
+ Decision d = tryShardOnNode(shard, node, allocation, includeYesDecisions);
+ nodeToDecision.put(discoNode, d);
+ }
+ }
+ return new ClusterAllocationExplanation(shard.shardId(), shard.primary(), shard.currentNodeId(), ui, nodeToDecision,
+ shardAllocator.weighShard(allocation, shard));
+ }
+
+ @Override
+ protected void masterOperation(final ClusterAllocationExplainRequest request, final ClusterState state,
+ final ActionListener<ClusterAllocationExplainResponse> listener) {
+ final RoutingNodes routingNodes = state.getRoutingNodes();
+ final RoutingAllocation allocation = new RoutingAllocation(allocationDeciders, routingNodes, state.nodes(),
+ clusterInfoService.getClusterInfo(), System.nanoTime());
+
+ ShardRouting shardRouting = null;
+ if (request.useAnyUnassignedShard()) {
+ // If we can use any shard, just pick the first unassigned one (if there are any)
+ RoutingNodes.UnassignedShards.UnassignedIterator ui = routingNodes.unassigned().iterator();
+ if (ui.hasNext()) {
+ shardRouting = ui.next();
+ }
+ } else {
+ String index = request.getIndex();
+ int shard = request.getShard();
+ if (request.isPrimary()) {
+ // If we're looking for the primary shard, there's only one copy, so pick it directly
+ shardRouting = allocation.routingTable().shardRoutingTable(index, shard).primaryShard();
+ } else {
+ // If looking for a replica, go through all the replica shards
+ List<ShardRouting> replicaShardRoutings = allocation.routingTable().shardRoutingTable(index, shard).replicaShards();
+ if (replicaShardRoutings.size() > 0) {
+ // Pick the first replica at the very least
+ shardRouting = replicaShardRoutings.get(0);
+ // In case there are multiple replicas where some are assigned and some aren't,
+ // try to find one that is unassigned at least
+ for (ShardRouting replica : replicaShardRoutings) {
+ if (replica.unassigned()) {
+ shardRouting = replica;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (shardRouting == null) {
+ listener.onFailure(new ElasticsearchException("unable to find any shards to explain [{}] in the routing table", request));
+ return;
+ }
+ logger.debug("explaining the allocation for [{}], found shard [{}]", request, shardRouting);
+
+ ClusterAllocationExplanation cae = explainShard(shardRouting, allocation, routingNodes,
+ request.includeYesDecisions(), shardAllocator);
+ listener.onResponse(new ClusterAllocationExplainResponse(cae));
+ }
+}
diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/node/stats/NodeStats.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/node/stats/NodeStats.java
index 1d62fc06f0..442b5edde7 100644
--- a/core/src/main/java/org/elasticsearch/action/admin/cluster/node/stats/NodeStats.java
+++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/node/stats/NodeStats.java
@@ -235,7 +235,7 @@ public class NodeStats extends BaseNodeResponse implements ToXContent {
breaker = AllCircuitBreakerStats.readOptionalAllCircuitBreakerStats(in);
scriptStats = in.readOptionalStreamable(ScriptStats::new);
discoveryStats = in.readOptionalStreamable(() -> new DiscoveryStats(null));
- ingestStats = in.readOptionalWritable(IngestStats::new);
+ ingestStats = in.readOptionalWriteable(IngestStats::new);
}
@Override
diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsNodes.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsNodes.java
index 5604616ed3..6020aa1a10 100644
--- a/core/src/main/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsNodes.java
+++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsNodes.java
@@ -302,7 +302,6 @@ public class ClusterStatsNodes implements ToXContent, Streamable {
int availableProcessors;
int allocatedProcessors;
- long availableMemory;
final ObjectIntHashMap<String> names;
public OsStats() {
@@ -326,15 +325,10 @@ public class ClusterStatsNodes implements ToXContent, Streamable {
return allocatedProcessors;
}
- public ByteSizeValue getAvailableMemory() {
- return new ByteSizeValue(availableMemory);
- }
-
@Override
public void readFrom(StreamInput in) throws IOException {
availableProcessors = in.readVInt();
allocatedProcessors = in.readVInt();
- availableMemory = in.readLong();
int size = in.readVInt();
names.clear();
for (int i = 0; i < size; i++) {
@@ -346,7 +340,6 @@ public class ClusterStatsNodes implements ToXContent, Streamable {
public void writeTo(StreamOutput out) throws IOException {
out.writeVInt(availableProcessors);
out.writeVInt(allocatedProcessors);
- out.writeLong(availableMemory);
out.writeVInt(names.size());
for (ObjectIntCursor<String> name : names) {
out.writeString(name.key);
@@ -365,9 +358,6 @@ public class ClusterStatsNodes implements ToXContent, Streamable {
static final XContentBuilderString ALLOCATED_PROCESSORS = new XContentBuilderString("allocated_processors");
static final XContentBuilderString NAME = new XContentBuilderString("name");
static final XContentBuilderString NAMES = new XContentBuilderString("names");
- static final XContentBuilderString MEM = new XContentBuilderString("mem");
- static final XContentBuilderString TOTAL = new XContentBuilderString("total");
- static final XContentBuilderString TOTAL_IN_BYTES = new XContentBuilderString("total_in_bytes");
static final XContentBuilderString COUNT = new XContentBuilderString("count");
}
@@ -375,10 +365,6 @@ public class ClusterStatsNodes implements ToXContent, Streamable {
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.field(Fields.AVAILABLE_PROCESSORS, availableProcessors);
builder.field(Fields.ALLOCATED_PROCESSORS, allocatedProcessors);
- builder.startObject(Fields.MEM);
- builder.byteSizeField(Fields.TOTAL_IN_BYTES, Fields.TOTAL, availableMemory);
- builder.endObject();
-
builder.startArray(Fields.NAMES);
for (ObjectIntCursor<String> name : names) {
builder.startObject();