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

sumitagrawal 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 9a164854f99 HDDS-11314. OM system audit for internal request and 
leader change (#9092)
9a164854f99 is described below

commit 9a164854f993b79c5e2bea9839692fac52e7b9f2
Author: Sarveksha Yeshavantha Raju 
<[email protected]>
AuthorDate: Mon Oct 13 14:54:59 2025 +0530

    HDDS-11314. OM system audit for internal request and leader change (#9092)
---
 .../hadoop/ozone/om/TestOMRatisSnapshots.java      |  8 +++
 .../apache/hadoop/ozone/audit/OMSystemAction.java  |  3 +
 .../org/apache/hadoop/ozone/om/OzoneManager.java   | 13 +++++
 .../ozone/om/ratis/OzoneManagerStateMachine.java   | 21 +++++++
 .../om/request/key/OMOpenKeysDeleteRequest.java    | 29 +++++++++-
 .../request/key/TestOMOpenKeysDeleteRequest.java   | 65 ++++++++++++++++++++++
 6 files changed, 137 insertions(+), 2 deletions(-)

diff --git 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMRatisSnapshots.java
 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMRatisSnapshots.java
index 404a32d2178..a1de8fc377a 100644
--- 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMRatisSnapshots.java
+++ 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMRatisSnapshots.java
@@ -67,6 +67,7 @@
 import org.apache.hadoop.hdds.utils.db.RDBStore;
 import org.apache.hadoop.ozone.MiniOzoneCluster;
 import org.apache.hadoop.ozone.MiniOzoneHAClusterImpl;
+import org.apache.hadoop.ozone.audit.AuditLogTestUtils;
 import org.apache.hadoop.ozone.client.BucketArgs;
 import org.apache.hadoop.ozone.client.ObjectStore;
 import org.apache.hadoop.ozone.client.OzoneBucket;
@@ -123,6 +124,7 @@ public class TestOMRatisSnapshots {
   private static final BucketLayout TEST_BUCKET_LAYOUT =
       BucketLayout.OBJECT_STORE;
   private OzoneClient client;
+  private GenericTestUtils.PrintStreamCapturer output;
 
   /**
    * Create a MiniOzoneCluster for testing. The cluster initially has one
@@ -145,6 +147,8 @@ public void init(TestInfo testInfo) throws Exception {
         testInfo.getTestMethod().get().getName()
             .equals("testInstallSnapshot")) {
       snapshotThreshold = SNAPSHOT_THRESHOLD * 10;
+      AuditLogTestUtils.enableAuditLog();
+      output = GenericTestUtils.captureOut();
     }
     conf.setLong(
         OMConfigKeys.OZONE_OM_RATIS_SNAPSHOT_AUTO_TRIGGER_THRESHOLD_KEY,
@@ -285,6 +289,8 @@ public void testInstallSnapshot(@TempDir Path tempDir) 
throws Exception {
 
     assertLogCapture(logCapture,
         "Install Checkpoint is finished");
+    assertThat(output.get()).contains("op=DB_CHECKPOINT_INSTALL 
{\"leaderId\":\"" + leaderOMNodeId + "\",\"term\":\"" +
+        leaderOMSnapshotTermIndex, "\"lastAppliedIndex\":\"" + 
followerOMLastAppliedIndex);
 
     // Read & Write after snapshot installed.
     List<String> newKeys = writeKeys(1);
@@ -308,6 +314,8 @@ public void testInstallSnapshot(@TempDir Path tempDir) 
throws Exception {
     // Confirm that there was no overlap of sst files
     // between the individual tarballs.
     assertEquals(sstFileUnion.size(), sstFileCount);
+
+    output.reset();
   }
 
   private void checkSnapshot(OzoneManager leaderOM, OzoneManager followerOM,
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/audit/OMSystemAction.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/audit/OMSystemAction.java
index 5da25966a28..3fbceeeae9c 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/audit/OMSystemAction.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/audit/OMSystemAction.java
@@ -23,6 +23,9 @@
  */
 public enum OMSystemAction implements AuditAction {
   STARTUP,
+  LEADER_CHANGE,
+  OPEN_KEY_CLEANUP,
+  DB_CHECKPOINT_INSTALL,
   DIRECTORY_DELETION,
   KEY_DELETION,
   SNAPSHOT_MOVE_TABLE_KEYS,
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
index 75dd29d67bf..56e51cf4026 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
@@ -362,6 +362,10 @@ public final class OzoneManager extends 
ServiceRuntimeInfoImpl
   private static final AuditLogger SYSTEMAUDIT = new AuditLogger(
       AuditLoggerType.OMSYSTEMLOGGER);
 
+  private static final String AUDIT_PARAM_LEADER_ID = "leaderId";
+  private static final String AUDIT_PARAM_TERM = "term";
+  private static final String AUDIT_PARAM_LAST_APPLIED_INDEX = 
"lastAppliedIndex";
+
   private static final String OM_DAEMON = "om";
   private static final String NO_LEADER_ERROR_MESSAGE =
           "There is no leader among the Ozone Manager servers. If this message 
" +
@@ -4136,6 +4140,7 @@ TermIndex installCheckpoint(String leaderId, Path 
checkpointLocation,
         exitManager.exitSystem(1, errorMsg, e, LOG);
       }
     }
+    buildDBCheckpointInstallAuditLog(leaderId, term, lastAppliedIndex);
 
     // Delete the backup DB
     try {
@@ -4162,6 +4167,14 @@ TermIndex installCheckpoint(String leaderId, Path 
checkpointLocation,
     return newTermIndex;
   }
 
+  private void buildDBCheckpointInstallAuditLog(String leaderId, long term, 
long lastAppliedIndex) {
+    Map<String, String> auditMap = new LinkedHashMap<>();
+    auditMap.put(AUDIT_PARAM_LEADER_ID, leaderId);
+    auditMap.put(AUDIT_PARAM_TERM, String.valueOf(term));
+    auditMap.put(AUDIT_PARAM_LAST_APPLIED_INDEX, 
String.valueOf(lastAppliedIndex));
+    
SYSTEMAUDIT.logWriteSuccess(buildAuditMessageForSuccess(OMSystemAction.DB_CHECKPOINT_INSTALL,
 auditMap));
+  }
+
   private void stopTrashEmptier() {
     if (this.emptier != null) {
       emptier.interrupt();
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerStateMachine.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerStateMachine.java
index a304e39dec3..a3ad217ceef 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerStateMachine.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerStateMachine.java
@@ -26,7 +26,9 @@
 import com.google.common.util.concurrent.ThreadFactoryBuilder;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ThreadFactory;
@@ -34,6 +36,9 @@
 import java.util.concurrent.atomic.AtomicInteger;
 import org.apache.hadoop.hdds.utils.NettyMetrics;
 import org.apache.hadoop.hdds.utils.TransactionInfo;
+import org.apache.hadoop.ozone.audit.AuditLogger;
+import org.apache.hadoop.ozone.audit.AuditLoggerType;
+import org.apache.hadoop.ozone.audit.OMSystemAction;
 import org.apache.hadoop.ozone.om.OMConfigKeys;
 import org.apache.hadoop.ozone.om.OzoneManager;
 import org.apache.hadoop.ozone.om.OzoneManagerPrepareState;
@@ -85,6 +90,10 @@ public class OzoneManagerStateMachine extends 
BaseStateMachine {
 
   private static final Logger LOG =
       LoggerFactory.getLogger(OzoneManagerStateMachine.class);
+  private static final AuditLogger AUDIT = new 
AuditLogger(AuditLoggerType.OMSYSTEMLOGGER);
+  private static final String AUDIT_PARAM_PREVIOUS_LEADER = "previousLeader";
+  private static final String AUDIT_PARAM_NEW_LEADER = "newLeader";
+  private RaftPeerId previousLeaderId = null;
   private final SimpleStateMachineStorage storage =
       new SimpleStateMachineStorage();
   private final OzoneManager ozoneManager;
@@ -169,8 +178,20 @@ public void notifyLeaderChanged(RaftGroupMemberId 
groupMemberId,
       // warmup cache
       ozoneManager.initializeEdekCache(ozoneManager.getConfiguration());
     }
+    // Store the previous leader before updating
+    RaftPeerId actualPreviousLeader = previousLeaderId;
+
+    // Update the previous leader for next time
+    previousLeaderId = newLeaderId;
     // Initialize OMHAMetrics
     ozoneManager.omHAMetricsInit(newLeaderId.toString());
+
+    Map<String, String> auditParams = new LinkedHashMap<>();
+    auditParams.put(AUDIT_PARAM_PREVIOUS_LEADER,
+        actualPreviousLeader != null ? String.valueOf(actualPreviousLeader) : 
"NONE");
+    auditParams.put(AUDIT_PARAM_NEW_LEADER, String.valueOf(newLeaderId));
+    
AUDIT.logWriteSuccess(ozoneManager.buildAuditMessageForSuccess(OMSystemAction.LEADER_CHANGE,
 auditParams));
+
     LOG.info("{}: leader changed to {}", groupMemberId, newLeaderId);
   }
 
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/key/OMOpenKeysDeleteRequest.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/key/OMOpenKeysDeleteRequest.java
index 45606468477..3997f1a6778 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/key/OMOpenKeysDeleteRequest.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/key/OMOpenKeysDeleteRequest.java
@@ -21,12 +21,17 @@
 
 import java.io.IOException;
 import java.nio.file.InvalidPathException;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.hadoop.hdds.utils.db.cache.CacheKey;
 import org.apache.hadoop.hdds.utils.db.cache.CacheValue;
+import org.apache.hadoop.ozone.audit.AuditLogger;
+import org.apache.hadoop.ozone.audit.AuditLoggerType;
+import org.apache.hadoop.ozone.audit.OMSystemAction;
 import org.apache.hadoop.ozone.om.OMMetadataManager;
 import org.apache.hadoop.ozone.om.OMMetrics;
 import org.apache.hadoop.ozone.om.OzoneManager;
@@ -55,6 +60,10 @@ public class OMOpenKeysDeleteRequest extends OMKeyRequest {
   private static final Logger LOG =
           LoggerFactory.getLogger(OMOpenKeysDeleteRequest.class);
 
+  private static final AuditLogger AUDIT = new 
AuditLogger(AuditLoggerType.OMSYSTEMLOGGER);
+  private static final String AUDIT_PARAM_NUM_OPEN_KEYS = "numOpenKeysDeleted";
+  private static final String AUDIT_PARAM_OPEN_KEYS = "openKeysDeleted";
+
   public OMOpenKeysDeleteRequest(OMRequest omRequest,
                                  BucketLayout bucketLayout) {
     super(omRequest, bucketLayout);
@@ -89,7 +98,7 @@ public OMClientResponse validateAndUpdateCache(OzoneManager 
ozoneManager, Execut
     Result result = null;
     // Map containing a pair of BucketId and delete key info.
     Map<String, Pair<Long, OmKeyInfo>> deletedOpenKeys = new HashMap<>();
-
+    Map<String, String> auditParams = new LinkedHashMap<>();
     try {
       for (OpenKeyBucket openKeyBucket: submittedOpenKeyBuckets) {
         // For each bucket where keys will be deleted from,
@@ -102,8 +111,23 @@ public OMClientResponse 
validateAndUpdateCache(OzoneManager ozoneManager, Execut
           deletedOpenKeys, getBucketLayout());
 
       result = Result.SUCCESS;
+
+      List<String> deletedOpenKeysLight = new 
ArrayList<>(deletedOpenKeys.size());
+      for (Pair<Long, OmKeyInfo> key : deletedOpenKeys.values()) {
+        OmKeyInfo keyInfo = key.getRight();
+        OzoneManagerProtocolProtos.KeyArgs keyArgs = 
OzoneManagerProtocolProtos.KeyArgs.newBuilder()
+            .setVolumeName(keyInfo.getVolumeName())
+            .setBucketName(keyInfo.getBucketName())
+            .setKeyName(keyInfo.getKeyName())
+            .build();
+        
deletedOpenKeysLight.add(buildLightKeyArgsAuditMap(keyArgs).toString());
+      }
+      auditParams.put(AUDIT_PARAM_NUM_OPEN_KEYS, 
String.valueOf(deletedOpenKeys.size()));
+      auditParams.put(AUDIT_PARAM_OPEN_KEYS, deletedOpenKeysLight.toString());
+      
AUDIT.logWriteSuccess(ozoneManager.buildAuditMessageForSuccess(OMSystemAction.OPEN_KEY_CLEANUP,
 auditParams));
     } catch (IOException | InvalidPathException ex) {
       result = Result.FAILURE;
+      
AUDIT.logWriteFailure(ozoneManager.buildAuditMessageForFailure(OMSystemAction.OPEN_KEY_CLEANUP,
 auditParams, ex));
       exception = ex;
       omClientResponse =
           new OMOpenKeysDeleteResponse(createErrorOMResponse(omResponse,
@@ -140,7 +164,7 @@ private void processResults(OMMetrics omMetrics, long 
numSubmittedOpenKeys,
     }
   }
 
-  private void updateOpenKeyTableCache(OzoneManager ozoneManager,
+  protected void updateOpenKeyTableCache(OzoneManager ozoneManager,
       long trxnLogIndex, OpenKeyBucket keysPerBucket,
       Map<String, Pair<Long, OmKeyInfo>> deletedOpenKeys) throws IOException {
 
@@ -148,6 +172,7 @@ private void updateOpenKeyTableCache(OzoneManager 
ozoneManager,
     String volumeName = keysPerBucket.getVolumeName();
     String bucketName = keysPerBucket.getBucketName();
     OMMetadataManager omMetadataManager = ozoneManager.getMetadataManager();
+
     try {
       mergeOmLockDetails(omMetadataManager.getLock()
           .acquireWriteLock(BUCKET_LOCK, volumeName, bucketName));
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMOpenKeysDeleteRequest.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMOpenKeysDeleteRequest.java
index 53f384b65ac..57a7b5a1f9d 100644
--- 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMOpenKeysDeleteRequest.java
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMOpenKeysDeleteRequest.java
@@ -21,19 +21,31 @@
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.anyMap;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.argThat;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 import java.util.stream.Collectors;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.hadoop.hdds.client.ReplicationConfig;
 import org.apache.hadoop.hdds.client.ReplicationFactor;
 import org.apache.hadoop.hdds.client.ReplicationType;
+import org.apache.hadoop.ozone.audit.OMSystemAction;
 import org.apache.hadoop.ozone.om.OMMetrics;
+import org.apache.hadoop.ozone.om.OzoneManager;
 import org.apache.hadoop.ozone.om.helpers.BucketLayout;
 import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
 import org.apache.hadoop.ozone.om.helpers.OzoneFSUtils;
@@ -296,6 +308,59 @@ public void testMetrics(BucketLayout buckLayout) throws 
Exception {
     assertEquals(numExistentKeys, metrics.getNumOpenKeysDeleted());
   }
 
+  /**
+   * Test OPEN_KEY_CLEANUP audit logging for both success and failure cases 
when open keys are deleted.
+   */
+  @ParameterizedTest
+  @MethodSource("bucketLayouts")
+  public void testOpenKeyCleanupAuditLogging(BucketLayout buckLayout) throws 
Exception {
+    this.bucketLayout = buckLayout;
+    final String volume = UUID.randomUUID().toString();
+    final String bucket = UUID.randomUUID().toString();
+
+    OMRequestTestUtils.addVolumeAndBucketToDB(volume, bucket,
+        omMetadataManager, getBucketLayout());
+
+    List<Pair<Long, OmKeyInfo>> openKeys = makeOpenKeys(volume, bucket, 3);
+    addToOpenKeyTableDB(openKeys);
+
+    OMRequest omRequest = doPreExecute(createDeleteOpenKeyRequest(openKeys));
+    OMOpenKeysDeleteRequest openKeyDeleteRequest = spy(new 
OMOpenKeysDeleteRequest(omRequest, getBucketLayout()));
+
+    OMClientResponse omClientResponse =
+        openKeyDeleteRequest.validateAndUpdateCache(ozoneManager, 100L);
+
+    assertEquals(Status.OK, omClientResponse.getOMResponse().getStatus());
+
+    verify(ozoneManager, times(1))
+        .buildAuditMessageForSuccess(eq(OMSystemAction.OPEN_KEY_CLEANUP),
+            argThat(params -> {
+              assertEquals("3", params.get("numOpenKeysDeleted"));
+              assertTrue(params.containsKey("openKeysDeleted"));
+              return true;
+            }));
+
+    assertNotInOpenKeyTable(openKeys);
+
+    // Simulate failure by mocking updateOpenKeyTableCache to throw an 
IOException, and verify the failure audit log.
+    doThrow(new IOException())
+        .when(openKeyDeleteRequest)
+        .updateOpenKeyTableCache(
+            any(OzoneManager.class),
+            anyLong(),
+            any(OpenKeyBucket.class),
+            anyMap());
+
+    omClientResponse = 
openKeyDeleteRequest.validateAndUpdateCache(ozoneManager, 100L);
+
+    assertEquals(Status.INTERNAL_ERROR, 
omClientResponse.getOMResponse().getStatus());
+
+    verify(ozoneManager, times(1))
+        .buildAuditMessageForFailure(eq(OMSystemAction.OPEN_KEY_CLEANUP),
+            argThat(Map::isEmpty),
+            any(Throwable.class));
+  }
+
   /**
    * Runs the validate and update cache step of
    * {@link OMOpenKeysDeleteRequest} to mark the keys in {@code openKeys}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to