Marvel: Add Cluster State collector

closes elastic/elasticsearch-marvelelastic/elasticsearch#445

Original commit: elastic/x-pack-elasticsearch@28c8b0f5af
This commit is contained in:
Tanguy Leroux 2015-07-30 12:11:40 +02:00
parent 52be1aa943
commit 5b519935be
16 changed files with 485 additions and 19 deletions

View File

@ -7,11 +7,13 @@ package org.elasticsearch.marvel.agent.collector;
import org.elasticsearch.ElasticsearchTimeoutException;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.common.component.AbstractLifecycleComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.marvel.agent.exporter.MarvelDoc;
import org.elasticsearch.marvel.agent.settings.MarvelSettingsService;
import java.util.Collection;
@ -20,12 +22,17 @@ public abstract class AbstractCollector<T> extends AbstractLifecycleComponent<T>
private final String name;
protected final ClusterService clusterService;
protected final ClusterName clusterName;
protected final MarvelSettingsService marvelSettings;
@Inject
public AbstractCollector(Settings settings, String name, ClusterService clusterService) {
public AbstractCollector(Settings settings, String name, ClusterService clusterService,
ClusterName clusterName, MarvelSettingsService marvelSettings) {
super(settings);
this.name = name;
this.clusterService = clusterService;
this.clusterName = clusterName;
this.marvelSettings = marvelSettings;
}
@Override

View File

@ -7,6 +7,7 @@ package org.elasticsearch.marvel.agent.collector;
import org.elasticsearch.common.inject.AbstractModule;
import org.elasticsearch.common.inject.multibindings.Multibinder;
import org.elasticsearch.marvel.agent.collector.cluster.ClusterStateCollector;
import org.elasticsearch.marvel.agent.collector.cluster.ClusterStatsCollector;
import org.elasticsearch.marvel.agent.collector.indices.IndexStatsCollector;
@ -21,6 +22,7 @@ public class CollectorModule extends AbstractModule {
// Registers default collectors
registerCollector(IndexStatsCollector.class);
registerCollector(ClusterStatsCollector.class);
registerCollector(ClusterStateCollector.class);
}
@Override

View File

@ -0,0 +1,62 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.marvel.agent.collector.cluster;
import com.google.common.collect.ImmutableList;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.marvel.agent.collector.AbstractCollector;
import org.elasticsearch.marvel.agent.exporter.MarvelDoc;
import org.elasticsearch.marvel.agent.settings.MarvelSettingsService;
import java.util.Collection;
/**
* Collector for cluster state.
* <p/>
* This collector runs on the master node only and collects the {@link ClusterStateMarvelDoc} document
* at a given frequency.
*/
public class ClusterStateCollector extends AbstractCollector<ClusterStateCollector> {
public static final String NAME = "cluster-state-collector";
public static final String TYPE = "marvel_cluster_state";
private final Client client;
@Inject
public ClusterStateCollector(Settings settings, ClusterService clusterService,
ClusterName clusterName, MarvelSettingsService marvelSettings, Client client) {
super(settings, NAME, clusterService, clusterName, marvelSettings);
this.client = client;
}
@Override
protected boolean masterOnly() {
return true;
}
@Override
protected Collection<MarvelDoc> doCollect() throws Exception {
ImmutableList.Builder<MarvelDoc> results = ImmutableList.builder();
ClusterState clusterState = clusterService.state();
ClusterHealthResponse clusterHealth = client.admin().cluster().prepareHealth().get(marvelSettings.clusterStateTimeout());
results.add(buildMarvelDoc(clusterName.value(), TYPE, System.currentTimeMillis(), clusterState, clusterHealth.getStatus()));
return results.build();
}
protected MarvelDoc buildMarvelDoc(String clusterName, String type, long timestamp, ClusterState clusterState, ClusterHealthStatus status) {
return ClusterStateMarvelDoc.createMarvelDoc(clusterName, type, timestamp, clusterState, status);
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.marvel.agent.collector.cluster;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.marvel.agent.exporter.MarvelDoc;
public class ClusterStateMarvelDoc extends MarvelDoc<ClusterStateMarvelDoc.Payload> {
private final Payload payload;
public ClusterStateMarvelDoc(String clusterName, String type, long timestamp, Payload payload) {
super(clusterName, type, timestamp);
this.payload = payload;
}
@Override
public ClusterStateMarvelDoc.Payload payload() {
return payload;
}
public static ClusterStateMarvelDoc createMarvelDoc(String clusterName, String type, long timestamp, ClusterState clusterState, ClusterHealthStatus status) {
return new ClusterStateMarvelDoc(clusterName, type, timestamp, new Payload(clusterState, status));
}
public static class Payload {
private final ClusterState clusterState;
private final ClusterHealthStatus status;
Payload(ClusterState clusterState, ClusterHealthStatus status) {
this.clusterState = clusterState;
this.status = status;
}
public ClusterState getClusterState() {
return clusterState;
}
public ClusterHealthStatus getStatus() {
return status;
}
}
}

View File

@ -14,12 +14,13 @@ import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.marvel.agent.collector.AbstractCollector;
import org.elasticsearch.marvel.agent.exporter.MarvelDoc;
import org.elasticsearch.marvel.agent.settings.MarvelSettingsService;
import java.util.Collection;
/**
* Collector for cluster stats.
* <p>
* <p/>
* This collector runs on the master node only and collects the {@link ClusterStatsMarvelDoc} document
* at a given frequency.
*/
@ -28,13 +29,12 @@ public class ClusterStatsCollector extends AbstractCollector<ClusterStatsCollect
public static final String NAME = "cluster-stats-collector";
public static final String TYPE = "marvel_cluster_stats";
private final ClusterName clusterName;
private final Client client;
@Inject
public ClusterStatsCollector(Settings settings, ClusterService clusterService, ClusterName clusterName, Client client) {
super(settings, NAME, clusterService);
this.clusterName = clusterName;
public ClusterStatsCollector(Settings settings, ClusterService clusterService,
ClusterName clusterName, MarvelSettingsService marvelSettings, Client client) {
super(settings, NAME, clusterService, clusterName, marvelSettings);
this.client = client;
}

View File

@ -30,16 +30,13 @@ public class IndexStatsCollector extends AbstractCollector<IndexStatsCollector>
public static final String NAME = "index-stats-collector";
public static final String TYPE = "marvel_index_stats";
private final ClusterName clusterName;
private final Client client;
private final MarvelSettingsService marvelSettings;
@Inject
public IndexStatsCollector(Settings settings, ClusterService clusterService, ClusterName clusterName, Client client, MarvelSettingsService marvelSettings) {
super(settings, NAME, clusterService);
public IndexStatsCollector(Settings settings, ClusterService clusterService,
ClusterName clusterName, MarvelSettingsService marvelSettings, Client client) {
super(settings, NAME, clusterService, clusterName, marvelSettings);
this.client = client;
this.clusterName = clusterName;
this.marvelSettings = marvelSettings;
}
@Override

View File

@ -7,8 +7,10 @@ package org.elasticsearch.marvel.agent.renderer;
import org.elasticsearch.common.inject.AbstractModule;
import org.elasticsearch.common.inject.multibindings.MapBinder;
import org.elasticsearch.marvel.agent.collector.cluster.ClusterStateCollector;
import org.elasticsearch.marvel.agent.collector.cluster.ClusterStatsCollector;
import org.elasticsearch.marvel.agent.collector.indices.IndexStatsCollector;
import org.elasticsearch.marvel.agent.renderer.cluster.ClusterStateRenderer;
import org.elasticsearch.marvel.agent.renderer.cluster.ClusterStatsRenderer;
import org.elasticsearch.marvel.agent.renderer.indices.IndexStatsRenderer;
@ -34,6 +36,9 @@ public class RendererModule extends AbstractModule {
bind(ClusterStatsRenderer.class).asEagerSingleton();
mbinder.addBinding(ClusterStatsCollector.TYPE).to(ClusterStatsRenderer.class);
bind(ClusterStateRenderer.class).asEagerSingleton();
mbinder.addBinding(ClusterStateCollector.TYPE).to(ClusterStateRenderer.class);
for (Map.Entry<String, Class<? extends Renderer>> entry : renderers.entrySet()) {
bind(entry.getValue()).asEagerSingleton();
mbinder.addBinding(entry.getKey()).to(entry.getValue());

View File

@ -0,0 +1,70 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.marvel.agent.renderer.cluster;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.routing.RoutingTable;
import org.elasticsearch.cluster.routing.ShardRouting;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentBuilderString;
import org.elasticsearch.marvel.agent.collector.cluster.ClusterStateMarvelDoc;
import org.elasticsearch.marvel.agent.renderer.AbstractRenderer;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
public class ClusterStateRenderer extends AbstractRenderer<ClusterStateMarvelDoc> {
private static final String[] FILTERS = {
"cluster_state.version",
"cluster_state.master_node",
"cluster_state.status",
"cluster_state.nodes",
"cluster_state.shards",
};
public ClusterStateRenderer() {
super(FILTERS, true);
}
@Override
protected void doRender(ClusterStateMarvelDoc marvelDoc, XContentBuilder builder, ToXContent.Params params) throws IOException {
builder.startObject(Fields.CLUSTER_STATE);
ClusterStateMarvelDoc.Payload payload = marvelDoc.payload();
if (payload != null) {
ClusterState clusterState = payload.getClusterState();
if (clusterState != null) {
builder.field(Fields.STATUS, payload.getStatus().name().toLowerCase(Locale.ROOT));
clusterState.toXContent(builder, params);
RoutingTable routingTable = clusterState.routingTable();
if (routingTable != null) {
List<ShardRouting> shards = routingTable.allShards();
if (shards != null) {
builder.startArray(Fields.SHARDS);
for (ShardRouting shard : shards) {
shard.toXContent(builder, params);
}
builder.endArray();
}
}
}
}
builder.endObject();
}
static final class Fields {
static final XContentBuilderString CLUSTER_STATE = new XContentBuilderString("cluster_state");
static final XContentBuilderString STATUS = new XContentBuilderString("status");
static final XContentBuilderString SHARDS = new XContentBuilderString("shards");
}
}

View File

@ -30,9 +30,9 @@ public class IndexStatsRenderer extends AbstractRenderer<IndexStatsMarvelDoc> {
@Override
protected void doRender(IndexStatsMarvelDoc marvelDoc, XContentBuilder builder, ToXContent.Params params) throws IOException {
IndexStatsMarvelDoc.Payload payload = marvelDoc.payload();
builder.startObject(Fields.INDEX_STATS);
IndexStatsMarvelDoc.Payload payload = marvelDoc.payload();
if (payload != null) {
IndexStats indexStats = payload.getIndexStats();
if (indexStats != null) {

View File

@ -27,11 +27,13 @@ public class MarvelSettingsService extends AbstractComponent implements NodeSett
private final List<MarvelSetting> settings;
final TimeValueSetting indexStatsTimeout = MarvelSetting.timeSetting(PREFIX + "index.stats.timeout", TimeValue.timeValueMinutes(10),
"Timeout value when collecting Index Stats (default to 10m)");
"Timeout value when collecting indices statistics (default to 10m)");
final StringArraySetting indices = MarvelSetting.arraySetting(PREFIX + "indices", Strings.EMPTY_ARRAY,
"List of indices names whose stats will be exported (default to all indices)");
final TimeValueSetting clusterStateTimeout = MarvelSetting.timeSetting(PREFIX + "cluster.state.timeout", TimeValue.timeValueMinutes(10),
"Timeout value when collecting the cluster state (default to 10m)");
MarvelSettingsService(Settings clusterSettings) {
super(clusterSettings);
@ -40,6 +42,7 @@ public class MarvelSettingsService extends AbstractComponent implements NodeSett
ImmutableList.Builder<MarvelSetting> builder = ImmutableList.builder();
builder.add(indexStatsTimeout);
builder.add(indices);
builder.add(clusterStateTimeout);
this.settings = builder.build();
logger.trace("initializing marvel settings:");
@ -85,4 +88,8 @@ public class MarvelSettingsService extends AbstractComponent implements NodeSett
public String[] indices() {
return indices.getValue();
}
public TimeValue clusterStateTimeout() {
return clusterStateTimeout.getValue();
}
}

View File

@ -78,6 +78,31 @@
}
}
}
},
"marvel_cluster_state": {
"properties": {
"cluster_state": {
"properties": {
"version": {
"type": "long"
},
"master_node": {
"type": "string",
"index": "not_analyzed"
},
"status": {
"type": "string",
"index": "not_analyzed"
},
"nodes": {
"type": "object"
},
"shards": {
"type": "object"
}
}
}
}
}
}
}

View File

@ -0,0 +1,127 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.marvel.agent.collector.cluster;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.marvel.agent.exporter.MarvelDoc;
import org.elasticsearch.marvel.agent.settings.MarvelSettingsService;
import org.elasticsearch.test.ElasticsearchSingleNodeTest;
import org.junit.Test;
import java.util.Collection;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
import static org.hamcrest.Matchers.*;
public class ClusterStateCollectorTests extends ElasticsearchSingleNodeTest {
@Test
public void testClusterStateCollectorNoIndices() throws Exception {
Collection<MarvelDoc> results = newClusterStateCollector().doCollect();
assertThat(results, hasSize(1));
MarvelDoc marvelDoc = results.iterator().next();
assertNotNull(marvelDoc);
assertThat(marvelDoc, instanceOf(ClusterStateMarvelDoc.class));
ClusterStateMarvelDoc clusterStateMarvelDoc = (ClusterStateMarvelDoc) marvelDoc;
assertThat(clusterStateMarvelDoc.clusterName(), equalTo(client().admin().cluster().prepareHealth().get().getClusterName()));
assertThat(clusterStateMarvelDoc.timestamp(), greaterThan(0L));
assertThat(clusterStateMarvelDoc.type(), equalTo(ClusterStateCollector.TYPE));
ClusterStateMarvelDoc.Payload payload = clusterStateMarvelDoc.payload();
assertNotNull(payload);
assertNotNull(payload.getClusterState());
ClusterState clusterState = payload.getClusterState();
assertThat(clusterState.getRoutingTable().allShards(), hasSize(0));
}
@Test
public void testClusterStateCollectorOneIndex() throws Exception {
int nbShards = randomIntBetween(1, 5);
createIndex("test", Settings.settingsBuilder().put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, nbShards).build());
int nbDocs = randomIntBetween(1, 20);
for (int i = 0; i < nbDocs; i++) {
client().prepareIndex("test", "test").setSource("num", i).get();
}
client().admin().indices().prepareRefresh().get();
assertHitCount(client().prepareCount().get(), nbDocs);
Collection<MarvelDoc> results = newClusterStateCollector().doCollect();
assertThat(results, hasSize(1));
MarvelDoc marvelDoc = results.iterator().next();
assertNotNull(marvelDoc);
assertThat(marvelDoc, instanceOf(ClusterStateMarvelDoc.class));
ClusterStateMarvelDoc clusterStateMarvelDoc = (ClusterStateMarvelDoc) marvelDoc;
assertThat(clusterStateMarvelDoc.clusterName(), equalTo(client().admin().cluster().prepareHealth().get().getClusterName()));
assertThat(clusterStateMarvelDoc.timestamp(), greaterThan(0L));
assertThat(clusterStateMarvelDoc.type(), equalTo(ClusterStateCollector.TYPE));
ClusterStateMarvelDoc.Payload payload = clusterStateMarvelDoc.payload();
assertNotNull(payload);
assertNotNull(payload.getClusterState());
ClusterState clusterState = payload.getClusterState();
assertThat(clusterState.getRoutingTable().allShards("test"), hasSize(nbShards));
}
@Test
public void testClusterStateCollectorMultipleIndices() throws Exception {
int nbIndices = randomIntBetween(1, 5);
int[] docsPerIndex = new int[nbIndices];
int[] shardsPerIndex = new int[nbIndices];
for (int i = 0; i < nbIndices; i++) {
shardsPerIndex[i] = randomIntBetween(1, 5);
createIndex("test-" + i, Settings.settingsBuilder().put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, shardsPerIndex[i]).build());
docsPerIndex[i] = randomIntBetween(1, 20);
for (int j = 0; j < docsPerIndex[i]; j++) {
client().prepareIndex("test-" + i, "test").setSource("num", i).get();
}
}
client().admin().indices().prepareRefresh().get();
for (int i = 0; i < nbIndices; i++) {
assertHitCount(client().prepareCount("test-" + i).get(), docsPerIndex[i]);
}
Collection<MarvelDoc> results = newClusterStateCollector().doCollect();
MarvelDoc marvelDoc = results.iterator().next();
assertNotNull(marvelDoc);
assertThat(marvelDoc, instanceOf(ClusterStateMarvelDoc.class));
ClusterStateMarvelDoc clusterStateMarvelDoc = (ClusterStateMarvelDoc) marvelDoc;
assertThat(clusterStateMarvelDoc.clusterName(), equalTo(client().admin().cluster().prepareHealth().get().getClusterName()));
assertThat(clusterStateMarvelDoc.timestamp(), greaterThan(0L));
assertThat(clusterStateMarvelDoc.type(), equalTo(ClusterStateCollector.TYPE));
ClusterStateMarvelDoc.Payload payload = clusterStateMarvelDoc.payload();
assertNotNull(payload);
assertNotNull(payload.getClusterState());
ClusterState clusterState = payload.getClusterState();
for (int i = 0; i < nbIndices; i++) {
assertThat(clusterState.getRoutingTable().allShards("test-" + i), hasSize(shardsPerIndex[i]));
}
}
private ClusterStateCollector newClusterStateCollector() {
return new ClusterStateCollector(getInstanceFromNode(Settings.class),
getInstanceFromNode(ClusterService.class),
getInstanceFromNode(ClusterName.class),
getInstanceFromNode(MarvelSettingsService.class),
client());
}
}

View File

@ -9,6 +9,7 @@ import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.marvel.agent.exporter.MarvelDoc;
import org.elasticsearch.marvel.agent.settings.MarvelSettingsService;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.junit.Test;
@ -39,7 +40,10 @@ public class ClusterStatsCollectorTests extends ElasticsearchIntegrationTest {
}
private ClusterStatsCollector newClusterStatsCollector() {
return new ClusterStatsCollector(internalCluster().getInstance(Settings.class), internalCluster().getInstance(ClusterService.class),
internalCluster().getInstance(ClusterName.class), client());
return new ClusterStatsCollector(internalCluster().getInstance(Settings.class),
internalCluster().getInstance(ClusterService.class),
internalCluster().getInstance(ClusterName.class),
internalCluster().getInstance(MarvelSettingsService.class),
client());
}
}

View File

@ -128,8 +128,8 @@ public class IndexStatsCollectorTests extends ElasticsearchSingleNodeTest {
return new IndexStatsCollector(getInstanceFromNode(Settings.class),
getInstanceFromNode(ClusterService.class),
getInstanceFromNode(ClusterName.class),
client(),
getInstanceFromNode(MarvelSettingsService.class));
getInstanceFromNode(MarvelSettingsService.class),
client());
}
public void waitForNoBlocksOnNode() throws InterruptedException {

View File

@ -0,0 +1,56 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.marvel.agent.renderer.cluster;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.Streams;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.marvel.agent.collector.cluster.ClusterStateMarvelDoc;
import org.elasticsearch.marvel.agent.renderer.Renderer;
import org.elasticsearch.marvel.agent.renderer.RendererTestUtils;
import org.elasticsearch.test.ElasticsearchSingleNodeTest;
import org.junit.Test;
public class ClusterStateRendererTests extends ElasticsearchSingleNodeTest {
private static final String SAMPLE_FILE = "/samples/marvel_cluster_state.json";
@Test
public void testClusterStateRenderer() throws Exception {
createIndex("my-index", Settings.settingsBuilder()
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 3)
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)
.build());
logger.debug("--> retrieving cluster state");
ClusterState clusterState = getInstanceFromNode(ClusterService.class).state();
logger.debug("--> retrieving cluster health");
ClusterHealthResponse clusterHealth = client().admin().cluster().prepareHealth().get();
logger.debug("--> creating the cluster state marvel document");
ClusterStateMarvelDoc marvelDoc = ClusterStateMarvelDoc.createMarvelDoc("test", "marvel_cluster_state", 1437580442979L,
clusterState, clusterHealth.getStatus());
logger.debug("--> rendering the document");
Renderer renderer = new ClusterStateRenderer();
String result = RendererTestUtils.renderAsJSON(marvelDoc, renderer);
logger.debug("--> loading sample document from file {}", SAMPLE_FILE);
String expected = Streams.copyToStringFromClasspath(SAMPLE_FILE);
String nodeId = clusterState.getNodes().getLocalNodeId();
logger.debug("--> replace the local node id in sample document with {}", nodeId);
expected = Strings.replace(expected, "__node_id__", nodeId);
logger.debug("--> comparing both documents, they must have the same structure");
RendererTestUtils.assertJSONStructure(result, expected);
}
}

View File

@ -0,0 +1,56 @@
{
"cluster_name": "test",
"timestamp": "2015-07-22T15:54:02.979Z",
"cluster_state": {
"status": "yellow",
"version": 14,
"master_node": "__node_id__",
"nodes": {
"__node_id__": {
"name": "Box",
"transport_address": "inet[/127.0.0.1:9300]",
"attributes": {
"local": "true"
}
}
},
"shards": [
{
"state": "STARTED",
"primary": true,
"node": "__node_id__",
"relocating_node": null,
"shard": 0,
"index": "my-index",
"version": 2,
"allocation_id": {
"id": "p6c9fBsNSc6SBTq0E0jLZw"
}
},
{
"state": "STARTED",
"primary": true,
"node": "__node_id__",
"relocating_node": null,
"shard": 1,
"index": "my-index",
"version": 2,
"allocation_id": {
"id": "KBOkx9UmRNmlZ6LGsnqxJw"
}
},
{
"state": "STARTED",
"primary": true,
"node": "__node_id__",
"relocating_node": null,
"shard": 2,
"index": "my-index",
"version": 2,
"allocation_id": {
"id": "aHXocqcnRme-y6OJIZX-CQ"
}
}
]
}
}