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 1d065024dfb..944fb25e133 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 @@ -746,8 +746,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 c912ad73550..48bca1cbef6 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 utilization." + + " 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 af730199ed8..708f13bbff7 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,11 @@ private String generateReport(List protos) { double threshold = p.getDiskBalancerConf().getThreshold(); double lt = Math.max(0.0, idealUsage - threshold / 100.0); double ut = Math.min(1.0, idealUsage + threshold / 100.0); - header.append("IdealUsage: ").append(String.format("%.8f", idealUsage)) - .append(" | Threshold: ").append(threshold).append('%') - .append(" | ThresholdRange: (").append(String.format("%.8f", lt)) - .append(", ").append(String.format("%.8f", ut)).append(')') + header.append("IdealUsage: ").append(formatPercent(idealUsage)) + .append(" | Threshold: ") + .append(String.format(Locale.ROOT, PERCENT_FORMAT, threshold)) + .append(" | ThresholdRange: (").append(formatPercent(lt)) + .append(", ").append(formatPercent(ut)).append(')') .append(System.lineSeparator()) .append(System.lineSeparator()) .append("Volume Details:").append(System.lineSeparator()); @@ -122,27 +127,29 @@ 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 %15s %15s%n"); contentList.add("StorageID"); contentList.add("StoragePath"); - contentList.add("TotalCapacity"); - contentList.add("UsedSpace"); - contentList.add("Container Pre-AllocatedSpace"); + contentList.add("OzoneCapacity"); + contentList.add("OzoneAvailable"); + contentList.add("OzoneUsed"); + 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 %30s %20s %15s %15s%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.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"); } @@ -155,17 +162,21 @@ 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(" - TotalCapacity: Total volume capacity.%n") - .append(" - UsedSpace: Ozone used space.%n") - .append(" - Container Pre-AllocatedSpace: Space reserved for containers not yet written to disk.%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(" - 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 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 +186,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 +201,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 +209,10 @@ private Map toJson(DatanodeDiskBalancerInfoProto report) { double threshold = report.getDiskBalancerConf().getThreshold(); double lt = Math.max(0.0, idealUsage - threshold / 100.0); double ut = Math.min(1.0, idealUsage + threshold / 100.0); - result.put("idealUsage", String.format("%.8f", idealUsage)); - result.put("threshold %", report.getDiskBalancerConf().getThreshold()); - result.put("thresholdRange", String.format("(%.08f, %.08f)", lt, ut)); + result.put("idealUsage", formatPercent(idealUsage)); + result.put("threshold %", String.format(Locale.ROOT, PERCENT_FORMAT, threshold)); + result.put("thresholdRange", String.format("(%s, %s)", + formatPercent(lt), formatPercent(ut))); } if (report.getVolumeInfoCount() > 0) { @@ -206,13 +222,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 e5a418fb0ae..7780114a831 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 @@ -682,8 +682,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\"")); @@ -835,6 +836,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"; @@ -844,8 +847,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() @@ -853,8 +857,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();