diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java
index 839d86bf9f10a..23cf9ca707374 100644
--- a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java
+++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java
@@ -42,6 +42,7 @@
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.cache.clear.ClearIndicesCacheRequest;
@@ -605,7 +606,7 @@ static Request searchTemplate(SearchTemplateRequest searchTemplateRequest) throw
request.setEntity(createEntity(searchTemplateRequest, REQUEST_BODY_CONTENT_TYPE));
return request;
}
-
+
static Request multiSearchTemplate(MultiSearchTemplateRequest multiSearchTemplateRequest) throws IOException {
Request request = new Request(HttpPost.METHOD_NAME, "/_msearch/template");
@@ -619,7 +620,7 @@ static Request multiSearchTemplate(MultiSearchTemplateRequest multiSearchTemplat
byte[] source = MultiSearchTemplateRequest.writeMultiLineFormat(multiSearchTemplateRequest, xContent);
request.setEntity(new ByteArrayEntity(source, createContentType(xContent.type())));
return request;
- }
+ }
static Request existsAlias(GetAliasesRequest getAliasesRequest) {
if ((getAliasesRequest.indices() == null || getAliasesRequest.indices().length == 0) &&
@@ -931,6 +932,20 @@ static Request getSnapshots(GetSnapshotsRequest getSnapshotsRequest) {
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())
@@ -1205,7 +1220,7 @@ Params withWaitForActiveShards(ActiveShardCount activeShardCount, ActiveShardCou
}
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) {
@@ -1224,6 +1239,12 @@ Params withIndicesOptions(IndicesOptions indicesOptions) {
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));
diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/SnapshotClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/SnapshotClient.java
index fa147a338de0a..bc0bbe95488f4 100644
--- a/client/rest-high-level/src/main/java/org/elasticsearch/client/SnapshotClient.java
+++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/SnapshotClient.java
@@ -30,6 +30,8 @@
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 void getAsync(GetSnapshotsRequest getSnapshotsRequest, RequestOptions opt
GetSnapshotsResponse::fromXContent, listener, emptySet());
}
+ /**
+ * Gets the status of requested snapshots.
+ * See Snapshot and Restore
+ * API on elastic.co
+ * @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 Snapshot and Restore
+ * API on elastic.co
+ * @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 listener) {
+ restHighLevelClient.performRequestAsyncAndParseEntity(snapshotsStatusRequest, RequestConverters::snapshotsStatus, options,
+ SnapshotsStatusResponse::fromXContent, listener, emptySet());
+ }
+
/**
* Deletes a snapshot.
* See Snapshot and Restore
diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java
index e838989a0c853..5cc0e830446e0 100644
--- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java
+++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java
@@ -42,6 +42,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.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;
@@ -171,6 +172,7 @@
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;
@@ -1374,42 +1376,42 @@ public void testRenderSearchTemplate() throws Exception {
assertEquals(Collections.emptyMap(), request.getParameters());
assertToXContentBody(searchTemplateRequest, request.getEntity());
}
-
+
public void testMultiSearchTemplate() throws Exception {
final int numSearchRequests = randomIntBetween(1, 10);
MultiSearchTemplateRequest multiSearchTemplateRequest = new MultiSearchTemplateRequest();
-
+
for (int i = 0; i < numSearchRequests; i++) {
// Create a random request.
String[] indices = randomIndicesNames(0, 5);
SearchRequest searchRequest = new SearchRequest(indices);
-
+
Map expectedParams = new HashMap<>();
setRandomSearchParams(searchRequest, expectedParams);
-
+
// scroll is not supported in the current msearch or msearchtemplate api, so unset it:
searchRequest.scroll((Scroll) null);
// batched reduce size is currently not set-able on a per-request basis as it is a query string parameter only
searchRequest.setBatchedReduceSize(SearchRequest.DEFAULT_BATCHED_REDUCE_SIZE);
-
+
setRandomIndicesOptions(searchRequest::indicesOptions, searchRequest::indicesOptions, expectedParams);
-
+
SearchTemplateRequest searchTemplateRequest = new SearchTemplateRequest(searchRequest);
-
+
searchTemplateRequest.setScript("{\"query\": { \"match\" : { \"{{field}}\" : \"{{value}}\" }}}");
searchTemplateRequest.setScriptType(ScriptType.INLINE);
searchTemplateRequest.setProfile(randomBoolean());
-
+
Map scriptParams = new HashMap<>();
scriptParams.put("field", "name");
scriptParams.put("value", randomAlphaOfLengthBetween(2, 5));
searchTemplateRequest.setScriptParams(scriptParams);
-
- multiSearchTemplateRequest.add(searchTemplateRequest);
+
+ multiSearchTemplateRequest.add(searchTemplateRequest);
}
Request multiRequest = RequestConverters.multiSearchTemplate(multiSearchTemplateRequest);
-
+
assertEquals(HttpPost.METHOD_NAME, multiRequest.getMethod());
assertEquals("/_msearch/template", multiRequest.getEndpoint());
List searchRequests = multiSearchTemplateRequest.requests();
@@ -1418,9 +1420,9 @@ public void testMultiSearchTemplate() throws Exception {
HttpEntity actualEntity = multiRequest.getEntity();
byte[] expectedBytes = MultiSearchTemplateRequest.writeMultiLineFormat(multiSearchTemplateRequest, XContentType.JSON.xContent());
assertEquals(XContentType.JSON.mediaTypeWithoutParameters(), actualEntity.getContentType().getValue());
- assertEquals(new BytesArray(expectedBytes), new BytesArray(EntityUtils.toByteArray(actualEntity)));
+ assertEquals(new BytesArray(expectedBytes), new BytesArray(EntityUtils.toByteArray(actualEntity)));
}
-
+
public void testExistsAlias() {
GetAliasesRequest getAliasesRequest = new GetAliasesRequest();
String[] indices = randomBoolean() ? null : randomIndicesNames(0, 5);
@@ -2111,6 +2113,29 @@ public void testGetAllSnapshots() {
assertNull(request.getEntity());
}
+ public void testSnapshotsStatus() {
+ Map 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 expectedParams = new HashMap<>();
String repository = randomIndicesNames(1, 1)[0];
diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/SnapshotIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/SnapshotIT.java
index 7ec2ee80f04ac..45f9b5bbb0b0a 100644
--- a/client/rest-high-level/src/test/java/org/elasticsearch/client/SnapshotIT.java
+++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/SnapshotIT.java
@@ -28,6 +28,9 @@
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 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 void testGetSnapshots() throws IOException {
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";
diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SnapshotClientDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SnapshotClientDocumentationIT.java
index 2d126fb970cbf..b1a292fa15747 100644
--- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SnapshotClientDocumentationIT.java
+++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SnapshotClientDocumentationIT.java
@@ -37,11 +37,16 @@
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;
@@ -81,8 +86,8 @@
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();
@@ -463,6 +468,7 @@ public void testSnapshotGetSnapshots() throws IOException {
RestHighLevelClient client = highLevelClient();
createTestRepositories();
+ createTestIndex();
createTestSnapshots();
// tag::get-snapshots-request
@@ -533,10 +539,84 @@ public void onFailure(Exception e) {
}
}
+ 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 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 listener =
+ new ActionListener() {
+ @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
@@ -598,9 +678,14 @@ private void createTestRepositories() throws IOException {
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());
diff --git a/docs/java-rest/high-level/snapshot/snapshots_status.asciidoc b/docs/java-rest/high-level/snapshot/snapshots_status.asciidoc
new file mode 100644
index 0000000000000..8f91d774f4e19
--- /dev/null
+++ b/docs/java-rest/high-level/snapshot/snapshots_status.asciidoc
@@ -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
diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc
index d2484db1d7860..fb40322d4f34a 100644
--- a/docs/java-rest/high-level/supported-apis.asciidoc
+++ b/docs/java-rest/high-level/supported-apis.asciidoc
@@ -146,6 +146,7 @@ The Java High Level REST Client supports the following Snapshot APIs:
* <>
* <>
* <>
+* <>
* <>
include::snapshot/get_repository.asciidoc[]
@@ -154,6 +155,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
diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotIndexShardStatus.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotIndexShardStatus.java
index 39abd8613caa4..834e238e4a0d3 100644
--- a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotIndexShardStatus.java
+++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotIndexShardStatus.java
@@ -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 @@ private SnapshotIndexShardStatus() {
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 @@ static final class Fields {
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 XContentBuilder toXContent(XContentBuilder builder, Params params) throws
builder.endObject();
return builder;
}
+
+ static final ObjectParser.NamedObjectParser PARSER;
+ static {
+ ConstructingObjectParser 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;
+ }
}
diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotIndexStatus.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotIndexStatus.java
index 1605e41dc61be..ba85849598060 100644
--- a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotIndexStatus.java
+++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotIndexStatus.java
@@ -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,
this.indexShards = unmodifiableMap(indexShards);
}
+ public SnapshotIndexStatus(String index, Map 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 @@ static final class Fields {
@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 XContentBuilder toXContent(XContentBuilder builder, Params params) throws
builder.endObject();
return builder;
}
+
+ static final ObjectParser.NamedObjectParser PARSER;
+ static {
+ ConstructingObjectParser 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 shardStatuses =
+ (List) parsedObjects[i];
+
+ final Map 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;
+ }
}
diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotShardsStats.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotShardsStats.java
index c74dd5af1eec9..c0ac432292ddc 100644
--- a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotShardsStats.java
+++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotShardsStats.java
@@ -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 @@ static final class Fields {
@Override
public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException {
- builder.startObject(Fields.SHARDS_STATS);
- 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.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 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;
+ }
}
diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStats.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStats.java
index 76f6b2191840d..6cb56bd88dcd9 100644
--- a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStats.java
+++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStats.java
@@ -26,12 +26,14 @@
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 @@ static final class Fields {
@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();
-
- 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();
+ {
+ 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()) {
+ builder.startObject(Fields.PROCESSED);
+ {
+ builder.field(Fields.FILE_COUNT, getProcessedFileCount());
+ builder.humanReadableField(Fields.SIZE_IN_BYTES, Fields.SIZE, new ByteSizeValue(getProcessedSize()));
+ }
+ builder.endObject();
+ }
+
+ 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()));
}
- // 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()));
-
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 @@ void add(SnapshotStats stats) {
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;
+ }
}
diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStatus.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStatus.java
index f7545ea0236a7..618bb54c9015d 100644
--- a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStatus.java
+++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStatus.java
@@ -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.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 shards,
+ Map 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 XContentBuilder toXContent(XContentBuilder builder, Params params) throws
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 XContentBuilder toXContent(XContentBuilder builder, Params params) throws
return builder;
}
+ static final ConstructingObjectParser 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 indices = ((List) parsedObjects[i]);
+
+ Snapshot snapshot = new Snapshot(repository, new SnapshotId(name, uuid));
+ SnapshotsInProgress.State state = SnapshotsInProgress.State.valueOf(rawState);
+ Map indicesStatus;
+ List 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 @@ private void updateShardStats() {
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;
+ }
}
diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotsStatusResponse.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotsStatusResponse.java
index d44a490680c9b..ef1435e41080c 100644
--- a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotsStatusResponse.java
+++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotsStatusResponse.java
@@ -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 XContentBuilder toXContent(XContentBuilder builder, Params params) throws
return builder;
}
+ private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>(
+ "snapshots_status_response", true,
+ (Object[] parsedObjects) -> {
+ @SuppressWarnings("unchecked") List snapshots = (List) 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;
+ }
}
diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotIndexShardStatusTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotIndexShardStatusTests.java
new file mode 100644
index 0000000000000..490319ef84074
--- /dev/null
+++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotIndexShardStatusTests.java
@@ -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 {
+
+ @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 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;
+ }
+}
diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotIndexStatusTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotIndexStatusTests.java
new file mode 100644
index 0000000000000..92eb355f3a621
--- /dev/null
+++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotIndexStatusTests.java
@@ -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 {
+
+ @Override
+ protected SnapshotIndexStatus createTestInstance() {
+ String index = randomAlphaOfLength(10);
+ List 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 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;
+ }
+}
diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotShardsStatsTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotShardsStatsTests.java
new file mode 100644
index 0000000000000..ac00896983d14
--- /dev/null
+++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotShardsStatsTests.java
@@ -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 {
+
+ @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;
+ }
+}
diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStatsTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStatsTests.java
new file mode 100644
index 0000000000000..2822a9661fd15
--- /dev/null
+++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStatsTests.java
@@ -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 {
+
+ @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;
+ }
+}
diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStatusTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStatusTests.java
index 3ece0f9f1072f..dbd45640c7b69 100644
--- a/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStatusTests.java
+++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStatusTests.java
@@ -21,16 +21,19 @@
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 {
public void testToString() throws Exception {
@@ -146,4 +149,39 @@ public void testToString() throws Exception {
"}";
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 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 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;
+ }
}
diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotsStatusResponseTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotsStatusResponseTests.java
new file mode 100644
index 0000000000000..d1ad028296ddb
--- /dev/null
+++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotsStatusResponseTests.java
@@ -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 {
+
+ @Override
+ protected SnapshotsStatusResponse doParseInstance(XContentParser parser) throws IOException {
+ return SnapshotsStatusResponse.fromXContent(parser);
+ }
+
+ @Override
+ protected Predicate 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 snapshotStatuses = new ArrayList<>();
+ for (int idx = 0; idx < randomIntBetween(0, 5); idx++) {
+ snapshotStatuses.add(statusBuilder.createTestInstance());
+ }
+ return new SnapshotsStatusResponse(snapshotStatuses);
+ }
+}