/** * 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 java.util.ArrayList; import java.util.List; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import java.util.Map; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.hdfs.protocol.DatanodeID; import org.apache.hadoop.hdfs.MiniDFSCluster; import org.apache.hadoop.hdfs.protocol.LocatedBlock; import org.apache.hadoop.hdfs.protocol.LocatedBlocks; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.fs.Path; import org.apache.hadoop.hdfs.DFSTestUtil; import org.apache.hadoop.hdfs.protocol.DatanodeInfo; import org.apache.hadoop.hdfs.server.datanode.DataNode; import org.apache.hadoop.hdfs.protocol.FSConstants; import org.apache.hadoop.raid.Utils; import junit.framework.TestCase; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import static org.junit.Assert.*; /** * This class tests the BlockPlacementPolicyF4BadHostsAndRacks policy. * The policy tries to spread the blocks of a file evenly over all the * available racks with some bad hosts and racks. * * This is the topology (R0-R9 are racks and h0-h39 are hosts within them): * * R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 * ----------------------------------------------- * h0 h1 h2 h3 h4 h5 h6 h7 h8 h9 * h10 h11 h12 h13 h14 h15 h16 h17 h18 h19 * h20 h21 h22 h23 h24 h25 h26 h27 h28 h29 * h30 h31 h32 h33 h34 h35 h36 h37 h38 h39 * * After we set R0 to R4 bad and h5-h9, h15-h19 bad, the expected * placement should happen such that all 40 blocks are equally * spread over h25-h29,h35-h39 * * X X X X X R5 R6 R7 R8 R9 * -------------------------------------------- * h0 h1 h2 h3 h4 x x x x x * h10 h11 h12 h13 x x x x x * h20 h21 h22 h23 h24 h25 h26 h27 h28 h29 * h30 h31 h32 h33 h34 h35 h36 h37 h38 h39 * * This is the placement that is verified in this test. */ public class TestFaultTolerantPlacementPolicyBadHostsAndRacks { private static MiniDFSCluster cluster; private static Configuration conf; private static final int BLOCK_SIZE = 1; private static final int STRIPE_SIZE = 10; private static final int PARITY_LEN = 4; private static final int NUM_RACKS = STRIPE_SIZE; private static final int BLOCKS_PER_RACK = 4; private static final String FILENAME = "testFile"; private static Set<String> BAD_RACKS = new HashSet<String>(); private static Set<String> BAD_HOSTS = new HashSet<String>(); @BeforeClass public static void setUpBeforeClass() throws Exception { conf = new Configuration(); conf.setLong("dfs.blockreport.intervalMsec", 1000L); conf.set("dfs.replication.pending.timeout.sec", "2"); conf.setLong("dfs.block.size", BLOCK_SIZE); conf.set("dfs.block.replicator.classname", "org.apache.hadoop.hdfs.server.namenode.FaultTolerantBlockPlacementPolicy"); conf.setInt("io.bytes.per.checksum", 1); Utils.loadTestCodecs(conf, STRIPE_SIZE, 1, PARITY_LEN, "/raid", "/raidrs"); int numBlocks = NUM_RACKS * BLOCKS_PER_RACK; List<String> racks = new ArrayList<String>(NUM_RACKS); String badRacks = ""; for (int i = 0; i < numBlocks; ++i) { int rackId = i%NUM_RACKS; String rack = new String("/r" + rackId); racks.add(rack); } cluster = new MiniDFSCluster(conf, numBlocks, true, racks.toArray(new String[numBlocks]), null); cluster.waitActive(true); TestFaultTolerantPlacementPolicy.updateDatanodeMap(cluster); // Now set up the bad hosts and racks such that 50% of the racks // are bad and 50% of the hosts in the remaining racks are bad. NameNode nn = cluster.getNameNode(); int i = 0; HashMap<String, Set<String>> rackToHost = new HashMap<String, Set<String>>(); for (DatanodeInfo a : nn.getDatanodeReport( FSConstants.DatanodeReportType.ALL)) { Set<String> hosts = rackToHost.get(a.getNetworkLocation()); if (hosts == null) { hosts = new HashSet<String>(); rackToHost.put(a.getNetworkLocation(), hosts); } hosts.add(a.getName()); } i = 0; for (Map.Entry<String, Set<String>> entry : rackToHost.entrySet()) { if (i++ < NUM_RACKS/2) { BAD_RACKS.add(entry.getKey()); } else { int j = 0; int size = entry.getValue().size(); for (String host : entry.getValue()) { if (j++ < size/2) { BAD_HOSTS.add(host); } } } } FaultTolerantBlockPlacementPolicy.setBadHostsAndRacks( BAD_RACKS, BAD_HOSTS); } @AfterClass public static void tearDownAfterClass() throws Exception { cluster.shutdown(); } @Test public void testChooseTargetRackBasedWithBadRack() throws Exception { String fileName = new String("/hay/" + FILENAME); DFSTestUtil.createFile(cluster.getFileSystem(), new Path(fileName), (long) STRIPE_SIZE*BLOCK_SIZE*BLOCKS_PER_RACK, (short)1, // replication (long)0); HashMap<String, Integer> rackMapCount = new HashMap<String, Integer>(); LocatedBlocks blocks = cluster.getNameNode().namesystem.getBlockLocations(fileName, 0, Long.MAX_VALUE); // This means that nothing should exist on any of the bad host or rack. // This also means that each host should have 4X the blocks it would // have otherwise = 1. HashMap<String, Integer> rackCount = new HashMap<String, Integer>(); HashMap<String, Integer> hostCount = new HashMap<String, Integer>(); for (LocatedBlock b : blocks.getLocatedBlocks()) { for (DatanodeInfo i : b.getLocations()) { String rack = i.getNetworkLocation(); String host = i.getName(); // Should not get placed in a bad rack or bad host. assertFalse( TestFaultTolerantPlacementPolicyBadHostsAndRacks.BAD_RACKS.contains(rack)); assertFalse( TestFaultTolerantPlacementPolicyBadHostsAndRacks.BAD_HOSTS.contains(host)); Integer count = rackCount.get(rack); if (count == null) { rackCount.put(rack, new Integer(1)); } else { int newCount = count.intValue() + 1; rackCount.put(rack, newCount); } count = hostCount.get(host); if (count == null) { hostCount.put(host, new Integer(1)); } else { int newCount = count.intValue() + 1; hostCount.put(host, newCount); } } } for (Integer count: hostCount.values()) { // Since the # good hosts is 1/4th, the number of blocks per // host should be 4X (=4) assertEquals(count.intValue(), 4); } for (Integer count : rackCount.values()) { // Since the number of good racks is 1/2th, the number of blocks // per rack should be 2X assertEquals(count.intValue(), 2*BLOCKS_PER_RACK); } } }