errose28 commented on code in PR #7944:
URL: https://github.com/apache/ozone/pull/7944#discussion_r2271461818
##########
hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/container/ReconcileSubcommand.java:
##########
@@ -34,15 +45,183 @@
versionProvider = HddsVersionProvider.class)
public class ReconcileSubcommand extends ScmSubcommand {
- @CommandLine.Parameters(description = "ID of the container to reconcile")
- private long containerId;
+ @CommandLine.Mixin
+ private ContainerIDParameters containerList;
+
+ @CommandLine.Option(names = { "--status" },
+ defaultValue = "false",
+ fallbackValue = "true",
+ description = "Display the reconciliation status of this container's
replicas")
+ private boolean status;
@Override
public void execute(ScmClient scmClient) throws IOException {
- scmClient.reconcileContainer(containerId);
- System.out.println("Reconciliation has been triggered for container " +
containerId);
- // TODO HDDS-12078 allow status to be checked from the reconcile
subcommand directly.
- System.out.println("Use \"ozone admin container info --json " +
containerId + "\" to see the checksums of each " +
- "container replica");
+ if (status) {
+ executeStatus(scmClient);
+ } else {
+ executeReconcile(scmClient);
+ }
+ }
+
+ private void executeStatus(ScmClient scmClient) throws IOException {
+ // Do validation outside the json array writer, otherwise failed
validation will print an empty json array.
+ List<Long> containerIDs = containerList.getValidatedIDs();
+ try (SequenceWriter arrayWriter = JsonUtils.getSequenceWriter(System.out))
{
+ // Since status is retrieved using container info, do client side
validation that it is only used for Ratis
+ // containers. If EC containers are given, print a message to stderr
and eventually exit non-zero, but continue
+ // processing the remaining containers.
+ int failureCount = 0;
+ for (Long containerID : containerIDs) {
+ if (!printReconciliationStatus(scmClient, containerID, arrayWriter)) {
+ failureCount++;
+ }
+ }
+ if (failureCount > 0) {
+ throw new RuntimeException("Failed to process reconciliation status
for " + failureCount + " containers");
+ }
+ }
+ // Array writer will not add a newline to the end.
+ System.out.println();
+ }
+
+ private boolean printReconciliationStatus(ScmClient scmClient, long
containerID, SequenceWriter arrayWriter) {
+ try {
+ ContainerInfo containerInfo = scmClient.getContainer(containerID);
+ if (containerInfo.getReplicationType() !=
HddsProtos.ReplicationType.RATIS) {
+ System.err.println("Cannot get status of container " + containerID +
+ ". Reconciliation is only supported for Ratis replicated
containers");
+ return false;
+ }
+ List<ContainerReplicaInfo> replicas =
scmClient.getContainerReplicas(containerID);
+ arrayWriter.write(new ContainerWrapper(containerInfo, replicas));
+ arrayWriter.flush();
+ } catch (Exception ex) {
+ System.err.println("Failed get reconciliation status of container " +
containerID + ": " + ex.getMessage());
+ return false;
+ }
+ return true;
+ }
+
+ private void executeReconcile(ScmClient scmClient) {
+ int failureCount = 0;
+ for (Long containerID : containerList.getValidatedIDs()) {
+ try {
+ scmClient.reconcileContainer(containerID);
+ System.out.println("Reconciliation has been triggered for container "
+ (long) containerID);
+ System.out.println("Use \"ozone admin container reconcile --status " +
containerID +
+ "\" to see the checksums of each container replica");
+ } catch (Exception ex) {
+ System.err.println("Failed to trigger reconciliation for container " +
containerID + ": " + ex.getMessage());
+ failureCount++;
+ }
+ }
+ if (failureCount > 0) {
+ throw new RuntimeException("Failed trigger reconciliation for " +
failureCount + " containers");
+ }
+ }
+
+ /**
+ * Used to json serialize the container and replica information for output.
+ */
+ private static class ContainerWrapper {
+ private final long containerID;
+ private final HddsProtos.LifeCycleState state;
+ private final ReplicationConfig replicationConfig;
+ private boolean replicasMatch;
+ private final List<ReplicaWrapper> replicas;
+
+ ContainerWrapper(ContainerInfo info, List<ContainerReplicaInfo> replicas) {
+ this.containerID = info.getContainerID();
+ this.state = info.getState();
+ this.replicationConfig = info.getReplicationConfig();
+
+ this.replicas = new ArrayList<>();
+ this.replicasMatch = true;
+ long firstChecksum = 0;
+ if (!replicas.isEmpty()) {
+ firstChecksum = replicas.get(0).getDataChecksum();
+ }
+ for (ContainerReplicaInfo replica: replicas) {
+ replicasMatch = replicasMatch && (firstChecksum ==
replica.getDataChecksum());
+ this.replicas.add(new ReplicaWrapper(replica));
+ }
+ }
+
+ public long getContainerID() {
+ return containerID;
+ }
+
+ public HddsProtos.LifeCycleState getState() {
+ return state;
+ }
+
+ public ReplicationConfig getReplicationConfig() {
+ return replicationConfig;
+ }
+
+ public boolean getReplicasMatch() {
+ return replicasMatch;
+ }
+
+ public List<ReplicaWrapper> getReplicas() {
+ return replicas;
+ }
+ }
+
+ private static class ReplicaWrapper {
+ private final DatanodeWrapper datanode;
+ private final String state;
+ private int replicaIndex;
+ @JsonSerialize(using = JsonUtils.ChecksumSerializer.class)
+ private final long dataChecksum;
+
+ ReplicaWrapper(ContainerReplicaInfo replica) {
+ this.datanode = new DatanodeWrapper(replica.getDatanodeDetails());
+ this.state = replica.getState();
+ // Only display replica index when it has a positive value for EC.
+ if (replica.getReplicaIndex() > 0) {
+ this.replicaIndex = replica.getReplicaIndex();
+ }
+ this.dataChecksum = replica.getDataChecksum();
+ }
+
+ public DatanodeWrapper getDatanode() {
+ return datanode;
+ }
+
+ public String getState() {
+ return state;
+ }
+
+ /**
+ * Replica index is only included in the output if it is non-zero, which
will be the case for EC.
+ * For Ratis, avoid printing all zero replica indices to avoid confusion.
+ */
+ @JsonInclude(JsonInclude.Include.NON_DEFAULT)
+ public int getReplicaIndex() {
+ return replicaIndex;
+ }
+
+ public long getDataChecksum() {
+ return dataChecksum;
+ }
+ }
+
+ private static class DatanodeWrapper {
Review Comment:
It's harder to match json queries to the value returned by that method
because the ID, hostname, and IP are all mixed together. If we want IP I can
add it here.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]