This is an automated email from the ASF dual-hosted git repository.

agupta 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 22a40b6c40 HDDS-8212. ozone admin printTopology command should have 
json output (#5099)
22a40b6c40 is described below

commit 22a40b6c40bd02183b6e4459b1887abd48039712
Author: Tejaskriya <[email protected]>
AuthorDate: Sun Aug 13 13:37:00 2023 +0530

    HDDS-8212. ozone admin printTopology command should have json output (#5099)
---
 .../hadoop/hdds/scm/cli/TopologySubcommand.java    | 150 +++++++++++++++++++--
 .../main/smoketest/admincli/printTopology.robot    |  32 +++++
 2 files changed, 171 insertions(+), 11 deletions(-)

diff --git 
a/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/TopologySubcommand.java
 
b/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/TopologySubcommand.java
index babcabe592..9ac275fd5c 100644
--- 
a/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/TopologySubcommand.java
+++ 
b/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/TopologySubcommand.java
@@ -22,12 +22,17 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Map;
 import java.util.HashMap;
 import java.util.InvalidPropertiesFormatException;
 import java.util.List;
 import java.util.TreeSet;
 import java.util.stream.Collectors;
 
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import org.apache.hadoop.hdds.cli.HddsVersionProvider;
 import org.apache.hadoop.hdds.cli.OzoneAdmin;
 import org.apache.hadoop.hdds.cli.SubcommandWithParent;
@@ -39,6 +44,7 @@ import static 
org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeState.DEAD;
 import static 
org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeState.HEALTHY;
 import static org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeState.STALE;
 
+import org.apache.hadoop.hdds.server.JsonUtils;
 import org.kohsuke.MetaInfServices;
 import picocli.CommandLine;
 
@@ -82,14 +88,17 @@ public class TopologySubcommand extends ScmSubcommand
           " HEALTHY, STALE, DEAD)")
   private String nodeState;
 
+  @CommandLine.Option(names = { "--json" },
+      defaultValue = "false",
+      description = "Format output as JSON")
+  private boolean json;
+
   @Override
   public void execute(ScmClient scmClient) throws IOException {
     for (HddsProtos.NodeState state : STATES) {
       List<HddsProtos.Node> nodes = scmClient.queryNode(null, state,
           HddsProtos.QueryScope.CLUSTER, "");
       if (nodes != null && nodes.size() > 0) {
-        // show node state
-        System.out.println("State = " + state.toString());
         if (nodeOperationalState != null) {
           if (nodeOperationalState.equals("IN_SERVICE") ||
               nodeOperationalState.equals("DECOMMISSIONING") ||
@@ -120,9 +129,9 @@ public class TopologySubcommand extends ScmSubcommand
           }
         }
         if (order) {
-          printOrderedByLocation(nodes);
+          printOrderedByLocation(nodes, state.toString());
         } else {
-          printNodesWithLocation(nodes);
+          printNodesWithLocation(nodes, state.toString());
         }
       }
     }
@@ -136,10 +145,10 @@ public class TopologySubcommand extends ScmSubcommand
   // Format
   // Location: rack1
   //  ipAddress(hostName) OperationalState
-  private void printOrderedByLocation(List<HddsProtos.Node> nodes) {
-    HashMap<String, TreeSet<DatanodeDetails>> tree =
-        new HashMap<>();
-    HashMap<DatanodeDetails, HddsProtos.NodeOperationalState> state =
+  private void printOrderedByLocation(List<HddsProtos.Node> nodes,
+                                      String state) throws IOException {
+    Map<String, TreeSet<DatanodeDetails>> tree = new HashMap<>();
+    Map<DatanodeDetails, HddsProtos.NodeOperationalState> operationalState =
         new HashMap<>();
     for (HddsProtos.Node node : nodes) {
       String location = node.getNodeID().getNetworkLocation();
@@ -148,16 +157,31 @@ public class TopologySubcommand extends ScmSubcommand
       }
       DatanodeDetails dn = DatanodeDetails.getFromProtoBuf(node.getNodeID());
       tree.get(location).add(dn);
-      state.put(dn, node.getNodeOperationalStates(0));
+      operationalState.put(dn, node.getNodeOperationalStates(0));
     }
     ArrayList<String> locations = new ArrayList<>(tree.keySet());
     Collections.sort(locations);
 
+    if (json) {
+      List<NodeTopologyOrder> nodesJson = new ArrayList<>();
+      locations.forEach(location -> {
+        tree.get(location).forEach(n -> {
+          NodeTopologyOrder nodeJson = new NodeTopologyOrder(n, state,
+              operationalState.get(n).toString());
+          nodesJson.add(nodeJson);
+        });
+      });
+      System.out.println(
+          JsonUtils.toJsonStringWithDefaultPrettyPrinter(nodesJson));
+      return;
+    }
+    // show node state
+    System.out.println("State = " + state);
     locations.forEach(location -> {
       System.out.println("Location: " + location);
       tree.get(location).forEach(n -> {
         System.out.println(" " + n.getIpAddress() + "(" + n.getHostName()
-            + ") " + state.get(n));
+            + ") " + operationalState.get(n));
       });
     });
   }
@@ -180,7 +204,33 @@ public class TopologySubcommand extends ScmSubcommand
 
   // Format "ipAddress(hostName):PortName1=PortValue1    OperationalState
   //     networkLocation
-  private void printNodesWithLocation(Collection<HddsProtos.Node> nodes) {
+  private void printNodesWithLocation(Collection<HddsProtos.Node> nodes,
+                                      String state) throws IOException {
+    if (json) {
+      if (fullInfo) {
+        List<NodeTopologyFull> nodesJson = new ArrayList<>();
+        nodes.forEach(node -> {
+          NodeTopologyFull nodeJson =
+              new NodeTopologyFull(
+                  DatanodeDetails.getFromProtoBuf(node.getNodeID()), state);
+          nodesJson.add(nodeJson);
+        });
+        System.out.println(
+            JsonUtils.toJsonStringWithDefaultPrettyPrinter(nodesJson));
+        return;
+      }
+      List<NodeTopologyDefault> nodesJson = new ArrayList<>();
+      nodes.forEach(node -> {
+        NodeTopologyDefault nodeJson = new NodeTopologyDefault(
+            DatanodeDetails.getFromProtoBuf(node.getNodeID()), state);
+        nodesJson.add(nodeJson);
+      });
+      System.out.println(
+          JsonUtils.toJsonStringWithDefaultPrettyPrinter(nodesJson));
+      return;
+    }
+    // show node state
+    System.out.println("State = " + state);
     nodes.forEach(node -> {
       System.out.print(" " + getAdditionNodeOutput(node) +
           node.getNodeID().getIpAddress() + "(" +
@@ -192,4 +242,82 @@ public class TopologySubcommand extends ScmSubcommand
               node.getNodeID().getNetworkLocation() : "NA"));
     });
   }
+
+  private static class ListJsonSerializer extends
+      JsonSerializer<List<DatanodeDetails.Port>> {
+    @Override
+    public void serialize(List<DatanodeDetails.Port> value, JsonGenerator jgen,
+                          SerializerProvider provider)
+        throws IOException {
+      jgen.writeStartObject();
+      for (DatanodeDetails.Port port : value) {
+        jgen.writeNumberField(port.getName().toString(), port.getValue());
+      }
+      jgen.writeEndObject();
+    }
+  }
+
+  private static class NodeTopologyOrder {
+    private String ipAddress;
+    private String hostName;
+    private String nodeState;
+    private String operationalState;
+    private String networkLocation;
+
+    NodeTopologyOrder(DatanodeDetails node, String state, String opState) {
+      ipAddress = node.getIpAddress();
+      hostName = node.getHostName();
+      nodeState = state;
+      operationalState = opState;
+      networkLocation = (node.getNetworkLocation() != null ?
+          node.getNetworkLocation() : "NA");
+    }
+
+    public String getIpAddress() {
+      return ipAddress;
+    }
+
+    public String getHostName() {
+      return hostName;
+    }
+
+    public String getNodeState() {
+      return nodeState;
+    }
+
+    public String getOperationalState() {
+      return operationalState;
+    }
+
+    public String getNetworkLocation() {
+      return networkLocation;
+    }
+  }
+
+  private static class NodeTopologyDefault extends NodeTopologyOrder {
+    private List<DatanodeDetails.Port> ports;
+
+    NodeTopologyDefault(DatanodeDetails node, String state) {
+      super(node, state, node.getPersistedOpState().toString());
+      ports = node.getPorts();
+    }
+
+    @JsonSerialize(using = ListJsonSerializer.class)
+    public List<DatanodeDetails.Port> getPorts() {
+      return ports;
+    }
+  }
+
+  private static class NodeTopologyFull extends NodeTopologyDefault {
+    private String uuid;
+
+    NodeTopologyFull(DatanodeDetails node, String state) {
+      super(node, state);
+      uuid = node.getUuid().toString();
+    }
+
+    public String getUuid() {
+      return uuid;
+    }
+  }
 }
diff --git a/hadoop-ozone/dist/src/main/smoketest/admincli/printTopology.robot 
b/hadoop-ozone/dist/src/main/smoketest/admincli/printTopology.robot
new file mode 100644
index 0000000000..50cce5652b
--- /dev/null
+++ b/hadoop-ozone/dist/src/main/smoketest/admincli/printTopology.robot
@@ -0,0 +1,32 @@
+# 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.
+
+*** Settings ***
+Documentation       Test ozone admin printTopology command
+Library             OperatingSystem
+Library             BuiltIn
+Resource            ../commonlib.robot
+Test Timeout        5 minutes
+
+
+*** Test Cases ***
+Run printTopology
+    ${output} =         Execute          ozone admin printTopology
+                        Should Match Regexp     ${output}     State =
+
+Run printTopology as JSON
+    ${output} =         Execute             ozone admin printTopology --json
+    ${keys} =           Execute             echo '${output}' | jq -r '.[0] | 
keys'
+                        Should Contain      ${output}           ipAddress


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to