HDFS-15531. Namenode UI: List snapshots in separate table for each snapshottable directory (#2230)
This commit is contained in:
parent
d1c60a53f6
commit
06793da100
@ -250,13 +250,15 @@
|
||||
|
||||
<script type="text/x-dust-template" id="tmpl-snapshot">
|
||||
<div class="page-header"><h1>Snapshot Summary</h1></div>
|
||||
<div class="page-header"><h1><small>Snapshottable directories: {@size key=SnapshottableDirectories}{/size}</small></div>
|
||||
<div class="snapshot-stats"><h2><small>Snapshottable Directories: {@size key=SnapshottableDirectories}{/size}</small></div>
|
||||
<div class="snapshot-stats"><h2><small>Total Snapshots: {@size key=Snapshots}{/size}</small></div>
|
||||
<small>
|
||||
<table class="table">
|
||||
<table class="table" id="table-snapshots">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Path</th>
|
||||
<th>Snapshot Number</th>
|
||||
<th>Snapshots Count</th>
|
||||
<th>Snapshot Quota</th>
|
||||
<th>Modification Time</th>
|
||||
<th>Permission</th>
|
||||
@ -264,42 +266,23 @@
|
||||
<th>Group</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#SnapshottableDirectories}
|
||||
<tr>
|
||||
<td>{path}</td>
|
||||
<td>{snapshotNumber}</td>
|
||||
<td>{snapshotQuota}</td>
|
||||
<td>{modificationTime|date_tostring}</td>
|
||||
<td class="details-control"></td>
|
||||
<td ng-value="{path}">{path}</td>
|
||||
<td ng-value="{snapshotNumber}">{snapshotNumber}</td>
|
||||
<td ng-value="{snapshotQuota}">{snapshotQuota}</td>
|
||||
<td ng-value="{modificationTime}">{modificationTime|date_tostring}</td>
|
||||
<td>{permission|helper_to_permission}</td>
|
||||
<td>{owner}</td>
|
||||
<td>{group}</td>
|
||||
<td ng-value="{owner}">{owner}</td>
|
||||
<td ng-value="{group}">{group}</td>
|
||||
</tr>
|
||||
{/SnapshottableDirectories}
|
||||
</tbody>
|
||||
</table>
|
||||
</small>
|
||||
|
||||
<div class="page-header"><h1><small>Snapshots: {@size key=Snapshots}{/size}</small></div>
|
||||
|
||||
<small>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Snapshot ID</th>
|
||||
<th>Snapshot Directory</th>
|
||||
<th>Modification Time</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{#Snapshots}
|
||||
<tr>
|
||||
<td>{snapshotID}</td>
|
||||
<td>{snapshotDirectory}</td>
|
||||
<td>{modificationTime|date_tostring}</td>
|
||||
<td>{status}</td>
|
||||
</tr>
|
||||
{/Snapshots}
|
||||
</table>
|
||||
</small>
|
||||
</script>
|
||||
|
||||
<script type="text/x-dust-template" id="tmpl-datanode">
|
||||
|
@ -429,10 +429,127 @@
|
||||
dust.render('snapshot-info', resp.beans[0], function(err, out) {
|
||||
$('#tab-snapshot').html(out);
|
||||
$('#ui-tabs a[href="#tab-snapshot"]').tab('show');
|
||||
|
||||
// Build a map to store snapshottable directory -> snapshots
|
||||
var snapshots = 'Snapshots' in resp.beans[0] ? resp.beans[0].Snapshots : [];
|
||||
var snapshotsMap = snapshots.reduce(function(result, snapshot) {
|
||||
var rootPath = snapshot.snapshotDirectory.substr(0, snapshot.snapshotDirectory.indexOf(".snapshot") -1 );
|
||||
if (rootPath in result) {
|
||||
var arr = result[rootPath];
|
||||
arr.push(snapshot);
|
||||
result[rootPath] = arr;
|
||||
} else {
|
||||
result[rootPath] = [snapshot];
|
||||
}
|
||||
return result;
|
||||
}, {});
|
||||
|
||||
var table = $('#table-snapshots').DataTable( {
|
||||
'lengthMenu': [ [25, 50, 100, -1], [25, 50, 100, "All"] ],
|
||||
'columns': [
|
||||
{ 'orderable': false, 'searchable': false, 'data': null, 'defaultContent': "" },
|
||||
{ 'data': 'path', 'orderDataType': 'ng-value', 'searchable': true , 'type': 'string', 'defaultContent': "" },
|
||||
{ 'data': 'snapshotNumber', 'orderDataType': 'ng-value', 'searchable': false , 'type': 'num', 'defaultContent': 0 },
|
||||
{ 'data': 'snapshotQuota', 'orderDataType': 'ng-value', 'searchable': false , 'type': 'num', 'defaultContent': 0 },
|
||||
{ 'data': 'modificationTime', 'orderDataType': 'ng-value', 'searchable': false , 'type': 'string', 'defaultContent': "" },
|
||||
{ 'data': 'permission', 'orderable': false, 'searchable': false , 'type': 'string', 'defaultContent': "" },
|
||||
{ 'data': 'owner', 'orderDataType': 'ng-value', 'searchable': true , 'type': 'string', 'defaultContent': "" },
|
||||
{ 'data': 'group', 'orderDataType': 'ng-value', 'searchable': true , 'type': 'string', 'defaultContent': "" }
|
||||
],
|
||||
'order': [[ 1, 'asc' ]]
|
||||
});
|
||||
// Add event listener for opening and closing details
|
||||
$('#table-snapshots tbody').on('click', 'td.details-control', function () {
|
||||
var tr = $(this).closest('tr');
|
||||
var row = table.row( tr );
|
||||
|
||||
if ( row.child.isShown() ) {
|
||||
// This row is already open - close it
|
||||
row.child.hide();
|
||||
tr.removeClass('shown');
|
||||
}
|
||||
else {
|
||||
// Open this row
|
||||
row.child( formatExpandedRow(row.data(), snapshotsMap) ).show();
|
||||
var tableId = getSubTableId(row.data());
|
||||
if (!$.fn.dataTable.isDataTable('#'+tableId)) {
|
||||
$('#' + tableId).DataTable({
|
||||
'lengthMenu': [[25, 50, 100, -1], [25, 50, 100, "All"]],
|
||||
'columns': [
|
||||
{
|
||||
'orderDataType': 'ng-value',
|
||||
'searchable': true,
|
||||
'type': 'num',
|
||||
'defaultContent': 0
|
||||
},
|
||||
{
|
||||
'orderDataType': 'ng-value',
|
||||
'searchable': true,
|
||||
'type': 'string',
|
||||
'defaultContent': ""
|
||||
},
|
||||
{
|
||||
'orderDataType': 'ng-value',
|
||||
'searchable': true,
|
||||
'type': 'string',
|
||||
'defaultContent': ""
|
||||
},
|
||||
{
|
||||
'orderDataType': 'ng-value',
|
||||
'searchable': true,
|
||||
'type': 'string',
|
||||
'defaultContent': ""
|
||||
}
|
||||
],
|
||||
'order': [[0, 'asc']]
|
||||
});
|
||||
}
|
||||
tr.addClass('shown');
|
||||
}
|
||||
});
|
||||
});
|
||||
})).fail(ajax_error_handler);
|
||||
}
|
||||
|
||||
function getSubTableId(row) {
|
||||
var path = row.path;
|
||||
// replace all "/" with "-"
|
||||
path = path.replace(/\//g, '-');
|
||||
return "table-snapshots"+path;
|
||||
}
|
||||
|
||||
function formatExpandedRow (row, snapshotsMap) {
|
||||
// `row` is the original data object for the row
|
||||
var tableId = getSubTableId(row);
|
||||
var path = row.path;
|
||||
var snapshots = snapshotsMap[path];
|
||||
if (!snapshots || snapshots.length === 0) {
|
||||
return 'No snapshots found for this path';
|
||||
}
|
||||
var tbody = snapshots.reduce(function(result, snapshot) {
|
||||
var html = '<tr>'+
|
||||
'<td ng-value="'+snapshot.snapshotID+'">'+ snapshot.snapshotID +'</td>'+
|
||||
'<td ng-value="'+snapshot.snapshotDirectory+'">'+ snapshot.snapshotDirectory +'</td>'+
|
||||
'<td ng-value="'+snapshot.modificationTime+'">'+ moment(Number(snapshot.modificationTime)).format('ddd MMM DD HH:mm:ss ZZ YYYY') +'</td>'+
|
||||
'<td ng-value="'+snapshot.status+'">'+ snapshot.status +'</td>'+
|
||||
'</tr>';
|
||||
return result + html;
|
||||
}, "");
|
||||
return '<table class="table sub-table" id='+ tableId +'>'+
|
||||
'<thead>'+
|
||||
'<tr>'+
|
||||
'<th>Snapshot ID</th>'+
|
||||
'<th>Snapshot Directory</th>'+
|
||||
'<th>Modification Time</th>' +
|
||||
'<th>Status</th>' +
|
||||
'</tr>'+
|
||||
'</thead>'+
|
||||
'<tbody>'+
|
||||
tbody +
|
||||
'</tbody>'+
|
||||
'</table>';
|
||||
}
|
||||
|
||||
function load_page() {
|
||||
var hash = window.location.hash;
|
||||
switch(hash) {
|
||||
|
@ -370,3 +370,34 @@ header.bs-docs-nav, header.bs-docs-nav .navbar-brand {
|
||||
fill: #fff;
|
||||
font: 10px sans-serif;
|
||||
}
|
||||
|
||||
td.details-control:before {
|
||||
color: #5fa341;
|
||||
content: "\2b";
|
||||
cursor: pointer;
|
||||
font-size: 1.5em;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
tr.shown td.details-control:before {
|
||||
color: #c7254e;
|
||||
content: "\2212";
|
||||
cursor: pointer;
|
||||
font-size: 1.5em;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
#table-snapshots_wrapper {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
table#table-snapshots>tbody>tr>td>.dataTables_wrapper {
|
||||
padding-left: 50px;
|
||||
padding-right: 10px;
|
||||
padding-top: 10px;
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
.snapshot-stats>h2 {
|
||||
margin: 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user