From 5b519935bed675eeaa4479c4a44efed38e130752 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Thu, 30 Jul 2015 12:11:40 +0200 Subject: [PATCH] Marvel: Add Cluster State collector closes elastic/elasticsearch-marvelelastic/elasticsearch#445 Original commit: elastic/x-pack-elasticsearch@28c8b0f5af740ce98bc4bd5fda2cf26f67f10424 --- .../agent/collector/AbstractCollector.java | 9 +- .../agent/collector/CollectorModule.java | 2 + .../cluster/ClusterStateCollector.java | 62 +++++++++ .../cluster/ClusterStateMarvelDoc.java | 48 +++++++ .../cluster/ClusterStatsCollector.java | 10 +- .../indices/IndexStatsCollector.java | 9 +- .../marvel/agent/renderer/RendererModule.java | 5 + .../cluster/ClusterStateRenderer.java | 70 ++++++++++ .../renderer/indices/IndexStatsRenderer.java | 4 +- .../agent/settings/MarvelSettingsService.java | 9 +- .../main/resources/marvel_index_template.json | 25 ++++ .../cluster/ClusterStateCollectorTests.java | 127 ++++++++++++++++++ .../cluster/ClusterStatsCollectorTests.java | 8 +- .../indices/IndexStatsCollectorTests.java | 4 +- .../cluster/ClusterStateRendererTests.java | 56 ++++++++ .../samples/marvel_cluster_state.json | 56 ++++++++ 16 files changed, 485 insertions(+), 19 deletions(-) create mode 100644 marvel/src/main/java/org/elasticsearch/marvel/agent/collector/cluster/ClusterStateCollector.java create mode 100644 marvel/src/main/java/org/elasticsearch/marvel/agent/collector/cluster/ClusterStateMarvelDoc.java create mode 100644 marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/cluster/ClusterStateRenderer.java create mode 100644 marvel/src/test/java/org/elasticsearch/marvel/agent/collector/cluster/ClusterStateCollectorTests.java create mode 100644 marvel/src/test/java/org/elasticsearch/marvel/agent/renderer/cluster/ClusterStateRendererTests.java create mode 100644 marvel/src/test/resources/samples/marvel_cluster_state.json diff --git a/marvel/src/main/java/org/elasticsearch/marvel/agent/collector/AbstractCollector.java b/marvel/src/main/java/org/elasticsearch/marvel/agent/collector/AbstractCollector.java index d2918ad9220..9de29cee36c 100644 --- a/marvel/src/main/java/org/elasticsearch/marvel/agent/collector/AbstractCollector.java +++ b/marvel/src/main/java/org/elasticsearch/marvel/agent/collector/AbstractCollector.java @@ -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 extends AbstractLifecycleComponent 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 diff --git a/marvel/src/main/java/org/elasticsearch/marvel/agent/collector/CollectorModule.java b/marvel/src/main/java/org/elasticsearch/marvel/agent/collector/CollectorModule.java index 05f780da2c9..2dc4b85a023 100644 --- a/marvel/src/main/java/org/elasticsearch/marvel/agent/collector/CollectorModule.java +++ b/marvel/src/main/java/org/elasticsearch/marvel/agent/collector/CollectorModule.java @@ -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 diff --git a/marvel/src/main/java/org/elasticsearch/marvel/agent/collector/cluster/ClusterStateCollector.java b/marvel/src/main/java/org/elasticsearch/marvel/agent/collector/cluster/ClusterStateCollector.java new file mode 100644 index 00000000000..66f495e8562 --- /dev/null +++ b/marvel/src/main/java/org/elasticsearch/marvel/agent/collector/cluster/ClusterStateCollector.java @@ -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. + *

+ * This collector runs on the master node only and collects the {@link ClusterStateMarvelDoc} document + * at a given frequency. + */ +public class ClusterStateCollector extends AbstractCollector { + + 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 doCollect() throws Exception { + ImmutableList.Builder 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); + } +} diff --git a/marvel/src/main/java/org/elasticsearch/marvel/agent/collector/cluster/ClusterStateMarvelDoc.java b/marvel/src/main/java/org/elasticsearch/marvel/agent/collector/cluster/ClusterStateMarvelDoc.java new file mode 100644 index 00000000000..f5f14aad357 --- /dev/null +++ b/marvel/src/main/java/org/elasticsearch/marvel/agent/collector/cluster/ClusterStateMarvelDoc.java @@ -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 { + + 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; + } + } +} diff --git a/marvel/src/main/java/org/elasticsearch/marvel/agent/collector/cluster/ClusterStatsCollector.java b/marvel/src/main/java/org/elasticsearch/marvel/agent/collector/cluster/ClusterStatsCollector.java index 1cf8c60acda..6ca2430e16e 100644 --- a/marvel/src/main/java/org/elasticsearch/marvel/agent/collector/cluster/ClusterStatsCollector.java +++ b/marvel/src/main/java/org/elasticsearch/marvel/agent/collector/cluster/ClusterStatsCollector.java @@ -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. - *

