Skip to content

Commit 1257cb1

Browse files
vivekratnavelShashikant Banerjee
authored andcommitted
HDFS-15531. Namenode UI: List snapshots in separate table for each snapshottable directory (apache#2230)
(cherry picked from commit 06793da) Change-Id: I34bfd68bf9ce482ae69310dffcc0dd5cc4fd4899 (cherry picked from commit 8fafc5d)
1 parent 00a22c5 commit 1257cb1

File tree

3 files changed

+167
-36
lines changed

3 files changed

+167
-36
lines changed

hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/hdfs/dfshealth.html

Lines changed: 18 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -243,56 +243,39 @@
243243

244244
<script type="text/x-dust-template" id="tmpl-snapshot">
245245
<div class="page-header"><h1>Snapshot Summary</h1></div>
246-
<div class="page-header"><h1><small>Snapshottable directories: {@size key=SnapshottableDirectories}{/size}</small></div>
246+
<div class="snapshot-stats"><h2><small>Snapshottable Directories: {@size key=SnapshottableDirectories}{/size}</small></div>
247+
<div class="snapshot-stats"><h2><small>Total Snapshots: {@size key=Snapshots}{/size}</small></div>
247248
<small>
248-
<table class="table">
249+
<table class="table" id="table-snapshots">
249250
<thead>
250251
<tr>
252+
<th></th>
251253
<th>Path</th>
252-
<th>Snapshot Number</th>
254+
<th>Snapshots Count</th>
253255
<th>Snapshot Quota</th>
254256
<th>Modification Time</th>
255257
<th>Permission</th>
256258
<th>Owner</th>
257259
<th>Group</th>
258260
</tr>
259261
</thead>
260-
{#SnapshottableDirectories}
261-
<tr>
262-
<td>{path}</td>
263-
<td>{snapshotNumber}</td>
264-
<td>{snapshotQuota}</td>
265-
<td>{modificationTime|date_tostring}</td>
266-
<td>{permission|helper_to_permission}</td>
267-
<td>{owner}</td>
268-
<td>{group}</td>
269-
</tr>
270-
{/SnapshottableDirectories}
271-
</table>
272-
</small>
273-
274-
<div class="page-header"><h1><small>Snapshots: {@size key=Snapshots}{/size}</small></div>
275-
276-
<small>
277-
<table class="table">
278-
<thead>
262+
<tbody>
263+
{#SnapshottableDirectories}
279264
<tr>
280-
<th>Snapshot ID</th>
281-
<th>Snapshot Directory</th>
282-
<th>Modification Time</th>
283-
<th>Status</th>
265+
<td class="details-control"></td>
266+
<td ng-value="{path}">{path}</td>
267+
<td ng-value="{snapshotNumber}">{snapshotNumber}</td>
268+
<td ng-value="{snapshotQuota}">{snapshotQuota}</td>
269+
<td ng-value="{modificationTime}">{modificationTime|date_tostring}</td>
270+
<td>{permission|helper_to_permission}</td>
271+
<td ng-value="{owner}">{owner}</td>
272+
<td ng-value="{group}">{group}</td>
284273
</tr>
285-
</thead>
286-
{#Snapshots}
287-
<tr>
288-
<td>{snapshotID}</td>
289-
<td>{snapshotDirectory}</td>
290-
<td>{modificationTime|date_tostring}</td>
291-
<td>{status}</td>
292-
</tr>
293-
{/Snapshots}
274+
{/SnapshottableDirectories}
275+
</tbody>
294276
</table>
295277
</small>
278+
296279
</script>
297280

298281
<script type="text/x-dust-template" id="tmpl-datanode">

hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/hdfs/dfshealth.js

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,10 +392,127 @@
392392
dust.render('snapshot-info', resp.beans[0], function(err, out) {
393393
$('#tab-snapshot').html(out);
394394
$('#ui-tabs a[href="#tab-snapshot"]').tab('show');
395+
396+
// Build a map to store snapshottable directory -> snapshots
397+
var snapshots = 'Snapshots' in resp.beans[0] ? resp.beans[0].Snapshots : [];
398+
var snapshotsMap = snapshots.reduce(function(result, snapshot) {
399+
var rootPath = snapshot.snapshotDirectory.substr(0, snapshot.snapshotDirectory.indexOf(".snapshot") -1 );
400+
if (rootPath in result) {
401+
var arr = result[rootPath];
402+
arr.push(snapshot);
403+
result[rootPath] = arr;
404+
} else {
405+
result[rootPath] = [snapshot];
406+
}
407+
return result;
408+
}, {});
409+
410+
var table = $('#table-snapshots').DataTable( {
411+
'lengthMenu': [ [25, 50, 100, -1], [25, 50, 100, "All"] ],
412+
'columns': [
413+
{ 'orderable': false, 'searchable': false, 'data': null, 'defaultContent': "" },
414+
{ 'data': 'path', 'orderDataType': 'ng-value', 'searchable': true , 'type': 'string', 'defaultContent': "" },
415+
{ 'data': 'snapshotNumber', 'orderDataType': 'ng-value', 'searchable': false , 'type': 'num', 'defaultContent': 0 },
416+
{ 'data': 'snapshotQuota', 'orderDataType': 'ng-value', 'searchable': false , 'type': 'num', 'defaultContent': 0 },
417+
{ 'data': 'modificationTime', 'orderDataType': 'ng-value', 'searchable': false , 'type': 'string', 'defaultContent': "" },
418+
{ 'data': 'permission', 'orderable': false, 'searchable': false , 'type': 'string', 'defaultContent': "" },
419+
{ 'data': 'owner', 'orderDataType': 'ng-value', 'searchable': true , 'type': 'string', 'defaultContent': "" },
420+
{ 'data': 'group', 'orderDataType': 'ng-value', 'searchable': true , 'type': 'string', 'defaultContent': "" }
421+
],
422+
'order': [[ 1, 'asc' ]]
423+
});
424+
// Add event listener for opening and closing details
425+
$('#table-snapshots tbody').on('click', 'td.details-control', function () {
426+
var tr = $(this).closest('tr');
427+
var row = table.row( tr );
428+
429+
if ( row.child.isShown() ) {
430+
// This row is already open - close it
431+
row.child.hide();
432+
tr.removeClass('shown');
433+
}
434+
else {
435+
// Open this row
436+
row.child( formatExpandedRow(row.data(), snapshotsMap) ).show();
437+
var tableId = getSubTableId(row.data());
438+
if (!$.fn.dataTable.isDataTable('#'+tableId)) {
439+
$('#' + tableId).DataTable({
440+
'lengthMenu': [[25, 50, 100, -1], [25, 50, 100, "All"]],
441+
'columns': [
442+
{
443+
'orderDataType': 'ng-value',
444+
'searchable': true,
445+
'type': 'num',
446+
'defaultContent': 0
447+
},
448+
{
449+
'orderDataType': 'ng-value',
450+
'searchable': true,
451+
'type': 'string',
452+
'defaultContent': ""
453+
},
454+
{
455+
'orderDataType': 'ng-value',
456+
'searchable': true,
457+
'type': 'string',
458+
'defaultContent': ""
459+
},
460+
{
461+
'orderDataType': 'ng-value',
462+
'searchable': true,
463+
'type': 'string',
464+
'defaultContent': ""
465+
}
466+
],
467+
'order': [[0, 'asc']]
468+
});
469+
}
470+
tr.addClass('shown');
471+
}
472+
});
395473
});
396474
})).fail(ajax_error_handler);
397475
}
398476

