/*
* 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.indices.shards;
import com.carrotsearch.hppc.cursors.IntObjectCursor;
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.Version;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.ShardOperationFailedException;
import org.elasticsearch.action.support.DefaultShardOperationFailedException;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.common.collect.ImmutableOpenIntMap;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Streamable;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.shard.ShardStateMetaData;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.elasticsearch.action.admin.indices.shards.IndicesShardStoresResponse.StoreStatus.readStoreStatus;
/**
* Response for {@link IndicesShardStoresAction}
*
* Consists of {@link StoreStatus}s for requested indices grouped by
* indices and shard ids and a list of encountered node {@link Failure}s
*/
public class IndicesShardStoresResponse extends ActionResponse implements ToXContent {
/**
* Shard store information from a node
*/
public static class StoreStatus implements Streamable, ToXContent, Comparable<StoreStatus> {
private DiscoveryNode node;
private String allocationId;
private Exception storeException;
private AllocationStatus allocationStatus;
/**
* The status of the shard store with respect to the cluster
*/
public enum AllocationStatus {
/**
* Allocated as primary
*/
PRIMARY((byte) 0),
/**
* Allocated as a replica
*/
REPLICA((byte) 1),
/**
* Not allocated
*/
UNUSED((byte) 2);
private final byte id;
AllocationStatus(byte id) {
this.id = id;
}
private static AllocationStatus fromId(byte id) {
switch (id) {
case 0: return PRIMARY;
case 1: return REPLICA;
case 2: return UNUSED;
default: throw new IllegalArgumentException("unknown id for allocation status [" + id + "]");
}
}
public String value() {
switch (id) {
case 0: return "primary";
case 1: return "replica";
case 2: return "unused";
default: throw new IllegalArgumentException("unknown id for allocation status [" + id + "]");
}
}
private static AllocationStatus readFrom(StreamInput in) throws IOException {
return fromId(in.readByte());
}
private void writeTo(StreamOutput out) throws IOException {
out.writeByte(id);
}
}
private StoreStatus() {
}
public StoreStatus(DiscoveryNode node, String allocationId, AllocationStatus allocationStatus, Exception storeException) {
this.node = node;
this.allocationId = allocationId;
this.allocationStatus = allocationStatus;
this.storeException = storeException;
}
/**
* Node the store belongs to
*/
public DiscoveryNode getNode() {
return node;
}
/**
* AllocationStatus id of the store, used to select the store that will be
* used as a primary.
*/
public String getAllocationId() {
return allocationId;
}
/**
* Exception while trying to open the
* shard index or from when the shard failed
*/
public Exception getStoreException() {
return storeException;
}
/**
* The allocationStatus status of the store.
* {@link AllocationStatus#PRIMARY} indicates a primary shard copy
* {@link AllocationStatus#REPLICA} indicates a replica shard copy
* {@link AllocationStatus#UNUSED} indicates an unused shard copy
*/
public AllocationStatus getAllocationStatus() {
return allocationStatus;
}
public static StoreStatus readStoreStatus(StreamInput in) throws IOException {
StoreStatus storeStatus = new StoreStatus();
storeStatus.readFrom(in);
return storeStatus;
}
@Override
public void readFrom(StreamInput in) throws IOException {
node = new DiscoveryNode(in);
if (in.getVersion().before(Version.V_6_0_0_alpha1_UNRELEASED)) {
// legacy version
in.readLong();
}
allocationId = in.readOptionalString();
allocationStatus = AllocationStatus.readFrom(in);
if (in.readBoolean()) {
storeException = in.readException();
}
}
@Override
public void writeTo(StreamOutput out) throws IOException {
node.writeTo(out);
if (out.getVersion().before(Version.V_6_0_0_alpha1_UNRELEASED)) {
// legacy version
out.writeLong(-1L);
}
out.writeOptionalString(allocationId);
allocationStatus.writeTo(out);
if (storeException != null) {
out.writeBoolean(true);
out.writeException(storeException);
} else {
out.writeBoolean(false);
}
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
node.toXContent(builder, params);
if (allocationId != null) {
builder.field(Fields.ALLOCATION_ID, allocationId);
}
builder.field(Fields.ALLOCATED, allocationStatus.value());
if (storeException != null) {
builder.startObject(Fields.STORE_EXCEPTION);
ElasticsearchException.generateThrowableXContent(builder, params, storeException);
builder.endObject();
}
return builder;
}
@Override
public int compareTo(StoreStatus other) {
if (storeException != null && other.storeException == null) {
return 1;
} else if (other.storeException != null && storeException == null) {
return -1;
}
if (allocationId != null && other.allocationId == null) {
return -1;
} else if (allocationId == null && other.allocationId != null) {
return 1;
} else if (allocationId == null && other.allocationId == null) {
return Integer.compare(allocationStatus.id, other.allocationStatus.id);
} else {
int compare = Integer.compare(allocationStatus.id, other.allocationStatus.id);
if (compare == 0) {
return allocationId.compareTo(other.allocationId);
}
return compare;
}
}
}
/**
* Single node failure while retrieving shard store information
*/
public static class Failure extends DefaultShardOperationFailedException {
private String nodeId;
public Failure(String nodeId, String index, int shardId, Throwable reason) {
super(index, shardId, reason);
this.nodeId = nodeId;
}
private Failure() {
}
public String nodeId() {
return nodeId;
}
public static Failure readFailure(StreamInput in) throws IOException {
Failure failure = new Failure();
failure.readFrom(in);
return failure;
}
@Override
public void readFrom(StreamInput in) throws IOException {
nodeId = in.readString();
super.readFrom(in);
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(nodeId);
super.writeTo(out);
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.field("node", nodeId());
super.toXContent(builder, params);
return builder;
}
}
private ImmutableOpenMap<String, ImmutableOpenIntMap<List<StoreStatus>>> storeStatuses;
private List<Failure> failures;
public IndicesShardStoresResponse(ImmutableOpenMap<String, ImmutableOpenIntMap<List<StoreStatus>>> storeStatuses, List<Failure> failures) {
this.storeStatuses = storeStatuses;
this.failures = failures;
}
IndicesShardStoresResponse() {
this(ImmutableOpenMap.<String, ImmutableOpenIntMap<List<StoreStatus>>>of(), Collections.<Failure>emptyList());
}
/**
* Returns {@link StoreStatus}s
* grouped by their index names and shard ids.
*/
public ImmutableOpenMap<String, ImmutableOpenIntMap<List<StoreStatus>>> getStoreStatuses() {
return storeStatuses;
}
/**
* Returns node {@link Failure}s encountered
* while executing the request
*/
public List<Failure> getFailures() {
return failures;
}
@Override
public void readFrom(StreamInput in) throws IOException {
super.readFrom(in);
int numResponse = in.readVInt();
ImmutableOpenMap.Builder<String, ImmutableOpenIntMap<List<StoreStatus>>> storeStatusesBuilder = ImmutableOpenMap.builder();
for (int i = 0; i < numResponse; i++) {
String index = in.readString();
int indexEntries = in.readVInt();
ImmutableOpenIntMap.Builder<List<StoreStatus>> shardEntries = ImmutableOpenIntMap.builder();
for (int shardCount = 0; shardCount < indexEntries; shardCount++) {
int shardID = in.readInt();
int nodeEntries = in.readVInt();
List<StoreStatus> storeStatuses = new ArrayList<>(nodeEntries);
for (int nodeCount = 0; nodeCount < nodeEntries; nodeCount++) {
storeStatuses.add(readStoreStatus(in));
}
shardEntries.put(shardID, storeStatuses);
}
storeStatusesBuilder.put(index, shardEntries.build());
}
int numFailure = in.readVInt();
List<Failure> failureBuilder = new ArrayList<>();
for (int i = 0; i < numFailure; i++) {
failureBuilder.add(Failure.readFailure(in));
}
storeStatuses = storeStatusesBuilder.build();
failures = Collections.unmodifiableList(failureBuilder);
}
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeVInt(storeStatuses.size());
for (ObjectObjectCursor<String, ImmutableOpenIntMap<List<StoreStatus>>> indexShards : storeStatuses) {
out.writeString(indexShards.key);
out.writeVInt(indexShards.value.size());
for (IntObjectCursor<List<StoreStatus>> shardStatusesEntry : indexShards.value) {
out.writeInt(shardStatusesEntry.key);
out.writeVInt(shardStatusesEntry.value.size());
for (StoreStatus storeStatus : shardStatusesEntry.value) {
storeStatus.writeTo(out);
}
}
}
out.writeVInt(failures.size());
for (ShardOperationFailedException failure : failures) {
failure.writeTo(out);
}
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
if (failures.size() > 0) {
builder.startArray(Fields.FAILURES);
for (ShardOperationFailedException failure : failures) {
builder.startObject();
failure.toXContent(builder, params);
builder.endObject();
}
builder.endArray();
}
builder.startObject(Fields.INDICES);
for (ObjectObjectCursor<String, ImmutableOpenIntMap<List<StoreStatus>>> indexShards : storeStatuses) {
builder.startObject(indexShards.key);
builder.startObject(Fields.SHARDS);
for (IntObjectCursor<List<StoreStatus>> shardStatusesEntry : indexShards.value) {
builder.startObject(String.valueOf(shardStatusesEntry.key));
builder.startArray(Fields.STORES);
for (StoreStatus storeStatus : shardStatusesEntry.value) {
builder.startObject();
storeStatus.toXContent(builder, params);
builder.endObject();
}
builder.endArray();
builder.endObject();
}
builder.endObject();
builder.endObject();
}
builder.endObject();
return builder;
}
static final class Fields {
static final String INDICES = "indices";
static final String SHARDS = "shards";
static final String FAILURES = "failures";
static final String STORES = "stores";
// StoreStatus fields
static final String ALLOCATION_ID = "allocation_id";
static final String STORE_EXCEPTION = "store_exception";
static final String ALLOCATED = "allocation";
}
}