HDDS-6863. Add Group-Id & Ratis-Roles Information for OM UI (#3520)

diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
index e260e2a..65a2f67 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
@@ -36,11 +36,14 @@
 import java.util.Optional;
 import java.util.OptionalInt;
 import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.Comparator;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.hdds.conf.ConfigurationSource;
 import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
 import org.apache.hadoop.hdds.scm.client.HddsClientUtils;
 import org.apache.hadoop.net.NetUtils;
 import org.apache.hadoop.ozone.conf.OMClientConfig;
@@ -49,6 +52,7 @@
 import org.apache.hadoop.ozone.om.helpers.OMNodeDetails;
 import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
 import org.apache.hadoop.ozone.om.helpers.RepeatedOmKeyInfo;
+import org.apache.hadoop.ozone.om.helpers.ServiceInfo;
 import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
 
 import org.apache.commons.lang3.StringUtils;
@@ -795,4 +799,38 @@
     printString.append("]");
     return printString.toString();
   }
+
+  public static String format(List<ServiceInfo> nodes, int port,
+                              String leaderId) {
+    StringBuilder sb = new StringBuilder();
+    // Ensuring OM's are printed in correct order
+    List<ServiceInfo> omNodes = nodes.stream()
+        .filter(node -> node.getNodeType() == HddsProtos.NodeType.OM)
+        .sorted(Comparator.comparing(ServiceInfo::getHostname))
+        .collect(Collectors.toList());
+    int count = 0;
+    for (ServiceInfo info : omNodes) {
+      // Printing only the OM's running
+      if (info.getNodeType() == HddsProtos.NodeType.OM) {
+        String role =
+            info.getOmRoleInfo().getNodeId().equals(leaderId) ? "LEADER" :
+                "FOLLOWER";
+        sb.append(
+            String.format(
+                " { HostName: %s | Node-Id: %s | Ratis-Port : %d | Role: %s} ",
+                info.getHostname(),
+                info.getOmRoleInfo().getNodeId(),
+                port,
+                role
+            ));
+        count++;
+      }
+    }
+    // Print Stand-alone if only one OM exists
+    if (count == 1) {
+      return "STANDALONE";
+    } else {
+      return sb.toString();
+    }
+  }
 }
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMXBean.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMXBean.java
index f378a8a..791379b 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMXBean.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMXBean.java
@@ -29,6 +29,8 @@
 
   String getRpcPort();
 
+  String getRatisRoles();
+
   String getRatisLogDirectory();
 
   String getRocksDbDirectory();
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
index 7ddaca5..2898fc8 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
@@ -279,6 +279,7 @@
 import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse.PrepareStatus;
 import org.apache.ratis.proto.RaftProtos.RaftPeerRole;
 import org.apache.ratis.protocol.RaftGroupId;
+import org.apache.ratis.protocol.RaftPeer;
 import org.apache.ratis.protocol.RaftPeerId;
 import org.apache.ratis.server.protocol.TermIndex;
 import org.apache.ratis.util.ExitUtils;
@@ -2985,6 +2986,24 @@
   }
 
   @Override
+  public String getRatisRoles() {
+    List<ServiceInfo> serviceList = null;
+    int port = omNodeDetails.getRatisPort();
+    RaftPeer leaderId;
+    if (isRatisEnabled) {
+      try {
+        leaderId = omRatisServer.getLeader();
+        serviceList = getServiceList();
+      } catch (IOException e) {
+        LOG.error("IO-Exception Occurred", e);
+        return "Exception: " + e.toString();
+      }
+      return OmUtils.format(serviceList, port, leaderId.getId().toString());
+    } else {
+      return "Ratis-Disabled";
+    }
+  }
+
   public String getRatisLogDirectory() {
     return  OzoneManagerRatisUtils.getOMRatisDirectory(configuration);
   }
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerRatisServer.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerRatisServer.java
index 5ee7b27..92b685d 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerRatisServer.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerRatisServer.java
@@ -82,6 +82,7 @@
 import org.apache.ratis.server.RaftServer;
 import org.apache.ratis.server.RaftServerConfigKeys;
 import org.apache.ratis.server.protocol.TermIndex;
