ソースを参照

Add phase configs to ILM telemetry (#74575)

This PR adds some ILM action configurations to each set of phase stats within the 
ILM usage response. Additionally, the change detects if the migrate action should 
be added for a phase and lists the action in the list of phases.
James Baiera 4 年 前
コミット
e14ba5e0d5

+ 273 - 6
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/IndexLifecycleFeatureSetUsage.java

@@ -7,14 +7,15 @@
 package org.elasticsearch.xpack.core.ilm;
 
 import org.elasticsearch.Version;
-import org.elasticsearch.common.xcontent.ParseField;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.common.io.stream.Writeable;
-import org.elasticsearch.core.TimeValue;
+import org.elasticsearch.common.unit.ByteSizeValue;
+import org.elasticsearch.common.xcontent.ParseField;
 import org.elasticsearch.common.xcontent.ToXContentObject;
 import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.core.TimeValue;
 import org.elasticsearch.xpack.core.XPackFeatureSet;
 import org.elasticsearch.xpack.core.XPackField;
 
@@ -155,23 +156,36 @@ public class IndexLifecycleFeatureSetUsage extends XPackFeatureSet.Usage {
     }
 
     public static final class PhaseStats implements ToXContentObject, Writeable {
+
+        public static final ParseField CONFIGURATIONS_FIELD = new ParseField("configurations");
+
         private final String[] actionNames;
+        private final ActionConfigStats configurations;
         private final TimeValue minimumAge;
 
-        public PhaseStats(TimeValue after, String[] actionNames) {
-            this.actionNames = actionNames;
-            this.minimumAge = after;
+        public PhaseStats(TimeValue after, String[] actionNames, ActionConfigStats configurations) {
+            this.actionNames = Objects.requireNonNull(actionNames, "Missing required action names");
+            this.configurations = Objects.requireNonNull(configurations, "Missing required action configurations");
+            this.minimumAge = Objects.requireNonNull(after, "Missing required minimum age");
         }
 
         public PhaseStats(StreamInput in) throws IOException {
             actionNames = in.readStringArray();
             minimumAge = in.readTimeValue();
+            if (in.getVersion().onOrAfter(Version.V_8_0_0)) {
+                configurations = new ActionConfigStats(in);
+            } else {
+                configurations = ActionConfigStats.builder().build();
+            }
         }
 
         @Override
         public void writeTo(StreamOutput out) throws IOException {
             out.writeStringArray(actionNames);
             out.writeTimeValue(minimumAge);
+            if (out.getVersion().onOrAfter(Version.V_8_0_0)) {
+                configurations.writeTo(out);
+            }
         }
 
         @Override
@@ -179,6 +193,7 @@ public class IndexLifecycleFeatureSetUsage extends XPackFeatureSet.Usage {
             builder.startObject();
             builder.field(Phase.MIN_AGE.getPreferredName(), minimumAge.getMillis());
             builder.field(Phase.ACTIONS_FIELD.getPreferredName(), actionNames);
+            builder.field(CONFIGURATIONS_FIELD.getPreferredName(), configurations);
             builder.endObject();
             return builder;
         }
@@ -191,9 +206,13 @@ public class IndexLifecycleFeatureSetUsage extends XPackFeatureSet.Usage {
             return minimumAge;
         }
 
+        public ActionConfigStats getConfigurations() {
+            return configurations;
+        }
+
         @Override
         public int hashCode() {
-            return Objects.hash(Arrays.hashCode(actionNames), minimumAge);
+            return Objects.hash(Arrays.hashCode(actionNames), configurations, minimumAge);
         }
 
         @Override
@@ -206,7 +225,255 @@ public class IndexLifecycleFeatureSetUsage extends XPackFeatureSet.Usage {
             }
             PhaseStats other = (PhaseStats) obj;
             return Objects.equals(minimumAge, other.minimumAge) &&
+                    Objects.deepEquals(configurations, other.configurations) &&
                     Objects.deepEquals(actionNames, other.actionNames);
         }
     }
+
+    public static final class ActionConfigStats implements ToXContentObject, Writeable {
+        private final Integer allocateNumberOfReplicas;
+        private final Integer forceMergeMaxNumberOfSegments;
+        private final TimeValue rolloverMaxAge;
+        private final Long rolloverMaxDocs;
+        private final ByteSizeValue rolloverMaxPrimaryShardSize;
+        private final ByteSizeValue rolloverMaxSize;
+        private final Integer setPriorityPriority;
+        private final ByteSizeValue shrinkMaxPrimaryShardSize;
+        private final Integer shrinkNumberOfShards;
+
+        public static Builder builder() {
+            return new Builder();
+        }
+
+        public static Builder builder(ActionConfigStats existing) {
+            return new Builder(existing);
+        }
+
+        public static final class Builder {
+            private Integer allocateNumberOfReplicas;
+            private Integer forceMergeMaxNumberOfSegments;
+            private TimeValue rolloverMaxAge;
+            private Long rolloverMaxDocs;
+            private ByteSizeValue rolloverMaxPrimaryShardSize;
+            private ByteSizeValue rolloverMaxSize;
+            private Integer setPriorityPriority;
+            private ByteSizeValue shrinkMaxPrimaryShardSize;
+            private Integer shrinkNumberOfShards;
+
+            public Builder() {}
+
+            public Builder(ActionConfigStats existing) {
+                this.allocateNumberOfReplicas = existing.allocateNumberOfReplicas;
+                this.forceMergeMaxNumberOfSegments = existing.forceMergeMaxNumberOfSegments;
+                this.rolloverMaxAge = existing.rolloverMaxAge;
+                this.rolloverMaxDocs = existing.rolloverMaxDocs;
+                this.rolloverMaxPrimaryShardSize = existing.rolloverMaxPrimaryShardSize;
+                this.rolloverMaxSize = existing.rolloverMaxSize;
+                this.setPriorityPriority = existing.setPriorityPriority;
+                this.shrinkMaxPrimaryShardSize = existing.shrinkMaxPrimaryShardSize;
+                this.shrinkNumberOfShards = existing.shrinkNumberOfShards;
+            }
+
+            public Builder setAllocateNumberOfReplicas(Integer allocateNumberOfReplicas) {
+                this.allocateNumberOfReplicas = allocateNumberOfReplicas;
+                return this;
+            }
+
+            public Builder setForceMergeMaxNumberOfSegments(Integer forceMergeMaxNumberOfSegments) {
+                this.forceMergeMaxNumberOfSegments = forceMergeMaxNumberOfSegments;
+                return this;
+            }
+
+            public Builder setRolloverMaxAge(TimeValue rolloverMaxAge) {
+                this.rolloverMaxAge = rolloverMaxAge;
+                return this;
+            }
+
+            public Builder setRolloverMaxDocs(Long rolloverMaxDocs) {
+                this.rolloverMaxDocs = rolloverMaxDocs;
+                return this;
+            }
+
+            public Builder setRolloverMaxPrimaryShardSize(ByteSizeValue rolloverMaxPrimaryShardSize) {
+                this.rolloverMaxPrimaryShardSize = rolloverMaxPrimaryShardSize;
+                return this;
+            }
+
+            public Builder setRolloverMaxSize(ByteSizeValue rolloverMaxSize) {
+                this.rolloverMaxSize = rolloverMaxSize;
+                return this;
+            }
+
+            public Builder setPriority(Integer priority) {
+                this.setPriorityPriority = priority;
+                return this;
+            }
+
+            public Builder setShrinkMaxPrimaryShardSize(ByteSizeValue shrinkMaxPrimaryShardSize) {
+                this.shrinkMaxPrimaryShardSize = shrinkMaxPrimaryShardSize;
+                return this;
+            }
+
+            public Builder setShrinkNumberOfShards(Integer shrinkNumberOfShards) {
+                this.shrinkNumberOfShards = shrinkNumberOfShards;
+                return this;
+            }
+
+            public ActionConfigStats build() {
+                return new ActionConfigStats(allocateNumberOfReplicas, forceMergeMaxNumberOfSegments, rolloverMaxAge, rolloverMaxDocs,
+                    rolloverMaxPrimaryShardSize, rolloverMaxSize, setPriorityPriority, shrinkMaxPrimaryShardSize, shrinkNumberOfShards);
+            }
+        }
+
+        public ActionConfigStats(Integer allocateNumberOfReplicas, Integer forceMergeMaxNumberOfSegments, TimeValue rolloverMaxAge,
+                                 Long rolloverMaxDocs, ByteSizeValue rolloverMaxPrimaryShardSize, ByteSizeValue rolloverMaxSize,
+                                 Integer setPriorityPriority, ByteSizeValue shrinkMaxPrimaryShardSize, Integer shrinkNumberOfShards) {
+            this.allocateNumberOfReplicas = allocateNumberOfReplicas;
+            this.forceMergeMaxNumberOfSegments = forceMergeMaxNumberOfSegments;
+            this.rolloverMaxAge = rolloverMaxAge;
+            this.rolloverMaxDocs = rolloverMaxDocs;
+            this.rolloverMaxPrimaryShardSize = rolloverMaxPrimaryShardSize;
+            this.rolloverMaxSize = rolloverMaxSize;
+            this.setPriorityPriority = setPriorityPriority;
+            this.shrinkMaxPrimaryShardSize = shrinkMaxPrimaryShardSize;
+            this.shrinkNumberOfShards = shrinkNumberOfShards;
+        }
+
+        public ActionConfigStats(StreamInput in) throws IOException {
+            this.allocateNumberOfReplicas = in.readOptionalVInt();
+            this.forceMergeMaxNumberOfSegments = in.readOptionalVInt();
+            this.rolloverMaxAge = in.readOptionalTimeValue();
+            this.rolloverMaxDocs = in.readOptionalVLong();
+            this.rolloverMaxPrimaryShardSize = in.readOptionalWriteable(ByteSizeValue::new);
+            this.rolloverMaxSize = in.readOptionalWriteable(ByteSizeValue::new);
+            this.setPriorityPriority = in.readOptionalVInt();
+            this.shrinkMaxPrimaryShardSize = in.readOptionalWriteable(ByteSizeValue::new);
+            this.shrinkNumberOfShards = in.readOptionalVInt();
+        }
+
+        @Override
+        public void writeTo(StreamOutput out) throws IOException {
+            out.writeOptionalVInt(allocateNumberOfReplicas);
+            out.writeOptionalVInt(forceMergeMaxNumberOfSegments);
+            out.writeOptionalTimeValue(rolloverMaxAge);
+            out.writeOptionalVLong(rolloverMaxDocs);
+            out.writeOptionalWriteable(rolloverMaxPrimaryShardSize);
+            out.writeOptionalWriteable(rolloverMaxSize);
+            out.writeOptionalVInt(setPriorityPriority);
+            out.writeOptionalWriteable(shrinkMaxPrimaryShardSize);
+            out.writeOptionalVInt(shrinkNumberOfShards);
+        }
+
+        @Override
+        public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
+            builder.startObject();
+            if (allocateNumberOfReplicas != null) {
+                builder.startObject(AllocateAction.NAME);
+                builder.field(AllocateAction.NUMBER_OF_REPLICAS_FIELD.getPreferredName(), allocateNumberOfReplicas);
+                builder.endObject();
+            }
+            if (forceMergeMaxNumberOfSegments != null) {
+                builder.startObject(ForceMergeAction.NAME);
+                builder.field(ForceMergeAction.MAX_NUM_SEGMENTS_FIELD.getPreferredName(), forceMergeMaxNumberOfSegments);
+                builder.endObject();
+            }
+            if (rolloverMaxAge != null || rolloverMaxDocs != null || rolloverMaxSize != null || rolloverMaxPrimaryShardSize != null) {
+                builder.startObject(RolloverAction.NAME);
+                if (rolloverMaxAge != null) {
+                    builder.field(RolloverAction.MAX_AGE_FIELD.getPreferredName(), rolloverMaxAge.getStringRep());
+                    builder.field(RolloverAction.MAX_AGE_FIELD.getPreferredName() + "_millis", rolloverMaxAge.getMillis());
+                }
+                if (rolloverMaxDocs != null) {
+                    builder.field(RolloverAction.MAX_DOCS_FIELD.getPreferredName(), rolloverMaxDocs);
+                }
+                if (rolloverMaxSize != null) {
+                    builder.field(RolloverAction.MAX_SIZE_FIELD.getPreferredName(), rolloverMaxSize.getStringRep());
+                    builder.field(RolloverAction.MAX_SIZE_FIELD.getPreferredName() + "_bytes", rolloverMaxSize.getBytes());
+                }
+                if (rolloverMaxPrimaryShardSize != null) {
+                    builder.field(RolloverAction.MAX_PRIMARY_SHARD_SIZE_FIELD.getPreferredName(),
+                        rolloverMaxPrimaryShardSize.getStringRep());
+                    builder.field(RolloverAction.MAX_PRIMARY_SHARD_SIZE_FIELD.getPreferredName() + "_bytes",
+                        rolloverMaxPrimaryShardSize.getBytes());
+                }
+                builder.endObject();
+            }
+            if (setPriorityPriority != null) {
+                builder.startObject(SetPriorityAction.NAME);
+                builder.field(SetPriorityAction.RECOVERY_PRIORITY_FIELD.getPreferredName(), setPriorityPriority);
+                builder.endObject();
+            }
+            if (shrinkMaxPrimaryShardSize != null || shrinkNumberOfShards != null) {
+                builder.startObject(ShrinkAction.NAME);
+                if (shrinkMaxPrimaryShardSize != null) {
+                    builder.field(ShrinkAction.MAX_PRIMARY_SHARD_SIZE.getPreferredName(), shrinkMaxPrimaryShardSize.getStringRep());
+                    builder.field(ShrinkAction.MAX_PRIMARY_SHARD_SIZE.getPreferredName() + "_bytes", shrinkMaxPrimaryShardSize.getBytes());
+                }
+                if (shrinkNumberOfShards != null) {
+                    builder.field(ShrinkAction.NUMBER_OF_SHARDS_FIELD.getPreferredName(), shrinkNumberOfShards);
+                }
+                builder.endObject();
+            }
+            builder.endObject();
+            return builder;
+        }
+
+        public Integer getAllocateNumberOfReplicas() {
+            return allocateNumberOfReplicas;
+        }
+
+        public Integer getForceMergeMaxNumberOfSegments() {
+            return forceMergeMaxNumberOfSegments;
+        }
+
+        public TimeValue getRolloverMaxAge() {
+            return rolloverMaxAge;
+        }
+
+        public Long getRolloverMaxDocs() {
+            return rolloverMaxDocs;
+        }
+
+        public ByteSizeValue getRolloverMaxPrimaryShardSize() {
+            return rolloverMaxPrimaryShardSize;
+        }
+
+        public ByteSizeValue getRolloverMaxSize() {
+            return rolloverMaxSize;
+        }
+
+        public Integer getSetPriorityPriority() {
+            return setPriorityPriority;
+        }
+
+        public ByteSizeValue getShrinkMaxPrimaryShardSize() {
+            return shrinkMaxPrimaryShardSize;
+        }
+
+        public Integer getShrinkNumberOfShards() {
+            return shrinkNumberOfShards;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            ActionConfigStats that = (ActionConfigStats) o;
+            return Objects.equals(allocateNumberOfReplicas, that.allocateNumberOfReplicas) &&
+                    Objects.equals(forceMergeMaxNumberOfSegments, that.forceMergeMaxNumberOfSegments) &&
+                    Objects.equals(rolloverMaxAge, that.rolloverMaxAge) &&
+                    Objects.equals(rolloverMaxDocs, that.rolloverMaxDocs) &&
+                    Objects.equals(rolloverMaxPrimaryShardSize, that.rolloverMaxPrimaryShardSize) &&
+                    Objects.equals(rolloverMaxSize, that.rolloverMaxSize) &&
+                    Objects.equals(setPriorityPriority, that.setPriorityPriority) &&
+                    Objects.equals(shrinkMaxPrimaryShardSize, that.shrinkMaxPrimaryShardSize) &&
+                    Objects.equals(shrinkNumberOfShards, that.shrinkNumberOfShards);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(allocateNumberOfReplicas, forceMergeMaxNumberOfSegments, rolloverMaxAge, rolloverMaxDocs,
+                rolloverMaxPrimaryShardSize, rolloverMaxSize, setPriorityPriority, shrinkMaxPrimaryShardSize, shrinkNumberOfShards);
+        }
+    }
 }

