/** * 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.snapshot; import static org.apache.hadoop.test.GenericTestUtils.assertExceptionContains; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; 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.blockmanagement.BlockManager; import org.apache.hadoop.hdfs.server.namenode.FSDirectory; import org.apache.hadoop.hdfs.server.namenode.FSNamesystem; import org.apache.hadoop.hdfs.server.namenode.INodeFile; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; /** * Test cases for snapshot-related information in blocksMap. */ public class TestSnapshotBlocksMap { private static final long seed = 0; private static final short REPLICATION = 3; private static final int BLOCKSIZE = 1024; private final Path dir = new Path("/TestSnapshot"); private final Path sub1 = new Path(dir, "sub1"); protected Configuration conf; protected MiniDFSCluster cluster; protected FSNamesystem fsn; FSDirectory fsdir; BlockManager blockmanager; protected DistributedFileSystem hdfs; @Before public void setUp() throws Exception { conf = new Configuration(); conf.setLong(DFSConfigKeys.DFS_BLOCK_SIZE_KEY, BLOCKSIZE); cluster = new MiniDFSCluster.Builder(conf).numDataNodes(REPLICATION) .build(); cluster.waitActive(); fsn = cluster.getNamesystem(); fsdir = fsn.getFSDirectory(); blockmanager = fsn.getBlockManager(); hdfs = cluster.getFileSystem(); } @After public void tearDown() throws Exception { if (cluster != null) { cluster.shutdown(); } } void assertAllNull(INodeFile inode, Path path, String[] snapshots) throws Exception { Assert.assertNull(inode.getBlocks()); assertINodeNull(path.toString()); assertINodeNullInSnapshots(path, snapshots); } void assertINodeNull(String path) throws Exception { Assert.assertNull(fsdir.getINode(path)); } void assertINodeNullInSnapshots(Path path, String... snapshots) throws Exception { for(String s : snapshots) { assertINodeNull(SnapshotTestHelper.getSnapshotPath( path.getParent(), s, path.getName()).toString()); } } static INodeFile assertBlockCollection(String path, int numBlocks, final FSDirectory dir, final BlockManager blkManager) throws Exception { final INodeFile file = INodeFile.valueOf(dir.getINode(path), path); assertEquals(numBlocks, file.getBlocks().length); for(BlockInfo b : file.getBlocks()) { assertBlockCollection(blkManager, file, b); } return file; } static void assertBlockCollection(final BlockManager blkManager, final INodeFile file, final BlockInfo b) { Assert.assertSame(b, blkManager.getStoredBlock(b)); Assert.assertSame(file, blkManager.getBlockCollection(b)); Assert.assertSame(file, b.getBlockCollection()); } /** * Test deleting a file with snapshots. Need to check the blocksMap to make * sure the corresponding record is updated correctly. */ @Test (timeout=60000) public void testDeletionWithSnapshots() throws Exception { Path file0 = new Path(sub1, "file0"); Path file1 = new Path(sub1, "file1"); Path sub2 = new Path(sub1, "sub2"); Path file2 = new Path(sub2, "file2"); Path file3 = new Path(sub1, "file3"); Path file4 = new Path(sub1, "file4"); Path file5 = new Path(sub1, "file5"); // Create file under sub1 DFSTestUtil.createFile(hdfs, file0, 4*BLOCKSIZE, REPLICATION, seed); DFSTestUtil.createFile(hdfs, file1, 2*BLOCKSIZE, REPLICATION, seed); DFSTestUtil.createFile(hdfs, file2, 3*BLOCKSIZE, REPLICATION, seed); // Normal deletion { final INodeFile f2 = assertBlockCollection(file2.toString(), 3, fsdir, blockmanager); BlockInfo[] blocks = f2.getBlocks(); hdfs.delete(sub2, true); // The INode should have been removed from the blocksMap for(BlockInfo b : blocks) { assertNull(blockmanager.getBlockCollection(b)); } } // Create snapshots for sub1 final String[] snapshots = {"s0", "s1", "s2"}; DFSTestUtil.createFile(hdfs, file3, 5*BLOCKSIZE, REPLICATION, seed); SnapshotTestHelper.createSnapshot(hdfs, sub1, snapshots[0]); DFSTestUtil.createFile(hdfs, file4, 1*BLOCKSIZE, REPLICATION, seed); SnapshotTestHelper.createSnapshot(hdfs, sub1, snapshots[1]); DFSTestUtil.createFile(hdfs, file5, 7*BLOCKSIZE, REPLICATION, seed); SnapshotTestHelper.createSnapshot(hdfs, sub1, snapshots[2]); // set replication so that the inode should be replaced for snapshots { INodeFile f1 = assertBlockCollection(file1.toString(), 2, fsdir, blockmanager); Assert.assertSame(INodeFile.class, f1.getClass()); hdfs.setReplication(file1, (short)2); f1 = assertBlockCollection(file1.toString(), 2, fsdir, blockmanager); Assert.assertSame(INodeFileWithSnapshot.class, f1.getClass()); } // Check the block information for file0 final INodeFile f0 = assertBlockCollection(file0.toString(), 4, fsdir, blockmanager); BlockInfo[] blocks0 = f0.getBlocks(); // Also check the block information for snapshot of file0 Path snapshotFile0 = SnapshotTestHelper.getSnapshotPath(sub1, "s0", file0.getName()); assertBlockCollection(snapshotFile0.toString(), 4, fsdir, blockmanager); // Delete file0 hdfs.delete(file0, true); // Make sure the blocks of file0 is still in blocksMap for(BlockInfo b : blocks0) { assertNotNull(blockmanager.getBlockCollection(b)); } assertBlockCollection(snapshotFile0.toString(), 4, fsdir, blockmanager); // Compare the INode in the blocksMap with INodes for snapshots String s1f0 = SnapshotTestHelper.getSnapshotPath(sub1, "s1", file0.getName()).toString(); assertBlockCollection(s1f0, 4, fsdir, blockmanager); // Delete snapshot s1 hdfs.deleteSnapshot(sub1, "s1"); // Make sure the first block of file0 is still in blocksMap for(BlockInfo b : blocks0) { assertNotNull(blockmanager.getBlockCollection(b)); } assertBlockCollection(snapshotFile0.toString(), 4, fsdir, blockmanager); try { INodeFile.valueOf(fsdir.getINode(s1f0), s1f0); fail("Expect FileNotFoundException when identifying the INode in a deleted Snapshot"); } catch (IOException e) { assertExceptionContains("File does not exist: " + s1f0, e); } } }