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

amansinha pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/drill.git

commit 11036c556f6445bcf1418b6512ba71b44a0a6031
Author: Kunal Khatua <ku...@apache.org>
AuthorDate: Fri Apr 27 16:27:45 2018 -0700

    DRILL-6364: Handle Cluster Info in WebUI when existing/new bits restart
    
    As a follow up to DRILL-6289, the following improvements have been done:
    1. When loading the page for the first time, the WebUI enables the shutdown 
button without actually checking the state of the Drillbits.
       The ideal behaviour should be to disable the button till the state is 
verified. [Done]
       If a Drillbit is confirmed down (i.e. not in `/state` response), it is 
marked as OFFLINE and button is disabled.
    2. When shutting down the current Drillbit, the WebUI no more has access to 
the cluster state.
       The ideal behaviour here should be to fetch the state from any of the 
other Drillbits to update the status. [Done]
       With the current Drillbit down, the other bits are requested for cluster 
state info and update accordingly.
    3. When a new, previously unseen Drillbit comes up, the WebUI will never 
render it because the table is statically generated during the first page load.
       The idea behaviour should be to append to the table on discovery of a 
new node. [Done]
       The new Drillbit info is injected and a prompt appears to refresh the 
page to re-populate any missing info. This also works with feature (2) 
mentioned above.
    
    The only Java code change was to have the state response carry the address 
and http-port as a tuple, instead of the user-port (which is never used).
    
    Updates based on review
    
    1. Added descriptive comments to functions
    2. Handled possible race condition from multiple httpRequests for cluster 
state
    3. Eliminated unused stateKey variable
    4. Best practice of using local (let) instead of global (var) objects, and 
substituting currentRow variable instead of the jQuery search.
    
    Additional changes based on review
    
    1. Random selection of Drillbits to query for state when primary Drillbit 
is down (limited to 3; with a timeout of 3 sec)
    2. Indicate when an Offline Drillbit is de-registered, versus just Offline 
as per Zookeeper
    3. Hide shutdown column when Authentication is enabled, but the user is NOT 
an Admin.
       When the column is visible, remote bits are disabled because
    4. Metrics will be shown all the time (except HTTPS), because the 
information is available and a non-admin user would anyway not have actionable 
capabilities
    5. Basic clean up on unused variables
    
    Hide shutdown buttons for HTTPS scenarios
    
    Fixed check for 'https'
    
    Since, `location.protocol` returns a trailing ':' as well
    
    Handle metrics lookup on local Drillbit for HTTPS
    
    If the webpage is accessed via IP, the certificate is tied to the IP and 
not the FQDN, which is what is used for fetching the metrics.
    
    close apache/drill#1241
---
 .../apache/drill/exec/server/rest/DrillRoot.java   |   3 +-
 exec/java-exec/src/main/resources/rest/index.ftl   | 319 +++++++++++++++------
 2 files changed, 239 insertions(+), 83 deletions(-)

diff --git 
a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRoot.java 
b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRoot.java
index 50d99b0..0a48801 100644
--- 
a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRoot.java
+++ 
b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRoot.java
@@ -88,7 +88,7 @@ public class DrillRoot {
     Collection<DrillbitInfo> drillbits = getClusterInfoJSON().getDrillbits();
     Map<String, String> drillStatusMap = new HashMap<String, String>();
     for (DrillbitInfo drillbit : drillbits) {
-      drillStatusMap.put(drillbit.getAddress() + "-" + drillbit.getUserPort(), 
drillbit.getState());
+      drillStatusMap.put(drillbit.getAddress() + "-" + drillbit.getHttpPort(), 
drillbit.getState());
     }
     return setResponse(drillStatusMap);
   }
