tools: nfd-status-http-server: serve XML

refs #1690

Change-Id: Ie5de0b8e5041f91cf9bdf1ec7e2c24cde5ffbe38
diff --git a/tools/nfd-status-http-server-files/nfd-status.xsl b/tools/nfd-status-http-server-files/nfd-status.xsl
new file mode 100644
index 0000000..2111ffe
--- /dev/null
+++ b/tools/nfd-status-http-server-files/nfd-status.xsl
@@ -0,0 +1,148 @@
+<xsl:stylesheet version="1.0"
+xmlns="http://www.w3.org/1999/xhtml"
+xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+xmlns:nfd="ndn:/localhost/nfd/status/1">
+
+<xsl:template match="/">
+  <html>
+  <head>
+  <title>NFD Status</title>
+  </head>
+  <body>
+  <xsl:apply-templates/>
+  </body>
+  </html>
+</xsl:template>
+
+<xsl:template match="nfd:generalStatus">
+  <h2>General NFD status</h2>
+  <table>
+    <tr>
+      <td>Version</td>
+      <td><xsl:value-of select="nfd:version"/></td>
+    </tr>
+    <tr>
+      <td>startTime</td>
+      <td><xsl:value-of select="nfd:startTime"/></td>
+    </tr>
+    <tr>
+      <td>currentTime</td>
+      <td><xsl:value-of select="nfd:currentTime"/></td>
+    </tr>
+    <tr>
+      <td>upTime</td>
+      <td><xsl:value-of select="nfd:uptime"/></td>
+    </tr>
+    <tr>
+      <td>nNameTreeEntries</td>
+      <td><xsl:value-of select="nfd:nNameTreeEntries"/></td>
+    </tr>
+    <tr>
+      <td>nFibEntries</td>
+      <td><xsl:value-of select="nfd:nFibEntries"/></td>
+    </tr>
+    <tr>
+      <td>nPitEntries</td>
+      <td><xsl:value-of select="nfd:nPitEntries"/></td>
+    </tr>
+    <tr>
+      <td>nMeasurementsEntries</td>
+      <td><xsl:value-of select="nfd:nMeasurementsEntries"/></td>
+    </tr>
+    <tr>
+      <td>nCsEntries</td>
+      <td><xsl:value-of select="nfd:nCsEntries"/></td>
+    </tr>
+    <tr>
+      <td>nInInterests</td>
+      <td><xsl:value-of select="nfd:packetCounters/nfd:incomingPackets/nfd:nInterests"/></td>
+    </tr>
+    <tr>
+      <td>nOutInterests</td>
+      <td><xsl:value-of select="nfd:packetCounters/nfd:outgoingPackets/nfd:nInterests"/></td>
+    </tr>
+    <tr>
+      <td>nInDatas</td>
+      <td><xsl:value-of select="nfd:packetCounters/nfd:incomingPackets/nfd:nDatas"/></td>
+    </tr>
+    <tr>
+      <td>nOutDatas</td>
+      <td><xsl:value-of select="nfd:packetCounters/nfd:outgoingPackets/nfd:nDatas"/></td>
+    </tr>
+  </table>
+</xsl:template>
+
+<xsl:template match="nfd:channels">
+  <h2>Channels</h2>
+  <table>
+    <xsl:for-each select="nfd:channel">
+    <tr>
+      <td><xsl:value-of select="nfd:localUri"/></td>
+    </tr>
+    </xsl:for-each>
+  </table>
+</xsl:template>
+
+<xsl:template match="nfd:faces">
+  <h2>Faces</h2>
+  <table>
+    <tr style="background-color: #9acd32;">
+      <th>faceID</th>
+      <th>remoteUri</th>
+      <th>localUri</th>
+      <th>nInInterests</th>
+      <th>nInDatas</th>
+      <th>nOutInterests</th>
+      <th>nOutDatas</th>
+    </tr>
+    <xsl:for-each select="nfd:face">
+    <tr>
+      <td><xsl:value-of select="nfd:faceId"/></td>
+      <td><xsl:value-of select="nfd:remoteUri"/></td>
+      <td><xsl:value-of select="nfd:localUri"/></td>
+      <td><xsl:value-of select="nfd:packetCounters/nfd:incomingPackets/nfd:nInterests"/></td>
+      <td><xsl:value-of select="nfd:packetCounters/nfd:incomingPackets/nfd:nDatas"/></td>
+      <td><xsl:value-of select="nfd:packetCounters/nfd:outgoingPackets/nfd:nInterests"/></td>
+      <td><xsl:value-of select="nfd:packetCounters/nfd:outgoingPackets/nfd:nDatas"/></td>
+    </tr>
+    </xsl:for-each>
+  </table>
+</xsl:template>
+
+<xsl:template match="nfd:fib">
+  <h2>FIB</h2>
+  <table>
+    <tr style="background-color: #9acd32;">
+      <th>prefix</th>
+      <th>nextHops</th>
+    </tr>
+    <xsl:for-each select="nfd:fibEntry">
+    <tr>
+      <td style="text-align:left;vertical-align:top;padding:0"><xsl:value-of select="nfd:prefix"/></td>
+      <td>
+      <xsl:for-each select="nfd:nextHops/nfd:nextHop">
+        faceid=<xsl:value-of select="nfd:faceId"/> (cost=<xsl:value-of select="nfd:cost"/>);
+      </xsl:for-each>
+      </td>
+    </tr>
+    </xsl:for-each>
+  </table>
+</xsl:template>
+
+<xsl:template match="nfd:strategyChoices">
+  <h2>Strategy Choices</h2>
+  <table>
+    <tr style="background-color: #9acd32;">
+      <th>Namespace</th>
+      <th>Strategy Name</th>
+    </tr>
+    <xsl:for-each select="nfd:strategyChoice">
+    <tr>
+      <td><xsl:value-of select="nfd:namespace"/></td>
+      <td><xsl:value-of select="nfd:strategy/nfd:name"/></td>
+    </tr>
+    </xsl:for-each>
+  </table>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/tools/nfd-status-http-server-files/robots.txt b/tools/nfd-status-http-server-files/robots.txt
new file mode 100644
index 0000000..1f53798
--- /dev/null
+++ b/tools/nfd-status-http-server-files/robots.txt
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow: /
diff --git a/tools/nfd-status-http-server.py b/tools/nfd-status-http-server.py
index 6e0f167..33adb48 100755
--- a/tools/nfd-status-http-server.py
+++ b/tools/nfd-status-http-server.py
@@ -24,7 +24,8 @@
 NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
 """
 
-from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
+from BaseHTTPServer import HTTPServer
+from SimpleHTTPServer import SimpleHTTPRequestHandler
 from SocketServer import ThreadingMixIn
 import sys
 import commands
@@ -34,84 +35,63 @@
 import cgi
 import argparse
 import socket
+import os
 
 
-class StatusHandler(BaseHTTPRequestHandler):
+class StatusHandler(SimpleHTTPRequestHandler):
     """ The handler class to handle requests."""
     def do_GET(self):
         # get the url info to decide how to respond
         parsedPath = urlparse.urlparse(self.path)
-        resultMessage = ""
-        if parsedPath.path == "/robots.txt":
-            if self.server.robots == False:
-                # return User-agent: * Disallow: / to disallow robots
-                resultMessage = "User-agent: * \nDisallow: /\n"
+        if parsedPath.path == "/":
+            # get current nfd status, and use it as result message
+            (res, resultMessage) = self.getNfdStatus()
+            self.send_response(200)
+            if res == 0:
+                self.send_header("Content-type", "text/xml; charset=UTF-8")
+            else:
+                self.send_header("Content-type", "text/html; charset=UTF-8")
+
+            self.end_headers()
+            self.wfile.write(resultMessage)
+        elif parsedPath.path == "/robots.txt" and self.server.robots == True:
+            resultMessage = ""
             self.send_response(200)
             self.send_header("Content-type", "text/plain")
-        elif parsedPath.path == "/":
-            # get current nfd status, and use it as result message
-            resultMessage = self.getNfdStatus()
-            self.send_response(200)
-            self.send_header("Content-type", "text/html; charset=UTF-8")
+            self.end_headers()
+            self.wfile.write(resultMessage)
         else:
-            # non-existing content
-            resultMessage = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 '\
-                   + 'Transitional//EN" '\
-                   + '"http://www.w3.org/TR/html4/loose.dtd">\n'\
-                   + '<html><head><title>Object not '\
-                   + 'found!</title><meta http-equiv="Content-type" ' \
-                   + 'content="text/html;charset=UTF-8"></head>\n'\
-                   + '<body><h1>Object not found!</h1>'\
-                   + '<p>The requested URL was not found on this server.</p>'\
-                   + '<h2>Error 404</h2>'\
-                   + '</body></html>'
-            self.send_response(404)
-            self.send_header("Content-type", "text/html; charset=UTF-8")
-
-        self.end_headers()
-        self.wfile.write(resultMessage)
+            SimpleHTTPRequestHandler.do_GET(self)
 
     def log_message(self, format, *args):
         if self.server.verbose:
             logging.info("%s - %s\n" % (self.address_string(),
-                         format % args))
+                        format % args))
 
     def getNfdStatus(self):
         """
