tools: visualize RIB dataset in nfd-status

refs #1749

Change-Id: Ia11b88187635ffa4eda4b829afd6a20120765d58
diff --git a/docs/_static/nfd-status.xsd b/docs/_static/nfd-status.xsd
index 829ba7c..425bbd8 100644
--- a/docs/_static/nfd-status.xsd
+++ b/docs/_static/nfd-status.xsd
@@ -47,8 +47,8 @@
 
 <xs:complexType name="faceFlagsType">
   <xs:sequence>
-    <xs:element type="xs:string" name="local" maxOccurs="1" minOccurs="0"/>
-    <xs:element type="xs:string" name="on-demand" maxOccurs="1" minOccurs="0"/>
+    <xs:element type="xs:string" name="local" minOccurs="0"/>
+    <xs:element type="xs:string" name="on-demand" minOccurs="0"/>
   </xs:sequence>
 </xs:complexType>
 
@@ -57,9 +57,9 @@
     <xs:element type="xs:nonNegativeInteger" name="faceId"/>
     <xs:element type="xs:anyURI" name="remoteUri"/>
     <xs:element type="xs:anyURI" name="localUri"/>
-    <xs:element type="xs:duration" name="expirationPeriod" maxOccurs="1" minOccurs="0"/>
+    <xs:element type="xs:duration" name="expirationPeriod" minOccurs="0"/>
     <xs:element type="nfd:bidirectionalPacketCountersType" name="packetCounters"/>
-    <xs:element type="nfd:faceFlagsType" name="flags" maxOccurs="1" minOccurs="0"/>
+    <xs:element type="nfd:faceFlagsType" name="flags" minOccurs="0"/>
   </xs:sequence>
 </xs:complexType>
 
@@ -95,6 +95,35 @@
   </xs:sequence>
 </xs:complexType>
 
+<xs:complexType name="routeType">
+  <xs:sequence>
+    <xs:element type="xs:nonNegativeInteger" name="faceId"/>
+    <xs:element type="xs:nonNegativeInteger" name="origin"/>
+    <xs:element type="xs:nonNegativeInteger" name="cost"/>
+    <xs:element type="xs:nonNegativeInteger" name="flags"/>
+    <xs:element type="xs:duration" name="expirationPeriod" minOccurs="0"/>
+  </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="ribEntryType">
+  <xs:sequence>
+    <xs:element type="xs:anyURI" name="prefix"/>
+    <xs:element name="routes">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element type="nfd:routeType" name="route" maxOccurs="unbounded"/>
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+  </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="ribType">
+  <xs:sequence>
+    <xs:element type="nfd:ribEntryType" name="ribEntry" maxOccurs="unbounded" minOccurs="0"/>
+  </xs:sequence>
+</xs:complexType>
+
 <xs:complexType name="strategyType">
   <xs:sequence>
     <xs:element type="xs:anyURI" name="name"/>
@@ -122,6 +151,7 @@
       <xs:element type="nfd:channelsType" name="channels"/>
       <xs:element type="nfd:facesType" name="faces"/>
       <xs:element type="nfd:fibType" name="fib"/>
+      <xs:element type="nfd:ribType" name="rib"/>
       <xs:element type="nfd:strategyChoicesType" name="strategyChoices"/>
     </xs:sequence>
   </xs:complexType>
diff --git a/docs/manpages/nfd-status.rst b/docs/manpages/nfd-status.rst
index 5a52ae2..217fca5 100644
--- a/docs/manpages/nfd-status.rst
+++ b/docs/manpages/nfd-status.rst
@@ -31,6 +31,9 @@
 ``-b``
   Retrieve FIB information.
 
+``-r``
+  Retrieve RIB information.
+
 ``-s``
   Retrieve configured strategy choice for NDN namespaces.
 
diff --git a/tools/nfd-status-http-server-files/nfd-status.xsl b/tools/nfd-status-http-server-files/nfd-status.xsl
index fb25599..37038c5 100644
--- a/tools/nfd-status-http-server-files/nfd-status.xsl
+++ b/tools/nfd-status-http-server-files/nfd-status.xsl
@@ -176,7 +176,7 @@
   <table class="item-list">
     <thead>
       <tr>
-        <th>Prefix</th>
+        <th width="20%">Prefix</th>
         <th>NextHops</th>
       </tr>
     </thead>
@@ -193,9 +193,90 @@
         <tr class="{$style}">
         <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>
