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 @@
|
|||||||
|
|
||||||
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.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 boolean accept(File file) {
|
|||||||
|
|
||||||
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
|
||||||
*/
|
*/
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
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();
|
||||||
}
|
}
|
||||||
|
@ -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" />
|
<meta http-equiv="REFRESH" content="0;url=journalnode.html" />
|
||||||
<link rel="stylesheet" type="text/css" href="/static/bootstrap-3.4.1/css/bootstrap.min.css" />
|
<title>Hadoop Administration</title>
|
||||||
<link rel="stylesheet" type="text/css" href="/static/hadoop.css" />
|
|
||||||
<title>JournalNode Information</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>
|
||||||
|
@ -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>
|
@ -96,7 +96,17 @@ public void testJournalNodeMXBean() throws Exception {
|
|||||||
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();
|
||||||
|
Loading…
Reference in New Issue
Block a user