/** * 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 static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileUtil; import org.apache.hadoop.hdfs.protocol.AvatarConstants.InstanceId; import org.apache.hadoop.hdfs.server.common.Util; import org.apache.hadoop.hdfs.server.namenode.AvatarNode.StartupInfo; import org.apache.hadoop.hdfs.server.namenode.JournalStream.JournalType; import org.apache.hadoop.hdfs.server.namenode.NNStorage.StorageLocationType; import org.apache.hadoop.hdfs.server.namenode.ValidateNamespaceDirPolicy.NNStorageLocation; import org.junit.Before; import org.junit.Test; public class TestAvatarStorageSetup { public static final Log LOG = LogFactory.getLog(TestAvatarStorageSetup.class .getName()); private static String baseDir = System.getProperty("test.build.data","build/test/data/") + "/"; // names for image/edits private final String imageLclName = baseDir + "image/"; private final String imageShdName0 = baseDir + "imageShared0/"; private final String imageShdName1 = baseDir + "imageShared1/"; private final String editsLclName = baseDir + "edits/"; private final String editsShdName0 = baseDir + "editsShared0/"; private final String editsShdName1 = baseDir + "editsShared1/"; // usr's for image/edits private URI imageLcl, imageShd0, imageShd1; private URI editsLcl, editsShd0, editsShd1; private Configuration conf; @Before public void setUp() throws Exception { Collection<String> dirList = new ArrayList<String>(); dirList.add(imageLclName); dirList.add(imageShdName0); dirList.add(imageShdName1); dirList.add(editsLclName); dirList.add(editsShdName0); dirList.add(editsShdName1); for(String name : dirList) { File dir = new File(name); FileUtil.fullyDelete(dir); dir.mkdirs(); } imageLcl = Util.stringAsURI(imageLclName); imageShd0 = Util.stringAsURI(imageShdName0); imageShd1 = Util.stringAsURI(imageShdName1); editsLcl = Util.stringAsURI(editsLclName); editsShd0 = Util.stringAsURI(editsShdName0); editsShd1 = Util.stringAsURI(editsShdName1); conf = new Configuration(); } @Test public void testStringToUris() { // set strings in configuration conf.set("dfs.name.dir", imageLclName + "," + imageShdName0); conf.set("dfs.name.edits.dir", editsLclName + "," + editsShdName0); Collection<URI> dirs = NNStorageConfiguration.getNamespaceDirs(conf, null); // check if conversion to URIs is correct assertEqualsCol(getList(imageLcl, imageShd0), dirs); dirs = NNStorageConfiguration.getNamespaceEditsDirs(conf, null); // check if conversion to URIs is correct assertEqualsCol(getList(editsLcl, editsShd0), dirs); } // test parameter correctness @Test(expected = IOException.class) public void sharedImageShoudlNotOverlap() throws IOException { // dfs.name.dir already contains the shared directory AvatarStorageSetup.validate(conf, getList(imageLcl, imageShd0), getList(editsLcl), imageShd0, imageShd1, editsShd0, editsShd1); } @Test(expected = IOException.class) public void sharedImageQJMNotEqualToEdits() throws IOException, URISyntaxException { // QJM shared image location URI qjmSharedImage = new URI("qjm://testjournalImage"); URI qjmSharedJournal = new URI("qjm://testjournalJournal"); AvatarStorageSetup.validate(conf, getList(imageLcl), getList(editsLcl), qjmSharedImage, imageShd1, qjmSharedJournal, editsShd1); } @Test public void sharedImageQJM() throws IOException, URISyntaxException { // QJM shared image location URI qjmShared = new URI("qjm://testjournal"); AvatarStorageSetup.validate(conf, getList(imageLcl), getList(editsLcl), qjmShared, imageShd1, qjmShared, editsShd1); } @Test(expected = IOException.class) public void sharedEditsShoudlNotOverlap() throws IOException { // dfs.name.edits.dir already contains the shared directory AvatarStorageSetup.validate(conf, getList(imageLcl), getList(editsLcl, editsShd0), imageShd0, imageShd1, editsShd0, editsShd1); } @Test(expected = IOException.class) public void shouldNotContainSharedImageLocation() throws IOException { // dfs.name.dir.shared is set manually conf.set("dfs.name.dir.shared", "/somelocation"); AvatarStorageSetup.validate(conf, getList(imageLcl), getList(editsLcl), imageShd0, imageShd1, editsShd0, editsShd1); } @Test(expected = IOException.class) public void shouldNotContainSharedEditsLocation() throws IOException { // dfs.name.edits.dir.shared is set manually conf.set("dfs.name.edits.dir.shared", "/somelocation"); AvatarStorageSetup.validate(conf, getList(imageLcl), getList(editsLcl), imageShd0, imageShd1, editsShd0, editsShd1); } @Test(expected = IOException.class) public void shouldNotContainNulls() throws IOException { conf.set("dfs.name.edits.dir.shared", "/somelocation"); AvatarStorageSetup.validate(conf, getList(imageLcl), getList(editsLcl), null, imageShd1, editsShd0, editsShd1); } // test configuration setup @Test public void testSetup() throws IOException { Configuration newconf = null; // validate AvatarStorageSetup.validate(conf, getList(imageLcl), getList(editsLcl), imageShd0, imageShd1, editsShd0, editsShd1); // update configuration for nodezero newconf = new Configuration(); StartupInfo startInfoZero = new StartupInfo(null, InstanceId.NODEZERO, false, "service", false); updateConf(startInfoZero, newconf); // check configuration assertEqualsCol(getList(imageLcl, imageShd0), NNStorageConfiguration.getNamespaceDirs(newconf, null)); assertEqualsCol(getList(editsLcl, editsShd0), NNStorageConfiguration.getNamespaceEditsDirs(newconf, null)); // check shared directory checkKey(imageShd0, "dfs.name.dir.shared", newconf); checkKey(editsShd0, "dfs.name.edits.dir.shared", newconf); // update configuration for nodeone newconf = new Configuration(); StartupInfo startInfoOne = new StartupInfo(null, InstanceId.NODEONE, true, "service", false); updateConf(startInfoOne, newconf); // check configuration assertEqualsCol(getList(imageLcl, imageShd1), NNStorageConfiguration.getNamespaceDirs(newconf, null)); assertEqualsCol(getList(editsLcl, editsShd1), NNStorageConfiguration.getNamespaceEditsDirs(newconf, null)); // check shared directory checkKey(imageShd1, "dfs.name.dir.shared", newconf); checkKey(editsShd1, "dfs.name.edits.dir.shared", newconf); } @Test public void testLocationMapForFiles() throws IOException { // standard configuration with all file journal/images testLocationMap(conf, imageLcl, imageShd0, imageShd1, editsLcl, editsShd0, editsShd1); } @Test public void testLocationMapForNonFiles() throws Exception { // configure non-file journals // local image and edits must be file-based editsShd0 = Util.stringAsURI("foo:/editsshd0"); editsShd1 = Util.stringAsURI("foo:/editsshd1"); testLocationMap(conf, imageLcl, imageShd0, imageShd1, editsLcl, editsShd0, editsShd1); } public void testLocationMap(Configuration conf, URI imageLcl, URI imageShd0, URI imageShd1, URI editsLcl, URI editsShd0, URI editsShd1) throws IOException { Configuration newconf = null; // validate AvatarStorageSetup.validate(conf, getList(imageLcl), getList(editsLcl), imageShd0, imageShd1, editsShd0, editsShd1); // update configuration for nodezero newconf = new Configuration(); StartupInfo startInfoZero = new StartupInfo(null, InstanceId.NODEZERO, false, "service", false); updateConf(startInfoZero, newconf); // validate Map<URI, NNStorageLocation> map = ValidateNamespaceDirPolicy.validate(newconf); // shared image and edits assertTrue(map.get(imageShd0).type == StorageLocationType.SHARED); assertTrue(map.get(editsShd0).type == StorageLocationType.SHARED); // local are not shared Collection<URI> l = getList(imageLcl, editsLcl); for (URI u : l) { if ((u.getScheme().compareTo(JournalType.FILE.name().toLowerCase()) == 0)) assertTrue(map.get(editsLcl).type == StorageLocationType.LOCAL); else assertTrue(map.get(editsLcl).type == StorageLocationType.REMOTE); } // all locations are present for (URI u : getList(imageLcl, imageShd0, editsLcl, editsShd0)) { assertNotNull(map.get(u)); } } @Test public void testSameSharedImageLocation() throws Exception { Configuration conf = new Configuration(); URI img0 = new URI("qjm://localhost:1234;localhost:1235;localhost:1236/test-id/"); URI img1 = new URI("qjm://localhost:1234;localhost:1235;localhost:1236/test-id/"); URI edit0 = new URI("qjm://localhost:1234;localhost:1235;localhost:1236/test-id/zero/"); URI edit1 = new URI("qjm://localhost:1234;localhost:1235;localhost:1236/test-id/one/"); conf.set("dfs.name.dir.shared0", img0.toString()); conf.set("dfs.name.dir.shared1", img1.toString()); conf.set("dfs.name.edits.dir.shared0", edit0.toString()); conf.set("dfs.name.edits.dir.shared1", edit1.toString()); // local locations for image and edits Collection<URI> namedirs = NNStorageConfiguration.getNamespaceDirs(conf, null); Collection<URI> editsdir = NNStorageConfiguration.getNamespaceEditsDirs(conf, null); try { AvatarStorageSetup.validate(conf, namedirs, editsdir, img0, img1, edit0, edit1); fail("fail of same shared image location"); } catch (IOException ex) { assertTrue(ex.getMessage().contains("same image location")); } } @Test public void testSameSharedEditsLocation() throws Exception { Configuration conf = new Configuration(); URI img0 = new URI("qjm://localhost:1234;localhost:1235;localhost:1236/test-id/zero/"); URI img1 = new URI("qjm://localhost:1234;localhost:1235;localhost:1236/test-id/one/"); URI edit0 = new URI("qjm://localhost:1234;localhost:1235;localhost:1236/test-id/"); URI edit1 = new URI("qjm://localhost:1234;localhost:1235;localhost:1236/test-id/"); conf.set("dfs.name.dir.shared0", img0.toString()); conf.set("dfs.name.dir.shared1", img1.toString()); conf.set("dfs.name.edits.dir.shared0", edit0.toString()); conf.set("dfs.name.edits.dir.shared1", edit1.toString()); // local locations for image and edits Collection<URI> namedirs = NNStorageConfiguration.getNamespaceDirs(conf, null); Collection<URI> editsdir = NNStorageConfiguration.getNamespaceEditsDirs(conf, null); try { AvatarStorageSetup.validate(conf, namedirs, editsdir, img0, img1, edit0, edit1); fail("fail of same shared eduts location"); } catch (IOException ex) { assertTrue(ex.getMessage().contains("same edits location")); } } ///// helpers private void checkKey(URI expected, String key, Configuration conf) throws IOException { assertEquals(expected, Util.stringAsURI(conf.get(key))); } private void updateConf(StartupInfo si, Configuration newconf) { AvatarStorageSetup.updateConf(si, newconf, getList(imageLcl), imageShd0, imageShd1, "dfs.name.dir"); AvatarStorageSetup.updateConf(si, newconf, getList(editsLcl), editsShd0, editsShd1, "dfs.name.edits.dir"); } private void assertEqualsCol(Collection<URI> obj1, Collection<URI> obj2) { assertTrue(obj1.containsAll(obj2)); assertTrue(obj2.containsAll(obj1)); } private Collection<URI> getList(URI... uris) { List<URI> result = new ArrayList<URI>(); for (URI u : uris) { result.add(u); } return result; } }