tools: nfdc face create command accepts local FaceUri
refs #4017
Change-Id: I2d13403f13c30146c53744828ba2a37a6fab7de2
diff --git a/docs/manpages/nfdc-face.rst b/docs/manpages/nfdc-face.rst
index f393a93..cfec8e2 100644
--- a/docs/manpages/nfdc-face.rst
+++ b/docs/manpages/nfdc-face.rst
@@ -3,9 +3,9 @@
SYNOPSIS
--------
-| nfdc face [list]
+| nfdc face [list [[remote] <FACEURI>] [local <FACEURI>] [scheme <SCHEME>]]
| nfdc face show [id] <FACEID>
-| nfdc face create [remote] <FACEURI> [[persistency] <PERSISTENCY>]
+| nfdc face create [remote] <FACEURI> [[persistency] <PERSISTENCY>] [local <FACEURI>]
| nfdc face destroy [face] <FACEID|FACEURI>
| nfdc channel [list]
@@ -16,11 +16,14 @@
an overlay communication channel between NFD and a remote node,
or an inter-process communication channel between NFD and a local application.
-The **nfdc face list** command shows a list of faces, their properties, and statistics.
+The **nfdc face list** command shows a list of faces, their properties, and statistics,
+optionally filtered by remote endpoint, local endpoint, and FaceUri scheme.
+When multiple filters are specified, returned faces must satisfy all filters.
The **nfdc face show** command shows properties and statistics of one specific face.
-The **nfdc face create** command creates a unicast UDP or TCP face.
+The **nfdc face create** command creates a UDP unicast, TCP, or Ethernet unicast face.
+Local FaceUri is required for creating Ethernet unicast faces; otherwise it must be omitted.
The **nfdc face destroy** command destroys an existing face.
@@ -34,14 +37,30 @@
It is displayed in the output of **nfdc face list** and **nfdc face create** commands.
<FACEURI>
- An URI representing the remote endpoint of a face.
- Its syntax is:
+ A URI representing the remote or local endpoint of a face.
+ Examples:
- - udp[4|6]://<IP-or-host>[:<port>]
- - tcp[4|6]://<IP-or-host>[:<port>]
+ - udp4://192.0.2.1:6363
+ - udp6://[2001:db8::1]:6363
+ - udp://example.net
+ - tcp4://192.0.2.1:6363
+ - tcp6://[2001:db8::1]:6363
+ - tcp://example.net
+ - unix:///var/run/nfd.sock
+ - fd://6
+ - ether://[08:00:27:01:01:01]
+ - dev://eth0
When a hostname is specified, a DNS query is used to obtain the IP address.
+<SCHEME>
+ The scheme portion of either remote or local endpoint.
+ Examples:
+
+ - udp4
+ - unix
+ - dev
+
<PERSISTENCY>
Either "persistent" or "permanent".
A "persistent" face (the default) is closed when a socket error occurs.
diff --git a/tests/tools/nfdc/face-module.t.cpp b/tests/tools/nfdc/face-module.t.cpp
index bce68b7..de68fe6 100644
--- a/tests/tools/nfdc/face-module.t.cpp
+++ b/tests/tools/nfdc/face-module.t.cpp
@@ -247,6 +247,7 @@
ControlParameters req = MOCK_NFD_MGMT_REQUIRE_COMMAND_IS("/localhost/nfd/faces/create");
BOOST_REQUIRE(req.hasUri());
BOOST_CHECK_EQUAL(req.getUri(), "udp4://159.242.33.78:6363");
+ BOOST_CHECK(!req.hasLocalUri());
BOOST_REQUIRE(req.hasFacePersistency());
BOOST_CHECK_EQUAL(req.getFacePersistency(), FacePersistency::FACE_PERSISTENCY_PERSISTENT);
@@ -262,6 +263,29 @@
BOOST_CHECK(err.is_empty());
}
+BOOST_AUTO_TEST_CASE(CreatingWithLocalUri)
+{
+ this->processInterest = [this] (const Interest& interest) {
+ ControlParameters req = MOCK_NFD_MGMT_REQUIRE_COMMAND_IS("/localhost/nfd/faces/create");
+ BOOST_REQUIRE(req.hasUri());
+ BOOST_CHECK_EQUAL(req.getUri(), "udp4://22.91.89.51:19903");
+ BOOST_REQUIRE(req.hasLocalUri());
+ BOOST_CHECK_EQUAL(req.getLocalUri(), "udp4://98.68.23.71:6363");
+ BOOST_REQUIRE(req.hasFacePersistency());
+ BOOST_CHECK_EQUAL(req.getFacePersistency(), FacePersistency::FACE_PERSISTENCY_PERMANENT);
+
+ ControlParameters resp;
+ resp.setFaceId(301)
+ .setFacePersistency(FacePersistency::FACE_PERSISTENCY_PERMANENT);
+ this->succeedCommand(interest, resp);
+ };
+
+ this->execute("face create udp://22.91.89.51:19903 permanent local udp://98.68.23.71");
+ BOOST_CHECK_EQUAL(exitCode, 0);
+ BOOST_CHECK(out.is_equal("face-created id=301 remote=udp4://22.91.89.51:19903 persistency=permanent\n"));
+ BOOST_CHECK(err.is_empty());
+}
+
BOOST_AUTO_TEST_CASE(UpgradingPersistency)
{
bool hasUpdateCommand = false;
@@ -319,6 +343,22 @@
BOOST_CHECK(err.is_empty());
}
+BOOST_AUTO_TEST_CASE(ErrorCanonizeRemote)
+{
+ this->execute("face create invalid://");
+ BOOST_CHECK_EQUAL(exitCode, 4);
+ BOOST_CHECK(out.is_empty());
+ BOOST_CHECK(err.is_equal("Error when canonizing 'invalid://': scheme not supported\n"));
+}
+
+BOOST_AUTO_TEST_CASE(ErrorCanonizeLocal)
+{
+ this->execute("face create udp4://24.37.20.47:6363 local invalid://");
+ BOOST_CHECK_EQUAL(exitCode, 4);
+ BOOST_CHECK(out.is_empty());
+ BOOST_CHECK(err.is_equal("Error when canonizing 'invalid://': scheme not supported\n"));
+}
+
BOOST_AUTO_TEST_CASE(ErrorCreate)
{
this->processInterest = nullptr; // no response
diff --git a/tools/nfdc/face-module.cpp b/tools/nfdc/face-module.cpp
index f0c9dfc..2ec3719 100644
--- a/tools/nfdc/face-module.cpp
+++ b/tools/nfdc/face-module.cpp
@@ -52,7 +52,8 @@
defFaceCreate
.setTitle("create a face")
.addArg("remote", ArgValueType::FACE_URI, Required::YES, Positional::YES)
- .addArg("persistency", ArgValueType::FACE_PERSISTENCY, Required::NO, Positional::YES);
+ .addArg("persistency", ArgValueType::FACE_PERSISTENCY, Required::NO, Positional::YES)
+ .addArg("local", ArgValueType::FACE_URI, Required::NO, Positional::NO);
parser.addCommand(defFaceCreate, &FaceModule::create);
CommandDefinition defFaceDestroy("face", "destroy");
@@ -147,23 +148,30 @@
void
FaceModule::create(ExecuteContext& ctx)
{
- auto faceUri = ctx.args.get<FaceUri>("remote");
+ auto remoteUri = ctx.args.get<FaceUri>("remote");
+ auto localUri = ctx.args.getOptional<FaceUri>("local");
auto persistency = ctx.args.get<FacePersistency>("persistency", FacePersistency::FACE_PERSISTENCY_PERSISTENT);
- FaceUri canonicalUri;
+ FaceUri canonicalRemote;
+ ndn::optional<FaceUri> canonicalLocal;
+
+ auto handleCanonizeError = [&] (const FaceUri& faceUri, const std::string& error) {
+ ctx.exitCode = 4;
+ ctx.err << "Error when canonizing '" << faceUri << "': " << error << '\n';
+ };
auto printPositiveResult = [&] (const std::string& actionSummary, const ControlParameters& resp) {
text::ItemAttributes ia;
ctx.out << actionSummary << ' '
<< ia("id") << resp.getFaceId()
- << ia("remote") << canonicalUri
+ << ia("remote") << canonicalRemote
<< ia("persistency") << resp.getFacePersistency()
<< '\n';
- ///\todo #3956 display local=localUri before 'remote' field
+ ///\todo #3956 display local FaceUri before 'remote' field
};
auto handle409 = [&] (const ControlResponse& resp) {
ControlParameters respParams(resp.getBody());
- if (respParams.getUri() != canonicalUri.toString()) {
+ if (respParams.getUri() != canonicalRemote.toString()) {
// we are conflicting with a different face, which is a general error
return false;
}
@@ -183,24 +191,43 @@
return true;
};
- faceUri.canonize(
- [&] (const FaceUri& canonicalUri1) {
- canonicalUri = canonicalUri1;
- ctx.controller.start<ndn::nfd::FaceCreateCommand>(
- ControlParameters().setUri(canonicalUri.toString()).setFacePersistency(persistency),
- bind(printPositiveResult, "face-created", _1),
- [&] (const ControlResponse& resp) {
- if (resp.getCode() == 409 && handle409(resp)) {
- return;
- }
- ctx.makeCommandFailureHandler("creating face")(resp); // invoke general error handler
- },
- ctx.makeCommandOptions());
+ auto doCreateFace = [&] {
+ ControlParameters params;
+ params.setUri(canonicalRemote.toString());
+ if (canonicalLocal) {
+ params.setLocalUri(canonicalLocal->toString());
+ }
+ params.setFacePersistency(persistency);
+
+ ctx.controller.start<ndn::nfd::FaceCreateCommand>(
+ params,
+ bind(printPositiveResult, "face-created", _1),
+ [&] (const ControlResponse& resp) {
+ if (resp.getCode() == 409 && handle409(resp)) {
+ return;
+ }
+ ctx.makeCommandFailureHandler("creating face")(resp); // invoke general error handler
+ },
+ ctx.makeCommandOptions());
+ };
+
+ remoteUri.canonize(
+ [&] (const FaceUri& canonicalUri) {
+ canonicalRemote = canonicalUri;
+ if (localUri) {
+ localUri->canonize(
+ [&] (const FaceUri& canonicalUri) {
+ canonicalLocal = canonicalUri;
+ doCreateFace();
+ },
+ bind(handleCanonizeError, *localUri, _1),
+ ctx.face.getIoService(), ctx.getTimeout());
+ }
+ else {
+ doCreateFace();
+ }
},
- [&] (const std::string& canonizeError) {
- ctx.exitCode = 4;
- ctx.err << "Error when canonizing FaceUri: " << canonizeError << '\n';
- },
+ bind(handleCanonizeError, remoteUri, _1),
ctx.face.getIoService(), ctx.getTimeout());
ctx.face.processEvents();