Skip to content

Commit d4b2d21

Browse files
authored
Add option to filter ILM explain response (#44777)
In order to make it easier to interpret the output of the ILM Explain API, this commit adds two request parameters to that API: - `only_managed`, which causes the response to only contain indices which have `index.lifecycle.name` set - `only_errors`, which causes the response to contain only indices in an ILM error state "Error state" is defined as either being in the `ERROR` step or having `index.lifecycle.name` set to a policy that does not exist.
1 parent 97177a3 commit d4b2d21

File tree

9 files changed

+227
-32
lines changed

9 files changed

+227
-32
lines changed

docs/reference/ilm/apis/explain.asciidoc

+9
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@ about any failures.
2626

2727
==== Request Parameters
2828

29+
`only_managed`::
30+
(boolean) Filters the returned indices to only indices that are managed by
31+
ILM.
32+
33+
`only_errors`::
34+
(boolean) Filters the returned indices to only indices that are managed by
35+
ILM and are in an error state, either due to an encountering an error while
36+
executing the policy, or attempting to use a policy that does not exist.
37+
2938
include::{docdir}/rest-api/timeoutparms.asciidoc[]
3039

3140
==== Authorization

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifecycle/ExplainLifecycleRequest.java

+43-3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66

77
package org.elasticsearch.xpack.core.indexlifecycle;
88

9+
import org.elasticsearch.Version;
910
import org.elasticsearch.action.ActionRequestValidationException;
1011
import org.elasticsearch.action.support.master.info.ClusterInfoRequest;
1112
import org.elasticsearch.common.io.stream.StreamInput;
13+
import org.elasticsearch.common.io.stream.StreamOutput;
1214

1315
import java.io.IOException;
1416
import java.util.Arrays;
@@ -21,13 +23,48 @@
2123
* {@link #indices(String...)} method
2224
*/
2325
public class ExplainLifecycleRequest extends ClusterInfoRequest<ExplainLifecycleRequest> {
26+
private static final Version FILTERS_INTRODUCED_VERSION = Version.V_7_4_0;
27+
28+
private boolean onlyErrors = false;
29+
private boolean onlyManaged = false;
2430

2531
public ExplainLifecycleRequest() {
2632
super();
2733
}
2834

2935
public ExplainLifecycleRequest(StreamInput in) throws IOException {
3036
super(in);
37+
if (in.getVersion().onOrAfter(FILTERS_INTRODUCED_VERSION)) {
38+
onlyErrors = in.readBoolean();
39+
onlyManaged = in.readBoolean();
40+
}
41+
}
42+
43+
@Override
44+
public void writeTo(StreamOutput out) throws IOException {
45+
super.writeTo(out);
46+
if (out.getVersion().onOrAfter(FILTERS_INTRODUCED_VERSION)) {
47+
out.writeBoolean(onlyErrors);
48+
out.writeBoolean(onlyManaged);
49+
}
50+
}
51+
52+
public boolean onlyErrors() {
53+
return onlyErrors;
54+
}
55+
56+
public ExplainLifecycleRequest onlyErrors(boolean onlyErrors) {
57+
this.onlyErrors = onlyErrors;
58+
return this;
59+
}
60+
61+
public boolean onlyManaged() {
62+
return onlyManaged;
63+
}
64+
65+
public ExplainLifecycleRequest onlyManaged(boolean onlyManaged) {
66+
this.onlyManaged = onlyManaged;
67+
return this;
3168
}
3269

3370
@Override
@@ -37,7 +74,7 @@ public ActionRequestValidationException validate() {
3774

3875
@Override
3976
public int hashCode() {
40-
return Objects.hash(Arrays.hashCode(indices()), indicesOptions());
77+
return Objects.hash(Arrays.hashCode(indices()), indicesOptions(), onlyErrors, onlyManaged);
4178
}
4279

4380
@Override
@@ -50,12 +87,15 @@ public boolean equals(Object obj) {
5087
}
5188
ExplainLifecycleRequest other = (ExplainLifecycleRequest) obj;
5289
return Objects.deepEquals(indices(), other.indices()) &&
53-
Objects.equals(indicesOptions(), other.indicesOptions());
90+
Objects.equals(indicesOptions(), other.indicesOptions()) &&
91+
Objects.equals(onlyErrors(), other.onlyErrors()) &&
92+
Objects.equals(onlyManaged(), other.onlyManaged());
5493
}
5594

5695
@Override
5796
public String toString() {
58-
return "ExplainLifecycleRequest [indices()=" + Arrays.toString(indices()) + ", indicesOptions()=" + indicesOptions() + "]";
97+
return "ExplainLifecycleRequest [indices()=" + Arrays.toString(indices()) + ", indicesOptions()=" + indicesOptions() +
98+
", onlyErrors()=" + onlyErrors() + ", onlyManaged()=" + onlyManaged() + "]";
5999
}
60100

61101
}

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifecycle/ExplainLifecycleRequestTests.java

+25-9
Original file line numberDiff line numberDiff line change
@@ -26,28 +26,44 @@ protected ExplainLifecycleRequest createTestInstance() {
2626
randomBoolean(), randomBoolean(), randomBoolean(), randomBoolean());
2727
request.indicesOptions(indicesOptions);
2828
}
29+
if (randomBoolean()) {
30+
request.onlyErrors(randomBoolean());
31+
}
32+
if (randomBoolean()) {
33+
request.onlyManaged(randomBoolean());
34+
}
2935
return request;
3036
}
3137

