فهرست منبع

HDFS-15245. Improve JournalNode web UI. Contributed by Jianfei Jiang.

(cherry picked from commit 960c9ebaea31586c7a6d5a029a1555c4ed6cfadb)

Conflicts:
    hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/qjournal/server/JournalNode.java
Ayush Saxena 4 سال پیش
والد
کامیت
41b39ff292

+ 23 - 1
hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/qjournal/server/JournalNode.java

@@ -19,8 +19,10 @@ package org.apache.hadoop.hdfs.qjournal.server;
 
 
 import org.apache.hadoop.thirdparty.com.google.common.annotations.VisibleForTesting;
 import org.apache.hadoop.thirdparty.com.google.common.annotations.VisibleForTesting;
 import org.apache.hadoop.thirdparty.com.google.common.base.Preconditions;
 import org.apache.hadoop.thirdparty.com.google.common.base.Preconditions;
+import org.apache.hadoop.thirdparty.com.google.common.base.Strings;
 import org.apache.hadoop.thirdparty.com.google.common.collect.Lists;
 import org.apache.hadoop.thirdparty.com.google.common.collect.Lists;
 import org.apache.hadoop.thirdparty.com.google.common.collect.Maps;
 import org.apache.hadoop.thirdparty.com.google.common.collect.Maps;
+import org.apache.hadoop.util.VersionInfo;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
 import org.apache.hadoop.classification.InterfaceAudience;
 import org.apache.hadoop.classification.InterfaceAudience;
@@ -57,7 +59,9 @@ import java.net.InetSocketAddress;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 
 /**
 /**
  * The JournalNode is a daemon which allows namenodes using
  * The JournalNode is a daemon which allows namenodes using
@@ -392,7 +396,25 @@ public class JournalNode implements Tool, Configurable, JournalNodeMXBean {
 
 
     return JSON.toString(status);
     return JSON.toString(status);
   }
   }
-  
+
+  @Override // JournalNodeMXBean
+  public String getHostAndPort() {
+    return NetUtils.getHostPortString(rpcServer.getAddress());
+  }
+
+  @Override // JournalNodeMXBean
+  public List<String> getClusterIds() {
+    return journalsById.values().stream()
+        .map(j -> j.getStorage().getClusterID())
+        .filter(cid -> !Strings.isNullOrEmpty(cid))
+        .distinct().collect(Collectors.toList());
+  }
+
+  @Override // JournalNodeMXBean
+  public String getVersion() {
+    return VersionInfo.getVersion() + ", r" + VersionInfo.getRevision();
+  }
+
   /**
   /**
    * Register JournalNodeMXBean
    * Register JournalNodeMXBean
    */
    */

+ 25 - 1
hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/qjournal/server/JournalNodeMXBean.java

@@ -20,6 +20,8 @@ package org.apache.hadoop.hdfs.qjournal.server;
 import org.apache.hadoop.classification.InterfaceAudience;
 import org.apache.hadoop.classification.InterfaceAudience;
 import org.apache.hadoop.classification.InterfaceStability;
 import org.apache.hadoop.classification.InterfaceStability;
 
 
+import java.util.List;
+
 /**
 /**
  * This is the JMX management interface for JournalNode information
  * This is the JMX management interface for JournalNode information
  */
  */
@@ -32,5 +34,27 @@ public interface JournalNodeMXBean {
    * 
    * 
    * @return A string presenting status for each journal
    * @return A string presenting status for each journal
    */
    */
-  public String getJournalsStatus();
+  String getJournalsStatus();
+
+  /**
+   * Get host and port of JournalNode.
+   *
+   * @return colon separated host and port.
+   */
+  String getHostAndPort();
+
+  /**
+   * Get list of the clusters of JournalNode's journals
+   * as one JournalNode may support multiple clusters.
+   *
+   * @return list of clusters.
+   */
+  List<String> getClusterIds();
+
+  /**
+   * Gets the version of Hadoop.
+   *
+   * @return the version of Hadoop.
+   */
+  String getVersion();
 }
 }

+ 4 - 42
hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/journal/index.html

@@ -1,5 +1,3 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <!--
 <!--
    Licensed to the Apache Software Foundation (ASF) under one or more
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
    contributor license agreements.  See the NOTICE file distributed with
@@ -16,47 +14,11 @@
    See the License for the specific language governing permissions and
    See the License for the specific language governing permissions and
    limitations under the License.
    limitations under the License.
 -->
 -->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml">
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
 <head>
