Add Snapshots Status API to High Level Rest Client (#31515)

This PR adds the Snapshots Status API to the Snapshot Client, as 
well as additional documentation for the status api.
This commit is contained in:
James Baiera 2018-07-11 12:07:31 -04:00 committed by GitHub
parent 51bb27a991
commit 5bcdff73d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1153 additions and 45 deletions

View File

@ -43,6 +43,7 @@ import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest;
import org.elasticsearch.action.admin.cluster.storedscripts.DeleteStoredScriptRequest;
import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRequest;
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeRequest;
@ -963,6 +964,20 @@ final class RequestConverters {
return request;
}
static Request snapshotsStatus(SnapshotsStatusRequest snapshotsStatusRequest) {
String endpoint = new EndpointBuilder().addPathPartAsIs("_snapshot")
.addPathPart(snapshotsStatusRequest.repository())
.addCommaSeparatedPathParts(snapshotsStatusRequest.snapshots())
.addPathPartAsIs("_status")
.build();
Request request = new Request(HttpGet.METHOD_NAME, endpoint);
Params parameters = new Params(request);
parameters.withMasterTimeout(snapshotsStatusRequest.masterNodeTimeout());
parameters.withIgnoreUnavailable(snapshotsStatusRequest.ignoreUnavailable());
return request;
}
static Request deleteSnapshot(DeleteSnapshotRequest deleteSnapshotRequest) {
String endpoint = new EndpointBuilder().addPathPartAsIs("_snapshot")
.addPathPart(deleteSnapshotRequest.repository())
@ -1262,7 +1277,7 @@ final class RequestConverters {
}
Params withIndicesOptions(IndicesOptions indicesOptions) {
putParam("ignore_unavailable", Boolean.toString(indicesOptions.ignoreUnavailable()));
withIgnoreUnavailable(indicesOptions.ignoreUnavailable());
putParam("allow_no_indices", Boolean.toString(indicesOptions.allowNoIndices()));
String expandWildcards;
if (indicesOptions.expandWildcardsOpen() == false && indicesOptions.expandWildcardsClosed() == false) {
@ -1281,6 +1296,12 @@ final class RequestConverters {
return this;
}
Params withIgnoreUnavailable(boolean ignoreUnavailable) {
// Always explicitly place the ignore_unavailable value.
putParam("ignore_unavailable", Boolean.toString(ignoreUnavailable));
return this;
}
Params withHuman(boolean human) {
if (human) {
putParam("human", Boolean.toString(human));

View File

@ -30,6 +30,8 @@ import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyReposito
import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryResponse;
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotRequest;
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusResponse;
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest;
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotResponse;
import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest;
@ -221,6 +223,35 @@ public final class SnapshotClient {
GetSnapshotsResponse::fromXContent, listener, emptySet());
}
/**
* Gets the status of requested snapshots.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html"> Snapshot and Restore
* API on elastic.co</a>
* @param snapshotsStatusRequest the request
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return the response
* @throws IOException in case there is a problem sending the request or parsing back the response
*/
public SnapshotsStatusResponse status(SnapshotsStatusRequest snapshotsStatusRequest, RequestOptions options)
throws IOException {
return restHighLevelClient.performRequestAndParseEntity(snapshotsStatusRequest, RequestConverters::snapshotsStatus, options,
SnapshotsStatusResponse::fromXContent, emptySet());
}
/**
* Asynchronously gets the status of requested snapshots.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html"> Snapshot and Restore
* API on elastic.co</a>
* @param snapshotsStatusRequest the request
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @param listener the listener to be notified upon request completion
*/
public void statusAsync(SnapshotsStatusRequest snapshotsStatusRequest, RequestOptions options,
ActionListener<SnapshotsStatusResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(snapshotsStatusRequest, RequestConverters::snapshotsStatus, options,
SnapshotsStatusResponse::fromXContent, listener, emptySet());
}
/**
* Deletes a snapshot.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html"> Snapshot and Restore

View File

@ -43,6 +43,7 @@ import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotReq
import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest;
import org.elasticsearch.action.admin.cluster.storedscripts.DeleteStoredScriptRequest;
import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRequest;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
@ -175,6 +176,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertToXC
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
@ -2171,6 +2173,29 @@ public class RequestConvertersTests extends ESTestCase {
assertNull(request.getEntity());
}
public void testSnapshotsStatus() {
Map<String, String> expectedParams = new HashMap<>();
String repository = randomIndicesNames(1, 1)[0];
String[] snapshots = randomIndicesNames(1, 5);
StringBuilder snapshotNames = new StringBuilder(snapshots[0]);
for (int idx = 1; idx < snapshots.length; idx++) {
snapshotNames.append(",").append(snapshots[idx]);
}
boolean ignoreUnavailable = randomBoolean();
String endpoint = "/_snapshot/" + repository + "/" + snapshotNames.toString() + "/_status";
SnapshotsStatusRequest snapshotsStatusRequest = new SnapshotsStatusRequest(repository, snapshots);
setRandomMasterTimeout(snapshotsStatusRequest, expectedParams);
snapshotsStatusRequest.ignoreUnavailable(ignoreUnavailable);
expectedParams.put("ignore_unavailable", Boolean.toString(ignoreUnavailable));
Request request = RequestConverters.snapshotsStatus(snapshotsStatusRequest);
assertThat(request.getEndpoint(), equalTo(endpoint));
assertThat(request.getMethod(), equalTo(HttpGet.METHOD_NAME));
assertThat(request.getParameters(), equalTo(expectedParams));
assertThat(request.getEntity(), is(nullValue()));
}
public void testDeleteSnapshot() {
Map<String, String> expectedParams = new HashMap<>();
String repository = randomIndicesNames(1, 1)[0];

View File

@ -28,6 +28,9 @@ import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequ
import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryResponse;
import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryRequest;
import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryResponse;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusResponse;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotRequest;
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse;
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest;
@ -43,6 +46,7 @@ import java.util.stream.Collectors;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
public class SnapshotIT extends ESRestHighLevelClientTestCase {
@ -173,6 +177,34 @@ public class SnapshotIT extends ESRestHighLevelClientTestCase {
contains("test_snapshot1", "test_snapshot2"));
}
public void testSnapshotsStatus() throws IOException {
String testRepository = "test";
String testSnapshot = "snapshot";
String testIndex = "test_index";
PutRepositoryResponse putRepositoryResponse = createTestRepository(testRepository, FsRepository.TYPE, "{\"location\": \".\"}");
assertTrue(putRepositoryResponse.isAcknowledged());
createIndex(testIndex, Settings.EMPTY);
CreateSnapshotRequest createSnapshotRequest = new CreateSnapshotRequest(testRepository, testSnapshot);
createSnapshotRequest.indices(testIndex);
createSnapshotRequest.waitForCompletion(true);
CreateSnapshotResponse createSnapshotResponse = createTestSnapshot(createSnapshotRequest);
// check that the request went ok without parsing JSON here. When using the high level client, check acknowledgement instead.
assertEquals(RestStatus.OK, createSnapshotResponse.status());
SnapshotsStatusRequest request = new SnapshotsStatusRequest();
request.repository(testRepository);
request.snapshots(new String[]{testSnapshot});
SnapshotsStatusResponse response = execute(request, highLevelClient().snapshot()::status,
highLevelClient().snapshot()::statusAsync);
assertThat(response.getSnapshots().size(), equalTo(1));
assertThat(response.getSnapshots().get(0).getSnapshot().getRepository(), equalTo(testRepository));
assertThat(response.getSnapshots().get(0).getSnapshot().getSnapshotId().getName(), equalTo(testSnapshot));
assertThat(response.getSnapshots().get(0).getIndices().containsKey(testIndex), is(true));
}
public void testDeleteSnapshot() throws IOException {
String repository = "test_repository";
String snapshot = "test_snapshot";

View File

@ -37,11 +37,16 @@ import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest;
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotResponse;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotStats;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotStatus;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusResponse;
import org.elasticsearch.client.ESRestHighLevelClientTestCase;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.cluster.SnapshotsInProgress;
import org.elasticsearch.cluster.metadata.RepositoryMetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
@ -84,8 +89,8 @@ import static org.hamcrest.Matchers.equalTo;
public class SnapshotClientDocumentationIT extends ESRestHighLevelClientTestCase {
private static final String repositoryName = "test_repository";
private static final String snapshotName = "test_snapshot";
private static final String indexName = "test_index";
public void testSnapshotCreateRepository() throws IOException {
RestHighLevelClient client = highLevelClient();
@ -466,6 +471,7 @@ public class SnapshotClientDocumentationIT extends ESRestHighLevelClientTestCase
RestHighLevelClient client = highLevelClient();
createTestRepositories();
createTestIndex();
createTestSnapshots();
// tag::get-snapshots-request
@ -543,10 +549,84 @@ public class SnapshotClientDocumentationIT extends ESRestHighLevelClientTestCase
}
}
public void testSnapshotSnapshotsStatus() throws IOException {
RestHighLevelClient client = highLevelClient();
createTestRepositories();
createTestIndex();
createTestSnapshots();
// tag::snapshots-status-request
SnapshotsStatusRequest request = new SnapshotsStatusRequest();
// end::snapshots-status-request
// tag::snapshots-status-request-repository
request.repository(repositoryName); // <1>
// end::snapshots-status-request-repository
// tag::snapshots-status-request-snapshots
String [] snapshots = new String[] {snapshotName};
request.snapshots(snapshots); // <1>
// end::snapshots-status-request-snapshots
// tag::snapshots-status-request-ignoreUnavailable
request.ignoreUnavailable(true); // <1>
// end::snapshots-status-request-ignoreUnavailable
// tag::snapshots-status-request-masterTimeout
request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1>
request.masterNodeTimeout("1m"); // <2>
// end::snapshots-status-request-masterTimeout
// tag::snapshots-status-execute
SnapshotsStatusResponse response = client.snapshot().status(request, RequestOptions.DEFAULT);
// end::snapshots-status-execute
// tag::snapshots-status-response
List<SnapshotStatus> snapshotStatusesResponse = response.getSnapshots();
SnapshotStatus snapshotStatus = snapshotStatusesResponse.get(0); // <1>
SnapshotsInProgress.State snapshotState = snapshotStatus.getState(); // <2>
SnapshotStats shardStats = snapshotStatus.getIndices().get(indexName).getShards().get(0).getStats(); // <3>
// end::snapshots-status-response
assertThat(snapshotStatusesResponse.size(), equalTo(1));
assertThat(snapshotStatusesResponse.get(0).getSnapshot().getRepository(), equalTo(SnapshotClientDocumentationIT.repositoryName));
assertThat(snapshotStatusesResponse.get(0).getSnapshot().getSnapshotId().getName(), equalTo(snapshotName));
assertThat(snapshotState.completed(), equalTo(true));
}
public void testSnapshotSnapshotsStatusAsync() throws InterruptedException {
RestHighLevelClient client = highLevelClient();
{
SnapshotsStatusRequest request = new SnapshotsStatusRequest();
// tag::snapshots-status-execute-listener
ActionListener<SnapshotsStatusResponse> listener =
new ActionListener<SnapshotsStatusResponse>() {
@Override
public void onResponse(SnapshotsStatusResponse snapshotsStatusResponse) {
// <1>
}
@Override
public void onFailure(Exception e) {
// <2>
}
};
// end::snapshots-status-execute-listener
// Replace the empty listener with a blocking listener in test
final CountDownLatch latch = new CountDownLatch(1);
listener = new LatchedActionListener<>(listener, latch);
// tag::snapshots-status-execute-async
client.snapshot().statusAsync(request, RequestOptions.DEFAULT, listener); // <1>
// end::snapshots-status-execute-async
assertTrue(latch.await(30L, TimeUnit.SECONDS));
}
}
public void testSnapshotDeleteSnapshot() throws IOException {
RestHighLevelClient client = highLevelClient();
createTestRepositories();
createTestIndex();
createTestSnapshots();
// tag::delete-snapshot-request
@ -608,9 +688,14 @@ public class SnapshotClientDocumentationIT extends ESRestHighLevelClientTestCase
assertTrue(highLevelClient().snapshot().createRepository(request, RequestOptions.DEFAULT).isAcknowledged());
}
private void createTestIndex() throws IOException {
createIndex(indexName, Settings.EMPTY);
}
private void createTestSnapshots() throws IOException {
Request createSnapshot = new Request("put", String.format(Locale.ROOT, "_snapshot/%s/%s", repositoryName, snapshotName));
createSnapshot.addParameter("wait_for_completion", "true");
createSnapshot.setJsonEntity("{\"indices\":\"" + indexName + "\"}");
Response response = highLevelClient().getLowLevelClient().performRequest(createSnapshot);
// check that the request went ok without parsing JSON here. When using the high level client, check acknowledgement instead.
assertEquals(200, response.getStatusLine().getStatusCode());

View File

@ -0,0 +1,97 @@
[[java-rest-high-snapshot-snapshots-status]]
=== Snapshots Status API
The Snapshots Status API allows to retrieve detailed information about snapshots in progress.
[[java-rest-high-snapshot-snapshots-status-request]]
==== Snapshots Status Request
A `SnapshotsStatusRequest`:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-request]
--------------------------------------------------
==== Required Arguments
The following arguments must be provided:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-request-repository]
--------------------------------------------------
<1> Sets the repository to check for snapshot statuses
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-request-snapshots]
--------------------------------------------------
<1> The list of snapshot names to check the status of
==== Optional Arguments
The following arguments can optionally be provided:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-request-ignoreUnavailable]
--------------------------------------------------
<1> The command will fail if some of the snapshots are unavailable. The `ignore_unavailable` flag
set to true will return all snapshots that are currently available.
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-request-masterTimeout]
--------------------------------------------------
<1> Timeout to connect to the master node as a `TimeValue`
<2> Timeout to connect to the master node as a `String`
[[java-rest-high-snapshot-snapshots-status-sync]]
==== Synchronous Execution
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-execute]
--------------------------------------------------
[[java-rest-high-snapshot-snapshots-status-async]]
==== Asynchronous Execution
The asynchronous execution of retrieving snapshot statuses requires both the
`SnapshotsStatusRequest` instance and an `ActionListener` instance to be
passed to the asynchronous method:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-execute-async]
--------------------------------------------------
<1> The `SnapshotsStatusRequest` to execute and the `ActionListener`
to use when the execution completes
The asynchronous method does not block and returns immediately. Once it is
completed the `ActionListener` is called back using the `onResponse` method
if the execution successfully completed or using the `onFailure` method if
it failed.
A typical listener for `SnapshotsStatusResponse` looks like:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-execute-listener]
--------------------------------------------------
<1> Called when the execution is successfully completed. The response is
provided as an argument
<2> Called in case of a failure. The raised exception is provided as an argument
[[java-rest-high-snapshot-snapshots-status-response]]
==== Snapshots Status Response
The returned `SnapshotsStatusResponse` allows to retrieve information about the
executed operation as follows:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-response]
--------------------------------------------------
<1> Request contains a list of snapshot statuses
<2> Each status contains information about the snapshot
<3> Example of reading snapshot statistics about a specific index and shard

View File

@ -154,6 +154,7 @@ The Java High Level REST Client supports the following Snapshot APIs:
* <<java-rest-high-snapshot-verify-repository>>
* <<java-rest-high-snapshot-create-snapshot>>
* <<java-rest-high-snapshot-get-snapshots>>
* <<java-rest-high-snapshot-snapshots-status>>
* <<java-rest-high-snapshot-delete-snapshot>>
include::snapshot/get_repository.asciidoc[]
@ -162,6 +163,7 @@ include::snapshot/delete_repository.asciidoc[]
include::snapshot/verify_repository.asciidoc[]
include::snapshot/create_snapshot.asciidoc[]
include::snapshot/get_snapshots.asciidoc[]
include::snapshot/snapshots_status.asciidoc[]
include::snapshot/delete_snapshot.asciidoc[]
== Tasks APIs

View File

@ -19,16 +19,27 @@
package org.elasticsearch.action.admin.cluster.snapshots.status;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.action.support.broadcast.BroadcastShardResponse;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.ToXContentFragment;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentParserUtils;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.index.snapshots.IndexShardSnapshotStatus;
import java.io.IOException;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
public class SnapshotIndexShardStatus extends BroadcastShardResponse implements ToXContentFragment {
private SnapshotIndexShardStage stage = SnapshotIndexShardStage.INIT;
@ -80,6 +91,14 @@ public class SnapshotIndexShardStatus extends BroadcastShardResponse implements
this.nodeId = nodeId;
}
SnapshotIndexShardStatus(ShardId shardId, SnapshotIndexShardStage stage, SnapshotStats stats, String nodeId, String failure) {
super(shardId);
this.stage = stage;
this.stats = stats;
this.nodeId = nodeId;
this.failure = failure;
}
/**
* Returns snapshot stage
*/
@ -143,7 +162,7 @@ public class SnapshotIndexShardStatus extends BroadcastShardResponse implements
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(Integer.toString(getShardId().getId()));
builder.field(Fields.STAGE, getStage());
stats.toXContent(builder, params);
builder.field(SnapshotStats.Fields.STATS, stats, params);
if (getNodeId() != null) {
builder.field(Fields.NODE, getNodeId());
}
@ -153,4 +172,72 @@ public class SnapshotIndexShardStatus extends BroadcastShardResponse implements
builder.endObject();
return builder;
}
static final ObjectParser.NamedObjectParser<SnapshotIndexShardStatus, String> PARSER;
static {
ConstructingObjectParser<SnapshotIndexShardStatus, ShardId> innerParser = new ConstructingObjectParser<>(
"snapshot_index_shard_status", true,
(Object[] parsedObjects, ShardId shard) -> {
int i = 0;
String rawStage = (String) parsedObjects[i++];
String nodeId = (String) parsedObjects[i++];
String failure = (String) parsedObjects[i++];
SnapshotStats stats = (SnapshotStats) parsedObjects[i];
SnapshotIndexShardStage stage;
try {
stage = SnapshotIndexShardStage.valueOf(rawStage);
} catch (IllegalArgumentException iae) {
throw new ElasticsearchParseException(
"failed to parse snapshot index shard status [{}][{}], unknonwn stage [{}]",
shard.getIndex().getName(), shard.getId(), rawStage);
}
return new SnapshotIndexShardStatus(shard, stage, stats, nodeId, failure);
}
);
innerParser.declareString(constructorArg(), new ParseField(Fields.STAGE));
innerParser.declareString(optionalConstructorArg(), new ParseField(Fields.NODE));
innerParser.declareString(optionalConstructorArg(), new ParseField(Fields.REASON));
innerParser.declareObject(constructorArg(), (p, c) -> SnapshotStats.fromXContent(p), new ParseField(SnapshotStats.Fields.STATS));
PARSER = (p, indexId, shardName) -> {
// Combine the index name in the context with the shard name passed in for the named object parser
// into a ShardId to pass as context for the inner parser.
int shard;
try {
shard = Integer.parseInt(shardName);
} catch (NumberFormatException nfe) {
throw new ElasticsearchParseException(
"failed to parse snapshot index shard status [{}], expected numeric shard id but got [{}]", indexId, shardName);
}
ShardId shardId = new ShardId(new Index(indexId, IndexMetaData.INDEX_UUID_NA_VALUE), shard);
return innerParser.parse(p, shardId);
};
}
public static SnapshotIndexShardStatus fromXContent(XContentParser parser, String indexId) throws IOException {
XContentParserUtils.ensureExpectedToken(XContentParser.Token.FIELD_NAME, parser.currentToken(), parser::getTokenLocation);
return PARSER.parse(parser, indexId, parser.currentName());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SnapshotIndexShardStatus that = (SnapshotIndexShardStatus) o;
if (stage != that.stage) return false;
if (stats != null ? !stats.equals(that.stats) : that.stats != null) return false;
if (nodeId != null ? !nodeId.equals(that.nodeId) : that.nodeId != null) return false;
return failure != null ? failure.equals(that.failure) : that.failure == null;
}
@Override
public int hashCode() {
int result = stage != null ? stage.hashCode() : 0;
result = 31 * result + (stats != null ? stats.hashCode() : 0);
result = 31 * result + (nodeId != null ? nodeId.hashCode() : 0);
result = 31 * result + (failure != null ? failure.hashCode() : 0);
return result;
}
}

View File

@ -19,17 +19,24 @@
package org.elasticsearch.action.admin.cluster.snapshots.status;
import org.elasticsearch.common.xcontent.ToXContent.Params;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.ToXContentFragment;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentParserUtils;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import static java.util.Collections.emptyMap;
import static java.util.Collections.unmodifiableMap;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
/**
* Represents snapshot status of all shards in the index
@ -57,6 +64,14 @@ public class SnapshotIndexStatus implements Iterable<SnapshotIndexShardStatus>,
this.indexShards = unmodifiableMap(indexShards);
}
public SnapshotIndexStatus(String index, Map<Integer, SnapshotIndexShardStatus> indexShards, SnapshotShardsStats shardsStats,
SnapshotStats stats) {
this.index = index;
this.indexShards = indexShards;
this.shardsStats = shardsStats;
this.stats = stats;
}
/**
* Returns the index name
*/
@ -97,8 +112,8 @@ public class SnapshotIndexStatus implements Iterable<SnapshotIndexShardStatus>,
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(getIndex());
shardsStats.toXContent(builder, params);
stats.toXContent(builder, params);
builder.field(SnapshotShardsStats.Fields.SHARDS_STATS, shardsStats, params);
builder.field(SnapshotStats.Fields.STATS, stats, params);
builder.startObject(Fields.SHARDS);
for (SnapshotIndexShardStatus shard : indexShards.values()) {
shard.toXContent(builder, params);
@ -107,4 +122,61 @@ public class SnapshotIndexStatus implements Iterable<SnapshotIndexShardStatus>,
builder.endObject();
return builder;
}
static final ObjectParser.NamedObjectParser<SnapshotIndexStatus, Void> PARSER;
static {
ConstructingObjectParser<SnapshotIndexStatus, String> innerParser = new ConstructingObjectParser<>(
"snapshot_index_status", true,
(Object[] parsedObjects, String index) -> {
int i = 0;
SnapshotShardsStats shardsStats = ((SnapshotShardsStats) parsedObjects[i++]);
SnapshotStats stats = ((SnapshotStats) parsedObjects[i++]);
@SuppressWarnings("unchecked") List<SnapshotIndexShardStatus> shardStatuses =
(List<SnapshotIndexShardStatus>) parsedObjects[i];
final Map<Integer, SnapshotIndexShardStatus> indexShards;
if (shardStatuses == null || shardStatuses.isEmpty()) {
indexShards = emptyMap();
} else {
indexShards = new HashMap<>(shardStatuses.size());
for (SnapshotIndexShardStatus shardStatus : shardStatuses) {
indexShards.put(shardStatus.getShardId().getId(), shardStatus);
}
}
return new SnapshotIndexStatus(index, indexShards, shardsStats, stats);
});
innerParser.declareObject(constructorArg(), (p, c) -> SnapshotShardsStats.PARSER.apply(p, null),
new ParseField(SnapshotShardsStats.Fields.SHARDS_STATS));
innerParser.declareObject(constructorArg(), (p, c) -> SnapshotStats.fromXContent(p),
new ParseField(SnapshotStats.Fields.STATS));
innerParser.declareNamedObjects(constructorArg(), SnapshotIndexShardStatus.PARSER, new ParseField(Fields.SHARDS));
PARSER = ((p, c, name) -> innerParser.apply(p, name));
}
public static SnapshotIndexStatus fromXContent(XContentParser parser) throws IOException {
XContentParserUtils.ensureExpectedToken(XContentParser.Token.FIELD_NAME, parser.currentToken(), parser::getTokenLocation);
return PARSER.parse(parser, null, parser.currentName());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SnapshotIndexStatus that = (SnapshotIndexStatus) o;
if (index != null ? !index.equals(that.index) : that.index != null) return false;
if (indexShards != null ? !indexShards.equals(that.indexShards) : that.indexShards != null) return false;
if (shardsStats != null ? !shardsStats.equals(that.shardsStats) : that.shardsStats != null) return false;
return stats != null ? stats.equals(that.stats) : that.stats == null;
}
@Override
public int hashCode() {
int result = index != null ? index.hashCode() : 0;
result = 31 * result + (indexShards != null ? indexShards.hashCode() : 0);
result = 31 * result + (shardsStats != null ? shardsStats.hashCode() : 0);
result = 31 * result + (stats != null ? stats.hashCode() : 0);
return result;
}
}

View File

@ -19,17 +19,22 @@
package org.elasticsearch.action.admin.cluster.snapshots.status;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.ToXContentFragment;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
import java.util.Collection;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
/**
* Status of a snapshot shards
*/
public class SnapshotShardsStats implements ToXContentFragment {
public class SnapshotShardsStats implements ToXContentObject {
private int initializingShards;
private int startedShards;
@ -63,6 +68,16 @@ public class SnapshotShardsStats implements ToXContentFragment {
}
}
public SnapshotShardsStats(int initializingShards, int startedShards, int finalizingShards, int doneShards, int failedShards,
int totalShards) {
this.initializingShards = initializingShards;
this.startedShards = startedShards;
this.finalizingShards = finalizingShards;
this.doneShards = doneShards;
this.failedShards = failedShards;
this.totalShards = totalShards;
}
/**
* Number of shards with the snapshot in the initializing stage
*/
@ -117,15 +132,68 @@ public class SnapshotShardsStats implements ToXContentFragment {
@Override
public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException {
builder.startObject(Fields.SHARDS_STATS);
builder.startObject();
{
builder.field(Fields.INITIALIZING, getInitializingShards());
builder.field(Fields.STARTED, getStartedShards());
builder.field(Fields.FINALIZING, getFinalizingShards());
builder.field(Fields.DONE, getDoneShards());
builder.field(Fields.FAILED, getFailedShards());
builder.field(Fields.TOTAL, getTotalShards());
}
builder.endObject();
return builder;
}
static final ConstructingObjectParser<SnapshotShardsStats, Void> PARSER = new ConstructingObjectParser<>(
Fields.SHARDS_STATS, true,
(Object[] parsedObjects) -> {
int i = 0;
int initializingShards = (int) parsedObjects[i++];
int startedShards = (int) parsedObjects[i++];
int finalizingShards = (int) parsedObjects[i++];
int doneShards = (int) parsedObjects[i++];
int failedShards = (int) parsedObjects[i++];
int totalShards = (int) parsedObjects[i];
return new SnapshotShardsStats(initializingShards, startedShards, finalizingShards, doneShards, failedShards, totalShards);
}
);
static {
PARSER.declareInt(constructorArg(), new ParseField(Fields.INITIALIZING));
PARSER.declareInt(constructorArg(), new ParseField(Fields.STARTED));
PARSER.declareInt(constructorArg(), new ParseField(Fields.FINALIZING));
PARSER.declareInt(constructorArg(), new ParseField(Fields.DONE));
PARSER.declareInt(constructorArg(), new ParseField(Fields.FAILED));
PARSER.declareInt(constructorArg(), new ParseField(Fields.TOTAL));
}
public static SnapshotShardsStats fromXContent(XContentParser parser) throws IOException {
return PARSER.apply(parser, null);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SnapshotShardsStats that = (SnapshotShardsStats) o;
if (initializingShards != that.initializingShards) return false;
if (startedShards != that.startedShards) return false;
if (finalizingShards != that.finalizingShards) return false;
if (doneShards != that.doneShards) return false;
if (failedShards != that.failedShards) return false;
return totalShards == that.totalShards;
}
@Override
public int hashCode() {
int result = initializingShards;
result = 31 * result + startedShards;
result = 31 * result + finalizingShards;
result = 31 * result + doneShards;
result = 31 * result + failedShards;
result = 31 * result + totalShards;
return result;
}
}

View File

@ -26,12 +26,14 @@ import org.elasticsearch.common.io.stream.Streamable;
import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.ToXContentFragment;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentParserUtils;
import java.io.IOException;
public class SnapshotStats implements Streamable, ToXContentFragment {
public class SnapshotStats implements Streamable, ToXContentObject {
private long startTime;
private long time;
@ -176,35 +178,132 @@ public class SnapshotStats implements Streamable, ToXContentFragment {
@Override
public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException {
builder.startObject(Fields.STATS)
// incremental starts
.startObject(Fields.INCREMENTAL)
.field(Fields.FILE_COUNT, getIncrementalFileCount())
.humanReadableField(Fields.SIZE_IN_BYTES, Fields.SIZE, new ByteSizeValue(getIncrementalSize()))
// incremental ends
.endObject();
builder.startObject();
{
builder.startObject(Fields.INCREMENTAL);
{
builder.field(Fields.FILE_COUNT, getIncrementalFileCount());
builder.humanReadableField(Fields.SIZE_IN_BYTES, Fields.SIZE, new ByteSizeValue(getIncrementalSize()));
}
builder.endObject();
if (getProcessedFileCount() != getIncrementalFileCount()) {
// processed starts
builder.startObject(Fields.PROCESSED)
.field(Fields.FILE_COUNT, getProcessedFileCount())
.humanReadableField(Fields.SIZE_IN_BYTES, Fields.SIZE, new ByteSizeValue(getProcessedSize()))
// processed ends
.endObject();
builder.startObject(Fields.PROCESSED);
{
builder.field(Fields.FILE_COUNT, getProcessedFileCount());
builder.humanReadableField(Fields.SIZE_IN_BYTES, Fields.SIZE, new ByteSizeValue(getProcessedSize()));
}
builder.endObject();
}
// total starts
builder.startObject(Fields.TOTAL)
.field(Fields.FILE_COUNT, getTotalFileCount())
.humanReadableField(Fields.SIZE_IN_BYTES, Fields.SIZE, new ByteSizeValue(getTotalSize()))
// total ends
.endObject();
// timings stats
builder.field(Fields.START_TIME_IN_MILLIS, getStartTime())
.humanReadableField(Fields.TIME_IN_MILLIS, Fields.TIME, new TimeValue(getTime()));
builder.startObject(Fields.TOTAL);
{
builder.field(Fields.FILE_COUNT, getTotalFileCount());
builder.humanReadableField(Fields.SIZE_IN_BYTES, Fields.SIZE, new ByteSizeValue(getTotalSize()));
}
builder.endObject();
// timings stats
builder.field(Fields.START_TIME_IN_MILLIS, getStartTime());
builder.humanReadableField(Fields.TIME_IN_MILLIS, Fields.TIME, new TimeValue(getTime()));
}
return builder.endObject();
}
public static SnapshotStats fromXContent(XContentParser parser) throws IOException {
// Parse this old school style instead of using the ObjectParser since there's an impedance mismatch between how the
// object has historically been written as JSON versus how it is structured in Java.
XContentParser.Token token = parser.currentToken();
if (token == null) {
token = parser.nextToken();
}
XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, token, parser::getTokenLocation);
long startTime = 0;
long time = 0;
int incrementalFileCount = 0;
int totalFileCount = 0;
int processedFileCount = 0;
long incrementalSize = 0;
long totalSize = 0;
long processedSize = 0;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
XContentParserUtils.ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser::getTokenLocation);
String currentName = parser.currentName();
token = parser.nextToken();
if (currentName.equals(Fields.INCREMENTAL)) {
XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, token, parser::getTokenLocation);
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
XContentParserUtils.ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser::getTokenLocation);
String innerName = parser.currentName();
token = parser.nextToken();
if (innerName.equals(Fields.FILE_COUNT)) {
XContentParserUtils.ensureExpectedToken(XContentParser.Token.VALUE_NUMBER, token, parser::getTokenLocation);
incrementalFileCount = parser.intValue();
} else if (innerName.equals(Fields.SIZE_IN_BYTES)) {
XContentParserUtils.ensureExpectedToken(XContentParser.Token.VALUE_NUMBER, token, parser::getTokenLocation);
incrementalSize = parser.longValue();
} else {
// Unknown sub field, skip
if (token == XContentParser.Token.START_OBJECT || token == XContentParser.Token.START_ARRAY) {
parser.skipChildren();
}
}
}
} else if (currentName.equals(Fields.PROCESSED)) {
XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, token, parser::getTokenLocation);
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
XContentParserUtils.ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser::getTokenLocation);
String innerName = parser.currentName();
token = parser.nextToken();
if (innerName.equals(Fields.FILE_COUNT)) {
XContentParserUtils.ensureExpectedToken(XContentParser.Token.VALUE_NUMBER, token, parser::getTokenLocation);
processedFileCount = parser.intValue();
} else if (innerName.equals(Fields.SIZE_IN_BYTES)) {
XContentParserUtils.ensureExpectedToken(XContentParser.Token.VALUE_NUMBER, token, parser::getTokenLocation);
processedSize = parser.longValue();
} else {
// Unknown sub field, skip
if (token == XContentParser.Token.START_OBJECT || token == XContentParser.Token.START_ARRAY) {
parser.skipChildren();
}
}
}
} else if (currentName.equals(Fields.TOTAL)) {
XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, token, parser::getTokenLocation);
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
XContentParserUtils.ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser::getTokenLocation);
String innerName = parser.currentName();
token = parser.nextToken();
if (innerName.equals(Fields.FILE_COUNT)) {
XContentParserUtils.ensureExpectedToken(XContentParser.Token.VALUE_NUMBER, token, parser::getTokenLocation);
totalFileCount = parser.intValue();
} else if (innerName.equals(Fields.SIZE_IN_BYTES)) {
XContentParserUtils.ensureExpectedToken(XContentParser.Token.VALUE_NUMBER, token, parser::getTokenLocation);
totalSize = parser.longValue();
} else {
// Unknown sub field, skip
if (token == XContentParser.Token.START_OBJECT || token == XContentParser.Token.START_ARRAY) {
parser.skipChildren();
}
}
}
} else if (currentName.equals(Fields.START_TIME_IN_MILLIS)) {
XContentParserUtils.ensureExpectedToken(XContentParser.Token.VALUE_NUMBER, token, parser::getTokenLocation);
startTime = parser.longValue();
} else if (currentName.equals(Fields.TIME_IN_MILLIS)) {
XContentParserUtils.ensureExpectedToken(XContentParser.Token.VALUE_NUMBER, token, parser::getTokenLocation);
time = parser.longValue();
} else {
// Unknown field, skip
if (token == XContentParser.Token.START_OBJECT || token == XContentParser.Token.START_ARRAY) {
parser.skipChildren();
}
}
}
return new SnapshotStats(startTime, time, incrementalFileCount, totalFileCount, processedFileCount, incrementalSize, totalSize,
processedSize);
}
void add(SnapshotStats stats) {
incrementalFileCount += stats.incrementalFileCount;
totalFileCount += stats.totalFileCount;
@ -229,4 +328,34 @@ public class SnapshotStats implements Streamable, ToXContentFragment {
time = endTime - startTime;
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SnapshotStats that = (SnapshotStats) o;
if (startTime != that.startTime) return false;
if (time != that.time) return false;
if (incrementalFileCount != that.incrementalFileCount) return false;
if (totalFileCount != that.totalFileCount) return false;
if (processedFileCount != that.processedFileCount) return false;
if (incrementalSize != that.incrementalSize) return false;
if (totalSize != that.totalSize) return false;
return processedSize == that.processedSize;
}
@Override
public int hashCode() {
int result = (int) (startTime ^ (startTime >>> 32));
result = 31 * result + (int) (time ^ (time >>> 32));
result = 31 * result + incrementalFileCount;
result = 31 * result + totalFileCount;
result = 31 * result + processedFileCount;
result = 31 * result + (int) (incrementalSize ^ (incrementalSize >>> 32));
result = 31 * result + (int) (totalSize ^ (totalSize >>> 32));
result = 31 * result + (int) (processedSize ^ (processedSize >>> 32));
return result;
}
}

