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]

Reply via email to