-<meta http-equiv="X-UA-Compatible" content="IE=edge" />
-<link rel="stylesheet" type="text/css" href="/static/bootstrap-3.4.1/css/bootstrap.min.css" />
-<link rel="stylesheet" type="text/css" href="/static/hadoop.css" />
-<title>JournalNode Information</title>
+    <meta http-equiv="REFRESH" content="0;url=journalnode.html" />
+    <title>Hadoop Administration</title>
 </head>
 </head>
-<body>
-
-<header class="navbar navbar-inverse bs-docs-nav" role="banner">
-<div class="container">
-  <div class="navbar-header">
-    <div class="navbar-brand">Hadoop</div>
-  </div>
-
-  <ul class="nav navbar-nav" id="ui-tabs">
-    <li><a>Overview</a></li>
-  </ul>
-</div>
-</header>
-
-<div class="container">
-
-<div class="tab-content">
-  <div class="tab-pane" id="tab-overview">
-    <div class="page-header"><h1>JournalNode on <small><div id="authority" style="display: inline-block"></div></small></h1></div>
-  </div>
-</div>
-
-<div class="row">
-  <hr />
-  <div class="col-xs-2"><p>Hadoop, {release-year-token}.</p></div>
-</div>
-</div>
-
-<script type="text/javascript" src="/static/jquery-3.4.1.min.js">
-</script><script type="text/javascript" src="/static/bootstrap-3.4.1/js/bootstrap.min.js">
-</script>
-<script type="text/javascript">
-$('#authority').html(window.location.host);
-$('#tab-overview').addClass('active');
-</script>
-</body>
 </html>
 </html>

+ 82 - 0
hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/journal/jn.js

@@ -0,0 +1,82 @@
+/**
+ * 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.
+ */
+(function () {
+    "use strict";
+
+    var data = {};
+
+    dust.loadSource(dust.compile($('#tmpl-jn').html(), 'jn'));
+
+    var BEANS = [
+        {"name": "jn",      "url": "/jmx?qry=Hadoop:service=JournalNode,name=JournalNodeInfo"},
+        {"name": "journals", "url": "/jmx?qry=Hadoop:service=JournalNode,name=Journal-*"}
+
+    ];
+
+    var HELPERS = {
+        'helper_date_tostring' : function (chunk, ctx, bodies, params) {
+            var value = dust.helpers.tap(params.value, chunk, ctx);
+            return chunk.write('' + moment(Number(value)).format('ddd MMM DD HH:mm:ss ZZ YYYY'));
+        }
+    };
+
+    load_json(
+        BEANS,
+        guard_with_startup_progress(function(d) {
+            for (var k in d) {
+                data[k] = k === 'journals' ? workaround(d[k].beans) : d[k].beans[0];
+            }
+            render();
+        }),
+        function (url, jqxhr, text, err) {
+            show_err_msg('<p>Failed to retrieve data from ' + url + ', cause: ' + err + '</p>');
+        });
+
+    function guard_with_startup_progress(fn) {
+        return function() {
+            try {
+                fn.apply(this, arguments);
+            } catch (err) {
+                if (err instanceof TypeError) {
+                    show_err_msg('JournalNode error: ' + err);
+                }
+            }
+        };
+    }
+
+    function workaround(journals) {
+        for (var i in journals){
+            journals[i]['NameService']= journals[i]['modelerType'].split("-")[1];
+        }
+
+        return journals;
+    }
+
+    function render() {
+        var base = dust.makeBase(HELPERS);
+        dust.render('jn', base.push(data), function(err, out) {
+            $('#tab-overview').html(out);
+            $('#tab-overview').addClass('active');
+        });
+    }
+
+    function show_err_msg() {
+        $('#alert-panel-body').html("Failed to load journalnode information");
+        $('#alert-panel').show();
+    }
+})();

+ 108 - 0
hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/journal/journalnode.html

