@Override
  public void readFrom(StreamInput in) throws IOException {
    index = in.readString();
    numberOfShards = in.readVInt();
    numberOfReplicas = in.readVInt();
    activePrimaryShards = in.readVInt();
    activeShards = in.readVInt();
    relocatingShards = in.readVInt();
    initializingShards = in.readVInt();
    unassignedShards = in.readVInt();
    status = ClusterHealthStatus.fromValue(in.readByte());

    int size = in.readVInt();
    for (int i = 0; i < size; i++) {
      ClusterShardHealth shardHealth = readClusterShardHealth(in);
      shards.put(shardHealth.getId(), shardHealth);
    }
    size = in.readVInt();
    if (size == 0) {
      validationFailures = ImmutableList.of();
    } else {
      for (int i = 0; i < size; i++) {
        validationFailures.add(in.readString());
      }
    }
  }
  @Override
  public void writeTo(StreamOutput out) throws IOException {
    out.writeString(index);
    out.writeVInt(numberOfShards);
    out.writeVInt(numberOfReplicas);
    out.writeVInt(activePrimaryShards);
    out.writeVInt(activeShards);
    out.writeVInt(relocatingShards);
    out.writeVInt(initializingShards);
    out.writeVInt(unassignedShards);
    out.writeByte(status.value());

    out.writeVInt(shards.size());
    for (ClusterShardHealth shardHealth : this) {
      shardHealth.writeTo(out);
    }

    out.writeVInt(validationFailures.size());
    for (String failure : validationFailures) {
      out.writeString(failure);
    }
  }
  @Override
  public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
    builder.field(Fields.STATUS, getStatus().name().toLowerCase(Locale.ROOT));
    builder.field(Fields.NUMBER_OF_SHARDS, getNumberOfShards());
    builder.field(Fields.NUMBER_OF_REPLICAS, getNumberOfReplicas());
    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());

    if (!getValidationFailures().isEmpty()) {
      builder.startArray(Fields.VALIDATION_FAILURES);
      for (String validationFailure : getValidationFailures()) {
        builder.value(validationFailure);
      }
      builder.endArray();
    }

    if ("shards".equals(params.param("level", "indices"))) {
      builder.startObject(Fields.SHARDS);

      for (ClusterShardHealth shardHealth : shards.values()) {
        builder.startObject(Integer.toString(shardHealth.getId()));

        builder.field(Fields.STATUS, shardHealth.getStatus().name().toLowerCase(Locale.ROOT));
        builder.field(Fields.PRIMARY_ACTIVE, shardHealth.isPrimaryActive());
        builder.field(Fields.ACTIVE_SHARDS, shardHealth.getActiveShards());
        builder.field(Fields.RELOCATING_SHARDS, shardHealth.getRelocatingShards());
        builder.field(Fields.INITIALIZING_SHARDS, shardHealth.getInitializingShards());
        builder.field(Fields.UNASSIGNED_SHARDS, shardHealth.getUnassignedShards());

        builder.endObject();
      }

      builder.endObject();
    }
    return builder;
  }
  public ClusterIndexHealth(IndexMetaData indexMetaData, IndexRoutingTable indexRoutingTable) {
    this.index = indexMetaData.index();
    this.numberOfShards = indexMetaData.getNumberOfShards();
    this.numberOfReplicas = indexMetaData.getNumberOfReplicas();
    this.validationFailures = indexRoutingTable.validate(indexMetaData);

    for (IndexShardRoutingTable shardRoutingTable : indexRoutingTable) {
      ClusterShardHealth shardHealth = new ClusterShardHealth(shardRoutingTable.shardId().id());
      for (ShardRouting shardRouting : shardRoutingTable) {
        if (shardRouting.active()) {
          shardHealth.activeShards++;
          if (shardRouting.relocating()) {
            // the shard is relocating, the one it is relocating to will be in initializing state,
            // so we don't count it
            shardHealth.relocatingShards++;
          }
          if (shardRouting.primary()) {
            shardHealth.primaryActive = true;
          }
        } else if (shardRouting.initializing()) {
          shardHealth.initializingShards++;
        } else if (shardRouting.unassigned()) {
          shardHealth.unassignedShards++;
        }
      }
      if (shardHealth.primaryActive) {
        if (shardHealth.activeShards == shardRoutingTable.size()) {
          shardHealth.status = ClusterHealthStatus.GREEN;
        } else {
          shardHealth.status = ClusterHealthStatus.YELLOW;
        }
      } else {
        shardHealth.status = ClusterHealthStatus.RED;
      }
      shards.put(shardHealth.getId(), shardHealth);
    }

    // update the index status
    status = ClusterHealthStatus.GREEN;

    for (ClusterShardHealth shardHealth : shards.values()) {
      if (shardHealth.isPrimaryActive()) {
        activePrimaryShards++;
      }
      activeShards += shardHealth.activeShards;
      relocatingShards += shardHealth.relocatingShards;
      initializingShards += shardHealth.initializingShards;
      unassignedShards += shardHealth.unassignedShards;

      if (shardHealth.getStatus() == ClusterHealthStatus.RED) {
        status = ClusterHealthStatus.RED;
      } else if (shardHealth.getStatus() == ClusterHealthStatus.YELLOW
          && status != ClusterHealthStatus.RED) {
        // do not override an existing red
        status = ClusterHealthStatus.YELLOW;
      }
    }
    if (!validationFailures.isEmpty()) {
      status = ClusterHealthStatus.RED;
    } else if (shards
        .isEmpty()) { // might be since none has been created yet (two phase index creation)
      status = ClusterHealthStatus.RED;
    }
  }