YARN-11506. The formatted yarn queue list is displayed on CLI (#5716). Contributed by Lu Yuan.
Reviewed-by: Shilun Fan <slfan1989@apache.org> Signed-off-by: Ayush Saxena <ayushsaxena@apache.org>
This commit is contained in:
parent
ba08f26a15
commit
7c54a78343
@ -23,6 +23,7 @@ import java.io.PrintWriter;
|
|||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@ -36,6 +37,7 @@ import org.apache.hadoop.classification.InterfaceStability.Unstable;
|
|||||||
import org.apache.hadoop.util.ToolRunner;
|
import org.apache.hadoop.util.ToolRunner;
|
||||||
import org.apache.hadoop.yarn.api.records.NodeLabel;
|
import org.apache.hadoop.yarn.api.records.NodeLabel;
|
||||||
import org.apache.hadoop.yarn.api.records.QueueInfo;
|
import org.apache.hadoop.yarn.api.records.QueueInfo;
|
||||||
|
import org.apache.hadoop.yarn.client.util.FormattingCLIUtils;
|
||||||
import org.apache.hadoop.yarn.exceptions.YarnException;
|
import org.apache.hadoop.yarn.exceptions.YarnException;
|
||||||
|
|
||||||
import org.apache.hadoop.classification.VisibleForTesting;
|
import org.apache.hadoop.classification.VisibleForTesting;
|
||||||
@ -222,27 +224,20 @@ public class QueueCLI extends YarnCLI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void printQueueInfos(PrintWriter writer, List<QueueInfo> queueInfos) {
|
private void printQueueInfos(PrintWriter writer, List<QueueInfo> queueInfos) {
|
||||||
writer.print(queueInfos.size() + " queues were found : \n");
|
String titleString = queueInfos.size() + " queues were found";
|
||||||
writer.print("Queue Name\tQueue Path\tState\tCapacity\tCurrent Capacity" +
|
List<String> headerStrings = Arrays.asList("Queue Name", "Queue Path", "State", "Capacity",
|
||||||
"\tMaximum Capacity\tWeight\tMaximum Parallel Apps\n");
|
"Current Capacity", "Maximum Capacity", "Weight", "Maximum Parallel Apps");
|
||||||
for (QueueInfo queueInfo : queueInfos) {
|
FormattingCLIUtils formattingCLIUtils = new FormattingCLIUtils(titleString)
|
||||||
writer.print(queueInfo.getQueueName());
|
.addHeaders(headerStrings);
|
||||||
writer.print("\t");
|
|
||||||
writer.print(queueInfo.getQueuePath());
|
|
||||||
writer.print("\t");
|
|
||||||
writer.print(queueInfo.getQueueState());
|
|
||||||
DecimalFormat df = new DecimalFormat("#.00");
|
DecimalFormat df = new DecimalFormat("#.00");
|
||||||
writer.print("\t");
|
for (QueueInfo queueInfo : queueInfos) {
|
||||||
writer.print(df.format(queueInfo.getCapacity() * 100) + "%");
|
formattingCLIUtils.addLine(queueInfo.getQueueName(), queueInfo.getQueuePath(),
|
||||||
writer.print("\t");
|
queueInfo.getQueueState(), df.format(queueInfo.getCapacity() * 100) + "%",
|
||||||
writer.print(df.format(queueInfo.getCurrentCapacity() * 100) + "%");
|
df.format(queueInfo.getCurrentCapacity() * 100) + "%",
|
||||||
writer.print("\t");
|
df.format(queueInfo.getMaximumCapacity() * 100) + "%",
|
||||||
writer.print(df.format(queueInfo.getMaximumCapacity() * 100) + "%");
|
df.format(queueInfo.getWeight()),
|
||||||
writer.print("\t");
|
queueInfo.getMaxParallelApps());
|
||||||
writer.print(df.format(queueInfo.getWeight()));
|
}
|
||||||
writer.print("\t");
|
writer.print(formattingCLIUtils.render());
|
||||||
writer.print(queueInfo.getMaxParallelApps());
|
|
||||||
writer.print("\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,277 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.hadoop.yarn.client.util;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main core class that generates the ASCII TABLE.
|
||||||
|
*/
|
||||||
|
public final class FormattingCLIUtils {
|
||||||
|
/** Table title. */
|
||||||
|
private String title;
|
||||||
|
/** Last processed row type. */
|
||||||
|
private TableRowType lastTableRowType;
|
||||||
|
/** StringBuilder object used to concatenate strings. */
|
||||||
|
private StringBuilder join;
|
||||||
|
/** An ordered Map that holds each row of data. */
|
||||||
|
private List<TableRow> tableRows;
|
||||||
|
/** Maps the maximum length of each column. */
|
||||||
|
private Map<Integer, Integer> maxColMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains the title constructor.
|
||||||
|
* @param title titleName
|
||||||
|
*/
|
||||||
|
public FormattingCLIUtils(String title) {
|
||||||
|
this.init();
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the data.
|
||||||
|
*/
|
||||||
|
private void init() {
|
||||||
|
this.join = new StringBuilder();
|
||||||
|
this.tableRows = new ArrayList<>();
|
||||||
|
this.maxColMap = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds elements from the collection to the header data in the table.
|
||||||
|
* @param headers Header data
|
||||||
|
* @return FormattingCLIUtils object
|
||||||
|
*/
|
||||||
|
public FormattingCLIUtils addHeaders(List<?> headers) {
|
||||||
|
return this.appendRows(TableRowType.HEADER, headers.toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a row of normal data to the table.
|
||||||
|
* @param objects Common row data
|
||||||
|
* @return FormattingCLIUtils object
|
||||||
|
*/
|
||||||
|
public FormattingCLIUtils addLine(Object... objects) {
|
||||||
|
return this.appendRows(TableRowType.LINE, objects);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the middle row of data to the table.
|
||||||
|
* @param tableRowType TableRowType
|
||||||
|
* @param objects Table row data
|
||||||
|
* @return FormattingCLIUtils object
|
||||||
|
*/
|
||||||
|
private FormattingCLIUtils appendRows(TableRowType tableRowType, Object... objects) {
|
||||||
|
if (objects != null && objects.length > 0) {
|
||||||
|
int len = objects.length;
|
||||||
|
if (this.maxColMap.size() > len) {
|
||||||
|
throw new IllegalArgumentException("The number of columns that inserted a row " +
|
||||||
|
"of data into the table is different from the number of previous columns, check!");
|
||||||
|
}
|
||||||
|
List<String> lines = new ArrayList<>();
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
Object o = objects[i];
|
||||||
|
String value = o == null ? "null" : o.toString();
|
||||||
|
lines.add(value);
|
||||||
|
Integer maxColSize = this.maxColMap.get(i);
|
||||||
|
if (maxColSize == null) {
|
||||||
|
this.maxColMap.put(i, value.length());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (value.length() > maxColSize) {
|
||||||
|
this.maxColMap.put(i, value.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.tableRows.add(new TableRow(tableRowType, lines));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the string for the row of the table title.
|
||||||
|
*/
|
||||||
|
private void buildTitle() {
|
||||||
|
if (this.title != null) {
|
||||||
|
int maxTitleSize = 0;
|
||||||
|
for (Integer maxColSize : this.maxColMap.values()) {
|
||||||
|
maxTitleSize += maxColSize;
|
||||||
|
}
|
||||||
|
maxTitleSize += 3 * (this.maxColMap.size() - 1);
|
||||||
|
if (this.title.length() > maxTitleSize) {
|
||||||
|
this.title = this.title.substring(0, maxTitleSize);
|
||||||
|
}
|
||||||
|
this.join.append("+");
|
||||||
|
for (int i = 0; i < maxTitleSize + 2; i++) {
|
||||||
|
this.join.append("-");
|
||||||
|
}
|
||||||
|
this.join.append("+\n")
|
||||||
|
.append("|")
|
||||||
|
.append(StrUtils.center(this.title, maxTitleSize + 2, ' '))
|
||||||
|
.append("|\n");
|
||||||
|
this.lastTableRowType = TableRowType.TITLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the table, first build the title, and then walk through each row of data to build.
|
||||||
|
*/
|
||||||
|
private void buildTable() {
|
||||||
|
this.buildTitle();
|
||||||
|
for (int i = 0, len = this.tableRows.size(); i < len; i++) {
|
||||||
|
List<String> data = this.tableRows.get(i).data;
|
||||||
|
switch (this.tableRows.get(i).tableRowType) {
|
||||||
|
case HEADER:
|
||||||
|
if (this.lastTableRowType != TableRowType.HEADER) {
|
||||||
|
this.buildRowBorder(data);
|
||||||
|
}
|
||||||
|
this.buildRowLine(data);
|
||||||
|
this.buildRowBorder(data);
|
||||||
|
break;
|
||||||
|
case LINE:
|
||||||
|
this.buildRowLine(data);
|
||||||
|
if (i == len - 1) {
|
||||||
|
this.buildRowBorder(data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to build a border row.
|
||||||
|
* @param data dataLine
|
||||||
|
*/
|
||||||
|
private void buildRowBorder(List<String> data) {
|
||||||
|
this.join.append("+");
|
||||||
|
for (int i = 0, len = data.size(); i < len; i++) {
|
||||||
|
for (int j = 0; j < this.maxColMap.get(i) + 2; j++) {
|
||||||
|
this.join.append("-");
|
||||||
|
}
|
||||||
|
this.join.append("+");
|
||||||
|
}
|
||||||
|
this.join.append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A way to build rows of data.
|
||||||
|
* @param data dataLine
|
||||||
|
*/
|
||||||
|
private void buildRowLine(List<String> data) {
|
||||||
|
this.join.append("|");
|
||||||
|
for (int i = 0, len = data.size(); i < len; i++) {
|
||||||
|
this.join.append(StrUtils.center(data.get(i), this.maxColMap.get(i) + 2, ' '))
|
||||||
|
.append("|");
|
||||||
|
}
|
||||||
|
this.join.append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rendering is born as a result.
|
||||||
|
* @return ASCII string of Table
|
||||||
|
*/
|
||||||
|
public String render() {
|
||||||
|
this.buildTable();
|
||||||
|
return this.join.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of each table row and the entity class of the data.
|
||||||
|
*/
|
||||||
|
private static class TableRow {
|
||||||
|
private TableRowType tableRowType;
|
||||||
|
private List<String> data;
|
||||||
|
TableRow(TableRowType tableRowType, List<String> data) {
|
||||||
|
this.tableRowType = tableRowType;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An enumeration class that distinguishes between table headers and normal table data.
|
||||||
|
*/
|
||||||
|
private enum TableRowType {
|
||||||
|
TITLE, HEADER, LINE
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String utility class.
|
||||||
|
*/
|
||||||
|
private static final class StrUtils {
|
||||||
|
/**
|
||||||
|
* Puts a string in the middle of a given size.
|
||||||
|
* @param str Character string
|
||||||
|
* @param size Total size
|
||||||
|
* @param padChar Fill character
|
||||||
|
* @return String result
|
||||||
|
*/
|
||||||
|
private static String center(String str, int size, char padChar) {
|
||||||
|
if (str != null && size > 0) {
|
||||||
|
int strLen = str.length();
|
||||||
|
int pads = size - strLen;
|
||||||
|
if (pads > 0) {
|
||||||
|
str = leftPad(str, strLen + pads / 2, padChar);
|
||||||
|
str = rightPad(str, size, padChar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Left-fill the given string and size.
|
||||||
|
* @param str String
|
||||||
|
* @param size totalSize
|
||||||
|
* @param padChar Fill character
|
||||||
|
* @return String result
|
||||||
|
*/
|
||||||
|
private static String leftPad(final String str, int size, char padChar) {
|
||||||
|
int pads = size - str.length();
|
||||||
|
return pads <= 0 ? str : repeat(padChar, pads).concat(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Right-fill the given string and size.
|
||||||
|
* @param str String
|
||||||
|
* @param size totalSize
|
||||||
|
* @param padChar Fill character
|
||||||
|
* @return String result
|
||||||
|
*/
|
||||||
|
private static String rightPad(final String str, int size, char padChar) {
|
||||||
|
int pads = size - str.length();
|
||||||
|
return pads <= 0 ? str : str.concat(repeat(padChar, pads));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-fill characters as strings.
|
||||||
|
* @param ch String
|
||||||
|
* @param repeat Number of repeats
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
private static String repeat(char ch, int repeat) {
|
||||||
|
char[] buf = new char[repeat];
|
||||||
|
for (int i = repeat - 1; i >= 0; i--) {
|
||||||
|
buf[i] = ch;
|
||||||
|
}
|
||||||
|
return new String(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -40,6 +40,7 @@ import java.io.PrintWriter;
|
|||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
@ -82,6 +83,7 @@ import org.apache.hadoop.yarn.api.records.SignalContainerCommand;
|
|||||||
import org.apache.hadoop.yarn.api.records.YarnApplicationAttemptState;
|
import org.apache.hadoop.yarn.api.records.YarnApplicationAttemptState;
|
||||||
import org.apache.hadoop.yarn.api.records.YarnApplicationState;
|
import org.apache.hadoop.yarn.api.records.YarnApplicationState;
|
||||||
import org.apache.hadoop.yarn.client.api.YarnClient;
|
import org.apache.hadoop.yarn.client.api.YarnClient;
|
||||||
|
import org.apache.hadoop.yarn.client.util.FormattingCLIUtils;
|
||||||
import org.apache.hadoop.yarn.conf.YarnConfiguration;
|
import org.apache.hadoop.yarn.conf.YarnConfiguration;
|
||||||
import org.apache.hadoop.yarn.exceptions.ApplicationAttemptNotFoundException;
|
import org.apache.hadoop.yarn.exceptions.ApplicationAttemptNotFoundException;
|
||||||
import org.apache.hadoop.yarn.exceptions.ApplicationNotFoundException;
|
import org.apache.hadoop.yarn.exceptions.ApplicationNotFoundException;
|
||||||
@ -1774,28 +1776,21 @@ public class TestYarnCLI {
|
|||||||
verify(client).getAllQueues();
|
verify(client).getAllQueues();
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
PrintWriter writer = new PrintWriter(baos);
|
PrintWriter writer = new PrintWriter(baos);
|
||||||
writer.print(queueInfos.size() + " queues were found : \n");
|
String titleString = queueInfos.size() + " queues were found";
|
||||||
writer.print("Queue Name\tQueue Path\tState\tCapacity\tCurrent Capacity\t" +
|
List<String> headerStrings = Arrays.asList("Queue Name", "Queue Path", "State", "Capacity",
|
||||||
"Maximum Capacity\tWeight\tMaximum Parallel Apps\n");
|
"Current Capacity", "Maximum Capacity", "Weight", "Maximum Parallel Apps");
|
||||||
for (QueueInfo queueInfoe : queueInfos) {
|
FormattingCLIUtils formattingCLIUtils = new FormattingCLIUtils(titleString)
|
||||||
writer.print(queueInfoe.getQueueName());
|
.addHeaders(headerStrings);
|
||||||
writer.print("\t");
|
|
||||||
writer.print(queueInfoe.getQueuePath());
|
|
||||||
writer.print("\t");
|
|
||||||
writer.print(queueInfoe.getQueueState());
|
|
||||||
DecimalFormat df = new DecimalFormat("#.00");
|
DecimalFormat df = new DecimalFormat("#.00");
|
||||||
writer.print("\t");
|
for (QueueInfo queueInfoe : queueInfos) {
|
||||||
writer.print(df.format(queueInfoe.getCapacity() * 100) + "%");
|
formattingCLIUtils.addLine(queueInfoe.getQueueName(), queueInfoe.getQueuePath(),
|
||||||
writer.print("\t");
|
queueInfoe.getQueueState(), df.format(queueInfoe.getCapacity() * 100) + "%",
|
||||||
writer.print(df.format(queueInfoe.getCurrentCapacity() * 100) + "%");
|
df.format(queueInfoe.getCurrentCapacity() * 100) + "%",
|
||||||
writer.print("\t");
|
df.format(queueInfoe.getMaximumCapacity() * 100) + "%",
|
||||||
writer.print(df.format(queueInfoe.getMaximumCapacity() * 100) + "%");
|
df.format(queueInfoe.getWeight()),
|
||||||
writer.print("\t");
|
queueInfoe.getMaxParallelApps());
|
||||||
writer.print(df.format(queueInfoe.getWeight()));
|
|
||||||
writer.print("\t");
|
|
||||||
writer.print(queueInfoe.getMaxParallelApps());
|
|
||||||
writer.print("\n");
|
|
||||||
}
|
}
|
||||||
|
writer.print(formattingCLIUtils.render());
|
||||||
writer.close();
|
writer.close();
|
||||||
String queueInfoStr = baos.toString("UTF-8");
|
String queueInfoStr = baos.toString("UTF-8");
|
||||||
Assert.assertEquals(queueInfoStr, sysOutStream.toString());
|
Assert.assertEquals(queueInfoStr, sysOutStream.toString());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user