|
@@ -18,12 +18,20 @@
|
|
|
package org.apache.hadoop.hdfs.server.datanode.fsdataset.impl;
|
|
|
|
|
|
import org.apache.hadoop.conf.Configuration;
|
|
|
+import org.apache.hadoop.fs.FileSystemTestHelper;
|
|
|
import org.apache.hadoop.hdfs.DFSConfigKeys;
|
|
|
+import org.apache.hadoop.hdfs.StorageType;
|
|
|
+import org.apache.hadoop.hdfs.protocol.Block;
|
|
|
+import org.apache.hadoop.hdfs.protocol.ExtendedBlock;
|
|
|
+import org.apache.hadoop.hdfs.server.common.HdfsServerConstants;
|
|
|
import org.apache.hadoop.hdfs.server.common.Storage;
|
|
|
+import org.apache.hadoop.hdfs.server.common.StorageInfo;
|
|
|
import org.apache.hadoop.hdfs.server.datanode.DNConf;
|
|
|
+import org.apache.hadoop.hdfs.server.datanode.DataBlockScanner;
|
|
|
import org.apache.hadoop.hdfs.server.datanode.DataNode;
|
|
|
import org.apache.hadoop.hdfs.server.datanode.DataStorage;
|
|
|
import org.apache.hadoop.hdfs.server.datanode.StorageLocation;
|
|
|
+import org.apache.hadoop.test.GenericTestUtils;
|
|
|
import org.apache.hadoop.util.StringUtils;
|
|
|
import org.junit.Before;
|
|
|
import org.junit.Test;
|
|
@@ -35,25 +43,44 @@ import java.util.ArrayList;
|
|
|
import java.util.List;
|
|
|
|
|
|
import static org.junit.Assert.assertEquals;
|
|
|
+import static org.junit.Assert.fail;
|
|
|
+import static org.mockito.Matchers.any;
|
|
|
+import static org.mockito.Matchers.anyString;
|
|
|
+import static org.mockito.Mockito.times;
|
|
|
+import static org.mockito.Mockito.verify;
|
|
|
import static org.mockito.Mockito.when;
|
|
|
|
|
|
public class TestFsDatasetImpl {
|
|
|
private static final String BASE_DIR =
|
|
|
- System.getProperty("test.build.dir") + "/fsdatasetimpl";
|
|
|
+ new FileSystemTestHelper().getTestRootDir();
|
|
|
private static final int NUM_INIT_VOLUMES = 2;
|
|
|
+ private static final String[] BLOCK_POOL_IDS = {"bpid-0", "bpid-1"};
|
|
|
|
|
|
+ // Use to generate storageUuid
|
|
|
+ private static final DataStorage dsForStorageUuid = new DataStorage(
|
|
|
+ new StorageInfo(HdfsServerConstants.NodeType.DATA_NODE));
|
|
|
+
|
|
|
+ private Configuration conf;
|
|
|
private DataStorage storage;
|
|
|
+ private DataBlockScanner scanner;
|
|
|
private FsDatasetImpl dataset;
|
|
|
|
|
|
+ private static Storage.StorageDirectory createStorageDirectory(File root) {
|
|
|
+ Storage.StorageDirectory sd = new Storage.StorageDirectory(root);
|
|
|
+ dsForStorageUuid.createStorageID(sd);
|
|
|
+ return sd;
|
|
|
+ }
|
|
|
+
|
|
|
private static void createStorageDirs(DataStorage storage, Configuration conf,
|
|
|
int numDirs) throws IOException {
|
|
|
List<Storage.StorageDirectory> dirs =
|
|
|
new ArrayList<Storage.StorageDirectory>();
|
|
|
List<String> dirStrings = new ArrayList<String>();
|
|
|
for (int i = 0; i < numDirs; i++) {
|
|
|
- String loc = BASE_DIR + "/data" + i;
|
|
|
- dirStrings.add(loc);
|
|
|
- dirs.add(new Storage.StorageDirectory(new File(loc)));
|
|
|
+ File loc = new File(BASE_DIR + "/data" + i);
|
|
|
+ dirStrings.add(loc.toString());
|
|
|
+ loc.mkdirs();
|
|
|
+ dirs.add(createStorageDirectory(loc));
|
|
|
when(storage.getStorageDir(i)).thenReturn(dirs.get(i));
|
|
|
}
|
|
|
|
|
@@ -66,14 +93,19 @@ public class TestFsDatasetImpl {
|
|
|
public void setUp() throws IOException {
|
|
|
final DataNode datanode = Mockito.mock(DataNode.class);
|
|
|
storage = Mockito.mock(DataStorage.class);
|
|
|
- Configuration conf = new Configuration();
|
|
|
+ scanner = Mockito.mock(DataBlockScanner.class);
|
|
|
+ this.conf = new Configuration();
|
|
|
final DNConf dnConf = new DNConf(conf);
|
|
|
|
|
|
when(datanode.getConf()).thenReturn(conf);
|
|
|
when(datanode.getDnConf()).thenReturn(dnConf);
|
|
|
+ when(datanode.getBlockScanner()).thenReturn(scanner);
|
|
|
|
|
|
createStorageDirs(storage, conf, NUM_INIT_VOLUMES);
|
|
|
dataset = new FsDatasetImpl(datanode, storage, conf);
|
|
|
+ for (String bpid : BLOCK_POOL_IDS) {
|
|
|
+ dataset.addBlockPool(bpid, conf);
|
|
|
+ }
|
|
|
|
|
|
assertEquals(NUM_INIT_VOLUMES, dataset.getVolumes().size());
|
|
|
assertEquals(0, dataset.getNumFailedVolumes());
|
|
@@ -89,15 +121,63 @@ public class TestFsDatasetImpl {
|
|
|
String path = BASE_DIR + "/newData" + i;
|
|
|
newLocations.add(StorageLocation.parse(path));
|
|
|
when(storage.getStorageDir(numExistingVolumes + i))
|
|
|
- .thenReturn(new Storage.StorageDirectory(new File(path)));
|
|
|
+ .thenReturn(createStorageDirectory(new File(path)));
|
|
|
}
|
|
|
when(storage.getNumStorageDirs()).thenReturn(totalVolumes);
|
|
|
|
|
|
dataset.addVolumes(newLocations);
|
|
|
assertEquals(totalVolumes, dataset.getVolumes().size());
|
|
|
+ assertEquals(totalVolumes, dataset.storageMap.size());
|
|
|
for (int i = 0; i < numNewVolumes; i++) {
|
|
|
assertEquals(newLocations.get(i).getFile().getPath(),
|
|
|
dataset.getVolumes().get(numExistingVolumes + i).getBasePath());
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testRemoveVolumes() throws IOException {
|
|
|
+ // Feed FsDataset with block metadata.
|
|
|
+ final int NUM_BLOCKS = 100;
|
|
|
+ for (int i = 0; i < NUM_BLOCKS; i++) {
|
|
|
+ String bpid = BLOCK_POOL_IDS[NUM_BLOCKS % BLOCK_POOL_IDS.length];
|
|
|
+ ExtendedBlock eb = new ExtendedBlock(bpid, i);
|
|
|
+ dataset.createRbw(StorageType.DEFAULT, eb);
|
|
|
+ }
|
|
|
+ final String[] dataDirs =
|
|
|
+ conf.get(DFSConfigKeys.DFS_DATANODE_DATA_DIR_KEY).split(",");
|
|
|
+ final String volumePathToRemove = dataDirs[0];
|
|
|
+ List<StorageLocation> volumesToRemove = new ArrayList<StorageLocation>();
|
|
|
+ volumesToRemove.add(StorageLocation.parse(volumePathToRemove));
|
|
|
+
|
|
|
+ dataset.removeVolumes(volumesToRemove);
|
|
|
+ int expectedNumVolumes = dataDirs.length - 1;
|
|
|
+ assertEquals("The volume has been removed from the volumeList.",
|
|
|
+ expectedNumVolumes, dataset.getVolumes().size());
|
|
|
+ assertEquals("The volume has been removed from the storageMap.",
|
|
|
+ expectedNumVolumes, dataset.storageMap.size());
|
|
|
+
|
|
|
+ try {
|
|
|
+ dataset.asyncDiskService.execute(volumesToRemove.get(0).getFile(),
|
|
|
+ new Runnable() {
|
|
|
+ @Override
|
|
|
+ public void run() {}
|
|
|
+ });
|
|
|
+ fail("Expect RuntimeException: the volume has been removed from the "
|
|
|
+ + "AsyncDiskService.");
|
|
|
+ } catch (RuntimeException e) {
|
|
|
+ GenericTestUtils.assertExceptionContains("Cannot find root", e);
|
|
|
+ }
|
|
|
+
|
|
|
+ int totalNumReplicas = 0;
|
|
|
+ for (String bpid : dataset.volumeMap.getBlockPoolList()) {
|
|
|
+ totalNumReplicas += dataset.volumeMap.size(bpid);
|
|
|
+ }
|
|
|
+ assertEquals("The replica infos on this volume has been removed from the "
|
|
|
+ + "volumeMap.", NUM_BLOCKS / NUM_INIT_VOLUMES,
|
|
|
+ totalNumReplicas);
|
|
|
+
|
|
|
+ // Verify that every BlockPool deletes the removed blocks from the volume.
|
|
|
+ verify(scanner, times(BLOCK_POOL_IDS.length))
|
|
|
+ .deleteBlocks(anyString(), any(Block[].class));
|
|
|
+ }
|
|
|
}
|