Skip to content

Commit

Permalink
HADOOP-7713. dfs -count -q should label output column (Jonathan Allen…
Browse files Browse the repository at this point in the history
… via aw)
  • Loading branch information
aw-was-here committed Feb 5, 2015
1 parent 4e7ad4f commit fb06c00
Show file tree
Hide file tree
Showing 8 changed files with 237 additions and 46 deletions.
3 changes: 3 additions & 0 deletions hadoop-common-project/hadoop-common/CHANGES.txt
Expand Up @@ -154,6 +154,9 @@ Trunk (Unreleased)
HADOOP-10976. moving the source code of hadoop-tools docs to the HADOOP-10976. moving the source code of hadoop-tools docs to the
directory under hadoop-tools (Masatake Iwasaki via aw) directory under hadoop-tools (Masatake Iwasaki via aw)


HADOOP-7713. dfs -count -q should label output column (Jonathan Allen
via aw)

BUG FIXES BUG FIXES


HADOOP-11473. test-patch says "-1 overall" even when all checks are +1 HADOOP-11473. test-patch says "-1 overall" even when all checks are +1
Expand Down
Expand Up @@ -97,28 +97,35 @@ public void readFields(DataInput in) throws IOException {
this.spaceConsumed = in.readLong(); this.spaceConsumed = in.readLong();
this.spaceQuota = in.readLong(); this.spaceQuota = in.readLong();
} }

/** /**
* Output format: * Output format:
* <----12----> <----12----> <-------18-------> * <----12----> <----12----> <-------18------->
* DIR_COUNT FILE_COUNT CONTENT_SIZE FILE_NAME * DIR_COUNT FILE_COUNT CONTENT_SIZE
*/ */
private static final String STRING_FORMAT = "%12s %12s %18s "; private static final String SUMMARY_FORMAT = "%12s %12s %18s ";
/** /**
* Output format: * Output format:
* <----12----> <----15----> <----15----> <----15----> <----12----> <----12----> <-------18-------> * <----12----> <------15-----> <------15-----> <------15----->
* QUOTA REMAINING_QUATA SPACE_QUOTA SPACE_QUOTA_REM DIR_COUNT FILE_COUNT CONTENT_SIZE FILE_NAME * QUOTA REM_QUOTA SPACE_QUOTA REM_SPACE_QUOTA
* <----12----> <----12----> <-------18------->
* DIR_COUNT FILE_COUNT CONTENT_SIZE
*/ */
private static final String QUOTA_STRING_FORMAT = "%12s %15s "; private static final String QUOTA_SUMMARY_FORMAT = "%12s %15s ";
private static final String SPACE_QUOTA_STRING_FORMAT = "%15s %15s "; private static final String SPACE_QUOTA_SUMMARY_FORMAT = "%15s %15s ";


private static final String[] HEADER_FIELDS = new String[] { "DIR_COUNT",
"FILE_COUNT", "CONTENT_SIZE"};
private static final String[] QUOTA_HEADER_FIELDS = new String[] { "QUOTA",
"REM_QUOTA", "SPACE_QUOTA", "REM_SPACE_QUOTA" };

/** The header string */ /** The header string */
private static final String HEADER = String.format( private static final String HEADER = String.format(
STRING_FORMAT.replace('d', 's'), "directories", "files", "bytes"); SUMMARY_FORMAT, (Object[]) HEADER_FIELDS);


private static final String QUOTA_HEADER = String.format( private static final String QUOTA_HEADER = String.format(
QUOTA_STRING_FORMAT + SPACE_QUOTA_STRING_FORMAT, QUOTA_SUMMARY_FORMAT + SPACE_QUOTA_SUMMARY_FORMAT,
"name quota", "rem name quota", "space quota", "rem space quota") + (Object[]) QUOTA_HEADER_FIELDS) +
HEADER; HEADER;


/** Return the header of the output. /** Return the header of the output.
Expand All @@ -131,7 +138,25 @@ public void readFields(DataInput in) throws IOException {
public static String getHeader(boolean qOption) { public static String getHeader(boolean qOption) {
return qOption ? QUOTA_HEADER : HEADER; return qOption ? QUOTA_HEADER : HEADER;
} }


/**
* Returns the names of the fields from the summary header.
*
* @return names of fields as displayed in the header
*/
public static String[] getHeaderFields() {
return HEADER_FIELDS;
}

