Преглед изворни кода

HADOOP-392. Improvements to DFS web UI. Contributed by Devaraj.

git-svn-id: https://svn.apache.org/repos/asf/lucene/hadoop/trunk@427715 13f79535-47bb-0310-9956-ffa450edef68
Doug Cutting пре 19 година
родитељ
комит
bb2cad5761

+ 11 - 2
src/java/org/apache/hadoop/dfs/FSNamesystem.java

@@ -61,6 +61,11 @@ class FSNamesystem implements FSConstants {
     //
     TreeMap datanodeMap = new TreeMap();
 
+    // 
+    // Stores the datanode.name-->datanodeInfo map. Used for getting a sorted
+    // list of datanodes sorted by their names
+    TreeMap datanodeMapByName = new TreeMap();
+
     //
     // Stores the set of dead datanodes
     TreeMap deaddatanodeMap = new TreeMap();
@@ -1083,8 +1088,10 @@ class FSNamesystem implements FSConstants {
               + "new storageID " + nodeReg.getStorageID() + " assigned." );
         }
         // register new datanode
+        DatanodeDescriptor dinfo;
         datanodeMap.put(nodeReg.getStorageID(), 
-                        new DatanodeDescriptor( nodeReg ));
+                        (dinfo = new DatanodeDescriptor( nodeReg )));
+        datanodeMapByName.put(nodeReg.getName(), dinfo);
         NameNode.stateChangeLog.debug(
             "BLOCK* NameSystem.registerDatanode: "
             + "node registered." );
@@ -1152,6 +1159,7 @@ class FSNamesystem implements FSConstants {
                     +"brand-new heartbeat from "+nodeID.getName() );
             nodeinfo = new DatanodeDescriptor(nodeID, capacity, remaining, xceiverCount);
             datanodeMap.put(nodeinfo.getStorageID(), nodeinfo);