+import org.apache.ratis.thirdparty.com.google.protobuf.ByteString;
 import org.apache.ratis.util.LifeCycle;
 import org.apache.ratis.util.SizeInBytes;
 import org.apache.ratis.util.StringUtils;
@@ -728,6 +729,18 @@
         .getPropsMatchPrefixAndTrimPrefix(OZONE_OM_HA_PREFIX + ".");
   }
 
+  public RaftPeer getLeader() throws IOException {
+    RaftServer.Division division = server.getDivision(raftGroupId);
+    if (division.getInfo().isLeader()) {
+      return division.getPeer();
+    } else {
+      ByteString leaderId = division.getInfo().getRoleInfoProto()
+          .getFollowerInfo().getLeaderInfo().getId().getId();
+      return leaderId.isEmpty() ? null :
+          division.getRaftConf().getPeer(RaftPeerId.valueOf(leaderId));
+    }
+  }
+
   /**
    * Defines RaftServer Status.
    */
diff --git a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/om-overview.html b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/om-overview.html
index 66bcfd8..8a54aa0 100644
--- a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/om-overview.html
+++ b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/om-overview.html
@@ -22,6 +22,26 @@
         <td>Rpc port</td>
         <td>{{$ctrl.overview.jmx.RpcPort}}</td>
     </tr>
+    <tr>
+        <td>OM Roles (HA)	</td>
+        <td>{{$ctrl.overview.jmx.RatisRoles}}</td>
+    </tr>
+    <tr>
+        <td>Current-Role</td>
+        <td>{{$ctrl.role.Role}}</td>
+    </tr>
+    <tr>
+        <td>Group-Id</td>
+        <td>{{$ctrl.role.GroupId}}</td>
+    </tr>
+    <tr ng-hide="!$ctrl.electionCount.Count || $ctrl.electionCount.Count==-1">
+        <td>Election-Count</td>
+        <td>{{$ctrl.electionCount.Count}}</td>
+    </tr>
+    <tr ng-hide="!$ctrl.elapsedTime.Value || $ctrl.elapsedTime.Value==-1">
+        <td>Last Election Elapsed Time</td>
+        <td>{{$ctrl.elapsedTime.Value}}</td>
+    </tr>
     </tbody>
 </table>
 
diff --git a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/ozoneManager.js b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/ozoneManager.js
index 6c59a5b..bd402b1 100644
--- a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/ozoneManager.js
+++ b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/ozoneManager.js
@@ -113,5 +113,35 @@
         require: {
             overview: "^overview"
         },
+        controller: function ($http) {
+            var ctrl = this;
+            $http.get("jmx?qry=Ratis:service=RaftServer,group=*,id=*")
+                .then(function (result) {
+                    ctrl.role = result.data.beans[0];
+                });
+
+            $http.get("jmx?qry=ratis:name=ratis.leader_election.*electionCount")
+                .then(function (result) {
+                    ctrl.electionCount = result.data.beans[0];
+                });
+
+            $http.get("jmx?qry=ratis:name=ratis.leader_election.*lastLeaderElectionElapsedTime")
+                .then(function (result) {
+                    ctrl.elapsedTime = result.data.beans[0];
+                    if(ctrl.elapsedTime.Value != -1){
+                        ctrl.elapsedTime.Value = convertMsToTime(ctrl.elapsedTime.Value);
+                    }
+                });
+        }
     });
+        function convertMsToTime(ms) {
+          let seconds = (ms / 1000).toFixed(1);
+          let minutes = (ms / (1000 * 60)).toFixed(1);
+          let hours = (ms / (1000 * 60 * 60)).toFixed(1);
+          let days = (ms / (1000 * 60 * 60 * 24)).toFixed(1);
+          if (seconds < 60) return seconds + " Seconds";
+          else if (minutes < 60) return minutes + " Minutes";
+          else if (hours < 24) return hours + " Hours";
+          else return days + " Days"
+        }
 })();