-        This function tries to call nfd-status command
-        to get nfd's current status, after convert it
-        to html format, return it to the caller
+        This function is to call nfd-status command
+        to get xml format output
         """
-        (res, output) = commands.getstatusoutput('nfd-status')
-
-        htmlStr = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional'\
-                + '//EN" "http://www.w3.org/TR/html4/loose.dtd">\n'\
-                + '<html><head><title>NFD status</title>'\
-                + '<meta http-equiv="Content-type" content="text/html;'\
-                + 'charset=UTF-8"></head>\n<body>'
+        (res, output) = commands.getstatusoutput('nfd-status -x')
         if res == 0:
-            # parse the output
-            buf = StringIO.StringIO(output)
-            firstLine = 0
-            for line in buf:
-                if line.endswith(":\n"):
-                    if firstLine != 0:
-                        htmlStr += "</ul>\n"
-                    firstLine += 1
-                    htmlStr += "<b>" + cgi.escape(line.strip()) + "</b><br>\n"\
-                             + "<ul>\n"
-                    continue
-                line = line.strip()
-                htmlStr += "<li>" + cgi.escape(line) + "</li>\n"
-            buf.close()
-            htmlStr += "</ul>\n"
+            # add the xml-stylesheet processing instruction after the 1st '>' symbol
+            newLineIndex = output.index('>') + 1
+            resultStr = output[:newLineIndex]\
+                      + "<?xml-stylesheet type=\"text/xsl\" href=\"nfd-status.xsl\"?>"\
+                      + output[newLineIndex:]
+            return (res, resultStr)
         else:
+            htmlStr = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional'\
+                    + '//EN" "http://www.w3.org/TR/html4/loose.dtd">\n'\
+                    + '<html><head><title>NFD status</title>'\
+                    + '<meta http-equiv="Content-type" content="text/html;'\
+                    + 'charset=UTF-8"></head>\n<body>'
             # return connection error code
             htmlStr = htmlStr + "<p>Cannot connect to NFD,"\
                     + " Code = " + str(res) + "</p>\n"
-        htmlStr = htmlStr + "</body></html>"
-        return htmlStr
-
+            htmlStr = htmlStr + "</body></html>"
+            return (res, htmlStr)
 
 class ThreadHttpServer(ThreadingMixIn, HTTPServer):
     """ Handle requests using threads """
@@ -162,6 +142,8 @@
                         help="Specify the HTTP server IP address.")
     parser.add_argument("-r", default=False, dest="robots", action="store_true",
                         help="Enable HTTP robots to crawl; disabled by default.")
+    parser.add_argument("-f", default="/usr/local/share/ndn/", metavar="Server Directory", dest="serverDir",
+                        help="Specify the working directory of nfd-status-http-server, default is /usr/local/share/ndn.")
     parser.add_argument("-v", default=False, dest="verbose", action="store_true",
                         help="Verbose mode.")
     parser.add_argument("--version", default=False, dest="version", action="store_true",
@@ -177,6 +159,9 @@
     localAddr = args["addr"]
     verbose = args["verbose"]
     robots = args["robots"]
+    serverDirectory = args["serverDir"]
+
+    os.chdir(serverDirectory)
 
     # setting log message format
     logging.basicConfig(format='%(asctime)s [%(levelname)s] %(message)s',
diff --git a/wscript b/wscript
index 93c6a2d..553529b 100644
--- a/wscript
+++ b/wscript
@@ -190,6 +190,13 @@
         IF_HAVE_LIBPCAP="" if bld.env['HAVE_LIBPCAP'] else "; ",
         IF_HAVE_WEBSOCKET="" if bld.env['HAVE_WEBSOCKET'] else "; ")
 
+    for file in bld.path.ant_glob('tools/nfd-status-http-server-files/*'):
+        bld(features="subst",
+            source='tools/nfd-status-http-server-files/%s' % (str(file)),
+            target='nfd-status-http-server/%s' % (str(file)),
+            install_path="${DATAROOTDIR}/ndn",
+            VERSION=VERSION)
+
     bld(features='subst',
         source='tools/nfd-status-http-server.py',
         target='bin/nfd-status-http-server',