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

sarvekshayr 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 8546bb0b637 HDDS-14619. Add option in Container Balancer CLI for 
excluding containers (#9785)
8546bb0b637 is described below

commit 8546bb0b63705d8b03a5046a97f7cff3a3438502
Author: sravani <[email protected]>
AuthorDate: Tue Feb 24 14:01:12 2026 +0530

    HDDS-14619. Add option in Container Balancer CLI for excluding containers 
(#9785)
---
 .../apache/hadoop/hdds/scm/client/ScmClient.java   |  3 ++-
 .../protocol/StorageContainerLocationProtocol.java |  3 ++-
 ...inerLocationProtocolClientSideTranslatorPB.java |  8 +++++-
 .../src/main/proto/ScmAdminProtocol.proto          |  1 +
 ...inerLocationProtocolServerSideTranslatorPB.java |  7 +++++-
 .../hdds/scm/server/SCMClientProtocolServer.java   |  9 ++++++-
 .../scm/cli/ContainerBalancerStartSubcommand.java  |  9 ++++++-
 .../hdds/scm/cli/ContainerOperationClient.java     |  5 ++--
 .../datanode/TestContainerBalancerSubCommand.java  |  4 +--
 .../src/main/smoketest/balancer/testBalancer.robot | 29 +++++++++++++++++++++-
 .../ozone/TestContainerBalancerOperations.java     | 26 ++++++++++++++++---
 11 files changed, 89 insertions(+), 15 deletions(-)

diff --git 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/scm/client/ScmClient.java
 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/scm/client/ScmClient.java
index 892dd4de1ff..5ef0515b92e 100644
--- 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/scm/client/ScmClient.java
+++ 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/scm/client/ScmClient.java
@@ -375,7 +375,8 @@ StartContainerBalancerResponseProto startContainerBalancer(
       Optional<Integer> moveReplicationTimeout,
       Optional<Boolean> networkTopologyEnable,
       Optional<String> includeNodes,
-      Optional<String> excludeNodes) throws IOException;
+      Optional<String> excludeNodes,
+      Optional<String> excludeContainers) throws IOException;
 
   /**
    * Stop ContainerBalancer.
diff --git 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/scm/protocol/StorageContainerLocationProtocol.java
 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/scm/protocol/StorageContainerLocationProtocol.java
index 92ddfa7eb8d..f0fb3378c6f 100644
--- 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/scm/protocol/StorageContainerLocationProtocol.java
+++ 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/scm/protocol/StorageContainerLocationProtocol.java
@@ -433,7 +433,8 @@ StartContainerBalancerResponseProto startContainerBalancer(
       Optional<Integer> moveReplicationTimeout,
       Optional<Boolean> networkTopologyEnable,
       Optional<String> includeNodes,
-      Optional<String> excludeNodes) throws IOException;
+      Optional<String> excludeNodes,
+      Optional<String> excludeContainers) throws IOException;
 
   /**
    * Stop ContainerBalancer.
diff --git 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/scm/protocolPB/StorageContainerLocationProtocolClientSideTranslatorPB.java
 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/scm/protocolPB/StorageContainerLocationProtocolClientSideTranslatorPB.java
index 94b2230e68b..678b40ec788 100644
--- 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/scm/protocolPB/StorageContainerLocationProtocolClientSideTranslatorPB.java
+++ 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/scm/protocolPB/StorageContainerLocationProtocolClientSideTranslatorPB.java
@@ -929,7 +929,8 @@ public StartContainerBalancerResponseProto 
startContainerBalancer(
       Optional<Integer> moveReplicationTimeout,
       Optional<Boolean> networkTopologyEnable,
       Optional<String> includeNodes,
-      Optional<String> excludeNodes) throws IOException {
+      Optional<String> excludeNodes,
+      Optional<String> excludeContainers) throws IOException {
     StartContainerBalancerRequestProto.Builder builder =
         StartContainerBalancerRequestProto.newBuilder();
     builder.setTraceID(TracingUtil.exportCurrentSpan());
@@ -1015,6 +1016,11 @@ public StartContainerBalancerResponseProto 
startContainerBalancer(
       builder.setExcludeNodes(ex);
     }
 
+    if (excludeContainers.isPresent()) {
+      String ec = excludeContainers.get();
+      builder.setExcludeContainers(ec);
+    }
+
     StartContainerBalancerRequestProto request = builder.build();
     return submitRequest(Type.StartContainerBalancer,
         builder1 -> builder1.setStartContainerBalancerRequest(request))
diff --git a/hadoop-hdds/interface-admin/src/main/proto/ScmAdminProtocol.proto 
b/hadoop-hdds/interface-admin/src/main/proto/ScmAdminProtocol.proto
index f80a50a3be9..d1aa4798bb6 100644
--- a/hadoop-hdds/interface-admin/src/main/proto/ScmAdminProtocol.proto
+++ b/hadoop-hdds/interface-admin/src/main/proto/ScmAdminProtocol.proto
@@ -603,6 +603,7 @@ message StartContainerBalancerRequestProto {
   optional bool networkTopologyEnable = 13;
   optional string includeNodes = 14;
   optional string excludeNodes = 15;
+  optional string excludeContainers = 16;
 }
 
 message StartContainerBalancerResponseProto {
diff --git 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/protocol/StorageContainerLocationProtocolServerSideTranslatorPB.java
 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/protocol/StorageContainerLocationProtocolServerSideTranslatorPB.java
index 62f06079bf0..6693f7be025 100644
--- 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/protocol/StorageContainerLocationProtocolServerSideTranslatorPB.java
+++ 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/protocol/StorageContainerLocationProtocolServerSideTranslatorPB.java
@@ -1141,6 +1141,7 @@ public StartContainerBalancerResponseProto 
startContainerBalancer(
     Optional<Boolean> networkTopologyEnable = Optional.empty();
     Optional<String> includeNodes = Optional.empty();
     Optional<String> excludeNodes = Optional.empty();
+    Optional<String> excludeContainers = Optional.empty();
 
     if (request.hasThreshold()) {
       threshold = Optional.of(request.getThreshold());
@@ -1201,12 +1202,16 @@ public StartContainerBalancerResponseProto 
startContainerBalancer(
       excludeNodes = Optional.of(request.getExcludeNodes());
     }
 
+    if (request.hasExcludeContainers()) {
+      excludeContainers = Optional.of(request.getExcludeContainers());
+    }
+
     return impl.startContainerBalancer(threshold, iterations,
         maxDatanodesPercentageToInvolvePerIteration,
         maxSizeToMovePerIterationInGB, maxSizeEnteringTargetInGB,
         maxSizeLeavingSourceInGB, balancingInterval, moveTimeout,
         moveReplicationTimeout, networkTopologyEnable, includeNodes,
-        excludeNodes);
+        excludeNodes, excludeContainers);
   }
 
   public StopContainerBalancerResponseProto stopContainerBalancer(
diff --git 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMClientProtocolServer.java
 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMClientProtocolServer.java
index 10749615274..80fbb062e90 100644
--- 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMClientProtocolServer.java
+++ 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMClientProtocolServer.java
@@ -1160,7 +1160,8 @@ public StartContainerBalancerResponseProto 
startContainerBalancer(
       Optional<Integer> moveReplicationTimeout,
       Optional<Boolean> networkTopologyEnable,
       Optional<String> includeNodes,
-      Optional<String> excludeNodes) throws IOException {
+      Optional<String> excludeNodes,
+      Optional<String> excludeContainers) throws IOException {
     Map<String, String> auditMap = Maps.newHashMap();
     try {
       getScm().checkAdminAccess(getRemoteUser(), false);
@@ -1270,6 +1271,12 @@ public StartContainerBalancerResponseProto 
startContainerBalancer(
         cbc.setExcludeNodes(ex);
       }
 
+      if (excludeContainers.isPresent()) {
+        String ec = excludeContainers.get();
+        auditMap.put("excludeContainers", (ec));
+        cbc.setExcludeContainers(ec);
+      }
+
       ContainerBalancer containerBalancer = scm.getContainerBalancer();
       containerBalancer.startBalancer(cbc);
 
diff --git 
a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/ContainerBalancerStartSubcommand.java
 
b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/ContainerBalancerStartSubcommand.java
index 3db8de84811..2a9925c3c26 100644
--- 
a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/ContainerBalancerStartSubcommand.java
+++ 
b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/ContainerBalancerStartSubcommand.java
@@ -119,6 +119,13 @@ public class ContainerBalancerStartSubcommand extends 
ScmSubcommand {
           "by default (specify \"hostname1,hostname2,hostname3\").")
   private Optional<String> excludeNodes;
 
+  @Option(names = {"--exclude-containers"},
+      description = "A list of container IDs separated by commas. " +
+          "The containers specified in this list are excluded from balancing. 
" +
+          "This configuration is empty by default " +
+          "(specify \"1,2,3\" for container IDs).")
+  private Optional<String> excludeContainers;
+
   @Override
   public void execute(ScmClient scmClient) throws IOException {
     StartContainerBalancerResponseProto response = scmClient.
@@ -127,7 +134,7 @@ public void execute(ScmClient scmClient) throws IOException 
{
         maxSizeToMovePerIterationInGB, maxSizeEnteringTargetInGB,
         maxSizeLeavingSourceInGB, balancingInterval, moveTimeout,
         moveReplicationTimeout, networkTopologyEnable, includeNodes,
-        excludeNodes);
+        excludeNodes, excludeContainers);
     if (response.getStart()) {
       System.out.println("Container Balancer started successfully.");
     } else {
diff --git 
a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/ContainerOperationClient.java
 
b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/ContainerOperationClient.java
index 61c0f4150c3..3ca49be2443 100644
--- 
a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/ContainerOperationClient.java
+++ 
b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/ContainerOperationClient.java
@@ -510,13 +510,14 @@ public StartContainerBalancerResponseProto 
startContainerBalancer(
       Optional<Integer> moveReplicationTimeout,
       Optional<Boolean> networkTopologyEnable,
       Optional<String> includeNodes,
-      Optional<String> excludeNodes) throws IOException {
+      Optional<String> excludeNodes,
+      Optional<String> excludeContainers) throws IOException {
     return storageContainerLocationClient.startContainerBalancer(threshold,
         iterations, maxDatanodesPercentageToInvolvePerIteration,
         maxSizeToMovePerIterationInGB, maxSizeEnteringTargetInGB,
         maxSizeLeavingSourceInGB, balancingInterval, moveTimeout,
         moveReplicationTimeout, networkTopologyEnable, includeNodes,
-        excludeNodes);
+        excludeNodes, excludeContainers);
   }
 
   @Override
diff --git 
a/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestContainerBalancerSubCommand.java
 
b/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestContainerBalancerSubCommand.java
index 18d3cc74a7f..d71b8d00be2 100644
--- 
a/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestContainerBalancerSubCommand.java
+++ 
b/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestContainerBalancerSubCommand.java
@@ -449,7 +449,7 @@ public void 
testContainerBalancerStartSubcommandWhenBalancerIsNotRunning()
       throws IOException {
     ScmClient scmClient = mock(ScmClient.class);
     when(scmClient.startContainerBalancer(
-        null, null, null, null, null, null, null, null, null, null, null, 
null))
+        null, null, null, null, null, null, null, null, null, null, null, 
null, null))
         .thenReturn(
             StorageContainerLocationProtocolProtos
                 .StartContainerBalancerResponseProto.newBuilder()
@@ -465,7 +465,7 @@ public void 
testContainerBalancerStartSubcommandWhenBalancerIsRunning()
       throws IOException {
     ScmClient scmClient = mock(ScmClient.class);
     when(scmClient.startContainerBalancer(
-        null, null, null, null, null, null, null, null, null, null, null, 
null))
+        null, null, null, null, null, null, null, null, null, null, null, 
null, null))
         .thenReturn(StorageContainerLocationProtocolProtos
             .StartContainerBalancerResponseProto.newBuilder()
             .setStart(false)
diff --git a/hadoop-ozone/dist/src/main/smoketest/balancer/testBalancer.robot 
b/hadoop-ozone/dist/src/main/smoketest/balancer/testBalancer.robot
index 0436348239b..7642719d9d3 100644
--- a/hadoop-ozone/dist/src/main/smoketest/balancer/testBalancer.robot
+++ b/hadoop-ozone/dist/src/main/smoketest/balancer/testBalancer.robot
@@ -66,6 +66,11 @@ Run Container Balancer
     ${result} =             Execute                         ozone admin 
containerbalancer start -t 0.1 -d 100 -i 3
                             Should Contain                  ${result}          
   Container Balancer started successfully.
 
+Run Container Balancer With Exclude Containers
+    [arguments]             ${exclude_containers}
+    ${result} =             Execute                         ozone admin 
containerbalancer start --exclude-containers "${exclude_containers}" -t 0.1 -d 
100 -i 3
+                            Should Contain                  ${result}          
   Container Balancer started successfully.
+
 Wait Finish Of Balancing
     ${result} =             Execute                         ozone admin 
containerbalancer status
                             Wait Until Keyword Succeeds      4min    10sec    
ContainerBalancer is Not Running
@@ -148,6 +153,11 @@ All container is closed
     ${output} =         Execute           ozone admin container list --state 
OPEN
                         Should Be Equal   ${output}   [ ]
 
+Get All Container IDs
+    [Documentation]    Fetches all container IDs from standard text output 
using awk
+    ${result} =        Execute    ozone admin container list | grep 
'"containerID"' | awk '{print $3}' | tr -d ',' | xargs | tr ' ' ','
+    [return]           ${result}
+
 Get Datanode Ozone Used Bytes Info
     [arguments]             ${uuid}
     ${output} =    Execute    export DATANODES=$(ozone admin datanode list 
--json) && for datanode in $(echo "$\{DATANODES\}" | jq -r '.[].id'); do ozone 
admin datanode usageinfo --uuid=$\{datanode\} --json | jq 
'{(.[0].datanodeDetails.uuid) : .[0].ozoneUsed}'; done | jq -s add
@@ -155,7 +165,7 @@ Get Datanode Ozone Used Bytes Info
     [return]          ${result}
 
 ** Test Cases ***
-Verify Container Balancer for RATIS/EC containers
+Verify exclude command CLI for Container Balancer
     Prepare For Tests
 
     Datanode In Maintenance Mode
@@ -172,6 +182,23 @@ Verify Container Balancer for RATIS/EC containers
 
     Datanode Recommission
 
+    ${all_containers} =         Get All Container IDs
+
+    Run Container Balancer With Exclude Containers          ${all_containers}
+
+    Wait Finish Of Balancing
+
+    ${datanodeOzoneUsedBytesInfoAfterContainerBalancing} =    Get Datanode 
Ozone Used Bytes Info          ${uuid}
+    Should Be Equal As Integers     ${datanodeOzoneUsedBytesInfo}    
${datanodeOzoneUsedBytesInfoAfterContainerBalancing}
+
+Verify Container Balancer for RATIS/EC containers
+
+    ${uuid} =                   Get Uuid
+    Datanode Usageinfo          ${uuid}
+
+    ${datanodeOzoneUsedBytesInfo} =    Get Datanode Ozone Used Bytes Info      
    ${uuid}
+    Should Be True    ${datanodeOzoneUsedBytesInfo} < ${SIZE}
+
     Run Container Balancer
 
     Run Balancer Status
diff --git 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestContainerBalancerOperations.java
 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestContainerBalancerOperations.java
index b759e7e8cbe..34b990278ab 100644
--- 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestContainerBalancerOperations.java
+++ 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestContainerBalancerOperations.java
@@ -25,12 +25,16 @@
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
 import org.apache.hadoop.hdds.conf.OzoneConfiguration;
 import org.apache.hadoop.hdds.scm.PlacementPolicy;
 import org.apache.hadoop.hdds.scm.ScmConfigKeys;
 import org.apache.hadoop.hdds.scm.cli.ContainerOperationClient;
 import org.apache.hadoop.hdds.scm.client.ScmClient;
+import org.apache.hadoop.hdds.scm.container.ContainerID;
 import 
org.apache.hadoop.hdds.scm.container.balancer.ContainerBalancerConfiguration;
 import 
org.apache.hadoop.hdds.scm.container.placement.algorithms.SCMContainerPlacementCapacity;
 import org.apache.ozone.test.GenericTestUtils;
@@ -89,12 +93,13 @@ public void testContainerBalancerCLIOperations() throws 
Exception {
     Optional<Boolean> networkTopologyEnable = Optional.of(false);
     Optional<String> includeNodes = Optional.of("");
     Optional<String> excludeNodes = Optional.of("");
+    Optional<String> excludeContainers = Optional.of("");
     containerBalancerClient.startContainerBalancer(threshold, iterations,
         maxDatanodesPercentageToInvolvePerIteration,
         maxSizeToMovePerIterationInGB, maxSizeEnteringTargetInGB,
         maxSizeLeavingSourceInGB, balancingInterval, moveTimeout,
         moveReplicationTimeout, networkTopologyEnable, includeNodes,
-        excludeNodes);
+        excludeNodes, excludeContainers);
     running = containerBalancerClient.getContainerBalancerStatus();
     assertTrue(running);
 
@@ -116,7 +121,7 @@ public void testContainerBalancerCLIOperations() throws 
Exception {
         maxSizeToMovePerIterationInGB, maxSizeEnteringTargetInGB,
         maxSizeLeavingSourceInGB, balancingInterval, moveTimeout,
         moveReplicationTimeout, networkTopologyEnable, includeNodes,
-        excludeNodes);
+        excludeNodes, excludeContainers);
     running = containerBalancerClient.getContainerBalancerStatus();
     assertTrue(running);
 
@@ -143,6 +148,7 @@ public void testIfCBCLIOverridesConfigs() throws Exception {
     //CLI option for iterations and balancing interval is not passed
     Optional<Integer> iterations = Optional.empty();
     Optional<Integer> balancingInterval = Optional.empty();
+    String excludedContainersList = "1,2,3";
 
     //CLI options are passed
     Optional<Double> threshold = Optional.of(0.1);
@@ -156,12 +162,13 @@ public void testIfCBCLIOverridesConfigs() throws 
Exception {
     Optional<Boolean> networkTopologyEnable = Optional.of(true);
     Optional<String> includeNodes = Optional.of("");
     Optional<String> excludeNodes = Optional.of("");
+    Optional<String> excludeContainers = Optional.of(excludedContainersList);
     containerBalancerClient.startContainerBalancer(threshold, iterations,
             maxDatanodesPercentageToInvolvePerIteration,
             maxSizeToMovePerIterationInGB, maxSizeEnteringTargetInGB,
             maxSizeLeavingSourceInGB, balancingInterval, moveTimeout,
             moveReplicationTimeout, networkTopologyEnable, includeNodes,
-            excludeNodes);
+            excludeNodes, excludeContainers);
     running = containerBalancerClient.getContainerBalancerStatus();
     assertTrue(running);
 
@@ -179,6 +186,9 @@ public void testIfCBCLIOverridesConfigs() throws Exception {
     //then it takes the CLI option.
     assertEquals(100, config.getMaxDatanodesPercentageToInvolvePerIteration());
 
+    //Verifies that the 'excludeContainers' passed via CLI overrides the 
default empty set
+    assertEquals(parseContainerIDs(excludedContainersList), 
config.getExcludeContainers());
+
     containerBalancerClient.stopContainerBalancer();
     running = containerBalancerClient.getContainerBalancerStatus();
     assertFalse(running);
@@ -207,12 +217,13 @@ public void testStopBalancerIdempotent() throws 
IOException {
     Optional<Boolean> networkTopologyEnable = Optional.of(false);
     Optional<String> includeNodes = Optional.of("");
     Optional<String> excludeNodes = Optional.of("");
+    Optional<String> excludeContainers = Optional.of("");
     containerBalancerClient.startContainerBalancer(threshold, iterations,
         maxDatanodesPercentageToInvolvePerIteration,
         maxSizeToMovePerIterationInGB, maxSizeEnteringTargetInGB,
         maxSizeLeavingSourceInGB, balancingInterval, moveTimeout,
         moveReplicationTimeout, networkTopologyEnable, includeNodes,
-        excludeNodes);
+        excludeNodes, excludeContainers);
     running = containerBalancerClient.getContainerBalancerStatus();
     assertTrue(running);
 
@@ -223,4 +234,11 @@ public void testStopBalancerIdempotent() throws 
IOException {
     // Calling stop balancer again should not throw an exception
     assertDoesNotThrow(() -> containerBalancerClient.stopContainerBalancer());
   }
+
+  private Set<ContainerID> parseContainerIDs(String containerList) {
+    return Arrays.stream(containerList.split(","))
+        .map(Long::parseLong)
+        .map(ContainerID::valueOf)
+        .collect(Collectors.toSet());
+  }
 }


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

Reply via email to