@@ -239,6 +239,7 @@ public class DrillRoot {
   private Response shutdown(String resp) throws Exception {
     Map<String, String> shutdownInfo = new HashMap<String, String>();
     new Thread(new Runnable() {
+        @Override
         public void run() {
           try {
             drillbit.close();
diff --git a/exec/java-exec/src/main/resources/rest/index.ftl 
b/exec/java-exec/src/main/resources/rest/index.ftl
index 5259e44..11d1dd2 100644
--- a/exec/java-exec/src/main/resources/rest/index.ftl
+++ b/exec/java-exec/src/main/resources/rest/index.ftl
@@ -54,9 +54,11 @@
 
   <div class="row">
     <div class="col-md-12">
-      <h3>Drillbits <span class="label label-primary" id="size" 
>${model.getDrillbits()?size}</span></h3>
+      <h3><span id="sizeLabel">Drillbits <span class="label label-primary" 
id="size">${model.getDrillbits()?size}</span>
+          <button type="button" class="btn btn-warning" id='reloadBtn' 
style="display:none;font-size:50%" title='New Drillbits detected! Click to 
Refresh' onclick='location.reload();'>
+          <span class="glyphicon glyphicon-refresh"></span></button></h3>
       <div class="table-responsive">
-        <table class="table table-hover">
+        <table class="table table-hover" id="bitTable">
           <thead>
             <tr>
               <th>#</th>
@@ -71,9 +73,7 @@
               <th>Version</th>
               <th>Status</th>
               <th>Uptime</th>
-              <#if (model.shouldShowAdminInfo() || !model.isAuthEnabled()) >
-              <th>Shutdown</th>
-              </#if>
+              <th style="display:${(model.isAuthEnabled() && 
!model.shouldShowAdminInfo())?then("none","table-cell")}">Shutdown</th>
             </tr>
           </thead>
           <tbody>
@@ -103,14 +103,14 @@
                 </td>
                 <td id="status" >${drillbit.getState()}</td>
                 <td class="uptime" >Not Available</td>
-                  <td>
-                <#if ( model.shouldShowAdminInfo() &&  ( drillbit.isCurrent() 
|| ( !model.isAuthEnabled() && location.protocol != "https" ))) >
-                      <button type="button" id="shutdown" 
onClick="shutdown($(this), 
'${drillbit.getAddress()}:${drillbit.getHttpPort()}');">
+                <td>
+                <#if (model.isAuthEnabled() && !model.shouldShowAdminInfo())>
+                      <button class='shutdownCtrl' type="button" id="shutdown" 
disabled="true" style="opacity:0.5;cursor:not-allowed;display:none" onClick="" 
comment="Placeholder. Hidden by default">
                 <#else>
-                      <button type="button" id="shutdown" title="Drillbit 
cannot be shutdown remotely" disabled="true" 
style="opacity:0.5;cursor:not-allowed;">
+                      <button class='shutdownCtrl' type="button" id="shutdown" 
disabled="true" style="opacity:0.5;cursor:not-allowed;display:inline" 
onClick="shutdown($(this));" title="Not available for remote Drillbits">
                 </#if>
                       <span class="glyphicon glyphicon-off"></span></button>
-                  </td>
+                </td>
                 <td id="queriesCount">  </td>
               </tr>
               <#assign i = i + 1>
@@ -217,21 +217,34 @@
       </div>
   </div>
    <script charset="utf-8">
-      var updateRemoteInfo = <#if 
(model.isAuthEnabled())>false<#else>true</#if>;
       var refreshTime = 10000;
+      var nAText = "Not Available";
       var refresh = getRefreshTime();
       var timeout;
       var size = $("#size").html();
       reloadMetrics();
       setInterval(reloadMetrics, refreshTime);
+      var hideShutdownBtns = setShutdownCtrl();
+
+      //Hide Shutdown buttons for remote HTTPS entries
+      function setShutdownCtrl() {
+        for (i = 1; i <= size; i++) {
+          let currentRow = $("#row-"+i);
+          if ( location.protocol == "https:" && 
(currentRow.find("#current").html() != "Current") ) {
+            //Hide Shutdown Button for remote nodes with HTTPS enabled
+            currentRow.find(".shutdownCtrl").css('display','none');
+          }
+        }
+      }
 
+      //Gets a refresh time for graceful shutdown
       function getRefreshTime() {
           $.ajax({
               type: 'GET',
               url: '/gracePeriod',
               dataType: "json",
               complete: function (data) {
-                  var gracePeriod = data.responseJSON["gracePeriod"];
+                  let gracePeriod = data.responseJSON["gracePeriod"];
                   if (gracePeriod > 0) {
                       refreshTime = gracePeriod / 3;
                   }
@@ -240,8 +253,9 @@
           });
       }
 
+      //Periodic reload of status of Drillbits
       function reloadStatus () {
-          var result = $.ajax({
+          let result = $.ajax({
                       type: 'GET',
                       url: '/state',
                       dataType: "json",
@@ -252,37 +266,161 @@
           timeout = setTimeout(reloadStatus, refreshTime);
       }
 
-      function fillStatus(data,size) {
-          var status_map = (data.responseJSON);
-          for (i = 1; i <= size; i++) {
-            var address = 
$("#row-"+i).find("#address").contents().get(0).nodeValue;
-            address = address.trim();
-            var port = $("#row-"+i).find("#port").html();
-            var key = address+"-"+port;
+      //Fill the Status table for all the listed drillbits
+      function fillStatus(dataResponse,size) {
+          let status_map = (dataResponse.responseJSON);
+          //In case localhost has gone down (i.e. we don't know status from ZK)
+          if (typeof status_map == 'undefined') {
+            //Query a couple of other nodes for state details
+            //Pick 3 Random bits
+            let randomAltBitList = getRandomIndexList(size, 3);
+            let numAltBits = randomAltBitList.length;
+            for (j = 0; j < numAltBits; j++) {
+              let currentRow = $("#row-"+ randomAltBitList[j]);
+              if (currentRow.find("#current").html() == "Current") {
+                continue; //Skip LocalHost
+              }
+              let address = 
currentRow.find("#address").contents().get(0).nodeValue.trim();
+              let restPort = 
currentRow.find("#httpPort").contents().get(0).nodeValue.trim();
+              let altStateUrl = location.protocol + "//" + 
address+":"+restPort + "/state";
+              let altResponse = $.ajax({
+               url:altStateUrl, 
+               dataType:'json', 
+               timeout: 3000,
+                    success : function(stateDataJson) {
+                        //Update Status & Buttons for alternate stateData
+                        if (typeof status_map == 'undefined') {
+                          //Note: Race-condition would not exist due to 
JScript's single threaded model, only one thread can update the variable
+                          //Ref: 
https://stackoverflow.com/questions/7238586/do-i-need-to-be-concerned-with-race-conditions-with-asynchronous-javascript
+                          status_map = stateDataJson; //Marking map as 
'defined'
+                          updateStatusAndShutdown(stateDataJson);
+                        }
+                    }
+                });
+              //Don't loop any more (for small #bits, status_map will never 
update fast enough)
+              if (typeof status_map != 'undefined') {
+                break;
+              }
+            }
+          } else {
+            updateStatusAndShutdown(status_map);
+          }
+      }
+
+      //Generates a list of indices
+      function getRandomIndexList(inputSize, outputSize) {
+        //Generate inputList
+        let inputList = [];
+        let outputList = [];
+        //Skip 'row-1'
+        for (p = 2; p <= inputSize; p++) {
+          let currentRow = $("#row-"+ p);
+          if (currentRow.find("#status").text() == 'ONLINE' ) {
+              inputList.push(p);
+          }
+        }
+        //Define how many to pick (pick min if numDBits is too small)
+        let actualOPSize = (inputList.length > outputSize ? outputSize : 
inputList.length);
+        for (q = 0; q < actualOPSize; q++) {
+          let randomIndex = Math.floor(Math.random() * inputList.length);
+          outputList.push(inputList[randomIndex]); //Capture random pick
+          inputList.splice(randomIndex, 1); //Remove from list
+        }
+        return outputList;
+      }
 
-            if (status_map[key] == null) {
-                $("#row-"+i).find("#status").text("OFFLINE");
-                
$("#row-"+i).find("#shutdown").prop('disabled',true).css('opacity',0.5);
-                $("#row-"+i).find("#queriesCount").text("");
+      //Updates the status map
+      function updateStatusAndShutdown(status_map) {
+        let bitMap = {};
+        if (typeof status_map != 'undefined') {
+            for (let k in status_map) {
+              bitMap[k] = status_map[k];
             }
-            else {
+        }
+        for (i = 1; i <= size; i++) {
+            let currentRow = $("#row-"+i);
+            let address = 
currentRow.find("#address").contents().get(0).nodeValue.trim();
+            let port = currentRow.find("#httpPort").html();
+            let key = address+"-"+port;
+            if (typeof status_map == 'undefined') {
+                
currentRow.find("#status").text(nAText).css('font-style','').prop('title','');
+                
currentRow.find("#shutdown").prop('disabled',true).css('opacity',0.5).css('cursor','not-allowed');
+                currentRow.find("#queriesCount").text("");
+            } else if (status_map[key] == null) {
+                
currentRow.find("#status").text("OFFLINE*").css('font-style','italic').prop('title','Drillbit
 is De-Registered from ZooKeeper');
+                
currentRow.find("#shutdown").prop('disabled',true).css('opacity',0.5).css('cursor','not-allowed');
+                currentRow.find("#queriesCount").text("");
+            } else {
                 if (status_map[key] == "ONLINE") {
-                    $("#row-"+i).find("#status").text(status_map[key]);
-                    
$("#row-"+i).find("#shutdown").prop('disabled',false).css('opacity',1.0);
-                }
-                else {
-                    if ($("#row-"+i).find("#current").html() == "Current") {
+                    
currentRow.find("#status").text(status_map[key]).css('font-style','').prop('title','');
+                    //EnableShutdown IFF => !isAuthEnabled-&&-!HTTPS OR 
isAuthEnabled-&&-current
+                    if ( ( !${model.isAuthEnabled()?c} && location.protocol != 
"https:" ) || ( ${model.shouldShowAdminInfo()?c} && 
currentRow.find("#current").html() == "Current" ) ) {
+                      
currentRow.find("#shutdown").prop('disabled',false).css('opacity',1.0).css('cursor','pointer').attr('title','');
+                    }
+                } else {
+                    if (currentRow.find("#current").html() == "Current") {
                         fillQueryCount(i);
                     }
-                    $("#row-"+i).find("#status").text(status_map[key]);
+                    
currentRow.find("#status").text(status_map[key]).css('font-style','').prop('title','');;
                 }
+                //Removing accounted key
+                delete bitMap[key];
             }
-          }
+        }
+        //If bitMap is not empty, then new bits have been discovered!
+        listNewDrillbits(bitMap);
+      }
+
+      //Add new Bits for listing
+      function listNewDrillbits(newBits) {
+        let newBitList = Object.keys(newBits);
+        let tableRef = 
document.getElementById('bitTable').getElementsByTagName('tbody')[0];
+        let bitId = size;
+        for (i = 0; i < newBitList.length; i++) {
+           let splitPt = newBitList[i].lastIndexOf("-");
+           let displayNodeName = newBitList[i].substring(0, splitPt);
+           let newBitHttpPort = newBitList[i].substring(splitPt+1);
+           let newBitElemId = "neo-"+newBitList[i];
+           let newBitElem = document.getElementsByName(newBitElemId);
+           bitId++;
+           let bitState = newBits[newBitList[i]];
+           //Injecting new row for previously unseen Drillbit
+           $('#bitTable').find('tbody')
+               .append("<tr id='row-" + bitId + "' class='newbit' 
title='Recommend page refresh for more info'>"
+                 + "<td>"+bitId+"</td>"
+                 + "<td id='address' 
name='"+newBitElemId+"'>"+displayNodeName+" <span class='label label-primary' 
id='size' >new</span></td>"
+                 + "<td id='httpPort' 
style='display:none'>"+newBitHttpPort+"</td>"
+                 + "<td class='heap'>"+nAText+"</td>"
+                 + "<td class='direct'>"+nAText+"</td>"
+                 + "<td class='bitload'>"+nAText+"</td>"
+                 + "<td  class='avgload'>"+nAText+"</td>"
+                 + "<td uiElem='userPort'>--</td>"
+                 + "<td uiElem='ctrlPort'>--</td>"
+                 + "<td uiElem='dataPort'>--</td>"
+                 + "<td uiElem='version'>"+nAText+"</td>"
+                 + "<td id='status'>"+bitState+"</td>"
+                 + "<td class='uptime'>"+nAText+"</td>"
+                 + "<td>"
+                   + "<button type='button' id='shutdown' 
onclick='shutdown($(this));' disabled='true' 
style='opacity:0.5;cursor:not-allowed;${(model.isAuthEnabled() && 
!model.shouldShowAdminInfo())?then('display:none','')}'>"
+                   + "<span class='glyphicon 
glyphicon-off'></span></button></td>"
+                 + "<td uiElem='queriesCount' />"
+                 + "</tr>"
+            );
+        }
+        //Update Drillbits count on top
+        if (newBitList.length > 0) {
+          //Setting Drillbit Count & Display Refresh Icon
+          $('#size').text(bitId);
+          $('#reloadBtn').css('display', 'inline');
+          size = bitId;
+        }
       }
+
+      //Updates the outstanding queries in flight before shutdown
       function fillQueryCount(row_id) {
-          var requestPath = "/queriesCount";
-          var url = getRequestUrl(requestPath);
-       var result = $.ajax({
+          let requestPath = "/queriesCount";
+          let url = getRequestUrl(requestPath);
+          let result = $.ajax({
                         type: 'GET',
                         url: url,
                         complete: function(data) {
@@ -293,10 +431,15 @@
                         });
       }
        <#if (model.shouldShowAdminInfo() || !model.isAuthEnabled()) >
-          function shutdown(button,host) {
-          if (confirm("Are you sure you want to shutdown Drillbit running on " 
+ host + " node?")) {
-              var url = location.protocol + "//" + host + "/gracefulShutdown";
-              var result = $.ajax({
+        function shutdown(shutdownBtn) {
+            let rowElem = $(shutdownBtn).parent().parent();
+            let hostAddr = 
$(rowElem).find('#address').contents().get(0).nodeValue.trim();
+            let hostPort = $(rowElem).find('#httpPort').html();
+            let host = hostAddr+":"+hostPort
+
+            if (confirm("Are you sure you want to shutdown Drillbit running on 
" + host + " node?")) {
+              let url = location.protocol + "//" + host + "/gracefulShutdown";
+              let result = $.ajax({
                     type: 'POST',
                     url: url,
                     contentType : 'text/plain',
@@ -305,22 +448,24 @@
                     },
                     success: function(data) {
                         alert(data.responseJSON["response"]);
-                        button.prop('disabled',true).css('opacity',0.5);
+                        shutdownBtn.prop('disabled',true).css('opacity',0.5);
                     }
               });
             }
-          }
-          </#if>
+        }
+        </#if>
 
+      //Pops out the WebUI for a remote Drillbit
       function popOutRemoteDbitUI(dbitHost, dbitPort) {
-            var dbitWebUIUrl = location.protocol+'//'+ dbitHost+':'+dbitPort;
-            var tgtWindow = 'dbit_'+dbitHost;
+            let dbitWebUIUrl = location.protocol+'//'+ dbitHost+':'+dbitPort;
+            let tgtWindow = 'dbit_'+dbitHost;
             window.open(dbitWebUIUrl, tgtWindow);
       }
 
+      //Construct the URL with the appropriate protocol and host
       function getRequestUrl(requestPath) {
-            var protocol = location.protocol;
-            var host = location.host;
+            let protocol = location.protocol;
+            let host = location.host;
             var url = protocol + "//" + host + requestPath;
             return url;
       }
@@ -328,9 +473,20 @@
       //Iterates through all the nodes for update
       function reloadMetrics() {
           for (i = 1; i <= size; i++) {
-              var address = 
$("#row-"+i).find("#address").contents().get(0).nodeValue.trim();
-              var httpPort = 
$("#row-"+i).find("#httpPort").contents().get(0).nodeValue.trim();
-              updateMetricsHtml(address, httpPort, i);
+            //Skip metrics update for remote bits in HTTPS mode
+            if (i > 1 && location.protocol == "https:") {
+                break;
+            }
+            let currentRow = $("#row-"+i);
+            let address = "";
+            //For 'current' bit, address is referred to by location.hostname 
instead of FQDN ()
+            if (i == 1) {
+              address = location.hostname;
+            } else {
+              address = 
currentRow.find("#address").contents().get(0).nodeValue.trim();
+            }
+            let httpPort = 
currentRow.find("#httpPort").contents().get(0).nodeValue.trim();
+            updateMetricsHtml(address, httpPort, i);
           }
       }
 
@@ -339,82 +495,81 @@
         /* NOTE: For remote drillbits:
            If Authentication or SSL is enabled; we'll assume we don't have 
valid certificates
         */
-        var remoteHost = location.protocol+"//"+drillbit+":"+webport;
-        if ( !updateRemoteInfo || (location.protocol == "https" && remoteHost 
!= location.host) ) {
-          return;
-        }
+        let remoteHost = location.protocol+"//"+drillbit+":"+webport;
         //
-        var result = $.ajax({
+        let result = $.ajax({
           type: 'GET',
           url: location.protocol+"//"+drillbit+":"+webport+"/status/metrics",
           dataType: "json",
           error: function(data) {
             resetMetricsHtml(idx);
-          }, 
+          },
           complete: function(data) {
             if (typeof data.responseJSON == 'undefined') {
                 resetMetricsHtml(idx);
                 return;
             }
-            var metrics = data.responseJSON['gauges'];
+            let metrics = data.responseJSON['gauges'];
             //Memory
-            var usedHeap = metrics['heap.used'].value;
-            var maxHeap  = metrics['heap.max'].value;
-            var usedDirect = metrics['drill.allocator.root.used'].value;
-            var peakDirect = metrics['drill.allocator.root.peak'].value;
-            var heapUsage    = computeMemUsage(usedHeap,maxHeap);
-            var directUsage  = computeMemUsage(usedDirect,peakDirect);
-            var rowElem = document.getElementById("row-"+idx);
-            var heapElem = rowElem.getElementsByClassName("heap")[0];
+            let usedHeap = metrics['heap.used'].value;
+            let maxHeap  = metrics['heap.max'].value;
+            let usedDirect = metrics['drill.allocator.root.used'].value;
+            let peakDirect = metrics['drill.allocator.root.peak'].value;
+            let heapUsage    = computeMemUsage(usedHeap,maxHeap);
+            let directUsage  = computeMemUsage(usedDirect,peakDirect);
+            let rowElem = document.getElementById("row-"+idx);
+            let heapElem = rowElem.getElementsByClassName("heap")[0];
             heapElem.innerHTML = heapUsage;
-            var directElem = rowElem.getElementsByClassName("direct")[0];
+            let directElem = rowElem.getElementsByClassName("direct")[0];
             directElem.innerHTML = directUsage;
             //DrillbitLoad
-            var dbitLoad = metrics['drillbit.load.avg'].value;
-            var dbitLoadElem = rowElem.getElementsByClassName("bitload")[0];
+            let dbitLoad = metrics['drillbit.load.avg'].value;
+            let dbitLoadElem = rowElem.getElementsByClassName("bitload")[0];
             if (dbitLoad >= 0) {
               dbitLoadElem.innerHTML = parseFloat(Math.round(dbitLoad * 
10000)/100).toFixed(2) + "%";
             } else {
-              dbitLoadElem.innerHTML = "Not Available";
+              dbitLoadElem.innerHTML = nAText;
             }
             //AvgSysLoad
-            var avgSysLoad = metrics['os.load.avg'].value;
-            var sysLoadElem = rowElem.getElementsByClassName("avgload")[0];
+            let avgSysLoad = metrics['os.load.avg'].value;
+            let sysLoadElem = rowElem.getElementsByClassName("avgload")[0];
             sysLoadElem.innerHTML = avgSysLoad;
             //Uptime
-            var uptimeValue = metrics['drillbit.uptime'].value;
-            var uptimeElem = rowElem.getElementsByClassName("uptime")[0];
+            let uptimeValue = metrics['drillbit.uptime'].value;
+            let uptimeElem = rowElem.getElementsByClassName("uptime")[0];
             uptimeElem.innerHTML = elapsedTime(uptimeValue);
           }
         });
       }
 
+      //Reset the metrics of a Drillbit in the table
       function resetMetricsHtml(idx) {
         $.each(["heap","direct","bitload","avgload","uptime"], function(i, 
key) {
-          var resetElem = 
document.getElementById("row-"+idx).getElementsByClassName(key)[0];
-          resetElem.innerHTML = "Not Available";
+          let resetElem = 
document.getElementById("row-"+idx).getElementsByClassName(key)[0];
+          resetElem.innerHTML = nAText;
         });
     };
 
       //Compute Usage
       function computeMemUsage(used, max) {
-        var percent = 0;
+        let percent = 0;
         if ( max > 0) {
-          var percent = Math.round((100 * used / max), 2);
+          let percent = Math.round((100 * used / max), 2);
         }
-        var usage   =  bytesInGB(used, 2) + "GB (" + Math.max(0, percent) + "% 
of "+ bytesInGB(max, 2) +"GB)";
+        let usage   =  bytesInGB(used, 2) + "GB (" + Math.max(0, percent) + "% 
of "+ bytesInGB(max, 2) +"GB)";
         return usage;
       }
 
+      //Translate bytes into GB with decimal places
       function bytesInGB(byteVal, decimal) {
-        var scale = Math.pow(10,decimal);
+        let scale = Math.pow(10,decimal);
         return Math.round(scale * byteVal / 1073741824)/scale;
       }
 
       //Calculate Uptime
       function elapsedTime(valueInMsec) {
-        var elapsedTime = ""; 
-        var d, h, m, s; 
+        let elapsedTime = "";
+        let d, h, m, s;
         s = Math.floor(valueInMsec / 1000);
         m = Math.floor(s / 60);
         s = s % 60;
@@ -422,7 +577,7 @@
         m = m % 60;
         d = Math.floor(h / 24);
         h = h % 24;
-        var detailCount = 0;
+        let detailCount = 0;
         if (!isNaN(d) && d > 0) {
           elapsedTime = d+"d ";
           detailCount += 1;
@@ -439,7 +594,7 @@
           elapsedTime = elapsedTime + s+"s";
         }
         return elapsedTime;
-};
+     };
   </script>
 </#macro>
 

-- 
To stop receiving notification emails like this one, please contact
amansi...@apache.org.

Reply via email to