477+
function getSubTableId(row) {
478+
var path = row.path;
479+
// replace all "/" with "-"
480+
path = path.replace(/\//g, '-');
481+
return "table-snapshots"+path;
482+
}
483+
484+
function formatExpandedRow (row, snapshotsMap) {
485+
// `row` is the original data object for the row
486+
var tableId = getSubTableId(row);
487+
var path = row.path;
488+
var snapshots = snapshotsMap[path];
489+
if (!snapshots || snapshots.length === 0) {
490+
return 'No snapshots found for this path';
491+
}
492+
var tbody = snapshots.reduce(function(result, snapshot) {
493+
var html = '<tr>'+
494+
'<td ng-value="'+snapshot.snapshotID+'">'+ snapshot.snapshotID +'</td>'+
495+
'<td ng-value="'+snapshot.snapshotDirectory+'">'+ snapshot.snapshotDirectory +'</td>'+
496+
'<td ng-value="'+snapshot.modificationTime+'">'+ moment(Number(snapshot.modificationTime)).format('ddd MMM DD HH:mm:ss ZZ YYYY') +'</td>'+
497+
'<td ng-value="'+snapshot.status+'">'+ snapshot.status +'</td>'+
498+
'</tr>';
499+
return result + html;
500+
}, "");
501+
return '<table class="table sub-table" id='+ tableId +'>'+
502+
'<thead>'+
503+
'<tr>'+
504+
'<th>Snapshot ID</th>'+
505+
'<th>Snapshot Directory</th>'+
506+
'<th>Modification Time</th>' +
507+
'<th>Status</th>' +
508+
'</tr>'+
509+
'</thead>'+
510+
'<tbody>'+
511+
tbody +
512+
'</tbody>'+
513+
'</table>';
514+
}
515+
399516
function load_page() {
400517
var hash = window.location.hash;
401518
switch(hash) {

hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/static/hadoop.css

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,4 +321,35 @@ header.bs-docs-nav, header.bs-docs-nav .navbar-brand {
321321
.bar text {
322322
fill: #fff;
323323
font: 10px sans-serif;
324-
}
324+
}
325+
326+
td.details-control:before {
327+
color: #5fa341;
328+
content: "\2b";
329+
cursor: pointer;
330+
font-size: 1.5em;
331+
line-height: 1em;
332+
}
333+
334+
tr.shown td.details-control:before {
335+
color: #c7254e;
336+
content: "\2212";
337+
cursor: pointer;
338+
font-size: 1.5em;
339+
line-height: 1em;
340+
}
341+
342+
#table-snapshots_wrapper {
343+
margin-top: 20px;
344+
}
345+
346+
table#table-snapshots>tbody>tr>td>.dataTables_wrapper {
347+
padding-left: 50px;
348+
padding-right: 10px;
349+
padding-top: 10px;
350+
background-color: #ddd;
351+
}
352+
353+
.snapshot-stats>h2 {
354+
margin: 0;
355+
}

0 commit comments

Comments
 (0)