@@ -0,0 +1,108 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<!--
+   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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+  <link rel="stylesheet" type="text/css" href="/static/bootstrap-3.4.1/css/bootstrap.min.css" />
+  <link rel="stylesheet" type="text/css" href="/static/hadoop.css" />
+  <title>JournalNode Information</title>
+</head>
+<body>
+
+<header class="navbar navbar-inverse bs-docs-nav" role="banner">
+  <div class="container">
+    <div class="navbar-header">
+      <div class="navbar-brand">Hadoop</div>
+    </div>
+
+    <ul class="nav navbar-nav" id="ui-tabs">
+      <li><a href="#tab-overview">Overview</a></li>
+      <li class="dropdown">
+        <a href="#" class="dropdown-toggle" data-toggle="dropdown">Utilities <b class="caret"></b></a>
+        <ul class="dropdown-menu">
+          <li><a href="logs/">Logs</a></li>
+          <li><a href="logLevel">Log Level</a></li>
+          <li><a href="jmx">Metrics</a></li>
+          <li><a href="conf">Configuration</a></li>
+          <li><a href="stacks">Process Thread Dump</a></li>
+        </ul>
+      </li>
+    </ul>
+  </div>
+</header>
+
+<div class="container">
+
+  <div id="alert-panel">
+    <div class="alert alert-danger">
+      <button type="button" class="close" onclick="$('#alert-panel').hide();">&times;</button>
+      <div class="alert-body" id="alert-panel-body"></div>
+    </div>
+  </div>
+
+  <div class="tab-content">
+    <div class="tab-pane" id="tab-overview"></div>
+  </div>
+
+  <div class="row">
+    <hr />
+    <div class="col-xs-2"><p>Hadoop, {release-year-token}.</p></div>
+  </div>
+</div>
+
+<script type="text/x-dust-template" id="tmpl-jn">
+  {#jn}
+  <div class="page-header"><h1>JournalNode on <small>{HostAndPort}</small></h1></div>
+  <table class="table table-bordered table-striped">
+    <tr><th>Cluster ID:</th><td>{ClusterIds}</td></tr>
+    <tr><th>Version:</th><td>{Version}</td></tr>
+  </table>
+  {/jn}
+
+  <div class="page-header"><h1>Journal Status</h1></div>
+  <table class="table">
+    <thead>
+    <tr>
+      <th>Nameservice</th>
+      <th>Current Transaction ID</th>
+      <th>Current Lag Transactions</th>
+      <th>Last Journal Timestamp</th>
+    </tr>
+    </thead>
+    {#journals}
+    <tr>
+      <td>{NameService}</td>
+      <td>{LastWrittenTxId}</td>
+      <td>{CurrentLagTxns}</td>
+      <td>{#helper_date_tostring value="{LastJournalTimestamp}"/}</td>
+    </tr>
+    {/journals}
+  </table>
+</script>
+
+<script type="text/javascript" src="/static/jquery-3.4.1.min.js"></script>
+<script type="text/javascript" src="/static/bootstrap-3.4.1/js/bootstrap.min.js"></script>
+<script type="text/javascript" src="/static/moment.min.js"></script>
+<script type="text/javascript" src="/static/dust-full-2.0.0.min.js"></script>
+<script type="text/javascript" src="/static/dust-helpers-1.1.1.min.js"></script>
+<script type="text/javascript" src="/static/dfs-dust.js"></script>
+<script type="text/javascript" src="jn.js"></script>
+
+</body>
+</html>

+ 11 - 1
hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/qjournal/server/TestJournalNodeMXBean.java

@@ -96,7 +96,17 @@ public class TestJournalNodeMXBean {
     infoMap1.put("Formatted", "false");
     infoMap1.put("Formatted", "false");
     jMap.put(MiniJournalCluster.CLUSTER_WAITACTIVE_URI, infoMap1);
     jMap.put(MiniJournalCluster.CLUSTER_WAITACTIVE_URI, infoMap1);
     assertEquals(JSON.toString(jMap), journalStatus);
     assertEquals(JSON.toString(jMap), journalStatus);
-    
+
+    // check attributes
+    String hostAndPort = (String) mbs.getAttribute(mxbeanName, "HostAndPort");
+    assertEquals(jn.getHostAndPort(), hostAndPort);
+    assertTrue(hostAndPort.matches("localhost:\\d+"));
+    String[] clusterId = (String[]) mbs.getAttribute(mxbeanName, "ClusterIds");
+    assertEquals(jn.getClusterIds().size(), clusterId.length);
+    assertEquals("mycluster", clusterId[0]);
+    String version = (String) mbs.getAttribute(mxbeanName, "Version");
+    assertEquals(jn.getVersion(), version);
+
     // restart journal node without formatting
     // restart journal node without formatting
     jCluster = new MiniJournalCluster.Builder(new Configuration()).format(false)
     jCluster = new MiniJournalCluster.Builder(new Configuration()).format(false)
         .numJournalNodes(NUM_JN).build();
         .numJournalNodes(NUM_JN).build();