View File

@ -20,15 +20,21 @@
package org.elasticsearch.action.admin.cluster.snapshots.status;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.SnapshotsInProgress;
import org.elasticsearch.cluster.SnapshotsInProgress.State;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Streamable;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.snapshots.Snapshot;
import org.elasticsearch.snapshots.SnapshotId;
import java.io.IOException;
import java.util.ArrayList;
@ -40,7 +46,11 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static java.util.Collections.unmodifiableMap;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
/**
* Status of a snapshot
@ -72,6 +82,18 @@ public class SnapshotStatus implements ToXContentObject, Streamable {
updateShardStats();
}
private SnapshotStatus(Snapshot snapshot, State state, List<SnapshotIndexShardStatus> shards,
Map<String, SnapshotIndexStatus> indicesStatus, SnapshotShardsStats shardsStats,
SnapshotStats stats, Boolean includeGlobalState) {
this.snapshot = snapshot;
this.state = state;
this.shards = shards;
this.indicesStatus = indicesStatus;
this.shardsStats = shardsStats;
this.stats = stats;
this.includeGlobalState = includeGlobalState;
}
SnapshotStatus() {
}
@ -207,8 +229,8 @@ public class SnapshotStatus implements ToXContentObject, Streamable {
if (includeGlobalState != null) {
builder.field(INCLUDE_GLOBAL_STATE, includeGlobalState);
}
shardsStats.toXContent(builder, params);
stats.toXContent(builder, params);
builder.field(SnapshotShardsStats.Fields.SHARDS_STATS, shardsStats, params);
builder.field(SnapshotStats.Fields.STATS, stats, params);
builder.startObject(INDICES);
for (SnapshotIndexStatus indexStatus : getIndices().values()) {
indexStatus.toXContent(builder, params);
@ -218,6 +240,52 @@ public class SnapshotStatus implements ToXContentObject, Streamable {
return builder;
}
static final ConstructingObjectParser<SnapshotStatus, Void> PARSER = new ConstructingObjectParser<>(
"snapshot_status", true,
(Object[] parsedObjects) -> {
int i = 0;
String name = (String) parsedObjects[i++];
String repository = (String) parsedObjects[i++];
String uuid = (String) parsedObjects[i++];
String rawState = (String) parsedObjects[i++];
Boolean includeGlobalState = (Boolean) parsedObjects[i++];
SnapshotStats stats = ((SnapshotStats) parsedObjects[i++]);
SnapshotShardsStats shardsStats = ((SnapshotShardsStats) parsedObjects[i++]);
@SuppressWarnings("unchecked") List<SnapshotIndexStatus> indices = ((List<SnapshotIndexStatus>) parsedObjects[i]);
Snapshot snapshot = new Snapshot(repository, new SnapshotId(name, uuid));
SnapshotsInProgress.State state = SnapshotsInProgress.State.valueOf(rawState);
Map<String, SnapshotIndexStatus> indicesStatus;
List<SnapshotIndexShardStatus> shards;
if (indices == null || indices.isEmpty()) {
indicesStatus = emptyMap();
shards = emptyList();
} else {
indicesStatus = new HashMap<>(indices.size());
shards = new ArrayList<>();
for (SnapshotIndexStatus index : indices) {
indicesStatus.put(index.getIndex(), index);
shards.addAll(index.getShards().values());
}
}
return new SnapshotStatus(snapshot, state, shards, indicesStatus, shardsStats, stats, includeGlobalState);
});
static {
PARSER.declareString(constructorArg(), new ParseField(SNAPSHOT));
PARSER.declareString(constructorArg(), new ParseField(REPOSITORY));
PARSER.declareString(constructorArg(), new ParseField(UUID));
PARSER.declareString(constructorArg(), new ParseField(STATE));
PARSER.declareBoolean(optionalConstructorArg(), new ParseField(INCLUDE_GLOBAL_STATE));
PARSER.declareField(constructorArg(), SnapshotStats::fromXContent, new ParseField(SnapshotStats.Fields.STATS),
ObjectParser.ValueType.OBJECT);
PARSER.declareObject(constructorArg(), SnapshotShardsStats.PARSER, new ParseField(SnapshotShardsStats.Fields.SHARDS_STATS));
PARSER.declareNamedObjects(constructorArg(), SnapshotIndexStatus.PARSER, new ParseField(INDICES));
}
public static SnapshotStatus fromXContent(XContentParser parser) throws IOException {
return PARSER.parse(parser, null);
}
private void updateShardStats() {
stats = new SnapshotStats();
shardsStats = new SnapshotShardsStats(shards);
@ -225,4 +293,31 @@ public class SnapshotStatus implements ToXContentObject, Streamable {
stats.add(shard.getStats());
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SnapshotStatus that = (SnapshotStatus) o;
if (snapshot != null ? !snapshot.equals(that.snapshot) : that.snapshot != null) return false;
if (state != that.state) return false;
if (indicesStatus != null ? !indicesStatus.equals(that.indicesStatus) : that.indicesStatus != null)
return false;
if (shardsStats != null ? !shardsStats.equals(that.shardsStats) : that.shardsStats != null) return false;
if (stats != null ? !stats.equals(that.stats) : that.stats != null) return false;
return includeGlobalState != null ? includeGlobalState.equals(that.includeGlobalState) : that.includeGlobalState == null;
}
@Override
public int hashCode() {
int result = snapshot != null ? snapshot.hashCode() : 0;
result = 31 * result + (state != null ? state.hashCode() : 0);
result = 31 * result + (indicesStatus != null ? indicesStatus.hashCode() : 0);
result = 31 * result + (shardsStats != null ? shardsStats.hashCode() : 0);
result = 31 * result + (stats != null ? stats.hashCode() : 0);
result = 31 * result + (includeGlobalState != null ? includeGlobalState.hashCode() : 0);
return result;
}
}

View File

@ -20,16 +20,21 @@
package org.elasticsearch.action.admin.cluster.snapshots.status;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
/**
* Snapshot status response
*/
@ -85,4 +90,33 @@ public class SnapshotsStatusResponse extends ActionResponse implements ToXConten
return builder;
}
private static final ConstructingObjectParser<SnapshotsStatusResponse, Void> PARSER = new ConstructingObjectParser<>(
"snapshots_status_response", true,
(Object[] parsedObjects) -> {
@SuppressWarnings("unchecked") List<SnapshotStatus> snapshots = (List<SnapshotStatus>) parsedObjects[0];
return new SnapshotsStatusResponse(snapshots);
}
);
static {
PARSER.declareObjectArray(constructorArg(), SnapshotStatus.PARSER, new ParseField("snapshots"));
}
public static SnapshotsStatusResponse fromXContent(XContentParser parser) throws IOException {
return PARSER.parse(parser, null);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SnapshotsStatusResponse response = (SnapshotsStatusResponse) o;
return snapshots != null ? snapshots.equals(response.snapshots) : response.snapshots == null;
}
@Override
public int hashCode() {
return snapshots != null ? snapshots.hashCode() : 0;
}
}

