tools: create face during nfdc route add
refs #4332
Change-Id: I1a30bf87abd57bab08a2c027870e779392de0104
diff --git a/tools/nfdc/canonizer.cpp b/tools/nfdc/canonizer.cpp
new file mode 100644
index 0000000..3e526cd
--- /dev/null
+++ b/tools/nfdc/canonizer.cpp
@@ -0,0 +1,61 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2020, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "canonizer.hpp"
+
+namespace nfd {
+namespace tools {
+namespace nfdc {
+
+std::pair<optional<FaceUri>, std::string>
+canonize(ExecuteContext& ctx, const FaceUri& uri)
+{
+ optional<FaceUri> result;
+ std::string error;
+ uri.canonize(
+ [&result] (const FaceUri& canonicalUri) { result = canonicalUri; },
+ [&error] (const std::string& errorReason) { error = errorReason; },
+ ctx.face.getIoService(), ctx.getTimeout());
+ ctx.face.processEvents();
+
+ return {result, error};
+}
+
+std::pair<FindFace::Code, std::string>
+canonizeErrorHelper(const FaceUri& uri,
+ const std::string& error,
+ const std::string& field)
+{
+ std::string msg = "Error during canonization of ";
+ if (!field.empty()) {
+ msg += field + " ";
+ }
+ msg += "'" + uri.toString() + "': " + error;
+ return {FindFace::Code::CANONIZE_ERROR, msg};
+}
+
+} // namespace nfdc
+} // namespace tools
+} // namespace nfd
diff --git a/tools/nfdc/canonizer.hpp b/tools/nfdc/canonizer.hpp
new file mode 100644
index 0000000..b58372d
--- /dev/null
+++ b/tools/nfdc/canonizer.hpp
@@ -0,0 +1,60 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2020, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_TOOLS_NFDC_CANONIZER_HPP
+#define NFD_TOOLS_NFDC_CANONIZER_HPP
+
+#include "core/common.hpp"
+#include "execute-command.hpp"
+#include "find-face.hpp"
+
+#include <ndn-cxx/net/face-uri.hpp>
+
+namespace nfd {
+namespace tools {
+namespace nfdc {
+
+/** \brief canonize FaceUri
+ * \return pair of canonical FaceUri (nullopt if failure) and error string
+ */
+std::pair<optional<FaceUri>, std::string>
+canonize(ExecuteContext& ctx, const FaceUri& uri);
+
+/** \brief helper to generate exit code and error message for face canonization failures
+ * \param uri FaceUri
+ * \param error error string returned by canonization process
+ * \param field optional field identifier to include with message
+ * \return pair of exit code and error message
+ */
+std::pair<FindFace::Code, std::string>
+canonizeErrorHelper(const FaceUri& uri,
+ const std::string& error,
+ const std::string& field = "");
+
+} // namespace nfdc
+} // namespace tools
+} // namespace nfd
+
+#endif // NFD_TOOLS_NFDC_CANONIZER_HPP
diff --git a/tools/nfdc/face-module.cpp b/tools/nfdc/face-module.cpp
index 37f5bd9..e46279d2 100644
--- a/tools/nfdc/face-module.cpp
+++ b/tools/nfdc/face-module.cpp
@@ -24,6 +24,7 @@
*/
#include "face-module.hpp"
+#include "canonizer.hpp"
#include "find-face.hpp"
namespace nfd {
@@ -179,34 +180,19 @@
mtu = static_cast<uint64_t>(v);
}
- FaceUri canonicalRemote;
+ optional<FaceUri> canonicalRemote;
optional<FaceUri> canonicalLocal;
- auto handleCanonizeError = [&] (const FaceUri& faceUri, const std::string& error) {
- ctx.exitCode = static_cast<int>(FindFace::Code::CANONIZE_ERROR);
- 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("local") << resp.getLocalUri()
- << ia("remote") << resp.getUri()
- << ia("persistency") << resp.getFacePersistency();
- printFaceParams(ctx.out, ia, resp);
- };
-
- auto updateFace = [&printPositiveResult] (ControlParameters respParams, ControlParameters resp) {
+ auto updateFace = [&] (ControlParameters respParams, ControlParameters resp) {
// faces/update response does not have FaceUris, copy from faces/create response
resp.setLocalUri(respParams.getLocalUri())
.setUri(respParams.getUri());
- printPositiveResult("face-updated", resp);
+ printSuccess(ctx.out, "face-updated", resp);
};
auto handle409 = [&] (const ControlResponse& resp) {
ControlParameters respParams(resp.getBody());
- if (respParams.getUri() != canonicalRemote.toString()) {
+ if (respParams.getUri() != canonicalRemote->toString()) {
// we are conflicting with a different face, which is a general error
return false;
}
@@ -256,7 +242,7 @@
}
else {
// don't do anything
- printPositiveResult("face-exists", respParams);
+ printSuccess(ctx.out, "face-exists", respParams);
}
return true;
@@ -264,7 +250,7 @@
auto doCreateFace = [&] {
ControlParameters params;
- params.setUri(canonicalRemote.toString());
+ params.setUri(canonicalRemote->toString());
if (canonicalLocal) {
params.setLocalUri(canonicalLocal->toString());
}
@@ -287,7 +273,9 @@
ctx.controller.start<ndn::nfd::FaceCreateCommand>(
params,
- bind(printPositiveResult, "face-created", _1),
+ [&] (const ControlParameters& resp) {
+ printSuccess(ctx.out, "face-created", resp);
+ },
[&] (const ControlResponse& resp) {
if (resp.getCode() == 409 && handle409(resp)) {
return;
@@ -297,24 +285,33 @@
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 {
+ std::string error;
+ std::tie(canonicalRemote, error) = canonize(ctx, remoteUri);
+ if (canonicalRemote) {
+ // RemoteUri canonization successful
+ if (localUri) {
+ std::tie(canonicalLocal, error) = canonize(ctx, *localUri);
+ if (canonicalLocal) {
+ // LocalUri canonization successful
doCreateFace();
}
- },
- bind(handleCanonizeError, remoteUri, _1),
- ctx.face.getIoService(), ctx.getTimeout());
+ else {
+ // LocalUri canonization failure
+ auto canonizationError = canonizeErrorHelper(*localUri, error, "local FaceUri");
+ ctx.exitCode = static_cast<int>(canonizationError.first);
+ ctx.err << canonizationError.second << '\n';
+ }
+ }
+ else {
+ doCreateFace();
+ }
+ }
+ else {
+ // RemoteUri canonization failure
+ auto canonizationError = canonizeErrorHelper(remoteUri, error, "remote FaceUri");
+ ctx.exitCode = static_cast<int>(canonizationError.first);
+ ctx.err << canonizationError.second << '\n';
+ }
ctx.face.processEvents();
}
@@ -349,6 +346,7 @@
ctx.controller.start<ndn::nfd::FaceDestroyCommand>(
ControlParameters().setFaceId(face.getFaceId()),
[&] (const ControlParameters& resp) {
+ // We can't use printSuccess because some face attributes come from FaceStatus not ControlResponse
ctx.out << "face-destroyed ";
text::ItemAttributes ia;
ctx.out << ia("id") << face.getFaceId()
@@ -528,6 +526,20 @@
}
void
+FaceModule::printSuccess(std::ostream& os,
+ const std::string& actionSummary,
+ const ControlParameters& resp)
+{
+ text::ItemAttributes ia;
+ os << actionSummary << ' '
+ << ia("id") << resp.getFaceId()
+ << ia("local") << resp.getLocalUri()
+ << ia("remote") << resp.getUri()
+ << ia("persistency") << resp.getFacePersistency();
+ printFaceParams(os, ia, resp);
+}
+
+void
FaceModule::printFaceParams(std::ostream& os, text::ItemAttributes& ia, const ControlParameters& resp)
{
os << ia("reliability") << text::OnOff{resp.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED)}
diff --git a/tools/nfdc/face-module.hpp b/tools/nfdc/face-module.hpp
index 8083e74..cffd98b 100644
--- a/tools/nfdc/face-module.hpp
+++ b/tools/nfdc/face-module.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2014-2018, Regents of the University of California,
+ * Copyright (c) 2014-2020, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -94,6 +94,14 @@
static void
formatItemText(std::ostream& os, const FaceStatus& item, bool wantMultiLine);
+ /** \brief print face action success message to specified ostream
+ * \param os output stream
+ * \param actionSummary description of action taken
+ * \param resp response control parameters to print
+ */
+ static void
+ printSuccess(std::ostream& os, const std::string& actionSummary, const ControlParameters& resp);
+
/** \brief print face response parameters to specified ostream
* \param os output stream
* \param ia ItemAttributes used to format output
diff --git a/tools/nfdc/find-face.cpp b/tools/nfdc/find-face.cpp
index bb6183d..de0fd8e 100644
--- a/tools/nfdc/find-face.cpp
+++ b/tools/nfdc/find-face.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2014-2018, Regents of the University of California,
+ * Copyright (c) 2014-2020, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -24,6 +24,7 @@
*/
#include "find-face.hpp"
+#include "canonizer.hpp"
#include "format-helpers.hpp"
#include <ndn-cxx/util/logger.hpp>
@@ -75,18 +76,16 @@
m_filter = filter;
if (m_filter.hasRemoteUri()) {
- auto remoteUri = this->canonize("remote", FaceUri(m_filter.getRemoteUri()));
+ auto remoteUri = canonize("remote FaceUri", FaceUri(m_filter.getRemoteUri()));
if (!remoteUri) {
- m_res = Code::CANONIZE_ERROR;
return m_res;
}
m_filter.setRemoteUri(remoteUri->toString());
}
if (m_filter.hasLocalUri()) {
- auto localUri = this->canonize("local", FaceUri(m_filter.getLocalUri()));
+ auto localUri = canonize("local FaceUri", FaceUri(m_filter.getLocalUri()));
if (!localUri) {
- m_res = Code::CANONIZE_ERROR;
return m_res;
}
m_filter.setLocalUri(localUri->toString());
@@ -107,23 +106,27 @@
}
optional<FaceUri>
-FindFace::canonize(const std::string& fieldName, const FaceUri& input)
+FindFace::canonize(const std::string& fieldName, const FaceUri& uri)
{
- if (!FaceUri::canCanonize(input.getScheme())) {
- NDN_LOG_DEBUG("Using " << fieldName << '=' << input << " without canonization");
- return input;
+ // We use a wrapper because we want to accept FaceUris that cannot be canonized
+ if (!FaceUri::canCanonize(uri.getScheme())) {
+ NDN_LOG_DEBUG("Using " << fieldName << "=" << uri << " without canonization");
+ return uri;
}
optional<FaceUri> result;
- input.canonize(
- [&result] (const FaceUri& canonicalUri) { result = canonicalUri; },
- [this, fieldName] (const std::string& errorReason) {
- m_errorReason = "Error during " + fieldName + " FaceUri canonization: " + errorReason;
- },
- m_ctx.face.getIoService(), m_ctx.getTimeout());
- m_ctx.face.processEvents();
+ std::string error;
+ std::tie(result, error) = nfdc::canonize(m_ctx, uri);
- return result;
+ if (result) {
+ // Canonization succeeded
+ return result;
+ }
+ else {
+ // Canonization failed
+ std::tie(m_res, m_errorReason) = canonizeErrorHelper(uri, error);
+ return nullopt;
+ }
}
void
diff --git a/tools/nfdc/find-face.hpp b/tools/nfdc/find-face.hpp
index ea1245b..3a6bba5 100644
--- a/tools/nfdc/find-face.hpp
+++ b/tools/nfdc/find-face.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2014-2018, Regents of the University of California,
+ * Copyright (c) 2014-2020, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -121,12 +121,8 @@
printDisambiguation(std::ostream& os, DisambiguationStyle style) const;
private:
- /** \brief canonize FaceUri
- * \return canonical FaceUri if canonization succeeds, input if canonization is unsupported
- * \retval nullopt canonization fails; m_errorReason describes the failure
- */
optional<FaceUri>
- canonize(const std::string& fieldName, const FaceUri& input);
+ canonize(const std::string& fieldName, const FaceUri& uri);
/** \brief retrieve FaceStatus from filter
* \post m_res == Code::OK and m_results is populated if retrieval succeeds
diff --git a/tools/nfdc/rib-module.cpp b/tools/nfdc/rib-module.cpp
index b41d33a..544da6e 100644
--- a/tools/nfdc/rib-module.cpp
+++ b/tools/nfdc/rib-module.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2014-2018, Regents of the University of California,
+ * Copyright (c) 2014-2020, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -24,6 +24,8 @@
*/
#include "rib-module.hpp"
+#include "canonizer.hpp"
+#include "face-module.hpp"
#include "find-face.hpp"
#include "format-helpers.hpp"
@@ -151,18 +153,96 @@
bool wantCapture = ctx.args.get<bool>("capture", false);
auto expiresMillis = ctx.args.getOptional<uint64_t>("expires");
+ auto registerRoute = [&] (uint64_t faceId) {
+ ControlParameters registerParams;
+ registerParams
+ .setName(prefix)
+ .setFaceId(faceId)
+ .setOrigin(origin)
+ .setCost(cost)
+ .setFlags((wantChildInherit ? ndn::nfd::ROUTE_FLAG_CHILD_INHERIT : ndn::nfd::ROUTE_FLAGS_NONE) |
+ (wantCapture ? ndn::nfd::ROUTE_FLAG_CAPTURE : ndn::nfd::ROUTE_FLAGS_NONE));
+ if (expiresMillis) {
+ registerParams.setExpirationPeriod(time::milliseconds(*expiresMillis));
+ }
+
+ ctx.controller.start<ndn::nfd::RibRegisterCommand>(
+ registerParams,
+ [&] (const ControlParameters& resp) {
+ ctx.exitCode = static_cast<int>(FindFace::Code::OK);
+ ctx.out << "route-add-accepted ";
+ text::ItemAttributes ia;
+ ctx.out << ia("prefix") << resp.getName()
+ << ia("nexthop") << resp.getFaceId()
+ << ia("origin") << resp.getOrigin()
+ << ia("cost") << resp.getCost()
+ << ia("flags") << static_cast<ndn::nfd::RouteFlags>(resp.getFlags());
+ if (resp.hasExpirationPeriod()) {
+ ctx.out << ia("expires") << text::formatDuration<time::milliseconds>(resp.getExpirationPeriod()) << "\n";
+ }
+ else {
+ ctx.out<< ia("expires") << "never\n";
+ }
+ },
+ ctx.makeCommandFailureHandler("adding route"),
+ ctx.makeCommandOptions());
+ };
+
+ auto handleFaceNotFound = [&] {
+ const FaceUri* faceUri = ndn::any_cast<FaceUri>(&nexthop);
+ if (faceUri == nullptr) {
+ ctx.err << "Face not found\n";
+ return;
+ }
+
+ if (faceUri->getScheme() == "ether") {
+ // Unicast Ethernet faces require a LocalUri, which hasn't been provided
+ // Multicast Ethernet faces cannot be created via management (already exist on each interface)
+ ctx.err << "Unable to implicitly create Ethernet faces\n";
+ ctx.err << "Please create the face with 'nfdc face create' before adding the route\n";
+ return;
+ }
+
+ optional<FaceUri> canonized;
+ std::string error;
+ std::tie(canonized, error) = canonize(ctx, *faceUri);
+ if (!canonized) {
+ // Canonization failed
+ auto canonizationError = canonizeErrorHelper(*faceUri, error);
+ ctx.exitCode = static_cast<int>(canonizationError.first);
+ ctx.err << canonizationError.second << '\n';
+ return;
+ }
+
+ ControlParameters faceCreateParams;
+ faceCreateParams.setUri(canonized->toString());
+
+ ctx.controller.start<ndn::nfd::FaceCreateCommand>(
+ faceCreateParams,
+ [&] (const ControlParameters& resp) {
+ FaceModule::printSuccess(ctx.out, "face-created", resp);
+ registerRoute(resp.getFaceId());
+ },
+ ctx.makeCommandFailureHandler("implicitly creating face"),
+ ctx.makeCommandOptions());
+ };
+
FindFace findFace(ctx);
FindFace::Code res = findFace.execute(nexthop);
ctx.exitCode = static_cast<int>(res);
switch (res) {
case FindFace::Code::OK:
+ registerRoute(findFace.getFaceId());
break;
case FindFace::Code::ERROR:
case FindFace::Code::CANONIZE_ERROR:
- case FindFace::Code::NOT_FOUND:
ctx.err << findFace.getErrorReason() << '\n';
return;
+ case FindFace::Code::NOT_FOUND:
+ // Attempt to create face if it doesn't exist
+ handleFaceNotFound();
+ break;
case FindFace::Code::AMBIGUOUS:
ctx.err << "Multiple faces match specified remote FaceUri. Re-run the command with a FaceId:";
findFace.printDisambiguation(ctx.err, FindFace::DisambiguationStyle::LOCAL_URI);
@@ -173,38 +253,6 @@
return;
}
- ControlParameters registerParams;
- registerParams
- .setName(prefix)
- .setFaceId(findFace.getFaceId())
- .setOrigin(origin)
- .setCost(cost)
- .setFlags((wantChildInherit ? ndn::nfd::ROUTE_FLAG_CHILD_INHERIT : ndn::nfd::ROUTE_FLAGS_NONE) |
- (wantCapture ? ndn::nfd::ROUTE_FLAG_CAPTURE : ndn::nfd::ROUTE_FLAGS_NONE));
- if (expiresMillis) {
- registerParams.setExpirationPeriod(time::milliseconds(*expiresMillis));
- }
-
- ctx.controller.start<ndn::nfd::RibRegisterCommand>(
- registerParams,
- [&] (const ControlParameters& resp) {
- ctx.out << "route-add-accepted ";
- text::ItemAttributes ia;
- ctx.out << ia("prefix") << resp.getName()
- << ia("nexthop") << resp.getFaceId()
- << ia("origin") << resp.getOrigin()
- << ia("cost") << resp.getCost()
- << ia("flags") << static_cast<ndn::nfd::RouteFlags>(resp.getFlags());
- if (resp.hasExpirationPeriod()) {
- ctx.out << ia("expires") << text::formatDuration<time::milliseconds>(resp.getExpirationPeriod()) << "\n";
- }
- else {
- ctx.out<< ia("expires") << "never\n";
- }
- },
- ctx.makeCommandFailureHandler("adding route"),
- ctx.makeCommandOptions());
-
ctx.face.processEvents();
}