blob: d29fdee37969abd9c1691db3a734090d3960709e [file] [log] [blame]
/**
* 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.server.namenode;
import static org.junit.Assert.assertEquals;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hdfs.DFSConfigKeys;
import org.apache.hadoop.hdfs.DFSTestUtil;
import org.apache.hadoop.hdfs.DistributedFileSystem;
import org.apache.hadoop.hdfs.MiniDFSCluster;
import org.apache.hadoop.hdfs.server.blockmanagement.BlockInfo;
import org.apache.hadoop.hdfs.server.namenode.INodeFile.HeaderFormat;
import org.apache.hadoop.hdfs.server.namenode.snapshot.SnapshotTestHelper;
import org.apache.hadoop.test.LambdaTestUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* Test SwapBlockListOp working.
*/
public class TestSwapBlockList {
private static final short REPLICATION = 3;
private static final long SEED = 0;
private final Path rootDir = new Path("/" + getClass().getSimpleName());
private final Path subDir1 = new Path(rootDir, "dir1");
private final Path file1 = new Path(subDir1, "file1");
private final Path file2 = new Path(subDir1, "file2");
private final Path subDir11 = new Path(subDir1, "dir11");
private final Path file3 = new Path(subDir11, "file3");
private final Path subDir2 = new Path(rootDir, "dir2");
private final Path file4 = new Path(subDir2, "file4");
private Configuration conf;
private MiniDFSCluster cluster;
private FSNamesystem fsn;
private FSDirectory fsdir;
private DistributedFileSystem hdfs;
@Before
public void setUp() throws Exception {
conf = new Configuration();
conf.setInt(DFSConfigKeys.DFS_NAMENODE_MAX_XATTRS_PER_INODE_KEY, 2);
cluster = new MiniDFSCluster.Builder(conf)
.numDataNodes(REPLICATION)
.build();
cluster.waitActive();
fsn = cluster.getNamesystem();
fsdir = fsn.getFSDirectory();
hdfs = cluster.getFileSystem();
hdfs.mkdirs(subDir2);
DFSTestUtil.createFile(hdfs, file1, 1024, REPLICATION, SEED);
DFSTestUtil.createFile(hdfs, file2, 1024, REPLICATION, SEED);
DFSTestUtil.createFile(hdfs, file3, 1024, REPLICATION, SEED);
DFSTestUtil.createFile(hdfs, file4, 1024, REPLICATION, SEED);
}
@After
public void tearDown() throws Exception {
if (cluster != null) {
cluster.shutdown();
}
}
@Test
public void testInputValidation() throws Exception {
// Source file not found.
LambdaTestUtils.intercept(FileNotFoundException.class,
"/TestSwapBlockList/dir1/fileXYZ", () -> fsn.swapBlockList(
"/TestSwapBlockList/dir1/fileXYZ", "/TestSwapBlockList/dir1/dir11" +
"/file3", 0L));
// Destination file not found.
LambdaTestUtils.intercept(FileNotFoundException.class,
"/TestSwapBlockList/dir1/dir11/fileXYZ",
() -> fsn.swapBlockList("/TestSwapBlockList/dir1/file1",
"/TestSwapBlockList/dir1/dir11/fileXYZ", 0L));
// Source is Directory, not a file.
LambdaTestUtils.intercept(IOException.class,
"/TestSwapBlockList/dir1 is not a file.",
() -> fsn.swapBlockList("/TestSwapBlockList/dir1",
"/TestSwapBlockList/dir1/dir11/file3", 0L));
String sourceFile = "/TestSwapBlockList/dir1/file1";
String dstFile1 = "/TestSwapBlockList/dir1/dir11/file3";
// Destination file is under construction.
INodeFile dstInodeFile = fsdir.resolvePath(fsdir.getPermissionChecker(),
dstFile1, FSDirectory.DirOp.WRITE).getLastINode().asFile();
dstInodeFile.toUnderConstruction("TestClient", "TestClientMachine");
LambdaTestUtils.intercept(IOException.class,
dstFile1 + " is under construction.",
() -> fsn.swapBlockList(sourceFile, dstFile1, 0L));
// Check if parent directory is in snapshot.
SnapshotTestHelper.createSnapshot(hdfs, subDir2, "s0");
String dstFile2 = "/TestSwapBlockList/dir2/file4";
LambdaTestUtils.intercept(IOException.class,
dstFile2 + " is in a snapshot directory",
() -> fsn.swapBlockList(sourceFile, dstFile2, 0L));
// Check if gen timestamp validation works.
String dstFile3 = "/TestSwapBlockList/dir1/file2";
dstInodeFile = (INodeFile) fsdir.resolvePath(fsdir.getPermissionChecker(),
dstFile3, FSDirectory.DirOp.WRITE).getLastINode();
long genStamp = dstInodeFile.getLastBlock().getGenerationStamp();
dstInodeFile.getLastBlock().setGenerationStamp(genStamp + 1);
LambdaTestUtils.intercept(IOException.class,
dstFile3 + " has last block with different gen timestamp.",
() -> fsn.swapBlockList(sourceFile, dstFile3, genStamp));
}
@Test
public void testSwapBlockListOp() throws Exception {
String sourceFile = "/TestSwapBlockList/dir1/file1";
String dstFile = "/TestSwapBlockList/dir1/dir11/file3";
INodeFile srcInodeFile =
(INodeFile) fsdir.resolvePath(fsdir.getPermissionChecker(),
sourceFile, FSDirectory.DirOp.WRITE).getLastINode();
INodeFile dstInodeFile =
(INodeFile) fsdir.resolvePath(fsdir.getPermissionChecker(),
dstFile, FSDirectory.DirOp.WRITE).getLastINode();
BlockInfo[] srcBlockLocationsBeforeSwap = srcInodeFile.getBlocks();
long srcHeader = srcInodeFile.getHeaderLong();
BlockInfo[] dstBlockLocationsBeforeSwap = dstInodeFile.getBlocks();
long dstHeader = dstInodeFile.getHeaderLong();
fsn.swapBlockList(sourceFile, dstFile,
dstInodeFile.getLastBlock().getGenerationStamp());
assertBlockListEquality(dstBlockLocationsBeforeSwap,
srcInodeFile.getBlocks(), srcInodeFile.getId());
assertBlockListEquality(srcBlockLocationsBeforeSwap,
dstInodeFile.getBlocks(), dstInodeFile.getId());
// Assert Block Layout
assertEquals(HeaderFormat.getBlockLayoutPolicy(srcHeader),
HeaderFormat.getBlockLayoutPolicy(dstInodeFile.getHeaderLong()));
assertEquals(HeaderFormat.getBlockLayoutPolicy(dstHeader),
HeaderFormat.getBlockLayoutPolicy(srcInodeFile.getHeaderLong()));
// Assert Storage policy
assertEquals(HeaderFormat.getStoragePolicyID(srcHeader),
HeaderFormat.getStoragePolicyID(dstInodeFile.getHeaderLong()));
assertEquals(HeaderFormat.getStoragePolicyID(dstHeader),
HeaderFormat.getStoragePolicyID(srcInodeFile.getHeaderLong()));
}
@Test
public void testSwapBlockListOpRollback() throws Exception {
// Invoke swap twice and make sure the blocks are back to their original
// file.
String sourceFile = "/TestSwapBlockList/dir1/file1";
String dstFile = "/TestSwapBlockList/dir1/dir11/file3";
INodeFile srcInodeFile =
(INodeFile) fsdir.resolvePath(fsdir.getPermissionChecker(),
sourceFile, FSDirectory.DirOp.WRITE).getLastINode();
INodeFile dstInodeFile =
(INodeFile) fsdir.resolvePath(fsdir.getPermissionChecker(),
dstFile, FSDirectory.DirOp.WRITE).getLastINode();
BlockInfo[] srcBlockLocationsBeforeSwap = srcInodeFile.getBlocks();
long srcHeader = srcInodeFile.getHeaderLong();
BlockInfo[] dstBlockLocationsBeforeSwap = dstInodeFile.getBlocks();
long dstHeader = dstInodeFile.getHeaderLong();
testSwapBlockListOp();
testSwapBlockListOp();
assertBlockListEquality(dstBlockLocationsBeforeSwap,
dstInodeFile.getBlocks(), dstInodeFile.getId());
assertBlockListEquality(srcBlockLocationsBeforeSwap,
srcInodeFile.getBlocks(), srcInodeFile.getId());
// Assert Block Layout
assertEquals(HeaderFormat.getBlockLayoutPolicy(srcHeader),
HeaderFormat.getBlockLayoutPolicy(srcInodeFile.getHeaderLong()));
assertEquals(HeaderFormat.getBlockLayoutPolicy(dstHeader),
HeaderFormat.getBlockLayoutPolicy(dstInodeFile.getHeaderLong()));
}
private void assertBlockListEquality(BlockInfo[] expected,
BlockInfo[] actual,
long expectedId) {
assertEquals(expected.length, actual.length);
for (int i = 0; i < expected.length; i++) {
assertEquals(expected[i].getBlockId(), actual[i].getBlockId());
assertEquals(expectedId, actual[i].getBlockCollectionId());
}
}
}