1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 package org.apache.hadoop.hdfs.server.datanode; 20 21 import org.apache.hadoop.fs.FileUtil; 22 import org.apache.hadoop.hdfs.server.common.HdfsServerConstants.StartupOption; 23 import org.apache.hadoop.hdfs.server.common.Storage; 24 import org.apache.hadoop.hdfs.server.protocol.NamespaceInfo; 25 import org.apache.hadoop.test.GenericTestUtils; 26 import org.junit.After; 27 import org.junit.Before; 28 import org.junit.Test; 29 import org.mockito.Mockito; 30 31 import java.io.File; 32 import java.io.IOException; 33 import java.net.URISyntaxException; 34 import java.util.ArrayList; 35 import java.util.List; 36 37 import static org.junit.Assert.assertEquals; 38 import static org.junit.Assert.assertTrue; 39 import static org.junit.Assert.fail; 40 41 public class TestDataStorage { 42 private final static String DEFAULT_BPID = "bp-0"; 43 private final static String CLUSTER_ID = "cluster0"; 44 private final static String BUILD_VERSION = "2.0"; 45 private final static String SOFTWARE_VERSION = "2.0"; 46 private final static long CTIME = 1; 47 private final static File TEST_DIR = 48 new File(System.getProperty("test.build.data") + "/dstest"); 49 private final static StartupOption START_OPT = StartupOption.REGULAR; 50 51 private DataNode mockDN = Mockito.mock(DataNode.class); 52 private NamespaceInfo nsInfo; 53 private DataStorage storage; 54 55 @Before setUp()56 public void setUp() throws IOException { 57 storage = new DataStorage(); 58 nsInfo = new NamespaceInfo(0, CLUSTER_ID, DEFAULT_BPID, CTIME, 59 BUILD_VERSION, SOFTWARE_VERSION); 60 FileUtil.fullyDelete(TEST_DIR); 61 assertTrue("Failed to make test dir.", TEST_DIR.mkdirs()); 62 } 63 64 @After tearDown()65 public void tearDown() throws IOException { 66 storage.unlockAll(); 67 FileUtil.fullyDelete(TEST_DIR); 68 } 69 createStorageLocations(int numLocs)70 private static List<StorageLocation> createStorageLocations(int numLocs) 71 throws IOException { 72 return createStorageLocations(numLocs, false); 73 } 74 75 /** 76 * Create a list of StorageLocations. 77 * If asFile sets to true, create StorageLocation as regular files, otherwise 78 * create directories for each location. 79 * @param numLocs the total number of StorageLocations to be created. 80 * @param asFile set to true to create as file. 81 * @return a list of StorageLocations. 82 */ createStorageLocations( int numLocs, boolean asFile)83 private static List<StorageLocation> createStorageLocations( 84 int numLocs, boolean asFile) throws IOException { 85 List<StorageLocation> locations = new ArrayList<StorageLocation>(); 86 for (int i = 0; i < numLocs; i++) { 87 String uri = TEST_DIR + "/data" + i; 88 File file = new File(uri); 89 if (asFile) { 90 file.getParentFile().mkdirs(); 91 file.createNewFile(); 92 } else { 93 file.mkdirs(); 94 } 95 StorageLocation loc = StorageLocation.parse(uri); 96 locations.add(loc); 97 } 98 return locations; 99 } 100 createNamespaceInfos(int num)101 private static List<NamespaceInfo> createNamespaceInfos(int num) { 102 List<NamespaceInfo> nsInfos = new ArrayList<NamespaceInfo>(); 103 for (int i = 0; i < num; i++) { 104 String bpid = "bp-" + i; 105 nsInfos.add(new NamespaceInfo(0, CLUSTER_ID, bpid, CTIME, BUILD_VERSION, 106 SOFTWARE_VERSION)); 107 } 108 return nsInfos; 109 } 110 111 /** Check whether the path is a valid DataNode data directory. */ checkDir(File dataDir)112 private static void checkDir(File dataDir) { 113 Storage.StorageDirectory sd = new Storage.StorageDirectory(dataDir); 114 assertTrue(sd.getRoot().isDirectory()); 115 assertTrue(sd.getCurrentDir().isDirectory()); 116 assertTrue(sd.getVersionFile().isFile()); 117 } 118 119 /** Check whether the root is a valid BlockPoolSlice storage. */ checkDir(File root, String bpid)120 private static void checkDir(File root, String bpid) { 121 Storage.StorageDirectory sd = new Storage.StorageDirectory(root); 122 File bpRoot = new File(sd.getCurrentDir(), bpid); 123 Storage.StorageDirectory bpSd = new Storage.StorageDirectory(bpRoot); 124 assertTrue(bpSd.getRoot().isDirectory()); 125 assertTrue(bpSd.getCurrentDir().isDirectory()); 126 assertTrue(bpSd.getVersionFile().isFile()); 127 } 128 129 @Test testAddStorageDirectories()130 public void testAddStorageDirectories() throws IOException, 131 URISyntaxException { 132 final int numLocations = 3; 133 final int numNamespace = 3; 134 List<StorageLocation> locations = createStorageLocations(numLocations); 135 136 // Add volumes for multiple namespaces. 137 List<NamespaceInfo> namespaceInfos = createNamespaceInfos(numNamespace); 138 for (NamespaceInfo ni : namespaceInfos) { 139 storage.addStorageLocations(mockDN, ni, locations, START_OPT); 140 for (StorageLocation sl : locations) { 141 checkDir(sl.getFile()); 142 checkDir(sl.getFile(), ni.getBlockPoolID()); 143 } 144 } 145 146 assertEquals(numLocations, storage.getNumStorageDirs()); 147 148 locations = createStorageLocations(numLocations); 149 List<StorageLocation> addedLocation = 150 storage.addStorageLocations(mockDN, namespaceInfos.get(0), 151 locations, START_OPT); 152 assertTrue(addedLocation.isEmpty()); 153 154 // The number of active storage dirs has not changed, since it tries to 155 // add the storage dirs that are under service. 156 assertEquals(numLocations, storage.getNumStorageDirs()); 157 158 // Add more directories. 159 locations = createStorageLocations(6); 160 storage.addStorageLocations(mockDN, nsInfo, locations, START_OPT); 161 assertEquals(6, storage.getNumStorageDirs()); 162 } 163 164 @Test testRecoverTransitionReadFailure()165 public void testRecoverTransitionReadFailure() throws IOException { 166 final int numLocations = 3; 167 List<StorageLocation> locations = 168 createStorageLocations(numLocations, true); 169 try { 170 storage.recoverTransitionRead(mockDN, nsInfo, locations, START_OPT); 171 fail("An IOException should throw: all StorageLocations are NON_EXISTENT"); 172 } catch (IOException e) { 173 GenericTestUtils.assertExceptionContains( 174 "All specified directories are failed to load.", e); 175 } 176 assertEquals(0, storage.getNumStorageDirs()); 177 } 178 179 /** 180 * This test enforces the behavior that if there is an exception from 181 * doTransition() during DN starts up, the storage directories that have 182 * already been processed are still visible, i.e., in 183 * DataStorage.storageDirs(). 184 */ 185 @Test testRecoverTransitionReadDoTransitionFailure()186 public void testRecoverTransitionReadDoTransitionFailure() 187 throws IOException { 188 final int numLocations = 3; 189 List<StorageLocation> locations = createStorageLocations(numLocations); 190 // Prepare volumes 191 storage.recoverTransitionRead(mockDN, nsInfo, locations, START_OPT); 192 assertEquals(numLocations, storage.getNumStorageDirs()); 193 194 // Reset DataStorage 195 storage.unlockAll(); 196 storage = new DataStorage(); 197 // Trigger an exception from doTransition(). 198 nsInfo.clusterID = "cluster1"; 199 try { 200 storage.recoverTransitionRead(mockDN, nsInfo, locations, START_OPT); 201 fail("Expect to throw an exception from doTransition()"); 202 } catch (IOException e) { 203 GenericTestUtils.assertExceptionContains("All specified directories", e); 204 } 205 assertEquals(0, storage.getNumStorageDirs()); 206 } 207 } 208