tools: refactor ndn-autoconfig
Hub face creation and prefix registrations are moved into a new
Procedure class. Main function is simplified.
refs #4158
Change-Id: I15b660e3b8a1bde89498a1cb549a87788de46c7a
diff --git a/tests/tools/mock-nfd-mgmt-fixture.hpp b/tests/tools/mock-nfd-mgmt-fixture.hpp
new file mode 100644
index 0000000..4eeab41
--- /dev/null
+++ b/tests/tools/mock-nfd-mgmt-fixture.hpp
@@ -0,0 +1,226 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2017, 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_TESTS_TOOLS_MOCK_NFD_MGMT_FIXTURE_HPP
+#define NFD_TESTS_TOOLS_MOCK_NFD_MGMT_FIXTURE_HPP
+
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
+#include "tests/test-common.hpp"
+#include "tests/identity-management-fixture.hpp"
+
+namespace nfd {
+namespace tools {
+namespace tests {
+
+using namespace nfd::tests;
+using ndn::nfd::ControlParameters;
+
+/** \brief fixture to emulate NFD management
+ */
+class MockNfdMgmtFixture : public IdentityManagementTimeFixture
+{
+protected:
+ MockNfdMgmtFixture()
+ : face(g_io, m_keyChain,
+ {true, false, bind(&MockNfdMgmtFixture::processEventsOverride, this, _1)})
+ {
+ face.onSendInterest.connect([=] (const Interest& interest) {
+ g_io.post([=] {
+ if (processInterest != nullptr) {
+ processInterest(interest);
+ }
+ });
+ });
+ }
+
+protected: // ControlCommand
+ /** \brief check the Interest is a command with specified prefix
+ * \retval nullopt last Interest is not the expected command
+ * \return command parameters
+ */
+ static ndn::optional<ControlParameters>
+ parseCommand(const Interest& interest, const Name& expectedPrefix)
+ {
+ if (!expectedPrefix.isPrefixOf(interest.getName())) {
+ return ndn::nullopt;
+ }
+ return ControlParameters(interest.getName().at(expectedPrefix.size()).blockFromValue());
+ }
+
+ /** \brief send successful response to a command Interest
+ */
+ void
+ succeedCommand(const Interest& interest, const ControlParameters& parameters)
+ {
+ this->sendCommandReply(interest, 200, "OK", parameters.wireEncode());
+ }
+
+ /** \brief send failure response to a command Interest
+ */
+ void
+ failCommand(const Interest& interest, uint32_t code, const std::string& text)
+ {
+ this->sendCommandReply(interest, {code, text});
+ }
+
+ /** \brief send failure response to a command Interest
+ */
+ void
+ failCommand(const Interest& interest, uint32_t code, const std::string& text, const ControlParameters& body)
+ {
+ this->sendCommandReply(interest, code, text, body.wireEncode());
+ }
+
+protected: // StatusDataset
+ /** \brief send an empty dataset in reply to StatusDataset request
+ * \param prefix dataset prefix without version and segment
+ * \pre Interest for dataset has been expressed, sendDataset has not been invoked
+ */
+ void
+ sendEmptyDataset(const Name& prefix)
+ {
+ this->sendDatasetReply(prefix, nullptr, 0);
+ }
+
+ /** \brief send one WireEncodable in reply to StatusDataset request
+ * \param prefix dataset prefix without version and segment
+ * \param payload payload block
+ * \note payload must fit in one Data
+ * \pre Interest for dataset has been expressed, sendDataset has not been invoked
+ */
+ template<typename T>
+ void
+ sendDataset(const Name& prefix, const T& payload)
+ {
+ BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T>));
+
+ this->sendDatasetReply(prefix, payload.wireEncode());
+ }
+
+ /** \brief send two WireEncodables in reply to StatusDataset request
+ * \param prefix dataset prefix without version and segment
+ * \param payload1 first vector item
+ * \param payload2 second vector item
+ * \note all payloads must fit in one Data
+ * \pre Interest for dataset has been expressed, sendDataset has not been invoked
+ */
+ template<typename T1, typename T2>
+ void
+ sendDataset(const Name& prefix, const T1& payload1, const T2& payload2)
+ {
+ BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T1>));
+ BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T2>));
+
+ ndn::encoding::EncodingBuffer buffer;
+ payload2.wireEncode(buffer);
+ payload1.wireEncode(buffer);
+
+ this->sendDatasetReply(prefix, buffer.buf(), buffer.size());
+ }
+
+private:
+ virtual void
+ processEventsOverride(time::milliseconds timeout)
+ {
+ if (timeout <= time::milliseconds::zero()) {
+ // give enough time to finish execution
+ timeout = time::seconds(30);
+ }
+ this->advanceClocks(time::milliseconds(100), timeout);
+ }
+
+ void
+ sendCommandReply(const Interest& interest, const ndn::nfd::ControlResponse& resp)
+ {
+ auto data = makeData(interest.getName());
+ data->setContent(resp.wireEncode());
+ face.receive(*data);
+ }
+
+ void
+ sendCommandReply(const Interest& interest, uint32_t code, const std::string& text,
+ const Block& body)
+ {
+ this->sendCommandReply(interest,
+ ndn::nfd::ControlResponse(code, text).setBody(body));
+ }
+
+ /** \brief send a payload in reply to StatusDataset request
+ * \param name dataset prefix without version and segment
+ * \param contentArgs passed to Data::setContent
+ */
+ template<typename ...ContentArgs>
+ void
+ sendDatasetReply(Name name, ContentArgs&&... contentArgs)
+ {
+ name.appendVersion().appendSegment(0);
+
+ // These warnings assist in debugging when nfdc does not receive StatusDataset.
+ // They usually indicate a misspelled prefix or incorrect timing in the test case.
+ if (face.sentInterests.empty()) {
+ BOOST_WARN_MESSAGE(false, "no Interest expressed");
+ }
+ else {
+ BOOST_WARN_MESSAGE(face.sentInterests.back().getName().isPrefixOf(name),
+ "last Interest " << face.sentInterests.back().getName() <<
+ " cannot be satisfied by this Data " << name);
+ }
+
+ auto data = make_shared<Data>(name);
+ data->setFinalBlockId(name[-1]);
+ data->setContent(std::forward<ContentArgs>(contentArgs)...);
+ this->signDatasetReply(*data);
+ face.receive(*data);
+ }
+
+ virtual void
+ signDatasetReply(Data& data)
+ {
+ signData(data);
+ }
+
+protected:
+ ndn::util::DummyClientFace face;
+ std::function<void(const Interest&)> processInterest;
+};
+
+} // namespace tests
+} // namespace tools
+} // namespace nfd
+
+/** \brief require the command in \p interest has expected prefix
+ * \note This must be used in processInterest lambda, and the Interest must be named 'interest'.
+ * \return ControlParameters, or nullopt if \p interest does match \p expectedPrefix
+ */
+#define MOCK_NFD_MGMT_REQUIRE_COMMAND_IS(expectedPrefix) \
+ [interest] { \
+ auto params = parseCommand(interest, (expectedPrefix)); \
+ BOOST_REQUIRE_MESSAGE(params, "Interest " << interest.getName() << \
+ " does not match command prefix " << (expectedPrefix)); \
+ return *params; \
+ } ()
+
+#endif // NFD_TESTS_TOOLS_MOCK_NFD_MGMT_FIXTURE_HPP
diff --git a/tests/tools/ndn-autoconfig/procedure.t.cpp b/tests/tools/ndn-autoconfig/procedure.t.cpp
new file mode 100644
index 0000000..27d9dcf
--- /dev/null
+++ b/tests/tools/ndn-autoconfig/procedure.t.cpp
@@ -0,0 +1,286 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2017, 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 "ndn-autoconfig/procedure.hpp"
+
+#include "../mock-nfd-mgmt-fixture.hpp"
+#include <boost/logic/tribool.hpp>
+
+namespace ndn {
+namespace tools {
+namespace autoconfig {
+namespace tests {
+
+using namespace ::nfd::tests;
+using nfd::ControlParameters;
+
+template<typename ProcedureClass>
+class ProcedureFixture : public ::nfd::tools::tests::MockNfdMgmtFixture
+{
+public:
+ void
+ initialize(const Options& options)
+ {
+ procedure = make_unique<ProcedureClass>(face, m_keyChain);
+ procedure->initialize(options);
+ }
+
+ bool
+ runOnce()
+ {
+ BOOST_ASSERT(procedure != nullptr);
+ boost::logic::tribool result;
+ procedure->onComplete.connectSingleShot([&] (bool result1) { result = result1; });
+ procedure->runOnce();
+ face.processEvents();
+ BOOST_CHECK_MESSAGE(!boost::logic::indeterminate(result), "onComplete is not invoked");
+ return result;
+ }
+
+public:
+ unique_ptr<ProcedureClass> procedure;
+};
+
+class DummyStage : public Stage
+{
+public:
+ /** \param stageName stage name
+ * \param nCalls pointer to a variable which is incremented each time doStart is invoked
+ * \param result expected result, nullopt to cause a failued
+ * \param io io_service to asynchronously post the result
+ */
+ DummyStage(const std::string& stageName, int* nCalls, const optional<FaceUri>& result, boost::asio::io_service& io)
+ : m_stageName(stageName)
+ , m_nCalls(nCalls)
+ , m_result(result)
+ , m_io(io)
+ {
+ }
+
+ const std::string&
+ getName() const override
+ {
+ return m_stageName;
+ }
+
+private:
+ void
+ doStart() override
+ {
+ if (m_nCalls != nullptr) {
+ ++(*m_nCalls);
+ }
+ m_io.post([this] {
+ if (m_result) {
+ this->succeed(*m_result);
+ }
+ else {
+ this->fail("DUMMY-STAGE-FAIL");
+ }
+ });
+ }
+
+private:
+ std::string m_stageName;
+ int* m_nCalls;
+ optional<FaceUri> m_result;
+ boost::asio::io_service& m_io;
+};
+
+/** \brief two-stage Procedure where the first stage succeeds and the second stage fails
+ *
+ * But the second stage shouldn't be invoked after the first stage succeeds.
+ */
+class ProcedureSuccessFailure : public Procedure
+{
+public:
+ ProcedureSuccessFailure(Face& face, KeyChain& keyChain)
+ : Procedure(face, keyChain)
+ , m_io(face.getIoService())
+ {
+ }
+
+private:
+ void
+ makeStages(const Options& options) override
+ {
+ m_stages.push_back(make_unique<DummyStage>("first", &nCalls1, FaceUri("udp://188.7.60.95"), m_io));
+ m_stages.push_back(make_unique<DummyStage>("second", &nCalls2, nullopt, m_io));
+ }
+
+public:
+ int nCalls1 = 0;
+ int nCalls2 = 0;
+
+private:
+ boost::asio::io_service& m_io;
+};
+
+/** \brief two-stage Procedure where the first stage fails and the second stage succeeds
+ */
+class ProcedureFailureSuccess : public Procedure
+{
+public:
+ ProcedureFailureSuccess(Face& face, KeyChain& keyChain)
+ : Procedure(face, keyChain)
+ , m_io(face.getIoService())
+ {
+ }
+
+private:
+ void
+ makeStages(const Options& options) override
+ {
+ m_stages.push_back(make_unique<DummyStage>("first", &nCalls1, nullopt, m_io));
+ m_stages.push_back(make_unique<DummyStage>("second", &nCalls2, FaceUri("tcp://40.23.174.71"), m_io));
+ }
+
+public:
+ int nCalls1 = 0;
+ int nCalls2 = 0;
+
+private:
+ boost::asio::io_service& m_io;
+};
+
+BOOST_AUTO_TEST_SUITE(NdnAutoconfig)
+BOOST_AUTO_TEST_SUITE(TestProcedure)
+
+BOOST_FIXTURE_TEST_CASE(Normal, ProcedureFixture<ProcedureSuccessFailure>)
+{
+ this->initialize(Options{});
+
+ int nRegisterNdn = 0, nRegisterLocalhopNfd = 0;
+ this->processInterest = [&] (const Interest& interest) {
+ optional<ControlParameters> req = parseCommand(interest, "/localhost/nfd/faces/create");
+ if (req) {
+ BOOST_REQUIRE(req->hasUri());
+ BOOST_CHECK_EQUAL(req->getUri(), "udp4://188.7.60.95:6363");
+
+ ControlParameters resp;
+ resp.setFaceId(1178)
+ .setUri("udp4://188.7.60.95:6363")
+ .setLocalUri("udp4://110.69.164.68:23197")
+ .setFacePersistency(nfd::FacePersistency::FACE_PERSISTENCY_PERSISTENT)
+ .setFlags(0);
+ this->succeedCommand(interest, resp);
+ return;
+ }
+
+ req = parseCommand(interest, "/localhost/nfd/rib/register");
+ if (req) {
+ BOOST_REQUIRE(req->hasFaceId());
+ BOOST_CHECK_EQUAL(req->getFaceId(), 1178);
+ BOOST_REQUIRE(req->hasOrigin());
+ BOOST_CHECK_EQUAL(req->getOrigin(), nfd::ROUTE_ORIGIN_AUTOCONF);
+ BOOST_REQUIRE(req->hasName());
+ if (req->getName() == "/ndn") {
+ ++nRegisterNdn;
+ }
+ else if (req->getName() == "/localhop/nfd") {
+ ++nRegisterLocalhopNfd;
+ }
+ else {
+ BOOST_ERROR("unexpected prefix registration " << req->getName());
+ }
+
+ ControlParameters resp;
+ resp.setName(req->getName())
+ .setFaceId(1178)
+ .setOrigin(nfd::ROUTE_ORIGIN_AUTOCONF)
+ .setCost(1)
+ .setFlags(0);
+ this->succeedCommand(interest, resp);
+ return;
+ }
+
+ BOOST_FAIL("unrecognized command Interest " << interest);
+ };
+
+ BOOST_CHECK_EQUAL(this->runOnce(), true);
+ BOOST_CHECK_EQUAL(procedure->nCalls1, 1);
+ BOOST_CHECK_EQUAL(procedure->nCalls2, 0);
+ BOOST_CHECK_EQUAL(nRegisterNdn, 1);
+ BOOST_CHECK_EQUAL(nRegisterLocalhopNfd, 1);
+}
+
+BOOST_FIXTURE_TEST_CASE(ExistingFace, ProcedureFixture<ProcedureFailureSuccess>)
+{
+ this->initialize(Options{});
+
+ int nRegisterNdn = 0, nRegisterLocalhopNfd = 0;
+ this->processInterest = [&] (const Interest& interest) {
+ optional<ControlParameters> req = parseCommand(interest, "/localhost/nfd/faces/create");
+ if (req) {
+ ControlParameters resp;
+ resp.setFaceId(3146)
+ .setUri("tcp4://40.23.174.71:6363")
+ .setLocalUri("tcp4://34.213.69.67:14445")
+ .setFacePersistency(nfd::FacePersistency::FACE_PERSISTENCY_PERSISTENT)
+ .setFlags(0);
+ this->failCommand(interest, 409, "conflict-409", resp);
+ return;
+ }
+
+ req = parseCommand(interest, "/localhost/nfd/rib/register");
+ if (req) {
+ BOOST_REQUIRE(req->hasName());
+ if (req->getName() == "/ndn") {
+ ++nRegisterNdn;
+ }
+ else if (req->getName() == "/localhop/nfd") {
+ ++nRegisterLocalhopNfd;
+ }
+ else {
+ BOOST_ERROR("unexpected prefix registration " << req->getName());
+ }
+
+ ControlParameters resp;
+ resp.setName(req->getName())
+ .setFaceId(3146)
+ .setOrigin(nfd::ROUTE_ORIGIN_AUTOCONF)
+ .setCost(1)
+ .setFlags(0);
+ this->succeedCommand(interest, resp);
+ return;
+ }
+
+ BOOST_FAIL("unrecognized command Interest " << interest);
+ };
+
+ BOOST_CHECK_EQUAL(this->runOnce(), true);
+ BOOST_CHECK_EQUAL(procedure->nCalls1, 1);
+ BOOST_CHECK_EQUAL(procedure->nCalls2, 1);
+ BOOST_CHECK_EQUAL(nRegisterNdn, 1);
+ BOOST_CHECK_EQUAL(nRegisterLocalhopNfd, 1);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestProcedure
+BOOST_AUTO_TEST_SUITE_END() // NdnAutoconfig
+
+} // namespace tests
+} // namespace autoconfig
+} // namespace tools
+} // namespace ndn
diff --git a/tests/tools/nfdc/mock-nfd-mgmt-fixture.hpp b/tests/tools/nfdc/mock-nfd-mgmt-fixture.hpp
index fd309bf..9841de8 100644
--- a/tests/tools/nfdc/mock-nfd-mgmt-fixture.hpp
+++ b/tests/tools/nfdc/mock-nfd-mgmt-fixture.hpp
@@ -1,5 +1,5 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
@@ -26,11 +26,7 @@
#ifndef NFD_TESTS_TOOLS_NFDC_MOCK_NFD_MGMT_FIXTURE_HPP
#define NFD_TESTS_TOOLS_NFDC_MOCK_NFD_MGMT_FIXTURE_HPP
-#include <ndn-cxx/mgmt/nfd/face-query-filter.hpp>
-#include <ndn-cxx/util/dummy-client-face.hpp>
-
-#include "tests/test-common.hpp"
-#include "tests/identity-management-fixture.hpp"
+#include "../mock-nfd-mgmt-fixture.hpp"
namespace nfd {
namespace tools {
@@ -38,111 +34,12 @@
namespace tests {
using namespace nfd::tests;
-using ndn::nfd::ControlParameters;
/** \brief fixture to emulate NFD management
*/
-class MockNfdMgmtFixture : public IdentityManagementTimeFixture
+class MockNfdMgmtFixture : public nfd::tools::tests::MockNfdMgmtFixture
{
protected:
- MockNfdMgmtFixture()
- : face(g_io, m_keyChain,
- {true, false, bind(&MockNfdMgmtFixture::processEventsOverride, this, _1)})
- {
- face.onSendInterest.connect([=] (const Interest& interest) {
- g_io.post([=] {
- if (processInterest != nullptr) {
- processInterest(interest);
- }
- });
- });
- }
-
-protected: // ControlCommand
- /** \brief check the Interest is a command with specified prefix
- * \retval nullopt last Interest is not the expected command
- * \return command parameters
- */
- static ndn::optional<ControlParameters>
- parseCommand(const Interest& interest, const Name& expectedPrefix)
- {
- if (!expectedPrefix.isPrefixOf(interest.getName())) {
- return ndn::nullopt;
- }
- return ControlParameters(interest.getName().at(expectedPrefix.size()).blockFromValue());
- }
-
- /** \brief send successful response to a command Interest
- */
- void
- succeedCommand(const Interest& interest, const ControlParameters& parameters)
- {
- this->sendCommandReply(interest, 200, "OK", parameters.wireEncode());
- }
-
- /** \brief send failure response to a command Interest
- */
- void
- failCommand(const Interest& interest, uint32_t code, const std::string& text)
- {
- this->sendCommandReply(interest, {code, text});
- }
-
- /** \brief send failure response to a command Interest
- */
- void
- failCommand(const Interest& interest, uint32_t code, const std::string& text, const ControlParameters& body)
- {
- this->sendCommandReply(interest, code, text, body.wireEncode());
- }
-
-protected: // StatusDataset
- /** \brief send an empty dataset in reply to StatusDataset request
- * \param prefix dataset prefix without version and segment
- * \pre Interest for dataset has been expressed, sendDataset has not been invoked
- */
- void
- sendEmptyDataset(const Name& prefix)
- {
- this->sendDatasetReply(prefix, nullptr, 0);
- }
-
- /** \brief send one WireEncodable in reply to StatusDataset request
- * \param prefix dataset prefix without version and segment
- * \param payload payload block
- * \note payload must fit in one Data
- * \pre Interest for dataset has been expressed, sendDataset has not been invoked
- */
- template<typename T>
- void
- sendDataset(const Name& prefix, const T& payload)
- {
- BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T>));
-
- this->sendDatasetReply(prefix, payload.wireEncode());
- }
-
- /** \brief send two WireEncodables in reply to StatusDataset request
- * \param prefix dataset prefix without version and segment
- * \param payload1 first vector item
- * \param payload2 second vector item
- * \note all payloads must fit in one Data
- * \pre Interest for dataset has been expressed, sendDataset has not been invoked
- */
- template<typename T1, typename T2>
- void
- sendDataset(const Name& prefix, const T1& payload1, const T2& payload2)
- {
- BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T1>));
- BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T2>));
-
- ndn::encoding::EncodingBuffer buffer;
- payload2.wireEncode(buffer);
- payload1.wireEncode(buffer);
-
- this->sendDatasetReply(prefix, buffer.buf(), buffer.size());
- }
-
/** \brief respond to specific FaceQuery requests
* \retval true the Interest matches one of the defined patterns and is responded
* \retval false the Interest is not responded
@@ -201,71 +98,6 @@
return false;
}
-
-private:
- virtual void
- processEventsOverride(time::milliseconds timeout)
- {
- if (timeout <= time::milliseconds::zero()) {
- // give enough time to finish execution
- timeout = time::seconds(30);
- }
- this->advanceClocks(time::milliseconds(100), timeout);
- }
-
- void
- sendCommandReply(const Interest& interest, const ndn::nfd::ControlResponse& resp)
- {
- auto data = makeData(interest.getName());
- data->setContent(resp.wireEncode());
- face.receive(*data);
- }
-
- void
- sendCommandReply(const Interest& interest, uint32_t code, const std::string& text,
- const Block& body)
- {
- this->sendCommandReply(interest,
- ndn::nfd::ControlResponse(code, text).setBody(body));
- }
-
- /** \brief send a payload in reply to StatusDataset request
- * \param name dataset prefix without version and segment
- * \param contentArgs passed to Data::setContent
- */
- template<typename ...ContentArgs>
- void
- sendDatasetReply(Name name, ContentArgs&&... contentArgs)
- {
- name.appendVersion().appendSegment(0);
-
- // These warnings assist in debugging when nfdc does not receive StatusDataset.
- // They usually indicate a misspelled prefix or incorrect timing in the test case.
- if (face.sentInterests.empty()) {
- BOOST_WARN_MESSAGE(false, "no Interest expressed");
- }
- else {
- BOOST_WARN_MESSAGE(face.sentInterests.back().getName().isPrefixOf(name),
- "last Interest " << face.sentInterests.back().getName() <<
- " cannot be satisfied by this Data " << name);
- }
-
- auto data = make_shared<Data>(name);
- data->setFinalBlockId(name[-1]);
- data->setContent(std::forward<ContentArgs>(contentArgs)...);
- this->signDatasetReply(*data);
- face.receive(*data);
- }
-
- virtual void
- signDatasetReply(Data& data)
- {
- signData(data);
- }
-
-protected:
- ndn::util::DummyClientFace face;
- std::function<void(const Interest&)> processInterest;
};
} // namespace tests
@@ -273,16 +105,4 @@
} // namespace tools
} // namespace nfd
-/** \brief require the command in \p interest has expected prefix
- * \note This must be used in processInterest lambda, and the Interest must be named 'interest'.
- * \return ControlParameters, or nullopt if \p interest does match \p expectedPrefix
- */
-#define MOCK_NFD_MGMT_REQUIRE_COMMAND_IS(expectedPrefix) \
- [interest] { \
- auto params = parseCommand(interest, (expectedPrefix)); \
- BOOST_REQUIRE_MESSAGE(params, "Interest " << interest.getName() << \
- " does not match command prefix " << (expectedPrefix)); \
- return *params; \
- } ()
-
#endif // NFD_TESTS_TOOLS_NFDC_MOCK_NFD_MGMT_FIXTURE_HPP
diff --git a/tools/ndn-autoconfig/guess-from-identity-name.cpp b/tools/ndn-autoconfig/guess-from-identity-name.cpp
index c26ec81..8ef112d 100644
--- a/tools/ndn-autoconfig/guess-from-identity-name.cpp
+++ b/tools/ndn-autoconfig/guess-from-identity-name.cpp
@@ -32,14 +32,13 @@
namespace tools {
namespace autoconfig {
-GuessFromIdentityName::GuessFromIdentityName(Face& face, KeyChain& keyChain,
- const NextStageCallback& nextStageOnFailure)
- : Stage(face, keyChain, nextStageOnFailure)
+GuessFromIdentityName::GuessFromIdentityName(KeyChain& keyChain)
+ : m_keyChain(keyChain)
{
}
void
-GuessFromIdentityName::start()
+GuessFromIdentityName::doStart()
{
std::cerr << "Trying default identity name..." << std::endl;
@@ -53,11 +52,10 @@
try {
std::string hubUri = querySrvRr(serverName.str());
- this->connectToHub(hubUri);
+ this->provideHubFaceUri(hubUri);
}
catch (const DnsSrvError& e) {
- m_nextStageOnFailure(std::string("Failed to find a home router based on the default identity "
- "name (") + e.what() + ")");
+ this->fail(e.what());
}
}
diff --git a/tools/ndn-autoconfig/guess-from-identity-name.hpp b/tools/ndn-autoconfig/guess-from-identity-name.hpp
index 315e856..9da9093 100644
--- a/tools/ndn-autoconfig/guess-from-identity-name.hpp
+++ b/tools/ndn-autoconfig/guess-from-identity-name.hpp
@@ -27,6 +27,7 @@
#define NFD_TOOLS_NDN_AUTOCONFIG_GUESS_FROM_IDENTITY_NAME_HPP
#include "stage.hpp"
+#include <ndn-cxx/security/key-chain.hpp>
namespace ndn {
namespace tools {
@@ -56,14 +57,22 @@
class GuessFromIdentityName : public Stage
{
public:
- /**
- * @brief Create stage to guess home router based on the default identity name
- */
- GuessFromIdentityName(Face& face, KeyChain& keyChain,
- const NextStageCallback& nextStageOnFailure);
+ explicit
+ GuessFromIdentityName(KeyChain& keyChain);
+ const std::string&
+ getName() const override
+ {
+ static const std::string STAGE_NAME("guess from identity name");
+ return STAGE_NAME;
+ }
+
+private:
void
- start() override;
+ doStart() override;
+
+private:
+ KeyChain& m_keyChain;
};
} // namespace autoconfig
diff --git a/tools/ndn-autoconfig/guess-from-search-domains.cpp b/tools/ndn-autoconfig/guess-from-search-domains.cpp
index f53c4fc..2f92cc3 100644
--- a/tools/ndn-autoconfig/guess-from-search-domains.cpp
+++ b/tools/ndn-autoconfig/guess-from-search-domains.cpp
@@ -30,24 +30,15 @@
namespace tools {
namespace autoconfig {
-GuessFromSearchDomains::GuessFromSearchDomains(Face& face, KeyChain& keyChain,
- const NextStageCallback& nextStageOnFailure)
- : Stage(face, keyChain, nextStageOnFailure)
-{
-}
-
void
-GuessFromSearchDomains::start()
+GuessFromSearchDomains::doStart()
{
- std::cerr << "Trying default suffix DNS query..." << std::endl;
-
try {
std::string hubUri = querySrvRrSearch();
- this->connectToHub(hubUri);
+ this->provideHubFaceUri(hubUri);
}
catch (const DnsSrvError& e) {
- m_nextStageOnFailure(std::string("Failed to find NDN router using default suffix DNS query (") +
- e.what() + ")");
+ this->fail(e.what());
}
}
diff --git a/tools/ndn-autoconfig/guess-from-search-domains.hpp b/tools/ndn-autoconfig/guess-from-search-domains.hpp
index a2670ec..9bd6521 100644
--- a/tools/ndn-autoconfig/guess-from-search-domains.hpp
+++ b/tools/ndn-autoconfig/guess-from-search-domains.hpp
@@ -49,14 +49,16 @@
class GuessFromSearchDomains : public Stage
{
public:
- /**
- * @brief Create stage to guess home router based on DNS query with default suffix
- */
- GuessFromSearchDomains(Face& face, KeyChain& keyChain,
- const NextStageCallback& nextStageOnFailure);
+ const std::string&
+ getName() const override
+ {
+ static const std::string STAGE_NAME("guess from search domains");
+ return STAGE_NAME;
+ }
+private:
void
- start() override;
+ doStart() override;
};
} // namespace autoconfig
diff --git a/tools/ndn-autoconfig/main.cpp b/tools/ndn-autoconfig/main.cpp
index db45610..fad4933 100644
--- a/tools/ndn-autoconfig/main.cpp
+++ b/tools/ndn-autoconfig/main.cpp
@@ -1,5 +1,5 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
@@ -23,23 +23,20 @@
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "procedure.hpp"
+#include "core/extended-error-message.hpp"
+#include "core/scheduler.hpp"
#include "core/version.hpp"
-#include "multicast-discovery.hpp"
-#include "guess-from-search-domains.hpp"
-#include "ndn-fch-discovery.hpp"
-#include "guess-from-identity-name.hpp"
-
-#include <ndn-cxx/net/network-monitor.hpp>
-#include <ndn-cxx/util/scheduler.hpp>
-#include <ndn-cxx/util/scheduler-scoped-event-id.hpp>
-
-#include <boost/noncopyable.hpp>
+#include <signal.h>
+#include <string.h>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp>
-
-namespace po = boost::program_options;
+#include <ndn-cxx/net/network-monitor.hpp>
+#include <ndn-cxx/util/scheduler.hpp>
+#include <ndn-cxx/util/scheduler-scoped-event-id.hpp>
+#include <ndn-cxx/util/time.hpp>
namespace ndn {
namespace tools {
@@ -47,201 +44,144 @@
// ndn-autoconfig is an NDN tool not an NFD tool, so it uses ndn::tools::autoconfig namespace.
// It lives in NFD repository because nfd-start can automatically start ndn-autoconfig in daemon mode.
-class NdnAutoconfig : boost::noncopyable
+static const time::nanoseconds DAEMON_INITIAL_DELAY = time::milliseconds(100);
+static const time::nanoseconds DAEMON_UNCONDITIONAL_INTERVAL = time::hours(1);
+static const time::nanoseconds NETMON_DAMPEN_PERIOD = time::seconds(5);
+
+namespace po = boost::program_options;
+
+static void
+usage(std::ostream& os,
+ const po::options_description& optionsDescription,
+ const char* programName)
{
-public:
- class Error : public std::runtime_error
- {
- public:
- explicit
- Error(const std::string& what)
- : std::runtime_error(what)
- {
+ os << "Usage:\n"
+ << " " << programName << " [options]\n"
+ << "\n";
+ os << optionsDescription;
+}
+
+static void
+runDaemon(Procedure& proc)
+{
+ boost::asio::signal_set terminateSignals(proc.getIoService());
+ terminateSignals.add(SIGINT);
+ terminateSignals.add(SIGTERM);
+ terminateSignals.async_wait([&] (const boost::system::error_code& error, int signalNo) {
+ if (error) {
+ return;
}
+ const char* signalName = ::strsignal(signalNo);
+ std::cerr << "Exit on signal ";
+ if (signalName == nullptr) {
+ std::cerr << signalNo;
+ }
+ else {
+ std::cerr << signalName;
+ }
+ std::cerr << std::endl;
+ proc.getIoService().stop();
+ });
+
+ util::Scheduler sched(proc.getIoService());
+ util::scheduler::ScopedEventId runEvt(sched);
+ auto scheduleRerun = [&] (time::nanoseconds delay) {
+ runEvt = sched.scheduleEvent(delay, [&] { proc.runOnce(); });
};
- explicit
- NdnAutoconfig(const std::string& ndnFchUrl, bool isDaemonMode)
- : m_face(m_io)
- , m_scheduler(m_io)
- , m_startStagesEvent(m_scheduler)
- , m_isDaemonMode(isDaemonMode)
- , m_terminationSignalSet(m_io)
- , m_stage1(m_face, m_keyChain,
- [&] (const std::string& errorMessage) {
- std::cerr << "Stage 1 failed: " << errorMessage << std::endl;
- m_stage2.start();
- })
- , m_stage2(m_face, m_keyChain,
- [&] (const std::string& errorMessage) {
- std::cerr << "Stage 2 failed: " << errorMessage << std::endl;
- m_stage3.start();
- })
- , m_stage3(m_face, m_keyChain,
- ndnFchUrl,
- [&] (const std::string& errorMessage) {
- std::cerr << "Stage 3 failed: " << errorMessage << std::endl;
- m_stage4.start();
- })
- , m_stage4(m_face, m_keyChain,
- [&] (const std::string& errorMessage) {
- std::cerr << "Stage 4 failed: " << errorMessage << std::endl;
- if (!m_isDaemonMode)
- BOOST_THROW_EXCEPTION(Error("No more stages, automatic discovery failed"));
- else
- std::cerr << "No more stages, automatic discovery failed" << std::endl;
- })
- {
- if (m_isDaemonMode) {
- m_networkMonitor.reset(new net::NetworkMonitor(m_io));
- m_networkMonitor->onNetworkStateChanged.connect([this] {
- // delay stages, so if multiple events are triggered in short sequence,
- // only one auto-detection procedure is triggered
- m_startStagesEvent = m_scheduler.scheduleEvent(time::seconds(5),
- bind(&NdnAutoconfig::startStages, this));
- });
- }
+ proc.onComplete.connect([&] (bool isSuccess) {
+ scheduleRerun(DAEMON_UNCONDITIONAL_INTERVAL);
+ });
- // Delay a little bit
- m_startStagesEvent = m_scheduler.scheduleEvent(time::milliseconds(100),
- bind(&NdnAutoconfig::startStages, this));
- }
+ net::NetworkMonitor netmon(proc.getIoService());
+ netmon.onNetworkStateChanged.connect([&] { scheduleRerun(NETMON_DAMPEN_PERIOD); });
- void
- run()
- {
- if (m_isDaemonMode) {
- m_terminationSignalSet.add(SIGINT);
- m_terminationSignalSet.add(SIGTERM);
- m_terminationSignalSet.async_wait(bind(&NdnAutoconfig::terminate, this, _1, _2));
- }
-
- m_io.run();
- }
-
- void
- terminate(const boost::system::error_code& error, int signalNo)
- {
- if (error)
- return;
-
- m_io.stop();
- }
-
- static void
- usage(std::ostream& os,
- const po::options_description& optionDescription,
- const char* programName)
- {
- os << "Usage:\n"
- << " " << programName << " [options]\n"
- << "\n";
- os << optionDescription;
- }
-
-private:
- void
- startStages()
- {
- m_stage1.start();
- if (m_isDaemonMode) {
- m_startStagesEvent = m_scheduler.scheduleEvent(time::hours(1),
- bind(&NdnAutoconfig::startStages, this));
- }
- }
-
-private:
- boost::asio::io_service m_io;
- Face m_face;
- KeyChain m_keyChain;
- unique_ptr<net::NetworkMonitor> m_networkMonitor;
- util::Scheduler m_scheduler;
- util::scheduler::ScopedEventId m_startStagesEvent;
- bool m_isDaemonMode;
- boost::asio::signal_set m_terminationSignalSet;
-
- MulticastDiscovery m_stage1;
- GuessFromSearchDomains m_stage2;
- NdnFchDiscovery m_stage3;
- GuessFromIdentityName m_stage4;
-};
+ scheduleRerun(DAEMON_INITIAL_DELAY);
+ proc.getIoService().run();
+}
static int
main(int argc, char** argv)
{
- bool isDaemonMode = false;
+ Options options;
+ bool isDaemon = false;
std::string configFile;
- std::string ndnFchUrl;
- po::options_description optionDescription("Options");
- optionDescription.add_options()
- ("help,h", "produce help message")
- ("daemon,d", po::bool_switch(&isDaemonMode)->default_value(isDaemonMode),
- "run in daemon mode, detecting network change events and re-running "
- "auto-discovery procedure. In addition, the auto-discovery procedure "
- "is unconditionally re-run every hour.\n"
+ po::options_description optionsDescription("Options");
+ optionsDescription.add_options()
+ ("help,h", "print this message and exit")
+ ("version,V", "display version and exit")
+ ("daemon,d", po::bool_switch(&isDaemon)->default_value(isDaemon),
+ "run in daemon mode, detecting network change events and re-running auto-discovery procedure. "
+ "In addition, the auto-discovery procedure is unconditionally re-run every hour.\n"
"NOTE: if connection to NFD fails, the daemon will be terminated.")
- ("ndn-fch-url", po::value<std::string>(&ndnFchUrl)->default_value("http://ndn-fch.named-data.net"),
+ ("ndn-fch-url", po::value<std::string>(&options.ndnFchUrl)->default_value(options.ndnFchUrl),
"URL for NDN-FCH (Find Closest Hub) service")
- ("config,c", po::value<std::string>(&configFile), "configuration file. If `enabled = true` "
- "is not specified, no actions will be performed.")
- ("version,V", "show version and exit")
+ ("config,c", po::value<std::string>(&configFile),
+ "configuration file. Exit immediately if `enabled = true` is not specified in config file.")
;
- po::variables_map options;
+ po::variables_map vm;
try {
- po::store(po::parse_command_line(argc, argv, optionDescription), options);
- po::notify(options);
+ po::store(po::parse_command_line(argc, argv, optionsDescription), vm);
+ po::notify(vm);
}
catch (const std::exception& e) {
- std::cerr << "ERROR: " << e.what() << "\n" << std::endl;
- NdnAutoconfig::usage(std::cerr, optionDescription, argv[0]);
- return 1;
+ std::cerr << "ERROR: " << e.what() << "\n" << "\n\n";
+ usage(std::cerr, optionsDescription, argv[0]);
+ return 2;
}
- if (options.count("help")) {
- NdnAutoconfig::usage(std::cout, optionDescription, argv[0]);
+ if (vm.count("help")) {
+ usage(std::cout, optionsDescription, argv[0]);
return 0;
}
- if (options.count("version")) {
+ if (vm.count("version")) {
std::cout << NFD_VERSION_BUILD_STRING << std::endl;
return 0;
}
- // Enable (one-shot or daemon mode whenever config file is not specified)
- bool isEnabled = true;
-
- po::options_description configFileOptions;
- configFileOptions.add_options()
- ("enabled", po::value<bool>(&isEnabled))
- ;
-
- if (!configFile.empty()) {
- isEnabled = false; // Disable by default if config file is specified
+ if (vm.count("config")) {
+ po::options_description configFileOptions;
+ configFileOptions.add_options()
+ ("enabled", po::value<bool>()->default_value(false))
+ ;
try {
- po::store(po::parse_config_file<char>(configFile.c_str(), configFileOptions), options);
- po::notify(options);
+ po::store(po::parse_config_file<char>(configFile.data(), configFileOptions), vm);
+ po::notify(vm);
}
catch (const std::exception& e) {
- std::cerr << "ERROR: " << e.what() << std::endl << std::endl;
- return 1;
+ std::cerr << "ERROR in config: " << e.what() << "\n\n";
+ return 2;
+ }
+ if (!vm["enabled"].as<bool>()) {
+ // not enabled in config
+ return 0;
}
}
- if (!isEnabled) {
- return 0;
- }
-
+ int exitCode = 0;
try {
- NdnAutoconfig autoConfigInstance(ndnFchUrl, isDaemonMode);
- autoConfigInstance.run();
+ Face face;
+ KeyChain keyChain;
+ Procedure proc(face, keyChain);
+ proc.initialize(options);
+
+ if (isDaemon) {
+ runDaemon(proc);
+ }
+ else {
+ proc.onComplete.connect([&exitCode] (bool isSuccess) { exitCode = isSuccess ? 0 : 3; });
+ proc.runOnce();
+ face.processEvents();
+ }
}
- catch (const std::exception& error) {
- std::cerr << "ERROR: " << error.what() << std::endl;
+ catch (const std::exception& e) {
+ std::cerr << ::nfd::getExtendedErrorMessage(e) << std::endl;
return 1;
}
- return 0;
+ return exitCode;
}
} // namespace autoconfig
diff --git a/tools/ndn-autoconfig/multicast-discovery.cpp b/tools/ndn-autoconfig/multicast-discovery.cpp
index 6692bc2..f9b4797 100644
--- a/tools/ndn-autoconfig/multicast-discovery.cpp
+++ b/tools/ndn-autoconfig/multicast-discovery.cpp
@@ -33,46 +33,44 @@
static const Name LOCALHOP_HUB_DISCOVERY_PREFIX = "/localhop/ndn-autoconf/hub";
-MulticastDiscovery::MulticastDiscovery(Face& face, KeyChain& keyChain,
- const NextStageCallback& nextStageOnFailure)
- : Stage(face, keyChain, nextStageOnFailure)
+MulticastDiscovery::MulticastDiscovery(Face& face, nfd::Controller& controller)
+ : m_face(face)
+ , m_controller(controller)
, m_nRequestedRegs(0)
, m_nFinishedRegs(0)
{
}
void
-MulticastDiscovery::start()
+MulticastDiscovery::doStart()
{
- std::cerr << "Trying multicast discovery..." << std::endl;
-
this->collectMulticastFaces();
}
void
MulticastDiscovery::collectMulticastFaces()
{
- ndn::nfd::FaceQueryFilter filter;
- filter.setLinkType(ndn::nfd::LINK_TYPE_MULTI_ACCESS);
- m_controller.fetch<ndn::nfd::FaceQueryDataset>(
+ nfd::FaceQueryFilter filter;
+ filter.setLinkType(nfd::LINK_TYPE_MULTI_ACCESS);
+ m_controller.fetch<nfd::FaceQueryDataset>(
filter,
bind(&MulticastDiscovery::registerHubDiscoveryPrefix, this, _1),
- bind(m_nextStageOnFailure, _2)
+ bind(&MulticastDiscovery::fail, this, _2)
);
}
void
-MulticastDiscovery::registerHubDiscoveryPrefix(const std::vector<ndn::nfd::FaceStatus>& dataset)
+MulticastDiscovery::registerHubDiscoveryPrefix(const std::vector<nfd::FaceStatus>& dataset)
{
std::vector<uint64_t> multicastFaces;
std::transform(dataset.begin(), dataset.end(), std::back_inserter(multicastFaces),
- [] (const ndn::nfd::FaceStatus& faceStatus) { return faceStatus.getFaceId(); });
+ [] (const nfd::FaceStatus& faceStatus) { return faceStatus.getFaceId(); });
if (multicastFaces.empty()) {
- m_nextStageOnFailure("No multicast faces available, skipping multicast discovery stage");
+ this->fail("No multicast faces available, skipping multicast discovery stage");
}
else {
- ControlParameters parameters;
+ nfd::ControlParameters parameters;
parameters
.setName(LOCALHOP_HUB_DISCOVERY_PREFIX)
.setCost(1)
@@ -83,7 +81,7 @@
for (const auto& face : multicastFaces) {
parameters.setFaceId(face);
- m_controller.start<ndn::nfd::RibRegisterCommand>(
+ m_controller.start<nfd::RibRegisterCommand>(
parameters,
bind(&MulticastDiscovery::onRegisterSuccess, this),
bind(&MulticastDiscovery::onRegisterFailure, this, _1));
@@ -102,7 +100,7 @@
}
void
-MulticastDiscovery::onRegisterFailure(const ControlResponse& response)
+MulticastDiscovery::onRegisterFailure(const nfd::ControlResponse& response)
{
std::cerr << "ERROR: " << response.getText() << " (code: " << response.getCode() << ")" << std::endl;
--m_nRequestedRegs;
@@ -112,8 +110,8 @@
MulticastDiscovery::setStrategy();
}
else {
- m_nextStageOnFailure("Failed to register " + LOCALHOP_HUB_DISCOVERY_PREFIX.toUri() +
- " for all multicast faces, skipping multicast discovery stage");
+ this->fail("Failed to register " + LOCALHOP_HUB_DISCOVERY_PREFIX.toUri() +
+ " for all multicast faces, skipping multicast discovery stage");
}
}
}
@@ -121,23 +119,23 @@
void
MulticastDiscovery::setStrategy()
{
- ControlParameters parameters;
+ nfd::ControlParameters parameters;
parameters
.setName(LOCALHOP_HUB_DISCOVERY_PREFIX)
.setStrategy("/localhost/nfd/strategy/multicast");
- m_controller.start<ndn::nfd::StrategyChoiceSetCommand>(
+ m_controller.start<nfd::StrategyChoiceSetCommand>(
parameters,
bind(&MulticastDiscovery::requestHubData, this),
bind(&MulticastDiscovery::onSetStrategyFailure, this, _1));
}
void
-MulticastDiscovery::onSetStrategyFailure(const ControlResponse& response)
+MulticastDiscovery::onSetStrategyFailure(const nfd::ControlResponse& response)
{
- m_nextStageOnFailure("Failed to set multicast strategy for " +
- LOCALHOP_HUB_DISCOVERY_PREFIX.toUri() + " namespace (" + response.getText() + "). "
- "Skipping multicast discovery stage");
+ this->fail("Failed to set multicast strategy for " +
+ LOCALHOP_HUB_DISCOVERY_PREFIX.toUri() + " namespace (" + response.getText() + "). "
+ "Skipping multicast discovery stage");
}
void
@@ -149,8 +147,8 @@
m_face.expressInterest(interest,
bind(&MulticastDiscovery::onSuccess, this, _2),
- bind(m_nextStageOnFailure, "HUB Data not received: nacked"),
- bind(m_nextStageOnFailure, "HUB Data not received: timeout"));
+ bind(&MulticastDiscovery::fail, this, "HUB Data not received: nacked"),
+ bind(&MulticastDiscovery::fail, this, "HUB Data not received: timeout"));
}
void
@@ -162,11 +160,10 @@
// Get Uri
Block::element_const_iterator blockValue = content.find(tlv::nfd::Uri);
if (blockValue == content.elements_end()) {
- m_nextStageOnFailure("Incorrect reply to multicast discovery stage");
+ this->fail("Incorrect reply to multicast discovery stage");
return;
}
- std::string hubUri(reinterpret_cast<const char*>(blockValue->value()), blockValue->value_size());
- this->connectToHub(hubUri);
+ this->provideHubFaceUri(std::string(reinterpret_cast<const char*>(blockValue->value()), blockValue->value_size()));
}
} // namespace autoconfig
diff --git a/tools/ndn-autoconfig/multicast-discovery.hpp b/tools/ndn-autoconfig/multicast-discovery.hpp
index 1a10d89..d714306 100644
--- a/tools/ndn-autoconfig/multicast-discovery.hpp
+++ b/tools/ndn-autoconfig/multicast-discovery.hpp
@@ -27,6 +27,9 @@
#define NFD_TOOLS_NDN_AUTOCONFIG_MULTICAST_DISCOVERY_HPP
#include "stage.hpp"
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/mgmt/nfd/controller.hpp>
+#include <ndn-cxx/mgmt/nfd/face-status.hpp>
namespace ndn {
namespace tools {
@@ -53,29 +56,36 @@
/**
* @brief Create multicast discovery stage
*/
- MulticastDiscovery(Face& face, KeyChain& keyChain, const NextStageCallback& nextStageOnFailure);
+ MulticastDiscovery(Face& face, nfd::Controller& controller);
- void
- start() override;
+ const std::string&
+ getName() const override
+ {
+ static const std::string STAGE_NAME("multicast discovery");
+ return STAGE_NAME;
+ }
private:
void
+ doStart() override;
+
+ void
collectMulticastFaces();
void
- registerHubDiscoveryPrefix(const std::vector<ndn::nfd::FaceStatus>& dataset);
+ registerHubDiscoveryPrefix(const std::vector<nfd::FaceStatus>& dataset);
void
onRegisterSuccess();
void
- onRegisterFailure(const ControlResponse& response);
+ onRegisterFailure(const nfd::ControlResponse& response);
void
setStrategy();
void
- onSetStrategyFailure(const ControlResponse& response);
+ onSetStrategyFailure(const nfd::ControlResponse& response);
// Start to look for a hub (NDN hub discovery first stage)
void
@@ -85,6 +95,8 @@
onSuccess(const Data& data);
private:
+ Face& m_face;
+ nfd::Controller& m_controller;
size_t m_nRequestedRegs;
size_t m_nFinishedRegs;
};
diff --git a/tools/ndn-autoconfig/ndn-fch-discovery.cpp b/tools/ndn-autoconfig/ndn-fch-discovery.cpp
index 6f043a3..01c169e 100644
--- a/tools/ndn-autoconfig/ndn-fch-discovery.cpp
+++ b/tools/ndn-autoconfig/ndn-fch-discovery.cpp
@@ -132,19 +132,14 @@
}
};
-NdnFchDiscovery::NdnFchDiscovery(Face& face, KeyChain& keyChain,
- const std::string& url,
- const NextStageCallback& nextStageOnFailure)
- : Stage(face, keyChain, nextStageOnFailure)
- , m_url(url)
+NdnFchDiscovery::NdnFchDiscovery(const std::string& url)
+ : m_url(url)
{
}
void
-NdnFchDiscovery::start()
+NdnFchDiscovery::doStart()
{
- std::cerr << "Trying NDN-FCH service..." << std::endl;
-
try {
using namespace boost::asio::ip;
tcp::iostream requestStream;
@@ -205,10 +200,10 @@
BOOST_THROW_EXCEPTION(HttpException("NDN-FCH did not return hub host"));
}
- this->connectToHub("udp://" + hubHost);
+ this->provideHubFaceUri("udp://" + hubHost);
}
catch (const std::runtime_error& e) {
- m_nextStageOnFailure(std::string("Failed to find NDN router using NDN-FCH service (") + e.what() + ")");
+ this->fail(e.what());
}
}
diff --git a/tools/ndn-autoconfig/ndn-fch-discovery.hpp b/tools/ndn-autoconfig/ndn-fch-discovery.hpp
index 2b8de07..000568d 100644
--- a/tools/ndn-autoconfig/ndn-fch-discovery.hpp
+++ b/tools/ndn-autoconfig/ndn-fch-discovery.hpp
@@ -43,12 +43,19 @@
/**
* @brief Create stage to discover NDN hub using NDN-FCH protocol
*/
- NdnFchDiscovery(Face& face, KeyChain& keyChain,
- const std::string& url,
- const NextStageCallback& nextStageOnFailure);
+ explicit
+ NdnFchDiscovery(const std::string& url);
+ const std::string&
+ getName() const override
+ {
+ static const std::string STAGE_NAME("NDN-FCH");
+ return STAGE_NAME;
+ }
+
+private:
void
- start() override;
+ doStart() override;
private:
std::string m_url;
diff --git a/tools/ndn-autoconfig/procedure.cpp b/tools/ndn-autoconfig/procedure.cpp
new file mode 100644
index 0000000..042ac17
--- /dev/null
+++ b/tools/ndn-autoconfig/procedure.cpp
@@ -0,0 +1,143 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2017, 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 "procedure.hpp"
+#include "guess-from-identity-name.hpp"
+#include "guess-from-search-domains.hpp"
+#include "multicast-discovery.hpp"
+#include "ndn-fch-discovery.hpp"
+
+namespace ndn {
+namespace tools {
+namespace autoconfig {
+
+using nfd::ControlParameters;
+using nfd::ControlResponse;
+
+static const time::nanoseconds FACEURI_CANONIZE_TIMEOUT = time::seconds(4);
+static const std::vector<Name> HUB_PREFIXES{"/ndn", "/localhop/nfd"};
+static const nfd::RouteOrigin HUB_ROUTE_ORIGIN = nfd::ROUTE_ORIGIN_AUTOCONF;
+static const uint64_t HUB_ROUTE_COST = 100;
+
+Procedure::Procedure(Face& face, KeyChain& keyChain)
+ : m_face(face)
+ , m_keyChain(keyChain)
+ , m_controller(face, keyChain)
+{
+}
+
+void
+Procedure::initialize(const Options& options)
+{
+ BOOST_ASSERT(m_stages.empty());
+ this->makeStages(options);
+ BOOST_ASSERT(!m_stages.empty());
+
+ for (size_t i = 0; i < m_stages.size(); ++i) {
+ m_stages[i]->onSuccess.connect(bind(&Procedure::connect, this, _1));
+ if (i + 1 < m_stages.size()) {
+ m_stages[i]->onFailure.connect([=] (const std::string&) { m_stages[i + 1]->start(); });
+ }
+ else {
+ m_stages[i]->onFailure.connect([=] (const std::string&) { this->onComplete(false); });
+ }
+ }
+}
+
+void
+Procedure::makeStages(const Options& options)
+{
+ m_stages.push_back(make_unique<MulticastDiscovery>(m_face, m_controller));
+ m_stages.push_back(make_unique<GuessFromSearchDomains>());
+ m_stages.push_back(make_unique<NdnFchDiscovery>(options.ndnFchUrl));
+ m_stages.push_back(make_unique<GuessFromIdentityName>(m_keyChain));
+}
+
+void
+Procedure::runOnce()
+{
+ BOOST_ASSERT(!m_stages.empty());
+ m_stages.front()->start();
+}
+
+void
+Procedure::connect(const FaceUri& hubFaceUri)
+{
+ hubFaceUri.canonize(
+ [this] (const FaceUri& canonicalUri) {
+ m_controller.start<nfd::FaceCreateCommand>(
+ ControlParameters().setUri(canonicalUri.toString()),
+ [this] (const ControlParameters& params) {
+ std::cerr << "Connected to HUB " << params.getUri() << std::endl;
+ this->registerPrefixes(params.getFaceId());
+ },
+ [this, canonicalUri] (const ControlResponse& resp) {
+ if (resp.getCode() == 409) {
+ ControlParameters params(resp.getBody());
+ std::cerr << "Already connected to HUB " << params.getUri() << std::endl;
+ this->registerPrefixes(params.getFaceId());
+ }
+ else {
+ std::cerr << "Failed to connect to HUB " << canonicalUri << ": "
+ << resp.getText() << " (" << resp.getCode() << ")" << std::endl;
+ this->onComplete(false);
+ }
+ });
+ },
+ [this] (const std::string& reason) {
+ std::cerr << "Failed to canonize HUB FaceUri: " << reason << std::endl;
+ this->onComplete(false);
+ },
+ m_face.getIoService(), FACEURI_CANONIZE_TIMEOUT);
+}
+
+void
+Procedure::registerPrefixes(uint64_t hubFaceId, size_t index)
+{
+ if (index >= HUB_PREFIXES.size()) {
+ this->onComplete(true);
+ return;
+ }
+
+ m_controller.start<nfd::RibRegisterCommand>(
+ ControlParameters()
+ .setName(HUB_PREFIXES[index])
+ .setFaceId(hubFaceId)
+ .setOrigin(HUB_ROUTE_ORIGIN)
+ .setCost(HUB_ROUTE_COST),
+ [=] (const ControlParameters&) {
+ std::cerr << "Registered prefix " << HUB_PREFIXES[index] << std::endl;
+ this->registerPrefixes(hubFaceId, index + 1);
+ },
+ [=] (const ControlResponse& resp) {
+ std::cerr << "Failed to register " << HUB_PREFIXES[index] << ": "
+ << resp.getText() << " (" << resp.getCode() << ")" << std::endl;
+ this->onComplete(false);
+ });
+}
+
+} // namespace autoconfig
+} // namespace tools
+} // namespace ndn
diff --git a/tools/ndn-autoconfig/procedure.hpp b/tools/ndn-autoconfig/procedure.hpp
new file mode 100644
index 0000000..0f65b89
--- /dev/null
+++ b/tools/ndn-autoconfig/procedure.hpp
@@ -0,0 +1,92 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2017, 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_NDN_AUTOCONFIG_PROCEDURE_HPP
+#define NFD_TOOLS_NDN_AUTOCONFIG_PROCEDURE_HPP
+
+#include "stage.hpp"
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/mgmt/nfd/controller.hpp>
+#include <ndn-cxx/security/key-chain.hpp>
+
+namespace ndn {
+namespace tools {
+namespace autoconfig {
+
+struct Options
+{
+ std::string ndnFchUrl = "http://ndn-fch.named-data.net"; ///< HTTP base URL of NDN-FCH service
+};
+
+class Procedure : noncopyable
+{
+public:
+ Procedure(Face& face, KeyChain& keyChain);
+
+ void
+ initialize(const Options& options);
+
+ /** \brief run HUB discovery procedure once
+ */
+ void
+ runOnce();
+
+ boost::asio::io_service&
+ getIoService()
+ {
+ return m_face.getIoService();
+ }
+
+private:
+ VIRTUAL_WITH_TESTS void
+ makeStages(const Options& options);
+
+ void
+ connect(const FaceUri& hubFaceUri);
+
+ void
+ registerPrefixes(uint64_t hubFaceId, size_t index = 0);
+
+public:
+ /** \brief signal when procedure completes
+ *
+ * Argument indicates whether the procedure succeeds (true) or fails (false).
+ */
+ util::Signal<Procedure, bool> onComplete;
+
+PROTECTED_WITH_TESTS_ELSE_PRIVATE:
+ std::vector<unique_ptr<Stage>> m_stages;
+
+private:
+ Face& m_face;
+ KeyChain& m_keyChain;
+ nfd::Controller m_controller;
+};
+
+} // namespace autoconfig
+} // namespace tools
+} // namespace ndn
+
+#endif // NFD_TOOLS_NDN_AUTOCONFIG_PROCEDURE_HPP
diff --git a/tools/ndn-autoconfig/stage.cpp b/tools/ndn-autoconfig/stage.cpp
index dbad283..2e1e048 100644
--- a/tools/ndn-autoconfig/stage.cpp
+++ b/tools/ndn-autoconfig/stage.cpp
@@ -29,106 +29,46 @@
namespace tools {
namespace autoconfig {
-Stage::Stage(Face& face, KeyChain& keyChain, const NextStageCallback& nextStageOnFailure)
- : m_face(face)
- , m_keyChain(keyChain)
- , m_controller(face, keyChain)
- , m_nextStageOnFailure(nextStageOnFailure)
-{
-}
-
void
-Stage::connectToHub(const std::string& uri)
+Stage::start()
{
- FaceUri faceUri(uri);
- std::cerr << "About to connect to: " << uri << std::endl;
-
- faceUri.canonize(bind(&Stage::onCanonizeSuccess, this, _1),
- bind(&Stage::onCanonizeFailure, this, _1),
- m_face.getIoService(), time::seconds(4));
-
-}
-
-
-void
-Stage::onCanonizeSuccess(const FaceUri& canonicalUri)
-{
- m_controller.start<ndn::nfd::FaceCreateCommand>(
- ControlParameters().setUri(canonicalUri.toString()),
- bind(&Stage::onHubConnectSuccess, this, _1),
- bind(&Stage::onHubConnectError, this, _1));
-}
-
-void
-Stage::onCanonizeFailure(const std::string& reason)
-{
- BOOST_THROW_EXCEPTION(Error("FaceUri canonization failed: " + reason));
-}
-
-void
-Stage::onHubConnectSuccess(const ControlParameters& resp)
-{
- std::cerr << "Successfully created face: " << resp << std::endl;
-
- registerAutoConfigNames(resp.getFaceId());
-}
-
-void
-Stage::onHubConnectError(const ControlResponse& response)
-{
- // If face exists, continue proceeding with the existing face
- if (response.getCode() == 409) {
- std::cerr << "Face exists. Proceeding with existing face: " << ControlParameters(response.getBody()) << std::endl;
-
- registerAutoConfigNames(ControlParameters(response.getBody()).getFaceId());
+ if (m_isInProgress) {
+ BOOST_THROW_EXCEPTION(Error("Cannot start a stage when it's in progress"));
}
- // Otherwise, report the failure and throw out exception
+ m_isInProgress = true;
+
+ std::cerr << "Starting " << this->getName() << " stage" << std::endl;
+ this->doStart();
+}
+
+void
+Stage::provideHubFaceUri(const std::string& s)
+{
+ FaceUri u;
+ if (u.parse(s)) {
+ this->succeed(u);
+ }
else {
- std::ostringstream os;
- os << "Failed to create face: " << response.getText() << " (code: " << response.getCode() << ")";
- BOOST_THROW_EXCEPTION(Error(os.str()));
+ this->fail("Cannot parse FaceUri: " + s);
}
}
void
-Stage::registerAutoConfigNames(uint64_t faceId)
+Stage::succeed(const FaceUri& hubFaceUri)
{
- static const Name TESTBED_PREFIX = "/ndn";
- registerPrefix(TESTBED_PREFIX, faceId);
-
- static const Name LOCALHOP_NFD_PREFIX = "/localhop/nfd";
- registerPrefix(LOCALHOP_NFD_PREFIX, faceId);
+ std::cerr << "Stage " << this->getName() << " succeeded with " << hubFaceUri << std::endl;
+ this->onSuccess(hubFaceUri);
+ m_isInProgress = false;
}
void
-Stage::registerPrefix(const Name& prefix, uint64_t faceId)
+Stage::fail(const std::string& msg)
{
- // Register a prefix in RIB
- m_controller.start<ndn::nfd::RibRegisterCommand>(
- ControlParameters()
- .setName(prefix)
- .setFaceId(faceId)
- .setOrigin(ndn::nfd::ROUTE_ORIGIN_AUTOCONF)
- .setCost(100)
- .setExpirationPeriod(time::milliseconds::max()),
- bind(&Stage::onPrefixRegistrationSuccess, this, _1),
- bind(&Stage::onPrefixRegistrationError, this, _1));
+ std::cerr << "Stage " << this->getName() << " failed: " << msg << std::endl;
+ this->onFailure(msg);
+ m_isInProgress = false;
}
-void
-Stage::onPrefixRegistrationSuccess(const ControlParameters& commandSuccessResult)
-{
- std::cerr << "Successful in name registration: " << commandSuccessResult << std::endl;
-}
-
-void
-Stage::onPrefixRegistrationError(const ControlResponse& response)
-{
- BOOST_THROW_EXCEPTION(Error("Failed in name registration, " + response.getText() +
- " (code: " + to_string(response.getCode()) + ")"));
-}
-
-
} // namespace autoconfig
} // namespace tools
} // namespace ndn
diff --git a/tools/ndn-autoconfig/stage.hpp b/tools/ndn-autoconfig/stage.hpp
index 2c63280..015153d 100644
--- a/tools/ndn-autoconfig/stage.hpp
+++ b/tools/ndn-autoconfig/stage.hpp
@@ -27,22 +27,14 @@
#define NFD_TOOLS_NDN_AUTOCONFIG_STAGE_HPP
#include "core/common.hpp"
-
-#include <ndn-cxx/face.hpp>
-#include <ndn-cxx/mgmt/nfd/controller.hpp>
-#include <ndn-cxx/mgmt/nfd/face-status.hpp>
#include <ndn-cxx/net/face-uri.hpp>
-#include <ndn-cxx/security/key-chain.hpp>
+#include <ndn-cxx/util/signal.hpp>
namespace ndn {
namespace tools {
namespace autoconfig {
-using ndn::nfd::ControlParameters;
-using ndn::nfd::ControlResponse;
-
-/**
- * @brief Base class for discovery stages
+/** \brief a discovery stage
*/
class Stage : boost::noncopyable
{
@@ -57,63 +49,51 @@
}
};
- /**
- * @brief Callback to be called when the stage fails
- */
- typedef std::function<void(const std::string&)> NextStageCallback;
+ virtual ~Stage() = default;
- /**
- * @brief Start the stage
+ /** \brief get stage name
+ * \return stage name as a phrase, typically starting with lower case
*/
- virtual void
- start() = 0;
+ virtual const std::string&
+ getName() const = 0;
-protected:
- /**
- * @brief Initialize variables and create Controller instance
- * @param face Face to be used for all operations (e.g., will send registration commands)
- * @param keyChain KeyChain object
- * @param nextStageOnFailure Callback to be called after the stage failed
- */
- Stage(Face& face, KeyChain& keyChain, const NextStageCallback& nextStageOnFailure);
-
- /**
- * @brief Attempt to connect to local hub using the \p uri FaceUri
- * @throw Error when failed to establish the tunnel
+ /** \brief start running this stage
+ * \throw Error stage is already running
*/
void
- connectToHub(const std::string& uri);
+ start();
+
+protected:
+ /** \brief parse HUB FaceUri from string and declare success
+ */
+ void
+ provideHubFaceUri(const std::string& s);
+
+ void
+ succeed(const FaceUri& hubFaceUri);
+
+ void
+ fail(const std::string& msg);
private:
- void
- onCanonizeSuccess(const FaceUri& canonicalUri);
+ virtual void
+ doStart() = 0;
- void
- onCanonizeFailure(const std::string& reason);
+public:
+ /** \brief signal when a HUB FaceUri is found
+ *
+ * Argument is HUB FaceUri, may not be canonical.
+ */
+ util::Signal<Stage, FaceUri> onSuccess;
- void
- onHubConnectSuccess(const ControlParameters& resp);
+ /** \brief signal when discovery fails
+ *
+ * Argument is error message.
+ */
+ util::Signal<Stage, std::string> onFailure;
- void
- onHubConnectError(const ControlResponse& response);
-
- void
- registerAutoConfigNames(uint64_t faceId);
-
- void
- registerPrefix(const Name& prefix, uint64_t faceId);
-
- void
- onPrefixRegistrationSuccess(const ControlParameters& commandSuccessResult);
-
- void
- onPrefixRegistrationError(const ControlResponse& response);
-
-protected:
- Face& m_face;
- KeyChain& m_keyChain;
- ndn::nfd::Controller m_controller;
- NextStageCallback m_nextStageOnFailure;
+private:
+ bool m_isInProgress = false;
};
} // namespace autoconfig