[prev in list] [next in list] [prev in thread] [next in thread] 

List:       hadoop-commits
Subject:    [hadoop] branch trunk updated: HDFS-15531. Namenode UI: List snapshots in separate table for each sn
From:       vivekratnavel () apache ! org
Date:       2020-08-27 18:36:59
Message-ID: 159855341915.19028.2955070039088127876 () gitbox ! apache ! org
[Download RAW message or body]

This is an automated email from the ASF dual-hosted git repository.

vivekratnavel pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/hadoop.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 06793da  HDFS-15531. Namenode UI: List snapshots in separate table for each \
snapshottable directory (#2230) 06793da is described below

commit 06793da100123bda67afcda95d6f2221efd9c281
Author: Vivek Ratnavel Subramanian <vivekratnavel90@gmail.com>
AuthorDate: Thu Aug 27 11:36:30 2020 -0700

    HDFS-15531. Namenode UI: List snapshots in separate table for each snapshottable \
                directory (#2230)
---
 .../src/main/webapps/hdfs/dfshealth.html           |  53 ++++------
 .../hadoop-hdfs/src/main/webapps/hdfs/dfshealth.js | 117 +++++++++++++++++++++
 .../hadoop-hdfs/src/main/webapps/static/hadoop.css |  33 +++++-
 3 files changed, 167 insertions(+), 36 deletions(-)

diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/hdfs/dfshealth.html \
b/hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/hdfs/dfshealth.html index \
                8b03185..27616fb 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/hdfs/dfshealth.html
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/hdfs/dfshealth.html
@@ -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>
-  {#SnapshottableDirectories}
-  <tr>
-    <td>{path}</td>
-    <td>{snapshotNumber}</td>
-    <td>{snapshotQuota}</td>
-    <td>{modificationTime|date_tostring}</td>
-    <td>{permission|helper_to_permission}</td>
-    <td>{owner}</td>
-    <td>{group}</td>
-  </tr>
-  {/SnapshottableDirectories}
-</table>
-</small>
-
-<div class="page-header"><h1><small>Snapshots: {@size \
                key=Snapshots}{/size}</small></div>
-
-<small>
-<table class="table">
-  <thead>
+  <tbody>
+    {#SnapshottableDirectories}
     <tr>
-      <th>Snapshot ID</th>
-      <th>Snapshot Directory</th>
-      <th>Modification Time</th>
-      <th>Status</th>
+      <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 ng-value="{owner}">{owner}</td>
+      <td ng-value="{group}">{group}</td>
     </tr>
-  </thead>
-  {#Snapshots}
-  <tr>
-    <td>{snapshotID}</td>
-    <td>{snapshotDirectory}</td>
-    <td>{modificationTime|date_tostring}</td>
-    <td>{status}</td>
-  </tr>
-  {/Snapshots}
+    {/SnapshottableDirectories}
+  </tbody>
 </table>
 </small>
+
 </script>
 
 <script type="text/x-dust-template" id="tmpl-datanode">
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/hdfs/dfshealth.js \
b/hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/hdfs/dfshealth.js index \
                9715c37..9be19fe 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/hdfs/dfshealth.js
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/hdfs/dfshealth.js
@@ -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) {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/static/hadoop.css \
b/hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/static/hadoop.css index \
                1f0d7a8..1245ebc 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/static/hadoop.css
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/static/hadoop.css
@@ -369,4 +369,35 @@ header.bs-docs-nav, header.bs-docs-nav .navbar-brand {
 .bar text {
     fill: #fff;
     font: 10px sans-serif;
-}
\ No newline at end of file
+}
+
+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;
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: common-commits-unsubscribe@hadoop.apache.org
For additional commands, e-mail: common-commits-help@hadoop.apache.org


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic