From e13ffb80bf72b3dae3e968292cb654a997f3cbf5 Mon Sep 17 00:00:00 2001 From: Aryan Gupta Date: Wed, 29 Apr 2026 18:06:14 +0530 Subject: [PATCH] HDDS-15138. SCM safemode pipeline rules should honor default EC replication config. --- .../pipeline/BackgroundPipelineCreator.java | 82 ++++++++++++------- .../safemode/HealthyPipelineSafeModeRule.java | 54 ++++++------ .../OneReplicaPipelineSafeModeRule.java | 23 +++--- .../TestBackgroundPipelineCreator.java | 75 +++++++++++++++++ .../TestHealthyPipelineSafeModeRule.java | 50 +++++++++++ .../TestOneReplicaPipelineSafeModeRule.java | 37 +++++++++ 6 files changed, 255 insertions(+), 66 deletions(-) create mode 100644 hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/pipeline/TestBackgroundPipelineCreator.java diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/pipeline/BackgroundPipelineCreator.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/pipeline/BackgroundPipelineCreator.java index 0aefbedbd43..9fc928db75a 100644 --- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/pipeline/BackgroundPipelineCreator.java +++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/pipeline/BackgroundPipelineCreator.java @@ -25,6 +25,7 @@ import static org.apache.hadoop.hdds.scm.ha.SCMService.Event.PRE_CHECK_COMPLETED; import static org.apache.hadoop.hdds.scm.ha.SCMService.Event.UNHEALTHY_TO_HEALTHY_NODE_HANDLER_TRIGGERED; +import com.google.common.annotations.VisibleForTesting; import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.io.IOException; import java.time.Clock; @@ -199,47 +200,25 @@ private boolean skipCreation(ReplicationConfig replicationConfig, // used. return ((StandaloneReplicationConfig) replicationConfig) .getReplicationFactor() != ReplicationFactor.ONE; + } else if (replicationConfig.getReplicationType().equals(EC)) { + return false; } return true; } private void createPipelines() throws RuntimeException { - // TODO: #CLUTIL Different replication factor may need to be supported - HddsProtos.ReplicationType type = HddsProtos.ReplicationType.valueOf( - conf.get(OzoneConfigKeys.OZONE_REPLICATION_TYPE, - OzoneConfigKeys.OZONE_REPLICATION_TYPE_DEFAULT)); - boolean autoCreateFactorOne = conf.getBoolean( - ScmConfigKeys.OZONE_SCM_PIPELINE_AUTO_CREATE_FACTOR_ONE, + boolean autoCreateFactorOne = conf.getBoolean(ScmConfigKeys.OZONE_SCM_PIPELINE_AUTO_CREATE_FACTOR_ONE, ScmConfigKeys.OZONE_SCM_PIPELINE_AUTO_CREATE_FACTOR_ONE_DEFAULT); - List list = - new ArrayList<>(); - for (HddsProtos.ReplicationFactor factor : HddsProtos.ReplicationFactor - .values()) { - if (factor == ReplicationFactor.ZERO) { - continue; // Ignore it. - } - final ReplicationConfig replicationConfig; - if (type != EC) { - replicationConfig = - ReplicationConfig.fromProtoTypeAndFactor(type, factor); - } else if (factor == ReplicationFactor.ONE) { - replicationConfig = - ReplicationConfig.fromProtoTypeAndFactor(RATIS, factor); - } else { - continue; - } - if (skipCreation(replicationConfig, autoCreateFactorOne)) { - // Skip this iteration for creating pipeline - continue; - } - list.add(replicationConfig); + List list = getReplicationConfigs(autoCreateFactorOne); + if (list.isEmpty()) { + LOG.debug("No replication configs selected for background pipeline creation."); + return; } LoopingIterator it = new LoopingIterator(list); while (it.hasNext()) { - ReplicationConfig replicationConfig = - (ReplicationConfig) it.next(); + ReplicationConfig replicationConfig = (ReplicationConfig) it.next(); try { Pipeline pipeline = pipelineManager.createPipeline(replicationConfig); @@ -255,6 +234,49 @@ private void createPipelines() throws RuntimeException { LOG.debug("BackgroundPipelineCreator createPipelines finished."); } + @VisibleForTesting + List getReplicationConfigs(boolean autoCreateFactorOne) { + List list = new ArrayList<>(); + // TODO: #CLUTIL Different replication factor may need to be supported + HddsProtos.ReplicationType type = HddsProtos.ReplicationType.valueOf( + conf.get(OzoneConfigKeys.OZONE_REPLICATION_TYPE, OzoneConfigKeys.OZONE_REPLICATION_TYPE_DEFAULT)); + if (type == EC) { + try { + ReplicationConfig defaultConfig = ReplicationConfig.getDefault(conf); + if (defaultConfig.getReplicationType() == EC) { + list.add(defaultConfig); + } + } catch (IllegalArgumentException e) { + LOG.warn( + "Skipping EC pipeline creation due to invalid default EC " + "replication config. type={}, replication={}", + conf.get(OzoneConfigKeys.OZONE_REPLICATION_TYPE, OzoneConfigKeys.OZONE_REPLICATION_TYPE_DEFAULT), + conf.get(OzoneConfigKeys.OZONE_REPLICATION, OzoneConfigKeys.OZONE_REPLICATION_DEFAULT), e); + } + } + + for (HddsProtos.ReplicationFactor factor : HddsProtos.ReplicationFactor.values()) { + if (factor == ReplicationFactor.ZERO) { + continue; // Ignore it. + } + final ReplicationConfig replicationConfig; + if (type != EC) { + replicationConfig = ReplicationConfig.fromProtoTypeAndFactor(type, factor); + } else if (factor == ReplicationFactor.ONE) { + replicationConfig = ReplicationConfig.fromProtoTypeAndFactor(RATIS, factor); + } else { + continue; + } + if (skipCreation(replicationConfig, autoCreateFactorOne)) { + // Skip this iteration for creating pipeline + continue; + } + if (!list.contains(replicationConfig)) { + list.add(replicationConfig); + } + } + return list; + } + @Override public void notifyStatusChanged() { serviceLock.lock(); diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/safemode/HealthyPipelineSafeModeRule.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/safemode/HealthyPipelineSafeModeRule.java index 3e590013c11..be29ddd0aba 100644 --- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/safemode/HealthyPipelineSafeModeRule.java +++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/safemode/HealthyPipelineSafeModeRule.java @@ -27,10 +27,9 @@ import java.util.Set; import java.util.stream.Collectors; import org.apache.hadoop.hdds.HddsConfigKeys; -import org.apache.hadoop.hdds.client.RatisReplicationConfig; +import org.apache.hadoop.hdds.client.ReplicationConfig; import org.apache.hadoop.hdds.conf.ConfigurationSource; import org.apache.hadoop.hdds.protocol.DatanodeDetails; -import org.apache.hadoop.hdds.protocol.proto.HddsProtos; import org.apache.hadoop.hdds.scm.events.SCMEvents; import org.apache.hadoop.hdds.scm.ha.SCMContext; import org.apache.hadoop.hdds.scm.node.NodeManager; @@ -66,6 +65,9 @@ public class HealthyPipelineSafeModeRule extends SafeModeExitRule { private final SCMContext scmContext; private final Set unProcessedPipelineSet = new HashSet<>(); private final NodeManager nodeManager; + private final ReplicationConfig targetReplicationConfig; + private final int targetRequiredNodes; + private final String targetReplicationLabel; HealthyPipelineSafeModeRule(EventQueue eventQueue, PipelineManager pipelineManager, SCMSafeModeManager manager, @@ -74,13 +76,15 @@ public class HealthyPipelineSafeModeRule extends SafeModeExitRule { this.pipelineManager = pipelineManager; this.scmContext = scmContext; this.nodeManager = nodeManager; + this.targetReplicationConfig = ReplicationConfig.getDefault(configuration); + this.targetRequiredNodes = targetReplicationConfig.getRequiredNodes(); + this.targetReplicationLabel = targetReplicationConfig.configFormat(); healthyPipelinesPercent = configuration.getDouble(HddsConfigKeys. HDDS_SCM_SAFEMODE_HEALTHY_PIPELINE_THRESHOLD_PCT, HddsConfigKeys. HDDS_SCM_SAFEMODE_HEALTHY_PIPELINE_THRESHOLD_PCT_DEFAULT); - // We only care about THREE replica pipeline minHealthyPipelines = getMinHealthyPipelines(configuration); Preconditions.checkArgument( @@ -97,8 +101,7 @@ private int getMinHealthyPipelines(ConfigurationSource config) { HddsConfigKeys.HDDS_SCM_SAFEMODE_MIN_DATANODE, HddsConfigKeys.HDDS_SCM_SAFEMODE_MIN_DATANODE_DEFAULT); - // We only care about THREE replica pipeline - return minDatanodes / HddsProtos.ReplicationFactor.THREE_VALUE; + return minDatanodes / targetRequiredNodes; } @@ -141,14 +144,12 @@ protected synchronized void process(Pipeline pipeline) { // datanode can send pipeline report again, or SCMPipelineManager will // create new pipelines. - // Only handle RATIS + 3-replica pipelines. - if (pipeline.getType() != HddsProtos.ReplicationType.RATIS || - ((RatisReplicationConfig) pipeline.getReplicationConfig()).getReplicationFactor() != - HddsProtos.ReplicationFactor.THREE) { + // Only handle pipelines matching the configured default replication. + if (!targetReplicationConfig.equals(pipeline.getReplicationConfig())) { Logger safeModeManagerLog = SCMSafeModeManager.getLogger(); if (safeModeManagerLog.isDebugEnabled()) { - safeModeManagerLog.debug("Skipping pipeline safemode report processing as Replication type isn't RATIS " + - "or replication factor isn't 3."); + safeModeManagerLog.debug("Skipping pipeline safemode report processing as replication config {} " + + "does not match target {}.", pipeline.getReplicationConfig(), targetReplicationConfig); } return; } @@ -161,9 +162,9 @@ protected synchronized void process(Pipeline pipeline) { } List pipelineDns = pipeline.getNodes(); - if (pipelineDns.size() != 3) { - LOG.warn("Only {} DNs reported this pipeline: {}, all 3 DNs should report the pipeline", pipelineDns.size(), - pipeline.getId()); + if (pipelineDns.size() != targetRequiredNodes) { + LOG.warn("Only {} DNs reported this pipeline: {}, all {} DNs should report the pipeline", + pipelineDns.size(), pipeline.getId(), targetRequiredNodes); return; } @@ -218,8 +219,7 @@ public synchronized void refresh(boolean forceRefresh) { private synchronized void initializeRule(boolean refresh) { unProcessedPipelineSet.addAll(pipelineManager.getPipelines( - RatisReplicationConfig.getInstance( - HddsProtos.ReplicationFactor.THREE), + targetReplicationConfig, Pipeline.PipelineState.OPEN).stream().map(Pipeline::getId) .collect(Collectors.toSet())); @@ -245,10 +245,11 @@ private synchronized void initializeRule(boolean refresh) { private boolean validateHealthyPipelineSafeModeRuleUsingPipelineManager() { // Query PipelineManager directly for healthy pipeline count List openPipelines = pipelineManager.getPipelines( - RatisReplicationConfig.getInstance(HddsProtos.ReplicationFactor.THREE), + targetReplicationConfig, Pipeline.PipelineState.OPEN); - - LOG.debug("Found {} open RATIS/THREE pipelines", openPipelines.size()); + + LOG.debug("Found {} open {} pipelines", openPipelines.size(), + targetReplicationLabel); int pipelineCount = openPipelines.size(); healthyPipelineThresholdCount = Math.max(minHealthyPipelines, @@ -271,11 +272,11 @@ private boolean validateHealthyPipelineSafeModeRuleUsingPipelineManager() { } boolean isPipelineHealthy(Pipeline pipeline) { - // Verify pipeline has all 3 nodes + // Verify pipeline has all required nodes for target replication. List nodes = pipeline.getNodes(); - if (nodes.size() != 3) { - LOG.debug("Pipeline {} is not healthy: has {} nodes instead of 3", - pipeline.getId(), nodes.size()); + if (nodes.size() != targetRequiredNodes) { + LOG.debug("Pipeline {} is not healthy: has {} nodes instead of {}", + pipeline.getId(), nodes.size(), targetRequiredNodes); return false; } @@ -316,8 +317,9 @@ public synchronized int getHealthyPipelineThresholdCount() { @Override public String getStatusText() { String status = String.format( - "healthy Ratis/THREE pipelines (=%d) >= healthyPipelineThresholdCount" + - " (=%d)", getCurrentHealthyPipelineCount(), + "healthy %s pipelines (=%d) >= healthyPipelineThresholdCount" + + " (=%d)", targetReplicationLabel, + getCurrentHealthyPipelineCount(), getHealthyPipelineThresholdCount()); status = updateStatusTextWithSamplePipelines(status); return status; @@ -327,7 +329,7 @@ private synchronized String updateStatusTextWithSamplePipelines( String status) { if (validateBasedOnReportProcessing()) { List openPipelines = pipelineManager.getPipelines( - RatisReplicationConfig.getInstance(HddsProtos.ReplicationFactor.THREE), + targetReplicationConfig, Pipeline.PipelineState.OPEN); Set unhealthyPipelines = openPipelines.stream() diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/safemode/OneReplicaPipelineSafeModeRule.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/safemode/OneReplicaPipelineSafeModeRule.java index 8b1fc593af3..e02a0b5e997 100644 --- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/safemode/OneReplicaPipelineSafeModeRule.java +++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/safemode/OneReplicaPipelineSafeModeRule.java @@ -26,9 +26,8 @@ import java.util.Set; import java.util.stream.Collectors; import org.apache.hadoop.hdds.HddsConfigKeys; -import org.apache.hadoop.hdds.client.RatisReplicationConfig; +import org.apache.hadoop.hdds.client.ReplicationConfig; import org.apache.hadoop.hdds.conf.ConfigurationSource; -import org.apache.hadoop.hdds.protocol.proto.HddsProtos.ReplicationFactor; import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.PipelineReport; import org.apache.hadoop.hdds.scm.events.SCMEvents; import org.apache.hadoop.hdds.scm.pipeline.Pipeline; @@ -58,6 +57,8 @@ public class OneReplicaPipelineSafeModeRule extends private int currentReportedPipelineCount = 0; private PipelineManager pipelineManager; private final double pipelinePercent; + private final ReplicationConfig targetReplicationConfig; + private final String targetReplicationLabel; public OneReplicaPipelineSafeModeRule(EventQueue eventQueue, PipelineManager pipelineManager, SCMSafeModeManager safeModeManager, ConfigurationSource configuration) { @@ -76,6 +77,8 @@ public OneReplicaPipelineSafeModeRule(EventQueue eventQueue, PipelineManager pip " value should be >= 0.0 and <= 1.0"); this.pipelineManager = pipelineManager; + this.targetReplicationConfig = ReplicationConfig.getDefault(configuration); + this.targetReplicationLabel = targetReplicationConfig.configFormat(); initializeRule(false); } @@ -108,8 +111,7 @@ protected synchronized void process(PipelineReportFromDatanode report) { continue; } - if (RatisReplicationConfig - .hasFactor(pipeline.getReplicationConfig(), ReplicationFactor.THREE) + if (targetReplicationConfig.equals(pipeline.getReplicationConfig()) && pipeline.isOpen() && !reportedPipelineIDSet.contains(pipeline.getId())) { if (oldPipelineIDSet.contains(pipeline.getId())) { @@ -152,8 +154,9 @@ Set getReportedPipelineIDSet() { @Override public String getStatusText() { String status = String.format( - "reported Ratis/THREE pipelines with at least one datanode (=%d) " - + ">= threshold (=%d)", getCurrentReportedPipelineCount(), + "reported %s pipelines with at least one datanode (=%d) " + + ">= threshold (=%d)", targetReplicationLabel, + getCurrentReportedPipelineCount(), getThresholdCount()); status = updateStatusTextWithSamplePipelines(status); return status; @@ -184,11 +187,11 @@ public synchronized void refresh(boolean forceRefresh) { } private void updateReportedPipelineSet() { - List openRatisPipelines = - pipelineManager.getPipelines(RatisReplicationConfig.getInstance(ReplicationFactor.THREE), + List openTargetPipelines = + pipelineManager.getPipelines(targetReplicationConfig, Pipeline.PipelineState.OPEN); - for (Pipeline pipeline : openRatisPipelines) { + for (Pipeline pipeline : openTargetPipelines) { PipelineID pipelineID = pipeline.getId(); if (!pipeline.getNodeSet().isEmpty() && oldPipelineIDSet.contains(pipelineID) @@ -202,7 +205,7 @@ private void updateReportedPipelineSet() { private void initializeRule(boolean refresh) { oldPipelineIDSet = pipelineManager.getPipelines( - RatisReplicationConfig.getInstance(ReplicationFactor.THREE), + targetReplicationConfig, Pipeline.PipelineState.OPEN) .stream().map(p -> p.getId()).collect(Collectors.toSet()); diff --git a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/pipeline/TestBackgroundPipelineCreator.java b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/pipeline/TestBackgroundPipelineCreator.java new file mode 100644 index 00000000000..b38cf0c91e0 --- /dev/null +++ b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/pipeline/TestBackgroundPipelineCreator.java @@ -0,0 +1,75 @@ +/* + * 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.hdds.scm.pipeline; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; + +import java.time.Clock; +import java.util.List; +import org.apache.hadoop.hdds.client.ECReplicationConfig; +import org.apache.hadoop.hdds.client.RatisReplicationConfig; +import org.apache.hadoop.hdds.client.ReplicationConfig; +import org.apache.hadoop.hdds.conf.OzoneConfiguration; +import org.apache.hadoop.hdds.protocol.proto.HddsProtos; +import org.apache.hadoop.hdds.scm.ha.SCMContext; +import org.apache.hadoop.ozone.OzoneConfigKeys; +import org.junit.jupiter.api.Test; + +/** + * Tests for BackgroundPipelineCreator replication config selection. + */ +public class TestBackgroundPipelineCreator { + + @Test + public void testEcDefaultReplicationAddsEcPipelineConfig() throws Exception { + OzoneConfiguration conf = new OzoneConfiguration(); + conf.set(OzoneConfigKeys.OZONE_REPLICATION_TYPE, + HddsProtos.ReplicationType.EC.name()); + conf.set(OzoneConfigKeys.OZONE_REPLICATION, "rs-3-2-1024k"); + + BackgroundPipelineCreator creator = new BackgroundPipelineCreator( + mock(PipelineManager.class), conf, mock(SCMContext.class), + Clock.systemUTC()); + + List configs = creator.getReplicationConfigs(false); + + assertTrue(configs.stream().anyMatch(c -> + c.getReplicationType() == HddsProtos.ReplicationType.EC)); + assertTrue(configs.contains(new ECReplicationConfig("rs-3-2-1024k"))); + } + + @Test + public void testRatisDefaultReplicationBehaviorUnchanged() throws Exception { + OzoneConfiguration conf = new OzoneConfiguration(); + conf.set(OzoneConfigKeys.OZONE_REPLICATION_TYPE, + HddsProtos.ReplicationType.RATIS.name()); + + BackgroundPipelineCreator creator = new BackgroundPipelineCreator( + mock(PipelineManager.class), conf, mock(SCMContext.class), + Clock.systemUTC()); + + List configs = creator.getReplicationConfigs(false); + + assertEquals(1, configs.size()); + assertTrue(RatisReplicationConfig.hasFactor(configs.get(0), + HddsProtos.ReplicationFactor.THREE)); + } + +} diff --git a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/safemode/TestHealthyPipelineSafeModeRule.java b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/safemode/TestHealthyPipelineSafeModeRule.java index b1312512d56..4c1d87a3951 100644 --- a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/safemode/TestHealthyPipelineSafeModeRule.java +++ b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/safemode/TestHealthyPipelineSafeModeRule.java @@ -27,9 +27,11 @@ import java.time.Clock; import java.time.ZoneOffset; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.apache.hadoop.hdds.HddsConfigKeys; import org.apache.hadoop.hdds.client.RatisReplicationConfig; +import org.apache.hadoop.hdds.client.ReplicationConfig; import org.apache.hadoop.hdds.conf.OzoneConfiguration; import org.apache.hadoop.hdds.protocol.DatanodeDetails; import org.apache.hadoop.hdds.protocol.proto.HddsProtos; @@ -44,11 +46,15 @@ import org.apache.hadoop.hdds.scm.ha.SCMServiceManager; import org.apache.hadoop.hdds.scm.metadata.SCMMetadataStore; import org.apache.hadoop.hdds.scm.metadata.SCMMetadataStoreImpl; +import org.apache.hadoop.hdds.scm.node.NodeManager; +import org.apache.hadoop.hdds.scm.node.NodeStatus; import org.apache.hadoop.hdds.scm.pipeline.MockRatisPipelineProvider; import org.apache.hadoop.hdds.scm.pipeline.Pipeline; +import org.apache.hadoop.hdds.scm.pipeline.PipelineID; import org.apache.hadoop.hdds.scm.pipeline.PipelineManagerImpl; import org.apache.hadoop.hdds.scm.pipeline.PipelineProvider; import org.apache.hadoop.hdds.server.events.EventQueue; +import org.apache.hadoop.ozone.OzoneConfigKeys; import org.apache.ozone.test.GenericTestUtils; import org.apache.ozone.test.GenericTestUtils.LogCapturer; import org.junit.jupiter.api.Test; @@ -480,6 +486,50 @@ public void testPipelineIgnoredWhenDnIsUnhealthy() throws Exception { } } + @Test + public void testValidateWithEcDefaultReplicationConfig() throws Exception { + EventQueue eventQueue = new EventQueue(); + PipelineManagerImpl mockedPipelineManager = mock(PipelineManagerImpl.class); + SCMSafeModeManager mockedSafeModeManager = mock(SCMSafeModeManager.class); + SafeModeMetrics mockedMetrics = mock(SafeModeMetrics.class); + NodeManager mockedNodeManager = mock(NodeManager.class); + when(mockedSafeModeManager.getSafeModeMetrics()).thenReturn(mockedMetrics); + when(mockedSafeModeManager.getInSafeMode()).thenReturn(true); + + OzoneConfiguration conf = new OzoneConfiguration(); + conf.set(OzoneConfigKeys.OZONE_REPLICATION_TYPE, + HddsProtos.ReplicationType.EC.name()); + conf.set(OzoneConfigKeys.OZONE_REPLICATION, "rs-3-2-1024k"); + conf.setInt(HddsConfigKeys.HDDS_SCM_SAFEMODE_MIN_DATANODE, 0); + ReplicationConfig targetReplicationConfig = ReplicationConfig.getDefault(conf); + + DatanodeDetails dn1 = mock(DatanodeDetails.class); + DatanodeDetails dn2 = mock(DatanodeDetails.class); + DatanodeDetails dn3 = mock(DatanodeDetails.class); + DatanodeDetails dn4 = mock(DatanodeDetails.class); + DatanodeDetails dn5 = mock(DatanodeDetails.class); + List dnList = Arrays.asList(dn1, dn2, dn3, dn4, dn5); + for (DatanodeDetails dn : dnList) { + when(mockedNodeManager.getNodeStatus(dn)) + .thenReturn(NodeStatus.inServiceHealthy()); + } + + Pipeline ecPipeline = mock(Pipeline.class); + when(ecPipeline.getId()).thenReturn(PipelineID.randomId()); + when(ecPipeline.getNodes()).thenReturn(dnList); + when(mockedPipelineManager.getPipelines(targetReplicationConfig, + Pipeline.PipelineState.OPEN)).thenReturn(Arrays.asList(ecPipeline)); + + HealthyPipelineSafeModeRule localRule = + new HealthyPipelineSafeModeRule(eventQueue, mockedPipelineManager, + mockedSafeModeManager, conf, SCMContext.emptyContext(), + mockedNodeManager); + localRule.setValidateBasedOnReportProcessing(false); + + assertTrue(localRule.validate()); + assertTrue(localRule.getStatusText().contains("EC/3-2-1024k")); + } + private void firePipelineEvent(Pipeline pipeline, EventQueue eventQueue) { eventQueue.fireEvent(SCMEvents.OPEN_PIPELINE, pipeline); } diff --git a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/safemode/TestOneReplicaPipelineSafeModeRule.java b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/safemode/TestOneReplicaPipelineSafeModeRule.java index e83b6e51a93..0a2948e808d 100644 --- a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/safemode/TestOneReplicaPipelineSafeModeRule.java +++ b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/safemode/TestOneReplicaPipelineSafeModeRule.java @@ -57,6 +57,7 @@ import org.apache.hadoop.hdds.scm.pipeline.PipelineProvider; import org.apache.hadoop.hdds.scm.server.SCMDatanodeHeartbeatDispatcher; import org.apache.hadoop.hdds.server.events.EventQueue; +import org.apache.hadoop.ozone.OzoneConfigKeys; import org.apache.ozone.test.GenericTestUtils; import org.apache.ozone.test.GenericTestUtils.LogCapturer; import org.apache.ozone.test.TestClock; @@ -235,6 +236,42 @@ public void testOneReplicaPipelineRuleWithReportProcessingFalse() { assertTrue(localRule.getReportedPipelineIDSet().contains(pipelineID)); } + @Test + public void testOneReplicaPipelineRuleForEcDefaultReplication() { + EventQueue localEventQueue = new EventQueue(); + PipelineManager mockedPipelineManager = mock(PipelineManager.class); + SCMSafeModeManager mockedSafeModeManager = mock(SCMSafeModeManager.class); + SafeModeMetrics mockedMetrics = mock(SafeModeMetrics.class); + when(mockedSafeModeManager.getSafeModeMetrics()).thenReturn(mockedMetrics); + when(mockedSafeModeManager.getInSafeMode()).thenReturn(true); + + OzoneConfiguration conf = new OzoneConfiguration(); + conf.set(OzoneConfigKeys.OZONE_REPLICATION_TYPE, + HddsProtos.ReplicationType.EC.name()); + conf.set(OzoneConfigKeys.OZONE_REPLICATION, "rs-3-2-1024k"); + ReplicationConfig targetReplicationConfig = ReplicationConfig.getDefault(conf); + + PipelineID pipelineID = PipelineID.randomId(); + Pipeline mockedPipeline = mock(Pipeline.class); + when(mockedPipeline.getId()).thenReturn(pipelineID); + when(mockedPipeline.getNodeSet()) + .thenReturn(new java.util.HashSet<>( + java.util.Collections.singletonList(mock(DatanodeDetails.class)))); + + when(mockedPipelineManager.getPipelines(targetReplicationConfig, + Pipeline.PipelineState.OPEN)) + .thenReturn(java.util.Collections.singletonList(mockedPipeline)); + + OneReplicaPipelineSafeModeRule localRule = + new OneReplicaPipelineSafeModeRule(localEventQueue, mockedPipelineManager, + mockedSafeModeManager, conf); + localRule.setValidateBasedOnReportProcessing(false); + + assertTrue(localRule.validate()); + assertTrue(localRule.getReportedPipelineIDSet().contains(pipelineID)); + assertTrue(localRule.getStatusText().contains("EC/3-2-1024k")); + } + private void createPipelines(int count, HddsProtos.ReplicationFactor factor) throws Exception { for (int i = 0; i < count; i++) {