Skip to content

Commit e40928a

Browse files
HDDS-14662. Container Balancer does not validate include/exclude datanode names (#9812)
1 parent b829d26 commit e40928a

2 files changed

Lines changed: 66 additions & 0 deletions

File tree

hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancer.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.io.IOException;
2222
import java.time.Duration;
2323
import java.time.OffsetDateTime;
24+
import java.util.Set;
2425
import java.util.concurrent.TimeUnit;
2526
import java.util.concurrent.atomic.AtomicInteger;
2627
import java.util.concurrent.locks.ReentrantLock;
@@ -492,6 +493,9 @@ private void validateConfiguration(ContainerBalancerConfiguration conf)
492493
LOG.warn(msg);
493494
throw new InvalidContainerBalancerConfigurationException(msg);
494495
}
496+
497+
validateNodeList(conf.getIncludeNodes(), "included");
498+
validateNodeList(conf.getExcludeNodes(), "excluded");
495499
}
496500

497501
public ContainerBalancerMetrics getMetrics() {
@@ -510,4 +514,29 @@ public String toString() {
510514
"%-30s %b%n", "Key", "Value", "Running", isBalancerRunning());
511515
return status + config.toString();
512516
}
517+
518+
/**
519+
* Validates if the provided datanodes are known by SCM.
520+
*
521+
* @param nodes set of datanode hostnames or IP addresses
522+
* @param type context label for the error message
523+
* @throws InvalidContainerBalancerConfigurationException if a node is unknown
524+
*/
525+
private void validateNodeList(Set<String> nodes, String type)
526+
throws InvalidContainerBalancerConfigurationException {
527+
if (nodes == null || nodes.isEmpty()) {
528+
return;
529+
}
530+
531+
for (String node : nodes) {
532+
// Check if SCM knows about this node by hostname or IP
533+
if (scm.getScmNodeManager().getNodesByAddress(node).isEmpty()) {
534+
String errorMessage = String.format("Invalid configuration: The %s datanode '%s' " +
535+
"does not exist or is not registered with SCM. Please check the hostname/IP.",
536+
type, node);
537+
LOG.warn(errorMessage);
538+
throw new InvalidContainerBalancerConfigurationException(errorMessage);
539+
}
540+
}
541+
}
513542
}

hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancer.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,13 @@
3535

3636
import com.google.protobuf.ByteString;
3737
import java.io.IOException;
38+
import java.util.Collections;
3839
import java.util.HashMap;
3940
import java.util.Map;
4041
import java.util.concurrent.TimeUnit;
4142
import java.util.concurrent.TimeoutException;
4243
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
44+
import org.apache.hadoop.hdds.protocol.DatanodeDetails;
4345
import org.apache.hadoop.hdds.scm.ha.SCMContext;
4446
import org.apache.hadoop.hdds.scm.ha.SCMServiceManager;
4547
import org.apache.hadoop.hdds.scm.ha.StatefulServiceStateManager;
@@ -291,6 +293,41 @@ public void testGetBalancerStatusInfo() throws Exception {
291293
assertSame(ContainerBalancerTask.Status.STOPPED, containerBalancer.getBalancerStatus());
292294
}
293295

296+
@Test
297+
public void testStartBalancerWithInvalidNodes() throws Exception {
298+
NodeManager nm = scm.getScmNodeManager();
299+
String validHost = "1.2.3.4";
300+
String invalidHost = "invalid-host-name";
301+
302+
when(nm.getNodesByAddress(invalidHost)).thenReturn(Collections.emptyList());
303+
when(nm.getNodesByAddress(validHost)).thenReturn(Collections.singletonList(mock(DatanodeDetails.class)));
304+
305+
// Test invalid includeNodes
306+
balancerConfiguration.setIncludeNodes(invalidHost);
307+
InvalidContainerBalancerConfigurationException ex =
308+
assertThrows(InvalidContainerBalancerConfigurationException.class,
309+
() -> containerBalancer.startBalancer(balancerConfiguration));
310+
assertThat(ex.getMessage()).contains(invalidHost);
311+
assertSame(ContainerBalancerTask.Status.STOPPED, containerBalancer.getBalancerStatus());
312+
313+
// Test invalid excludeNodes
314+
balancerConfiguration.setIncludeNodes("");
315+
balancerConfiguration.setExcludeNodes(invalidHost);
316+
ex = assertThrows(InvalidContainerBalancerConfigurationException.class,
317+
() -> containerBalancer.startBalancer(balancerConfiguration));
318+
assertThat(ex.getMessage()).contains(invalidHost);
319+
assertSame(ContainerBalancerTask.Status.STOPPED, containerBalancer.getBalancerStatus());
320+
321+
// Test a valid case
322+
balancerConfiguration.setExcludeNodes("");
323+
balancerConfiguration.setIncludeNodes(validHost);
324+
assertDoesNotThrow(() -> startBalancer(balancerConfiguration));
325+
assertSame(ContainerBalancerTask.Status.RUNNING, containerBalancer.getBalancerStatus());
326+
327+
stopBalancer();
328+
assertSame(ContainerBalancerTask.Status.STOPPED, containerBalancer.getBalancerStatus());
329+
}
330+
294331
private void startBalancer(ContainerBalancerConfiguration config)
295332
throws IllegalContainerBalancerStateException, IOException,
296333
InvalidContainerBalancerConfigurationException, TimeoutException {

0 commit comments

Comments
 (0)