diff --git a/ChangeLog b/ChangeLog index a42db2d4a3dc577330732c56e27f67e7489b317e..79672b695584cc7eebc968621741ffdf46acfc56 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +1942. [func] fdupont + Added basic statistics to the DHCP-DDNS server. + (Gitlab #2040) + 1941. [func] fdupont Per DNS server TSIG keys are now supported in the DHCP-DDNS (aka D2) server configuration. A new callout point 'select_key' diff --git a/src/bin/d2/d2_process.cc b/src/bin/d2/d2_process.cc index ebd3865e79009b02bcb17fa3cc5ceafc32b31657..a4a738be240c93708e62ee95908d599a65fb37b2 100644 --- a/src/bin/d2/d2_process.cc +++ b/src/bin/d2/d2_process.cc @@ -16,7 +16,6 @@ #include #include #include -#include using namespace isc::hooks; using namespace isc::process; @@ -66,16 +65,8 @@ D2Process::D2Process(const char* name, const asiolink::IOServicePtr& io_service) D2CfgMgrPtr tmp = getD2CfgMgr(); update_mgr_.reset(new D2UpdateMgr(queue_mgr_, tmp, getIoService())); - // Instantiate stats manager. - // Initialize statistics. - isc::stats::StatsMgr& stats_mgr = isc::stats::StatsMgr::instance(); - stats_mgr.setMaxSampleCountDefault(0); - for (const auto& name : D2Stats::ncr) { - stats_mgr.setValue(name, static_cast(0)); - } - for (const auto& name : D2Stats::update) { - stats_mgr.setValue(name, static_cast(0)); - } + // Initialize stats manager. + D2Stats::init(); }; void diff --git a/src/bin/d2/tests/Makefile.am b/src/bin/d2/tests/Makefile.am index 2d0013561c070b95f5c5345ea8fcffe7fd830241..820c4900c470ba783817d7604d53e4be7a5b5146 100644 --- a/src/bin/d2/tests/Makefile.am +++ b/src/bin/d2/tests/Makefile.am @@ -60,6 +60,7 @@ d2_unittests_SOURCES += get_config_unittest.cc d2_unittests_SOURCES += d2_command_unittest.cc d2_unittests_SOURCES += simple_add_unittests.cc d2_unittests_SOURCES += simple_remove_unittests.cc +d2_unittests_SOURCES += stats_test_utils.cc stats_test_utils.h d2_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) d2_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) diff --git a/src/bin/d2/tests/dns_client_unittests.cc b/src/bin/d2/tests/dns_client_unittests.cc index 85927cbe01762b4e248aee300636dd41ab0c3dee..33369935ab0904ddca73e031b9c21efa4090c0ed 100644 --- a/src/bin/d2/tests/dns_client_unittests.cc +++ b/src/bin/d2/tests/dns_client_unittests.cc @@ -16,6 +16,7 @@ #include #include #include +#include #include using namespace std; @@ -23,9 +24,10 @@ using namespace isc; using namespace isc::asiolink; using namespace isc::asiodns; using namespace isc::d2; - -using namespace isc; +using namespace isc::d2::test; +using namespace isc::data; using namespace isc::dns; +using namespace isc::stats; using namespace isc::util; using namespace boost::asio; using namespace boost::asio::ip; @@ -51,7 +53,7 @@ const long TEST_TIMEOUT = 5 * 1000; // properly handled a task may be hanging for a long time. In order to prevent // it, the asiolink::IntervalTimer is used to break a running test if test // timeout is hit. This will result in test failure. -class DNSClientTest : public virtual ::testing::Test, DNSClient::Callback { +class DNSClientTest : public virtual D2StatTest, DNSClient::Callback { public: IOService service_; D2UpdateMessagePtr response_; @@ -490,12 +492,20 @@ TEST_F(DNSClientTest, runTSIGTest) { D2TsigKey(Name("one.com"), TSIGKey::HMACMD5_NAME(), secret.c_str(), secret.size()))); + StatMap stats_key = { + { "update-sent", 0}, + { "update-success", 0}, + { "update-timeout", 0}, + { "update-error", 0} + }; + checkStats("one.com.", stats_key); secret = "key number two"; D2TsigKeyPtr key_two; ASSERT_NO_THROW(key_two.reset(new D2TsigKey(Name("two.com"), TSIGKey::HMACMD5_NAME(), secret.c_str(), secret.size()))); + checkStats("two.com.", stats_key); D2TsigKeyPtr nokey; // Should be able to send and receive with no keys. @@ -514,6 +524,25 @@ TEST_F(DNSClientTest, runTSIGTest) { // Client neither signs nor verifies, server responds with a signed answer // Since we are "liberal" in what we accept this should be ok. runTSIGTest(nokey, key_two); + + // Check statistics. + StatMap stats_one = { + { "update-sent", 3}, + { "update-success", 1}, + { "update-timeout", 0}, + { "update-error", 2} + }; + checkStats("one.com.", stats_one); + checkStats("two.com.", stats_key); + StatMap stats_upd = { + { "update-sent", 5}, + { "update-signed", 3}, + { "update-unsigned", 2}, + { "update-success", 3}, + { "update-timeout", 0}, + { "update-error", 2} + }; + checkStats(stats_upd); } // Verify that the DNSClient receives the response from DNS and the received diff --git a/src/bin/d2/tests/stats_test_utils.cc b/src/bin/d2/tests/stats_test_utils.cc new file mode 100644 index 0000000000000000000000000000000000000000..7c8f6df6e328a997fe59e9ab35337ce3f1249628 --- /dev/null +++ b/src/bin/d2/tests/stats_test_utils.cc @@ -0,0 +1,56 @@ +// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + +#include +#include + +using namespace isc::data; +using namespace isc::stats; +using namespace std; + +namespace isc { +namespace d2 { +namespace test { + +D2StatTest::D2StatTest() { + StatsMgr::instance().removeAll(); + D2Stats::init(); +} + +D2StatTest::~D2StatTest() { + StatsMgr::instance().removeAll(); +} + +void +D2StatTest::checkStat(const string& name, const int64_t expected_value) { + ObservationPtr obs = StatsMgr::instance().getObservation(name); + ASSERT_TRUE(obs) << " stat: " << name << " not found "; + ASSERT_EQ(expected_value, obs->getInteger().first) + << " stat: " << name << " value wrong"; +} + +void +D2StatTest::checkStats(const StatMap& expected_stats) { + for (const auto& it : expected_stats) { + checkStat(it.first, it.second); + } +} + +void +D2StatTest::checkStats(const string& key_name, const StatMap& expected_stats) { + StatMap key_stats; + for (const auto& it : expected_stats) { + const string& stat_name = + StatsMgr::generateName("key", key_name, it.first); + key_stats[stat_name] = it.second; + } + checkStats(key_stats); +} + +} +} +} diff --git a/src/bin/d2/tests/stats_test_utils.h b/src/bin/d2/tests/stats_test_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..1cc17fe559f1175bf785b6aaffb76e5cc3867d91 --- /dev/null +++ b/src/bin/d2/tests/stats_test_utils.h @@ -0,0 +1,66 @@ +// Copyright (C) 2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef STATS_TEST_UTILS_H +#define STATS_TEST_UTILS_H + +#include +#include +#include +#include + +#include + +namespace isc { +namespace d2 { +namespace test { + +/// @brief Type of name x value for statistics. +typedef std::map StatMap; + +/// @brief Test fixture class with utility functions to test statistics. +class D2StatTest : public ::testing::Test { +public: + /// @brief Constructor. + D2StatTest(); + + /// @brief Destructor. + virtual ~D2StatTest(); + + /// @brief Compares a statistic to an expected value. + /// + /// Attempt to fetch the named statistic from the StatsMgr and if + /// found, compare its observed value to the given value. + /// Fails if the stat is not found or if the values do not match. + /// + /// @param name StatsMgr name for the statistic to check. + /// @param expected_value expected value of the statistic. + void checkStat(const std::string& name, const int64_t expected_value); + + /// @brief Compares StatsMgr statistics against expected values. + /// + /// Iterates over a list of statistic names and expected values, attempting + /// to fetch each from the StatsMgr and if found, compare its observed + /// value to the expected value. Fails if any of the expected stats are not + /// found or if the values do not match. + /// + /// @param expected_stats Map of expected static names and values. + void checkStats(const StatMap& expected_stats); + + /// @brief Compares StatsMgr key statistics against expected values. + /// + /// Prepend key part of names before calling checkStats simpler variant. + /// + /// @param key_name Name of the key. + /// @param expected_stats Map of expected static names and values. + void checkStats(const std::string& key_name, const StatMap& expected_stats); +}; + +} +} +} + +#endif // STATS_TEST_UTILS_H diff --git a/src/lib/d2srv/d2_stats.cc b/src/lib/d2srv/d2_stats.cc index 5c94f4304ffa8e363900676458d3d0e5e16f93b5..3d3d3b6ff19c2c49e06887a52a13069b94e368ac 100644 --- a/src/lib/d2srv/d2_stats.cc +++ b/src/lib/d2srv/d2_stats.cc @@ -9,8 +9,10 @@ #include #include +#include using namespace std; +using namespace isc::stats; namespace isc { namespace d2 { @@ -40,5 +42,17 @@ D2Stats::key = { "update-error" }; +void +D2Stats::init() { + StatsMgr& stats_mgr = isc::stats::StatsMgr::instance(); + stats_mgr.setMaxSampleCountDefault(0); + for (const auto& name : D2Stats::ncr) { + stats_mgr.setValue(name, static_cast(0)); + } + for (const auto& name : D2Stats::update) { + stats_mgr.setValue(name, static_cast(0)); + } +}; + } // namespace d2 } // namespace isc diff --git a/src/lib/d2srv/d2_stats.h b/src/lib/d2srv/d2_stats.h index be40415e50de11fbbdede698009be97717020af3..4bfd1da7ab191c97ad3cccdfa90d3883d7cc64fb 100644 --- a/src/lib/d2srv/d2_stats.h +++ b/src/lib/d2srv/d2_stats.h @@ -40,6 +40,11 @@ public: /// - update-timeout /// - update-error static std::set key; + + /// @brief Initialize D2 statistics. + /// + /// @note: Add default samples if needed. + static void init(); }; } // namespace d2