HDFS-7701. Support reporting per storage type quota and usage with hadoop/hdfs shell. (Contributed by Peter Shi)
This commit is contained in:
parent
838b06ac87
commit
18a3dad44a
|
@ -20,8 +20,8 @@ package org.apache.hadoop.fs;
|
||||||
import java.io.DataInput;
|
import java.io.DataInput;
|
||||||
import java.io.DataOutput;
|
import java.io.DataOutput;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.hadoop.fs.StorageType;
|
|
||||||
import org.apache.hadoop.classification.InterfaceAudience;
|
import org.apache.hadoop.classification.InterfaceAudience;
|
||||||
import org.apache.hadoop.classification.InterfaceStability;
|
import org.apache.hadoop.classification.InterfaceStability;
|
||||||
import org.apache.hadoop.io.Writable;
|
import org.apache.hadoop.io.Writable;
|
||||||
|
@ -255,6 +255,8 @@ public class ContentSummary implements Writable{
|
||||||
private static final String QUOTA_SUMMARY_FORMAT = "%12s %15s ";
|
private static final String QUOTA_SUMMARY_FORMAT = "%12s %15s ";
|
||||||
private static final String SPACE_QUOTA_SUMMARY_FORMAT = "%15s %15s ";
|
private static final String SPACE_QUOTA_SUMMARY_FORMAT = "%15s %15s ";
|
||||||
|
|
||||||
|
private static final String STORAGE_TYPE_SUMMARY_FORMAT = "%13s %17s ";
|
||||||
|
|
||||||
private static final String[] HEADER_FIELDS = new String[] { "DIR_COUNT",
|
private static final String[] HEADER_FIELDS = new String[] { "DIR_COUNT",
|
||||||
"FILE_COUNT", "CONTENT_SIZE"};
|
"FILE_COUNT", "CONTENT_SIZE"};
|
||||||
private static final String[] QUOTA_HEADER_FIELDS = new String[] { "QUOTA",
|
private static final String[] QUOTA_HEADER_FIELDS = new String[] { "QUOTA",
|
||||||
|
@ -269,6 +271,10 @@ public class ContentSummary implements Writable{
|
||||||
(Object[]) QUOTA_HEADER_FIELDS) +
|
(Object[]) QUOTA_HEADER_FIELDS) +
|
||||||
HEADER;
|
HEADER;
|
||||||
|
|
||||||
|
/** default quota display string */
|
||||||
|
private static final String QUOTA_NONE = "none";
|
||||||
|
private static final String QUOTA_INF = "inf";
|
||||||
|
|
||||||
/** Return the header of the output.
|
/** Return the header of the output.
|
||||||
* if qOption is false, output directory count, file count, and content size;
|
* if qOption is false, output directory count, file count, and content size;
|
||||||
* if qOption is true, output quota and remaining quota as well.
|
* if qOption is true, output quota and remaining quota as well.
|
||||||
|
@ -280,6 +286,26 @@ public class ContentSummary implements Writable{
|
||||||
return qOption ? QUOTA_HEADER : HEADER;
|
return qOption ? QUOTA_HEADER : HEADER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return the header of with the StorageTypes
|
||||||
|
*
|
||||||
|
* @param storageTypes
|
||||||
|
* @return storage header string
|
||||||
|
*/
|
||||||
|
public static String getStorageTypeHeader(List<StorageType> storageTypes) {
|
||||||
|
StringBuffer header = new StringBuffer();
|
||||||
|
|
||||||
|
for (StorageType st : storageTypes) {
|
||||||
|
/* the field length is 13/17 for quota and remain quota
|
||||||
|
* as the max length for quota name is ARCHIVE_QUOTA
|
||||||
|
* and remain quota name REM_ARCHIVE_QUOTA */
|
||||||
|
String storageName = st.toString();
|
||||||
|
header.append(String.format(STORAGE_TYPE_SUMMARY_FORMAT, storageName + "_QUOTA",
|
||||||
|
"REM_" + storageName + "_QUOTA"));
|
||||||
|
}
|
||||||
|
return header.toString();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the names of the fields from the summary header.
|
* Returns the names of the fields from the summary header.
|
||||||
*
|
*
|
||||||
|
@ -325,12 +351,48 @@ public class ContentSummary implements Writable{
|
||||||
* @return the string representation of the object
|
* @return the string representation of the object
|
||||||
*/
|
*/
|
||||||
public String toString(boolean qOption, boolean hOption) {
|
public String toString(boolean qOption, boolean hOption) {
|
||||||
|
return toString(qOption, hOption, false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the string representation of the object in the output format.
|
||||||
|
* if tOption is true, display the quota by storage types,
|
||||||
|
* Otherwise, same logic with #toString(boolean,boolean)
|
||||||
|
*
|
||||||
|
* @param qOption a flag indicating if quota needs to be printed or not
|
||||||
|
* @param hOption a flag indicating if human readable output if to be used
|
||||||
|
* @param tOption a flag indicating if display quota by storage types
|
||||||
|
* @param types Storage types to display
|
||||||
|
* @return the string representation of the object
|
||||||
|
*/
|
||||||
|
public String toString(boolean qOption, boolean hOption,
|
||||||
|
boolean tOption, List<StorageType> types) {
|
||||||
String prefix = "";
|
String prefix = "";
|
||||||
|
|
||||||
|
if (tOption) {
|
||||||
|
StringBuffer content = new StringBuffer();
|
||||||
|
for (StorageType st : types) {
|
||||||
|
long typeQuota = getTypeQuota(st);
|
||||||
|
long typeConsumed = getTypeConsumed(st);
|
||||||
|
String quotaStr = QUOTA_NONE;
|
||||||
|
String quotaRem = QUOTA_INF;
|
||||||
|
|
||||||
|
if (typeQuota > 0) {
|
||||||
|
quotaStr = formatSize(typeQuota, hOption);
|
||||||
|
quotaRem = formatSize(typeQuota - typeConsumed, hOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
content.append(String.format(STORAGE_TYPE_SUMMARY_FORMAT,
|
||||||
|
quotaStr, quotaRem));
|
||||||
|
}
|
||||||
|
return content.toString();
|
||||||
|
}
|
||||||
|
|
||||||
if (qOption) {
|
if (qOption) {
|
||||||
String quotaStr = "none";
|
String quotaStr = QUOTA_NONE;
|
||||||
String quotaRem = "inf";
|
String quotaRem = QUOTA_INF;
|
||||||
String spaceQuotaStr = "none";
|
String spaceQuotaStr = QUOTA_NONE;
|
||||||
String spaceQuotaRem = "inf";
|
String spaceQuotaRem = QUOTA_INF;
|
||||||
|
|
||||||
if (quota>0) {
|
if (quota>0) {
|
||||||
quotaStr = formatSize(quota, hOption);
|
quotaStr = formatSize(quota, hOption);
|
||||||
|
@ -342,14 +404,15 @@ public class ContentSummary implements Writable{
|
||||||
}
|
}
|
||||||
|
|
||||||
prefix = String.format(QUOTA_SUMMARY_FORMAT + SPACE_QUOTA_SUMMARY_FORMAT,
|
prefix = String.format(QUOTA_SUMMARY_FORMAT + SPACE_QUOTA_SUMMARY_FORMAT,
|
||||||
quotaStr, quotaRem, spaceQuotaStr, spaceQuotaRem);
|
quotaStr, quotaRem, spaceQuotaStr, spaceQuotaRem);
|
||||||
}
|
}
|
||||||
|
|
||||||
return prefix + String.format(SUMMARY_FORMAT,
|
return prefix + String.format(SUMMARY_FORMAT,
|
||||||
formatSize(directoryCount, hOption),
|
formatSize(directoryCount, hOption),
|
||||||
formatSize(fileCount, hOption),
|
formatSize(fileCount, hOption),
|
||||||
formatSize(length, hOption));
|
formatSize(length, hOption));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a size to be human readable or in bytes
|
* Formats a size to be human readable or in bytes
|
||||||
* @param size value to be formatted
|
* @param size value to be formatted
|
||||||
|
|
|
@ -31,6 +31,7 @@ import java.util.Set;
|
||||||
public class CommandFormat {
|
public class CommandFormat {
|
||||||
final int minPar, maxPar;
|
final int minPar, maxPar;
|
||||||
final Map<String, Boolean> options = new HashMap<String, Boolean>();
|
final Map<String, Boolean> options = new HashMap<String, Boolean>();
|
||||||
|
final Map<String, String> optionsWithValue = new HashMap<String, String>();
|
||||||
boolean ignoreUnknownOpts = false;
|
boolean ignoreUnknownOpts = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,6 +65,18 @@ public class CommandFormat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add option with value
|
||||||
|
*
|
||||||
|
* @param option option name
|
||||||
|
*/
|
||||||
|
public void addOptionWithValue(String option) {
|
||||||
|
if (options.containsKey(option)) {
|
||||||
|
throw new DuplicatedOptionException(option);
|
||||||
|
}
|
||||||
|
optionsWithValue.put(option, null);
|
||||||
|
}
|
||||||
|
|
||||||
/** Parse parameters starting from the given position
|
/** Parse parameters starting from the given position
|
||||||
* Consider using the variant that directly takes a List
|
* Consider using the variant that directly takes a List
|
||||||
*
|
*
|
||||||
|
@ -99,6 +112,17 @@ public class CommandFormat {
|
||||||
if (options.containsKey(opt)) {
|
if (options.containsKey(opt)) {
|
||||||
args.remove(pos);
|
args.remove(pos);
|
||||||
options.put(opt, Boolean.TRUE);
|
options.put(opt, Boolean.TRUE);
|
||||||
|
} else if (optionsWithValue.containsKey(opt)) {
|
||||||
|
args.remove(pos);
|
||||||
|
if (pos < args.size() && (args.size() > minPar)) {
|
||||||
|
arg = args.get(pos);
|
||||||
|
args.remove(pos);
|
||||||
|
} else {
|
||||||
|
arg = "";
|
||||||
|
}
|
||||||
|
if (!arg.startsWith("-") || arg.equals("-")) {
|
||||||
|
optionsWithValue.put(opt, arg);
|
||||||
|
}
|
||||||
} else if (ignoreUnknownOpts) {
|
} else if (ignoreUnknownOpts) {
|
||||||
pos++;
|
pos++;
|
||||||
} else {
|
} else {
|
||||||
|
@ -123,6 +147,18 @@ public class CommandFormat {
|
||||||
return options.containsKey(option) ? options.get(option) : false;
|
return options.containsKey(option) ? options.get(option) : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the option's value
|
||||||
|
*
|
||||||
|
* @param option option name
|
||||||
|
* @return option value
|
||||||
|
* if option exists, but no value assigned, return ""
|
||||||
|
* if option not exists, return null
|
||||||
|
*/
|
||||||
|
public String getOptValue(String option) {
|
||||||
|
return optionsWithValue.get(option);
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns all the options that are set
|
/** Returns all the options that are set
|
||||||
*
|
*
|
||||||
* @return Set<String> of the enabled options
|
* @return Set<String> of the enabled options
|
||||||
|
@ -203,4 +239,15 @@ public class CommandFormat {
|
||||||
return option;
|
return option;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used when a duplicated option is supplied to a command.
|
||||||
|
*/
|
||||||
|
public static class DuplicatedOptionException extends IllegalArgumentException {
|
||||||
|
private static final long serialVersionUID = 0L;
|
||||||
|
|
||||||
|
public DuplicatedOptionException(String duplicatedOption) {
|
||||||
|
super("option " + duplicatedOption + " already exsits!");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,10 @@
|
||||||
package org.apache.hadoop.fs.shell;
|
package org.apache.hadoop.fs.shell;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.apache.hadoop.classification.InterfaceAudience;
|
import org.apache.hadoop.classification.InterfaceAudience;
|
||||||
|
@ -27,6 +29,7 @@ import org.apache.hadoop.classification.InterfaceStability;
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.ContentSummary;
|
import org.apache.hadoop.fs.ContentSummary;
|
||||||
import org.apache.hadoop.fs.FsShell;
|
import org.apache.hadoop.fs.FsShell;
|
||||||
|
import org.apache.hadoop.fs.StorageType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Count the number of directories, files, bytes, quota, and remaining quota.
|
* Count the number of directories, files, bytes, quota, and remaining quota.
|
||||||
|
@ -46,11 +49,12 @@ public class Count extends FsCommand {
|
||||||
private static final String OPTION_QUOTA = "q";
|
private static final String OPTION_QUOTA = "q";
|
||||||
private static final String OPTION_HUMAN = "h";
|
private static final String OPTION_HUMAN = "h";
|
||||||
private static final String OPTION_HEADER = "v";
|
private static final String OPTION_HEADER = "v";
|
||||||
|
private static final String OPTION_TYPE = "t";
|
||||||
|
|
||||||
public static final String NAME = "count";
|
public static final String NAME = "count";
|
||||||
public static final String USAGE =
|
public static final String USAGE =
|
||||||
"[-" + OPTION_QUOTA + "] [-" + OPTION_HUMAN + "] [-" + OPTION_HEADER
|
"[-" + OPTION_QUOTA + "] [-" + OPTION_HUMAN + "] [-" + OPTION_HEADER
|
||||||
+ "] <path> ...";
|
+ "] [-" + OPTION_TYPE + " [<storage type>]] <path> ...";
|
||||||
public static final String DESCRIPTION =
|
public static final String DESCRIPTION =
|
||||||
"Count the number of directories, files and bytes under the paths\n" +
|
"Count the number of directories, files and bytes under the paths\n" +
|
||||||
"that match the specified file pattern. The output columns are:\n" +
|
"that match the specified file pattern. The output columns are:\n" +
|
||||||
|
@ -63,10 +67,19 @@ public class Count extends FsCommand {
|
||||||
" PATHNAME\n" +
|
" PATHNAME\n" +
|
||||||
"The -" + OPTION_HUMAN +
|
"The -" + OPTION_HUMAN +
|
||||||
" option shows file sizes in human readable format.\n" +
|
" option shows file sizes in human readable format.\n" +
|
||||||
"The -" + OPTION_HEADER + " option displays a header line.";
|
"The -" + OPTION_HEADER + " option displays a header line.\n" +
|
||||||
|
"The -" + OPTION_TYPE + " option displays quota by storage types.\n" +
|
||||||
|
"It must be used with -" + OPTION_QUOTA + " option.\n" +
|
||||||
|
"If a comma-separated list of storage types is given after the -" +
|
||||||
|
OPTION_TYPE + " option, \n" +
|
||||||
|
"it displays the quota and usage for the specified types. \n" +
|
||||||
|
"Otherwise, it displays the quota and usage for all the storage \n" +
|
||||||
|
"types that support quota";
|
||||||
|
|
||||||
private boolean showQuotas;
|
private boolean showQuotas;
|
||||||
private boolean humanReadable;
|
private boolean humanReadable;
|
||||||
|
private boolean showQuotabyType;
|
||||||
|
private List<StorageType> storageTypes = null;
|
||||||
|
|
||||||
/** Constructor */
|
/** Constructor */
|
||||||
public Count() {}
|
public Count() {}
|
||||||
|
@ -87,21 +100,54 @@ public class Count extends FsCommand {
|
||||||
protected void processOptions(LinkedList<String> args) {
|
protected void processOptions(LinkedList<String> args) {
|
||||||
CommandFormat cf = new CommandFormat(1, Integer.MAX_VALUE,
|
CommandFormat cf = new CommandFormat(1, Integer.MAX_VALUE,
|
||||||
OPTION_QUOTA, OPTION_HUMAN, OPTION_HEADER);
|
OPTION_QUOTA, OPTION_HUMAN, OPTION_HEADER);
|
||||||
|
cf.addOptionWithValue(OPTION_TYPE);
|
||||||
cf.parse(args);
|
cf.parse(args);
|
||||||
if (args.isEmpty()) { // default path is the current working directory
|
if (args.isEmpty()) { // default path is the current working directory
|
||||||
args.add(".");
|
args.add(".");
|
||||||
}
|
}
|
||||||
showQuotas = cf.getOpt(OPTION_QUOTA);
|
showQuotas = cf.getOpt(OPTION_QUOTA);
|
||||||
humanReadable = cf.getOpt(OPTION_HUMAN);
|
humanReadable = cf.getOpt(OPTION_HUMAN);
|
||||||
if (cf.getOpt(OPTION_HEADER)) {
|
|
||||||
out.println(ContentSummary.getHeader(showQuotas) + "PATHNAME");
|
if (showQuotas) {
|
||||||
|
String types = cf.getOptValue(OPTION_TYPE);
|
||||||
|
|
||||||
|
if (null != types) {
|
||||||
|
showQuotabyType = true;
|
||||||
|
storageTypes = getAndCheckStorageTypes(types);
|
||||||
|
} else {
|
||||||
|
showQuotabyType = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cf.getOpt(OPTION_HEADER)) {
|
||||||
|
if (showQuotabyType) {
|
||||||
|
out.println(ContentSummary.getStorageTypeHeader(storageTypes) + "PATHNAME");
|
||||||
|
} else {
|
||||||
|
out.println(ContentSummary.getHeader(showQuotas) + "PATHNAME");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<StorageType> getAndCheckStorageTypes(String types) {
|
||||||
|
if ("".equals(types) || "all".equalsIgnoreCase(types)) {
|
||||||
|
return StorageType.getTypesSupportingQuota();
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] typeArray = StringUtils.split(types, ',');
|
||||||
|
List<StorageType> stTypes = new ArrayList<>();
|
||||||
|
|
||||||
|
for (String t : typeArray) {
|
||||||
|
stTypes.add(StorageType.parseStorageType(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
return stTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void processPath(PathData src) throws IOException {
|
protected void processPath(PathData src) throws IOException {
|
||||||
ContentSummary summary = src.fs.getContentSummary(src.path);
|
ContentSummary summary = src.fs.getContentSummary(src.path);
|
||||||
out.println(summary.toString(showQuotas, isHumanReadable()) + src);
|
out.println(summary.toString(showQuotas, isHumanReadable(),
|
||||||
|
showQuotabyType, storageTypes) + src);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -121,4 +167,23 @@ public class Count extends FsCommand {
|
||||||
boolean isHumanReadable() {
|
boolean isHumanReadable() {
|
||||||
return humanReadable;
|
return humanReadable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* should print quota by storage types
|
||||||
|
* @return true if enables quota by storage types
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
boolean isShowQuotabyType() {
|
||||||
|
return showQuotabyType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* show specified storage types
|
||||||
|
* @return specified storagetypes
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
List<StorageType> getStorageTypes() {
|
||||||
|
return storageTypes;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,13 +24,15 @@ import java.io.PrintStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.ContentSummary;
|
|
||||||
import org.apache.hadoop.fs.FileStatus;
|
import org.apache.hadoop.fs.FileStatus;
|
||||||
import org.apache.hadoop.fs.FileSystem;
|
import org.apache.hadoop.fs.FileSystem;
|
||||||
import org.apache.hadoop.fs.FilterFileSystem;
|
|
||||||
import org.apache.hadoop.fs.Path;
|
import org.apache.hadoop.fs.Path;
|
||||||
|
import org.apache.hadoop.fs.StorageType;
|
||||||
|
import org.apache.hadoop.fs.ContentSummary;
|
||||||
|
import org.apache.hadoop.fs.FilterFileSystem;
|
||||||
import org.apache.hadoop.fs.shell.CommandFormat.NotEnoughArgumentsException;
|
import org.apache.hadoop.fs.shell.CommandFormat.NotEnoughArgumentsException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -79,11 +81,17 @@ public class TestCount {
|
||||||
LinkedList<String> options = new LinkedList<String>();
|
LinkedList<String> options = new LinkedList<String>();
|
||||||
options.add("-q");
|
options.add("-q");
|
||||||
options.add("-h");
|
options.add("-h");
|
||||||
|
options.add("-t");
|
||||||
|
options.add("SSD");
|
||||||
options.add("dummy");
|
options.add("dummy");
|
||||||
Count count = new Count();
|
Count count = new Count();
|
||||||
count.processOptions(options);
|
count.processOptions(options);
|
||||||
assertTrue(count.isShowQuotas());
|
assertTrue(count.isShowQuotas());
|
||||||
assertTrue(count.isHumanReadable());
|
assertTrue(count.isHumanReadable());
|
||||||
|
assertTrue(count.isShowQuotabyType());
|
||||||
|
assertEquals(1, count.getStorageTypes().size());
|
||||||
|
assertEquals(StorageType.SSD, count.getStorageTypes().get(0));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check no options is handled correctly
|
// check no options is handled correctly
|
||||||
|
@ -253,6 +261,112 @@ public class TestCount {
|
||||||
verify(out).println(HUMAN + NO_QUOTAS + path.toString());
|
verify(out).println(HUMAN + NO_QUOTAS + path.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void processPathWithQuotasByStorageTypesHeader() throws Exception {
|
||||||
|
Path path = new Path("mockfs:/test");
|
||||||
|
|
||||||
|
when(mockFs.getFileStatus(eq(path))).thenReturn(fileStat);
|
||||||
|
|
||||||
|
PrintStream out = mock(PrintStream.class);
|
||||||
|
|
||||||
|
Count count = new Count();
|
||||||
|
count.out = out;
|
||||||
|
|
||||||
|
LinkedList<String> options = new LinkedList<String>();
|
||||||
|
options.add("-q");
|
||||||
|
options.add("-v");
|
||||||
|
options.add("-t");
|
||||||
|
options.add("all");
|
||||||
|
options.add("dummy");
|
||||||
|
count.processOptions(options);
|
||||||
|
String withStorageTypeHeader =
|
||||||
|
// <----13---> <-------17------> <----13-----> <------17------->
|
||||||
|
" DISK_QUOTA REM_DISK_QUOTA SSD_QUOTA REM_SSD_QUOTA " +
|
||||||
|
// <----13---> <-------17------>
|
||||||
|
"ARCHIVE_QUOTA REM_ARCHIVE_QUOTA " +
|
||||||
|
"PATHNAME";
|
||||||
|
verify(out).println(withStorageTypeHeader);
|
||||||
|
verifyNoMoreInteractions(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void processPathWithQuotasBySSDStorageTypesHeader() throws Exception {
|
||||||
|
Path path = new Path("mockfs:/test");
|
||||||
|
|
||||||
|
when(mockFs.getFileStatus(eq(path))).thenReturn(fileStat);
|
||||||
|
|
||||||
|
PrintStream out = mock(PrintStream.class);
|
||||||
|
|
||||||
|
Count count = new Count();
|
||||||
|
count.out = out;
|
||||||
|
|
||||||
|
LinkedList<String> options = new LinkedList<String>();
|
||||||
|
options.add("-q");
|
||||||
|
options.add("-v");
|
||||||
|
options.add("-t");
|
||||||
|
options.add("SSD");
|
||||||
|
options.add("dummy");
|
||||||
|
count.processOptions(options);
|
||||||
|
String withStorageTypeHeader =
|
||||||
|
// <----13---> <-------17------>
|
||||||
|
" SSD_QUOTA REM_SSD_QUOTA " +
|
||||||
|
"PATHNAME";
|
||||||
|
verify(out).println(withStorageTypeHeader);
|
||||||
|
verifyNoMoreInteractions(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void processPathWithQuotasByMultipleStorageTypesContent() throws Exception {
|
||||||
|
Path path = new Path("mockfs:/test");
|
||||||
|
|
||||||
|
when(mockFs.getFileStatus(eq(path))).thenReturn(fileStat);
|
||||||
|
PathData pathData = new PathData(path.toString(), conf);
|
||||||
|
|
||||||
|
PrintStream out = mock(PrintStream.class);
|
||||||
|
|
||||||
|
Count count = new Count();
|
||||||
|
count.out = out;
|
||||||
|
|
||||||
|
LinkedList<String> options = new LinkedList<String>();
|
||||||
|
options.add("-q");
|
||||||
|
options.add("-t");
|
||||||
|
options.add("SSD,DISK");
|
||||||
|
options.add("dummy");
|
||||||
|
count.processOptions(options);
|
||||||
|
count.processPath(pathData);
|
||||||
|
String withStorageType = BYTES + StorageType.SSD.toString()
|
||||||
|
+ " " + StorageType.DISK.toString() + " " + pathData.toString();
|
||||||
|
verify(out).println(withStorageType);
|
||||||
|
verifyNoMoreInteractions(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void processPathWithQuotasByMultipleStorageTypes() throws Exception {
|
||||||
|
Path path = new Path("mockfs:/test");
|
||||||
|
|
||||||
|
when(mockFs.getFileStatus(eq(path))).thenReturn(fileStat);
|
||||||
|
|
||||||
|
PrintStream out = mock(PrintStream.class);
|
||||||
|
|
||||||
|
Count count = new Count();
|
||||||
|
count.out = out;
|
||||||
|
|
||||||
|
LinkedList<String> options = new LinkedList<String>();
|
||||||
|
options.add("-q");
|
||||||
|
options.add("-v");
|
||||||
|
options.add("-t");
|
||||||
|
options.add("SSD,DISK");
|
||||||
|
options.add("dummy");
|
||||||
|
count.processOptions(options);
|
||||||
|
String withStorageTypeHeader =
|
||||||
|
// <----13---> <------17------->
|
||||||
|
" SSD_QUOTA REM_SSD_QUOTA " +
|
||||||
|
" DISK_QUOTA REM_DISK_QUOTA " +
|
||||||
|
"PATHNAME";
|
||||||
|
verify(out).println(withStorageTypeHeader);
|
||||||
|
verifyNoMoreInteractions(out);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getCommandName() {
|
public void getCommandName() {
|
||||||
Count count = new Count();
|
Count count = new Count();
|
||||||
|
@ -289,7 +403,7 @@ public class TestCount {
|
||||||
public void getUsage() {
|
public void getUsage() {
|
||||||
Count count = new Count();
|
Count count = new Count();
|
||||||
String actual = count.getUsage();
|
String actual = count.getUsage();
|
||||||
String expected = "-count [-q] [-h] [-v] <path> ...";
|
String expected = "-count [-q] [-h] [-v] [-t [<storage type>]] <path> ...";
|
||||||
assertEquals("Count.getUsage", expected, actual);
|
assertEquals("Count.getUsage", expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,7 +420,13 @@ public class TestCount {
|
||||||
+ "QUOTA REM_QUOTA SPACE_QUOTA REM_SPACE_QUOTA\n"
|
+ "QUOTA REM_QUOTA SPACE_QUOTA REM_SPACE_QUOTA\n"
|
||||||
+ " DIR_COUNT FILE_COUNT CONTENT_SIZE PATHNAME\n"
|
+ " DIR_COUNT FILE_COUNT CONTENT_SIZE PATHNAME\n"
|
||||||
+ "The -h option shows file sizes in human readable format.\n"
|
+ "The -h option shows file sizes in human readable format.\n"
|
||||||
+ "The -v option displays a header line.";
|
+ "The -v option displays a header line.\n"
|
||||||
|
+ "The -t option displays quota by storage types.\n"
|
||||||
|
+ "It must be used with -q option.\n"
|
||||||
|
+ "If a comma-separated list of storage types is given after the -t option, \n"
|
||||||
|
+ "it displays the quota and usage for the specified types. \n"
|
||||||
|
+ "Otherwise, it displays the quota and usage for all the storage \n"
|
||||||
|
+ "types that support quota";
|
||||||
|
|
||||||
assertEquals("Count.getDescription", expected, actual);
|
assertEquals("Count.getDescription", expected, actual);
|
||||||
}
|
}
|
||||||
|
@ -321,7 +441,19 @@ public class TestCount {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString(boolean qOption, boolean hOption) {
|
public String toString(boolean qOption, boolean hOption,
|
||||||
|
boolean tOption, List<StorageType> types) {
|
||||||
|
if (tOption) {
|
||||||
|
StringBuffer result = new StringBuffer();
|
||||||
|
result.append(hOption ? HUMAN : BYTES);
|
||||||
|
|
||||||
|
for (StorageType type : types) {
|
||||||
|
result.append(type.toString());
|
||||||
|
result.append(" ");
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
if (qOption) {
|
if (qOption) {
|
||||||
if (hOption) {
|
if (hOption) {
|
||||||
return (HUMAN + WITH_QUOTAS);
|
return (HUMAN + WITH_QUOTAS);
|
||||||
|
|
|
@ -262,7 +262,7 @@
|
||||||
<comparators>
|
<comparators>
|
||||||
<comparator>
|
<comparator>
|
||||||
<type>RegexpComparator</type>
|
<type>RegexpComparator</type>
|
||||||
<expected-output>^-count \[-q\] \[-h\] \[-v\] <path> \.\.\. :( )*</expected-output>
|
<expected-output>^-count \[-q\] \[-h\] \[-v\] \[-t \[<storage type>\]\] <path> \.\.\. :( )*</expected-output>
|
||||||
</comparator>
|
</comparator>
|
||||||
<comparator>
|
<comparator>
|
||||||
<type>RegexpComparator</type>
|
<type>RegexpComparator</type>
|
||||||
|
|
|
@ -491,6 +491,9 @@ Release 2.8.0 - UNRELEASED
|
||||||
HDFS-8111. NPE thrown when invalid FSImage filename given for
|
HDFS-8111. NPE thrown when invalid FSImage filename given for
|
||||||
'hdfs oiv_legacy' cmd ( surendra singh lilhore via vinayakumarb )
|
'hdfs oiv_legacy' cmd ( surendra singh lilhore via vinayakumarb )
|
||||||
|
|
||||||
|
HDFS-7701. Support reporting per storage type quota and usage
|
||||||
|
with hadoop/hdfs shell. (Peter Shi via Arpit Agarwal)
|
||||||
|
|
||||||
Release 2.7.1 - UNRELEASED
|
Release 2.7.1 - UNRELEASED
|
||||||
|
|
||||||
INCOMPATIBLE CHANGES
|
INCOMPATIBLE CHANGES
|
||||||
|
|
Loading…
Reference in New Issue