From 104a5404a6218565d672114790615053e633c22c Mon Sep 17 00:00:00 2001 From: Gargi Jaiswal Date: Wed, 28 Jan 2026 11:49:10 +0530 Subject: [PATCH 1/9] display both hostname and ip address in cli json output --- .../AbstractDiskBalancerSubCommand.java | 20 ++- .../DiskBalancerReportSubcommand.java | 7 +- .../datanode/DiskBalancerStartSubcommand.java | 4 +- .../DiskBalancerStatusSubcommand.java | 7 +- .../datanode/DiskBalancerStopSubcommand.java | 4 +- .../datanode/DiskBalancerSubCommandUtil.java | 132 ++++++++++++++++++ .../DiskBalancerUpdateSubcommand.java | 4 +- 7 files changed, 172 insertions(+), 6 deletions(-) diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/AbstractDiskBalancerSubCommand.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/AbstractDiskBalancerSubCommand.java index 1c92bd831cd8..50644e195df1 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/AbstractDiskBalancerSubCommand.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/AbstractDiskBalancerSubCommand.java @@ -220,7 +220,9 @@ protected Map getConfigurationMap() { */ private Map createErrorResult(String datanode, String errorMsg) { Map errorResult = new LinkedHashMap<>(); - errorResult.put("datanode", datanode); + // Format datanode string with hostname if available + String formattedDatanode = formatDatanodeDisplayName(datanode); + errorResult.put("datanode", formattedDatanode); errorResult.put("action", getActionName()); errorResult.put("status", "failure"); errorResult.put("errorMsg", errorMsg); @@ -233,5 +235,21 @@ private Map createErrorResult(String datanode, String errorMsg) return errorResult; } + + /** + * Format a datanode address string to include hostname if available. + * Queries SCM to get the hostname for the given IP address and port. + * + * @param address the datanode address in "ip:port" format + * @return formatted string "hostname (ip:port)" or "ip:port" if hostname is not available + */ + protected String formatDatanodeDisplayName(String address) { + try (ScmClient scmClient = new ContainerOperationClient(new OzoneConfiguration())) { + return DiskBalancerSubCommandUtil.getDatanodeHostAndIp(scmClient, address); + } catch (IOException e) { + // If SCM query fails, return original address + return address; + } + } } diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerReportSubcommand.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerReportSubcommand.java index 9f1d5347860c..ca410cea1b7c 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerReportSubcommand.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerReportSubcommand.java @@ -121,7 +121,12 @@ protected String getActionName() { private Map createReportResult( HddsProtos.DatanodeDiskBalancerInfoProto report) { Map result = new LinkedHashMap<>(); - result.put("datanode", report.getNode().getHostName()); + // Format datanode string with hostname and IP address + String[] hostnameIpPort = DiskBalancerSubCommandUtil.extractHostIpAndPort( + report.getNode()); + String formattedDatanode = DiskBalancerSubCommandUtil.getDatanodeHostAndIp( + hostnameIpPort[0], hostnameIpPort[1], Integer.parseInt(hostnameIpPort[2])); + result.put("datanode", formattedDatanode); result.put("action", "report"); result.put("status", "success"); result.put("volumeDensity", report.getCurrentVolumeDensitySum()); diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStartSubcommand.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStartSubcommand.java index 8aefc0dca9f4..d603091adf31 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStartSubcommand.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStartSubcommand.java @@ -65,7 +65,9 @@ protected Object executeCommand(String hostName) throws IOException { diskBalancerProxy.startDiskBalancer(config); Map result = new LinkedHashMap<>(); - result.put("datanode", hostName); + // Format datanode string with hostname if available + String formattedDatanode = formatDatanodeDisplayName(hostName); + result.put("datanode", formattedDatanode); result.put("action", "start"); result.put("status", "success"); Map configMap = getConfigurationMap(); diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStatusSubcommand.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStatusSubcommand.java index 34d4d1e1bc76..ffe71f33b9b6 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStatusSubcommand.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStatusSubcommand.java @@ -149,7 +149,12 @@ protected String getActionName() { */ private Map createStatusResult(DatanodeDiskBalancerInfoProto status) { Map result = new LinkedHashMap<>(); - result.put("datanode", status.getNode().getHostName()); + // Format datanode string with hostname and IP address + String[] hostnameIpPort = DiskBalancerSubCommandUtil.extractHostIpAndPort( + status.getNode()); + String formattedDatanode = DiskBalancerSubCommandUtil.getDatanodeHostAndIp( + hostnameIpPort[0], hostnameIpPort[1], Integer.parseInt(hostnameIpPort[2])); + result.put("datanode", formattedDatanode); result.put("action", "status"); result.put("status", "success"); result.put("serviceStatus", status.getRunningStatus().name()); diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStopSubcommand.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStopSubcommand.java index ba3355cb9d5f..df13533cdbbf 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStopSubcommand.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStopSubcommand.java @@ -41,7 +41,9 @@ protected Object executeCommand(String hostName) throws IOException { try { diskBalancerProxy.stopDiskBalancer(); Map result = new java.util.LinkedHashMap<>(); - result.put("datanode", hostName); + // Format datanode string with hostname if available + String formattedDatanode = formatDatanodeDisplayName(hostName); + result.put("datanode", formattedDatanode); result.put("action", "stop"); result.put("status", "success"); return result; diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerSubCommandUtil.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerSubCommandUtil.java index b5e0b4e57dde..cc590bfbcf75 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerSubCommandUtil.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerSubCommandUtil.java @@ -21,7 +21,9 @@ import static org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeOperationalState; import java.io.IOException; +import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; import org.apache.hadoop.hdds.conf.OzoneConfiguration; @@ -119,5 +121,135 @@ public static List getAllOperableNodesClientRpcAddress( return addresses; } + + /** + * Extracts hostname, IP address, and port from a DatanodeDetailsProto of status and report. + * + * @param nodeProto the DatanodeDetailsProto from the diskbalancer info + * @return array with [hostname, ipAddress, port] where port is the CLIENT_RPC port + */ + public static String[] extractHostIpAndPort(HddsProtos.DatanodeDetailsProto nodeProto) { + String hostname = nodeProto.getHostName(); + String ipAddress = nodeProto.getIpAddress(); + int port = nodeProto.getPortsList().stream() + .filter(p -> p.getName().equals( + DatanodeDetails.Port.Name.CLIENT_RPC.name())) + .mapToInt(HddsProtos.Port::getValue) + .findFirst() + .orElse(19864); // Default port if not found + return new String[]{hostname, ipAddress, String.valueOf(port)}; + } + + /** + * Gets the hostname and IP address for a datanode given its address (hostname or IP) and port. + * Queries SCM to find the matching datanode and returns both hostname and IP address. + * Internal helper method used by getDatanodeHostAndIp(ScmClient, String). + * + * @param scmClient the SCM client + * @param address the hostname or IP address of the datanode + * @param port the port of the datanode + * @return array with [hostname, ipAddress] if found, null otherwise + */ + private static String[] getHostnameAndIpFromAddress(ScmClient scmClient, + String address, int port) { + try { + // Resolve hostname to IP if it's a hostname (not an IP address) + String ipToMatch = address; + try { + // Try to resolve - if it's already an IP, getByName will return it + InetAddress inetAddr = InetAddress.getByName(address); + ipToMatch = inetAddr.getHostAddress(); + } catch (UnknownHostException e) { + // If resolution fails, use original address + } + + List nodes = scmClient.queryNode( + NodeOperationalState.IN_SERVICE, null, + HddsProtos.QueryScope.CLUSTER, ""); + + for (HddsProtos.Node node : nodes) { + DatanodeDetails details = + DatanodeDetails.getFromProtoBuf(node.getNodeID()); + Port datanodePort = details.getPort(Port.Name.CLIENT_RPC); + if (datanodePort != null && datanodePort.getValue() == port) { + String hostname = details.getHostName(); + String ipAddress = details.getIpAddress(); + // Match by IP address (more reliable than hostname matching) + if (ipToMatch.equals(ipAddress)) { + return new String[]{hostname, ipAddress}; + } + } + } + } catch (IOException e) { + // Return null if query fails + } + return null; + } + + /** + * Returns a formatted string combining hostname and IP address. + * If hostname is null or empty, returns just "ip:port". + * + * @param hostname the hostname of the datanode + * @param ipAddress the IP address of the datanode + * @param port the port of the datanode + * @return formatted string "hostname (ip:port)" or "ip:port" if hostname is not available + */ + public static String getDatanodeHostAndIp(String hostname, + String ipAddress, int port) { + String addressPort = ipAddress + ":" + port; + if (hostname != null && !hostname.isEmpty() && !hostname.equals(ipAddress)) { + return hostname + " (" + addressPort + ")"; + } + return addressPort; + } + + /** + * Parses "hostname:port", "ip:port", or "hostname" address, queries SCM to get both hostname and IP. + * Returns a formatted string combining hostname and IP address. + * + * @param scmClient the SCM client + * @param address the datanode address in "hostname:port", "ip:port", or "hostname" format + * @return formatted string "hostname (ip:port)" or "ip:port" if hostname is not available + */ + public static String getDatanodeHostAndIp(ScmClient scmClient, + String address) { + if (address == null || address.isEmpty()) { + return address; + } + + String addressPart; + int port; + + // Parse address - handle both "hostname:port" and "hostname" formats + String[] parts = address.split(":"); + if (parts.length == 2) { + // Format: "hostname:port" or "ip:port" + addressPart = parts[0]; + try { + port = Integer.parseInt(parts[1]); + } catch (NumberFormatException e) { + return address; + } + } else if (parts.length == 1) { + // Format: "hostname" or "ip" - use default port + addressPart = parts[0]; + port = HDDS_DATANODE_CLIENT_PORT_DEFAULT; + } else { + // Invalid format + return address; + } + + // Query SCM to get both hostname and IP address + String[] hostnameAndIp = getHostnameAndIpFromAddress(scmClient, addressPart, port); + if (hostnameAndIp != null && hostnameAndIp.length == 2) { + String hostname = hostnameAndIp[0]; + String ipAddress = hostnameAndIp[1]; + return getDatanodeHostAndIp(hostname, ipAddress, port); + } + + // If not found in SCM, return the original address + return address; + } } diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerUpdateSubcommand.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerUpdateSubcommand.java index 825a81c8aca0..69525694c18a 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerUpdateSubcommand.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerUpdateSubcommand.java @@ -74,7 +74,9 @@ protected Object executeCommand(String hostName) throws IOException { diskBalancerProxy.updateDiskBalancerConfiguration(config); Map result = new LinkedHashMap<>(); - result.put("datanode", hostName); + // Format datanode string with hostname if available + String formattedDatanode = formatDatanodeDisplayName(hostName); + result.put("datanode", formattedDatanode); result.put("action", "update"); result.put("status", "success"); Map configMap = getConfigurationMap(); From c03ab61da0164aec10907f544dfb5044e2474aa1 Mon Sep 17 00:00:00 2001 From: Gargi Jaiswal Date: Wed, 28 Jan 2026 12:28:02 +0530 Subject: [PATCH 2/9] use set instead of list for storing dn hostnames for cli output --- .../scm/cli/datanode/AbstractDiskBalancerSubCommand.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/AbstractDiskBalancerSubCommand.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/AbstractDiskBalancerSubCommand.java index 50644e195df1..2e6beaac5cd1 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/AbstractDiskBalancerSubCommand.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/AbstractDiskBalancerSubCommand.java @@ -20,8 +20,10 @@ import java.io.IOException; import java.util.ArrayList; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.Callable; import org.apache.hadoop.hdds.HddsConfigKeys; import org.apache.hadoop.hdds.conf.OzoneConfiguration; @@ -74,6 +76,10 @@ public Void call() throws Exception { return null; } + // Remove duplicates while preserving order + Set uniqueDatanodes = new LinkedHashSet<>(targetDatanodes); + List deduplicatedDatanodes = new ArrayList<>(uniqueDatanodes); + // Track if we're using batch mode for display isBatchMode = options.isInServiceDatanodes(); @@ -83,7 +89,7 @@ public Void call() throws Exception { List jsonResults = new ArrayList<>(); // Execute commands and collect results - for (String dn : targetDatanodes) { + for (String dn : deduplicatedDatanodes) { try { Object result = executeCommand(dn); successNodes.add(dn); From 03d374c10396b574a699f1baaefaf7bfa2bec540 Mon Sep 17 00:00:00 2001 From: Gargi Jaiswal Date: Wed, 28 Jan 2026 12:40:11 +0530 Subject: [PATCH 3/9] Updated TestDiskBalancerSubCmd --- .../datanode/TestDiskBalancerSubCommands.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestDiskBalancerSubCommands.java b/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestDiskBalancerSubCommands.java index 105cb5d8566b..304dfc3a953f 100644 --- a/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestDiskBalancerSubCommands.java +++ b/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestDiskBalancerSubCommands.java @@ -19,6 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; @@ -42,6 +43,7 @@ import org.apache.hadoop.hdds.HddsConfigKeys; import org.apache.hadoop.hdds.conf.OzoneConfiguration; import org.apache.hadoop.hdds.protocol.DiskBalancerProtocol; +import org.apache.hadoop.hdds.protocol.proto.HddsProtos; import org.apache.hadoop.hdds.protocol.proto.HddsProtos.DatanodeDetailsProto; import org.apache.hadoop.hdds.protocol.proto.HddsProtos.DatanodeDiskBalancerInfoProto; import org.apache.hadoop.hdds.protocol.proto.HddsProtos.DiskBalancerConfigurationProto; @@ -136,6 +138,33 @@ private DiskBalancerMocks setupAllMocks() { mockedUtil.when(() -> DiskBalancerSubCommandUtil .getSingleNodeDiskBalancerProxy(anyString())) .thenReturn(mockProtocol); + // Mock getDatanodeHostAndIp(ScmClient, String) to return the address as-is for tests + mockedUtil.when(() -> DiskBalancerSubCommandUtil + .getDatanodeHostAndIp(any(), anyString())) + .thenAnswer(invocation -> { + String address = invocation.getArgument(1); + return address; // Return address as-is for tests + }); + // Mock extractHostIpAndPort to return test data + mockedUtil.when(() -> DiskBalancerSubCommandUtil + .extractHostIpAndPort(any(HddsProtos.DatanodeDetailsProto.class))) + .thenAnswer(invocation -> { + HddsProtos.DatanodeDetailsProto proto = invocation.getArgument(0); + return new String[]{ + proto.getHostName(), + proto.getIpAddress(), + String.valueOf(19864) + }; + }); + // Mock getDatanodeHostAndIp(String, String, int) to format the output + mockedUtil.when(() -> DiskBalancerSubCommandUtil + .getDatanodeHostAndIp(anyString(), anyString(), anyInt())) + .thenAnswer(invocation -> { + String hostname = invocation.getArgument(0); + String ipAddress = invocation.getArgument(1); + int port = invocation.getArgument(2); + return hostname + " (" + ipAddress + ":" + port + ")"; + }); return new DiskBalancerMocks(mockedConf, mockedClient, mockedUtil); } @@ -633,6 +662,10 @@ private DatanodeDiskBalancerInfoProto createStatusProto(String hostname, DatanodeDetailsProto nodeProto = DatanodeDetailsProto.newBuilder() .setHostName(hostname) .setIpAddress("127.0.0.1") + .addPorts(HddsProtos.Port.newBuilder() + .setName("CLIENT_RPC") + .setValue(19864) + .build()) .build(); DiskBalancerConfigurationProto configProto = DiskBalancerConfigurationProto.newBuilder() @@ -684,6 +717,10 @@ private DatanodeDiskBalancerInfoProto generateRandomReportProto(String hostname) DatanodeDetailsProto nodeProto = DatanodeDetailsProto.newBuilder() .setHostName(hostname) .setIpAddress("127.0.0.1") + .addPorts(HddsProtos.Port.newBuilder() + .setName("CLIENT_RPC") + .setValue(19864) + .build()) .build(); return DatanodeDiskBalancerInfoProto.newBuilder() From 392a00687048e30880202ed634a372239726c24f Mon Sep 17 00:00:00 2001 From: Gargi Jaiswal Date: Wed, 28 Jan 2026 14:44:49 +0530 Subject: [PATCH 4/9] use both ip and host consistently in cli output --- .../DiskBalancerReportSubcommand.java | 17 +++++++++++++---- .../datanode/DiskBalancerStartSubcommand.java | 14 +++++++++++--- .../DiskBalancerStatusSubcommand.java | 19 ++++++++++++++----- .../datanode/DiskBalancerStopSubcommand.java | 14 +++++++++++--- .../DiskBalancerUpdateSubcommand.java | 14 +++++++++++--- 5 files changed, 60 insertions(+), 18 deletions(-) diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerReportSubcommand.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerReportSubcommand.java index ca410cea1b7c..0f1ad48fdeb8 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerReportSubcommand.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerReportSubcommand.java @@ -17,6 +17,8 @@ package org.apache.hadoop.hdds.scm.cli.datanode; +import static java.util.stream.Collectors.toList; + import java.io.IOException; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -72,8 +74,10 @@ protected void displayResults(List successNodes, List failedNode // Display error messages for failed nodes if (!failedNodes.isEmpty()) { - System.err.printf("Failed to get DiskBalancer report from nodes: [%s]%n", - String.join(", ", failedNodes)); + System.err.printf("Failed to get DiskBalancer report from nodes: [%s]%n", + String.join(", ", failedNodes.stream() + .map(this::formatDatanodeDisplayName) + .collect(toList()))); } // Display consolidated report for successful nodes @@ -91,7 +95,7 @@ private String generateReport(List protos) { Double.compare(b.getCurrentVolumeDensitySum(), a.getCurrentVolumeDensitySum())); StringBuilder formatBuilder = new StringBuilder("Report result:%n" + - "%-50s %s%n"); + "%-55s %s%n"); List contentList = new ArrayList<>(); contentList.add("Datanode"); @@ -99,7 +103,12 @@ private String generateReport(List protos) { for (DatanodeDiskBalancerInfoProto proto : sortedProtos) { formatBuilder.append("%-50s %s%n"); - contentList.add(proto.getNode().getHostName()); + // Format datanode string with hostname and IP address + String[] hostnameIpPort = DiskBalancerSubCommandUtil.extractHostIpAndPort( + proto.getNode()); + String formattedDatanode = DiskBalancerSubCommandUtil.getDatanodeHostAndIp( + hostnameIpPort[0], hostnameIpPort[1], Integer.parseInt(hostnameIpPort[2])); + contentList.add(formattedDatanode); contentList.add(String.valueOf(proto.getCurrentVolumeDensitySum())); } diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStartSubcommand.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStartSubcommand.java index d603091adf31..a6fd395f8f1b 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStartSubcommand.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStartSubcommand.java @@ -17,6 +17,8 @@ package org.apache.hadoop.hdds.scm.cli.datanode; +import static java.util.stream.Collectors.toList; + import java.io.IOException; import java.util.LinkedHashMap; import java.util.List; @@ -109,7 +111,9 @@ protected void displayResults(List successNodes, if (isBatchMode()) { if (!failedNodes.isEmpty()) { System.err.printf("Failed to start DiskBalancer on nodes: [%s]%n", - String.join(", ", failedNodes)); + String.join(", ", failedNodes.stream() + .map(this::formatDatanodeDisplayName) + .collect(toList()))); } else { System.out.println("Started DiskBalancer on all IN_SERVICE nodes."); } @@ -117,11 +121,15 @@ protected void displayResults(List successNodes, // Detailed message for specific nodes if (!successNodes.isEmpty()) { System.out.printf("Started DiskBalancer on nodes: [%s]%n", - String.join(", ", successNodes)); + String.join(", ", successNodes.stream() + .map(this::formatDatanodeDisplayName) + .collect(toList()))); } if (!failedNodes.isEmpty()) { System.err.printf("Failed to start DiskBalancer on nodes: [%s]%n", - String.join(", ", failedNodes)); + String.join(", ", failedNodes.stream() + .map(this::formatDatanodeDisplayName) + .collect(toList()))); } } } diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStatusSubcommand.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStatusSubcommand.java index ffe71f33b9b6..c4d4b9fafa05 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStatusSubcommand.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStatusSubcommand.java @@ -17,6 +17,8 @@ package org.apache.hadoop.hdds.scm.cli.datanode; +import static java.util.stream.Collectors.toList; + import java.io.IOException; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -72,8 +74,10 @@ protected void displayResults(List successNodes, List failedNode // Display error messages for failed nodes if (!failedNodes.isEmpty()) { - System.err.printf("Failed to get DiskBalancer status from nodes: [%s]%n", - String.join(", ", failedNodes)); + System.err.printf("Failed to get DiskBalancer status from nodes: [%s]%n", + String.join(", ", failedNodes.stream() + .map(this::formatDatanodeDisplayName) + .collect(toList()))); } // Display consolidated status for successful nodes @@ -86,7 +90,7 @@ protected void displayResults(List successNodes, List failedNode private String generateStatus(List protos) { StringBuilder formatBuilder = new StringBuilder("Status result:%n" + - "%-35s %-15s %-15s %-15s %-12s %-20s %-12s %-12s %-15s %-18s %-20s%n"); + "%-55s %-15s %-15s %-15s %-12s %-20s %-12s %-12s %-15s %-18s %-20s%n"); List contentList = new ArrayList<>(); contentList.add("Datanode"); @@ -102,12 +106,17 @@ private String generateStatus(List protos) { contentList.add("EstTimeLeft(min)"); for (HddsProtos.DatanodeDiskBalancerInfoProto proto : protos) { - formatBuilder.append("%-35s %-15s %-15s %-15s %-12s %-20s %-12s %-12s %-15s %-18s %-20s%n"); + formatBuilder.append("%-60s %-12s %-15s %-15s %-12s %-20s %-12s %-12s %-15s %-18s %-20s%n"); long estimatedTimeLeft = calculateEstimatedTimeLeft(proto); long bytesMovedMB = (long) Math.ceil(proto.getBytesMoved() / (1024.0 * 1024.0)); long bytesToMoveMB = (long) Math.ceil(proto.getBytesToMove() / (1024.0 * 1024.0)); - contentList.add(proto.getNode().getHostName()); + // Format datanode string with hostname and IP address + String[] hostnameIpPort = DiskBalancerSubCommandUtil.extractHostIpAndPort( + proto.getNode()); + String formattedDatanode = DiskBalancerSubCommandUtil.getDatanodeHostAndIp( + hostnameIpPort[0], hostnameIpPort[1], Integer.parseInt(hostnameIpPort[2])); + contentList.add(formattedDatanode); contentList.add(proto.getRunningStatus().name()); contentList.add( String.format("%.4f", proto.getDiskBalancerConf().getThreshold())); diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStopSubcommand.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStopSubcommand.java index df13533cdbbf..dcb79480756a 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStopSubcommand.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStopSubcommand.java @@ -17,6 +17,8 @@ package org.apache.hadoop.hdds.scm.cli.datanode; +import static java.util.stream.Collectors.toList; + import java.io.IOException; import java.util.List; import java.util.Map; @@ -63,7 +65,9 @@ protected void displayResults(List successNodes, List failedNode // Simpler message for batch mode if (!failedNodes.isEmpty()) { System.err.printf("Failed to stop DiskBalancer on nodes: [%s]%n", - String.join(", ", failedNodes)); + String.join(", ", failedNodes.stream() + .map(this::formatDatanodeDisplayName) + .collect(toList()))); } else { System.out.println("Stopped DiskBalancer on all IN_SERVICE nodes."); } @@ -71,11 +75,15 @@ protected void displayResults(List successNodes, List failedNode // Detailed message for specific nodes if (!successNodes.isEmpty()) { System.out.printf("Stopped DiskBalancer on nodes: [%s]%n", - String.join(", ", successNodes)); + String.join(", ", successNodes.stream() + .map(this::formatDatanodeDisplayName) + .collect(toList()))); } if (!failedNodes.isEmpty()) { System.err.printf("Failed to stop DiskBalancer on nodes: [%s]%n", - String.join(", ", failedNodes)); + String.join(", ", failedNodes.stream() + .map(this::formatDatanodeDisplayName) + .collect(toList()))); } } } diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerUpdateSubcommand.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerUpdateSubcommand.java index 69525694c18a..9777ce78fab8 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerUpdateSubcommand.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerUpdateSubcommand.java @@ -17,6 +17,8 @@ package org.apache.hadoop.hdds.scm.cli.datanode; +import static java.util.stream.Collectors.toList; + import java.io.IOException; import java.util.LinkedHashMap; import java.util.List; @@ -119,7 +121,9 @@ protected void displayResults(List successNodes, // Simpler message for batch mode if (!failedNodes.isEmpty()) { System.err.printf("Failed to update DiskBalancer configuration on nodes: [%s]%n", - String.join(", ", failedNodes)); + String.join(", ", failedNodes.stream() + .map(this::formatDatanodeDisplayName) + .collect(toList()))); } else { System.out.println("Updated DiskBalancer configuration on all IN_SERVICE nodes."); } @@ -127,11 +131,15 @@ protected void displayResults(List successNodes, // Detailed message for specific nodes if (!successNodes.isEmpty()) { System.out.printf("Updated DiskBalancer configuration on nodes: [%s]%n", - String.join(", ", successNodes)); + String.join(", ", successNodes.stream() + .map(this::formatDatanodeDisplayName) + .collect(toList()))); } if (!failedNodes.isEmpty()) { System.err.printf("Failed to update DiskBalancer configuration on nodes: [%s]%n", - String.join(", ", failedNodes)); + String.join(", ", failedNodes.stream() + .map(this::formatDatanodeDisplayName) + .collect(toList()))); } } } From bab7571ac36e0c7e6ae3dc1aa734849a8052c361 Mon Sep 17 00:00:00 2001 From: Gargi Jaiswal Date: Wed, 28 Jan 2026 15:25:11 +0530 Subject: [PATCH 5/9] fix findbugs --- .../datanode/TestDiskBalancerSubCommands.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestDiskBalancerSubCommands.java b/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestDiskBalancerSubCommands.java index 304dfc3a953f..e208d1ecb171 100644 --- a/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestDiskBalancerSubCommands.java +++ b/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestDiskBalancerSubCommands.java @@ -157,14 +157,16 @@ private DiskBalancerMocks setupAllMocks() { }; }); // Mock getDatanodeHostAndIp(String, String, int) to format the output - mockedUtil.when(() -> DiskBalancerSubCommandUtil - .getDatanodeHostAndIp(anyString(), anyString(), anyInt())) - .thenAnswer(invocation -> { - String hostname = invocation.getArgument(0); - String ipAddress = invocation.getArgument(1); - int port = invocation.getArgument(2); - return hostname + " (" + ipAddress + ":" + port + ")"; - }); + // Return value is used by Mockito internally for mock setup + mockedUtil.when(() -> { + @SuppressWarnings("RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT") + String ignored = DiskBalancerSubCommandUtil + .getDatanodeHostAndIp(anyString(), anyString(), anyInt()); + // Use the value to avoid "ignored return value" static analysis warnings. + System.out.println(ignored); + }).thenAnswer(invocation -> invocation.getArgument(0) + " (" + + invocation.getArgument(1) + ":" + + invocation.getArgument(2) + ")"); return new DiskBalancerMocks(mockedConf, mockedClient, mockedUtil); } From a70336ad12fa6a06699f4bc90a8b3544cd832fce Mon Sep 17 00:00:00 2001 From: Gargi Jaiswal Date: Wed, 11 Feb 2026 10:10:44 +0530 Subject: [PATCH 6/9] consitent header and row width --- .../hdds/scm/cli/datanode/DiskBalancerReportSubcommand.java | 4 ++-- .../hdds/scm/cli/datanode/DiskBalancerStatusSubcommand.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerReportSubcommand.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerReportSubcommand.java index 0f1ad48fdeb8..687fc5e05bf7 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerReportSubcommand.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerReportSubcommand.java @@ -95,14 +95,14 @@ private String generateReport(List protos) { Double.compare(b.getCurrentVolumeDensitySum(), a.getCurrentVolumeDensitySum())); StringBuilder formatBuilder = new StringBuilder("Report result:%n" + - "%-55s %s%n"); + "%-60s %s%n"); List contentList = new ArrayList<>(); contentList.add("Datanode"); contentList.add("VolumeDensity"); for (DatanodeDiskBalancerInfoProto proto : sortedProtos) { - formatBuilder.append("%-50s %s%n"); + formatBuilder.append("%-60s %s%n"); // Format datanode string with hostname and IP address String[] hostnameIpPort = DiskBalancerSubCommandUtil.extractHostIpAndPort( proto.getNode()); diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStatusSubcommand.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStatusSubcommand.java index c4d4b9fafa05..df19c562e647 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStatusSubcommand.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerStatusSubcommand.java @@ -90,7 +90,7 @@ protected void displayResults(List successNodes, List failedNode private String generateStatus(List protos) { StringBuilder formatBuilder = new StringBuilder("Status result:%n" + - "%-55s %-15s %-15s %-15s %-12s %-20s %-12s %-12s %-15s %-18s %-20s%n"); + "%-60s %-12s %-15s %-15s %-12s %-20s %-12s %-12s %-15s %-18s %-20s%n"); List contentList = new ArrayList<>(); contentList.add("Datanode"); From 6abf0aabede845261e29631800f0959554fd9638 Mon Sep 17 00:00:00 2001 From: Gargi Jaiswal Date: Fri, 27 Feb 2026 11:05:57 +0530 Subject: [PATCH 7/9] address review comments --- .../AbstractDiskBalancerSubCommand.java | 2 +- .../datanode/TestDiskBalancerSubCommands.java | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/AbstractDiskBalancerSubCommand.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/AbstractDiskBalancerSubCommand.java index 2e6beaac5cd1..681046f55fca 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/AbstractDiskBalancerSubCommand.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/AbstractDiskBalancerSubCommand.java @@ -162,7 +162,7 @@ private List getAllInServiceDatanodes() { try (ScmClient scmClient = new ContainerOperationClient(new OzoneConfiguration())) { return DiskBalancerSubCommandUtil.getAllOperableNodesClientRpcAddress(scmClient); } catch (IOException e) { - System.err.println("Error querying SCM for in-service datanodes: %n" + e.getMessage()); + System.err.printf("Error querying SCM for in-service datanodes. %n%s%n", e.getMessage()); return null; } } diff --git a/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestDiskBalancerSubCommands.java b/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestDiskBalancerSubCommands.java index e208d1ecb171..95aded363149 100644 --- a/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestDiskBalancerSubCommands.java +++ b/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestDiskBalancerSubCommands.java @@ -233,6 +233,25 @@ public void testStartDiskBalancerWithMultipleNodes() throws Exception { } } + @Test + public void testStartDiskBalancerWithDuplicateHostnames() throws Exception { + DiskBalancerStartSubcommand cmd = new DiskBalancerStartSubcommand(); + doNothing().when(mockProtocol).startDiskBalancer(any(DiskBalancerConfigurationProto.class)); + + try (DiskBalancerMocks mocks = setupAllMocks()) { + + CommandLine c = new CommandLine(cmd); + c.parseArgs("host-1", "host-1", "host-2"); + cmd.call(); + + // output should show each host only once + String output = outContent.toString(DEFAULT_ENCODING); + Pattern p = Pattern.compile("Started DiskBalancer on nodes: \\[host-1, host-2\\]"); + Matcher m = p.matcher(output); + assertTrue(m.find()); + } + } + @Test public void testStartDiskBalancerWithStdin() throws Exception { DiskBalancerStartSubcommand cmd = new DiskBalancerStartSubcommand(); From 207faf43c461a1d8a954819a0dcb8a02cc2059b6 Mon Sep 17 00:00:00 2001 From: Gargi Jaiswal Date: Mon, 2 Mar 2026 10:42:48 +0530 Subject: [PATCH 8/9] reuse existing scm call for storing display names --- .../AbstractDiskBalancerSubCommand.java | 32 ++--- .../datanode/DiskBalancerSubCommandUtil.java | 116 ++---------------- .../datanode/TestDiskBalancerSubCommands.java | 15 ++- 3 files changed, 37 insertions(+), 126 deletions(-) diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/AbstractDiskBalancerSubCommand.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/AbstractDiskBalancerSubCommand.java index 681046f55fca..266795fcb085 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/AbstractDiskBalancerSubCommand.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/AbstractDiskBalancerSubCommand.java @@ -20,11 +20,10 @@ import java.io.IOException; import java.util.ArrayList; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.concurrent.Callable; +import java.util.stream.Collectors; import org.apache.hadoop.hdds.HddsConfigKeys; import org.apache.hadoop.hdds.conf.OzoneConfiguration; import org.apache.hadoop.hdds.scm.cli.ContainerOperationClient; @@ -44,6 +43,9 @@ public abstract class AbstractDiskBalancerSubCommand implements Callable { // Track if we're in batch mode to run commands on all in-service datanodes private boolean isBatchMode = false; + // Pre-fetched datanode address names for batch mode (address -> "hostname (ip:port)"); null in non-batch + private Map datanodeDisplayNames = null; + @Override public Void call() throws Exception { // Check if DiskBalancer is enabled in configuration @@ -77,8 +79,9 @@ public Void call() throws Exception { } // Remove duplicates while preserving order - Set uniqueDatanodes = new LinkedHashSet<>(targetDatanodes); - List deduplicatedDatanodes = new ArrayList<>(uniqueDatanodes); + List deduplicatedDatanodes = targetDatanodes.stream() + .distinct() + .collect(Collectors.toList()); // Track if we're using batch mode for display isBatchMode = options.isInServiceDatanodes(); @@ -151,6 +154,7 @@ private List getTargetDatanodes() { if (options.isInServiceDatanodes()) { return getAllInServiceDatanodes(); } else { + datanodeDisplayNames = null; // Non-batch: use user input as-is, no SCM for formatting return options.getDatanodes(); } } @@ -160,7 +164,8 @@ private List getTargetDatanodes() { */ private List getAllInServiceDatanodes() { try (ScmClient scmClient = new ContainerOperationClient(new OzoneConfiguration())) { - return DiskBalancerSubCommandUtil.getAllOperableNodesClientRpcAddress(scmClient); + datanodeDisplayNames = DiskBalancerSubCommandUtil.getAllOperableNodesClientRpcAddress(scmClient); + return new ArrayList<>(datanodeDisplayNames.keySet()); } catch (IOException e) { System.err.printf("Error querying SCM for in-service datanodes. %n%s%n", e.getMessage()); return null; @@ -243,19 +248,18 @@ private Map createErrorResult(String datanode, String errorMsg) } /** - * Format a datanode address string to include hostname if available. - * Queries SCM to get the hostname for the given IP address and port. - * + * Format a datanode address for display. + * In batch mode, uses pre-fetched display names from the SCM query. + * In non-batch mode, returns the user's input as-is (no SCM call). + * * @param address the datanode address in "ip:port" format - * @return formatted string "hostname (ip:port)" or "ip:port" if hostname is not available + * @return formatted string "hostname (ip:port)" in batch mode, or address as-is in non-batch */ protected String formatDatanodeDisplayName(String address) { - try (ScmClient scmClient = new ContainerOperationClient(new OzoneConfiguration())) { - return DiskBalancerSubCommandUtil.getDatanodeHostAndIp(scmClient, address); - } catch (IOException e) { - // If SCM query fails, return original address - return address; + if (datanodeDisplayNames != null) { + return datanodeDisplayNames.getOrDefault(address, address); } + return address; } } diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerSubCommandUtil.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerSubCommandUtil.java index cc590bfbcf75..c6b9506a1624 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerSubCommandUtil.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerSubCommandUtil.java @@ -21,11 +21,10 @@ import static org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeOperationalState; import java.io.IOException; -import java.net.InetAddress; import java.net.InetSocketAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import org.apache.hadoop.hdds.conf.OzoneConfiguration; import org.apache.hadoop.hdds.protocol.DatanodeDetails; import org.apache.hadoop.hdds.protocol.DatanodeDetails.Port; @@ -88,20 +87,20 @@ private static InetSocketAddress parseAddress(String address, int defaultPort) { } /** - * Retrieves all IN_SERVICE datanode addresses from SCM. + * Retrieves all IN_SERVICE datanode addresses with their hostnames from SCM. * Used for batch operations with --in-service-datanodes flag. - * + * * @param scmClient the SCM client - * @return list of datanode addresses in "ip:port" format + * @return map of address (ip:port) to display string (hostname (ip:port) or ip:port) * @throws IOException if SCM query fails */ - public static List getAllOperableNodesClientRpcAddress( + public static Map getAllOperableNodesClientRpcAddress( ScmClient scmClient) throws IOException { List nodes = scmClient.queryNode( NodeOperationalState.IN_SERVICE, HddsProtos.NodeState.HEALTHY, HddsProtos.QueryScope.CLUSTER, ""); - List addresses = new ArrayList<>(); + Map addressToDisplay = new LinkedHashMap<>(); for (HddsProtos.Node node : nodes) { DatanodeDetails details = DatanodeDetails.getFromProtoBuf(node.getNodeID()); @@ -110,8 +109,10 @@ public static List getAllOperableNodesClientRpcAddress( } Port port = details.getPort(Port.Name.CLIENT_RPC); if (port != null) { - // Use IP address for reliable connection (hostnames with underscores may not be valid) - addresses.add(details.getIpAddress() + ":" + port.getValue()); + String address = details.getIpAddress() + ":" + port.getValue(); + String display = getDatanodeHostAndIp( + details.getHostName(), details.getIpAddress(), port.getValue()); + addressToDisplay.put(address, display); } else { System.out.printf("host: %s(%s) %s port not found%n", details.getHostName(), details.getIpAddress(), @@ -119,7 +120,7 @@ public static List getAllOperableNodesClientRpcAddress( } } - return addresses; + return addressToDisplay; } /** @@ -140,52 +141,6 @@ public static String[] extractHostIpAndPort(HddsProtos.DatanodeDetailsProto node return new String[]{hostname, ipAddress, String.valueOf(port)}; } - /** - * Gets the hostname and IP address for a datanode given its address (hostname or IP) and port. - * Queries SCM to find the matching datanode and returns both hostname and IP address. - * Internal helper method used by getDatanodeHostAndIp(ScmClient, String). - * - * @param scmClient the SCM client - * @param address the hostname or IP address of the datanode - * @param port the port of the datanode - * @return array with [hostname, ipAddress] if found, null otherwise - */ - private static String[] getHostnameAndIpFromAddress(ScmClient scmClient, - String address, int port) { - try { - // Resolve hostname to IP if it's a hostname (not an IP address) - String ipToMatch = address; - try { - // Try to resolve - if it's already an IP, getByName will return it - InetAddress inetAddr = InetAddress.getByName(address); - ipToMatch = inetAddr.getHostAddress(); - } catch (UnknownHostException e) { - // If resolution fails, use original address - } - - List nodes = scmClient.queryNode( - NodeOperationalState.IN_SERVICE, null, - HddsProtos.QueryScope.CLUSTER, ""); - - for (HddsProtos.Node node : nodes) { - DatanodeDetails details = - DatanodeDetails.getFromProtoBuf(node.getNodeID()); - Port datanodePort = details.getPort(Port.Name.CLIENT_RPC); - if (datanodePort != null && datanodePort.getValue() == port) { - String hostname = details.getHostName(); - String ipAddress = details.getIpAddress(); - // Match by IP address (more reliable than hostname matching) - if (ipToMatch.equals(ipAddress)) { - return new String[]{hostname, ipAddress}; - } - } - } - } catch (IOException e) { - // Return null if query fails - } - return null; - } - /** * Returns a formatted string combining hostname and IP address. * If hostname is null or empty, returns just "ip:port". @@ -204,52 +159,5 @@ public static String getDatanodeHostAndIp(String hostname, return addressPort; } - /** - * Parses "hostname:port", "ip:port", or "hostname" address, queries SCM to get both hostname and IP. - * Returns a formatted string combining hostname and IP address. - * - * @param scmClient the SCM client - * @param address the datanode address in "hostname:port", "ip:port", or "hostname" format - * @return formatted string "hostname (ip:port)" or "ip:port" if hostname is not available - */ - public static String getDatanodeHostAndIp(ScmClient scmClient, - String address) { - if (address == null || address.isEmpty()) { - return address; - } - - String addressPart; - int port; - - // Parse address - handle both "hostname:port" and "hostname" formats - String[] parts = address.split(":"); - if (parts.length == 2) { - // Format: "hostname:port" or "ip:port" - addressPart = parts[0]; - try { - port = Integer.parseInt(parts[1]); - } catch (NumberFormatException e) { - return address; - } - } else if (parts.length == 1) { - // Format: "hostname" or "ip" - use default port - addressPart = parts[0]; - port = HDDS_DATANODE_CLIENT_PORT_DEFAULT; - } else { - // Invalid format - return address; - } - - // Query SCM to get both hostname and IP address - String[] hostnameAndIp = getHostnameAndIpFromAddress(scmClient, addressPart, port); - if (hostnameAndIp != null && hostnameAndIp.length == 2) { - String hostname = hostnameAndIp[0]; - String ipAddress = hostnameAndIp[1]; - return getDatanodeHostAndIp(hostname, ipAddress, port); - } - - // If not found in SCM, return the original address - return address; - } } diff --git a/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestDiskBalancerSubCommands.java b/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestDiskBalancerSubCommands.java index 95aded363149..0c5b7709ced3 100644 --- a/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestDiskBalancerSubCommands.java +++ b/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestDiskBalancerSubCommands.java @@ -36,7 +36,9 @@ import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Random; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -132,19 +134,16 @@ private DiskBalancerMocks setupAllMocks() { MockedStatic mockedUtil = mockStatic(DiskBalancerSubCommandUtil.class); + Map addressToDisplay = new LinkedHashMap<>(); + for (String addr : inServiceDatanodes) { + addressToDisplay.put(addr, addr); + } mockedUtil.when(() -> DiskBalancerSubCommandUtil .getAllOperableNodesClientRpcAddress(any())) - .thenReturn(inServiceDatanodes); + .thenReturn(addressToDisplay); mockedUtil.when(() -> DiskBalancerSubCommandUtil .getSingleNodeDiskBalancerProxy(anyString())) .thenReturn(mockProtocol); - // Mock getDatanodeHostAndIp(ScmClient, String) to return the address as-is for tests - mockedUtil.when(() -> DiskBalancerSubCommandUtil - .getDatanodeHostAndIp(any(), anyString())) - .thenAnswer(invocation -> { - String address = invocation.getArgument(1); - return address; // Return address as-is for tests - }); // Mock extractHostIpAndPort to return test data mockedUtil.when(() -> DiskBalancerSubCommandUtil .extractHostIpAndPort(any(HddsProtos.DatanodeDetailsProto.class))) From 93d2268f4fe7be1d6b0df89e4418ebd7fc127756 Mon Sep 17 00:00:00 2001 From: Gargi Jaiswal Date: Mon, 2 Mar 2026 16:37:14 +0530 Subject: [PATCH 9/9] Dummy Commit to Trigger Build