HDFS-5382. Implement the UI of browsing filesystems in HTML 5 page. Contributed by Haohui Mai.
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1534368 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
dc523bd182
commit
fc99f3e8a3
|
@ -261,6 +261,9 @@ Release 2.3.0 - UNRELEASED
|
|||
HDFS-5379. Update links to datanode information in dfshealth.html. (Haohui
|
||||
Mai via jing9)
|
||||
|
||||
HDFS-5382. Implement the UI of browsing filesystems in HTML 5 page. (Haohui
|
||||
Mai via jing9)
|
||||
|
||||
IMPROVEMENTS
|
||||
|
||||
HDFS-5267. Remove volatile from LightWeightHashSet. (Junping Du via llu)
|
||||
|
|
|
@ -550,6 +550,8 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|||
<exclude>src/main/webapps/static/dust-full-2.0.0.min.js</exclude>
|
||||
<exclude>src/main/webapps/static/dust-helpers-1.1.1.min.js</exclude>
|
||||
<exclude>src/main/webapps/hdfs/dfshealth.dust.html</exclude>
|
||||
<exclude>src/main/webapps/hdfs/explorer-block-info.dust.html</exclude>
|
||||
<exclude>src/main/webapps/hdfs/explorer.dust.html</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
|
|
@ -413,8 +413,15 @@ public class DatanodeWebHdfsMethods {
|
|||
final long n = length.getValue() != null ?
|
||||
Math.min(length.getValue(), in.getVisibleLength() - offset.getValue()) :
|
||||
in.getVisibleLength() - offset.getValue();
|
||||
return Response.ok(new OpenEntity(in, n, dfsclient)).type(
|
||||
MediaType.APPLICATION_OCTET_STREAM).build();
|
||||
|
||||
/**
|
||||
* Allow the Web UI to perform an AJAX request to get the data.
|
||||
*/
|
||||
return Response.ok(new OpenEntity(in, n, dfsclient))
|
||||
.type(MediaType.APPLICATION_OCTET_STREAM)
|
||||
.header("Access-Control-Allow-Methods", "GET")
|
||||
.header("Access-Control-Allow-Origin", "*")
|
||||
.build();
|
||||
}
|
||||
case GETFILECHECKSUM:
|
||||
{
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<a id="browse-dir-first" style="cursor:pointer">Browse the filesystem</a> <a href="/logs/">NameNode Logs</a>
|
||||
<a href="explorer.html">Browse the filesystem</a> <a href="/logs/">NameNode Logs</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
|
@ -56,7 +56,7 @@
|
|||
<div class="panel-body">
|
||||
|
||||
<p>
|
||||
Security is {#nnstat}{#SecurityModeEnabled}on{:else}off{/SecurityModeEnabled}{/nnstat}.</p>
|
||||
Security is {#nnstat}{#SecurityEnabled}on{:else}off{/SecurityEnabled}{/nnstat}.</p>
|
||||
<p>{#nn}{#Safemode}{.}{:else}Safemode is off.{/Safemode}{/nn}</p>
|
||||
|
||||
<p>
|
||||
|
@ -207,7 +207,7 @@
|
|||
</thead>
|
||||
{#nn.LiveNodes}
|
||||
<tr>
|
||||
<td><a class="browse-dir-links" info-http-addr="{infoAddr}" info-https-addr="{infoSecureAddr}">{name}</a> ({xferaddr})</td>
|
||||
<td>{name} ({xferaddr})</td>
|
||||
<td>{lastContact}</td>
|
||||
<td>{adminState}</td>
|
||||
<td>{capacity|fmt_bytes}</td>
|
||||
|
|
|
@ -19,19 +19,6 @@
|
|||
"use strict";
|
||||
|
||||
var data = {};
|
||||
function generate_browse_dn_link(info_http_addr, info_https_addr) {
|
||||
var is_https = window.location.protocol === 'https:';
|
||||
var authority = is_https ? info_https_addr : info_http_addr;
|
||||
|
||||
var nn_info_port = window.location.port;
|
||||
if (nn_info_port === "") {
|
||||
nn_info_port = is_https ? 443 : 80;
|
||||
}
|
||||
|
||||
var l = '//' + authority + '/browseDirectory.jsp?dir=%2F&namenodeInfoPort=' +
|
||||
nn_info_port + '&nnaddr=' + data.nnstat.HostAndPort;
|
||||
return l;
|
||||
}
|
||||
|
||||
function render() {
|
||||
var helpers = {
|
||||
|
@ -56,24 +43,7 @@
|
|||
|
||||
load_templates(dust, TEMPLATES, function() {
|
||||
dust.render('dfshealth', base.push(data), function(err, out) {
|
||||
|
||||
$('#panel').append(out);
|
||||
|
||||
$('#browse-dir-first').click(function () {
|
||||
var len = data.nn.LiveNodes.length;
|
||||
if (len < 1) {
|
||||
show_err_msg('Cannot browse the DFS since there are no live nodes available.');
|
||||
return false;
|
||||
}
|
||||
|
||||
var dn = data.nn.LiveNodes[Math.floor(Math.random() * len)];
|
||||
window.location.href = generate_browse_dn_link(dn.infoAddr, dn.infoSecureAddr);
|
||||
});
|
||||
|
||||
$('.browse-dir-links').click(function () {
|
||||
var http_addr = $(this).attr('info-http-addr'), https_addr = $(this).attr('info-https-addr');
|
||||
window.location.href = generate_browse_dn_link(http_addr, https_addr);
|
||||
});
|
||||
$('#panel').html(out);
|
||||
});
|
||||
}, function () {
|
||||
show_err_msg('Failed to load the page.');
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
{#block}
|
||||
<p>Block ID: {blockId}</p>
|
||||
<p>Block Pool ID: {blockPoolId}</p>
|
||||
<p>Generation Stamp: {generationStamp}</p>
|
||||
<p>Size: {numBytes}</p>
|
||||
{/block}
|
||||
<p>Availability:
|
||||
<ul>
|
||||
{#locations}
|
||||
<li>{hostName}</li>
|
||||
{/locations}
|
||||
</ul>
|
||||
</p>
|
|
@ -0,0 +1,26 @@
|
|||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Permission</th>
|
||||
<th>Owner</th>
|
||||
<th>Group</th>
|
||||
<th>Size</th>
|
||||
<th>Replication</th>
|
||||
<th>Block Size</th>
|
||||
<th>Name</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#FileStatus}
|
||||
<tr>
|
||||
<td>{#helper_to_permission/}</td>
|
||||
<td>{owner}</td>
|
||||
<td>{group}</td>
|
||||
<td>{length|fmt_bytes}</td>
|
||||
<td>{replication}</td>
|
||||
<td>{blockSize|fmt_bytes}</td>
|
||||
<td><a style="cursor:pointer" inode-type="{type}" class="explorer-browse-links" inode-path="{pathSuffix}">{pathSuffix}</a></td>
|
||||
</tr>
|
||||
{/FileStatus}
|
||||
</tbody>
|
||||
</table>
|
|
@ -0,0 +1,86 @@
|
|||
<!--
|
||||
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.
|
||||
-->
|
||||
<!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>
|
||||
<link rel="stylesheet" type="text/css" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/static/hadoop.css" />
|
||||
<title>Browsing HDFS</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="modal" id="file-info" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header"><button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title" id="file-info-title">File information</h4>
|
||||
</div>
|
||||
<div class="modal-body" id="file-info-body">
|
||||
<a id="file-info-download">Download</a>
|
||||
<a id="file-info-preview" style="cursor:pointer">Tail the file (last 32K)</a>
|
||||
<hr />
|
||||
<div class="panel panel-info" id="file-info-blockinfo-panel">
|
||||
<div class="panel-heading">
|
||||
Block information --
|
||||
<select class="btn btn-default" id="file-info-blockinfo-list">
|
||||
</select>
|
||||
</div>
|
||||
<div class="panel-body" id="file-info-blockinfo-body"></div>
|
||||
</div>
|
||||
<div class="panel panel-info" id="file-info-tail" style="display:none">
|
||||
<div class="panel-heading">File contents</div>
|
||||
<div class="panel-body">
|
||||
<div class="input-group-sm">
|
||||
<textarea class="form-control" style="height: 150px" id="file-info-preview-body"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer"><button type="button" class="btn btn-primary"
|
||||
data-dismiss="modal">Close</button></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="page-header">
|
||||
<h1>Browse Directory</h1>
|
||||
</div>
|
||||
<div class="alert alert-danger" id="alert-panel" style="display:none">
|
||||
<button type="button" class="close" onclick="$('#alert-panel').hide();">×</button>
|
||||
<div class="alert-body" id="alert-panel-body"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<form onsubmit="return false;">
|
||||
<div class="input-group"><input type="text" class="form-control" id=
|
||||
"directory" /> <span class="input-group-btn"><button class="btn btn-default"
|
||||
type="submit" id="btn-nav-directory"><span class="input-group-btn">Go!</span></button></span></div>
|
||||
</form>
|
||||
</div>
|
||||
<br />
|
||||
<div id="panel"></div>
|
||||
</div>
|
||||
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js">
|
||||
</script><script type="text/javascript" src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.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="dfs-dust.js">
|
||||
</script><script type="text/javascript" src="explorer.js">
|
||||
</script>
|
||||
<hr />
|
||||
<p><a href="http://hadoop.apache.org/core">Hadoop</a>, 2013.</p>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,182 @@
|
|||
/**
|
||||
* 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";
|
||||
|
||||
// The chunk size of tailing the files, i.e., how many bytes will be shown
|
||||
// in the preview.
|
||||
var TAIL_CHUNK_SIZE = 32768;
|
||||
var helpers = {
|
||||
'helper_to_permission': function(chunk, ctx, bodies, params) {
|
||||
var p = ctx.current().permission;
|
||||
var dir = ctx.current().type == 'DIRECTORY' ? 'd' : '-';
|
||||
var symbols = [ '---', '--x', '-w-', '-wx', 'r--', 'r-x', 'rw-', 'rwx' ];
|
||||
var sticky = p > 1000;
|
||||
|
||||
var res = "";
|
||||
for (var i = 0; i < 3; ++i) {
|
||||
res = symbols[(p % 10)] + res;
|
||||
p = Math.floor(p / 10);
|
||||
}
|
||||
|
||||
if (sticky) {
|
||||
var exec = ((parms.perm % 10) & 1) == 1;
|
||||
res[res.length - 1] = exec ? 't' : 'T';
|
||||
}
|
||||
|
||||
chunk.write(dir + res);
|
||||
return chunk;
|
||||
}
|
||||
};
|
||||
|
||||
var base = dust.makeBase(helpers);
|
||||
var current_directory = "";
|
||||
|
||||
function show_err_msg(msg) {
|
||||
$('#alert-panel-body').html(msg);
|
||||
$('#alert-panel').show();
|
||||
}
|
||||
|
||||
function network_error_handler(url) {
|
||||
return function (jqxhr, text, err) {
|
||||
var msg = '<p>Failed to retreive data from ' + url + ', cause: ' + err + '</p>';
|
||||
if (url.indexOf('/webhdfs/v1') === 0) {
|
||||
msg += '<p>WebHDFS might be disabled. WebHDFS is required to browse the filesystem.</p>';
|
||||
}
|
||||
show_err_msg(msg);
|
||||
};
|
||||
}
|
||||
|
||||
function append_path(prefix, s) {
|
||||
var l = prefix.length;
|
||||
var p = l > 0 && prefix[l - 1] == '/' ? prefix.substring(0, l - 1) : prefix;
|
||||
return p + '/' + s;
|
||||
}
|
||||
|
||||
function get_response(data, type) {
|
||||
return data[type] !== undefined ? data[type] : null;
|
||||
}
|
||||
|
||||
function get_response_err_msg(data) {
|
||||
var msg = data.RemoteException !== undefined ? data.RemoteException.message : "";
|
||||
return msg;
|
||||
}
|
||||
|
||||
function view_file_details(path, abs_path) {
|
||||
function show_block_info(blocks) {
|
||||
var menus = $('#file-info-blockinfo-list');
|
||||
menus.empty();
|
||||
|
||||
menus.data("blocks", blocks);
|
||||
menus.change(function() {
|
||||
var d = $(this).data('blocks')[$(this).val()];
|
||||
if (d === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
dust.render('block-info', d, function(err, out) {
|
||||
$('#file-info-blockinfo-body').html(out);
|
||||
});
|
||||
|
||||
});
|
||||
for (var i = 0; i < blocks.length; ++i) {
|
||||
var item = $('<option value="' + i + '">Block ' + i + '</option>');
|
||||
menus.append(item);
|
||||
}
|
||||
menus.change();
|
||||
}
|
||||
|
||||
var url = '/webhdfs/v1' + abs_path + '?op=GET_BLOCK_LOCATIONS';
|
||||
$.ajax({"url": url, "crossDomain": true}).done(function(data) {
|
||||
var d = get_response(data, "LocatedBlocks");
|
||||
if (d === null) {
|
||||
show_err_msg(get_response_err_msg(data));
|
||||
return;
|
||||
}
|
||||
|
||||
$('#file-info-tail').hide();
|
||||
$('#file-info-title').text("File information - " + path);
|
||||
|
||||
var download_url = '/webhdfs/v1' + abs_path + '/?op=OPEN';
|
||||
|
||||
$('#file-info-download').attr('href', download_url);
|
||||
$('#file-info-preview').click(function() {
|
||||
var offset = d.fileLength - TAIL_CHUNK_SIZE;
|
||||
var url = offset > 0 ? download_url + '&offset=' + offset : download_url;
|
||||
$.get(url, function(t) {
|
||||
$('#file-info-preview-body').val(t);
|
||||
$('#file-info-tail').show();
|
||||
}, "text").error(network_error_handler(url));
|
||||
});
|
||||
|
||||
if (d.fileLength > 0) {
|
||||
show_block_info(d.locatedBlocks);
|
||||
$('#file-info-blockinfo-panel').show();
|
||||
} else {
|
||||
$('#file-info-blockinfo-panel').hide();
|
||||
}
|
||||
$('#file-info').modal();
|
||||
}).error(network_error_handler(url));
|
||||
}
|
||||
|
||||
function browse_directory(dir) {
|
||||
var url = '/webhdfs/v1' + dir + '?op=LISTSTATUS';
|
||||
$.get(url, function(data) {
|
||||
var d = get_response(data, "FileStatuses");
|
||||
if (d === null) {
|
||||
show_err_msg(get_response_err_msg(data));
|
||||
return;
|
||||
}
|
||||
|
||||
current_directory = dir;
|
||||
$('#directory').val(dir);
|
||||
dust.render('explorer', base.push(d), function(err, out) {
|
||||
$('#panel').html(out);
|
||||
|
||||
$('.explorer-browse-links').click(function() {
|
||||
var type = $(this).attr('inode-type');
|
||||
var path = $(this).attr('inode-path');
|
||||
var abs_path = append_path(current_directory, path);
|
||||
if (type == 'DIRECTORY') {
|
||||
browse_directory(abs_path);
|
||||
} else {
|
||||
view_file_details(path, abs_path);
|
||||
}
|
||||
});
|
||||
});
|
||||
}).error(network_error_handler(url));
|
||||
}
|
||||
|
||||
|
||||
function init() {
|
||||
var templates = [
|
||||
{ 'name': 'explorer', 'url': 'explorer.dust.html'},
|
||||
{ 'name': 'block-info', 'url': 'explorer-block-info.dust.html'}
|
||||
];
|
||||
|
||||
load_templates(dust, templates, function () {
|
||||
var b = function() { browse_directory($('#directory').val()); };
|
||||
$('#btn-nav-directory').click(b);
|
||||
browse_directory('/');
|
||||
}, function (url, jqxhr, text, err) {
|
||||
network_error_handler(url)(jqxhr, text, err);
|
||||
});
|
||||
}
|
||||
|
||||
init();
|
||||
})();
|
Loading…
Reference in New Issue