HDFS-15245. Improve JournalNode web UI. Contributed by Jianfei Jiang.
(cherry picked from commit 960c9ebaea
)
Conflicts:
hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/qjournal/server/JournalNode.java
This commit is contained in:
parent
1c6c3920b6
commit
41b39ff292
|
@ -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
|
||||
|
@ -393,6 +397,24 @@ 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
|
||||
*/
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
})();
|
|
@ -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();">×</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>
|
|
@ -97,6 +97,16 @@ public class TestJournalNodeMXBean {
|
|||
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();
|
||||
|
|
Loading…
Reference in New Issue