diff --git a/hadoop-hdds/common/src/main/resources/ozone-default.xml b/hadoop-hdds/common/src/main/resources/ozone-default.xml
index 2cb1f52ede15..78e8148b5bf4 100644
--- a/hadoop-hdds/common/src/main/resources/ozone-default.xml
+++ b/hadoop-hdds/common/src/main/resources/ozone-default.xml
@@ -3936,6 +3936,16 @@
+
+ ozone.om.snapshot.rename.allowed
+ false
+ OZONE, OM
+
+ Allows the Ozone snapshot rename operation if set to true on the OM side.
+ Blocks it otherwise.
+
+
+
ozone.snapshot.deleting.service.timeout
300s
diff --git a/hadoop-hdds/docs/content/feature/Snapshot-Configuration-Properties.md b/hadoop-hdds/docs/content/feature/Snapshot-Configuration-Properties.md
index 998a798d9c09..90c5d0ca6163 100644
--- a/hadoop-hdds/docs/content/feature/Snapshot-Configuration-Properties.md
+++ b/hadoop-hdds/docs/content/feature/Snapshot-Configuration-Properties.md
@@ -36,6 +36,7 @@ These parameters, defined in `ozone-site.xml`, control how Ozone manages snapsho
* `ozone.om.ratis.snapshot.max.total.sst.size`: The maximum total size of SST files to be included in a Ratis snapshot (Default: 10737418240).
* `ozone.om.snapshot.load.native.lib`: Use native RocksDB library for snapshot operations (Default: true). Set to false as a workaround for native library issues.
* `ozone.om.snapshot.checkpoint.dir.creation.poll.timeout`: Timeout for polling the creation of the snapshot checkpoint directory (Default: 20s).
+ * `ozone.om.snapshot.rename.allowed`: Allow snapshot rename operation (Default: false).
* **SnapshotDiff Service**
* `ozone.om.snapshot.diff.db.dir`: Directory for SnapshotDiff job data. Defaults to OM metadata dir. Use a spacious location for large diffs.
diff --git a/hadoop-hdds/docs/content/feature/Snapshot.md b/hadoop-hdds/docs/content/feature/Snapshot.md
index 65667efbf726..3ac1d931d497 100644
--- a/hadoop-hdds/docs/content/feature/Snapshot.md
+++ b/hadoop-hdds/docs/content/feature/Snapshot.md
@@ -135,7 +135,7 @@ Manage snapshots using `ozone sh` or `ozone fs` (Hadoop-compatible) commands:
```shell
ozone sh snapshot rename /vol1/bucket1
```
- Requires bucket owner or admin.
+ Requires `ozone.om.snapshot.rename.allowed=true` on the OM side and bucket owner or admin privileges.
* **Snapshot Info:**
```shell
diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/OMConfigKeys.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/OMConfigKeys.java
index 05083d506304..e24a9036b2d8 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/OMConfigKeys.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/OMConfigKeys.java
@@ -30,6 +30,9 @@ public final class OMConfigKeys {
public static final String OZONE_FILESYSTEM_SNAPSHOT_ENABLED_KEY =
"ozone.filesystem.snapshot.enabled";
public static final boolean OZONE_FILESYSTEM_SNAPSHOT_ENABLED_DEFAULT = true;
+ public static final String OZONE_OM_SNAPSHOT_RENAME_ALLOWED_KEY =
+ "ozone.om.snapshot.rename.allowed";
+ public static final boolean OZONE_OM_SNAPSHOT_RENAME_ALLOWED_DEFAULT = false;
// Location where the OM stores its DB files. In the future we may support
// multiple entries for performance (sharding)..
diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/fs/ozone/TestOzoneFsSnapshot.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/fs/ozone/TestOzoneFsSnapshot.java
index 6a97796af32b..f86005399d0b 100644
--- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/fs/ozone/TestOzoneFsSnapshot.java
+++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/fs/ozone/TestOzoneFsSnapshot.java
@@ -27,13 +27,17 @@
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OFS_URI_SCHEME;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_SNAPSHOT_SST_FILTERING_SERVICE_INTERVAL;
import static org.apache.hadoop.ozone.om.OmSnapshotManager.getSnapshotPath;
+import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.FEATURE_NOT_ENABLED;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
+import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@@ -45,12 +49,16 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
import org.apache.commons.io.FileUtils;
+import org.apache.hadoop.hdds.cli.GenericCli;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.ozone.MiniOzoneCluster;
+import org.apache.hadoop.ozone.client.OzoneClient;
import org.apache.hadoop.ozone.om.OMConfigKeys;
import org.apache.hadoop.ozone.om.OmConfig;
import org.apache.hadoop.ozone.om.OzoneManager;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.om.helpers.SnapshotInfo;
+import org.apache.hadoop.ozone.shell.OzoneShell;
import org.apache.hadoop.util.ToolRunner;
import org.apache.ozone.test.GenericTestUtils;
import org.junit.jupiter.api.AfterAll;
@@ -68,6 +76,9 @@
*/
class TestOzoneFsSnapshot {
+ private static final String SNAPSHOT_RENAME_NOT_ALLOWED_MESSAGE =
+ "Ozone snapshot rename feature is not allowed per Ozone Manager server config";
+
private static MiniOzoneCluster cluster;
private static final String OM_SERVICE_ID = "om-service-test1";
private static OzoneManager ozoneManager;
@@ -95,6 +106,8 @@ static void initClass() throws Exception {
conf.setInt(OZONE_SNAPSHOT_SST_FILTERING_SERVICE_INTERVAL, -1);
conf.setInt(OmConfig.Keys.SERVER_LIST_MAX_SIZE, 20);
conf.setInt(OZONE_FS_LISTING_PAGE_SIZE, 30);
+ // Explicitly disable snapshot rename for the test
+ conf.setBoolean(OMConfigKeys.OZONE_OM_SNAPSHOT_RENAME_ALLOWED_KEY, false);
// Start the cluster
cluster = MiniOzoneCluster.newHABuilder(conf)
@@ -212,6 +225,44 @@ void testCreateSnapshotSuccess(String snapshotName)
assertNotNull(snapshotInfo);
}
+ @Test
+ void testSnapshotRenameBlockedWhenConfigDisallows(@TempDir Path tempDir)
+ throws Exception {
+ assertFalse(cluster.getConf().getBoolean(
+ OMConfigKeys.OZONE_OM_SNAPSHOT_RENAME_ALLOWED_KEY,
+ OMConfigKeys.OZONE_OM_SNAPSHOT_RENAME_ALLOWED_DEFAULT));
+
+ String oldSnapshotName = createSnapshot();
+ String newSnapshotName = "snap-" + counter.incrementAndGet();
+
+ try (OzoneClient ozoneClient = cluster.newClient()) {
+ OMException omException = assertThrows(OMException.class,
+ () -> ozoneClient.getObjectStore().renameSnapshot(
+ VOLUME, BUCKET, oldSnapshotName, newSnapshotName));
+ assertEquals(FEATURE_NOT_ENABLED, omException.getResult());
+ assertEquals(SNAPSHOT_RENAME_NOT_ALLOWED_MESSAGE,
+ omException.getMessage());
+ }
+
+ Path confPath = tempDir.resolve("ozone-site.xml");
+ try (OutputStream outputStream = Files.newOutputStream(confPath)) {
+ cluster.getConf().writeXml(outputStream);
+ }
+
+ try (GenericTestUtils.SystemErrCapturer capture =
+ new GenericTestUtils.SystemErrCapturer()) {
+ OzoneShell ozoneShell = new OzoneShell();
+ int res = ozoneShell.execute(new String[] {
+ "-conf", confPath.toString(), "snapshot", "rename",
+ "o3://" + OM_SERVICE_ID + BUCKET_PATH,
+ oldSnapshotName, newSnapshotName});
+
+ assertEquals(GenericCli.EXECUTION_ERROR_EXIT_CODE, res);
+ assertThat(capture.getOutput()).contains(
+ SNAPSHOT_RENAME_NOT_ALLOWED_MESSAGE);
+ }
+ }
+
private static Stream createSnapshotFailureScenarios() {
String invalidBucketPath = "/invalid/uri";
return Stream.of(
diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOmMetrics.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOmMetrics.java
index 9d3aedd4edcd..bad68bea43b6 100644
--- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOmMetrics.java
+++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOmMetrics.java
@@ -116,6 +116,7 @@ public void setup() throws Exception {
conf.setTimeDuration(OMConfigKeys.OZONE_DIR_DELETING_SERVICE_INTERVAL, 1000, TimeUnit.MILLISECONDS);
// For testing fs operations with legacy buckets.
conf.setBoolean(OMConfigKeys.OZONE_OM_ENABLE_FILESYSTEM_PATHS, true);
+ conf.setBoolean(OMConfigKeys.OZONE_OM_SNAPSHOT_RENAME_ALLOWED_KEY, true);
clusterBuilder = MiniOzoneCluster.newBuilder(conf).setNumDatanodes(5);
startCluster();
}
diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestOzoneManagerSnapshotAcl.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestOzoneManagerSnapshotAcl.java
index ae6cddba7cf7..4f4508ebdc7d 100644
--- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestOzoneManagerSnapshotAcl.java
+++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestOzoneManagerSnapshotAcl.java
@@ -51,6 +51,7 @@
import org.apache.hadoop.ozone.client.VolumeArgs;
import org.apache.hadoop.ozone.client.io.OzoneOutputStream;
import org.apache.hadoop.ozone.om.KeyManagerImpl;
+import org.apache.hadoop.ozone.om.OMConfigKeys;
import org.apache.hadoop.ozone.om.OMStorage;
import org.apache.hadoop.ozone.om.OmSnapshotManager;
import org.apache.hadoop.ozone.om.OzoneManager;
@@ -111,6 +112,7 @@ public static void init() throws Exception {
conf.setBoolean(OZONE_TEST_AUTHORIZATION_ENABLED, true);
conf.setBoolean(OZONE_ACL_ENABLED, true);
conf.set(OZONE_ACL_AUTHORIZER_CLASS, OZONE_ACL_AUTHORIZER_CLASS_NATIVE);
+ conf.setBoolean(OMConfigKeys.OZONE_OM_SNAPSHOT_RENAME_ALLOWED_KEY, true);
final String omServiceId = "om-service-test-1"
+ RandomStringUtils.secure().nextNumeric(32);
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotRenameRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotRenameRequest.java
index 2d9bd5c21ab1..2d0a0cf4bfdc 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotRenameRequest.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotRenameRequest.java
@@ -17,6 +17,9 @@
package org.apache.hadoop.ozone.om.request.snapshot;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_SNAPSHOT_RENAME_ALLOWED_DEFAULT;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_SNAPSHOT_RENAME_ALLOWED_KEY;
+import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.FEATURE_NOT_ENABLED;
import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.FILE_ALREADY_EXISTS;
import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.FILE_NOT_FOUND;
import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.LeveledResource.BUCKET_LOCK;
@@ -69,6 +72,13 @@ public OMSnapshotRenameRequest(OMRequest omRequest) {
public OMRequest preExecute(OzoneManager ozoneManager) throws IOException {
final OMRequest omRequest = super.preExecute(ozoneManager);
+ if (!ozoneManager.getConfiguration().getBoolean(
+ OZONE_OM_SNAPSHOT_RENAME_ALLOWED_KEY,
+ OZONE_OM_SNAPSHOT_RENAME_ALLOWED_DEFAULT)) {
+ throw new OMException("Ozone snapshot rename feature is not allowed per Ozone Manager server config",
+ FEATURE_NOT_ENABLED);
+ }
+
final RenameSnapshotRequest renameSnapshotRequest =
omRequest.getRenameSnapshotRequest();
diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotRenameRequest.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotRenameRequest.java
index 87de986a2a12..7b67b753e55e 100644
--- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotRenameRequest.java
+++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotRenameRequest.java
@@ -18,6 +18,7 @@
package org.apache.hadoop.ozone.om.request.snapshot;
import static org.apache.hadoop.hdds.protocol.proto.HddsProtos.ReplicationFactor.THREE;
+import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.FEATURE_NOT_ENABLED;
import static org.apache.hadoop.ozone.om.helpers.SnapshotInfo.SnapshotStatus.SNAPSHOT_ACTIVE;
import static org.apache.hadoop.ozone.om.helpers.SnapshotInfo.getFromProtobuf;
import static org.apache.hadoop.ozone.om.helpers.SnapshotInfo.getTableKey;
@@ -26,6 +27,7 @@
import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status.OK;
import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type.RenameSnapshot;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -38,6 +40,7 @@
import org.apache.hadoop.hdds.client.RatisReplicationConfig;
import org.apache.hadoop.hdds.utils.db.cache.CacheKey;
import org.apache.hadoop.hdds.utils.db.cache.CacheValue;
+import org.apache.hadoop.ozone.om.OMConfigKeys;
import org.apache.hadoop.ozone.om.OzoneManager;
import org.apache.hadoop.ozone.om.ResolvedBucket;
import org.apache.hadoop.ozone.om.exceptions.OMException;
@@ -67,6 +70,8 @@ public class TestOMSnapshotRenameRequest extends TestSnapshotRequestAndResponse
public void setup() throws Exception {
snapshotName1 = UUID.randomUUID().toString();
snapshotName2 = UUID.randomUUID().toString();
+ getOzoneManager().getConfiguration().setBoolean(
+ OMConfigKeys.OZONE_OM_SNAPSHOT_RENAME_ALLOWED_KEY, true);
}
@ValueSource(strings = {
@@ -87,6 +92,21 @@ public void testPreExecute(String toSnapshotName) throws Exception {
doPreExecute(omRequest);
}
+ @Test
+ public void testPreExecuteFailsWhenSnapshotRenameNotAllowed() {
+ assertFalse(OMConfigKeys.OZONE_OM_SNAPSHOT_RENAME_ALLOWED_DEFAULT);
+ getOzoneManager().getConfiguration().unset(
+ OMConfigKeys.OZONE_OM_SNAPSHOT_RENAME_ALLOWED_KEY);
+
+ OzoneManagerProtocolProtos.OMRequest omRequest = renameSnapshotRequest(
+ getVolumeName(), getBucketName(), snapshotName1, snapshotName2);
+ OMException omException = assertThrows(OMException.class,
+ () -> doPreExecute(omRequest));
+ assertEquals(FEATURE_NOT_ENABLED, omException.getResult());
+ assertEquals("Ozone snapshot rename feature is not allowed per Ozone Manager server config",
+ omException.getMessage());
+ }
+
@ValueSource(strings = {
// '-' is allowed.
"9cdf0e8a-6946-41ad-a2d1-9eb724fab126",