/** * 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.blockmanagement; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Random; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.CommonConfigurationKeysPublic; import org.apache.hadoop.hdfs.DFSConfigKeys; import org.apache.hadoop.hdfs.server.namenode.FSNamesystem; import org.apache.hadoop.hdfs.server.protocol.DatanodeRegistration; import org.apache.hadoop.net.DNSToSwitchMapping; import org.junit.Assert; import org.junit.Test; import org.mockito.Mockito; import static org.junit.Assert.*; public class TestDatanodeManager { public static final Log LOG = LogFactory.getLog(TestDatanodeManager.class); //The number of times the registration / removal of nodes should happen final int NUM_ITERATIONS = 500; /** * This test sends a random sequence of node registrations and node removals * to the DatanodeManager (of nodes with different IDs and versions), and * checks that the DatanodeManager keeps a correct count of different software * versions at all times. */ @Test public void testNumVersionsReportedCorrect() throws IOException { //Create the DatanodeManager which will be tested FSNamesystem fsn = Mockito.mock(FSNamesystem.class); Mockito.when(fsn.hasWriteLock()).thenReturn(true); DatanodeManager dm = new DatanodeManager(Mockito.mock(BlockManager.class), fsn, new Configuration()); //Seed the RNG with a known value so test failures are easier to reproduce Random rng = new Random(); int seed = rng.nextInt(); rng = new Random(seed); LOG.info("Using seed " + seed + " for testing"); //A map of the Storage IDs to the DN registration it was registered with HashMap <String, DatanodeRegistration> sIdToDnReg = new HashMap<String, DatanodeRegistration>(); for(int i=0; i<NUM_ITERATIONS; ++i) { //If true, remove a node for every 3rd time (if there's one) if(rng.nextBoolean() && i%3 == 0 && sIdToDnReg.size()!=0) { //Pick a random node. int randomIndex = rng.nextInt() % sIdToDnReg.size(); //Iterate to that random position Iterator<Map.Entry<String, DatanodeRegistration>> it = sIdToDnReg.entrySet().iterator(); for(int j=0; j<randomIndex-1; ++j) { it.next(); } DatanodeRegistration toRemove = it.next().getValue(); LOG.info("Removing node " + toRemove.getDatanodeUuid() + " ip " + toRemove.getXferAddr() + " version : " + toRemove.getSoftwareVersion()); //Remove that random node dm.removeDatanode(toRemove); it.remove(); } // Otherwise register a node. This node may be a new / an old one else { //Pick a random storageID to register. String storageID = "someStorageID" + rng.nextInt(5000); DatanodeRegistration dr = Mockito.mock(DatanodeRegistration.class); Mockito.when(dr.getDatanodeUuid()).thenReturn(storageID); //If this storageID had already been registered before if(sIdToDnReg.containsKey(storageID)) { dr = sIdToDnReg.get(storageID); //Half of the times, change the IP address if(rng.nextBoolean()) { dr.setIpAddr(dr.getIpAddr() + "newIP"); } } else { //This storageID has never been registered //Ensure IP address is unique to storageID String ip = "someIP" + storageID; Mockito.when(dr.getIpAddr()).thenReturn(ip); Mockito.when(dr.getXferAddr()).thenReturn(ip + ":9000"); Mockito.when(dr.getXferPort()).thenReturn(9000); } //Pick a random version to register with Mockito.when(dr.getSoftwareVersion()).thenReturn( "version" + rng.nextInt(5)); LOG.info("Registering node storageID: " + dr.getDatanodeUuid() + ", version: " + dr.getSoftwareVersion() + ", IP address: " + dr.getXferAddr()); //Register this random node dm.registerDatanode(dr); sIdToDnReg.put(storageID, dr); } //Verify DatanodeManager still has the right count Map<String, Integer> mapToCheck = dm.getDatanodesSoftwareVersions(); //Remove counts from versions and make sure that after removing all nodes //mapToCheck is empty for(Entry<String, DatanodeRegistration> it: sIdToDnReg.entrySet()) { String ver = it.getValue().getSoftwareVersion(); if(!mapToCheck.containsKey(ver)) { throw new AssertionError("The correct number of datanodes of a " + "version was not found on iteration " + i); } mapToCheck.put(ver, mapToCheck.get(ver) - 1); if(mapToCheck.get(ver) == 0) { mapToCheck.remove(ver); } } for(Entry <String, Integer> entry: mapToCheck.entrySet()) { LOG.info("Still in map: " + entry.getKey() + " has " + entry.getValue()); } assertEquals("The map of version counts returned by DatanodeManager was" + " not what it was expected to be on iteration " + i, 0, mapToCheck.size()); } } @Test (timeout = 100000) public void testRejectUnresolvedDatanodes() throws IOException { //Create the DatanodeManager which will be tested FSNamesystem fsn = Mockito.mock(FSNamesystem.class); Mockito.when(fsn.hasWriteLock()).thenReturn(true); Configuration conf = new Configuration(); //Set configuration property for rejecting unresolved topology mapping conf.setBoolean( DFSConfigKeys.DFS_REJECT_UNRESOLVED_DN_TOPOLOGY_MAPPING_KEY, true); //set TestDatanodeManager.MyResolver to be used for topology resolving conf.setClass( CommonConfigurationKeysPublic.NET_TOPOLOGY_NODE_SWITCH_MAPPING_IMPL_KEY, TestDatanodeManager.MyResolver.class, DNSToSwitchMapping.class); //create DatanodeManager DatanodeManager dm = new DatanodeManager(Mockito.mock(BlockManager.class), fsn, conf); //storageID to register. String storageID = "someStorageID-123"; DatanodeRegistration dr = Mockito.mock(DatanodeRegistration.class); Mockito.when(dr.getDatanodeUuid()).thenReturn(storageID); try { //Register this node dm.registerDatanode(dr); Assert.fail("Expected an UnresolvedTopologyException"); } catch (UnresolvedTopologyException ute) { LOG.info("Expected - topology is not resolved and " + "registration is rejected."); } catch (Exception e) { Assert.fail("Expected an UnresolvedTopologyException"); } } /** * MyResolver class provides resolve method which always returns null * in order to simulate unresolved topology mapping. */ public static class MyResolver implements DNSToSwitchMapping { @Override public List<String> resolve(List<String> names) { return null; } @Override public void reloadCachedMappings() { } @Override public void reloadCachedMappings(List<String> names) { } } }