Add start and stop time to cat recovery API (#40378)
The cat recovery API is incredibly useful. Yet it is missing the start and stop time as an option from the output. This commit adds these as options to the cat recovery API. We elect to make these not visible by default to avoid breaking the output that users might rely on.
This commit is contained in:
parent
6f26156abf
commit
7255562afd
|
@ -289,6 +289,7 @@ import org.elasticsearch.rest.action.cat.AbstractCatAction;
|
||||||
import org.elasticsearch.rest.action.cat.RestAliasAction;
|
import org.elasticsearch.rest.action.cat.RestAliasAction;
|
||||||
import org.elasticsearch.rest.action.cat.RestAllocationAction;
|
import org.elasticsearch.rest.action.cat.RestAllocationAction;
|
||||||
import org.elasticsearch.rest.action.cat.RestCatAction;
|
import org.elasticsearch.rest.action.cat.RestCatAction;
|
||||||
|
import org.elasticsearch.rest.action.cat.RestCatRecoveryAction;
|
||||||
import org.elasticsearch.rest.action.cat.RestFielddataAction;
|
import org.elasticsearch.rest.action.cat.RestFielddataAction;
|
||||||
import org.elasticsearch.rest.action.cat.RestHealthAction;
|
import org.elasticsearch.rest.action.cat.RestHealthAction;
|
||||||
import org.elasticsearch.rest.action.cat.RestIndicesAction;
|
import org.elasticsearch.rest.action.cat.RestIndicesAction;
|
||||||
|
@ -665,7 +666,7 @@ public class ActionModule extends AbstractModule {
|
||||||
// Fully qualified to prevent interference with rest.action.count.RestCountAction
|
// Fully qualified to prevent interference with rest.action.count.RestCountAction
|
||||||
registerHandler.accept(new org.elasticsearch.rest.action.cat.RestCountAction(settings, restController));
|
registerHandler.accept(new org.elasticsearch.rest.action.cat.RestCountAction(settings, restController));
|
||||||
// Fully qualified to prevent interference with rest.action.indices.RestRecoveryAction
|
// Fully qualified to prevent interference with rest.action.indices.RestRecoveryAction
|
||||||
registerHandler.accept(new org.elasticsearch.rest.action.cat.RestRecoveryAction(settings, restController));
|
registerHandler.accept(new RestCatRecoveryAction(settings, restController));
|
||||||
registerHandler.accept(new RestHealthAction(settings, restController));
|
registerHandler.accept(new RestHealthAction(settings, restController));
|
||||||
registerHandler.accept(new org.elasticsearch.rest.action.cat.RestPendingClusterTasksAction(settings, restController));
|
registerHandler.accept(new org.elasticsearch.rest.action.cat.RestPendingClusterTasksAction(settings, restController));
|
||||||
registerHandler.accept(new RestAliasAction(settings, restController));
|
registerHandler.accept(new RestAliasAction(settings, restController));
|
||||||
|
|
|
@ -30,6 +30,7 @@ import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.Table;
|
import org.elasticsearch.common.Table;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentElasticsearchExtension;
|
||||||
import org.elasticsearch.indices.recovery.RecoveryState;
|
import org.elasticsearch.indices.recovery.RecoveryState;
|
||||||
import org.elasticsearch.rest.RestController;
|
import org.elasticsearch.rest.RestController;
|
||||||
import org.elasticsearch.rest.RestRequest;
|
import org.elasticsearch.rest.RestRequest;
|
||||||
|
@ -47,8 +48,8 @@ import static org.elasticsearch.rest.RestRequest.Method.GET;
|
||||||
* in a string format, designed to be used at the command line. An Index can
|
* in a string format, designed to be used at the command line. An Index can
|
||||||
* be specified to limit output to a particular index or indices.
|
* be specified to limit output to a particular index or indices.
|
||||||
*/
|
*/
|
||||||
public class RestRecoveryAction extends AbstractCatAction {
|
public class RestCatRecoveryAction extends AbstractCatAction {
|
||||||
public RestRecoveryAction(Settings settings, RestController restController) {
|
public RestCatRecoveryAction(Settings settings, RestController restController) {
|
||||||
super(settings);
|
super(settings);
|
||||||
restController.registerHandler(GET, "/_cat/recovery", this);
|
restController.registerHandler(GET, "/_cat/recovery", this);
|
||||||
restController.registerHandler(GET, "/_cat/recovery/{index}", this);
|
restController.registerHandler(GET, "/_cat/recovery/{index}", this);
|
||||||
|
@ -86,6 +87,10 @@ public class RestRecoveryAction extends AbstractCatAction {
|
||||||
t.startHeaders()
|
t.startHeaders()
|
||||||
.addCell("index", "alias:i,idx;desc:index name")
|
.addCell("index", "alias:i,idx;desc:index name")
|
||||||
.addCell("shard", "alias:s,sh;desc:shard name")
|
.addCell("shard", "alias:s,sh;desc:shard name")
|
||||||
|
.addCell("start_time", "default:false;alias:start;desc:recovery start time")
|
||||||
|
.addCell("start_time_millis", "default:false;alias:start_millis;desc:recovery start time in epoch milliseconds")
|
||||||
|
.addCell("stop_time", "default:false;alias:stop;desc:recovery stop time")
|
||||||
|
.addCell("stop_time_millis", "default:false;alias:stop_millis;desc:recovery stop time in epoch milliseconds")
|
||||||
.addCell("time", "alias:t,ti;desc:recovery time")
|
.addCell("time", "alias:t,ti;desc:recovery time")
|
||||||
.addCell("type", "alias:ty;desc:recovery type")
|
.addCell("type", "alias:ty;desc:recovery type")
|
||||||
.addCell("stage", "alias:st;desc:recovery stage")
|
.addCell("stage", "alias:st;desc:recovery stage")
|
||||||
|
@ -149,6 +154,10 @@ public class RestRecoveryAction extends AbstractCatAction {
|
||||||
t.startRow();
|
t.startRow();
|
||||||
t.addCell(index);
|
t.addCell(index);
|
||||||
t.addCell(state.getShardId().id());
|
t.addCell(state.getShardId().id());
|
||||||
|
t.addCell(XContentElasticsearchExtension.DEFAULT_DATE_PRINTER.print(state.getTimer().startTime()));
|
||||||
|
t.addCell(state.getTimer().startTime());
|
||||||
|
t.addCell(XContentElasticsearchExtension.DEFAULT_DATE_PRINTER.print(state.getTimer().stopTime()));
|
||||||
|
t.addCell(state.getTimer().stopTime());
|
||||||
t.addCell(new TimeValue(state.getTimer().time()));
|
t.addCell(new TimeValue(state.getTimer().time()));
|
||||||
t.addCell(state.getRecoverySource().getType().toString().toLowerCase(Locale.ROOT));
|
t.addCell(state.getRecoverySource().getType().toString().toLowerCase(Locale.ROOT));
|
||||||
t.addCell(state.getStage().toString().toLowerCase(Locale.ROOT));
|
t.addCell(state.getStage().toString().toLowerCase(Locale.ROOT));
|
|
@ -29,6 +29,7 @@ import org.elasticsearch.common.Randomness;
|
||||||
import org.elasticsearch.common.Table;
|
import org.elasticsearch.common.Table;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentElasticsearchExtension;
|
||||||
import org.elasticsearch.index.Index;
|
import org.elasticsearch.index.Index;
|
||||||
import org.elasticsearch.index.shard.ShardId;
|
import org.elasticsearch.index.shard.ShardId;
|
||||||
import org.elasticsearch.indices.recovery.RecoveryState;
|
import org.elasticsearch.indices.recovery.RecoveryState;
|
||||||
|
@ -37,7 +38,9 @@ import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.usage.UsageService;
|
import org.elasticsearch.usage.UsageService;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
@ -53,7 +56,7 @@ public class RestRecoveryActionTests extends ESTestCase {
|
||||||
final Settings settings = Settings.EMPTY;
|
final Settings settings = Settings.EMPTY;
|
||||||
UsageService usageService = new UsageService();
|
UsageService usageService = new UsageService();
|
||||||
final RestController restController = new RestController(Collections.emptySet(), null, null, null, usageService);
|
final RestController restController = new RestController(Collections.emptySet(), null, null, null, usageService);
|
||||||
final RestRecoveryAction action = new RestRecoveryAction(settings, restController);
|
final RestCatRecoveryAction action = new RestCatRecoveryAction(settings, restController);
|
||||||
final int totalShards = randomIntBetween(1, 32);
|
final int totalShards = randomIntBetween(1, 32);
|
||||||
final int successfulShards = Math.max(0, totalShards - randomIntBetween(1, 2));
|
final int successfulShards = Math.max(0, totalShards - randomIntBetween(1, 2));
|
||||||
final int failedShards = totalShards - successfulShards;
|
final int failedShards = totalShards - successfulShards;
|
||||||
|
@ -64,7 +67,11 @@ public class RestRecoveryActionTests extends ESTestCase {
|
||||||
final RecoveryState state = mock(RecoveryState.class);
|
final RecoveryState state = mock(RecoveryState.class);
|
||||||
when(state.getShardId()).thenReturn(new ShardId(new Index("index", "_na_"), i));
|
when(state.getShardId()).thenReturn(new ShardId(new Index("index", "_na_"), i));
|
||||||
final RecoveryState.Timer timer = mock(RecoveryState.Timer.class);
|
final RecoveryState.Timer timer = mock(RecoveryState.Timer.class);
|
||||||
when(timer.time()).thenReturn((long)randomIntBetween(1000000, 10 * 1000000));
|
final long startTime = randomLongBetween(0, new Date().getTime());
|
||||||
|
when(timer.startTime()).thenReturn(startTime);
|
||||||
|
final long time = randomLongBetween(1000000, 10 * 1000000);
|
||||||
|
when(timer.time()).thenReturn(time);
|
||||||
|
when(timer.stopTime()).thenReturn(startTime + time);
|
||||||
when(state.getTimer()).thenReturn(timer);
|
when(state.getTimer()).thenReturn(timer);
|
||||||
when(state.getRecoverySource()).thenReturn(TestShardRouting.randomRecoverySource());
|
when(state.getRecoverySource()).thenReturn(TestShardRouting.randomRecoverySource());
|
||||||
when(state.getStage()).thenReturn(randomFrom(RecoveryState.Stage.values()));
|
when(state.getStage()).thenReturn(randomFrom(RecoveryState.Stage.values()));
|
||||||
|
@ -122,63 +129,78 @@ public class RestRecoveryActionTests extends ESTestCase {
|
||||||
|
|
||||||
List<Table.Cell> headers = table.getHeaders();
|
List<Table.Cell> headers = table.getHeaders();
|
||||||
|
|
||||||
assertThat(headers.get(0).value, equalTo("index"));
|
final List<String> expectedHeaders = Arrays.asList(
|
||||||
assertThat(headers.get(1).value, equalTo("shard"));
|
"index",
|
||||||
assertThat(headers.get(2).value, equalTo("time"));
|
"shard",
|
||||||
assertThat(headers.get(3).value, equalTo("type"));
|
"start_time",
|
||||||
assertThat(headers.get(4).value, equalTo("stage"));
|
"start_time_millis",
|
||||||
assertThat(headers.get(5).value, equalTo("source_host"));
|
"stop_time",
|
||||||
assertThat(headers.get(6).value, equalTo("source_node"));
|
"stop_time_millis",
|
||||||
assertThat(headers.get(7).value, equalTo("target_host"));
|
"time",
|
||||||
assertThat(headers.get(8).value, equalTo("target_node"));
|
"type",
|
||||||
assertThat(headers.get(9).value, equalTo("repository"));
|
"stage",
|
||||||
assertThat(headers.get(10).value, equalTo("snapshot"));
|
"source_host",
|
||||||
assertThat(headers.get(11).value, equalTo("files"));
|
"source_node",
|
||||||
assertThat(headers.get(12).value, equalTo("files_recovered"));
|
"target_host",
|
||||||
assertThat(headers.get(13).value, equalTo("files_percent"));
|
"target_node",
|
||||||
assertThat(headers.get(14).value, equalTo("files_total"));
|
"repository",
|
||||||
assertThat(headers.get(15).value, equalTo("bytes"));
|
"snapshot",
|
||||||
assertThat(headers.get(16).value, equalTo("bytes_recovered"));
|
"files",
|
||||||
assertThat(headers.get(17).value, equalTo("bytes_percent"));
|
"files_recovered",
|
||||||
assertThat(headers.get(18).value, equalTo("bytes_total"));
|
"files_percent",
|
||||||
assertThat(headers.get(19).value, equalTo("translog_ops"));
|
"files_total",
|
||||||
assertThat(headers.get(20).value, equalTo("translog_ops_recovered"));
|
"bytes",
|
||||||
assertThat(headers.get(21).value, equalTo("translog_ops_percent"));
|
"bytes_recovered",
|
||||||
|
"bytes_percent",
|
||||||
|
"bytes_total",
|
||||||
|
"translog_ops",
|
||||||
|
"translog_ops_recovered",
|
||||||
|
"translog_ops_percent");
|
||||||
|
|
||||||
|
for (int i = 0; i < expectedHeaders.size(); i++) {
|
||||||
|
assertThat(headers.get(i).value, equalTo(expectedHeaders.get(i)));
|
||||||
|
}
|
||||||
|
|
||||||
assertThat(table.getRows().size(), equalTo(successfulShards));
|
assertThat(table.getRows().size(), equalTo(successfulShards));
|
||||||
|
|
||||||
for (int i = 0; i < successfulShards; i++) {
|
for (int i = 0; i < successfulShards; i++) {
|
||||||
final RecoveryState state = recoveryStates.get(i);
|
final RecoveryState state = recoveryStates.get(i);
|
||||||
List<Table.Cell> cells = table.getRows().get(i);
|
final List<Object> expectedValues = Arrays.asList(
|
||||||
assertThat(cells.get(0).value, equalTo("index"));
|
"index",
|
||||||
assertThat(cells.get(1).value, equalTo(i));
|
i,
|
||||||
assertThat(cells.get(2).value, equalTo(new TimeValue(state.getTimer().time())));
|
XContentElasticsearchExtension.DEFAULT_DATE_PRINTER.print(state.getTimer().startTime()),
|
||||||
assertThat(cells.get(3).value, equalTo(state.getRecoverySource().getType().name().toLowerCase(Locale.ROOT)));
|
state.getTimer().startTime(),
|
||||||
assertThat(cells.get(4).value, equalTo(state.getStage().name().toLowerCase(Locale.ROOT)));
|
XContentElasticsearchExtension.DEFAULT_DATE_PRINTER.print(state.getTimer().stopTime()),
|
||||||
assertThat(cells.get(5).value, equalTo(state.getSourceNode() == null ? "n/a" : state.getSourceNode().getHostName()));
|
state.getTimer().stopTime(),
|
||||||
assertThat(cells.get(6).value, equalTo(state.getSourceNode() == null ? "n/a" : state.getSourceNode().getName()));
|
new TimeValue(state.getTimer().time()),
|
||||||
assertThat(cells.get(7).value, equalTo(state.getTargetNode().getHostName()));
|
state.getRecoverySource().getType().name().toLowerCase(Locale.ROOT),
|
||||||
assertThat(cells.get(8).value, equalTo(state.getTargetNode().getName()));
|
state.getStage().name().toLowerCase(Locale.ROOT),
|
||||||
assertThat(
|
state.getSourceNode() == null ? "n/a" : state.getSourceNode().getHostName(),
|
||||||
cells.get(9).value,
|
state.getSourceNode() == null ? "n/a" : state.getSourceNode().getName(),
|
||||||
equalTo(state.getRecoverySource() == null || state.getRecoverySource().getType() != RecoverySource.Type.SNAPSHOT ?
|
state.getTargetNode().getHostName(),
|
||||||
"n/a" :
|
state.getTargetNode().getName(),
|
||||||
((SnapshotRecoverySource) state.getRecoverySource()).snapshot().getRepository()));
|
state.getRecoverySource() == null || state.getRecoverySource().getType() != RecoverySource.Type.SNAPSHOT ?
|
||||||
assertThat(
|
"n/a" :
|
||||||
cells.get(10).value,
|
((SnapshotRecoverySource) state.getRecoverySource()).snapshot().getRepository(),
|
||||||
equalTo(state.getRecoverySource() == null || state.getRecoverySource().getType() != RecoverySource.Type.SNAPSHOT ?
|
state.getRecoverySource() == null || state.getRecoverySource().getType() != RecoverySource.Type.SNAPSHOT ?
|
||||||
"n/a" :
|
"n/a" :
|
||||||
((SnapshotRecoverySource) state.getRecoverySource()).snapshot().getSnapshotId().getName()));
|
((SnapshotRecoverySource) state.getRecoverySource()).snapshot().getSnapshotId().getName(),
|
||||||
assertThat(cells.get(11).value, equalTo(state.getIndex().totalRecoverFiles()));
|
state.getIndex().totalRecoverFiles(),
|
||||||
assertThat(cells.get(12).value, equalTo(state.getIndex().recoveredFileCount()));
|
state.getIndex().recoveredFileCount(),
|
||||||
assertThat(cells.get(13).value, equalTo(percent(state.getIndex().recoveredFilesPercent())));
|
percent(state.getIndex().recoveredFilesPercent()),
|
||||||
assertThat(cells.get(14).value, equalTo(state.getIndex().totalFileCount()));
|
state.getIndex().totalFileCount(),
|
||||||
assertThat(cells.get(15).value, equalTo(state.getIndex().totalRecoverBytes()));
|
state.getIndex().totalRecoverBytes(),
|
||||||
assertThat(cells.get(16).value, equalTo(state.getIndex().recoveredBytes()));
|
state.getIndex().recoveredBytes(),
|
||||||
assertThat(cells.get(17).value, equalTo(percent(state.getIndex().recoveredBytesPercent())));
|
percent(state.getIndex().recoveredBytesPercent()),
|
||||||
assertThat(cells.get(18).value, equalTo(state.getIndex().totalBytes()));
|
state.getIndex().totalBytes(),
|
||||||
assertThat(cells.get(19).value, equalTo(state.getTranslog().totalOperations()));
|
state.getTranslog().totalOperations(),
|
||||||
assertThat(cells.get(20).value, equalTo(state.getTranslog().recoveredOperations()));
|
state.getTranslog().recoveredOperations(),
|
||||||
assertThat(cells.get(21).value, equalTo(percent(state.getTranslog().recoveredPercent())));
|
percent(state.getTranslog().recoveredPercent()));
|
||||||
|
|
||||||
|
final List<Table.Cell> cells = table.getRows().get(i);
|
||||||
|
for (int j = 0; j < expectedValues.size(); j++) {
|
||||||
|
assertThat(cells.get(j).value, equalTo(expectedValues.get(j)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue