diff options
author | Andrei Pavel <andrei@isc.org> | 2023-04-26 14:45:29 +0200 |
---|---|---|
committer | Andrei Pavel <andrei@isc.org> | 2023-05-17 18:09:38 +0200 |
commit | 4a06d2baf0dff1d21948bd9a39f100c94a159fe0 (patch) | |
tree | 83c4bce0b946e63e204521806ad0341f747e5f46 | |
parent | [#2658] add BigIntElement (diff) | |
download | kea-4a06d2baf0dff1d21948bd9a39f100c94a159fe0.tar.xz kea-4a06d2baf0dff1d21948bd9a39f100c94a159fe0.zip |
[#2658] add big integer support to statistics
Pool capacities have been promoted in code from uint64_t to uint128_t.
Bigint is modelled as an int128_t in statistics, following the precedent of
signed integer of int64_t from statistics.
- "total-nas" is extended to 128 bits as requested in the issue.
- "total-pds" has the same risk of overflowing so it has been extended
as well.
- "total-addresses" always fits in 64 bits, but certain code forces a
128 bit value on it. See Pool::getCapacity(), Subnet::getPoolCapacity(),
Subnet::sumPoolCapacity(). It could have been truncated to a 64 bit value,
but that seems like an unnecessary complication.
Because of the disparity in signedness there is some truncation that can
happen when pool capacity values are passed on to statistics. That only happens
for the last half of the value range, so for prefix ranges larger than /1.
35 files changed, 556 insertions, 360 deletions
diff --git a/src/bin/dhcp4/tests/fqdn_unittest.cc b/src/bin/dhcp4/tests/fqdn_unittest.cc index 2f1cda9b5f..2b87a360a1 100644 --- a/src/bin/dhcp4/tests/fqdn_unittest.cc +++ b/src/bin/dhcp4/tests/fqdn_unittest.cc @@ -894,11 +894,12 @@ public: } - /// @brief Checks the value of statistic for a given subnet. + /// @brief Checks the value of an integer statistic for a given subnet. /// - /// @param subnet_id Identifier of a subnet for which statistics should be - /// @param name statistic name (e.g. "assigned-addresses", "total-addresses" ...) + /// @param subnet_id identifier of a subnet for which the statistic should be checked + /// @param name statistic name (e.g. "assigned-addresses", "total-addresses", ...) /// @param exp_value expected value of the statistic + /// /// @return Number of assigned addresses for a subnet. void checkSubnetStat(const SubnetID& subnet_id, const std::string& name, int64_t exp_value) const { // Retrieve statistics name, e.g. subnet[1234].assigned-addresses. @@ -907,6 +908,21 @@ public: ASSERT_TRUE(obs) << "cannot find: " << stats_name; EXPECT_EQ(exp_value, obs->getInteger().first); } + + /// @brief Checks the value of a big integer statistic for a given subnet. + /// + /// @param subnet_id identifier of a subnet for which the statistic should be checked + /// @param name statistic name (e.g. "total-nas", "total-pds", ...) + /// @param exp_value expected value of the statistic + /// + /// @return Number of assigned addresses for a subnet. + void checkBigSubnetStat(const SubnetID& subnet_id, const std::string& name, int64_t exp_value) const { + // Retrieve statistics name, e.g. subnet[1234].assigned-addresses. + const std::string stats_name = StatsMgr::generateName("subnet", subnet_id, name); + ObservationPtr obs = StatsMgr::instance().getObservation(stats_name); + ASSERT_TRUE(obs) << "cannot find: " << stats_name; + EXPECT_EQ(exp_value, obs->getBigInteger().first); + } }; // Tests the following scenario: diff --git a/src/hooks/dhcp/stat_cmds/stat_cmds.cc b/src/hooks/dhcp/stat_cmds/stat_cmds.cc index 026da5c9e3..eb43ae771b 100644 --- a/src/hooks/dhcp/stat_cmds/stat_cmds.cc +++ b/src/hooks/dhcp/stat_cmds/stat_cmds.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2023 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 @@ -205,13 +205,21 @@ public: void addValueRow6(ElementPtr value_rows, const SubnetID &subnet_id, int64_t assigned, int64_t declined, int64_t assigned_pds); - /// @brief Fetches a single statistic for a subnet from StatsMgr + /// @brief Fetches a single integer statistic for a subnet from StatsMgr. /// /// Uses the given id and name to query the StatsMgr for the desired value. /// - /// @param subnet_id id of the desired subnet - /// @param name name of the desired statistic + /// @param subnet_id the subnet ID for the desired statistic + /// @param name the name of the desired statistic int64_t getSubnetStat(const SubnetID& subnet_id, const std::string& name); + + /// @brief Fetches a single bigint statistic for a subnet from StatsMgr. + /// + /// Uses the given id and name to query the StatsMgr for the desired value. + /// + /// @param subnet_id the subnet ID for the desired statistic + /// @param name the name of the desired statistic + int128_t getBigSubnetStat(const SubnetID& subnet_id, const std::string& name); }; int @@ -676,7 +684,7 @@ LeaseStatCmdsImpl::createResultSet(const ElementPtr &result_wrapper, void LeaseStatCmdsImpl::addValueRow4(ElementPtr value_rows, const SubnetID &subnet_id, - int64_t assigned, int64_t declined) { + int64_t assigned, int64_t declined) { ElementPtr row = Element::createList(); row->add(Element::create(static_cast<int64_t>(subnet_id))); row->add(Element::create(getSubnetStat(subnet_id, "total-addresses"))); @@ -688,14 +696,14 @@ LeaseStatCmdsImpl::addValueRow4(ElementPtr value_rows, const SubnetID &subnet_id void LeaseStatCmdsImpl::addValueRow6(ElementPtr value_rows, const SubnetID &subnet_id, - int64_t assigned, int64_t declined, int64_t assigned_pds) { + int64_t assigned, int64_t declined, int64_t assigned_pds) { ElementPtr row = Element::createList(); row->add(Element::create(static_cast<int64_t>(subnet_id))); - row->add(Element::create(getSubnetStat(subnet_id, "total-nas"))); + row->add(Element::create(getBigSubnetStat(subnet_id, "total-nas"))); row->add(Element::create(getSubnetStat(subnet_id, "cumulative-assigned-nas"))); row->add(Element::create(assigned)); row->add(Element::create(declined)); - row->add(Element::create(getSubnetStat(subnet_id, "total-pds"))); + row->add(Element::create(getBigSubnetStat(subnet_id, "total-pds"))); row->add(Element::create(getSubnetStat(subnet_id, "cumulative-assigned-pds"))); row->add(Element::create(assigned_pds)); value_rows->add(row); @@ -712,6 +720,17 @@ LeaseStatCmdsImpl::getSubnetStat(const SubnetID& subnet_id, const std::string& n return (0); } +int128_t +LeaseStatCmdsImpl::getBigSubnetStat(const SubnetID& subnet_id, const std::string& name) { + ObservationPtr stat = StatsMgr::instance(). + getObservation(StatsMgr::generateName("subnet", subnet_id, name)); + if (stat) { + return (stat->getBigInteger().first); + } + + return (0); +} + // Using a critical section to avoid any changes in parallel. int diff --git a/src/lib/asiolink/addr_utilities.cc b/src/lib/asiolink/addr_utilities.cc index 04b44dc517..d99c66b49a 100644 --- a/src/lib/asiolink/addr_utilities.cc +++ b/src/lib/asiolink/addr_utilities.cc @@ -15,6 +15,7 @@ using namespace isc; using namespace isc::asiolink; +using namespace isc::util; namespace { @@ -199,7 +200,7 @@ IOAddress getNetmask4(uint8_t len) { return (IOAddress(x)); } -uint64_t +uint128_t addrsInRange(const IOAddress& min, const IOAddress& max) { if (min.getFamily() != max.getFamily()) { isc_throw(BadValue, "Both addresses have to be the same family"); @@ -349,28 +350,19 @@ prefixLengthFromRange(const IOAddress& min, const IOAddress& max) { } } -uint64_t prefixesInRange(const uint8_t pool_len, const uint8_t delegated_len) { +uint128_t prefixesInRange(const uint8_t pool_len, const uint8_t delegated_len) { if (delegated_len < pool_len) { return (0); } - uint64_t count = delegated_len - pool_len; - - if (count == 0) { - // If we want to delegate /64 out of /64 pool, we have only - // one prefix. - return (1); - } else if (count >= 64) { - // If the difference is greater than or equal 64, e.g. we want to - // delegate /96 out of /16 pool, the number is bigger than we can - // express, so we'll stick with maximum value of uint64_t. - return (std::numeric_limits<uint64_t>::max()); - } else { - // Now count specifies the exponent (e.g. if the difference between the - // delegated and pool length is 4, we have 16 prefixes), so we need - // to calculate 2^(count - 1) - return ((static_cast<uint64_t>(2)) << (count - 1)); + uint8_t const count(delegated_len - pool_len); + + if (count == 128) { + // One off is the best we can do, unless we promote to uint256_t. + return uint128_t(-1); } + + return (uint128_t(1) << count); } IOAddress offsetAddress(const IOAddress& addr, uint64_t offset) { diff --git a/src/lib/asiolink/addr_utilities.h b/src/lib/asiolink/addr_utilities.h index bd78deec4c..9aa9af2d10 100644 --- a/src/lib/asiolink/addr_utilities.h +++ b/src/lib/asiolink/addr_utilities.h @@ -8,6 +8,7 @@ #define ADDR_UTILITIES_H #include <asiolink/io_address.h> +#include <util/bigints.h> namespace isc { namespace asiolink { @@ -54,7 +55,7 @@ IOAddress getNetmask4(uint8_t len); /// @param min the first address in range /// @param max the last address in range /// @return number of addresses in range -uint64_t addrsInRange(const IOAddress& min, const IOAddress& max); +isc::util::uint128_t addrsInRange(const IOAddress& min, const IOAddress& max); /// @brief Returns prefix length from the specified range (min - max). /// @@ -78,7 +79,7 @@ int prefixLengthFromRange(const IOAddress& min, const IOAddress& max); /// @param pool_len length of the pool in bits /// @param delegated_len length of the prefixes to be delegated from the pool /// @return number of prefixes in range -uint64_t prefixesInRange(const uint8_t pool_len, const uint8_t delegated_len); +isc::util::uint128_t prefixesInRange(const uint8_t pool_len, const uint8_t delegated_len); /// @brief Finds the address increased by offset. /// @@ -92,7 +93,7 @@ uint64_t prefixesInRange(const uint8_t pool_len, const uint8_t delegated_len); /// @return address being offset greater than the input address IOAddress offsetAddress(const IOAddress& addr, uint64_t offset); -}; -}; +} // namespace asiolink +} // namespace isc #endif // ADDR_UTILITIES_H diff --git a/src/lib/asiolink/tests/addr_utilities_unittest.cc b/src/lib/asiolink/tests/addr_utilities_unittest.cc index a8564379a6..df7a6aca18 100644 --- a/src/lib/asiolink/tests/addr_utilities_unittest.cc +++ b/src/lib/asiolink/tests/addr_utilities_unittest.cc @@ -8,6 +8,7 @@ #include <asiolink/addr_utilities.h> #include <exceptions/exceptions.h> +#include <util/bigints.h> #include <gtest/gtest.h> @@ -18,6 +19,7 @@ using namespace std; using namespace isc::asiolink; +using namespace isc::util; namespace { @@ -358,13 +360,13 @@ TEST(AddrUtilitiesTest, prefixesInRange) { EXPECT_EQ(uint64_t(9223372036854775808ull), prefixesInRange(64, 127)); // How many /128 prefixes are in /64 pool? - EXPECT_EQ(std::numeric_limits<uint64_t>::max(), - prefixesInRange(64, 128)); + EXPECT_EQ(uint128_t(1) << 64, prefixesInRange(64, 128)); // Let's go overboard again. How many IPv6 addresses are there? - EXPECT_EQ(std::numeric_limits<uint64_t>::max(), - prefixesInRange(0, 128)); + EXPECT_EQ(uint128_t(1) << 127, prefixesInRange(1, 128)); + // Let's go overboard again. How many IPv6 addresses are there? + EXPECT_EQ(uint128_t(-1), prefixesInRange(0, 128)); } // Checks the function which finds an IPv4 address from input address and offset. diff --git a/src/lib/dhcpsrv/alloc_engine.cc b/src/lib/dhcpsrv/alloc_engine.cc index 374aa0695f..733404a17c 100644 --- a/src/lib/dhcpsrv/alloc_engine.cc +++ b/src/lib/dhcpsrv/alloc_engine.cc @@ -35,7 +35,6 @@ #include <boost/make_shared.hpp> #include <algorithm> -#include <limits> #include <sstream> #include <stdint.h> #include <string.h> @@ -90,7 +89,7 @@ AllocEngineHooks Hooks; namespace isc { namespace dhcp { -AllocEngine::AllocEngine(uint64_t attempts) +AllocEngine::AllocEngine(uint128_t const& attempts) : attempts_(attempts), incomplete_v4_reclamations_(0), incomplete_v6_reclamations_(0) { @@ -969,17 +968,21 @@ AllocEngine::allocateBestMatch(ClientContext6& ctx, // - we find a free address // - we find an address for which the lease has expired // - we exhaust number of tries - uint64_t possible_attempts = subnet->getPoolCapacity(ctx.currentIA().type_, - classes, - prefix_length_match, - hint_prefix_length); + uint128_t const possible_attempts = + subnet->getPoolCapacity(ctx.currentIA().type_, + classes, + prefix_length_match, + hint_prefix_length); // If the number of tries specified in the allocation engine constructor // is set to 0 (unlimited) or the pools capacity is lower than that number, // let's use the pools capacity as the maximum number of tries. Trying // more than the actual pools capacity is a waste of time. If the specified // number of tries is lower than the pools capacity, use that number. - uint64_t max_attempts = ((attempts_ == 0) || (possible_attempts < attempts_)) ? possible_attempts : attempts_; + uint128_t const max_attempts = + (attempts_ == 0 || possible_attempts < attempts_) ? + possible_attempts : + attempts_; if (max_attempts > 0) { // If max_attempts is greater than 0, there are some pools in this subnet @@ -4360,7 +4363,7 @@ AllocEngine::allocateUnreservedLease4(ClientContext4& ctx) { Subnet4Ptr original_subnet = subnet; - uint64_t total_attempts = 0; + uint128_t total_attempts = 0; // The following counter tracks the number of subnets with matching client // classes from which the allocation engine attempted to assign leases. @@ -4377,7 +4380,7 @@ AllocEngine::allocateUnreservedLease4(ClientContext4& ctx) { client_id = ctx.clientid_; } - uint64_t possible_attempts = + uint128_t const possible_attempts = subnet->getPoolCapacity(Lease::TYPE_V4, classes); // If the number of tries specified in the allocation engine constructor @@ -4385,7 +4388,10 @@ AllocEngine::allocateUnreservedLease4(ClientContext4& ctx) { // let's use the pools capacity as the maximum number of tries. Trying // more than the actual pools capacity is a waste of time. If the specified // number of tries is lower than the pools capacity, use that number. - uint64_t max_attempts = ((attempts_ == 0 || possible_attempts < attempts_) ? possible_attempts : attempts_); + uint128_t const max_attempts = + (attempts_ == 0 || possible_attempts < attempts_) ? + possible_attempts : + attempts_; if (max_attempts > 0) { // If max_attempts is greater than 0, there are some pools in this subnet @@ -4402,7 +4408,7 @@ AllocEngine::allocateUnreservedLease4(ClientContext4& ctx) { CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE; - for (uint64_t i = 0; i < max_attempts; ++i) { + for (uint128_t i = 0; i < max_attempts; ++i) { ++total_attempts; diff --git a/src/lib/dhcpsrv/alloc_engine.h b/src/lib/dhcpsrv/alloc_engine.h index f844fa7710..88bb9044db 100644 --- a/src/lib/dhcpsrv/alloc_engine.h +++ b/src/lib/dhcpsrv/alloc_engine.h @@ -56,7 +56,7 @@ public: /// /// @param attempts number of attempts for each lease allocation before /// we give up (0 means unlimited) - AllocEngine(uint64_t attempts); + AllocEngine(isc::util::uint128_t const& attempts); /// @brief Destructor. virtual ~AllocEngine() { } @@ -64,7 +64,7 @@ public: private: /// @brief number of attempts before we give up lease allocation (0=unlimited) - uint64_t attempts_; + isc::util::uint128_t attempts_; /// @brief Hook name indexes (used in hooks callouts) int hook_index_lease4_select_; ///< index for lease4_select hook diff --git a/src/lib/dhcpsrv/base_host_data_source.h b/src/lib/dhcpsrv/base_host_data_source.h index fa512ec3d7..f3013a6591 100644 --- a/src/lib/dhcpsrv/base_host_data_source.h +++ b/src/lib/dhcpsrv/base_host_data_source.h @@ -13,6 +13,7 @@ #include <exceptions/exceptions.h> #include <boost/shared_ptr.hpp> +#include <limits> #include <vector> namespace isc { diff --git a/src/lib/dhcpsrv/cfg_subnets4.cc b/src/lib/dhcpsrv/cfg_subnets4.cc index 381b4d4404..aaebbd693a 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.cc +++ b/src/lib/dhcpsrv/cfg_subnets4.cc @@ -553,8 +553,7 @@ CfgSubnets4::updateStatistics() { stats_mgr.setValue(StatsMgr:: generateName("subnet", subnet_id, "total-addresses"), - static_cast<int64_t> - (subnet4->getPoolCapacity(Lease::TYPE_V4))); + int64_t(subnet4->getPoolCapacity(Lease::TYPE_V4))); std::string name = StatsMgr::generateName("subnet", subnet_id, "cumulative-assigned-addresses"); if (!stats_mgr.getObservation(name)) { diff --git a/src/lib/dhcpsrv/cfg_subnets6.cc b/src/lib/dhcpsrv/cfg_subnets6.cc index 45b42fd99f..819d77aded 100644 --- a/src/lib/dhcpsrv/cfg_subnets6.cc +++ b/src/lib/dhcpsrv/cfg_subnets6.cc @@ -450,13 +450,11 @@ CfgSubnets6::updateStatistics() { stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id, "total-nas"), - static_cast<int64_t> - (subnet6->getPoolCapacity(Lease::TYPE_NA))); + subnet6->getPoolCapacity(Lease::TYPE_NA)); stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id, "total-pds"), - static_cast<int64_t> - (subnet6->getPoolCapacity(Lease::TYPE_PD))); + subnet6->getPoolCapacity(Lease::TYPE_PD)); const std::string& name_nas = StatsMgr::generateName("subnet", subnet_id, "cumulative-assigned-nas"); diff --git a/src/lib/dhcpsrv/host_data_source_factory.cc b/src/lib/dhcpsrv/host_data_source_factory.cc index 6f6141d787..fa27d08f8f 100644 --- a/src/lib/dhcpsrv/host_data_source_factory.cc +++ b/src/lib/dhcpsrv/host_data_source_factory.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2015-2022 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2015-2023 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 @@ -21,7 +21,6 @@ #include <boost/algorithm/string.hpp> #include <boost/foreach.hpp> -#include <boost/scoped_ptr.hpp> #include <algorithm> #include <iostream> diff --git a/src/lib/dhcpsrv/host_data_source_factory.h b/src/lib/dhcpsrv/host_data_source_factory.h index 76b2a9db72..f2ae0dbb72 100644 --- a/src/lib/dhcpsrv/host_data_source_factory.h +++ b/src/lib/dhcpsrv/host_data_source_factory.h @@ -10,7 +10,6 @@ #include <database/database_connection.h> #include <dhcpsrv/base_host_data_source.h> #include <exceptions/exceptions.h> -#include <boost/scoped_ptr.hpp> #include <functional> #include <string> diff --git a/src/lib/dhcpsrv/lease_mgr.cc b/src/lib/dhcpsrv/lease_mgr.cc index 9511a67df4..ded2577f7f 100644 --- a/src/lib/dhcpsrv/lease_mgr.cc +++ b/src/lib/dhcpsrv/lease_mgr.cc @@ -21,6 +21,7 @@ #include <algorithm> #include <iostream> #include <iterator> +#include <limits> #include <map> #include <sstream> #include <string> diff --git a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc index 2aa74afc8b..eed5493f58 100644 --- a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc +++ b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc @@ -27,6 +27,7 @@ #include <boost/make_shared.hpp> #include <boost/scoped_ptr.hpp> +#include <limits> #include <map> #include <string> #include <vector> diff --git a/src/lib/dhcpsrv/parsers/duid_config_parser.cc b/src/lib/dhcpsrv/parsers/duid_config_parser.cc index 5817bafe7e..f2922816eb 100644 --- a/src/lib/dhcpsrv/parsers/duid_config_parser.cc +++ b/src/lib/dhcpsrv/parsers/duid_config_parser.cc @@ -15,7 +15,7 @@ #include <exceptions/exceptions.h> #include <boost/foreach.hpp> #include <boost/lexical_cast.hpp> -#include <limits> + #include <string> using namespace isc::data; diff --git a/src/lib/dhcpsrv/parsers/multi_threading_config_parser.cc b/src/lib/dhcpsrv/parsers/multi_threading_config_parser.cc index 8793ba67b9..68f6c41991 100644 --- a/src/lib/dhcpsrv/parsers/multi_threading_config_parser.cc +++ b/src/lib/dhcpsrv/parsers/multi_threading_config_parser.cc @@ -11,6 +11,8 @@ #include <dhcpsrv/parsers/multi_threading_config_parser.h> #include <util/multi_threading_mgr.h> +#include <limits> + using namespace isc::data; using namespace isc::util; diff --git a/src/lib/dhcpsrv/pool.h b/src/lib/dhcpsrv/pool.h index 8917e81167..368cc6e0b6 100644 --- a/src/lib/dhcpsrv/pool.h +++ b/src/lib/dhcpsrv/pool.h @@ -1,4 +1,4 @@ -// Copyright (C) 2012-2022 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2012-2023 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 @@ -16,6 +16,7 @@ #include <dhcpsrv/cfg_option.h> #include <dhcpsrv/lease.h> #include <dhcpsrv/ip_range_permutation.h> +#include <util/bigints.h> #include <boost/shared_ptr.hpp> @@ -84,7 +85,7 @@ public: /// Note that this is the upper bound, assuming that no leases are used /// and there are no host reservations. This is just a theoretical calculation. /// @return number of possible leases in this pool - uint64_t getCapacity() const { + isc::util::uint128_t getCapacity() const { return (capacity_); } @@ -208,7 +209,7 @@ protected: /// involved, so it is more efficient to calculate it once and just store /// the result. Note that for very large pools, the number is capped at /// max value of uint64_t. - uint64_t capacity_; + isc::util::uint128_t capacity_; /// @brief Pointer to the option data configuration for this pool. CfgOptionPtr cfg_option_; diff --git a/src/lib/dhcpsrv/subnet.cc b/src/lib/dhcpsrv/subnet.cc index ef37ca52fb..fca60a5829 100644 --- a/src/lib/dhcpsrv/subnet.cc +++ b/src/lib/dhcpsrv/subnet.cc @@ -23,6 +23,7 @@ #include <boost/make_shared.hpp> #include <algorithm> +#include <limits> #include <sstream> using namespace isc::asiolink; @@ -30,6 +31,8 @@ using namespace isc::data; using namespace isc::dhcp; using namespace isc::util; +using namespace std; + namespace { /// @brief Function used in calls to std::upper_bound to check @@ -89,7 +92,7 @@ Subnet::toText() const { return (tmp.str()); } -uint64_t +uint128_t Subnet::getPoolCapacity(Lease::Type type) const { switch (type) { case Lease::TYPE_V4: @@ -105,7 +108,7 @@ Subnet::getPoolCapacity(Lease::Type type) const { } } -uint64_t +uint128_t Subnet::getPoolCapacity(Lease::Type type, const ClientClasses& client_classes) const { switch (type) { @@ -122,7 +125,7 @@ Subnet::getPoolCapacity(Lease::Type type, } } -uint64_t +uint128_t Subnet::getPoolCapacity(Lease::Type type, const ClientClasses& client_classes, Allocator::PrefixLenMatchType prefix_length_match, @@ -142,52 +145,53 @@ Subnet::getPoolCapacity(Lease::Type type, } } -uint64_t +uint128_t Subnet::sumPoolCapacity(const PoolCollection& pools) const { - uint64_t sum = 0; + uint128_t sum(0); for (auto const& p : pools) { - uint64_t x = p->getCapacity(); + uint128_t const c(p->getCapacity()); - // Check if we can add it. If sum + x > uint64::max, then we would have + // Check if we can add it. If sum + c > UINT128_MAX, then we would have // overflown if we tried to add it. - if (x > std::numeric_limits<uint64_t>::max() - sum) { - return (std::numeric_limits<uint64_t>::max()); + if (c > numeric_limits<uint128_t>::max() - sum) { + return (numeric_limits<uint128_t>::max()); } - sum += x; + sum += c; } return (sum); } -uint64_t +uint128_t Subnet::sumPoolCapacity(const PoolCollection& pools, const ClientClasses& client_classes) const { - uint64_t sum = 0; + uint128_t sum(0); for (auto const& p : pools) { if (!p->clientSupported(client_classes)) { continue; } - uint64_t x = p->getCapacity(); - // Check if we can add it. If sum + x > uint64::max, then we would have + uint128_t const c(p->getCapacity()); + + // Check if we can add it. If sum + c > UINT128_MAX, then we would have // overflown if we tried to add it. - if (x > std::numeric_limits<uint64_t>::max() - sum) { - return (std::numeric_limits<uint64_t>::max()); + if (c > numeric_limits<uint128_t>::max() - sum) { + return (numeric_limits<uint128_t>::max()); } - sum += x; + sum += c; } return (sum); } -uint64_t +uint128_t Subnet::sumPoolCapacity(const PoolCollection& pools, const ClientClasses& client_classes, Allocator::PrefixLenMatchType prefix_length_match, uint8_t hint_prefix_length) const { - uint64_t sum = 0; + uint128_t sum(0); for (auto const& p : pools) { if (!p->clientSupported(client_classes)) { continue; @@ -198,15 +202,15 @@ Subnet::sumPoolCapacity(const PoolCollection& pools, continue; } - uint64_t x = p->getCapacity(); + uint128_t const c(p->getCapacity()); - // Check if we can add it. If sum + x > uint64::max, then we would have + // Check if we can add it. If sum + c > UINT128_MAX, then we would have // overflown if we tried to add it. - if (x > std::numeric_limits<uint64_t>::max() - sum) { - return (std::numeric_limits<uint64_t>::max()); + if (c > numeric_limits<uint128_t>::max() - sum) { + return (numeric_limits<uint128_t>::max()); } - sum += x; + sum += c; } return (sum); diff --git a/src/lib/dhcpsrv/subnet.h b/src/lib/dhcpsrv/subnet.h index 891d20d424..a875bbeac8 100644 --- a/src/lib/dhcpsrv/subnet.h +++ b/src/lib/dhcpsrv/subnet.h @@ -17,6 +17,7 @@ #include <dhcpsrv/network.h> #include <dhcpsrv/pool.h> #include <dhcpsrv/subnet_id.h> +#include <util/bigints.h> #include <util/dhcp_space.h> #include <util/triplet.h> @@ -28,7 +29,6 @@ #include <boost/multi_index_container.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/pointer_cast.hpp> -#include <boost/scoped_ptr.hpp> #include <boost/shared_ptr.hpp> #include <cstdint> @@ -176,7 +176,7 @@ public: /// @brief Returns the number of possible leases for specified lease type. /// /// @param type type of the lease - uint64_t getPoolCapacity(Lease::Type type) const; + isc::util::uint128_t getPoolCapacity(Lease::Type type) const; /// @brief Returns the number of possible leases for specified lease type /// allowed for a client which belongs to classes. @@ -184,8 +184,8 @@ public: /// @param type type of the lease /// @param client_classes list of classes the client belongs to /// @return number of leases matching lease type and classes - uint64_t getPoolCapacity(Lease::Type type, - const ClientClasses& client_classes) const; + isc::util::uint128_t getPoolCapacity(Lease::Type type, + const ClientClasses& client_classes) const; /// @brief Returns the number of possible leases for specified lease type /// allowed for a client which belongs to classes and matching selection @@ -198,10 +198,10 @@ public: /// @param hint_prefix_length the hint prefix length that the client /// provided /// @return number of leases matching lease type and classes - uint64_t getPoolCapacity(Lease::Type type, - const ClientClasses& client_classes, - Allocator::PrefixLenMatchType prefix_length_match, - uint8_t hint_prefix_length) const; + isc::util::uint128_t getPoolCapacity(Lease::Type type, + const ClientClasses& client_classes, + Allocator::PrefixLenMatchType prefix_length_match, + uint8_t hint_prefix_length) const; /// @brief Returns textual representation of the subnet (e.g. /// "2001:db8::/64"). @@ -393,15 +393,15 @@ protected: /// /// @param pools list of pools /// @return sum of possible leases - uint64_t sumPoolCapacity(const PoolCollection& pools) const; + isc::util::uint128_t sumPoolCapacity(const PoolCollection& pools) const; /// @brief Returns a sum of possible leases in all pools allowing classes. /// /// @param pools list of pools /// @param client_classes list of classes /// @return sum of possible/allowed leases - uint64_t sumPoolCapacity(const PoolCollection& pools, - const ClientClasses& client_classes) const; + isc::util::uint128_t sumPoolCapacity(const PoolCollection& pools, + const ClientClasses& client_classes) const; /// @brief Returns a sum of possible leases in all pools allowing classes /// and matching selection criteria relative to provided hint prefix length. @@ -415,10 +415,10 @@ protected: /// @param hint_prefix_length the hint prefix length that the client /// provided /// @return sum of possible/allowed leases - uint64_t sumPoolCapacity(const PoolCollection& pools, - const ClientClasses& client_classes, - Allocator::PrefixLenMatchType prefix_length_match, - uint8_t hint_prefix_length) const; + isc::util::uint128_t sumPoolCapacity(const PoolCollection& pools, + const ClientClasses& client_classes, + Allocator::PrefixLenMatchType prefix_length_match, + uint8_t hint_prefix_length) const; /// @brief Checks if the specified pool overlaps with an existing pool. /// diff --git a/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc index 71ad585300..e5a5f511a0 100644 --- a/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc @@ -1717,13 +1717,13 @@ TEST(CfgSubnets6Test, updateStatistics) { StatsMgr::generateName("subnet", subnet_id, "total-nas")); ASSERT_TRUE(observation); - ASSERT_EQ(0, observation->getInteger().first); + ASSERT_EQ(0, observation->getBigInteger().first); observation = StatsMgr::instance().getObservation( StatsMgr::generateName("subnet", subnet_id, "total-pds")); ASSERT_TRUE(observation); - ASSERT_EQ(0, observation->getInteger().first); + ASSERT_EQ(0, observation->getBigInteger().first); observation = StatsMgr::instance().getObservation( StatsMgr::generateName("subnet", subnet_id, @@ -1786,24 +1786,24 @@ TEST(CfgSubnets6Test, removeStatistics) { StatsMgr::instance().setValue( StatsMgr::generateName("subnet", subnet_id, "total-nas"), - int64_t(0)); + int128_t(0)); observation = StatsMgr::instance().getObservation( StatsMgr::generateName("subnet", subnet_id, "total-nas")); ASSERT_TRUE(observation); - ASSERT_EQ(0, observation->getInteger().first); + ASSERT_EQ(0, observation->getBigInteger().first); StatsMgr::instance().setValue( StatsMgr::generateName("subnet", subnet_id, "total-pds"), - int64_t(0)); + int128_t(0)); observation = StatsMgr::instance().getObservation( StatsMgr::generateName("subnet", subnet_id, "total-pds")); ASSERT_TRUE(observation); - ASSERT_EQ(0, observation->getInteger().first); + ASSERT_EQ(0, observation->getBigInteger().first); StatsMgr::instance().setValue( StatsMgr::generateName("subnet", subnet_id, diff --git a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc index d1b5413aad..e84fab7b6f 100644 --- a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2012-2022 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2012-2023 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 @@ -17,8 +17,6 @@ #include <stats/stats_mgr.h> #include <util/chrono_time_utils.h> -#include <boost/scoped_ptr.hpp> - #include <gtest/gtest.h> #include <iostream> @@ -36,10 +34,6 @@ using namespace isc::stats; using namespace isc::process; using namespace isc; -// don't import the entire boost namespace. It will unexpectedly hide uint8_t -// for some systems. -using boost::scoped_ptr; - namespace { template <typename Storage> @@ -593,7 +587,7 @@ TEST_F(CfgMgrTest, commitStats4) { CfgSubnets4Ptr subnets = cfg_mgr.getStagingCfg()->getCfgSubnets4(); subnets->add(subnet1); cfg_mgr.commit(); - stats_mgr.addValue("subnet[123].total-addresses", static_cast<int64_t>(256)); + stats_mgr.addValue("subnet[123].total-addresses", int64_t(256)); stats_mgr.setValue("subnet[123].assigned-addresses", static_cast<int64_t>(150)); // Now, let's change the configuration to something new. @@ -646,7 +640,7 @@ TEST_F(CfgMgrTest, mergeIntoCurrentStats4) { CfgSubnets4Ptr subnets = cfg_mgr.getStagingCfg()->getCfgSubnets4(); subnets->add(subnet1); cfg_mgr.commit(); - stats_mgr.addValue("subnet[123].total-addresses", static_cast<int64_t>(256)); + stats_mgr.addValue("subnet[123].total-addresses", int64_t(256)); stats_mgr.setValue("subnet[123].assigned-addresses", static_cast<int64_t>(150)); // There should be no stats for subnet 42 at this point. @@ -705,7 +699,7 @@ TEST_F(CfgMgrTest, clearStats4) { CfgSubnets4Ptr subnets = cfg_mgr.getStagingCfg()->getCfgSubnets4(); subnets->add(subnet1); cfg_mgr.commit(); - stats_mgr.addValue("subnet[123].total-addresses", static_cast<int64_t>(256)); + stats_mgr.addValue("subnet[123].total-addresses", int64_t(256)); stats_mgr.setValue("subnet[123].assigned-addresses", static_cast<int64_t>(150)); // The stats should be there. @@ -733,10 +727,10 @@ TEST_F(CfgMgrTest, commitStats6) { CfgSubnets6Ptr subnets = cfg_mgr.getStagingCfg()->getCfgSubnets6(); subnets->add(subnet1); cfg_mgr.commit(); - stats_mgr.addValue("subnet[123].total-nas", static_cast<int64_t>(256)); + stats_mgr.addValue("subnet[123].total-nas", int128_t(256)); stats_mgr.setValue("subnet[123].assigned-nas", static_cast<int64_t>(150)); - stats_mgr.addValue("subnet[123].total-pds", static_cast<int64_t>(256)); + stats_mgr.addValue("subnet[123].total-pds", int128_t(256)); stats_mgr.setValue("subnet[123].assigned-pds", static_cast<int64_t>(150)); // Now, let's change the configuration to something new. @@ -774,7 +768,7 @@ TEST_F(CfgMgrTest, commitStats6) { ObservationPtr total_addrs; EXPECT_NO_THROW(total_addrs = stats_mgr.getObservation("subnet[42].total-nas")); ASSERT_TRUE(total_addrs); - EXPECT_EQ(128, total_addrs->getInteger().first); + EXPECT_EQ(128, total_addrs->getBigInteger().first); EXPECT_TRUE(total_addrs->getMaxSampleCount().first); EXPECT_EQ(14, total_addrs->getMaxSampleCount().second); EXPECT_FALSE(total_addrs->getMaxSampleAge().first); @@ -783,7 +777,7 @@ TEST_F(CfgMgrTest, commitStats6) { ObservationPtr total_prfx; EXPECT_NO_THROW(total_prfx = stats_mgr.getObservation("subnet[42].total-pds")); ASSERT_TRUE(total_prfx); - EXPECT_EQ(65536, total_prfx->getInteger().first); + EXPECT_EQ(65536, total_prfx->getBigInteger().first); EXPECT_TRUE(total_prfx->getMaxSampleCount().first); EXPECT_EQ(14, total_prfx->getMaxSampleCount().second); EXPECT_FALSE(total_prfx->getMaxSampleAge().first); @@ -856,11 +850,11 @@ TEST_F(CfgMgrTest, DISABLED_mergeIntoCurrentStats6) { ObservationPtr total_addrs; EXPECT_NO_THROW(total_addrs = stats_mgr.getObservation("subnet[42].total-nas")); ASSERT_TRUE(total_addrs); - EXPECT_EQ(128, total_addrs->getInteger().first); + EXPECT_EQ(128, total_addrs->getBigInteger().first); EXPECT_NO_THROW(total_addrs = stats_mgr.getObservation("subnet[42].total-pds")); ASSERT_TRUE(total_addrs); - EXPECT_EQ(65536, total_addrs->getInteger().first); + EXPECT_EQ(65536, total_addrs->getBigInteger().first); } // This test verifies that once the configuration is cleared, the v6 statistics diff --git a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc index 489f7cca0c..de9f6a2e5d 100644 --- a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc +++ b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc @@ -38,7 +38,6 @@ #include <gtest/gtest.h> #include <boost/foreach.hpp> #include <boost/pointer_cast.hpp> -#include <boost/scoped_ptr.hpp> #include <map> #include <string> diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc index d6c46cc520..93e132dbe5 100644 --- a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc @@ -16,7 +16,9 @@ #include <dhcpsrv/testutils/test_utils.h> #include <exceptions/exceptions.h> #include <stats/stats_mgr.h> +#include <stats/testutils/stats_test_utils.h> #include <testutils/gtest_utils.h> +#include <util/bigints.h> #include <boost/foreach.hpp> #include <boost/scoped_ptr.hpp> @@ -31,6 +33,11 @@ using namespace std; using namespace isc::asiolink; using namespace isc::data; using namespace isc::db; +using namespace isc::stats; +using namespace isc::util; + +using isc::stats::test::checkStat; + namespace ph = std::placeholders; namespace isc { @@ -75,6 +82,17 @@ GenericLeaseMgrTest::GenericLeaseMgrTest() /// a template leasetype6_.push_back(LEASETYPE6[i]); } + + // Clear all subnets defined in previous tests. + for (Subnet4Ptr const& subnet : *CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->getAll()) { + CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->del(subnet); + } + for (Subnet6Ptr const& subnet : *CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->getAll()) { + CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->del(subnet); + } + + // Clear all stats. + StatsMgr::instance().removeAll(); } GenericLeaseMgrTest::~GenericLeaseMgrTest() { @@ -2855,17 +2873,6 @@ GenericLeaseMgrTest::testGetDeclinedLeases6() { } void -GenericLeaseMgrTest::checkStat(const std::string& name, - const int64_t expected_value) { - stats::ObservationPtr obs = - stats::StatsMgr::instance().getObservation(name); - - ASSERT_TRUE(obs) << " stat: " << name << " not found "; - ASSERT_EQ(expected_value, obs->getInteger().first) - << " stat: " << name << " value wrong"; -} - -void GenericLeaseMgrTest::checkLeaseStats(const StatValMapList& expectedStats) { // Global accumulators int64_t declined_addresses = 0; @@ -3063,7 +3070,6 @@ GenericLeaseMgrTest::testRecountLeaseStats4() { ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats)); } - void GenericLeaseMgrTest::testRecountLeaseStats6() { using namespace stats; @@ -4577,6 +4583,44 @@ GenericLeaseMgrTest::testRecreateWithoutCallbacks(const std::string& access) { EXPECT_TRUE(logs_.empty()); } +void +GenericLeaseMgrTest::testBigStats() { + StatValMapList expected_stats(1); + + // Create the largest possible subnet with the largest possible IPv4 pool. + Subnet4Ptr subnet4(new Subnet4(IOAddress("0.0.0.0"), 1, 1, 2, 3, 1)); + subnet4->addPool(Pool4Ptr(new Pool4(IOAddress("0.0.0.0"), 1))); + expected_stats[0]["total-addresses"] = 2147483648; + + // Create the largest possible subnet with the largest possible IA_NA pool. + Subnet6Ptr subnet6(new Subnet6(IOAddress("::"), 1, 1, 2, 3, 4, 1)); + subnet6->addPool(Pool6Ptr(new Pool6(Lease::TYPE_NA, IOAddress("::"), 1))); + + // Add the largest possible IA_PD pool. + subnet6->addPool(Pool6Ptr(new Pool6(Lease::TYPE_PD, IOAddress("8000::"), 1))); + + // Commit the subnets to the configurations. + CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet4); + CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet6); + ASSERT_NO_THROW(CfgMgr::instance().commit()); + + // Create the expected stats list. At this point, the only stats + // that should be non-zero are total-addresses, total-nas, total-pds. + expected_stats[0]["assigned-nas"] = 0; + expected_stats[0]["declined-addresses"] = 0; + expected_stats[0]["reclaimed-declined-addresses"] = 0; + expected_stats[0]["assigned-pds"] = 0; + expected_stats[0]["reclaimed-leases"] = 0; + + // Make sure stats are as expected. + ASSERT_NO_THROW(checkLeaseStats(expected_stats)); + + // Check the big integers separately. + int128_t const two_to_the_power_of_127(int128_t(1) << 127); + checkStat(StatsMgr::generateName("subnet", 1, "total-nas"), two_to_the_power_of_127); + checkStat(StatsMgr::generateName("subnet", 1, "total-pds"), two_to_the_power_of_127); +} + } // namespace test } // namespace dhcp } // namespace isc diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h index 3f47baf2ff..b0d64eb56a 100644 --- a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h +++ b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h @@ -115,16 +115,6 @@ public: /// @return vector<Lease6Ptr> Vector of pointers to leases std::vector<Lease6Ptr> createLeases6(); - /// @brief Compares a StatsMgr 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 an expected list of values /// /// Iterates over a list of statistic names and expected values, attempting @@ -669,6 +659,9 @@ public: /// lease manager. void testRecreateWithoutCallbacks(const std::string& access); + /// @brief Checks that statistic with big integer values are handled correctly. + void testBigStats(); + /// @brief String forms of IPv4 addresses std::vector<std::string> straddress4_; diff --git a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc index 443e7f4df5..8a8daadf8d 100644 --- a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc @@ -4404,4 +4404,9 @@ TEST_F(MemfileLeaseMgrTest, recreateWithoutCallbacks) { testRecreateWithoutCallbacks(getConfigString(V4)); } +TEST_F(MemfileLeaseMgrTest, bigStats) { + startBackend(V4); + testBigStats(); +} + } // namespace diff --git a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc index 87b6b82500..eebe5a9b79 100644 --- a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc @@ -1339,4 +1339,8 @@ TEST_F(MySqlLeaseMgrTest, recreateWithoutCallbacks) { testRecreateWithoutCallbacks(validMySQLConnectionString()); } +TEST_F(MySqlLeaseMgrTest, bigStats) { + testBigStats(); +} + } // namespace diff --git a/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc index b0be50cc70..0045cefd74 100644 --- a/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc @@ -1328,5 +1328,8 @@ TEST_F(PgSqlLeaseMgrTest, recreateWithoutCallbacks) { testRecreateWithoutCallbacks(validPgSQLConnectionString()); } +TEST_F(PgSqlLeaseMgrTest, bigStats) { + testBigStats(); +} } // namespace diff --git a/src/lib/dhcpsrv/tests/pool_unittest.cc b/src/lib/dhcpsrv/tests/pool_unittest.cc index 3b532674c4..8ee26e75aa 100644 --- a/src/lib/dhcpsrv/tests/pool_unittest.cc +++ b/src/lib/dhcpsrv/tests/pool_unittest.cc @@ -12,15 +12,12 @@ #include <dhcpsrv/pool.h> #include <testutils/test_to_element.h> -#include <boost/scoped_ptr.hpp> - #include <gtest/gtest.h> #include <iostream> #include <vector> #include <sstream> -using boost::scoped_ptr; using namespace isc; using namespace isc::dhcp; using namespace isc::data; diff --git a/src/lib/dhcpsrv/tests/subnet_unittest.cc b/src/lib/dhcpsrv/tests/subnet_unittest.cc index b4151b5277..dfab6fceb0 100644 --- a/src/lib/dhcpsrv/tests/subnet_unittest.cc +++ b/src/lib/dhcpsrv/tests/subnet_unittest.cc @@ -25,13 +25,8 @@ #include <exceptions/exceptions.h> #include <boost/pointer_cast.hpp> -#include <boost/scoped_ptr.hpp> -#include <gtest/gtest.h> -#include <limits> -// don't import the entire boost namespace. It will unexpectedly hide uint8_t -// for some systems. -using boost::scoped_ptr; +#include <gtest/gtest.h> using namespace isc; using namespace isc::dhcp; @@ -1103,12 +1098,12 @@ TEST(Subnet6Test, Pool6PdgetPoolCapacity) { // This is 2^64. PoolPtr pool4(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:4::"), 48, 112)); subnet->addPool(pool4); - EXPECT_EQ(std::numeric_limits<uint64_t>::max(), + EXPECT_EQ(65536 + 4294967296ull + 4294967296ull + (int128_t(1) << 64), subnet->getPoolCapacity(Lease::TYPE_PD)); PoolPtr pool5(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:5::"), 48, 112)); subnet->addPool(pool5); - EXPECT_EQ(std::numeric_limits<uint64_t>::max(), + EXPECT_EQ(65536 + 4294967296ull + 4294967296ull + (int128_t(1) << 64) + (int128_t(1) << 64), subnet->getPoolCapacity(Lease::TYPE_PD)); } diff --git a/src/lib/stats/observation.cc b/src/lib/stats/observation.cc index c8e7b0f5fd..034f2b9745 100644 --- a/src/lib/stats/observation.cc +++ b/src/lib/stats/observation.cc @@ -9,12 +9,14 @@ #include <stats/observation.h> #include <util/chrono_time_utils.h> #include <cc/data.h> + #include <chrono> #include <utility> using namespace std; using namespace std::chrono; using namespace isc::data; +using namespace isc::util; namespace isc { namespace stats { @@ -33,6 +35,13 @@ Observation::Observation(const std::string& name, const int64_t value) : setValue(value); } +Observation::Observation(const std::string& name, const int128_t& value) : + name_(name), type_(STAT_BIG_INTEGER), + max_sample_count_(default_max_sample_count_), + max_sample_age_(default_max_sample_age_) { + setValue(value); +} + Observation::Observation(const std::string& name, const double value) : name_(name), type_(STAT_FLOAT), max_sample_count_(default_max_sample_count_), @@ -60,6 +69,10 @@ void Observation::setMaxSampleAge(const StatsDuration& duration) { setMaxSampleAgeInternal(integer_samples_, duration, STAT_INTEGER); return; } + case STAT_BIG_INTEGER: { + setMaxSampleAgeInternal(big_integer_samples_, duration, STAT_BIG_INTEGER); + return; + } case STAT_FLOAT: { setMaxSampleAgeInternal(float_samples_, duration, STAT_FLOAT); return; @@ -84,6 +97,10 @@ void Observation::setMaxSampleCount(uint32_t max_samples) { setMaxSampleCountInternal(integer_samples_, max_samples, STAT_INTEGER); return; } + case STAT_BIG_INTEGER: { + setMaxSampleCountInternal(big_integer_samples_, max_samples, STAT_BIG_INTEGER); + return; + } case STAT_FLOAT: { setMaxSampleCountInternal(float_samples_, max_samples, STAT_FLOAT); return; @@ -107,6 +124,11 @@ void Observation::addValue(const int64_t value) { setValue(current.first + value); } +void Observation::addValue(const int128_t& value) { + BigIntegerSample current = getBigInteger(); + setValue(current.first + value); +} + void Observation::addValue(const double value) { FloatSample current = getFloat(); setValue(current.first + value); @@ -126,6 +148,10 @@ void Observation::setValue(const int64_t value) { setValueInternal(value, integer_samples_, STAT_INTEGER); } +void Observation::setValue(const int128_t& value) { + setValueInternal(value, big_integer_samples_, STAT_BIG_INTEGER); +} + void Observation::setValue(const double value) { setValueInternal(value, float_samples_, STAT_FLOAT); } @@ -145,6 +171,10 @@ size_t Observation::getSize() const { size = getSizeInternal(integer_samples_, STAT_INTEGER); return (size); } + case STAT_BIG_INTEGER: { + size = getSizeInternal(big_integer_samples_, STAT_BIG_INTEGER); + return (size); + } case STAT_FLOAT: { size = getSizeInternal(float_samples_, STAT_FLOAT); return (size); @@ -223,6 +253,10 @@ IntegerSample Observation::getInteger() const { return (getValueInternal<IntegerSample>(integer_samples_, STAT_INTEGER)); } +BigIntegerSample Observation::getBigInteger() const { + return (getValueInternal<BigIntegerSample>(big_integer_samples_, STAT_BIG_INTEGER)); +} + FloatSample Observation::getFloat() const { return (getValueInternal<FloatSample>(float_samples_, STAT_FLOAT)); } @@ -256,6 +290,10 @@ std::list<IntegerSample> Observation::getIntegers() const { return (getValuesInternal<IntegerSample>(integer_samples_, STAT_INTEGER)); } +std::list<BigIntegerSample> Observation::getBigIntegers() const { + return (getValuesInternal<BigIntegerSample>(big_integer_samples_, STAT_BIG_INTEGER)); +} + std::list<FloatSample> Observation::getFloats() const { return (getValuesInternal<FloatSample>(float_samples_, STAT_FLOAT)); } @@ -370,6 +408,9 @@ std::string Observation::typeToText(Type type) { case STAT_INTEGER: tmp << "integer"; break; + case STAT_BIG_INTEGER: + tmp << "big integer"; + break; case STAT_FLOAT: tmp << "float"; break; @@ -414,6 +455,23 @@ Observation::getJSON() const { } break; } + case STAT_BIG_INTEGER: { + std::list<BigIntegerSample> const& samples(getBigIntegers()); + + // Iterate over all elements in the list and alternately add + // value and timestamp to the entry. + for (BigIntegerSample const& i : samples) { + entry = isc::data::Element::createList(); + value = isc::data::Element::create(i.first); + timestamp = isc::data::Element::create(isc::util::clockToText(i.second)); + + entry->add(value); + entry->add(timestamp); + + list->add(entry); + } + break; + } case STAT_FLOAT: { std::list<FloatSample> s = getFloats(); @@ -480,6 +538,11 @@ void Observation::reset() { setValue(static_cast<int64_t>(0)); return; } + case STAT_BIG_INTEGER: { + big_integer_samples_.clear(); + setValue(int128_t(0)); + return; + } case STAT_FLOAT: { float_samples_.clear(); setValue(0.0); diff --git a/src/lib/stats/observation.h b/src/lib/stats/observation.h index f1e0117ef4..883d7e7d1d 100644 --- a/src/lib/stats/observation.h +++ b/src/lib/stats/observation.h @@ -9,9 +9,13 @@ #include <cc/data.h> #include <exceptions/exceptions.h> +#include <util/bigints.h> + #include <boost/shared_ptr.hpp> + #include <chrono> #include <list> + #include <stdint.h> namespace isc { @@ -55,6 +59,9 @@ inline long toSeconds(const StatsDuration& dur) { /// @brief Integer (implemented as signed 64-bit integer) typedef std::pair<int64_t, SampleClock::time_point> IntegerSample; +/// @brief BigInteger (implemented as signed 128-bit integer) +typedef std::pair<isc::util::int128_t, SampleClock::time_point> BigIntegerSample; + /// @brief Float (implemented as double precision) typedef std::pair<double, SampleClock::time_point> FloatSample; @@ -93,10 +100,11 @@ public: /// int64_t and double. If convincing use cases appear to change them /// to something else, we may change the underlying type. enum Type { - STAT_INTEGER, ///< this statistic is unsigned 64-bit integer value - STAT_FLOAT, ///< this statistic is a floating point value - STAT_DURATION,///< this statistic represents time duration - STAT_STRING ///< this statistic represents a string + STAT_INTEGER, ///< this statistic is signed 64-bit integer value + STAT_BIG_INTEGER, ///< this statistic is signed 128-bit integer value + STAT_FLOAT, ///< this statistic is a floating point value + STAT_DURATION, ///< this statistic represents time duration + STAT_STRING ///< this statistic represents a string }; /// @brief Constructor for integer observations @@ -105,6 +113,12 @@ public: /// @param value integer value observed. Observation(const std::string& name, const int64_t value); + /// @brief Constructor for big integer observations + /// + /// @param name observation name + /// @param value integer value observed. + Observation(const std::string& name, const isc::util::int128_t& value); + /// @brief Constructor for floating point observations /// /// @param name observation name @@ -184,6 +198,12 @@ public: /// @throw InvalidStatType if statistic is not integer void setValue(const int64_t value); + /// @brief Records big integer observation + /// + /// @param value integer value observed + /// @throw InvalidStatType if statistic is not integer + void setValue(const isc::util::int128_t& value); + /// @brief Records absolute floating point observation /// /// @param value floating point value observed @@ -208,6 +228,12 @@ public: /// @throw InvalidStatType if statistic is not integer void addValue(const int64_t value); + /// @brief Records incremental integer observation + /// + /// @param value integer value observed + /// @throw InvalidStatType if statistic is not integer + void addValue(const isc::util::int128_t& value); + /// @brief Records incremental floating point observation /// /// @param value floating point value observed @@ -258,6 +284,11 @@ public: /// @throw InvalidStatType if statistic is not integer IntegerSample getInteger() const; + /// @brief Returns observed integer sample + /// @return observed sample (value + timestamp) + /// @throw InvalidStatType if statistic is not integer + BigIntegerSample getBigInteger() const; + /// @brief Returns observed float sample /// @return observed sample (value + timestamp) /// @throw InvalidStatType if statistic is not fp @@ -278,6 +309,11 @@ public: /// @throw InvalidStatType if statistic is not integer std::list<IntegerSample> getIntegers() const; + /// @brief Returns observed big-integer samples + /// @return list of observed samples (value + timestamp) + /// @throw InvalidStatType if statistic is not integer + std::list<BigIntegerSample> getBigIntegers() const; + /// @brief Returns observed float samples /// @return list of observed samples (value + timestamp) /// @throw InvalidStatType if statistic is not fp @@ -425,6 +461,9 @@ private: /// @brief Storage for integer samples std::list<IntegerSample> integer_samples_; + /// @brief Storage for big integer samples + std::list<BigIntegerSample> big_integer_samples_; + /// @brief Storage for floating point samples std::list<FloatSample> float_samples_; diff --git a/src/lib/stats/stats_mgr.cc b/src/lib/stats/stats_mgr.cc index 58b36abeed..37536d31d0 100644 --- a/src/lib/stats/stats_mgr.cc +++ b/src/lib/stats/stats_mgr.cc @@ -11,7 +11,10 @@ #include <cc/data.h> #include <cc/command_interpreter.h> #include <util/multi_threading_mgr.h> +#include <util/bigints.h> + #include <boost/make_shared.hpp> + #include <chrono> using namespace std; @@ -35,92 +38,68 @@ StatsMgr::StatsMgr() : void StatsMgr::setValue(const string& name, const int64_t value) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - setValueInternal(name, value); - } else { - setValueInternal(name, value); - } + MultiThreadingLock lock(*mutex_); + setValueInternal(name, value); +} + +void +StatsMgr::setValue(const string& name, const int128_t& value) { + MultiThreadingLock lock(*mutex_); + setValueInternal(name, value); } void StatsMgr::setValue(const string& name, const double value) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - setValueInternal(name, value); - } else { - setValueInternal(name, value); - } + MultiThreadingLock lock(*mutex_); + setValueInternal(name, value); } void StatsMgr::setValue(const string& name, const StatsDuration& value) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - setValueInternal(name, value); - } else { - setValueInternal(name, value); - } + MultiThreadingLock lock(*mutex_); + setValueInternal(name, value); } void StatsMgr::setValue(const string& name, const string& value) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - setValueInternal(name, value); - } else { - setValueInternal(name, value); - } + MultiThreadingLock lock(*mutex_); + setValueInternal(name, value); } void StatsMgr::addValue(const string& name, const int64_t value) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - addValueInternal(name, value); - } else { - addValueInternal(name, value); - } + MultiThreadingLock lock(*mutex_); + addValueInternal(name, value); +} + +void +StatsMgr::addValue(const string& name, const int128_t& value) { + MultiThreadingLock lock(*mutex_); + addValueInternal(name, value); } void StatsMgr::addValue(const string& name, const double value) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - addValueInternal(name, value); - } else { - addValueInternal(name, value); - } + MultiThreadingLock lock(*mutex_); + addValueInternal(name, value); } void StatsMgr::addValue(const string& name, const StatsDuration& value) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - addValueInternal(name, value); - } else { - addValueInternal(name, value); - } + MultiThreadingLock lock(*mutex_); + addValueInternal(name, value); } void StatsMgr::addValue(const string& name, const string& value) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - addValueInternal(name, value); - } else { - addValueInternal(name, value); - } + MultiThreadingLock lock(*mutex_); + addValueInternal(name, value); } ObservationPtr StatsMgr::getObservation(const string& name) const { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - return (getObservationInternal(name)); - } else { - return (getObservationInternal(name)); - } + MultiThreadingLock lock(*mutex_); + return (getObservationInternal(name)); } ObservationPtr @@ -132,12 +111,8 @@ StatsMgr::getObservationInternal(const string& name) const { void StatsMgr::addObservation(const ObservationPtr& stat) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - addObservationInternal(stat); - } else { - addObservationInternal(stat); - } + MultiThreadingLock lock(*mutex_); + addObservationInternal(stat); } void @@ -149,12 +124,8 @@ StatsMgr::addObservationInternal(const ObservationPtr& stat) { bool StatsMgr::deleteObservation(const string& name) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - return (deleteObservationInternal(name)); - } else { - return (deleteObservationInternal(name)); - } + MultiThreadingLock lock(*mutex_); + return (deleteObservationInternal(name)); } bool @@ -166,12 +137,8 @@ StatsMgr::deleteObservationInternal(const string& name) { bool StatsMgr::setMaxSampleAge(const string& name, const StatsDuration& duration) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - return (setMaxSampleAgeInternal(name, duration)); - } else { - return (setMaxSampleAgeInternal(name, duration)); - } + MultiThreadingLock lock(*mutex_); + return (setMaxSampleAgeInternal(name, duration)); } bool @@ -187,12 +154,8 @@ StatsMgr::setMaxSampleAgeInternal(const string& name, bool StatsMgr::setMaxSampleCount(const string& name, uint32_t max_samples) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - return (setMaxSampleCountInternal(name, max_samples)); - } else { - return (setMaxSampleCountInternal(name, max_samples)); - } + MultiThreadingLock lock(*mutex_); + return (setMaxSampleCountInternal(name, max_samples)); } bool @@ -208,12 +171,8 @@ StatsMgr::setMaxSampleCountInternal(const string& name, void StatsMgr::setMaxSampleAgeAll(const StatsDuration& duration) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - setMaxSampleAgeAllInternal(duration); - } else { - setMaxSampleAgeAllInternal(duration); - } + MultiThreadingLock lock(*mutex_); + setMaxSampleAgeAllInternal(duration); } void @@ -223,12 +182,8 @@ StatsMgr::setMaxSampleAgeAllInternal(const StatsDuration& duration) { void StatsMgr::setMaxSampleCountAll(uint32_t max_samples) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - setMaxSampleCountAllInternal(max_samples); - } else { - setMaxSampleCountAllInternal(max_samples); - } + MultiThreadingLock lock(*mutex_); + setMaxSampleCountAllInternal(max_samples); } void @@ -238,12 +193,8 @@ StatsMgr::setMaxSampleCountAllInternal(uint32_t max_samples) { void StatsMgr::setMaxSampleAgeDefault(const StatsDuration& duration) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - setMaxSampleAgeDefaultInternal(duration); - } else { - setMaxSampleAgeDefaultInternal(duration); - } + MultiThreadingLock lock(*mutex_); + setMaxSampleAgeDefaultInternal(duration); } void @@ -253,12 +204,8 @@ StatsMgr::setMaxSampleAgeDefaultInternal(const StatsDuration& duration) { void StatsMgr::setMaxSampleCountDefault(uint32_t max_samples) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - setMaxSampleCountDefaultInternal(max_samples); - } else { - setMaxSampleCountDefaultInternal(max_samples); - } + MultiThreadingLock lock(*mutex_); + setMaxSampleCountDefaultInternal(max_samples); } void @@ -268,12 +215,8 @@ StatsMgr::setMaxSampleCountDefaultInternal(uint32_t max_samples) { const StatsDuration& StatsMgr::getMaxSampleAgeDefault() const { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - return (getMaxSampleAgeDefaultInternal()); - } else { - return (getMaxSampleAgeDefaultInternal()); - } + MultiThreadingLock lock(*mutex_); + return (getMaxSampleAgeDefaultInternal()); } const StatsDuration& @@ -283,12 +226,8 @@ StatsMgr::getMaxSampleAgeDefaultInternal() const { uint32_t StatsMgr::getMaxSampleCountDefault() const { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - return (getMaxSampleCountDefaultInternal()); - } else { - return (getMaxSampleCountDefaultInternal()); - } + MultiThreadingLock lock(*mutex_); + return (getMaxSampleCountDefaultInternal()); } uint32_t @@ -298,12 +237,8 @@ StatsMgr::getMaxSampleCountDefaultInternal() const { bool StatsMgr::reset(const string& name) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - return (resetInternal(name)); - } else { - return (resetInternal(name)); - } + MultiThreadingLock lock(*mutex_); + return (resetInternal(name)); } bool @@ -318,12 +253,8 @@ StatsMgr::resetInternal(const string& name) { bool StatsMgr::del(const string& name) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - return (delInternal(name)); - } else { - return (delInternal(name)); - } + MultiThreadingLock lock(*mutex_); + return (delInternal(name)); } bool @@ -333,12 +264,8 @@ StatsMgr::delInternal(const string& name) { void StatsMgr::removeAll() { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - removeAllInternal(); - } else { - removeAllInternal(); - } + MultiThreadingLock lock(*mutex_); + removeAllInternal(); } void @@ -348,12 +275,8 @@ StatsMgr::removeAllInternal() { ConstElementPtr StatsMgr::get(const string& name) const { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - return (getInternal(name)); - } else { - return (getInternal(name)); - } + MultiThreadingLock lock(*mutex_); + return (getInternal(name)); } ConstElementPtr @@ -368,12 +291,8 @@ StatsMgr::getInternal(const string& name) const { ConstElementPtr StatsMgr::getAll() const { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - return (getAllInternal()); - } else { - return (getAllInternal()); - } + MultiThreadingLock lock(*mutex_); + return (getAllInternal()); } ConstElementPtr @@ -383,12 +302,8 @@ StatsMgr::getAllInternal() const { void StatsMgr::resetAll() { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - resetAllInternal(); - } else { - resetAllInternal(); - } + MultiThreadingLock lock(*mutex_); + resetAllInternal(); } void @@ -398,12 +313,8 @@ StatsMgr::resetAllInternal() { size_t StatsMgr::getSize(const string& name) const { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - return (getSizeInternal(name)); - } else { - return (getSizeInternal(name)); - } + MultiThreadingLock lock(*mutex_); + return (getSizeInternal(name)); } size_t @@ -417,12 +328,8 @@ StatsMgr::getSizeInternal(const string& name) const { size_t StatsMgr::count() const { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - return (countInternal()); - } else { - return (countInternal()); - } + MultiThreadingLock lock(*mutex_); + return (countInternal()); } size_t @@ -545,16 +452,10 @@ StatsMgr::statisticSetMaxSampleAgeAllHandler(const ConstElementPtr& params) { if (!StatsMgr::getStatDuration(params, duration, error)) { return (createAnswer(CONTROL_RESULT_ERROR, error)); } - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - StatsMgr::instance().setMaxSampleCountDefaultInternal(0); - StatsMgr::instance().setMaxSampleAgeDefaultInternal(duration); - StatsMgr::instance().setMaxSampleAgeAllInternal(duration); - } else { - StatsMgr::instance().setMaxSampleCountDefaultInternal(0); - StatsMgr::instance().setMaxSampleAgeDefaultInternal(duration); - StatsMgr::instance().setMaxSampleAgeAllInternal(duration); - } + MultiThreadingLock lock(*mutex_); + StatsMgr::instance().setMaxSampleCountDefaultInternal(0); + StatsMgr::instance().setMaxSampleAgeDefaultInternal(duration); + StatsMgr::instance().setMaxSampleAgeAllInternal(duration); return (createAnswer(CONTROL_RESULT_SUCCESS, "All statistics duration limit are set.")); } @@ -570,14 +471,9 @@ StatsMgr::statisticSetMaxSampleCountAllHandler(const ConstElementPtr& params) { error = "'max-samples' parameter must not be zero"; return (createAnswer(CONTROL_RESULT_ERROR, error)); } - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - StatsMgr::instance().setMaxSampleCountDefaultInternal(max_samples); - StatsMgr::instance().setMaxSampleCountAllInternal(max_samples); - } else { - StatsMgr::instance().setMaxSampleCountDefaultInternal(max_samples); - StatsMgr::instance().setMaxSampleCountAllInternal(max_samples); - } + MultiThreadingLock lock(*mutex_); + StatsMgr::instance().setMaxSampleCountDefaultInternal(max_samples); + StatsMgr::instance().setMaxSampleCountAllInternal(max_samples); return (createAnswer(CONTROL_RESULT_SUCCESS, "All statistics count limit are set.")); } diff --git a/src/lib/stats/stats_mgr.h b/src/lib/stats/stats_mgr.h index 564df43976..c1e688d290 100644 --- a/src/lib/stats/stats_mgr.h +++ b/src/lib/stats/stats_mgr.h @@ -9,6 +9,8 @@ #include <stats/observation.h> #include <stats/context.h> +#include <util/bigints.h> + #include <boost/noncopyable.hpp> #include <boost/scoped_ptr.hpp> @@ -79,6 +81,13 @@ public: /// @throw InvalidStatType if statistic is not integer void setValue(const std::string& name, const int64_t value); + /// @brief Records an absolute big integer observation. + /// + /// @param name name of the observation + /// @param value integer value observed + /// @throw InvalidStatType if statistic is not integer + void setValue(const std::string& name, const isc::util::int128_t& value); + /// @brief Records absolute floating point observation. /// /// @param name name of the observation @@ -107,6 +116,13 @@ public: /// @throw InvalidStatType if statistic is not integer void addValue(const std::string& name, const int64_t value); + /// @brief Records an incremental big integer observation. + /// + /// @param name name of the observation + /// @param value integer value observed + /// @throw InvalidStatType if statistic is not integer + void addValue(const std::string& name, const isc::util::int128_t& value); + /// @brief Records incremental floating point observation. /// /// @param name name of the observation diff --git a/src/lib/stats/tests/observation_unittest.cc b/src/lib/stats/tests/observation_unittest.cc index ad1036809c..dc0a4d9826 100644 --- a/src/lib/stats/tests/observation_unittest.cc +++ b/src/lib/stats/tests/observation_unittest.cc @@ -6,10 +6,13 @@ #include <config.h> -#include <stats/observation.h> #include <exceptions/exceptions.h> +#include <stats/observation.h> #include <util/chrono_time_utils.h> +#include <util/bigints.h> + #include <boost/shared_ptr.hpp> + #include <gtest/gtest.h> #include <iostream> @@ -19,6 +22,7 @@ using namespace isc; using namespace isc::stats; +using namespace isc::util; using namespace std::chrono; namespace { @@ -51,13 +55,15 @@ public: a("alpha", static_cast<int64_t>(1234)), // integer b("beta", 12.34), // float c("gamma", dur1234), // duration - d("delta", "1234") { // string + d("delta", "1234"), // string + e("epsilon", int128_t(12e34)) { // big integer } Observation a; Observation b; Observation c; Observation d; + Observation e; }; // Basic tests for the Observation constructors. This test checks whether @@ -67,29 +73,40 @@ TEST_F(ObservationTest, constructor) { EXPECT_EQ(Observation::STAT_FLOAT, b.getType()); EXPECT_EQ(Observation::STAT_DURATION, c.getType()); EXPECT_EQ(Observation::STAT_STRING, d.getType()); + EXPECT_EQ(Observation::STAT_BIG_INTEGER, e.getType()); EXPECT_EQ(1234, a.getInteger().first); EXPECT_EQ(12.34, b.getFloat().first); EXPECT_EQ(dur1234, c.getDuration().first); EXPECT_EQ("1234", d.getString().first); + EXPECT_EQ(int128_t(12e34), e.getBigInteger().first); // Let's check that attempting to get a different type // than used will cause an exception. EXPECT_THROW(a.getFloat(), InvalidStatType); EXPECT_THROW(a.getDuration(), InvalidStatType); EXPECT_THROW(a.getString(), InvalidStatType); + EXPECT_THROW(a.getBigInteger(), InvalidStatType); EXPECT_THROW(b.getInteger(), InvalidStatType); EXPECT_THROW(b.getDuration(), InvalidStatType); EXPECT_THROW(b.getString(), InvalidStatType); + EXPECT_THROW(b.getBigInteger(), InvalidStatType); EXPECT_THROW(c.getInteger(), InvalidStatType); EXPECT_THROW(c.getFloat(), InvalidStatType); EXPECT_THROW(c.getString(), InvalidStatType); + EXPECT_THROW(c.getBigInteger(), InvalidStatType); EXPECT_THROW(d.getInteger(), InvalidStatType); EXPECT_THROW(d.getFloat(), InvalidStatType); EXPECT_THROW(d.getDuration(), InvalidStatType); + EXPECT_THROW(d.getBigInteger(), InvalidStatType); + + EXPECT_THROW(e.getInteger(), InvalidStatType); + EXPECT_THROW(e.getFloat(), InvalidStatType); + EXPECT_THROW(e.getDuration(), InvalidStatType); + EXPECT_THROW(e.getString(), InvalidStatType); } // This test checks whether it is possible to set to an absolute value for all @@ -99,30 +116,40 @@ TEST_F(ObservationTest, setValue) { EXPECT_NO_THROW(b.setValue(56e+78)); EXPECT_NO_THROW(c.setValue(dur5678)); EXPECT_NO_THROW(d.setValue("fiveSixSevenEight")); - + EXPECT_NO_THROW(e.setValue(int128_t(43e21))); EXPECT_EQ(5678, a.getInteger().first); EXPECT_EQ(56e+78, b.getFloat().first); EXPECT_EQ(dur5678, c.getDuration().first); EXPECT_EQ("fiveSixSevenEight", d.getString().first); + EXPECT_EQ(int128_t(43e21), e.getBigInteger().first); // Now check whether setting value to a different type does // throw an exception EXPECT_THROW(a.setValue(56e+78), InvalidStatType); EXPECT_THROW(a.setValue(dur5678), InvalidStatType); EXPECT_THROW(a.setValue("fiveSixSevenEight"), InvalidStatType); + EXPECT_THROW(a.setValue(int128_t(43e21)), InvalidStatType); EXPECT_THROW(b.setValue(static_cast<int64_t>(5678)), InvalidStatType); EXPECT_THROW(b.setValue(dur5678), InvalidStatType); EXPECT_THROW(b.setValue("fiveSixSevenEight"), InvalidStatType); + EXPECT_THROW(b.setValue(int128_t(43e21)), InvalidStatType); EXPECT_THROW(c.setValue(static_cast<int64_t>(5678)), InvalidStatType); EXPECT_THROW(c.setValue(56e+78), InvalidStatType); EXPECT_THROW(c.setValue("fiveSixSevenEight"), InvalidStatType); + EXPECT_THROW(c.setValue(int128_t(43e21)), InvalidStatType); EXPECT_THROW(d.setValue(static_cast<int64_t>(5678)), InvalidStatType); EXPECT_THROW(d.setValue(56e+78), InvalidStatType); EXPECT_THROW(d.setValue(dur5678), InvalidStatType); + EXPECT_THROW(d.setValue(int128_t(43e21)), InvalidStatType); + + EXPECT_THROW(e.setValue(int64_t(5678)), InvalidStatType); + EXPECT_THROW(e.setValue(56e+78), InvalidStatType); + EXPECT_THROW(e.setValue(dur5678), InvalidStatType); + EXPECT_THROW(e.setValue("fiveSixSevenEight"), InvalidStatType); } // This test checks whether it is possible to add value to existing @@ -135,16 +162,19 @@ TEST_F(ObservationTest, addValue) { EXPECT_NO_THROW(b.addValue(56.78)); EXPECT_NO_THROW(c.addValue(dur5678)); EXPECT_NO_THROW(d.addValue("fiveSixSevenEight")); + EXPECT_NO_THROW(e.addValue(int128_t(43e21))); EXPECT_EQ(6912, a.getInteger().first); EXPECT_EQ(69.12, b.getFloat().first); EXPECT_EQ(dur681012, c.getDuration().first); EXPECT_EQ("1234fiveSixSevenEight", d.getString().first); + EXPECT_EQ(int128_t(12e34) + int128_t(43e21), e.getBigInteger().first); ASSERT_EQ(a.getSize(), 2); ASSERT_EQ(b.getSize(), 2); ASSERT_EQ(c.getSize(), 2); ASSERT_EQ(d.getSize(), 2); + ASSERT_EQ(e.getSize(), 2); } // This test checks if collecting more than one sample @@ -153,37 +183,42 @@ TEST_F(ObservationTest, moreThanOne) { // Arrays of 4 types of samples int64_t int_samples[3] = {1234, 6912, 5678}; double float_samples[3] = {12.34, 69.12, 56e+78}; - StatsDuration duration_samples[3] = {dur1234, - dur681012, dur5678}; + StatsDuration duration_samples[3] = {dur1234, dur681012, dur5678}; std::string string_samples[3] = {"1234", "1234fiveSixSevenEight", "fiveSixSevenEight"}; + int128_t bigint_samples[3] = {int128_t(12e34), int128_t(12e34) + int128_t(43e21), + int128_t(43e21)}; EXPECT_NO_THROW(a.addValue(static_cast<int64_t>(5678))); EXPECT_NO_THROW(b.addValue(56.78)); EXPECT_NO_THROW(c.addValue(dur5678)); EXPECT_NO_THROW(d.addValue("fiveSixSevenEight")); + EXPECT_NO_THROW(e.addValue(int128_t(43e21))); EXPECT_NO_THROW(a.setValue(static_cast<int64_t>(5678))); EXPECT_NO_THROW(b.setValue(56e+78)); EXPECT_NO_THROW(c.setValue(dur5678)); EXPECT_NO_THROW(d.setValue("fiveSixSevenEight")); + EXPECT_NO_THROW(e.setValue(int128_t(43e21))); ASSERT_EQ(a.getSize(), 3); ASSERT_EQ(b.getSize(), 3); ASSERT_EQ(c.getSize(), 3); ASSERT_EQ(d.getSize(), 3); + ASSERT_EQ(e.getSize(), 3); ASSERT_NO_THROW(a.getIntegers()); ASSERT_NO_THROW(b.getFloats()); ASSERT_NO_THROW(c.getDurations()); ASSERT_NO_THROW(d.getStrings()); + ASSERT_NO_THROW(e.getBigIntegers()); std::list<IntegerSample> samples_int = a.getIntegers(); // List of all integer samples std::list<FloatSample> samples_float = b.getFloats(); // List of all float samples std::list<DurationSample> samples_dur = c.getDurations(); // List of all duration samples std::list<StringSample> samples_str = d.getStrings(); // List of all string samples + std::list<BigIntegerSample> samples_bigint = e.getBigIntegers(); // List of all big integer samples uint32_t i = 2; // Index pointed to the end of array of samples - for (std::list<IntegerSample>::iterator it = samples_int.begin(); it != samples_int.end(); ++it) { EXPECT_EQ(int_samples[i], static_cast<int64_t>((*it).first)); --i; @@ -203,6 +238,11 @@ TEST_F(ObservationTest, moreThanOne) { EXPECT_EQ(string_samples[i], (*it).first); --i; } + i = 2; + for (BigIntegerSample const& sample : samples_bigint) { + EXPECT_EQ(bigint_samples[i], sample.first); + --i; + } } // This test checks whether the size of storage @@ -213,27 +253,32 @@ TEST_F(ObservationTest, getSize) { ASSERT_EQ(b.getSize(), 1); ASSERT_EQ(c.getSize(), 1); ASSERT_EQ(d.getSize(), 1); + ASSERT_EQ(e.getSize(), 1); a.addValue(static_cast<int64_t>(5678)); b.addValue(56.78); c.addValue(dur5678); d.addValue("fiveSixSevenEight"); + e.addValue(int128_t(43e21)); EXPECT_NO_THROW(a.getSize()); EXPECT_NO_THROW(b.getSize()); EXPECT_NO_THROW(c.getSize()); EXPECT_NO_THROW(d.getSize()); + EXPECT_NO_THROW(e.getSize()); // Check if size of storages is equal to 2 ASSERT_EQ(a.getSize(), 2); ASSERT_EQ(b.getSize(), 2); ASSERT_EQ(c.getSize(), 2); ASSERT_EQ(d.getSize(), 2); + ASSERT_EQ(e.getSize(), 2); a.setValue(static_cast<int64_t>(5678)); b.setValue(56e+78); c.setValue(dur5678); d.setValue("fiveSixSevenEight"); + e.setValue(int128_t(43e21)); EXPECT_NO_THROW(a.getSize()); EXPECT_NO_THROW(b.getSize()); @@ -245,6 +290,7 @@ TEST_F(ObservationTest, getSize) { ASSERT_EQ(b.getSize(), 3); ASSERT_EQ(c.getSize(), 3); ASSERT_EQ(d.getSize(), 3); + ASSERT_EQ(e.getSize(), 3); } // Checks whether setting amount limits works properly @@ -279,18 +325,23 @@ TEST_F(ObservationTest, setCountLimit) { for (uint32_t i = 0; i < 21; ++i) { d.setValue(string_samples[i]); } + for (uint32_t i = 0; i < 21; ++i) { + e.setValue(int128_t(int_samples[i])); + } // Getting all 4 types of samples after inserting 21 values std::list<IntegerSample> samples_int = a.getIntegers(); std::list<FloatSample> samples_float = b.getFloats(); std::list<DurationSample> samples_duration = c.getDurations(); std::list<StringSample> samples_string = d.getStrings(); + std::list<BigIntegerSample> samples_bigint = e.getBigIntegers(); // Check if size of storages is equal to 20 ASSERT_EQ(a.getSize(), 20); ASSERT_EQ(b.getSize(), 20); ASSERT_EQ(c.getSize(), 20); ASSERT_EQ(d.getSize(), 20); + ASSERT_EQ(e.getSize(), 20); // And whether stored values are correct uint32_t i = 20; // index of the last element in array of test's samples @@ -313,23 +364,31 @@ TEST_F(ObservationTest, setCountLimit) { EXPECT_EQ((*it).first, string_samples[i]); --i; } + i = 20; // index of last element in array of test's samples + for (BigIntegerSample const& sample : samples_bigint) { + EXPECT_EQ(sample.first, int_samples[i]); + --i; + } // Change size of storage to smaller one ASSERT_NO_THROW(a.setMaxSampleCount(10)); ASSERT_NO_THROW(b.setMaxSampleCount(10)); ASSERT_NO_THROW(c.setMaxSampleCount(10)); ASSERT_NO_THROW(d.setMaxSampleCount(10)); + ASSERT_NO_THROW(e.setMaxSampleCount(10)); samples_int = a.getIntegers(); samples_float = b.getFloats(); samples_duration = c.getDurations(); samples_string = d.getStrings(); + samples_bigint = e.getBigIntegers(); // Check if size of storages is equal to 10 ASSERT_EQ(a.getSize(), 10); ASSERT_EQ(b.getSize(), 10); ASSERT_EQ(c.getSize(), 10); ASSERT_EQ(d.getSize(), 10); + ASSERT_EQ(e.getSize(), 10); // And whether storages contain only the 10 newest values i = 20; // index of last element in array of test's samples @@ -352,34 +411,44 @@ TEST_F(ObservationTest, setCountLimit) { EXPECT_EQ((*it).first, string_samples[i]); --i; } + i = 20; // index of last element in array of test's samples + for (BigIntegerSample const& sample : samples_bigint) { + EXPECT_EQ(sample.first, int_samples[i]); + --i; + } // Resize max_sample_count to greater ASSERT_NO_THROW(a.setMaxSampleCount(50)); ASSERT_NO_THROW(b.setMaxSampleCount(50)); ASSERT_NO_THROW(c.setMaxSampleCount(50)); ASSERT_NO_THROW(d.setMaxSampleCount(50)); + ASSERT_NO_THROW(e.setMaxSampleCount(50)); // Check if size of storages did not change without adding new value ASSERT_EQ(a.getSize(), 10); ASSERT_EQ(b.getSize(), 10); ASSERT_EQ(c.getSize(), 10); ASSERT_EQ(d.getSize(), 10); + ASSERT_EQ(e.getSize(), 10); // Add new values to each type of Observation a.setValue(static_cast<int64_t>(21)); b.setValue(21.0); c.setValue(milliseconds(21)); d.setValue("v"); + e.setValue(int128_t(21)); samples_int = a.getIntegers(); samples_float = b.getFloats(); samples_duration = c.getDurations(); samples_string = d.getStrings(); + samples_bigint = e.getBigIntegers(); ASSERT_EQ(a.getSize(), 11); ASSERT_EQ(b.getSize(), 11); ASSERT_EQ(c.getSize(), 11); ASSERT_EQ(d.getSize(), 11); + ASSERT_EQ(e.getSize(), 11); i = 21; // index of last element in array of test's samples for (std::list<IntegerSample>::iterator it = samples_int.begin(); it != samples_int.end(); ++it) { @@ -401,7 +470,11 @@ TEST_F(ObservationTest, setCountLimit) { EXPECT_EQ((*it).first, string_samples[i]); --i; } - + i = 21; // index of last element in array of test's samples + for (BigIntegerSample const& sample : samples_bigint) { + EXPECT_EQ(sample.first, int_samples[i]); + --i; + } } // Checks whether setting age limits works properly @@ -451,42 +524,50 @@ TEST_F(ObservationTest, getLimits) { EXPECT_EQ(b.getMaxSampleAge().first, false); EXPECT_EQ(c.getMaxSampleAge().first, false); EXPECT_EQ(d.getMaxSampleAge().first, false); + EXPECT_EQ(e.getMaxSampleAge().first, false); EXPECT_EQ(a.getMaxSampleCount().first, true); EXPECT_EQ(b.getMaxSampleCount().first, true); EXPECT_EQ(c.getMaxSampleCount().first, true); EXPECT_EQ(d.getMaxSampleCount().first, true); + EXPECT_EQ(e.getMaxSampleCount().first, true); EXPECT_EQ(a.getMaxSampleCount().second, 20); EXPECT_EQ(b.getMaxSampleCount().second, 20); EXPECT_EQ(c.getMaxSampleCount().second, 20); EXPECT_EQ(d.getMaxSampleCount().second, 20); + EXPECT_EQ(e.getMaxSampleCount().second, 20); // change limit to time duration ASSERT_NO_THROW(a.setMaxSampleAge(dur453)); ASSERT_NO_THROW(b.setMaxSampleAge(dur453)); ASSERT_NO_THROW(c.setMaxSampleAge(dur453)); ASSERT_NO_THROW(d.setMaxSampleAge(dur453)); + ASSERT_NO_THROW(e.setMaxSampleAge(dur453)); EXPECT_EQ(a.getMaxSampleAge().first, true); EXPECT_EQ(b.getMaxSampleAge().first, true); EXPECT_EQ(c.getMaxSampleAge().first, true); EXPECT_EQ(d.getMaxSampleAge().first, true); + EXPECT_EQ(e.getMaxSampleAge().first, true); EXPECT_EQ(a.getMaxSampleAge().second, dur453); EXPECT_EQ(b.getMaxSampleAge().second, dur453); EXPECT_EQ(c.getMaxSampleAge().second, dur453); EXPECT_EQ(d.getMaxSampleAge().second, dur453); + EXPECT_EQ(e.getMaxSampleAge().second, dur453); EXPECT_EQ(a.getMaxSampleCount().first, false); EXPECT_EQ(b.getMaxSampleCount().first, false); EXPECT_EQ(c.getMaxSampleCount().first, false); EXPECT_EQ(d.getMaxSampleCount().first, false); + EXPECT_EQ(e.getMaxSampleCount().first, false); EXPECT_EQ(a.getMaxSampleCount().second, 20); EXPECT_EQ(b.getMaxSampleCount().second, 20); EXPECT_EQ(c.getMaxSampleCount().second, 20); EXPECT_EQ(d.getMaxSampleCount().second, 20); + EXPECT_EQ(e.getMaxSampleCount().second, 20); } // limit defaults are tested with StatsMgr. @@ -525,7 +606,6 @@ TEST_F(ObservationTest, integerToJSON) { std::string exp = "[ [ 1234, \"" + isc::util::clockToText(a.getInteger().second) + "\" ]" + first_sample; - std::cout << a.getJSON()->str() << std::endl; EXPECT_EQ(exp, a.getJSON()->str()); } @@ -546,7 +626,6 @@ TEST_F(ObservationTest, floatToJSON) { std::string exp = "[ [ 1234.5, \"" + isc::util::clockToText(b.getFloat().second) + "\" ]" + first_sample; - std::cout << b.getJSON()->str() << std::endl; EXPECT_EQ(exp, b.getJSON()->str()); } @@ -564,7 +643,6 @@ TEST_F(ObservationTest, durationToJSON) { std::string exp = "[ [ \"01:02:03.004000\", \"" + isc::util::clockToText(c.getDuration().second) + "\" ]" + first_sample; - std::cout << c.getJSON()->str() << std::endl; EXPECT_EQ(exp, c.getJSON()->str()); } @@ -581,31 +659,50 @@ TEST_F(ObservationTest, stringToJSON) { std::string exp = "[ [ \"Lorem ipsum dolor sit amet\", \"" + isc::util::clockToText(d.getString().second) + "\" ]" + first_sample; - std::cout << d.getJSON()->str() << std::endl; EXPECT_EQ(exp, d.getJSON()->str()); } +// Checks whether a big integer statistic can generate proper JSON structures. +// See https://gitlab.isc.org/isc-projects/kea/wikis/designs/Stats-design +// for details. +TEST_F(ObservationTest, bigIntegerToJSON) { + // String which contains first added sample + std::string first_sample = ", [ 120000000000000007304085773727301632, \"" + + isc::util::clockToText(e.getBigInteger().second) + "\" ] ]"; + + e.setValue(int128_t(43e21)); + + std::string exp = "[ [ 43000000000000002097152, \"" + + isc::util::clockToText(e.getBigInteger().second) + "\" ]" + first_sample; + + EXPECT_EQ(exp, e.getJSON()->str()); +} + // Checks whether reset() resets the statistics properly. TEST_F(ObservationTest, reset) { EXPECT_NO_THROW(a.addValue(static_cast<int64_t>(5678))); EXPECT_NO_THROW(b.addValue(56.78)); EXPECT_NO_THROW(c.addValue(dur5678)); EXPECT_NO_THROW(d.addValue("fiveSixSevenEight")); + EXPECT_NO_THROW(e.addValue(int128_t(43e21))); a.reset(); // integer b.reset(); // float c.reset(); // duration d.reset(); // string + e.reset(); // big integer EXPECT_EQ(0, a.getInteger().first); EXPECT_EQ(0.0, b.getFloat().first); EXPECT_EQ(StatsDuration::zero(), c.getDuration().first); EXPECT_EQ("", d.getString().first); + EXPECT_EQ(0, e.getBigInteger().first); ASSERT_EQ(a.getSize(), 1); ASSERT_EQ(b.getSize(), 1); ASSERT_EQ(c.getSize(), 1); ASSERT_EQ(d.getSize(), 1); + ASSERT_EQ(e.getSize(), 1); } // Checks whether an observation can keep its name. @@ -614,6 +711,7 @@ TEST_F(ObservationTest, names) { EXPECT_EQ("beta", b.getName()); EXPECT_EQ("gamma", c.getName()); EXPECT_EQ("delta", d.getName()); + EXPECT_EQ("epsilon", e.getName()); } -} +} // namespace diff --git a/src/lib/stats/testutils/stats_test_utils.h b/src/lib/stats/testutils/stats_test_utils.h index 808dced39a..cad71a35d1 100644 --- a/src/lib/stats/testutils/stats_test_utils.h +++ b/src/lib/stats/testutils/stats_test_utils.h @@ -27,12 +27,21 @@ typedef std::map<std::string, int64_t> StatMap; /// /// @param name StatsMgr name for the statistic to check. /// @param expected_value expected value of the statistic. -inline void checkStat(const std::string& name, const int64_t expected_value) { - using namespace isc::stats; - ObservationPtr obs = StatsMgr::instance().getObservation(name); - ASSERT_TRUE(obs) << " stat: " << name << " not found "; - ASSERT_EQ(expected_value, obs->getInteger().first) - << " stat: " << name << " value wrong"; +inline void checkStat(const std::string& name, + const isc::util::int128_t expected_value) { + ObservationPtr const obs(isc::stats::StatsMgr::instance().getObservation(name)); + + ASSERT_TRUE(obs) << "stat " << name << " not found "; + if (obs->getType() == Observation::STAT_INTEGER) { + EXPECT_EQ(expected_value, obs->getInteger().first) + << " wrong value for stat " << name; + } else if (obs->getType() == Observation::STAT_BIG_INTEGER) { + EXPECT_EQ(expected_value, obs->getBigInteger().first) + << " wrong value for stat " << name; + } else { + GTEST_FAIL() << "unexpected statistic type: " + << Observation::typeToText(obs->getType()); + } } /// @brief Check if a statistic does not exists. |