This is an automated email from the ASF dual-hosted git repository.
hemant pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/master by this push:
new a1f839036a HDDS-10010. Support snapshot rename operation (#6006)
a1f839036a is described below
commit a1f839036a77cb7d6e95dfa181d29bee93a3ea91
Author: Cyrill <[email protected]>
AuthorDate: Tue Feb 20 21:57:29 2024 +0300
HDDS-10010. Support snapshot rename operation (#6006)
---
.../apache/hadoop/ozone/client/ObjectStore.java | 15 +
.../ozone/client/protocol/ClientProtocol.java | 13 +
.../apache/hadoop/ozone/client/rpc/RpcClient.java | 25 ++
.../main/java/org/apache/hadoop/ozone/OmUtils.java | 1 +
.../ozone/om/protocol/OzoneManagerProtocol.java | 15 +
...OzoneManagerProtocolClientSideTranslatorPB.java | 43 ++-
.../src/main/proto/OmClientProtocol.proto | 15 +
.../org/apache/hadoop/ozone/audit/OMAction.java | 1 +
.../hadoop/ozone/om/SnapshotChainManager.java | 8 +
.../om/ratis/utils/OzoneManagerRatisUtils.java | 3 +
.../request/snapshot/OMSnapshotRenameRequest.java | 230 +++++++++++++
.../snapshot/OMSnapshotRenameResponse.java | 67 ++++
.../ozone/om/request/OMRequestTestUtils.java | 35 ++
.../snapshot/TestOMSnapshotRenameRequest.java | 359 +++++++++++++++++++++
.../fs/ozone/BasicOzoneClientAdapterImpl.java | 10 +
.../hadoop/fs/ozone/BasicOzoneFileSystem.java | 6 +
.../ozone/BasicRootedOzoneClientAdapterImpl.java | 10 +
.../fs/ozone/BasicRootedOzoneFileSystem.java | 6 +
.../apache/hadoop/fs/ozone/OzoneClientAdapter.java | 2 +
.../hadoop/ozone/client/ClientProtocolStub.java | 7 +
.../shell/snapshot/RenameSnapshotHandler.java | 64 ++++
.../ozone/shell/snapshot/SnapshotCommands.java | 3 +-
22 files changed, 931 insertions(+), 7 deletions(-)
diff --git
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/ObjectStore.java
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/ObjectStore.java
index 481bdbbd5c..e96d0f84a4 100644
---
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/ObjectStore.java
+++
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/ObjectStore.java
@@ -565,6 +565,21 @@ public class ObjectStore {
return proxy.createSnapshot(volumeName, bucketName, snapshotName);
}
+ /**
+ * Rename snapshot.
+ *
+ * @param volumeName vol to be used
+ * @param bucketName bucket to be used
+ * @param snapshotOldName Old name of the snapshot
+ * @param snapshotNewName New name of the snapshot
+ *
+ * @throws IOException
+ */
+ public void renameSnapshot(String volumeName,
+ String bucketName, String snapshotOldName, String snapshotNewName)
throws IOException {
+ proxy.renameSnapshot(volumeName, bucketName, snapshotOldName,
snapshotNewName);
+ }
+
/**
* Delete snapshot.
* @param volumeName vol to be used
diff --git
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
index 46e7e20b51..492cd31b67 100644
---
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
+++
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
@@ -1092,6 +1092,19 @@ public interface ClientProtocol {
String createSnapshot(String volumeName,
String bucketName, String snapshotName) throws IOException;
+ /**
+ * Rename snapshot.
+ *
+ * @param volumeName Vol to be used
+ * @param bucketName Bucket to be used
+ * @param snapshotOldName Old name of the snapshot
+ * @param snapshotNewName New name of the snapshot
+ *
+ * @throws IOException
+ */
+ void renameSnapshot(String volumeName,
+ String bucketName, String snapshotOldName, String snapshotNewName)
throws IOException;
+
/**
* Delete snapshot.
* @param volumeName vol to be used
diff --git
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
index 94d6ae9769..3e71262040 100644
---
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
+++
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
@@ -964,6 +964,31 @@ public class RpcClient implements ClientProtocol {
bucketName, snapshotName);
}
+ /**
+ * Rename Snapshot.
+ *
+ * @param volumeName vol to be used
+ * @param bucketName bucket to be used
+ * @param snapshotOldName Old name of the snapshot
+ * @param snapshotNewName New name of the snapshot
+ *
+ * @throws IOException
+ */
+ @Override
+ public void renameSnapshot(String volumeName,
+ String bucketName, String snapshotOldName, String snapshotNewName)
throws IOException {
+ Preconditions.checkArgument(StringUtils.isNotBlank(volumeName),
+ "volume can't be null or empty.");
+ Preconditions.checkArgument(StringUtils.isNotBlank(bucketName),
+ "bucket can't be null or empty.");
+ Preconditions.checkArgument(StringUtils.isNotBlank(snapshotOldName),
+ "old snapshot name can't be null or empty.");
+ Preconditions.checkArgument(StringUtils.isNotBlank(snapshotNewName),
+ "new snapshot name can't be null or empty.");
+
+ ozoneManagerClient.renameSnapshot(volumeName, bucketName, snapshotOldName,
snapshotNewName);
+ }
+
/**
* Delete Snapshot.
* @param volumeName vol to be used
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
index f23a703bd0..d58d922b0e 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
@@ -319,6 +319,7 @@ public final class OmUtils {
case SetRangerServiceVersion:
case CreateSnapshot:
case DeleteSnapshot:
+ case RenameSnapshot:
case SnapshotMoveDeletedKeys:
case SnapshotPurge:
case RecoverLease:
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
index 9fc8e82f03..ab3f576d44 100644
---
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
+++
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
@@ -681,6 +681,21 @@ public interface OzoneManagerProtocol
"this to be implemented");
}
+ /**
+ * Rename snapshot.
+ * @param volumeName vol to be used
+ * @param bucketName bucket to be used
+ * @param snapshotOldName Old name of the snapshot
+ * @param snapshotNewName New name of the snapshot
+ *
+ * @throws IOException
+ */
+ default void renameSnapshot(String volumeName,
+ String bucketName, String snapshotOldName, String snapshotNewName)
throws IOException {
+ throw new UnsupportedOperationException("OzoneManager does not require " +
+ "this to be implemented");
+ }
+
/**
* Delete snapshot.
* @param volumeName vol to be used
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
index 5864102758..dd201a4262 100644
---
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
+++
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
@@ -85,6 +85,8 @@ import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Allocat
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.BucketArgs;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.BucketInfo;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CancelDelegationTokenResponseProto;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CancelPrepareRequest;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CancelPrepareResponse;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CheckVolumeAccessRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CommitKeyRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateBucketRequest;
@@ -106,6 +108,8 @@ import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DeleteS
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DeleteTenantRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DeleteTenantResponse;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DeleteVolumeRequest;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.EchoRPCRequest;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.EchoRPCResponse;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.FinalizeUpgradeProgressRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.FinalizeUpgradeProgressResponse;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.FinalizeUpgradeRequest;
@@ -121,7 +125,6 @@ import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetS3Se
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetS3SecretResponse;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetS3VolumeContextRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetS3VolumeContextResponse;
-import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.SnapshotInfoRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.InfoBucketRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.InfoBucketResponse;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.InfoVolumeRequest;
@@ -129,14 +132,14 @@ import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.InfoVol
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.KeyArgs;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListBucketsRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListBucketsResponse;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListKeysLightResponse;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListKeysRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListKeysResponse;
-import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListKeysLightResponse;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListMultipartUploadsRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListMultipartUploadsResponse;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListStatusLightResponse;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListStatusRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListStatusResponse;
-import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListStatusLightResponse;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListTenantRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListTenantResponse;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListTrashRequest;
@@ -161,6 +164,11 @@ import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRespo
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OzoneAclInfo;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OzoneFileStatusProto;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OzoneFileStatusProtoLight;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareRequest;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareRequestArgs;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareResponse;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusRequest;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrintCompactionLogDagRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RangerBGSyncRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RangerBGSyncResponse;
@@ -172,12 +180,14 @@ import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Refetch
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RefetchSecretKeyResponse;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RemoveAclRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RemoveAclResponse;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RenameKeyRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RenameKeysArgs;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RenameKeysMap;
-import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RenameKeyRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RenameKeysRequest;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RenameSnapshotRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RenewDelegationTokenResponseProto;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RevokeS3SecretRequest;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.S3Authentication;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.S3Secret;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.SafeMode;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ServiceListRequest;
@@ -185,12 +195,15 @@ import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Service
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.SetAclRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.SetAclResponse;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.SetBucketPropertyRequest;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.SetBucketPropertyResponse;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.SetS3SecretRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.SetS3SecretResponse;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.SetSafeModeRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.SetSafeModeResponse;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.SetTimesRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.SetVolumePropertyRequest;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.SetVolumePropertyResponse;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.SnapshotInfoRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantAssignAdminRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantAssignUserAccessIdRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantAssignUserAccessIdResponse;
@@ -202,8 +215,6 @@ import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantR
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantRevokeUserAccessIdRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.VolumeInfo;
-import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.EchoRPCRequest;
-import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.EchoRPCResponse;
import org.apache.hadoop.ozone.protocolPB.OMPBHelper;
import org.apache.hadoop.ozone.security.OzoneTokenIdentifier;
import org.apache.hadoop.ozone.security.acl.OzoneObj;
@@ -1224,6 +1235,26 @@ public final class
OzoneManagerProtocolClientSideTranslatorPB
return snapshotInfo.getName();
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void renameSnapshot(String volumeName, String bucketName,
+ String snapshotOldName, String snapshotNewName) throws IOException {
+ RenameSnapshotRequest.Builder requestBuilder =
+ RenameSnapshotRequest.newBuilder()
+ .setVolumeName(volumeName)
+ .setBucketName(bucketName)
+ .setSnapshotOldName(snapshotOldName)
+ .setSnapshotNewName(snapshotNewName);
+
+ final OMRequest omRequest = createOMRequest(Type.RenameSnapshot)
+ .setRenameSnapshotRequest(requestBuilder)
+ .build();
+ final OMResponse omResponse = submitRequest(omRequest);
+ handleError(omResponse);
+ }
+
/**
* {@inheritDoc}
*/
diff --git
a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
index 5c737fdad9..b0d26020c8 100644
--- a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
+++ b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
@@ -146,6 +146,7 @@ enum Type {
SetSnapshotProperty = 128;
ListStatusLight = 129;
GetSnapshotInfo = 130;
+ RenameSnapshot = 131;
}
enum SafeMode {
@@ -281,6 +282,7 @@ message OMRequest {
optional MultipartUploadsExpiredAbortRequest
multipartUploadsExpiredAbortRequest = 126;
optional SetSnapshotPropertyRequest SetSnapshotPropertyRequest =
127;
optional SnapshotInfoRequest SnapshotInfoRequest =
128;
+ optional RenameSnapshotRequest RenameSnapshotRequest =
129;
}
message OMResponse {
@@ -403,6 +405,7 @@ message OMResponse {
optional ListStatusLightResponse listStatusLightResponse =
129;
optional SnapshotInfoResponse SnapshotInfoResponse =
130;
optional OMLockDetailsProto omLockDetails =
131;
+ optional RenameSnapshotResponse RenameSnapshotResponse =
132;
}
enum Status {
@@ -1830,6 +1833,14 @@ message CreateSnapshotRequest {
optional uint64 creationTime = 5;
}
+message RenameSnapshotRequest {
+ optional string volumeName = 1;
+ optional string bucketName = 2;
+ optional string snapshotOldName = 3;
+ optional string snapshotNewName = 4;
+ optional uint64 renameTime = 5;
+}
+
message ListSnapshotRequest {
optional string volumeName = 1;
optional string bucketName = 2;
@@ -1992,6 +2003,10 @@ message DeleteSnapshotResponse {
}
+message RenameSnapshotResponse {
+ optional SnapshotInfo snapshotInfo = 1;
+}
+
message SnapshotInfoResponse {
optional SnapshotInfo snapshotInfo = 1;
}
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java
index 4e9039252f..4804b317bc 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java
@@ -98,6 +98,7 @@ public enum OMAction implements AuditAction {
CREATE_SNAPSHOT,
LIST_SNAPSHOT,
DELETE_SNAPSHOT,
+ RENAME_SNAPSHOT,
SNAPSHOT_MOVE_DELETED_KEYS,
SNAPSHOT_INFO,
SET_TIMES,
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/SnapshotChainManager.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/SnapshotChainManager.java
index 18deca1a4f..60353590e7 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/SnapshotChainManager.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/SnapshotChainManager.java
@@ -348,6 +348,14 @@ public class SnapshotChainManager {
snapshotInfo.getTableKey());
}
+ /**
+ * Update snapshot chain when snapshot changes (e.g. renamed).
+ */
+ public synchronized void updateSnapshot(SnapshotInfo snapshotInfo) {
+ snapshotIdToTableKey.computeIfPresent(snapshotInfo.getSnapshotId(),
+ (snapshotId, dbTableKey) -> snapshotInfo.getTableKey());
+ }
+
/**
* Delete snapshot from snapshot chain.
*/
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java
index 3ab65346e7..b055a1f92f 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java
@@ -79,6 +79,7 @@ import
org.apache.hadoop.ozone.om.request.snapshot.OMSnapshotCreateRequest;
import org.apache.hadoop.ozone.om.request.snapshot.OMSnapshotDeleteRequest;
import
org.apache.hadoop.ozone.om.request.snapshot.OMSnapshotMoveDeletedKeysRequest;
import org.apache.hadoop.ozone.om.request.snapshot.OMSnapshotPurgeRequest;
+import org.apache.hadoop.ozone.om.request.snapshot.OMSnapshotRenameRequest;
import
org.apache.hadoop.ozone.om.request.snapshot.OMSnapshotSetPropertyRequest;
import org.apache.hadoop.ozone.om.request.upgrade.OMCancelPrepareRequest;
import org.apache.hadoop.ozone.om.request.upgrade.OMFinalizeUpgradeRequest;
@@ -224,6 +225,8 @@ public final class OzoneManagerRatisUtils {
return new OMSnapshotCreateRequest(omRequest);
case DeleteSnapshot:
return new OMSnapshotDeleteRequest(omRequest);
+ case RenameSnapshot:
+ return new OMSnapshotRenameRequest(omRequest);
case SnapshotMoveDeletedKeys:
return new OMSnapshotMoveDeletedKeysRequest(omRequest);
case SnapshotPurge:
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
new file mode 100644
index 0000000000..9f1875f65d
--- /dev/null
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotRenameRequest.java
@@ -0,0 +1,230 @@
+/*
+ * 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.hadoop.ozone.om.request.snapshot;
+
+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.Resource.BUCKET_LOCK;
+import static
org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.SNAPSHOT_LOCK;
+import static
org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature.FILESYSTEM_SNAPSHOT;
+
+import java.io.IOException;
+import java.nio.file.InvalidPathException;
+import org.apache.hadoop.hdds.utils.db.cache.CacheKey;
+import org.apache.hadoop.hdds.utils.db.cache.CacheValue;
+import org.apache.hadoop.ozone.OmUtils;
+import org.apache.hadoop.ozone.audit.AuditLogger;
+import org.apache.hadoop.ozone.audit.OMAction;
+import org.apache.hadoop.ozone.om.OmMetadataManagerImpl;
+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.om.request.OMClientRequest;
+import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
+import org.apache.hadoop.ozone.om.response.OMClientResponse;
+import org.apache.hadoop.ozone.om.response.snapshot.OMSnapshotRenameResponse;
+import org.apache.hadoop.ozone.om.snapshot.RequireSnapshotFeatureState;
+import org.apache.hadoop.ozone.om.upgrade.DisallowedUntilLayoutVersion;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RenameSnapshotRequest;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.UserInfo;
+import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
+import org.apache.hadoop.ozone.security.acl.OzoneObj;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.util.Time;
+import org.apache.ratis.server.protocol.TermIndex;
+
+/**
+ * Changes snapshot name.
+ */
+public class OMSnapshotRenameRequest extends OMClientRequest {
+
+ public OMSnapshotRenameRequest(OMRequest omRequest) {
+ super(omRequest);
+ }
+
+ @Override
+ @DisallowedUntilLayoutVersion(FILESYSTEM_SNAPSHOT)
+ @RequireSnapshotFeatureState(true)
+ public OMRequest preExecute(OzoneManager ozoneManager) throws IOException {
+ final OMRequest omRequest = super.preExecute(ozoneManager);
+
+ final RenameSnapshotRequest renameSnapshotRequest =
+ omRequest.getRenameSnapshotRequest();
+
+ final String snapshotNewName = renameSnapshotRequest.getSnapshotNewName();
+
+ OmUtils.validateSnapshotName(snapshotNewName);
+
+ String volumeName = renameSnapshotRequest.getVolumeName();
+ String bucketName = renameSnapshotRequest.getBucketName();
+
+ // Permission check
+ UserGroupInformation ugi = createUGIForApi();
+ String bucketOwner = ozoneManager.getBucketOwner(volumeName, bucketName,
+
IAccessAuthorizer.ACLType.READ, OzoneObj.ResourceType.BUCKET);
+ if (!ozoneManager.isAdmin(ugi) &&
+ !ozoneManager.isOwner(ugi, bucketOwner)) {
+ throw new OMException(
+ "Only bucket owners and Ozone admins can rename snapshots",
+ OMException.ResultCodes.PERMISSION_DENIED);
+ }
+
+ // Set rename time here so OM leader and follower would have the
+ // exact same timestamp.
+ OMRequest.Builder omRequestBuilder = omRequest.toBuilder()
+ .setRenameSnapshotRequest(
+ RenameSnapshotRequest.newBuilder()
+ .setVolumeName(volumeName)
+ .setBucketName(bucketName)
+ .setSnapshotNewName(snapshotNewName)
+ .setSnapshotOldName(renameSnapshotRequest.getSnapshotOldName())
+ .setRenameTime(Time.now()));
+
+ return omRequestBuilder.build();
+ }
+
+
+ @Override
+ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager,
+ TermIndex termIndex) {
+ boolean acquiredBucketLock = false;
+ boolean acquiredSnapshotOldLock = false;
+ boolean acquiredSnapshotNewLock = false;
+ Exception exception = null;
+ OmMetadataManagerImpl omMetadataManager = (OmMetadataManagerImpl)
+ ozoneManager.getMetadataManager();
+
+ OMResponse.Builder omResponse = OmResponseUtil.getOMResponseBuilder(
+ getOmRequest());
+ OMClientResponse omClientResponse = null;
+ AuditLogger auditLogger = ozoneManager.getAuditLogger();
+
+ UserInfo userInfo = getOmRequest().getUserInfo();
+
+ final RenameSnapshotRequest request =
+ getOmRequest().getRenameSnapshotRequest();
+
+ final String volumeName = request.getVolumeName();
+ final String bucketName = request.getBucketName();
+ final String snapshotNewName = request.getSnapshotNewName();
+ final String snapshotOldName = request.getSnapshotOldName();
+
+ SnapshotInfo snapshotOldInfo = null;
+
+ try {
+ // Acquire bucket lock
+ mergeOmLockDetails(
+ omMetadataManager.getLock().acquireWriteLock(BUCKET_LOCK,
+ volumeName,
bucketName));
+ acquiredBucketLock = getOmLockDetails().isLockAcquired();
+
+
mergeOmLockDetails(omMetadataManager.getLock().acquireWriteLock(SNAPSHOT_LOCK,
+ volumeName, bucketName,
snapshotOldName));
+ acquiredSnapshotOldLock = getOmLockDetails().isLockAcquired();
+
+
mergeOmLockDetails(omMetadataManager.getLock().acquireWriteLock(SNAPSHOT_LOCK,
+ volumeName, bucketName,
snapshotNewName));
+ acquiredSnapshotNewLock = getOmLockDetails().isLockAcquired();
+
+ // Retrieve SnapshotInfo from the table
+ String snapshotNewTableKey = SnapshotInfo.getTableKey(volumeName,
bucketName, snapshotNewName);
+
+ if
(omMetadataManager.getSnapshotInfoTable().isExist(snapshotNewTableKey)) {
+ throw new OMException("Snapshot with name " + snapshotNewName +
"already exist",
+ FILE_ALREADY_EXISTS);
+ }
+
+ // Retrieve SnapshotInfo from the table
+ String snapshotOldTableKey = SnapshotInfo.getTableKey(volumeName,
bucketName,
+ snapshotOldName);
+ snapshotOldInfo =
+ omMetadataManager.getSnapshotInfoTable().get(snapshotOldTableKey);
+
+ if (snapshotOldInfo == null) {
+ // Snapshot does not exist
+ throw new OMException("Snapshot with name " + snapshotOldName + "does
not exist",
+ FILE_NOT_FOUND);
+ }
+
+ switch (snapshotOldInfo.getSnapshotStatus()) {
+ case SNAPSHOT_DELETED:
+ throw new OMException("Snapshot is already deleted. "
+ + "Pending reclamation.", FILE_NOT_FOUND);
+ case SNAPSHOT_ACTIVE:
+ break;
+ default:
+ // Unknown snapshot non-active state
+ throw new OMException("Snapshot exists but no longer in active state",
+ FILE_NOT_FOUND);
+ }
+
+ snapshotOldInfo.setName(snapshotNewName);
+
+ omMetadataManager.getSnapshotInfoTable().addCacheEntry(
+ new CacheKey<>(snapshotOldTableKey),
+ CacheValue.get(termIndex.getIndex()));
+
+ omMetadataManager.getSnapshotInfoTable().addCacheEntry(
+ new CacheKey<>(snapshotNewTableKey),
+ CacheValue.get(termIndex.getIndex(), snapshotOldInfo));
+
+
omMetadataManager.getSnapshotChainManager().updateSnapshot(snapshotOldInfo);
+
+ omResponse.setRenameSnapshotResponse(
+ OzoneManagerProtocolProtos.RenameSnapshotResponse.newBuilder()
+ .setSnapshotInfo(snapshotOldInfo.getProtobuf()));
+ omClientResponse = new OMSnapshotRenameResponse(
+ omResponse.build(), snapshotOldTableKey, snapshotNewTableKey,
snapshotOldInfo);
+
+ } catch (IOException | InvalidPathException ex) {
+ exception = ex;
+ omClientResponse = new OMSnapshotRenameResponse(
+ createErrorOMResponse(omResponse, exception));
+ } finally {
+ if (acquiredSnapshotNewLock) {
+
mergeOmLockDetails(omMetadataManager.getLock().releaseWriteLock(SNAPSHOT_LOCK,
volumeName,
+ bucketName,
snapshotNewName));
+ }
+ if (acquiredSnapshotOldLock) {
+
mergeOmLockDetails(omMetadataManager.getLock().releaseWriteLock(SNAPSHOT_LOCK,
volumeName,
+ bucketName,
snapshotOldName));
+ }
+ if (acquiredBucketLock) {
+
mergeOmLockDetails(omMetadataManager.getLock().releaseWriteLock(BUCKET_LOCK,
volumeName,
+ bucketName));
+ }
+ if (omClientResponse != null) {
+ omClientResponse.setOmLockDetails(getOmLockDetails());
+ }
+ }
+
+ if (snapshotOldInfo == null) {
+ // Dummy SnapshotInfo for logging and audit logging when erred
+ snapshotOldInfo = SnapshotInfo.newInstance(volumeName, bucketName,
+ snapshotOldName, null,
Time.now());
+ }
+
+ // Perform audit logging outside the lock
+ auditLog(auditLogger, buildAuditMessage(OMAction.RENAME_SNAPSHOT,
+ snapshotOldInfo.toAuditMap(),
exception, userInfo));
+ return omClientResponse;
+ }
+}
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/snapshot/OMSnapshotRenameResponse.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/snapshot/OMSnapshotRenameResponse.java
new file mode 100644
index 0000000000..05bb16a8f5
--- /dev/null
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/snapshot/OMSnapshotRenameResponse.java
@@ -0,0 +1,67 @@
+/*
+ * 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.hadoop.ozone.om.response.snapshot;
+
+import static
org.apache.hadoop.ozone.om.OmMetadataManagerImpl.SNAPSHOT_INFO_TABLE;
+
+import jakarta.annotation.Nonnull;
+import java.io.IOException;
+import org.apache.hadoop.hdds.utils.db.BatchOperation;
+import org.apache.hadoop.ozone.om.OMMetadataManager;
+import org.apache.hadoop.ozone.om.helpers.SnapshotInfo;
+import org.apache.hadoop.ozone.om.response.CleanupTableInfo;
+import org.apache.hadoop.ozone.om.response.OMClientResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
+
+/**
+ * Response for OMSnapshotRenameRequest.
+ */
+@CleanupTableInfo(cleanupTables = {SNAPSHOT_INFO_TABLE})
+public class OMSnapshotRenameResponse extends OMClientResponse {
+
+ private String snapshotOldName;
+ private String snapshotNewName;
+ private SnapshotInfo renamedInfo;
+
+ public OMSnapshotRenameResponse(OzoneManagerProtocolProtos.OMResponse
omResponse,
+ String snapshotOldName, String
snapshotNewName,
+ @Nonnull SnapshotInfo renamedInfo) {
+ super(omResponse);
+ this.snapshotOldName = snapshotOldName;
+ this.snapshotNewName = snapshotNewName;
+ this.renamedInfo = renamedInfo;
+ }
+
+ /**
+ * For when the request is not successful.
+ * For a successful request, the other constructor should be used.
+ */
+ public OMSnapshotRenameResponse(@Nonnull
OzoneManagerProtocolProtos.OMResponse omResponse) {
+ super(omResponse);
+ checkStatusNotOK();
+ }
+
+ @Override
+ protected void addToDBBatch(OMMetadataManager omMetadataManager,
BatchOperation batchOperation)
+ throws IOException {
+ omMetadataManager.getSnapshotInfoTable()
+ .putWithBatch(batchOperation, snapshotNewName, renamedInfo);
+ omMetadataManager.getSnapshotInfoTable()
+ .deleteWithBatch(batchOperation, snapshotOldName);
+ }
+}
diff --git
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/OMRequestTestUtils.java
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/OMRequestTestUtils.java
index 1bd642fce7..e85675e9b0 100644
---
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/OMRequestTestUtils.java
+++
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/OMRequestTestUtils.java
@@ -1298,6 +1298,41 @@ public final class OMRequestTestUtils {
.build();
}
+ /**
+ * Create OMRequest for Rename Snapshot.
+ *
+ * @param volumeName vol to be used
+ * @param bucketName bucket to be used
+ * @param snapshotOldName Old name of the snapshot
+ * @param snapshotNewName New name of the snapshot
+ */
+ public static OMRequest renameSnapshotRequest(String volumeName,
+ String bucketName,
+ String snapshotOldName,
+ String snapshotNewName) {
+ OzoneManagerProtocolProtos.RenameSnapshotRequest renameSnapshotRequest =
+ OzoneManagerProtocolProtos.RenameSnapshotRequest.newBuilder()
+ .setVolumeName(volumeName)
+ .setBucketName(bucketName)
+ .setSnapshotOldName(snapshotOldName)
+ .setSnapshotNewName(snapshotNewName)
+ .build();
+
+ OzoneManagerProtocolProtos.UserInfo userInfo =
+ OzoneManagerProtocolProtos.UserInfo.newBuilder()
+ .setUserName("user")
+ .setHostName("host")
+ .setRemoteAddress("remote-address")
+ .build();
+
+ return OMRequest.newBuilder()
+ .setRenameSnapshotRequest(renameSnapshotRequest)
+ .setCmdType(Type.RenameSnapshot)
+ .setClientId(UUID.randomUUID().toString())
+ .setUserInfo(userInfo)
+ .build();
+ }
+
/**
* Create OMRequest for Delete Snapshot.
* @param volumeName vol to be used
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
new file mode 100644
index 0000000000..14af3e28b8
--- /dev/null
+++
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotRenameRequest.java
@@ -0,0 +1,359 @@
+/*
+ * 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.hadoop.ozone.om.request.snapshot;
+
+import org.apache.hadoop.hdds.client.RatisReplicationConfig;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.utils.db.BatchOperation;
+import org.apache.hadoop.hdds.utils.db.cache.CacheKey;
+import org.apache.hadoop.hdds.utils.db.cache.CacheValue;
+import org.apache.hadoop.ozone.OzoneConfigKeys;
+import org.apache.hadoop.ozone.audit.AuditLogger;
+import org.apache.hadoop.ozone.audit.AuditMessage;
+import org.apache.hadoop.ozone.om.OMConfigKeys;
+import org.apache.hadoop.ozone.om.OMMetrics;
+import org.apache.hadoop.ozone.om.OmMetadataManagerImpl;
+import org.apache.hadoop.ozone.om.OmSnapshotManager;
+import org.apache.hadoop.ozone.om.OzoneManager;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.om.helpers.OmBucketInfo;
+import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
+import org.apache.hadoop.ozone.om.helpers.SnapshotInfo;
+import org.apache.hadoop.ozone.om.request.OMRequestTestUtils;
+import org.apache.hadoop.ozone.om.response.OMClientResponse;
+import org.apache.hadoop.ozone.om.upgrade.OMLayoutVersionManager;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
+import org.apache.hadoop.util.Time;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.io.File;
+import java.util.UUID;
+
+import static
org.apache.hadoop.hdds.protocol.proto.HddsProtos.ReplicationFactor.THREE;
+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;
+import static
org.apache.hadoop.ozone.om.request.OMRequestTestUtils.createSnapshotRequest;
+import static
org.apache.hadoop.ozone.om.request.OMRequestTestUtils.renameSnapshotRequest;
+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.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.framework;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests OMSnapshotRenameRequest class, which handles RenameSnapshot request.
+ */
+public class TestOMSnapshotRenameRequest {
+
+ @TempDir
+ private File anotherTempDir;
+
+ private OzoneManager ozoneManager;
+ private OMMetrics omMetrics;
+ private OmMetadataManagerImpl omMetadataManager;
+ private BatchOperation batchOperation;
+
+ private String volumeName;
+ private String bucketName;
+ private String snapshotName1;
+ private String snapshotName2;
+
+ @BeforeEach
+ public void setup() throws Exception {
+ ozoneManager = mock(OzoneManager.class);
+ omMetrics = OMMetrics.create();
+ OzoneConfiguration ozoneConfiguration = new OzoneConfiguration();
+ ozoneConfiguration.set(OMConfigKeys.OZONE_OM_DB_DIRS,
+ anotherTempDir.getAbsolutePath());
+ ozoneConfiguration.set(OzoneConfigKeys.OZONE_METADATA_DIRS,
+ anotherTempDir.getAbsolutePath());
+ omMetadataManager = new OmMetadataManagerImpl(ozoneConfiguration,
+ ozoneManager);
+ when(ozoneManager.getMetrics()).thenReturn(omMetrics);
+ when(ozoneManager.getMetadataManager()).thenReturn(omMetadataManager);
+ when(ozoneManager.isRatisEnabled()).thenReturn(true);
+ when(ozoneManager.isFilesystemSnapshotEnabled()).thenReturn(true);
+ when(ozoneManager.isAdmin(any())).thenReturn(false);
+ when(ozoneManager.isOwner(any(), any())).thenReturn(false);
+ when(ozoneManager.getBucketOwner(any(), any(),
+ any(), any())).thenReturn("dummyBucketOwner");
+ OMLayoutVersionManager lvm = mock(OMLayoutVersionManager.class);
+ when(lvm.isAllowed(anyString())).thenReturn(true);
+ when(ozoneManager.getVersionManager()).thenReturn(lvm);
+ AuditLogger auditLogger = mock(AuditLogger.class);
+ when(ozoneManager.getAuditLogger()).thenReturn(auditLogger);
+ doNothing().when(auditLogger).logWrite(any(AuditMessage.class));
+ batchOperation = omMetadataManager.getStore().initBatchOperation();
+ when(ozoneManager.getConfiguration()).thenReturn(ozoneConfiguration);
+ OmSnapshotManager omSnapshotManager = new OmSnapshotManager(ozoneManager);
+ when(ozoneManager.getOmSnapshotManager()).thenReturn(omSnapshotManager);
+
+ volumeName = UUID.randomUUID().toString();
+ bucketName = UUID.randomUUID().toString();
+ snapshotName1 = UUID.randomUUID().toString();
+ snapshotName2 = UUID.randomUUID().toString();
+ OMRequestTestUtils.addVolumeAndBucketToDB(volumeName, bucketName,
+ omMetadataManager);
+ }
+
+ @AfterEach
+ public void stop() {
+ omMetrics.unRegister();
+ framework().clearInlineMocks();
+ if (batchOperation != null) {
+ batchOperation.close();
+ }
+ }
+
+ @ValueSource(strings = {
+ // '-' is allowed.
+ "9cdf0e8a-6946-41ad-a2d1-9eb724fab126",
+ // 3 chars name is allowed.
+ "sn1",
+ // less than or equal to 63 chars are allowed.
+ "snap75795657617173401188448010125899089001363595171500499231286"
+ })
+ @ParameterizedTest
+ public void testPreExecute(String toSnapshotName) throws Exception {
+ when(ozoneManager.isOwner(any(), any())).thenReturn(true);
+
+ String currentSnapshotName = "current";
+ OzoneManagerProtocolProtos.OMRequest omRequest =
renameSnapshotRequest(volumeName,
+ bucketName, currentSnapshotName, toSnapshotName);
+ doPreExecute(omRequest);
+ }
+
+ @ValueSource(strings = {
+ // ? is not allowed in snapshot name.
+ "a?b",
+ // only numeric name not allowed.
+ "1234",
+ // less than 3 chars are not allowed.
+ "s1",
+ // more than or equal to 64 chars are not allowed.
+ "snap156808943643007724443266605711479126926050896107709081166294",
+ // Underscore is not allowed.
+ "snap_1",
+ // CamelCase is not allowed.
+ "NewSnapshot"
+ })
+ @ParameterizedTest
+ public void testPreExecuteFailure(String toSnapshotName) {
+ when(ozoneManager.isOwner(any(), any())).thenReturn(true);
+ String currentSnapshotName = "current";
+ OzoneManagerProtocolProtos.OMRequest omRequest =
renameSnapshotRequest(volumeName,
+ bucketName, currentSnapshotName, toSnapshotName);
+ OMException omException =
+ assertThrows(OMException.class, () -> doPreExecute(omRequest));
+ assertEquals("Invalid snapshot name: " + toSnapshotName,
+ omException.getMessage());
+ }
+
+ @Test
+ public void testPreExecuteBadOwner() {
+ // Owner is not set for the request.
+ OzoneManagerProtocolProtos.OMRequest omRequest =
renameSnapshotRequest(volumeName,
+ bucketName, snapshotName1, snapshotName2);
+
+ OMException omException = assertThrows(OMException.class,
+ () -> doPreExecute(omRequest));
+ assertEquals("Only bucket owners and Ozone admins can rename snapshots",
+ omException.getMessage());
+ }
+
+ @Test
+ public void testValidateAndUpdateCache() throws Exception {
+ when(ozoneManager.isAdmin(any())).thenReturn(true);
+ OzoneManagerProtocolProtos.OMRequest omRequest =
renameSnapshotRequest(volumeName,
+ bucketName, snapshotName1, snapshotName2);
+ OMSnapshotRenameRequest omSnapshotRenameRequest = doPreExecute(omRequest);
+ String key = getTableKey(volumeName, bucketName, snapshotName1);
+ String bucketKey = omMetadataManager.getBucketKey(volumeName, bucketName);
+
+ // Add a 1000-byte key to the bucket
+ OmKeyInfo key1 = addKey("key-testValidateAndUpdateCache", 12345L);
+ addKeyToTable(key1);
+
+ OmBucketInfo omBucketInfo = omMetadataManager.getBucketTable().get(
+ bucketKey);
+ long bucketDataSize = key1.getDataSize();
+ long bucketUsedBytes = omBucketInfo.getUsedBytes();
+ assertEquals(key1.getReplicatedSize(), bucketUsedBytes);
+
+ // Value in cache should be null as of now.
+ assertNull(omMetadataManager.getSnapshotInfoTable().get(key));
+
+ // Add key to cache.
+ SnapshotInfo snapshotInfo = SnapshotInfo.newInstance(volumeName,
bucketName,
+ snapshotName1, UUID.randomUUID(), Time.now());
+ snapshotInfo.setReferencedSize(1000L);
+ snapshotInfo.setReferencedReplicatedSize(3 * 1000L);
+ assertEquals(SNAPSHOT_ACTIVE, snapshotInfo.getSnapshotStatus());
+ omMetadataManager.getSnapshotInfoTable().addCacheEntry(
+ new CacheKey<>(key),
+ CacheValue.get(1L, snapshotInfo));
+
+ // Run validateAndUpdateCache.
+ OMClientResponse omClientResponse =
+ omSnapshotRenameRequest.validateAndUpdateCache(ozoneManager, 2L);
+
+ assertNotNull(omClientResponse.getOMResponse());
+
+ OzoneManagerProtocolProtos.OMResponse omResponse =
omClientResponse.getOMResponse();
+ assertNotNull(omResponse.getRenameSnapshotResponse());
+ assertEquals(RenameSnapshot, omResponse.getCmdType());
+ assertEquals(OK, omResponse.getStatus());
+
+ // verify table data with response data.
+ OzoneManagerProtocolProtos.SnapshotInfo snapshotInfoProto =
+ omClientResponse
+ .getOMResponse()
+ .getRenameSnapshotResponse()
+ .getSnapshotInfo();
+
+ assertEquals(bucketDataSize, snapshotInfoProto.getReferencedSize());
+ assertEquals(bucketUsedBytes,
+ snapshotInfoProto.getReferencedReplicatedSize());
+
+ SnapshotInfo snapshotInfoOldProto = getFromProtobuf(snapshotInfoProto);
+
+ String key2 = getTableKey(volumeName, bucketName, snapshotName2);
+
+ // Get value from cache
+ SnapshotInfo snapshotInfoNewInCache =
+ omMetadataManager.getSnapshotInfoTable().get(key2);
+ assertNotNull(snapshotInfoNewInCache);
+ assertEquals(snapshotInfoOldProto, snapshotInfoNewInCache);
+ assertEquals(snapshotInfo.getSnapshotId(),
snapshotInfoNewInCache.getSnapshotId());
+
+ SnapshotInfo snapshotInfoOldInCache =
+ omMetadataManager.getSnapshotInfoTable().get(key);
+ assertNull(snapshotInfoOldInCache);
+ }
+
+ @Test
+ public void testEntryExists() throws Exception {
+ when(ozoneManager.isAdmin(any())).thenReturn(true);
+
+ String keyNameOld = getTableKey(volumeName, bucketName, snapshotName1);
+ String keyNameNew = getTableKey(volumeName, bucketName, snapshotName2);
+
+ assertNull(omMetadataManager.getSnapshotInfoTable().get(keyNameOld));
+ assertNull(omMetadataManager.getSnapshotInfoTable().get(keyNameNew));
+
+ // First make sure we have two snapshots.
+ OzoneManagerProtocolProtos.OMRequest createOmRequest =
+ createSnapshotRequest(volumeName, bucketName, snapshotName1);
+ OMSnapshotCreateRequest omSnapshotCreateRequest =
+ TestOMSnapshotCreateRequest.doPreExecute(createOmRequest,
ozoneManager);
+ omSnapshotCreateRequest.validateAndUpdateCache(ozoneManager, 1);
+
+ createOmRequest =
+ createSnapshotRequest(volumeName, bucketName, snapshotName2);
+ omSnapshotCreateRequest =
+ TestOMSnapshotCreateRequest.doPreExecute(createOmRequest,
ozoneManager);
+ omSnapshotCreateRequest.validateAndUpdateCache(ozoneManager, 2);
+
+ assertNotNull(omMetadataManager.getSnapshotInfoTable().get(keyNameOld));
+ assertNotNull(omMetadataManager.getSnapshotInfoTable().get(keyNameNew));
+
+ // Now try renaming and get an error.
+ OzoneManagerProtocolProtos.OMRequest omRequest =
+ renameSnapshotRequest(volumeName, bucketName, snapshotName1,
snapshotName2);
+ OMSnapshotRenameRequest omSnapshotRenameRequest = doPreExecute(omRequest);
+
+ OMClientResponse omClientResponse =
+ omSnapshotRenameRequest.validateAndUpdateCache(ozoneManager, 3);
+
+ assertNotNull(omMetadataManager.getSnapshotInfoTable().get(keyNameOld));
+ assertNotNull(omMetadataManager.getSnapshotInfoTable().get(keyNameNew));
+
+ OzoneManagerProtocolProtos.OMResponse omResponse =
omClientResponse.getOMResponse();
+ assertNotNull(omResponse.getRenameSnapshotResponse());
+ assertEquals(OzoneManagerProtocolProtos.Status.FILE_ALREADY_EXISTS,
+ omResponse.getStatus());
+ }
+
+ @Test
+ public void testEntryNotFound() throws Exception {
+ when(ozoneManager.isAdmin(any())).thenReturn(true);
+
+ String keyNameOld = getTableKey(volumeName, bucketName, snapshotName1);
+ String keyNameNew = getTableKey(volumeName, bucketName, snapshotName2);
+
+ assertNull(omMetadataManager.getSnapshotInfoTable().get(keyNameOld));
+ assertNull(omMetadataManager.getSnapshotInfoTable().get(keyNameNew));
+
+ // Now try renaming and get an error.
+ OzoneManagerProtocolProtos.OMRequest omRequest =
+ renameSnapshotRequest(volumeName, bucketName, snapshotName1,
snapshotName2);
+ OMSnapshotRenameRequest omSnapshotRenameRequest = doPreExecute(omRequest);
+
+ OMClientResponse omClientResponse =
+ omSnapshotRenameRequest.validateAndUpdateCache(ozoneManager, 3);
+
+ assertNull(omMetadataManager.getSnapshotInfoTable().get(keyNameOld));
+ assertNull(omMetadataManager.getSnapshotInfoTable().get(keyNameNew));
+
+ OzoneManagerProtocolProtos.OMResponse omResponse =
omClientResponse.getOMResponse();
+ assertNotNull(omResponse.getRenameSnapshotResponse());
+ assertEquals(OzoneManagerProtocolProtos.Status.FILE_NOT_FOUND,
+ omResponse.getStatus());
+ }
+
+ private OMSnapshotRenameRequest doPreExecute(
+ OzoneManagerProtocolProtos.OMRequest originalRequest) throws Exception {
+ return doPreExecute(originalRequest, ozoneManager);
+ }
+
+ public static OMSnapshotRenameRequest doPreExecute(
+ OzoneManagerProtocolProtos.OMRequest originalRequest, OzoneManager
ozoneManager) throws Exception {
+ OMSnapshotRenameRequest omSnapshotRenameRequest =
+ new OMSnapshotRenameRequest(originalRequest);
+
+ OzoneManagerProtocolProtos.OMRequest modifiedRequest =
+ omSnapshotRenameRequest.preExecute(ozoneManager);
+ return new OMSnapshotRenameRequest(modifiedRequest);
+ }
+
+ private OmKeyInfo addKey(String keyName, long objectId) {
+ return OMRequestTestUtils.createOmKeyInfo(volumeName, bucketName, keyName,
+ RatisReplicationConfig.getInstance(THREE)).setObjectID(objectId)
+ .build();
+ }
+
+ protected String addKeyToTable(OmKeyInfo keyInfo) throws Exception {
+ OMRequestTestUtils.addKeyToTable(false, true, keyInfo, 0, 0L,
+ omMetadataManager);
+ return omMetadataManager.getOzoneKey(keyInfo.getVolumeName(),
+ keyInfo.getBucketName(), keyInfo.getKeyName());
+ }
+
+}
diff --git
a/hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/BasicOzoneClientAdapterImpl.java
b/hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/BasicOzoneClientAdapterImpl.java
index e6892d9784..1614f81087 100644
---
a/hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/BasicOzoneClientAdapterImpl.java
+++
b/hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/BasicOzoneClientAdapterImpl.java
@@ -604,6 +604,16 @@ public class BasicOzoneClientAdapterImpl implements
OzoneClientAdapter {
snapshotName);
}
+ @Override
+ public void renameSnapshot(String pathStr, String snapshotOldName, String
snapshotNewName)
+ throws IOException {
+ OFSPath ofsPath = new OFSPath(pathStr, config);
+ objectStore.renameSnapshot(ofsPath.getVolumeName(),
+ ofsPath.getBucketName(),
+ snapshotOldName,
+ snapshotNewName);
+ }
+
@Override
public void deleteSnapshot(String pathStr, String snapshotName)
throws IOException {
diff --git
a/hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/BasicOzoneFileSystem.java
b/hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/BasicOzoneFileSystem.java
index dbe3b517e5..cd09cf1d5a 100644
---
a/hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/BasicOzoneFileSystem.java
+++
b/hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/BasicOzoneFileSystem.java
@@ -954,6 +954,12 @@ public class BasicOzoneFileSystem extends FileSystem {
OM_SNAPSHOT_INDICATOR + OZONE_URI_DELIMITER + snapshot);
}
+ @Override
+ public void renameSnapshot(Path path, String snapshotOldName, String
snapshotNewName)
+ throws IOException {
+ getAdapter().renameSnapshot(pathToKey(path), snapshotOldName,
snapshotNewName);
+ }
+
@Override
public void deleteSnapshot(Path path, String snapshotName)
throws IOException {
diff --git
a/hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/BasicRootedOzoneClientAdapterImpl.java
b/hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/BasicRootedOzoneClientAdapterImpl.java
index 8804278612..7a80878549 100644
---
a/hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/BasicRootedOzoneClientAdapterImpl.java
+++
b/hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/BasicRootedOzoneClientAdapterImpl.java
@@ -1254,6 +1254,16 @@ public class BasicRootedOzoneClientAdapterImpl
snapshotName);
}
+ @Override
+ public void renameSnapshot(String pathStr, String snapshotOldName, String
snapshotNewName)
+ throws IOException {
+ OFSPath ofsPath = new OFSPath(pathStr, config);
+ proxy.renameSnapshot(ofsPath.getVolumeName(),
+ ofsPath.getBucketName(),
+ snapshotOldName,
+ snapshotNewName);
+ }
+
@Override
public void deleteSnapshot(String pathStr, String snapshotName)
throws IOException {
diff --git
a/hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/BasicRootedOzoneFileSystem.java
b/hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/BasicRootedOzoneFileSystem.java
index b13d726371..1fcb1554b6 100644
---
a/hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/BasicRootedOzoneFileSystem.java
+++
b/hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/BasicRootedOzoneFileSystem.java
@@ -532,6 +532,12 @@ public class BasicRootedOzoneFileSystem extends FileSystem
{
OM_SNAPSHOT_INDICATOR + OZONE_URI_DELIMITER + snapshot);
}
+ @Override
+ public void renameSnapshot(Path path, String snapshotOldName, String
snapshotNewName)
+ throws IOException {
+ getAdapter().renameSnapshot(pathToKey(path), snapshotOldName,
snapshotNewName);
+ }
+
@Override
public void deleteSnapshot(Path path, String snapshotName)
throws IOException {
diff --git
a/hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/OzoneClientAdapter.java
b/hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/OzoneClientAdapter.java
index c48f1a6366..1a6462c1bb 100644
---
a/hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/OzoneClientAdapter.java
+++
b/hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/OzoneClientAdapter.java
@@ -89,6 +89,8 @@ public interface OzoneClientAdapter {
String createSnapshot(String pathStr, String snapshotName) throws
IOException;
+ void renameSnapshot(String pathStr, String snapshotOldName, String
snapshotNewName) throws IOException;
+
void deleteSnapshot(String pathStr, String snapshotName) throws IOException;
SnapshotDiffReport getSnapshotDiffReport(Path snapshotDir,
diff --git
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/client/ClientProtocolStub.java
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/client/ClientProtocolStub.java
index 7515d991eb..d9b834c318 100644
---
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/client/ClientProtocolStub.java
+++
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/client/ClientProtocolStub.java
@@ -650,6 +650,13 @@ public class ClientProtocolStub implements ClientProtocol {
return "";
}
+ @Override
+ public void renameSnapshot(String volumeName, String bucketName,
+ String snapshotOldName, String snapshotNewName)
+ throws IOException {
+
+ }
+
@Override
public List<OzoneSnapshot> listSnapshot(
String volumeName, String bucketName, String snapshotPrefix,
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/snapshot/RenameSnapshotHandler.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/snapshot/RenameSnapshotHandler.java
new file mode 100644
index 0000000000..63b61b1ec6
--- /dev/null
+++
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/snapshot/RenameSnapshotHandler.java
@@ -0,0 +1,64 @@
+/*
+ * 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.hadoop.ozone.shell.snapshot;
+
+import java.io.IOException;
+import org.apache.hadoop.ozone.OmUtils;
+import org.apache.hadoop.ozone.client.OzoneClient;
+import org.apache.hadoop.ozone.client.OzoneClientException;
+import org.apache.hadoop.ozone.shell.Handler;
+import org.apache.hadoop.ozone.shell.OzoneAddress;
+import org.apache.hadoop.ozone.shell.bucket.BucketUri;
+import picocli.CommandLine;
+
+/**
+ * ozone sh snapshot rename.
+ */
[email protected](name = "rename",
+ description = "Rename a snapshot")
+public class RenameSnapshotHandler extends Handler {
+
+ @CommandLine.Mixin
+ private BucketUri snapshotPath;
+
+ @CommandLine.Parameters(description = "Current snapshot name",
+ index = "1", arity = "1")
+ private String snapshotOldName;
+
+ @CommandLine.Parameters(description = "New snapshot name",
+ index = "2", arity = "1")
+ private String snapshotNewName;
+
+ @Override
+ protected OzoneAddress getAddress() {
+ return snapshotPath.getValue();
+ }
+
+ @Override
+ protected void execute(OzoneClient client, OzoneAddress address) throws
IOException, OzoneClientException {
+ String volumeName = snapshotPath.getValue().getVolumeName();
+ String bucketName = snapshotPath.getValue().getBucketName();
+ OmUtils.validateSnapshotName(snapshotNewName);
+ client.getObjectStore()
+ .renameSnapshot(volumeName, bucketName, snapshotOldName,
snapshotNewName);
+ if (isVerbose()) {
+ out().format("Renamed snapshot from'%s' to %s under '%s/%s'.%n",
+ snapshotOldName, snapshotNewName, volumeName, bucketName);
+ }
+ }
+}
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/snapshot/SnapshotCommands.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/snapshot/SnapshotCommands.java
index cf513b9e91..25a3c1c66f 100644
---
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/snapshot/SnapshotCommands.java
+++
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/snapshot/SnapshotCommands.java
@@ -43,7 +43,8 @@ import picocli.CommandLine.ParentCommand;
ListSnapshotHandler.class,
SnapshotDiffHandler.class,
ListSnapshotDiffHandler.class,
- InfoSnapshotHandler.class
+ InfoSnapshotHandler.class,
+ RenameSnapshotHandler.class
},
mixinStandardHelpOptions = true,
versionProvider = HddsVersionProvider.class)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]