View File

@ -0,0 +1,70 @@
/*
* 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.action.admin.cluster.snapshots.status;
import java.io.IOException;
import java.util.function.Predicate;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentParserUtils;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.test.AbstractXContentTestCase;
public class SnapshotIndexShardStatusTests extends AbstractXContentTestCase<SnapshotIndexShardStatus> {
@Override
protected SnapshotIndexShardStatus createTestInstance() {
return createForIndex(randomAlphaOfLength(10));
}
protected SnapshotIndexShardStatus createForIndex(String indexName) {
ShardId shardId = new ShardId(new Index(indexName, IndexMetaData.INDEX_UUID_NA_VALUE), randomIntBetween(0, 500));
SnapshotIndexShardStage stage = randomFrom(SnapshotIndexShardStage.values());
SnapshotStats stats = new SnapshotStatsTests().createTestInstance();
String nodeId = randomAlphaOfLength(20);
String failure = null;
if (rarely()) {
failure = randomAlphaOfLength(200);
}
return new SnapshotIndexShardStatus(shardId, stage, stats, nodeId, failure);
}
@Override
protected Predicate<String> getRandomFieldsExcludeFilter() {
// Do not place random fields in the root object since its fields correspond to shard names.
return String::isEmpty;
}
@Override
protected SnapshotIndexShardStatus doParseInstance(XContentParser parser) throws IOException {
XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation);
XContentParserUtils.ensureExpectedToken(XContentParser.Token.FIELD_NAME, parser.nextToken(), parser::getTokenLocation);
SnapshotIndexShardStatus status = SnapshotIndexShardStatus.fromXContent(parser, parser.currentName());
XContentParserUtils.ensureExpectedToken(XContentParser.Token.END_OBJECT, parser.nextToken(), parser::getTokenLocation);
return status;
}
@Override
protected boolean supportsUnknownFields() {
return true;
}
}

View File

@ -0,0 +1,64 @@
/*
* 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.action.admin.cluster.snapshots.status;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentParserUtils;
import org.elasticsearch.test.AbstractXContentTestCase;
public class SnapshotIndexStatusTests extends AbstractXContentTestCase<SnapshotIndexStatus> {
@Override
protected SnapshotIndexStatus createTestInstance() {
String index = randomAlphaOfLength(10);
List<SnapshotIndexShardStatus> shardStatuses = new ArrayList<>();
SnapshotIndexShardStatusTests builder = new SnapshotIndexShardStatusTests();
for (int idx = 0; idx < randomIntBetween(0, 10); idx++) {
shardStatuses.add(builder.createForIndex(index));
}
return new SnapshotIndexStatus(index, shardStatuses);
}
@Override
protected Predicate<String> getRandomFieldsExcludeFilter() {
// Do not place random fields in the root object or the shards field since their fields correspond to names.
return (s) -> s.isEmpty() || s.endsWith("shards");
}
@Override
protected SnapshotIndexStatus doParseInstance(XContentParser parser) throws IOException {
XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation);
XContentParserUtils.ensureExpectedToken(XContentParser.Token.FIELD_NAME, parser.nextToken(), parser::getTokenLocation);
SnapshotIndexStatus status = SnapshotIndexStatus.fromXContent(parser);
XContentParserUtils.ensureExpectedToken(XContentParser.Token.END_OBJECT, parser.nextToken(), parser::getTokenLocation);
return status;
}
@Override
protected boolean supportsUnknownFields() {
return true;
}
}

View File

@ -0,0 +1,49 @@
/*
* 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.action.admin.cluster.snapshots.status;
import java.io.IOException;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.test.AbstractXContentTestCase;
public class SnapshotShardsStatsTests extends AbstractXContentTestCase<SnapshotShardsStats> {
@Override
protected SnapshotShardsStats createTestInstance() {
int initializingShards = randomInt();
int startedShards = randomInt();
int finalizingShards = randomInt();
int doneShards = randomInt();
int failedShards = randomInt();
int totalShards = randomInt();
return new SnapshotShardsStats(initializingShards, startedShards, finalizingShards, doneShards, failedShards, totalShards);
}
@Override
protected SnapshotShardsStats doParseInstance(XContentParser parser) throws IOException {
return SnapshotShardsStats.fromXContent(parser);
}
@Override
protected boolean supportsUnknownFields() {
return true;
}
}

View File

@ -0,0 +1,52 @@
/*
* 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.action.admin.cluster.snapshots.status;
import java.io.IOException;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.test.AbstractXContentTestCase;
public class SnapshotStatsTests extends AbstractXContentTestCase<SnapshotStats> {
@Override
protected SnapshotStats createTestInstance() {
long startTime = randomNonNegativeLong();
long time = randomNonNegativeLong();
int incrementalFileCount = randomIntBetween(0, Integer.MAX_VALUE);
int totalFileCount = randomIntBetween(0, Integer.MAX_VALUE);
int processedFileCount = randomIntBetween(0, Integer.MAX_VALUE);
long incrementalSize = ((long)randomIntBetween(0, Integer.MAX_VALUE)) * 2;
long totalSize = ((long)randomIntBetween(0, Integer.MAX_VALUE)) * 2;
long processedSize = ((long)randomIntBetween(0, Integer.MAX_VALUE)) * 2;
return new SnapshotStats(startTime, time, incrementalFileCount, totalFileCount,
processedFileCount, incrementalSize, totalSize, processedSize);
}
@Override
protected SnapshotStats doParseInstance(XContentParser parser) throws IOException {
return SnapshotStats.fromXContent(parser);
}
@Override
protected boolean supportsUnknownFields() {
return true;
}
}

View File

@ -21,16 +21,19 @@ package org.elasticsearch.action.admin.cluster.snapshots.status;
import org.elasticsearch.cluster.SnapshotsInProgress;
import org.elasticsearch.common.UUIDs;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.snapshots.Snapshot;
import org.elasticsearch.snapshots.SnapshotId;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.AbstractXContentTestCase;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class SnapshotStatusTests extends ESTestCase {
public class SnapshotStatusTests extends AbstractXContentTestCase<SnapshotStatus> {
public void testToString() throws Exception {
@ -146,4 +149,39 @@ public class SnapshotStatusTests extends ESTestCase {
"}";
assertEquals(expected, status.toString());
}
@Override
protected SnapshotStatus createTestInstance() {
SnapshotsInProgress.State state = randomFrom(SnapshotsInProgress.State.values());
String uuid = UUIDs.randomBase64UUID();
SnapshotId id = new SnapshotId("test-snap", uuid);
Snapshot snapshot = new Snapshot("test-repo", id);
SnapshotIndexShardStatusTests builder = new SnapshotIndexShardStatusTests();
builder.createTestInstance();
List<SnapshotIndexShardStatus> snapshotIndexShardStatuses = new ArrayList<>();
for (int idx = 0; idx < randomIntBetween(0, 10); idx++) {
SnapshotIndexShardStatus snapshotIndexShardStatus = builder.createTestInstance();
snapshotIndexShardStatuses.add(snapshotIndexShardStatus);
}
boolean includeGlobalState = randomBoolean();
return new SnapshotStatus(snapshot, state, snapshotIndexShardStatuses, includeGlobalState);
}
@Override
protected Predicate<String> getRandomFieldsExcludeFilter() {
// Do not place random fields in the indices field or shards field since their fields correspond to names.
return (s) -> s.endsWith("shards") || s.endsWith("indices");
}
@Override
protected SnapshotStatus doParseInstance(XContentParser parser) throws IOException {
return SnapshotStatus.fromXContent(parser);
}
@Override
protected boolean supportsUnknownFields() {
return true;
}
}

View File

@ -0,0 +1,57 @@
/*
* 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.action.admin.cluster.snapshots.status;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.test.AbstractXContentTestCase;
public class SnapshotsStatusResponseTests extends AbstractXContentTestCase<SnapshotsStatusResponse> {
@Override
protected SnapshotsStatusResponse doParseInstance(XContentParser parser) throws IOException {
return SnapshotsStatusResponse.fromXContent(parser);
}
@Override
protected Predicate<String> getRandomFieldsExcludeFilter() {
// Do not place random fields in the indices field or shards field since their fields correspond to names.
return (s) -> s.endsWith("shards") || s.endsWith("indices");
}
@Override
protected boolean supportsUnknownFields() {
return true;
}
@Override
protected SnapshotsStatusResponse createTestInstance() {
SnapshotStatusTests statusBuilder = new SnapshotStatusTests();
List<SnapshotStatus> snapshotStatuses = new ArrayList<>();
for (int idx = 0; idx < randomIntBetween(0, 5); idx++) {
snapshotStatuses.add(statusBuilder.createTestInstance());
}
return new SnapshotsStatusResponse(snapshotStatuses);
}
}