/**
* Returns the names of the fields used in the quota summary.
*
* @return names of quota fields as displayed in the header
*/
public static String[] getQuotaHeaderFields() {
return QUOTA_HEADER_FIELDS;
}

@Override @Override
public String toString() { public String toString() {
return toString(true); return toString(true);
Expand Down Expand Up @@ -175,11 +200,11 @@ public String toString(boolean qOption, boolean hOption) {
spaceQuotaRem = formatSize(spaceQuota - spaceConsumed, hOption); spaceQuotaRem = formatSize(spaceQuota - spaceConsumed, hOption);
} }


prefix = String.format(QUOTA_STRING_FORMAT + SPACE_QUOTA_STRING_FORMAT, prefix = String.format(QUOTA_SUMMARY_FORMAT + SPACE_QUOTA_SUMMARY_FORMAT,
quotaStr, quotaRem, spaceQuotaStr, spaceQuotaRem); quotaStr, quotaRem, spaceQuotaStr, spaceQuotaRem);
} }


return prefix + String.format(STRING_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));
Expand Down
Expand Up @@ -21,6 +21,7 @@
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedList; import java.util.LinkedList;


import org.apache.commons.lang.StringUtils;
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.conf.Configuration; import org.apache.hadoop.conf.Configuration;
Expand All @@ -44,18 +45,26 @@ public static void registerCommands(CommandFactory factory) {


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";


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 + "] <path> ..."; "[-" + OPTION_QUOTA + "] [-" + OPTION_HUMAN + "] [-" + OPTION_HEADER
public static final String DESCRIPTION = + "] <path> ...";
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" +
"DIR_COUNT FILE_COUNT CONTENT_SIZE FILE_NAME or\n" + StringUtils.join(ContentSummary.getHeaderFields(), ' ') +
"QUOTA REMAINING_QUOTA SPACE_QUOTA REMAINING_SPACE_QUOTA \n" + " PATHNAME\n" +
" DIR_COUNT FILE_COUNT CONTENT_SIZE FILE_NAME\n" + "or, with the -" + OPTION_QUOTA + " option:\n" +
"The -h option shows file sizes in human readable format."; StringUtils.join(ContentSummary.getQuotaHeaderFields(), ' ') + "\n" +

" " +
StringUtils.join(ContentSummary.getHeaderFields(), ' ') +
" PATHNAME\n" +
"The -" + OPTION_HUMAN +
" option shows file sizes in human readable format.\n" +
"The -" + OPTION_HEADER + " option displays a header line.";

private boolean showQuotas; private boolean showQuotas;
private boolean humanReadable; private boolean humanReadable;


Expand All @@ -65,7 +74,7 @@ public Count() {}
/** Constructor /** Constructor
* @deprecated invoke via {@link FsShell} * @deprecated invoke via {@link FsShell}
* @param cmd the count command * @param cmd the count command
* @param pos the starting index of the arguments * @param pos the starting index of the arguments
* @param conf configuration * @param conf configuration
*/ */
@Deprecated @Deprecated
Expand All @@ -77,13 +86,16 @@ public Count(String[] cmd, int pos, Configuration conf) {
@Override @Override
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_QUOTA, OPTION_HUMAN, OPTION_HEADER);
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");
}
} }


@Override @Override
Expand Down
Expand Up @@ -154,17 +154,19 @@ bin/hadoop fs <args>


* count * count


Usage: <<<hadoop fs -count [-q] [-h] <paths> >>> Usage: <<<hadoop fs -count [-q] [-h] [-v] <paths> >>>


Count the number of directories, files and bytes under the paths that match Count the number of directories, files and bytes under the paths that match
the specified file pattern. The output columns with -count are: DIR_COUNT, the specified file pattern. The output columns with -count are: DIR_COUNT,
FILE_COUNT, CONTENT_SIZE FILE_NAME FILE_COUNT, CONTENT_SIZE PATHNAME


