This is an automated email from the ASF dual-hosted git repository.
sarvekshayr pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/master by this push:
new e31f36d2316 HDDS-15667. `containerbalancer status`: show stop reason
and iteration details after stop (#10681)
e31f36d2316 is described below
commit e31f36d2316a5be67e2e86a91bc6deecbacf0a0f
Author: sreejasahithi <[email protected]>
AuthorDate: Wed Jul 15 09:38:26 2026 +0530
HDDS-15667. `containerbalancer status`: show stop reason and iteration
details after stop (#10681)
---
.../src/main/proto/ScmAdminProtocol.proto | 3 +
.../interface-admin/src/main/resources/proto.lock | 18 ++
.../scm/container/balancer/ContainerBalancer.java | 19 +-
.../balancer/ContainerBalancerStatusInfo.java | 43 +++-
.../balancer/ContainerBalancerStopReason.java | 63 ++++++
.../container/balancer/ContainerBalancerTask.java | 101 ++++++++-
.../hdds/scm/server/SCMClientProtocolServer.java | 13 +-
.../container/balancer/TestContainerBalancer.java | 43 ++++
.../balancer/TestContainerBalancerStatusInfo.java | 66 ++++++
.../scm/cli/ContainerBalancerStatusSubcommand.java | 133 +++++++----
.../datanode/TestContainerBalancerSubCommand.java | 242 ++++++++++++++++++++-
.../src/main/smoketest/balancer/testBalancer.robot | 2 +-
12 files changed, 684 insertions(+), 62 deletions(-)
diff --git a/hadoop-hdds/interface-admin/src/main/proto/ScmAdminProtocol.proto
b/hadoop-hdds/interface-admin/src/main/proto/ScmAdminProtocol.proto
index d33e949c01a..6c39fc22cc4 100644
--- a/hadoop-hdds/interface-admin/src/main/proto/ScmAdminProtocol.proto
+++ b/hadoop-hdds/interface-admin/src/main/proto/ScmAdminProtocol.proto
@@ -658,6 +658,9 @@ message ContainerBalancerStatusInfoProto {
optional uint64 startedAt = 1;
optional ContainerBalancerConfigurationProto configuration = 2;
repeated ContainerBalancerTaskIterationStatusInfoProto iterationsStatusInfo
= 3;
+ optional uint64 stoppedAt = 4;
+ optional string stopReason = 5;
+ optional string stopMessage = 6;
}
message ContainerBalancerTaskIterationStatusInfoProto {
diff --git a/hadoop-hdds/interface-admin/src/main/resources/proto.lock
b/hadoop-hdds/interface-admin/src/main/resources/proto.lock
index e4d79fc60e2..02184011b69 100644
--- a/hadoop-hdds/interface-admin/src/main/resources/proto.lock
+++ b/hadoop-hdds/interface-admin/src/main/resources/proto.lock
@@ -2250,6 +2250,24 @@
"name": "iterationsStatusInfo",
"type": "ContainerBalancerTaskIterationStatusInfoProto",
"is_repeated": true
+ },
+ {
+ "id": 4,
+ "name": "stoppedAt",
+ "type": "uint64",
+ "optional": true
+ },
+ {
+ "id": 5,
+ "name": "stopReason",
+ "type": "string",
+ "optional": true
+ },
+ {
+ "id": 6,
+ "name": "stopMessage",
+ "type": "string",
+ "optional": true
}
]
},
diff --git
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancer.java
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancer.java
index c516447f09f..3631df49f5a 100644
---
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancer.java
+++
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancer.java
@@ -191,13 +191,28 @@ public ContainerBalancerTask.Status getBalancerStatus() {
public ContainerBalancerStatusInfo getBalancerStatusInfo() throws
IOException {
lock.lock();
try {
- if (isBalancerRunning()) {
+ if (task == null) {
+ return null;
+ }
+ ContainerBalancerTask.Status status = task.getBalancerStatus();
+ if (status == ContainerBalancerTask.Status.RUNNING
+ || status == ContainerBalancerTask.Status.STOPPING) {
return new ContainerBalancerStatusInfo(
this.startedAt,
config.toProtobufBuilder().setShouldRun(true).build(),
task.getCurrentIterationsStatistic()
);
}
+ if (status == ContainerBalancerTask.Status.STOPPED) {
+ return new ContainerBalancerStatusInfo(
+ this.startedAt,
+ config.toProtobufBuilder().setShouldRun(false).build(),
+ task.getCurrentIterationsStatistic(),
+ task.getStoppedAt(),
+ task.getStopReason(),
+ task.getStopMessage()
+ );
+ }
return null;
} finally {
lock.unlock();
@@ -362,6 +377,7 @@ public void stop() {
return;
}
LOG.info("Trying to stop ContainerBalancer in this SCM.");
+ task.recordStopReason(ContainerBalancerStopReason.SCM_STATE_CHANGE);
task.stop();
balancingThread = currentBalancingThread;
} finally {
@@ -402,6 +418,7 @@ public void stopBalancer()
saveConfiguration(config, false, 0);
if (isBalancerRunning()) {
LOG.info("Trying to stop ContainerBalancer service.");
+ task.recordStopReason(ContainerBalancerStopReason.USER_REQUESTED);
task.stop();
balancingThread = currentBalancingThread;
}
diff --git
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancerStatusInfo.java
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancerStatusInfo.java
index 0ac0a26682a..be1ea624560 100644
---
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancerStatusInfo.java
+++
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancerStatusInfo.java
@@ -30,14 +30,30 @@ public class ContainerBalancerStatusInfo {
private final OffsetDateTime startedAt;
private final HddsProtos.ContainerBalancerConfigurationProto configuration;
private final List<ContainerBalancerTaskIterationStatusInfo>
iterationsStatusInfo;
+ private final OffsetDateTime stoppedAt;
+ private final String stopReason;
+ private final String stopMessage;
public ContainerBalancerStatusInfo(
OffsetDateTime startedAt,
HddsProtos.ContainerBalancerConfigurationProto configuration,
List<ContainerBalancerTaskIterationStatusInfo> iterationsStatusInfo)
{
+ this(startedAt, configuration, iterationsStatusInfo, null, null, null);
+ }
+
+ public ContainerBalancerStatusInfo(
+ OffsetDateTime startedAt,
+ HddsProtos.ContainerBalancerConfigurationProto configuration,
+ List<ContainerBalancerTaskIterationStatusInfo> iterationsStatusInfo,
+ OffsetDateTime stoppedAt,
+ String stopReason,
+ String stopMessage) {
this.startedAt = startedAt;
this.configuration = configuration;
this.iterationsStatusInfo = iterationsStatusInfo;
+ this.stoppedAt = stoppedAt;
+ this.stopReason = stopReason;
+ this.stopMessage = stopMessage;
}
public OffsetDateTime getStartedAt() {
@@ -52,12 +68,25 @@ public List<ContainerBalancerTaskIterationStatusInfo>
getIterationsStatusInfo()
return iterationsStatusInfo;
}
+ public OffsetDateTime getStoppedAt() {
+ return stoppedAt;
+ }
+
+ public String getStopReason() {
+ return stopReason;
+ }
+
+ public String getStopMessage() {
+ return stopMessage;
+ }
+
/**
* Converts an instance into a protobuf-compatible object.
* @return proto representation
*/
public
StorageContainerLocationProtocolProtos.ContainerBalancerStatusInfoProto
toProto() {
- return
StorageContainerLocationProtocolProtos.ContainerBalancerStatusInfoProto
+
StorageContainerLocationProtocolProtos.ContainerBalancerStatusInfoProto.Builder
builder =
+ StorageContainerLocationProtocolProtos.ContainerBalancerStatusInfoProto
.newBuilder()
.setStartedAt(getStartedAt().toEpochSecond())
.setConfiguration(getConfiguration())
@@ -66,6 +95,16 @@ public
StorageContainerLocationProtocolProtos.ContainerBalancerStatusInfoProto t
.stream()
.map(ContainerBalancerTaskIterationStatusInfo::toProto)
.collect(Collectors.toList())
- ).build();
+ );
+ if (stoppedAt != null) {
+ builder.setStoppedAt(stoppedAt.toEpochSecond());
+ }
+ if (stopReason != null) {
+ builder.setStopReason(stopReason);
+ }
+ if (stopMessage != null) {
+ builder.setStopMessage(stopMessage);
+ }
+ return builder.build();
}
}
diff --git
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancerStopReason.java
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancerStopReason.java
new file mode 100644
index 00000000000..46b9908db8c
--- /dev/null
+++
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancerStopReason.java
@@ -0,0 +1,63 @@
+/*
+ * 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.container.balancer;
+
+/**
+ * Stop reason codes and messages for ContainerBalancer.
+ */
+public enum ContainerBalancerStopReason {
+ USER_REQUESTED("Stopped by user request."),
+ SCM_STATE_CHANGE("Stopped because SCM state changed."),
+ COMPLETED_ALL_ITERATIONS("Completed all configured number of iterations."),
+ CAN_NOT_BALANCE_ANY_MORE("No more eligible container moves were found."),
+ INITIALIZATION_FAILED("Failed to initialize a container balancer
iteration."),
+ ERROR("Stopped because of an unexpected error."),
+ UNKNOWN("Stopped for an unknown reason.");
+
+ public static final String INIT_SCM_NOT_READY = "SCM is in safe mode or is
not leader ready.";
+ public static final String INIT_EMPTY_DATANODE_LIST = "Received an empty
list of Datanodes from Node Manager.";
+ public static final String INIT_NO_UNBALANCED_DATANODES = "Did not find any
unbalanced Datanodes.";
+
+ private final String message;
+
+ ContainerBalancerStopReason(String message) {
+ this.message = message;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public String formatMessage(String details) {
+ if (details == null || details.isEmpty()) {
+ return message;
+ }
+ return message + " Details: " + details;
+ }
+
+ public static String exceptionDetails(Throwable throwable) {
+ if (throwable == null) {
+ return "";
+ }
+ String exceptionMessage = throwable.getMessage();
+ if (exceptionMessage != null && !exceptionMessage.isEmpty()) {
+ return throwable.getClass().getName() + ": " + exceptionMessage;
+ }
+ return throwable.toString();
+ }
+}
diff --git
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancerTask.java
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancerTask.java
index e0281575f17..0a7c2c62156 100644
---
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancerTask.java
+++
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancerTask.java
@@ -120,6 +120,10 @@ public class ContainerBalancerTask implements Runnable {
private Queue<ContainerBalancerTaskIterationStatusInfo> iterationsStatistic;
private OffsetDateTime currentIterationStarted;
private AtomicBoolean isCurrentIterationInProgress = new
AtomicBoolean(false);
+ private volatile String stopReason;
+ private volatile String stopMessage;
+ private volatile OffsetDateTime stoppedAt;
+ private volatile String lastInitializationFailureDetail;
/**
* Constructs ContainerBalancerTask with the specified arguments.
@@ -190,8 +194,17 @@ public void run() {
balance();
} catch (Exception e) {
LOG.error("Container Balancer is stopped abnormally, ", e);
+ recordStopReason(ContainerBalancerStopReason.ERROR,
+ ContainerBalancerStopReason.exceptionDetails(e));
} finally {
synchronized (this) {
+ finalizeInProgressIteration();
+ if (stoppedAt == null) {
+ stoppedAt = now();
+ }
+ if (stopReason == null) {
+ recordStopReason(ContainerBalancerStopReason.UNKNOWN);
+ }
taskStatus = Status.STOPPED;
}
}
@@ -264,8 +277,9 @@ private void balance() {
return;
}
// otherwise, try to stop balancer
- tryStopWithSaveConfiguration("Could not initialize " +
- "ContainerBalancer's iteration number " + i);
+ isCurrentIterationInProgress.compareAndSet(true, false);
+
tryStopWithSaveConfiguration(ContainerBalancerStopReason.INITIALIZATION_FAILED,
+ " iteration number " + (i + 1) + ", " +
lastInitializationFailureDetail);
return;
}
@@ -286,7 +300,7 @@ private void balance() {
// if no new move option is generated, it means the cluster cannot be
// balanced anymore; so just stop balancer
if (currentIterationResult == IterationResult.CAN_NOT_BALANCE_ANY_MORE) {
- tryStopWithSaveConfiguration(currentIterationResult.toString());
+
tryStopWithSaveConfiguration(ContainerBalancerStopReason.CAN_NOT_BALANCE_ANY_MORE);
return;
}
@@ -319,7 +333,7 @@ private void balance() {
}
}
- tryStopWithSaveConfiguration("Completed all iterations.");
+
tryStopWithSaveConfiguration(ContainerBalancerStopReason.COMPLETED_ALL_ITERATIONS);
}
private ContainerBalancerTaskIterationStatusInfo
getIterationStatistic(Integer iterationNumber,
@@ -417,21 +431,88 @@ private long getCurrentIterationDuration() {
/**
* Logs the reason for stop and save configuration and stop the task.
*
- * @param stopReason a string specifying the reason for stop
+ * @param reason stop reason
+ */
+ private void tryStopWithSaveConfiguration(ContainerBalancerStopReason
reason) {
+ tryStopWithSaveConfiguration(reason, null);
+ }
+
+ /**
+ * Logs the reason for stop and save configuration and stop the task.
+ *
+ * @param reason stable stop reason code
+ * @param details optional details appended to the human-readable message
*/
- private void tryStopWithSaveConfiguration(String stopReason) {
+ private void tryStopWithSaveConfiguration(ContainerBalancerStopReason
reason, String details) {
synchronized (this) {
try {
- LOG.info("Save Configuration for stopping. Reason: {}", stopReason);
saveConfiguration(config, false, 0);
+ recordStopReason(reason, details);
+ LOG.info("Save Configuration for stopping. Reason: {}, Message: {}",
+ reason.name(), stopMessage);
stop();
} catch (IOException | TimeoutException e) {
+ recordStopReason(reason, details);
LOG.warn("Save configuration failed. Reason for " +
- "stopping: {}", stopReason, e);
+ "stopping: {}, Message: {}", reason.name(), stopMessage, e);
}
}
}
+ /**
+ * Records the reason why the balancer task is stopping.
+ *
+ * @param reason stop reason
+ */
+ public void recordStopReason(ContainerBalancerStopReason reason) {
+ recordStopReason(reason, null);
+ }
+
+ /**
+ * Records the reason why the balancer task is stopping.
+ *
+ * @param reason stop reason
+ * @param details optional details appended to the message
+ */
+ public void recordStopReason(ContainerBalancerStopReason reason, String
details) {
+ synchronized (this) {
+ if (stopReason == null) {
+ stopReason = reason.name();
+ stopMessage = reason.formatMessage(details);
+ }
+ }
+ }
+
+ private void finalizeInProgressIteration() {
+ if (!isCurrentIterationInProgress.get()) {
+ return;
+ }
+ List<ContainerBalancerTaskIterationStatusInfo> resultList = new
ArrayList<>(iterationsStatistic);
+ int lastIterationNumber = resultList.stream()
+ .mapToInt(ContainerBalancerTaskIterationStatusInfo::getIterationNumber)
+ .max()
+ .orElse(0);
+ long iterationDuration = getCurrentIterationDuration();
+ iterationsStatistic.offer(
+ getIterationStatistic(
+ lastIterationNumber + 1,
+ IterationResult.ITERATION_INTERRUPTED,
+ iterationDuration));
+ isCurrentIterationInProgress.set(false);
+ }
+
+ public String getStopReason() {
+ return stopReason;
+ }
+
+ public String getStopMessage() {
+ return stopMessage;
+ }
+
+ public OffsetDateTime getStoppedAt() {
+ return stoppedAt;
+ }
+
private void saveConfiguration(ContainerBalancerConfiguration configuration,
boolean shouldRun, int index)
throws IOException, TimeoutException {
@@ -454,7 +535,9 @@ private void
saveConfiguration(ContainerBalancerConfiguration configuration,
* @return true if successfully initialized, otherwise false.
*/
private boolean initializeIteration() {
+ lastInitializationFailureDetail = null;
if (!isValidSCMState()) {
+ lastInitializationFailureDetail =
ContainerBalancerStopReason.INIT_SCM_NOT_READY;
return false;
}
// sorted list in order from most to least used
@@ -463,6 +546,7 @@ private boolean initializeIteration() {
if (datanodeUsageInfos.isEmpty()) {
LOG.warn("Received an empty list of datanodes from Node Manager when " +
"trying to identify which nodes to balance");
+ lastInitializationFailureDetail =
ContainerBalancerStopReason.INIT_EMPTY_DATANODE_LIST;
return false;
}
@@ -540,6 +624,7 @@ private boolean initializeIteration() {
if (overUtilizedNodes.isEmpty() && underUtilizedNodes.isEmpty()) {
LOG.info("Did not find any unbalanced Datanodes.");
+ lastInitializationFailureDetail =
ContainerBalancerStopReason.INIT_NO_UNBALANCED_DATANODES;
return false;
}
diff --git
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMClientProtocolServer.java
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMClientProtocolServer.java
index 27f9d124401..7324ae32b6d 100644
---
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMClientProtocolServer.java
+++
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMClientProtocolServer.java
@@ -1379,14 +1379,13 @@ public ContainerBalancerStatusInfoResponseProto
getContainerBalancerStatusInfo()
.newBuilder()
.setIsRunning(false)
.build();
- } else {
-
- return ContainerBalancerStatusInfoResponseProto
- .newBuilder()
- .setIsRunning(true)
- .setContainerBalancerStatusInfo(balancerStatusInfo.toProto())
- .build();
}
+
+ return ContainerBalancerStatusInfoResponseProto
+ .newBuilder()
+ .setIsRunning(balancerStatusInfo.getConfiguration().getShouldRun())
+ .setContainerBalancerStatusInfo(balancerStatusInfo.toProto())
+ .build();
}
/**
diff --git
a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancer.java
b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancer.java
index 78a6491a70e..4864f235d5b 100644
---
a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancer.java
+++
b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancer.java
@@ -23,6 +23,7 @@
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
@@ -328,6 +329,48 @@ public void testStartBalancerWithInvalidNodes() throws
Exception {
assertSame(ContainerBalancerTask.Status.STOPPED,
containerBalancer.getBalancerStatus());
}
+ @Test
+ public void testGetBalancerStatusInfoAfterUserStop() throws Exception {
+ balancerConfiguration.setIterations(10);
+ balancerConfiguration.setTriggerDuEnable(true);
+ conf.setFromObject(balancerConfiguration);
+
+ startBalancer(balancerConfiguration);
+ assertSame(ContainerBalancerTask.Status.RUNNING,
containerBalancer.getBalancerStatus());
+
+ stopBalancer();
+ assertSame(ContainerBalancerTask.Status.STOPPED,
containerBalancer.getBalancerStatus());
+
+ ContainerBalancerStatusInfo statusInfo =
containerBalancer.getBalancerStatusInfo();
+ assertNotNull(statusInfo);
+ assertEquals(ContainerBalancerStopReason.USER_REQUESTED.name(),
+ statusInfo.getStopReason());
+ assertEquals(ContainerBalancerStopReason.USER_REQUESTED.getMessage(),
+ statusInfo.getStopMessage());
+ assertNotNull(statusInfo.getStoppedAt());
+ assertFalse(statusInfo.getConfiguration().getShouldRun());
+ assertFalse(containerBalancer.isBalancerRunning());
+ }
+
+ @Test
+ public void testGetBalancerStatusInfoAfterScmStop() throws Exception {
+ balancerConfiguration.setIterations(10);
+ balancerConfiguration.setTriggerDuEnable(true);
+ conf.setFromObject(balancerConfiguration);
+
+ startBalancer(balancerConfiguration);
+ containerBalancer.stop();
+ assertSame(ContainerBalancerTask.Status.STOPPED,
containerBalancer.getBalancerStatus());
+
+ ContainerBalancerStatusInfo statusInfo =
containerBalancer.getBalancerStatusInfo();
+ assertNotNull(statusInfo);
+ assertEquals(ContainerBalancerStopReason.SCM_STATE_CHANGE.name(),
+ statusInfo.getStopReason());
+ assertEquals(ContainerBalancerStopReason.SCM_STATE_CHANGE.getMessage(),
+ statusInfo.getStopMessage());
+ assertNotNull(statusInfo.getStoppedAt());
+ }
+
private void startBalancer(ContainerBalancerConfiguration config)
throws IllegalContainerBalancerStateException, IOException,
InvalidContainerBalancerConfigurationException, TimeoutException {
diff --git
a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancerStatusInfo.java
b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancerStatusInfo.java
index 6d082fe7cf5..f0c4e32c829 100644
---
a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancerStatusInfo.java
+++
b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancerStatusInfo.java
@@ -22,6 +22,9 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import java.util.List;
import java.util.Map;
@@ -29,6 +32,7 @@
import org.apache.hadoop.hdds.HddsConfigKeys;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.protocol.DatanodeID;
+import org.apache.hadoop.hdds.scm.node.NodeManager;
import org.apache.hadoop.hdds.scm.server.StorageContainerManager;
import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.ozone.test.LambdaTestUtils;
@@ -258,4 +262,66 @@ void
testGetCurrentIterationsStatisticDoesNotThrowNullPointerExceptionWhenBalanc
thread.start();
Assertions.assertDoesNotThrow(task::getCurrentIterationsStatistic);
}
+
+ @Test
+ void testFinalizeInProgressIterationOnStop() throws Exception {
+ MockedSCM mockedScm = new MockedSCM(new MockCluster(20, OzoneConsts.GB));
+
+ ContainerBalancerConfiguration config =
+ new
OzoneConfiguration().getObject(ContainerBalancerConfiguration.class);
+ config.setIterations(3);
+ config.setBalancingInterval(0);
+ config.setMaxSizeToMovePerIteration(50 * OzoneConsts.GB);
+ config.setTriggerDuEnable(false);
+
+ ContainerBalancerTask task = mockedScm.startBalancerTaskAsync(config,
false);
+ LambdaTestUtils.await(5000, 10,
+ () -> !task.getCurrentIterationsStatistic().isEmpty()
+ && task.getCurrentIterationsStatistic().stream()
+ .anyMatch(it -> it.getContainerMovesScheduled() > 0));
+
+ task.stop();
+ LambdaTestUtils.await(5000, 10,
+ () -> task.getBalancerStatus() ==
ContainerBalancerTask.Status.STOPPED);
+ assertNotNull(task.getStoppedAt());
+ assertEquals(ContainerBalancerStopReason.UNKNOWN.name(),
task.getStopReason());
+ assertEquals(ContainerBalancerStopReason.UNKNOWN.getMessage(),
task.getStopMessage());
+
+ boolean hasInterruptedIteration =
task.getCurrentIterationsStatistic().stream()
+ .anyMatch(it ->
"ITERATION_INTERRUPTED".equals(it.getIterationResult()));
+ assertTrue(hasInterruptedIteration);
+ }
+
+ @Test
+ void testAbnormalStopRecordsErrorReasonAndFinalizesIteration() throws
Exception {
+ MockedSCM mockedScm = new MockedSCM(new MockCluster(20, OzoneConsts.GB));
+
+ ContainerBalancerConfiguration config =
+ new
OzoneConfiguration().getObject(ContainerBalancerConfiguration.class);
+ config.setIterations(3);
+ config.setBalancingInterval(0);
+ config.setMaxSizeToMovePerIteration(50 * OzoneConsts.GB);
+ config.setTriggerDuEnable(false);
+
+ mockedScm.init(config, new OzoneConfiguration());
+ NodeManager throwingNodeManager = mock(NodeManager.class);
+ when(throwingNodeManager.getMostOrLeastUsedDatanodes(anyBoolean()))
+ .thenThrow(new RuntimeException());
+ when(mockedScm.getStorageContainerManager().getScmNodeManager())
+ .thenReturn(throwingNodeManager);
+
+ ContainerBalancerTask task = mockedScm.startBalancerTaskAsync(
+ new ContainerBalancer(mockedScm.getStorageContainerManager()), config,
false);
+ LambdaTestUtils.await(5000, 10,
+ () -> task.getBalancerStatus() ==
ContainerBalancerTask.Status.STOPPED);
+ assertEquals(ContainerBalancerStopReason.ERROR.name(),
task.getStopReason());
+ assertEquals(ContainerBalancerStopReason.ERROR.formatMessage(
+ ContainerBalancerStopReason.exceptionDetails(new
RuntimeException())),
+ task.getStopMessage());
+ assertNotNull(task.getStoppedAt());
+
+ boolean hasInterruptedIteration =
task.getCurrentIterationsStatistic().stream()
+ .anyMatch(it ->
"ITERATION_INTERRUPTED".equals(it.getIterationResult()));
+ assertTrue(hasInterruptedIteration);
+ }
}
diff --git
a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/ContainerBalancerStatusSubcommand.java
b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/ContainerBalancerStatusSubcommand.java
index a6180d687c1..a55fc7906db 100644
---
a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/ContainerBalancerStatusSubcommand.java
+++
b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/ContainerBalancerStatusSubcommand.java
@@ -56,54 +56,107 @@ public class ContainerBalancerStatusSubcommand extends
ScmSubcommand {
@Override
public void execute(ScmClient scmClient) throws IOException {
+ if (verboseWithHistory && !isVerbose()) {
+ System.err.println("Warning: -H/--history has no effect without
-v/--verbose.");
+ }
ContainerBalancerStatusInfoResponseProto response =
scmClient.getContainerBalancerStatusInfo();
boolean isRunning = response.getIsRunning();
ContainerBalancerStatusInfoProto balancerStatusInfo =
response.getContainerBalancerStatusInfo();
if (isRunning) {
- Instant startedAtInstant =
Instant.ofEpochSecond(balancerStatusInfo.getStartedAt());
- LocalDateTime dateTime =
- LocalDateTime.ofInstant(startedAtInstant, ZoneId.systemDefault());
System.out.println("ContainerBalancer is Running.");
+ } else if (response.hasContainerBalancerStatusInfo()) {
+ System.out.println("ContainerBalancer is Not Running.");
+ printStopReasonAndMessage(balancerStatusInfo);
+ } else {
+ System.out.println("ContainerBalancer is Not Running.");
+ }
- if (isVerbose()) {
- System.out.printf("Started at: %s %s%n",
- dateTime.toLocalDate().format(DateTimeFormatter.ISO_LOCAL_DATE),
- dateTime.toLocalTime().format(DateTimeFormatter.ISO_LOCAL_TIME));
- Duration balancingDuration = Duration.between(startedAtInstant,
OffsetDateTime.now());
- System.out.printf("Balancing duration: %s%n%n",
getPrettyDuration(balancingDuration));
-
System.out.println(getConfigurationPrettyString(balancerStatusInfo.getConfiguration()));
- List<ContainerBalancerTaskIterationStatusInfoProto>
iterationsStatusInfoList
- = balancerStatusInfo.getIterationsStatusInfoList();
-
- System.out.println("Current iteration info:");
- ContainerBalancerTaskIterationStatusInfoProto
currentIterationStatistic = iterationsStatusInfoList.stream()
- .filter(it -> it.getIterationResult().isEmpty())
- .findFirst()
- .orElse(null);
- if (currentIterationStatistic == null) {
- System.out.println("-");
- System.out.println();
- } else {
- System.out.println(
- getPrettyIterationStatusInfo(currentIterationStatistic)
- );
- }
-
-
- if (verboseWithHistory) {
- System.out.println("Iteration history list:");
- System.out.println(
- iterationsStatusInfoList
- .stream()
- .filter(it -> !it.getIterationResult().isEmpty())
- .map(this::getPrettyIterationStatusInfo)
- .collect(Collectors.joining(System.lineSeparator()))
- );
- }
- }
+ if (isVerbose() && response.hasContainerBalancerStatusInfo()) {
+ printVerboseStatusInfo(balancerStatusInfo, isRunning);
+ }
+ }
+
+ private void printVerboseStatusInfo(ContainerBalancerStatusInfoProto
balancerStatusInfo, boolean isRunning) {
+ Instant startedAtInstant =
Instant.ofEpochSecond(balancerStatusInfo.getStartedAt());
+ LocalDateTime startedAtDateTime =
+ LocalDateTime.ofInstant(startedAtInstant, ZoneId.systemDefault());
+ System.out.printf("Started at: %s %s%n",
+
startedAtDateTime.toLocalDate().format(DateTimeFormatter.ISO_LOCAL_DATE),
+
startedAtDateTime.toLocalTime().format(DateTimeFormatter.ISO_LOCAL_TIME));
+
+ Instant endInstant = balancerStatusInfo.hasStoppedAt()
+ ? Instant.ofEpochSecond(balancerStatusInfo.getStoppedAt())
+ : OffsetDateTime.now().toInstant();
+ if (balancerStatusInfo.hasStoppedAt()) {
+ LocalDateTime stoppedAtDateTime =
+ LocalDateTime.ofInstant(endInstant, ZoneId.systemDefault());
+ System.out.printf("Stopped at: %s %s%n",
+
stoppedAtDateTime.toLocalDate().format(DateTimeFormatter.ISO_LOCAL_DATE),
+
stoppedAtDateTime.toLocalTime().format(DateTimeFormatter.ISO_LOCAL_TIME));
+ }
+ Duration balancingDuration = Duration.between(startedAtInstant,
endInstant);
+ System.out.printf("Balancing duration: %s%n%n",
getPrettyDuration(balancingDuration));
+
System.out.println(getConfigurationPrettyString(balancerStatusInfo.getConfiguration()));
+ List<ContainerBalancerTaskIterationStatusInfoProto>
iterationsStatusInfoList =
+ balancerStatusInfo.getIterationsStatusInfoList();
+ ContainerBalancerTaskIterationStatusInfoProto lastIterationStatistic =
null;
+ if (isRunning) {
+ System.out.println("Current iteration info:");
+ ContainerBalancerTaskIterationStatusInfoProto currentIterationStatistic
= iterationsStatusInfoList.stream()
+ .filter(it -> it.getIterationResult().isEmpty())
+ .findFirst()
+ .orElse(null);
+ if (currentIterationStatistic == null) {
+ System.out.println("-");
+ System.out.println();
+ } else {
+ System.out.println(
+ getPrettyIterationStatusInfo(currentIterationStatistic)
+ );
+ }
} else {
- System.out.println("ContainerBalancer is Not Running.");
+ System.out.println("Last iteration info:");
+ lastIterationStatistic = iterationsStatusInfoList.stream()
+ .filter(it -> !it.getIterationResult().isEmpty())
+ .reduce((first, second) -> second)
+ .orElse(null);
+ if (lastIterationStatistic == null) {
+ System.out.println("-");
+ System.out.println();
+ } else {
+ System.out.println(
+ getPrettyIterationStatusInfo(lastIterationStatistic)
+ );
+ }
+ }
+
+ if (verboseWithHistory) {
+ System.out.println("Completed iteration history:");
+ final int lastCompletedIterationNumber = lastIterationStatistic == null
+ ? -1
+ : lastIterationStatistic.getIterationNumber();
+ String history = iterationsStatusInfoList
+ .stream()
+ .filter(it -> !it.getIterationResult().isEmpty())
+ .filter(it -> isRunning || it.getIterationNumber() !=
lastCompletedIterationNumber)
+ .map(this::getPrettyIterationStatusInfo)
+ .collect(Collectors.joining(System.lineSeparator()));
+ if (history.isEmpty()) {
+ System.out.println("-");
+ } else {
+ System.out.println(history);
+ }
+ System.out.println();
+ }
+ }
+
+ private void printStopReasonAndMessage(ContainerBalancerStatusInfoProto
balancerStatusInfo) {
+ if (balancerStatusInfo.hasStopReason()) {
+ System.out.printf("Stop reason: %s%n",
balancerStatusInfo.getStopReason());
+ }
+ if (balancerStatusInfo.hasStopMessage()) {
+ System.out.printf("Message: %s%n", balancerStatusInfo.getStopMessage());
}
}
diff --git
a/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestContainerBalancerSubCommand.java
b/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestContainerBalancerSubCommand.java
index 86e9129dcfa..613ca37e586 100644
---
a/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestContainerBalancerSubCommand.java
+++
b/hadoop-ozone/cli-admin/src/test/java/org/apache/hadoop/hdds/scm/cli/datanode/TestContainerBalancerSubCommand.java
@@ -85,6 +85,78 @@ class TestContainerBalancerSubCommand {
"Datanodes Specified to be Balanced None\n" +
"Datanodes Excluded from Balancing None";
+ private static final String ITERATION_1_COMPLETED_OUTPUT =
+ "Key Value\n" +
+ "Iteration number 1\n" +
+ "Iteration duration 6m 40s\n" +
+ "Iteration result
ITERATION_COMPLETED\n" +
+ "Size scheduled to move 54 GB\n" +
+ "Moved data size 54 GB\n" +
+ "Scheduled to move containers 11\n" +
+ "Already moved containers 11\n" +
+ "Failed to move containers 0\n" +
+ "Failed to move containers by timeout 0\n" +
+ "Entered data to nodes \n" +
+ "80f6bc27-e6f3-493e-b1f4-25f810ad960d <- 28 GB\n" +
+ "701ca98e-aa1a-4b36-b817-e28ed634bba6 <- 26 GB\n" +
+ "Exited data from nodes \n" +
+ "b8b9c511-c30f-4933-8938-2f272e307070 -> 25 GB\n" +
+ "7bd99815-47e7-4015-bc61-ca6ef6dfd130 -> 29 GB";
+
+ private static final String ITERATION_2_COMPLETED_OUTPUT =
+ "Key Value\n" +
+ "Iteration number 2\n" +
+ "Iteration duration 5m 0s\n" +
+ "Iteration result
ITERATION_COMPLETED\n" +
+ "Size scheduled to move 30 GB\n" +
+ "Moved data size 30 GB\n" +
+ "Scheduled to move containers 8\n" +
+ "Already moved containers 8\n" +
+ "Failed to move containers 0\n" +
+ "Failed to move containers by timeout 0\n" +
+ "Entered data to nodes \n" +
+ "80f6bc27-e6f3-493e-b1f4-25f810ad960d <- 20 GB\n" +
+ "701ca98e-aa1a-4b36-b817-e28ed634bba6 <- 10 GB\n" +
+ "Exited data from nodes \n" +
+ "b8b9c511-c30f-4933-8938-2f272e307070 -> 15 GB\n" +
+ "7bd99815-47e7-4015-bc61-ca6ef6dfd130 -> 15 GB";
+
+ private static final String ITERATION_3_INTERRUPTED_OUTPUT =
+ "Key Value\n" +
+ "Iteration number 3\n" +
+ "Iteration duration 6m 10s\n" +
+ "Iteration result
ITERATION_INTERRUPTED\n" +
+ "Size scheduled to move 48 GB\n" +
+ "Moved data size 48 GB\n" +
+ "Scheduled to move containers 5\n" +
+ "Already moved containers 5\n" +
+ "Failed to move containers 0\n" +
+ "Failed to move containers by timeout 0\n" +
+ "Entered data to nodes \n" +
+ "80f6bc27-e6f3-493e-b1f4-25f810ad960d <- 20 GB\n" +
+ "701ca98e-aa1a-4b36-b817-e28ed634bba6 <- 28 GB\n" +
+ "Exited data from nodes \n" +
+ "b8b9c511-c30f-4933-8938-2f272e307070 -> 30 GB\n" +
+ "7bd99815-47e7-4015-bc61-ca6ef6dfd130 -> 18 GB";
+
+ private static final String ITERATION_3_COMPLETED_OUTPUT =
+ "Key Value\n" +
+ "Iteration number 3\n" +
+ "Iteration duration 6m 10s\n" +
+ "Iteration result
ITERATION_COMPLETED\n" +
+ "Size scheduled to move 48 GB\n" +
+ "Moved data size 48 GB\n" +
+ "Scheduled to move containers 5\n" +
+ "Already moved containers 5\n" +
+ "Failed to move containers 0\n" +
+ "Failed to move containers by timeout 0\n" +
+ "Entered data to nodes \n" +
+ "80f6bc27-e6f3-493e-b1f4-25f810ad960d <- 20 GB\n" +
+ "701ca98e-aa1a-4b36-b817-e28ed634bba6 <- 28 GB\n" +
+ "Exited data from nodes \n" +
+ "b8b9c511-c30f-4933-8938-2f272e307070 -> 30 GB\n" +
+ "7bd99815-47e7-4015-bc61-ca6ef6dfd130 -> 18 GB";
+
private ContainerBalancerStopSubcommand stopCmd;
private ContainerBalancerStartSubcommand startCmd;
private ContainerBalancerStatusSubcommand statusCmd;
@@ -92,6 +164,17 @@ class TestContainerBalancerSubCommand {
private GenericTestUtils.PrintStreamCapturer err;
private AtomicBoolean verbose;
+ private static final Pattern STOP_REASON = Pattern.compile(
+ "^Stop reason: USER_REQUESTED$", Pattern.MULTILINE);
+ private static final Pattern STOP_MESSAGE = Pattern.compile(
+ "^Message: Stopped by user request\\.$", Pattern.MULTILINE);
+ private static final Pattern COMPLETED_ALL_ITERATIONS_STOP_REASON =
Pattern.compile(
+ "^Stop reason: COMPLETED_ALL_ITERATIONS$", Pattern.MULTILINE);
+ private static final Pattern COMPLETED_ALL_ITERATIONS_STOP_MESSAGE =
Pattern.compile(
+ "^Message: Completed all configured number of iterations\\.$",
Pattern.MULTILINE);
+ private static final Pattern STOPPED_AT = Pattern.compile(
+ "^Stopped at: (\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2})$",
Pattern.MULTILINE);
+
private static ContainerBalancerStatusInfoResponseProto
getContainerBalancerStatusInfoResponseProto(
ContainerBalancerConfiguration config) {
StorageContainerLocationProtocolProtos.ContainerBalancerTaskIterationStatusInfoProto
iteration1StatusInfo =
@@ -234,6 +317,47 @@ private static ContainerBalancerConfiguration
getContainerBalancerConfiguration(
return config;
}
+ /**
+ * Builds a stopped-balancer response.
+ *
+ * @param config configuration
+ * @param stopReason stop reason
+ * @param stopMessage stop message
+ * @param lastIterationResult result for iteration 3, e.g.
ITERATION_INTERRUPTED or ITERATION_COMPLETED
+ * @param balancingDurationSeconds wall-clock duration between startedAt and
stoppedAt
+ */
+ private static ContainerBalancerStatusInfoResponseProto
getStoppedStatusInfoResponseProto(
+ ContainerBalancerConfiguration config, String stopReason, String
stopMessage,
+ String lastIterationResult, long balancingDurationSeconds) {
+ ContainerBalancerStatusInfoProto runningInfo =
+
getContainerBalancerStatusInfoResponseProto(config).getContainerBalancerStatusInfo();
+
+
StorageContainerLocationProtocolProtos.ContainerBalancerTaskIterationStatusInfoProto
iteration3 =
+ runningInfo.getIterationsStatusInfo(2).toBuilder()
+ .setIterationResult(lastIterationResult)
+ .build();
+
+ long stoppedAt = OffsetDateTime.now().toEpochSecond();
+ long startedAt = stoppedAt - balancingDurationSeconds;
+
+ ContainerBalancerStatusInfoProto stoppedInfo = runningInfo.toBuilder()
+ .setStartedAt(startedAt)
+ .setStoppedAt(stoppedAt)
+ .setStopReason(stopReason)
+ .setStopMessage(stopMessage)
+ .setConfiguration(config.toProtobufBuilder().setShouldRun(false))
+ .clearIterationsStatusInfo()
+ .addIterationsStatusInfo(runningInfo.getIterationsStatusInfo(0))
+ .addIterationsStatusInfo(runningInfo.getIterationsStatusInfo(1))
+ .addIterationsStatusInfo(iteration3)
+ .build();
+
+ return ContainerBalancerStatusInfoResponseProto.newBuilder()
+ .setIsRunning(false)
+ .setContainerBalancerStatusInfo(stoppedInfo)
+ .build();
+ }
+
@BeforeEach
void setup() {
verbose = new AtomicBoolean();
@@ -290,7 +414,7 @@ void
testContainerBalancerStatusInfoSubcommandRunningWithoutFlags()
assertThat(out.get()).containsPattern(IS_RUNNING)
.doesNotContain(BALANCER_CONFIG_OUTPUT)
.doesNotContain(currentIterationOutput)
- .doesNotContain("Iteration history list:");
+ .doesNotContain("Completed iteration history:");
}
@Test
@@ -351,7 +475,7 @@ void
testContainerBalancerStatusInfoSubcommandVerboseHistory()
.containsPattern(STARTED_AT)
.containsPattern(DURATION)
.contains(BALANCER_CONFIG_OUTPUT)
- .contains("Iteration history list:")
+ .contains("Completed iteration history:")
.contains(firstHistoryIterationOutput)
.contains(secondHistoryIterationOutput);
}
@@ -396,7 +520,7 @@ void testContainerBalancerStatusInfoSubcommandVerbose()
.containsPattern(DURATION)
.contains(BALANCER_CONFIG_OUTPUT)
.contains(currentIterationOutput)
- .doesNotContain("Iteration history list:");
+ .doesNotContain("Completed iteration history:");
}
@Test
@@ -476,4 +600,116 @@ public void
testContainerBalancerStartSubcommandWhenBalancerIsRunning()
assertThat(err.get()).containsPattern(FAILED_TO_START);
}
+ @Test
+ void
testContainerBalancerStatusSubcommandStoppedWithoutFlagsShowsStopReason()
throws IOException {
+ ScmClient scmClient = mock(ScmClient.class);
+ ContainerBalancerConfiguration config =
getContainerBalancerConfiguration();
+ when(scmClient.getContainerBalancerStatusInfo())
+ .thenReturn(getStoppedStatusInfoResponseProto(
+ config, "USER_REQUESTED", "Stopped by user request.",
+ "ITERATION_INTERRUPTED", 1070L));
+ statusCmd.execute(scmClient);
+ assertThat(out.get())
+ .containsPattern(IS_NOT_RUNNING)
+ .containsPattern(STOP_REASON)
+ .containsPattern(STOP_MESSAGE)
+ .doesNotContain(BALANCER_CONFIG_OUTPUT)
+ .doesNotContain("Last iteration info:")
+ .doesNotContain("Stopped at:")
+ .doesNotContain("Completed iteration history:");
+ }
+
+ @Test
+ void testContainerBalancerStatusSubcommandStoppedVerbose() throws
IOException {
+ ScmClient scmClient = mock(ScmClient.class);
+ ContainerBalancerConfiguration config =
getContainerBalancerConfiguration();
+ when(scmClient.getContainerBalancerStatusInfo())
+ .thenReturn(getStoppedStatusInfoResponseProto(
+ config, "USER_REQUESTED", "Stopped by user request.",
+ "ITERATION_INTERRUPTED", 1070L));
+ verbose.set(true);
+ statusCmd.execute(scmClient);
+
+ assertThat(out.get())
+ .containsPattern(IS_NOT_RUNNING)
+ .containsPattern(STOP_REASON)
+ .containsPattern(STOP_MESSAGE)
+ .containsPattern(STARTED_AT)
+ .containsPattern(STOPPED_AT)
+ .contains(BALANCER_CONFIG_OUTPUT)
+ .contains("Last iteration info:")
+ .contains(ITERATION_3_INTERRUPTED_OUTPUT)
+ .doesNotContain("Current iteration info:")
+ .doesNotContain("Completed iteration history:");
+ }
+
+ @Test
+ void testContainerBalancerStatusSubcommandStoppedVerboseWithHistory() throws
IOException {
+ ScmClient scmClient = mock(ScmClient.class);
+ ContainerBalancerConfiguration config =
getContainerBalancerConfiguration();
+ when(scmClient.getContainerBalancerStatusInfo())
+ .thenReturn(getStoppedStatusInfoResponseProto(
+ config, "USER_REQUESTED", "Stopped by user request.",
+ "ITERATION_INTERRUPTED", 1070L));
+ CommandLine cmd = new CommandLine(statusCmd);
+ verbose.set(true);
+ cmd.parseArgs("--history");
+ statusCmd.execute(scmClient);
+
+ String output = out.get();
+ int lastIterationStart = output.indexOf("Last iteration info:");
+ int historyStart = output.indexOf("Completed iteration history:");
+ String lastIterationSection = output.substring(lastIterationStart,
historyStart);
+ String historySection = output.substring(historyStart);
+
+ assertThat(output)
+ .containsPattern(IS_NOT_RUNNING)
+ .containsPattern(STOP_REASON)
+ .containsPattern(STOP_MESSAGE)
+ .containsPattern(STARTED_AT)
+ .containsPattern(STOPPED_AT)
+ .contains(BALANCER_CONFIG_OUTPUT)
+ .doesNotContain("Current iteration info:");
+ assertThat(lastIterationSection).contains(ITERATION_3_INTERRUPTED_OUTPUT);
+ assertThat(historySection)
+ .contains(ITERATION_1_COMPLETED_OUTPUT)
+ .contains(ITERATION_2_COMPLETED_OUTPUT)
+ .doesNotContain(ITERATION_3_INTERRUPTED_OUTPUT);
+ }
+
+ @Test
+ void
testContainerBalancerStatusSubcommandStoppedAfterAllIterationsCompleteVerboseWithHistory()
+ throws IOException {
+ ScmClient scmClient = mock(ScmClient.class);
+ ContainerBalancerConfiguration config =
getContainerBalancerConfiguration();
+ when(scmClient.getContainerBalancerStatusInfo())
+ .thenReturn(getStoppedStatusInfoResponseProto(config,
"COMPLETED_ALL_ITERATIONS",
+ "Completed all configured number of iterations.",
"ITERATION_COMPLETED",
+ 1070L));
+
+ CommandLine cmd = new CommandLine(statusCmd);
+ verbose.set(true);
+ cmd.parseArgs("--history");
+ statusCmd.execute(scmClient);
+
+ String output = out.get();
+ int lastIterationStart = output.indexOf("Last iteration info:");
+ int historyStart = output.indexOf("Completed iteration history:");
+ String lastIterationSection = output.substring(lastIterationStart,
historyStart);
+ String historySection = output.substring(historyStart);
+
+ assertThat(output)
+ .containsPattern(IS_NOT_RUNNING)
+ .containsPattern(COMPLETED_ALL_ITERATIONS_STOP_REASON)
+ .containsPattern(COMPLETED_ALL_ITERATIONS_STOP_MESSAGE)
+ .containsPattern(STARTED_AT)
+ .containsPattern(STOPPED_AT)
+ .contains(BALANCER_CONFIG_OUTPUT)
+ .doesNotContain("Current iteration info:");
+ assertThat(lastIterationSection).contains(ITERATION_3_COMPLETED_OUTPUT);
+ assertThat(historySection)
+ .contains(ITERATION_1_COMPLETED_OUTPUT)
+ .contains(ITERATION_2_COMPLETED_OUTPUT)
+ .doesNotContain(ITERATION_3_COMPLETED_OUTPUT);
+ }
}
diff --git a/hadoop-ozone/dist/src/main/smoketest/balancer/testBalancer.robot
b/hadoop-ozone/dist/src/main/smoketest/balancer/testBalancer.robot
index 4dc9ce74106..3419ac2a9f4 100644
--- a/hadoop-ozone/dist/src/main/smoketest/balancer/testBalancer.robot
+++ b/hadoop-ozone/dist/src/main/smoketest/balancer/testBalancer.robot
@@ -90,7 +90,7 @@ Verify Balancer Iteration
Verify Balancer Iteration History
[arguments] ${output}
- Should Contain ${output} Iteration history
list:
+ Should Contain ${output} Completed iteration
history:
Should Contain X Times ${output} Size scheduled to
move 1 collapse_spaces=True
Should Contain X Times ${output} Moved data size
1 collapse_spaces=True
Should Contain X Times ${output} Scheduled to move
containers 1 collapse_spaces=True
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]