/**
* 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.hdfs;
import java.io.EOFException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.URI;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hdfs.protocol.Block;
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
import org.apache.hadoop.hdfs.protocol.FSConstants;
import org.apache.hadoop.hdfs.protocol.LocatedBlock;
import org.apache.hadoop.hdfs.protocol.LocatedBlocksWithMetaInfo;
import org.apache.hadoop.hdfs.server.datanode.DataNode;
import org.apache.hadoop.hdfs.server.namenode.NameNode;
import org.apache.hadoop.hdfs.tools.FastCopy;
import org.apache.hadoop.hdfs.tools.FastCopy.FastFileCopyRequest;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.HardLink;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hdfs.tools.FastCopy.FastCopyFileStatus;
import org.apache.hadoop.hdfs.util.InjectionEvent;
import org.apache.hadoop.util.InjectionEventI;
import org.apache.hadoop.util.InjectionHandler;
import org.junit.AfterClass;
import static org.junit.Assert.*;
public class FastCopySetupUtil {
private static final Random random = new Random();
protected static Configuration conf;
protected static Configuration remoteConf;
private static MiniDFSCluster cluster;
protected static DistributedFileSystem fs;
private static MiniDFSCluster remoteCluster;
private static DistributedFileSystem remoteFs;
private static boolean pass = true;
private static RWThread rwThread;
public static final int FILESIZE = 1024 * 5; // 5 KB
private static Map<Integer, DataNode> dnMap = new HashMap<Integer, DataNode>();
private static Log LOG = LogFactory.getLog(FastCopySetupUtil.class);
public static final int BLOCK_SIZE = 1024;
private static final byte[] buffer = new byte[BLOCK_SIZE];
public static final int TMPFILESIZE = 2048;
private static final byte[] fileBuffer = new byte[TMPFILESIZE];
public static final int BYTES_PER_CHECKSUM = 512;
public static final int COPIES = 5;
private static String confFile = "build/test/extraconf/core-site.xml";
private static final int softLeasePeriod = 3 * 1000; // 3 sec
private static final int hardLeasePeriod = 5 * 1000; // 5 sec
public static void setUpClass() throws Exception {
// Require the complete file to be replicated before we return in unit
// tests.
setConf("dfs.replication.min", 3);
// Lower the pending replication timeout to make sure if any of our blocks
// timeout the unit test catches it.
setConf("dfs.replication.pending.timeout.sec", 60);
// Make sure we get multiple blocks.
setConf("dfs.block.size", BLOCK_SIZE);
setConf("io.bytes.per.checksum", BYTES_PER_CHECKSUM);
// Set low soft and hard lease period.
setConf(FSConstants.DFS_HARD_LEASE_KEY, hardLeasePeriod);
setConf(FSConstants.DFS_SOFT_LEASE_KEY, softLeasePeriod);
System.setProperty("test.build.data", "build/test/data1");
cluster = new MiniDFSCluster(conf, 6, new String[] { "/r1", "/r2",
"/r1", "/r2", "/r1", "/r2" }, null, true, true);
for (DataNode dn : cluster.getDataNodes()) {
dnMap.put(dn.getSelfAddr().getPort(), dn);
}
// Writing conf to disk so that the FastCopy tool picks it up.
FileOutputStream out = new FileOutputStream(confFile);
conf.writeXml(out);
fs = (DistributedFileSystem) cluster.getFileSystem();
System.setProperty("test.build.data", "build/test/data2");
remoteCluster = new MiniDFSCluster(remoteConf, 6, new String[] { "/r1", "/r2", "/r1", "/r2",
"/r1", "/r2" }, null, true, true);
for (DataNode dn : remoteCluster.getDataNodes()) {
dnMap.put(dn.getSelfAddr().getPort(), dn);
}
remoteFs = (DistributedFileSystem) remoteCluster.getFileSystem();
random.nextBytes(fileBuffer);
rwThread = new RWThread();
rwThread.start();
}
private static void setConf(String name, int value) {
conf.setInt(name, value);
remoteConf.setInt(name, value);
}
@AfterClass
public static void tearDownClass() throws Exception {
rwThread.stopRW();
rwThread.join();
remoteFs.close();
remoteCluster.shutdown();
fs.close();
cluster.shutdown();
// Remove the extra conf file.
new File(confFile).delete();
}
private static class RWThread extends Thread {
private boolean flag = true;
private byte[] tmpBuffer = new byte[TMPFILESIZE];
public void run() {
while (flag) {
try {
// Make sure we have no pendingReplicationBlocks
pass = (0 == cluster.getNameNode().namesystem
.getPendingReplicationBlocks()) && (0 == remoteCluster.getNameNode().namesystem
.getPendingReplicationBlocks());
create_verify_file();
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
} catch (IOException e) {
pass = false;
LOG.warn("Create Verify Failed", e);
}
}
}
public void stopRW() {
flag = false;
}
public void create_verify_file() throws IOException {
String filename = "/create_verify_file" + random.nextInt();
Path filePath = new Path(filename);
// Write.
FSDataOutputStream out = fs.create(filePath, true, 4096);
out.write(fileBuffer);
out.close();
// Read.
FSDataInputStream in = fs.open(filePath, 4096);
in.readFully(tmpBuffer);
in.close();
// Verify and delete.
pass = Arrays.equals(tmpBuffer, fileBuffer);
fs.dfs.delete(filePath.toString(), true);
}
}
/**
* Generates a file with random data.
*
* @param fs
* the FileSystem on which to generate the file
*
* @param filename
* the full path name of the file
*/
protected static void generateRandomFile(FileSystem fs, String filename,
int filesize)
throws IOException {
Path filePath = new Path(filename);
OutputStream out = fs.create(filePath, true, 4096);
int bytesWritten = 0;
while (bytesWritten < filesize) {
random.nextBytes(buffer);
out.write(buffer);
bytesWritten += buffer.length;
}
out.close();
}
public void testFastCopy(boolean hardlink) throws Exception {
// Create a source file.
String src = "/testFastCopySrc" + hardlink;
generateRandomFile(fs, src, FILESIZE);
String destination = "/testFastCopyDestination" + hardlink;
FastCopy fastCopy = new FastCopy(conf);
NameNode namenode = cluster.getNameNode();
try {
for (int i = 0; i < COPIES; i++) {
fastCopy.copy(src, destination + i, fs, fs);
assertTrue(verifyCopiedFile(src, destination + i, namenode, namenode,
fs, fs, hardlink));
verifyFileStatus(destination + i, namenode, fastCopy);
}
} catch (Exception e) {
LOG.error("Fast Copy failed with exception : ", e);
fail("Fast Copy failed");
} finally {
fastCopy.shutdown();
}
assertTrue(pass);
}
public void testFastCopyOldAPI(boolean hardlink) throws Exception {
// Create a source file.
String src = "/testFastCopySrc" + hardlink;
generateRandomFile(fs, src, FILESIZE);
String destination = "/testFastCopyDestination" + hardlink;
FastCopy fastCopy = new FastCopy(conf, fs, fs);
NameNode namenode = cluster.getNameNode();
try {
for (int i = 0; i < COPIES; i++) {
fastCopy.copy(src, destination + i);
assertTrue(verifyCopiedFile(src, destination + i, namenode, namenode,
fs, fs, hardlink));
verifyFileStatus(destination + i, namenode, fastCopy);
}
} catch (Exception e) {
LOG.error("Fast Copy failed with exception : ", e);
fail("Fast Copy failed");
} finally {
fastCopy.shutdown();
}
assertTrue(pass);
}
public void testFastCopyMultiple(boolean hardlink) throws Exception {
// Create a source file.
String src = "/testFastCopyMultipleSrc" + hardlink;
generateRandomFile(fs, src, FILESIZE);
String destination = "/testFastCopyMultipleDestination" + hardlink;
FastCopy fastCopy = new FastCopy(conf);
List<FastFileCopyRequest> requests = new ArrayList<FastFileCopyRequest>();
for (int i = 0; i < COPIES; i++) {
requests.add(new FastFileCopyRequest(src, destination + i, fs, fs));
}
NameNode namenode = cluster.getNameNode();
try {
fastCopy.copy(requests);
for (FastFileCopyRequest r : requests) {
assertTrue(verifyCopiedFile(r.getSrc(), r.getDestination(), namenode,
namenode, fs, fs, hardlink));
verifyFileStatus(r.getDestination(), namenode, fastCopy);
}
} catch (Exception e) {
LOG.error("Fast Copy failed with exception : ", e);
fail("Fast Copy failed");
} finally {
fastCopy.shutdown();
}
assertTrue(pass);
}
private void verifyFileStatus(String file, NameNode namenode,
FastCopy fastCopy) throws Exception {
LOG.info("Verifying for file : " + file);
FastCopyFileStatus fstat = fastCopy.getFileStatus(file);
assertNotNull(fstat);
int totalBlocks = namenode.getBlockLocations(file, 0,
Long.MAX_VALUE).locatedBlockCount();
assertEquals(totalBlocks, fstat.getTotalBlocks());
assertEquals(fstat.getTotalBlocks(), fstat.getBlocksDone());
assertEquals(FILESIZE / BLOCK_SIZE, totalBlocks);
}
public void testInterFileSystemFastCopy(boolean hardlink) throws Exception {
// Create a source file.
setInjectionHandler();
String src = "/testInterFileSystemFastCopySrc" + hardlink;
generateRandomFile(fs, src, FILESIZE);
String destination = "/testInterFileSystemFastCopyDst" + hardlink;
FastCopy fastCopy = new FastCopy(conf);
NameNode srcNameNode = cluster.getNameNode();
NameNode dstNameNode = remoteCluster.getNameNode();
try {
for (int i = 0; i < COPIES; i++) {
fastCopy.copy(src, destination + i, fs, remoteFs);
assertTrue(verifyCopiedFile(src, destination + i, srcNameNode,
dstNameNode, fs, remoteFs, hardlink));
verifyFileStatus(destination + i, dstNameNode, fastCopy);
}
} catch (Exception e) {
LOG.error("Fast Copy failed with exception : ", e);
fail("Fast Copy failed");
} finally {
fastCopy.shutdown();
}
assertTrue(pass);
}
private static void setInjectionHandler() {
InjectionHandler.set(new InjectionHandler() {
protected boolean _trueCondition(InjectionEventI event, Object... args) {
if (event == InjectionEvent.FSNAMESYSTEM_SKIP_LOCAL_DN_LOOKUP) {
if (args == null) {
return true;
}
if (args.length == 1) {
if (args[0] == null || !(args[0] instanceof String)) {
return true;
}
String datanodeIpAddress = (String) args[0];
return !("127.0.0.1".equals(datanodeIpAddress));
}
return true;
}
return true;
}
});
}
public void testInterFileSystemFastCopyMultiple(boolean hardlink)
throws Exception {
// Create a source file.
String src = "/testInterFileSystemFastCopy MultipleSrc" + hardlink;
generateRandomFile(fs, src, FILESIZE);
String destination = "/testInterFileSystemFastCopy MultipleDestination"
+ hardlink;
FastCopy fastCopy = new FastCopy(conf);
List<FastFileCopyRequest> requests = new ArrayList<FastFileCopyRequest>();
for (int i = 0; i < COPIES; i++) {
requests.add(new FastFileCopyRequest(src, destination + i, fs, remoteFs));
}
NameNode srcNameNode = cluster.getNameNode();
NameNode dstNameNode = remoteCluster.getNameNode();
try {
fastCopy.copy(requests);
for (FastFileCopyRequest r : requests) {
assertTrue(verifyCopiedFile(r.getSrc(), r.getDestination(),
srcNameNode, dstNameNode, fs, remoteFs, hardlink));
verifyFileStatus(r.getDestination(), dstNameNode, fastCopy);
}
} catch (Exception e) {
LOG.error("Fast Copy failed with exception : ", e);
fail("Fast Copy failed");
} finally {
fastCopy.shutdown();
}
assertTrue(pass);
}
public void testFastCopyShellMultiple(boolean hardlink, String extraargs[])
throws Exception {
// Create a source file.
String src = "/testFastCopyShellMultipleSrc" + hardlink;
List<String> argsList = new ArrayList<String>();
int i;
for (i = 0; i < COPIES; i++) {
generateRandomFile(fs, src + i, TMPFILESIZE); // Create a file
argsList.add(fs.makeQualified(new Path(src + i)).toString());
}
String destination = "/testFastCopyShellMultipleDestination" + hardlink;
fs.mkdirs(new Path(destination));
NameNode namenode = cluster.getNameNode();
argsList.add(fs.makeQualified(new Path(destination)).toString());
argsList.addAll(Arrays.asList(extraargs));
String args[] = new String[argsList.size()];
args = argsList.toArray(args);
try {
FastCopy.runTool(args);
for (i = 0; i < COPIES; i++) {
String dstPath = destination + src + i;
assertTrue(fs.exists(new Path(dstPath)));
assertTrue(verifyCopiedFile(src + i, dstPath, namenode, namenode, fs,
fs, hardlink));
}
} catch (Exception e) {
LOG.error("Fast Copy failed with exception : ", e);
fail("Fast Copy failed");
}
assertTrue(pass);
}
public void testInterFileSystemFastCopyShellMultiple(boolean hardlink,
String extraargs[]) throws Exception {
// Create a source file.
String fsname = new URI(conf.get("fs.default.name")).getAuthority();
String remoteFsname = new URI(remoteConf.get("fs.default.name"))
.getAuthority();
String srcFile = "/testInterFileSystemFastCopyShellMultipleSrc" + hardlink;
String src = "hdfs://" + fsname + srcFile;
List<String> argsList = new ArrayList<String>();
int i;
for (i = 0; i < COPIES; i++) {
generateRandomFile(fs, src + i, TMPFILESIZE); // Create a file
argsList.add(src + i);
}
String destDir = "/testInterFileSystemFastCopyShellMultipleDestination"
+ hardlink;
String destination = "hdfs://" + remoteFsname + destDir;
remoteFs.mkdirs(new Path(destination));
NameNode srcNamenode = cluster.getNameNode();
NameNode dstNamenode = remoteCluster.getNameNode();
argsList.add(destination);
argsList.addAll(Arrays.asList(extraargs));
String args[] = new String[argsList.size()];
args = argsList.toArray(args);
FastCopy.runTool(args);
for (i = 0; i < COPIES; i++) {
String dstPath = destDir + srcFile + i;
assertTrue(remoteFs.exists(new Path(dstPath)));
assertTrue(verifyCopiedFile(srcFile + i, dstPath, srcNamenode,
dstNamenode, fs, remoteFs, hardlink));
}
assertTrue(pass);
}
public void testFastCopyShellGlob(boolean hardlink, String[] files,
String[] args, String srcPrefix, String dstPrefix, boolean isDir)
throws Exception {
int i;
if (isDir) {
String destination = args[args.length - 1];
fs.mkdirs(new Path(destination));
}
NameNode namenode = cluster.getNameNode();
try {
FastCopy.runTool(args);
for (i = 0; i < files.length; i++) {
String dstPath = dstPrefix + files[i];
String srcPath = srcPrefix + files[i];
LOG.info("srcPath : " + srcPath + " dstPath : " + dstPath);
assertTrue(fs.exists(new Path(dstPath)));
assertTrue(verifyCopiedFile(srcPath, dstPath, namenode, namenode, fs,
fs, hardlink));
}
} catch (Exception e) {
LOG.error("Fast Copy failed with exception : ", e);
throw e;
}
assertTrue(pass);
}
public static boolean compareFiles(String src, FileSystem srcFs, String dst,
FileSystem dstFs) throws Exception {
Path srcFilePath = new Path(src);
Path destFilePath = new Path(dst);
FSDataInputStream srcStream = srcFs.open(srcFilePath, 4096);
FSDataInputStream destStream = dstFs.open(destFilePath, 4096);
int counter = 0;
byte[] buffer1 = new byte[4096]; // 4KB
byte[] buffer2 = new byte[4096]; // 4KB
while (true) {
try {
srcStream.readFully(buffer1);
} catch (EOFException e) {
System.out.println("Src file EOF reached");
counter++;
}
try {
destStream.readFully(buffer2);
} catch (EOFException e) {
System.out.println("Destination file EOF reached");
counter++;
}
if (counter == 1) {
System.out.println("One file larger than other");
return false;
} else if (counter == 2) {
return true;
}
if (!Arrays.equals(buffer1, buffer2)) {
System.out.println("Files Mismatch");
return false;
}
}
}
public boolean verifyCopiedFile(String src, String destination,
NameNode srcNameNode, NameNode dstNameNode, FileSystem srcFs,
FileSystem dstFs, boolean hardlink) throws Exception {
verifyBlockLocations(src, destination, srcNameNode, dstNameNode, hardlink);
return compareFiles(src, srcFs, destination, dstFs);
}
public boolean verifyBlockLocations(String src, String destination,
NameNode srcNameNode, NameNode dstNameNode, boolean hardlink)
throws IOException {
LocatedBlocksWithMetaInfo srcLocatedBlocks =
srcNameNode.openAndFetchMetaInfo(src, 0,Long.MAX_VALUE);
List<LocatedBlock> srcblocks = srcLocatedBlocks.getLocatedBlocks();
LocatedBlocksWithMetaInfo dstLocatedBlocks =
dstNameNode.openAndFetchMetaInfo(destination, 0, Long.MAX_VALUE);
List<LocatedBlock> dstblocks = dstLocatedBlocks.getLocatedBlocks();
assertEquals(srcblocks.size(), dstblocks.size());
Iterator<LocatedBlock> srcIt = srcblocks.iterator();
Iterator<LocatedBlock> dstIt = dstblocks.iterator();
while (srcIt.hasNext()) {
LocatedBlock srcBlock = srcIt.next();
LocatedBlock dstBlock = dstIt.next();
List<DatanodeInfo> srcLocations = Arrays.asList(srcBlock.getLocations());
List<DatanodeInfo> dstLocations = Arrays.asList(dstBlock.getLocations());
System.out.println("Locations for src block : " + srcBlock.getBlock()
+ " file : " + src);
for (DatanodeInfo info : srcLocations) {
System.out.println("Datanode : " + info.toString() + " rack: " + info.getNetworkLocation());
}
System.out.println("Locations for dst block : " + dstBlock.getBlock()
+ " file : " + destination);
for (DatanodeInfo info : dstLocations) {
System.out.println("Datanode : " + info.toString() + " rack: " + info.getNetworkLocation());
}
assertEquals(srcLocations.size(), dstLocations.size());
if (srcNameNode.getNameNodeAddress().equals(
dstNameNode.getNameNodeAddress())) {
// Same FS copy, verify blocks are machine local.
assertTrue(srcLocations.containsAll(dstLocations));
assertTrue(dstLocations.containsAll(srcLocations));
} else {
// Since all datanodes are on the same host in a unit test, the inter
// filesystem copy can have blocks end up on any datanode.
Iterator<DatanodeInfo> sit = srcLocations.iterator();
while (sit.hasNext()) {
DatanodeInfo srcInfo = sit.next();
// Verify location.
Iterator<DatanodeInfo> dit = dstLocations.iterator();
while (dit.hasNext()) {
DatanodeInfo dstInfo = dit.next();
if (dstInfo.getHost().equals(srcInfo.getHost())) {
verifyHardLinks(srcInfo, dstInfo,
srcLocatedBlocks.getNamespaceID(), srcBlock.getBlock(),
dstLocatedBlocks.getNamespaceID(), dstBlock.getBlock(),
hardlink);
}
}
}
}
}
return true;
}
private void verifyHardLinks(DatanodeInfo srcInfo, DatanodeInfo dstInfo,
int srcNamespaceId, Block srcBlock, int dstNamespaceId, Block dstBlock,
boolean hardlink) throws IOException {
// Verify hard links.
DataNode dnSrc = dnMap.get(srcInfo.getPort());
File blockFileSrc = dnSrc.data.getBlockFile(srcNamespaceId, srcBlock);
LOG.warn("Link count for : " + blockFileSrc + " is : "
+ HardLink.getLinkCount(blockFileSrc));
if (hardlink) {
assertTrue(HardLink.getLinkCount(blockFileSrc) > 1);
} else {
assertEquals(1, HardLink.getLinkCount(blockFileSrc));
}
DataNode dnDst = dnMap.get(dstInfo.getPort());
File blockFileDst = dnDst.data.getBlockFile(dstNamespaceId, dstBlock);
if (hardlink) {
assertTrue(HardLink.getLinkCount(blockFileDst) > 1);
} else {
assertEquals(1, HardLink.getLinkCount(blockFileDst));
}
}
}