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

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


The following commit(s) were added to refs/heads/HDDS-8342 by this push:
     new b16ea44f588 HDDS-12791. Make Retention Service suspendable (#9426)
b16ea44f588 is described below

commit b16ea44f588434091f8b9b2ae2f776f757ac2f2f
Author: XiChen <[email protected]>
AuthorDate: Mon Dec 29 18:42:02 2025 +0800

    HDDS-12791. Make Retention Service suspendable (#9426)
---
 .../main/java/org/apache/hadoop/ozone/OmUtils.java |   2 +
 .../ozone/om/protocol/OzoneManagerProtocol.java    |  29 ++++++
 ...OzoneManagerProtocolClientSideTranslatorPB.java |  44 +++++++++
 .../hadoop/ozone/shell/TestOzoneShellHA.java       |  76 +++++++++++++++
 .../src/main/proto/OmClientProtocol.proto          |  25 +++++
 .../org/apache/hadoop/ozone/audit/OMAction.java    |   4 +-
 .../org/apache/hadoop/ozone/om/OzoneManager.java   |  16 ++++
 .../hadoop/ozone/om/helpers/OMAuditLogger.java     |   1 +
 .../om/ratis/utils/OzoneManagerRatisUtils.java     |   3 +
 .../OMLifecycleSetServiceStatusRequest.java        | 103 +++++++++++++++++++++
 .../OMLifecycleSetServiceStatusResponse.java       |  46 +++++++++
 .../ozone/om/service/KeyLifecycleService.java      |  41 ++++++--
 .../protocolPB/OzoneManagerRequestHandler.java     |   7 ++
 .../ozone/om/response/TestCleanupTableInfo.java    |   2 +
 .../ozone/om/service/TestKeyLifecycleService.java  |  41 ++++++++
 .../ozone/admin/om/LifecycleResumeSubCommand.java  |  73 +++++++++++++++
 .../ozone/admin/om/LifecycleStatusSubCommand.java  |  89 ++++++++++++++++++
 .../hadoop/ozone/admin/om/LifecycleSubCommand.java |  47 ++++++++++
 .../ozone/admin/om/LifecycleSuspendSubCommand.java |  76 +++++++++++++++
 .../org/apache/hadoop/ozone/admin/om/OMAdmin.java  |   3 +-
 20 files changed, 720 insertions(+), 8 deletions(-)

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 9a945dfea87..2287d904da3 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
@@ -278,6 +278,7 @@ public static boolean isReadOnly(
     case GetQuotaRepairStatus:
     case StartQuotaRepair:
     case GetLifecycleConfiguration:
+    case GetLifecycleServiceStatus:
       return true;
     case CreateVolume:
     case SetVolumeProperty:
@@ -341,6 +342,7 @@ public static boolean isReadOnly(
     case DeleteObjectTagging:
     case SetLifecycleConfiguration:
     case DeleteLifecycleConfiguration:
+    case SetLifecycleServiceStatus:
     case UnknownCommand:
       return false;
     case EchoRPC:
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 626bef65044..f8d686f53ad 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
@@ -66,6 +66,7 @@
 import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CancelPrepareResponse;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.EchoRPCResponse;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetLifecycleServiceStatusResponse;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OzoneAclInfo;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse.PrepareStatus;
@@ -1226,4 +1227,32 @@ default void deleteLifecycleConfiguration(String 
volumeName,
     throw new UnsupportedOperationException("OzoneManager does not require " +
         "this to be implemented, as write requests use a new approach.");
   }
+
+  /**
+   * Gets the lifecycle service status.
+   * @return GetLifecycleServiceStatusResponse
+   * @throws IOException
+   */
+  default GetLifecycleServiceStatusResponse getLifecycleServiceStatus() throws 
IOException {
+    throw new UnsupportedOperationException("OzoneManager does not require " +
+        "this to be implemented, as write requests use a new approach.");
+  }
+
+  /**
+   * Suspends the lifecycle service.
+   * @throws IOException
+   */
+  default void suspendLifecycleService() throws IOException {
+    throw new UnsupportedOperationException("OzoneManager does not require " +
+        "this to be implemented, as write requests use a new approach.");
+  }
+
+  /**
+   * Resumes the lifecycle service.
+   * @throws IOException
+   */
+  default void resumeLifecycleService() throws IOException {
+    throw new UnsupportedOperationException("OzoneManager does not require " +
+        "this to be implemented, as write requests use a new approach.");
+  }
 }
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 3a2d41c0caf..94fbe0e0f06 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
@@ -141,6 +141,7 @@
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetKeyInfoResponse;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetLifecycleConfigurationRequest;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetLifecycleConfigurationResponse;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetLifecycleServiceStatusResponse;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetObjectTaggingRequest;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetObjectTaggingResponse;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetS3SecretRequest;
@@ -2691,6 +2692,21 @@ public OmLifecycleConfiguration 
getLifecycleConfiguration(String volumeName,
         resp.getLifecycleConfiguration());
   }
 
+  @Override
+  public GetLifecycleServiceStatusResponse getLifecycleServiceStatus() throws 
IOException {
+    OzoneManagerProtocolProtos.GetLifecycleServiceStatusRequest
+        getLifecycleServiceStatusRequest =
+        OzoneManagerProtocolProtos.GetLifecycleServiceStatusRequest
+            .newBuilder().build();
+
+    OMRequest omRequest = createOMRequest(Type.GetLifecycleServiceStatus)
+        .setGetLifecycleServiceStatusRequest(getLifecycleServiceStatusRequest)
+        .build();
+
+    return handleError(submitRequest(omRequest))
+        .getGetLifecycleServiceStatusResponse();
+  }
+
   @Override
   public void setLifecycleConfiguration(
       OmLifecycleConfiguration omLifecycleConfiguration) throws IOException {
@@ -2724,6 +2740,34 @@ public void deleteLifecycleConfiguration(String 
volumeName, String bucketName)
     handleError(submitRequest(omRequest));
   }
 
+  @Override
+  public void suspendLifecycleService() throws IOException {
+    OzoneManagerProtocolProtos.SetLifecycleServiceStatusRequest
+        setLifecycleServiceStatusRequest =
+        OzoneManagerProtocolProtos.SetLifecycleServiceStatusRequest
+            .newBuilder().setSuspend(true).build();
+
+    OMRequest omRequest = createOMRequest(Type.SetLifecycleServiceStatus)
+        .setSetLifecycleServiceStatusRequest(setLifecycleServiceStatusRequest)
+        .build();
+
+    handleError(submitRequest(omRequest));
+  }
+
+  @Override
+  public void resumeLifecycleService() throws IOException {
+    OzoneManagerProtocolProtos.SetLifecycleServiceStatusRequest
+        setLifecycleServiceStatusRequest =
+        OzoneManagerProtocolProtos.SetLifecycleServiceStatusRequest
+            .newBuilder().setSuspend(false).build();
+
+    OMRequest omRequest = createOMRequest(Type.SetLifecycleServiceStatus)
+        .setSetLifecycleServiceStatusRequest(setLifecycleServiceStatusRequest)
+        .build();
+
+    handleError(submitRequest(omRequest));
+  }
+
   private SafeMode toProtoBuf(SafeModeAction action) {
     switch (action) {
     case ENTER:
diff --git 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java
 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java
index d7fbce84f1e..2dad2eef7ac 100644
--- 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java
+++ 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java
@@ -194,6 +194,7 @@ protected static void startCluster(OzoneConfiguration conf) 
throws Exception {
     conf.setInt(OMConfigKeys.OZONE_DIR_DELETING_SERVICE_INTERVAL, 10);
     conf.setBoolean(OMConfigKeys.OZONE_OM_ENABLE_FILESYSTEM_PATHS, true);
     conf.setInt(ScmConfigKeys.OZONE_SCM_CONTAINER_LIST_MAX_COUNT, 1);
+    conf.setBoolean(OMConfigKeys.OZONE_KEY_LIFECYCLE_SERVICE_ENABLED, true);
     ozoneConfiguration = conf;
     MiniOzoneHAClusterImpl.Builder builder = 
MiniOzoneCluster.newHABuilder(conf);
     builder.setOMServiceId(omServiceId)
@@ -1834,6 +1835,81 @@ public void testSetEncryptionKey() throws Exception {
     assertEquals(newEncKey, 
volume.getBucket("bucket0").getEncryptionKeyName());
   }
 
+  @Test
+  public void testLifecycleStatus() throws UnsupportedEncodingException {
+    String[] args = new String[] {"om", "lifecycle", "status", "--service-id", 
omServiceId};
+    execute(ozoneAdminShell, args);
+    String output = out.toString(DEFAULT_ENCODING);
+    assertThat(output).contains("IsEnabled:");
+  }
+
+  @Test
+  public void testLifecycleSuspendAndResume() throws Exception {
+    List<OzoneManager> ozoneManagers = cluster.getOzoneManagersList();
+    for (OzoneManager om : ozoneManagers) {
+      assertNotNull(om.getKeyManager().getKeyLifecycleService());
+      assertTrue(om.getLifecycleServiceStatus().getIsEnabled());
+      assertFalse(om.getLifecycleServiceStatus().getIsSuspended());
+    }
+
+    // Execute suspend command
+    String[] args = new String[] {"om", "lifecycle", "suspend", 
"--service-id", omServiceId};
+    execute(ozoneAdminShell, args);
+    String output = out.toString(DEFAULT_ENCODING);
+    assertThat(output).contains("Lifecycle Service has been suspended");
+    out.reset();
+
+    // Wait for the suspend command to propagate through Ratis to all OMs
+    GenericTestUtils.waitFor(() -> {
+      for (OzoneManager om : ozoneManagers) {
+        assertNotNull(om.getKeyManager().getKeyLifecycleService());
+        if (!om.getLifecycleServiceStatus().getIsSuspended()) {
+          return false;
+        }
+      }
+      return true;
+    }, 100, 10000);
+
+    // Verify lifecycle service is suspended on all OMs
+    for (OzoneManager om : ozoneManagers) {
+      if (om.getKeyManager().getKeyLifecycleService() != null) {
+        assertTrue(om.getLifecycleServiceStatus().getIsSuspended(),
+            "Lifecycle service should be suspended on OM: " + 
om.getOMNodeId());
+        // isEnabled should still be true (based on configuration)
+        assertTrue(om.getLifecycleServiceStatus().getIsEnabled(),
+            "Lifecycle service isEnabled should still be true on OM: " + 
om.getOMNodeId());
+      }
+    }
+
+    // Execute resume command
+    args = new String[] {"om", "lifecycle", "resume", "--service-id", 
omServiceId};
+    execute(ozoneAdminShell, args);
+    output = out.toString(DEFAULT_ENCODING);
+    assertThat(output).contains("Lifecycle Service has been resumed");
+    out.reset();
+
+    // Wait for the resume command to propagate through Ratis to all OMs
+    GenericTestUtils.waitFor(() -> {
+      for (OzoneManager om : ozoneManagers) {
+        assertNotNull(om.getKeyManager().getKeyLifecycleService());
+        if (om.getLifecycleServiceStatus().getIsSuspended()) {
+          return false;
+        }
+      }
+      return true;
+    }, 100, 10000);
+
+    // Verify lifecycle service is resumed on all OMs
+    for (OzoneManager om : ozoneManagers) {
+      if (om.getKeyManager().getKeyLifecycleService() != null) {
+        assertFalse(om.getLifecycleServiceStatus().getIsSuspended(),
+            "Lifecycle service should be resumed on OM: " + om.getOMNodeId());
+        assertTrue(om.getLifecycleServiceStatus().getIsEnabled(),
+            "Lifecycle service isEnabled should be true on OM: " + 
om.getOMNodeId());
+      }
+    }
+  }
+
   @Test
   public void testCreateBucketWithECReplicationConfigWithoutReplicationParam() 
{
     getVolume("volume102");
diff --git 
a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto 
b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
index 137e44be877..0c0265db83f 100644
--- a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
+++ b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
@@ -159,6 +159,8 @@ enum Type {
   SetLifecycleConfiguration = 150;
   GetLifecycleConfiguration   = 151;
   DeleteLifecycleConfiguration = 152;
+  GetLifecycleServiceStatus    = 153;
+  SetLifecycleServiceStatus     = 154;
 }
 
 enum SafeMode {
@@ -310,6 +312,9 @@ message OMRequest {
   optional SetLifecycleConfigurationRequest setLifecycleConfigurationRequest = 
150;
   optional GetLifecycleConfigurationRequest   getLifecycleConfigurationRequest 
  = 151;
   optional DeleteLifecycleConfigurationRequest 
deleteLifecycleConfigurationRequest = 152;
+
+  optional GetLifecycleServiceStatusRequest getLifecycleServiceStatusRequest = 
153;
+  optional SetLifecycleServiceStatusRequest setLifecycleServiceStatusRequest = 
154;
 }
 
 message OMResponse {
@@ -447,6 +452,9 @@ message OMResponse {
   optional SetLifecycleConfigurationResponse setLifecycleConfigurationResponse 
= 150;
   optional GetLifecycleConfigurationResponse   
getLifecycleConfigurationResponse   = 151;
   optional DeleteLifecycleConfigurationResponse 
deleteLifecycleConfigurationResponse = 152;
+
+  optional GetLifecycleServiceStatusResponse getLifecycleServiceStatusResponse 
= 153;
+  optional SetLifecycleServiceStatusResponse setLifecycleServiceStatusResponse 
= 154;
 }
 
 enum Status {
@@ -2439,3 +2447,20 @@ service OzoneManagerService {
     rpc submitRequest(OMRequest)
           returns(OMResponse);
 }
+
+message GetLifecycleServiceStatusRequest {
+}
+
+message GetLifecycleServiceStatusResponse {
+  required bool isEnabled = 1;
+  optional bool isSuspended = 2;
+  repeated string runningBuckets = 3;
+}
+
+message SetLifecycleServiceStatusRequest {
+  required bool suspend = 1;
+}
+
+message SetLifecycleServiceStatusResponse {
+}
+
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 10cd0b21291..fa045294175 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
@@ -117,7 +117,9 @@ public enum OMAction implements AuditAction {
 
   GET_LIFECYCLE_CONFIGURATION,
   SET_LIFECYCLE_CONFIGURATION,
-  DELETE_LIFECYCLE_CONFIGURATION;
+  DELETE_LIFECYCLE_CONFIGURATION,
+  GET_LIFECYCLE_SERVICE_STATUS,
+  SET_LIFECYCLE_SERVICE_STATUS;
 
   @Override
   public String getAction() {
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 b78368a441f..19838691c8d 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
@@ -53,6 +53,8 @@
 import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_DEFAULT_BUCKET_LAYOUT;
 import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_DEFAULT_BUCKET_LAYOUT_DEFAULT;
 import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_KEY_DELETING_LIMIT_PER_TASK;
+import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_KEY_LIFECYCLE_SERVICE_ENABLED;
+import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_KEY_LIFECYCLE_SERVICE_ENABLED_DEFAULT;
 import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_ADDRESS_KEY;
 import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_EDEKCACHELOADER_INITIAL_DELAY_MS_DEFAULT;
 import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_EDEKCACHELOADER_INITIAL_DELAY_MS_KEY;
@@ -283,6 +285,7 @@
 import org.apache.hadoop.ozone.om.s3.S3SecretCacheProvider;
 import org.apache.hadoop.ozone.om.s3.S3SecretStoreProvider;
 import org.apache.hadoop.ozone.om.service.CompactDBService;
+import org.apache.hadoop.ozone.om.service.KeyLifecycleService;
 import org.apache.hadoop.ozone.om.service.OMRangerBGSyncService;
 import org.apache.hadoop.ozone.om.service.QuotaRepairTask;
 import org.apache.hadoop.ozone.om.snapshot.OmSnapshotUtils;
@@ -295,6 +298,7 @@
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DBUpdatesRequest;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.EchoRPCResponse;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ExtendedUserAccessIdInfo;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetLifecycleServiceStatusResponse;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.KeyArgs;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRoleInfo;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.S3Authentication;
@@ -3159,6 +3163,18 @@ public OmLifecycleConfiguration 
getLifecycleConfiguration(String volumeName,
     }
   }
 
+  @Override
+  public GetLifecycleServiceStatusResponse getLifecycleServiceStatus() {
+    KeyLifecycleService keyLifecycleService = 
keyManager.getKeyLifecycleService();
+    if (keyLifecycleService == null) {
+      return GetLifecycleServiceStatusResponse.newBuilder()
+          
.setIsEnabled(getConfiguration().getBoolean(OZONE_KEY_LIFECYCLE_SERVICE_ENABLED,
+              OZONE_KEY_LIFECYCLE_SERVICE_ENABLED_DEFAULT))
+          .build();
+    }
+    return keyLifecycleService.status();
+  }
+
   private Map<String, String> buildAuditMap(String volume) {
     Map<String, String> auditMap = new LinkedHashMap<>();
     auditMap.put(OzoneConsts.VOLUME, volume);
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/helpers/OMAuditLogger.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/helpers/OMAuditLogger.java
index 80c20f7af6d..7673663fa04 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/helpers/OMAuditLogger.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/helpers/OMAuditLogger.java
@@ -93,6 +93,7 @@ private static void init() {
     CMD_AUDIT_ACTION_MAP.put(Type.GetObjectTagging, 
OMAction.GET_OBJECT_TAGGING);
     CMD_AUDIT_ACTION_MAP.put(Type.PutObjectTagging, 
OMAction.PUT_OBJECT_TAGGING);
     CMD_AUDIT_ACTION_MAP.put(Type.DeleteObjectTagging, 
OMAction.DELETE_OBJECT_TAGGING);
+    CMD_AUDIT_ACTION_MAP.put(Type.SetLifecycleServiceStatus, 
OMAction.SET_LIFECYCLE_SERVICE_STATUS);
   }
 
   private static OMAction getAction(OzoneManagerProtocolProtos.OMRequest 
request) {
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 1f8f0e24a27..dcaf47b1754 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
@@ -68,6 +68,7 @@
 import org.apache.hadoop.ozone.om.request.key.acl.prefix.OMPrefixSetAclRequest;
 import 
org.apache.hadoop.ozone.om.request.lifecycle.OMLifecycleConfigurationDeleteRequest;
 import 
org.apache.hadoop.ozone.om.request.lifecycle.OMLifecycleConfigurationSetRequest;
+import 
org.apache.hadoop.ozone.om.request.lifecycle.OMLifecycleSetServiceStatusRequest;
 import 
org.apache.hadoop.ozone.om.request.s3.multipart.S3ExpiredMultipartUploadsAbortRequest;
 import org.apache.hadoop.ozone.om.request.s3.security.OMSetSecretRequest;
 import org.apache.hadoop.ozone.om.request.s3.security.S3GetSecretRequest;
@@ -351,6 +352,8 @@ public static OMClientRequest createClientRequest(OMRequest 
omRequest,
       return new OMLifecycleConfigurationSetRequest(omRequest);
     case DeleteLifecycleConfiguration:
       return new OMLifecycleConfigurationDeleteRequest(omRequest);
+    case SetLifecycleServiceStatus:
+      return new OMLifecycleSetServiceStatusRequest(omRequest);
     default:
       throw new OMException("Unrecognized write command type request "
           + cmdType, OMException.ResultCodes.INVALID_REQUEST);
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/lifecycle/OMLifecycleSetServiceStatusRequest.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/lifecycle/OMLifecycleSetServiceStatusRequest.java
new file mode 100644
index 00000000000..be9e92b3c1d
--- /dev/null
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/lifecycle/OMLifecycleSetServiceStatusRequest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.lifecycle;
+
+import java.io.IOException;
+import java.util.HashMap;
+import org.apache.hadoop.ozone.audit.AuditLogger;
+import org.apache.hadoop.ozone.audit.OMAction;
+import org.apache.hadoop.ozone.om.OzoneManager;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.om.execution.flowcontrol.ExecutionContext;
+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.lifecycle.OMLifecycleSetServiceStatusResponse;
+import org.apache.hadoop.ozone.om.service.KeyLifecycleService;
+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.SetLifecycleServiceStatusResponse;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.UserInfo;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Handles SetLifecycleServiceStatus Request.
+ * This request suspends or resumes the KeyLifecycleService.
+ */
+public class OMLifecycleSetServiceStatusRequest extends OMClientRequest {
+  private static final Logger LOG =
+      LoggerFactory.getLogger(OMLifecycleSetServiceStatusRequest.class);
+
+  public OMLifecycleSetServiceStatusRequest(OMRequest omRequest) {
+    super(omRequest);
+  }
+
+  @Override
+  public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, 
ExecutionContext context) {
+    OMResponse.Builder omResponse = 
OmResponseUtil.getOMResponseBuilder(getOmRequest());
+    AuditLogger auditLogger = ozoneManager.getAuditLogger();
+    UserInfo userInfo = getOmRequest().getUserInfo();
+    HashMap<String, String> auditMap = new HashMap<>();
+    IOException exception = null;
+    OMClientResponse omClientResponse;
+    boolean suspend = 
getOmRequest().getSetLifecycleServiceStatusRequest().getSuspend();
+    auditMap.put("suspend", String.valueOf(suspend));
+
+    try {
+      if (ozoneManager.getAclsEnabled()) {
+        UserGroupInformation ugi = createUGIForApi();
+        if (!ozoneManager.isAdmin(ugi)) {
+          throw new OMException("Access denied for user " + ugi + ". "
+              + "Superuser privilege is required to " + (suspend ? "suspend" : 
"resume") + " Lifecycle Service.",
+              OMException.ResultCodes.ACCESS_DENIED);
+        }
+      }
+
+      KeyLifecycleService keyLifecycleService = 
ozoneManager.getKeyManager().getKeyLifecycleService();
+      if (keyLifecycleService != null) {
+        if (suspend) {
+          keyLifecycleService.suspend();
+          LOG.info("KeyLifecycleService has been suspended by user: {}",
+              userInfo != null ? userInfo.getUserName() : "unknown");
+        } else {
+          keyLifecycleService.resume();
+          LOG.info("KeyLifecycleService resume called by user: {}",
+              userInfo != null ? userInfo.getUserName() : "unknown");
+        }
+      } else {
+        LOG.warn("KeyLifecycleService is not available");
+      }
+
+      omResponse.setSetLifecycleServiceStatusResponse(
+          SetLifecycleServiceStatusResponse.newBuilder().build());
+      omClientResponse = new 
OMLifecycleSetServiceStatusResponse(omResponse.build());
+    } catch (IOException ex) {
+      exception = ex;
+      LOG.error("Failed to " + (suspend ? "suspend" : "resume") + " 
KeyLifecycleService", ex);
+      omClientResponse = new OMLifecycleSetServiceStatusResponse(
+          createErrorOMResponse(omResponse, ex));
+    }
+
+    markForAudit(auditLogger, 
buildAuditMessage(OMAction.SET_LIFECYCLE_SERVICE_STATUS,
+        auditMap, exception, userInfo));
+    return omClientResponse;
+  }
+}
+
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/lifecycle/OMLifecycleSetServiceStatusResponse.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/lifecycle/OMLifecycleSetServiceStatusResponse.java
new file mode 100644
index 00000000000..59f8b0963ff
--- /dev/null
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/lifecycle/OMLifecycleSetServiceStatusResponse.java
@@ -0,0 +1,46 @@
+/*
+ * 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.lifecycle;
+
+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.response.CleanupTableInfo;
+import org.apache.hadoop.ozone.om.response.OMClientResponse;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
+
+/**
+ * Response for SetLifecycleServiceStatus request.
+ * This response does not modify any database tables.
+ */
+@CleanupTableInfo
+public class OMLifecycleSetServiceStatusResponse extends OMClientResponse {
+
+  public OMLifecycleSetServiceStatusResponse(OMResponse omResponse) {
+    super(omResponse);
+  }
+
+  @Override
+  protected void addToDBBatch(OMMetadataManager omMetadataManager,
+                              BatchOperation batchOperation)
+      throws IOException {
+    // No database update required for setting the lifecycle service state.
+    // The service state is maintained in memory only.
+  }
+}
+
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/service/KeyLifecycleService.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/service/KeyLifecycleService.java
index 6b87cd124b5..f115ef181e2 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/service/KeyLifecycleService.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/service/KeyLifecycleService.java
@@ -43,6 +43,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -84,6 +85,7 @@
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DeleteKeyArgs;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DeleteKeyError;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DeleteKeysRequest;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetLifecycleServiceStatusResponse;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.KeyArgs;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RenameKeyRequest;
@@ -106,8 +108,8 @@ public class KeyLifecycleService extends BackgroundService {
   private int listMaxSize;
   private long cachedDirMaxCount;
   private final AtomicBoolean suspended;
+  private final AtomicBoolean isServiceEnabled;
   private KeyLifecycleServiceMetrics metrics;
-  private boolean isServiceEnabled;
   // A set of bucket name that have LifecycleActionTask scheduled
   private final ConcurrentHashMap<String, LifecycleActionTask> inFlight;
   private OMMetadataManager omMetadataManager;
@@ -133,8 +135,8 @@ public KeyLifecycleService(OzoneManager ozoneManager,
         OZONE_KEY_LIFECYCLE_SERVICE_DELETE_CACHED_DIRECTORY_MAX_COUNT_DEFAULT);
     this.suspended = new AtomicBoolean(false);
     this.metrics = KeyLifecycleServiceMetrics.create();
-    this.isServiceEnabled = 
conf.getBoolean(OZONE_KEY_LIFECYCLE_SERVICE_ENABLED,
-        OZONE_KEY_LIFECYCLE_SERVICE_ENABLED_DEFAULT);
+    this.isServiceEnabled = new 
AtomicBoolean(conf.getBoolean(OZONE_KEY_LIFECYCLE_SERVICE_ENABLED,
+        OZONE_KEY_LIFECYCLE_SERVICE_ENABLED_DEFAULT));
     this.inFlight = new ConcurrentHashMap();
     this.omMetadataManager = ozoneManager.getMetadataManager();
     int limit = (int) conf.getStorageSize(
@@ -193,7 +195,7 @@ private boolean shouldRun() {
       // OzoneManager can be null for testing
       return true;
     }
-    return isServiceEnabled && !suspended.get() && 
getOzoneManager().isLeaderReady();
+    return isServiceEnabled.get() && !suspended.get() && 
getOzoneManager().isLeaderReady();
   }
 
   public KeyLifecycleServiceMetrics getMetrics() {
@@ -207,7 +209,6 @@ public OzoneManager getOzoneManager() {
   /**
    * Suspend the service.
    */
-  @VisibleForTesting
   public void suspend() {
     suspended.set(true);
   }
@@ -215,17 +216,33 @@ public void suspend() {
   /**
    * Resume the service if suspended.
    */
-  @VisibleForTesting
   public void resume() {
     suspended.set(false);
   }
 
+  public boolean isSuspended() {
+    return suspended.get();
+  }
+
   @Override
   public void shutdown() {
     super.shutdown();
     KeyLifecycleServiceMetrics.unregister();
   }
 
+  /**
+   * Build a GetLifecycleServiceStatusResponse instance.
+   * @return GetLifecycleServiceStatusResponse instance
+   */
+  public GetLifecycleServiceStatusResponse status() {
+    Set<String> runningBuckets = new HashSet<>(inFlight.keySet());
+    return GetLifecycleServiceStatusResponse.newBuilder()
+        .setIsEnabled(isServiceEnabled.get())
+        .setIsSuspended(suspended.get())
+        .addAllRunningBuckets(runningBuckets)
+        .build();
+  }
+
   /**
    * A lifecycle action task for one specific bucket, scanning OM DB and 
evaluating if any existing
    * object/key qualified for expiration according to bucket's lifecycle 
configuration, and sending
@@ -471,6 +488,13 @@ private void evaluateKeyAndDirTable(OmBucketInfo bucket, 
long volumeObjId, Table
 
       HashSet<Long> deletedDirSet = new HashSet<>();
       while (!stack.isEmpty()) {
+        if (!shouldRun()) {
+          LOG.info("LifecycleActionTask for bucket {} stopping. " +
+              "Service enabled: {}, suspended: {}, leader ready: {}",
+              bucketName, isServiceEnabled.get(), suspended.get(), 
+              getOzoneManager() != null ? getOzoneManager().isLeaderReady() : 
"N/A");
+          return;
+        }
         PendingEvaluateDirectory item = stack.pop();
         OmDirectoryInfo currentDir = item.getDirectoryInfo();
         String currentDirPath = item.getDirPath();
@@ -699,6 +723,11 @@ private void evaluateBucket(OmBucketInfo bucketInfo,
       try (TableIterator<String, ? extends Table.KeyValue<String, OmKeyInfo>> 
keyTblItr =
                keyTable.iterator(omMetadataManager.getBucketKey(volumeName, 
bucketName))) {
         while (keyTblItr.hasNext()) {
+          if (!shouldRun()) {
+            LOG.info("KeyLifecycleService is suspended or disabled. " +
+                "Stopping LifecycleActionTask for bucket {}.", bucketName);
+            return;
+          }
           Table.KeyValue<String, OmKeyInfo> keyValue = keyTblItr.next();
           OmKeyInfo key = keyValue.getValue();
           numKeyIterated++;
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java
index debbbebf874..eaa578f6136 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java
@@ -116,6 +116,7 @@
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetKeyInfoResponse;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetLifecycleConfigurationRequest;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetLifecycleConfigurationResponse;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetLifecycleServiceStatusResponse;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetObjectTaggingRequest;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetObjectTaggingResponse;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetS3VolumeContextResponse;
@@ -402,6 +403,12 @@ public OMResponse handleReadRequest(OMRequest request) {
         responseBuilder.setGetLifecycleConfigurationResponse(
             getLifecycleConfigurationResponse);
         break;
+      case GetLifecycleServiceStatus:
+        GetLifecycleServiceStatusResponse getLifecycleServiceStatusResponse =
+            impl.getLifecycleServiceStatus();
+        responseBuilder.setGetLifecycleServiceStatusResponse(
+            getLifecycleServiceStatusResponse);
+        break;
       default:
         responseBuilder.setSuccess(false);
         responseBuilder.setMessage("Unrecognized Command Type: " + cmdType);
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/TestCleanupTableInfo.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/TestCleanupTableInfo.java
index 7def54ae5e8..62bdacb8c4a 100644
--- 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/TestCleanupTableInfo.java
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/TestCleanupTableInfo.java
@@ -60,6 +60,7 @@
 import org.apache.hadoop.ozone.om.response.file.OMFileCreateResponse;
 import org.apache.hadoop.ozone.om.response.key.OMKeyCreateResponse;
 import org.apache.hadoop.ozone.om.response.key.OmKeyResponse;
+import 
org.apache.hadoop.ozone.om.response.lifecycle.OMLifecycleSetServiceStatusResponse;
 import org.apache.hadoop.ozone.om.response.util.OMEchoRPCWriteResponse;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateFileRequest;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateKeyRequest;
@@ -135,6 +136,7 @@ public void checkAnnotationAndTableName() {
     // OMEchoRPCWriteResponse does not need CleanupTable.
     subTypes.remove(OMEchoRPCWriteResponse.class);
     subTypes.remove(DummyOMClientResponse.class);
+    subTypes.remove(OMLifecycleSetServiceStatusResponse.class);
     subTypes.forEach(aClass -> {
       assertTrue(aClass.isAnnotationPresent(CleanupTableInfo.class),
           aClass + " does not have annotation of" +
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/service/TestKeyLifecycleService.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/service/TestKeyLifecycleService.java
index 8d9d457ef61..7b1b0e57c7c 100644
--- 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/service/TestKeyLifecycleService.java
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/service/TestKeyLifecycleService.java
@@ -101,6 +101,7 @@
 import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol;
 import org.apache.hadoop.ozone.om.request.OMRequestTestUtils;
 import org.apache.hadoop.ozone.om.request.key.OMKeysDeleteRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.LifecycleConfiguration;
 import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
 import org.apache.hadoop.ozone.security.acl.OzoneObj;
@@ -114,6 +115,7 @@
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestInstance;
 import org.junit.jupiter.api.Timeout;
 import org.junit.jupiter.api.io.TempDir;
@@ -1537,6 +1539,45 @@ void testMultipleDirectoriesMatched(String keyPrefix1, 
String keyPrefix2, String
       }
       deleteLifecyclePolicy(volumeName, bucketName);
     }
+
+    @Test
+    void testGetLifecycleServiceStatus() throws Exception {
+      final String volumeName = getTestName();
+      final String bucketName = uniqueObjectName("bucket");
+      String prefix = "key";
+      
+      //Service should be enabled but not running
+      OzoneManagerProtocolProtos.GetLifecycleServiceStatusResponse status =
+          om.getLifecycleServiceStatus();
+      assertTrue(status.getIsEnabled());
+      assertEquals(0, status.getRunningBucketsCount());
+      
+      // Create and inject for test
+      createKeys(volumeName, bucketName, FILE_SYSTEM_OPTIMIZED, KEY_COUNT, 1, 
prefix, null);
+      ZonedDateTime date = 
ZonedDateTime.now(ZoneOffset.UTC).plusSeconds(EXPIRE_SECONDS);
+      KeyLifecycleService.setInjectors(
+          Arrays.asList(new FaultInjectorImpl(), new FaultInjectorImpl()));
+      createLifecyclePolicy(volumeName, bucketName, FILE_SYSTEM_OPTIMIZED, 
prefix, null, date.toString(), true);
+      Thread.sleep(SERVICE_INTERVAL + 100);
+      
+      // Verify service is running and processing the bucket
+      status = om.getLifecycleServiceStatus();
+      assertTrue(status.getIsEnabled());
+      assertEquals(1, status.getRunningBucketsCount());
+      assertTrue(status.getRunningBucketsList().contains("/" + volumeName + 
"/" + bucketName));
+      
+      KeyLifecycleService.getInjector(0).resume();
+      KeyLifecycleService.getInjector(1).resume();
+      GenericTestUtils.waitFor(() -> 
om.getLifecycleServiceStatus().getRunningBucketsCount() == 0,
+          WAIT_CHECK_INTERVAL, 10000);
+      
+      // Verify service completed and is no longer running
+      status = om.getLifecycleServiceStatus();
+      assertTrue(status.getIsEnabled());
+      assertEquals(0, status.getRunningBucketsCount());
+      
+      deleteLifecyclePolicy(volumeName, bucketName);
+    }
   }
 
   /**
diff --git 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/om/LifecycleResumeSubCommand.java
 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/om/LifecycleResumeSubCommand.java
new file mode 100644
index 00000000000..69f6c66a90d
--- /dev/null
+++ 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/om/LifecycleResumeSubCommand.java
@@ -0,0 +1,73 @@
+/*
+ * 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.admin.om;
+
+import java.io.PrintStream;
+import java.util.concurrent.Callable;
+import org.apache.hadoop.hdds.cli.HddsVersionProvider;
+import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+
+/**
+ * Handler of ozone admin om lifecycle resume command.
+ */
+@Command(
+    name = "resume",
+    description = "Resume Lifecycle Service that was previously suspended",
+    mixinStandardHelpOptions = true,
+    versionProvider = HddsVersionProvider.class)
+public class LifecycleResumeSubCommand implements Callable<Void> {
+
+  @CommandLine.ParentCommand
+  private LifecycleSubCommand parent;
+
+  @CommandLine.Option(
+      names = {"-id", "--service-id"},
+      description = "Ozone Manager Service ID"
+  )
+  private String omServiceId;
+
+  @CommandLine.Option(
+      names = {"-host", "--service-host"},
+      description = "Ozone Manager Host"
+  )
+  private String omHost;
+
+  @Override
+  public Void call() throws Exception {
+    try (OzoneManagerProtocol ozoneManagerClient =
+             parent.getParent().createOmClient(omServiceId, omHost, false)) {
+      ozoneManagerClient.resumeLifecycleService();
+      output();
+    }
+    return null;
+  }
+
+  protected void output() {
+    PrintStream out = out();
+    out.println("========================================");
+    out.println("Lifecycle Service has been resumed.");
+    out.println("========================================");
+  }
+
+  protected PrintStream out() {
+    return System.out;
+  }
+}
+
diff --git 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/om/LifecycleStatusSubCommand.java
 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/om/LifecycleStatusSubCommand.java
new file mode 100644
index 00000000000..a443471391a
--- /dev/null
+++ 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/om/LifecycleStatusSubCommand.java
@@ -0,0 +1,89 @@
+/*
+ * 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.admin.om;
+
+import java.io.PrintStream;
+import java.util.concurrent.Callable;
+import org.apache.hadoop.hdds.cli.HddsVersionProvider;
+import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetLifecycleServiceStatusResponse;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+
+/**
+ * Handler of ozone admin om lifecycle status command.
+ */
+@Command(
+    name = "status",
+    description = "Check Lifecycle Service status",
+    mixinStandardHelpOptions = true,
+    versionProvider = HddsVersionProvider.class)
+public class LifecycleStatusSubCommand implements Callable<Void> {
+
+  @CommandLine.ParentCommand
+  private LifecycleSubCommand parent;
+
+  @CommandLine.Option(
+      names = {"-id", "--service-id"},
+      description = "Ozone Manager Service ID"
+  )
+  private String omServiceId;
+
+  @CommandLine.Option(
+      names = {"-host", "--service-host"},
+      description = "Ozone Manager Host"
+  )
+  private String omHost;
+
+  @Override
+  public Void call() throws Exception {
+    try (OzoneManagerProtocol ozoneManagerClient =
+             parent.getParent().createOmClient(omServiceId, omHost, false)) {
+      GetLifecycleServiceStatusResponse lifecycleServiceStatus =
+          ozoneManagerClient.getLifecycleServiceStatus();
+      output(lifecycleServiceStatus);
+    }
+    return null;
+  }
+
+  protected void output(GetLifecycleServiceStatusResponse status) {
+    PrintStream out = out();
+    out.println("========================================");
+    out.println("          Lifecycle Service Status");
+    out.println("========================================");
+    out.printf("IsEnabled: %s%n", status.getIsEnabled());
+    if (status.getIsEnabled() && status.hasIsSuspended()) {
+      out.printf("IsSuspended: %s%n", status.getIsSuspended());
+    }
+
+    if (status.getRunningBucketsCount() > 0) {
+      out.println("Running Buckets:");
+      for (String bucket : status.getRunningBucketsList()) {
+        out.printf("  - %s%n", bucket);
+      }
+    } else {
+      out.println("No buckets are currently being processed.");
+    }
+    out.println("========================================");
+  }
+
+  protected PrintStream out() {
+    return System.out;
+  }
+}
+
diff --git 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/om/LifecycleSubCommand.java
 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/om/LifecycleSubCommand.java
new file mode 100644
index 00000000000..7517618e84b
--- /dev/null
+++ 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/om/LifecycleSubCommand.java
@@ -0,0 +1,47 @@
+/*
+ * 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.admin.om;
+
+import org.apache.hadoop.hdds.cli.AdminSubcommand;
+import org.apache.hadoop.hdds.cli.HddsVersionProvider;
+import picocli.CommandLine;
+
+/**
+ * Subcommand to admin operations related to Lifecycle Service.
+ */
[email protected](
+    name = "lifecycle",
+    description = "Ozone Manager Lifecycle Service specific admin operations",
+    mixinStandardHelpOptions = true,
+    versionProvider = HddsVersionProvider.class,
+    subcommands = {
+        LifecycleStatusSubCommand.class,
+        LifecycleSuspendSubCommand.class,
+        LifecycleResumeSubCommand.class,
+    })
+public class LifecycleSubCommand implements AdminSubcommand {
+
+  @CommandLine.ParentCommand
+  private OMAdmin parent;
+
+  public OMAdmin getParent() {
+    return parent;
+  }
+
+}
+
diff --git 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/om/LifecycleSuspendSubCommand.java
 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/om/LifecycleSuspendSubCommand.java
new file mode 100644
index 00000000000..8a420c3d41b
--- /dev/null
+++ 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/om/LifecycleSuspendSubCommand.java
@@ -0,0 +1,76 @@
+/*
+ * 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.admin.om;
+
+import java.io.PrintStream;
+import java.util.concurrent.Callable;
+import org.apache.hadoop.hdds.cli.HddsVersionProvider;
+import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+
+/**
+ * Handler of ozone admin om lifecycle suspend command.
+ */
+@Command(
+    name = "suspend",
+    description = "Suspend Lifecycle Service. Use 'resume' command to resume 
it, " +
+        "or it will be re-enabled after OM restarts based on the 
configuration",
+    mixinStandardHelpOptions = true,
+    versionProvider = HddsVersionProvider.class)
+public class LifecycleSuspendSubCommand implements Callable<Void> {
+
+  @CommandLine.ParentCommand
+  private LifecycleSubCommand parent;
+
+  @CommandLine.Option(
+      names = {"-id", "--service-id"},
+      description = "Ozone Manager Service ID"
+  )
+  private String omServiceId;
+
+  @CommandLine.Option(
+      names = {"-host", "--service-host"},
+      description = "Ozone Manager Host"
+  )
+  private String omHost;
+
+  @Override
+  public Void call() throws Exception {
+    try (OzoneManagerProtocol ozoneManagerClient =
+             parent.getParent().createOmClient(omServiceId, omHost, false)) {
+      ozoneManagerClient.suspendLifecycleService();
+      output();
+    }
+    return null;
+  }
+
+  protected void output() {
+    PrintStream out = out();
+    out.println("========================================");
+    out.println("Lifecycle Service has been suspended.");
+    out.println("Use 'ozone admin om lifecycle resume' to resume it,");
+    out.println("or it will be re-enabled after OM restarts based on the 
configuration.");
+    out.println("========================================");
+  }
+
+  protected PrintStream out() {
+    return System.out;
+  }
+}
+
diff --git 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/om/OMAdmin.java
 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/om/OMAdmin.java
index d536b81be14..1ba095390d3 100644
--- 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/om/OMAdmin.java
+++ 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/om/OMAdmin.java
@@ -59,7 +59,8 @@
         UpdateRangerSubcommand.class,
         TransferOmLeaderSubCommand.class,
         FetchKeySubCommand.class,
-        LeaseSubCommand.class
+        LeaseSubCommand.class,
+        LifecycleSubCommand.class
     })
 @MetaInfServices(AdminSubcommand.class)
 public class OMAdmin implements AdminSubcommand {


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

Reply via email to