+            datanodeMapByName.put(nodeinfo.getName(), nodeinfo);
             capacityDiff = capacity;
             remainingDiff = remaining;
           } else {
@@ -1208,6 +1216,7 @@ class FSNamesystem implements FSConstants {
     private void removeDatanode( DatanodeDescriptor nodeInfo ) {
       heartbeats.remove(nodeInfo);
       datanodeMap.remove(nodeInfo.getStorageID());
+      datanodeMapByName.remove(nodeInfo.getName());
       deaddatanodeMap.put(nodeInfo.getName(), nodeInfo);
       NameNode.stateChangeLog.debug("BLOCK* NameSystem.removeDatanode: "
               + nodeInfo.getName() + " is removed from datanodeMap");
@@ -1543,7 +1552,7 @@ class FSNamesystem implements FSConstants {
     public void DFSNodesStatus(Vector live, Vector dead) {
         synchronized (heartbeats) {
             synchronized (datanodeMap) {
-                live.addAll(datanodeMap.values());
+                live.addAll(datanodeMapByName.values());
                 dead.addAll(deaddatanodeMap.values());
             }
         }

+ 166 - 24
src/webapps/datanode/browseBlock.jsp

@@ -14,12 +14,156 @@
 <%!
   static JspHelper jspHelper = new JspHelper();
 
+  public void generateFileDetails(JspWriter out, HttpServletRequest req) 
+    throws IOException {
+
+    int chunkSizeToView = 0;
+    long startOffset = 0;
+    int datanodePort;
+
+    String blockIdStr = null;
+    long currBlockId = 0;
+    blockIdStr = req.getParameter("blockId");
+    if (blockIdStr == null) {
+      out.print("Invalid input (blockId absent)");
+      return;
+    }
+    currBlockId = Long.parseLong(blockIdStr);
+
+    String datanodePortStr = req.getParameter("datanodePort");
+    if (datanodePortStr == null) {
+      out.print("Invalid input (datanodePort absent)");
+      return;
+    }
+    datanodePort = Integer.parseInt(datanodePortStr);
+
+    String namenodeInfoPortStr = req.getParameter("namenodeInfoPort");
+    int namenodeInfoPort = -1;
+    if (namenodeInfoPortStr != null)
+      namenodeInfoPort = Integer.parseInt(namenodeInfoPortStr);
+
+    String chunkSizeToViewStr = req.getParameter("chunkSizeToView");
+    if (chunkSizeToViewStr != null && Integer.parseInt(chunkSizeToViewStr) > 0)
+      chunkSizeToView = Integer.parseInt(chunkSizeToViewStr);
+    else chunkSizeToView = jspHelper.defaultChunkSizeToView;
+
+    String startOffsetStr = req.getParameter("startOffset");
+    if (startOffsetStr == null || Long.parseLong(startOffsetStr) < 0)
+      startOffset = 0;
+    else startOffset = Long.parseLong(startOffsetStr);
+    
+    String filename = req.getParameter("filename");
+    if (filename == null || filename.length() == 0) {
+      out.print("Invalid input");
+      return;
+    }
+
+    String blockSizeStr = req.getParameter("blockSize"); 
+    long blockSize = 0;
+    if (blockSizeStr == null && blockSizeStr.length() == 0) {
+      out.print("Invalid input");
+      return;
+    } 
+    blockSize = Long.parseLong(blockSizeStr);
+
+    DFSClient dfs = new DFSClient(jspHelper.nameNodeAddr, jspHelper.conf);
+    LocatedBlock[] blocks = dfs.namenode.open(filename);
+    //Add the various links for looking at the file contents
+    //URL for downloading the full file
+    String downloadUrl = "http://" + req.getServerName() + ":" +
+                         + req.getServerPort() + "/streamFile?" + "filename=" +
+                         URLEncoder.encode(filename, "UTF-8");
+    out.print("<a name=\"viewOptions\"></a>");
+    out.print("<a href=\"" + downloadUrl + "\">Download this file</a><br>");
+    
+    DatanodeInfo chosenNode;
+    //URL for TAIL 
+    LocatedBlock lastBlk = blocks[blocks.length - 1];
+    long blockId = lastBlk.getBlock().getBlockId();
+    try {
+      chosenNode = jspHelper.bestNode(lastBlk);
+    } catch (IOException e) {
+      out.print(e.toString());
+      dfs.close();
+      return;
+    }
+    String fqdn = 
+           InetAddress.getByName(chosenNode.getHost()).getCanonicalHostName();
+    String tailUrl = "http://" + fqdn + ":" +
+                     chosenNode.getInfoPort() + 
+                 "/tail.jsp?filename=" + URLEncoder.encode(filename, "UTF-8") +
+                 "&chunkSizeToView=" + chunkSizeToView +
+                 "&referrer=" + 
+          URLEncoder.encode(req.getRequestURL() + "?" + req.getQueryString(),
+                            "UTF-8");
+    out.print("<a href=\"" + tailUrl + "\">TAIL this file</a><br>");
+
+    out.print("<form action=\"/browseBlock.jsp\" method=GET>");
+    out.print("<b>Chunk Size to view (in bytes, upto file's DFS blocksize): </b>");
+    out.print("<input type=\"hidden\" name=\"blockId\" value=\"" + currBlockId +
+              "\">");
+    out.print("<input type=\"hidden\" name=\"blockSize\" value=\"" + 
+              blockSize + "\">");
+    out.print("<input type=\"hidden\" name=\"startOffset\" value=\"" + 
+              startOffset + "\">");
+    out.print("<input type=\"hidden\" name=\"filename\" value=\"" + filename +
+              "\">");
+    out.print("<input type=\"hidden\" name=\"datanodePort\" value=\"" + 
+              datanodePort+ "\">");
+    out.print("<input type=\"hidden\" name=\"namenodeInfoPort\" value=\"" +
+              namenodeInfoPort + "\">");
+    out.print("<input type=\"text\" name=\"chunkSizeToView\" value=" +
+              chunkSizeToView + " size=10 maxlength=10>");
+    out.print("&nbsp;&nbsp;<input type=\"submit\" name=\"submit\" value=\"Refresh\">");
+    out.print("</form>");
+    out.print("<hr>"); 
+    out.print("<a name=\"blockDetails\"></a>");
+    out.print("<B>Total number of blocks: "+blocks.length+"</B><br>");
+    //generate a table and dump the info
+    for (int i = 0; i < blocks.length; i++) {
+      blockId = blocks[i].getBlock().getBlockId();
+      blockSize = blocks[i].getBlock().getNumBytes();
+      String blk = "blk_" + Long.toString(blockId);
+      DatanodeInfo[] locs = blocks[i].getLocations();
+      int r = jspHelper.rand.nextInt(locs.length);
+      String datanodeAddr = locs[r].getName();
+      datanodePort = Integer.parseInt(datanodeAddr.substring(
+                                        datanodeAddr.indexOf(':') + 1, 
+                                    datanodeAddr.length())); 
+      fqdn = InetAddress.getByName(locs[r].getHost()).getCanonicalHostName();
+      String blockUrl = "http://"+ fqdn + ":" +
+                        locs[r].getInfoPort() +
+                        "/browseBlock.jsp?blockId=" + Long.toString(blockId) +
+                        "&blockSize=" + blockSize +
+               "&filename=" + URLEncoder.encode(filename, "UTF-8")+ 
+                        "&datanodePort=" + datanodePort + 
+                        "&namenodeInfoPort=" + namenodeInfoPort +
+                        "&chunkSizeToView=" + chunkSizeToView;
+      out.print("<a href=\"" + blockUrl + "\">" + "blk_" + blockId + 
+                        "</a>");
+      if (i < blocks.length - 1)
+        out.print("&nbsp;&nbsp;&nbsp;&nbsp;");
+      if (i % 3 == 0) out.print("<br>");
+    }
+    out.print("<hr>");
+    String namenodeHost = jspHelper.nameNodeAddr.getHostName();
+    out.print("<br><a href=\"http://" + 
+              InetAddress.getByName(namenodeHost).getCanonicalHostName() + ":" +
+              namenodeInfoPort + "/dfshealth.jsp\">Go back to DFS home</a>");
+    dfs.close();
+  }
+
   public void generateFileChunks(JspWriter out, HttpServletRequest req) 
     throws IOException {
     long startOffset = 0;
     int datanodePort = 0; 
     int chunkSizeToView = 0;
 
+    String namenodeInfoPortStr = req.getParameter("namenodeInfoPort");
+    int namenodeInfoPort = -1;
+    if (namenodeInfoPortStr != null)
+      namenodeInfoPort = Integer.parseInt(namenodeInfoPortStr);
+
     String filename = req.getParameter("filename");
     if (filename == null) {
       out.print("Invalid input (filename absent)");
@@ -63,23 +207,13 @@
 
     out.print("<h2>File: " + filename + "</h2>");
     out.print("<a href=\"http://" + req.getServerName() + ":" + 
-              req.getServerPort() + "/browseData.jsp?filename=" + filename + 
-              "\">Go back to File details</a><br>");
-    out.print("<b>Chunk Size to view (in bytes, upto file's DFS blocksize): </b>");
-    out.print("<input type=\"hidden\" name=\"blockId\" value=\"" + blockId + 
-              "\">");
-    out.print("<input type=\"hidden\" name=\"blockSize\" value=\"" + 
-              blockSize + "\">");
-    out.print("<input type=\"hidden\" name=\"startOffset\" value=\"" + 
-              startOffset + "\">");
-    out.print("<input type=\"hidden\" name=\"filename\" value=\"" + filename +
-              "\">");
-    out.print("<input type=\"hidden\" name=\"datanodePort\" value=\"" + 
-              datanodePort+ "\">");
-    out.print("<input type=\"text\" name=\"chunkSizeToView\" value=" +
-              chunkSizeToView + " size=10 maxlength=10>");
-    out.print("&nbsp;&nbsp;<input type=\"submit\" name=\"submit\" value=\"Refresh\"><hr>");
-   out.print("</form>");
+              req.getServerPort() + 
+              "/browseDirectory.jsp?dir=" + 
+              URLEncoder.encode(new File(filename).getParent(), "UTF-8") +
+              "&namenodeInfoPort=" + namenodeInfoPort + 
+              "\"><i>Go back to dir listing</i></a><br>");
+    out.print("<a href=\"#viewOptions\">Advanced view/download options</a><br>");
+    out.print("<hr>");
 
     //Determine the prev & next blocks
     DFSClient dfs = new DFSClient(jspHelper.nameNodeAddr, jspHelper.conf);
@@ -124,10 +258,12 @@
                 nextPort + 
                 "/browseBlock.jsp?blockId=" + nextBlockIdStr +
                 "&blockSize=" + nextBlockSize + "&startOffset=" + 
-                nextStartOffset + "&filename=" + filename +
+                nextStartOffset + 
+                "&filename=" + URLEncoder.encode(filename, "UTF-8") +
                 "&chunkSizeToView=" + chunkSizeToView + 
-                "&datanodePort=" + nextDatanodePort;
-      out.print("<a href=\"" + nextUrl + "\">Next</a>&nbsp;&nbsp;");        
+                "&datanodePort=" + nextDatanodePort +
+                "&namenodeInfoPort=" + namenodeInfoPort;
+      out.print("<a href=\"" + nextUrl + "\">View Next chunk</a>&nbsp;&nbsp;");        
     }
     //determine data for the prev link
     String prevBlockIdStr = null;
@@ -173,12 +309,15 @@
                 prevPort + 
                 "/browseBlock.jsp?blockId=" + prevBlockIdStr + 
                 "&blockSize=" + prevBlockSize + "&startOffset=" + 
-                prevStartOffset + "&filename=" + filename + 
+                prevStartOffset + 
+                "&filename=" + URLEncoder.encode(filename, "UTF-8") + 
                 "&chunkSizeToView=" + chunkSizeToView +
-                "&datanodePort=" + prevDatanodePort;
-      out.print("<a href=\"" + prevUrl + "\">Prev</a>&nbsp;&nbsp;");
+                "&datanodePort=" + prevDatanodePort +
+                "&namenodeInfoPort=" + namenodeInfoPort;
+      out.print("<a href=\"" + prevUrl + "\">View Prev chunk</a>&nbsp;&nbsp;");
     }
     out.print("<hr>");
+    out.print("<textarea cols=\"100\" rows=\"25\" wrap=\"virtual\" READONLY>");
     try {
     jspHelper.streamBlockInAscii(
             new InetSocketAddress(req.getServerName(), datanodePort), blockId, 
@@ -186,6 +325,7 @@
     } catch (Exception e){
         out.print(e);
     }
+    out.print("</textarea>");
     dfs.close();
   }
 
@@ -195,11 +335,13 @@
 <title>Hadoop DFS File Viewer</title>
 
 <body>
-<form action="/browseBlock.jsp" method=GET>
 <% 
    generateFileChunks(out,request);
 %>
 <hr>
+<% 
+   generateFileDetails(out,request);
+%>
 
 <h2>Local logs</h2>
 <a href="/logs/">Log</a> directory

+ 0 - 143
src/webapps/datanode/browseData.jsp

@@ -1,143 +0,0 @@
-<%@ page
-  contentType="text/html; charset=UTF-8"
-  import="javax.servlet.*"
-  import="javax.servlet.http.*"
-  import="java.io.*"
-  import="java.util.*"
-  import="java.net.*"
-  import="org.apache.hadoop.dfs.*"
-  import="org.apache.hadoop.io.*"
-  import="org.apache.hadoop.conf.*"
-  import="java.text.DateFormat"
-%>
-<%!
-  static JspHelper jspHelper = new JspHelper();
-
-  public void generateFileDetails(JspWriter out, HttpServletRequest req) 
-    throws IOException {
-
-    String filename = req.getParameter("filename");
-    if (filename == null || filename.length() == 0) {
-      out.print("Invalid input");
-      return;
-    }
-
-    String blockSizeStr = req.getParameter("blockSize"); 
-    long blockSize;
-    if (blockSizeStr != null && blockSizeStr.length() != 0)
-      blockSize = Long.parseLong(blockSizeStr);
-
-    DFSClient dfs = new DFSClient(jspHelper.nameNodeAddr, jspHelper.conf);
-    LocatedBlock[] blocks = dfs.namenode.open(filename);
-    out.print("<h2>Filename: "+filename+"</h2>");
-    out.print("<a href=\"http://" + req.getServerName() + ":" + 
-              req.getServerPort() + 
-              "/browseDirectory.jsp?dir=" + new File(filename).getParent() + 
-              "\"><i>Go back to dir listing</i></a><br><hr>");
-    //Add the various links for looking at the file contents
-    //URL for downloading the full file
-    String downloadUrl = "http://" + req.getServerName() + ":" +
-                         + req.getServerPort() + "/streamFile?" + "filename=" +
-                         filename;
-    out.print("<a href=\"" + downloadUrl + "\">Download this file</a><br>");
-    
-    DatanodeInfo chosenNode;
-    //URL for TAIL 
-    LocatedBlock lastBlk = blocks[blocks.length - 1];
-    long blockId = lastBlk.getBlock().getBlockId();
-    blockSize = lastBlk.getBlock().getNumBytes();
-    try {
-      chosenNode = jspHelper.bestNode(lastBlk);
-    } catch (IOException e) {
-      out.print(e.toString());
-      dfs.close();
-      return;
-    }
-    String fqdn = 
-           InetAddress.getByName(chosenNode.getHost()).getCanonicalHostName();
-    String tailUrl = "http://" + fqdn + ":" +
-                     chosenNode.getInfoPort() + 
-                     "/tail.jsp?filename=" + filename;
-    out.print("<a href=\"" + tailUrl + "\">TAIL this file</a><br>");
-
-    //URL for chunk viewing of a file
-    LocatedBlock firstBlk = blocks[0];
-    blockId = firstBlk.getBlock().getBlockId();
-    blockSize = firstBlk.getBlock().getNumBytes();
-    try {
-      chosenNode = jspHelper.bestNode(firstBlk);
-    } catch (IOException e) {
-      out.print(e.toString());
-      dfs.close();
-      return;
-    }
-    String datanodeAddr = chosenNode.getName();
-    int datanodePort = Integer.parseInt(datanodeAddr.substring(
-                                          datanodeAddr.indexOf(':') + 1, 
-                                      datanodeAddr.length())); 
-    fqdn = InetAddress.getByName(chosenNode.getHost()).getCanonicalHostName();
-    String chunkViewUrl = "http://" + fqdn + ":" +
-                     chosenNode.getInfoPort() + 
-                     "/browseBlock.jsp?blockId=" + Long.toString(blockId) +
-                     "&tail=false&blockSize=" + blockSize +
-                     "&filename=" + filename +
-                     "&datanodePort=" + datanodePort;
-    out.print("<a href=\"" + chunkViewUrl + 
-              "\">View this file (in a chunked fashion)</a><br>");
-    out.print("<hr>"); 
-    out.print("<B>Total number of blocks: "+blocks.length+"</B><HR>");
-    //generate a table and dump the info
-    String [] headings = new String[2];
-    headings[0] = "Block ID"; headings[1] = "Datanodes containing this block"; 
-    jspHelper.addTableHeader(out);
-    jspHelper.addTableRow(out, headings);
-    String cols [] = new String[2];
-    for (int i = 0; i < blocks.length; i++) {
-      blockId = blocks[i].getBlock().getBlockId();
-      blockSize = blocks[i].getBlock().getNumBytes();
-      cols[0] = "blk_" + Long.toString(blockId);
-      DatanodeInfo[] locs = blocks[i].getLocations();
-      String locations = new String();
-      for (int j = 0; j < locs.length; j++) {
-        datanodeAddr = locs[j].getName();
-        datanodePort = Integer.parseInt(datanodeAddr.substring(
-                                          datanodeAddr.indexOf(':') + 1, 
-                                      datanodeAddr.length())); 
-        fqdn = InetAddress.getByName(locs[j].getHost()).getCanonicalHostName();
-        String blockUrl = "http://"+ fqdn + ":" +
-                          locs[j].getInfoPort() +
-                          "/browseBlock.jsp?blockId=" + Long.toString(blockId) +
-                          "&blockSize=" + blockSize +
-                          "&filename=" + filename + 
-                          "&datanodePort=" + datanodePort;
-        locations += "<a href=\"" + blockUrl + "\">" + fqdn + "</a>";
-        if (j < locs.length - 1)
-          locations += ", ";
-      }
-      cols[1] = locations;
-      jspHelper.addTableRow(out, cols);
-    }
-    jspHelper.addTableFooter(out);
-    dfs.close();
-  }
-
-%>
-
-<html>
-
-<title>Hadoop DFS File Browsing</title>
-
-<body>
-
-<% 
-   generateFileDetails(out,request);
-%>
-<hr>
-
-<h2>Local logs</h2>
-<a href="/logs/">Log</a> directory
-
-<hr>
-<a href="http://lucene.apache.org/hadoop">Hadoop</a>, 2006.<br>
-</body>
-</html>

+ 25 - 5
src/webapps/datanode/browseDirectory.jsp

@@ -20,6 +20,11 @@
       out.print("Invalid input");
       return;
     }
+
+    String namenodeInfoPortStr = req.getParameter("namenodeInfoPort");
+    int namenodeInfoPort = -1;
+    if (namenodeInfoPortStr != null)
+      namenodeInfoPort = Integer.parseInt(namenodeInfoPortStr);
     
     DFSClient dfs = new DFSClient(jspHelper.nameNodeAddr, jspHelper.conf);
     DFSFileInfo[] files = dfs.listPaths(new UTF8(dir));
@@ -32,7 +37,8 @@
     File f = new File(dir);
     String parent;
     if ((parent = f.getParent()) != null)
-      out.print("<a href=\"" + req.getRequestURL() + "?dir=" + parent + 
+      out.print("<a href=\"" + req.getRequestURL() + "?dir=" + parent +
+                "&namenodeInfoPort=" + namenodeInfoPort +
                 "\">Go to parent directory</a><br>");
 
     if (files == null || files.length == 0) {
@@ -61,11 +67,19 @@
         }
         DatanodeInfo chosenNode = jspHelper.bestNode(blocks[0]);
         String fqdn = InetAddress.getByName(chosenNode.getHost()).getCanonicalHostName();
+        String datanodeAddr = chosenNode.getName();
+        int datanodePort = Integer.parseInt(
+                                  datanodeAddr.substring(
+                                        datanodeAddr.indexOf(':') + 1, 
+                                  datanodeAddr.length())); 
         String datanodeUrl = "http://"+fqdn+":" +
                              chosenNode.getInfoPort() + 
-                             "/browseData.jsp?filename=" +
-                             files[i].getPath() + "&blockSize=" + 
-                             files[i].getBlockSize();
+                             "/browseBlock.jsp?blockId=" +
+                             blocks[0].getBlock().getBlockId() +
+                             "&blockSize=" + files[i].getBlockSize() +
+               "&filename=" + URLEncoder.encode(files[i].getPath(), "UTF-8") + 
+                             "&datanodePort=" + datanodePort + 
+                             "&namenodeInfoPort=" + namenodeInfoPort;
         cols[0] = "<a href=\""+datanodeUrl+"\">"+files[i].getPath()+"</a>";
         cols[1] = "file";
         cols[2] = Long.toString(files[i].getLen());
@@ -74,7 +88,9 @@
         jspHelper.addTableRow(out, cols);
       }
       else {
-        String datanodeUrl = req.getRequestURL()+"?dir="+files[i].getPath();
+        String datanodeUrl = req.getRequestURL()+"?dir="+
+            URLEncoder.encode(files[i].getPath(), "UTF-8") + 
+            "&namenodeInfoPort=" + namenodeInfoPort;
         cols[0] = "<a href=\""+datanodeUrl+"\">"+files[i].getPath()+"</a>";
         cols[1] = "dir";
         cols[2] = "0";
@@ -84,6 +100,10 @@
       }
     }
     jspHelper.addTableFooter(out);
+    String namenodeHost = jspHelper.nameNodeAddr.getHostName();
+    out.print("<br><a href=\"http://" + 
+              InetAddress.getByName(namenodeHost).getCanonicalHostName() + ":" +
+              namenodeInfoPort + "/dfshealth.jsp\">Go back to DFS home</a>");
     dfs.close();
   }
 

+ 16 - 5
src/webapps/datanode/tail.jsp

@@ -20,6 +20,12 @@
     
     int chunkSizeToView = 0;
 
+    String referrer = req.getParameter("referrer");
+    boolean noLink = false;
+    if (referrer == null) {
+      noLink = true;
+    }
+
     String filename = req.getParameter("filename");
     if (filename == null) {
       out.print("Invalid input (filename absent)");
@@ -30,17 +36,20 @@
     if (chunkSizeToViewStr != null && Integer.parseInt(chunkSizeToViewStr) > 0)
       chunkSizeToView = Integer.parseInt(chunkSizeToViewStr);
     else chunkSizeToView = jspHelper.defaultChunkSizeToView;
-    
-    out.print("<h2>File: " + filename + "</h2>");
-    out.print("<a href=\"http://" + req.getServerName() + ":" + 
-              req.getServerPort() + "/browseData.jsp?filename=" + filename + 
-              "\">Go back to File details</a><br>");
+
+    if (!noLink)
+      out.print("<h2><a href=\"" + referrer + "\">" + filename + "</a></h2>");
+    else
+      out.print("<h2>" + filename + "</h2>");
     out.print("<b>Chunk Size to view (in bytes, upto file's DFS blocksize): </b>");
     out.print("<input type=\"text\" name=\"chunkSizeToView\" value=" +
               chunkSizeToView + " size=10 maxlength=10>");
     out.print("&nbsp;&nbsp;<input type=\"submit\" name=\"submit\" value=\"Refresh\"><hr>");
     out.print("<input type=\"hidden\" name=\"filename\" value=\"" + filename +
               "\">");
+    if (!noLink)
+      out.print("<input type=\"hidden\" name=\"referrer\" value=\"" + 
+                referrer+ "\">");
 
     //fetch the block from the datanode that has the last block for this file
     DFSClient dfs = new DFSClient(jspHelper.nameNodeAddr, 
@@ -68,7 +77,9 @@
       startOffset = blockSize - chunkSizeToView;
     else startOffset = 0;
 
+    out.print("<textarea cols=\"100\" rows=\"25\" wrap=\"virtual\" READONLY>");
     jspHelper.streamBlockInAscii(addr, blockId, blockSize, startOffset, chunkSizeToView, out);
+    out.print("</textarea>");
     dfs.close();
   }
 

+ 25 - 5
src/webapps/dfs/browseDirectory.jsp

@@ -20,6 +20,11 @@
       out.print("Invalid input");
       return;
     }
+
+    String namenodeInfoPortStr = req.getParameter("namenodeInfoPort");
+    int namenodeInfoPort = -1;
+    if (namenodeInfoPortStr != null)
+      namenodeInfoPort = Integer.parseInt(namenodeInfoPortStr);
     
     DFSClient dfs = new DFSClient(jspHelper.nameNodeAddr, jspHelper.conf);
     DFSFileInfo[] files = dfs.listPaths(new UTF8(dir));
@@ -32,7 +37,8 @@
     File f = new File(dir);
     String parent;
     if ((parent = f.getParent()) != null)
-      out.print("<a href=\"" + req.getRequestURL() + "?dir=" + parent + 
+      out.print("<a href=\"" + req.getRequestURL() + "?dir=" + parent +
+                "&namenodeInfoPort=" + namenodeInfoPort +
                 "\">Go to parent directory</a><br>");
 
     if (files == null || files.length == 0) {
@@ -61,11 +67,19 @@
         }
         DatanodeInfo chosenNode = jspHelper.bestNode(blocks[0]);
         String fqdn = InetAddress.getByName(chosenNode.getHost()).getCanonicalHostName();
+        String datanodeAddr = chosenNode.getName();
+        int datanodePort = Integer.parseInt(
+                                  datanodeAddr.substring(
+                                        datanodeAddr.indexOf(':') + 1, 
+                                  datanodeAddr.length())); 
         String datanodeUrl = "http://"+fqdn+":" +
                              chosenNode.getInfoPort() + 
-                             "/browseData.jsp?filename=" +
-                             files[i].getPath() + "&blockSize=" + 
-                             files[i].getBlockSize();
+                             "/browseBlock.jsp?blockId=" +
+                             blocks[0].getBlock().getBlockId() +
+                             "&blockSize=" + files[i].getBlockSize() +
+               "&filename=" + URLEncoder.encode(files[i].getPath(), "UTF-8") + 
+                             "&datanodePort=" + datanodePort + 
+                             "&namenodeInfoPort=" + namenodeInfoPort;
         cols[0] = "<a href=\""+datanodeUrl+"\">"+files[i].getPath()+"</a>";
         cols[1] = "file";
         cols[2] = Long.toString(files[i].getLen());
@@ -74,7 +88,9 @@
         jspHelper.addTableRow(out, cols);
       }
       else {
-        String datanodeUrl = req.getRequestURL()+"?dir="+files[i].getPath();
+        String datanodeUrl = req.getRequestURL()+"?dir="+
+            URLEncoder.encode(files[i].getPath(), "UTF-8") + 
+            "&namenodeInfoPort=" + namenodeInfoPort;
         cols[0] = "<a href=\""+datanodeUrl+"\">"+files[i].getPath()+"</a>";
         cols[1] = "dir";
         cols[2] = "0";
@@ -84,6 +100,10 @@
       }
     }
     jspHelper.addTableFooter(out);