+ *

* 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 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 diff --git a/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/RendererModule.java b/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/RendererModule.java index 8505bd05f7e..3be43ce7810 100644 --- a/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/RendererModule.java +++ b/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/RendererModule.java @@ -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> entry : renderers.entrySet()) { bind(entry.getValue()).asEagerSingleton(); mbinder.addBinding(entry.getKey()).to(entry.getValue()); diff --git a/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/cluster/ClusterStateRenderer.java b/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/cluster/ClusterStateRenderer.java new file mode 100644 index 00000000000..32edfae3163 --- /dev/null +++ b/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/cluster/ClusterStateRenderer.java @@ -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 { + + 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 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"); + } +} diff --git a/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/indices/IndexStatsRenderer.java b/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/indices/IndexStatsRenderer.java index 0414420231e..56d1c59907e 100644 --- a/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/indices/IndexStatsRenderer.java +++ b/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/indices/IndexStatsRenderer.java @@ -30,9 +30,9 @@ public class IndexStatsRenderer extends AbstractRenderer { @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) { diff --git a/marvel/src/main/java/org/elasticsearch/marvel/agent/settings/MarvelSettingsService.java b/marvel/src/main/java/org/elasticsearch/marvel/agent/settings/MarvelSettingsService.java index 212a3676dc9..5205f7d80ab 100644 --- a/marvel/src/main/java/org/elasticsearch/marvel/agent/settings/MarvelSettingsService.java +++ b/marvel/src/main/java/org/elasticsearch/marvel/agent/settings/MarvelSettingsService.java @@ -27,11 +27,13 @@ public class MarvelSettingsService extends AbstractComponent implements NodeSett private final List 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 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(); + } } diff --git a/marvel/src/main/resources/marvel_index_template.json b/marvel/src/main/resources/marvel_index_template.json index 472f5a13340..74a57fbd6c0 100644 --- a/marvel/src/main/resources/marvel_index_template.json +++ b/marvel/src/main/resources/marvel_index_template.json @@ -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" + } + } + } + } } } } \ No newline at end of file diff --git a/marvel/src/test/java/org/elasticsearch/marvel/agent/collector/cluster/ClusterStateCollectorTests.java b/marvel/src/test/java/org/elasticsearch/marvel/agent/collector/cluster/ClusterStateCollectorTests.java new file mode 100644 index 00000000000..697d402d7ef --- /dev/null +++ b/marvel/src/test/java/org/elasticsearch/marvel/agent/collector/cluster/ClusterStateCollectorTests.java @@ -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 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 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 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()); + } +} diff --git a/marvel/src/test/java/org/elasticsearch/marvel/agent/collector/cluster/ClusterStatsCollectorTests.java b/marvel/src/test/java/org/elasticsearch/marvel/agent/collector/cluster/ClusterStatsCollectorTests.java index edaf9cbcbf8..c358db2cb61 100644 --- a/marvel/src/test/java/org/elasticsearch/marvel/agent/collector/cluster/ClusterStatsCollectorTests.java +++ b/marvel/src/test/java/org/elasticsearch/marvel/agent/collector/cluster/ClusterStatsCollectorTests.java @@ -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()); } } diff --git a/marvel/src/test/java/org/elasticsearch/marvel/agent/collector/indices/IndexStatsCollectorTests.java b/marvel/src/test/java/org/elasticsearch/marvel/agent/collector/indices/IndexStatsCollectorTests.java index 7d314e87a45..3f854518925 100644 --- a/marvel/src/test/java/org/elasticsearch/marvel/agent/collector/indices/IndexStatsCollectorTests.java +++ b/marvel/src/test/java/org/elasticsearch/marvel/agent/collector/indices/IndexStatsCollectorTests.java @@ -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 { diff --git a/marvel/src/test/java/org/elasticsearch/marvel/agent/renderer/cluster/ClusterStateRendererTests.java b/marvel/src/test/java/org/elasticsearch/marvel/agent/renderer/cluster/ClusterStateRendererTests.java new file mode 100644 index 00000000000..631d2719de5 --- /dev/null +++ b/marvel/src/test/java/org/elasticsearch/marvel/agent/renderer/cluster/ClusterStateRendererTests.java @@ -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); + } +} diff --git a/marvel/src/test/resources/samples/marvel_cluster_state.json b/marvel/src/test/resources/samples/marvel_cluster_state.json new file mode 100644 index 00000000000..549ef1ec480 --- /dev/null +++ b/marvel/src/test/resources/samples/marvel_cluster_state.json @@ -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" + } + } + ] + } +} \ No newline at end of file