nfd-status: Define format and implement --xml option
The ether URI format "ether://01:00:5e:00:17:aa" violates
the URI specification, so it has been changed to
ether://[01:00:5e:00:17:aa] in this commit.
refs: #1438
Change-Id: I1218374b75f919a5e1ab158bbab2e217e64aced3
diff --git a/core/face-uri.cpp b/core/face-uri.cpp
index 7eaaddd..1b2092c 100644
--- a/core/face-uri.cpp
+++ b/core/face-uri.cpp
@@ -70,7 +70,7 @@
// pattern for IPv6 address enclosed in [ ], with optional port number
static const boost::regex v6Exp("^\\[([a-fA-F0-9:]+)\\](?:\\:(\\d+))?$");
// pattern for Ethernet address in standard hex-digits-and-colons notation
- static const boost::regex etherExp("^((?:[a-fA-F0-9]{1,2}\\:){5}(?:[a-fA-F0-9]{1,2}))$");
+ static const boost::regex etherExp("^\\[((?:[a-fA-F0-9]{1,2}\\:){5}(?:[a-fA-F0-9]{1,2}))\\]$");
// pattern for IPv4/hostname/fd/ifname, with optional port number
static const boost::regex v4HostExp("^([^:]+)(?:\\:(\\d+))?$");
@@ -132,7 +132,7 @@
#ifdef HAVE_LIBPCAP
FaceUri::FaceUri(const ethernet::Address& address)
- : m_isV6(false)
+ : m_isV6(true)
{
m_scheme = "ether";
m_host = address.toString();
diff --git a/docs/_static/nfd-status.xsd b/docs/_static/nfd-status.xsd
new file mode 100644
index 0000000..44b5e93
--- /dev/null
+++ b/docs/_static/nfd-status.xsd
@@ -0,0 +1,87 @@
+<?xml version="1.0"?>
+
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+targetNamespace="ndn:/localhost/nfd/status/1" xmlns:nfd="ndn:/localhost/nfd/status/1"
+elementFormDefault="qualified">
+
+<xs:complexType name="unidirectionalPacketCountersType">
+ <xs:sequence>
+ <xs:element type="xs:nonNegativeInteger" name="nInterests"/>
+ <xs:element type="xs:nonNegativeInteger" name="nDatas"/>
+ </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="bidirectionalPacketCountersType">
+ <xs:sequence>
+ <xs:element type="nfd:unidirectionalPacketCountersType" name="incomingPackets"/>
+ <xs:element type="nfd:unidirectionalPacketCountersType" name="outgoingPackets"/>
+ </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="generalStatusType">
+ <xs:sequence>
+ <xs:element type="xs:string" name="version"/>
+ <xs:element type="xs:dateTime" name="startTime"/>
+ <xs:element type="xs:dateTime" name="currentTime"/>
+ <xs:element type="xs:duration" name="uptime"/>
+ <xs:element type="xs:nonNegativeInteger" name="nNameTreeEntries"/>
+ <xs:element type="xs:nonNegativeInteger" name="nFibEntries"/>
+ <xs:element type="xs:nonNegativeInteger" name="nPitEntries"/>
+ <xs:element type="xs:nonNegativeInteger" name="nMeasurementsEntries"/>
+ <xs:element type="xs:nonNegativeInteger" name="nCsEntries"/>
+ <xs:element type="nfd:bidirectionalPacketCountersType" name="packetCounters"/>
+ </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="faceType">
+ <xs:sequence>
+ <xs:element type="xs:nonNegativeInteger" name="faceId"/>
+ <xs:element type="xs:anyURI" name="remoteUri"/>
+ <xs:element type="xs:anyURI" name="localUri"/>
+ <xs:element type="nfd:bidirectionalPacketCountersType" name="packetCounters"/>
+ </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="facesType">
+ <xs:sequence>
+ <xs:element type="nfd:faceType" name="face" maxOccurs="unbounded" minOccurs="0"/>
+ </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="nextHopType">
+ <xs:sequence>
+ <xs:element type="xs:nonNegativeInteger" name="faceId"/>
+ <xs:element type="xs:nonNegativeInteger" name="cost"/>
+ </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="fibEntryType">
+ <xs:sequence>
+ <xs:element type="xs:anyURI" name="prefix"/>
+ <xs:element name="nextHops">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element type="nfd:nextHopType" name="nextHop" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="fibType">
+ <xs:sequence>
+ <xs:element type="nfd:fibEntryType" name="fibEntry" maxOccurs="unbounded" minOccurs="0"/>
+ </xs:sequence>
+</xs:complexType>
+
+<xs:element name="nfdStatus">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element type="nfd:generalStatusType" name="generalStatus"/>
+ <xs:element type="nfd:facesType" name="faces"/>
+ <xs:element type="nfd:fibType" name="fib"/>
+ </xs:sequence>
+ </xs:complexType>
+</xs:element>
+
+</xs:schema>
diff --git a/docs/manpages.rst b/docs/manpages.rst
index 3b8a496..6a52897 100644
--- a/docs/manpages.rst
+++ b/docs/manpages.rst
@@ -8,6 +8,7 @@
manpages/nrd
manpages/nfdc
manpages/nfd-status
+ schema
manpages/nfd-status-http-server
manpages/ndn-autoconfig
manpages/ndn-autoconfig-server
diff --git a/docs/schema.rst b/docs/schema.rst
new file mode 100644
index 0000000..08e40b7
--- /dev/null
+++ b/docs/schema.rst
@@ -0,0 +1,10 @@
+NFD Status XML Schema
+=====================
+
+The NFD status XML schema describes the structure of the NFD status XML
+document generated by command ``nfd-status``.
+
+Users can use this schema to validate the generated XML document.
+
+.. literalinclude:: _static/nfd-status.xsd
+ :language: xml
diff --git a/tests/core/face-uri.cpp b/tests/core/face-uri.cpp
index 0e54f04..e1e07a9 100644
--- a/tests/core/face-uri.cpp
+++ b/tests/core/face-uri.cpp
@@ -156,7 +156,7 @@
{
FaceUri uri;
- BOOST_CHECK(uri.parse("ether://08:00:27:01:dd:01"));
+ BOOST_CHECK(uri.parse("ether://[08:00:27:01:dd:01]"));
BOOST_CHECK_EQUAL(uri.getScheme(), "ether");
BOOST_CHECK_EQUAL(uri.getHost(), "08:00:27:01:dd:01");
BOOST_CHECK_EQUAL(uri.getPort(), "");
@@ -167,7 +167,7 @@
#ifdef HAVE_LIBPCAP
ethernet::Address address = ethernet::Address::fromString("33:33:01:01:01:01");
BOOST_REQUIRE_NO_THROW(FaceUri(address));
- BOOST_CHECK_EQUAL(FaceUri(address).toString(), "ether://33:33:01:01:01:01");
+ BOOST_CHECK_EQUAL(FaceUri(address).toString(), "ether://[33:33:01:01:01:01]");
#endif // HAVE_LIBPCAP
}
diff --git a/tests/daemon/face/ethernet.cpp b/tests/daemon/face/ethernet.cpp
index 270abb0..936406b 100644
--- a/tests/daemon/face/ethernet.cpp
+++ b/tests/daemon/face/ethernet.cpp
@@ -118,7 +118,7 @@
BOOST_CHECK(!face->isOnDemand());
BOOST_CHECK_EQUAL(face->isLocal(), false);
BOOST_CHECK_EQUAL(face->getRemoteUri().toString(),
- "ether://" + ethernet::getDefaultMulticastAddress().toString());
+ "ether://[" + ethernet::getDefaultMulticastAddress().toString()+"]");
BOOST_CHECK_EQUAL(face->getLocalUri().toString(),
"dev://" + m_interfaces.front()->name);
diff --git a/tools/nfd-status.cpp b/tools/nfd-status.cpp
index 9e8fa28..1792961 100644
--- a/tools/nfd-status.cpp
+++ b/tools/nfd-status.cpp
@@ -46,6 +46,7 @@
, m_needVersionRetrieval(false)
, m_needFaceStatusRetrieval(false)
, m_needFibEnumerationRetrieval(false)
+ , m_needXmlDataRetrieval(false)
{
}
@@ -59,10 +60,12 @@
" [-v] - retrieve version information\n"
" [-f] - retrieve face status information\n"
" [-b] - retrieve FIB information\n"
+ " [-x] - retrieve NFD status information in XML format\n"
"\n"
" [-V] - show version information of nfd-status and exit\n"
"\n"
"If no options are provided, all information is retrieved.\n"
+ "If -x is provided, other options(v, f and b) are ignored, and all information is retrieved in XML format.\n"
;
}
@@ -85,6 +88,12 @@
}
void
+ enableXmlDataRetrieval()
+ {
+ m_needXmlDataRetrieval = true;
+ }
+
+ void
onTimeout()
{
std::cerr << "Request timed out" << std::endl;
@@ -112,32 +121,86 @@
}
}
+ void escapeSpecialCharacters(std::string *data)
+ {
+ using boost::algorithm::replace_all;
+ replace_all(*data, "&", "&");
+ replace_all(*data, "\"", """);
+ replace_all(*data, "\'", "'");
+ replace_all(*data, "<", "<");
+ replace_all(*data, ">", ">");
+ }
+
void
afterFetchedVersionInformation(const Data& data)
{
- std::cout << "General NFD status:" << std::endl;
-
nfd::ForwarderStatus status(data.getContent());
- std::cout << " version="
- << status.getNfdVersion() << std::endl;
- std::cout << " startTime="
- << time::toIsoString(status.getStartTimestamp()) << std::endl;
- std::cout << " currentTime="
- << time::toIsoString(status.getCurrentTimestamp()) << std::endl;
- std::cout << " uptime="
- << time::duration_cast<time::seconds>(status.getCurrentTimestamp()
- - status.getStartTimestamp()) << std::endl;
+ if (m_needXmlDataRetrieval)
+ {
+ std::cout << "<?xml version=\"1.0\"?>";
+ std::cout << "<nfdStatus xmlns=\"ndn:/localhost/nfd/status/1\">";
+ std::cout << "<generalStatus>";
+ std::cout << "<version>"
+ << status.getNfdVersion() << "</version>";
+ std::cout << "<startTime>"
+ << time::toString(status.getStartTimestamp(), "%Y-%m-%dT%H:%M:%S%F")
+ << "</startTime>";
+ std::cout << "<currentTime>"
+ << time::toString(status.getCurrentTimestamp(), "%Y-%m-%dT%H:%M:%S%F")
+ << "</currentTime>";
+ std::cout << "<uptime>PT"
+ << time::duration_cast<time::seconds>(status.getCurrentTimestamp()
+ - status.getStartTimestamp()).count()
+ << "S</uptime>";
+ std::cout << "<nNameTreeEntries>" << status.getNNameTreeEntries()
+ << "</nNameTreeEntries>";
+ std::cout << "<nFibEntries>" << status.getNFibEntries()
+ << "</nFibEntries>";
+ std::cout << "<nPitEntries>" << status.getNPitEntries()
+ << "</nPitEntries>";
+ std::cout << "<nMeasurementsEntries>" << status.getNMeasurementsEntries()
+ << "</nMeasurementsEntries>";
+ std::cout << "<nCsEntries>" << status.getNCsEntries()
+ << "</nCsEntries>";
+ std::cout << "<packetCounters>";
+ std::cout << "<incomingPackets>";
+ std::cout << "<nInterests>" << status.getNInInterests()
+ << "</nInterests>";
+ std::cout << "<nDatas>" << status.getNInDatas()
+ << "</nDatas>";
+ std::cout << "</incomingPackets>";
+ std::cout << "<outgoingPackets>";
+ std::cout << "<nInterests>" << status.getNOutInterests()
+ << "</nInterests>";
+ std::cout << "<nDatas>" << status.getNOutDatas()
+ << "</nDatas>";
+ std::cout << "</outgoingPackets>";
+ std::cout << "</packetCounters>";
+ std::cout << "</generalStatus>";
+ }
+ else
+ {
+ std::cout << "General NFD status:" << std::endl;
+ std::cout << " version="
+ << status.getNfdVersion() << std::endl;
+ std::cout << " startTime="
+ << time::toIsoString(status.getStartTimestamp()) << std::endl;
+ std::cout << " currentTime="
+ << time::toIsoString(status.getCurrentTimestamp()) << std::endl;
+ std::cout << " uptime="
+ << time::duration_cast<time::seconds>(status.getCurrentTimestamp()
+ - status.getStartTimestamp()) << std::endl;
- std::cout << " nNameTreeEntries=" << status.getNNameTreeEntries() << std::endl;
- std::cout << " nFibEntries=" << status.getNFibEntries() << std::endl;
- std::cout << " nPitEntries=" << status.getNPitEntries() << std::endl;
- std::cout << " nMeasurementsEntries=" << status.getNMeasurementsEntries() << std::endl;
- std::cout << " nCsEntries=" << status.getNCsEntries() << std::endl;
- std::cout << " nInInterests=" << status.getNInInterests() << std::endl;
- std::cout << " nOutInterests=" << status.getNOutInterests() << std::endl;
- std::cout << " nInDatas=" << status.getNInDatas() << std::endl;
- std::cout << " nOutDatas=" << status.getNOutDatas() << std::endl;
-
+ std::cout << " nNameTreeEntries=" << status.getNNameTreeEntries() << std::endl;
+ std::cout << " nFibEntries=" << status.getNFibEntries() << std::endl;
+ std::cout << " nPitEntries=" << status.getNPitEntries() << std::endl;
+ std::cout << " nMeasurementsEntries=" << status.getNMeasurementsEntries() << std::endl;
+ std::cout << " nCsEntries=" << status.getNCsEntries() << std::endl;
+ std::cout << " nInInterests=" << status.getNInInterests() << std::endl;
+ std::cout << " nOutInterests=" << status.getNOutInterests() << std::endl;
+ std::cout << " nInDatas=" << status.getNInDatas() << std::endl;
+ std::cout << " nOutDatas=" << status.getNOutDatas() << std::endl;
+ }
if (m_needFaceStatusRetrieval)
{
fetchFaceStatusInformation();
@@ -163,35 +226,84 @@
void
afterFetchedFaceStatusInformation()
{
- std::cout << "Faces:" << std::endl;
-
ConstBufferPtr buf = m_buffer->buf();
-
- Block block;
- size_t offset = 0;
- while (offset < buf->size())
+ if (m_needXmlDataRetrieval)
{
- bool ok = Block::fromBuffer(buf, offset, block);
- if (!ok)
+ std::cout << "<faces>";
+
+ Block block;
+ size_t offset = 0;
+ while (offset < buf->size())
{
- std::cerr << "ERROR: cannot decode FaceStatus TLV" << std::endl;
- break;
+ bool ok = Block::fromBuffer(buf, offset, block);
+ if (!ok)
+ {
+ std::cerr << "ERROR: cannot decode FaceStatus TLV" << std::endl;
+ break;
+ }
+
+ offset += block.size();
+
+ nfd::FaceStatus faceStatus(block);
+
+ std::cout << "<face>";
+ std::cout << "<faceId>" << faceStatus.getFaceId() << "</faceId>";
+
+ std::string remoteUri(faceStatus.getRemoteUri());
+ escapeSpecialCharacters(&remoteUri);
+ std::cout << "<remoteUri>" << remoteUri << "</remoteUri>";
+
+ std::string localUri(faceStatus.getLocalUri());
+ escapeSpecialCharacters(&localUri);
+ std::cout << "<localUri>" << localUri << "</localUri>";
+ std::cout << "<packetCounters>";
+ std::cout << "<incomingPackets>";
+ std::cout << "<nInterests>" << faceStatus.getNInInterests()
+ << "</nInterests>";
+ std::cout << "<nDatas>" << faceStatus.getNInInterests()
+ << "</nDatas>";
+ std::cout << "</incomingPackets>";
+ std::cout << "<outgoingPackets>";
+ std::cout << "<nInterests>" << faceStatus.getNOutInterests()
+ << "</nInterests>";
+ std::cout << "<nDatas>" << faceStatus.getNOutInterests()
+ << "</nDatas>";
+ std::cout << "</outgoingPackets>";
+ std::cout << "</packetCounters>";
+ std::cout << "</face>";
}
-
- offset += block.size();
-
- nfd::FaceStatus faceStatus(block);
-
- std::cout << " faceid=" << faceStatus.getFaceId()
- << " remote=" << faceStatus.getRemoteUri()
- << " local=" << faceStatus.getLocalUri()
- << " counters={"
- << "in={" << faceStatus.getNInInterests() << "i "
- << faceStatus.getNInDatas() << "d}"
- << " out={" << faceStatus.getNOutInterests() << "i "
- << faceStatus.getNOutDatas() << "d}"
- << "}" << std::endl;
+ std::cout << "</faces>";
}
+ else
+ {
+ std::cout << "Faces:" << 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 FaceStatus TLV" << std::endl;
+ break;
+ }
+
+ offset += block.size();
+
+ nfd::FaceStatus faceStatus(block);
+
+ std::cout << " faceid=" << faceStatus.getFaceId()
+ << " remote=" << faceStatus.getRemoteUri()
+ << " local=" << faceStatus.getLocalUri()
+ << " counters={"
+ << "in={" << faceStatus.getNInInterests() << "i "
+ << faceStatus.getNInDatas() << "d}"
+ << " out={" << faceStatus.getNOutInterests() << "i "
+ << faceStatus.getNOutDatas() << "d}"
+ << "}" << std::endl;
+ }
+ }
if (m_needFibEnumerationRetrieval)
{
@@ -217,36 +329,78 @@
void
afterFetchedFibEnumerationInformation()
{
- std::cout << "FIB:" << std::endl;
-
ConstBufferPtr buf = m_buffer->buf();
-
- Block block;
- size_t offset = 0;
- while (offset < buf->size())
+ if (m_needXmlDataRetrieval)
{
- bool ok = Block::fromBuffer(buf, offset, block);
- if (!ok)
- {
- std::cerr << "ERROR: cannot decode FibEntry TLV" << std::endl;
- break;
- }
- offset += block.size();
+ std::cout << "<fib>";
- nfd::FibEntry fibEntry(block);
-
- std::cout << " " << fibEntry.getPrefix() << " nexthops={";
- for (std::list<nfd::NextHopRecord>::const_iterator
- nextHop = fibEntry.getNextHopRecords().begin();
- nextHop != fibEntry.getNextHopRecords().end();
- ++nextHop)
+ Block block;
+ size_t offset = 0;
+ while (offset < buf->size())
{
- if (nextHop != fibEntry.getNextHopRecords().begin())
- std::cout << ", ";
- std::cout << "faceid=" << nextHop->getFaceId()
- << " (cost=" << nextHop->getCost() << ")";
+ bool ok = Block::fromBuffer(buf, offset, block);
+ if (!ok)
+ {
+ std::cerr << "ERROR: cannot decode FibEntry TLV";
+ break;
+ }
+ offset += block.size();
+
+ nfd::FibEntry fibEntry(block);
+
+ std::cout << "<fibEntry>";
+ std::string prefix(fibEntry.getPrefix().toUri());
+ escapeSpecialCharacters(&prefix);
+ std::cout << "<prefix>" << prefix << "</prefix>";
+ std::cout << "<nextHops>";
+ for (std::list<nfd::NextHopRecord>::const_iterator
+ nextHop = fibEntry.getNextHopRecords().begin();
+ nextHop != fibEntry.getNextHopRecords().end();
+ ++nextHop)
+ {
+ std::cout << "<nextHop>" ;
+ std::cout << "<faceId>" << nextHop->getFaceId() << "</faceId>";
+ std::cout << "<cost>" << nextHop->getCost() << "</cost>";
+ std::cout << "</nextHop>";
+ }
+ std::cout << "</nextHops>";
+ std::cout << "</fibEntry>";
}
- std::cout << "}" << std::endl;
+
+ std::cout << "</fib>";
+ std::cout << "</nfdStatus>";
+ }
+ else
+ {
+ std::cout << "FIB:" << 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 FibEntry TLV" << std::endl;
+ break;
+ }
+ offset += block.size();
+
+ nfd::FibEntry fibEntry(block);
+
+ std::cout << " " << fibEntry.getPrefix() << " nexthops={";
+ for (std::list<nfd::NextHopRecord>::const_iterator
+ nextHop = fibEntry.getNextHopRecords().begin();
+ nextHop != fibEntry.getNextHopRecords().end();
+ ++nextHop)
+ {
+ if (nextHop != fibEntry.getNextHopRecords().begin())
+ std::cout << ", ";
+ std::cout << "faceid=" << nextHop->getFaceId()
+ << " (cost=" << nextHop->getCost() << ")";
+ }
+ std::cout << "}" << std::endl;
+ }
}
}
@@ -267,9 +421,10 @@
void
fetchInformation()
{
- if (!m_needVersionRetrieval &&
+ if (m_needXmlDataRetrieval ||
+ (!m_needVersionRetrieval &&
!m_needFaceStatusRetrieval &&
- !m_needFibEnumerationRetrieval)
+ !m_needFibEnumerationRetrieval))
{
enableVersionRetrieval();
enableFaceStatusRetrieval();
@@ -297,6 +452,7 @@
bool m_needVersionRetrieval;
bool m_needFaceStatusRetrieval;
bool m_needFibEnumerationRetrieval;
+ bool m_needXmlDataRetrieval;
Face m_face;
shared_ptr<OBufferStream> m_buffer;
@@ -309,7 +465,7 @@
int option;
ndn::NfdStatus nfdStatus(argv[0]);
- while ((option = getopt(argc, argv, "hvfbV")) != -1) {
+ while ((option = getopt(argc, argv, "hvfbxV")) != -1) {
switch (option) {
case 'h':
nfdStatus.usage();
@@ -323,6 +479,9 @@
case 'b':
nfdStatus.enableFibEnumerationRetrieval();
break;
+ case 'x':
+ nfdStatus.enableXmlDataRetrieval();
+ break;
case 'V':
std::cout << NFD_VERSION_BUILD_STRING << std::endl;
return 0;