From f07288c2832a911816daa6967f899df065523cb8 Mon Sep 17 00:00:00 2001 From: Gargi Jaiswal Date: Tue, 19 May 2026 19:04:57 +0530 Subject: [PATCH 1/2] unhide disbalancer option and improved usability --- .../diskbalancer/DiskBalancerService.java | 5 +- .../TestDiskBalancerProtocolServer.java | 20 ++++--- .../src/main/proto/hdds.proto | 11 ++-- .../scm/cli/datanode/DatanodeParameters.java | 20 +++++-- .../cli/datanode/DiskBalancerCommands.java | 7 ++- .../DiskBalancerReportSubcommand.java | 59 ++++++++++++------- .../datanode/TestDiskBalancerSubCommands.java | 17 ++++-- 7 files changed, 89 insertions(+), 50 deletions(-) diff --git a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/DiskBalancerService.java b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/DiskBalancerService.java index a962c0e80ff3..e1aa9289dce5 100644 --- a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/DiskBalancerService.java +++ b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/DiskBalancerService.java @@ -745,8 +745,9 @@ public static List buildVolumeReportProto(List -", + " Then type one datanode per line and end input:", + " - Linux/macOS: Ctrl-D", + " - Windows: Ctrl-Z, then Enter", + "Examples:", + " # Piped (recommended for scripts)", + " echo -e \"DN-1\\nDN-2\" | ozone admin datanode diskbalancer status -", + " # From file having list of dns to balance", + " ozone admin datanode diskbalancer report - < datanode-lists.txt", + "Port is optional and defaults to 19864 (CLIENT_RPC port).", + "Address examples: 'DN-1', 'DN-1:19864', '192.168.1.10'." + }, arity = "0..*", paramLabel = "") diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerCommands.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerCommands.java index c912ad735508..6bfd41930b2f 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerCommands.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerCommands.java @@ -17,6 +17,8 @@ package org.apache.hadoop.hdds.scm.cli.datanode; +import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_DATANODE_DISK_BALANCER_ENABLED_KEY; + import org.apache.hadoop.hdds.cli.HddsVersionProvider; import picocli.CommandLine.Command; @@ -155,11 +157,10 @@ @Command( name = "diskbalancer", - description = "DiskBalancer specific operations. It is disabled by default." + - " To enable it, set 'hdds.datanode.disk.balancer.enabled' as true", + description = "DiskBalancer specific operations to ensure even disk utilisation." + + " It is disabled by default. To enable it, set " + HDDS_DATANODE_DISK_BALANCER_ENABLED_KEY + " as true.", mixinStandardHelpOptions = true, versionProvider = HddsVersionProvider.class, - hidden = true, subcommands = { DiskBalancerStartSubcommand.class, DiskBalancerStopSubcommand.class, 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 e124ee0bc161..bf4a48092e9d 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 @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.hadoop.hdds.cli.HddsVersionProvider; @@ -46,6 +47,8 @@ public class DiskBalancerReportSubcommand extends AbstractDiskBalancerSubCommand private final Map reports = new ConcurrentHashMap<>(); + private static final String PERCENT_FORMAT = "%.2f%%"; + @Override protected Object executeCommand(String hostName) throws IOException { DiskBalancerProtocol diskBalancerProxy = DiskBalancerSubCommandUtil @@ -101,7 +104,8 @@ private String generateReport(List protos) { StringBuilder header = new StringBuilder(); header.append("Datanode: ").append(dn).append(System.lineSeparator()) - .append("Aggregate VolumeDataDensity: ").append(p.getCurrentVolumeDensitySum()) + .append("Aggregate VolumeDataDensity: ") + .append(formatPercent(p.getCurrentVolumeDensitySum())) .append(System.lineSeparator()); if (p.hasIdealUsage() && p.hasDiskBalancerConf() @@ -110,10 +114,10 @@ private String generateReport(List protos) { double threshold = p.getDiskBalancerConf().getThreshold(); double lt = idealUsage - threshold / 100.0; double ut = idealUsage + threshold / 100.0; - header.append("IdealUsage: ").append(String.format("%.8f", idealUsage)) + header.append("IdealUsage: ").append(formatPercent(idealUsage)) .append(" | Threshold: ").append(threshold).append('%') - .append(" | ThresholdRange: (").append(String.format("%.8f", lt)) - .append(", ").append(String.format("%.8f", ut)).append(')') + .append(" | ThresholdRange: (").append(formatPercent(lt)) + .append(", ").append(formatPercent(ut)).append(')') .append(System.lineSeparator()) .append(System.lineSeparator()) .append("Volume Details:").append(System.lineSeparator()); @@ -122,11 +126,12 @@ private String generateReport(List protos) { contentList.add(header.toString()); if (p.getVolumeInfoCount() > 0 && p.hasIdealUsage()) { - formatBuilder.append("%-45s %-40s %15s %15s %30s %20s %15s %15s%n"); + formatBuilder.append("%-45s %-40s %15s %15s %15s %30s %20s %5s %5s%n"); contentList.add("StorageID"); contentList.add("StoragePath"); - contentList.add("TotalCapacity"); - contentList.add("UsedSpace"); + contentList.add("OzoneCapacity"); + contentList.add("OzoneAvailable"); + contentList.add("OzoneUsed"); contentList.add("Container Pre-AllocatedSpace"); contentList.add("EffectiveUsedSpace"); contentList.add("Utilization"); @@ -134,15 +139,16 @@ private String generateReport(List protos) { double ideal = p.getIdealUsage(); for (VolumeReportProto v : p.getVolumeInfoList()) { - formatBuilder.append("%-45s %-40s %15s %15s %30s %20s %15s %15s%n"); + formatBuilder.append("%-45s %-40s %15s %15s %15s %30s %15s %6s %6s%n"); contentList.add(v.hasStorageId() ? v.getStorageId() : "-"); contentList.add(v.hasStoragePath() ? v.getStoragePath() : "-"); - contentList.add(v.hasTotalCapacity() ? StringUtils.byteDesc(v.getTotalCapacity()) : "-"); - contentList.add(v.hasUsedSpace() ? StringUtils.byteDesc(v.getUsedSpace()) : "-"); + contentList.add(v.hasOzoneCapacity() ? StringUtils.byteDesc(v.getOzoneCapacity()) : "-"); + contentList.add(v.hasOzoneAvailable() ? StringUtils.byteDesc(v.getOzoneAvailable()) : "-"); + contentList.add(v.hasOzoneUsedSpace() ? StringUtils.byteDesc(v.getOzoneUsedSpace()) : "-"); contentList.add(StringUtils.byteDesc(v.getCommittedBytes())); contentList.add(v.hasEffectiveUsedSpace() ? StringUtils.byteDesc(v.getEffectiveUsedSpace()) : "-"); - contentList.add(String.format("%.8f", v.getUtilization())); - contentList.add(String.format("%.8f", Math.abs(v.getUtilization() - ideal))); + contentList.add(formatPercent(v.getUtilization())); + contentList.add(formatPercent(Math.abs(v.getUtilization() - ideal))); } formatBuilder.append("%n"); } @@ -160,12 +166,15 @@ private String generateReport(List protos) { .append(" IdealUsage +/- Threshold are considered balanced.%n") .append(" - VolumeDensity: Deviation of a particular volume's utilization from IdealUsage.%n") .append(" - Utilization: Ratio of actual used space to capacity (0-1) for a particular volume.%n") - .append(" - TotalCapacity: Total volume capacity.%n") - .append(" - UsedSpace: Ozone used space.%n") + .append(" - OzoneCapacity: Ozone volume capacity.%n") + .append(" - OzoneAvailable: Ozone available space.%n") + .append(" - OzoneUsed: Ozone used space.%n") .append(" - Container Pre-AllocatedSpace: Space reserved for containers not yet written to disk.%n") .append(" - EffectiveUsedSpace: This is the actual used space of volume which is visible") .append(" to the diskBalancer : (ozoneCapacity minus ozoneAvailable) + containerPreAllocatedSpace + ") - .append("move delta for source volume.%n"); + .append("move delta.%n") + .append(" - move delta: source volume space to be reclaimed after move completion;" + + " this value is reflected only when diskBalancer is running else it is 0.%n"); return String.format(formatBuilder.toString(), contentList.toArray(new String[0])); } @@ -175,6 +184,10 @@ protected String getActionName() { return "report"; } + private static String formatPercent(double ratio) { + return String.format(Locale.US, PERCENT_FORMAT, ratio * 100.0); + } + /** * Create a JSON result map for a report. * @@ -186,7 +199,7 @@ private Map toJson(DatanodeDiskBalancerInfoProto report) { result.put("datanode", DiskBalancerSubCommandUtil.getDatanodeHostAndIp(report.getNode())); result.put("action", "report"); result.put("status", "success"); - result.put("volumeDensity", report.getCurrentVolumeDensitySum()); + result.put("volumeDensity", formatPercent(report.getCurrentVolumeDensitySum())); if (report.hasIdealUsage() && report.hasDiskBalancerConf() && report.getDiskBalancerConf().hasThreshold()) { @@ -194,9 +207,10 @@ private Map toJson(DatanodeDiskBalancerInfoProto report) { double threshold = report.getDiskBalancerConf().getThreshold(); double lt = idealUsage - threshold / 100.0; double ut = idealUsage + threshold / 100.0; - result.put("idealUsage", String.format("%.8f", idealUsage)); + result.put("idealUsage", formatPercent(idealUsage)); result.put("threshold %", report.getDiskBalancerConf().getThreshold()); - result.put("thresholdRange", String.format("(%.08f, %.08f)", lt, ut)); + result.put("thresholdRange", String.format("(%s, %s)", + formatPercent(lt), formatPercent(ut))); } if (report.getVolumeInfoCount() > 0) { @@ -206,13 +220,14 @@ private Map toJson(DatanodeDiskBalancerInfoProto report) { Map vm = new LinkedHashMap<>(); vm.put("storageId", v.getStorageId()); vm.put("storagePath", v.hasStoragePath() ? v.getStoragePath() : "-"); - vm.put("totalCapacity", v.hasTotalCapacity() ? StringUtils.byteDesc(v.getTotalCapacity()) : "-"); - vm.put("usedSpace", v.hasUsedSpace() ? StringUtils.byteDesc(v.getUsedSpace()) : "-"); + vm.put("ozoneCapacity", v.hasOzoneCapacity() ? StringUtils.byteDesc(v.getOzoneCapacity()) : "-"); + vm.put("ozoneAvailable", v.hasOzoneAvailable() ? StringUtils.byteDesc(v.getOzoneAvailable()) : "-"); + vm.put("ozoneUsed", v.hasOzoneUsedSpace() ? StringUtils.byteDesc(v.getOzoneUsedSpace()) : "-"); vm.put("containerPreAllocatedSpace", StringUtils.byteDesc(v.getCommittedBytes())); vm.put("effectiveUsedSpace", v.hasEffectiveUsedSpace() ? StringUtils.byteDesc(v.getEffectiveUsedSpace()) : "-"); - vm.put("utilization", v.getUtilization()); - vm.put("volumeDensity", Math.abs(v.getUtilization() - ideal)); + vm.put("utilization", formatPercent(v.getUtilization())); + vm.put("volumeDensity", formatPercent(Math.abs(v.getUtilization() - ideal))); vols.add(vm); } 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 fd6450c1124a..ba709387be07 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 @@ -638,8 +638,9 @@ public void testReportDiskBalancerWithJson() throws Exception { assertTrue(output.contains("\"volumes\"")); assertTrue(output.contains("\"storageId\"")); assertTrue(output.contains("\"storagePath\"")); - assertTrue(output.contains("\"totalCapacity\"")); - assertTrue(output.contains("\"usedSpace\"")); + assertTrue(output.contains("\"ozoneCapacity\"")); + assertTrue(output.contains("\"ozoneAvailable\"")); + assertTrue(output.contains("\"ozoneUsed\"")); assertTrue(output.contains("\"effectiveUsedSpace\"")); assertTrue(output.contains("\"utilization\"")); assertTrue(output.contains("\"volumeDensity\"")); @@ -791,6 +792,8 @@ private DatanodeDiskBalancerInfoProto generateRandomReportProto(String hostname) double util2 = idealUsage - random.nextDouble() * 0.1; long used1 = (long) (capacity1 * util1); long used2 = (long) (capacity2 * util2); + long available1 = capacity1 - used1; + long available2 = capacity2 - used2; long effective1 = used1 + committed1; long effective2 = used2 + committed2; String path1 = "/data/hdds-" + hostname + "-1"; @@ -800,8 +803,9 @@ private DatanodeDiskBalancerInfoProto generateRandomReportProto(String hostname) .setStoragePath(path1) .setUtilization(util1) .setCommittedBytes(committed1) - .setTotalCapacity(capacity1) - .setUsedSpace(used1) + .setOzoneCapacity(capacity1) + .setOzoneAvailable(available1) + .setOzoneUsedSpace(used1) .setEffectiveUsedSpace(effective1) .build(); VolumeReportProto vol2 = VolumeReportProto.newBuilder() @@ -809,8 +813,9 @@ private DatanodeDiskBalancerInfoProto generateRandomReportProto(String hostname) .setStoragePath(path2) .setUtilization(util2) .setCommittedBytes(committed2) - .setTotalCapacity(capacity2) - .setUsedSpace(used2) + .setOzoneCapacity(capacity2) + .setOzoneAvailable(available2) + .setOzoneUsedSpace(used2) .setEffectiveUsedSpace(effective2) .build(); From a1960813df3952ecf6cdd3da00a32a2b12933573 Mon Sep 17 00:00:00 2001 From: Gargi Jaiswal Date: Wed, 20 May 2026 13:07:26 +0530 Subject: [PATCH 2/2] address review comments --- .../interface-client/src/main/proto/hdds.proto | 10 +++++----- .../scm/cli/datanode/DiskBalancerCommands.java | 2 +- .../datanode/DiskBalancerReportSubcommand.java | 18 ++++++++++-------- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/hadoop-hdds/interface-client/src/main/proto/hdds.proto b/hadoop-hdds/interface-client/src/main/proto/hdds.proto index 374507181952..c91a2734ed8a 100644 --- a/hadoop-hdds/interface-client/src/main/proto/hdds.proto +++ b/hadoop-hdds/interface-client/src/main/proto/hdds.proto @@ -587,11 +587,11 @@ message VolumeReportProto { optional string storageId = 1; optional string storagePath = 2; optional uint64 ozoneCapacity = 3; - optional uint64 ozoneAvailable = 4; - optional uint64 ozoneUsedSpace = 5; - optional uint64 committedBytes = 6; - optional uint64 effectiveUsedSpace = 7; - optional double utilization = 8; + optional uint64 ozoneUsedSpace = 4; + optional uint64 committedBytes = 5; + optional uint64 effectiveUsedSpace = 6; + optional double utilization = 7; + optional uint64 ozoneAvailable = 8; } message DatanodeDiskBalancerInfoProto { diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerCommands.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerCommands.java index 6bfd41930b2f..48bca1cbef6d 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerCommands.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/datanode/DiskBalancerCommands.java @@ -157,7 +157,7 @@ @Command( name = "diskbalancer", - description = "DiskBalancer specific operations to ensure even disk utilisation." + + description = "DiskBalancer specific operations to ensure even disk utilization." + " It is disabled by default. To enable it, set " + HDDS_DATANODE_DISK_BALANCER_ENABLED_KEY + " as true.", mixinStandardHelpOptions = true, versionProvider = HddsVersionProvider.class, 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 bf4a48092e9d..24f8b147efa4 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 @@ -115,7 +115,8 @@ private String generateReport(List protos) { double lt = idealUsage - threshold / 100.0; double ut = idealUsage + threshold / 100.0; header.append("IdealUsage: ").append(formatPercent(idealUsage)) - .append(" | Threshold: ").append(threshold).append('%') + .append(" | Threshold: ") + .append(String.format(Locale.ROOT, PERCENT_FORMAT, threshold)) .append(" | ThresholdRange: (").append(formatPercent(lt)) .append(", ").append(formatPercent(ut)).append(')') .append(System.lineSeparator()) @@ -126,20 +127,20 @@ private String generateReport(List protos) { contentList.add(header.toString()); if (p.getVolumeInfoCount() > 0 && p.hasIdealUsage()) { - formatBuilder.append("%-45s %-40s %15s %15s %15s %30s %20s %5s %5s%n"); + formatBuilder.append("%-45s %-40s %15s %15s %15s %30s %20s %15s %15s%n"); contentList.add("StorageID"); contentList.add("StoragePath"); contentList.add("OzoneCapacity"); contentList.add("OzoneAvailable"); contentList.add("OzoneUsed"); - contentList.add("Container Pre-AllocatedSpace"); + contentList.add("ContainerPreAllocatedSpace"); contentList.add("EffectiveUsedSpace"); contentList.add("Utilization"); contentList.add("VolumeDensity"); double ideal = p.getIdealUsage(); for (VolumeReportProto v : p.getVolumeInfoList()) { - formatBuilder.append("%-45s %-40s %15s %15s %15s %30s %15s %6s %6s%n"); + formatBuilder.append("%-45s %-40s %15s %15s %15s %30s %20s %15s %15s%n"); contentList.add(v.hasStorageId() ? v.getStorageId() : "-"); contentList.add(v.hasStoragePath() ? v.getStoragePath() : "-"); contentList.add(v.hasOzoneCapacity() ? StringUtils.byteDesc(v.getOzoneCapacity()) : "-"); @@ -161,15 +162,16 @@ private String generateReport(List protos) { formatBuilder.append("%nNote:%n") .append(" - Aggregate VolumeDataDensity: Sum of per-volume density (deviation from ideal);") .append(" higher means more imbalance.%n") - .append(" - IdealUsage: Target utilization ratio (0-1) when volumes are evenly balanced.%n") + .append(" - IdealUsage: Target utilization (0-100%%) when volumes are evenly balanced.%n") .append(" - ThresholdRange: Acceptable deviation (percent); volumes within") .append(" IdealUsage +/- Threshold are considered balanced.%n") .append(" - VolumeDensity: Deviation of a particular volume's utilization from IdealUsage.%n") - .append(" - Utilization: Ratio of actual used space to capacity (0-1) for a particular volume.%n") + .append(" - Utilization: how much a particular volume is utilized ") + .append("effectiveUsedSpace / ozoneCapacity) in %%.%n") .append(" - OzoneCapacity: Ozone volume capacity.%n") .append(" - OzoneAvailable: Ozone available space.%n") .append(" - OzoneUsed: Ozone used space.%n") - .append(" - Container Pre-AllocatedSpace: Space reserved for containers not yet written to disk.%n") + .append(" - ContainerPreAllocatedSpace: Space reserved for containers not yet written to disk.%n") .append(" - EffectiveUsedSpace: This is the actual used space of volume which is visible") .append(" to the diskBalancer : (ozoneCapacity minus ozoneAvailable) + containerPreAllocatedSpace + ") .append("move delta.%n") @@ -208,7 +210,7 @@ private Map toJson(DatanodeDiskBalancerInfoProto report) { double lt = idealUsage - threshold / 100.0; double ut = idealUsage + threshold / 100.0; result.put("idealUsage", formatPercent(idealUsage)); - result.put("threshold %", report.getDiskBalancerConf().getThreshold()); + result.put("threshold %", String.format(Locale.ROOT, PERCENT_FORMAT, threshold)); result.put("thresholdRange", String.format("(%s, %s)", formatPercent(lt), formatPercent(ut))); }