+ 5 - 1
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/SetPriorityAction.java

@@ -29,7 +29,7 @@ import java.util.List;
  */
 public class SetPriorityAction implements LifecycleAction {
     public static final String NAME = "set_priority";
-    private static final ParseField RECOVERY_PRIORITY_FIELD = new ParseField("priority");
+    public static final ParseField RECOVERY_PRIORITY_FIELD = new ParseField("priority");
 
     @SuppressWarnings("unchecked")
     private static final ConstructingObjectParser<SetPriorityAction, Void> PARSER = new ConstructingObjectParser<>(NAME,
@@ -64,6 +64,10 @@ public class SetPriorityAction implements LifecycleAction {
         return NAME;
     }
 
+    public Integer getRecoveryPriority() {
+        return recoveryPriority;
+    }
+
     @Override
     public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
         builder.startObject();

+ 3 - 3
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/ShrinkAction.java

@@ -38,7 +38,7 @@ public class ShrinkAction implements LifecycleAction {
 
     public static final String NAME = "shrink";
     public static final ParseField NUMBER_OF_SHARDS_FIELD = new ParseField("number_of_shards");
-    private static final ParseField MAX_PRIMARY_SHARD_SIZE = new ParseField("max_primary_shard_size");
+    public static final ParseField MAX_PRIMARY_SHARD_SIZE = new ParseField("max_primary_shard_size");
     public static final String CONDITIONAL_SKIP_SHRINK_STEP = BranchingStep.NAME + "-check-prerequisites";
     public static final String CONDITIONAL_DATASTREAM_CHECK_KEY = BranchingStep.NAME + "-on-datastream-check";
 
@@ -89,11 +89,11 @@ public class ShrinkAction implements LifecycleAction {
         }
     }
 
-    Integer getNumberOfShards() {
+    public Integer getNumberOfShards() {
         return numberOfShards;
     }
 
-    ByteSizeValue getMaxPrimaryShardSize() {
+    public ByteSizeValue getMaxPrimaryShardSize() {
         return maxPrimaryShardSize;
     }
 

+ 1 - 1
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java

@@ -126,7 +126,7 @@ public class TimeseriesLifecycleType implements LifecycleType {
         return orderedPhases;
     }
 
-    static boolean shouldInjectMigrateStepForPhase(Phase phase) {
+    public static boolean shouldInjectMigrateStepForPhase(Phase phase) {
         AllocateAction allocateAction = (AllocateAction) phase.getActions().get(AllocateAction.NAME);
         if (allocateAction != null) {
             if (definesAllocationRules(allocateAction)) {

+ 106 - 0
x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/ActionConfigStatsTests.java

@@ -0,0 +1,106 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.xpack.core.ilm;
+
+import org.elasticsearch.common.io.stream.Writeable;
+import org.elasticsearch.common.unit.ByteSizeValue;
+import org.elasticsearch.core.TimeValue;
+import org.elasticsearch.test.AbstractWireSerializingTestCase;
+import org.elasticsearch.xpack.core.ilm.IndexLifecycleFeatureSetUsage.ActionConfigStats;
+
+import java.io.IOException;
+
+public class ActionConfigStatsTests extends AbstractWireSerializingTestCase<ActionConfigStats> {
+
+    @Override
+    protected ActionConfigStats createTestInstance() {
+        return createRandomInstance();
+    }
+
+    public static ActionConfigStats createRandomInstance() {
+        ActionConfigStats.Builder builder = ActionConfigStats.builder();
+        if (randomBoolean()) {
+            builder.setAllocateNumberOfReplicas(randomIntBetween(0, 10000));
+        }
+        if (randomBoolean()) {
+            builder.setForceMergeMaxNumberOfSegments(randomIntBetween(0, 10000));
+        }
+        if (randomBoolean()) {
+            TimeValue randomAge = TimeValue.parseTimeValue(randomTimeValue(), "action_config_stats_tests");
+            builder.setRolloverMaxAge(randomAge);
+        }
+        if (randomBoolean()) {
+            builder.setRolloverMaxDocs(randomLongBetween(0, Long.MAX_VALUE));
+        }
+        if (randomBoolean()) {
+            ByteSizeValue randomByteSize = ByteSizeValue.ofBytes(randomLongBetween(0, 1024L*1024L*1024L*50L));
+            builder.setRolloverMaxPrimaryShardSize(randomByteSize);
+        }
+        if (randomBoolean()) {
+            ByteSizeValue randomByteSize = ByteSizeValue.ofBytes(randomLongBetween(0, 1024L*1024L*1024L*50L));
+            builder.setRolloverMaxSize(randomByteSize);
+        }
+        if (randomBoolean()) {
+            builder.setPriority(randomIntBetween(0, 50));
+        }
+        if (randomBoolean()) {
+            ByteSizeValue randomByteSize = ByteSizeValue.ofBytes(randomLongBetween(0, 1024L*1024L*1024L*50L));
+            builder.setShrinkMaxPrimaryShardSize(randomByteSize);
+        }
+        if (randomBoolean()) {
+            builder.setShrinkNumberOfShards(randomIntBetween(0, 50));
+        }
+        return builder.build();
+    }
+
+    @Override
+    protected Writeable.Reader<ActionConfigStats> instanceReader() {
+        return ActionConfigStats::new;
+    }
+
+    @Override
+    protected ActionConfigStats mutateInstance(ActionConfigStats instance) throws IOException {
+        ActionConfigStats.Builder builder = ActionConfigStats.builder(instance);
+        switch (between(0, 8)) {
+            case 0:
+                builder.setAllocateNumberOfReplicas(randomIntBetween(0, 10000));
+                break;
+            case 1:
+                builder.setForceMergeMaxNumberOfSegments(randomIntBetween(0, 10000));
+                break;
+            case 2:
+                TimeValue randomAge = TimeValue.parseTimeValue(randomTimeValue(), "action_config_stats_tests");
+                builder.setRolloverMaxAge(randomAge);
+                break;
+            case 3:
+                builder.setRolloverMaxDocs(randomLongBetween(0, Long.MAX_VALUE));
+                break;
+            case 4:
+                ByteSizeValue randomByteSize = ByteSizeValue.ofBytes(randomLongBetween(0, 1024L*1024L*1024L*50L));
+                builder.setRolloverMaxPrimaryShardSize(randomByteSize);
+                break;
+            case 5:
+                ByteSizeValue randomMaxByteSize = ByteSizeValue.ofBytes(randomLongBetween(0, 1024L*1024L*1024L*50L));
+                builder.setRolloverMaxSize(randomMaxByteSize);
+                break;
+            case 6:
+                builder.setPriority(randomIntBetween(0, 50));
+                break;
+            case 7:
+                ByteSizeValue randomPrimaryByteSize = ByteSizeValue.ofBytes(randomLongBetween(0, 1024L*1024L*1024L*50L));
+                builder.setShrinkMaxPrimaryShardSize(randomPrimaryByteSize);
+                break;
+            case 8:
+                builder.setShrinkNumberOfShards(randomIntBetween(0, 50));
+                break;
+            default:
+                throw new IllegalStateException("Illegal randomization branch");
+        }
+        return builder.build();
+    }
+}

+ 2 - 2
x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/PhaseStatsTests.java

@@ -25,7 +25,7 @@ public class PhaseStatsTests extends AbstractWireSerializingTestCase<PhaseStats>
     public static PhaseStats createRandomInstance() {
         TimeValue after = TimeValue.parseTimeValue(randomTimeValue(), "phase_stats_tests");
         String[] actionNames = randomArray(0, 20, size -> new String[size], () -> randomAlphaOfLengthBetween(1, 20));
-        return new PhaseStats(after, actionNames);
+        return new PhaseStats(after, actionNames, ActionConfigStatsTests.createRandomInstance());
     }
 
     @Override
@@ -43,7 +43,7 @@ public class PhaseStatsTests extends AbstractWireSerializingTestCase<PhaseStats>
         default:
             throw new AssertionError("Illegal randomisation branch");
         }
-        return new PhaseStats(after, actionNames);
+        return new PhaseStats(after, actionNames, instance.getConfigurations());
     }
 
     @Override

+ 49 - 2
x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycleUsageTransportAction.java

@@ -21,14 +21,25 @@ import org.elasticsearch.transport.TransportService;
 import org.elasticsearch.xpack.core.action.XPackUsageFeatureAction;
 import org.elasticsearch.xpack.core.action.XPackUsageFeatureResponse;
 import org.elasticsearch.xpack.core.action.XPackUsageFeatureTransportAction;
+import org.elasticsearch.xpack.core.ilm.AllocateAction;
+import org.elasticsearch.xpack.core.ilm.ForceMergeAction;
 import org.elasticsearch.xpack.core.ilm.IndexLifecycleFeatureSetUsage;
+import org.elasticsearch.xpack.core.ilm.IndexLifecycleFeatureSetUsage.ActionConfigStats;
 import org.elasticsearch.xpack.core.ilm.IndexLifecycleMetadata;
+import org.elasticsearch.xpack.core.ilm.LifecycleAction;
 import org.elasticsearch.xpack.core.ilm.LifecycleSettings;
+import org.elasticsearch.xpack.core.ilm.RolloverAction;
+import org.elasticsearch.xpack.core.ilm.SetPriorityAction;
+import org.elasticsearch.xpack.core.ilm.ShrinkAction;
+import org.elasticsearch.xpack.core.ilm.TimeseriesLifecycleType;
 
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.elasticsearch.xpack.core.ilm.TimeseriesLifecycleType.shouldInjectMigrateStepForPhase;
 
 public class IndexLifecycleUsageTransportAction extends XPackUsageFeatureTransportAction {
 
@@ -59,8 +70,15 @@ public class IndexLifecycleUsageTransportAction extends XPackUsageFeatureTranspo
             });
             List<IndexLifecycleFeatureSetUsage.PolicyStats> policyStats = lifecycleMetadata.getPolicies().values().stream().map(policy -> {
                 Map<String, IndexLifecycleFeatureSetUsage.PhaseStats> phaseStats = policy.getPhases().values().stream().map(phase -> {
-                    String[] actionNames = phase.getActions().keySet().toArray(new String[phase.getActions().size()]);
-                    return new Tuple<>(phase.getName(), new IndexLifecycleFeatureSetUsage.PhaseStats(phase.getMinimumAge(), actionNames));
+                    ActionConfigStats.Builder configurations = ActionConfigStats.builder();
+                    Stream<String> actionStream = phase.getActions().keySet().stream();
+                    if (policy.getType() instanceof TimeseriesLifecycleType && shouldInjectMigrateStepForPhase(phase)) {
+                        actionStream = Stream.concat(actionStream, Stream.of("migrate"));
+                    }
+                    String[] actionNames = actionStream.toArray(String[]::new);
+                    phase.getActions().forEach((k, v) -> collectActionConfigurations(k, v, configurations));
+                    return new Tuple<>(phase.getName(), new IndexLifecycleFeatureSetUsage.PhaseStats(phase.getMinimumAge(), actionNames,
+                        configurations.build()));
                 }).collect(Collectors.toMap(Tuple::v1, Tuple::v2));
                 return new IndexLifecycleFeatureSetUsage.PolicyStats(phaseStats, policyUsage.getOrDefault(policy.getName(), 0));
             }).collect(Collectors.toList());
@@ -70,4 +88,33 @@ public class IndexLifecycleUsageTransportAction extends XPackUsageFeatureTranspo
         }
         listener.onResponse(new XPackUsageFeatureResponse(usage));
     }
+
+    private void collectActionConfigurations(String actionName, LifecycleAction action, ActionConfigStats.Builder consumer) {
+        switch (actionName) {
+            case AllocateAction.NAME:
+                AllocateAction allocateAction = (AllocateAction) action;
+                consumer.setAllocateNumberOfReplicas(allocateAction.getNumberOfReplicas());
+                break;
+            case ForceMergeAction.NAME:
+                ForceMergeAction forceMergeAction = (ForceMergeAction) action;
+                consumer.setForceMergeMaxNumberOfSegments(forceMergeAction.getMaxNumSegments());
+                break;
+            case RolloverAction.NAME:
+                RolloverAction rolloverAction = (RolloverAction) action;
+                consumer.setRolloverMaxAge(rolloverAction.getMaxAge());
+                consumer.setRolloverMaxDocs(rolloverAction.getMaxDocs());
+                consumer.setRolloverMaxPrimaryShardSize(rolloverAction.getMaxPrimaryShardSize());
+                consumer.setRolloverMaxSize(rolloverAction.getMaxSize());
+                break;
+            case SetPriorityAction.NAME:
+                SetPriorityAction setPriorityAction = (SetPriorityAction) action;
+                consumer.setPriority(setPriorityAction.getRecoveryPriority());
+                break;
+            case ShrinkAction.NAME:
+                ShrinkAction shrinkAction = (ShrinkAction) action;
+                consumer.setShrinkMaxPrimaryShardSize(shrinkAction.getMaxPrimaryShardSize());
+                consumer.setShrinkNumberOfShards(shrinkAction.getNumberOfShards());
+                break;
+        }
+    }
 }

+ 3 - 2
x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/PhaseStatsTests.java

@@ -10,6 +10,7 @@ package org.elasticsearch.xpack.ilm;
 import org.elasticsearch.common.io.stream.Writeable.Reader;
 import org.elasticsearch.core.TimeValue;
 import org.elasticsearch.test.AbstractWireSerializingTestCase;
+import org.elasticsearch.xpack.core.ilm.ActionConfigStatsTests;
 import org.elasticsearch.xpack.core.ilm.IndexLifecycleFeatureSetUsage.PhaseStats;
 
 import java.io.IOException;
@@ -25,7 +26,7 @@ public class PhaseStatsTests extends AbstractWireSerializingTestCase<PhaseStats>
     static PhaseStats randomPhaseStats() {
         TimeValue minimumAge = TimeValue.parseTimeValue(randomTimeValue(0, 1000000000, "s", "m", "h", "d"), "test_after");
         String[] actionNames = generateRandomStringArray(10, 20, false);
-        return new PhaseStats(minimumAge, actionNames);
+        return new PhaseStats(minimumAge, actionNames, ActionConfigStatsTests.createRandomInstance());
     }
 
     @Override
@@ -44,7 +45,7 @@ public class PhaseStatsTests extends AbstractWireSerializingTestCase<PhaseStats>
         default:
             throw new AssertionError("Illegal randomisation branch");
         }
-        return new PhaseStats(minimumAge, actionNames);
+        return new PhaseStats(minimumAge, actionNames, instance.getConfigurations());
     }
 
     @Override