Bläddra i källkod

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 år sedan
förälder
incheckning
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.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.Maps;
+import org.apache.hadoop.util.VersionInfo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.hadoop.classification.InterfaceAudience;
@@ -57,7 +59,9 @@ import java.net.InetSocketAddress;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * The JournalNode is a daemon which allows namenodes using
@@ -392,7 +396,25 @@ public class JournalNode implements Tool, Configurable, JournalNodeMXBean {
 
     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
    */

+ 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.InterfaceStability;
 
+import java.util.List;
+
 /**
  * This is the JMX management interface for JournalNode information
  */
@@ -32,5 +34,27 @@ public interface JournalNodeMXBean {
    * 
    * @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
    contributor license agreements.  See the NOTICE file distributed with
@@ -16,47 +14,11 @@
    See the License for the specific language governing permissions and
    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">
 <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>
-<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>

+ 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");
     jMap.put(MiniJournalCluster.CLUSTER_WAITACTIVE_URI, infoMap1);
     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
     jCluster = new MiniJournalCluster.Builder(new Configuration()).format(false)
         .numJournalNodes(NUM_JN).build();