You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ozone.apache.org by na...@apache.org on 2022/08/03 11:41:53 UTC

[ozone] branch HDDS-6517-Snapshot updated: HDDS-6849. OM changes for tracking Snapshot Metadata. (#3582)

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

nanda pushed a commit to branch HDDS-6517-Snapshot
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/HDDS-6517-Snapshot by this push:
     new 2410551d2e HDDS-6849. OM changes for tracking Snapshot Metadata. (#3582)
2410551d2e is described below

commit 2410551d2ecdafb3b8ef044e11249a10d522f345
Author: Neil Joshi <ne...@gmail.com>
AuthorDate: Wed Aug 3 05:41:46 2022 -0600

    HDDS-6849. OM changes for tracking Snapshot Metadata. (#3582)
---
 .../hadoop/ozone/om/helpers/SnapshotInfo.java      | 368 +++++++++++++++++++++
 .../ozone/om/helpers/TestOmSnapshotInfo.java       | 134 ++++++++
 .../src/main/proto/OmClientProtocol.proto          |  27 ++
 .../apache/hadoop/ozone/om/OMMetadataManager.java  |   3 +
 .../ozone/om/codec/OmDBSnapshotInfoCodec.java      |  58 ++++
 .../hadoop/ozone/om/OmMetadataManagerImpl.java     |  23 +-
 .../hadoop/ozone/om/codec/OMDBDefinition.java      |  13 +-
 .../apache/hadoop/ozone/om/TestSnapshotInfo.java   | 102 ++++++
 8 files changed, 725 insertions(+), 3 deletions(-)

diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/SnapshotInfo.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/SnapshotInfo.java
new file mode 100644
index 0000000000..37273f2f59
--- /dev/null
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/SnapshotInfo.java
@@ -0,0 +1,368 @@
+package org.apache.hadoop.ozone.om.helpers;
+
+/*
+ * 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.
+ */
+
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.SnapshotStatusProto;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * This class is used for storing info related to Snapshots.
+ *
+ * Each snapshot created has an associated SnapshotInfo entry
+ * containing the snapshotid, snapshot path,
+ * snapshot checkpoint directory, previous snapshotid
+ * for the snapshot path & global amongst other necessary fields.
+ */
+public final class SnapshotInfo {
+
+  /**
+   * SnapshotStatus enum composed of
+   * active, deleted and reclaimed statues.
+   */
+  public enum SnapshotStatus {
+    SNAPSHOT_ACTIVE,
+    SNAPSHOT_DELETED,
+    SNAPSHOT_RECLAIMED;
+
+    public static final SnapshotStatus DEFAULT = SNAPSHOT_ACTIVE;
+
+    public SnapshotStatusProto toProto() {
+      switch (this) {
+      case SNAPSHOT_ACTIVE:
+        return SnapshotStatusProto.SNAPSHOT_ACTIVE;
+      case SNAPSHOT_DELETED:
+        return SnapshotStatusProto.SNAPSHOT_DELETED;
+      case SNAPSHOT_RECLAIMED:
+        return SnapshotStatusProto.SNAPSHOT_RECLAIMED;
+      default:
+        throw new IllegalStateException(
+            "BUG: missing valid SnapshotStatus, found status=" + this);
+      }
+    }
+
+    public static SnapshotStatus valueOf(SnapshotStatusProto status) {
+      switch (status) {
+      case SNAPSHOT_ACTIVE:
+        return SNAPSHOT_ACTIVE;
+      case SNAPSHOT_DELETED:
+        return SNAPSHOT_DELETED;
+      case SNAPSHOT_RECLAIMED:
+        return SNAPSHOT_RECLAIMED;
+      default:
+        throw new IllegalStateException(
+            "BUG: missing valid SnapshotStatus, found status=" + status);
+      }
+    }
+  };
+
+  private final String snapshotID;  // UUID
+  private String name;
+  private String volumeName;
+  private String bucketName;
+  private SnapshotStatus snapshotStatus;
+  private final long creationTime;
+  private long deletionTime;
+  private String pathPreviousSnapshotID;
+  private String globalPreviousSnapshotID;
+  private String snapshotPath; // snapshot mask
+  private String checkpointDir;
+
+  /**
+   * Private constructor, constructed via builder.
+   * @param snapshotID - Snapshot UUID.
+   * @param name - snapshot name.
+   * @param volumeName - volume name.
+   * @param bucketName - bucket name.
+   * @param snapshotStatus - status: SNAPSHOT_ACTIVE, SNAPSHOT_DELETED,
+   *                      SNAPSHOT_RECLAIMED
+   * @param creationTime - Snapshot creation time.
+   * @param deletionTime - Snapshot deletion time.
+   * @param pathPreviousSnapshotID - Snapshot path previous snapshot id.
+   * @param globalPreviousSnapshotID - Snapshot global previous snapshot id.
+   * @param snapshotPath - Snapshot path, bucket .snapshot path.
+   * @param checkpointDir - Snapshot checkpoint directory.
+   */
+  @SuppressWarnings("checkstyle:ParameterNumber")
+  private SnapshotInfo(String snapshotID,
+                       String name,
+                       String volumeName,
+                       String bucketName,
+                       SnapshotStatus snapshotStatus,
+                       long creationTime,
+                       long deletionTime,
+                       String pathPreviousSnapshotID,
+                       String globalPreviousSnapshotID,
+                       String snapshotPath,
+                       String checkpointDir) {
+    this.snapshotID = snapshotID;
+    this.name = name;
+    this.volumeName = volumeName;
+    this.bucketName = bucketName;
+    this.snapshotStatus = snapshotStatus;
+    this.creationTime = creationTime;
+    this.deletionTime = deletionTime;
+    this.pathPreviousSnapshotID = pathPreviousSnapshotID;
+    this.globalPreviousSnapshotID = globalPreviousSnapshotID;
+    this.snapshotPath = snapshotPath;
+    this.checkpointDir = checkpointDir;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public void setVolumeName(String volumeName) {
+    this.volumeName = volumeName;
+  }
+
+  public void setBucketName(String bucketName) {
+    this.bucketName = bucketName;
+  }
+
+  public void setSnapshotStatus(SnapshotStatus snapshotStatus) {
+    this.snapshotStatus = snapshotStatus;
+  }
+
+  public void setDeletionTime(long delTime) {
+    this.deletionTime = delTime;
+  }
+
+  public void setPathPreviousSnapshotID(String pathPreviousSnapshotID) {
+    this.pathPreviousSnapshotID = pathPreviousSnapshotID;
+  }
+
+  public void setGlobalPreviousSnapshotID(String globalPreviousSnapshotID) {
+    this.globalPreviousSnapshotID = globalPreviousSnapshotID;
+  }
+
+  public void setSnapshotPath(String snapshotPath) {
+    this.snapshotPath = snapshotPath;
+  }
+
+  public void setCheckpointDir(String checkpointDir) {
+    this.checkpointDir = checkpointDir;
+  }
+
+  public String getSnapshotID() {
+    return snapshotID;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public String getVolumeName() {
+    return volumeName;
+  }
+
+  public String getBucketName() {
+    return bucketName;
+  }
+
+  public SnapshotStatus getSnapshotStatus() {
+    return snapshotStatus;
+  }
+
+  public long getCreationTime() {
+    return creationTime;
+  }
+
+  public long getDeletionTime() {
+    return deletionTime;
+  }
+
+  public String getPathPreviousSnapshotID() {
+    return pathPreviousSnapshotID;
+  }
+
+  public String getGlobalPreviousSnapshotID() {
+    return globalPreviousSnapshotID;
+  }
+
+  public String getSnapshotPath() {
+    return snapshotPath;
+  }
+
+  public String getCheckpointDir() {
+    return checkpointDir;
+  }
+
+  public static org.apache.hadoop.ozone.om.helpers.SnapshotInfo.Builder
+      newBuilder() {
+    return new org.apache.hadoop.ozone.om.helpers.SnapshotInfo.Builder();
+  }
+
+  public SnapshotInfo.Builder toBuilder() {
+    return new SnapshotInfo.Builder()
+        .setSnapshotID(snapshotID)
+        .setName(name)
+        .setVolumeName(volumeName)
+        .setBucketName(bucketName)
+        .setSnapshotStatus(snapshotStatus)
+        .setCreationTime(creationTime)
+        .setDeletionTime(deletionTime)
+        .setPathPreviousSnapshotID(pathPreviousSnapshotID)
+        .setGlobalPreviousSnapshotID(globalPreviousSnapshotID)
+        .setSnapshotPath(snapshotPath)
+        .setCheckpointDir(checkpointDir);
+  }
+
+  /**
+   * Builder of SnapshotInfo.
+   */
+  public static class Builder {
+    private String snapshotID;
+    private String name;
+    private String volumeName;
+    private String bucketName;
+    private SnapshotStatus snapshotStatus;
+    private long creationTime;
+    private long deletionTime;
+    private String pathPreviousSnapshotID;
+    private String globalPreviousSnapshotID;
+    private String snapshotPath;
+    private String checkpointDir;
+
+    public Builder() {
+      // default values
+      this.snapshotStatus = SnapshotStatus.DEFAULT;
+    }
+
+    public Builder setSnapshotID(String snapshotID) {
+      this.snapshotID = snapshotID;
+      return this;
+    }
+
+    public Builder setName(String name) {
+      this.name = name;
+      return this;
+    }
+
+    public Builder setVolumeName(String volumeName) {
+      this.volumeName = volumeName;
+      return this;
+    }
+
+    public Builder setBucketName(String bucketName) {
+      this.bucketName = bucketName;
+      return this;
+    }
+
+    public Builder setSnapshotStatus(SnapshotStatus snapshotStatus) {
+      this.snapshotStatus = snapshotStatus;
+      return this;
+    }
+
+    public Builder setCreationTime(long crTime) {
+      this.creationTime = crTime;
+      return this;
+    }
+
+    public Builder setDeletionTime(long delTime) {
+      this.deletionTime = delTime;
+      return this;
+    }
+
+    public Builder setPathPreviousSnapshotID(String pathPreviousSnapshotID) {
+      this.pathPreviousSnapshotID = pathPreviousSnapshotID;
+      return this;
+    }
+
+    public Builder setGlobalPreviousSnapshotID(
+        String globalPreviousSnapshotID) {
+      this.globalPreviousSnapshotID = globalPreviousSnapshotID;
+      return this;
+    }
+
+    public Builder setSnapshotPath(String snapshotPath) {
+      this.snapshotPath = snapshotPath;
+      return this;
+    }
+
+    public Builder setCheckpointDir(String checkpointDir) {
+      this.checkpointDir = checkpointDir;
+      return this;
+    }
+
+    public SnapshotInfo build() {
+      Preconditions.checkNotNull(name);
+      return new SnapshotInfo(
+          snapshotID,
+          name,
+          volumeName,
+          bucketName,
+          snapshotStatus,
+          creationTime,
+          deletionTime,
+          pathPreviousSnapshotID,
+          globalPreviousSnapshotID,
+          snapshotPath,
+          checkpointDir
+      );
+    }
+  }
+
+  /**
+   * Creates SnapshotInfo protobuf from SnapshotInfo.
+   */
+  public OzoneManagerProtocolProtos.SnapshotInfo getProtobuf() {
+    OzoneManagerProtocolProtos.SnapshotInfo.Builder sib =
+        OzoneManagerProtocolProtos.SnapshotInfo.newBuilder()
+        .setSnapshotID(snapshotID)
+        .setName(name)
+        .setVolumeName(volumeName)
+        .setBucketName(bucketName)
+        .setSnapshotStatus(snapshotStatus.toProto())
+        .setCreationTime(creationTime)
+        .setDeletionTime(deletionTime)
+        .setPathPreviousSnapshotID(pathPreviousSnapshotID)
+        .setGlobalPreviousSnapshotID(globalPreviousSnapshotID)
+        .setSnapshotPath(snapshotPath)
+        .setCheckpointDir(checkpointDir);
+    return sib.build();
+  }
+
+  /**
+   * Parses SnapshotInfo protobuf and creates SnapshotInfo.
+   * @param snapshotInfo protobuf
+   * @return instance of SnapshotInfo
+   */
+  public static SnapshotInfo getFromProtobuf(
+      OzoneManagerProtocolProtos.SnapshotInfo snapshotInfoProto) {
+    SnapshotInfo.Builder osib = SnapshotInfo.newBuilder()
+        .setSnapshotID(snapshotInfoProto.getSnapshotID())
+        .setName(snapshotInfoProto.getName())
+        .setVolumeName(snapshotInfoProto.getVolumeName())
+        .setBucketName(snapshotInfoProto.getBucketName())
+        .setSnapshotStatus(SnapshotStatus.valueOf(snapshotInfoProto
+            .getSnapshotStatus()))
+        .setCreationTime(snapshotInfoProto.getCreationTime())
+        .setDeletionTime(snapshotInfoProto.getDeletionTime())
+        .setPathPreviousSnapshotID(snapshotInfoProto.
+            getPathPreviousSnapshotID())
+        .setGlobalPreviousSnapshotID(snapshotInfoProto.
+            getGlobalPreviousSnapshotID())
+        .setSnapshotPath(snapshotInfoProto.getSnapshotPath())
+        .setCheckpointDir(snapshotInfoProto.getCheckpointDir());
+
+    return osib.build();
+  }
+}
diff --git a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmSnapshotInfo.java b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmSnapshotInfo.java
new file mode 100644
index 0000000000..d70522ba95
--- /dev/null
+++ b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmSnapshotInfo.java
@@ -0,0 +1,134 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.helpers;
+
+import org.apache.hadoop.ozone.om.helpers.SnapshotInfo.SnapshotStatus;
+
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.SnapshotStatusProto;
+
+import org.apache.hadoop.util.Time;
+import org.junit.Test;
+import org.junit.Assert;
+
+import java.util.UUID;
+
+/**
+ * Tests SnapshotInfo metadata data structure holding state info for
+ * object storage snapshots.
+ */
+public class TestOmSnapshotInfo {
+
+  private static final String SNAPSHOT_ID = UUID.randomUUID().toString();
+  private static final String NAME = "snapshot1";
+  private static final String VOLUME_NAME = "vol1";
+  private static final String BUCKET_NAME = "bucket1";
+  private static final SnapshotStatus SNAPSHOT_STATUS =
+      SnapshotStatus.SNAPSHOT_ACTIVE;
+  private static final long CREATION_TIME = Time.now();
+  private static final long DELETION_TIME = -1;
+  private static final String PATH_PREVIOUS_SNAPSHOT_ID =
+      UUID.randomUUID().toString();
+  private static final String GLOBAL_PREVIOUS_SNAPSHOT_ID =
+      PATH_PREVIOUS_SNAPSHOT_ID;
+  private static final String SNAPSHOT_PATH = "test/path";
+  private static final String CHECKPOINT_DIR = "checkpoint.testdir";
+
+  private SnapshotInfo createSnapshotInfo() {
+    return new SnapshotInfo.Builder()
+        .setSnapshotID(SNAPSHOT_ID)
+        .setName(NAME)
+        .setVolumeName(VOLUME_NAME)
+        .setBucketName(BUCKET_NAME)
+        .setSnapshotStatus(SNAPSHOT_STATUS)
+        .setCreationTime(CREATION_TIME)
+        .setDeletionTime(DELETION_TIME)
+        .setPathPreviousSnapshotID(PATH_PREVIOUS_SNAPSHOT_ID)
+        .setGlobalPreviousSnapshotID(GLOBAL_PREVIOUS_SNAPSHOT_ID)
+        .setSnapshotPath(SNAPSHOT_PATH)
+        .setCheckpointDir(CHECKPOINT_DIR)
+        .build();
+  }
+
+  private OzoneManagerProtocolProtos.SnapshotInfo createSnapshotInfoProto() {
+    return OzoneManagerProtocolProtos.SnapshotInfo.newBuilder()
+        .setSnapshotID(SNAPSHOT_ID)
+        .setName(NAME)
+        .setVolumeName(VOLUME_NAME)
+        .setBucketName(BUCKET_NAME)
+        .setSnapshotStatus(SnapshotStatusProto.SNAPSHOT_ACTIVE)
+        .setCreationTime(CREATION_TIME)
+        .setDeletionTime(DELETION_TIME)
+        .setPathPreviousSnapshotID(PATH_PREVIOUS_SNAPSHOT_ID)
+        .setGlobalPreviousSnapshotID(GLOBAL_PREVIOUS_SNAPSHOT_ID)
+        .setSnapshotPath(SNAPSHOT_PATH)
+        .setCheckpointDir(CHECKPOINT_DIR)
+        .build();
+  }
+
+  @Test
+  public void testSnapshotStatusProtoToObject() {
+    OzoneManagerProtocolProtos.SnapshotInfo snapshotInfoEntry =
+        createSnapshotInfoProto();
+    Assert.assertEquals(SNAPSHOT_STATUS,
+        SnapshotStatus.valueOf(snapshotInfoEntry.getSnapshotStatus()));
+  }
+
+  @Test
+  public void testSnapshotInfoToProto() {
+    SnapshotInfo snapshotInfo = createSnapshotInfo();
+    OzoneManagerProtocolProtos.SnapshotInfo snapshotInfoEntryExpected =
+        createSnapshotInfoProto();
+
+    OzoneManagerProtocolProtos.SnapshotInfo snapshotInfoEntryActual =
+        snapshotInfo.getProtobuf();
+    Assert.assertEquals(snapshotInfoEntryExpected.getSnapshotID(),
+        snapshotInfoEntryActual.getSnapshotID());
+    Assert.assertEquals(snapshotInfoEntryExpected.getName(),
+        snapshotInfoEntryActual.getName());
+    Assert.assertEquals(snapshotInfoEntryExpected.getVolumeName(),
+        snapshotInfoEntryActual.getVolumeName());
+    Assert.assertEquals(snapshotInfoEntryExpected.getBucketName(),
+        snapshotInfoEntryActual.getBucketName());
+    Assert.assertEquals(snapshotInfoEntryExpected.getSnapshotStatus(),
+        snapshotInfoEntryActual.getSnapshotStatus());
+
+  }
+
+  @Test
+  public void testSnapshotInfoProtoToSnapshotInfo() {
+    SnapshotInfo snapshotInfoExpected = createSnapshotInfo();
+    OzoneManagerProtocolProtos.SnapshotInfo snapshotInfoEntry =
+        createSnapshotInfoProto();
+
+    SnapshotInfo snapshotInfoActual = SnapshotInfo
+        .getFromProtobuf(snapshotInfoEntry);
+    Assert.assertEquals(snapshotInfoExpected.getSnapshotID(),
+        snapshotInfoActual.getSnapshotID());
+    Assert.assertEquals(snapshotInfoExpected.getName(),
+        snapshotInfoActual.getName());
+    Assert.assertEquals(snapshotInfoExpected.getVolumeName(),
+        snapshotInfoActual.getVolumeName());
+    Assert.assertEquals(snapshotInfoExpected.getBucketName(),
+        snapshotInfoActual.getBucketName());
+    Assert.assertEquals(snapshotInfoExpected.getSnapshotStatus(),
+        snapshotInfoActual.getSnapshotStatus());
+
+  }
+}
diff --git a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
index 4b6a8dd342..6f6467247e 100644
--- a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
+++ b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
@@ -711,6 +711,33 @@ message PrefixInfo {
     optional uint64 updateID = 5;
 }
 
+/**
+ * SnapshotStatus - snapshot states.
+ * Snapshot in one of : active, deleted, or reclaimed state
+ */
+enum SnapshotStatusProto {
+  SNAPSHOT_ACTIVE = 1;
+  SNAPSHOT_DELETED = 2;
+  SNAPSHOT_RECLAIMED = 3;
+}
+
+/**
+ * SnapshotInfo table entry for Bucket/Volume snapshot metadata
+ */
+message SnapshotInfo {
+  required string snapshotID = 1;
+  required string name = 2;
+  required string volumeName = 3;
+  required string bucketName = 4;
+  required SnapshotStatusProto snapshotStatus = 5;
+  required uint64 creationTime = 6;
+  required uint64 deletionTime = 7;
+  required string pathPreviousSnapshotID = 8;
+  required string globalPreviousSnapshotID = 9;
+  required string snapshotPath = 10;
+  required string checkpointDir = 11;
+ }
+
 message OzoneObj {
   enum ObjectType {
     VOLUME = 1;
diff --git a/hadoop-ozone/interface-storage/src/main/java/org/apache/hadoop/ozone/om/OMMetadataManager.java b/hadoop-ozone/interface-storage/src/main/java/org/apache/hadoop/ozone/om/OMMetadataManager.java
index 67356a88f3..d92110775a 100644
--- a/hadoop-ozone/interface-storage/src/main/java/org/apache/hadoop/ozone/om/OMMetadataManager.java
+++ b/hadoop-ozone/interface-storage/src/main/java/org/apache/hadoop/ozone/om/OMMetadataManager.java
@@ -40,6 +40,7 @@ import org.apache.hadoop.ozone.om.helpers.OmDBTenantState;
 import org.apache.hadoop.ozone.om.helpers.OmVolumeArgs;
 import org.apache.hadoop.ozone.om.helpers.RepeatedOmKeyInfo;
 import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
+import org.apache.hadoop.ozone.om.helpers.SnapshotInfo;
 import org.apache.hadoop.ozone.om.helpers.BucketLayout;
 import org.apache.hadoop.ozone.om.lock.OzoneManagerLock;
 import org.apache.hadoop.hdds.utils.TransactionInfo;
@@ -370,6 +371,8 @@ public interface OMMetadataManager extends DBStoreHAManager {
 
   Table<String, OmDBTenantState> getTenantStateTable();
 
+  Table<String, SnapshotInfo> getSnapshotInfoTable();
+
   /**
    * Gets the OM Meta table.
    * @return meta table reference.
diff --git a/hadoop-ozone/interface-storage/src/main/java/org/apache/hadoop/ozone/om/codec/OmDBSnapshotInfoCodec.java b/hadoop-ozone/interface-storage/src/main/java/org/apache/hadoop/ozone/om/codec/OmDBSnapshotInfoCodec.java
new file mode 100644
index 0000000000..d7da0daf96
--- /dev/null
+++ b/hadoop-ozone/interface-storage/src/main/java/org/apache/hadoop/ozone/om/codec/OmDBSnapshotInfoCodec.java
@@ -0,0 +1,58 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.codec;
+
+import org.apache.hadoop.hdds.utils.db.Codec;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
+import org.apache.hadoop.ozone.om.helpers.SnapshotInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Codec to encode SnapshotInfo as byte array.
+ */
+public class OmDBSnapshotInfoCodec implements Codec<SnapshotInfo> {
+  private static final Logger LOG =
+      LoggerFactory.getLogger(OmDBSnapshotInfoCodec.class);
+
+  @Override
+  public byte[] toPersistedFormat(SnapshotInfo object) throws IOException {
+    checkNotNull(object, "Null object can't be converted to byte array.");
+    return object.getProtobuf().toByteArray();
+  }
+
+  @Override
+  public SnapshotInfo fromPersistedFormat(byte[] rawData)
+      throws IOException {
+    checkNotNull(rawData, "Null byte array can't be converted to " +
+        "real object.");
+    return SnapshotInfo.getFromProtobuf(
+        OzoneManagerProtocolProtos.SnapshotInfo.parseFrom(rawData));
+  }
+
+  @Override
+  public SnapshotInfo copyObject(SnapshotInfo object) {
+    // Note: Not really a "copy". from OMDBSnapshotInfoCodec
+    return object;
+  }
+}
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java
index 8e9ddd7c01..792696a4ed 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java
@@ -58,6 +58,7 @@ import org.apache.hadoop.ozone.om.codec.OmDBTenantStateCodec;
 import org.apache.hadoop.ozone.om.codec.OmVolumeArgsCodec;
 import org.apache.hadoop.ozone.om.codec.RepeatedOmKeyInfoCodec;
 import org.apache.hadoop.ozone.om.codec.S3SecretValueCodec;
+import org.apache.hadoop.ozone.om.codec.OmDBSnapshotInfoCodec;
 import org.apache.hadoop.ozone.om.codec.TokenIdentifierCodec;
 import org.apache.hadoop.ozone.om.codec.UserVolumeInfoCodec;
 import org.apache.hadoop.ozone.om.exceptions.OMException;
@@ -76,6 +77,7 @@ import org.apache.hadoop.ozone.om.helpers.OmVolumeArgs;
 import org.apache.hadoop.ozone.om.helpers.OzoneFSUtils;
 import org.apache.hadoop.ozone.om.helpers.RepeatedOmKeyInfo;
 import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
+import org.apache.hadoop.ozone.om.helpers.SnapshotInfo;
 import org.apache.hadoop.ozone.om.helpers.BucketLayout;
 import org.apache.hadoop.ozone.om.lock.OzoneManagerLock;
 import org.apache.hadoop.hdds.utils.TransactionInfo;
@@ -194,6 +196,7 @@ public class OmMetadataManagerImpl implements OMMetadataManager {
   public static final String PRINCIPAL_TO_ACCESS_IDS_TABLE =
       "principalToAccessIdsTable";
   public static final String TENANT_STATE_TABLE = "tenantStateTable";
+  public static final String SNAPSHOT_INFO_TABLE = "snapshotInfoTable";
 
   static final String[] ALL_TABLES = new String[] {
       USER_TABLE,
@@ -214,7 +217,8 @@ public class OmMetadataManagerImpl implements OMMetadataManager {
       META_TABLE,
       TENANT_ACCESS_ID_TABLE,
       PRINCIPAL_TO_ACCESS_IDS_TABLE,
-      TENANT_STATE_TABLE
+      TENANT_STATE_TABLE,
+      SNAPSHOT_INFO_TABLE
   };
 
   private DBStore store;
@@ -241,6 +245,7 @@ public class OmMetadataManagerImpl implements OMMetadataManager {
   private Table tenantAccessIdTable;
   private Table principalToAccessIdsTable;
   private Table tenantStateTable;
+  private Table snapshotInfoTable;
 
   private boolean isRatisEnabled;
   private boolean ignorePipelineinKey;
@@ -443,6 +448,7 @@ public class OmMetadataManagerImpl implements OMMetadataManager {
         .addTable(TENANT_ACCESS_ID_TABLE)
         .addTable(PRINCIPAL_TO_ACCESS_IDS_TABLE)
         .addTable(TENANT_STATE_TABLE)
+        .addTable(SNAPSHOT_INFO_TABLE)
         .addCodec(OzoneTokenIdentifier.class, new TokenIdentifierCodec())
         .addCodec(OmKeyInfo.class, new OmKeyInfoCodec(true))
         .addCodec(RepeatedOmKeyInfo.class,
@@ -458,7 +464,9 @@ public class OmMetadataManagerImpl implements OMMetadataManager {
         .addCodec(OmDBTenantState.class, new OmDBTenantStateCodec())
         .addCodec(OmDBAccessIdInfo.class, new OmDBAccessIdInfoCodec())
         .addCodec(OmDBUserPrincipalInfo.class,
-            new OmDBUserPrincipalInfoCodec());
+            new OmDBUserPrincipalInfoCodec())
+        .addCodec(SnapshotInfo.class,
+            new OmDBSnapshotInfoCodec());
   }
 
   /**
@@ -550,6 +558,12 @@ public class OmMetadataManagerImpl implements OMMetadataManager {
     tenantStateTable = this.store.getTable(TENANT_STATE_TABLE,
         String.class, OmDBTenantState.class);
     checkTableStatus(tenantStateTable, TENANT_STATE_TABLE);
+
+    // path -> snapshotInfo (snapshot info for snapshot)
+    snapshotInfoTable = this.store.getTable(SNAPSHOT_INFO_TABLE,
+        String.class, SnapshotInfo.class);
+    checkTableStatus(snapshotInfoTable, SNAPSHOT_INFO_TABLE);
+
   }
 
   /**
@@ -1400,6 +1414,11 @@ public class OmMetadataManagerImpl implements OMMetadataManager {
     return tenantStateTable;
   }
 
+  @Override
+  public Table<String, SnapshotInfo> getSnapshotInfoTable() {
+    return snapshotInfoTable;
+  }
+
   /**
    * Update store used by subclass.
    *
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/codec/OMDBDefinition.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/codec/OMDBDefinition.java
index 0df19af147..aa40cc87ad 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/codec/OMDBDefinition.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/codec/OMDBDefinition.java
@@ -36,6 +36,7 @@ import org.apache.hadoop.ozone.om.helpers.RepeatedOmKeyInfo;
 import org.apache.hadoop.ozone.om.helpers.OmMultipartKeyInfo;
 import org.apache.hadoop.ozone.om.helpers.OmPrefixInfo;
 import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
+import org.apache.hadoop.ozone.om.helpers.SnapshotInfo;
 import org.apache.hadoop.ozone.om.helpers.OmDirectoryInfo;
 
 import org.apache.hadoop.hdds.utils.TransactionInfo;
@@ -219,6 +220,15 @@ public class OMDBDefinition implements DBDefinition {
 
   // End tables for S3 multi-tenancy
 
+  public static final DBColumnFamilyDefinition<String, SnapshotInfo>
+      SNAPSHOT_INFO_TABLE =
+      new DBColumnFamilyDefinition<>(
+          OmMetadataManagerImpl.SNAPSHOT_INFO_TABLE,
+          String.class,  // snapshot path
+          new StringCodec(),
+          SnapshotInfo.class,
+          new OmDBSnapshotInfoCodec());
+
   @Override
   public String getName() {
     return OzoneConsts.OM_DB_NAME;
@@ -237,7 +247,8 @@ public class OMDBDefinition implements DBDefinition {
         S3_SECRET_TABLE, TRANSACTION_INFO_TABLE, DIRECTORY_TABLE,
         FILE_TABLE, OPEN_FILE_TABLE, DELETED_DIR_TABLE, META_TABLE,
         TENANT_ACCESS_ID_TABLE,
-        PRINCIPAL_TO_ACCESS_IDS_TABLE, TENANT_STATE_TABLE};
+        PRINCIPAL_TO_ACCESS_IDS_TABLE, TENANT_STATE_TABLE,
+        SNAPSHOT_INFO_TABLE};
   }
 }
 
diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestSnapshotInfo.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestSnapshotInfo.java
new file mode 100644
index 0000000000..bafe1d33eb
--- /dev/null
+++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestSnapshotInfo.java
@@ -0,0 +1,102 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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;
+
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.ozone.om.helpers.SnapshotInfo;
+import org.apache.hadoop.ozone.om.helpers.SnapshotInfo.SnapshotStatus;
+import org.apache.hadoop.hdds.utils.db.Table;
+import org.apache.hadoop.util.Time;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.util.UUID;
+
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_DB_DIRS;
+
+/**
+ * Tests SnapshotInfo om database table for Ozone object storage snapshots.
+ */
+public class TestSnapshotInfo {
+
+  private OMMetadataManager omMetadataManager;
+  private static final String EXPECTED_SNAPSHOT_KEY = "snapshot1";
+  private static final String EXPECTED_SNAPSHOT_ID =
+      UUID.randomUUID().toString();
+  private static final String EXPECTED_PREVIOUS_SNAPSHOT_ID =
+      UUID.randomUUID().toString();
+
+  @Rule
+  public TemporaryFolder folder = new TemporaryFolder();
+
+  @Before
+  public void setup() throws Exception {
+    OzoneConfiguration conf = new OzoneConfiguration();
+    conf.set(OZONE_OM_DB_DIRS,
+        folder.getRoot().getAbsolutePath());
+    omMetadataManager = new OmMetadataManagerImpl(conf);
+  }
+
+  private SnapshotInfo createSnapshotInfo() {
+    return new SnapshotInfo.Builder()
+        .setSnapshotID(EXPECTED_SNAPSHOT_ID)
+        .setName("snapshot1")
+        .setVolumeName("vol1")
+        .setBucketName("bucket1")
+        .setSnapshotStatus(SnapshotStatus.SNAPSHOT_ACTIVE)
+        .setCreationTime(Time.now())
+        .setDeletionTime(-1L)
+        .setPathPreviousSnapshotID(EXPECTED_PREVIOUS_SNAPSHOT_ID)
+        .setGlobalPreviousSnapshotID(EXPECTED_PREVIOUS_SNAPSHOT_ID)
+        .setSnapshotPath("test/path")
+        .setCheckpointDir("checkpoint.testdir")
+        .build();
+  }
+
+  @Test
+  public void testTableExists() throws Exception {
+    Table<String, SnapshotInfo> snapshotInfo =
+        omMetadataManager.getSnapshotInfoTable();
+    Assert.assertTrue(snapshotInfo.isEmpty());
+  }
+
+  @Test
+  public void testAddNewSnapshot() throws Exception {
+    Table<String, SnapshotInfo> snapshotInfo =
+        omMetadataManager.getSnapshotInfoTable();
+    snapshotInfo.put(EXPECTED_SNAPSHOT_KEY, createSnapshotInfo());
+    Assert.assertEquals(EXPECTED_SNAPSHOT_ID,
+        snapshotInfo.get(EXPECTED_SNAPSHOT_KEY).getSnapshotID());
+  }
+
+  @Test
+  public void testDeleteSnapshotInfo() throws Exception {
+    Table<String, SnapshotInfo> snapshotInfo =
+        omMetadataManager.getSnapshotInfoTable();
+
+    Assert.assertFalse(snapshotInfo.isExist(EXPECTED_SNAPSHOT_KEY));
+    snapshotInfo.put(EXPECTED_SNAPSHOT_KEY, createSnapshotInfo());
+    Assert.assertTrue(snapshotInfo.isExist(EXPECTED_SNAPSHOT_KEY));
+    snapshotInfo.delete(EXPECTED_SNAPSHOT_KEY);
+    Assert.assertFalse(snapshotInfo.isExist(EXPECTED_SNAPSHOT_KEY));
+  }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@ozone.apache.org
For additional commands, e-mail: commits-help@ozone.apache.org