3238
@Override
3339
protected ExplainLifecycleRequest mutateInstance(ExplainLifecycleRequest instance) throws IOException {
3440
String[] indices = instance.indices();
3541
IndicesOptions indicesOptions = instance.indicesOptions();
36-
switch (between(0, 1)) {
37-
case 0:
38-
indices = randomValueOtherThanMany(i -> Arrays.equals(i, instance.indices()),
42+
boolean onlyErrors = instance.onlyErrors();
43+
boolean onlyManaged = instance.onlyManaged();
44+
switch (between(0, 3)) {
45+
case 0:
46+
indices = randomValueOtherThanMany(i -> Arrays.equals(i, instance.indices()),
3947
() -> generateRandomStringArray(20, 10, false, false));
40-
break;
41-
case 1:
42-
indicesOptions = randomValueOtherThan(indicesOptions, () -> IndicesOptions.fromOptions(randomBoolean(), randomBoolean(),
48+
break;
49+
case 1:
50+
indicesOptions = randomValueOtherThan(indicesOptions, () -> IndicesOptions.fromOptions(randomBoolean(), randomBoolean(),
4351
randomBoolean(), randomBoolean(), randomBoolean(), randomBoolean(), randomBoolean(), randomBoolean()));
44-
break;
45-
default:
46-
throw new AssertionError("Illegal randomisation branch");
52+
break;
53+
case 2:
54+
onlyErrors = !onlyErrors;
55+
break;
56+
case 3:
57+
onlyManaged = !onlyManaged;
58+
break;
59+
default:
60+
throw new AssertionError("Illegal randomisation branch");
4761
}
4862
ExplainLifecycleRequest newRequest = new ExplainLifecycleRequest();
4963
newRequest.indices(indices);
5064
newRequest.indicesOptions(indicesOptions);
65+
newRequest.onlyErrors(onlyErrors);
66+
newRequest.onlyManaged(onlyManaged);
5167
return newRequest;
5268
}
5369

x-pack/plugin/ilm/qa/multi-node/src/test/java/org/elasticsearch/xpack/ilm/TimeSeriesLifecycleActionsIT.java

+50-3
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,11 @@
5454

5555
import static java.util.Collections.singletonMap;
5656
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
57+
import static org.hamcrest.Matchers.allOf;
5758
import static org.hamcrest.Matchers.containsString;
5859
import static org.hamcrest.Matchers.equalTo;
5960
import static org.hamcrest.Matchers.greaterThan;
61+
import static org.hamcrest.Matchers.hasKey;
6062
import static org.hamcrest.Matchers.not;
6163
import static org.hamcrest.Matchers.nullValue;
6264

@@ -838,6 +840,45 @@ public void testCanStopILMWithPolicyUsingNonexistentPolicy() throws Exception {
838840
assertOK(client().performRequest(startILMReqest));
839841
}
840842

843+
public void testExplainFilters() throws Exception {
844+
String goodIndex = index + "-good-000001";
845+
String errorIndex = index + "-error";
846+
String nonexistantPolicyIndex = index + "-nonexistant-policy";
847+
String unmanagedIndex = index + "-unmanaged";
848+
849+
createFullPolicy(TimeValue.ZERO);
850+
851+
createIndexWithSettings(goodIndex, Settings.builder()
852+
.put(RolloverAction.LIFECYCLE_ROLLOVER_ALIAS, "alias")
853+
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)
854+
.put(LifecycleSettings.LIFECYCLE_NAME, policy));
855+
createIndexWithSettingsNoAlias(errorIndex, Settings.builder()
856+
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)
857+
.put(LifecycleSettings.LIFECYCLE_NAME, policy));
858+
createIndexWithSettingsNoAlias(nonexistantPolicyIndex, Settings.builder()
859+
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)
860+
.put(LifecycleSettings.LIFECYCLE_NAME, randomValueOtherThan(policy, () -> randomAlphaOfLengthBetween(3,10))));
861+
createIndexWithSettingsNoAlias(unmanagedIndex, Settings.builder()
862+
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0));
863+
864+
assertBusy(() -> {
865+
Map<String, Map<String, Object>> explainResponse = explain(index + "*", false, false);
866+
assertNotNull(explainResponse);
867+
assertThat(explainResponse,
868+
allOf(hasKey(goodIndex), hasKey(errorIndex), hasKey(nonexistantPolicyIndex), hasKey(unmanagedIndex)));
869+
870+
Map<String, Map<String, Object>> onlyManagedResponse = explain(index + "*", false, true);
871+
assertNotNull(onlyManagedResponse);
872+
assertThat(onlyManagedResponse, allOf(hasKey(goodIndex), hasKey(errorIndex), hasKey(nonexistantPolicyIndex)));
873+
assertThat(onlyManagedResponse, not(hasKey(unmanagedIndex)));
874+
875+
Map<String, Map<String, Object>> onlyErrorsResponse = explain(index + "*", true, randomBoolean());
876+
assertNotNull(onlyErrorsResponse);
877+
assertThat(onlyErrorsResponse, allOf(hasKey(errorIndex), hasKey(nonexistantPolicyIndex)));
878+
assertThat(onlyErrorsResponse, allOf(not(hasKey(goodIndex)), not(hasKey(unmanagedIndex))));
879+
});
880+
}
881+
841882
private void createFullPolicy(TimeValue hotTime) throws IOException {
842883
Map<String, LifecycleAction> hotActions = new HashMap<>();
843884
hotActions.put(SetPriorityAction.NAME, new SetPriorityAction(100));
@@ -957,15 +998,21 @@ private String getReasonForIndex(String indexName) throws IOException {
957998
}
958999

9591000
private Map<String, Object> explainIndex(String indexName) throws IOException {
960-
Request explainRequest = new Request("GET", indexName + "/_ilm/explain");
1001+
return explain(indexName, false, false).get(indexName);
1002+
}
1003+
1004+
private Map<String, Map<String, Object>> explain(String indexPattern, boolean onlyErrors, boolean onlyManaged) throws IOException {
1005+
Request explainRequest = new Request("GET", indexPattern + "/_ilm/explain");
1006+
explainRequest.addParameter("only_errors", Boolean.toString(onlyErrors));
1007+
explainRequest.addParameter("only_managed", Boolean.toString(onlyManaged));
9611008
Response response = client().performRequest(explainRequest);
9621009
Map<String, Object> responseMap;
9631010
try (InputStream is = response.getEntity().getContent()) {
9641011
responseMap = XContentHelper.convertToMap(XContentType.JSON.xContent(), is, true);
9651012
}
9661013

967-
@SuppressWarnings("unchecked") Map<String, Object> indexResponse = ((Map<String, Map<String, Object>>) responseMap.get("indices"))
968-
.get(indexName);
1014+
@SuppressWarnings("unchecked") Map<String, Map<String, Object>> indexResponse =
1015+
((Map<String, Map<String, Object>>) responseMap.get("indices"));
9691016
return indexResponse;
9701017
}
9711018

x-pack/plugin/ilm/qa/rest/src/test/resources/rest-api-spec/test/ilm/40_explain_lifecycle.yml

+53
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ setup:
6060
- do:
6161
indices.create:
6262
index: my_index_no_policy
63+
- do:
64+
indices.create:
65+
index: index_with_policy_that_doesnt_exist
66+
body:
67+
settings:
68+
index.lifecycle.name: "a_policy_that_doesnt_exist"
6369

6470
---
6571
teardown:
@@ -81,6 +87,10 @@ teardown:
8187
indices.delete:
8288
index: my_index_no_policy
8389

90+
- do:
91+
indices.delete:
92+
index: index_with_policy_that_doesnt_exist
93+
8494
- do:
8595
ilm.delete_lifecycle:
8696
policy: "my_moveable_timeseries_lifecycle"
@@ -112,6 +122,7 @@ teardown:
112122
- is_false: indices.my_index2
113123
- is_false: indices.another_index
114124
- is_false: indices.unmanaged_index
125+
- is_false: indices.index_with_policy_that_doesnt_exist
115126

116127
---
117128
"Test Wildcard Index Lifecycle Explain":
@@ -146,6 +157,7 @@ teardown:
146157

147158
- is_false: indices.another_index
148159
- is_false: indices.unmanaged_index
160+
- is_false: indices.index_with_policy_that_doesnt_exist
149161

150162

151163
---
@@ -201,6 +213,16 @@ teardown:
201213
- is_false: indices.another_index.failed_step
202214
- is_false: indices.another_index.step_info
203215

216+
- match: { indices.index_with_policy_that_doesnt_exist.index: "index_with_policy_that_doesnt_exist" }
217+
- match: { indices.index_with_policy_that_doesnt_exist.policy: "a_policy_that_doesnt_exist" }
218+
- match: { indices.index_with_policy_that_doesnt_exist.step_info.reason: "policy [a_policy_that_doesnt_exist] does not exist" }
219+
- is_true: indices.index_with_policy_that_doesnt_exist.managed
220+
- is_false: indices.index_with_policy_that_doesnt_exist.phase
221+
- is_false: indices.index_with_policy_that_doesnt_exist.action
222+
- is_false: indices.index_with_policy_that_doesnt_exist.step
223+
- is_false: indices.index_with_policy_that_doesnt_exist.age
224+
- is_false: indices.index_with_policy_that_doesnt_exist.failed_step
225+
204226
---
205227
"Test Unmanaged Index Lifecycle Explain":
206228

@@ -221,3 +243,34 @@ teardown:
221243
- is_false: indices.my_index
222244
- is_false: indices.my_index2
223245
- is_false: indices.another_index
246+
- is_false: indices.index_with_policy_that_doesnt_exist
247+
248+
---
249+
"Test filter for only managed indices":
250+
251+
- do:
252+
ilm.explain_lifecycle:
253+
index: "*"
254+
only_managed: true
255+
256+
- match: { indices.my_index.index: "my_index" }
257+
- match: { indices.my_index2.index: "my_index2" }
258+
- match: { indices.another_index.index: "another_index" }
259+
- match: { indices.index_with_policy_that_doesnt_exist.index: "index_with_policy_that_doesnt_exist" }
260+
- is_false: indices.unmanaged_index
261+
- is_false: indices.my_index_no_policy
262+
263+
---
264+
"Test filter for only error indices":
265+
266+
- do:
267+
ilm.explain_lifecycle:
268+
index: "*"
269+
only_errors: true
270+
271+
- match: { indices.index_with_policy_that_doesnt_exist.index: "index_with_policy_that_doesnt_exist" }
272+
- is_false: indices.unmanaged_index
273+
- is_false: indices.my_index_no_policy
274+
- is_false: indices.my_index
275+
- is_false: indices.my_index2
276+
- is_false: indices.another_index

x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycleService.java

+4
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,10 @@ public void triggered(SchedulerEngine.Event event) {
214214
}
215215
}
216216

217+
public boolean policyExists(String policyId) {
218+
return policyRegistry.policyExists(policyId);
219+
}
220+
217221
/**
218222
* executes the policy execution on the appropriate indices by running cluster-state tasks per index.
219223
*

x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/RestExplainLifecycleAction.java

+2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient
3737
ExplainLifecycleRequest explainLifecycleRequest = new ExplainLifecycleRequest();
3838
explainLifecycleRequest.indices(indexes);
3939
explainLifecycleRequest.indicesOptions(IndicesOptions.fromRequest(restRequest, IndicesOptions.strictExpandOpen()));
40+
explainLifecycleRequest.onlyManaged(restRequest.paramAsBoolean("only_managed", false));
41+
explainLifecycleRequest.onlyErrors(restRequest.paramAsBoolean("only_errors", false));
4042
String masterNodeTimeout = restRequest.param("master_timeout");
4143
if (masterNodeTimeout != null) {
4244
explainLifecycleRequest.masterNodeTimeout(masterNodeTimeout);

0 commit comments

Comments
 (0)