The output columns with -count -q are: QUOTA, REMAINING_QUATA, SPACE_QUOTA, The output columns with -count -q are: QUOTA, REMAINING_QUATA, SPACE_QUOTA,
REMAINING_SPACE_QUOTA, DIR_COUNT, FILE_COUNT, CONTENT_SIZE, FILE_NAME REMAINING_SPACE_QUOTA, DIR_COUNT, FILE_COUNT, CONTENT_SIZE, PATHNAME


The -h option shows sizes in human readable format. The -h option shows sizes in human readable format.


The -v option displays a header line.

Example: Example:


* <<<hadoop fs -count hdfs://nn1.example.com/file1 hdfs://nn2.example.com/file2>>> * <<<hadoop fs -count hdfs://nn1.example.com/file1 hdfs://nn2.example.com/file2>>>
Expand All @@ -173,6 +175,8 @@ bin/hadoop fs <args>


* <<<hadoop fs -count -q -h hdfs://nn1.example.com/file1>>> * <<<hadoop fs -count -q -h hdfs://nn1.example.com/file1>>>


* <<<hdfs dfs -count -q -h -v hdfs://nn1.example.com/file1>>>

Exit Code: Exit Code:


Returns 0 on success and -1 on error. Returns 0 on success and -1 on error.
Expand Down Expand Up @@ -743,4 +747,4 @@ permissions userid groupid modification_date modification_time dirname


Usage: <<<hadoop fs -usage command>>> Usage: <<<hadoop fs -usage command>>>


