package org.apache.hadoop.raid;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URI;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import java.util.zip.CRC32;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.BlockMissingException;
import org.apache.hadoop.fs.ChecksumException;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hdfs.DistributedFileSystem;
import org.apache.hadoop.hdfs.DistributedRaidFileSystem;
import org.apache.hadoop.hdfs.MiniDFSCluster;
import org.apache.hadoop.hdfs.RaidDFSUtil;
import org.apache.hadoop.hdfs.TestDatanodeBlockScanner;
import org.apache.hadoop.hdfs.TestRaidDfs;
import org.apache.hadoop.hdfs.protocol.LocatedBlocks;
import junit.framework.TestCase;
public class TestReadConstruction extends TestCase {
final static String TEST_DIR = new File(
System.getProperty("test.build.data",
"build/contrib/raid/test/data")).getAbsolutePath();
final static String CONFIG_FILE = new File(TEST_DIR,
"test-raid.xml").getAbsolutePath();
final static long RELOAD_INTERVAL = 1000;
final static Log LOG = LogFactory.getLog(
"org.apache.hadoop.raid.TestReadConstruction");
final static Random rand = new Random();
final static int NUM_DATANODES = 3;
Configuration conf;
String namenode = null;
String hftp = null;
MiniDFSCluster dfs = null;
FileSystem fileSys = null;
Path root = null;
Set<String> allExpectedMissingFiles = null;
protected void mySetup() throws Exception {
conf = new Configuration();
if (System.getProperty("hadoop.log.dir") == null) {
String base = new File(".").getAbsolutePath();
System.setProperty("hadoop.log.dir", new Path(base).toString()
+ "/logs");
}
new File(TEST_DIR).mkdirs(); // Make sure data directory exists
conf.set("raid.config.file", CONFIG_FILE);
conf.setBoolean("raid.config.reload", true);
conf.setLong("raid.config.reload.interval", RELOAD_INTERVAL);
// the RaidNode does the raiding inline
conf.set("raid.classname", "org.apache.hadoop.raid.LocalRaidNode");
// use local block fixer
conf.set("raid.blockfix.classname",
"org.apache.hadoop.raid.LocalBlockIntegrityMonitor");
conf.set("raid.server.address", "localhost:0");
Utils.loadTestCodecs(conf, 10, 1, 4, "/destraid", "/destraidrs");
conf.setInt("fs.trash.interval", 1440);
dfs = new MiniDFSCluster(conf, NUM_DATANODES, true, null);
dfs.waitActive();
fileSys = dfs.getFileSystem();
namenode = fileSys.getUri().toString();
hftp = "hftp://localhost.localdomain:" + dfs.getNameNodePort();
FileSystem.setDefaultUri(conf, namenode);
}
private DistributedRaidFileSystem getRaidFS() throws IOException {
DistributedFileSystem dfs = (DistributedFileSystem)fileSys;
Configuration clientConf = new Configuration(conf);
clientConf.set("fs.hdfs.impl",
"org.apache.hadoop.hdfs.DistributedRaidFileSystem");
clientConf.set("fs.raid.underlyingfs.impl",
"org.apache.hadoop.hdfs.DistributedFileSystem");
clientConf.setBoolean("fs.hdfs.impl.disable.cache", true);
URI dfsUri = dfs.getUri();
return (DistributedRaidFileSystem)FileSystem.get(dfsUri, clientConf);
}
private void doRaid(Path srcPath, Codec codec) throws IOException {
RaidNode.doRaid(conf, fileSys.getFileStatus(srcPath),
new Path(codec.parityDirectory), codec,
new RaidNode.Statistics(),
RaidUtils.NULL_PROGRESSABLE,
false, 1, 1);
}
private boolean doThePartialTest(Codec codec,
int blockNum,
int[] corruptBlockIdxs) throws Exception {
long blockSize = 8192L;
int bufferSize = 4192;
Path srcPath = new Path("/user/dikang/raidtest/file" +
UUID.randomUUID().toString());
long crc = TestRaidDfs.createTestFilePartialLastBlock(fileSys, srcPath,
1, blockNum, blockSize);
DistributedRaidFileSystem raidFs = getRaidFS();
assertTrue(raidFs.exists(srcPath));
// generate the parity files.
doRaid(srcPath, codec);
FileStatus file1Stat = fileSys.getFileStatus(srcPath);
long length = file1Stat.getLen();
LocatedBlocks file1Loc =
RaidDFSUtil.getBlockLocations((DistributedFileSystem)fileSys,
srcPath.toUri().getPath(),
0, length);
// corrupt file1
for (int idx: corruptBlockIdxs) {
corruptBlock(file1Loc.get(idx).getBlock().getBlockName(),
dfs);
}
RaidDFSUtil.reportCorruptBlocks((DistributedFileSystem)fileSys, srcPath,
corruptBlockIdxs, blockSize);
// verify the partial read
byte[] buffer = new byte[bufferSize];
FSDataInputStream in = raidFs.open(srcPath);
long numRead = 0;
CRC32 newcrc = new CRC32();
int num = 0;
while (num >= 0) {
num = in.read(numRead, buffer, 0, bufferSize);
if (num < 0) {
break;
}
numRead += num;
newcrc.update(buffer, 0, num);
}
in.close();
if (numRead != length) {
LOG.info("Number of bytes read " + numRead +
" does not match file size " + length);
return false;
}
LOG.info(" Newcrc " + newcrc.getValue() + " old crc " + crc);
if (newcrc.getValue() != crc) {
LOG.info("CRC mismatch of file " + srcPath.toUri().getPath() + ": " +
newcrc + " vs. " + crc);
return false;
}
return true;
}
public void testReadCorruptPartialSuccess() throws Exception {
// test the "rs"
try {
mySetup();
int blockNum = 10;
int[][] corruptBlockIdxs = new int[][] {{0, 3, 6, 9}, {0, 1, 2, 3}};
for (int i=0; i<corruptBlockIdxs.length; i++) {
assertTrue(doThePartialTest(Codec.getCodec("rs"),
blockNum, corruptBlockIdxs[i]));
}
} finally {
if (null != dfs) {
dfs.shutdown();
}
}
// test the "xor"
try {
mySetup();
int blockNum = 10;
for (int i=0; i<blockNum; i++) {
int[] corruptBlockIdxs = new int[]{i};
LOG.info("Corrupt Block " + i +
" in testReadCorruptParitalSuccess for xor.");
assertTrue(doThePartialTest(Codec.getCodec("xor"),
blockNum, corruptBlockIdxs));
}
} finally {
if (null != dfs) {
dfs.shutdown();
}
}
}
public void testReadCorruptPartialFail() throws Exception {
// test the "rs"
try {
mySetup();
int blockNum = 10;
int[] corruptBlockIdxs = new int[]{0, 4, 6, 8, 9};
doThePartialTest(Codec.getCodec("rs"),
blockNum, corruptBlockIdxs);
fail();
} catch(ChecksumException e) {
} catch(BlockMissingException e) {
} finally {
if (null != dfs) {
dfs.shutdown();
}
}
// test the "xor"
try {
mySetup();
int blockNum = 10;
int[] corruptBlockIdxs = new int[]{0, 1};
doThePartialTest(Codec.getCodec("xor"),
blockNum, corruptBlockIdxs);
fail();
} catch(ChecksumException e) {
} catch(BlockMissingException e) {
} finally {
if (null != dfs) {
dfs.shutdown();
}
}
}
static void corruptBlock(String blockName, MiniDFSCluster dfs)
throws IOException {
boolean corrupted = false;
for (int i = 0; i < NUM_DATANODES; i++) {
corrupted |= TestDatanodeBlockScanner.corruptReplica(blockName, i, dfs);
}
assertTrue("could not corrupt block: " + blockName, corrupted);
}
}