+    String namenodeHost = jspHelper.nameNodeAddr.getHostName();
+    out.print("<br><a href=\"http://" + 
+              InetAddress.getByName(namenodeHost).getCanonicalHostName() + ":" +
+              namenodeInfoPort + "/dfshealth.jsp\">Go back to DFS home</a>");
     dfs.close();
   }
 

+ 12 - 6
src/webapps/dfs/dfshealth.jsp

@@ -10,6 +10,7 @@
 <%!
   FSNamesystem fsn = FSNamesystem.getFSNamesystem();
   String namenodeLabel = fsn.getDFSNameNodeMachine() + ":" + fsn.getDFSNameNodePort();
+  long currentTime;
 
   public void generateLiveNodeData(JspWriter out, DatanodeInfo d) 
     throws IOException {
@@ -21,8 +22,8 @@
     String percentUsed = DFSShell.limitDecimal(((1.0 * u)/c)*100, 2);
     out.print("<td style=\"vertical-align: top;\"> <b>" + 
               d.getName() +
-              "</b>&nbsp;<br><i><b>LastContact:</b>" + 
-              new Date(d.getLastUpdate())+ ";&nbsp;");
+              "</b>&nbsp;<br><i><b>LastContact:</b>&nbsp;" + 
+             (currentTime - d.getLastUpdate())/1000 + " second(s) back;&nbsp;");
     out.print("<b>Total raw bytes:</b>&nbsp;" + c + "(" + cGb + 
               "&nbsp;GB);&nbsp;");
     out.print("<b>Percent used:</b>&nbsp;" + percentUsed);
