tools: nfdc route list and nfd route show commands

refs #3866

Change-Id: Ic8feab0ce9e5707c1cf382cdea7264e28e3edb30
This commit is contained in:
Junxiao Shi
2017-03-08 22:39:28 +00:00
parent 89c0ea0621
commit 1d62e62bc0
7 changed files with 336 additions and 43 deletions
+11 -6
View File
@@ -3,11 +3,12 @@ nfdc-route
SYNOPSIS
--------
| nfdc route [list]
| nfdc fib [list]
| nfdc route [list [[nexthop] <FACEID|FACEURI>] [origin <ORIGIN>]]
| nfdc route show [prefix] <PREFIX>
| nfdc route add [prefix] <PREFIX> [nexthop] <FACEID|FACEURI> [origin <ORIGIN>] [cost <COST>]
| [no-inherit] [capture] [expires <EXPIRATION-MILLIS>]
| nfdc unregister [-o <ORIGIN>] <PREFIX> <FACEID>
| nfdc route remove [prefix] <PREFIX> [nexthop] <FACEID|FACEURI> [origin <ORIGIN>]
| nfdc fib [list]
DESCRIPTION
-----------
@@ -17,10 +18,9 @@ Each *route* in the RIB indicates that contents under a name prefix may be avail
A route contains a name prefix, a nexthop face, the origin, a cost, and a set of route inheritance flags;
refer to NFD Management protocol for more information.
The **nfdc route list** command shows a list of routes in the RIB.
The **nfdc route list** command lists RIB routes, optionally filtered by nexthop and origin.
The **nfdc fib list** command shows the forwarding information base (FIB),
which is calculated from RIB routes and used directly by NFD forwarding.
The **nfdc route show** command shows RIB routes at a specified name prefix.
The **nfdc route add** command requests to add a route.
If a route with the same prefix, nexthop, and origin already exists,
@@ -29,6 +29,9 @@ This command returns when the request has been accepted, but does not wait for R
The **nfdc route remove** command removes a route with matching prefix, nexthop, and origin.
The **nfdc fib list** command shows the forwarding information base (FIB),
which is calculated from RIB routes and used directly by NFD forwarding.
OPTIONS
-------
<PREFIX>
@@ -77,6 +80,8 @@ EXIT CODES
5: Ambiguous: multiple matching faces are found (**nfdc route add** only)
6: Route not found (**nfdc route list** and **nfdc route show** only)
SEE ALSO
--------
nfd(1), nfdc(1)
+157 -4
View File
@@ -36,6 +36,158 @@ namespace tests {
BOOST_AUTO_TEST_SUITE(Nfdc)
BOOST_FIXTURE_TEST_SUITE(TestRibModule, StatusFixture<RibModule>)
class RouteListFixture : public ExecuteCommandFixture
{
protected:
bool
respondRibDataset(const Interest& interest)
{
if (!Name("/localhost/nfd/rib/list").isPrefixOf(interest.getName())) {
return false;
}
RibEntry entry1;
entry1.setName("/5BBmTevRJ");
entry1.addRoute(Route()
.setFaceId(6720)
.setOrigin(ndn::nfd::ROUTE_ORIGIN_CLIENT)
.setCost(2956)
.setFlags(ndn::nfd::ROUTE_FLAG_CHILD_INHERIT | ndn::nfd::ROUTE_FLAG_CAPTURE)
.setExpirationPeriod(time::milliseconds(29950035)));
entry1.addRoute(Route()
.setFaceId(6720)
.setOrigin(ndn::nfd::ROUTE_ORIGIN_STATIC)
.setCost(425)
.setFlags(ndn::nfd::ROUTE_FLAGS_NONE));
entry1.addRoute(Route()
.setFaceId(8599)
.setOrigin(ndn::nfd::ROUTE_ORIGIN_STATIC)
.setCost(9140)
.setFlags(ndn::nfd::ROUTE_FLAG_CHILD_INHERIT));
RibEntry entry2;
entry2.setName("/aDPTKCio");
entry2.addRoute(Route()
.setFaceId(31066)
.setOrigin(ndn::nfd::ROUTE_ORIGIN_CLIENT)
.setCost(4617)
.setFlags(ndn::nfd::ROUTE_FLAG_CAPTURE));
this->sendDataset(interest.getName(), entry1, entry2);
return true;
}
};
BOOST_FIXTURE_TEST_SUITE(ListShowCommand, RouteListFixture)
const std::string NOFILTER_OUTPUT = std::string(R"TEXT(
prefix=/5BBmTevRJ nexthop=6720 origin=65 cost=2956 flags=child-inherit|capture expires=29950s
prefix=/5BBmTevRJ nexthop=6720 origin=255 cost=425 flags=none expires=never
prefix=/5BBmTevRJ nexthop=8599 origin=255 cost=9140 flags=child-inherit expires=never
prefix=/aDPTKCio nexthop=31066 origin=65 cost=4617 flags=capture expires=never
)TEXT").substr(1);
BOOST_AUTO_TEST_CASE(ListNoFilter)
{
this->processInterest = [this] (const Interest& interest) {
BOOST_CHECK(this->respondRibDataset(interest));
};
this->execute("route");
BOOST_CHECK_EQUAL(exitCode, 0);
BOOST_CHECK(out.is_equal(NOFILTER_OUTPUT));
BOOST_CHECK(err.is_empty());
}
const std::string NEXTHOP_OUTPUT = std::string(R"TEXT(
prefix=/5BBmTevRJ nexthop=6720 origin=65 cost=2956 flags=child-inherit|capture expires=29950s
prefix=/5BBmTevRJ nexthop=6720 origin=255 cost=425 flags=none expires=never
prefix=/aDPTKCio nexthop=31066 origin=65 cost=4617 flags=capture expires=never
)TEXT").substr(1);
BOOST_AUTO_TEST_CASE(ListByNexthop)
{
this->processInterest = [this] (const Interest& interest) {
BOOST_CHECK(this->respondFaceQuery(interest) || this->respondRibDataset(interest));
};
this->execute("route list udp4://225.131.75.231:56363");
BOOST_CHECK_EQUAL(exitCode, 0);
BOOST_CHECK(out.is_equal(NEXTHOP_OUTPUT));
BOOST_CHECK(err.is_empty());
}
const std::string ORIGIN_OUTPUT = std::string(R"TEXT(
prefix=/5BBmTevRJ nexthop=6720 origin=255 cost=425 flags=none expires=never
prefix=/5BBmTevRJ nexthop=8599 origin=255 cost=9140 flags=child-inherit expires=never
)TEXT").substr(1);
BOOST_AUTO_TEST_CASE(ListByOrigin)
{
this->processInterest = [this] (const Interest& interest) {
BOOST_CHECK(this->respondRibDataset(interest));
};
this->execute("route list origin 255");
BOOST_CHECK_EQUAL(exitCode, 0);
BOOST_CHECK(out.is_equal(ORIGIN_OUTPUT));
BOOST_CHECK(err.is_empty());
}
const std::string PREFIX_OUTPUT = std::string(R"TEXT(
prefix=/5BBmTevRJ nexthop=6720 origin=65 cost=2956 flags=child-inherit|capture expires=29950s
prefix=/5BBmTevRJ nexthop=6720 origin=255 cost=425 flags=none expires=never
prefix=/5BBmTevRJ nexthop=8599 origin=255 cost=9140 flags=child-inherit expires=never
)TEXT").substr(1);
BOOST_AUTO_TEST_CASE(ShowByPrefix)
{
this->processInterest = [this] (const Interest& interest) {
BOOST_CHECK(this->respondRibDataset(interest));
};
this->execute("route show 5BBmTevRJ");
BOOST_CHECK_EQUAL(exitCode, 0);
BOOST_CHECK(out.is_equal(PREFIX_OUTPUT));
BOOST_CHECK(err.is_empty());
}
BOOST_AUTO_TEST_CASE(FaceNotExist)
{
this->processInterest = [this] (const Interest& interest) {
BOOST_CHECK(this->respondFaceQuery(interest));
};
this->execute("route list 23728");
BOOST_CHECK_EQUAL(exitCode, 3);
BOOST_CHECK(out.is_empty());
BOOST_CHECK(err.is_equal("Face not found\n"));
}
BOOST_AUTO_TEST_CASE(RouteNotExist)
{
this->processInterest = [this] (const Interest& interest) {
BOOST_CHECK(this->respondFaceQuery(interest) || this->respondRibDataset(interest));
};
this->execute("route list 10156");
BOOST_CHECK_EQUAL(exitCode, 6);
BOOST_CHECK(out.is_empty());
BOOST_CHECK(err.is_equal("Route not found\n"));
}
BOOST_AUTO_TEST_CASE(ErrorDataset)
{
this->processInterest = nullptr; // no response to dataset
this->execute("route list");
BOOST_CHECK_EQUAL(exitCode, 1);
BOOST_CHECK(out.is_empty());
BOOST_CHECK(err.is_equal("Error 10060 when fetching RIB dataset: Timeout\n"));
}
BOOST_AUTO_TEST_SUITE_END() // ListShowCommand
BOOST_FIXTURE_TEST_SUITE(AddCommand, ExecuteCommandFixture)
BOOST_AUTO_TEST_CASE(NormalByFaceId)
@@ -339,10 +491,11 @@ const std::string STATUS_XML = stripXmlSpaces(R"XML(
const std::string STATUS_TEXT =
"RIB:\n"
" / route={faceid=262 (origin=255 cost=9 RibCapture), faceid=272 (origin=255 cost=50), "
"faceid=274 (origin=255 cost=78 ChildInherit RibCapture), "
"faceid=276 (origin=255 cost=79 expires=47s ChildInherit)}\n"
" /localhost/nfd route={faceid=258 (origin=0 cost=0 ChildInherit)}\n";
" / routes={nexthop=262 origin=255 cost=9 flags=capture expires=never, "
"nexthop=272 origin=255 cost=50 flags=none expires=never, "
"nexthop=274 origin=255 cost=78 flags=child-inherit|capture expires=never, "
"nexthop=276 origin=255 cost=79 flags=child-inherit expires=47s}\n"
" /localhost/nfd routes={nexthop=258 origin=0 cost=0 flags=child-inherit expires=never}\n";
BOOST_AUTO_TEST_CASE(Status)
{
+10
View File
@@ -148,6 +148,16 @@ FindFace::query()
m_ctx.face.processEvents();
}
std::set<uint64_t>
FindFace::getFaceIds() const
{
std::set<uint64_t> faceIds;
for (const FaceStatus& faceStatus : m_results) {
faceIds.insert(faceStatus.getFaceId());
}
return faceIds;
}
const FaceStatus&
FindFace::getFaceStatus() const
{
+5
View File
@@ -92,6 +92,11 @@ public:
return m_results;
}
/** \return FaceId for all results
*/
std::set<uint64_t>
getFaceIds() const;
/** \return a single face status
* \pre getResults().size() == 1
*/
+120 -21
View File
@@ -34,6 +34,19 @@ namespace nfdc {
void
RibModule::registerCommands(CommandParser& parser)
{
CommandDefinition defRouteList("route", "list");
defRouteList
.setTitle("print RIB routes")
.addArg("nexthop", ArgValueType::FACE_ID_OR_URI, Required::NO, Positional::YES)
.addArg("origin", ArgValueType::UNSIGNED, Required::NO, Positional::NO);
parser.addCommand(defRouteList, &RibModule::list);
CommandDefinition defRouteShow("route", "show");
defRouteShow
.setTitle("show routes toward a prefix")
.addArg("prefix", ArgValueType::NAME, Required::YES, Positional::YES);
parser.addCommand(defRouteShow, &RibModule::show);
CommandDefinition defRouteAdd("route", "add");
defRouteAdd
.setTitle("add a route")
@@ -55,6 +68,77 @@ RibModule::registerCommands(CommandParser& parser)
parser.addCommand(defRouteRemove, &RibModule::remove);
}
void
RibModule::list(ExecuteContext& ctx)
{
auto nexthopIt = ctx.args.find("nexthop");
std::set<uint64_t> nexthops;
auto origin = ctx.args.getOptional<uint64_t>("origin");
if (nexthopIt != ctx.args.end()) {
FindFace findFace(ctx);
FindFace::Code res = findFace.execute(nexthopIt->second, true);
ctx.exitCode = static_cast<int>(res);
switch (res) {
case FindFace::Code::OK:
break;
case FindFace::Code::ERROR:
case FindFace::Code::CANONIZE_ERROR:
case FindFace::Code::NOT_FOUND:
ctx.err << findFace.getErrorReason() << '\n';
return;
default:
BOOST_ASSERT_MSG(false, "unexpected FindFace result");
return;
}
nexthops = findFace.getFaceIds();
}
listRoutesImpl(ctx, [&] (const RibEntry& entry, const Route& route) {
return (nexthops.empty() || nexthops.count(route.getFaceId()) > 0) &&
(!origin || route.getOrigin() == *origin);
});
}
void
RibModule::show(ExecuteContext& ctx)
{
auto prefix = ctx.args.get<Name>("prefix");
listRoutesImpl(ctx, [&] (const RibEntry& entry, const Route& route) {
return entry.getName() == prefix;
});
}
void
RibModule::listRoutesImpl(ExecuteContext& ctx, const RoutePredicate& filter)
{
ctx.controller.fetch<ndn::nfd::RibDataset>(
[&] (const std::vector<RibEntry>& dataset) {
bool hasRoute = false;
for (const RibEntry& entry : dataset) {
for (const Route& route : entry.getRoutes()) {
if (filter(entry, route)) {
hasRoute = true;
formatRouteText(ctx.out, entry, route, true);
ctx.out << '\n';
}
}
}
if (!hasRoute) {
ctx.exitCode = 6;
ctx.err << "Route not found\n";
}
},
ctx.makeDatasetFailureHandler("RIB dataset"),
ctx.makeCommandOptions());
ctx.face.processEvents();
}
void
RibModule::add(ExecuteContext& ctx)
{
@@ -147,11 +231,11 @@ RibModule::remove(ExecuteContext& ctx)
return;
}
for (const FaceStatus& faceStatus : findFace.getResults()) {
for (uint64_t faceId : findFace.getFaceIds()) {
ControlParameters unregisterParams;
unregisterParams
.setName(prefix)
.setFaceId(faceStatus.getFaceId())
.setFaceId(faceId)
.setOrigin(origin);
ctx.controller.start<ndn::nfd::RibUnregisterCommand>(
@@ -238,35 +322,50 @@ RibModule::formatStatusText(std::ostream& os) const
{
os << "RIB:\n";
for (const RibEntry& item : m_status) {
this->formatItemText(os, item);
os << " ";
formatEntryText(os, item);
os << '\n';
}
}
void
RibModule::formatItemText(std::ostream& os, const RibEntry& item) const
RibModule::formatEntryText(std::ostream& os, const RibEntry& entry)
{
os << " " << item.getName() << " route={";
os << entry.getName() << " routes={";
text::Separator sep(", ");
for (const Route& route : item.getRoutes()) {
os << sep
<< "faceid=" << route.getFaceId()
<< " (origin=" << route.getOrigin()
<< " cost=" << route.getCost();
if (route.hasExpirationPeriod()) {
os << " expires=" << text::formatDuration(route.getExpirationPeriod());
}
if (route.isChildInherit()) {
os << " ChildInherit";
}
if (route.isRibCapture()) {
os << " RibCapture";
}
os << ")";
for (const Route& route : entry.getRoutes()) {
os << sep;
formatRouteText(os, entry, route, false);
}
os << "}";
os << "\n";
}
void
RibModule::formatRouteText(std::ostream& os, const RibEntry& entry, const Route& route,
bool includePrefix)
{
text::ItemAttributes ia;
if (includePrefix) {
os << ia("prefix") << entry.getName();
}
os << ia("nexthop") << route.getFaceId();
os << ia("origin") << static_cast<uint64_t>(route.getOrigin());
os << ia("cost") << route.getCost();
os << ia("flags") << static_cast<ndn::nfd::RouteFlags>(route.getFlags());
// 'origin' field is printed as a number, because printing 'origin' as string may mislead user
// into passing strings to 'origin' command line argument which currently only accepts numbers.
///\todo #3987 print 'origin' with RouteOrigin stream insertion operator
if (route.hasExpirationPeriod()) {
os << ia("expires") << text::formatDuration(route.getExpirationPeriod());
}
else {
os << ia("expires") << "never";
}
}
} // namespace nfdc
+33 -7
View File
@@ -47,6 +47,16 @@ public:
static void
registerCommands(CommandParser& parser);
/** \brief the 'route list' command
*/
static void
list(ExecuteContext& ctx);
/** \brief the 'route show' command
*/
static void
show(ExecuteContext& ctx);
/** \brief the 'route add' command
*/
static void
@@ -66,6 +76,15 @@ public:
void
formatStatusXml(std::ostream& os) const override;
void
formatStatusText(std::ostream& os) const override;
private:
using RoutePredicate = function<bool(const RibEntry&, const Route&)>;
static void
listRoutesImpl(ExecuteContext& ctx, const RoutePredicate& filter);
/** \brief format a single status item as XML
* \param os output stream
* \param item status item
@@ -73,15 +92,22 @@ public:
void
formatItemXml(std::ostream& os, const RibEntry& item) const;
void
formatStatusText(std::ostream& os) const override;
/** \brief format a single status item as text
/** \brief format a RibEntry as text
* \param os output stream
* \param item status item
* \param entry RIB entry
*/
void
formatItemText(std::ostream& os, const RibEntry& item) const;
static void
formatEntryText(std::ostream& os, const RibEntry& entry);
/** \brief format a Route as text
* \param os output stream
* \param entry RIB entry
* \param route RIB route within \p entry
* \param includePrefix whether to print the name prefix
*/
static void
formatRouteText(std::ostream& os, const RibEntry& entry, const Route& route,
bool includePrefix);
private:
std::vector<RibEntry> m_status;
-5
View File
@@ -145,11 +145,6 @@ registerStatusCommands(CommandParser& parser)
defFibList
.setTitle("print FIB entries");
parser.addCommand(defFibList, bind(&reportStatusSingleSection, _1, &StatusReportOptions::wantFib));
CommandDefinition defRouteList("route", "list");
defRouteList
.setTitle("print RIB entries");
parser.addCommand(defRouteList, bind(&reportStatusSingleSection, _1, &StatusReportOptions::wantRib));
}
} // namespace nfdc