You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by na...@apache.org on 2022/08/18 14:42:19 UTC

[ignite] branch master updated: IGNITE-13510 Added snapshot status command to control.sh and JMX. (#10202)

This is an automated email from the ASF dual-hosted git repository.

namelchev pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new 2361ff3d818 IGNITE-13510 Added snapshot status command to control.sh and JMX. (#10202)
2361ff3d818 is described below

commit 2361ff3d8189ead8f7274ad4bf318df993b5404f
Author: Nikita Amelchev <ns...@gmail.com>
AuthorDate: Thu Aug 18 17:42:09 2022 +0300

    IGNITE-13510 Added snapshot status command to control.sh and JMX. (#10202)
---
 docs/_docs/snapshots/snapshots.adoc                |  36 +++-
 .../snapshot/SnapshotRestoreCommand.java           |   7 +-
 .../snapshot/SnapshotStatusCommand.java            | 113 +++++++++++
 .../commandline/snapshot/SnapshotSubcommands.java  |   5 +-
 .../util/GridCommandHandlerAbstractTest.java       |   9 +-
 .../apache/ignite/util/GridCommandHandlerTest.java |  86 +++++++-
 .../snapshot/IgniteSnapshotManager.java            |   5 +
 .../persistence/snapshot/SnapshotMXBeanImpl.java   |  29 +++
 .../snapshot/SnapshotOperationRequest.java         |  10 +
 .../visor/snapshot/VisorSnapshotRestoreTask.java   |   5 +-
 .../snapshot/VisorSnapshotRestoreTaskAction.java   |   7 +-
 .../visor/snapshot/VisorSnapshotStatusTask.java    | 219 +++++++++++++++++++++
 .../org/apache/ignite/mxbean/SnapshotMXBean.java   |   8 +
 .../IgniteClusterSnapshotRestoreSelfTest.java      |   8 +-
 .../snapshot/IgniteSnapshotMXBeanTest.java         |  91 ++++++++-
 ...ridCommandHandlerClusterByClassTest_help.output |   5 +-
 ...andHandlerClusterByClassWithSSLTest_help.output |   5 +-
 17 files changed, 625 insertions(+), 23 deletions(-)

diff --git a/docs/_docs/snapshots/snapshots.adoc b/docs/_docs/snapshots/snapshots.adoc
index b3f03cdc766..ed26326f7ef 100644
--- a/docs/_docs/snapshots/snapshots.adoc
+++ b/docs/_docs/snapshots/snapshots.adoc
@@ -218,7 +218,7 @@ control.(sh|bat) --snapshot restore snapshot_02092020 --start --groups snapshot-
 --
 
 ==== Using CLI to control restore operation
-The `control.sh|bat` script provides the ability to start, stop, and get the status of the restore operation.
+The `control.sh|bat` script provides the ability to start and stop the restore operation.
 
 [source,shell]
 ----
@@ -234,13 +234,41 @@ control.(sh|bat) --snapshot restore snapshot_09062021 --src /tmp/ignite/snapshot
 # Start restoring only "cache-group1" and "cache-group2" from the snapshot "snapshot_09062021" in the background.
 control.(sh|bat) --snapshot restore snapshot_09062021 --start --groups cache-group1,cache-group2
 
-# Get the status of the restore operation for "snapshot_09062021".
-control.(sh|bat) --snapshot restore snapshot_09062021 --status
-
 # Cancel the restore operation for "snapshot_09062021".
 control.(sh|bat) --snapshot restore snapshot_09062021 --cancel
 ----
 
+== Getting Snapshot Operation Status
+
+The status of the current snapshot operation in the cluster can be obtained using the `control.sh|bat` script or JMX interface:
+
+[tabs]
+--
+tab:Unix[]
+[source,shell]
+----
+# Get the status of the snapshot operation.
+control.sh --snapshot status
+----
+
+tab:Windows[]
+[source,shell]
+----
+# Get the status of the snapshot operation.
+control.bat --snapshot status
+----
+
+tab:JMX[]
+You can also get the current snapshot status via the `SnapshotMXBean` interface:
+[source,java]
+----
+SnapshotMXBean mxBean = ...;
+
+// The status of a current snapshot operation in the cluster.
+String status = mxBean.status();
+----
+--
+
 == Consistency Guarantees
 
 All snapshots are fully consistent in terms of concurrent cluster-wide operations as well as ongoing changes with Ignite.
diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/snapshot/SnapshotRestoreCommand.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/snapshot/SnapshotRestoreCommand.java
index 0fa21259a54..ae90116f561 100644
--- a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/snapshot/SnapshotRestoreCommand.java
+++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/snapshot/SnapshotRestoreCommand.java
@@ -37,6 +37,7 @@ import static org.apache.ignite.internal.commandline.snapshot.SnapshotRestoreCom
 import static org.apache.ignite.internal.commandline.snapshot.SnapshotRestoreCommandOption.SYNC;
 import static org.apache.ignite.internal.commandline.snapshot.SnapshotSubcommands.RESTORE;
 import static org.apache.ignite.internal.visor.snapshot.VisorSnapshotRestoreTaskAction.START;
+import static org.apache.ignite.internal.visor.snapshot.VisorSnapshotRestoreTaskAction.STATUS;
 
 /**
  * Sub-command to restore snapshot.
@@ -49,6 +50,9 @@ public class SnapshotRestoreCommand extends SnapshotSubcommand {
 
     /** {@inheritDoc} */
     @Override public Object execute(GridClientConfiguration clientCfg, Logger log) throws Exception {
+        if (cmdArg instanceof VisorSnapshotRestoreTaskArg && ((VisorSnapshotRestoreTaskArg)cmdArg).jobAction() == STATUS)
+            log.warning("Command deprecated. Use '" + SNAPSHOT + ' ' + SnapshotSubcommands.STATUS + "' instead.");
+
         Object res = super.execute(clientCfg, log);
 
         log.info(String.valueOf(res));
@@ -122,7 +126,8 @@ public class SnapshotRestoreCommand extends SnapshotSubcommand {
 
         usage(log, "Restore snapshot:", SNAPSHOT, startParams, RESTORE.toString(), SNAPSHOT_NAME_ARG, "--start",
             optional(GROUPS.argName(), GROUPS.arg()), optional(SOURCE.argName(), SOURCE.arg()), optional(SYNC.argName()));
-        usage(log, "Snapshot restore operation status:", SNAPSHOT, params, RESTORE.toString(), SNAPSHOT_NAME_ARG, "--status");
+        usage(log, "Snapshot restore operation status (Command deprecated. Use '" + SNAPSHOT + ' '
+            + SnapshotSubcommands.STATUS + "' instead.):", SNAPSHOT, params, RESTORE.toString(), SNAPSHOT_NAME_ARG, "--status");
         usage(log, "Cancel snapshot restore operation:", SNAPSHOT, params, RESTORE.toString(), SNAPSHOT_NAME_ARG, "--cancel");
     }
 
diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/snapshot/SnapshotStatusCommand.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/snapshot/SnapshotStatusCommand.java
new file mode 100644
index 00000000000..c0f2f2157e4
--- /dev/null
+++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/snapshot/SnapshotStatusCommand.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.ignite.internal.commandline.snapshot;
+
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+import org.apache.ignite.internal.commandline.CommandArgIterator;
+import org.apache.ignite.internal.commandline.systemview.SystemViewCommand;
+import org.apache.ignite.internal.util.GridStringBuilder;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.internal.visor.snapshot.VisorSnapshotStatusTask;
+import org.apache.ignite.internal.visor.snapshot.VisorSnapshotStatusTask.SnapshotStatus;
+
+import static org.apache.ignite.internal.commandline.CommandList.SNAPSHOT;
+import static org.apache.ignite.internal.commandline.snapshot.SnapshotSubcommands.STATUS;
+import static org.apache.ignite.internal.visor.systemview.VisorSystemViewTask.SimpleType.NUMBER;
+import static org.apache.ignite.internal.visor.systemview.VisorSystemViewTask.SimpleType.STRING;
+
+/**
+ * Command to get the status of the current snapshot operation in the cluster.
+ */
+public class SnapshotStatusCommand extends SnapshotSubcommand {
+    /** */
+    protected SnapshotStatusCommand() {
+        super("status", VisorSnapshotStatusTask.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void printResult(Object res, Logger log) {
+        if (res == null) {
+            log.info("There is no create or restore snapshot operation in progress.");
+
+            return;
+        }
+
+        SnapshotStatus status = (SnapshotStatus)res;
+
+        boolean isCreating = status.operation() == VisorSnapshotStatusTask.SnapshotOperation.CREATE;
+
+        GridStringBuilder s = new GridStringBuilder();
+
+        if (isCreating)
+            s.a("Create snapshot operation is in progress.").nl();
+        else
+            s.a("Restore snapshot operation is in progress.").nl();
+
+        s.a("Snapshot name: ").a(status.name()).nl();
+        s.a("Operation ID: ").a(status.requestId()).nl();
+        s.a("Started at: ").a(DateFormat.getDateTimeInstance().format(new Date(status.startTime()))).nl();
+        s.a("Duration: ").a(X.timeSpan2DHMSM(System.currentTimeMillis() - status.startTime())).nl()
+                .nl();
+        s.a("Estimated operation progress:").nl();
+
+        log.info(s.toString());
+
+        List<String> titles = isCreating ? F.asList("Node ID", "Processed, bytes", "Total, bytes", "Percent") :
+                F.asList("Node ID", "Processed, partitions", "Total, partitions", "Percent");
+
+        List<List<?>> rows = status.progress().entrySet().stream().sorted(Map.Entry.comparingByKey()).map(e -> {
+            UUID nodeId = e.getKey();
+            long processed = e.getValue().get1();
+            long total = e.getValue().get2();
+
+            if (total <= 0)
+                return F.asList(nodeId, "unknown", "unknown", "unknown");
+
+            String percent = (int)(processed * 100 / total) + "%";
+
+            if (isCreating)
+                return F.asList(nodeId, U.humanReadableByteCount(processed), U.humanReadableByteCount(total), percent);
+            else
+                return F.asList(nodeId, processed, total, percent);
+        }).collect(Collectors.toList());
+
+        SystemViewCommand.printTable(titles, F.asList(STRING, NUMBER, NUMBER, NUMBER),
+                rows, log);
+
+        log.info(U.nl());
+    }
+
+    /** {@inheritDoc} */
+    @Override public void parseArguments(CommandArgIterator argIter) {
+        if (argIter.hasNextSubArg())
+            throw new IllegalArgumentException("Unexpected argument: " + argIter.peekNextArg() + '.');
+    }
+
+    /** {@inheritDoc} */
+    @Override public void printUsage(Logger log) {
+        usage(log, "Get the status of the current snapshot operation:", SNAPSHOT, STATUS.toString());
+    }
+}
diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/snapshot/SnapshotSubcommands.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/snapshot/SnapshotSubcommands.java
index c26b5ade4b4..abe16c9d69f 100644
--- a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/snapshot/SnapshotSubcommands.java
+++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/snapshot/SnapshotSubcommands.java
@@ -36,7 +36,10 @@ public enum SnapshotSubcommands {
     CHECK(new SnapshotCheckCommand()),
 
     /** Sub-command to restore snapshot. */
-    RESTORE(new SnapshotRestoreCommand());
+    RESTORE(new SnapshotRestoreCommand()),
+
+    /** Sub-command to get the status of the current snapshot operation. */
+    STATUS(new SnapshotStatusCommand());
 
     /** Sub-command. */
     private final SnapshotSubcommand cmd;
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerAbstractTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerAbstractTest.java
index 67552e27b0b..340fe958b36 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerAbstractTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerAbstractTest.java
@@ -33,7 +33,7 @@ import java.util.logging.Handler;
 import java.util.logging.Logger;
 import java.util.stream.Collectors;
 import org.apache.ignite.Ignite;
-import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteDataStreamer;
 import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.configuration.AtomicConfiguration;
@@ -455,9 +455,10 @@ public abstract class GridCommandHandlerAbstractTest extends GridCommonAbstractT
 
         ignite.createCache(ccfg);
 
-        IgniteCache<Object, Object> cache = ignite.cache(cacheName);
-        for (int i = 0; i < countEntries; i++)
-            cache.put(i, i);
+        try (IgniteDataStreamer<Object, Object> streamer = ignite.dataStreamer(cacheName)) {
+            for (int i = 0; i < countEntries; i++)
+                streamer.addData(i, i);
+        }
     }
 
     /**
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTest.java
index 1669e2251e6..90ce6cccf2e 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTest.java
@@ -106,6 +106,7 @@ import org.apache.ignite.internal.processors.cache.warmup.WarmUpTestPluginProvid
 import org.apache.ignite.internal.processors.cluster.ChangeGlobalStateFinishMessage;
 import org.apache.ignite.internal.processors.cluster.GridClusterStateProcessor;
 import org.apache.ignite.internal.util.BasicRateLimiter;
+import org.apache.ignite.internal.util.distributed.SingleNodeMessage;
 import org.apache.ignite.internal.util.future.IgniteFinishedFutureImpl;
 import org.apache.ignite.internal.util.lang.GridAbsPredicate;
 import org.apache.ignite.internal.util.lang.GridFunc;
@@ -3474,9 +3475,92 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
         assertNull(ig.cache(DEFAULT_CACHE_NAME));
     }
 
+    /** @throws Exception If fails. */
+    @Test
+    public void testSnapshotStatus() throws Exception {
+        String snapshotName = "snapshot1";
+        int keysCnt = 10_000;
+
+        IgniteEx srv = startGrids(3);
+
+        srv.cluster().state(ACTIVE);
+
+        createCacheAndPreload(srv, keysCnt);
+
+        checkSnapshotStatus(false, false, null);
+
+        TestRecordingCommunicationSpi spi = TestRecordingCommunicationSpi.spi(grid(1));
+
+        spi.blockMessages((node, msg) -> msg instanceof SingleNodeMessage);
+
+        IgniteFuture<Void> fut = srv.snapshot().createSnapshot(snapshotName);
+
+        spi.waitForBlocked();
+
+        checkSnapshotStatus(true, false, snapshotName);
+
+        spi.stopBlock();
+
+        fut.get(getTestTimeout());
+
+        checkSnapshotStatus(false, false, null);
+
+        srv.destroyCache(DEFAULT_CACHE_NAME);
+
+        spi.blockMessages((node, msg) -> msg instanceof SingleNodeMessage);
+
+        fut = srv.snapshot().restoreSnapshot(snapshotName, F.asList(DEFAULT_CACHE_NAME));
+
+        spi.waitForBlocked();
+
+        checkSnapshotStatus(false, true, snapshotName);
+
+        spi.stopBlock();
+
+        fut.get(getTestTimeout());
+
+        checkSnapshotStatus(false, false, null);
+    }
+
     /**
-     * @throws Exception If failed.
+     * @param isCreating {@code True} if create snapshot operation is in progress.
+     * @param isRestoring {@code True} if restore snapshot operation is in progress.
+     * @param expName Expected snapshot name.
      */
+    private void checkSnapshotStatus(boolean isCreating, boolean isRestoring, String expName) throws Exception {
+        assertTrue(waitForCondition(() -> G.allGrids().stream().allMatch(
+                ignite -> {
+                    IgniteSnapshotManager mgr = ((IgniteEx)ignite).context().cache().context().snapshotMgr();
+
+                    return isCreating == mgr.isSnapshotCreating() && isRestoring == mgr.isRestoring();
+                }),
+            getTestTimeout()));
+
+        injectTestSystemOut();
+
+        int status = execute("--snapshot", "status");
+
+        String out = testOut.toString();
+
+        assertEquals(out, EXIT_CODE_OK, status);
+
+        if (!isCreating && !isRestoring) {
+            assertContains(log, out, "There is no create or restore snapshot operation in progress.");
+
+            return;
+        }
+
+        if (isCreating)
+            assertContains(log, out, "Create snapshot operation is in progress.");
+        else
+            assertContains(log, out, "Restore snapshot operation is in progress.");
+
+        assertContains(log, out, "Snapshot name: " + expName);
+
+        G.allGrids().forEach(srv -> assertContains(log, out, srv.cluster().localNode().id().toString()));
+    }
+
+    /** @throws Exception If failed. */
     @Test
     @WithSystemProperty(key = IGNITE_PDS_SKIP_CHECKPOINT_ON_NODE_STOP, value = "true")
     public void testCleaningGarbageAfterCacheDestroyedAndNodeStop_ControlConsoleUtil() throws Exception {
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotManager.java
index 2d1981aa70f..322293573db 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotManager.java
@@ -1037,6 +1037,11 @@ public class IgniteSnapshotManager extends GridCacheSharedManagerAdapter
         }
     }
 
+    /** @return Current create snapshot request. {@code Null} if there is no create snapshot operation in progress. */
+    @Nullable public SnapshotOperationRequest currentCreateRequest() {
+        return clusterSnpReq;
+    }
+
     /**
      * Check if snapshot restore process is currently running.
      *
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/SnapshotMXBeanImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/SnapshotMXBeanImpl.java
index 33389881a26..ea0a1debd1f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/SnapshotMXBeanImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/SnapshotMXBeanImpl.java
@@ -21,10 +21,14 @@ import java.util.Arrays;
 import java.util.Set;
 import java.util.stream.Collectors;
 import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.processors.metric.GridMetricManager;
+import org.apache.ignite.internal.processors.metric.MetricRegistry;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.lang.IgniteFuture;
 import org.apache.ignite.mxbean.SnapshotMXBean;
 
+import static org.apache.ignite.internal.processors.cache.persistence.snapshot.SnapshotRestoreProcess.SNAPSHOT_RESTORE_METRICS;
+
 /**
  * Snapshot MBean features.
  */
@@ -32,11 +36,15 @@ public class SnapshotMXBeanImpl implements SnapshotMXBean {
     /** Instance of snapshot cache shared manager. */
     private final IgniteSnapshotManager mgr;
 
+    /** Instance of metric manager. */
+    private final GridMetricManager metricMgr;
+
     /**
      * @param ctx Kernal context.
      */
     public SnapshotMXBeanImpl(GridKernalContext ctx) {
         mgr = ctx.cache().context().snapshotMgr();
+        metricMgr = ctx.metric();
     }
 
     /** {@inheritDoc} */
@@ -67,4 +75,25 @@ public class SnapshotMXBeanImpl implements SnapshotMXBean {
     @Override public void cancelSnapshotRestore(String name) {
         mgr.cancelSnapshotRestore(name).get();
     }
+
+    /** {@inheritDoc} */
+    @Override public String status() {
+        SnapshotOperationRequest req = mgr.currentCreateRequest();
+
+        if (req != null) {
+            return "Create snapshot operation is in progress [name=" + req.snapshotName() +
+                ", id=" + req.requestId() + ']';
+        }
+
+        if (mgr.isRestoring()) {
+            MetricRegistry mreg = metricMgr.registry(SNAPSHOT_RESTORE_METRICS);
+
+            String name = mreg.findMetric("snapshotName").getAsString();
+            String id = mreg.findMetric("requestId").getAsString();
+
+            return "Restore snapshot operation is in progress [name=" + name + ", id=" + id + ']';
+        }
+
+        return "There is no create or restore snapshot operation in progress.";
+    }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/SnapshotOperationRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/SnapshotOperationRequest.java
index 6c20c6d2910..14c72f661ea 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/SnapshotOperationRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/SnapshotOperationRequest.java
@@ -25,6 +25,7 @@ import org.apache.ignite.internal.util.distributed.DistributedProcess;
 import org.apache.ignite.internal.util.distributed.DistributedProcess.DistributedProcessType;
 import org.apache.ignite.internal.util.tostring.GridToStringInclude;
 import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.U;
 import org.jetbrains.annotations.Nullable;
 
 /**
@@ -60,6 +61,9 @@ public class SnapshotOperationRequest implements Serializable {
     /** Flag indicating that the {@link DistributedProcessType#START_SNAPSHOT} phase has completed. */
     private transient volatile boolean startStageEnded;
 
+    /** Operation start time. */
+    private final long startTime;
+
     /**
      * @param reqId Request ID.
      * @param opNodeId Operational node ID.
@@ -82,6 +86,7 @@ public class SnapshotOperationRequest implements Serializable {
         this.grps = grps;
         this.nodes = nodes;
         this.snpPath = snpPath;
+        startTime = U.currentTimeMillis();
     }
 
     /**
@@ -140,6 +145,11 @@ public class SnapshotOperationRequest implements Serializable {
         this.err = err;
     }
 
+    /** @return Start time. */
+    public long startTime() {
+        return startTime;
+    }
+
     /**
      * @return Flag indicating that the {@link DistributedProcessType#START_SNAPSHOT} phase has completed.
      */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/snapshot/VisorSnapshotRestoreTask.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/snapshot/VisorSnapshotRestoreTask.java
index 49e5e62d166..1892af84bb3 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/visor/snapshot/VisorSnapshotRestoreTask.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/snapshot/VisorSnapshotRestoreTask.java
@@ -98,7 +98,10 @@ public class VisorSnapshotRestoreTask extends VisorSnapshotOneNodeTask<VisorSnap
         }
     }
 
-    /** */
+    /**
+     * @deprecated Use {@link VisorSnapshotStatusTask} instead.
+     */
+    @Deprecated
     private static class VisorSnapshotRestoreStatusJob extends VisorJob<VisorSnapshotRestoreTaskArg, String> {
         /** Serial version uid. */
         private static final long serialVersionUID = 0L;
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/snapshot/VisorSnapshotRestoreTaskAction.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/snapshot/VisorSnapshotRestoreTaskAction.java
index ad101556045..04c2c740b76 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/visor/snapshot/VisorSnapshotRestoreTaskAction.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/snapshot/VisorSnapshotRestoreTaskAction.java
@@ -25,6 +25,11 @@ public enum VisorSnapshotRestoreTaskAction {
     /** Cancel snapshot restore operation. */
     CANCEL,
 
-    /** Status of the snapshot restore operation. */
+    /**
+     * Status of the snapshot restore operation.
+     *
+     * @deprecated Use {@link VisorSnapshotStatusTask} instead.
+     */
+    @Deprecated
     STATUS;
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/snapshot/VisorSnapshotStatusTask.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/snapshot/VisorSnapshotStatusTask.java
new file mode 100644
index 00000000000..0cb165c0299
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/snapshot/VisorSnapshotStatusTask.java
@@ -0,0 +1,219 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.ignite.internal.visor.snapshot;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.compute.ComputeJobResult;
+import org.apache.ignite.internal.processors.cache.persistence.snapshot.IgniteSnapshotManager;
+import org.apache.ignite.internal.processors.cache.persistence.snapshot.SnapshotOperationRequest;
+import org.apache.ignite.internal.processors.metric.MetricRegistry;
+import org.apache.ignite.internal.processors.task.GridInternal;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.T2;
+import org.apache.ignite.internal.visor.VisorJob;
+import org.apache.ignite.internal.visor.VisorMultiNodeTask;
+import org.apache.ignite.internal.visor.VisorTaskArgument;
+import org.apache.ignite.spi.metric.IntMetric;
+import org.apache.ignite.spi.metric.LongMetric;
+import org.jetbrains.annotations.Nullable;
+
+import static org.apache.ignite.internal.processors.cache.persistence.snapshot.IgniteSnapshotManager.SNAPSHOT_METRICS;
+import static org.apache.ignite.internal.processors.cache.persistence.snapshot.SnapshotRestoreProcess.SNAPSHOT_RESTORE_METRICS;
+import static org.apache.ignite.internal.visor.snapshot.VisorSnapshotStatusTask.SnapshotStatus;
+
+/**
+ * Task to get the status of the current snapshot operation in the cluster.
+ */
+@GridInternal
+public class VisorSnapshotStatusTask extends VisorMultiNodeTask<Void, VisorSnapshotTaskResult, SnapshotStatus> {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** {@inheritDoc} */
+    @Override protected VisorJob<Void, SnapshotStatus> job(Void arg) {
+        return new VisorSnapshotStatusJob(arg, debug);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected Collection<UUID> jobNodes(VisorTaskArgument<Void> arg) {
+        return F.nodeIds(ignite.cluster().nodes());
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override protected VisorSnapshotTaskResult reduce0(List<ComputeJobResult> results) {
+        if (results.isEmpty())
+            return new VisorSnapshotTaskResult(null, new IgniteException("Failed to get the snapshot status. Topology is empty."));
+
+        IgniteException error = F.find(F.viewReadOnly(results, ComputeJobResult::getException,
+            r -> r.getException() != null), null, F.notNull());
+
+        if (error != null)
+            return new VisorSnapshotTaskResult(null, new IgniteException("Failed to get the snapshot status.", error));
+
+        Collection<SnapshotStatus> res = F.viewReadOnly(results, ComputeJobResult::getData, r -> r.getData() != null);
+
+        // There is no snapshot operation.
+        if (res.isEmpty())
+            return new VisorSnapshotTaskResult(null, null);
+
+        SnapshotStatus s0 = F.first(res);
+
+        // Filter out differing requests due to concurrent updates on nodes.
+        res = F.view(res, s -> s.requestId.equals(s0.requestId));
+
+        // Merge nodes progress.
+        Map<UUID, T2<Long, Long>> progress = new HashMap<>();
+
+        res.forEach(s -> progress.putAll(s.progress));
+
+        return new VisorSnapshotTaskResult(new SnapshotStatus(s0.op, s0.name, s0.requestId, s0.startTime, progress), null);
+    }
+
+    /** */
+    private static class VisorSnapshotStatusJob extends VisorJob<Void, SnapshotStatus> {
+        /** */
+        private static final long serialVersionUID = 0L;
+
+        /**
+         * @param arg Job argument.
+         * @param debug Flag indicating whether debug information should be printed into node log.
+         */
+        protected VisorSnapshotStatusJob(@Nullable Void arg, boolean debug) {
+            super(arg, debug);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected SnapshotStatus run(@Nullable Void arg) throws IgniteException {
+            IgniteSnapshotManager snpMgr = ignite.context().cache().context().snapshotMgr();
+
+            SnapshotOperationRequest req = snpMgr.currentCreateRequest();
+
+            if (req != null) {
+                MetricRegistry mreg = ignite.context().metric().registry(SNAPSHOT_METRICS);
+
+                return new SnapshotStatus(
+                    SnapshotOperation.CREATE,
+                    req.snapshotName(),
+                    req.requestId().toString(),
+                    req.startTime(),
+                    F.asMap(
+                        ignite.localNode().id(),
+                        new T2<>(
+                            mreg.<LongMetric>findMetric("CurrentSnapshotProcessedSize").value(),
+                            mreg.<LongMetric>findMetric("CurrentSnapshotTotalSize").value()
+                        )
+                    )
+                );
+            }
+
+            MetricRegistry mreg = ignite.context().metric().registry(SNAPSHOT_RESTORE_METRICS);
+
+            long startTime = mreg.<LongMetric>findMetric("startTime").value();
+
+            if (startTime > mreg.<LongMetric>findMetric("endTime").value()) {
+                return new SnapshotStatus(
+                    SnapshotOperation.RESTORE,
+                    mreg.findMetric("snapshotName").getAsString(),
+                    mreg.findMetric("requestId").getAsString(),
+                    mreg.<LongMetric>findMetric("startTime").value(),
+                    F.asMap(
+                        ignite.localNode().id(),
+                        new T2<>(
+                            (long)mreg.<IntMetric>findMetric("processedPartitions").value(),
+                            (long)mreg.<IntMetric>findMetric("totalPartitions").value()
+                        )
+                    )
+                );
+            }
+
+            return null;
+        }
+    }
+
+    /** Snapshot operation status. */
+    public static class SnapshotStatus implements Serializable {
+        /** */
+        private static final long serialVersionUID = 0L;
+
+        /** Operation type. */
+        private final SnapshotOperation op;
+
+        /** Snapshot name. */
+        private final String name;
+
+        /** Request ID. */
+        private final String requestId;
+
+        /** Start time. */
+        private final long startTime;
+
+        /** Progress of operation on nodes. */
+        private final Map<UUID, T2<Long, Long>> progress;
+
+        /** */
+        public SnapshotStatus(SnapshotOperation op, String name, String requestId, long startTime,
+            Map<UUID, T2<Long, Long>> progress) {
+            this.op = op;
+            this.name = name;
+            this.requestId = requestId;
+            this.startTime = startTime;
+            this.progress = Collections.unmodifiableMap(progress);
+        }
+
+        /** @return Operation type. */
+        public SnapshotOperation operation() {
+            return op;
+        }
+
+        /** @return Snapshot name. */
+        public String name() {
+            return name;
+        }
+
+        /** @return Request ID. */
+        public String requestId() {
+            return requestId;
+        }
+
+        /** @return Start time. */
+        public long startTime() {
+            return startTime;
+        }
+
+        /** @return Progress of operation on nodes. */
+        public Map<UUID, T2<Long, Long>> progress() {
+            return progress;
+        }
+    }
+
+    /** Snapshot operation type. */
+    public enum SnapshotOperation {
+        /** Create snapshot. */
+        CREATE,
+
+        /** Restore snapshot. */
+        RESTORE
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/mxbean/SnapshotMXBean.java b/modules/core/src/main/java/org/apache/ignite/mxbean/SnapshotMXBean.java
index b18ac51dc1b..e6cdef4a26e 100644
--- a/modules/core/src/main/java/org/apache/ignite/mxbean/SnapshotMXBean.java
+++ b/modules/core/src/main/java/org/apache/ignite/mxbean/SnapshotMXBean.java
@@ -74,4 +74,12 @@ public interface SnapshotMXBean {
      */
     @MXBeanDescription("Cancel previously started snapshot restore operation.")
     public void cancelSnapshotRestore(@MXBeanParameter(name = "snpName", description = "Snapshot name.") String name);
+
+    /**
+     * Get the status of the current snapshot operation in the cluster.
+     *
+     * @return The status of a current snapshot operation in the cluster.
+     */
+    @MXBeanDescription("The status of a current snapshot operation in the cluster.")
+    public String status();
 }
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteClusterSnapshotRestoreSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteClusterSnapshotRestoreSelfTest.java
index 8cb9049fd49..0ec6648f45d 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteClusterSnapshotRestoreSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteClusterSnapshotRestoreSelfTest.java
@@ -31,6 +31,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.IntSupplier;
+import com.google.common.base.Throwables;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.IgniteCheckedException;
@@ -61,6 +62,7 @@ import org.apache.ignite.internal.util.distributed.DistributedProcess.Distribute
 import org.apache.ignite.internal.util.distributed.SingleNodeMessage;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.G;
+import org.apache.ignite.internal.util.typedef.X;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lang.IgniteFuture;
 import org.apache.ignite.spi.IgniteSpiException;
@@ -269,14 +271,16 @@ public class IgniteClusterSnapshotRestoreSelfTest extends IgniteClusterSnapshotR
 
         IgniteInternalFuture<Long> fut = GridTestUtils.runMultiThreadedAsync(() -> {
             try {
-                nodeIdxSupplier.getAsInt();
-
                 grid(nodeIdxSupplier.getAsInt()).snapshot().restoreSnapshot(
                     SNAPSHOT_NAME, Collections.singleton(DEFAULT_CACHE_NAME)).get(TIMEOUT);
 
                 successCnt.incrementAndGet();
             }
             catch (Exception e) {
+                assertTrue("Unexpected exception: " + Throwables.getStackTraceAsString(e),
+                    X.hasCause(e, "The previous snapshot restore operation was not completed.",
+                    IgniteCheckedException.class, IgniteException.class));
+
                 failCnt.incrementAndGet();
             }
         }, 2, "runner");
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotMXBeanTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotMXBeanTest.java
index ef44ce14e41..837038b31c8 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotMXBeanTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotMXBeanTest.java
@@ -22,12 +22,15 @@ import javax.management.AttributeNotFoundException;
 import javax.management.DynamicMBean;
 import javax.management.MBeanException;
 import javax.management.ReflectionException;
+import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.TestRecordingCommunicationSpi;
 import org.apache.ignite.internal.util.distributed.SingleNodeMessage;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.G;
 import org.apache.ignite.lang.IgniteFuture;
 import org.apache.ignite.mxbean.SnapshotMXBean;
 import org.apache.ignite.spi.metric.jmx.JmxMetricExporterSpi;
@@ -37,6 +40,7 @@ import org.junit.Test;
 import static org.apache.ignite.internal.processors.cache.persistence.snapshot.IgniteSnapshotManager.SNAPSHOT_METRICS;
 import static org.apache.ignite.internal.processors.cache.persistence.snapshot.SnapshotRestoreProcess.SNAPSHOT_RESTORE_METRICS;
 import static org.apache.ignite.internal.util.distributed.DistributedProcess.DistributedProcessType.RESTORE_CACHE_GROUP_SNAPSHOT_PREPARE;
+import static org.apache.ignite.testframework.GridTestUtils.assertContains;
 import static org.apache.ignite.testframework.GridTestUtils.assertThrowsAnyCause;
 import static org.apache.ignite.testframework.GridTestUtils.waitForCondition;
 
@@ -44,12 +48,13 @@ import static org.apache.ignite.testframework.GridTestUtils.waitForCondition;
  * Tests {@link SnapshotMXBean}.
  */
 public class IgniteSnapshotMXBeanTest extends AbstractSnapshotSelfTest {
-    /** Metric group name. */
-    private static final String METRIC_GROUP = "Snapshot";
+    /** Snapshot group name. */
+    private static final String SNAPSHOT_GROUP = "Snapshot";
 
     /** {@inheritDoc} */
     @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
         return super.getConfiguration(igniteInstanceName)
+            .setCommunicationSpi(new TestRecordingCommunicationSpi())
             .setMetricExporterSpi(new JmxMetricExporterSpi());
     }
 
@@ -63,7 +68,7 @@ public class IgniteSnapshotMXBeanTest extends AbstractSnapshotSelfTest {
         assertEquals("Snapshot end time must be undefined on first snapshot operation starts.",
             0, (long)getMetric("LastSnapshotEndTime", snpMBean));
 
-        SnapshotMXBean mxBean = getMxBean(ignite.name(), METRIC_GROUP, SnapshotMXBeanImpl.class, SnapshotMXBean.class);
+        SnapshotMXBean mxBean = getMxBean(ignite.name(), SNAPSHOT_GROUP, SnapshotMXBeanImpl.class, SnapshotMXBean.class);
 
         mxBean.createSnapshot(SNAPSHOT_NAME, "");
 
@@ -84,7 +89,7 @@ public class IgniteSnapshotMXBeanTest extends AbstractSnapshotSelfTest {
         IgniteEx startCli = startClientGrid(1);
         IgniteEx killCli = startClientGrid(2);
 
-        SnapshotMXBean mxBean = getMxBean(killCli.name(), METRIC_GROUP, SnapshotMXBeanImpl.class,
+        SnapshotMXBean mxBean = getMxBean(killCli.name(), SNAPSHOT_GROUP, SnapshotMXBeanImpl.class,
             SnapshotMXBean.class);
 
         doSnapshotCancellationTest(startCli,
@@ -104,7 +109,7 @@ public class IgniteSnapshotMXBeanTest extends AbstractSnapshotSelfTest {
         assertEquals(0, (long)getMetric("endTime", mReg0));
         assertEquals(0, (long)getMetric("endTime", mReg1));
 
-        getMxBean(ignite.name(), METRIC_GROUP, SnapshotMXBeanImpl.class, SnapshotMXBean.class)
+        getMxBean(ignite.name(), SNAPSHOT_GROUP, SnapshotMXBeanImpl.class, SnapshotMXBean.class)
             .restoreSnapshot(SNAPSHOT_NAME, "", "");
 
         assertTrue(GridTestUtils.waitForCondition(() -> (long)getMetric("endTime", mReg0) > 0, TIMEOUT));
@@ -117,7 +122,7 @@ public class IgniteSnapshotMXBeanTest extends AbstractSnapshotSelfTest {
     @Test
     public void testCancelRestoreSnapshot() throws Exception {
         IgniteEx ignite = startGridsWithSnapshot(2, CACHE_KEYS_RANGE, false);
-        SnapshotMXBean mxBean = getMxBean(ignite.name(), METRIC_GROUP, SnapshotMXBeanImpl.class, SnapshotMXBean.class);
+        SnapshotMXBean mxBean = getMxBean(ignite.name(), SNAPSHOT_GROUP, SnapshotMXBeanImpl.class, SnapshotMXBean.class);
         DynamicMBean mReg0 = metricRegistry(grid(0).name(), null, SNAPSHOT_RESTORE_METRICS);
         DynamicMBean mReg1 = metricRegistry(grid(1).name(), null, SNAPSHOT_RESTORE_METRICS);
 
@@ -164,6 +169,80 @@ public class IgniteSnapshotMXBeanTest extends AbstractSnapshotSelfTest {
         assertNull(ignite.cache(DEFAULT_CACHE_NAME));
     }
 
+    /** @throws Exception If fails. */
+    @Test
+    public void testStatus() throws Exception {
+        IgniteEx srv = startGridsWithCache(2, dfltCacheCfg, CACHE_KEYS_RANGE);
+
+        checkSnapshotStatus(false, false, null);
+
+        TestRecordingCommunicationSpi spi = TestRecordingCommunicationSpi.spi(grid(1));
+
+        spi.blockMessages((node, msg) -> msg instanceof SingleNodeMessage);
+
+        IgniteFuture<Void> fut = srv.snapshot().createSnapshot(SNAPSHOT_NAME);
+
+        spi.waitForBlocked();
+
+        checkSnapshotStatus(true, false, SNAPSHOT_NAME);
+
+        spi.stopBlock();
+
+        fut.get(getTestTimeout());
+
+        checkSnapshotStatus(false, false, null);
+
+        srv.destroyCache(DEFAULT_CACHE_NAME);
+
+        spi.blockMessages((node, msg) -> msg instanceof SingleNodeMessage);
+
+        fut = srv.snapshot().restoreSnapshot(SNAPSHOT_NAME, F.asList(DEFAULT_CACHE_NAME));
+
+        spi.waitForBlocked();
+
+        checkSnapshotStatus(false, true, SNAPSHOT_NAME);
+
+        spi.stopBlock();
+
+        fut.get(getTestTimeout());
+
+        checkSnapshotStatus(false, false, null);
+    }
+
+    /**
+     * @param isCreating {@code True} if create snapshot operation is in progress.
+     * @param isRestoring {@code True} if restore snapshot operation is in progress.
+     * @param expName Expected snapshot name.
+     */
+    private void checkSnapshotStatus(boolean isCreating, boolean isRestoring, String expName) throws Exception {
+        assertTrue(waitForCondition(() -> G.allGrids().stream().allMatch(
+                ignite -> {
+                    IgniteSnapshotManager mgr = ((IgniteEx)ignite).context().cache().context().snapshotMgr();
+
+                    return isCreating == mgr.isSnapshotCreating() && isRestoring == mgr.isRestoring();
+                }),
+            getTestTimeout()));
+
+        for (Ignite grid : G.allGrids()) {
+            SnapshotMXBean bean = getMxBean(grid.name(), SNAPSHOT_GROUP, SnapshotMXBeanImpl.class, SnapshotMXBean.class);
+
+            String status = bean.status();
+
+            if (!isCreating && !isRestoring) {
+                assertContains(log, status, "There is no create or restore snapshot operation in progress");
+
+                continue;
+            }
+
+            if (isCreating)
+                assertContains(log, status, "Create snapshot operation is in progress");
+            else
+                assertContains(log, status, "Restore snapshot operation is in progress");
+
+            assertContains(log, status, "name=" + expName);
+        }
+    }
+
     /**
      * @param mBean Ignite snapshot restore MBean.
      * @param name Metric name.
diff --git a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output
index 39024a5c748..569974c2e4c 100644
--- a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output
+++ b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output
@@ -169,7 +169,7 @@ This utility can do the following commands:
       --src path                 - Path to the directory where the snapshot files are located. If not specified, the default configured snapshot directory will be used.
       --sync                     - Run the operation synchronously, the command will wait for the entire operation to complete. Otherwise, it will be performed in the background, and the command will immediately return control.
 
-  Snapshot restore operation status:
+  Snapshot restore operation status (Command deprecated. Use '--snapshot status' instead.):
     control.(sh|bat) --snapshot restore snapshot_name --status
 
     Parameters:
@@ -181,6 +181,9 @@ This utility can do the following commands:
     Parameters:
       snapshot_name  - Snapshot name.
 
+  Get the status of the current snapshot operation:
+    control.(sh|bat) --snapshot status
+
   Change cluster tag to new value:
     control.(sh|bat) --change-tag newTagValue [--yes]
 
diff --git a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output
index 39024a5c748..569974c2e4c 100644
--- a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output
+++ b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output
@@ -169,7 +169,7 @@ This utility can do the following commands:
       --src path                 - Path to the directory where the snapshot files are located. If not specified, the default configured snapshot directory will be used.
       --sync                     - Run the operation synchronously, the command will wait for the entire operation to complete. Otherwise, it will be performed in the background, and the command will immediately return control.
 
-  Snapshot restore operation status:
+  Snapshot restore operation status (Command deprecated. Use '--snapshot status' instead.):
     control.(sh|bat) --snapshot restore snapshot_name --status
 
     Parameters:
@@ -181,6 +181,9 @@ This utility can do the following commands:
     Parameters:
       snapshot_name  - Snapshot name.
 
+  Get the status of the current snapshot operation:
+    control.(sh|bat) --snapshot status
+
   Change cluster tag to new value:
     control.(sh|bat) --change-tag newTagValue [--yes]