@@ -37,16 +38,19 @@
       out.print("There are no datanodes in the cluster");
     }
     else {
+      out.print("<br><b>Number of live data stores: " + live.size() + 
+                ", dead datanodes: " + dead.size() + "</b></br>");
       out.print("<table style=\"width: 100%; text-align: left;\" border=\"1\""+
                 " cellpadding=\"2\" cellspacing=\"2\">");
       out.print("<tbody>");
       out.print("<tr>");
-      out.print("<td style=\"vertical-align: top;\"><B>Live Nodes</B><br></td>");
-      out.print("<td style=\"vertical-align: top;\"><B>Dead Nodes</B><br></td>");
+      out.print("<td style=\"vertical-align: top;\"><B>Live Data Stores</B><br></td>");
+      out.print("<td style=\"vertical-align: top;\"><B>Dead Data Stores</B><br></td>");
       out.print("</tr>");
       int i = 0;
       int min = (live.size() > dead.size()) ? dead.size() : live.size();
       int max = (live.size() > dead.size()) ? live.size() : dead.size();
+      currentTime = System.currentTimeMillis();
       for (i = 0; i < min; i++) {
         DatanodeInfo l = (DatanodeInfo)live.elementAt(i);
         DatanodeInfo d = (DatanodeInfo)dead.elementAt(i);
@@ -87,15 +91,17 @@
 
 <html>
 
-<title>Hadoop DFS Health/Status</title>
+<title>Hadoop NameNode <%=namenodeLabel%></title>
 
 <body>
 <h1>NameNode '<%=namenodeLabel%>'</h1>
 
-This NameNode has been up since <%= fsn.getStartTime()%>.<br>
+This NameNode has been up since <%= fsn.getStartTime()%>.<br><br>
+<b><a href="/nn_browsedfscontent.jsp">Browse the filesystem</a></b>
 <hr>
 <h2>Cluster Summary</h2>
 The capacity of this cluster is <%= totalCapacity()%> and remaining is <%= totalRemaining()%>.
+<br>
 <% 
    generateDFSHealthReport(out); 
 %>

+ 1 - 1
src/webapps/dfs/index.html

@@ -1,3 +1,4 @@
+<meta HTTP-EQUIV="REFRESH" content="0;url=dfshealth.jsp"/>
 <html>
 
 <head>
@@ -11,7 +12,6 @@
 <ul>
 
 <li><a href="dfshealth.jsp">DFS Health/Status</a></li>
-<li><a href="nn_browsedfscontent.jsp">Browse DFS Content</a></li>
 
 </ul>
 

+ 4 - 1
src/webapps/dfs/nn_browsedfscontent.jsp

@@ -7,6 +7,7 @@
   import="org.apache.hadoop.dfs.*"
   import="java.text.DateFormat"
   import="java.net.InetAddress"
+  import="java.net.URLEncoder"
 %>
 <%!
   FSNamesystem fsn = FSNamesystem.getFSNamesystem();
@@ -25,7 +26,9 @@
     }
     String fqdn = InetAddress.getByName(nodeToRedirect).getCanonicalHostName();
     redirectLocation = "http://" + fqdn + ":" + redirectPort + 
-                       "/browseDirectory.jsp?dir=/";
+                       "/browseDirectory.jsp?namenodeInfoPort=" + 
+                       fsn.getNameNodeInfoPort() +
+                       "&dir=" + URLEncoder.encode("/", "UTF-8");
     resp.sendRedirect(redirectLocation);
   }
 %>