From e56d55115bbd1f64e884227f8c6603bce82750d9 Mon Sep 17 00:00:00 2001 From: Andrei Mikhalev <4503006+Montura@users.noreply.github.com> Date: Wed, 31 Jul 2024 22:21:25 -0700 Subject: [PATCH] HDDS-10917. Refactor more tests from TestContainerBalancerTask (#6734) --- ...estContainerBalancerDatanodeNodeLimit.java | 275 +++++++++++++++++- .../balancer/TestContainerBalancerTask.java | 259 ----------------- 2 files changed, 269 insertions(+), 265 deletions(-) diff --git a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancerDatanodeNodeLimit.java b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancerDatanodeNodeLimit.java index fc8eaf2ff55..7a8f655f067 100644 --- a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancerDatanodeNodeLimit.java +++ b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancerDatanodeNodeLimit.java @@ -21,8 +21,16 @@ import jakarta.annotation.Nonnull; import org.apache.hadoop.hdds.conf.OzoneConfiguration; import org.apache.hadoop.hdds.protocol.DatanodeDetails; +import org.apache.hadoop.hdds.protocol.proto.HddsProtos; +import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos; +import org.apache.hadoop.hdds.scm.ContainerPlacementStatus; import org.apache.hadoop.hdds.scm.container.ContainerID; +import org.apache.hadoop.hdds.scm.container.ContainerInfo; +import org.apache.hadoop.hdds.scm.container.ContainerNotFoundException; +import org.apache.hadoop.hdds.scm.container.ContainerReplica; +import org.apache.hadoop.hdds.scm.container.ContainerReplicaNotFoundException; import org.apache.hadoop.hdds.scm.node.DatanodeUsageInfo; +import org.apache.hadoop.hdds.scm.node.NodeStatus; import org.apache.hadoop.hdds.scm.node.states.NodeNotFoundException; import org.apache.hadoop.ozone.OzoneConsts; import org.apache.ozone.test.GenericTestUtils; @@ -37,9 +45,13 @@ import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; import java.util.stream.Stream; import static org.apache.hadoop.hdds.scm.container.balancer.TestableCluster.RANDOM; @@ -47,6 +59,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.atLeastOnce; @@ -256,14 +269,11 @@ public void testBalancerWithMoveManager(@Nonnull MockedSCM mockedSCM) mockedSCM.disableLegacyReplicationManager(); mockedSCM.startBalancerTask(config); - verify(mockedSCM.getMoveManager(), atLeastOnce()) - .move(any(ContainerID.class), - any(DatanodeDetails.class), - any(DatanodeDetails.class)); + verify(mockedSCM.getMoveManager(), atLeastOnce()). + move(any(ContainerID.class), any(DatanodeDetails.class), any(DatanodeDetails.class)); verify(mockedSCM.getReplicationManager(), times(0)) - .move(any(ContainerID.class), any( - DatanodeDetails.class), any(DatanodeDetails.class)); + .move(any(ContainerID.class), any(DatanodeDetails.class), any(DatanodeDetails.class)); } @ParameterizedTest(name = "MockedSCM #{index}: {0}") @@ -325,6 +335,259 @@ public void testMetrics(@Nonnull MockedSCM mockedSCM) throws IOException, NodeNo assertEquals(1, metrics.getNumContainerMovesFailed()); } + @ParameterizedTest(name = "MockedSCM #{index}: {0}") + @MethodSource("createMockedSCMs") + public void containerBalancerShouldSelectOnlyClosedContainers(@Nonnull MockedSCM mockedSCM) { + ContainerBalancerConfiguration config = balancerConfigByOzoneConfig(new OzoneConfiguration()); + int nodeCount = mockedSCM.getCluster().getNodeCount(); + if (nodeCount < DATANODE_COUNT_LIMIT_FOR_SMALL_CLUSTER) { + config.setMaxDatanodesPercentageToInvolvePerIteration(100); + } + config.setIterations(1); + config.setThreshold(10); + config.setMaxSizeToMovePerIteration(50 * STORAGE_UNIT); + config.setMaxSizeEnteringTarget(50 * STORAGE_UNIT); + + Map cidToInfoMap = mockedSCM.getCluster().getCidToInfoMap(); + // Make all containers open, balancer should not select any of them + for (ContainerInfo containerInfo : cidToInfoMap.values()) { + containerInfo.setState(HddsProtos.LifeCycleState.OPEN); + } + + ContainerBalancerTask task = mockedSCM.startBalancerTask(config); + + // Balancer should have identified unbalanced nodes + assertFalse(TestContainerBalancerDatanodeNodeLimit.getUnBalancedNodes(task).isEmpty()); + // No container should have been selected + assertTrue(task.getContainerToSourceMap().isEmpty()); + + // Iteration result should be CAN_NOT_BALANCE_ANY_MORE because no container move is generated + assertEquals(ContainerBalancerTask.IterationResult.CAN_NOT_BALANCE_ANY_MORE, task.getIterationResult()); + + // Now, close all containers + for (ContainerInfo containerInfo : cidToInfoMap.values()) { + containerInfo.setState(HddsProtos.LifeCycleState.CLOSED); + } + ContainerBalancerTask nextTask = mockedSCM.startBalancerTask(config); + + // Check whether all selected containers are closed + for (ContainerID cid: nextTask.getContainerToSourceMap().keySet()) { + assertSame(cidToInfoMap.get(cid).getState(), HddsProtos.LifeCycleState.CLOSED); + } + } + + @ParameterizedTest(name = "MockedSCM #{index}: {0}") + @MethodSource("createMockedSCMs") + public void balancerShouldNotSelectNonClosedContainerReplicas(@Nonnull MockedSCM mockedSCM) + throws ContainerNotFoundException { + ContainerBalancerConfiguration config = balancerConfigByOzoneConfig(new OzoneConfiguration()); + int nodeCount = mockedSCM.getCluster().getNodeCount(); + if (nodeCount < DATANODE_COUNT_LIMIT_FOR_SMALL_CLUSTER) { + config.setMaxDatanodesPercentageToInvolvePerIteration(100); + } + config.setIterations(1); + config.setThreshold(10); + config.setMaxSizeToMovePerIteration(50 * STORAGE_UNIT); + config.setMaxSizeEnteringTarget(50 * STORAGE_UNIT); + + // Let's mock such that all replicas have CLOSING state + Map> cidToReplicasMap = mockedSCM.getCluster().getCidToReplicasMap(); + when(mockedSCM.getContainerManager().getContainerReplicas(any(ContainerID.class))) + .thenAnswer(invocationOnMock -> { + ContainerID cid = (ContainerID) invocationOnMock.getArguments()[0]; + Set replicas = cidToReplicasMap.get(cid); + Set replicasToReturn = new HashSet<>(replicas.size()); + for (ContainerReplica replica : replicas) { + ContainerReplica newReplica = replica.toBuilder() + .setContainerState(StorageContainerDatanodeProtocolProtos.ContainerReplicaProto.State.CLOSING) + .build(); + replicasToReturn.add(newReplica); + } + + return replicasToReturn; + }); + + ContainerBalancerTask task = mockedSCM.startBalancerTask(config); + + // Balancer should have identified unbalanced nodes + assertFalse(TestContainerBalancerDatanodeNodeLimit.getUnBalancedNodes(task).isEmpty()); + // No container should have moved because all replicas are CLOSING + assertTrue(task.getContainerToSourceMap().isEmpty()); + } + + @ParameterizedTest(name = "MockedSCM #{index}: {0}") + @MethodSource("createMockedSCMs") + public void containerBalancerShouldObeyMaxSizeToMoveLimit(@Nonnull MockedSCM mockedSCM) { + ContainerBalancerConfiguration config = balancerConfigByOzoneConfig(new OzoneConfiguration()); + int nodeCount = mockedSCM.getCluster().getNodeCount(); + if (nodeCount < DATANODE_COUNT_LIMIT_FOR_SMALL_CLUSTER) { + config.setMaxDatanodesPercentageToInvolvePerIteration(100); + } + config.setIterations(1); + config.setThreshold(1); + config.setMaxSizeToMovePerIteration(10 * STORAGE_UNIT); + config.setMaxSizeEnteringTarget(10 * STORAGE_UNIT); + + ContainerBalancerTask task = mockedSCM.startBalancerTask(config); + + // Balancer should not have moved more size than the limit + assertThat(task.getSizeScheduledForMoveInLatestIteration()).isLessThanOrEqualTo(10 * STORAGE_UNIT); + + long size = task.getMetrics().getDataSizeMovedGBInLatestIteration(); + assertThat(size).isGreaterThan(0); + assertThat(size).isLessThanOrEqualTo(10); + } + + @ParameterizedTest(name = "MockedSCM #{index}: {0}") + @MethodSource("createMockedSCMs") + public void targetDatanodeShouldNotAlreadyContainSelectedContainer(@Nonnull MockedSCM mockedSCM) { + ContainerBalancerConfiguration config = balancerConfigByOzoneConfig(new OzoneConfiguration()); + int nodeCount = mockedSCM.getCluster().getNodeCount(); + if (nodeCount < DATANODE_COUNT_LIMIT_FOR_SMALL_CLUSTER) { + config.setMaxDatanodesPercentageToInvolvePerIteration(100); + } + config.setIterations(1); + config.setThreshold(10); + config.setMaxSizeToMovePerIteration(50 * STORAGE_UNIT); + config.setMaxSizeEnteringTarget(50 * STORAGE_UNIT); + + ContainerBalancerTask task = mockedSCM.startBalancerTask(config); + + Map map = task.getContainerToTargetMap(); + Map> cidToReplicasMap = mockedSCM.getCluster().getCidToReplicasMap(); + for (Map.Entry entry : map.entrySet()) { + ContainerID container = entry.getKey(); + DatanodeDetails target = entry.getValue(); + assertTrue(cidToReplicasMap.get(container) + .stream() + .map(ContainerReplica::getDatanodeDetails) + .noneMatch(target::equals)); + } + } + + @ParameterizedTest(name = "MockedSCM #{index}: {0}") + @MethodSource("createMockedSCMs") + public void containerMoveSelectionShouldFollowPlacementPolicy(@Nonnull MockedSCM mockedSCM) { + ContainerBalancerConfiguration config = balancerConfigByOzoneConfig(new OzoneConfiguration()); + int nodeCount = mockedSCM.getCluster().getNodeCount(); + if (nodeCount < DATANODE_COUNT_LIMIT_FOR_SMALL_CLUSTER) { + config.setMaxDatanodesPercentageToInvolvePerIteration(100); + } + config.setIterations(1); + config.setThreshold(10); + config.setMaxSizeToMovePerIteration(50 * STORAGE_UNIT); + config.setMaxSizeEnteringTarget(50 * STORAGE_UNIT); + + ContainerBalancerTask task = mockedSCM.startBalancerTask(config); + + Map containerFromSourceMap = task.getContainerToSourceMap(); + Map containerToTargetMap = task.getContainerToTargetMap(); + + // For each move selection, check if {replicas - source + target} satisfies placement policy + for (Map.Entry entry : containerFromSourceMap.entrySet()) { + ContainerID container = entry.getKey(); + DatanodeDetails source = entry.getValue(); + + List replicas = mockedSCM.getCluster().getCidToReplicasMap().get(container) + .stream() + .map(ContainerReplica::getDatanodeDetails) + .collect(Collectors.toList()); + // Remove source and add target + replicas.remove(source); + replicas.add(containerToTargetMap.get(container)); + + ContainerInfo containerInfo = mockedSCM.getCluster().getCidToInfoMap().get(container); + ContainerPlacementStatus placementStatus; + int requiredNodes = containerInfo.getReplicationConfig().getRequiredNodes(); + if (containerInfo.getReplicationType() == HddsProtos.ReplicationType.RATIS) { + placementStatus = mockedSCM.getPlacementPolicy().validateContainerPlacement(replicas, requiredNodes); + } else { + placementStatus = mockedSCM.getEcPlacementPolicy().validateContainerPlacement(replicas, requiredNodes); + } + assertTrue(placementStatus.isPolicySatisfied()); + } + } + + @ParameterizedTest(name = "MockedSCM #{index}: {0}") + @MethodSource("createMockedSCMs") + public void targetDatanodeShouldBeInServiceHealthy(@Nonnull MockedSCM mockedSCM) throws NodeNotFoundException { + ContainerBalancerConfiguration config = balancerConfigByOzoneConfig(new OzoneConfiguration()); + int nodeCount = mockedSCM.getCluster().getNodeCount(); + if (nodeCount < DATANODE_COUNT_LIMIT_FOR_SMALL_CLUSTER) { + config.setMaxDatanodesPercentageToInvolvePerIteration(100); + } + config.setIterations(1); + config.setThreshold(10); + config.setMaxSizeToMovePerIteration(50 * STORAGE_UNIT); + config.setMaxSizeEnteringTarget(50 * STORAGE_UNIT); + + ContainerBalancerTask task = mockedSCM.startBalancerTask(config); + + for (DatanodeDetails target : task.getSelectedTargets()) { + NodeStatus status = mockedSCM.getNodeManager().getNodeStatus(target); + assertSame(HddsProtos.NodeOperationalState.IN_SERVICE, status.getOperationalState()); + assertTrue(status.isHealthy()); + } + } + + + @ParameterizedTest(name = "MockedSCM #{index}: {0}") + @MethodSource("createMockedSCMs") + public void selectedContainerShouldNotAlreadyHaveBeenSelected(@Nonnull MockedSCM mockedSCM) + throws NodeNotFoundException, ContainerNotFoundException, TimeoutException, ContainerReplicaNotFoundException { + ContainerBalancerConfiguration config = balancerConfigByOzoneConfig(new OzoneConfiguration()); + int nodeCount = mockedSCM.getCluster().getNodeCount(); + if (nodeCount < DATANODE_COUNT_LIMIT_FOR_SMALL_CLUSTER) { + config.setMaxDatanodesPercentageToInvolvePerIteration(100); + } + config.setIterations(1); + config.setThreshold(10); + config.setMaxSizeToMovePerIteration(50 * STORAGE_UNIT); + config.setMaxSizeEnteringTarget(50 * STORAGE_UNIT); + + mockedSCM.enableLegacyReplicationManager(); + + ContainerBalancerTask task = mockedSCM.startBalancerTask(config); + int numContainers = task.getContainerToTargetMap().size(); + + /* Assuming move is called exactly once for each unique container, number of calls to move should equal number of + unique containers. If number of calls to move is more than number of unique containers, at least one container + has been re-selected. It's expected that number of calls to move should equal number of unique, selected containers + (from containerToTargetMap). + */ + verify(mockedSCM.getReplicationManager(), times(numContainers)) + .move(any(ContainerID.class), any(DatanodeDetails.class), any(DatanodeDetails.class)); + + // Try the same test by disabling LegacyReplicationManager so that MoveManager is used. + mockedSCM.disableLegacyReplicationManager(); + ContainerBalancerTask nextTask = mockedSCM.startBalancerTask(config); + + numContainers = nextTask.getContainerToTargetMap().size(); + verify(mockedSCM.getMoveManager(), times(numContainers)) + .move(any(ContainerID.class), any(DatanodeDetails.class), any(DatanodeDetails.class)); + } + + @ParameterizedTest(name = "MockedSCM #{index}: {0}") + @MethodSource("createMockedSCMs") + public void balancerShouldNotSelectConfiguredExcludeContainers(@Nonnull MockedSCM mockedSCM) { + ContainerBalancerConfiguration config = balancerConfigByOzoneConfig(new OzoneConfiguration()); + int nodeCount = mockedSCM.getCluster().getNodeCount(); + if (nodeCount < DATANODE_COUNT_LIMIT_FOR_SMALL_CLUSTER) { + config.setMaxDatanodesPercentageToInvolvePerIteration(100); + } + config.setIterations(1); + config.setThreshold(10); + config.setMaxSizeToMovePerIteration(50 * STORAGE_UNIT); + config.setMaxSizeEnteringTarget(50 * STORAGE_UNIT); + config.setExcludeContainers("1, 4, 5"); + + ContainerBalancerTask task = mockedSCM.startBalancerTask(config); + + Set excludeContainers = config.getExcludeContainers(); + for (ContainerID container : task.getContainerToSourceMap().keySet()) { + assertThat(excludeContainers).doesNotContain(container); + } + } public static List getUnBalancedNodes(@Nonnull ContainerBalancerTask task) { ArrayList result = new ArrayList<>(); diff --git a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancerTask.java b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancerTask.java index 0f4551b45c2..d0e9cd53fec 100644 --- a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancerTask.java +++ b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancerTask.java @@ -27,7 +27,6 @@ import org.apache.hadoop.hdds.protocol.MockDatanodeDetails; import org.apache.hadoop.hdds.protocol.proto.HddsProtos; import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.ContainerReplicaProto; -import org.apache.hadoop.hdds.scm.ContainerPlacementStatus; import org.apache.hadoop.hdds.scm.PlacementPolicy; import org.apache.hadoop.hdds.scm.PlacementPolicyValidateProxy; import org.apache.hadoop.hdds.scm.container.ContainerID; @@ -47,7 +46,6 @@ import org.apache.hadoop.hdds.scm.ha.StatefulServiceStateManagerImpl; import org.apache.hadoop.hdds.scm.net.NetworkTopology; import org.apache.hadoop.hdds.scm.node.DatanodeUsageInfo; -import org.apache.hadoop.hdds.scm.node.NodeStatus; import org.apache.hadoop.hdds.scm.node.states.NodeNotFoundException; import org.apache.hadoop.hdds.scm.server.StorageContainerManager; import org.apache.hadoop.hdds.server.events.EventPublisher; @@ -74,11 +72,9 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotSame; -import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.apache.hadoop.hdds.scm.container.replication.ReplicationManager.ReplicationManagerConfiguration; @@ -86,10 +82,8 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.anyString; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; /** @@ -248,259 +242,6 @@ public void setup(TestInfo testInfo) throws IOException, NodeNotFoundException, sb.getMetrics(), balancerConfiguration, false); } - @Test - public void containerBalancerShouldSelectOnlyClosedContainers() - throws IllegalContainerBalancerStateException, IOException, - InvalidContainerBalancerConfigurationException, TimeoutException { - // make all containers open, balancer should not select any of them - for (ContainerInfo containerInfo : cidToInfoMap.values()) { - containerInfo.setState(HddsProtos.LifeCycleState.OPEN); - } - balancerConfiguration.setThreshold(10); - startBalancer(balancerConfiguration); - stopBalancer(); - - // balancer should have identified unbalanced nodes - assertFalse(TestContainerBalancerDatanodeNodeLimit.getUnBalancedNodes(containerBalancerTask).isEmpty()); - // no container should have been selected - assertTrue(containerBalancerTask.getContainerToSourceMap() - .isEmpty()); - /* - Iteration result should be CAN_NOT_BALANCE_ANY_MORE because no container - move is generated - */ - assertEquals( - ContainerBalancerTask.IterationResult.CAN_NOT_BALANCE_ANY_MORE, - containerBalancerTask.getIterationResult()); - - // now, close all containers - for (ContainerInfo containerInfo : cidToInfoMap.values()) { - containerInfo.setState(HddsProtos.LifeCycleState.CLOSED); - } - startBalancer(balancerConfiguration); - stopBalancer(); - - // check whether all selected containers are closed - for (ContainerID cid: - containerBalancerTask.getContainerToSourceMap().keySet()) { - assertSame( - cidToInfoMap.get(cid).getState(), HddsProtos.LifeCycleState.CLOSED); - } - } - - /** - * Container Balancer should not select a non-CLOSED replica for moving. - */ - @Test - public void balancerShouldNotSelectNonClosedContainerReplicas() - throws IOException, IllegalContainerBalancerStateException, - InvalidContainerBalancerConfigurationException, TimeoutException { - - // let's mock such that all replicas have CLOSING state - when(containerManager.getContainerReplicas(any(ContainerID.class))) - .thenAnswer(invocationOnMock -> { - ContainerID cid = (ContainerID) invocationOnMock.getArguments()[0]; - Set replicas = cidToReplicasMap.get(cid); - Set replicasToReturn = - new HashSet<>(replicas.size()); - for (ContainerReplica replica : replicas) { - ContainerReplica newReplica = - replica.toBuilder().setContainerState( - ContainerReplicaProto.State.CLOSING).build(); - replicasToReturn.add(newReplica); - } - - return replicasToReturn; - }); - - balancerConfiguration.setThreshold(10); - balancerConfiguration.setMaxDatanodesPercentageToInvolvePerIteration(100); - balancerConfiguration.setMaxSizeToMovePerIteration(50 * STORAGE_UNIT); - balancerConfiguration.setMaxSizeEnteringTarget(50 * STORAGE_UNIT); - - startBalancer(balancerConfiguration); - stopBalancer(); - - // balancer should have identified unbalanced nodes - assertFalse(TestContainerBalancerDatanodeNodeLimit.getUnBalancedNodes(containerBalancerTask).isEmpty()); - // no container should have moved because all replicas are CLOSING - assertTrue( - containerBalancerTask.getContainerToSourceMap().isEmpty()); - } - - @Test - public void containerBalancerShouldObeyMaxSizeToMoveLimit() - throws IllegalContainerBalancerStateException, IOException, - InvalidContainerBalancerConfigurationException, TimeoutException { - balancerConfiguration.setThreshold(1); - balancerConfiguration.setMaxSizeToMovePerIteration(10 * STORAGE_UNIT); - balancerConfiguration.setIterations(1); - startBalancer(balancerConfiguration); - - // balancer should not have moved more size than the limit - assertThat(containerBalancerTask.getSizeScheduledForMoveInLatestIteration()) - .isLessThanOrEqualTo(10 * STORAGE_UNIT); - - long size = containerBalancerTask.getMetrics() - .getDataSizeMovedGBInLatestIteration(); - assertThat(size).isGreaterThan(0); - assertThat(size).isLessThanOrEqualTo(10); - stopBalancer(); - } - - @Test - public void targetDatanodeShouldNotAlreadyContainSelectedContainer() - throws IllegalContainerBalancerStateException, IOException, - InvalidContainerBalancerConfigurationException, TimeoutException { - balancerConfiguration.setThreshold(10); - balancerConfiguration.setMaxSizeToMovePerIteration(100 * STORAGE_UNIT); - balancerConfiguration.setMaxDatanodesPercentageToInvolvePerIteration(100); - startBalancer(balancerConfiguration); - - stopBalancer(); - Map map = - containerBalancerTask.getContainerToTargetMap(); - for (Map.Entry entry : map.entrySet()) { - ContainerID container = entry.getKey(); - DatanodeDetails target = entry.getValue(); - assertTrue(cidToReplicasMap.get(container) - .stream() - .map(ContainerReplica::getDatanodeDetails) - .noneMatch(target::equals)); - } - } - - @Test - public void containerMoveSelectionShouldFollowPlacementPolicy() - throws IllegalContainerBalancerStateException, IOException, - InvalidContainerBalancerConfigurationException, TimeoutException { - balancerConfiguration.setThreshold(10); - balancerConfiguration.setMaxSizeToMovePerIteration(50 * STORAGE_UNIT); - balancerConfiguration.setMaxDatanodesPercentageToInvolvePerIteration(100); - balancerConfiguration.setIterations(1); - startBalancer(balancerConfiguration); - - stopBalancer(); - Map containerFromSourceMap = - containerBalancerTask.getContainerToSourceMap(); - Map containerToTargetMap = - containerBalancerTask.getContainerToTargetMap(); - - // for each move selection, check if {replicas - source + target} - // satisfies placement policy - for (Map.Entry entry : - containerFromSourceMap.entrySet()) { - ContainerID container = entry.getKey(); - DatanodeDetails source = entry.getValue(); - - List replicas = cidToReplicasMap.get(container) - .stream() - .map(ContainerReplica::getDatanodeDetails) - .collect(Collectors.toList()); - // remove source and add target - replicas.remove(source); - replicas.add(containerToTargetMap.get(container)); - - ContainerInfo containerInfo = cidToInfoMap.get(container); - ContainerPlacementStatus placementStatus; - if (containerInfo.getReplicationType() == - HddsProtos.ReplicationType.RATIS) { - placementStatus = placementPolicy.validateContainerPlacement(replicas, - containerInfo.getReplicationConfig().getRequiredNodes()); - } else { - placementStatus = - ecPlacementPolicy.validateContainerPlacement(replicas, - containerInfo.getReplicationConfig().getRequiredNodes()); - } - assertTrue(placementStatus.isPolicySatisfied()); - } - } - - @Test - public void targetDatanodeShouldBeInServiceHealthy() - throws NodeNotFoundException, IllegalContainerBalancerStateException, - IOException, InvalidContainerBalancerConfigurationException, - TimeoutException { - balancerConfiguration.setThreshold(10); - balancerConfiguration.setMaxDatanodesPercentageToInvolvePerIteration(100); - balancerConfiguration.setMaxSizeToMovePerIteration(50 * STORAGE_UNIT); - balancerConfiguration.setMaxSizeEnteringTarget(50 * STORAGE_UNIT); - balancerConfiguration.setIterations(1); - startBalancer(balancerConfiguration); - - stopBalancer(); - for (DatanodeDetails target : containerBalancerTask.getSelectedTargets()) { - NodeStatus status = mockNodeManager.getNodeStatus(target); - assertSame(HddsProtos.NodeOperationalState.IN_SERVICE, - status.getOperationalState()); - assertTrue(status.isHealthy()); - } - } - - @Test - public void selectedContainerShouldNotAlreadyHaveBeenSelected() - throws IllegalContainerBalancerStateException, IOException, - InvalidContainerBalancerConfigurationException, NodeNotFoundException, - TimeoutException { - balancerConfiguration.setThreshold(10); - balancerConfiguration.setMaxDatanodesPercentageToInvolvePerIteration(100); - balancerConfiguration.setMaxSizeToMovePerIteration(50 * STORAGE_UNIT); - balancerConfiguration.setMaxSizeEnteringTarget(50 * STORAGE_UNIT); - balancerConfiguration.setIterations(1); - rmConf.setEnableLegacy(true); - - startBalancer(balancerConfiguration); - - stopBalancer(); - - int numContainers = containerBalancerTask.getContainerToTargetMap().size(); - - /* - Assuming move is called exactly once for each unique container, number of - calls to move should equal number of unique containers. If number of - calls to move is more than number of unique containers, at least one - container has been re-selected. It's expected that number of calls to - move should equal number of unique, selected containers (from - containerToTargetMap). - */ - verify(replicationManager, times(numContainers)) - .move(any(ContainerID.class), any(DatanodeDetails.class), - any(DatanodeDetails.class)); - - /* - Try the same test by disabling LegacyReplicationManager so that - MoveManager is used. - */ - rmConf.setEnableLegacy(false); - startBalancer(balancerConfiguration); - stopBalancer(); - numContainers = containerBalancerTask.getContainerToTargetMap().size(); - verify(moveManager, times(numContainers)) - .move(any(ContainerID.class), any(DatanodeDetails.class), - any(DatanodeDetails.class)); - } - - @Test - public void balancerShouldNotSelectConfiguredExcludeContainers() - throws IllegalContainerBalancerStateException, IOException, - InvalidContainerBalancerConfigurationException, TimeoutException { - balancerConfiguration.setThreshold(10); - balancerConfiguration.setMaxDatanodesPercentageToInvolvePerIteration(100); - balancerConfiguration.setMaxSizeToMovePerIteration(50 * STORAGE_UNIT); - balancerConfiguration.setMaxSizeEnteringTarget(50 * STORAGE_UNIT); - balancerConfiguration.setExcludeContainers("1, 4, 5"); - - startBalancer(balancerConfiguration); - - stopBalancer(); - Set excludeContainers = - balancerConfiguration.getExcludeContainers(); - for (ContainerID container : - containerBalancerTask.getContainerToSourceMap().keySet()) { - assertThat(excludeContainers).doesNotContain(container); - } - } - /** * Tests if {@link ContainerBalancer} follows the includeNodes and * excludeNodes configurations in {@link ContainerBalancerConfiguration}.