This is an automated email from the ASF dual-hosted git repository.
siddhant 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 e40928ac0ce HDDS-14662. Container Balancer does not validate
include/exclude datanode names (#9812)
e40928ac0ce is described below
commit e40928ac0ceca13ac955b3d42e36acd9bfdeef3e
Author: sravani <[email protected]>
AuthorDate: Mon Mar 2 11:36:35 2026 +0530
HDDS-14662. Container Balancer does not validate include/exclude datanode
names (#9812)
---
.../scm/container/balancer/ContainerBalancer.java | 29 +++++++++++++++++
.../container/balancer/TestContainerBalancer.java | 37 ++++++++++++++++++++++
2 files changed, 66 insertions(+)
diff --git
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancer.java
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancer.java
index 07319471dcc..2d3d7a3bc4b 100644
---
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancer.java
+++
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancer.java
@@ -21,6 +21,7 @@
import java.io.IOException;
import java.time.Duration;
import java.time.OffsetDateTime;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
@@ -492,6 +493,9 @@ private void
validateConfiguration(ContainerBalancerConfiguration conf)
LOG.warn(msg);
throw new InvalidContainerBalancerConfigurationException(msg);
}
+
+ validateNodeList(conf.getIncludeNodes(), "included");
+ validateNodeList(conf.getExcludeNodes(), "excluded");
}
public ContainerBalancerMetrics getMetrics() {
@@ -510,4 +514,29 @@ public String toString() {
"%-30s %b%n", "Key", "Value", "Running", isBalancerRunning());
return status + config.toString();
}
+
+ /**
+ * Validates if the provided datanodes are known by SCM.
+ *
+ * @param nodes set of datanode hostnames or IP addresses
+ * @param type context label for the error message
+ * @throws InvalidContainerBalancerConfigurationException if a node is
unknown
+ */
+ private void validateNodeList(Set<String> nodes, String type)
+ throws InvalidContainerBalancerConfigurationException {
+ if (nodes == null || nodes.isEmpty()) {
+ return;
+ }
+
+ for (String node : nodes) {
+ // Check if SCM knows about this node by hostname or IP
+ if (scm.getScmNodeManager().getNodesByAddress(node).isEmpty()) {
+ String errorMessage = String.format("Invalid configuration: The %s
datanode '%s' " +
+ "does not exist or is not registered with SCM. Please check
the hostname/IP.",
+ type, node);
+ LOG.warn(errorMessage);
+ throw new InvalidContainerBalancerConfigurationException(errorMessage);
+ }
+ }
+ }
}
diff --git
a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancer.java
b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancer.java
index 33ed8f0e83c..78a6491a70e 100644
---
a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancer.java
+++
b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancer.java
@@ -35,11 +35,13 @@
import com.google.protobuf.ByteString;
import java.io.IOException;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.protocol.DatanodeDetails;
import org.apache.hadoop.hdds.scm.ha.SCMContext;
import org.apache.hadoop.hdds.scm.ha.SCMServiceManager;
import org.apache.hadoop.hdds.scm.ha.StatefulServiceStateManager;
@@ -291,6 +293,41 @@ public void testGetBalancerStatusInfo() throws Exception {
assertSame(ContainerBalancerTask.Status.STOPPED,
containerBalancer.getBalancerStatus());
}
+ @Test
+ public void testStartBalancerWithInvalidNodes() throws Exception {
+ NodeManager nm = scm.getScmNodeManager();
+ String validHost = "1.2.3.4";
+ String invalidHost = "invalid-host-name";
+
+
when(nm.getNodesByAddress(invalidHost)).thenReturn(Collections.emptyList());
+
when(nm.getNodesByAddress(validHost)).thenReturn(Collections.singletonList(mock(DatanodeDetails.class)));
+
+ // Test invalid includeNodes
+ balancerConfiguration.setIncludeNodes(invalidHost);
+ InvalidContainerBalancerConfigurationException ex =
+ assertThrows(InvalidContainerBalancerConfigurationException.class,
+ () -> containerBalancer.startBalancer(balancerConfiguration));
+ assertThat(ex.getMessage()).contains(invalidHost);
+ assertSame(ContainerBalancerTask.Status.STOPPED,
containerBalancer.getBalancerStatus());
+
+ // Test invalid excludeNodes
+ balancerConfiguration.setIncludeNodes("");
+ balancerConfiguration.setExcludeNodes(invalidHost);
+ ex = assertThrows(InvalidContainerBalancerConfigurationException.class,
+ () -> containerBalancer.startBalancer(balancerConfiguration));
+ assertThat(ex.getMessage()).contains(invalidHost);
+ assertSame(ContainerBalancerTask.Status.STOPPED,
containerBalancer.getBalancerStatus());
+
+ // Test a valid case
+ balancerConfiguration.setExcludeNodes("");
+ balancerConfiguration.setIncludeNodes(validHost);
+ assertDoesNotThrow(() -> startBalancer(balancerConfiguration));
+ assertSame(ContainerBalancerTask.Status.RUNNING,
containerBalancer.getBalancerStatus());
+
+ stopBalancer();
+ assertSame(ContainerBalancerTask.Status.STOPPED,
containerBalancer.getBalancerStatus());
+ }
+
private void startBalancer(ContainerBalancerConfiguration config)
throws IllegalContainerBalancerStateException, IOException,
InvalidContainerBalancerConfigurationException, TimeoutException {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]