Remove lenient stats parsing
Today when parsing a stats request, Elasticsearch silently ignores incorrect metrics. This commit removes lenient parsing of stats requests for the nodes stats and indices stats APIs. Relates #21417
This commit is contained in:
parent
568a7ea5f1
commit
f5ac0e5076
|
@ -71,49 +71,58 @@ public abstract class BaseRestHandler extends AbstractComponent implements RestH
|
|||
request.unconsumedParams().stream().filter(p -> !responseParams().contains(p)).collect(Collectors.toCollection(TreeSet::new));
|
||||
|
||||
// validate the non-response params
|
||||
if (unconsumedParams.isEmpty() == false) {
|
||||
String message = String.format(
|
||||
Locale.ROOT,
|
||||
"request [%s] contains unrecognized parameter%s: ",
|
||||
request.path(),
|
||||
unconsumedParams.size() > 1 ? "s" : "");
|
||||
boolean first = true;
|
||||
for (final String unconsumedParam : unconsumedParams) {
|
||||
final LevensteinDistance ld = new LevensteinDistance();
|
||||
final List<Tuple<Float, String>> scoredParams = new ArrayList<>();
|
||||
final Set<String> candidateParams = new HashSet<>();
|
||||
candidateParams.addAll(request.consumedParams());
|
||||
candidateParams.addAll(responseParams());
|
||||
for (final String candidateParam : candidateParams) {
|
||||
final float distance = ld.getDistance(unconsumedParam, candidateParam);
|
||||
if (distance > 0.5f) {
|
||||
scoredParams.add(new Tuple<>(distance, candidateParam));
|
||||
}
|
||||
}
|
||||
CollectionUtil.timSort(scoredParams, (a, b) -> {
|
||||
// sort by distance in reverse order, then parameter name for equal distances
|
||||
int compare = a.v1().compareTo(b.v1());
|
||||
if (compare != 0) return -compare;
|
||||
else return a.v2().compareTo(b.v2());
|
||||
});
|
||||
if (first == false) {
|
||||
message += ", ";
|
||||
}
|
||||
message += "[" + unconsumedParam + "]";
|
||||
final List<String> keys = scoredParams.stream().map(Tuple::v2).collect(Collectors.toList());
|
||||
if (keys.isEmpty() == false) {
|
||||
message += " -> did you mean " + (keys.size() == 1 ? "[" + keys.get(0) + "]": "any of " + keys.toString()) + "?";
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(message);
|
||||
if (!unconsumedParams.isEmpty()) {
|
||||
final Set<String> candidateParams = new HashSet<>();
|
||||
candidateParams.addAll(request.consumedParams());
|
||||
candidateParams.addAll(responseParams());
|
||||
throw new IllegalArgumentException(unrecognized(request, unconsumedParams, candidateParams, "parameter"));
|
||||
}
|
||||
|
||||
// execute the action
|
||||
action.accept(channel);
|
||||
}
|
||||
|
||||
protected final String unrecognized(
|
||||
final RestRequest request,
|
||||
final Set<String> invalids,
|
||||
final Set<String> candidates,
|
||||
final String detail) {
|
||||
String message = String.format(
|
||||
Locale.ROOT,
|
||||
"request [%s] contains unrecognized %s%s: ",
|
||||
request.path(),
|
||||
detail,
|
||||
invalids.size() > 1 ? "s" : "");
|
||||
boolean first = true;
|
||||
for (final String invalid : invalids) {
|
||||
final LevensteinDistance ld = new LevensteinDistance();
|
||||
final List<Tuple<Float, String>> scoredParams = new ArrayList<>();
|
||||
for (final String candidate : candidates) {
|
||||
final float distance = ld.getDistance(invalid, candidate);
|
||||
if (distance > 0.5f) {
|
||||
scoredParams.add(new Tuple<>(distance, candidate));
|
||||
}
|
||||
}
|
||||
CollectionUtil.timSort(scoredParams, (a, b) -> {
|
||||
// sort by distance in reverse order, then parameter name for equal distances
|
||||
int compare = a.v1().compareTo(b.v1());
|
||||
if (compare != 0) return -compare;
|
||||
else return a.v2().compareTo(b.v2());
|
||||
});
|
||||
if (first == false) {
|
||||
message += ", ";
|
||||
}
|
||||
message += "[" + invalid + "]";
|
||||
final List<String> keys = scoredParams.stream().map(Tuple::v2).collect(Collectors.toList());
|
||||
if (keys.isEmpty() == false) {
|
||||
message += " -> did you mean " + (keys.size() == 1 ? "[" + keys.get(0) + "]" : "any of " + keys.toString()) + "?";
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* REST requests are handled by preparing a channel consumer that represents the execution of
|
||||
* the request against a channel.
|
||||
|
|
|
@ -55,7 +55,7 @@ public class RestNodesInfoAction extends BaseRestHandler {
|
|||
public RestNodesInfoAction(Settings settings, RestController controller, SettingsFilter settingsFilter) {
|
||||
super(settings);
|
||||
controller.registerHandler(GET, "/_nodes", this);
|
||||
// this endpoint is used for metrics, not for nodeIds, like /_nodes/fs
|
||||
// this endpoint is used for metrics, not for node IDs, like /_nodes/fs
|
||||
controller.registerHandler(GET, "/_nodes/{nodeId}", this);
|
||||
controller.registerHandler(GET, "/_nodes/{nodeId}/{metrics}", this);
|
||||
// added this endpoint to be aligned with stats
|
||||
|
|
|
@ -33,7 +33,13 @@ import org.elasticsearch.rest.action.RestActions.NodesResponseRestListener;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static org.elasticsearch.rest.RestRequest.Method.GET;
|
||||
|
||||
|
@ -48,9 +54,38 @@ public class RestNodesStatsAction extends BaseRestHandler {
|
|||
controller.registerHandler(GET, "/_nodes/stats/{metric}", this);
|
||||
controller.registerHandler(GET, "/_nodes/{nodeId}/stats/{metric}", this);
|
||||
|
||||
controller.registerHandler(GET, "/_nodes/stats/{metric}/{indexMetric}", this);
|
||||
controller.registerHandler(GET, "/_nodes/stats/{metric}/{index_metric}", this);
|
||||
|
||||
controller.registerHandler(GET, "/_nodes/{nodeId}/stats/{metric}/{indexMetric}", this);
|
||||
controller.registerHandler(GET, "/_nodes/{nodeId}/stats/{metric}/{index_metric}", this);
|
||||
}
|
||||
|
||||
static final Map<String, Consumer<NodesStatsRequest>> METRICS;
|
||||
|
||||
static {
|
||||
final Map<String, Consumer<NodesStatsRequest>> metrics = new HashMap<>();
|
||||
metrics.put("os", r -> r.os(true));
|
||||
metrics.put("jvm", r -> r.jvm(true));
|
||||
metrics.put("thread_pool", r -> r.threadPool(true));
|
||||
metrics.put("fs", r -> r.fs(true));
|
||||
metrics.put("transport", r -> r.transport(true));
|
||||
metrics.put("http", r -> r.http(true));
|
||||
metrics.put("indices", r -> r.indices(true));
|
||||
metrics.put("process", r -> r.process(true));
|
||||
metrics.put("breaker", r -> r.breaker(true));
|
||||
metrics.put("script", r -> r.script(true));
|
||||
metrics.put("discovery", r -> r.discovery(true));
|
||||
metrics.put("ingest", r -> r.ingest(true));
|
||||
METRICS = Collections.unmodifiableMap(metrics);
|
||||
}
|
||||
|
||||
static final Map<String, Consumer<CommonStatsFlags>> FLAGS;
|
||||
|
||||
static {
|
||||
final Map<String, Consumer<CommonStatsFlags>> flags = new HashMap<>();
|
||||
for (final Flag flag : CommonStatsFlags.Flag.values()) {
|
||||
flags.put(flag.getRestName(), f -> f.set(flag, true));
|
||||
}
|
||||
FLAGS = Collections.unmodifiableMap(flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -62,35 +97,72 @@ public class RestNodesStatsAction extends BaseRestHandler {
|
|||
nodesStatsRequest.timeout(request.param("timeout"));
|
||||
|
||||
if (metrics.size() == 1 && metrics.contains("_all")) {
|
||||
if (request.hasParam("index_metric")) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
Locale.ROOT,
|
||||
"request [%s] contains index metrics [%s] but all stats requested",
|
||||
request.path(),
|
||||
request.param("index_metric")));
|
||||
}
|
||||
nodesStatsRequest.all();
|
||||
nodesStatsRequest.indices(CommonStatsFlags.ALL);
|
||||
} else if (metrics.contains("_all")) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(Locale.ROOT,
|
||||
"request [%s] contains _all and individual metrics [%s]",
|
||||
request.path(),
|
||||
request.param("metric")));
|
||||
} else {
|
||||
nodesStatsRequest.clear();
|
||||
nodesStatsRequest.os(metrics.contains("os"));
|
||||
nodesStatsRequest.jvm(metrics.contains("jvm"));
|
||||
nodesStatsRequest.threadPool(metrics.contains("thread_pool"));
|
||||
nodesStatsRequest.fs(metrics.contains("fs"));
|
||||
nodesStatsRequest.transport(metrics.contains("transport"));
|
||||
nodesStatsRequest.http(metrics.contains("http"));
|
||||
nodesStatsRequest.indices(metrics.contains("indices"));
|
||||
nodesStatsRequest.process(metrics.contains("process"));
|
||||
nodesStatsRequest.breaker(metrics.contains("breaker"));
|
||||
nodesStatsRequest.script(metrics.contains("script"));
|
||||
nodesStatsRequest.discovery(metrics.contains("discovery"));
|
||||
nodesStatsRequest.ingest(metrics.contains("ingest"));
|
||||
|
||||
// use a sorted set so the unrecognized parameters appear in a reliable sorted order
|
||||
final Set<String> invalidMetrics = new TreeSet<>();
|
||||
for (final String metric : metrics) {
|
||||
final Consumer<NodesStatsRequest> handler = METRICS.get(metric);
|
||||
if (handler != null) {
|
||||
handler.accept(nodesStatsRequest);
|
||||
} else {
|
||||
invalidMetrics.add(metric);
|
||||
}
|
||||
}
|
||||
|
||||
if (!invalidMetrics.isEmpty()) {
|
||||
throw new IllegalArgumentException(unrecognized(request, invalidMetrics, METRICS.keySet(), "metric"));
|
||||
}
|
||||
|
||||
// check for index specific metrics
|
||||
if (metrics.contains("indices")) {
|
||||
Set<String> indexMetrics = Strings.splitStringByCommaToSet(request.param("indexMetric", "_all"));
|
||||
Set<String> indexMetrics = Strings.splitStringByCommaToSet(request.param("index_metric", "_all"));
|
||||
if (indexMetrics.size() == 1 && indexMetrics.contains("_all")) {
|
||||
nodesStatsRequest.indices(CommonStatsFlags.ALL);
|
||||
} else {
|
||||
CommonStatsFlags flags = new CommonStatsFlags();
|
||||
for (Flag flag : CommonStatsFlags.Flag.values()) {
|
||||
flags.set(flag, indexMetrics.contains(flag.getRestName()));
|
||||
flags.clear();
|
||||
// use a sorted set so the unrecognized parameters appear in a reliable sorted order
|
||||
final Set<String> invalidIndexMetrics = new TreeSet<>();
|
||||
for (final String indexMetric : indexMetrics) {
|
||||
final Consumer<CommonStatsFlags> handler = FLAGS.get(indexMetric);
|
||||
if (handler != null) {
|
||||
handler.accept(flags);
|
||||
} else {
|
||||
invalidIndexMetrics.add(indexMetric);
|
||||
}
|
||||
}
|
||||
|
||||
if (!invalidIndexMetrics.isEmpty()) {
|
||||
throw new IllegalArgumentException(unrecognized(request, invalidIndexMetrics, FLAGS.keySet(), "index metric"));
|
||||
}
|
||||
|
||||
nodesStatsRequest.indices(flags);
|
||||
}
|
||||
} else if (request.hasParam("index_metric")) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
Locale.ROOT,
|
||||
"request [%s] contains index metrics [%s] but indices stats not requested",
|
||||
request.path(),
|
||||
request.param("index_metric")));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,13 @@ import org.elasticsearch.rest.action.RestBuilderListener;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static org.elasticsearch.rest.RestRequest.Method.GET;
|
||||
import static org.elasticsearch.rest.RestStatus.OK;
|
||||
|
@ -49,11 +55,34 @@ public class RestIndicesStatsAction extends BaseRestHandler {
|
|||
super(settings);
|
||||
controller.registerHandler(GET, "/_stats", this);
|
||||
controller.registerHandler(GET, "/_stats/{metric}", this);
|
||||
controller.registerHandler(GET, "/_stats/{metric}/{indexMetric}", this);
|
||||
controller.registerHandler(GET, "/{index}/_stats", this);
|
||||
controller.registerHandler(GET, "/{index}/_stats/{metric}", this);
|
||||
}
|
||||
|
||||
static Map<String, Consumer<IndicesStatsRequest>> METRICS;
|
||||
|
||||
static {
|
||||
final Map<String, Consumer<IndicesStatsRequest>> metrics = new HashMap<>();
|
||||
metrics.put("docs", r -> r.docs(true));
|
||||
metrics.put("store", r -> r.store(true));
|
||||
metrics.put("indexing", r -> r.indexing(true));
|
||||
metrics.put("search", r -> r.search(true));
|
||||
metrics.put("suggest", r -> r.search(true));
|
||||
metrics.put("get", r -> r.get(true));
|
||||
metrics.put("merge", r -> r.merge(true));
|
||||
metrics.put("refresh", r -> r.refresh(true));
|
||||
metrics.put("flush", r -> r.flush(true));
|
||||
metrics.put("warmer", r -> r.warmer(true));
|
||||
metrics.put("query_cache", r -> r.queryCache(true));
|
||||
metrics.put("segments", r -> r.segments(true));
|
||||
metrics.put("fielddata", r -> r.fieldData(true));
|
||||
metrics.put("completion", r -> r.completion(true));
|
||||
metrics.put("request_cache", r -> r.requestCache(true));
|
||||
metrics.put("recovery", r -> r.recovery(true));
|
||||
metrics.put("translog", r -> r.translog(true));
|
||||
METRICS = Collections.unmodifiableMap(metrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
|
||||
IndicesStatsRequest indicesStatsRequest = new IndicesStatsRequest();
|
||||
|
@ -65,24 +94,28 @@ public class RestIndicesStatsAction extends BaseRestHandler {
|
|||
// short cut, if no metrics have been specified in URI
|
||||
if (metrics.size() == 1 && metrics.contains("_all")) {
|
||||
indicesStatsRequest.all();
|
||||
} else if (metrics.contains("_all")) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(Locale.ROOT,
|
||||
"request [%s] contains _all and individual metrics [%s]",
|
||||
request.path(),
|
||||
request.param("metric")));
|
||||
} else {
|
||||
indicesStatsRequest.clear();
|
||||
indicesStatsRequest.docs(metrics.contains("docs"));
|
||||
indicesStatsRequest.store(metrics.contains("store"));
|
||||
indicesStatsRequest.indexing(metrics.contains("indexing"));
|
||||
indicesStatsRequest.search(metrics.contains("search") || metrics.contains("suggest"));
|
||||
indicesStatsRequest.get(metrics.contains("get"));
|
||||
indicesStatsRequest.merge(metrics.contains("merge"));
|
||||
indicesStatsRequest.refresh(metrics.contains("refresh"));
|
||||
indicesStatsRequest.flush(metrics.contains("flush"));
|
||||
indicesStatsRequest.warmer(metrics.contains("warmer"));
|
||||
indicesStatsRequest.queryCache(metrics.contains("query_cache"));
|
||||
indicesStatsRequest.segments(metrics.contains("segments"));
|
||||
indicesStatsRequest.fieldData(metrics.contains("fielddata"));
|
||||
indicesStatsRequest.completion(metrics.contains("completion"));
|
||||
indicesStatsRequest.requestCache(metrics.contains("request_cache"));
|
||||
indicesStatsRequest.recovery(metrics.contains("recovery"));
|
||||
indicesStatsRequest.translog(metrics.contains("translog"));
|
||||
// use a sorted set so the unrecognized parameters appear in a reliable sorted order
|
||||
final Set<String> invalidMetrics = new TreeSet<>();
|
||||
for (final String metric : metrics) {
|
||||
final Consumer<IndicesStatsRequest> consumer = METRICS.get(metric);
|
||||
if (consumer != null) {
|
||||
consumer.accept(indicesStatsRequest);
|
||||
} else {
|
||||
invalidMetrics.add(metric);
|
||||
}
|
||||
}
|
||||
|
||||
if (!invalidMetrics.isEmpty()) {
|
||||
throw new IllegalArgumentException(unrecognized(request, invalidMetrics, METRICS.keySet(), "metric"));
|
||||
}
|
||||
}
|
||||
|
||||
if (request.hasParam("groups")) {
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* 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.rest.action.admin.cluster;
|
||||
|
||||
import org.elasticsearch.client.node.NodeClient;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.rest.RestController;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.test.rest.FakeRestRequest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.object.HasToString.hasToString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class RestNodesStatsActionTests extends ESTestCase {
|
||||
|
||||
private RestNodesStatsAction action;
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
action = new RestNodesStatsAction(Settings.EMPTY, new RestController(Settings.EMPTY, Collections.emptySet()));
|
||||
}
|
||||
|
||||
public void testUnrecognizedMetric() throws IOException {
|
||||
final HashMap<String, String> params = new HashMap<>();
|
||||
final String metric = randomAsciiOfLength(64);
|
||||
params.put("metric", metric);
|
||||
final RestRequest request = new FakeRestRequest.Builder().withPath("/_nodes/stats").withParams(params).build();
|
||||
final IllegalArgumentException e = expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> action.prepareRequest(request, mock(NodeClient.class)));
|
||||
assertThat(e, hasToString(containsString("request [/_nodes/stats] contains unrecognized metric: [" + metric + "]")));
|
||||
}
|
||||
|
||||
public void testUnrecognizedMetricDidYouMean() throws IOException {
|
||||
final HashMap<String, String> params = new HashMap<>();
|
||||
params.put("metric", "os,transprot,unrecognized");
|
||||
final RestRequest request = new FakeRestRequest.Builder().withPath("/_nodes/stats").withParams(params).build();
|
||||
final IllegalArgumentException e = expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> action.prepareRequest(request, mock(NodeClient.class)));
|
||||
assertThat(
|
||||
e,
|
||||
hasToString(
|
||||
containsString(
|
||||
"request [/_nodes/stats] contains unrecognized metrics: [transprot] -> did you mean [transport]?, [unrecognized]")));
|
||||
}
|
||||
|
||||
public void testAllRequestWithOtherMetrics() throws IOException {
|
||||
final HashMap<String, String> params = new HashMap<>();
|
||||
final String metric = randomSubsetOf(1, RestNodesStatsAction.METRICS.keySet()).get(0);
|
||||
params.put("metric", "_all," + metric);
|
||||
final RestRequest request = new FakeRestRequest.Builder().withPath("/_nodes/stats").withParams(params).build();
|
||||
final IllegalArgumentException e = expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> action.prepareRequest(request, mock(NodeClient.class)));
|
||||
assertThat(e, hasToString(containsString("request [/_nodes/stats] contains _all and individual metrics [_all," + metric + "]")));
|
||||
}
|
||||
|
||||
public void testUnrecognizedIndexMetric() {
|
||||
final HashMap<String, String> params = new HashMap<>();
|
||||
params.put("metric", "indices");
|
||||
final String indexMetric = randomAsciiOfLength(64);
|
||||
params.put("index_metric", indexMetric);
|
||||
final RestRequest request = new FakeRestRequest.Builder().withPath("/_nodes/stats").withParams(params).build();
|
||||
final IllegalArgumentException e = expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> action.prepareRequest(request, mock(NodeClient.class)));
|
||||
assertThat(e, hasToString(containsString("request [/_nodes/stats] contains unrecognized index metric: [" + indexMetric + "]")));
|
||||
}
|
||||
|
||||
public void testUnrecognizedIndexMetricDidYouMean() {
|
||||
final HashMap<String, String> params = new HashMap<>();
|
||||
params.put("metric", "indices");
|
||||
params.put("index_metric", "indexing,stroe,unrecognized");
|
||||
final RestRequest request = new FakeRestRequest.Builder().withPath("/_nodes/stats").withParams(params).build();
|
||||
final IllegalArgumentException e = expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> action.prepareRequest(request, mock(NodeClient.class)));
|
||||
assertThat(
|
||||
e,
|
||||
hasToString(
|
||||
containsString(
|
||||
"request [/_nodes/stats] contains unrecognized index metrics: [stroe] -> did you mean [store]?, [unrecognized]")));
|
||||
}
|
||||
|
||||
public void testIndexMetricsRequestWithoutIndicesMetric() throws IOException {
|
||||
final HashMap<String, String> params = new HashMap<>();
|
||||
final Set<String> metrics = new HashSet<>(RestNodesStatsAction.METRICS.keySet());
|
||||
metrics.remove("indices");
|
||||
params.put("metric", randomSubsetOf(1, metrics).get(0));
|
||||
final String indexMetric = randomSubsetOf(1, RestNodesStatsAction.FLAGS.keySet()).get(0);
|
||||
params.put("index_metric", indexMetric);
|
||||
final RestRequest request = new FakeRestRequest.Builder().withPath("/_nodes/stats").withParams(params).build();
|
||||
final IllegalArgumentException e = expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> action.prepareRequest(request, mock(NodeClient.class)));
|
||||
assertThat(
|
||||
e,
|
||||
hasToString(
|
||||
containsString("request [/_nodes/stats] contains index metrics [" + indexMetric + "] but indices stats not requested")));
|
||||
}
|
||||
|
||||
public void testIndexMetricsRequestOnAllRequest() throws IOException {
|
||||
final HashMap<String, String> params = new HashMap<>();
|
||||
params.put("metric", "_all");
|
||||
final String indexMetric = randomSubsetOf(1, RestNodesStatsAction.FLAGS.keySet()).get(0);
|
||||
params.put("index_metric", indexMetric);
|
||||
final RestRequest request = new FakeRestRequest.Builder().withPath("/_nodes/stats").withParams(params).build();
|
||||
final IllegalArgumentException e = expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> action.prepareRequest(request, mock(NodeClient.class)));
|
||||
assertThat(
|
||||
e,
|
||||
hasToString(
|
||||
containsString("request [/_nodes/stats] contains index metrics [" + indexMetric + "] but all stats requested")));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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.rest.action.admin.indices;
|
||||
|
||||
import org.elasticsearch.client.node.NodeClient;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.rest.RestController;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.test.rest.FakeRestRequest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.object.HasToString.hasToString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class RestIndicesStatsActionTests extends ESTestCase {
|
||||
|
||||
private RestIndicesStatsAction action;
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
action = new RestIndicesStatsAction(Settings.EMPTY, new RestController(Settings.EMPTY, Collections.emptySet()));
|
||||
}
|
||||
|
||||
public void testUnrecognizedMetric() throws IOException {
|
||||
final HashMap<String, String> params = new HashMap<>();
|
||||
final String metric = randomAsciiOfLength(64);
|
||||
params.put("metric", metric);
|
||||
final RestRequest request = new FakeRestRequest.Builder().withPath("/_stats").withParams(params).build();
|
||||
final IllegalArgumentException e = expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> action.prepareRequest(request, mock(NodeClient.class)));
|
||||
assertThat(e, hasToString(containsString("request [/_stats] contains unrecognized metric: [" + metric + "]")));
|
||||
}
|
||||
|
||||
public void testUnrecognizedMetricDidYouMean() throws IOException {
|
||||
final HashMap<String, String> params = new HashMap<>();
|
||||
params.put("metric", "request_cache,fieldata,unrecognized");
|
||||
final RestRequest request = new FakeRestRequest.Builder().withPath("/_stats").withParams(params).build();
|
||||
final IllegalArgumentException e = expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> action.prepareRequest(request, mock(NodeClient.class)));
|
||||
assertThat(
|
||||
e,
|
||||
hasToString(
|
||||
containsString(
|
||||
"request [/_stats] contains unrecognized metrics: [fieldata] -> did you mean [fielddata]?, [unrecognized]")));
|
||||
}
|
||||
|
||||
public void testAllRequestWithOtherMetrics() throws IOException {
|
||||
final HashMap<String, String> params = new HashMap<>();
|
||||
final String metric = randomSubsetOf(1, RestIndicesStatsAction.METRICS.keySet()).get(0);
|
||||
params.put("metric", "_all," + metric);
|
||||
final RestRequest request = new FakeRestRequest.Builder().withPath("/_stats").withParams(params).build();
|
||||
final IllegalArgumentException e = expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> action.prepareRequest(request, mock(NodeClient.class)));
|
||||
assertThat(e, hasToString(containsString("request [/_stats] contains _all and individual metrics [_all," + metric + "]")));
|
||||
}
|
||||
|
||||
}
|
|
@ -65,12 +65,11 @@ of `indices`, `os`, `process`, `jvm`, `transport`, `http`,
|
|||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
# return indices and os
|
||||
curl -XGET 'http://localhost:9200/_nodes/stats/os'
|
||||
# return just indices
|
||||
curl -XGET 'http://localhost:9200/_nodes/stats/indices'
|
||||
# return just os and process
|
||||
curl -XGET 'http://localhost:9200/_nodes/stats/os,process'
|
||||
# specific type endpoint
|
||||
curl -XGET 'http://localhost:9200/_nodes/stats/process'
|
||||
# return just process for node with IP address 10.0.0.1
|
||||
curl -XGET 'http://localhost:9200/_nodes/10.0.0.1/stats/process'
|
||||
--------------------------------------------------
|
||||
|
||||
|
@ -280,27 +279,45 @@ the current running process:
|
|||
`process.mem.total_virtual_in_bytes`::
|
||||
Size in bytes of virtual memory that is guaranteed to be available to the running process
|
||||
|
||||
|
||||
[float]
|
||||
[[field-data]]
|
||||
=== Field data statistics
|
||||
[[indices-stats]]
|
||||
=== Indices statistics
|
||||
|
||||
You can get information about field data memory usage on node
|
||||
level or on index level.
|
||||
You can get information about indices stats on node level or on index level.
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
# Node Stats
|
||||
curl -XGET 'http://localhost:9200/_nodes/stats/indices/?fields=field1,field2&pretty'
|
||||
# Node level
|
||||
curl -XGET 'http://localhost:9200/_nodes/stats/indices/fielddata?fields=field1,field2&pretty'
|
||||
|
||||
# Indices Stat
|
||||
# Index level
|
||||
curl -XGET 'http://localhost:9200/_stats/fielddata/?fields=field1,field2&pretty'
|
||||
|
||||
# You can use wildcards for field names
|
||||
curl -XGET 'http://localhost:9200/_nodes/stats/indices/fielddata?fields=field*&pretty'
|
||||
curl -XGET 'http://localhost:9200/_stats/fielddata/?fields=field*&pretty'
|
||||
curl -XGET 'http://localhost:9200/_nodes/stats/indices/?fields=field*&pretty'
|
||||
--------------------------------------------------
|
||||
|
||||
Supported metrics are:
|
||||
|
||||
* `completion`
|
||||
* `docs`
|
||||
* `fielddata`
|
||||
* `flush`
|
||||
* `get`
|
||||
* `indexing`
|
||||
* `merge`
|
||||
* `query_cache`
|
||||
* `recovery`
|
||||
* `refresh`
|
||||
* `request_cache`
|
||||
* `search`
|
||||
* `segments`
|
||||
* `store`
|
||||
* `suggest`
|
||||
* `translog`
|
||||
* `warmer`
|
||||
|
||||
[float]
|
||||
[[search-groups]]
|
||||
=== Search groups
|
||||
|
|
|
@ -74,7 +74,7 @@ the <<indices-stats,indices stats>> API:
|
|||
|
||||
[source,sh]
|
||||
--------------------------------------------------
|
||||
GET twitter/_stats/commit?level=shards
|
||||
GET twitter/_stats?level=shards
|
||||
--------------------------------------------------
|
||||
// CONSOLE
|
||||
// TEST[s/^/PUT twitter\n/]
|
||||
|
|
|
@ -100,3 +100,17 @@ setup:
|
|||
- is_false: indices.test1
|
||||
- is_true: indices.test2
|
||||
|
||||
---
|
||||
"Indices stats unrecognized parameter":
|
||||
- skip:
|
||||
version: " - 5.99.99"
|
||||
reason: awaits strict stats handling to be backported to 5.x
|
||||
- do:
|
||||
indices.stats:
|
||||
metric: [ fieldata ]
|
||||
ignore: 400
|
||||
|
||||
- match: { status: 400 }
|
||||
- match: { error.type: illegal_argument_exception }
|
||||
- match: { error.reason: "request [/_stats/fieldata] contains unrecognized metric: [fieldata] -> did you mean [fielddata]?" }
|
||||
|
||||
|
|
|
@ -20,3 +20,17 @@
|
|||
level: "indices"
|
||||
|
||||
- is_true: nodes.$master.indices.indices
|
||||
|
||||
---
|
||||
"Nodes stats unrecognized parameter":
|
||||
- skip:
|
||||
version: " - 5.99.99"
|
||||
reason: awaits strict stats handling to be backported to 5.x
|
||||
- do:
|
||||
nodes.stats:
|
||||
metric: [ transprot ]
|
||||
ignore: 400
|
||||
|
||||
- match: { status: 400 }
|
||||
- match: { error.type: illegal_argument_exception }
|
||||
- match: { error.reason: "request [/_nodes/stats/transprot] contains unrecognized metric: [transprot] -> did you mean [transport]?" }
|
||||
|
|
Loading…
Reference in New Issue