+          <table class="item-sublist">
+            <tr>
+              <th>FaceId</th>
+              <xsl:for-each select="nfd:nextHops/nfd:nextHop">
+                <td><xsl:value-of select="nfd:faceId"/></td>
+              </xsl:for-each>
+            </tr>
+            <tr>
+              <th>Cost</th>
+              <xsl:for-each select="nfd:nextHops/nfd:nextHop">
+                <td><xsl:value-of select="nfd:cost"/></td>
+              </xsl:for-each>
+            </tr>
+          </table>
+        </td>
+      </tr>
+      </xsl:for-each>
+    </tbody>
+  </table>
+</xsl:template>
+
+<xsl:template match="nfd:rib">
+  <h2>RIB</h2>
+  <table class="item-list">
+    <thead>
+      <tr>
+        <th width="20%">Prefix</th>
+        <th>Routes</th>
+      </tr>
+    </thead>
+    <tbody>
+      <xsl:for-each select="nfd:ribEntry">
+        <xsl:variable name="style">
+          <xsl:choose>
+            <xsl:when test="position() mod 2 = 1">
+              <xsl:text>odd</xsl:text>
+            </xsl:when>
+            <xsl:otherwise>even</xsl:otherwise>
+          </xsl:choose>
+        </xsl:variable>
+        <tr class="{$style}">
+        <td style="text-align:left;vertical-align:top;padding:0"><xsl:value-of select="nfd:prefix"/></td>
+        <td>
+          <table class="item-sublist">
+            <tr>
+              <th>FaceId</th>
+              <xsl:for-each select="nfd:routes/nfd:route">
+                <td><xsl:value-of select="nfd:faceId"/></td>
+              </xsl:for-each>
+            </tr>
+            <tr>
+              <th>Origin</th>
+              <xsl:for-each select="nfd:routes/nfd:route">
+                <td><xsl:value-of select="nfd:origin"/></td>
+              </xsl:for-each>
+            </tr>
+            <tr>
+              <th>Cost</th>
+              <xsl:for-each select="nfd:routes/nfd:route">
+                <td><xsl:value-of select="nfd:cost"/></td>
+              </xsl:for-each>
+            </tr>
+            <tr>
+              <th>Flags</th>
+              <xsl:for-each select="nfd:routes/nfd:route">
+                <td><xsl:value-of select="nfd:flags"/></td>
+              </xsl:for-each>
+            </tr>
+            <tr>
+              <th>Expires in</th>
+              <xsl:for-each select="nfd:routes/nfd:route">
+                <td>
+                  <xsl:choose>
+                    <xsl:when test="nfd:expirationPeriod">
+                      <xsl:call-template name="formatDuration"><xsl:with-param name="duration" select="nfd:expirationPeriod" /></xsl:call-template>
+                    </xsl:when>
+                    <xsl:otherwise>
+                      Never
+                    </xsl:otherwise>
+                  </xsl:choose>
+                </td>
+              </xsl:for-each>
+            </tr>
+          </table>
         </td>
       </tr>
       </xsl:for-each>
@@ -208,7 +289,7 @@
   <table class="item-list">
     <thead>
       <tr>
-        <th>Namespace</th>
+        <th width="20%">Namespace</th>
         <th>Strategy Name</th>
       </tr>
     </thead>
diff --git a/tools/nfd-status-http-server-files/style.css b/tools/nfd-status-http-server-files/style.css
index f2b99d5..176da83 100644
--- a/tools/nfd-status-http-server-files/style.css
+++ b/tools/nfd-status-http-server-files/style.css
@@ -114,6 +114,43 @@
     border-top: 1px solid transparent;
 }
 
