HADOOP-7487. DF should throw a more reasonable exception when mount cannot be determined. Contributed by Andrew Wang.

git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1449992 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Aaron Myers 2013-02-26 01:37:28 +00:00
parent 7e2d98da40
commit 9397260411
3 changed files with 160 additions and 36 deletions

View File

@ -387,6 +387,9 @@ Release 2.0.4-beta - UNRELEASED
HADOOP-9323. Fix typos in API documentation. (suresh)
HADOOP-7487. DF should throw a more reasonable exception when mount cannot
be determined. (Andrew Wang via atm)
Release 2.0.3-alpha - 2013-02-06
INCOMPATIBLE CHANGES

View File

@ -17,19 +17,22 @@
*/
package org.apache.hadoop.fs;
import java.io.File;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.CommonConfigurationKeys;
import org.apache.hadoop.util.Shell;
import com.google.common.annotations.VisibleForTesting;
/** Filesystem disk space usage statistics.
* Uses the unix 'df' program to get mount points, and java.io.File for
* space utilization. Tested on Linux, FreeBSD, Cygwin. */
@ -44,6 +47,8 @@ public class DF extends Shell {
private final File dirFile;
private String filesystem;
private String mount;
private ArrayList<String> output;
enum OSType {
OS_TYPE_UNIX("UNIX"),
@ -84,6 +89,7 @@ public DF(File path, long dfInterval) throws IOException {
super(dfInterval);
this.dirPath = path.getCanonicalPath();
this.dirFile = new File(this.dirPath);
this.output = new ArrayList<String>();
}
protected OSType getOSType() {
@ -127,7 +133,21 @@ public int getPercentUsed() {
/** @return the filesystem mount point for the indicated volume */
public String getMount() throws IOException {
// Abort early if specified path does not exist
if (!dirFile.exists()) {
throw new FileNotFoundException("Specified path " + dirFile.getPath()
+ "does not exist");
}
run();
// Skip parsing if df was not successful
if (getExitCode() != 0) {
StringBuffer sb = new StringBuffer("df could not be run successfully: ");
for (String line: output) {
sb.append(line);
}
throw new IOException(sb.toString());
}
parseOutput();
return mount;
}
@ -152,46 +172,71 @@ protected String[] getExecString() {
@Override
protected void parseExecResult(BufferedReader lines) throws IOException {
lines.readLine(); // skip headings
output.clear();
String line = lines.readLine();
if (line == null) {
throw new IOException( "Expecting a line not the end of stream" );
while (line != null) {
output.add(line);
line = lines.readLine();
}
}
@VisibleForTesting
protected void parseOutput() throws IOException {
if (output.size() < 2) {
StringBuffer sb = new StringBuffer("Fewer lines of output than expected");
if (output.size() > 0) {
sb.append(": " + output.get(0));
}
throw new IOException(sb.toString());
}
String line = output.get(1);
StringTokenizer tokens =
new StringTokenizer(line, " \t\n\r\f%");
this.filesystem = tokens.nextToken();
try {
this.filesystem = tokens.nextToken();
} catch (NoSuchElementException e) {
throw new IOException("Unexpected empty line");
}
if (!tokens.hasMoreTokens()) { // for long filesystem name
line = lines.readLine();
if (line == null) {
throw new IOException( "Expecting a line not the end of stream" );
if (output.size() > 2) {
line = output.get(2);
} else {
throw new IOException("Expecting additional output after line: "
+ line);
}
tokens = new StringTokenizer(line, " \t\n\r\f%");
}
switch(getOSType()) {
case OS_TYPE_AIX:
Long.parseLong(tokens.nextToken()); // capacity
Long.parseLong(tokens.nextToken()); // available
Integer.parseInt(tokens.nextToken()); // pct used
tokens.nextToken();
tokens.nextToken();
this.mount = tokens.nextToken();
break;
try {
switch(getOSType()) {
case OS_TYPE_AIX:
Long.parseLong(tokens.nextToken()); // capacity
Long.parseLong(tokens.nextToken()); // available
Integer.parseInt(tokens.nextToken()); // pct used
tokens.nextToken();
tokens.nextToken();
this.mount = tokens.nextToken();
break;
case OS_TYPE_WIN:
case OS_TYPE_SOLARIS:
case OS_TYPE_MAC:
case OS_TYPE_UNIX:
default:
Long.parseLong(tokens.nextToken()); // capacity
Long.parseLong(tokens.nextToken()); // used
Long.parseLong(tokens.nextToken()); // available
Integer.parseInt(tokens.nextToken()); // pct used
this.mount = tokens.nextToken();
break;
}
case OS_TYPE_WIN:
case OS_TYPE_SOLARIS:
case OS_TYPE_MAC:
case OS_TYPE_UNIX:
default:
Long.parseLong(tokens.nextToken()); // capacity
Long.parseLong(tokens.nextToken()); // used
Long.parseLong(tokens.nextToken()); // available
Integer.parseInt(tokens.nextToken()); // pct used
this.mount = tokens.nextToken();
break;
}
} catch (NoSuchElementException e) {
throw new IOException("Could not parse line: " + line);
} catch (NumberFormatException e) {
throw new IOException("Could not parse line: " + line);
}
}
public static void main(String[] args) throws Exception {

View File

@ -17,15 +17,22 @@
*/
package org.apache.hadoop.fs;
import junit.framework.TestCase;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.StringReader;
import java.util.EnumSet;
import java.util.Random;
import org.apache.hadoop.test.GenericTestUtils;
import org.apache.hadoop.util.Shell;
import org.junit.Test;
public class TestDFVariations extends TestCase {
public class TestDFVariations {
public static class XXDF extends DF {
private final String osName;
@ -50,6 +57,7 @@ protected String[] getExecString() {
}
}
@Test(timeout=5000)
public void testOSParsing() throws Exception {
for (DF.OSType ost : EnumSet.allOf(DF.OSType.class)) {
XXDF df = new XXDF(ost.getId());
@ -58,6 +66,74 @@ public void testOSParsing() throws Exception {
df.getMount());
}
}
@Test(timeout=5000)
public void testDFInvalidPath() throws Exception {
// Generate a path that doesn't exist
Random random = new Random(0xDEADBEEFl);
File file = null;
byte[] bytes = new byte[64];
while (file == null) {
random.nextBytes(bytes);
final String invalid = new String("/" + bytes);
final File invalidFile = new File(invalid);
if (!invalidFile.exists()) {
file = invalidFile;
}
}
DF df = new DF(file, 0l);
try {
df.getMount();
} catch (FileNotFoundException e) {
// expected, since path does not exist
GenericTestUtils.assertExceptionContains(file.getName(), e);
}
}
@Test(timeout=5000)
public void testDFMalformedOutput() throws Exception {
DF df = new DF(new File("/"), 0l);
BufferedReader reader = new BufferedReader(new StringReader(
"Filesystem 1K-blocks Used Available Use% Mounted on\n" +
"/dev/sda5 19222656 10597036 7649060 59% /"));
df.parseExecResult(reader);
df.parseOutput();
reader = new BufferedReader(new StringReader(
"Filesystem 1K-blocks Used Available Use% Mounted on"));
df.parseExecResult(reader);
try {
df.parseOutput();
fail("Expected exception with missing line!");
} catch (IOException e) {
GenericTestUtils.assertExceptionContains(
"Fewer lines of output than expected", e);
System.out.println(e.toString());
}
reader = new BufferedReader(new StringReader(
"Filesystem 1K-blocks Used Available Use% Mounted on\n" +
" "));
df.parseExecResult(reader);
try {
df.parseOutput();
fail("Expected exception with empty line!");
} catch (IOException e) {
GenericTestUtils.assertExceptionContains("Unexpected empty line", e);
System.out.println(e.toString());
}
reader = new BufferedReader(new StringReader(
"Filesystem 1K-blocks Used Available Use% Mounted on\n" +
" 19222656 10597036 7649060 59% /"));
df.parseExecResult(reader);
try {
df.parseOutput();
fail("Expected exception with missing field!");
} catch (IOException e) {
GenericTestUtils.assertExceptionContains("Could not parse line: ", e);
System.out.println(e.toString());
}
}
}