diff --git a/daemon/fw/forwarder.hpp b/daemon/fw/forwarder.hpp index 62612f2f..c3898c6e 100644 --- a/daemon/fw/forwarder.hpp +++ b/daemon/fw/forwarder.hpp @@ -60,6 +60,9 @@ public: // forwarding entrypoints and tables void onData(Face& face, const Data& data); + NameTree& + getNameTree(); + Fib& getFib(); @@ -192,6 +195,12 @@ Forwarder::onData(Face& face, const Data& data) this->onIncomingData(face, data); } +inline NameTree& +Forwarder::getNameTree() +{ + return m_nameTree; +} + inline Fib& Forwarder::getFib() { diff --git a/daemon/main.cpp b/daemon/main.cpp index f80a4bde..8abdba60 100644 --- a/daemon/main.cpp +++ b/daemon/main.cpp @@ -12,6 +12,7 @@ #include "mgmt/face-manager.hpp" #include "mgmt/local-control-header-manager.hpp" #include "mgmt/strategy-choice-manager.hpp" +#include "mgmt/status-server.hpp" #include "mgmt/config-file.hpp" #include @@ -32,6 +33,7 @@ static FibManager* g_fibManager; static FaceManager* g_faceManager; static LocalControlHeaderManager* g_localControlHeaderManager; static StrategyChoiceManager* g_strategyChoiceManager; +static StatusServer* g_statusServer; static shared_ptr g_internalFace; @@ -42,7 +44,7 @@ usage(char* programName) "%s --help\n\tshow this help and exit\n" "%s " "[--config /path/to/nfd.conf]\n" - "\trun forwarding daemon\n" + "\trun forwarding daemon\n" "\t--config ]: path to configuration file\n" "\n", programName, programName @@ -55,12 +57,12 @@ parseCommandLine(int argc, char** argv) g_options.m_showUsage = false; g_options.m_config = DEFAULT_CONFIG_FILE; - while (1) { + while (true) { int option_index = 0; static ::option long_options[] = { - { "help" , no_argument , 0, 0 }, - { "config" , required_argument, 0, 0 }, - { 0 , 0 , 0, 0 } + { "help" , no_argument , 0, 0 }, + { "config" , required_argument, 0, 0 }, + { 0 , 0 , 0, 0 } }; int c = getopt_long_only(argc, argv, "", long_options, &option_index); if (c == -1) break; @@ -88,10 +90,8 @@ initializeMgmt() ConfigFile config; g_internalFace = make_shared(); - g_forwarder->addFace(g_internalFace); - g_internalFace->getValidator().setConfigFile(config); - + g_forwarder->addFace(g_internalFace); g_fibManager = new FibManager(g_forwarder->getFib(), bind(&Forwarder::getFace, g_forwarder, _1), @@ -107,6 +107,8 @@ initializeMgmt() g_strategyChoiceManager = new StrategyChoiceManager(g_forwarder->getStrategyChoice(), g_internalFace); + g_statusServer = new StatusServer(g_internalFace, *g_forwarder); + config.parse(g_options.m_config, true); config.parse(g_options.m_config, false); @@ -117,17 +119,17 @@ initializeMgmt() int main(int argc, char** argv) { - try { - bool isCommandLineValid = parseCommandLine(argc, argv); - if (!isCommandLineValid) { - usage(argv[0]); - return 1; - } - if (g_options.m_showUsage) { - usage(argv[0]); - return 0; - } + bool isCommandLineValid = parseCommandLine(argc, argv); + if (!isCommandLineValid) { + usage(argv[0]); + return 1; + } + if (g_options.m_showUsage) { + usage(argv[0]); + return 0; + } + try { g_forwarder = new Forwarder(); initializeMgmt(); @@ -138,15 +140,18 @@ main(int argc, char** argv) // NFD_LOG_ERROR("Error: " << error.what()); // NFD_LOG_ERROR("You should either specify --config option or copy sample configuration into " // << DEFAULT_CONFIG_FILE); - } catch(boost::filesystem::filesystem_error& error) { - if (error.code() == boost::system::errc::permission_denied) { - NFD_LOG_ERROR("Error: Permissions denied for " << error.path1()); + } + catch (boost::filesystem::filesystem_error& e) { + if (e.code() == boost::system::errc::permission_denied) { + NFD_LOG_ERROR("Error: Permissions denied for " << e.path1()); NFD_LOG_ERROR(argv[0] << " should be run as superuser"); - } else { - NFD_LOG_ERROR("Error: " << error.what()); } - } catch(std::exception& exception) { - NFD_LOG_ERROR("Error: " << exception.what()); + else { + NFD_LOG_ERROR("Error: " << e.what()); + } + } + catch (std::exception& e) { + NFD_LOG_ERROR("Error: " << e.what()); return 1; } @@ -160,3 +165,4 @@ main(int argc, char** argv) { return nfd::main(argc, argv); } + diff --git a/daemon/mgmt/status-server.cpp b/daemon/mgmt/status-server.cpp new file mode 100644 index 00000000..9635488a --- /dev/null +++ b/daemon/mgmt/status-server.cpp @@ -0,0 +1,87 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/** + * Copyright (C) 2014 Named Data Networking Project + * See COPYING for copyright and distribution information. + */ + +#include "status-server.hpp" +#include "fw/forwarder.hpp" +#include "core/version.hpp" + +namespace nfd { + +const Name StatusServer::DATASET_PREFIX = "ndn:/localhost/nfd/status"; +const ndn::Milliseconds StatusServer::RESPONSE_FRESHNESS = 5000; + +static inline ndn::nfd::Status::Timestamp +now() +{ + // make sure boost::chrono::system_clock's epoch is UNIX epoch + BOOST_ASSERT(static_cast(0) == boost::chrono::system_clock::to_time_t( + boost::chrono::system_clock::time_point( + boost::chrono::duration_cast( + ndn::nfd::Status::Timestamp::duration::zero() + ) + ) + )); + + return ndn::nfd::Status::Timestamp( + boost::chrono::duration_cast( + boost::chrono::system_clock::now().time_since_epoch() + ) + ); +} + +StatusServer::StatusServer(shared_ptr face, Forwarder& forwarder) + : m_face(face) + , m_forwarder(forwarder) + , m_startTimestamp(now()) +{ + m_face->setInterestFilter(DATASET_PREFIX, bind(&StatusServer::onInterest, this, _2)); +} + +void +StatusServer::onInterest(const Interest& interest) const +{ + Name name(DATASET_PREFIX); + // TODO use NumberComponent + name.append(ndn::Name::Component::fromNumberWithMarker(ndn::ndn_getNowMilliseconds(), 0x00)); + name.append(ndn::Name::Component::fromNumberWithMarker(0, 0x00)); + + shared_ptr data = make_shared(name); + data->setFreshnessPeriod(RESPONSE_FRESHNESS); + + shared_ptr payload = this->collectStatus(); + ndn::EncodingBuffer payloadBuffer; + payload->wireEncode(payloadBuffer); + data->setContent(payloadBuffer.buf(), payloadBuffer.size()); + + m_face->sign(*data); + m_face->put(*data); +} + +shared_ptr +StatusServer::collectStatus() const +{ + shared_ptr status = make_shared(); + + status->setNfdVersion(NFD_VERSION); + status->setStartTimestamp(m_startTimestamp); + status->setCurrentTimestamp(now()); + + status->setNNameTreeEntries(m_forwarder.getNameTree().size()); + status->setNFibEntries(m_forwarder.getFib().size()); + status->setNPitEntries(m_forwarder.getPit().size()); + status->setNMeasurementsEntries(m_forwarder.getMeasurements().size()); + status->setNCsEntries(m_forwarder.getCs().size()); + + const ForwarderCounters& counters = m_forwarder.getCounters(); + status->setNInInterests(counters.getInInterest()); + status->setNOutInterests(counters.getOutInterest()); + status->setNInDatas(counters.getInData()); + status->setNOutDatas(counters.getOutData()); + + return status; +} + +} // namespace nfd diff --git a/daemon/mgmt/status-server.hpp b/daemon/mgmt/status-server.hpp new file mode 100644 index 00000000..94c8e82a --- /dev/null +++ b/daemon/mgmt/status-server.hpp @@ -0,0 +1,40 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/** + * Copyright (C) 2014 Named Data Networking Project + * See COPYING for copyright and distribution information. + */ + +#ifndef NFD_MGMT_STATUS_SERVER_HPP +#define NFD_MGMT_STATUS_SERVER_HPP + +#include "mgmt/app-face.hpp" +#include + +namespace nfd { + +class Forwarder; + +class StatusServer : noncopyable +{ +public: + StatusServer(shared_ptr face, Forwarder& forwarder); + +private: + void + onInterest(const Interest& interest) const; + + shared_ptr + collectStatus() const; + +private: + static const Name DATASET_PREFIX; + static const ndn::Milliseconds RESPONSE_FRESHNESS; + + shared_ptr m_face; + Forwarder& m_forwarder; + ndn::nfd::Status::Timestamp m_startTimestamp; +}; + +} // namespace nfd + +#endif // NFD_MGMT_STATUS_SERVER_HPP diff --git a/tests/mgmt/status-server.cpp b/tests/mgmt/status-server.cpp new file mode 100644 index 00000000..3d8442b2 --- /dev/null +++ b/tests/mgmt/status-server.cpp @@ -0,0 +1,94 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/** + * Copyright (C) 2014 Named Data Networking Project + * See COPYING for copyright and distribution information. + */ + +#include "mgmt/status-server.hpp" +#include "fw/forwarder.hpp" +#include "core/version.hpp" +#include "mgmt/internal-face.hpp" + +#include "tests/test-common.hpp" +#include "tests/face/dummy-face.hpp" + +namespace nfd { +namespace tests { + +BOOST_FIXTURE_TEST_SUITE(MgmtStatusServer, BaseFixture) + +static inline ndn::nfd::Status::Timestamp +now() +{ + return ndn::nfd::Status::Timestamp( + boost::chrono::duration_cast( + boost::chrono::system_clock::now().time_since_epoch() + ) + ); +} + +shared_ptr g_response; + +void +interceptResponse(const Data& data) +{ + g_response = data.shared_from_this(); +} + +BOOST_AUTO_TEST_CASE(Status) +{ + // initialize + ndn::nfd::Status::Timestamp t1 = now(); + Forwarder forwarder; + shared_ptr internalFace = make_shared(); + internalFace->onReceiveData += &interceptResponse; + StatusServer statusServer(internalFace, boost::ref(forwarder)); + ndn::nfd::Status::Timestamp t2 = now(); + + // populate tables + forwarder.getFib().insert("ndn:/fib1"); + forwarder.getPit().insert(*makeInterest("ndn:/pit1")); + forwarder.getPit().insert(*makeInterest("ndn:/pit2")); + forwarder.getPit().insert(*makeInterest("ndn:/pit3")); + forwarder.getPit().insert(*makeInterest("ndn:/pit4")); + forwarder.getMeasurements().get("ndn:/measurements1"); + forwarder.getMeasurements().get("ndn:/measurements2"); + forwarder.getMeasurements().get("ndn:/measurements3"); + BOOST_CHECK_GE(forwarder.getFib().size(), 1); + BOOST_CHECK_GE(forwarder.getPit().size(), 4); + BOOST_CHECK_GE(forwarder.getMeasurements().size(), 3); + + // request + shared_ptr request = makeInterest("ndn:/localhost/nfd/status"); + request->setMustBeFresh(true); + request->setChildSelector(1); + + g_response.reset(); + ndn::nfd::Status::Timestamp t3 = now(); + internalFace->sendInterest(*request); + ndn::nfd::Status::Timestamp t4 = now(); + BOOST_REQUIRE(static_cast(g_response)); + + // verify + ndn::nfd::Status status; + BOOST_REQUIRE_NO_THROW(status.wireDecode(g_response->getContent())); + + BOOST_CHECK_EQUAL(status.getNfdVersion(), NFD_VERSION); + BOOST_CHECK_GE(status.getStartTimestamp(), t1); + BOOST_CHECK_LE(status.getStartTimestamp(), t2); + BOOST_CHECK_GE(status.getCurrentTimestamp(), t3); + BOOST_CHECK_LE(status.getCurrentTimestamp(), t4); + + // StatusServer under test isn't added to Forwarder, + // so request and response won't affect table size + BOOST_CHECK_EQUAL(status.getNNameTreeEntries(), forwarder.getNameTree().size()); + BOOST_CHECK_EQUAL(status.getNFibEntries(), forwarder.getFib().size()); + BOOST_CHECK_EQUAL(status.getNPitEntries(), forwarder.getPit().size()); + BOOST_CHECK_EQUAL(status.getNMeasurementsEntries(), forwarder.getMeasurements().size()); + BOOST_CHECK_EQUAL(status.getNCsEntries(), forwarder.getCs().size()); +} + +BOOST_AUTO_TEST_SUITE_END() + +} // namespace tests +} // namespace nfd diff --git a/wscript b/wscript index ea5ef35d..0a8652d9 100644 --- a/wscript +++ b/wscript @@ -22,20 +22,20 @@ def configure(conf): pass if conf.options.debug: - conf.define ('_DEBUG', 1) - conf.add_supported_cxxflags (cxxflags = ['-O0', - '-Wall', - '-Wno-unused-variable', - '-g3', - '-Wno-unused-private-field', # only clang supports - '-fcolor-diagnostics', # only clang supports - '-Qunused-arguments', # only clang supports - '-Wno-tautological-compare', # suppress warnings from CryptoPP - '-Wno-unused-function', # suppress warnings from CryptoPP - '-fno-inline', - ]) + conf.define('_DEBUG', 1) + conf.add_supported_cxxflags(cxxflags = ['-O0', + '-Wall', + '-Wno-unused-variable', + '-g3', + '-Wno-unused-private-field', # only clang supports + '-fcolor-diagnostics', # only clang supports + '-Qunused-arguments', # only clang supports + '-Wno-tautological-compare', # suppress warnings from CryptoPP + '-Wno-unused-function', # suppress warnings from CryptoPP + '-fno-inline', + ]) else: - conf.add_supported_cxxflags (cxxflags = ['-O3', '-g', '-Wno-tautological-compare', '-Wno-unused-function']) + conf.add_supported_cxxflags(cxxflags = ['-O3', '-g', '-Wno-tautological-compare', '-Wno-unused-function']) if not conf.options.ndn_cpp_dir: conf.check_cfg(package='libndn-cpp-dev', args=['--cflags', '--libs'], uselib_store='NDN_CPP', mandatory=True) @@ -45,7 +45,7 @@ def configure(conf): linkflags="-L%s/lib" % conf.options.ndn_cpp_dir, mandatory=True) - boost_libs='system' + boost_libs = 'system chrono' if conf.options.with_tests: conf.env['WITH_TESTS'] = 1 conf.define('WITH_TESTS', 1); @@ -54,9 +54,9 @@ def configure(conf): conf.check_boost(lib=boost_libs) if conf.env.BOOST_VERSION_NUMBER < 104800: - Logs.error ("Minimum required boost version is 1.48.0") - Logs.error ("Please upgrade your distribution or install custom boost libraries" + - " (http://redmine.named-data.net/projects/nfd/wiki/Boost_FAQ)") + Logs.error("Minimum required boost version is 1.48.0") + Logs.error("Please upgrade your distribution or install custom boost libraries" + + " (http://redmine.named-data.net/projects/nfd/wiki/Boost_FAQ)") return conf.load('unix-socket') @@ -106,7 +106,7 @@ def build(bld): # Unit tests if bld.env['WITH_TESTS']: - unit_tests = unittests = bld.program( + unit_tests = bld.program( target="unit-tests", features = "cxx cxxprogram", source = bld.path.ant_glob(['tests/**/*.cpp'],