+.item-sublist
+{
+    border-collapse:collapse;
+    border:1px solid gray;
+
+    padding: 4px;
+    font-family: sans-serif;
+    text-align: center;
+    margin-bottom: 0;
+}
+
+.item-sublist th
+{
+    background-color: transparent;
+    font-weight: bold;
+    padding: 4px;
+    text-align: center;
+
+    border-collapse:collapse;
+    border:1px solid gray;
+}
+
+.item-sublist th.border-left {
+}
+
+.item-sublist td
+{
+    border-collapse:collapse;
+    border:1px solid gray;
+
+    padding: 2px;
+    color: #000;
+    text-align: center;
+    min-width: 20px;
+}
+
+
 tr.center td
 {
     text-align: center;
diff --git a/tools/nfd-status-http-server.py b/tools/nfd-status-http-server.py
index 133cc37..f127d40 100755
--- a/tools/nfd-status-http-server.py
+++ b/tools/nfd-status-http-server.py
@@ -28,7 +28,7 @@
 from SimpleHTTPServer import SimpleHTTPRequestHandler
 from SocketServer import ThreadingMixIn
 import sys
-import commands
+import subprocess
 import StringIO
 import urlparse
 import logging
@@ -73,9 +73,11 @@
         This function is to call nfd-status command
         to get xml format output
         """
-        (res, output) = commands.getstatusoutput('nfd-status -x')
+        sp = subprocess.Popen(['nfd-status', '-x'], stdout=subprocess.PIPE)
+        res = sp.wait()
         if res == 0:
             # add the xml-stylesheet processing instruction after the 1st '>' symbol
+            output = sp.stdout.read()
             newLineIndex = output.index('>') + 1
             resultStr = output[:newLineIndex]\
                       + "<?xml-stylesheet type=\"text/xsl\" href=\"nfd-status.xsl\"?>"\
diff --git a/tools/nfd-status.cpp b/tools/nfd-status.cpp
index d06e8fb..affd2d8 100644
--- a/tools/nfd-status.cpp
+++ b/tools/nfd-status.cpp
@@ -36,6 +36,7 @@
 #include <ndn-cxx/management/nfd-channel-status.hpp>
 #include <ndn-cxx/management/nfd-face-status.hpp>
 #include <ndn-cxx/management/nfd-fib-entry.hpp>
+#include <ndn-cxx/management/nfd-rib-entry.hpp>
 #include <ndn-cxx/management/nfd-strategy-choice.hpp>
 
 #include <boost/algorithm/string/replace.hpp>
@@ -53,6 +54,7 @@
     , m_needChannelStatusRetrieval(false)
     , m_needFaceStatusRetrieval(false)
     , m_needFibEnumerationRetrieval(false)
+    , m_needRibStatusRetrieval(false)
     , m_needStrategyChoiceRetrieval(false)
     , m_isOutputXml(false)
   {
@@ -69,6 +71,7 @@
       "  [-c] - retrieve channel status information\n"
       "  [-f] - retrieve face status information\n"
       "  [-b] - retrieve FIB information\n"
+      "  [-r] - retrieve RIB information\n"
       "  [-s] - retrieve configured strategy choice for NDN namespaces\n"
       "  [-x] - output NFD status information in XML format\n"
       "\n"
@@ -110,6 +113,12 @@
   }
 
   void
+  enableRibStatusRetrieval()
+  {
+    m_needRibStatusRetrieval = true;
+  }
+
+  void
   enableXmlOutput()
   {
     m_isOutputXml = true;
@@ -381,13 +390,13 @@
             std::cout << "<incomingPackets>";
             std::cout << "<nInterests>"       << faceStatus.getNInInterests()
                       << "</nInterests>";
-            std::cout << "<nDatas>"           << faceStatus.getNInInterests()
+            std::cout << "<nDatas>"           << faceStatus.getNInDatas()
                       << "</nDatas>";
             std::cout << "</incomingPackets>";
             std::cout << "<outgoingPackets>";
             std::cout << "<nInterests>"       << faceStatus.getNOutInterests()
                       << "</nInterests>";
-            std::cout << "<nDatas>"           << faceStatus.getNOutInterests()
+            std::cout << "<nDatas>"           << faceStatus.getNOutDatas()
                       << "</nDatas>";
             std::cout << "</outgoingPackets>";
             std::cout << "</packetCounters>";
@@ -629,6 +638,118 @@
   }
 
   void
+  fetchRibStatusInformation()
+  {
+    m_buffer = make_shared<OBufferStream>();
+
+    Interest interest("/localhost/nfd/rib/list");
+    interest.setChildSelector(1);
+    interest.setMustBeFresh(true);
+
+    m_face.expressInterest(interest,
+                           bind(&NfdStatus::fetchSegments, this, _2,
+                                &NfdStatus::afterFetchedRibStatusInformation),
+                           bind(&NfdStatus::onTimeout, this));
+  }
+
+  void
+  afterFetchedRibStatusInformation()
+  {
+    ConstBufferPtr buf = m_buffer->buf();
+    if (m_isOutputXml)
+      {
+        std::cout << "<rib>";
+
+        Block block;
+        size_t offset = 0;
+        while (offset < buf->size())
+          {
+            bool ok = Block::fromBuffer(buf, offset, block);
+            if (!ok)
+              {
+                std::cerr << "ERROR: cannot decode RibEntry TLV";
+                break;
+              }
+            offset += block.size();
+
+            nfd::RibEntry ribEntry(block);
+
+            std::cout << "<ribEntry>";
+            std::string prefix(ribEntry.getName().toUri());
+            escapeSpecialCharacters(&prefix);
+            std::cout << "<prefix>" << prefix << "</prefix>";
+            std::cout << "<routes>";
+            for (std::list<nfd::Route>::const_iterator
+                   nextRoute = ribEntry.begin();
+                 nextRoute != ribEntry.end();
+                 ++nextRoute)
+              {
+                std::cout << "<route>" ;
+                std::cout << "<faceId>"  << nextRoute->getFaceId() << "</faceId>";
+                std::cout << "<origin>"  << nextRoute->getOrigin() << "</origin>";
+                std::cout << "<cost>"    << nextRoute->getCost()   << "</cost>";
+                std::cout << "<flags>"   << nextRoute->getFlags()  << "</flags>";
+                if (!nextRoute->hasInfiniteExpirationPeriod()) {
+                  std::cout << "<expirationPeriod>PT"
+                            << time::duration_cast<time::seconds>(nextRoute->getExpirationPeriod())
+                                 .count() << "S"
+                            << "</expirationPeriod>";
+                }
+                std::cout << "</route>";
+              }
+            std::cout << "</routes>";
+            std::cout << "</ribEntry>";
+          }
+
+        std::cout << "</rib>";
+      }
+    else
+      {
+        std::cout << "Rib:" << std::endl;
+
+        Block block;
+        size_t offset = 0;
+        while (offset < buf->size())
+          {
+            bool ok = Block::fromBuffer(buf, offset, block);
+            if (!ok)
+              {
+                std::cerr << "ERROR: cannot decode RibEntry TLV" << std::endl;
+                break;
+              }
+
+            offset += block.size();
+
+            nfd::RibEntry ribEntry(block);
+
+            std::cout << " " << ribEntry.getName().toUri() << " route={";
+            for (std::list<nfd::Route>::const_iterator
+                   nextRoute = ribEntry.begin();
+                 nextRoute != ribEntry.end();
+                 ++nextRoute)
+              {
+                if (nextRoute != ribEntry.begin())
+                  std::cout << ", ";
+                std::cout << "faceid="   << nextRoute->getFaceId()
+                          << " (origin="  << nextRoute->getOrigin()
+                          << " cost="    << nextRoute->getCost()
+                          << " flags="   << nextRoute->getFlags();
+                if (!nextRoute->hasInfiniteExpirationPeriod()) {
+                  std::cout << " expires="
+                            << time::duration_cast<time::seconds>(nextRoute->getExpirationPeriod())
+                               .count() << "s";
+                }
+                std::cout << ")";
+              }
+            std::cout << "}" << std::endl;
+          }
+       }
+
+    runNextStep();
+  }
+
+
+  void
   fetchInformation()
   {
     if (m_isOutputXml ||
@@ -636,12 +757,14 @@
          !m_needChannelStatusRetrieval &&
          !m_needFaceStatusRetrieval &&
          !m_needFibEnumerationRetrieval &&
+         !m_needRibStatusRetrieval &&
          !m_needStrategyChoiceRetrieval))
       {
         enableVersionRetrieval();
         enableChannelStatusRetrieval();
         enableFaceStatusRetrieval();
         enableFibEnumerationRetrieval();
+        enableRibStatusRetrieval();
         enableStrategyChoiceRetrieval();
       }
 
@@ -660,6 +783,9 @@
     if (m_needFibEnumerationRetrieval)
       m_fetchSteps.push_back(bind(&NfdStatus::fetchFibEnumerationInformation, this));
 
+    if (m_needRibStatusRetrieval)
+      m_fetchSteps.push_back(bind(&NfdStatus::fetchRibStatusInformation, this));
+
     if (m_needStrategyChoiceRetrieval)
       m_fetchSteps.push_back(bind(&NfdStatus::fetchStrategyChoiceInformation, this));
 
@@ -705,6 +831,7 @@
   bool m_needChannelStatusRetrieval;
   bool m_needFaceStatusRetrieval;
   bool m_needFibEnumerationRetrieval;
+  bool m_needRibStatusRetrieval;
   bool m_needStrategyChoiceRetrieval;
   bool m_isOutputXml;
   Face m_face;
@@ -721,7 +848,7 @@
   int option;
   ndn::NfdStatus nfdStatus(argv[0]);
 
-  while ((option = getopt(argc, argv, "hvcfbsxV")) != -1) {
+  while ((option = getopt(argc, argv, "hvcfbrsxV")) != -1) {
     switch (option) {
     case 'h':
       nfdStatus.usage();
@@ -738,6 +865,9 @@
     case 'b':
       nfdStatus.enableFibEnumerationRetrieval();
       break;
+    case 'r':
+      nfdStatus.enableRibStatusRetrieval();
+      break;
     case 's':
       nfdStatus.enableStrategyChoiceRetrieval();
       break;