Return the help for an individual command. Return the help for an individual command.
Expand Up @@ -137,15 +137,15 @@ public void testReadFields() throws IOException {
// check the header with quotas // check the header with quotas
@Test @Test
public void testGetHeaderWithQuota() { public void testGetHeaderWithQuota() {
String header = " name quota rem name quota space quota " String header = " QUOTA REM_QUOTA SPACE_QUOTA "
+ "rem space quota directories files bytes "; + "REM_SPACE_QUOTA DIR_COUNT FILE_COUNT CONTENT_SIZE ";
assertEquals(header, ContentSummary.getHeader(true)); assertEquals(header, ContentSummary.getHeader(true));
} }


// check the header without quotas // check the header without quotas
@Test @Test
public void testGetHeaderNoQuota() { public void testGetHeaderNoQuota() {
String header = " directories files bytes "; String header = " DIR_COUNT FILE_COUNT CONTENT_SIZE ";
assertEquals(header, ContentSummary.getHeader(false)); assertEquals(header, ContentSummary.getHeader(false));
} }


Expand Down
Expand Up @@ -31,6 +31,7 @@
import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.FilterFileSystem; import org.apache.hadoop.fs.FilterFileSystem;
import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.shell.CommandFormat.NotEnoughArgumentsException;
import org.junit.Test; import org.junit.Test;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
Expand All @@ -47,15 +48,13 @@ public class TestCount {
private static Configuration conf; private static Configuration conf;
private static FileSystem mockFs; private static FileSystem mockFs;
private static FileStatus fileStat; private static FileStatus fileStat;
private static ContentSummary mockCs;


@BeforeClass @BeforeClass
public static void setup() { public static void setup() {
conf = new Configuration(); conf = new Configuration();
conf.setClass("fs.mockfs.impl", MockFileSystem.class, FileSystem.class); conf.setClass("fs.mockfs.impl", MockFileSystem.class, FileSystem.class);
mockFs = mock(FileSystem.class); mockFs = mock(FileSystem.class);
fileStat = mock(FileStatus.class); fileStat = mock(FileStatus.class);
mockCs = mock(ContentSummary.class);
when(fileStat.isFile()).thenReturn(true); when(fileStat.isFile()).thenReturn(true);
} }


Expand Down Expand Up @@ -87,6 +86,85 @@ public void processOptionsAll() {
assertTrue(count.isHumanReadable()); assertTrue(count.isHumanReadable());
} }


// check no options is handled correctly
@Test
public void processOptionsNoOptions() {
LinkedList<String> options = new LinkedList<String>();
options.add("dummy");
Count count = new Count();
count.processOptions(options);
assertFalse(count.isShowQuotas());
}

// check -q is handled correctly
@Test
public void processOptionsShowQuotas() {
LinkedList<String> options = new LinkedList<String>();
options.add("-q");
options.add("dummy");
Count count = new Count();
count.processOptions(options);
assertTrue(count.isShowQuotas());
}

// check missing arguments is handled correctly
@Test
public void processOptionsMissingArgs() {
LinkedList<String> options = new LinkedList<String>();
Count count = new Count();
try {
count.processOptions(options);
fail("Count.processOptions - NotEnoughArgumentsException not thrown");
} catch (NotEnoughArgumentsException e) {
}
assertFalse(count.isShowQuotas());
}

// check the correct header is produced with no quotas (-v)
@Test
public void processOptionsHeaderNoQuotas() {
LinkedList<String> options = new LinkedList<String>();
options.add("-v");
options.add("dummy");

PrintStream out = mock(PrintStream.class);

Count count = new Count();
count.out = out;

count.processOptions(options);

String noQuotasHeader =
// <----12----> <----12----> <-------18------->
" DIR_COUNT FILE_COUNT CONTENT_SIZE PATHNAME";
verify(out).println(noQuotasHeader);
verifyNoMoreInteractions(out);
}

// check the correct header is produced with quotas (-q -v)
@Test
public void processOptionsHeaderWithQuotas() {
LinkedList<String> options = new LinkedList<String>();
options.add("-q");
options.add("-v");
options.add("dummy");

PrintStream out = mock(PrintStream.class);

Count count = new Count();
count.out = out;

count.processOptions(options);

String withQuotasHeader =
// <----12----> <-----15------> <-----15------> <-----15------>
" QUOTA REM_QUOTA SPACE_QUOTA REM_SPACE_QUOTA " +
// <----12----> <----12----> <-------18------->
" DIR_COUNT FILE_COUNT CONTENT_SIZE PATHNAME";
verify(out).println(withQuotasHeader);
verifyNoMoreInteractions(out);
}

// check quotas are reported correctly // check quotas are reported correctly
@Test @Test
public void processPathShowQuotas() throws Exception { public void processPathShowQuotas() throws Exception {
Expand Down Expand Up @@ -211,29 +289,48 @@ public void getName() {
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] <path> ..."; String expected = "-count [-q] [-h] [-v] <path> ...";
assertEquals("Count.getUsage", expected, actual); assertEquals("Count.getUsage", expected, actual);
} }


// check the correct description is returned
@Test
public void getDescription() {
Count count = new Count();
String actual = count.getDescription();
String expected =
"Count the number of directories, files and bytes under the paths\n"
+ "that match the specified file pattern. The output columns are:\n"
+ "DIR_COUNT FILE_COUNT CONTENT_SIZE PATHNAME\n"
+ "or, with the -q option:\n"
+ "QUOTA REM_QUOTA SPACE_QUOTA REM_SPACE_QUOTA\n"
+ " DIR_COUNT FILE_COUNT CONTENT_SIZE PATHNAME\n"
+ "The -h option shows file sizes in human readable format.\n"
+ "The -v option displays a header line.";

assertEquals("Count.getDescription", expected, actual);
}



// mock content system // mock content system
static class MockContentSummary extends ContentSummary { static class MockContentSummary extends ContentSummary {


public MockContentSummary() {} public MockContentSummary() {
}


@Override @Override
public String toString(boolean qOption, boolean hOption) { public String toString(boolean qOption, boolean hOption) {
if (qOption) { if (qOption) {
if (hOption) { if (hOption) {
return(HUMAN + WITH_QUOTAS); return (HUMAN + WITH_QUOTAS);
} else { } else {
return(BYTES + WITH_QUOTAS); return (BYTES + WITH_QUOTAS);
} }
} else { } else {
if (hOption) { if (hOption) {
return(HUMAN + NO_QUOTAS); return (HUMAN + NO_QUOTAS);
} else { } else {
return(BYTES + NO_QUOTAS); return (BYTES + NO_QUOTAS);
} }
} }
} }
Expand Down

0 comments on commit fb06c00

Please sign in to comment.